Browse Source

docs: complete project documentation and handover context

master
Budi Setiawan 3 weeks ago
parent
commit
83440818c4
  1. 54
      server-connection/.cursor/rules/api.mdc
  2. 44
      server-connection/.cursor/rules/architecture.mdc
  3. 45
      server-connection/.cursor/rules/database.mdc
  4. 44
      server-connection/.cursor/rules/deployment.mdc
  5. 32
      server-connection/.cursor/rules/documentation.mdc
  6. 37
      server-connection/.cursor/rules/security.mdc
  7. 104
      server-connection/README.md
  8. 39
      server-connection/docs/adr/ADR-0001-monorepo-structure.md
  9. 34
      server-connection/docs/adr/ADR-0002-database-postgresql.md
  10. 34
      server-connection/docs/adr/ADR-0003-ldap-authentication.md
  11. 36
      server-connection/docs/adr/ADR-0004-qnap-object-storage.md
  12. 14
      server-connection/docs/adr/README.md
  13. 72
      server-connection/docs/api/error-codes.md
  14. 77
      server-connection/docs/api/overview.md
  15. 93
      server-connection/docs/api/service-auth.md
  16. 43
      server-connection/docs/api/service-database.md
  17. 114
      server-connection/docs/api/service-ldap.md
  18. 73
      server-connection/docs/api/service-project-search.md
  19. 98
      server-connection/docs/api/service-tokens.md
  20. 89
      server-connection/docs/architecture/authentication-flow.md
  21. 78
      server-connection/docs/architecture/database-flow.md
  22. 88
      server-connection/docs/architecture/deployment-architecture.md
  23. 106
      server-connection/docs/architecture/folder-structure.md
  24. 66
      server-connection/docs/architecture/request-flow.md
  25. 57
      server-connection/docs/architecture/storage.md
  26. 77
      server-connection/docs/architecture/system-overview.md
  27. 86
      server-connection/docs/changelog.md
  28. 155
      server-connection/docs/context-index.md
  29. 126
      server-connection/docs/database.md
  30. 70
      server-connection/docs/decisions/naming-convention.md
  31. 78
      server-connection/docs/decisions/technology-stack.md
  32. 132
      server-connection/docs/deployment.md
  33. 147
      server-connection/docs/development.md
  34. 20
      server-connection/docs/diagrams/authentication.mmd
  35. 23
      server-connection/docs/diagrams/database.mmd
  36. 28
      server-connection/docs/diagrams/deployment.mmd
  37. 38
      server-connection/docs/diagrams/system.mmd
  38. 100
      server-connection/docs/environment.md
  39. 25
      server-connection/docs/glossary.md
  40. 74
      server-connection/docs/issues.md
  41. 76
      server-connection/docs/monitoring.md
  42. 72
      server-connection/docs/onboarding/coding-standard.md
  43. 67
      server-connection/docs/onboarding/first-day.md
  44. 75
      server-connection/docs/onboarding/local-setup.md
  45. 66
      server-connection/docs/project-status.md
  46. 57
      server-connection/docs/roadmap.md
  47. 61
      server-connection/docs/security.md
  48. 118
      server-connection/docs/server.md
  49. 68
      server-connection/docs/todo.md
  50. 119
      server-connection/docs/troubleshooting.md
  51. 1
      server-connection/perintah.md

54
server-connection/.cursor/rules/api.mdc

@ -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

44
server-connection/.cursor/rules/architecture.mdc

@ -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

45
server-connection/.cursor/rules/database.mdc

