# Panduan Deploy Signal-Learn ke my.php.id

**Domain:** `signal-learn.web.id`
**Hosting:** my.php.id (Node.js + MySQL)
**Stack:** TanStack Start (React 19) · Vite · Drizzle ORM · MySQL

Panduan ini menjelaskan langkah demi langkah cara men-deploy aplikasi Signal-Learn ke hosting my.php.id dengan domain `signal-learn.web.id`. Ikuti urutannya; setiap langkah memiliki checklist yang bisa dicentang.

---

## 1. Ringkasan Arsitektur

```
Browser ── HTTPS ──▶ Node.js App (server.mjs)
                        │  TanStack Start (fetch handler)
                        ├──▶ Static assets  → dist/client
                        └──▶ MySQL (my.php.id) → signal_learn
```

Hasil build (`bun run build`) menghasilkan dua folder:

| Folder | Isi | Fungsi |
|--------|-----|--------|
| `dist/server/server.js` | Bundle server (fetch handler) | Menangani request dinamis (SSR, API) |
| `dist/client` | HTML, JS, CSS hasil bundling | Asset statis frontend |

Server dijalankan melalui sebuah *wrapper* kecil (`server.mjs`) yang membungkus `fetch` handler menjadi HTTP server Node biasa, agar bisa mendengarkan `PORT` yang disediakan hosting.

---

## 2. Checklist Prasyarat

Sebelum mulai, pastikan semua ini sudah siap:

- [ ] Akses ke panel my.php.id (login akun hosting)
- [ ] Paket hosting my.php.id yang **mendukung Node.js** (cek apakah ada menu "Node.js App", "Application Manager", atau akses SSH/VPS)
- [ ] Database MySQL sudah dibuat di my.php.id (catat host, user, password, nama database)
- [ ] Domain `signal-learn.web.id` sudah dimiliki/diarahkan ke my.php.id
- [ ] Kredensial Google OAuth (Client ID & Secret) — bisa dibuat di [Google Cloud Console](https://console.cloud.google.com/apis/credentials)
- [ ] `Node.js` versi **20 LTS atau lebih baru** (Vite 8 butuh Node 20+)
- [ ] Kode sumber sudah di-push ke Git (GitHub/GitLab) atau siap di-upload

> Jika hosting my.php.id **hanya** mendukung PHP/static (tidak ada Node.js), aplikasi ini **tidak bisa** berjalan karena butuh server Node untuk SSR & API. Solusinya: gunakan paket Node.js mereka, atau sewa VPS. Lihat bagian *Troubleshooting*.

---

## 3. Checklist Pra-Deploy (Lokal)

Lakukan di komputer lokal/CI sebelum upload:

- [ ] `bun install` (atau `npm install`) berhasil
- [ ] `bun run build` menghasilkan folder `dist/`
- [ ] Environment variables produksi sudah disiapkan (lihat Langkah 2)
- [ ] Skema database siap di-push (lihat Langkah 4)
- [ ] Tidak ada file `.env`/`*.local` yang ikut ter-commit ke Git (sudah ada di `.gitignore`)

---

## 4. Langkah demi Langkah

### Langkah 1 — Siapkan environment variables produksi

Buat file `.env` (atau isikan via panel Environment Variables hosting) dengan nilai produksi. **Penting:** variabel berawalan `VITE_` di-inline saat build, jadi harus sudah ada **sebelum** `bun run build` dijalankan.

```dotenv
# --- Database MySQL (dari my.php.id) ---
DB_HOST=sql.my.php.id        # ganti dengan host MySQL dari panel my.php.id
DB_USER=signallearn_user     # ganti
DB_PASSWORD=password_mysql   # ganti
DB_NAME=signal_learn         # ganti
DB_PORT=3306

# --- Keamanan ---
JWT_SECRET=GANTI_DENGAN_STRING_ACAK_PANJANG

# --- Google OAuth (produksi) ---
GOOGLE_CLIENT_ID=xxxx.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=xxxx
GOOGLE_REDIRECT_URI=https://signal-learn.web.id/api/auth/google
VITE_GOOGLE_CLIENT_ID=xxxx.apps.googleusercontent.com
VITE_GOOGLE_REDIRECT_URI=https://signal-learn.web.id/api/auth/google
```

> `JWT_SECRET` sebaiknya string acak panjang, mis. hasil `openssl rand -hex 32`.

---

### Langkah 2 — Build aplikasi (dengan env produksi aktif)

Pastikan `.env` Langkah 1 sudah tersedia di environment, lalu build:

```bash
# menggunakan Bun (sesuai setup lokal)
bun install
bun run build

# atau menggunakan npm
npm install
npm run build
```

Hasilnya: folder `dist/server` dan `dist/client`.

---

### Langkah 3 — Buat wrapper server (`server.mjs`)

TanStack Start menghasilkan server berbasis Web `fetch`. Hosting Node butuh server yang mendengarkan `PORT`. Buat file `server.mjs` di **root** project dengan isi berikut (sudah diuji):

```js
import { createServer } from 'node:http'
import { Readable } from 'node:stream'
import start from './dist/server/server.js'

const port = process.env.PORT || 3000
createServer(async (req, res) => {
  const host = req.headers.host || `localhost:${port}`
  const url = `http://${host}${req.url}`
  const body =
    req.method === 'GET' || req.method === 'HEAD'
      ? undefined
      : Readable.toWeb(req)

  const request = new Request(url, {
    method: req.method,
    headers: req.headers,
    body,
    duplex: 'half',
  })

  const response = await start.fetch(request)
  res.statusCode = response.status
  response.headers.forEach((v, k) => res.setHeader(k, v))

  if (response.body) {
    Readable.fromWeb(response.body).pipe(res)
  } else {
    res.end()
  }
}).listen(port, () => console.log('Signal-Learn listening on', port))
```

Tambahkan script start agar mudah dijalankan (edit `package.json` → `scripts`):

```json
"start": "node server.mjs"
```

---

### Langkah 4 — Upload & pasang dependencies di hosting

**Opsi A — Panel Node.js App (CloudLinux / Application Manager):**
1. Buat aplikasi Node.js baru di panel.
2. **Application root** → arahkan ke folder project.
3. **Application startup file** → `server.mjs`
4. **Node version** → pilih 20 LTS atau lebih baru.
5. Upload seluruh isi project (kecuali `node_modules`, `dist` bisa dibuild di server).
6. Jalankan `npm install --omit=dev` (atau `bun install --production`) lewat terminal panel.
7. Set **Environment Variables** (Langkah 1) di panel.
8. Start aplikasi.

**Opsi B — SSH / VPS:**
```bash
# di server, di folder project
npm install --omit=dev        # atau: bun install --production
npm run build                 # pastikan env produksi sudah loaded
npm start                     # menjalankan node server.mjs
```
Agar tetap jalan setelah logout, jalankan dengan PM2:
```bash
npm i -g pm2
pm2 start server.mjs --name signal-learn
pm2 save
pm2 startup                   # ikuti instruksi agar auto-start saat boot
```

---

### Langkah 5 — Setup database MySQL & migrasi

1. Di panel my.php.id, buat database MySQL (mis. `signal_learn`) dan user-nya. Catat host/user/password.
2. Isikan ke environment (Langkah 1: `DB_HOST`, `DB_USER`, `DB_PASSWORD`, `DB_NAME`).
3. Buat tabel sesuai skema Drizzle:

```bash
# cara paling cepat: push skema langsung ke DB produksi
bun run db:push
# atau (npm): npm run db:push
```

> `db:push` membaca `src/server/db/schema.ts` dan menyelaraskan struktur tabel ke MySQL. Untuk kontrol versi migrasi, gunakan `bun run db:generate` lalu `bun run db:migrate`.

---

### Langkah 6 — Konfigurasi domain `signal-learn.web.id` + SSL

1. **Arahkan domain ke hosting:**
   - Jika `signal-learn.web.id` dibeli di my.php.id → cukup tambahkan domain di panel (pakai nameserver mereka).
   - Jika di registrar lain → tambahkan **A record** ke IP server my.php.id, atau **CNAME** sesuai petunjuk panel.
2. Di panel my.php.id, **Add Domain** → `signal-learn.web.id`, arahkan ke aplikasi Node.js yang dibuat (Langkah 4).
3. **Aktifkan SSL** (Let's Encrypt / SSL gratis di panel) untuk `signal-learn.web.id` agar berjalan di HTTPS.

---

### Langkah 7 — Update Google OAuth (produksi)

Di [Google Cloud Console → Credentials](https://console.cloud.google.com/apis/credentials):

1. Buka OAuth Client ID yang dipakai.
2. **Authorized redirect URIs** → tambahkan:
   ```
   https://signal-learn.web.id/api/auth/google
   ```
3. **Authorized JavaScript origins** → tambahkan:
   ```
   https://signal-learn.web.id
   ```
4. Simpan. Pastikan `GOOGLE_REDIRECT_URI` & `VITE_GOOGLE_REDIRECT_URI` di env sudah bernilai URL produksi di atas.

---

### Langkah 8 — Jalankan & verifikasi

1. Buka `https://signal-learn.web.id` di browser.
2. Cek landing page tampil (HTTP 200).
3. Cek `https://signal-learn.web.id/join` bisa dibuka.
4. Coba login Google → harus redirect balik ke dashboard.
5. Cek tidak ada error di log aplikasi (panel / `pm2 logs signal-learn`).

