Browse Source

feat: feedback system, SSH keys, USB passthrough, disk cleanup, agent v0.1.12

- feat(super-apps): tambah FeedbackButton + FeedbackDialog di semua halaman
- feat(geonet-console): endpoint POST/GET/PATCH /api/v1/feedback + migration
- feat(agent): GeoNetAgent v0.1.12-phase0 + shutdown task (GPO Shutdown script)
- ops(proxmox): USB passthrough controller 00:1a.0 ke VM 103 (Opr Proxmox)
- ops(ssh): setup key-based SSH ke Proxmox 10.100.1.101/102, ArcGIS 10.100.1.51/52
- ops(server): disk cleanup 10.100.1.24 (docker builder prune -af, freed 45GB)
- docs: deploy-log.md, host.md update, ISO.md standar EOL perangkat
master
Budi Setiawan 2 weeks ago
parent
commit
c0ed744f19
  1. 1
      .gitignore
  2. 115
      GeoNetAgent/ISO.md
  3. 2
      GeoNetAgent/agent/VERSION
  4. 6
      GeoNetAgent/agent/gpo/GeoNetAgent-GPO-Shutdown.cmd
  5. 59
      GeoNetAgent/agent/gpo/GeoNetAgent-GPO-Shutdown.ps1
  6. 2
      GeoNetAgent/agent/gpo/GeoNetAgent-GPO-Startup.ps1
  7. 58
      GeoNetAgent/agent/gpo/README.md
  8. 51
      GeoNetAgent/agent/install/index.html
  9. 2
      GeoNetAgent/agent/install/manifest.json
  10. 54
      GeoNetAgent/agent/lib/Agent-InstallCore.ps1
  11. 30
      GeoNetAgent/agent/lib/Get-GpuInfo.ps1
  12. 182
      Workspace-Context.md
  13. 91
      anydesk.md
  14. 82
      server-audit/deploy-log.md
  15. 77
      server-audit/host.md
  16. 99
      server-connection/geonet-console/app/Http/Controllers/Api/V1/FeedbackController.php
  17. 26
      server-connection/geonet-console/app/Models/Feedback.php
  18. 32
      server-connection/geonet-console/database/migrations/2026_07_01_000001_create_feedback_table.php
  19. 10
      server-connection/geonet-console/routes/api.php

1
.gitignore vendored

@ -3,6 +3,7 @@ ops/
support/ support/
support-api/ support-api/
LDAPWeb.code-workspace LDAPWeb.code-workspace
mysupperapps_FE_PWA/
# Environment & secrets # Environment & secrets
.env .env

115
GeoNetAgent/ISO.md

@ -0,0 +1,115 @@
# Standar Internasional — Acuan Masa Pakai & EOL Perangkat IT
Dokumen ini merangkum standar internasional yang menjadi acuan dalam menentukan masa pakai (*useful life*), siklus hidup, dan *End-of-Life* (EOL) perangkat laptop, PC, dan workstation di lingkungan organisasi.
---
## Ringkasan Standar
| Standar | Nama Lengkap | Fungsi Utama |
| ------- | ------------ | ------------ |
| ISO 55001 | Asset Management — Management Systems | Asset Lifecycle |
| ISO/IEC 19770-1 | IT Asset Management | IT Asset Management |
| ISO/IEC 20000-1 | IT Service Management | IT Service Management |
| ISO/IEC 27001 | Information Security Management | Information Security |
| NIST SP 800-53 | Security & Privacy Controls for IS | Hardware Lifecycle & Security Controls |
| ISACA COBIT | Control Objectives for IT | Governance & Asset Lifecycle |
---
## Detail Standar & Relevansinya untuk Perangkat Endpoint
### 1. ISO 55001 — Manajemen Aset (Asset Lifecycle)
**Relevansi:** Standar utama untuk pengelolaan siklus hidup aset fisik, termasuk perangkat IT.
- Mewajibkan organisasi mendefinisikan **masa pakai aset** secara eksplisit berdasarkan risiko dan kebutuhan bisnis.
- Memerlukan perencanaan **penghapusan aset** (*asset disposal*) yang terstruktur ketika perangkat mencapai EOL.
- Acuan umum masa pakai perangkat berdasarkan praktik ISO 55001:
- **Laptop/PC pengguna umum:** 3–5 tahun
- **Workstation teknis/desain:** 4–6 tahun
- **Server/infrastruktur:** 5–7 tahun
- Mensyaratkan dokumentasi kondisi aset, jadwal penggantian, dan analisis biaya siklus hidup penuh (*lifecycle cost analysis*).
---
### 2. ISO/IEC 19770-1 — IT Asset Management (ITAM)
**Relevansi:** Standar spesifik untuk pengelolaan aset IT, mencakup hardware dan software.
- Mendefinisikan proses **inventarisasi, pelacakan, dan penonaktifan** aset IT secara sistematis.
- Mensyaratkan pencatatan atribut EOL/EOS (*End-of-Support*) dari setiap perangkat, termasuk:
- Tanggal akhir dukungan hardware dari vendor (OEM EOL)
- Tanggal akhir dukungan OS dari Microsoft/Apple/Linux distro
- Mensyaratkan proses **decommissioning** yang mencakup penghapusan data aman (*secure data wipe*) sebelum perangkat dilepas.
- Tiga tingkat kematangan ITAM (Tier 1–3); untuk pengelolaan EOL yang efektif minimal Tier 2.
---
### 3. ISO/IEC 20000-1 — IT Service Management (ITSM)
**Relevansi:** Menghubungkan siklus hidup perangkat dengan manajemen layanan IT.
- Proses **Configuration Management** (CMDB) mewajibkan pencatatan status aset termasuk status EOL/aktif/pensiun.
- Proses **Change Management** mensyaratkan perencanaan penggantian perangkat yang mendekati EOL agar tidak mengganggu layanan.
- Proses **Capacity & Availability Management** menggunakan data EOL untuk merencanakan kapasitas dan anggaran pengadaan.
---
### 4. ISO/IEC 27001 — Information Security Management
**Relevansi:** Keamanan informasi mewajibkan kontrol ketat pada perangkat yang mendekati atau melewati EOL.
- Kontrol **A.8 — Asset Management**: semua aset informasi (termasuk endpoint) harus diinventarisasi dan diketahui status EOL-nya.
- Perangkat yang OS-nya sudah EOL (contoh: Windows 7, Windows 10 setelah Okt 2025) dianggap **risiko keamanan tinggi** karena tidak mendapat patch keamanan.
- Mensyaratkan prosedur penghapusan data aman saat perangkat diretire (*media sanitization*).
- Pengelolaan EOL yang buruk dapat menjadi temuan **nonkonformitas** saat audit ISO 27001.
---
### 5. NIST SP 800-53 — Hardware Lifecycle & Security Controls
**Relevansi:** Panduan kontrol keamanan hardware dari NIST (banyak diadopsi sebagai referensi di luar AS).
- **SA-22 (Unsupported System Components):** Mewajibkan organisasi untuk tidak mengoperasikan komponen IT yang sudah tidak didukung vendor (EOL/EOS), atau mendokumentasikan mitigasi risiko yang memadai.
- **MA-6 (Timely Maintenance):** Mensyaratkan perawatan dan penggantian tepat waktu berdasarkan jadwal siklus hidup.
- **SI-2 (Flaw Remediation):** Perangkat EOL tidak dapat menerima patch, secara otomatis melanggar kontrol ini.
---
### 6. ISACA COBIT — Governance & Asset Lifecycle
**Relevansi:** Kerangka tata kelola IT yang menghubungkan pengelolaan aset dengan nilai bisnis.
- **BAI09 (Manage Assets):** Proses khusus untuk inventarisasi, pemeliharaan, dan penonaktifan aset IT.
- **DSS01 (Manage Operations):** Mensyaratkan pemantauan kondisi hardware secara berkelanjutan.
- Pengelolaan EOL masuk dalam konteks **risk management** dan **value delivery** — perangkat yang melewati EOL meningkatkan risiko operasional dan biaya tidak terduga.
---
## Acuan Masa Pakai Umum Perangkat Endpoint
| Jenis Perangkat | Masa Pakai Ideal | Batas Maksimum | Catatan |
| --------------- | ---------------- | -------------- | ------- |
| Laptop (pengguna umum) | 3–4 tahun | 5 tahun | Berdasarkan siklus depresiasi fiskal & EOL OS |
| PC Desktop (pengguna umum) | 4–5 tahun | 6 tahun | Komponen lebih mudah diupgrade |
| Workstation (teknis/CAD/GIS) | 4–5 tahun | 6 tahun | Tergantung kebutuhan performa aplikasi |
| Laptop/PC manajemen | 3–5 tahun | 5 tahun | Prioritas keamanan data tinggi |
> **Catatan:** Masa pakai dihitung sejak tanggal pembelian pertama, bukan sejak mulai digunakan. Perangkat yang OS-nya mencapai EOL **wajib diganti atau diupgrade OS** terlepas dari usia hardware.
---
## Referensi EOL OS Umum
| Sistem Operasi | Tanggal EOL | Keterangan |
| -------------- | ----------- | ---------- |
| Windows 10 (semua edisi) | 14 Oktober 2025 | Tidak ada Extended Security Update gratis untuk konsumen |
| Windows 11 22H2 | 8 Oktober 2024 | Sudah EOL, wajib upgrade ke 23H2/24H2 |
| Windows 11 23H2 | 11 November 2025 | — |
| Windows 11 24H2 | Oktober 2026 (est.) | Rilis terbaru (2024) |
| Ubuntu 20.04 LTS | April 2025 (standard) | ESM hingga April 2030 |
| Ubuntu 22.04 LTS | April 2027 (standard) | ESM hingga April 2032 |
| Ubuntu 24.04 LTS | April 2029 (standard) | ESM hingga April 2034 |
> Sumber: Microsoft Lifecycle Policy, Ubuntu Release Schedule. Selalu verifikasi ke situs resmi vendor untuk tanggal terkini.

