# WorkPulse — Backend (Go / Gin) & API `apiwork.rycroftapparel.com`

Dokumen ini menjadi **sumber acuan implementasi BE** untuk frontend WorkPulse (`work.rycroftapparel.com`, repo `wp-system/FE`). Tujuannya: stack **Golang + Gin**, **API terpisah di subdomain sendiri**, **koneksi database aman tanpa mengganggu schema/database lain**, **realtime penuh**, serta **deploy Apache + TLS** yang **tidak menyentuh vhost/proyek lain** kecuali penambahan file konfigurasi baru.

---

## 1. Ringkasan arsitektur

| Komponen | Nilai |
|----------|--------|
| Frontend | Nuxt 3 (WorkPulse), domain publik: `https://work.rycroftapparel.com` |
| Backend API | **Go + Gin**, di-balik Apache reverse proxy |
| Domain API | `https://apiwork.rycroftapparel.com` (hanya `ServerName` ini; tidak memetakan ulang domain lain) |
| Realtime | **WebSocket** (utama) + opsional **SSE** untuk feed satu arah; skala horizontal memakai **Redis Pub/Sub** (disarankan) |
| Proses | **PM2** atau **systemd** — binary listen **`127.0.0.1:<PORT_INTERNAL>`** saja (tidak bind `0.0.0.0` publik) |
| Database | **Satu database MySQL/MariaDB dedikasi** (mis. `workpulse`) + **user SQL dedikasi** dengan hak **hanya** pada database itu |

Alur request: **Browser → Apache :443 (TLS per-domain) → `127.0.0.1:PORT` → Gin** — sama pola dengan FE WorkPulse ke Nitro.

---

## 2. Prinsip aman untuk database (wajib)

Tujuan: **tidak merusak atau mengubah data aplikasi lain** yang memakai instance MySQL yang sama.

1. **Database dedikasi**  
   Buat database baru, contoh: `workpulse`. Semua migration dan query BE **hanya** ke database ini.

2. **User SQL dedikasi**  
   ```sql
   CREATE USER 'workpulse_api'@'localhost' IDENTIFIED BY '***password_kuat***';
   GRANT ALL PRIVILEGES ON workpulse.* TO 'workpulse_api'@'localhost';
   FLUSH PRIVILEGES;
   ```  
   **Jangan** `GRANT ... ON *.*` atau `ON mysql.*`. **Jangan** pakai user root aplikasi lain.

3. **DSN di `.env` (tidak di-commit)**  
   Contoh:  
   `WORKPULSE_DATABASE_DSN=workpulse_api:***@tcp(127.0.0.1:3306)/workpulse?parseTime=true&loc=UTC&multiStatements=false`  
   Pakai `sql.Open` + connection pool; timeout query; prepared statement untuk query dinamis.

4. **Migration**  
   Tool: **golang-migrate**, **goose**, atau **Atlas** — **hanya** target `workpulse`. Jangan jalankan migration terhadap nama database kosong / default server.

5. **Backup sebelum migration besar**  
   `mysqldump workpulse ...` — scope eksplisit ke database `workpulse` saja.

---

## 3. Stack teknis backend

- **Go** (disarankan 1.22+)
- **Gin** — HTTP router + middleware (recovery, logger, CORS, JWT)
- **Driver DB**: `github.com/go-sql-driver/mysql` atau `github.com/glebarez/go-sqlite` jika nanti dev lokal; **production = MySQL sesuai server**
- **Realtime WebSocket**: `github.com/gorilla/websocket` (umum + stabil dengan Gin)
- **Redis** (opsional tapi disarankan untuk realtime multi-instance): `github.com/redis/go-redis/v9` — channel pub/sub per ruang (team, user, global)
- **Auth**: JWT access + refresh (httpOnly cookie atau header `Authorization`); OAuth Google terpisah endpoint callback
- **Validasi**: `github.com/go-playground/validator/v10`

Struktur modul disarankan (contoh):

```text
cmd/api/main.go
internal/
  config/
  http/router.go        // Gin routes + /ws
  http/middleware/
  auth/
  domain/report/        // repository + service + handler
  domain/user/
  domain/notification/
  realtime/hub.go       // WebSocket hub + Redis adapter
  db/migrations/
```

---

## 4. Kontrak API (REST) — selaras WorkPulse FE

FE saat ini masih **mock / localStorage** (`composables/useAuth.ts`). BE harus menyediakan endpoint berikut (prefix disarankan **`/api/v1`**):

### Auth & sesi

