hrdoo.cloud
← Kembali ke dashboard

Kelola Worker

Halo,

Host worker tempat instance Odoo customer dijalankan.

Worker baru

Alamat yang dipakai Traefik reverse-proxy ke instance di worker ini. Kalau publik, wajib firewall dibatasi cuma dari IP host1.

Dijumlah dari --workers semua instance di worker ini, bukan jumlah instance-nya.

Belum ada worker terdaftar.

Semua project & instance lintas customer. Project read-only (kelola lewat akun pemiliknya). Instance bisa di-start/stop/restart/destroy langsung dari sini untuk keperluan dukungan teknis.

Belum ada project.

Semua user terdaftar di platform.

Belum ada user terdaftar.

Log/compose email keluar dari platform. Murni data untuk sekarang — status diisi manual, belum ada pengiriman SMTP sungguhan.

Outgoing baru

Draft

Belum ada attachment.

Maksimal 2MB. Attachment baru ikut ke-upload begitu form disimpan.

Belum ada outgoing email.

Template email per jenis notifikasi. Murni data untuk sekarang — belum ada trigger yang mengirim email pakai template ini.

Template baru

Belum ada template email.

Outgoing mail server untuk pengiriman email dari platform (notifikasi, bukan email dari instance Odoo customer). Bisa lebih dari satu — prioritas ditentukan urutan sequence.

Outgoing mail server baru

Angka kecil dicoba lebih dulu kalau ada beberapa server aktif.

Kosongkan kalau tidak mau mengubah password yang sudah tersimpan.

Belum ada outgoing mail server terdaftar.

Daftar harga platform per product. Belum terhubung ke billing/project — murni data harga untuk sekarang.

Pricelist baru

Belum ada pricelist terdaftar.

Daftar harga kompetitor per product — dipakai untuk fitur price comparison di web frontend.

Competitor baru

Belum ada competitor terdaftar.

Annual Discount

Persentase potongan kalau bayar tahunan — satu nilai berlaku untuk semua pricelist, murni data untuk sekarang.

Kode Promo

Kode diskon — potongan bisa nominal (Rupiah) atau persentase.

Kode promo baru

Belum ada kode promo terdaftar.

Invoice dibuat manual — belum otomatis dari billing cycle recurring.

Invoice baru

Total: Rp 0
Nomor Customer Jenis Tanggal Jatuh Tempo Jumlah Status Payment

Belum ada invoice terdaftar.

Master data Product — dipakai sebagai referensi line item invoice.

Product baru

Belum ada product terdaftar.

Numbering Invoice

Format nomor invoice: PREFIX/YYYY/MM/0001, counter reset tiap bulan.

Payment Gateway

Gateway yang dipakai untuk checkout invoice BARU (klik "Pay Invoice" berikutnya). Checkout yang sudah terlanjur dibuat di gateway sebelumnya tetap valid sampai expired/dibayar.

Kop Invoice (Print Invoice)

Opsional — muncul di kop PDF saat invoice di-print. Kosongkan kalau belum perlu, kop cuma tampil nama app.

Belum ada logo.

PNG/JPEG/WEBP/SVG, maksimal 2MB. Kalau kosong, kop PDF pakai nama perusahaan sebagai teks.

Ditulis di baris paling bawah PDF. Cuma berlaku untuk invoice BARU — invoice yang sudah ada tidak ikut berubah kalau ini diedit.

Nama & logo customer yang ditampilkan di section "Trusted By" landing page (hrdoo.cloud).

Trusted Customer baru

Belum ada logo.

PNG/JPEG/WEBP/SVG, maksimal 1MB. Logo baru ikut ke-upload begitu form disimpan.

Belum ada trusted customer terdaftar.

Pertanyaan & jawaban yang ditampilkan di halaman FAQ landing page (hrdoo.cloud/faq).

FAQ baru

Belum ada FAQ terdaftar.

