51 changed files with 3530 additions and 0 deletions
@ -0,0 +1,54 @@ |
|||||||
|
--- |
||||||
|
description: Standar dan aturan API geonet-console |
||||||
|
globs: ["geonet-console/app/Http/**", "geonet-console/routes/**", "geonet-console/docapi/**"] |
||||||
|
alwaysApply: false |
||||||
|
--- |
||||||
|
|
||||||
|
# API Rules — geonet-console |
||||||
|
|
||||||
|
## Base URL |
||||||
|
|
||||||
|
- Production: `https://console.gisportal.id/api/v1` |
||||||
|
- Internal: `http://127.0.0.1:8091/api/v1` |
||||||
|
|
||||||
|
## Auth |
||||||
|
|
||||||
|
- Semua endpoint butuh `Authorization: Bearer <token>` kecuali `/auth/login`, `/auth/refresh`, `/auth/token-guide` |
||||||
|
- Token: JWT (dari login LDAP), API Token (personal), atau Service Token (antar-service) |
||||||
|
- Gunakan `ApiActor` dari `ApiAuthService::resolve()` — **jangan** `Auth::user()` |
||||||
|
|
||||||
|
## Format Response |
||||||
|
|
||||||
|
**Success:** |
||||||
|
```json |
||||||
|
{"data": {...}, "message": "success"} |
||||||
|
``` |
||||||
|
|
||||||
|
**Error:** |
||||||
|
```json |
||||||
|
{"message": "Error message", "errors": {"field": ["msg"]}} |
||||||
|
``` |
||||||
|
|
||||||
|
## Menambah Endpoint Baru |
||||||
|
|
||||||
|
1. Controller di `app/Http/Controllers/Api/V1/` |
||||||
|
2. Service di `app/Services/` |
||||||
|
3. Route di `routes/api.php` dengan middleware auth + rate limit |
||||||
|
4. **Update `docapi/index.md`** — wajib |
||||||
|
5. Buat `docapi/services/<service>/<endpoint>.md` |
||||||
|
6. Tambah audit log untuk aksi write via `AuditLogService` |
||||||
|
|
||||||
|
## Scope Convention |
||||||
|
|
||||||
|
| Scope | Akses | |
||||||
|
|-------|-------| |
||||||
|
| `ldap:read` | Baca data LDAP | |
||||||
|
| `ldap:write` | Ubah data LDAP | |
||||||
|
| `databases:read` | Baca database registry | |
||||||
|
| `databases:write` | Ubah status database | |
||||||
|
| `project-search:read` | Query project search | |
||||||
|
|
||||||
|
## Rate Limit |
||||||
|
|
||||||
|
- Login: 5 req/menit |
||||||
|
- General: 60 req/menit |
||||||
@ -0,0 +1,44 @@ |
|||||||
|
--- |
||||||
|
description: Prinsip arsitektur dan pattern yang wajib diikuti |
||||||
|
globs: ["geonet-console/app/**"] |
||||||
|
alwaysApply: false |
||||||
|
--- |
||||||
|
|
||||||
|
# Architecture Rules — geonet-console |
||||||
|
|
||||||
|
## Layer Structure |
||||||
|
|
||||||
|
``` |
||||||
|
Routes (routes/api.php) |
||||||
|
→ Middleware (auth, rate limit, scope) |
||||||
|
→ Controller (Http/Controllers/Api/V1/) — thin, validasi only |
||||||
|
→ Service (Services/) — business logic |
||||||
|
→ Model (Models/) — Eloquent |
||||||
|
→ Database |
||||||
|
``` |
||||||
|
|
||||||
|
## Prinsip Wajib |
||||||
|
|
||||||
|
- **Service layer** untuk semua logic — jangan taruh logic di controller atau model |
||||||
|
- **ApiActor** sebagai identity caller — jangan `Auth::user()` |
||||||
|
- **Audit log** untuk semua aksi write — via `AuditLogService` |
||||||
|
- **Jangan duplikasi Zabbix** — monitoring real-time tetap di Zabbix |
||||||
|
- **PostgreSQL untuk app baru** — SQL Server hanya legacy/GIS |
||||||
|
|
||||||
|
## Sistem Boundaries |
||||||
|
|
||||||
|
| Komponen | Tanggung Jawab | |
||||||
|
|----------|----------------| |
||||||
|
| geonet-console | IT portal: auth, LDAP mgmt, DB registry, API | |
||||||
|
| Zabbix | Monitoring server real-time — jangan duplikasi | |
||||||
|
| QNAP OSS | File storage (avatar) — via S3 API | |
||||||
|
| Active Directory | Identity source — auth via LDAP | |
||||||
|
| GeoNetAgent | Asset management endpoint (repo terpisah) | |
||||||
|
|
||||||
|
## Perubahan Arsitektur |
||||||
|
|
||||||
|
Jika ada perubahan arsitektur signifikan: |
||||||
|
1. Buat ADR di `docs/adr/ADR-NNNN-judul.md` |
||||||
|
2. Update diagram di `docs/diagrams/` |
||||||
|
3. Update `docs/architecture/` yang relevan |
||||||
|
4. Jangan delete ADR lama — buat baru yang reference ADR lama |
||||||
@ -0,0 +1,45 @@ |
|||||||
|
--- |
||||||
|
description: Aturan database — koneksi, migration, naming |
||||||
|
globs: ["geonet-console/database/**", "geonet-console/app/Models/**", "geonet-console/config/database.php"] |
||||||
|
alwaysApply: false |
||||||
|
--- |
||||||
|
|
||||||
|
# Database Rules — geonet-console |
||||||
|
|
||||||
|
## Koneksi |
||||||
|
|
||||||
|
| Name | DB | Host | |
||||||
|
|------|----|------| |
||||||
|
| `pgsql` (default) | `databaselist_app` | `audit-postgres` container | |
||||||
|
| `audit` | `databaselist_audit` | `audit-postgres` container | |
||||||
|
| `mssql` | `master` | 10.100.1.25:1433 (read-only) | |
||||||
|
| `project_search` | `geonet_project_search` | 10.100.1.25 (Fase 4) | |
||||||
|
|
||||||
|
## Larangan |
||||||
|
|
||||||
|
- ❌ JANGAN ubah `DB_CONNECTION` ke `sqlite` |
||||||
|
- ❌ JANGAN jalankan `migrate:fresh` di production |
||||||
|
- ❌ JANGAN tulis ke `mssql` — read-only |
||||||
|
|
||||||
|
## Migration Convention |
||||||
|
|
||||||
|
- File: `YYYY_MM_DD_HHMMSS_verb_noun_table.php` |
||||||
|
- Nama tabel: `snake_case` plural |
||||||
|
- Selalu buat rollback (`down()`) yang benar |
||||||
|
- Gunakan `connection('audit')` untuk migration audit DB |
||||||
|
|
||||||
|
## Naming |
||||||
|
|
||||||
|
| Tipe | Konvensi | |
||||||
|
|------|----------| |
||||||
|
| Tabel | `snake_case` plural | |
||||||
|
| Kolom | `snake_case` | |
||||||
|
| FK | `<table_singular>_id` | |
||||||
|
| Timestamp | `*_at` suffix (`created_at`, `last_used_at`) | |
||||||
|
|
||||||
|
## Audit Log Wajib |
||||||
|
|
||||||
|
Setiap aksi write wajib log via: |
||||||
|
```php |
||||||
|
AuditLogService::log($actor, $action, $target, $payload); |
||||||
|
``` |
||||||
@ -0,0 +1,44 @@ |
|||||||
|
--- |
||||||
|
description: Aturan dan prosedur deployment |
||||||
|
globs: ["scripts/**", "*.yaml", "Dockerfile", ".env.example"] |
||||||
|
alwaysApply: false |
||||||
|
--- |
||||||
|
|
||||||
|
# Deployment Rules |
||||||
|
|
||||||
|
## Deploy geonet-console |
||||||
|
|
||||||
|
```powershell |
||||||
|
.\scripts\deploy-geonet-console.ps1 |
||||||
|
``` |
||||||
|
|
||||||
|
Atau manual: |
||||||
|
1. `tar -czf` — exclude vendor, .git, node_modules |
||||||
|
2. Upload via `Start-Process cmd.exe` + SSH cat |
||||||
|
3. Extract di `/opt/stacks/geonet-console/` |
||||||
|
4. `docker compose up -d --build --force-recreate` |
||||||
|
|
||||||
|
## Larangan Deploy |
||||||
|
|
||||||
|
- ❌ JANGAN overwrite `.env` di server |
||||||
|
- ❌ JANGAN jalankan `migrate:fresh` di production |
||||||
|
- ❌ JANGAN gunakan `&&` di PowerShell — gunakan `;` |
||||||
|
|
||||||
|
## Setelah Deploy |
||||||
|
|
||||||
|
- Cek: `docker ps --filter name=geonet-console` |
||||||
|
- Cek log: `docker logs geonet-console --tail 30` |
||||||
|
- Test URL: `https://console.gisportal.id` |
||||||
|
|
||||||
|
## Stack Paths di Server 10.100.1.24 |
||||||
|
|
||||||
|
| App | Path | |
||||||
|
|-----|------| |
||||||
|
| geonet-console | `/opt/stacks/geonet-console/` | |
||||||
|
| databaselist | `/opt/stacks/databaselist/` | |
||||||
|
| sqlservercheck | `/opt/stacks/sqlservercheck/` | |
||||||
|
| my-portal | `/opt/stacks/my-portal/` | |
||||||
|
|
||||||
|
## SSH |
||||||
|
|
||||||
|
Semua SSH ke 10.100.1.24 wajib minta persetujuan user terlebih dahulu. |
||||||
@ -0,0 +1,32 @@ |
|||||||
|
--- |
||||||
|
description: Kapan dan bagaimana harus update dokumentasi |
||||||
|
globs: ["**/*.md", "**/*.php", "**/*.ps1"] |
||||||
|
alwaysApply: true |
||||||
|
--- |
||||||
|
|
||||||
|
# Documentation Rules |
||||||
|
|
||||||
|
## Wajib Update Setelah: |
||||||
|
|
||||||
|
- **Endpoint API baru/berubah** → update `geonet-console/docapi/index.md` + buat `docapi/services/<service>/<endpoint>.md` |
||||||
|
- **Perubahan arsitektur** → update `docs/architecture/` + buat ADR baru di `docs/adr/` |
||||||
|
- **Env var baru** → update `geonet-console/.env.example` + `docs/environment.md` |
||||||
|
- **Bug fix penting** → tambah ke `docs/issues.md` (section Fixed) |
||||||
|
- **Deploy procedure berubah** → update `docs/deployment.md` |
||||||
|
- **Server/infra berubah** → update `docs/server.md` + `.cursor/me.md` |
||||||
|
- **Akhir sesi pengembangan** → update `perintah.md` (Sync terakhir) |
||||||
|
|
||||||
|
## ADR Rules |
||||||
|
|
||||||
|
- ADR = Architecture Decision Record — tidak pernah dihapus |
||||||
|
- Jika keputusan berubah → buat ADR baru yang references ADR lama |
||||||
|
- Simpan di `docs/adr/ADR-NNNN-judul.md` |
||||||
|
- Status: Proposed / Accepted / Superseded / Deprecated |
||||||
|
|
||||||
|
## Definition of Done |
||||||
|
|
||||||
|
Implementasi dianggap selesai jika: |
||||||
|
- [ ] Code bekerja dan test lulus |
||||||
|
- [ ] `docapi/` diperbarui (untuk perubahan API) |
||||||
|
- [ ] `perintah.md` diperbarui (Sync terakhir) |
||||||
|
- [ ] Tidak ada secret/credential di code atau dokumentasi |
||||||
@ -0,0 +1,37 @@ |
|||||||
|
--- |
||||||
|
description: Aturan keamanan wajib — credential, SSH, secret management |
||||||
|
globs: ["**/*"] |
||||||
|
alwaysApply: true |
||||||
|
--- |
||||||
|
|
||||||
|
# Security Rules |
||||||
|
|
||||||
|
## Larangan Mutlak |
||||||
|
|
||||||
|
- ❌ JANGAN commit file `.env` ke repository |
||||||
|
- ❌ JANGAN commit password, API key, token, atau private key |
||||||
|
- ❌ JANGAN tampilkan nilai secret di output, log, atau response API |
||||||
|
- ❌ JANGAN hardcode credential di code PHP atau PowerShell |
||||||
|
- ❌ JANGAN overwrite `.env` di server saat deploy — `.env` berisi credentials production |
||||||
|
|
||||||
|
## Lokasi Secret yang Benar |
||||||
|
|
||||||
|
| Secret | Lokasi | |
||||||
|
|--------|--------| |
||||||
|
| App credentials | `/opt/stacks/geonet-console/.env` di server 10.100.1.24 | |
||||||
|
| LDAP bind password | `.env` server (tidak di repo) | |
||||||
|
| QNAP OSS secret key | `.env` server (tidak di repo) | |
||||||
|
| SSH private key | `~/.ssh/` di laptop developer | |
||||||
|
| MikroTik RSA key | `~/.ssh/id_rsa_mikrotik` | |
||||||
|
|
||||||
|
## SSH Rules |
||||||
|
|
||||||
|
- SSH commands ke server **wajib minta persetujuan user** sebelum dijalankan |
||||||
|
- Jangan auto-run SSH command yang bersifat destructive |
||||||
|
- SSH key: `id_ed25519` untuk server Linux, `id_rsa_mikrotik` untuk MikroTik |
||||||
|
|
||||||
|
## Untuk AI Agent |
||||||
|
|
||||||
|
- Jika diminta buat credential baru → catat lokasi penyimpanan di `docs/environment.md` (bukan nilainya) |
||||||
|
- Jika ditemukan token di code → laporkan ke user, jangan commit |
||||||
|
- Jika perlu generate APP_KEY → instruksikan user run `php artisan key:generate` di server |
||||||
@ -0,0 +1,39 @@ |
|||||||
|
# ADR-0001 — Struktur Monorepo server-connection |
||||||
|
|
||||||
|
**Tanggal:** 2025-xx-xx |
||||||
|
**Status:** Accepted |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Konteks |
||||||
|
|
||||||
|
Perlu mengelola beberapa aplikasi IT internal (geonet-console, databaselist, sqlservercheck), skrip deploy, dan skrip infrastruktur (MikroTik, SSH) dalam satu repositori. |
||||||
|
|
||||||
|
## Keputusan |
||||||
|
|
||||||
|
Menggunakan **monorepo** dengan struktur folder per-aplikasi: |
||||||
|
|
||||||
|
``` |
||||||
|
server-connection/ |
||||||
|
├── geonet-console/ |
||||||
|
├── databaselist/ |
||||||
|
├── sqlservercheck/ |
||||||
|
├── scripts/ |
||||||
|
└── server/ |
||||||
|
``` |
||||||
|
|
||||||
|
## Alasan |
||||||
|
|
||||||
|
- Semua aplikasi dikelola oleh satu tim IT kecil |
||||||
|
- Deploy scripts saling terkait (SSH ke server yang sama) |
||||||
|
- Mudah cross-reference antara app code dan infra scripts |
||||||
|
- Tidak perlu manage banyak repo untuk skala tim kecil |
||||||
|
|
||||||
|
## Alternatif Ditolak |
||||||
|
|
||||||
|
- **Polyrepo (repo terpisah per app):** Terlalu banyak overhead untuk tim kecil, sync deploy scripts jadi sulit |
||||||
|
|
||||||
|
## Konsekuensi |
||||||
|
|
||||||
|
- Semua perubahan ada di satu commit history |
||||||
|
- Harus hati-hati agar `.env` tidak masuk ke satu folder yang salah saat deploy |
||||||
@ -0,0 +1,34 @@ |
|||||||
|
# ADR-0002 — PostgreSQL untuk Aplikasi Baru |
||||||
|
|
||||||
|
**Tanggal:** 2025-xx-xx |
||||||
|
**Status:** Accepted |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Konteks |
||||||
|
|
||||||
|
Perlu memilih database engine untuk aplikasi internal baru (geonet-console). Sudah ada SQL Server 2019 di 10.100.1.25 untuk legacy/GIS. |
||||||
|
|
||||||
|
## Keputusan |
||||||
|
|
||||||
|
**PostgreSQL** sebagai database default untuk aplikasi baru. SQL Server hanya untuk legacy atau GIS yang sudah terikat. |
||||||
|
|
||||||
|
## Alasan |
||||||
|
|
||||||
|
- Open source, tidak ada biaya lisensi |
||||||
|
- Support pgvector (dibutuhkan Fase 4 Ollama AI search) |
||||||
|
- Laravel Eloquent support penuh untuk PostgreSQL |
||||||
|
- Docker container mudah — `postgres:15` image resmi |
||||||
|
- JSON/JSONB support untuk metadata fleksibel |
||||||
|
|
||||||
|
## Alternatif Ditolak |
||||||
|
|
||||||
|
- **SQL Server:** Berlisensi mahal, tidak support pgvector, sudah dipakai untuk GIS legacy |
||||||
|
- **MySQL:** Tidak support pgvector, kurang fitur JSON vs PostgreSQL |
||||||
|
- **SQLite:** Tidak cocok untuk multi-container atau concurrency production |
||||||
|
|
||||||
|
## Konsekuensi |
||||||
|
|
||||||
|
- `DB_CONNECTION=pgsql` wajib di `compose.yaml` — jangan ubah ke sqlite |
||||||
|
- Legacy SQL Server tetap dipertahankan untuk database registry read-only |
||||||
|
- Nama DB saat ini masih `databaselist_app` (nama legacy, rencana rename di Fase 3) |
||||||
@ -0,0 +1,34 @@ |
|||||||
|
# ADR-0003 — Auth via LDAP Active Directory |
||||||
|
|
||||||
|
**Tanggal:** 2025-xx-xx |
||||||
|
**Status:** Accepted |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Konteks |
||||||
|
|
||||||
|
Perlu menentukan mekanisme autentikasi untuk geonet-console. Sudah ada Active Directory di 10.100.1.40 (domain `gisportal.id`) yang mengelola semua user dan laptop IT. |
||||||
|
|
||||||
|
## Keputusan |
||||||
|
|
||||||
|
Autentikasi melalui **LDAP bind ke Active Directory**. Tidak ada user lokal di geonet-console. Setelah berhasil bind, server mengeluarkan **JWT** untuk sesi selanjutnya. |
||||||
|
|
||||||
|
## Alasan |
||||||
|
|
||||||
|
- Semua user IT sudah ada di AD — tidak perlu manage user terpisah |
||||||
|
- Single source of truth untuk identity |
||||||
|
- Group AD (`Domain Admins`, `IT-Admin`, dll.) dipakai untuk RBAC |
||||||
|
- Tidak perlu forgot password / registration flow (AD yang kelola) |
||||||
|
|
||||||
|
## Alternatif Ditolak |
||||||
|
|
||||||
|
- **Local user DB:** Perlu manage password terpisah, duplikasi identity |
||||||
|
- **OAuth2 external (Google, dll.):** Tidak cocok untuk internal IT tool |
||||||
|
- **Kerberos SSO:** Kompleksitas tinggi untuk tim kecil |
||||||
|
|
||||||
|
## Konsekuensi |
||||||
|
|
||||||
|
- `LDAP_BIND_PASSWORD` wajib diisi di `.env` server |
||||||
|
- Jika AD 10.100.1.40 down → login tidak bisa (unavoidable tradeoff) |
||||||
|
- JWT refresh tersedia agar user tidak perlu login ulang terlalu sering |
||||||
|
- Admin group: `Domain Admins`, `Administrators`, `Enterprise Admins` |
||||||
@ -0,0 +1,36 @@ |
|||||||
|
# ADR-0004 — QNAP OSS untuk File Storage |
||||||
|
|
||||||
|
**Tanggal:** 2026-xx-xx |
||||||
|
**Status:** Accepted |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Konteks |
||||||
|
|
||||||
|
geonet-console perlu storage untuk avatar user. Awalnya disimpan di filesystem lokal Docker container, tapi container rebuild akan menghapus semua file. |
||||||
|
|
||||||
|
## Keputusan |
||||||
|
|
||||||
|
Menggunakan **QNAP QuObjects (OSS)** — S3-compatible object storage di 10.100.1.10:8010. Laravel menggunakan disk `oss` dengan driver `s3` + `use_path_style_endpoint=true`. |
||||||
|
|
||||||
|
## Alasan |
||||||
|
|
||||||
|
- QNAP TS-932X sudah ada di infrastruktur (tidak perlu beli) |
||||||
|
- S3-compatible → bisa pakai Laravel `s3` driver standar |
||||||
|
- File persists meski container rebuild atau crash |
||||||
|
- URL publik via Nginx proxy (`file.gisportal.id/super-apps/`) tanpa expose QNAP langsung |
||||||
|
|
||||||
|
## Alternatif Ditolak |
||||||
|
|
||||||
|
- **Docker volume lokal:** File hilang saat image recreate, tidak scalable |
||||||
|
- **NFS mount:** Lebih kompleks, risiko hanging mount |
||||||
|
- **Cloud S3 (AWS, Cloudflare R2):** Biaya, data harus internal |
||||||
|
- **Minio:** Perlu server/container tambahan, QNAP sudah ada |
||||||
|
|
||||||
|
## Konsekuensi |
||||||
|
|
||||||
|
- Bucket `super-apps` wajib di-set `public-read` (`X-Container-Read: .r:*,.rlistings`) |
||||||
|
- ACL bisa ter-reset → monitoring manual atau script cek berkala |
||||||
|
- Format access key: `<storage-space>:<key-id>` (format QNAP) |
||||||
|
- Driver `s3` wajib pakai `use_path_style_endpoint=true` (QNAP tidak support virtual hosted-style) |
||||||
|
- `OSS_SECRET_ACCESS_KEY` hanya ada di `.env` server — tidak di repo |
||||||
@ -0,0 +1,14 @@ |
|||||||
|
# Architecture Decision Records (ADR) |
||||||
|
|
||||||
|
ADR mendokumentasikan keputusan arsitektur penting, alasan di baliknya, dan alternatif yang dipertimbangkan. |
||||||
|
|
||||||
|
**Aturan:** ADR tidak boleh dihapus. Jika keputusan berubah, buat ADR baru. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
| ADR | Judul | Status | |
||||||
|
|-----|-------|--------| |
||||||
|
| [ADR-0001](ADR-0001-monorepo-structure.md) | Struktur Monorepo server-connection | Accepted | |
||||||
|
| [ADR-0002](ADR-0002-database-postgresql.md) | PostgreSQL untuk Aplikasi Baru | Accepted | |
||||||
|
| [ADR-0003](ADR-0003-ldap-authentication.md) | Auth via LDAP Active Directory | Accepted | |
||||||
|
| [ADR-0004](ADR-0004-qnap-object-storage.md) | QNAP OSS untuk File Storage | Accepted | |
||||||
@ -0,0 +1,72 @@ |
|||||||
|
# API — Error Codes |
||||||
|
|
||||||
|
> **Terakhir Diperbarui:** 2026-06-29 |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## HTTP Status Codes |
||||||
|
|
||||||
|
| Code | Keterangan | Penyebab Umum | |
||||||
|
|------|------------|---------------| |
||||||
|
| 200 | OK | Request berhasil | |
||||||
|
| 201 | Created | Resource baru dibuat | |
||||||
|
| 204 | No Content | Berhasil, tanpa body (misal DELETE) | |
||||||
|
| 400 | Bad Request | Request tidak valid | |
||||||
|
| 401 | Unauthorized | Token tidak ada, expired, atau salah | |
||||||
|
| 403 | Forbidden | Token valid tapi tidak punya scope | |
||||||
|
| 404 | Not Found | Resource tidak ditemukan | |
||||||
|
| 422 | Unprocessable Entity | Validasi gagal | |
||||||
|
| 429 | Too Many Requests | Rate limit terlampaui | |
||||||
|
| 500 | Internal Server Error | Error server | |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Format Error Response |
||||||
|
|
||||||
|
```json |
||||||
|
{ |
||||||
|
"message": "Deskripsi error", |
||||||
|
"errors": { |
||||||
|
"field_name": ["Validasi pesan 1", "Validasi pesan 2"] |
||||||
|
} |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
**Untuk 401/403 (tanpa errors field):** |
||||||
|
```json |
||||||
|
{ |
||||||
|
"message": "Unauthenticated." |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Contoh Error |
||||||
|
|
||||||
|
**422 Validation:** |
||||||
|
```json |
||||||
|
{ |
||||||
|
"message": "The given data was invalid.", |
||||||
|
"errors": { |
||||||
|
"username": ["The username field is required."], |
||||||
|
"password": ["The password field is required."] |
||||||
|
} |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
**401 Unauthorized:** |
||||||
|
```json |
||||||
|
{"message": "Unauthenticated."} |
||||||
|
``` |
||||||
|
|
||||||
|
**429 Rate Limit:** |
||||||
|
```json |
||||||
|
{"message": "Too Many Attempts."} |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Lihat Juga |
||||||
|
|
||||||
|
- `geonet-console/docapi/common-error.md` — error codes per service (lebih detail) |
||||||
|
- `geonet-console/docapi/common-response.md` — format response lengkap |
||||||
@ -0,0 +1,77 @@ |
|||||||
|
# API Overview — geonet-console |
||||||
|
|
||||||
|
> **Base URL:** `https://console.gisportal.id/api/v1` |
||||||
|
> **Internal:** `http://127.0.0.1:8091/api/v1` |
||||||
|
> **Terakhir Diperbarui:** 2026-06-29 |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Auth |
||||||
|
|
||||||
|
Semua endpoint (kecuali `/auth/login`, `/auth/refresh`, `/auth/token-guide`) memerlukan header: |
||||||
|
|
||||||
|
``` |
||||||
|
Authorization: Bearer <token> |
||||||
|
``` |
||||||
|
|
||||||
|
Token bisa berupa: |
||||||
|
- **JWT** — dari `POST /auth/login` |
||||||
|
- **API Token** — personal access token |
||||||
|
- **Service Token** — token antar-service |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Format Response Standard |
||||||
|
|
||||||
|
**Success:** |
||||||
|
```json |
||||||
|
{"data": {...}, "message": "success"} |
||||||
|
``` |
||||||
|
|
||||||
|
**Success list:** |
||||||
|
```json |
||||||
|
{"data": [...], "meta": {"total": 42, "page": 1}} |
||||||
|
``` |
||||||
|
|
||||||
|
**Error:** |
||||||
|
```json |
||||||
|
{"message": "Pesan error", "errors": {"field": ["Validasi"]}} |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Endpoints Ringkas |
||||||
|
|
||||||
|
| Service | Endpoint Utama | Scope | |
||||||
|
|---------|---------------|-------| |
||||||
|
| Auth | `POST /auth/login` | Public | |
||||||
|
| Me | `GET /me` | — | |
||||||
|
| LDAP Users | `GET /ldap/users` | `ldap:read` | |
||||||
|
| LDAP Groups | `GET /ldap/groups` | `ldap:read` | |
||||||
|
| Databases | `GET /databases` | `databases:read` | |
||||||
|
| Service Tokens | `GET /service-tokens` | — | |
||||||
|
| Project Search | `POST /project-search/ask` | `project-search:read` | |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Rate Limiting |
||||||
|
|
||||||
|
| Endpoint | Limit | |
||||||
|
|----------|-------| |
||||||
|
| `/auth/login` | 5 req/menit | |
||||||
|
| General | 60 req/menit | |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Docs Detail |
||||||
|
|
||||||
|
| File | Isi | |
||||||
|
|------|-----| |
||||||
|
| `service-auth.md` | Login, refresh, token guide, `/me` | |
||||||
|
| `service-ldap.md` | LDAP users & groups | |
||||||
|
| `service-database.md` | Database registry | |
||||||
|
| `service-tokens.md` | Service token CRUD | |
||||||
|
| `service-project-search.md` | AI project search | |
||||||
|
| `error-codes.md` | Error format & codes | |
||||||
|
|
||||||
|
> Dokumentasi lengkap juga di `geonet-console/docapi/` (lebih detail, per-endpoint). |
||||||
@ -0,0 +1,93 @@ |
|||||||
|
# API — Auth Service |
||||||
|
|
||||||
|
> **Base URL:** `https://console.gisportal.id/api/v1` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## POST /auth/login |
||||||
|
|
||||||
|
Login via LDAP Active Directory. Tidak butuh auth header. |
||||||
|
|
||||||
|
**Request:** |
||||||
|
```json |
||||||
|
{"username": "rbsetiawan", "password": "password123"} |
||||||
|
``` |
||||||
|
|
||||||
|
**Response 200:** |
||||||
|
```json |
||||||
|
{ |
||||||
|
"token": "eyJ0eXAiOiJKV1Qi...", |
||||||
|
"expires_at": "2026-07-06T09:00:00+07:00", |
||||||
|
"user": { |
||||||
|
"username": "rbsetiawan", |
||||||
|
"display_name": "R B Setiawan", |
||||||
|
"groups": ["Domain Admins", "IT-Admin"] |
||||||
|
} |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
**Response 401:** Credentials salah atau user tidak ada di AD. |
||||||
|
**Rate limit:** 5 req/menit. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## POST /auth/refresh |
||||||
|
|
||||||
|
Refresh JWT tanpa login ulang (token lama masih valid). |
||||||
|
|
||||||
|
**Header:** `Authorization: Bearer <old-jwt>` |
||||||
|
|
||||||
|
**Response 200:** |
||||||
|
```json |
||||||
|
{"token": "eyJ0eXAiOiJKV1Qi...", "expires_at": "2026-07-06T09:00:00+07:00"} |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## GET /auth/token-guide |
||||||
|
|
||||||
|
Panduan penggunaan token. Public, tidak butuh auth. |
||||||
|
|
||||||
|
**Response 200:** HTML atau JSON guide. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## GET /me |
||||||
|
|
||||||
|
Info user yang sedang login. |
||||||
|
|
||||||
|
**Header:** `Authorization: Bearer <token>` |
||||||
|
|
||||||
|
**Response 200:** |
||||||
|
```json |
||||||
|
{ |
||||||
|
"username": "rbsetiawan", |
||||||
|
"display_name": "R B Setiawan", |
||||||
|
"email": "rbsetiawan@gisportal.id", |
||||||
|
"department": "IT", |
||||||
|
"groups": ["Domain Admins", "IT-Admin"], |
||||||
|
"avatar_url": "https://file.gisportal.id/super-apps/avatars/rbsetiawan-AbCd.jpg" |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## PUT /me/avatar |
||||||
|
|
||||||
|
Upload avatar user (base64 encoded). |
||||||
|
|
||||||
|
**Header:** `Authorization: Bearer <jwt>` |
||||||
|
|
||||||
|
**Request:** |
||||||
|
```json |
||||||
|
{"data": "data:image/jpeg;base64,/9j/4AAQSkZJRg..."} |
||||||
|
``` |
||||||
|
|
||||||
|
- Max size: 300KB (base64) |
||||||
|
- Format: JPEG, PNG |
||||||
|
- Disimpan ke QNAP OSS bucket `super-apps` |
||||||
|
|
||||||
|
**Response 200:** |
||||||
|
```json |
||||||
|
{"avatar_url": "https://file.gisportal.id/super-apps/avatars/rbsetiawan-AbCd1234.jpg"} |
||||||
|
``` |
||||||
@ -0,0 +1,43 @@ |
|||||||
|
# API — Database Registry |
||||||
|
|
||||||
|
> **Base URL:** `https://console.gisportal.id/api/v1` |
||||||
|
> **Auth:** `Authorization: Bearer <token>` + scope `databases:read` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## GET /databases |
||||||
|
|
||||||
|
List semua database yang terdaftar di registry. |
||||||
|
|
||||||
|
**Response 200:** |
||||||
|
```json |
||||||
|
{ |
||||||
|
"data": [ |
||||||
|
{ |
||||||
|
"id": "uuid", |
||||||
|
"name": "GIS_Production", |
||||||
|
"type": "mssql", |
||||||
|
"host": "10.100.1.25", |
||||||
|
"port": 1433, |
||||||
|
"status": "online", |
||||||
|
"last_checked_at": "2026-06-29T09:00:00+07:00" |
||||||
|
} |
||||||
|
] |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## POST /databases/{database}/offline |
||||||
|
|
||||||
|
Tandai database sebagai offline (scope: `databases:write`). |
||||||
|
|
||||||
|
**Request:** |
||||||
|
```json |
||||||
|
{"reason": "Maintenance scheduled"} |
||||||
|
``` |
||||||
|
|
||||||
|
**Response 200:** |
||||||
|
```json |
||||||
|
{"status": "offline", "updated_at": "2026-06-29T09:00:00+07:00"} |
||||||
|
``` |
||||||
@ -0,0 +1,114 @@ |
|||||||
|
# API — LDAP Service |
||||||
|
|
||||||
|
> **Base URL:** `https://console.gisportal.id/api/v1` |
||||||
|
> **Auth:** `Authorization: Bearer <token>` + scope `ldap:read` / `ldap:write` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## LDAP Users |
||||||
|
|
||||||
|
### GET /ldap/users |
||||||
|
|
||||||
|
List semua user Active Directory. |
||||||
|
|
||||||
|
**Response 200:** |
||||||
|
```json |
||||||
|
{ |
||||||
|
"data": [ |
||||||
|
{ |
||||||
|
"username": "rbsetiawan", |
||||||
|
"display_name": "R B Setiawan", |
||||||
|
"email": "rbsetiawan@gisportal.id", |
||||||
|
"department": "IT", |
||||||
|
"groups": ["Domain Admins"], |
||||||
|
"enabled": true |
||||||
|
} |
||||||
|
] |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
### GET /ldap/users/{username} |
||||||
|
|
||||||
|
Detail user LDAP. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
### PUT /ldap/users/{username} |
||||||
|
|
||||||
|
Update user LDAP (scope: `ldap:write`). |
||||||
|
|
||||||
|
**Request:** |
||||||
|
```json |
||||||
|
{"display_name": "Nama Baru", "department": "IT Infra"} |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
### POST /ldap/users/{username}/groups |
||||||
|
|
||||||
|
Tambah user ke group AD (scope: `ldap:write`). |
||||||
|
|
||||||
|
**Request:** |
||||||
|
```json |
||||||
|
{"group": "IT-Admin"} |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
### DELETE /ldap/users/{username}/groups/{group} |
||||||
|
|
||||||
|
Hapus user dari group AD (scope: `ldap:write`). |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## LDAP Groups |
||||||
|
|
||||||
|
### GET /ldap/groups |
||||||
|
|
||||||
|
List semua group AD. |
||||||
|
|
||||||
|
**Response 200:** |
||||||
|
```json |
||||||
|
{ |
||||||
|
"data": [ |
||||||
|
{"name": "Domain Admins", "member_count": 3}, |
||||||
|
{"name": "IT-Admin", "member_count": 5} |
||||||
|
] |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
### GET /ldap/groups/{group} |
||||||
|
|
||||||
|
Detail group termasuk daftar member. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
### POST /ldap/groups |
||||||
|
|
||||||
|
Buat group baru di AD (scope: `ldap:write`, admin only). |
||||||
|
|
||||||
|
**Request:** |
||||||
|
```json |
||||||
|
{"name": "IT-GIS", "description": "GIS Team"} |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
### POST /ldap/groups/{group}/members |
||||||
|
|
||||||
|
Tambah member ke group. |
||||||
|
|
||||||
|
**Request:** |
||||||
|
```json |
||||||
|
{"username": "rbsetiawan"} |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
### DELETE /ldap/groups/{group}/members/{username} |
||||||
|
|
||||||
|
Hapus member dari group. |
||||||
@ -0,0 +1,73 @@ |
|||||||
|
# API — Project Search (AI) |
||||||
|
|
||||||
|
> **Base URL:** `https://console.gisportal.id/api/v1` |
||||||
|
> **Auth:** `Authorization: Bearer <token>` + scope `project-search:read` |
||||||
|
> **Status:** Fase 4 — endpoint terdaftar, implementasi belum aktif |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## GET /project-search/sources |
||||||
|
|
||||||
|
Daftar sumber data yang tersedia untuk pencarian. |
||||||
|
|
||||||
|
**Response 200:** |
||||||
|
```json |
||||||
|
{ |
||||||
|
"data": [ |
||||||
|
{"id": "project", "name": "Project Files", "path": "\\\\10.100.1.10\\project", "status": "not_indexed"}, |
||||||
|
{"id": "marketing", "name": "Marketing Files", "status": "not_indexed"} |
||||||
|
] |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## GET /project-search/index/status |
||||||
|
|
||||||
|
Status indeks per source. |
||||||
|
|
||||||
|
**Response 200:** |
||||||
|
```json |
||||||
|
{ |
||||||
|
"data": [ |
||||||
|
{"source": "project", "total_files": 0, "indexed_files": 0, "last_indexed_at": null} |
||||||
|
] |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## POST /project-search/ask |
||||||
|
|
||||||
|
Query natural language ke project files. |
||||||
|
|
||||||
|
**Request:** |
||||||
|
```json |
||||||
|
{ |
||||||
|
"query": "dokumen IGN untuk proyek Kendal", |
||||||
|
"sources": ["project"], |
||||||
|
"limit": 5 |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
**Response 200 (saat aktif):** |
||||||
|
```json |
||||||
|
{ |
||||||
|
"answer": "Dokumen IGN untuk proyek Kendal tersedia di...", |
||||||
|
"sources": [ |
||||||
|
{"file": "\\\\10.100.1.10\\project\\2025\\Kendal\\IGN.pdf", "score": 0.95} |
||||||
|
] |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Catatan |
||||||
|
|
||||||
|
Implementasi lengkap memerlukan: |
||||||
|
1. Indexer worker di VM 10.100.1.26 (crawl + embed) |
||||||
|
2. pgvector di `geonet_project_search` PostgreSQL |
||||||
|
3. Ollama model `nomic-embed-text` untuk embedding |
||||||
|
4. Ollama model `qwen2.5` untuk answer generation |
||||||
|
|
||||||
|
Lihat `server/ollama-nas-project-search-requirements.md` untuk requirement detail. |
||||||
@ -0,0 +1,98 @@ |
|||||||
|
# API — Service Tokens |
||||||
|
|
||||||
|
> **Base URL:** `https://console.gisportal.id/api/v1` |
||||||
|
> **Auth:** `Authorization: Bearer <token>` |
||||||
|
|
||||||
|
Service Token adalah token antar-service (bukan user token). Contoh: GeoNetAgent Collector menggunakan service token untuk POST laporan. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## GET /service-tokens |
||||||
|
|
||||||
|
List semua service tokens. |
||||||
|
|
||||||
|
**Response 200:** |
||||||
|
```json |
||||||
|
{ |
||||||
|
"data": [ |
||||||
|
{ |
||||||
|
"id": "uuid", |
||||||
|
"name": "GeoNetAgent Production", |
||||||
|
"service": "geonetagent", |
||||||
|
"abilities": ["agent:report"], |
||||||
|
"expires_at": "2027-06-29T00:00:00+07:00", |
||||||
|
"last_used_at": "2026-06-29T09:00:00+07:00", |
||||||
|
"revoked_at": null |
||||||
|
} |
||||||
|
] |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## POST /service-tokens |
||||||
|
|
||||||
|
Buat service token baru. |
||||||
|
|
||||||
|
**Request:** |
||||||
|
```json |
||||||
|
{ |
||||||
|
"name": "GeoNetAgent Production", |
||||||
|
"service": "geonetagent", |
||||||
|
"abilities": ["agent:report"], |
||||||
|
"expires_at": "2027-06-29" |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
**Response 201:** Token value hanya ditampilkan sekali saat create. |
||||||
|
```json |
||||||
|
{ |
||||||
|
"id": "uuid", |
||||||
|
"token": "plain-text-token-nilai-ini-simpan-sekarang", |
||||||
|
"name": "GeoNetAgent Production" |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## POST /service-tokens/{id}/revoke |
||||||
|
|
||||||
|
Revoke token (non-destructive, bisa di-unrevoce). |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## POST /service-tokens/{id}/regenerate |
||||||
|
|
||||||
|
Generate token baru, token lama invalid. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## DELETE /service-tokens/{id} |
||||||
|
|
||||||
|
Hapus token permanen. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## GET /service-tokens/active |
||||||
|
|
||||||
|
Semua token aktif (tidak expired, tidak di-revoke). |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## GET /service-tokens/expired |
||||||
|
|
||||||
|
Semua token yang sudah expired. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## GET /service-tokens/by-service/{service} |
||||||
|
|
||||||
|
Token berdasarkan nama service. |
||||||
|
|
||||||
|
**Contoh:** `GET /service-tokens/by-service/geonetagent` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Sync ke GeoNetAgent DB |
||||||
|
|
||||||
|
Token yang dibuat di geonet-console di-sync otomatis ke tabel `agent_tokens` di `geonetagent_dev` PostgreSQL via `AgentTokenSyncService`. GeoNetAgent Collector bisa validasi token dari DB-nya sendiri. |
||||||
@ -0,0 +1,89 @@ |
|||||||
|
# Architecture — Authentication Flow |
||||||
|
|
||||||
|
> **Terakhir Diperbarui:** 2026-06-29 |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Auth Methods |
||||||
|
|
||||||
|
| Method | Digunakan Untuk | Class | |
||||||
|
|--------|-----------------|-------| |
||||||
|
| **JWT (LDAP login)** | User via browser/app | `LdapJwtService` | |
||||||
|
| **API Token** | Developer / personal access | `ApiToken` model | |
||||||
|
| **Service Token** | Antar-service | `ServiceToken` model | |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Alur Login LDAP → JWT |
||||||
|
|
||||||
|
```mermaid |
||||||
|
sequenceDiagram |
||||||
|
participant CLIENT as Browser / App |
||||||
|
participant GC as geonet-console |
||||||
|
participant LDAP as Active Directory (10.100.1.40) |
||||||
|
participant JWT as LdapJwtService |
||||||
|
|
||||||
|
CLIENT->>GC: POST /api/v1/auth/login {username, password} |
||||||
|
GC->>LDAP: LDAP bind dengan credentials user |
||||||
|
LDAP-->>GC: bind success / fail |
||||||
|
alt bind gagal |
||||||
|
GC-->>CLIENT: 401 Unauthorized |
||||||
|
end |
||||||
|
GC->>GC: LdapAuthService::attempt() — validasi grup admin (jika diperlukan) |
||||||
|
GC->>JWT: generate JWT payload {username, groups, iat, exp} |
||||||
|
JWT-->>GC: signed JWT token |
||||||
|
GC-->>CLIENT: 200 {token, expires_at} |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Alur Request dengan Bearer Token |
||||||
|
|
||||||
|
```mermaid |
||||||
|
sequenceDiagram |
||||||
|
participant CLIENT as Client |
||||||
|
participant GC as geonet-console |
||||||
|
participant DB as PostgreSQL |
||||||
|
|
||||||
|
CLIENT->>GC: GET /api/v1/* \nAuthorization: Bearer <token> |
||||||
|
GC->>GC: ApiAuthService::resolve(token) |
||||||
|
alt JWT token |
||||||
|
GC->>GC: decode + validasi JWT |
||||||
|
GC->>GC: buat ApiActor{type: user, identifier, groups} |
||||||
|
else API Token |
||||||
|
GC->>DB: lookup api_tokens WHERE token_hash = ? |
||||||
|
GC->>GC: buat ApiActor{type: api_token, scopes} |
||||||
|
else Service Token |
||||||
|
GC->>DB: lookup service_tokens WHERE token = ? AND active |
||||||
|
GC->>GC: buat ApiActor{type: service_token, abilities} |
||||||
|
end |
||||||
|
GC->>GC: Controller pakai ApiActor |
||||||
|
GC-->>CLIENT: Response |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## LDAP Config |
||||||
|
|
||||||
|
| Parameter | Nilai | |
||||||
|
|-----------|-------| |
||||||
|
| Host | `10.100.1.40` | |
||||||
|
| Port | `389` | |
||||||
|
| Bind DN | `Administrator@gisportal.id` | |
||||||
|
| Base DN | `DC=gisportal,DC=id` | |
||||||
|
| Admin Groups | `Domain Admins`, `Administrators`, `Enterprise Admins` | |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Service Token Flow (Antar-Service) |
||||||
|
|
||||||
|
``` |
||||||
|
GeoNetAgent Collector |
||||||
|
→ POST /api/v1/agent/report |
||||||
|
→ Authorization: Bearer <service-token> |
||||||
|
→ geonet-console: lookup service_tokens |
||||||
|
→ ApiActor{type: service_token, service: "geonetagent"} |
||||||
|
→ proses request |
||||||
|
``` |
||||||
|
|
||||||
|
Service token di-sync ke `geonetagent_dev.agent_tokens` via `AgentTokenSyncService`. |
||||||
@ -0,0 +1,78 @@ |
|||||||
|
# Architecture — Database Flow |
||||||
|
|
||||||
|
> **Terakhir Diperbarui:** 2026-06-29 |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Database Connections |
||||||
|
|
||||||
|
``` |
||||||
|
geonet-console Laravel |
||||||
|
│ |
||||||
|
├── pgsql (default) → audit-postgres:5432/databaselist_app |
||||||
|
│ Metadata: ldap_user_meta, service_tokens, api_tokens, database_connections |
||||||
|
│ |
||||||
|
├── audit → audit-postgres:5432/databaselist_audit |
||||||
|
│ Audit log: audit_logs |
||||||
|
│ |
||||||
|
├── mssql → 10.100.1.25:1433/master |
||||||
|
│ Read-only: database registry (SQL Server) |
||||||
|
│ |
||||||
|
└── project_search → 10.100.1.25:5432/geonet_project_search |
||||||
|
Vector search index (Fase 4 — Ollama) |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Alur Write — API Request |
||||||
|
|
||||||
|
```mermaid |
||||||
|
sequenceDiagram |
||||||
|
participant API as API Request |
||||||
|
participant SVC as Service Layer |
||||||
|
participant PGSQL as PostgreSQL (app) |
||||||
|
participant AUDIT as PostgreSQL (audit) |
||||||
|
|
||||||
|
API->>SVC: Update LDAP user meta |
||||||
|
SVC->>PGSQL: UPDATE ldap_user_meta SET ... |
||||||
|
SVC->>AUDIT: INSERT audit_logs {actor, action, payload} |
||||||
|
PGSQL-->>SVC: OK |
||||||
|
AUDIT-->>SVC: OK |
||||||
|
SVC-->>API: 200 success |
||||||
|
``` |
||||||
|
|
||||||
|
**Aturan:** Semua aksi write wajib log ke audit DB via `AuditLogService`. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Alur Read — Database Registry |
||||||
|
|
||||||
|
``` |
||||||
|
GET /api/v1/databases |
||||||
|
→ DatabaseConnectionManager::listAll() |
||||||
|
→ mssql connection → SELECT name, state FROM sys.databases |
||||||
|
→ PostgreSQL (pgsql) → SELECT * FROM database_connections |
||||||
|
→ merge + return |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Vector Search (Fase 4 — Belum Aktif) |
||||||
|
|
||||||
|
``` |
||||||
|
POST /api/v1/project-search/ask {query} |
||||||
|
→ ProjectSearchService::ask() |
||||||
|
→ OllamaService::embed(query) → 10.100.1.26:11434 |
||||||
|
→ project_search DB → SELECT ... ORDER BY embedding <-> $vector |
||||||
|
→ OllamaService::generate(context + query) |
||||||
|
→ return {answer, sources} |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Catatan Penting |
||||||
|
|
||||||
|
- `DB_CONNECTION=pgsql` di `compose.yaml` override `.env` — jangan ubah |
||||||
|
- `project_search` koneksi ke 10.100.1.25 external — migration gagal jika unreachable (non-critical) |
||||||
|
- OAuth keys ada di Docker volume `app_storage` (bukan DB) |
||||||
|
- Audit DB (`databaselist_audit`) terpisah dari app DB untuk isolasi |
||||||
@ -0,0 +1,88 @@ |
|||||||
|
# Architecture — Deployment Architecture |
||||||
|
|
||||||
|
> **Terakhir Diperbarui:** 2026-06-29 |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Topology |
||||||
|
|
||||||
|
``` |
||||||
|
Internet / LAN |
||||||
|
│ HTTPS :443 |
||||||
|
▼ |
||||||
|
┌──────────────────────────────────────────────────┐ |
||||||
|
│ Nginx Reverse Proxy (10.100.1.24) │ |
||||||
|
│ /etc/nginx/conf.d/production/ │ |
||||||
|
│ - geonet-console.conf → console.gisportal.id │ |
||||||
|
│ - databaselist.conf → databaselist.gisportal.id│ |
||||||
|
│ /etc/nginx/conf.d/nas.conf │ |
||||||
|
│ - file.gisportal.id/super-apps/ → QNAP OSS │ |
||||||
|
└──────────────┬───────────────────────────────────┘ |
||||||
|
│ proxy_pass 127.0.0.1:8091 |
||||||
|
▼ |
||||||
|
┌──────────────────────────────┐ |
||||||
|
│ geonet-console Container │ |
||||||
|
│ PHP 8.3 + Laravel 12 │ |
||||||
|
│ PHP-FPM + Nginx (in-cont) │ |
||||||
|
│ supervisord │ |
||||||
|
│ Port: :8091 │ |
||||||
|
└──────────┬───────────────────┘ |
||||||
|
│ |
||||||
|
┌──────────┴──────────────────┐ |
||||||
|
│ audit-postgres Container │ |
||||||
|
│ PostgreSQL 15 │ |
||||||
|
│ - databaselist_app │ |
||||||
|
│ - databaselist_audit │ |
||||||
|
└─────────────────────────────┘ |
||||||
|
|
||||||
|
External: |
||||||
|
QNAP OSS (10.100.1.10:8010) — avatar storage |
||||||
|
LDAP AD (10.100.1.40) — authentication |
||||||
|
SQL Server (10.100.1.25) — DB registry read |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Docker Compose (geonet-console) |
||||||
|
|
||||||
|
```yaml |
||||||
|
services: |
||||||
|
geonet-console: |
||||||
|
build: . |
||||||
|
ports: |
||||||
|
- "127.0.0.1:8091:80" |
||||||
|
env_file: .env |
||||||
|
depends_on: [audit-postgres] |
||||||
|
restart: unless-stopped |
||||||
|
volumes: |
||||||
|
- app_storage:/var/www/html/storage |
||||||
|
|
||||||
|
audit-postgres: |
||||||
|
image: postgres:15 |
||||||
|
env_file: .env |
||||||
|
restart: unless-stopped |
||||||
|
volumes: |
||||||
|
- audit_pg_data:/var/lib/postgresql/data |
||||||
|
|
||||||
|
volumes: |
||||||
|
app_storage: |
||||||
|
audit_pg_data: |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Stack Paths di Server |
||||||
|
|
||||||
|
| Stack | Path | |
||||||
|
|-------|------| |
||||||
|
| geonet-console | `/opt/stacks/geonet-console/` | |
||||||
|
| databaselist | `/opt/stacks/databaselist/` | |
||||||
|
| sqlservercheck | `/opt/stacks/sqlservercheck/` | |
||||||
|
| my-portal | `/opt/stacks/my-portal/` | |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## SSL |
||||||
|
|
||||||
|
HTTPS via Let's Encrypt (certbot) di Nginx 10.100.1.24. Semua domain `*.gisportal.id` di-terminate di Nginx. |
||||||
|
Port 80 redirect → 443. |
||||||
@ -0,0 +1,106 @@ |
|||||||
|
# Architecture — Folder Structure |
||||||
|
|
||||||
|
> **Terakhir Diperbarui:** 2026-06-29 |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Struktur Monorepo |
||||||
|
|
||||||
|
``` |
||||||
|
server-connection/ |
||||||
|
│ |
||||||
|
├── README.md — Project overview + quick start |
||||||
|
├── AGENTS.md — Aturan AI agent |
||||||
|
├── perintah.md — Quick reference + protokol sync |
||||||
|
├── readme.txt — SSH & deploy commands ringkas |
||||||
|
├── rules — Requirement bisnis manual |
||||||
|
├── rules-adv — Requirement bisnis lanjutan |
||||||
|
│ |
||||||
|
├── geonet-console/ — Laravel 12 unified IT portal |
||||||
|
│ ├── context-index.md ← Entry point AI untuk proyek ini |
||||||
|
│ ├── DOCKGE.md ← Ringkasan deploy & env vars |
||||||
|
│ ├── .env.example ← Template env vars |
||||||
|
│ ├── compose.yaml ← Docker Compose |
||||||
|
│ ├── Dockerfile ← Multi-stage PHP 8.3 build |
||||||
|
│ ├── docker/entrypoint.sh ← Startup script |
||||||
|
│ ├── app/ |
||||||
|
│ │ ├── Http/Controllers/Api/V1/ |
||||||
|
│ │ ├── Models/ |
||||||
|
│ │ ├── Services/ |
||||||
|
│ │ └── Support/ |
||||||
|
│ ├── config/ |
||||||
|
│ ├── database/migrations/ |
||||||
|
│ ├── routes/api.php |
||||||
|
│ ├── docapi/ ← Dokumentasi API lengkap |
||||||
|
│ │ ├── README.md |
||||||
|
│ │ ├── index.md |
||||||
|
│ │ ├── authentication.md |
||||||
|
│ │ ├── common-response.md |
||||||
|
│ │ ├── common-error.md |
||||||
|
│ │ ├── integration-guide.md |
||||||
|
│ │ └── services/ |
||||||
|
│ └── resources/ |
||||||
|
│ |
||||||
|
├── databaselist/ — Legacy Laravel (deprecated) |
||||||
|
│ |
||||||
|
├── sqlservercheck/ — Tool cek SQL Server |
||||||
|
│ |
||||||
|
├── scripts/ — Deploy & SSH scripts |
||||||
|
│ ├── deploy-geonet-console.ps1 |
||||||
|
│ ├── deploy-databaselist.ps1 |
||||||
|
│ ├── deploy-sqlservercheck.ps1 |
||||||
|
│ ├── deploy-geonet-console-nginx.ps1 |
||||||
|
│ ├── ssh-remote.ps1 |
||||||
|
│ ├── ssh-mikrotik.ps1 |
||||||
|
│ ├── ssh-ollama.ps1 |
||||||
|
│ ├── ssh-qnap.ps1 |
||||||
|
│ ├── ssh-zabbix.ps1 |
||||||
|
│ ├── setup-ssh-mikrotik.ps1 |
||||||
|
│ ├── cutover-geonet-console.ps1 |
||||||
|
│ ├── geonet-indexer/ ← Indexer worker scripts (Fase 4) |
||||||
|
│ └── *.ps1 / *.sh |
||||||
|
│ |
||||||
|
├── server/ — Audit & MikroTik configs |
||||||
|
│ ├── host.md ← Daftar host & IP |
||||||
|
│ ├── ollama-nas-project-search-requirements.md |
||||||
|
│ ├── *-audit.txt ← Audit snapshot server |
||||||
|
│ ├── mikrotik-*.rsc ← MikroTik scripts |
||||||
|
│ └── nginx/ |
||||||
|
│ |
||||||
|
├── docs/ — Dokumentasi lengkap ⭐ |
||||||
|
│ ├── context-index.md ← Entry point AI |
||||||
|
│ ├── architecture/ |
||||||
|
│ ├── api/ |
||||||
|
│ ├── adr/ |
||||||
|
│ ├── diagrams/ |
||||||
|
│ ├── onboarding/ |
||||||
|
│ └── decisions/ |
||||||
|
│ |
||||||
|
├── .cursor/ — AI context |
||||||
|
│ ├── context-index.md |
||||||
|
│ ├── me.md ← Profil + infrastruktur |
||||||
|
│ ├── rules/ |
||||||
|
│ │ ├── 00-project-context.mdc |
||||||
|
│ │ ├── 01-enterprise-principles.mdc |
||||||
|
│ │ ├── 02-implementation-status.mdc |
||||||
|
│ │ ├── 03-ai-behavior.mdc |
||||||
|
│ │ ├── 04-geonet-uiux.mdc |
||||||
|
│ │ ├── 05-laravel-backend.mdc |
||||||
|
│ │ └── 06-infrastructure.mdc |
||||||
|
│ └── adr/ |
||||||
|
│ |
||||||
|
└── config-check.rsc — MikroTik config check script |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Konvensi Penamaan |
||||||
|
|
||||||
|
| Tipe | Konvensi | Contoh | |
||||||
|
|------|----------|--------| |
||||||
|
| PHP file | `PascalCase.php` | `LdapAuthService.php` | |
||||||
|
| DB tabel | `snake_case` plural | `ldap_user_meta` | |
||||||
|
| Route | `/kebab-case` | `/api/v1/ldap/users` | |
||||||
|
| Script PS1 | `verb-noun.ps1` | `deploy-geonet-console.ps1` | |
||||||
|
| Doc file | `kebab-case.md` | `system-overview.md` | |
||||||
|
| ADR | `ADR-NNNN-title.md` | `ADR-0001-monorepo-structure.md` | |
||||||
@ -0,0 +1,66 @@ |
|||||||
|
# Architecture — Request Flow |
||||||
|
|
||||||
|
> **Terakhir Diperbarui:** 2026-06-29 |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Alur Request Browser → API |
||||||
|
|
||||||
|
```mermaid |
||||||
|
sequenceDiagram |
||||||
|
participant B as Browser / Next.js |
||||||
|
participant NGX as Nginx (10.100.1.24) |
||||||
|
participant GC as geonet-console (:8091) |
||||||
|
participant DB as PostgreSQL (audit-postgres) |
||||||
|
participant LOG as Audit DB |
||||||
|
|
||||||
|
B->>NGX: HTTPS GET console.gisportal.id/api/v1/ldap/users |
||||||
|
NGX->>GC: proxy_pass 127.0.0.1:8091 |
||||||
|
GC->>GC: Middleware: auth (ApiAuthService::resolve) |
||||||
|
GC->>GC: Rate limit check |
||||||
|
GC->>GC: Controller → LdapUserService |
||||||
|
GC->>DB: Query jika diperlukan (ldap_user_meta) |
||||||
|
DB-->>GC: data |
||||||
|
GC->>LOG: AuditLogService::log() (untuk write actions) |
||||||
|
GC-->>NGX: JSON response |
||||||
|
NGX-->>B: JSON response |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Middleware Stack (geonet-console) |
||||||
|
|
||||||
|
| Middleware | Fungsi | |
||||||
|
|-----------|--------| |
||||||
|
| `throttle:60,1` (general) | Rate limit umum | |
||||||
|
| `throttle:5,1` (login) | Rate limit login | |
||||||
|
| `auth.api` | Resolve Bearer token → ApiActor | |
||||||
|
| `scope:<name>` | Cek scope/ability token | |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Route Groups |
||||||
|
|
||||||
|
``` |
||||||
|
/api/v1/auth/* — Public (tidak butuh auth) |
||||||
|
/api/v1/me — Auth: JWT atau API Token |
||||||
|
/api/v1/ldap/* — Auth + scope: ldap:read / ldap:write |
||||||
|
/api/v1/databases/* — Auth + scope: databases:read / databases:write |
||||||
|
/api/v1/service-tokens/*— Auth (JWT atau token dengan akses service) |
||||||
|
/api/v1/project-search/*— Auth + scope: project-search:read |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## File Upload Flow (Avatar) |
||||||
|
|
||||||
|
``` |
||||||
|
Client → PUT /api/v1/me/avatar {data: base64} |
||||||
|
→ geonet-console: decode base64 |
||||||
|
→ Storage::disk('oss')->put('avatars/<filename>', $content) |
||||||
|
→ QNAP OSS (http://10.100.1.10:8010) |
||||||
|
→ ldap_user_meta.avatar = 'avatars/<filename>' (path relatif) |
||||||
|
→ return {avatar_url: 'https://file.gisportal.id/super-apps/avatars/<filename>'} |
||||||
|
``` |
||||||
|
|
||||||
|
Avatar URL: via Nginx proxy `/super-apps/` → QNAP OSS → bucket `super-apps` (public-read) |
||||||
@ -0,0 +1,57 @@ |
|||||||
|
# Architecture — Storage |
||||||
|
|
||||||
|
> **Terakhir Diperbarui:** 2026-06-29 |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## QNAP OSS — Object Storage (Avatar) |
||||||
|
|
||||||
|
| Item | Nilai | |
||||||
|
|------|-------| |
||||||
|
| Provider | QNAP QuObjects (S3-compatible) | |
||||||
|
| Host | `10.100.1.10:8010` | |
||||||
|
| Bucket | `super-apps` | |
||||||
|
| ACL | `public-read` | |
||||||
|
| URL Publik | `https://file.gisportal.id/super-apps/` | |
||||||
|
| Laravel Disk | `oss` (`config/filesystems.php`) | |
||||||
|
| Driver | `s3` dengan `use_path_style_endpoint=true` | |
||||||
|
|
||||||
|
### Cara Kerja Avatar |
||||||
|
|
||||||
|
``` |
||||||
|
PUT /api/v1/me/avatar {data: base64} |
||||||
|
→ decode base64 → Storage::disk('oss')->put('avatars/<filename>', $content) |
||||||
|
→ simpan path relatif 'avatars/<filename>' di ldap_user_meta.avatar |
||||||
|
→ return {avatar_url: 'https://file.gisportal.id/super-apps/avatars/<filename>'} |
||||||
|
``` |
||||||
|
|
||||||
|
### Nginx Proxy OSS |
||||||
|
|
||||||
|
`/etc/nginx/conf.d/nas.conf`: |
||||||
|
```nginx |
||||||
|
location /super-apps/ { |
||||||
|
proxy_pass http://10.100.1.10:8010/v1/AUTH_super-apps/super-apps/; |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Docker Volume — App Storage |
||||||
|
|
||||||
|
| Volume | Mount | Isi | |
||||||
|
|--------|-------|-----| |
||||||
|
| `app_storage` | `/var/www/html/storage` | Session, logs, OAuth keys | |
||||||
|
| `audit_pg_data` | `/var/lib/postgresql/data` | PostgreSQL data | |
||||||
|
|
||||||
|
> OAuth keys (`oauth-private.key`, `oauth-public.key`) di-generate otomatis saat container pertama kali start, disimpan di `app_storage`. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## NAS QNAP SMB (Fase 4 — Project Search) |
||||||
|
|
||||||
|
| Share | Path | Dipakai Untuk | |
||||||
|
|-------|------|---------------| |
||||||
|
| `\\10.100.1.10\project` | `/mnt/qnap/project` | Indexer crawl project files | |
||||||
|
| `\\10.100.1.10\Marketing` | `/mnt/qnap/marketing` | Indexer (future) | |
||||||
|
|
||||||
|
Indexer di Ollama VM (10.100.1.26) mount SMB → crawl → embed → simpan ke pgvector. |
||||||
@ -0,0 +1,77 @@ |
|||||||
|
# Architecture — System Overview |
||||||
|
|
||||||
|
> **Terakhir Diperbarui:** 2026-06-29 |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Diagram Sistem |
||||||
|
|
||||||
|
```mermaid |
||||||
|
graph TB |
||||||
|
subgraph CLIENTS["Clients"] |
||||||
|
BROWSER["Browser\n(IT Admin, User)"] |
||||||
|
NEXTJS["Next.js PWA\nmy.gisportal.id"] |
||||||
|
AGENT["PowerShell Agent\n(Endpoint Windows)"] |
||||||
|
end |
||||||
|
|
||||||
|
subgraph SERVER24["10.100.1.24 — App Server (CentOS 8)"] |
||||||
|
NGX["Nginx\nSSL termination\nReverse Proxy"] |
||||||
|
GC["geonet-console\nLaravel 12 + PHP 8.3\n:8091"] |
||||||
|
PGCON["audit-postgres\nPostgreSQL\n(container)"] |
||||||
|
end |
||||||
|
|
||||||
|
subgraph SERVER25["10.100.1.25 — Database Server"] |
||||||
|
MSSQL["SQL Server 2019\n:1433"] |
||||||
|
PGSEARCH["PostgreSQL\ngeonet_project_search\n(pgvector - Fase 4)"] |
||||||
|
end |
||||||
|
|
||||||
|
subgraph INFRA["Infrastruktur Existing"] |
||||||
|
AD["AD / LDAP\n10.100.1.40"] |
||||||
|
QNAP["QNAP NAS\n10.100.1.10\nOSS + SMB"] |
||||||
|
OLLAMA["Ollama VM\n10.100.1.26\n(Fase 4)"] |
||||||
|
ZABBIX["Zabbix\n10.100.1.42"] |
||||||
|
MIKROTIK["MikroTik CCR2004\n10.100.1.1"] |
||||||
|
end |
||||||
|
|
||||||
|
BROWSER -->|HTTPS| NGX |
||||||
|
NEXTJS -->|HTTPS API| NGX |
||||||
|
AGENT -->|HTTPS POST| NGX |
||||||
|
NGX -->|:8091| GC |
||||||
|
GC --> PGCON |
||||||
|
GC -->|LDAP bind| AD |
||||||
|
GC -->|read registry| MSSQL |
||||||
|
GC -->|OSS S3 avatar| QNAP |
||||||
|
GC -.->|Fase 4| PGSEARCH |
||||||
|
GC -.->|Fase 4| OLLAMA |
||||||
|
NGX -->|/super-apps/| QNAP |
||||||
|
|
||||||
|
style GC fill:#2196F3,color:#fff |
||||||
|
style PGCON fill:#FF9800,color:#fff |
||||||
|
style NGX fill:#607D8B,color:#fff |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Komponen |
||||||
|
|
||||||
|
| Komponen | Role | Host | |
||||||
|
|----------|------|------| |
||||||
|
| **Nginx** | SSL termination + reverse proxy | 10.100.1.24 | |
||||||
|
| **geonet-console** | IT portal — auth, LDAP mgmt, DB registry, API | 10.100.1.24:8091 | |
||||||
|
| **audit-postgres** | PostgreSQL app + audit DB | container di 10.100.1.24 | |
||||||
|
| **Active Directory** | Auth source (LDAP bind) | 10.100.1.40 | |
||||||
|
| **SQL Server** | Database registry (read-only) | 10.100.1.25:1433 | |
||||||
|
| **QNAP OSS** | Avatar + file storage (S3-compatible) | 10.100.1.10:8010 | |
||||||
|
| **Zabbix** | Server/VM monitoring real-time | 10.100.1.42 | |
||||||
|
| **MikroTik** | Gateway, firewall, routing | 10.100.1.1 | |
||||||
|
| **Ollama VM** | AI inference (Fase 4) | 10.100.1.26:11434 | |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Prinsip Arsitektur |
||||||
|
|
||||||
|
- **Geonet-console** = unified IT portal, bukan tool tunggal |
||||||
|
- **Jangan duplikasi Zabbix** — monitoring real-time tetap di Zabbix |
||||||
|
- **LDAP sebagai auth source** — tidak ada local user di geonet-console |
||||||
|
- **PostgreSQL untuk app baru** — SQL Server hanya untuk legacy/GIS |
||||||
|
- **QNAP OSS untuk file** — bukan filesystem lokal server |
||||||
@ -0,0 +1,86 @@ |
|||||||
|
# Changelog — server-connection |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## [Doc 1.0.0] — 2026-06-29 |
||||||
|
|
||||||
|
### Added (Dokumentasi) |
||||||
|
- `README.md` — root README lengkap |
||||||
|
- `docs/context-index.md` ⭐ — AI entry point utama |
||||||
|
- `docs/project-status.md` |
||||||
|
- `docs/roadmap.md` |
||||||
|
- `docs/todo.md` |
||||||
|
- `docs/issues.md` |
||||||
|
- `docs/changelog.md` (file ini) |
||||||
|
- `docs/environment.md` |
||||||
|
- `docs/server.md` |
||||||
|
- `docs/deployment.md` |
||||||
|
- `docs/development.md` |
||||||
|
- `docs/database.md` |
||||||
|
- `docs/troubleshooting.md` |
||||||
|
- `docs/monitoring.md` |
||||||
|
- `docs/security.md` |
||||||
|
- `docs/glossary.md` |
||||||
|
- `docs/architecture/system-overview.md` |
||||||
|
- `docs/architecture/folder-structure.md` |
||||||
|
- `docs/architecture/request-flow.md` |
||||||
|
- `docs/architecture/authentication-flow.md` |
||||||
|
- `docs/architecture/database-flow.md` |
||||||
|
- `docs/architecture/storage.md` |
||||||
|
- `docs/architecture/deployment-architecture.md` |
||||||
|
- `docs/api/overview.md` |
||||||
|
- `docs/api/service-auth.md` |
||||||
|
- `docs/api/service-ldap.md` |
||||||
|
- `docs/api/service-database.md` |
||||||
|
- `docs/api/service-tokens.md` |
||||||
|
- `docs/api/service-project-search.md` |
||||||
|
- `docs/api/error-codes.md` |
||||||
|
- `docs/adr/README.md` + ADR-0001 s/d ADR-0004 |
||||||
|
- `docs/diagrams/*.mmd` |
||||||
|
- `docs/onboarding/first-day.md` |
||||||
|
- `docs/onboarding/local-setup.md` |
||||||
|
- `docs/onboarding/coding-standard.md` |
||||||
|
- `docs/decisions/technology-stack.md` |
||||||
|
- `docs/decisions/naming-convention.md` |
||||||
|
- `.cursor/rules/coding.mdc` |
||||||
|
- `.cursor/rules/architecture.mdc` |
||||||
|
- `.cursor/rules/documentation.mdc` |
||||||
|
- `.cursor/rules/api.mdc` |
||||||
|
- `.cursor/rules/database.mdc` |
||||||
|
- `.cursor/rules/security.mdc` |
||||||
|
- `.cursor/rules/deployment.mdc` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## [2026-06-23] — geonet-console |
||||||
|
|
||||||
|
### Added |
||||||
|
- Service Tokens: CRUD API + sync ke GeoNetAgent DB (`AgentTokenSyncService`) |
||||||
|
- Service Tokens: migration, model, service, API endpoints, UI, audit log |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## [2026-06-11] — Infrastructure |
||||||
|
|
||||||
|
### Changed |
||||||
|
- Lengkapi `me.md` & `perintah.md` — URL produksi, indeks file |
||||||
|
- Selaraskan server-connection / GeoNetAgent context |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## [2026-06-xx] — geonet-console |
||||||
|
|
||||||
|
### Added |
||||||
|
- PWA Fase B: install banner + cache stale-while-revalidate |
||||||
|
- Avatar upload ke QNAP OSS (migrasi dari local storage) |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## [2026-05-xx] — geonet-console |
||||||
|
|
||||||
|
### Added |
||||||
|
- LDAP Auth + JWT (login, refresh, token-guide) |
||||||
|
- LDAP User & Group Management API |
||||||
|
- Database Registry API |
||||||
|
- Audit log ke DB terpisah |
||||||
|
- Personal API Token |
||||||
@ -0,0 +1,126 @@ |
|||||||
|
# Database Documentation |
||||||
|
|
||||||
|
> **Terakhir Diperbarui:** 2026-06-29 |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Koneksi Database |
||||||
|
|
||||||
|
| Connection Name | Driver | Host | Database | Dipakai Untuk | |
||||||
|
|----------------|--------|------|----------|----------------| |
||||||
|
| `pgsql` (default) | PostgreSQL | `audit-postgres` (container) | `databaselist_app` | App metadata, user meta, tokens, settings | |
||||||
|
| `audit` | PostgreSQL | `audit-postgres` (container) | `databaselist_audit` | Audit log aktivitas | |
||||||
|
| `project_search` | PostgreSQL | `10.100.1.25` (external) | `geonet_project_search` | Indeks AI project search (Fase Ollama) | |
||||||
|
| `mssql` | SQL Server | `10.100.1.25:1433` | `master` | Database registry (read-only) | |
||||||
|
|
||||||
|
> `DB_CONNECTION=pgsql` di `compose.yaml` override nilai di `.env`. **Jangan ubah ke sqlite.** |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Database: `databaselist_app` (PostgreSQL App) |
||||||
|
|
||||||
|
### Tabel Utama |
||||||
|
|
||||||
|
| Tabel | Model | Keterangan | |
||||||
|
|-------|-------|------------| |
||||||
|
| `ldap_user_meta` | `LdapUserMeta` | Metadata user LDAP (avatar, settings, last login) | |
||||||
|
| `ldap_group_meta` | `LdapGroupMeta` | Metadata group LDAP | |
||||||
|
| `database_connections` | `DatabaseConnection` | Registry database (SQL Server, PostgreSQL, dll.) | |
||||||
|
| `service_tokens` | `ServiceToken` | Token antar-service (sync ke GeoNetAgent) | |
||||||
|
| `api_tokens` | `ApiToken` | Personal API token per user | |
||||||
|
| `oauth_access_tokens` | — | Laravel Passport/Sanctum tokens | |
||||||
|
| `oauth_personal_access_clients` | — | OAuth clients | |
||||||
|
|
||||||
|
### `ldap_user_meta` |
||||||
|
|
||||||
|
| Kolom | Tipe | Keterangan | |
||||||
|
|-------|------|------------| |
||||||
|
| `id` | bigint PK | | |
||||||
|
| `username` | varchar UNIQUE | LDAP username | |
||||||
|
| `avatar` | varchar nullable | Path relatif di OSS: `avatars/<filename>` | |
||||||
|
| `display_name` | varchar nullable | | |
||||||
|
| `email` | varchar nullable | | |
||||||
|
| `department` | varchar nullable | | |
||||||
|
| `last_login_at` | timestamp nullable | Terakhir login | |
||||||
|
| `created_at` | timestamp | | |
||||||
|
| `updated_at` | timestamp | | |
||||||
|
|
||||||
|
### `service_tokens` |
||||||
|
|
||||||
|
| Kolom | Tipe | Keterangan | |
||||||
|
|-------|------|------------| |
||||||
|
| `id` | bigint PK | | |
||||||
|
| `name` | varchar | Nama token | |
||||||
|
| `service` | varchar | Nama service pemilik | |
||||||
|
| `token` | varchar UNIQUE | Token value (hashed) | |
||||||
|
| `abilities` | json | Scopes/abilities | |
||||||
|
| `expires_at` | timestamp nullable | | |
||||||
|
| `last_used_at` | timestamp nullable | | |
||||||
|
| `revoked_at` | timestamp nullable | | |
||||||
|
| `created_at` | timestamp | | |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Database: `databaselist_audit` (Audit Log) |
||||||
|
|
||||||
|
### `audit_logs` |
||||||
|
|
||||||
|
| Kolom | Tipe | Keterangan | |
||||||
|
|-------|------|------------| |
||||||
|
| `id` | bigint PK | | |
||||||
|
| `actor` | varchar | Username atau service yang melakukan aksi | |
||||||
|
| `actor_type` | varchar | `user`, `api_token`, `service_token` | |
||||||
|
| `action` | varchar | Nama aksi (mis. `ldap.user.update`) | |
||||||
|
| `target_type` | varchar nullable | Resource yang diubah | |
||||||
|
| `target_id` | varchar nullable | ID resource | |
||||||
|
| `payload` | json nullable | Data sebelum/sesudah perubahan | |
||||||
|
| `ip_address` | varchar nullable | | |
||||||
|
| `created_at` | timestamp | | |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Migration |
||||||
|
|
||||||
|
### Cara Menjalankan Migration |
||||||
|
|
||||||
|
Migration berjalan **otomatis saat container start** via `docker/entrypoint.sh`. |
||||||
|
|
||||||
|
Untuk manual: |
||||||
|
```bash |
||||||
|
docker exec geonet-console php artisan migrate --force |
||||||
|
``` |
||||||
|
|
||||||
|
### Catatan Penting |
||||||
|
|
||||||
|
- Migration `project_search` DB membutuhkan koneksi ke `10.100.1.25` — jika tidak reachable, container tetap start tapi log warning muncul. |
||||||
|
- Jangan jalankan `migrate:fresh` di production. |
||||||
|
- OAuth keys di-generate otomatis dan disimpan di Docker volume `app_storage`. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Akses Database (Lokal) |
||||||
|
|
||||||
|
```bash |
||||||
|
# Masuk ke container PostgreSQL |
||||||
|
docker exec -it audit-postgres psql -U geonet_app -d databaselist_app |
||||||
|
|
||||||
|
# Dari server 10.100.1.24 |
||||||
|
ssh -m hmac-sha1 root@10.100.1.24 -p 22 |
||||||
|
docker exec -it audit-postgres psql -U geonet_app -d databaselist_app |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## SQL Server (Registry Read-Only) |
||||||
|
|
||||||
|
SQL Server di `10.100.1.25:1433` digunakan oleh geonet-console hanya untuk **membaca daftar database** (database registry). Tidak ada write ke SQL Server dari geonet-console. |
||||||
|
|
||||||
|
Driver: `pdo_sqlsrv` (terinstall di container via Dockerfile) |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Lihat Juga |
||||||
|
|
||||||
|
- `geonet-console/database/migrations/` — semua migration files |
||||||
|
- `geonet-console/config/database.php` — konfigurasi koneksi |
||||||
|
- `geonet-console/.env.example` — env vars database |
||||||
@ -0,0 +1,70 @@ |
|||||||
|
# Decisions — Naming Convention |
||||||
|
|
||||||
|
> **Terakhir Diperbarui:** 2026-06-29 |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## File & Folder |
||||||
|
|
||||||
|
| Tipe | Konvensi | Contoh | |
||||||
|
|------|----------|--------| |
||||||
|
| PHP Class file | `PascalCase.php` | `LdapAuthService.php` | |
||||||
|
| Blade template | `kebab-case.blade.php` | `user-profile.blade.php` | |
||||||
|
| Migration | `YYYY_MM_DD_HHMMSS_verb_table.php` | `2026_06_23_create_service_tokens_table.php` | |
||||||
|
| PowerShell script | `verb-noun.ps1` | `deploy-geonet-console.ps1` | |
||||||
|
| Docs markdown | `kebab-case.md` | `system-overview.md` | |
||||||
|
| ADR | `ADR-NNNN-title.md` | `ADR-0001-monorepo-structure.md` | |
||||||
|
| Mermaid diagram | `purpose.mmd` | `deployment.mmd` | |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Database |
||||||
|
|
||||||
|
| Tipe | Konvensi | Contoh | |
||||||
|
|------|----------|--------| |
||||||
|
| Tabel | `snake_case` plural | `ldap_user_meta`, `service_tokens` | |
||||||
|
| Kolom | `snake_case` | `last_used_at`, `display_name` | |
||||||
|
| FK | `<table_singular>_id` | `user_id`, `token_id` | |
||||||
|
| Index | `<table>_<col>_index` | `service_tokens_service_index` | |
||||||
|
| DB name | `<project>_<type>` | `databaselist_app`, `databaselist_audit` | |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## API Routes |
||||||
|
|
||||||
|
| Konvensi | Contoh | |
||||||
|
|----------|--------| |
||||||
|
| `/kebab-case` plural | `/api/v1/ldap/users` | |
||||||
|
| Path param | `/api/v1/service-tokens/{id}` | |
||||||
|
| Nested resource | `/api/v1/ldap/users/{username}/groups` | |
||||||
|
| Action non-CRUD | `POST /api/v1/service-tokens/{id}/revoke` | |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## PHP / Laravel |
||||||
|
|
||||||
|
| Tipe | Konvensi | Contoh | |
||||||
|
|------|----------|--------| |
||||||
|
| Class | `PascalCase` | `LdapAuthService` | |
||||||
|
| Method | `camelCase` | `resolveToken()` | |
||||||
|
| Variable | `camelCase` | `$apiActor` | |
||||||
|
| Constant | `UPPER_SNAKE_CASE` | `TOKEN_TYPE_JWT` | |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Env Variables |
||||||
|
|
||||||
|
Semua `UPPER_SNAKE_CASE`: |
||||||
|
``` |
||||||
|
APP_NAME, DB_CONNECTION, LDAP_HOST, OSS_ENDPOINT |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Git Commit |
||||||
|
|
||||||
|
``` |
||||||
|
<type>: <deskripsi singkat dalam bahasa Indonesia> |
||||||
|
``` |
||||||
|
|
||||||
|
Contoh: `feat: tambah endpoint regenerate service token` |
||||||
@ -0,0 +1,78 @@ |
|||||||
|
# Decisions — Technology Stack |
||||||
|
|
||||||
|
> **Terakhir Diperbarui:** 2026-06-29 |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Backend — Laravel 12 (PHP 8.3) |
||||||
|
|
||||||
|
**Dipilih karena:** |
||||||
|
- Tim familiar dengan PHP/Laravel |
||||||
|
- Eloquent ORM + migration system matang |
||||||
|
- Built-in: auth, queue, mail, broadcast |
||||||
|
- Komunitas besar, LTS roadmap jelas |
||||||
|
|
||||||
|
**Ditolak:** |
||||||
|
- FastAPI (Python): Dipakai di GeoNetAgent — pilihan valid, tapi tim lebih familiar Laravel untuk portal |
||||||
|
- Node.js/Express: Less familiar, lebih cocok untuk real-time |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Database — PostgreSQL |
||||||
|
|
||||||
|
**Dipilih karena:** |
||||||
|
- Open source, tidak ada biaya lisensi |
||||||
|
- Support pgvector untuk Fase 4 (Ollama AI search) |
||||||
|
- JSON/JSONB support |
||||||
|
- Docker official image reliable |
||||||
|
|
||||||
|
**Ditolak:** |
||||||
|
- MySQL: Tidak support pgvector |
||||||
|
- SQL Server: Berlisensi, sudah ada hanya untuk GIS legacy |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Auth — LDAP Active Directory |
||||||
|
|
||||||
|
**Dipilih karena:** |
||||||
|
- Semua user IT sudah di AD |
||||||
|
- Single source of truth identity |
||||||
|
- Tidak perlu manage password terpisah |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## File Storage — QNAP QuObjects (OSS) |
||||||
|
|
||||||
|
**Dipilih karena:** |
||||||
|
- Hardware sudah ada (QNAP TS-932X) |
||||||
|
- S3-compatible → Laravel `s3` driver standar |
||||||
|
- File persist lintas container rebuild |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## AI Search — Ollama + pgvector (Fase 4) |
||||||
|
|
||||||
|
**Dipilih karena:** |
||||||
|
- Self-hosted, data tidak keluar jaringan internal |
||||||
|
- Ollama: mudah deploy + model management |
||||||
|
- pgvector: tetap di PostgreSQL yang sudah ada |
||||||
|
|
||||||
|
**Ditolak:** |
||||||
|
- OpenAI API: Data sensitif tidak boleh keluar, biaya berulang |
||||||
|
- Elasticsearch: Overkill untuk skala ini |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Monitoring — Zabbix |
||||||
|
|
||||||
|
**Dipilih karena:** |
||||||
|
- Sudah berjalan di 10.100.1.42 |
||||||
|
- Manage semua server + network |
||||||
|
|
||||||
|
**Prinsip:** Jangan duplikasi fungsi Zabbix di aplikasi custom. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Infrastruktur Jaringan — MikroTik CCR2004 |
||||||
|
|
||||||
|
Sudah ada, dikelola via RouterOS scripts (`server/*.rsc`). |
||||||
@ -0,0 +1,147 @@ |
|||||||
|
# Development Guide |
||||||
|
|
||||||
|
> **Terakhir Diperbarui:** 2026-06-29 |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Model Kerja |
||||||
|
|
||||||
|
Pengembangan dilakukan **remote** — kode di-edit di laptop Windows, build & run di server `10.100.1.24`. Tidak ada Docker lokal. |
||||||
|
|
||||||
|
``` |
||||||
|
Laptop (edit kode) → deploy script → Server 10.100.1.24 (build + run) |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Setup Lokal (geonet-console) |
||||||
|
|
||||||
|
### Prerequisites |
||||||
|
|
||||||
|
- PowerShell 5.1+ |
||||||
|
- Git |
||||||
|
- SSH key terdaftar di server (lihat `readme.txt`) |
||||||
|
|
||||||
|
### Workflow Development |
||||||
|
|
||||||
|
```powershell |
||||||
|
# 1. Edit kode di laptop |
||||||
|
# (VS Code → geonet-console/) |
||||||
|
|
||||||
|
# 2. Deploy ke server untuk testing |
||||||
|
.\scripts\deploy-geonet-console.ps1 |
||||||
|
|
||||||
|
# 3. Cek log untuk debug |
||||||
|
ssh -m hmac-sha1 root@10.100.1.24 -p 22 "docker logs geonet-console --tail 50" |
||||||
|
``` |
||||||
|
|
||||||
|
### Setup SSH Key (Sekali) |
||||||
|
|
||||||
|
```powershell |
||||||
|
ssh-keygen -t ed25519 -f "$env:USERPROFILE\.ssh\id_ed25519" -C "dev@$(hostname)" -N "" |
||||||
|
|
||||||
|
# Daftarkan ke reverse proxy |
||||||
|
type "$env:USERPROFILE\.ssh\id_ed25519.pub" | ssh -m hmac-sha1 root@10.100.1.24 -p 22 "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys" |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Struktur Kode — geonet-console |
||||||
|
|
||||||
|
``` |
||||||
|
geonet-console/ |
||||||
|
├── app/ |
||||||
|
│ ├── Http/Controllers/Api/V1/ — API controllers |
||||||
|
│ ├── Models/ — Eloquent models |
||||||
|
│ ├── Services/ — Business logic |
||||||
|
│ └── Support/ — ApiActor, helpers |
||||||
|
├── config/ |
||||||
|
│ ├── database.php — Koneksi DB (pgsql, audit, mssql, project_search) |
||||||
|
│ └── filesystems.php — Disk 'oss' (QNAP OSS S3) |
||||||
|
├── database/migrations/ — Migration files |
||||||
|
├── routes/api.php — Semua route API v1 |
||||||
|
├── docapi/ — Dokumentasi API (WAJIB BACA sebelum tambah endpoint) |
||||||
|
├── docker/entrypoint.sh — Startup: migrate, cache, OAuth keys |
||||||
|
├── compose.yaml — Docker Compose |
||||||
|
└── .env.example — Template env vars |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Coding Standard (Laravel) |
||||||
|
|
||||||
|
- **Service Layer**: Logic bisnis di `app/Services/`, bukan di controller |
||||||
|
- **Controller tipis**: Controller hanya validasi input → call service → return response |
||||||
|
- **ApiActor**: Gunakan `ApiActor` (dari `ApiAuthService`) untuk identitas user, bukan `Auth::user()` |
||||||
|
- **Audit log**: Setiap aksi write wajib log ke `AuditLogService` |
||||||
|
- **Response format**: Gunakan format standar — lihat `docapi/common-response.md` |
||||||
|
- **Error handling**: Gunakan error code standar — lihat `docapi/common-error.md` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Commit Convention |
||||||
|
|
||||||
|
``` |
||||||
|
<type>: <deskripsi singkat> |
||||||
|
``` |
||||||
|
|
||||||
|
| Type | Kapan | |
||||||
|
|------|-------| |
||||||
|
| `feat` | Fitur baru | |
||||||
|
| `fix` | Bug fix | |
||||||
|
| `api` | Perubahan API endpoint | |
||||||
|
| `db` | Migration atau perubahan DB | |
||||||
|
| `infra` | Nginx, Docker, deploy scripts | |
||||||
|
| `docs` | Dokumentasi | |
||||||
|
| `chore` | Config, deps, cleanup | |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Menambah Endpoint API Baru |
||||||
|
|
||||||
|
1. Buat controller di `app/Http/Controllers/Api/V1/` |
||||||
|
2. Buat service di `app/Services/` |
||||||
|
3. Daftarkan route di `routes/api.php` |
||||||
|
4. Tambah middleware auth + audit log |
||||||
|
5. **Update `geonet-console/docapi/index.md`** |
||||||
|
6. Buat file dokumentasi di `geonet-console/docapi/services/<service>/` |
||||||
|
7. Update `docs/api/` jika endpoint termasuk scope publik |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Environment di Server (Edit Langsung) |
||||||
|
|
||||||
|
```bash |
||||||
|
ssh -m hmac-sha1 root@10.100.1.24 -p 22 |
||||||
|
nano /opt/stacks/geonet-console/.env |
||||||
|
docker compose -f /opt/stacks/geonet-console/compose.yaml restart geonet-console |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Debugging |
||||||
|
|
||||||
|
```bash |
||||||
|
# Log real-time |
||||||
|
ssh -m hmac-sha1 root@10.100.1.24 -p 22 "docker logs geonet-console -f" |
||||||
|
|
||||||
|
# Laravel log |
||||||
|
ssh -m hmac-sha1 root@10.100.1.24 -p 22 "docker exec geonet-console tail -f storage/logs/laravel.log" |
||||||
|
|
||||||
|
# PHP artisan tinker |
||||||
|
ssh -m hmac-sha1 root@10.100.1.24 -p 22 "docker exec -it geonet-console php artisan tinker" |
||||||
|
|
||||||
|
# Cek migration status |
||||||
|
ssh -m hmac-sha1 root@10.100.1.24 -p 22 "docker exec geonet-console php artisan migrate:status" |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Branch Strategy |
||||||
|
|
||||||
|
| Branch | Tujuan | |
||||||
|
|--------|--------| |
||||||
|
| `main` / `master` | Production — push = deploy manual | |
||||||
|
| `dev` | Development — testing sebelum ke main | |
||||||
|
| `feature/<name>` | Fitur baru | |
||||||
|
| `fix/<name>` | Bug fix | |
||||||
@ -0,0 +1,20 @@ |
|||||||
|
sequenceDiagram |
||||||
|
participant C as Client |
||||||
|
participant GC as geonet-console |
||||||
|
participant AD as Active Directory (10.100.1.40) |
||||||
|
participant JWT as LdapJwtService |
||||||
|
|
||||||
|
C->>GC: POST /api/v1/auth/login {username, password} |
||||||
|
GC->>AD: LDAP bind |
||||||
|
AD-->>GC: bind result |
||||||
|
alt gagal |
||||||
|
GC-->>C: 401 Unauthorized |
||||||
|
end |
||||||
|
GC->>JWT: generate JWT {username, groups, exp} |
||||||
|
JWT-->>GC: signed token |
||||||
|
GC-->>C: 200 {token, expires_at} |
||||||
|
|
||||||
|
Note over C,GC: Subsequent requests |
||||||
|
C->>GC: GET /api/v1/* Bearer token |
||||||
|
GC->>GC: ApiAuthService::resolve(token) |
||||||
|
GC-->>C: Response |
||||||
@ -0,0 +1,23 @@ |
|||||||
|
graph TB |
||||||
|
subgraph GC["geonet-console (Laravel)"] |
||||||
|
APP["Application"] |
||||||
|
end |
||||||
|
|
||||||
|
subgraph PGCONT["audit-postgres (container di 10.100.1.24)"] |
||||||
|
APPDB["databaselist_app\n- ldap_user_meta\n- service_tokens\n- api_tokens\n- database_connections"] |
||||||
|
AUDITDB["databaselist_audit\n- audit_logs"] |
||||||
|
end |
||||||
|
|
||||||
|
subgraph EXT["External (10.100.1.25)"] |
||||||
|
MSSQL["SQL Server 2019\n- database registry (read)"] |
||||||
|
PGSEARCH["PostgreSQL\ngeonet_project_search\n(pgvector — Fase 4)"] |
||||||
|
end |
||||||
|
|
||||||
|
APP -->|pgsql connection| APPDB |
||||||
|
APP -->|audit connection| AUDITDB |
||||||
|
APP -->|mssql connection read-only| MSSQL |
||||||
|
APP -.->|project_search Fase 4| PGSEARCH |
||||||
|
|
||||||
|
style APPDB fill:#FF9800,color:#fff |
||||||
|
style AUDITDB fill:#9C27B0,color:#fff |
||||||
|
style MSSQL fill:#607D8B,color:#fff |
||||||
@ -0,0 +1,28 @@ |
|||||||
|
graph LR |
||||||
|
subgraph DEV["Developer (Laptop Windows)"] |
||||||
|
CODE["Edit Kode\ngeonet-console/"] |
||||||
|
SCRIPT[".\scripts\deploy-geonet-console.ps1"] |
||||||
|
end |
||||||
|
|
||||||
|
subgraph TRANSFER["Transfer"] |
||||||
|
TAR["tar -czf\ngeonet-console.tar.gz"] |
||||||
|
SCP["SSH upload\ncat > /tmp/*.tar.gz"] |
||||||
|
end |
||||||
|
|
||||||
|
subgraph SERVER["Server 10.100.1.24"] |
||||||
|
EXTRACT["Extract\n/opt/stacks/geonet-console/"] |
||||||
|
DC["docker compose up -d\n--build --force-recreate"] |
||||||
|
GC["Container\ngeonet-console"] |
||||||
|
NGX["Nginx\nconsole.gisportal.id"] |
||||||
|
end |
||||||
|
|
||||||
|
CODE --> SCRIPT |
||||||
|
SCRIPT --> TAR |
||||||
|
TAR --> SCP |
||||||
|
SCP --> EXTRACT |
||||||
|
EXTRACT --> DC |
||||||
|
DC --> GC |
||||||
|
NGX -->|proxy_pass :8091| GC |
||||||
|
|
||||||
|
style GC fill:#2196F3,color:#fff |
||||||
|
style NGX fill:#607D8B,color:#fff |
||||||
@ -0,0 +1,38 @@ |
|||||||
|
graph TB |
||||||
|
subgraph CLIENTS["Clients"] |
||||||
|
B["Browser\n(IT Admin)"] |
||||||
|
NEXTJS["Next.js PWA\nmy.gisportal.id"] |
||||||
|
end |
||||||
|
|
||||||
|
subgraph SERVER24["10.100.1.24 — App Server"] |
||||||
|
NGX["Nginx\nSSL + Reverse Proxy"] |
||||||
|
GC["geonet-console\nLaravel 12\n:8091"] |
||||||
|
PGCON["audit-postgres\nPostgreSQL"] |
||||||
|
end |
||||||
|
|
||||||
|
subgraph SERVER25["10.100.1.25 — DB Server"] |
||||||
|
MSSQL["SQL Server 2019"] |
||||||
|
PGSEARCH["PostgreSQL\npgvector (Fase 4)"] |
||||||
|
end |
||||||
|
|
||||||
|
subgraph INFRA["Infrastruktur"] |
||||||
|
AD["Active Directory\n10.100.1.40"] |
||||||
|
QNAP["QNAP NAS\n10.100.1.10\nOSS S3"] |
||||||
|
OLLAMA["Ollama VM\n10.100.1.26\n(Fase 4)"] |
||||||
|
ZABBIX["Zabbix\n10.100.1.42"] |
||||||
|
MK["MikroTik\n10.100.1.1"] |
||||||
|
end |
||||||
|
|
||||||
|
B -->|HTTPS| NGX |
||||||
|
NEXTJS -->|HTTPS API| NGX |
||||||
|
NGX -->|:8091| GC |
||||||
|
GC --> PGCON |
||||||
|
GC -->|LDAP bind| AD |
||||||
|
GC -->|read registry| MSSQL |
||||||
|
GC -->|avatar S3| QNAP |
||||||
|
GC -.->|Fase 4| PGSEARCH |
||||||
|
GC -.->|Fase 4| OLLAMA |
||||||
|
NGX -->|/super-apps/ proxy| QNAP |
||||||
|
|
||||||
|
style GC fill:#2196F3,color:#fff |
||||||
|
style NGX fill:#607D8B,color:#fff |
||||||
@ -0,0 +1,25 @@ |
|||||||
|
# Glossary |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
| Istilah | Definisi | |
||||||
|
|---------|----------| |
||||||
|
| **server-connection** | Nama repo — monorepo aplikasi IT internal & skrip infrastruktur | |
||||||
|
| **geonet-console** | Aplikasi Laravel 12 unified IT portal (`console.gisportal.id`) | |
||||||
|
| **databaselist** | Aplikasi legacy Laravel (akan di-redirect ke geonet-console) | |
||||||
|
| **gisportal.id** | Domain produksi internal untuk semua layanan IT | |
||||||
|
| **ApiActor** | Class representasi identity caller API (user, api_token, service_token) | |
||||||
|
| **Service Token** | Token antar-service (bukan user token) — misal GeoNetAgent → geonet-console | |
||||||
|
| **Audit DB** | Database `databaselist_audit` — log semua aktivitas write | |
||||||
|
| **LDAP** | Lightweight Directory Access Protocol — digunakan untuk auth via Active Directory | |
||||||
|
| **JWT** | JSON Web Token — format token setelah login LDAP | |
||||||
|
| **QNAP OSS** | QNAP QuObjects Object Storage — S3-compatible, dipakai untuk avatar storage | |
||||||
|
| **Stack path** | Path Docker stack di server, misal `/opt/stacks/geonet-console/` | |
||||||
|
| **Reverse Proxy** | Server 10.100.1.24 — menjalankan Nginx + Docker untuk semua app | |
||||||
|
| **pgvector** | PostgreSQL extension untuk vector similarity search (dipakai Ollama search) | |
||||||
|
| **Indexer** | Worker yang crawl file di QNAP NAS → embed → simpan ke pgvector | |
||||||
|
| **ADR** | Architecture Decision Record — dokumen keputusan arsitektur permanen | |
||||||
|
| **PWA** | Progressive Web App — fitur offline + installable di geonet-console | |
||||||
|
| **MikroTik CCR2004** | Router/firewall jaringan — 10.100.1.1 | |
||||||
|
| **Dockge** | UI manajemen Docker stack di server 10.100.1.24 | |
||||||
|
| **DoD** | Definition of Done — kriteria selesai suatu fase | |
||||||
@ -0,0 +1,74 @@ |
|||||||
|
# Known Issues |
||||||
|
|
||||||
|
> **Terakhir Diperbarui:** 2026-06-29 |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 🔶 Known Limitations |
||||||
|
|
||||||
|
### [LIMITATION] project_search DB timeout saat startup |
||||||
|
|
||||||
|
- **Dampak:** Migration warning di log saat container start jika 10.100.1.25 tidak reachable |
||||||
|
- **Penyebab:** `project_search` connection ke 10.100.1.25 external — migration gagal jika tidak reachable |
||||||
|
- **Workaround:** Fungsi utama app tidak terpengaruh. Warning bisa diabaikan. |
||||||
|
- **Prioritas:** Low |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
### [LIMITATION] OSS Bucket ACL bisa ter-reset |
||||||
|
|
||||||
|
- **Dampak:** Avatar URL tidak bisa diakses publik — `403 Forbidden` |
||||||
|
- **Penyebab:** QNAP QuObjects kadang reset ACL container ke private |
||||||
|
- **Workaround:** Jalankan Swift API untuk set ulang `X-Container-Read: .r:*,.rlistings` |
||||||
|
- **Prioritas:** Medium |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
### [LIMITATION] DB nama masih `databaselist_app` / `databaselist_audit` |
||||||
|
|
||||||
|
- **Dampak:** Nama DB tidak konsisten dengan nama proyek baru `geonet-console` |
||||||
|
- **Rencana:** Rename ke `geonet_console_app` + `geonet_console_audit` di Fase 3 |
||||||
|
- **Prioritas:** Low |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
### [LIMITATION] Tidak ada backup otomatis |
||||||
|
|
||||||
|
- **Dampak:** Jika server crash, data hilang |
||||||
|
- **Workaround:** Manual `pg_dump` ke NAS QNAP |
||||||
|
- **Prioritas:** Medium |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## ✅ Fixed |
||||||
|
|
||||||
|
### [FIXED] OAuth keys hilang saat rebuild container |
||||||
|
|
||||||
|
- **Penyebab:** OAuth keys tidak persist di Docker volume |
||||||
|
- **Fix:** `docker/entrypoint.sh` generate keys ke volume `app_storage` dan cek sebelum generate ulang |
||||||
|
- **Tanggal Fix:** 2026-06-xx |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 🔴 Open Issues |
||||||
|
|
||||||
|
### [OPEN] UI Redirect databaselist → geonet-console belum aktif |
||||||
|
|
||||||
|
- **Dampak:** User masih bisa akses `databaselist.gisportal.id` yang legacy |
||||||
|
- **Rencana:** Redirect di Fase 3 setelah semua fitur databaselist dimigrasi |
||||||
|
- **Prioritas:** Medium |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Cara Melaporkan Issue Baru |
||||||
|
|
||||||
|
Tambahkan di file ini: |
||||||
|
|
||||||
|
```markdown |
||||||
|
### [OPEN] Judul Issue |
||||||
|
- **Dampak:** ... |
||||||
|
- **Penyebab:** ... |
||||||
|
- **Workaround:** ... |
||||||
|
- **Prioritas:** High / Medium / Low |
||||||
|
- **Ditemukan:** YYYY-MM-DD |
||||||
|
``` |
||||||
@ -0,0 +1,76 @@ |
|||||||
|
# Monitoring |
||||||
|
|
||||||
|
> **Terakhir Diperbarui:** 2026-06-29 |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Stack Monitoring |
||||||
|
|
||||||
|
| Tool | URL | Fungsi | |
||||||
|
|------|-----|--------| |
||||||
|
| **Zabbix** | `http://10.100.1.42` | Monitoring server, VM, network real-time | |
||||||
|
| **Docker health** | Manual via SSH | Cek container status | |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Boundary Zabbix vs Aplikasi |
||||||
|
|
||||||
|
| Fungsi | Tool | |
||||||
|
|--------|------| |
||||||
|
| Uptime server, CPU, RAM, disk real-time | **Zabbix** | |
||||||
|
| Alert threshold (CPU spike, disk penuh) | **Zabbix** | |
||||||
|
| Container health | Manual `docker ps` | |
||||||
|
| Audit aktivitas user | **geonet-console** (`audit_logs` DB) | |
||||||
|
| DB registry health check | **geonet-console** (`/databases` endpoint) | |
||||||
|
|
||||||
|
**Aturan:** Jangan duplikasi fungsi Zabbix di aplikasi custom. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Cek Container Health |
||||||
|
|
||||||
|
```bash |
||||||
|
ssh -m hmac-sha1 root@10.100.1.24 -p 22 |
||||||
|
|
||||||
|
# Status semua container |
||||||
|
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" |
||||||
|
|
||||||
|
# Log geonet-console |
||||||
|
docker logs geonet-console --tail 50 |
||||||
|
|
||||||
|
# Laravel error log |
||||||
|
docker exec geonet-console tail -f storage/logs/laravel.log |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Audit Log (geonet-console) |
||||||
|
|
||||||
|
Semua aksi write tercatat di `databaselist_audit.audit_logs`. |
||||||
|
|
||||||
|
```bash |
||||||
|
# Query audit log terbaru |
||||||
|
docker exec -it audit-postgres psql -U geonet_app -d databaselist_audit \ |
||||||
|
-c "SELECT actor, action, created_at FROM audit_logs ORDER BY created_at DESC LIMIT 20;" |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Zabbix |
||||||
|
|
||||||
|
```bash |
||||||
|
# SSH ke Zabbix |
||||||
|
.\scripts\ssh-zabbix.ps1 |
||||||
|
# atau |
||||||
|
ssh -m hmac-sha1 root@10.100.1.42 -p 22 |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Health Check URL |
||||||
|
|
||||||
|
| Aplikasi | URL | |
||||||
|
|----------|-----| |
||||||
|
| geonet-console | `https://console.gisportal.id` (200 = OK) | |
||||||
|
| databaselist | `https://databaselist.gisportal.id` | |
||||||
|
| sqlservercheck | `https://sqlservercheck.gisportal.id/ts` | |
||||||
@ -0,0 +1,72 @@ |
|||||||
|
# Onboarding — Coding Standard |
||||||
|
|
||||||
|
> **Terakhir Diperbarui:** 2026-06-29 |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## PHP / Laravel (geonet-console) |
||||||
|
|
||||||
|
- **Service Layer wajib** — logic bisnis di `app/Services/`, bukan di controller |
||||||
|
- Controller: validasi → service → return response (tipis) |
||||||
|
- Gunakan `ApiActor` dari `ApiAuthService` — jangan gunakan `Auth::user()` |
||||||
|
- Setiap aksi write → `AuditLogService::log()` |
||||||
|
- Response mengikuti format di `docapi/common-response.md` |
||||||
|
- Error mengikuti format di `docapi/common-error.md` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Database |
||||||
|
|
||||||
|
- Driver default: **PostgreSQL** (`pgsql`) — jangan ubah ke sqlite |
||||||
|
- Migration: `database/migrations/` — jalankan otomatis via entrypoint |
||||||
|
- Nama tabel: `snake_case` plural (`ldap_user_meta`, `service_tokens`) |
||||||
|
- Selalu gunakan connection name eksplisit untuk non-default DB |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## PowerShell Scripts |
||||||
|
|
||||||
|
- Gunakan `Start-Process -FilePath "cmd.exe"` untuk pipe SSH (bukan heredoc) |
||||||
|
- Jangan gunakan `&&` — gunakan `;` sebagai separator di PowerShell |
||||||
|
- SSH flag wajib: `-m hmac-sha1` untuk semua server (kecuali QNAP/Ollama yang pakai key terpisah) |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Commit Convention |
||||||
|
|
||||||
|
``` |
||||||
|
<type>: <deskripsi> |
||||||
|
``` |
||||||
|
|
||||||
|
| Type | Kapan | |
||||||
|
|------|-------| |
||||||
|
| `feat` | Fitur baru | |
||||||
|
| `fix` | Bug fix | |
||||||
|
| `api` | Perubahan/tambah endpoint | |
||||||
|
| `db` | Migration atau perubahan DB | |
||||||
|
| `infra` | Nginx, Docker, scripts | |
||||||
|
| `docs` | Dokumentasi | |
||||||
|
| `chore` | Config, deps | |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Aturan Dokumentasi |
||||||
|
|
||||||
|
- Tambah endpoint baru → update `geonet-console/docapi/index.md` |
||||||
|
- Buat file dokumentasi di `geonet-console/docapi/services/<service>/` |
||||||
|
- Perubahan arsitektur → update `docs/architecture/` + buat ADR baru |
||||||
|
- Akhir sesi → update `perintah.md` (Sync terakhir) |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Naming Convention |
||||||
|
|
||||||
|
| Tipe | Konvensi | |
||||||
|
|------|----------| |
||||||
|
| PHP Class | `PascalCase` | |
||||||
|
| PHP method | `camelCase` | |
||||||
|
| DB tabel | `snake_case_plural` | |
||||||
|
| Route | `/kebab-case` | |
||||||
|
| Env var | `UPPER_SNAKE_CASE` | |
||||||
|
| Script | `verb-noun.ps1` | |
||||||
|
| Doc file | `kebab-case.md` | |
||||||
@ -0,0 +1,75 @@ |
|||||||
|
# Onboarding — Local Setup |
||||||
|
|
||||||
|
> Setup untuk mulai develop di repo `server-connection`. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Prerequisites |
||||||
|
|
||||||
|
- Windows (OS dev) |
||||||
|
- PowerShell 5.1+ |
||||||
|
- Git |
||||||
|
- VS Code (atau editor pilihan) |
||||||
|
- SSH key terdaftar di server 10.100.1.24 |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Clone Repo |
||||||
|
|
||||||
|
```powershell |
||||||
|
git clone <repo-url> "D:\Project\app on git\server-connection" |
||||||
|
cd "D:\Project\app on git\server-connection" |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Setup SSH Key (Sekali) |
||||||
|
|
||||||
|
```powershell |
||||||
|
# Buat key baru (jika belum ada) |
||||||
|
ssh-keygen -t ed25519 -f "$env:USERPROFILE\.ssh\id_ed25519" -C "dev@$(hostname)" -N "" |
||||||
|
|
||||||
|
# Daftarkan ke server 10.100.1.24 |
||||||
|
type "$env:USERPROFILE\.ssh\id_ed25519.pub" | ssh -m hmac-sha1 root@10.100.1.24 -p 22 "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys" |
||||||
|
|
||||||
|
# Verifikasi tanpa password |
||||||
|
ssh -o BatchMode=yes -m hmac-sha1 root@10.100.1.24 -p 22 "hostname" |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Workflow Dev (geonet-console) |
||||||
|
|
||||||
|
```powershell |
||||||
|
# 1. Edit kode di laptop |
||||||
|
# VS Code → D:\Project\app on git\server-connection\geonet-console\ |
||||||
|
|
||||||
|
# 2. Deploy ke server untuk test |
||||||
|
.\scripts\deploy-geonet-console.ps1 |
||||||
|
|
||||||
|
# 3. Cek hasil |
||||||
|
# https://console.gisportal.id |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Verifikasi Setup |
||||||
|
|
||||||
|
```powershell |
||||||
|
# Test SSH ke server |
||||||
|
.\scripts\ssh-remote.ps1 -Command "docker ps --filter name=geonet-console" |
||||||
|
|
||||||
|
# Test akses URL produksi |
||||||
|
curl https://console.gisportal.id/api/v1/auth/token-guide |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Alat Bantu |
||||||
|
|
||||||
|
| Alat | Fungsi | |
||||||
|
|------|--------| |
||||||
|
| `.\scripts\ssh-remote.ps1` | SSH shell atau command ke 10.100.1.24 | |
||||||
|
| `.\scripts\ssh-mikrotik.ps1` | SSH ke MikroTik | |
||||||
|
| `.\scripts\ssh-ollama.ps1` | SSH ke Ollama VM | |
||||||
|
| `.\scripts\ssh-qnap.ps1` | SSH ke QNAP NAS | |
||||||
@ -0,0 +1,57 @@ |
|||||||
|
# Roadmap |
||||||
|
|
||||||
|
> **Terakhir Diperbarui:** 2026-06-29 |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Status Saat Ini |
||||||
|
|
||||||
|
``` |
||||||
|
✅ Fase 1 — Foundation Selesai: Auth LDAP, LDAP mgmt, DB registry, Service Tokens |
||||||
|
✅ Fase 2 — Service Tokens Selesai: Token antar-service + sync ke GeoNetAgent |
||||||
|
🔄 Fase 3 — Konsolidasi Aktif: Redirect databaselist → geonet-console |
||||||
|
⏳ Fase 4 — Ollama AI Search Planned: Project search + license registry |
||||||
|
⏳ Fase 5 — Advanced Features Planned: Permission mgmt UI, email reset, dll. |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Fase 3 — Konsolidasi (Aktif) |
||||||
|
|
||||||
|
- [ ] Redirect `databaselist.gisportal.id` → `console.gisportal.id` |
||||||
|
- [ ] Rename DB: `databaselist_app` → `geonet_console_app` |
||||||
|
- [ ] Rename DB: `databaselist_audit` → `geonet_console_audit` |
||||||
|
- [ ] Stack path rename: `/opt/stacks/databaselist` → deprecated |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Fase 4 — Ollama AI Project Search |
||||||
|
|
||||||
|
**DoD:** IT bisa query project di NAS dengan bahasa alami melalui geonet-console. |
||||||
|
|
||||||
|
- [ ] Setup pgvector di 10.100.1.25 (`geonet_project_search` DB) |
||||||
|
- [ ] Deploy indexer worker di Ollama VM (10.100.1.26) |
||||||
|
- [ ] Mount SMB QNAP di Ollama VM |
||||||
|
- [ ] Indeks metadata folder `project` (crawl + embed) |
||||||
|
- [ ] UI project search di geonet-console |
||||||
|
- [ ] Audit log pencarian aktif |
||||||
|
- [ ] RBAC per source (Marketing/Sales sensitif) |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Fase 5 — Advanced Features |
||||||
|
|
||||||
|
- [ ] License registry (parser GM + ESRI license dari NAS) |
||||||
|
- [ ] Email forgot password LDAP via SMTP |
||||||
|
- [ ] Group permission management UI |
||||||
|
- [ ] Backup otomatis DB ke QNAP |
||||||
|
- [ ] Migrasi Ollama ke Windows 11 + GPU (~6 bulan) |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Lihat Juga |
||||||
|
|
||||||
|
- `docs/project-status.md` — status detail per modul |
||||||
|
- `docs/todo.md` — next actions berprioritas |
||||||
|
- `AGENTS.md` — detail rencana Ollama + NAS search |
||||||
|
- `server/ollama-nas-project-search-requirements.md` — requirement lengkap |
||||||
@ -0,0 +1,61 @@ |
|||||||
|
# Security |
||||||
|
|
||||||
|
> **Terakhir Diperbarui:** 2026-06-29 |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Larangan Mutlak |
||||||
|
|
||||||
|
- ❌ Jangan commit `.env` ke repository |
||||||
|
- ❌ Jangan commit password, API key, token, private key ke repository |
||||||
|
- ❌ Jangan tampilkan nilai secret di output AI / log |
||||||
|
- ❌ Jangan hardcode credentials di kode PHP atau script |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Model Autentikasi |
||||||
|
|
||||||
|
| Method | Digunakan Untuk | Keterangan | |
||||||
|
|--------|-----------------|------------| |
||||||
|
| **JWT (LDAP)** | User login via browser | Login AD → JWT, expire configurable | |
||||||
|
| **API Token** | Developer / personal access | Token per-user, scope-limited | |
||||||
|
| **Service Token** | Antar-service (misal GeoNetAgent) | Token per-service, bisa di-sync | |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Credential Management |
||||||
|
|
||||||
|
| Secret | Lokasi | |
||||||
|
|--------|--------| |
||||||
|
| Semua `.env` app | `/opt/stacks/geonet-console/.env` di server — **tidak di repo** | |
||||||
|
| LDAP bind password | `.env` server | |
||||||
|
| PostgreSQL password | `.env` server | |
||||||
|
| QNAP OSS secret key | `.env` server | |
||||||
|
| SQL Server password | `.env` server | |
||||||
|
| SSH private key | `~/.ssh/id_ed25519` di laptop developer | |
||||||
|
| MikroTik SSH key (RSA) | `~/.ssh/id_rsa_mikrotik` di laptop | |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Nginx Security Headers |
||||||
|
|
||||||
|
geonet-console sudah punya: |
||||||
|
- `Strict-Transport-Security` (HSTS) |
||||||
|
- `X-Frame-Options: SAMEORIGIN` |
||||||
|
- Rate limiting: login 5 req/menit, general traffic |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Aturan SSH |
||||||
|
|
||||||
|
- SSH ke server **wajib minta persetujuan user** sebelum dijalankan |
||||||
|
- Jangan auto-run SSH command destructive (rm, truncate, dll) |
||||||
|
- Edit `.env` hanya di server langsung — jangan sync dari laptop |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Aturan AI Agent |
||||||
|
|
||||||
|
- Jika diminta membuat credential baru: catat **lokasi penyimpanan** di `docs/environment.md`, bukan nilainya |
||||||
|
- Jika token bocor: instruksikan user rotasi manual — jangan buat token baru di repo |
||||||
|
- Jangan commit apapun yang mengandung password, token, atau key |
||||||
@ -0,0 +1,68 @@ |
|||||||
|
# TODO List |
||||||
|
|
||||||
|
> **Terakhir Diperbarui:** 2026-06-29 |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 🔴 High Priority |
||||||
|
|
||||||
|
### Ollama AI Search — Setup Infrastruktur |
||||||
|
- **Deskripsi:** Install Ollama + model di VM 10.100.1.26, setup pgvector di 10.100.1.25 |
||||||
|
- **File:** `scripts/geonet-indexer/`, `server/ollama-nas-project-search-requirements.md` |
||||||
|
- **Estimasi:** 1-2 minggu |
||||||
|
- **Dependency:** Ollama VM sudah vCPU 16 (siap) |
||||||
|
|
||||||
|
### Redirect databaselist → geonet-console |
||||||
|
- **Deskripsi:** Nginx redirect `databaselist.gisportal.id` ke `console.gisportal.id` |
||||||
|
- **File:** `server/nginx/`, `scripts/deploy-geonet-console-nginx.ps1` |
||||||
|
- **Estimasi:** 1 hari |
||||||
|
- **Dependency:** Semua fitur databaselist sudah tersedia di geonet-console |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 🟡 Medium Priority |
||||||
|
|
||||||
|
### Backup Otomatis Database |
||||||
|
- **Deskripsi:** Script cronjob `pg_dump` → upload ke QNAP NAS |
||||||
|
- **File:** `scripts/backup.sh` (buat baru) |
||||||
|
- **Estimasi:** 1-2 hari |
||||||
|
- **Dependency:** QNAP SSH access tersedia |
||||||
|
|
||||||
|
### License Registry — Parser GM & ESRI |
||||||
|
- **Deskripsi:** Parse file license dari NAS, simpan ke `license_registry` PostgreSQL |
||||||
|
- **File:** `scripts/geonet-indexer/`, geonet-console API |
||||||
|
- **Estimasi:** 2-3 minggu |
||||||
|
- **Dependency:** Fase 4 Ollama search |
||||||
|
|
||||||
|
### Email Forgot Password LDAP |
||||||
|
- **Deskripsi:** Kirim link reset password LDAP via SMTP `support@geonet.co.id` |
||||||
|
- **File:** `geonet-console/app/Services/`, routes |
||||||
|
- **Estimasi:** 2-3 hari |
||||||
|
- **Dependency:** - |
||||||
|
|
||||||
|
### Rename DB databaselist_* → geonet_console_* |
||||||
|
- **Deskripsi:** Rename DB dari nama legacy ke nama baru yang konsisten |
||||||
|
- **File:** `geonet-console/config/database.php`, migrations, `.env` |
||||||
|
- **Estimasi:** 2-4 jam (dengan downtime) |
||||||
|
- **Dependency:** Backup DB selesai |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 🟢 Low Priority |
||||||
|
|
||||||
|
### Group Permission Management UI |
||||||
|
- **Deskripsi:** UI untuk atur mapping LDAP group → permissions di geonet-console |
||||||
|
- **File:** `geonet-console/resources/views/`, controllers |
||||||
|
- **Estimasi:** 2-3 hari |
||||||
|
- **Dependency:** - |
||||||
|
|
||||||
|
### Monitoring Dashboard UI |
||||||
|
- **Deskripsi:** Halaman status server + container health di geonet-console |
||||||
|
- **File:** `geonet-console/app/Services/ZabbixService.php` (baru) |
||||||
|
- **Estimasi:** 3-5 hari |
||||||
|
- **Dependency:** Zabbix API token |
||||||
|
|
||||||
|
### OpenAPI / Swagger Export |
||||||
|
- **Deskripsi:** Auto-generate OpenAPI spec dari docapi/ untuk integrasi tools |
||||||
|
- **Estimasi:** 1-2 hari |
||||||
|
- **Dependency:** - |
||||||
@ -0,0 +1,119 @@ |
|||||||
|
# Troubleshooting |
||||||
|
|
||||||
|
> **Terakhir Diperbarui:** 2026-06-29 |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Login gagal — "Invalid credentials" |
||||||
|
|
||||||
|
**Cek:** |
||||||
|
1. Server AD 10.100.1.40 reachable dari container |
||||||
|
2. `LDAP_BIND_PASSWORD` di `.env` benar |
||||||
|
3. Akun AD aktif (tidak expired/locked) |
||||||
|
|
||||||
|
```bash |
||||||
|
ssh -m hmac-sha1 root@10.100.1.24 -p 22 |
||||||
|
docker exec -it geonet-console php artisan tinker |
||||||
|
# app(\App\Services\LdapAuthService::class)->attempt('username', 'password') |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Avatar tidak bisa diupload / ditampilkan |
||||||
|
|
||||||
|
**Penyebab 1:** OSS credentials salah |
||||||
|
```bash |
||||||
|
docker exec -it geonet-console php artisan tinker |
||||||
|
# Storage::disk('oss')->put('test.txt', 'hello') |
||||||
|
``` |
||||||
|
|
||||||
|
**Penyebab 2:** Bucket ACL ter-reset (tidak public-read) |
||||||
|
```bash |
||||||
|
curl -X POST http://10.100.1.10:8010/v1/AUTH_super-apps/super-apps \ |
||||||
|
-H "X-Auth-Token: <swift-token>" \ |
||||||
|
-H "X-Container-Read: .r:*,.rlistings" |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Container tidak mau start |
||||||
|
|
||||||
|
```bash |
||||||
|
ssh -m hmac-sha1 root@10.100.1.24 -p 22 |
||||||
|
docker ps -a --filter name=geonet-console |
||||||
|
docker logs geonet-console --tail 100 |
||||||
|
``` |
||||||
|
|
||||||
|
**Penyebab umum:** |
||||||
|
- `APP_KEY` kosong → `docker exec geonet-console php artisan key:generate` |
||||||
|
- Port 8091 sudah terpakai → `netstat -tlnp | grep 8091` |
||||||
|
- `.env` tidak ada → `cp .env.example .env && nano .env` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Migration gagal saat startup |
||||||
|
|
||||||
|
Error `project_search` DB (10.100.1.25) tidak reachable: |
||||||
|
``` |
||||||
|
SQLSTATE[HY000] [2002] Connection refused |
||||||
|
``` |
||||||
|
Ini **non-critical** — fungsi utama app tetap berjalan. `project_search` hanya untuk Ollama search (belum aktif). |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Deploy script error — transfer file |
||||||
|
|
||||||
|
```powershell |
||||||
|
# Gunakan cmd.exe method (bukan heredoc di PowerShell) |
||||||
|
Start-Process -FilePath "cmd.exe" -ArgumentList ` |
||||||
|
'/c ssh -m hmac-sha1 root@10.100.1.24 -p 22 "cat > /tmp/project.tar.gz" < "%TEMP%\project.tar.gz"' ` |
||||||
|
-Wait -NoNewWindow |
||||||
|
``` |
||||||
|
|
||||||
|
Jangan gunakan `&&` di PowerShell — gunakan `;`. |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## SSH "Could not negotiate a key exchange algorithm" |
||||||
|
|
||||||
|
```powershell |
||||||
|
# Tambahkan flag -m hmac-sha1 |
||||||
|
ssh -m hmac-sha1 root@10.100.1.24 -p 22 |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## MikroTik — SSH key tidak diterima |
||||||
|
|
||||||
|
```powershell |
||||||
|
# RouterOS 7.5 butuh RSA |
||||||
|
.\scripts\setup-ssh-mikrotik.ps1 -KeyType rsa |
||||||
|
|
||||||
|
# Recovery manual (via Winbox): |
||||||
|
# /user ssh-keys remove [find user=admin] |
||||||
|
# /ip ssh set password-authentication=yes |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Nginx error setelah config update |
||||||
|
|
||||||
|
```bash |
||||||
|
ssh -m hmac-sha1 root@10.100.1.24 -p 22 |
||||||
|
nginx -t && nginx -s reload |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Perintah Darurat |
||||||
|
|
||||||
|
```bash |
||||||
|
# Restart semua container |
||||||
|
docker restart geonet-console audit-postgres |
||||||
|
|
||||||
|
# Cek semua container |
||||||
|
docker ps --format "table {{.Names}}\t{{.Status}}" |
||||||
|
|
||||||
|
# Paksa rebuild |
||||||
|
docker compose -f /opt/stacks/geonet-console/compose.yaml up -d --build --force-recreate |
||||||
|
``` |
||||||
Loading…
Reference in new issue