---

## 5. Checklist Pasca-Deploy

- [ ] `https://signal-learn.web.id` menampilkan landing page (200)
- [ ] Login Google berhasil (redirect kembali ke app)
- [ ] Halaman `/join` bisa diakses
- [ ] Tidak ada error koneksi DB di log
- [ ] SSL aktif (gembok hijau, redirect HTTP→HTTPS)
- [ ] Aplikasi tetap jalan setelah server restart (PM2 `startup` terpasang)

---

## 6. Troubleshooting

**Aplikasi tidak jalan / 503**
- Pastikan `PORT` dari hosting diteruskan ke `server.mjs` (wrapper sudah pakai `process.env.PORT || 3000`).
- Cek log: panel error log atau `pm2 logs signal-learn`.

**Error koneksi MySQL**
- Pastikan `DB_HOST` benar (kadang `localhost` atau host khusus my.php.id, bukan IP publik).
- Pastikan user punya hak akses ke database dan dari host tempat app berjalan (cek "Remote MySQL"/akses host di panel).

**Asset (CSS/JS) tidak muncul / 404**
- TanStack Start menyajikan `dist/client` lewat handler. Jika hosting memisahkan static, arahkan web root statis ke folder `dist/client` dan proxy request lainnya ke Node app.

**Google login gagal (redirect_uri mismatch)**
- Pastikan exact match antara `GOOGLE_REDIRECT_URI` (env) dan daftar di Google Console, termasuk `https://` dan trailing path `/api/auth/google`.

**Hosting hanya PHP/static, tidak ada Node.js**
- Aplikasi ini butuh Node.js. Pindah ke paket Node.js my.php.id, atau sewa VPS (Contabo/DigitalOcean) lalu ikuti Opsi B (SSH/PM2).

**Perubahan env `VITE_*` tidak terlihat**
- Variabel `VITE_` di-inline saat build. Setelah mengubahnya, **build ulang** (`bun run build`) lalu restart app.

---

## 7. Keamanan & Catatan

- **Jangan commit** file `.env`, `*.local`, atau `.env.production` ke Git (sudah dicover `.gitignore`).
- `JWT_SECRET` harus unik per environment dan tidak dipakai bersama dev/prod.
- Selalu gunakan **HTTPS** (SSL aktif) karena OAuth & JWT butuh koneksi aman.
- Backup database secara berkala lewat fitur backup MySQL di panel my.php.id.

---

*Panduan ini disusun berdasarkan hasil build aktual project (`dist/server/server.js` + `dist/client`) dan telah diuji dengan wrapper `server.mjs` (respons HTTP 200). Sesuaikan nama host/DB dengan nilai sebenarnya dari panel my.php.id.*