@ -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);
```

44
server-connection/.cursor/rules/deployment.mdc

@ -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.

32
server-connection/.cursor/rules/documentation.mdc

@ -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

37
server-connection/.cursor/rules/security.mdc

@ -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

104
server-connection/README.md

@ -0,0 +1,104 @@
# server-connection — IT Operations Monorepo
Monorepo untuk aplikasi IT internal dan skrip infrastruktur **GIS Portal** (`gisportal.id`).
> **AI Agent & Developer baru:** Mulai dari [`docs/context-index.md`](docs/context-index.md) — peta navigasi seluruh dokumentasi.
---
## Aplikasi
| Folder | URL Produksi | Stack | Status |
|--------|-------------|-------|--------|
| `geonet-console/` | https://console.gisportal.id | Laravel 12 + PHP 8.3 | ✅ Production |
| `databaselist/` | https://databaselist.gisportal.id | Laravel (legacy) | ⚠ Legacy → akan di-redirect ke geonet-console |
| `sqlservercheck/` | https://sqlservercheck.gisportal.id | PHP slim | ✅ Running |
---
## Quick Deploy
```powershell
# Deploy geonet-console (default)
.\scripts\deploy-geonet-console.ps1
# Deploy databaselist (legacy)
.\scripts\deploy-databaselist.ps1
.\scripts\deploy-databaselist.ps1 -NoBuild # restart saja
# Deploy sqlservercheck
.\scripts\deploy-sqlservercheck.ps1
```
---
## Quick Start — Lokal Dev (geonet-console)
```powershell
# Clone & setup env
cd geonet-console
cp .env.example .env
# Edit .env — isi DB credentials, LDAP, OSS
# Build & jalankan di Docker
docker compose up -d --build
# Akses
# App: http://localhost:8091
# API: http://localhost:8091/api/v1
```
---
## Infrastruktur Server
| Role | IP | OS |
|------|----|----|
| **Reverse Proxy + Docker** | 10.100.1.24 | CentOS Linux 8 |
| **Database (PostgreSQL + SQL Server)** | 10.100.1.25 | Ubuntu 18.04 LTS |
| **Active Directory / LDAP** | 10.100.1.40 | Windows Server 2012 R2 |
| **NAS QNAP TS-932X** | 10.100.1.10 | QTS 5.2.9 |
| **Ollama AI VM** | 10.100.1.26 | Ubuntu 22.04 LTS |
| **Zabbix** | 10.100.1.42 | Ubuntu 22.04 LTS |
| **MikroTik CCR2004** | 10.100.1.1 | RouterOS 7.5 |
---
## Struktur Monorepo
```
server-connection/
├── geonet-console/ — Laravel 12 unified IT portal
├── databaselist/ — Legacy Laravel (akan di-deprecated)
├── sqlservercheck/ — Tool cek SQL Server koneksi
├── scripts/ — Deploy & SSH helper scripts
│ ├── deploy-geonet-console.ps1
│ ├── deploy-databaselist.ps1
│ ├── ssh-remote.ps1 — SSH ke reverse proxy
│ ├── ssh-mikrotik.ps1 — SSH ke MikroTik
│ ├── ssh-ollama.ps1 — SSH ke Ollama VM
│ └── ssh-qnap.ps1 — SSH ke QNAP NAS
├── server/ — Audit server + MikroTik configs
├── docs/ — Dokumentasi lengkap ⭐
├── .cursor/ — AI context (rules, me, context-index)
├── AGENTS.md — Aturan AI agent
├── perintah.md — Protokol sync + quick reference
└── readme.txt — SSH commands cepat
```
---
## Dokumentasi Lengkap
Lihat [`docs/context-index.md`](docs/context-index.md) untuk peta dokumentasi.
| Topik | File |
|-------|------|
| Status proyek | `docs/project-status.md` |
| Arsitektur sistem | `docs/architecture/system-overview.md` |
| API geonet-console | `docs/api/overview.md` |
| Deploy guide | `docs/deployment.md` |
| Environment vars | `docs/environment.md` |
| Server & SSH | `docs/server.md` |
| Database | `docs/database.md` |
| Troubleshooting | `docs/troubleshooting.md` |

39
server-connection/docs/adr/ADR-0001-monorepo-structure.md

@ -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

34
server-connection/docs/adr/ADR-0002-database-postgresql.md

@ -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)

34
server-connection/docs/adr/ADR-0003-ldap-authentication.md

@ -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`

36
server-connection/docs/adr/ADR-0004-qnap-object-storage.md

@ -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

14
server-connection/docs/adr/README.md

@ -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 |

72
server-connection/docs/api/error-codes.md

@ -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

77
server-connection/docs/api/overview.md

@ -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).

93
server-connection/docs/api/service-auth.md

@ -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"}
```

43
server-connection/docs/api/service-database.md

@ -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"}
```

114
server-connection/docs/api/service-ldap.md

@ -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.

73
server-connection/docs/api/service-project-search.md

@ -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.

98
server-connection/docs/api/service-tokens.md

@ -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.

89
server-connection/docs/architecture/authentication-flow.md

@ -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`.

78
server-connection/docs/architecture/database-flow.md

@ -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

88
server-connection/docs/architecture/deployment-architecture.md

@ -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.

106
server-connection/docs/architecture/folder-structure.md

@ -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` |

66
server-connection/docs/architecture/request-flow.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)

57
server-connection/docs/architecture/storage.md

@ -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.

77
server-connection/docs/architecture/system-overview.md

@ -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

86
server-connection/docs/changelog.md

@ -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

155
server-connection/docs/context-index.md

