Eksis — Sistem Manajemen Surat & Disposisi Internal
Platform korespondensi digital yang menghubungkan regulator pendidikan tinggi regional, universitas, dan yayasan pengelolanya, menggantikan surat dan disposisi berbasis kertas dengan alur kerja digital real-time.
Ringkasan
Eksis adalah platform korespondensi digital yang dibangun selama Magang Fullstack MBKM di LLDIKTI V Yogyakarta, regulator regional yang mengawasi perguruan tinggi swasta. Platform ini menghubungkan tiga jenis organisasi — regulator (LLDIKTI), universitas (PT), dan yayasan pengelolanya (BP) — dalam satu platform untuk mengirim surat, meneruskan disposisi (instruksi tindak lanjut internal), dan melacak statusnya, dengan model akses hybrid role- dan attribute-based (RBAC + ABAC) sehingga setiap jenis organisasi hanya melihat yang relevan baginya.

Pratinjau Layar & Antarmuka
| Login | Dashboard |
| :---: | :---: |
|
|
|
| Autentikasi berbasis session dengan penguncian akun setelah beberapa kali gagal login | Ringkasan surat, disposisi, dan aktivitas yang relevan untuk organisasi yang sedang login |
| Kirim Surat | Profil |
| :---: | :---: |
|
|
|
| Alur tulis & kirim dengan nomor pelacakan otomatis dan lampiran multi-file | Profil pengguna, terhubung ke record organisasi spesifik (LLDIKTI/PT/BP) |
Masalah
- Korespondensi resmi antara regulator, universitas, dan yayasan masih berbasis kertas dan lambat dilacak.
- Tindak lanjut internal atas surat masuk (disposisi) tidak memiliki jejak digital, sehingga sulit mengetahui siapa bertanggung jawab atas apa, dan sampai kapan.
- Staf dan institusi mitra tidak punya cara mengecek status surat tanpa menghubungi kantor secara langsung.
Solusi
- Alur surat digital: mengirim, menerima, dan melacak surat resmi antar institusi, masing-masing dengan nomor pelacakan yang dibuat otomatis.
- Disposisi & tindak lanjut: meneruskan surat masuk ke orang yang tepat lengkap dengan instruksi dan tenggat waktu, serta melacak responsnya hingga selesai.
- Notifikasi real-time (in-app, push, dan email) sehingga tidak ada surat yang terlewat di inbox.
- Pelacakan publik: institusi mitra bisa mengecek status surat tanpa perlu akun, mirip seperti melacak paket.
- Akses berbasis role yang disesuaikan untuk tiap jenis organisasi, sehingga setiap user hanya melihat yang relevan baginya.
Arsitektur
Arsitektur layered/clean di sisi backend (domain → application → infra → presentation), dipadukan dengan frontend React SPA/PWA. Setiap fitur (surat, disposisi, auth, pengguna, referensi, …) punya use-case class sendiri yang di-inject lewat DI container, dipanggil dari HTTP controller melalui shared use-case interface.
flowchart LR
subgraph Client
FE[React SPA / PWA]
end
subgraph Backend["Backend (Hono, arsitektur layered)"]
R[presentation<br/>routes + controllers]
UC[application<br/>use-cases]
REPO[infra<br/>repositories, Drizzle ORM]
MW[middleware<br/>JWT · permission · upload]
end
DB[(PostgreSQL)]
REDIS[(Redis)]
Q[Queue<br/>email · notifikasi]
WS[WebSocket]
PUSH[Web Push]
FE -->|REST/JSON, multipart| MW --> R --> UC --> REPO --> DB
UC -->|enqueue job| Q --> REDIS
Q --> PUSH -->|push ke browser| FE
UC -->|emit event| WS -->|realtime| FE
FE -.->|WS auth via JWT| WS
Alur Login
sequenceDiagram
participant U as Pengguna
participant FE as Frontend (React)
participant BE as Backend (Hono)
participant DB as PostgreSQL
U->>FE: masukkan username + password
FE->>BE: POST /auth/login
BE->>DB: cari user
alt user tidak ditemukan / password salah
BE->>DB: tambah failed_login_attempts
Note over BE: 5x gagal → terkunci selama 10 menit
BE-->>FE: error 400/403
else valid
BE->>DB: muat profil organisasi (LLDIKTI/PT/BP) + roles
Note over BE: profil organisasi belum lengkap → diblokir dengan pesan khusus
BE->>BE: terbitkan JWT access + refresh token<br/>(claims: userId, roles, org id)
BE->>DB: simpan token, update last_login
BE-->>FE: access_token, refresh_token
end
FE->>FE: simpan token (client-side state)
FE->>BE: GET /auth/permissions (bangun menu dari akses)
Kirim Surat → Disposisi → Respons
flowchart TD
A[User mengirim surat] -->|permission: send.letter| B{Upload file<br/>surat utama + lampiran}
B --> C[Buat nomor pelacakan otomatis<br/>per tahun + jenis surat]
C --> D[Simpan surat + penerima<br/>polymorphic: user / departemen LLDIKTI / BP / PT / eksternal]
D --> E[Status awal tercatat]
E --> F[Beri tahu penerima<br/>in-app + push + WebSocket]
F --> G{Penerima membuka surat}
G -->|opsional| H[Teruskan surat]
G --> I[Buat disposisi<br/>permission: create.disposition]
I --> J[Penerima disposisi<br/>+ aksi ditandai]
J --> K[Beri tahu penerima disposisi]
K --> L[Penerima merespons<br/>+ lampiran file]
L --> M[Riwayat status tercatat]
M --> N[Disposisi ditandai selesai]
Fitur pendukung lainnya: surat antedate (surat yang tanggal pembuatan aslinya berbeda dari tanggal input sistem, dicatat lengkap dengan alasannya), pelacakan publik untuk kontak eksternal mengecek status surat tanpa login, timeline riwayat/relasi lengkap per surat (balasan, parent/child), dan upload file dua fase (temporary → permanent) untuk menghindari file yatim akibat submission yang gagal.
Notifikasi (multi-channel)
flowchart LR
EV[Event use-case<br/>surat baru / disposisi baru / dll.] --> Q[Queue<br/>Redis]
Q --> N1[Job notifikasi]
Q --> N2[Job email]
N1 --> DB[(tabel notifications)]
N1 --> WS[Push WebSocket<br/>ke client online]
N1 --> PUSH[Web Push<br/>ke subscriber browser]
N2 --> MAIL[Kirim email]
Model Data (ERD)
Surat & Disposisi
lettersdanletter_recipientsmenggunakan sender/recipient polymorphic: hanya satu dari referensi user / departemen(LLDIKTI) / yayasan(BP) / universitas(PT) / kontak-eksternal yang diisi, tergantung jenis penerimanya.
erDiagram
USERS ||--o{ LETTERS : "created_by"
USERS ||--o{ LETTER_RECIPIENTS : "recipient"
USERS ||--o{ DISPOSITIONS : "from_user"
USERS ||--o{ DISPOSITION_RECIPIENTS : "to_user"
LETTERS ||--o{ LETTER_RECIPIENTS : memiliki
LETTERS ||--o{ LETTER_FILES : memiliki
LETTERS ||--o{ LETTER_TAGS : memiliki
LETTERS ||--o{ DISPOSITIONS : memicu
LETTERS ||--o| ANTEDATED_LETTERS : "jika antedate"
LETTERS ||--o{ LETTER_PROGRESS : "dilacak oleh"
LETTERS }o--o| LETTERS : "parent_letter (balasan/relasi)"
LETTER_FILES }o--|| FILES : mereferensikan
LETTER_RECIPIENTS }o--o| EXTERNAL_CONTACTS : "jika eksternal"
DISPOSITIONS ||--o{ DISPOSITION_RECIPIENTS : "dikirim ke"
DISPOSITIONS ||--o{ DISPOSITION_ACTIONS : menandai
DISPOSITIONS ||--o{ DISPOSITION_RESPONSES : respons
DISPOSITION_RESPONSES ||--o{ DISPOSITION_RESPONSE_FILES : lampiran
LETTERS {
uuid letter_id PK
varchar letter_number UK
varchar agenda_number UK
date date
varchar subject
uuid created_by_id FK
int status_id FK
int confidentiality_id FK
int priority_id FK
boolean is_antedated_letter
uuid parent_letter_id FK
}
DISPOSITIONS {
uuid disposition_id PK
uuid letter_id FK
uuid from_user_id FK
text instruction
int status_id FK
boolean is_public
date deadline_date
boolean is_need_report
}
FILES {
uuid file_id PK
varchar file_name
text file_url
enum file_status "temporary|permanent"
boolean is_external_file
}
Pengguna, Role & Permission (hybrid RBAC + ABAC)
Model permission dua tingkat: role → permission secara default, dengan override per-user lewat tabel user-permission langsung. Setiap permission bisa dibatasi lebih spesifik lewat attribute (misalnya permission "view letter" yang dibatasi hanya untuk data scope tertentu).
erDiagram
REF_USER_TYPE ||--o{ USERS : mengkategorikan
REF_USER_TYPE ||--o{ REF_ROLES : "role per tipe"
USERS ||--o| BP_USERS : "profil jika BP"
USERS ||--o| PT_USERS : "profil jika PT"
USERS ||--o| LLDIKTI_USERS : "profil jika LLDIKTI"
USERS ||--o{ USER_ROLES : memiliki
USERS ||--o{ USER_PERMISSIONS : "override langsung"
REF_ROLES ||--o{ USER_ROLES : "ditugaskan ke"
REF_ROLES ||--o{ ROLE_PERMISSIONS : memberi
REF_PERMISSIONS ||--o{ ROLE_PERMISSIONS : via
REF_PERMISSIONS ||--o{ USER_PERMISSIONS : via
REF_PERMISSION_ATTRIBUTES ||--o{ ROLE_PERMISSIONS : scope
REF_PERMISSION_ATTRIBUTES ||--o{ USER_PERMISSIONS : scope
USERS {
uuid user_id PK
varchar username UK
varchar email UK
int user_type_id FK
boolean is_active
int failed_login_attempts
timestamp blocked_until
}
REF_ROLES {
int role_id PK
varchar role_name
int user_type_id FK
}
REF_PERMISSIONS {
uuid permission_id PK
varchar permission_name
}
Permission mengikuti konvensi penamaan action.resource (contoh: send.letter, view.letter, create.disposition, update.forward-letter), diterapkan per-route lewat permission middleware, dengan JWT claim yang membawa roles dan organization id sehingga pengecekan otorisasi tidak perlu query tambahan.
Dampak
- Menggantikan korespondensi manual berbasis kertas dengan alur kerja digital yang bisa dilacak di regulator, universitas, dan yayasan.
- Mengurangi waktu yang dibutuhkan untuk menindaklanjuti dan menyelesaikan disposisi internal.
- Memberi setiap institusi visibilitas atas status korespondensi mereka sendiri secara real-time.