Cara worker agent (host worker/agent/agent.py) berkomunikasi dengan panel (control plane FastAPI ini) — digambar langsung dari kode, bukan dari desain awal. Worker selalu yang menghubungi panel lewat HTTP polling; panel tidak pernah mendorong perintah ke worker.

1. Satu siklus poll 2. Tangga prioritas /worker/poll 3. Build & Deploy

Figur 1

Satu siklus poll (dicontohkan lewat stop_instance)

Tiap tick loop (≈15 detik), worker menembak 6 endpoint poll yang independen satu sama lain (beda sumber antrian), lalu tiap ≈300 detik mengirim satu telemetry proaktif. Alur lengkap poll → job → eksekusi lokal → report digambar penuh untuk /worker/poll; 5 channel lain mengikuti pola yang persis sama, cuma beda sumber antrian.

WORKER AGENT tick ≈ every 15s PANEL FastAPI + Postgres ① POST /worker/poll panel cek status ladder — lihat figur 2 ② 200 · job: "stop_instance" | null eksekusi lokal docker stop web + db ③ POST /worker/report {status: "stopped"} tulis Instance.status + stale-report guard TICK YANG SAMA — 5 CHANNEL LAIN, ANTRIAN BEDA-BEDA /worker/poll-logs · log_fetch_requested_at lebih baru ↳ /worker/report-logs /worker/poll-backups · Backup(status=queued, source=created) ↳ /worker/report-backup /worker/poll-backup-deletes · Backup.pending_delete ↳ /worker/report-backup-delete /worker/poll-download-prep · Backup.download_status=preparing ↳ /worker/report-download-prep /worker/poll-import · Backup(source=imported, status=queued) ↳ /worker/report-backup /worker/report-disk-usage · tiap ≈300 detik push proaktif — bukan job fetch; panel bisa auto-stop kalau over kuota Tidak ada endpoint di arah sebaliknya — panel tidak pernah memanggil worker.

Yang berubah antar channel cuma sumber antriannya (kolom status vs baris Backup) — mekanismenya identik: worker tanya, panel jawab dari data yang sudah ada, worker kerja secara lokal, worker lapor balik.

Figur 2

/worker/poll — tangga prioritas

Channel yang paling relevan untuk create/stop/restart/destroy instance. Satu panggilan cuma pulang dengan satu job — panel mengecek 7 kondisi berurutan dari atas, berhenti di kecocokan pertama.

SATU JOB PER PANGGILAN — DICEK BERURUTAN DARI ATAS status == "destroying" match → destroy_instance docker rm -f web + db, drop network, hapus semua image instance ini, lalu rm -rf folder data host — irreversible. touches: web · db · network · host data dir · images else, cek berikutnya ↓ status == "stopping" stop_instance docker stop web + db — data di disk tidak disentuh. touches: web · db status == "restarting" restart_instance docker restart db, lalu web, berurutan. touches: web · db invoice upgrade sudah paid, instance running → panel set status = upgrading (promosi saja, belum kirim job) tick berikutnya status == "upgrading" resize_instance blue/green: container web baru dengan --workers/--cpus/ --memory baru, health-check, swap; rollback kalau gagal. touches: web saja (image, db, filestore tidak disentuh) status == "restoring" restore_backup safety pg_dump dulu, drop+recreate db, restore backup terpilih (+ filestore kalau scope-nya), restart web. touches: web · db · filestore · folder backup Build.status == "queued" process_build clone repo, build image baru, pg_dump safety backup, swap container web ke commit baru, migrasi + test, hapus lama. touches: web (baru+lama) · image · db (dump sementara) (detail penuh: lihat figur 3, ini instance yang SUDAH pernah running) status == "pending" Build queued buat instance ini? ya ↗ tidak ↘ initial_deploy clone+build image custom, provisioning network+db+web sekali jalan. touches: web · db · network · host data dir · image start_instance provisioning base image saja — atau docker start kalau container sudah ada. touches: web · db · network · host data dir tidak ada yang cocok → job: null worker idle, coba lagi tick berikutnya (≈15 detik)