@ -0,0 +1,155 @@
# Context Index — server-connection ⭐ AI Entry Point
> **Terakhir Diperbarui:** 2026-06-29
> **Versi:** 2.0.0
---
## Filosofi
Proyek ini menerapkan **AI Native Development**:
- Repository = sumber kebenaran utama (Single Source of Truth)
- AI tidak boleh mengandalkan riwayat percakapan
- Developer sering berpindah perangkat — konteks harus tersimpan di repo
- Implementasi tanpa dokumentasi = belum selesai
---
## Urutan Membaca
1. **File ini** — peta navigasi
2. `.cursor/me.md` — profil user + inventaris infrastruktur
3. `perintah.md` — quick reference + protokol sync
4. `.cursor/rules/` — behavior rules AI
5. Dokumen spesifik task (lihat tabel di bawah)
---
## Proyek dalam Repo Ini
| Proyek | URL | Stack | Status | Context |
|--------|-----|-------|--------|---------|
| **geonet-console** | https://console.gisportal.id | Laravel 12 | ✅ Production | [`geonet-console/context-index.md`](../geonet-console/context-index.md) |
| **databaselist** | https://databaselist.gisportal.id | Laravel legacy | ⚠ Legacy | — |
| **sqlservercheck** | https://sqlservercheck.gisportal.id | PHP | ✅ Running | — |
> AI yang bekerja pada proyek spesifik **wajib membaca context-index proyek tersebut**.
---
## Peta Dokumentasi
### `docs/` — Flat Files
| File | Isi | Baca Saat |
|------|-----|-----------|
| `docs/project-status.md` | Status modul, backlog, known issues | Selalu |
| `docs/roadmap.md` | Fase pengembangan, milestone | Planning |
| `docs/todo.md` | Next actions berprioritas | Sebelum coding |
| `docs/issues.md` | Known bugs + workaround | Debug |
| `docs/changelog.md` | Riwayat perubahan | Reference |
| `docs/environment.md` | Env vars (tanpa secret) | Setup, deploy |
| `docs/server.md` | Server list, SSH, Docker | Deploy, infra |
| `docs/deployment.md` | Step-by-step deploy | Deploy |
| `docs/development.md` | Setup lokal, coding standard | Onboarding |
| `docs/database.md` | Schema DB, migrations, koneksi | DB change |
| `docs/troubleshooting.md` | Debug common problems | Debug |
| `docs/monitoring.md` | Zabbix, health check | Monitoring |
| `docs/security.md` | Credential management | Security |
| `docs/glossary.md` | Kamus istilah | Reference |
### `docs/architecture/` — Arsitektur
| File | Isi |
|------|-----|
| `system-overview.md` | Diagram sistem + komponen |
| `folder-structure.md` | Struktur monorepo |
| `request-flow.md` | Alur request API |
| `authentication-flow.md` | Auth LDAP → JWT |
| `database-flow.md` | DB connections, read/write |
| `storage.md` | QNAP OSS object storage |
| `deployment-architecture.md` | Server topology, Docker, Nginx |
### `docs/api/` — API Documentation
| File | Isi |
|------|-----|
| `overview.md` | Base URL, auth, format response |
| `service-auth.md` | Login, refresh, token guide |
| `service-ldap.md` | LDAP users & groups |
| `service-database.md` | Database registry |
| `service-tokens.md` | Service tokens |
| `service-project-search.md` | AI project search |
| `error-codes.md` | Error format standard |
### `docs/adr/` — Architecture Decision Records
| ADR | Keputusan |
|-----|-----------|
| `ADR-0001-monorepo-structure.md` | Struktur monorepo + folder convention |
| `ADR-0002-database-postgresql.md` | PostgreSQL untuk app baru |
| `ADR-0003-ldap-authentication.md` | Auth via LDAP Active Directory |
| `ADR-0004-qnap-object-storage.md` | QNAP OSS untuk file storage |
### `.cursor/` — AI Context (Detail Teknis)
| File | Isi |
|------|-----|
| `.cursor/context-index.md` | Entry point detail (versi lama) |
| `.cursor/me.md` | Profil user + inventaris infrastruktur |
| `.cursor/rules/00-project-context.mdc` | Struktur repo, host SSH, deploy |
| `.cursor/rules/01-enterprise-principles.mdc` | Prinsip enterprise & UX |
| `.cursor/rules/02-implementation-status.mdc` | Status fitur geonet-console |
| `.cursor/rules/03-ai-behavior.mdc` | Perilaku agent, larangan |
| `.cursor/rules/04-geonet-uiux.mdc` | Standar UI/UX |
| `.cursor/rules/05-laravel-backend.mdc` | Backend Laravel |
| `.cursor/rules/06-infrastructure.mdc` | SSH, MikroTik, deploy |
---
## Quick Reference
### URL Produksi
| Fungsi | URL |
|--------|-----|
| Geonet Console login | https://console.gisportal.id |
| LDAP Users | https://console.gisportal.id/ldap-users |
| API Docs | https://console.gisportal.id/api-docs |
| REST API | https://console.gisportal.id/api/v1 |
| Databaselist (legacy) | https://databaselist.gisportal.id |
| SQL Server Check | https://sqlservercheck.gisportal.id/ts |
### Deploy Commands
```powershell
.\scripts\deploy-geonet-console.ps1
.\scripts\deploy-databaselist.ps1
.\scripts\deploy-sqlservercheck.ps1
```
### SSH Commands
| Server | Command |
|--------|---------|
| Reverse Proxy | `ssh -m hmac-sha1 root@10.100.1.24 -p 22` |
| Database | `ssh -m hmac-sha1 root@10.100.1.25 -p 22` |
| AD/LDAP | `ssh -m hmac-sha1 Administrator@10.100.1.40 -p 22` |
| MikroTik | `.\scripts\ssh-mikrotik.ps1` |
| QNAP NAS | `.\scripts\ssh-qnap.ps1` |
| Ollama VM | `.\scripts\ssh-ollama.ps1` |
| Zabbix | `.\scripts\ssh-zabbix.ps1` |
> SSH commands ke server **wajib minta persetujuan user** sebelum dijalankan.
---
## Aturan Utama AI
1. Baca `docs/context-index.md` ini terlebih dahulu
2. Baca `.cursor/me.md` untuk konteks infrastruktur
3. Untuk geonet-console: baca `geonet-console/context-index.md` dan `geonet-console/docapi/`
4. Setelah selesai: update `perintah.md` (Sync terakhir)
5. ADR tidak boleh dihapus — buat baru jika keputusan berubah
6. **Jangan commit secret** ke repository
7. **JANGAN overwrite `.env`** saat deploy — `.env` server berisi credentials production

