Bentuk tabel SQLite yang dipakai aplikasi dan alur pemrosesan endpoint login, dirender dengan Mermaid.
Mermaid.jsSQLite6 tabelPK · FK · UK
Entity Relationship Diagram
Relasi utama: satu pengguna memiliki banyak baris absensi dan banyak access token, serta satu profil SISDM. Tabel percobaan login dan metadata berdiri sendiri.
Memuat diagram…
users
Akun login, profil kepegawaian, unit, jabatan, dan role. Primary key user_id, NIP unik.
attendance
Absensi harian per NIP. Primary key gabungan user_no + work_date, foreign key ke users.user_no.
pegawai_sisdm
Profil kepegawaian dari SISDM: gelar, golongan, unit, jabatan, pendidikan, dan data lahir. Satu baris per user_id. NUP dan NUP gaji diturunkan dari NIP di tabel users.
access_tokens
Token login berkelanjutan. Secret token disimpan sebagai hash SHA-256, masa aktif sliding 30 hari sejak pemakaian terakhir, dan dapat dicabut lewat logout.
login_attempts
Riwayat percobaan gagal untuk throttle 5 kali / 15 menit per IP + User ID. Sengaja tanpa foreign key karena user bisa tidak ditemukan.
app_meta
Penyimpanan key-value internal, dipakai untuk marker seed_version agar seed hanya berjalan sekali.
Alur Login API
Urutan validasi, throttle, verifikasi password, pembuatan access token, dan pengambilan data absensi serta profil SISDM pada POST /apisisdm/api/apps/sign.
Memuat diagram…
Alur Bearer Token
Validasi token pada GET /apisisdm/api/apps/me dan POST /apisisdm/api/apps/logout. Setiap pemakaian memperpanjang masa aktif token.
Memuat diagram…
erDiagram
USERS ||--o{ ATTENDANCE : "user_no"
USERS ||--o| PEGAWAI_SISDM : "profil"
USERS ||--o{ ACCESS_TOKENS : "token"
USERS {
TEXT user_id PK "User ID login"
TEXT password_hash "Hash password"
TEXT user_no UK "NIP"
TEXT user_name "Nama lengkap"
TEXT dept_code "Kode unit"
TEXT dept_name "Nama unit"
TEXT dept_abbr "Singkatan unit"
INTEGER position_id "ID jabatan"
TEXT position_name "Nama jabatan"
TEXT position_name_2 "Nama jabatan lengkap"
TEXT position_alias "Alias jabatan"
TEXT position_category "Kategori jabatan"
TEXT level_id "Level"
TEXT islevel "Flag level"
INTEGER role_id "1 = admin"
TEXT strcompanyno "Nomor perusahaan"
TEXT personno "Nomor person"
TEXT device_id "ID perangkat"
TEXT device_type "Tipe perangkat"
TEXT datechangepass "Waktu ganti password"
}
ATTENDANCE {
TEXT user_no PK,FK "Referensi users.user_no"
TEXT work_date PK "Tanggal absensi"
TEXT jam_masuk "Bisa null"
TEXT jam_keluar "Bisa null"
}
PEGAWAI_SISDM {
TEXT user_id PK,FK "Referensi users.user_id"
TEXT gelar_depan
TEXT gelar_belakang
INTEGER gol_bp
TEXT gol_pns "Bisa null"
TEXT unit_kerja
TEXT unit_kerja_sub
TEXT kode_org
TEXT jabatan
TEXT jabatan_tingkat "Bisa null"
TEXT nama_jabatan
TEXT kelas_jabatan
TEXT pendidikan
TEXT jurusan
TEXT status_pegawai
TEXT gol_bp_gaji
TEXT tempat_lahir
TEXT tgl_lahir "Bisa null"
}
ACCESS_TOKENS {
INTEGER id PK "Prefix access_token"
TEXT user_id FK "Referensi users.user_id"
TEXT token_hash UK "Hash SHA-256 secret"
TEXT token_type "Bearer"
TEXT ip
TEXT user_agent
TEXT created_at
TEXT last_used_at "Bisa null"
TEXT expires_at "Sliding 30 hari"
TEXT revoked_at "Null jika aktif"
}
LOGIN_ATTEMPTS {
INTEGER id PK
TEXT ip "IP pemanggil"
TEXT user_id "User ID yang dicoba"
TEXT attempted_at "Waktu percobaan"
}
APP_META {
TEXT key PK
TEXT value "Contoh: seed_version"
}
flowchart TD
A(["POST /apisisdm/api/apps/sign"]) --> B{"Metode POST?"}
B -- "Tidak" --> B1["405 · code 04"]
B -- "Ya" --> C["Ambil id dan paswd dari form-data"]
C --> D{"id / paswd kosong?"}
D -- "Ya" --> D1["422 · code 02"]
D -- "Tidak" --> E{"Gagal 5x dalam 15 menit?"}
E -- "Ya" --> E1["429 · code 03"]
E -- "Tidak" --> F["Cari user berdasarkan id"]
F --> G{"User ada dan password valid?"}
G -- "Tidak" --> G1["Catat percobaan gagal<br/>401 · code 01"]
G -- "Ya" --> H["Hapus riwayat percobaan"]
H --> I["Ambil absensi dan profil SISDM"]
I --> K["Buat access_token<br/>simpan hash di access_tokens"]
K --> J(["200 · success<br/>user + absensi + pegawai + bearer"])
classDef step fill:#eef2ff,stroke:#6366f1,color:#312e81;
classDef fail fill:#fff1f2,stroke:#f43f5e,color:#881337;
classDef warn fill:#fffbeb,stroke:#f59e0b,color:#78350f;
classDef ok fill:#ecfdf5,stroke:#10b981,color:#065f46;
class A,C,F,H,I,K step;
class B1,D1,G1 fail;
class E1 warn;
class J ok;
flowchart TD
A(["Request dengan Authorization: Bearer id|secret"]) --> B{"Header Bearer ada?"}
B -- "Tidak" --> B1["401 · code 05"]
B -- "Ya" --> C["Cari id token di access_tokens"]
C --> D{"Hash cocok, belum revoked,<br/>belum expired?"}
D -- "Tidak" --> B1
D -- "Ya" --> E["Perbarui last_used_at<br/>expires_at = sekarang + 30 hari"]
E --> F{"Endpoint"}
F -- "GET /me" --> G(["200 · user + absensi + pegawai"])
F -- "POST /logout" --> H(["200 · token dicabut"])
classDef step fill:#eef2ff,stroke:#6366f1,color:#312e81;
classDef fail fill:#fff1f2,stroke:#f43f5e,color:#881337;
classDef ok fill:#ecfdf5,stroke:#10b981,color:#065f46;
class A,C,E step;
class B1 fail;
class G,H ok;