Audit dan perkuat backend & integrasi eksternal AWCMS (resiliensi, outbox, webhook/provider, versioning API, observability). Gunakan saat menambah/mengeraskan integrasi ke provider luar (R2, WhatsApp, email, AI, pajak), memperbaiki keandalan delivery, atau menaikkan ketahanan backend. Menegakkan ADR-0006 (provider opsional, di luar transaksi) dan pola outbox doc 16.
Installation
Mit Codex oder Claude installieren Kopieren Sie diesen Prompt, fügen Sie ihn in Codex, Claude oder einen anderen Assistant ein und lassen Sie die Skill-Seite prüfen und installieren.
Audit dan perkuat backend & integrasi eksternal AWCMS (resiliensi, outbox, webhook/provider, versioning API, observability). Gunakan saat menambah/mengeraskan integrasi ke provider luar (R2, WhatsApp, email, AI, pajak), memperbaiki keandalan delivery, atau menaikkan ketahanan backend. Menegakkan ADR-0006 (provider opsional, di luar transaksi) dan pola outbox doc 16.
AWCMS — Backend & Integration Hardening
Sumber kebenaran: docs/awcms/16_backend_data_access_integration.md (transactional outbox, transaction wrapper, idempotency), docs/awcms/05_openapi_asyncapi_detail.md (kontrak API/event), dan docs/awcms/10_template_kode_coding_standard.md (guardrail). Skill ini peningkatan: menaikkan keandalan & ketahanan, bukan sekadar membuat endpoint (itu awcms-new-endpoint).
Prinsip integrasi (non-negotiable)
Provider eksternal opsional (ADR-0006): R2/WA/email/AI/pajak tidak boleh jadi dependency alur operasional kritis, dan tidak boleh dipanggil di dalam transaksi DB. Provider off → aksi tetap masuk antrean, bukan gagal. Shared worker runner (src/lib/jobs/job-runner.ts, Issue #697) — dipakai untuk orkestrasi scripts/*.ts (lock, batching, exit code, telemetry) — dirancang KONSISTEN dengan aturan ini: tidak mendorong pola yang menaruh panggilan provider di dalam transaksi DB yang sama; handler job yang memanggil provider tetap wajib melakukannya di luar withTenant's transaction block, persis pola dispatcher outbox di bawah.
Secret hanya dari environment (doc 18); flag aktif tanpa kredensial → gagal start (fail-fast).
Checklist ketahanan
Transactional outbox — efek samping eksternal (kirim WA/email, upload objek, publish event) ditulis ke tabel outbox dalam transaksi bisnis, lalu dispatcher terpisah mengirim di luar transaksi (at-least-once). Bukan panggilan langsung inline. Contoh nyata sudah ada, ikuti pola ini: awcms_object_sync_queue + dispatchObjectSyncQueue (src/modules/sync-storage/application/object-dispatch.ts, Issue #436) — tiga fase CLAIM (transaksi pendek, FOR UPDATE SKIP LOCKED, status transien sending sebagai lease) → UPLOAD (di luar transaksi apa pun) → FINALIZE (transaksi pendek kedua). awcms_sync_outbox (event lokal antar-node) belum punya dispatcher live. Email (epic #492, Issue #493-#495) adalah contoh kedua nyata dari pola ini, dengan variasi: awcms_email_messages (sql/014) + dispatchEmailQueue (src/modules/email/application/email-dispatch.ts) — CLAIM (reuse next_attempt_at sebagai lease, sama seperti object queue) → SEND (render dari template_key/variables lalu panggil EmailProvider.send, di luar transaksi apa pun) → FINALIZE (retry_wait/sent/failed + catat email_delivery_attempts). Caller nyata sudah ada sejak Issue #496/#497 (epic #492 selesai): password reset (POST /auth/password/forgot) dan bulk announcement (POST /email/announcements[/preview], two-tier ABAC) sama-sama meng-enqueue ke email_messages sebelum bun run email:dispatch mengklaimnya. (social_publishing, Issue #643, adalah contoh ketiga dengan shape yang sama di awcms-mini — modulnya belum di-port ke sini, jadi jangan cari src/modules/social-publishing di repo ini; lihat skill awcms-social-publishing yang berstatus BACAAN SAJA.) Generic MULTI-consumer counterpart (Issue #742, epic platform-evolution #738): domain_event_runtime — satu event bisa fan-out ke banyak consumer terdaftar (bukan satu dispatcher/satu provider seperti tiga contoh di atas), dengan ordering eksplisit per aggregate/order-key. Dua reference consumer bawaannya same-process/DB-only (bukan provider eksternal), jadi claim+eksekusi+finalize sengaja SATU transaksi, bukan CLAIM/CALL/FINALIZE 3-fase — lihat src/modules/domain-event-runtime/README.md §Execution model untuk kapan pola 3-fase tetap wajib (consumer out-of-transaction/broker-backed masa depan).
Timeout + retry + backoff — tiap panggilan keluar punya timeout eksplisit (withTimeout, src/lib/integration/timeout.ts, Issue #436 — reuse, jangan bikin ulang); retry dengan exponential backoff berbatas (pola evaluateObjectRetry, maks percobaan); jangan retry tak terbatas.
Circuit breaker — dependency yang gagal beruntun dibuka sementara agar tak menyeret sistem. src/lib/database/circuit-breaker.ts punya registry per-provider sejak Issue #436: getProviderCircuitBreaker(providerKey) untuk provider eksternal (reuse, jangan bikin circuit breaker terpisah) — beda dari singleton database yang sudah ada sejak awal. Saat breaker terbuka, baris yang butuh provider itu tidak diklaim sama sekali pada pass tersebut (tetap pending); baris yang tidak butuh provider tetap jalan.
Idempotency di batas integrasi — dispatcher aman diulang (dedup by natural key / Idempotency-Key / ledger batch seperti sync_push_batches); pengiriman ganda tak menduplikasi efek. Untuk upload objek: key upload (objectKey) sendiri jadi dedup key alami (PUT S3/R2 ke key sama = overwrite, bukan duplikat) — tidak selalu perlu ledger terpisah.
Dead-letter / status gagal — item yang habis retry ditandai failed dengan last_error, bukan hilang diam-diam; ada jalur retry manual admin (mis. /sync/object-queue/{id}/retry).
Verifikasi webhook masuk — signature HMAC + anti-replay (timestamp skew) untuk callback provider, pola awcms-sync-hmac. Jangan percaya payload tak tertandatangani.
SSRF-safe — URL provider dari konfigurasi tepercaya (env), bukan dari input user; validasi host bila perlu. Contoh nyata: object-storage-uploader.ts (Issue #436) — endpoint R2 selalu dari process.env.R2_ACCOUNT_ID, tak pernah dari request.
Kontrak sinkron — endpoint baru/berubah → OpenAPI diperbarui; event baru/berubah → AsyncAPI diperbarui; module.ts publishes/subscribes akurat. bun run api:spec:check hijau. Dispatcher/CLI internal (bukan endpoint HTTP) tidak butuh update OpenAPI.
Versioning & kompatibilitas — perubahan breaking di path/skema butuh versi baru atau additive; jangan patahkan klien lama (pola cookie additive di /auth/login).
Observability — correlation ID otomatis di meta.correlationId untuk endpoint /api/* sejak Issue #447 (tidak perlu wiring manual, lihat awcms-observability); log terstruktur JSON (hormati LOG_LEVEL); aksi high-risk masuk audit; redaksi secret/PII sebelum log; extension point setLogSink/setAuditExportHook tersedia bagi deployment/consumer eksternal yang butuh forward ke SIEM eksternal (awcms-observability).
Degradasi anggun — bila provider/flag off, endpoint operasional tetap jalan (POS tak berhenti); pesan/objek tetap terantre.
Rate limiting / anti-abuse — batasi endpoint publik/mahal. src/lib/security/rate-limit.ts (Issue #437) sudah menyediakan fixed-window counter generik (checkRateLimit, keyed sumber+konteks) — dipakai login, reuse untuk endpoint publik/mahal lain, jangan bikin limiter terpisah. In-memory per-proses (tidak dibagi antar instance) — cukup untuk topologi single-instance, deployment multi-instance perlu rate limiting di edge/proxy.
Verifikasi
Uji provider off: aksi tetap masuk outbox/queue, tak ada error operasional.
Uji retry: paksa gagal → item mundur backoff → habis percobaan → failed + last_error, retry manual mengembalikan ke pending.
Uji idempotency dispatcher: kirim ulang batch → efek tak berganda.
bun run check hijau; OpenAPI/AsyncAPI sinkron; correlation ID muncul di log lintas hop.
Skill terkait
awcms-new-endpoint / awcms-new-event (kontrak), awcms-sync-hmac (signature), awcms-idempotency (mutation), awcms-audit-log (jejak), awcms-observability (correlation ID, extension point), awcms-performance (I/O di luar transaksi).