126
server-connection/docs/database.md

@ -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

70
server-connection/docs/decisions/naming-convention.md

@ -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`

78
server-connection/docs/decisions/technology-stack.md

@ -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`).

132
server-connection/docs/deployment.md

@ -0,0 +1,132 @@
# Deployment Guide
> **Terakhir Diperbarui:** 2026-06-29
---
## Deploy geonet-console
### Via Script (Direkomendasikan)
```powershell
.\scripts\deploy-geonet-console.ps1
```
### Manual Step-by-Step
**Step 1 — Buat archive (dari Windows)**
```powershell
tar -czf "$env:TEMP\geonet-console.tar.gz" `
--exclude="vendor" --exclude=".git" --exclude="node_modules" `
-C "D:\Project\app on git\server-connection\geonet-console" .
```
**Step 2 — Upload ke server**
```powershell
Start-Process -FilePath "cmd.exe" -ArgumentList `
'/c ssh -m hmac-sha1 root@10.100.1.24 -p 22 "cat > /tmp/geonet-console.tar.gz" < "%TEMP%\geonet-console.tar.gz"' `
-Wait -NoNewWindow
```
**Step 3 — Extract & deploy di server**
```bash
ssh -m hmac-sha1 root@10.100.1.24 -p 22
cd /opt/stacks/geonet-console
tar -xzf /tmp/geonet-console.tar.gz -C . && rm /tmp/geonet-console.tar.gz
docker compose up -d --build --force-recreate
```
**Step 4 — Verifikasi**
```bash
docker ps --filter name=geonet-console
docker logs geonet-console --tail 30
curl -s https://console.gisportal.id/health || curl -s http://127.0.0.1:8091/health
```
---
## Deploy Nginx Config
```powershell
.\scripts\deploy-geonet-console-nginx.ps1
```
Atau manual:
```bash
ssh -m hmac-sha1 root@10.100.1.24 -p 22
nginx -t && nginx -s reload
```
---
## Deploy databaselist (Legacy)
```powershell
.\scripts\deploy-databaselist.ps1 # build + deploy
.\scripts\deploy-databaselist.ps1 -NoBuild # restart saja
```
---
## Deploy sqlservercheck
```powershell
.\scripts\deploy-sqlservercheck.ps1
```
---
## Apply DB Migration
Migration berjalan otomatis saat container start. Untuk manual:
```bash
ssh -m hmac-sha1 root@10.100.1.24 -p 22
docker exec geonet-console php artisan migrate --force
```
---
## Setup Pertama Kali (Fresh Deploy)
```bash
ssh -m hmac-sha1 root@10.100.1.24 -p 22
mkdir -p /opt/stacks/geonet-console
# Upload file dari laptop (lihat step manual di atas)
cd /opt/stacks/geonet-console
cp .env.example .env
nano .env # isi semua credentials
docker compose up -d --build
```
---
## Rollback
```bash
ssh -m hmac-sha1 root@10.100.1.24 -p 22
cd /opt/stacks/geonet-console
docker compose down
# Upload versi sebelumnya
docker compose up -d --build
```
---
## Checklist Deployment
- [ ] Kode terbaru di-commit ke repo lokal
- [ ] Tidak ada file `.env` yang ikut ter-archive
- [ ] Script atau manual upload berhasil
- [ ] `docker compose up -d --build` sukses
- [ ] Container status `Up` (`docker ps`)
- [ ] `https://console.gisportal.id` accessible
- [ ] Login LDAP berhasil
- [ ] Avatar endpoint berfungsi (`GET /me` punya `avatar_url`)
- [ ] Update `perintah.md` (Sync terakhir)
---
## Penting
> ⚠ **JANGAN overwrite `.env`** saat deploy — `.env` server berisi credentials production yang tidak ada di repo.
> ⚠ Extract **tanpa** `--strip-components` agar struktur folder tetap benar.