| Method | Path | Keterangan |
|--------|------|------------|
| `POST` | `/api/v1/auth/login` | email + password → access + refresh token |
| `POST` | `/api/v1/auth/register` | registrasi internal (jika dipakai) |
| `POST` | `/api/v1/auth/refresh` | refresh token |
| `POST` | `/api/v1/auth/logout` | invalidate refresh |
| `GET` | `/api/v1/auth/me` | profil singkat user login |
| `GET` | `/api/v1/auth/google` | URL redirect OAuth Google |
| `GET` | `/api/v1/auth/google/callback` | callback OAuth |

### Domain bisnis (sesuai halaman FE: dashboard, team, report, calendar, notifications, analytics, settings, profile)

| Area | Contoh endpoint |
|------|-----------------|
| Reports | `GET/POST /api/v1/reports`, `GET/PATCH /api/v1/reports/:id`, filter by date/division |
| Team | `GET /api/v1/teams`, `GET /api/v1/teams/:id/members`, metrics |
| Calendar | `GET /api/v1/calendar/events` (query range) |
| Notifications | `GET /api/v1/notifications`, `PATCH .../read` |
| Analytics | `GET /api/v1/analytics/summary`, `GET /api/v1/analytics/trends` |
| Settings | `GET/PATCH /api/v1/settings` (scoped per user atau per org) |
| Profile | `GET/PATCH /api/v1/profile` |

Semua respons JSON konsisten, misalnya:

```json
{ "ok": true, "data": { }, "error": null }
```

### CORS

Izinkan origin: `https://work.rycroftapparel.com` (dan `http://localhost:3000` untuk dev). Credentials jika pakai cookie.

---

## 5. Realtime penuh (wajib dirancang dari awal)

### 5.1 WebSocket

- **URL**: `wss://apiwork.rycroftapparel.com/api/v1/ws` (atau `/ws/v1` — tetap konsisten di FE)
- **Auth**: token lewat query `?access_token=...` (TTL pendek) **atau** subprotocol / first message JSON `{"type":"auth","token":"..."}` — hindari token panjang di log Apache; prefer **singkat-lived ticket**: `POST /api/v1/ws/ticket` → one-time ticket → upgrade WS.

**Event server → klien (contoh):**

- `report.created`, `report.updated`, `report.submitted`
- `notification.new`
- `team.presence` (online/offline ringkas)
- `dashboard.stats_refresh` (invalidate cache ringan)

**Event klien → server (contoh):**

- `ping` / `pong` keepalive
- `subscribe` `{ "channel": "team:123" }` dengan validasi membership

### 5.2 Redis (disarankan jika >1 replika API)

- Setiap instance Gin subscribe Redis channel `workpulse:team:{id}`, `workpulse:user:{id}`.
- Saat REST mengubah data, publish ke channel → semua instance mendorong ke WebSocket klien yang terhubung.

### 5.3 SSE (opsional)

- `GET /api/v1/stream/notifications` — `Content-Type: text/event-stream` untuk feed notifikasi tanpa duplex.

### 5.4 Apache & WebSocket

Pastikan modul **`proxy_wstunnel`** aktif (di server ini biasanya sudah). Contoh inti:

```apache
RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule ^/api/v1/ws(.*)$ ws://127.0.0.1:PORT_INTERNAL/api/v1/ws$1 [P,L]
ProxyPass / http://127.0.0.1:PORT_INTERNAL/
ProxyPassReverse / http://127.0.0.1:PORT_INTERNAL/
```

Sesuaikan path dengan path WS final di Gin. **Jangan** mengubah blok `ProxyPass` domain lain.

---

## 6. Deploy domain `apiwork.rycroftapparel.com` (Apache + TLS) — terisolasi

### 6.1 DNS

Rekam **A** (dan **AAAA** jika ada) `apiwork.rycroftapparel.com` → IP server yang sama dengan vhost lain (sudah pola untuk `work.*`).

### 6.2 Port internal

Pilih port **unik** yang belum dipakai aplikasi lain, contoh: **`3040`**. Verifikasi:

```bash
ss -tlnp | grep 3040
```

Gin: `router.Run("127.0.0.1:3040")` atau set dari env `WORKPULSE_API_ADDR=127.0.0.1:3040`.

### 6.3 File Apache (hanya file baru)

Simpan template di repo BE (nanti), contoh path mirror FE:

- `deploy/apache/apiwork.rycroftapparel.com.conf` — `:80` dengan pengecualian `/.well-known/acme-challenge/` lalu redirect ke HTTPS (sama pola dengan `work.rycroftapparel.com`).
- `deploy/apache/apiwork.rycroftapparel.com-ssl.conf` — `:443`, `ServerName apiwork.rycroftapparel.com`, `ProxyPass` ke `http://127.0.0.1:3040/`, **WebSocket** seperti pasal 5.4, log terpisah:  
  `apiwork.rycroftapparel.com_error.log`, `apiwork.rycroftapparel.com_ssl_access.log`.

