Kembali ke Proyek
Sistem Internal
LLDIKTI V

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.

React
TypeScript
Hono.js
Drizzle ORM
PostgreSQL
Redis
WebSocket

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.

Dasbor


Pratinjau Layar & Antarmuka

| Login | Dashboard | | :---: | :---: | | Halaman 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 | | :---: | :---: | | 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

letters dan letter_recipients menggunakan 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.
Tangkapan Layar
Eksis — Sistem Manajemen Surat & Disposisi Internal — screenshot 1
Eksis — Sistem Manajemen Surat & Disposisi Internal — screenshot 2
Eksis — Sistem Manajemen Surat & Disposisi Internal — screenshot 3
Eksis — Sistem Manajemen Surat & Disposisi Internal — screenshot 4
Eksis — Sistem Manajemen Surat & Disposisi Internal — screenshot 5