2
GeoNetAgent/agent/VERSION

@ -1 +1 @@
0.1.10-phase0 0.1.12-phase0

6
GeoNetAgent/agent/gpo/GeoNetAgent-GPO-Shutdown.cmd

@ -0,0 +1,6 @@
@echo off
REM GeoNetAgent — GPO Computer Shutdown Script (wrapper)
REM Deploy folder ini ke SYSVOL / NETLOGON, lalu tautkan .cmd ini di GPO Shutdown.
powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "%~dp0GeoNetAgent-GPO-Shutdown.ps1"
exit /b 0

59
GeoNetAgent/agent/gpo/GeoNetAgent-GPO-Shutdown.ps1

@ -0,0 +1,59 @@
#Requires -Version 5.1
<#
.SYNOPSIS
GPO Shutdown Script kirim laporan GeoNetAgent sebelum Windows shutdown/restart.
.DESCRIPTION
Dipanggil dari Group Policy (Computer Configuration Shutdown Scripts).
Menjalankan UserAgent.ps1 -SkipAutoUpdate agar laporan terkirim sebelum PC mati.
Auto-update dilewati karena waktu shutdown terbatas.
.NOTES
Log: %ProgramData%\GeoNetAgent\logs\gpo-shutdown.log
Exit selalu 0 agar GPO tidak error.
Batas waktu eksekusi GPO Shutdown Script default: 600 detik (bisa diatur via GPO).
Rekomendasikan set "Maximum wait time for Group Policy scripts" ke 60 detik.
#>
[CmdletBinding()]
param()
$ErrorActionPreference = 'Continue'
$InstallRoot = Join-Path $env:ProgramData 'GeoNetAgent'
$LogDir = Join-Path $InstallRoot 'logs'
$LogFile = Join-Path $LogDir 'gpo-shutdown.log'
$UserAgent = Join-Path $InstallRoot 'UserAgent.ps1'
function Write-GeoNetShutdownLog {
param([string]$Message)
$line = '{0} {1}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $Message
try {
if (-not (Test-Path $LogDir)) {
New-Item -ItemType Directory -Path $LogDir -Force | Out-Null
}
Add-Content -Path $LogFile -Value $line -Encoding UTF8
}
catch { }
}
Write-GeoNetShutdownLog '=== GPO shutdown begin ==='
try {
if (-not (Test-Path $UserAgent)) {
Write-GeoNetShutdownLog "UserAgent.ps1 tidak ditemukan di $InstallRoot — skip"
exit 0
}
Write-GeoNetShutdownLog 'Mengirim laporan sebelum shutdown...'
& powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden `
-File $UserAgent -SkipAutoUpdate
$code = $LASTEXITCODE
Write-GeoNetShutdownLog "UserAgent selesai (exit $code)"
}
catch {
Write-GeoNetShutdownLog "ERROR: $($_.Exception.Message)"
}
Write-GeoNetShutdownLog '=== GPO shutdown end ==='
exit 0

2
GeoNetAgent/agent/gpo/GeoNetAgent-GPO-Startup.ps1

@ -108,7 +108,7 @@ function Invoke-GeoNetGpoWebInstall {
} }
function Test-GeoNetScheduledTasks { function Test-GeoNetScheduledTasks {
$names = @('GeoNetAgent-Report', 'GeoNetAgent-Report-Startup') $names = @('GeoNetAgent-Report', 'GeoNetAgent-Report-Startup', 'GeoNetAgent-Report-Shutdown')
foreach ($name in $names) { foreach ($name in $names) {
$null = & schtasks.exe /Query /TN $name /FO LIST 2>$null $null = & schtasks.exe /Query /TN $name /FO LIST 2>$null
if ($LASTEXITCODE -ne 0) { return $false } if ($LASTEXITCODE -ne 0) { return $false }

58
GeoNetAgent/agent/gpo/README.md

@ -6,8 +6,10 @@ Skrip startup untuk **Computer Configuration** Active Directory. Jalan sebagai *
| File | Fungsi | | File | Fungsi |
|------|--------| |------|--------|
| `GeoNetAgent-GPO-Startup.cmd` | Entry point untuk GPO (tambahkan file ini di policy) | | `GeoNetAgent-GPO-Startup.cmd` | Entry point GPO Startup (tambahkan di Startup Scripts) |
| `GeoNetAgent-GPO-Startup.ps1` | Logika install / update / perbaiki task | | `GeoNetAgent-GPO-Startup.ps1` | Logika install / update / perbaiki task saat boot |
| `GeoNetAgent-GPO-Shutdown.cmd` | Entry point GPO Shutdown (tambahkan di Shutdown Scripts) |
| `GeoNetAgent-GPO-Shutdown.ps1` | Kirim laporan sebelum PC mati / restart |
| `README.md` | Panduan ini | | `README.md` | Panduan ini |
## Prasyarat server ## Prasyarat server
@ -43,7 +45,15 @@ Pastikan **Computer Account** domain bisa **Read** folder tersebut.
\\gisportal.id\SYSVOL\gisportal.id\scripts\GeoNetAgent\GeoNetAgent-GPO-Startup.cmd \\gisportal.id\SYSVOL\gisportal.id\scripts\GeoNetAgent\GeoNetAgent-GPO-Startup.cmd
``` ```
5. **OK** → tutup editor GPO 5. Tab **Shutdown****Add** → pilih:
```
\\gisportal.id\SYSVOL\gisportal.id\scripts\GeoNetAgent\GeoNetAgent-GPO-Shutdown.cmd
```
6. **OK** → tutup editor GPO
> **Tip:** Set `Computer Configuration → Policies → Windows Settings → Scripts → Maximum wait time for Group Policy scripts` ke **60 detik** agar shutdown tidak terlalu lama menunggu agent.
## Langkah 3 — Link ke OU ## Langkah 3 — Link ke OU
@ -66,14 +76,18 @@ Setelah boot, cek:
Get-Content "$env:ProgramData\GeoNetAgent\VERSION" Get-Content "$env:ProgramData\GeoNetAgent\VERSION"
Get-Content "$env:ProgramData\GeoNetAgent\logs\gpo-startup.log" -Tail 20 Get-Content "$env:ProgramData\GeoNetAgent\logs\gpo-startup.log" -Tail 20
schtasks /Query /TN GeoNetAgent-Report /V /FO LIST schtasks /Query /TN GeoNetAgent-Report /V /FO LIST
schtasks /Query /TN GeoNetAgent-Report-Startup /V /FO LIST
schtasks /Query /TN GeoNetAgent-Report-Shutdown /V /FO LIST
Test-Path "$env:ProgramData\GeoNetAgent\UserAgent.ps1" Test-Path "$env:ProgramData\GeoNetAgent\UserAgent.ps1"
``` ```
Harapan: Harapan:
- `VERSION` = versi terbaru di server (mis. `0.1.8-phase0`) - `VERSION` = versi terbaru di server (mis. `0.1.11-phase0`)
- Log berisi `Selesai OK` - Log berisi `Selesai OK`
- Task `GeoNetAgent-Report` — HOURLY, interval **3** jam, run as **SYSTEM** - Task `GeoNetAgent-Report` — HOURLY, interval **3** jam, run as **SYSTEM**
- Task `GeoNetAgent-Report-Startup` — At Startup, run as **SYSTEM**
- Task `GeoNetAgent-Report-Shutdown` — Event trigger EventID 1074 (shutdown/restart), run as **SYSTEM**
## Perilaku skrip ## Perilaku skrip
@ -82,25 +96,44 @@ Harapan:
| Agent belum ada | Install penuh dari web (silent, `-SkipInitialRun`) | | Agent belum ada | Install penuh dari web (silent, `-SkipInitialRun`) |
| Agent lama tanpa modul auto-update | Reinstall | | Agent lama tanpa modul auto-update | Reinstall |
| Agent ada | Cek `manifest.json` vs `VERSION`, update jika beda | | Agent ada | Cek `manifest.json` vs `VERSION`, update jika beda |
| Scheduled Task hilang | Daftar ulang (startup + 3 jam) | | Scheduled Task hilang | Daftar ulang (startup + 3 jam + shutdown event) |
| Jaringan belum siap saat boot | Tunggu hingga 120 detik | | Jaringan belum siap saat boot | Tunggu hingga 120 detik |
| Error | Catat di log, **exit 0** (boot tidak terganggu) | | Error | Catat di log, **exit 0** (boot tidak terganggu) |
Laporan inventaris dikirim oleh Scheduled Task (`UserAgent.ps1`), bukan langsung dari skrip GPO. ### Kapan laporan dikirim
| Trigger | Keterangan |
|---------|------------|
| **Boot / Startup** | GPO Startup + Task `GeoNetAgent-Report-Startup` (ONSTART) |
| **Setiap 3 jam** | Task `GeoNetAgent-Report` (HOURLY) |
| **Shutdown / Restart** | GPO Shutdown + Task `GeoNetAgent-Report-Shutdown` (Event 1074) |
Task shutdown menggunakan `-SkipAutoUpdate` agar tidak delay proses mati PC.
## Log ## Log
``` ```
%ProgramData%\GeoNetAgent\logs\gpo-startup.log %ProgramData%\GeoNetAgent\logs\gpo-startup.log ← startup
%ProgramData%\GeoNetAgent\logs\gpo-shutdown.log ← shutdown
```
Contoh startup:
```
2026-06-29 08:15:02 === GPO startup begin ===
2026-06-29 08:15:08 Agent ada — cek update
2026-06-29 08:15:12 Updated: 0.1.10-phase0 -> 0.1.11-phase0
2026-06-29 08:15:13 Selesai OK. VERSION=0.1.11-phase0
2026-06-29 08:15:13 === GPO startup end ===
``` ```
Contoh: Contoh shutdown:
``` ```
2026-06-18 08:15:02 === GPO startup begin === 2026-06-29 17:30:01 === GPO shutdown begin ===
2026-06-18 08:15:08 Agent belum terpasang — install baru 2026-06-29 17:30:01 Mengirim laporan sebelum shutdown...
2026-06-18 08:15:45 Selesai OK. VERSION=0.1.8-phase0 2026-06-29 17:30:08 UserAgent selesai (exit 0)
2026-06-18 08:15:45 === GPO startup end === 2026-06-29 17:30:08 === GPO shutdown end ===
``` ```
## Firewall / proxy ## Firewall / proxy
@ -127,6 +160,7 @@ powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "%~d
```cmd ```cmd
schtasks /Delete /TN GeoNetAgent-Report /F schtasks /Delete /TN GeoNetAgent-Report /F
schtasks /Delete /TN GeoNetAgent-Report-Startup /F schtasks /Delete /TN GeoNetAgent-Report-Startup /F
schtasks /Delete /TN GeoNetAgent-Report-Shutdown /F
``` ```
3. Hapus folder: `%ProgramData%\GeoNetAgent` 3. Hapus folder: `%ProgramData%\GeoNetAgent`

51
GeoNetAgent/agent/install/index.html

@ -15,10 +15,20 @@
ol.steps { padding-left: 1.25rem; } ol.steps { padding-left: 1.25rem; }
ol.steps li { margin: .5rem 0; } ol.steps li { margin: .5rem 0; }
.label { display: inline-block; min-width: 4.5rem; font-weight: 600; color: #52525b; } .label { display: inline-block; min-width: 4.5rem; font-weight: 600; color: #52525b; }
.version-bar { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; margin: .75rem 0 1.25rem; }
.badge { display: inline-flex; align-items: center; gap: .4rem; background: #18181b; color: #fff; border-radius: 6px; padding: .3rem .75rem; font-size: .85rem; font-weight: 600; letter-spacing: .01em; }
.badge-label { opacity: .65; font-weight: 400; }
.badge-ver { color: #4ade80; }
.badge-date { color: #93c5fd; }
.badge-loading { opacity: .5; }
</style> </style>
</head> </head>
<body> <body>
<h1>GeoNetAgent — Install &amp; Update</h1> <h1>GeoNetAgent — Install &amp; Update</h1>
<div class="version-bar">
<span class="badge badge-loading" id="badge-ver"><span class="badge-label">versi</span> <span class="badge-ver" id="ver-text">memuat&hellip;</span></span>
<span class="badge badge-loading" id="badge-date"><span class="badge-label">diperbarui</span> <span class="badge-date" id="date-text">memuat&hellip;</span></span>
</div>
<p>Installer untuk laptop/PC Windows. Path install: <code>%ProgramData%\GeoNetAgent</code></p> <p>Installer untuk laptop/PC Windows. Path install: <code>%ProgramData%\GeoNetAgent</code></p>
<div class="warn"> <div class="warn">
@ -46,7 +56,7 @@
<div class="note"> <div class="note">
<strong>Harapan sukses:</strong> <strong>Harapan sukses:</strong>
<code>Scheduled Task OK: GeoNetAgent-Report, GeoNetAgent-Report-Startup</code> <code>Scheduled Task OK: GeoNetAgent-Report, GeoNetAgent-Report-Startup, GeoNetAgent-Report-Shutdown</code>
</div> </div>
<h2>Cek setelah install</h2> <h2>Cek setelah install</h2>
@ -60,6 +70,7 @@ schtasks /Query /TN GeoNetAgent-Report-Startup
<li>File agent di <code>%ProgramData%\GeoNetAgent</code></li> <li>File agent di <code>%ProgramData%\GeoNetAgent</code></li>
<li><code>GeoNetAgent-Report-Startup</code> — jalan saat boot (SYSTEM)</li> <li><code>GeoNetAgent-Report-Startup</code> — jalan saat boot (SYSTEM)</li>
<li><code>GeoNetAgent-Report</code> — tiap 3 jam (SYSTEM); cek update otomatis sebelum kirim laporan</li> <li><code>GeoNetAgent-Report</code> — tiap 3 jam (SYSTEM); cek update otomatis sebelum kirim laporan</li>
<li><code>GeoNetAgent-Report-Shutdown</code> — jalan saat shutdown/restart Windows (Event 1074)</li>
</ul> </ul>
<div class="warn"> <div class="warn">
@ -73,5 +84,43 @@ schtasks /Query /TN GeoNetAgent-Report-Startup
<li>PowerShell Admin: <code>cd $env:TEMP</code></li> <li>PowerShell Admin: <code>cd $env:TEMP</code></li>
<li><code>.\GeoNetAgent-Install.ps1 -SkipInitialRun</code></li> <li><code>.\GeoNetAgent-Install.ps1 -SkipInitialRun</code></li>
</ol> </ol>
<script>
(function () {
var url = 'manifest.json?_=' + Date.now();
fetch(url)
.then(function (r) { return r.json(); })
.then(function (m) {
var ver = m.version || '-';
document.getElementById('ver-text').textContent = ver;
document.getElementById('badge-ver').classList.remove('badge-loading');
var head = new XMLHttpRequest();
head.open('HEAD', url);
head.onload = function () {
var lm = head.getResponseHeader('Last-Modified');
if (lm) {
var d = new Date(lm);
var fmt = d.toLocaleDateString('id-ID', { day: '2-digit', month: 'short', year: 'numeric' })
+ ' ' + d.toLocaleTimeString('id-ID', { hour: '2-digit', minute: '2-digit' });
document.getElementById('date-text').textContent = fmt;
} else {
document.getElementById('date-text').textContent = '-';
}
document.getElementById('badge-date').classList.remove('badge-loading');
};
head.onerror = function () {
document.getElementById('date-text').textContent = '-';
document.getElementById('badge-date').classList.remove('badge-loading');
};
head.send();
})
.catch(function () {
document.getElementById('ver-text').textContent = 'gagal memuat';
document.getElementById('date-text').textContent = '-';
document.getElementById('badge-ver').classList.remove('badge-loading');
document.getElementById('badge-date').classList.remove('badge-loading');
});
})();
</script>
</body> </body>
</html> </html>

2
GeoNetAgent/agent/install/manifest.json

@ -1,5 +1,5 @@
{ {
"version": "0.1.10-phase0", "version": "0.1.12-phase0",
"files": [ "files": [
"Send-GeoNetReport.ps1", "Send-GeoNetReport.ps1",
"Collect-GeoNetReport.ps1", "Collect-GeoNetReport.ps1",

54
GeoNetAgent/agent/lib/Agent-InstallCore.ps1

@ -123,7 +123,7 @@ function Register-GeoNetAgentTask {
$taskArgs = "-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$userScript`"" $taskArgs = "-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$userScript`""
$taskCmd = "powershell.exe $taskArgs" $taskCmd = "powershell.exe $taskArgs"
$taskNames = @('GeoNetAgent-Report', 'GeoNetAgent-Report-Startup') $taskNames = @('GeoNetAgent-Report', 'GeoNetAgent-Report-Startup', 'GeoNetAgent-Report-Shutdown')
foreach ($name in $taskNames) { foreach ($name in $taskNames) {
Unregister-ScheduledTask -TaskName $name -Confirm:$false -ErrorAction SilentlyContinue Unregister-ScheduledTask -TaskName $name -Confirm:$false -ErrorAction SilentlyContinue
@ -139,6 +139,58 @@ function Register-GeoNetAgentTask {
'/Create', '/TN', 'GeoNetAgent-Report', '/TR', $taskCmd, '/Create', '/TN', 'GeoNetAgent-Report', '/TR', $taskCmd,
'/SC', 'HOURLY', '/MO', "$Hours", '/RU', 'SYSTEM', '/RL', 'HIGHEST', '/F' '/SC', 'HOURLY', '/MO', "$Hours", '/RU', 'SYSTEM', '/RL', 'HIGHEST', '/F'
) | Out-Null ) | Out-Null
Register-GeoNetShutdownTask -InstallRoot $InstallRoot -TaskCmd $taskCmd
}
function Register-GeoNetShutdownTask {
param(
[string]$InstallRoot,
[string]$TaskCmd
)
$xmlPath = Join-Path $env:TEMP 'GeoNetAgent-Shutdown-Task.xml'
$xml = @"
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<RegistrationInfo>
<Description>GeoNetAgent kirim laporan saat shutdown/restart Windows</Description>
</RegistrationInfo>
<Triggers>
<EventTrigger>
<Enabled>true</Enabled>
<Subscription>&lt;QueryList&gt;&lt;Query Id="0" Path="System"&gt;&lt;Select Path="System"&gt;*[System[Provider[@Name='USER32'] and EventID=1074]]&lt;/Select&gt;&lt;/Query&gt;&lt;/QueryList&gt;</Subscription>
</EventTrigger>
</Triggers>
<Principals>
<Principal id="Author">
<UserId>S-1-5-18</UserId>
<RunLevel>HighestAvailable</RunLevel>
</Principal>
</Principals>
<Settings>
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
<ExecutionTimeLimit>PT3M</ExecutionTimeLimit>
<Priority>0</Priority>
</Settings>
<Actions Context="Author">
<Exec>
<Command>powershell.exe</Command>
<Arguments>-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "$($InstallRoot -replace '"','')\UserAgent.ps1" -SkipAutoUpdate</Arguments>
</Exec>
</Actions>
</Task>
"@
try {
$xml | Out-File -FilePath $xmlPath -Encoding Unicode -Force
Invoke-GeoNetSchtasks -ArgumentList @(
'/Create', '/TN', 'GeoNetAgent-Report-Shutdown', '/XML', $xmlPath, '/F'
) | Out-Null
}
finally {
if (Test-Path $xmlPath) { Remove-Item $xmlPath -Force -ErrorAction SilentlyContinue }
}
} }
function Set-GeoNetAgentConfig { function Set-GeoNetAgentConfig {

30
GeoNetAgent/agent/lib/Get-GpuInfo.ps1

@ -18,6 +18,34 @@ function Convert-GeoNetAdapterRamMb {
} }
} }
function Get-GeoNetGpuMemoryMbFromRegistry {
param([AllowNull()][string]$PnpDeviceId)
if (-not $PnpDeviceId) { return $null }
try {
$baseKey = 'HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}'
$subKeys = Get-ChildItem $baseKey -ErrorAction Stop | Where-Object { $_.PSChildName -match '^\d{4}$' }
foreach ($key in $subKeys) {
$matchId = (Get-ItemProperty -Path $key.PSPath -Name 'MatchingDeviceId' -ErrorAction SilentlyContinue).'MatchingDeviceId'
$pnpNorm = $PnpDeviceId.ToUpper()
$matchNorm = if ($matchId) { $matchId.ToUpper() } else { '' }
if ($matchNorm -and $pnpNorm.StartsWith($matchNorm.Split('&')[0])) {
$memBytes = (Get-ItemProperty -Path $key.PSPath -Name 'HardwareInformation.MemorySize' -ErrorAction SilentlyContinue).'HardwareInformation.MemorySize'
if ($memBytes -and $memBytes -gt 0) {
$mb = [math]::Round([decimal]$memBytes / 1MB, 0)
if ($mb -gt 0 -and $mb -le 131072) { return [int]$mb }
}
}
}
} catch {
}
return $null
}
function Convert-GeoNetDriverDateIso { function Convert-GeoNetDriverDateIso {
param($DriverDate) param($DriverDate)
@ -74,7 +102,7 @@ function Get-GeoNetGpuInfo {
video_processor = Convert-GeoNetOptionalString -Value $_.VideoProcessor video_processor = Convert-GeoNetOptionalString -Value $_.VideoProcessor
driver_version = Convert-GeoNetOptionalString -Value $_.DriverVersion driver_version = Convert-GeoNetOptionalString -Value $_.DriverVersion
driver_date = Convert-GeoNetDriverDateIso -DriverDate $_.DriverDate driver_date = Convert-GeoNetDriverDateIso -DriverDate $_.DriverDate
dedicated_memory_mb = Convert-GeoNetAdapterRamMb -AdapterRam $_.AdapterRAM dedicated_memory_mb = $(if ($pnp) { $reg = Get-GeoNetGpuMemoryMbFromRegistry -PnpDeviceId $pnp; if ($null -ne $reg) { $reg } else { Convert-GeoNetAdapterRamMb -AdapterRam $_.AdapterRAM } } else { Convert-GeoNetAdapterRamMb -AdapterRam $_.AdapterRAM })
pnp_device_id = $pnp pnp_device_id = $pnp
status = Convert-GeoNetOptionalString -Value $_.Status status = Convert-GeoNetOptionalString -Value $_.Status
current_resolution = $resolution current_resolution = $resolution

182
Workspace-Context.md

@ -0,0 +1,182 @@
# Workspace Context
## Purpose
Dokumen ini adalah **root context** untuk seluruh workspace.
Workspace ini memiliki satu atau lebih repository.
**Jangan melakukan analisis seluruh workspace sekaligus.**
Selalu identifikasi repository yang relevan, kemudian baca **`docs/context-index.md`** pada repository tersebut sebagai sumber informasi utama.
---
# Rules
1. Selalu baca `Workspace-Context.md` terlebih dahulu.
2. Tentukan repository yang relevan.
3. Baca hanya `docs/context-index.md` pada repository tersebut.
4. Ikuti seluruh referensi dokumentasi yang terdapat pada `context-index.md`.
5. Jangan membaca atau memodifikasi repository lain kecuali benar-benar dibutuhkan.
6. Jika pekerjaan melibatkan lebih dari satu repository, baca `docs/context-index.md` masing-masing repository yang terkait.
7. Jangan membuat asumsi mengenai arsitektur, API, database, server, maupun implementasi tanpa membaca dokumentasi repository terkait.
---
# Repository Ownership Policy
* Setiap repository memiliki dokumentasi, aturan, dan lifecycle masing-masing.
* Seluruh perubahan harus dilakukan pada repository yang benar.
* Jangan membuat dokumentasi lintas repository yang menyebabkan duplikasi informasi.
* Dokumentasi teknis harus tetap berada pada repository pemiliknya (Single Source of Truth).
---
# Cross Repository Policy
Apabila sebuah pekerjaan melibatkan lebih dari satu repository:
1. Identifikasi seluruh repository yang terdampak.
2. Baca `docs/context-index.md` dari setiap repository terkait.
3. Perbarui dokumentasi pada masing-masing repository yang mengalami perubahan.
4. Dokumentasikan dependency dan integrasi antar repository apabila terjadi perubahan.
5. Pastikan perubahan tetap kompatibel dengan repository lain.
---
# Knowledge Preservation Policy
Setiap informasi yang diperoleh AI Agent selama proses:
* Analisis
* Debugging
* Development
* Testing
* Deployment
* Production Support
* Incident Handling
* Performance Tuning
* Security Review
yang berpotensi berguna di masa depan harus didokumentasikan pada repository yang sesuai.
AI Agent **tidak boleh menyimpan konteks penting hanya di dalam percakapan**.
Seluruh pengetahuan yang relevan harus dipindahkan ke dokumentasi repository agar dapat digunakan kembali oleh developer maupun AI Agent lain.
---
# Documentation Policy (Mandatory)
Seluruh repository merupakan **Single Source of Truth** untuk proses development, deployment, operasi, dan maintenance.
Dokumentasi merupakan bagian dari source code dan memiliki prioritas yang sama dengan implementasi.
Setiap perubahan wajib didokumentasikan.
Minimal dokumentasi yang harus diperbarui apabila relevan:
* Feature Development
* Enhancement
* Bug Fix
* Known Issues
* Root Cause Analysis (RCA)
* Technical Debt
* TODO
* Next Action
* ADR (Architecture Decision Record)
* Changelog
* API Documentation
* Database Structure
* Database Migration
* Environment Configuration
* Infrastructure
* Server Information
* Deployment Guide
* CI/CD
* Monitoring
* Logging
* Dependency antar service
* Breaking Changes
* Security Notes
* Workaround
* Troubleshooting
* Performance Optimization
* Operational Notes
* Catatan penting lainnya yang mempengaruhi proses development maupun deployment.
---
# Documentation Quality Standard
Dokumentasi harus:
* Akurat
* Ringkas
* Mudah dipahami
* Selalu diperbarui
* Tidak duplikat
* Konsisten dengan implementasi kode
* Menggunakan Markdown
* Mengacu pada `docs/context-index.md`
Apabila dokumentasi sudah tersedia, lakukan pembaruan daripada membuat file baru.
---
# AI Agent Responsibilities
Apabila AI Agent melakukan perubahan pada repository, maka AI Agent wajib:
1. Memperbarui dokumentasi yang relevan.
2. Memperbarui `docs/context-index.md` apabila terdapat dokumen baru.
3. Memperbarui TODO apabila status berubah.
4. Memperbarui Issue apabila ditemukan bug baru.
5. Membuat ADR apabila terdapat keputusan arsitektur baru.
6. Memperbarui dokumentasi API apabila endpoint berubah.
7. Memperbarui dokumentasi database apabila schema berubah.
8. Memperbarui deployment guide apabila proses deployment berubah.
9. Memperbarui changelog untuk setiap perubahan penting.
10. Memastikan seluruh dokumentasi konsisten dengan implementasi kode.
11. Menandai dokumentasi yang sudah tidak berlaku sebagai **Deprecated**, bukan langsung menghapusnya.
---
# Commit Policy
Sebelum pekerjaan dianggap selesai:
* Review seluruh perubahan.
* Pastikan dokumentasi telah diperbarui.
* Pastikan tidak ada secret yang ikut ter-commit.
* Pastikan repository dapat dibangun dan dijalankan apabila memungkinkan.
* Buat commit yang jelas dan deskriptif.
* Jangan melakukan commit yang mencampurkan perubahan yang tidak berkaitan.
---
# Definition of Done (DoD)
Sebuah pekerjaan **belum dianggap selesai** apabila salah satu kondisi berikut masih ada:
* Dokumentasi belum diperbarui.
* TODO belum diperbarui.
* Issue belum diperbarui.
* API belum diperbarui.
* ADR belum dibuat (jika diperlukan).
* Changelog belum diperbarui.
* `docs/context-index.md` belum diperbarui apabila terdapat dokumen baru.
---
# Notes
* Struktur workspace bersifat dinamis.
* Repository dapat ditambah, dipindahkan, atau dihapus.
* Pastikan daftar repository pada dokumen ini selalu diperbarui.
* Seluruh dokumentasi teknis berada di masing-masing repository.
* `Workspace-Context.md` hanya berfungsi sebagai navigasi dan entry point.
* Selalu gunakan dokumentasi terbaru sebagai acuan sebelum melakukan analisis atau implementasi.
* Prioritaskan pembaruan dokumentasi yang sudah ada daripada membuat dokumentasi baru yang berpotensi duplikat.

91
anydesk.md

@ -0,0 +1,91 @@
# Backup AnyDesk
Catatan sebelum uninstall AnyDesk.
## File konfigurasi yang perlu di-backup
| Apa | Lokasi |
|-----|--------|
| ID AnyDesk komputer ini | `C:\ProgramData\AnyDesk\service.conf` |
| Alias (jika ada) | `C:\ProgramData\AnyDesk\service.conf` |
| Daftar ID remote yang pernah dikoneksi | `C:\Users\<username>\AppData\Roaming\AnyDesk\user.conf` |
Baris yang perlu disimpan:
- `ad.anynet.id=...`
- `ad.anynet.alias=...`
- `ad.roster.items=...`
- `ad.roster.favorites=...`
## Perintah untuk langsung mengekstrak ID ke file ini
Jalankan di PowerShell:
```powershell
$md = 'd:\Project\app on git\anydesk.md'
Add-Content $md "`n## AnyDesk ID Backup`n"
Get-Content "$env:ProgramData\AnyDesk\service.conf" |
Select-String "^(ad\.anynet\.id|ad\.anynet\.alias)=" |
Add-Content $md
Get-Content "$env:APPDATA\AnyDesk\user.conf" |
Select-String "^(ad\.roster\.items|ad\.roster\.favorites)=" |
Add-Content $md
```
## Catatan uninstall
- Saat uninstaller AnyDesk menanyakan apakah ingin menghapus **configuration files**, pilih **No/Tidak** supaya ID tetap tersimpan.
- Jika ingin uninstall bersih, salin file `service.conf` dan `user.conf` dulu, lalu restore setelah install ulang.
## History exsisting ID Anydesk
Recent Sessions yang terlihat
| Nama/Alias | AnyDesk ID | Status |
| ------------- | ------------- | ----------------------- |
| luwsrv03@ad | 1 315 522 618 | 🟢 Online |
| pc putih | 1 586 132 192 | 🔴 Offline |
| wabotpc | 782 089 407 | 🟢 Online |
| 768 397 903 | 768 397 903 | 🟢 Online |
| 196 357 002 | 196 357 002 | 🔴 Offline |
| 1 134 767 402 | 1 134 767 402 | 🔴 Offline |
| 150 878 707 | 150 878 707 | 🔴 Offline |
| 1 837 279 788 | 1 837 279 788 | 🔴 Offline |
| 753 011 914 | 753 011 914 | 🟢 Online |
| 1 940 821 188 | 1 940 821 188 | 🟢 Online (This Device) |
| wabot | 1 087 714 174 | 🔴 Offline |
| 1 843 863 429 | 1 843 863 429 | 🔴 Offline |
| 320 578 236 | 320 578 236 | 🟢 Online |
| turen | 1 919 377 233 | 🟢 Online |
| msi | 1 404 328 337 | 🟢 Online |
| 788 800 406 | 788 800 406 | 🔴 Offline |
| 1 442 565 617 | 1 442 565 617 | 🔴 Offline |
| 492 865 875 | 492 865 875 | 🟢 Online |
| 605 743 051 | 605 743 051 | 🔴 Offline |
| 259 511 811 | 259 511 811 | 🔴 Offline |
| 120 239 635 | 120 239 635 | 🔴 Offline |
| 1 508 728 214 | 1 508 728 214 | 🔴 Offline |
| 1 835 890 522 | 1 835 890 522 | 🔴 Offline |
| 1 029 425 656 | 1 029 425 656 | 🔴 Offline |
| 1 252 927 543 | 1 252 927 543 | 🔴 Offline |
| 1 477 009 444 | 1 477 009 444 | 🟢 Online |
| 690 848 779 | 690 848 779 | 🔴 Offline |
| 652 598 394 | 652 598 394 | 🔴 Offline |
| 1 960 924 256 | 1 960 924 256 | 🔴 Offline |
| 1 049 300 050 | 1 049 300 050 | 🔴 Offline |
| 931 977 962 | 931 977 962 | 🔴 Offline |
| 1 362 964 774 | 1 362 964 774 | 🔴 Offline |
| 1 029 198 007 | 1 029 198 007 | 🔴 Offline |
| 1 817 523 476 | 1 817 523 476 | 🔴 Offline |
| 1 057 176 301 | 1 057 176 301 | 🔴 Offline |
| 1 156 489 064 | 1 156 489 064 | 🔴 Offline |
| 970 927 367 | 970 927 367 | 🔴 Offline |
| 784 419 109 | 784 419 109 | 🔴 Offline |
| 483 865 345 | 483 865 345 | 🔴 Offline |
| 1 067 787 928 | 1 067 787 928 | 🔴 Offline |
| 1 878 652 573 | 1 878 652 573 | 🔴 Offline |
| 820 781 760 | 820 781 760 | 🔴 Offline |
| 848 924 578 | 848 924 578 | 🔴 Offline |
| 728 652 074 | 728 652 074 | 🔴 Offline |
| 709 551 067 | 709 551 067 | 🟢 Online |
| Windows | 116 079 452 | 🟢 Online (Discovered) |
| lenovo | 934 784 136 | 🟢 Online (Discovered) |
| Windows | 1 205 966 377 | 🟢 Online (Discovered) |

82
server-audit/deploy-log.md

@ -0,0 +1,82 @@
# Deploy Log
---
## 2026-07-01 — Feature Feedback Super Apps
### Frontend (my.gisportal.id)
- Buat `src/components/feedback/feedback-dialog.tsx` — dialog form laporan bug/saran
- Buat `src/components/feedback/feedback-button.tsx` — floating button di semua halaman
- Update `src/app/(app)/layout.tsx` — tambah `<FeedbackButton />`
- Deploy: `docker compose up -d --build` di `/opt/stacks/my-portal`
### Backend (console.gisportal.id)
- Buat `app/Models/Feedback.php`
- Buat `app/Http/Controllers/Api/V1/FeedbackController.php`
- Buat `database/migrations/2026_07_01_000001_create_feedback_table.php`
- Update `routes/api.php` — tambah 3 route: `POST/GET /feedback`, `PATCH /feedback/{id}`
- Deploy: `docker cp` ke container `geonet-console` + `php artisan migrate --force`
- Hasil: migration `create_feedback_table` ✅ Ran, route terdaftar ✅
### Catatan
- Container geonet-console tidak mount source dari host (pakai image) → wajib `docker cp` untuk perubahan tanpa rebuild
- Untuk permanen: rebuild image
---
## 2026-07-01 — SSH Key Setup ArcGIS Servers
### Server 10.100.1.52 (DEV13 — ArcGIS Data Store Object)
- Install OpenSSH key via `ssh -t` dengan password sekali
- Public key: `id_ed25519_geonet_proxmox``C:\ProgramData\ssh\administrators_authorized_keys`
- SSH config alias: `dev13-object`
- Hasil: `ssh dev13-object` ✅ tanpa password
### Server 10.100.1.51 (DEV11 — ArcGIS Base Development)
- Install OpenSSH key via `ssh -t` dengan password sekali
- Public key: `id_ed25519_geonet_proxmox``C:\ProgramData\ssh\administrators_authorized_keys`
- SSH config alias: `dev51-base`
- Hasil: `ssh dev51-base` ✅ tanpa password
---
## 2026-07-01 — Disk Cleanup Server 10.100.1.24
- Kondisi sebelum: disk 98% (1.3 GB free / 50 GB)
- Root cause: Docker build cache 45.29 GB menumpuk
- Tindakan: `docker builder prune -af`
- Freed: 45.29 GB
- Kondisi sesudah: disk 50% (26 GB free)
- Dangling images: ~7.9 GB masih ada (aman untuk dibersihkan kapan saja)
---
## 2026-06-30 — USB Passthrough VM 103 (Opr Proxmox 10.100.1.102)
- Flashdisk Cruzer Blade terdeteksi di `usb 1-1.4` (idVendor=0781, idProduct=5567)
- Controller PCI: `0000:00:1a.0` (EHCI USB 2.0)
- Hapus passthrough per-device: `qm set 103 --delete usb1`
- Tambah passthrough controller: `qm set 103 -hostpci0 00:1a.0`
- VM 103 restart → `hostpci0: 00:1a.0` aktif
- Hasil: semua device USB yang dicolok ke bus 1 auto terdeteksi di VM 103
---
## 2026-06-30 — SSH Key Proxmox
### Server 10.100.1.101 & 10.100.1.102
- Key: `~/.ssh/id_ed25519_geonet_proxmox`
- SSH config alias: `proxmox` (10.100.1.101), `proxmox-opr` (10.100.1.102)
- Hasil: `ssh root@10.100.1.101` dan `ssh root@10.100.1.102` ✅ tanpa password
---
## 2026-06-29 — GeoNetAgent v0.1.12-phase0
### Perubahan
- Tambah scheduled task `GeoNetAgent-Report-Shutdown` (Event ID 1074)
- Buat `GeoNetAgent-GPO-Shutdown.cmd` dan `GeoNetAgent-GPO-Shutdown.ps1`
- Update `GeoNetAgent-GPO-Startup.ps1` — cek task shutdown saat startup
- Bump VERSION → `0.1.12-phase0`
- Update `manifest.json` dan `index.html` (version badge dinamis)
- Deploy ke `https://agent.gisportal.id/install/`

77
server-audit/host.md

@ -1,27 +1,67 @@
Mikrotik : # Mikrotik :
ssh -m hmac-sha1 admin@10.100.1.1 -p 255 ssh -m hmac-sha1 admin@10.100.1.1 -p 255
ssh -i "$env:USERPROFILE\.ssh\id_rsa_mikrotik" -o IdentitiesOnly=yes -m hmac-sha1 -p 255 admin@10.100.1.1 ssh -i "$env:USERPROFILE\.ssh\id_rsa_mikrotik" -o IdentitiesOnly=yes -m hmac-sha1 -p 255 admin@10.100.1.1
ssh -m hmac-sha1 admin@192.168.0.1 -p 255 ssh -m hmac-sha1 admin@192.168.0.1 -p 255
ssh -i "$env:USERPROFILE\.ssh\id_rsa_mikrotik" -o IdentitiesOnly=yes -m hmac-sha1 -p 255 admin@192.168.0.1 ssh -i "$env:USERPROFILE\.ssh\id_rsa_mikrotik" -o IdentitiesOnly=yes -m hmac-sha1 -p 255 admin@192.168.0.1
# Opr Proxmox
ssh root@10.100.1.102
Reverse_proxy: ## USB passthrough VM 103 (monitoring)
- USB bus 1 (controller PCI `0000:00:1a.0`) dipassthrough ke VM 103
- Tujuan: flashdisk/storage docking auto terdeteksi di VM 103
- Waktu setup: 30 Jun 2026
- Perintah setup:
```bash
ssh root@10.100.1.102
qm stop 103
qm set 103 --delete usb1
qm set 103 -hostpci0 00:1a.0
qm start 103
```
- **Rollback** (kembalikan USB bus 1 ke Proxmox):
```bash
ssh root@10.100.1.102
qm stop 103
qm set 103 --delete hostpci0
qm start 103
```
- Catatan: USB 2.0 saja (EHCI). Host kehilangan akses ke port USB bus 1.
# Dev Proxmox
ssh root@10.100.1.101
# Reverse_proxy (App Server):
ssh -m hmac-sha1 root@10.100.1.24 -p 22 ssh -m hmac-sha1 root@10.100.1.24 -p 22
Main_Database: ## Disk Cleanup — 1 Jul 2026
- Sebelum: disk 98% penuh (1.3 GB free / 50 GB)
- Penyebab: Docker build cache menumpuk 45.29 GB
- Tindakan: `docker builder prune -af` → hapus 45.29 GB
- Hasil: disk 50% (26 GB free)
- Sisa reclaimable: ~7.9 GB dangling images
## Rencana Deploy Zammad (ticketing)
- Target domain: `ticket.gisportal.id`
- Stack: `/opt/stacks/zammad`
- Resource dibutuhkan: ~2–3 GB RAM, ~15 GB disk
- Status: ✅ Resource tersedia, siap deploy
- Integrasi: feedback dari Super Apps → auto-create tiket Zammad
# Main_Database:
ssh -m hmac-sha1 root@10.100.1.25 -p 22 ssh -m hmac-sha1 root@10.100.1.25 -p 22
Main_Webserver: # Main_Webserver:
ssh -m hmac-sha1 Administrator@10.100.1.41 -p 22 ssh -m hmac-sha1 Administrator@10.100.1.41 -p 22
AD-LDAP: # AD-LDAP:
ssh -m hmac-sha1 Administrator@10.100.1.40 -p 22 ssh -m hmac-sha1 Administrator@10.100.1.40 -p 22
zabbix: # zabbix:
ssh -m hmac-sha1 root@10.100.1.42 -p 22 ssh -m hmac-sha1 root@10.100.1.42 -p 22
Ollama (aiopr, Ubuntu 22.04) — POC & inference CPU: # Ollama (aiopr, Ubuntu 22.04) — POC & inference CPU:
ssh ollama ssh ollama
.\scripts\ssh-ollama.ps1 .\scripts\ssh-ollama.ps1
ssh -i "$env:USERPROFILE\.ssh\id_rsa_geonet_laptop" -o IdentitiesOnly=yes -m hmac-sha1 root@10.100.1.26 -p 22 ssh -i "$env:USERPROFILE\.ssh\id_rsa_geonet_laptop" -o IdentitiesOnly=yes -m hmac-sha1 root@10.100.1.26 -p 22
@ -34,4 +74,27 @@ Ollama (aiopr, Ubuntu 22.04) — POC & inference CPU:
.\scripts\ssh-qnap.ps1 .\scripts\ssh-qnap.ps1
ssh -m hmac-sha1 admin@10.100.1.10 -p 22 ssh -m hmac-sha1 admin@10.100.1.10 -p 22
# ArcGIS Enterprise
## ArcGIS Base Development
- IIS
- ArcGIS Web Adaptor
- Portal for ArcGIS
- ArcGIS Server
- ArcGIS Data Store (Relational)
ssh dev51-base
ssh Administrator@10.100.1.51 -p 22
## ArcGIS Data Store (Object)
- ArcGIS Data Store (Object)
ssh dev13-object
ssh Administrator@10.100.1.52 -p 22
## Key setup (Windows OpenSSH)
- Key: `~/.ssh/id_ed25519_geonet_proxmox`
- Public key ditambahkan ke `C:\ProgramData\ssh\administrators_authorized_keys` di masing-masing server
- Perintah setup (jalankan sekali per server):
```powershell
ssh -t Administrator@10.100.1.51 -p 22 "powershell -Command `"Add-Content -Path 'C:\ProgramData\ssh\administrators_authorized_keys' -Value 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDxlXfuXAb/A2YCji3XWckW37Hs7SP/yen2CQwgV5M4x proxmox-geonet'; icacls C:\ProgramData\ssh\administrators_authorized_keys /inheritance:r; icacls C:\ProgramData\ssh\administrators_authorized_keys /grant SYSTEM:F; icacls C:\ProgramData\ssh\administrators_authorized_keys /grant Administrators:F; Restart-Service sshd`""
```

99
server-connection/geonet-console/app/Http/Controllers/Api/V1/FeedbackController.php

@ -0,0 +1,99 @@
<?php
namespace App\Http\Controllers\Api\V1;
use App\Http\Controllers\Api\Concerns\RespondsWithJson;
use App\Http\Controllers\Controller;
use App\Models\Feedback;
use App\Support\ApiActor;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
class FeedbackController extends Controller
{
use RespondsWithJson;
public function store(Request $request): JsonResponse
{
/** @var ApiActor $actor */
$actor = $request->attributes->get('api_actor');
$validated = $request->validate([
'type' => ['required', Rule::in(['bug', 'suggestion', 'other'])],
'title' => ['required', 'string', 'max:120'],
'message' => ['required', 'string', 'max:5000'],
'context' => ['sometimes', 'array'],
'context.page' => ['sometimes', 'string', 'max:500'],
'context.url' => ['sometimes', 'string', 'max:1000'],
'context.user_agent' => ['sometimes', 'string', 'max:500'],
'context.reporter' => ['sometimes', 'string', 'max:100'],
'context.reporter_name' => ['sometimes', 'string', 'max:200'],
'context.reported_at' => ['sometimes', 'string'],
]);
$ctx = $validated['context'] ?? [];
$feedback = Feedback::create([
'type' => $validated['type'],
'title' => $validated['title'],
'message' => $validated['message'],
'status' => 'open',
'reporter' => $ctx['reporter'] ?? ($actor?->identifier ?? null),
'reporter_name' => $ctx['reporter_name'] ?? null,
'page' => $ctx['page'] ?? null,
'url' => $ctx['url'] ?? null,
'user_agent' => $ctx['user_agent'] ?? null,
'reported_at' => isset($ctx['reported_at']) ? now()->parse($ctx['reported_at']) : now(),
]);
return $this->ok([
'id' => $feedback->id,
'status' => $feedback->status,
], [], 201);
}
public function index(Request $request): JsonResponse
{
/** @var ApiActor $actor */
$actor = $request->attributes->get('api_actor');
if (!$actor?->is_admin) {
return $this->fail('Forbidden', 'forbidden', 403);
}
$query = Feedback::query()->orderByDesc('created_at');
if ($request->filled('type')) {
$query->where('type', $request->query('type'));
}
if ($request->filled('status')) {
$query->where('status', $request->query('status'));
}
$items = $query->paginate(20);
return $this->ok($items);
}
public function update(Request $request, int $id): JsonResponse
{
/** @var ApiActor $actor */
$actor = $request->attributes->get('api_actor');
if (!$actor?->is_admin) {
return $this->fail('Forbidden', 'forbidden', 403);
}
$feedback = Feedback::findOrFail($id);
$validated = $request->validate([
'status' => ['sometimes', Rule::in(['open', 'in_progress', 'resolved', 'closed'])],
'admin_note' => ['sometimes', 'nullable', 'string', 'max:2000'],
]);
$feedback->update($validated);
return $this->ok($feedback->only(['id', 'status', 'admin_note']));
}
}

26
server-connection/geonet-console/app/Models/Feedback.php

@ -0,0 +1,26 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Feedback extends Model
{
protected $fillable = [
'type',
'title',
'message',
'status',
'reporter',
'reporter_name',
'page',
'url',
'user_agent',
'reported_at',
'admin_note',
];
protected $casts = [
'reported_at' => 'datetime',
];
}

32
server-connection/geonet-console/database/migrations/2026_07_01_000001_create_feedback_table.php

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('feedback', function (Blueprint $table) {
$table->id();
$table->string('type', 20)->default('bug');
$table->string('title', 120);
$table->text('message');
$table->string('status', 20)->default('open');
$table->string('reporter', 100)->nullable();
$table->string('reporter_name', 200)->nullable();
$table->string('page', 500)->nullable();
$table->string('url', 1000)->nullable();
$table->string('user_agent', 500)->nullable();
$table->timestamp('reported_at')->nullable();
$table->text('admin_note')->nullable();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('feedback');
}
};

10
server-connection/geonet-console/routes/api.php

@ -2,6 +2,7 @@
use App\Http\Controllers\Api\V1\AuthController; use App\Http\Controllers\Api\V1\AuthController;
use App\Http\Controllers\Api\V1\DatabaseController; use App\Http\Controllers\Api\V1\DatabaseController;
use App\Http\Controllers\Api\V1\FeedbackController;
use App\Http\Controllers\Api\V1\LdapGroupController; use App\Http\Controllers\Api\V1\LdapGroupController;
use App\Http\Controllers\Api\V1\LdapUserController; use App\Http\Controllers\Api\V1\LdapUserController;
use App\Http\Controllers\Api\V1\MeController; use App\Http\Controllers\Api\V1\MeController;
@ -98,5 +99,14 @@ Route::prefix('v1')->group(function () {
->name('api.v1.service-tokens.active'); ->name('api.v1.service-tokens.active');
Route::get('/service-tokens/expired', [ServiceTokenController::class, 'getExpired']) Route::get('/service-tokens/expired', [ServiceTokenController::class, 'getExpired'])
->name('api.v1.service-tokens.expired'); ->name('api.v1.service-tokens.expired');
// Feedback — bug reports & suggestions
Route::post('/feedback', [FeedbackController::class, 'store'])
->middleware('throttle:30,1')
->name('api.v1.feedback.store');
Route::get('/feedback', [FeedbackController::class, 'index'])
->name('api.v1.feedback.index');
Route::patch('/feedback/{id}', [FeedbackController::class, 'update'])
->name('api.v1.feedback.update');
}); });
}); });

Loading…
Cancel
Save