**Tidak mengedit** file `sites-enabled` domain lain — hanya:

```bash
sudo cp deploy/apache/apiwork.rycroftapparel.com.conf /etc/apache2/sites-available/
sudo cp deploy/apache/apiwork.rycroftapparel.com-ssl.conf /etc/apache2/sites-available/
sudo a2ensite apiwork.rycroftapparel.com.conf
# SSL: setelah cert ada
sudo a2ensite apiwork.rycroftapparel.com-ssl.conf
sudo apache2ctl configtest && sudo systemctl reload apache2
```

### 6.4 TLS (Let’s Encrypt, khusus subdomain ini)

Sertifikat tersimpan di **`/etc/letsencrypt/live/apiwork.rycroftapparel.com/`** — **folder terpisah** dari `work.*` dan domain lain; tidak ada “campur” isi file cert.

```bash
sudo certbot certonly --webroot -w /var/www/html/wp-system/BE/deploy/acme-challenge \
  -d apiwork.rycroftapparel.com --non-interactive --agree-tos -m info@rycroftapparel.com
```

Lalu isi `SSLCertificateFile` / `SSLCertificateKeyFile` di vhost SSL dari path Let’s Encrypt di atas + `Include /etc/letsencrypt/options-ssl-apache.conf`.

---

## 7. PM2 (contoh)

`ecosystem.config.cjs` di root repo BE:

```javascript
module.exports = {
  apps: [
    {
      name: "workpulse-api",
      cwd: "/var/www/html/wp-system/BE",
      script: "./bin/api",
      interpreter: "none",
      env: {
        WORKPULSE_API_ADDR: "127.0.0.1:3040",
        WORKPULSE_DATABASE_DSN: "workpulse_api:***@tcp(127.0.0.1:3306)/workpulse?parseTime=true&loc=UTC",
        WORKPULSE_REDIS_ADDR: "127.0.0.1:6379",
        WORKPULSE_JWT_SECRET: "***",
        GIN_MODE: "release"
      }
    }
  ]
};
```

Build: `go build -o bin/api ./cmd/api` lalu `pm2 start ecosystem.config.cjs` dan **`pm2 save`** dengan `PM2_HOME=/root/.pm2` bila sesuai server.

---

## 8. Integrasi FE (Nuxt)

- Tambah `runtimeConfig.public.apiBase = 'https://apiwork.rycroftapparel.com'` (atau env `NUXT_PUBLIC_API_BASE`).
- Ganti `useAuth` menjadi `fetch` ke `/api/v1/auth/*`; simpan token aman (httpOnly cookie diset server idealnya).
- Client WebSocket: `new WebSocket(...)` ke `wss://apiwork.rycroftapparel.com/...` dengan ticket auth.

---

## 9. Checklist “tidak merusak yang lain”

- [ ] Tidak mengubah `sites-enabled` vhost domain lain; hanya **tambah** symlink `apiwork.*`.
- [ ] Tidak memakai user MySQL root / user milik app lain.
- [ ] Tidak menjalankan migration tanpa nama database eksplisit `workpulse`.
- [ ] Bind API **hanya** `127.0.0.1` + Apache reverse proxy.
- [ ] `apache2ctl configtest` sebelum setiap `reload`.
- [ ] Log error API terpisah per domain (mudah debug tanpa tabrak log app lain).

---

## 10. Langkah implementasi berurutan (untuk tim dev)

1. Buat repo `/var/www/html/wp-system/BE` (folder baru), init Go module `github.com/rycroftapparel/workpulse-api` (atau nama internal perusahaan).
2. Skema DB + migration awal (`users`, `sessions`, `reports`, `notifications`, dll.).
3. Gin: health `GET /healthz`, REST `/api/v1/...`, middleware JWT + CORS.
4. Hub WebSocket + integrasi Redis (boleh fase 2 jika single node dulu).
5. Build binary, PM2, Apache HTTP + certbot + SSL vhost.
6. Hubungkan FE dengan `NUXT_PUBLIC_API_BASE`.

---

## 11. Referensi cepat domain & repo

| Aset | Lokasi / nilai |
|------|----------------|
| FE | `/var/www/html/wp-system/FE` |
| BE (direncanakan) | `/var/www/html/wp-system/BE` |
| Dokumen ini | `/var/www/html/wp-system/docs/BE_WORKPULSE_GIN_REALTIME.md` |
| API publik | `https://apiwork.rycroftapparel.com` |
| ACME webroot (contoh) | `/var/www/html/wp-system/BE/deploy/acme-challenge` (buat mirror struktur FE jika perlu) |

---

*Dokumen ini dapat diperbarui seiring penambahan endpoint dan kebijakan auth perusahaan.*
