Lompat ke konten
dokumentasi.id
Commit 0da916f
v1
Star0

Ada commit baru di branch utama.

2a17915

5

Skema Database Lokal

Termimus menyimpan seluruh data yang dapat diedit pengguna di satu berkas SQLite bernama termimus.db. get_database_path() menaruhnya di <config_dir>/termimus/termimus.db dan jatuh kembali ke PathBuf::from(".") bila dirs::config_dir() tidak tersedia. src-tauri/src/db/mod.rs:1250-1255 Database::new membuat direktori induk, membuka Connection, menyalakan PRAGMA foreign_keys = ON, lalu memanggil init_tables(); karena run() memakai expect("Failed to initialize SQLite database"), kegagalan di langkah mana pun menghentikan proses. src-tauri/src/db/mod.rs:13-26 src-tauri/src/lib.rs:23

Seluruh command berbagi satu Connection yang dibungkus Mutex, sehingga Database disimpan sebagai Arc<Database> di AppState. src-tauri/src/db/mod.rs:8-10

Skema dalam satu diagram

init_tables() menjalankan satu execute_batch berisi sembilan pernyataan CREATE TABLE IF NOT EXISTS, sehingga membuka database yang sudah ada tidak menjatuhkan tabel lama. src-tauri/src/db/mod.rs:28-129 Kesembilan tabel itu beserta relasinya:

erDiagram
    vault_meta {
        text key PK
        blob value
    }
    folders {
        text id PK
        text name
        text parent_id
        text created_at
        text updated_at
    }
    credentials {
        text id PK
        text kind
        blob ciphertext
        blob nonce
        blob passphrase_ciphertext
        blob passphrase_nonce
        text name
        text key_type
        text public_key
        text fingerprint
        text username
        text created_at
        text updated_at
    }
    hosts {
        text id PK
        text folder_id FK
        text credential_id FK
        text label
        text address
        integer port
        text username
        text auth_method
        text tags
        text last_connected_at
        text os_icon
        text jump_host_id
        text created_at
        text updated_at
    }
    port_forwards {
        text id PK
        text host_id FK
        text label
        text forward_type
        text local_address
        integer local_port
        text remote_address
        integer remote_port
        text created_at
    }
    snippets {
        text id PK
        text title
        text command
        text tags
        text created_at
        text updated_at
    }
    workspace_presets {
        text id PK
        text name
        text description
        text layout
        text nodes
        boolean broadcast_on_launch
        text created_at
        text updated_at
    }
    known_hosts {
        text address PK
        integer port PK
        text key_type
        text fingerprint
        text first_seen_at
        text last_seen_at
    }
    tombstones {
        text entity_type PK
        text entity_id PK
        text deleted_at
    }
    folders ||--o{ hosts : "folder_id, ON DELETE SET NULL"
    credentials ||--o{ hosts : "credential_id, ON DELETE SET NULL"
    hosts ||--o{ port_forwards : "host_id, ON DELETE CASCADE"
    folders ||--o{ folders : "parent_id, tanpa constraint"
    hosts ||--o{ hosts : "jump_host_id, tanpa constraint"
%% fig: Relasi antar tabel di termimus.db
%% desc: Tiga foreign key dideklarasikan pada hosts dan port_forwards, sementara parent_id dan jump_host_id hanya berupa kolom biasa. vault_meta, snippets, workspace_presets, known_hosts, dan tombstones berdiri sendiri.
Fig. 1: Relasi antar tabel di termimus.dbTiga foreign key dideklarasikan pada hosts dan port_forwards, sementara parent_id dan jump_host_id hanya berupa kolom biasa. vault_meta, snippets, workspace_presets, known_hosts, dan tombstones berdiri sendiri.
TabelPrimary keyStructPeran
vault_metakey—pasangan salt, verifier_ciphertext, dan verifier_nonce yang dipakai alur vault src-tauri/src/db/mod.rs:33-36
foldersidFolder src-tauri/src/db/models.rs:3-11hierarki folder lewat parent_id
credentialsidCredential src-tauri/src/db/models.rs:89-111ciphertext dan nonce secret
hostsidHost src-tauri/src/db/models.rs:39-63daftar host SSH, menunjuk folder dan credential
port_forwardsidPortForwardRule src-tauri/src/db/models.rs:190-201aturan tunnel per host_id
snippetsidSnippet src-tauri/src/db/models.rs:214-222perintah tersimpan, tanpa relasi
workspace_presetsidWorkspacePreset src-tauri/src/db/models.rs:242-256layout plus nodes berisi JSON
known_hosts(address, port)KnownHost src-tauri/src/db/models.rs:269-277fingerprint TOFU per alamat dan port
tombstones(entity_type, entity_id)Tombstone src-tauri/src/db/models.rs:279-284penanda hapus untuk penggabungan data

Tabel credentials merangkap dua peran: penyimpan secret terenkripsi sekaligus sumber data Keychain. Credential membawa ciphertext dan nonce, sedangkan KeychainItem adalah tampilan tanpa secret yang dibentuk lewat impl From<&Credential>; list_keychain_items hanya membaca baris yang name-nya tidak kosong. src-tauri/src/db/models.rs:89-111 src-tauri/src/db/models.rs:115-126 src-tauri/src/db/mod.rs:489-496

Dua kolom bertipe TEXT sebenarnya menyimpan JSON: list_hosts mem-parse tags dengan serde_json::from_str, dan save_workspace_preset men-serialize nodes dengan serde_json::to_string. src-tauri/src/db/mod.rs:208-236 src-tauri/src/db/mod.rs:803-808

Tidak ada CREATE INDEX eksplisit di berkas ini; setiap query daftar mengurutkan sendiri lewat ORDER BY, misalnya ORDER BY label ASC di list_hosts. src-tauri/src/db/mod.rs:208-212

Foreign key dan pembersihan rujukan

Hanya hosts dan port_forwards yang mendeklarasikan FOREIGN KEY, dan PRAGMA foreign_keys = ON membuat aksinya benar-benar berlaku. src-tauri/src/db/mod.rs:62-90

KolomArahAksi saat induk dihapusRujukan
hosts.folder_idhosts → foldersON DELETE SET NULLsrc-tauri/src/db/mod.rs:62-77
hosts.credential_idhosts → credentialsON DELETE SET NULLsrc-tauri/src/db/mod.rs:62-77
port_forwards.host_idport_forwards → hostsON DELETE CASCADEsrc-tauri/src/db/mod.rs:79-90
folders.parent_idfolders → folderstidak ada constraintsrc-tauri/src/db/mod.rs:38-44
hosts.jump_host_idhosts → hoststidak ada constraintsrc-tauri/src/db/mod.rs:131-146

⚠️ folders.parent_id dan hosts.jump_host_id tidak punya constraint, jadi rujukan dibersihkan manual: delete_folder men-NULL-kan hosts.folder_id dan folders.parent_id sebelum menghapus barisnya, dan delete_host men-NULL-kan jump_host_id yang menunjuk host tersebut. src-tauri/src/db/mod.rs:574-586 src-tauri/src/db/mod.rs:319-330 Import backup mengulang pembersihan yang sama sebagai langkah penutup lewat tiga UPDATE ... NOT IN (SELECT ...). src-tauri/src/db/mod.rs:1185-1200

Urutan hapus saat replace_all mengikuti arah foreign key: tombstones, known_hosts, snippets, workspace_presets, port_forwards, hosts, credentials, lalu folders. src-tauri/src/db/mod.rs:985-994

Karena port_forwards.host_id memakai ON DELETE CASCADE, menghapus satu host ikut menghapus aturan tunnel-nya, sementara delete_host hanya menulis tombstone dengan entity_type 'host'. src-tauri/src/db/mod.rs:79-90 src-tauri/src/db/mod.rs:319-330

Operasi yang menyentuh banyak baris sekaligus dibungkus satu transaksi: rekey_vault memperbarui tiga baris vault_meta dan seluruh credentials, lalu memanggil tx.commit() di akhir. src-tauri/src/db/mod.rs:381-420

Bootstrap dan migrasi kolom

Setelah blok CREATE TABLE, init_tables() menjalankan ALTER TABLE satu per satu dan membuang galatnya dengan let _ =, sehingga kolom yang sudah ada cukup gagal tanpa menghentikan startup. src-tauri/src/db/mod.rs:131-146 Konsekuensinya, ALTER untuk credentials.name, key_type, public_key, fingerprint, username, created_at, dan updated_at selalu gagal pada database baru, karena kolom-kolom itu sudah ada di blok CREATE TABLE. src-tauri/src/db/mod.rs:46-60

graph TD
  PATH["get_database_path: config_dir()/termimus/termimus.db"] --> OPEN["Connection::open"]
  OPEN --> FKON["PRAGMA foreign_keys = ON"]
  FKON --> CREATE["init_tables: 9 x CREATE TABLE IF NOT EXISTS"]
  CREATE --> ALTER["ALTER TABLE kolom tambahan"]
  ALTER --> IGNORE["galat ALTER diabaikan lewat let _ ="]
  ALTER --> BACKFILL["UPDATE folders SET updated_at = created_at"]
  IGNORE --> READY["Database dipakai command save_* dan list_*"]
  BACKFILL --> READY
  style PATH fill:#336791,color:#fff
  style CREATE fill:#336791,color:#fff
  style ALTER fill:#f59e0b,color:#000
  style IGNORE fill:#dc2626,color:#fff
  style READY fill:#2d7d46,color:#fff
%% fig: Bootstrap skema saat Database::new
%% desc: Database dibuka dengan foreign key aktif, sembilan tabel dibuat bila belum ada, lalu kolom tambahan dimigrasikan dengan galat yang sengaja diabaikan.
Fig. 2: Bootstrap skema saat Database::newDatabase dibuka dengan foreign key aktif, sembilan tabel dibuat bila belum ada, lalu kolom tambahan dimigrasikan dengan galat yang sengaja diabaikan.
TabelKolomDefinisi di ALTERCatatan
credentialsname, key_type, public_key, fingerprint, created_at, updated_atTEXT NOT NULL DEFAULT ''sudah ada di blok CREATE TABLE, jadi ALTER gagal
credentialsusernameTEXTjuga sudah ada di blok CREATE TABLE
hostslast_connected_atTEXTtanpa DEFAULT
hostsos_iconTEXTkomentar kode menandainya v0.4.8+ auto-detected OS icon
hostsjump_host_idTEXTkomentar kode menandainya v0.5.0: bastion / jump host proxyjump
foldersupdated_atTEXT NOT NULL DEFAULT ''ditutup backfill UPDATE folders SET updated_at = created_at WHERE updated_at = ''

Tombstone: jejak penghapusan

Tabel tombstones memakai primary key gabungan (entity_type, entity_id), sehingga satu entitas hanya punya satu penanda. src-tauri/src/db/mod.rs:122-127 record_tombstone menimpa deleted_at dengan waktu sekarang, sedangkan save_tombstone memakai MAX(deleted_at, excluded.deleted_at) agar penanda dari backup yang lebih tua tidak memundurkan waktu hapus. src-tauri/src/db/mod.rs:151-172 is_tombstoned mengembalikan Option<String> berisi deleted_at, dan nilai itulah yang dipakai saat merge untuk memutuskan baris mana yang dilewati; mekanisme merge-nya dibahas di Backup, Sync Relay, dan API Server. src-tauri/src/db/mod.rs:191-200

entity_typeDitulis oleh
hostdelete_host src-tauri/src/db/mod.rs:319-330
credentialdelete_credential src-tauri/src/db/mod.rs:475-485
folderdelete_folder src-tauri/src/db/mod.rs:574-586
port_forwarddelete_port_forward src-tauri/src/db/mod.rs:663-673
snippetdelete_snippet src-tauri/src/db/mod.rs:721-731
workspace_presetdelete_workspace_preset src-tauri/src/db/mod.rs:831-841

Tidak semua tabel ikut tercatat: vault_meta dan known_hosts tidak punya padanan entity_type, dan known_hosts dihapus langsung lewat delete_known_host tanpa meninggalkan penanda. src-tauri/src/db/mod.rs:884-891

Saat menambah tabel baru, konvensi repository meminta CREATE TABLE IF NOT EXISTS di init_tables(), struct baris di models.rs, method CRUD di Database, dan bila tabel itu portabel juga disambungkan ke BackupBundle. AGENTS.md:34