Garis putus-putus di kiri (spine) = "tidak cocok, lanjut cek kondisi berikutnya"; panah solid ke kanan = "cocok, job ini yang dikirim" — dua pola itu berulang di ketujuh baris. Merah menandai satu-satunya operasi yang menghapus data host secara permanen.

Figur 3

Build & Deploy — dari git push sampai container baru live

Isi penuh process_build (dipicu row Build.status == "queued" di figur 2). Tiga titik bisa gagal — ketiganya jatuh ke prosedur rollback yang sama persis, digambar sekali di kanan.

Beberapa detail di sini beda dari deskripsi awal di CLAUDE.md (sudah dicek langsung ke agent.py): image cuma FROM base + COPY seluruh repo (tidak ada step requirements.txt/pip-install terpisah), flag migrasinya -i {modul} bukan -u all, dan tidak ada panggilan eksplisit "switch Traefik" — lihat catatan di step 7.

PUSH ke branch stage prod/staging (webhook, HMAC) Branch DIPINDAH ke stage prod/staging (Change Branch) Build(status="queued", test_result="pending") worker /worker/poll match → process_build 1 · Clone commit git clone repo (token GitHub owner) + git checkout {commit_sha} 2 · Build image docker build — FROM hrdoo/odoo:{versi}, COPY seluruh repo tag: hrdoo/odoo-{subdomain}:{commit_sha singkat} 3 · Backup DB (safety) pg_dump --clean --if-exists — SEBELUM apa pun disentuh gagal di sini → cuma rmi image baru, container lama belum di-stop sama sekali 4 · Stop container lama docker stop (BUKAN rm) — jaring pengaman rollback 5 · Migrasi + unit test container sekali-pakai: odoo -i {modul} --test-enable --stop-after-init migrasi skema PERMANEN di sini walau data test di-rollback skip (test_result=skipped) kalau repo tanpa __manifest__.py di root lolos/gagal dibaca dari string " FAIL: " di log — Odoo tidak selalu exit non-zero ① test gagal 6 · Cek folder enterprise (khusus EE) pastikan {DATA_ROOT}/{subdomain}/enterprise sudah ada dari provisioning awal ② folder tidak ada 7 · Start container baru docker run -d, nama pakai commit SHA, HOST PORT SAMA dgn lama tanpa -i/-u lagi — migrasi sudah kejadian di step 5 8 · Health check docker logs, poll tiap 2 detik × 90 (180 detik) cari "Modules loaded." lalu docker ps — pastikan belum crash setelah modules loaded bukan HTTP ping, bukan docker inspect health ③ timeout/crash Build SUKSES docker rm -f container lama simpan 3 image terakhir (_cleanup_old_images), sisanya di-rmi Build.status = "success" kenapa tidak ada langkah "switch Traefik"? container baru pakai host port SAMA (step 7). dynamic config Traefik sudah arah ke worker_ip:port sejak instance pertama kali di-assign ke worker — begitu container lama mati & yang baru dengar di port itu, trafik otomatis pindah — bukan panggilan eksplisit. ROLLBACK — sama untuk ketiganya kalau container baru sempat dibuat: → docker rm -f container baru docker exec psql < backup.sql (restore) docker start container lama (cuma di-stop tadi, bukan dihapus) docker rmi -f image baru Build.status = "failed" (+ log alasan) restore DB SELALU jalan tiap gagal setelah backup ada — tidak dicek dulu apakah skema benar-benar berubah (restore --clean --if-exists aman diulang) instance lama TAK TERSENTUH sepanjang proses ini — stop di step 4 baru jadi rm kalau step 9 sukses.

Instance lama tidak pernah dihapus sampai instance baru terbukti sehat (step 9) — kalau gagal di titik mana pun setelah step 4, yang lama tinggal di-start lagi. Satu-satunya penghapusan permanen di alur ini adalah rm -rf folder host, dan itu tidak pernah terjadi di sini — cuma lewat destroy_instance (figur 2).