Skip to content

Database Schema ​

Teldock menyimpan metadata di MySQL/MariaDB melalui Sequelize. Model berada di backend/src/models/ dan didaftarkan bersama di backend/src/models/index.js, yang juga mendeklarasikan asosiasinya. Byte file mentah tidak pernah disimpan di database.

Relasi entitas ​

User ​

Record akun dan pemilik seluruh resource lain. Tabel: users.

KolomTipeCatatan
idUUIDPrimary key, default UUIDV4.
telegramIdBIGINTUnik, nullable.
emailSTRING(255)Unik.
usernameSTRING(255)Nullable.
firstName / lastNameSTRING(100)Nullable.
avatarUrlSTRING(500)Nullable.
passwordHashSTRING(255)Nullable; hash bcrypt.
storageQuotaBytesBIGINTNullable; NULL berarti tanpa kuota tetap.
storageUsedBytesBIGINTDefault 0; perkiraan pemakaian untuk statistik.
premiumUntilDATENullable.
isPremiumBOOLEANDefault false.
isActiveBOOLEANDefault true.

Indeks: telegramId, email. Method helper: canUpload(), User.updateStorageUsage().

BotToken ​

Pool bot token Telegram per pengguna untuk throughput paralel. Tabel: bot_tokens.

KolomTipeCatatan
idUUIDPrimary key.
userIdUUIDFK ke users.id, ON DELETE CASCADE.
tokenEncryptedTEXTBot token terenkripsi AES-256-CBC (iv:ciphertext).
botUsernameSTRING(100)Nama tampilan.
botIdBIGINTID bot Telegram numerik.
isActiveBOOLEANApakah bot tersedia di pool.
lastUsedAtDATETimestamp untuk keadilan round-robin.

Indeks: userId, isActive. getToken() mendekripsi nilai tersimpan saat dibutuhkan.

File ​

Metadata file dan referensi Telegram. Tabel: files.

KolomTipeCatatan
idUUIDPrimary key.
userIdUUIDFK ke users.id, ON DELETE CASCADE.
folderIdUUIDFK ke folders.id, ON DELETE SET NULL.
telegramChatIdBIGINTID channel penyimpanan (tidak diekspos API).
telegramMessageIdINTEGERNullable untuk file chunked.
telegramFileIdSTRING(255)Nullable untuk file chunked.
originalFilenameSTRING(500)Nama saat diunggah.
displayFilenameSTRING(500)Nama yang ditampilkan di UI.
mimeTypeSTRING(100)Tipe MIME.
fileSizeBIGINTUkuran dalam byte.
isChunkedBOOLEANDipecah ke beberapa pesan Telegram.
partCountINTEGERDefault 1.
isEncryptedBOOLEANApakah part dienkripsi saat disimpan.
encryptionSaltSTRING(64)Salt hex untuk key per file.
checksumSTRING(64)SHA-256 dari file asli.
isPublicBOOLEANDefault false.
isFavoriteBOOLEANDefault false.
sharedTokenSTRING(64)Unik, nullable.
downloadCountINTEGERDefault 0.
lastDownloadedAtDATENullable.
isDeletedBOOLEANFlag soft delete.
deletedAtDATENullable.

Indeks: userId, folderId, idx_telegram_file (telegramFileId), idx_user_files (userId, isDeleted, createdAt), idx_telegram_chat_msg (telegramChatId, telegramMessageId), unik idx_shared_token (sharedToken), dan FULLTEXT idx_search (originalFilename, mimeType).

INFO

File.toJSON() menghapus telegramChatId, telegramMessageId, dan telegramFileId sehingga identifier penyimpanan internal tidak pernah sampai ke response API.

FilePart ​

Satu chunk dari file besar, merujuk pada satu pesan Telegram. Tabel: file_parts.

KolomTipeCatatan
idUUIDPrimary key.
fileIdUUIDFK ke files.id, ON DELETE CASCADE.
partIndexINTEGERUrutan berbasis nol dalam file.
telegramChatIdBIGINTChannel yang menyimpan part ini.
telegramMessageIdINTEGERID pesan part ini.
telegramFileIdSTRING(255)File ID Telegram part ini.
partSizeBIGINTUkuran tersimpan (ukuran terenkripsi jika dienkripsi).
plainSizeBIGINTUkuran sebelum enkripsi.
encryptionIvSTRING(32)IV hex, nullable.
checksumSTRING(64)SHA-256 dari plaintext part.

Indeks: fileId, dan unik idx_file_part_order (fileId, partIndex).

FileVersion ​

Snapshot immutable sebuah file untuk riwayat versi. Tabel: file_versions.

KolomTipeCatatan
idUUIDPrimary key.
fileIdUUIDFK ke files.id, ON DELETE CASCADE.
userIdUUIDFK ke users.id, ON DELETE CASCADE.
versionNumberINTEGERBerurutan (1, 2, 3...).
telegramMessageId / telegramFileId / telegramChatIdmixedReferensi praktis untuk single-part.
isChunkedBOOLEANDefault false.
partCountINTEGERDefault 1.
isEncryptedBOOLEANDefault false.
encryptionSaltSTRING(64)Nullable.
partsSnapshotLONGTEXTArray JSON berisi record FilePart versi tersebut.
originalFilename / displayFilenameSTRING(500)Nama saat snapshot.
mimeTypeSTRING(100)Tipe MIME.
fileSizeBIGINTUkuran dalam byte.
checksumSTRING(64)Hash SHA-256.

Indeks: fileId, userId, versionNumber. getMetadata() mengembalikan ringkasan yang dipangkas.

FileTag ​

