51 changed files with 3530 additions and 0 deletions
@ -0,0 +1,54 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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