Skip to content

feat: Sites Proxy — routing domain lokal ke dev server via Caddy#14

Open
devhardiyanto wants to merge 5 commits into
mainfrom
phase-12-sites-proxy
Open

feat: Sites Proxy — routing domain lokal ke dev server via Caddy#14
devhardiyanto wants to merge 5 commits into
mainfrom
phase-12-sites-proxy

Conversation

@devhardiyanto

Copy link
Copy Markdown
Owner

What

Melengkapi fitur Sites: setelah Phase 11 memberi lapisan DNS lokal (hosts), PR ini menambahkan lapisan routing. myapp.test sekarang bisa benar-benar nyambung ke dev server, bukan cuma resolve nama ke 127.0.0.1.

Why

Entri hosts 127.0.0.1 myapp.test hanya name resolution. Saat browser membuka http://myapp.test, nama resolve ke 127.0.0.1:80 — tapi tak ada yang listen di sana, karena Servel tidak menjalankan web server. Hasilnya connection refused. Fitur Sites setengah jadi: DNS ada, routing hilang.

How

Reverse proxy Caddy dijalankan sebagai child-process, bukan Docker service — proxy di container harus menembak balik host port, yang mulus di Docker Desktop tapi rewel di Linux, dan TLS CA dari container ribet. Child-process memakai networking host langsung, jadi 127.0.0.1:PORT jalan apa adanya.

Binary Caddy diunduh saat pertama dibutuhkan dengan versi dipin dan digest SHA-512 hard-code, bukan trust-on-first-use. Alternatifnya membundel binary, yang menambah ~50 MB per-OS ke installer; jalur unduh hanya menambah ~2 MB (reqwest/rustls + sha2 + zip).

Target site berupa port, bukan docroot PHP. Docroot butuh Servel mengorkestrasi php-fpm per-site, sementara phpvm sekarang hanya menukar versi. Konsekuensinya user tetap menjalankan dev server-nya sendiri — diterima, ini dev tool, bukan app server. Bentuk target sengaja tagged supaya varian docroot bisa menyusul tanpa migrasi config ulang.

Arah ini divalidasi lebih dulu lewat spike di mesin Windows nyata sebelum kode ditulis. Tiga temuannya membentuk desain akhir:

  • JAVA_HOME yang ter-set membuat instalasi root CA gagal total — Caddy mencoba menulis ke cacerts JDK lewat keytool, gagal, lalu membatalkan seluruh proses termasuk ke trust store OS. Proses anak karena itu dijalankan tanpa JAVA_HOME.
  • Pemasangan CA di Windows bukan UAC, melainkan dialog konfirmasi sertifikat ke store CurrentUser. Rencana awal me-reuse mesin elevation Phase 11 karenanya dibatalkan.
  • Caddy diam-diam memasang CA saat start. skip_install_trust menahannya, sehingga pemasangan sertifikat jadi aksi eksplisit dari UI.

Mode TLS mengikuti kondisi trust store: HTTPS hanya kalau CA sudah dipercaya OS, selain itu HTTP-only. Lebih baik jalan tanpa TLS daripada terlihat rusak di browser, jadi user yang menolak dialog sertifikat tidak kehilangan fitur.

Changes

  • src-tauri/src/proxy/binary.rs — unduh, verifikasi SHA-512, dan pasang binary Caddy; helper caddy_command yang membuang JAVA_HOME.
  • src-tauri/src/proxy/caddyfile.rs — generator Caddyfile murni dari daftar sites, dengan pagar karakter domain dan output stabil terurut.
  • src-tauri/src/proxy/lifecycle.rsproxy_start/stop/reload/status/check_ports/install_cert, probe konflik :80/:443, validasi config via caddy adapt, stop-on-shutdown.
  • src-tauri/src/commands/config.rsSite.target + migrasi config v3 → v4; site lama tetap hosts-only.
  • src/composables/useProxy.ts — bridge status proxy + listener proxy-status-changed.
  • src/views/Settings.vue, src/composables/useSites.ts, src/composables/useConfig.ts — kolom port per site dan section Reverse Proxy.

Command baru: proxy_binary_status, proxy_binary_install, proxy_status, proxy_check_ports, proxy_start, proxy_stop, proxy_reload, proxy_install_cert. Event baru: proxy-status-changed, proxy-binary-progress.

Testing

cargo test 96/96 hijau, vue-tsc --noEmit dan vite build bersih. Jalur Caddy — bind port tanpa admin, reverse proxy, hot-reload via admin API, CA lokal dipercaya Chrome, stop bersih — sudah dibuktikan empiris di spike sebelum implementasi.

Belum dijalankan: QA e2e lewat aplikasi (happy path domain→port, deteksi konflik port, fallback HTTP-only). Smoke UI sudah dicek: engine terpasang, status proxy dan sertifikat tampil benar.

Scope yang sengaja ditinggal

Docroot PHP, wildcard domain, dan upstream non-localhost tidak masuk rilis ini. Deteksi sertifikat baru diimplementasi untuk Windows; macOS dan Linux dilaporkan belum terpasang sampai jalurnya diuji, sehingga keduanya jalan HTTP-only yang selalu aman.

devhardiyanto

Fondasi Phase 12 (Sites Proxy): modul `proxy::binary` yang mengunduh
binary Caddy dari GitHub release resmi saat pertama dibutuhkan, lalu
memasangnya di `<app_data_dir>/bin`.

Versi dipin (2.11.4) dengan digest SHA-512 hard-code per target OS+arch,
jadi verifikasi bukan trust-on-first-use — arsip yang tidak cocok
dibatalkan sebelum menyentuh disk. Rilis Caddy memublikasikan SHA-512,
bukan SHA-256. Penulisan lewat file sementara + rename supaya tak pernah
ada binary setengah-tertulis yang terlihat sudah terpasang.