147
server-connection/docs/development.md

@ -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 |

20
server-connection/docs/diagrams/authentication.mmd

@ -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

23
server-connection/docs/diagrams/database.mmd

@ -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

28
server-connection/docs/diagrams/deployment.mmd

@ -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

38
server-connection/docs/diagrams/system.mmd

@ -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

100
server-connection/docs/environment.md

@ -0,0 +1,100 @@
# Environment Variables
> **Terakhir Diperbarui:** 2026-06-29
> ⚠ **Jangan commit nilai secret ke repository.** Template ada di `geonet-console/.env.example`.
---
## geonet-console (Laravel 12)
File: `/opt/stacks/geonet-console/.env` di server 10.100.1.24.
### App
| Variable | Contoh | Keterangan |
|----------|--------|------------|
| `APP_NAME` | `Geonet Console` | Nama aplikasi |
| `APP_ENV` | `production` | Environment |
| `APP_KEY` | `base64:...` | **SECRET** — auto-generate jika kosong |
| `APP_URL` | `https://console.gisportal.id` | URL produksi |
| `APP_DEBUG` | `false` | Debug mode (false di production) |
### Database
| Variable | Keterangan |
|----------|------------|
| `DB_CONNECTION` | `pgsql`**wajib**, jangan ubah ke sqlite |
| `DB_HOST` | `audit-postgres` (container) |
| `DB_PORT` | `5432` |
| `DB_DATABASE` | `databaselist_app` |
| `DB_USERNAME` | `geonet_app` |
| `APP_DB_PASSWORD` | **SECRET** — password PostgreSQL app |
| `AUDIT_DB_DATABASE` | `databaselist_audit` |
### LDAP / Active Directory
| Variable | Keterangan |
|----------|------------|
| `LDAP_HOST` | `10.100.1.40` |
| `LDAP_PORT` | `389` |
| `LDAP_BASE_DN` | `DC=gisportal,DC=id` |
| `LDAP_BIND_DN` | `Administrator@gisportal.id` |
| `LDAP_BIND_PASSWORD` | **SECRET** — password AD bind account |
| `LDAP_ADMIN_GROUPS` | `Domain Admins,Administrators,Enterprise Admins` |
### SQL Server (Registry)
| Variable | Keterangan |
|----------|------------|
| `MSSQL_HOST` | `10.100.1.25` |
| `MSSQL_PORT` | `1433` |
| `MSSQL_DATABASE` | `master` |
| `MSSQL_USERNAME` | `sa` |
| `MSSQL_PASSWORD` | **SECRET** |
### QNAP OSS (Object Storage — Avatar)
| Variable | Keterangan |
|----------|------------|
| `OSS_ENDPOINT` | `http://10.100.1.10:8010` |
| `OSS_ACCESS_KEY_ID` | Format: `<storage-space>:<key-id>` |
| `OSS_SECRET_ACCESS_KEY` | **SECRET** — lihat `.env` di server |
| `OSS_BUCKET` | `super-apps` |
| `OSS_REGION` | `us-east-1` |
| `OSS_PUBLIC_URL` | `https://file.gisportal.id/super-apps` |
### Mail (SMTP)
| Variable | Keterangan |
|----------|------------|
| `MAIL_HOST` | `sc138.idcloudhosting.cloud` |
| `MAIL_PORT` | `465` |
| `MAIL_USERNAME` | `support@geonet.co.id` |
| `MAIL_PASSWORD` | **SECRET** |
| `MAIL_ENCRYPTION` | `ssl` |
### Ollama AI (Fase — Belum aktif)
| Variable | Keterangan |
|----------|------------|
| `OLLAMA_BASE_URL` | `http://10.100.1.26:11434` |
| `OLLAMA_MODEL` | `qwen2.5` |
---
## Lokasi Secret
| Secret | Lokasi |
|--------|--------|
| Semua credentials app | `/opt/stacks/geonet-console/.env` di server 10.100.1.24 |
| PostgreSQL password | `.env` (tidak di repo) |
| LDAP bind password | `.env` (tidak di repo) |
| QNAP OSS secret key | `.env` (tidak di repo) |
| SQL Server password | `.env` (tidak di repo) |
| SSH private keys | `~/.ssh/` di laptop developer |
---
## Template
Gunakan `geonet-console/.env.example` sebagai template — sudah berisi semua variable dengan placeholder aman.

25
server-connection/docs/glossary.md

@ -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 |

74
server-connection/docs/issues.md

@ -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
```

76
server-connection/docs/monitoring.md

@ -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` |

72
server-connection/docs/onboarding/coding-standard.md

@ -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` |

67
server-connection/docs/onboarding/first-day.md

@ -0,0 +1,67 @@
# Onboarding — First Day
> **Untuk:** Developer atau AI Agent baru yang bekerja di repo `server-connection`.
---
## Bacaan Wajib (Urutan)
1. [`docs/context-index.md`](../context-index.md) ← mulai dari sini
2. [`.cursor/me.md`](../../.cursor/me.md) — profil user + inventaris infrastruktur
3. [`perintah.md`](../../perintah.md) — quick reference
4. [`geonet-console/context-index.md`](../../geonet-console/context-index.md) — jika task terkait geonet-console
5. [`geonet-console/docapi/index.md`](../../geonet-console/docapi/index.md) — semua API endpoint
---
## Pahami Monorepo
Repo ini berisi:
- **geonet-console** — aplikasi utama (Laravel 12) di `console.gisportal.id`
- **scripts/** — deploy + SSH helper untuk Windows PowerShell
- **server/** — audit server + MikroTik configs
- **docs/** — dokumentasi yang sedang kamu baca ini
---
## URL Penting
| URL | Keterangan |
|-----|------------|
| https://console.gisportal.id | geonet-console production |
| https://console.gisportal.id/api/v1 | REST API |
| https://console.gisportal.id/api-docs | API Docs UI |
| https://databaselist.gisportal.id | Legacy (akan di-redirect) |
---
## Cara Kerja Dev
```powershell
# Kode di laptop → deploy ke server via script
.\scripts\deploy-geonet-console.ps1
# Tidak ada Docker lokal — build & run di server 10.100.1.24
```
---
## Server Penting
| Server | IP | Akses |
|--------|----|-|
| App + Nginx | 10.100.1.24 | `ssh -m hmac-sha1 root@10.100.1.24 -p 22` |
| Database | 10.100.1.25 | `ssh -m hmac-sha1 root@10.100.1.25 -p 22` |
| AD/LDAP | 10.100.1.40 | `ssh -m hmac-sha1 Administrator@10.100.1.40 -p 22` |
> ⚠ SSH ke server **wajib minta persetujuan user** — jangan auto-run.
---
## Aturan Penting
- Jangan commit `.env` atau secret ke repo
- Jangan overwrite `.env` di server saat deploy
- Update `perintah.md` (Sync terakhir) setelah sesi pengembangan
- Gunakan `docapi/` di geonet-console untuk dokumentasi endpoint API detail
- Implementasi tanpa dokumentasi = belum selesai

75
server-connection/docs/onboarding/local-setup.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 |

66
server-connection/docs/project-status.md

@ -0,0 +1,66 @@
# Project Status
> **Terakhir Diperbarui:** 2026-06-29
---
## Snapshot Cepat
| Proyek | Status | Keterangan |
|--------|--------|------------|
| **geonet-console** | ✅ Production | Laravel 12 + LDAP auth + API running |
| **databaselist** | ⚠ Legacy | Akan di-redirect ke geonet-console |
| **sqlservercheck** | ✅ Running | Tool diagnostik SQL Server |
| **Ollama NAS Search** | 📋 Direncanakan | POC belum diimplementasi |
---
## geonet-console — Modul Selesai
| Modul | Keterangan |
|-------|------------|
| ✅ Auth LDAP + JWT | Login AD, refresh token, token-guide |
| ✅ User Profile (`/me`) | Avatar upload ke QNAP OSS |
| ✅ LDAP User Management | CRUD user AD via API |
| ✅ LDAP Group Management | CRUD group + member management |
| ✅ Database Registry | List + offline mark database |
| ✅ Service Tokens | Token antar-service + sync ke GeoNetAgent DB |
| ✅ Audit Log | Log aktivitas ke DB terpisah |
| ✅ API Token (personal) | User bisa buat API token untuk akses programatik |
| ✅ PWA Fase A | Manifest + service worker + offline page |
| ✅ PWA Fase B | Install banner + cache stale-while-revalidate |
| ✅ Project Search API | Endpoint `/project-search/*` (UI belum ada) |
---
## geonet-console — Modul Belum Dibuat (Backlog)
| Modul | Prioritas | Keterangan |
|-------|-----------|------------|
| **Ollama AI assistant** | High | POC indexer + pgvector + chat UI |
| **Project index UI** | High | Tampilkan status indeks di UI |
| **License registry** | Medium | Parser GM + ESRI license dari NAS |
| **Redirect databaselist → geonet-console** | Medium | Fase 3 migration |
| **Email SMTP reset password** | Medium | Forgot password LDAP via SMTP |
| **Group Permission Management UI** | Low | UI untuk atur LDAP group permissions |
---
## Technical Debt
| Item | Prioritas | Keterangan |
|------|-----------|------------|
| DB `databaselist_app` nama harus direname | Medium | Nama DB legacy, target: `geonet_console_app` (Fase 3) |
| `project_search` DB di 10.100.1.25 | Low | Kalau 10.100.1.25 tidak reachable, migration gagal — tidak pengaruhi app utama |
| OAuth keys di Docker volume | Low | `app_storage` — perlu backup strategi |
---
## Known Issues
Lihat [`issues.md`](issues.md) untuk detail.
| Issue | Dampak |
|-------|--------|
| `project_search` DB timeout | Migration gagal saat 10.100.1.25 unreachable (non-critical) |
| Bucket OSS ACL bisa ter-reset | Avatar tidak bisa diakses publik — fix via Swift API |

57
server-connection/docs/roadmap.md

@ -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

61
server-connection/docs/security.md

@ -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

118
server-connection/docs/server.md

@ -0,0 +1,118 @@
# Server Information
> **Terakhir Diperbarui:** 2026-06-29
---
## Daftar Server
| Role | IP | OS | SSH Command |
|------|----|----|-------------|
| **Reverse Proxy + Docker** | 10.100.1.24 | CentOS Linux 8 | `ssh -m hmac-sha1 root@10.100.1.24 -p 22` |
| **Database (PostgreSQL + SQL Server)** | 10.100.1.25 | Ubuntu 18.04 LTS | `ssh -m hmac-sha1 root@10.100.1.25 -p 22` |
| **Active Directory / LDAP** | 10.100.1.40 | Windows Server 2012 R2 | `ssh -m hmac-sha1 Administrator@10.100.1.40 -p 22` |
| **Main Webserver** | 10.100.1.41 | Windows Server 2012 R2 | `ssh -m hmac-sha1 Administrator@10.100.1.41 -p 22` |
| **NAS QNAP TS-932X** | 10.100.1.10 | QTS 5.2.9 | `ssh -i "$env:USERPROFILE\.ssh\id_ed25519" -o IdentitiesOnly=yes -m hmac-sha1 admin@10.100.1.10 -p 22` |
| **Ollama AI VM** | 10.100.1.26 | Ubuntu 22.04 LTS | `ssh -i "$env:USERPROFILE\.ssh\id_rsa_geonet_laptop" -o IdentitiesOnly=yes -m hmac-sha1 root@10.100.1.26 -p 22` |
| **Zabbix** | 10.100.1.42 | Ubuntu 22.04 LTS | `.\scripts\ssh-zabbix.ps1` |
| **MikroTik CCR2004** | 10.100.1.1 | RouterOS 7.5 | `.\scripts\ssh-mikrotik.ps1` |
> QNAP & Ollama: Wajib **PowerShell** (bukan CMD) agar `$env:USERPROFILE` terbaca.
> SSH commands ke server **wajib minta persetujuan user** sebelum dijalankan.
---
## Server 10.100.1.24 — Reverse Proxy + Docker
### Services Running
| Stack | Container | Port Host | Path |
|-------|-----------|-----------|------|
| geonet-console | `geonet-console` | `127.0.0.1:8091` | `/opt/stacks/geonet-console/` |
| databaselist (legacy) | `databaselist` | (legacy) | `/opt/stacks/databaselist/` |
| sqlservercheck | `sqlservercheck` | — | `/opt/stacks/sqlservercheck/` |
| my-portal (Next.js) | `my-portal` | `127.0.0.1:8092` | `/opt/stacks/my-portal/` |
### Nginx Config
| File | Domain |
|------|--------|
| `/etc/nginx/conf.d/production/geonet-console.conf` | `console.gisportal.id` |
| `/etc/nginx/conf.d/production/databaselist.conf` | `databaselist.gisportal.id` |
| `/etc/nginx/conf.d/nas.conf` | `file.gisportal.id` (QNAP OSS proxy) |
### Perintah Umum
```bash
# Cek semua container
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
# Log container
docker logs geonet-console --tail 50 -f
# Restart container
docker restart geonet-console
# Nginx reload
nginx -t && nginx -s reload
```
---
## Server 10.100.1.25 — Database
```bash
ssh -m hmac-sha1 root@10.100.1.25 -p 22
# PostgreSQL (dalam container di 10.100.1.24 — bukan langsung di .25)
# SQL Server: 10.100.1.25:1433 — dipakai geonet-console untuk database registry
# Akses PostgreSQL (di container dari server 10.100.1.24)
docker exec -it audit-postgres psql -U geonet_app -d databaselist_app
```
---
## Domain Map
| Domain | Backend | Keterangan |
|--------|---------|------------|
| `console.gisportal.id` | 10.100.1.24 → container :8091 | geonet-console |
| `databaselist.gisportal.id` | 10.100.1.24 → container legacy | databaselist legacy |
| `sqlservercheck.gisportal.id` | 10.100.1.24 → container | sqlservercheck |
| `file.gisportal.id/super-apps/` | 10.100.1.24 → QNAP OSS :8010 | Object storage proxy |
---
## Transfer File ke Server (Windows → Linux)
```powershell
# Buat archive
tar -czf "$env:TEMP\geonet-console.tar.gz" `
--exclude="vendor" --exclude=".git" --exclude="node_modules" `
-C "D:\Project\app on git\server-connection\geonet-console" .
# Upload via cmd.exe (wajib pakai Start-Process di PowerShell)
Start-Process -FilePath "cmd.exe" -ArgumentList `
'/c ssh -m hmac-sha1 root@10.100.1.24 -p 22 "cat > /tmp/geonet-console.tar.gz" < "%TEMP%\geonet-console.tar.gz"' `
-Wait -NoNewWindow
# Extract di server
ssh -m hmac-sha1 root@10.100.1.24 -p 22 "tar -xzf /tmp/geonet-console.tar.gz -C /opt/stacks/geonet-console && rm /tmp/geonet-console.tar.gz"
```
> ⚠ **JANGAN overwrite `.env`** saat deploy — `.env` server berisi credentials production.
---
## SSL
HTTPS via Let's Encrypt (certbot) di 10.100.1.24. Semua domain `*.gisportal.id` di-terminate di Nginx.
---
## Backup
- Docker volumes: manual backup (belum otomatis)
- `.env` server: disimpan hanya di server (tidak di repo)
- Database backup: manual via `pg_dump` ke NAS QNAP

68
server-connection/docs/todo.md

@ -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:** -

119
server-connection/docs/troubleshooting.md

@ -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
```

1
server-connection/perintah.md

@ -148,3 +148,4 @@ SSH reverse proxy: `ssh -m hmac-sha1 root@10.100.1.24 -p 22` · Edit `.env` hany
| 2026-06-11 | Lengkapi `me.md` & `perintah.md` — URL produksi, indeks file, selaraskan server-connection / GeoNetAgent | | 2026-06-11 | Lengkapi `me.md` & `perintah.md` — URL produksi, indeks file, selaraskan server-connection / GeoNetAgent |
| 2026-06-23 | Implementasi fitur Service Tokens di geonet-console (migration, model, service, API endpoints, UI, audit log) | | 2026-06-23 | Implementasi fitur Service Tokens di geonet-console (migration, model, service, API endpoints, UI, audit log) |
| 2026-06-23 | Implementasi sinkronisasi Service Tokens → geonetagent_dev.agent_tokens (AgentTokenSyncService, Opsi C) | | 2026-06-23 | Implementasi sinkronisasi Service Tokens → geonetagent_dev.agent_tokens (AgentTokenSyncService, Opsi C) |
| 2026-06-29 | Audit & lengkapi dokumentasi server-connection: buat docs/ lengkap (context-index, architecture, api, adr, diagrams, onboarding, decisions), README.md root, .cursor/rules/ tambahan (documentation, security, deployment, api, database, architecture) |

Loading…
Cancel
Save