Tabel join untuk relasi many-to-many File ↔ Tag. Tabel: file_tags.

KolomTipeCatatan
fileIdUUIDFK ke files.id, ON DELETE CASCADE.
tagIdUUIDFK ke tags.id, ON DELETE CASCADE.

Indeks: unik uniq_file_tag (fileId, tagId). Tanpa timestamp.

Folder ​

Folder hierarkis milik pengguna. Tabel: folders.

KolomTipeCatatan
idUUIDPrimary key.
userIdUUIDFK ke users.id, ON DELETE CASCADE.
parentFolderIdUUIDSelf-reference, ON DELETE SET NULL.
nameSTRING(255)Wajib.
pathSTRING(1000)Path lengkap (misalnya /Documents/Projects).
depthINTEGERRoot bernilai 0.
displayOrderINTEGERUrutan di dalam parent.
iconSTRING(50)Default "folder".

Indeks: userId, parentFolderId, folders_tree_index (userId, parentFolderId, displayOrder), dan unik folders_path_idx (path).

Public share yang dibuat untuk sebuah file. Tabel: shared_links.

KolomTipeCatatan
idUUIDPrimary key.
fileIdUUIDFK ke files.id, ON DELETE CASCADE.
creatorIdUUIDFK ke users.id, ON DELETE CASCADE.
tokenSTRING(512)JWT unik yang dipakai di URL share.
passwordHashSTRING(255)Hash bcrypt opsional.
downloadLimitINTEGERNullable (tanpa batas).
usedDownloadsINTEGERDefault 0.
expiresAtDATENullable (tidak pernah kedaluwarsa).
allowDownloadBOOLEANDefault true.
allowPreviewBOOLEANDefault true.
accessedCountINTEGERDefault 0.
lastAccessedAtDATENullable.

Indeks: token, idx_expires (expiresAt), idx_file_creator (fileId, creatorId). SharedLink.consumeDownload() memesan satu download dengan satu update kondisional sehingga request yang bersamaan tidak dapat melewati batas.

SmartFolder ​

Filter file tersimpan (bukan folder sesungguhnya). Tabel: smart_folders.

KolomTipeCatatan
idUUIDPrimary key.
userIdUUIDFK ke users.id, ON DELETE CASCADE.
nameSTRING(80)Wajib.
iconSTRING(30)Default "sparkles".
criteriaTEXTJSON: { favorite?, tagId?, type?, q? }.

Indeks: userId.

Tag ​

Label yang ditentukan pengguna. Tabel: tags.

KolomTipeCatatan
idUUIDPrimary key.
userIdUUIDFK ke users.id, ON DELETE CASCADE.
nameSTRING(50)Wajib.
colorSTRING(20)Default "#10b981".

Indeks: userId, dan unik uniq_user_tag (userId, name).

TelegramConfig ​

Kredensial bot dan channel penyimpanan Telegram per pengguna. Tabel: user_telegram_configs.

KolomTipeCatatan
idUUIDPrimary key.
userIdUUIDFK ke users.id, ON DELETE CASCADE.
botTokenEncryptedTEXTBot token terenkripsi AES-256-CBC.
storageChatIdSTRING(50)ID chat/channel penyimpanan.
chatTypeENUM('channel','group','private')Default "channel".
usernameSTRING(100)Nullable.
isActiveTINYINT(1)Default 1.

Indeks: userId, isActive. TelegramConfig.toJSON() menghapus botTokenEncrypted dan storageChatId; decryptToken() mendekripsi saat dibutuhkan.

Model penyimpanan chunked ​

Bot API Telegram membatasi satu kali upload, sehingga Teldock memecah file besar menjadi part dengan ukuran terbatas (sekitar 18 MB, diatur oleh TG_PART_SIZE). Setiap part diunggah sebagai pesan Telegram tersendiri dan dicatat sebagai baris FilePart:

  • File.isChunked menandai file multi-part, dan File.partCount mencatat jumlah part yang ada.
  • Setiap FilePart menyimpan partIndex, telegramChatId, telegramMessageId, dan telegramFileId untuk chunk tersebut, plus ukurannya dan IV enkripsi opsional.
  • Saat download, part dibaca kembali sesuai urutan partIndex, didekripsi bila perlu, lalu disatukan menjadi satu stream dengan dukungan HTTP Range.
  • FileVersion.partsSnapshot menyimpan daftar part lengkap untuk file chunked/terenkripsi sehingga versi sebelumnya dapat dipulihkan secara persis.

Migrasi ​

Skrip migrasi berada di backend/scripts/ dan dijalankan dari direktori backend.

SkripPerintah npmFungsi
migrate.jsnpm run migrateMembuat/memperbarui seluruh tabel dari model.
migrate-chunked-storage.jsnpm run migrate:chunkedMenambah kolom chunked/encrypted pada files.
migrate-drop-quota.jsnpm run migrate:drop-quotaMembuat storageQuotaBytes nullable dan membersihkan kuota lama.
migrate-widen-share-token.jsnpm run migrate:widen-share-tokenMelebarkan shared_links.token menjadi VARCHAR(512).
migrate-version-snapshots.jsnpm run migrate:version-snapshotsMenambah kolom part snapshot pada file_versions.
migrate-tags-favorites.jsnpm run migrate:tags-favoritesMenambah files.isFavorite, tags, file_tags, smart_folders.

Dua skrip tambahan dijalankan langsung dengan Node saat meningkatkan database lama: scripts/migrate-file-versions.js dan scripts/migrate-telegram-configs.js.

bash
cd backend
npm run migrate

Lihat Deployment untuk menjalankan migrasi di produksi.

Halaman terkait ​

Released under the MIT License.