`caddy_command()` membuang `JAVA_HOME` dari environment proses anak: bila
ter-set, truststore Caddy mencoba menulis ke cacerts JDK lewat keytool
(butuh admin), gagal, lalu membatalkan seluruh instalasi root CA termasuk
ke trust store OS. Terbukti empiris di spike T0.

Download-on-first-run dipilih ketimbang bundling: dampak ke ukuran
binary hanya ~2 MB (reqwest/rustls + sha2 + zip) dibanding ~50 MB per-OS
kalau caddy ikut dibundel.

Command baru: proxy_binary_status, proxy_binary_install.
Event baru: proxy-binary-progress.

devhardiyanto
`Site` kini punya `target: Option<SiteTarget>` untuk menandai ke mana
domain diteruskan reverse proxy. Bentuk on-disk sengaja tagged
(`{ "kind": "port", "value": 5173 }`) supaya varian lain — mis. docroot
untuk mode Valet penuh yang di-descope dari v1.5 — bisa menyusul tanpa
migrasi ulang.

Migrasi v3 ke v4 tidak memberi target default: site lama tak punya makna
proxy, jadi tetap hosts-only (`None`). Nama tetap resolve ke IP-nya,
persis perilaku v1.4, dan proxy hanya menyentuh site yang ber-target.

Test: migrasi v3 ke v4 mempertahankan sites apa adanya, config v4 tak
kehilangan target yang sudah di-set, dan bentuk JSON target dikunci.
Assertion versi di test v2 ke v3 diikat ke CURRENT_VERSION supaya tidak
lapuk tiap kali skema naik.

devhardiyanto
Modul murni `proxy::caddyfile` yang memproyeksikan sites menjadi config
Caddy. Tanpa I/O dan tanpa state, jadi seluruh perilakunya bisa diuji
tanpa menjalankan Caddy.

Bentuk config mengikuti yang sudah divalidasi di spike T0: mode HTTPS
memakai `skip_install_trust` supaya Caddy tidak memasang root CA sendiri
saat start — pemasangan sertifikat jadi langkah eksplisit dari UI, bukan
dialog yang muncul tiba-tiba. Mode HttpOnly memakai `auto_https off`
sebagai fallback saat user menolak atau gagal memasang sertifikat.

Hanya site yang aktif dan ber-target yang masuk; site hosts-only
dilewati. Domain disaring lewat pagar karakter supaya isi config.json
tak pernah bisa menyuntikkan direktif ke Caddyfile, dan port 0 ditolak.
Domain kembar diringkas jadi satu blok karena Caddy menolak site-address
yang sama dua kali.

Output di-sort per domain, jadi config yang sama secara semantik selalu
menghasilkan teks identik — reload hanya perlu saat isinya benar-benar
berubah.

Konstanta dan `TlsMode` sementara belum terpakai; konsumennya menyusul
di lifecycle proxy.

devhardiyanto
Command baru: proxy_status, proxy_check_ports, proxy_start, proxy_stop,
proxy_reload, proxy_install_cert. Semua mengembalikan Result dan
meng-emit `proxy-status-changed` saat status berubah.

Start memprobe :80 dan :443 lebih dulu supaya konflik dengan IIS atau
web server lain muncul sebagai pesan yang bisa ditindaklanjuti, bukan
proses yang mati diam-diam. Config divalidasi lewat `caddy adapt`
sebelum dipakai, dengan alasan yang sama.

Mode TLS ditentukan dari kondisi trust store: HTTPS hanya kalau root CA
lokal sudah dipercaya OS, selain itu HTTP-only. Lebih baik jalan tanpa
TLS daripada terlihat rusak di browser. Pemasangan sertifikat jadi aksi
eksplisit; user menolak dialognya bukan error fatal, proxy tetap
melayani lewat HTTP.

Reload memakai admin API sehingga perubahan site diterapkan tanpa
mematikan proses. Stop mencoba jalur admin API dulu agar port dilepas
bersih, baru kill sebagai cadangan. Proses juga dihentikan saat app
exit — tanpa itu :80/:443 bisa nyangkut setelah Servel ditutup.

Deteksi sertifikat baru diimplementasi untuk Windows (store CurrentUser,
tempat Caddy memasangnya tanpa elevation). macOS/Linux sengaja dilaporkan
belum terpasang sampai jalurnya diuji empiris, jadi keduanya jalan
HTTP-only yang selalu aman.

devhardiyanto
Daftar domain kini punya kolom port tujuan. Dikosongkan berarti site
hosts-only — nama tetap resolve, tapi tak ada yang mem-proxy-kan, dan
baris itu ditandai "tanpa proxy" supaya bedanya terlihat. Port divalidasi
1 sampai 65535 sebelum disimpan.

Section Reverse Proxy baru menampilkan tiga keadaan berurutan: engine
belum terpasang (dengan progres unduhan), proxy jalan atau berhenti
beserta jumlah domain yang dilayani dan mode HTTP/HTTPS-nya, lalu status
sertifikat lokal. Tombol jalankan dinonaktifkan saat belum ada domain
yang layak di-proxy, dengan alasannya di tooltip — lebih baik daripada
membiarkan backend menolak setelah diklik.

Composable `useProxy` mendengarkan `proxy-status-changed`, jadi status di
UI ikut berubah tanpa polling. Setiap mutasi site memicu reload proxy
kalau sedang jalan, didahului flush config supaya backend membaca versi
terbaru dari disk, bukan yang tertinggal di debounce.

Pesan error dari backend ditampilkan apa adanya karena sudah Bahasa
Indonesia dan menyebut langkah perbaikannya.

devhardiyanto
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant