6.2 KiB
Dimentorin — Catatan Temuan Infra (Dev Audit, 2026-08-04)
Dokumen ini mencatat temuan yang membutuhkan perhatian tim sebelum produksi.
Semua diuji lokal (Postgres dimentorin, backend :4099).
1. SMTP email verification broken (blocker aktivasi user baru) — ✅ FIXED (2026-08-05, Google App Password)
- Endpoint
POST /v1/iam/auth/send-otpgagal:SMTP transport error (535): Username and Password not accepted— kredensial.env(SMTP_EMAIL=dev@example.com,SMTP_PASSWORD=dev) ditolak Google SMTP. POST /v1/iam/auth/verify-emailtetap butuh OTP untuk memanggil, tapi lihat poin 2.- Dampak: mentee/mentor baru tak bisa menerima OTP lewat email → tak bisa aktivasi → tak bisa login, kecuali via verify-email langsung.
- Diperlukan: SMTP credential institution yang valid (Gmail App Password atau SMTP relay), sebaiknya dari BWS secret management, bukan hardcode.
2. ✅ FIXED — verify-email TIDAK memverifikasi OTP (security issue)
Status: FIXED di branch feat/dimentorin-postgres (2026-08-04).
imphnen-iam/src/auth/application/mod.rs → verify_email():
- OTP sekarang dipersist ke tabel
app_otp_cache(entity baruimphnen-entities/src/seaorm/common/otp_cache.rs, resourceapp_otp_cachesudah direncanakan diResourceEnum::OtpCache). register()&resend_otp()menyimpanotp_hash+expires_atsetelah email terkirim (kalau email gagal, tidak ada OTP yatim / OTP lama tidak di-overwrite).verify_email()memanggilOtpManager::validate_otp_hash(stored_hash, expires_at, payload.otp)sebelum setis_active.validate_otp_hashditambahkan keOtpManager(pure hash+expiry tanpa perlu plaintext code).- OTP single-use: di-delete setelah verifikasi sukses. Reuse / OTP tanpa cache / OTP expired semua ditolak (400).
- Uji lokal (Postgres, :4099): OTP salah → 400 "Invalid or expired OTP", user tetap inactive; OTP benar → 200 "Email verified successfully", user aktif, OTP dihapus; verify ulang → 400 "User already active"; email tanpa OTP → 400 "No OTP issued".
- Tabel dibuat via SQL manual (
create_schema.rsditambahotp_cacheuntuk bootstrap penuh).
3. (OK, sudah benar) Register mentor + booking
POST /v1/dimentorin/mentors/create→ 200, user + mentor profile dibuat, statuspending, user tak tampil di list public sampai verified.POST /v1/dimentorin/mentors/{id}/sessions/create→ 200, session pending.- Kedua endpoint fungsional setelah fix UUID (commit
9b5efef).
Rekomendasi
Tangani #1 dan #2 sebelum go-live. #2 adalah kelas bug "OTP di-generate tapi tak dipakai" — sisi verifikasi email saat ini tidak lebih dari form "set is_active=true tanpa autentikasi".
4. ✅ DONE — Payment flow (alur bisnis menjual)
Status: DONE di feat/dimentorin-postgres (2026-08-05).
- Tabel
app_payments: amount (darimentoring_ratementor) + service_fee 2000 + total; methodva/qris/manual; providermanualdefault (swap Midtrans/Xendit nanti — cukup ganti nilaiproviderdan implementasigenerate_external_ref/notifikasi webhook). - Routes protected:
POST /payments/sessions/{id}/create,GET /payments/me,GET /payments/{id},POST /payments/{id}/confirm. - Guard: mentee hanya bisa akses payment miliknya (403 kalau bukan); confirm hanya Admin / Admin Pembayaran.
confirm_paymentotomatis mengubah session terkaitpending->confirmed(loop bisnis lengkap: book -> bayar -> sesi terkonfirmasi -> feedback).- FE: PaymentStep pilih VA/QRIS, rate real dari
mentoring_rate; modal appointment: book -> create payment -> tampil VA/QR denganexternal_ref+ total + expiry -> success. Service lib:postCreatePayment/getMyPayments/getPaymentById/postConfirmPayment. - E2E verified (lokal :4099): create VA dan QRIS, confirm 200, re-confirm 409, non-admin 403, akses payment orang lain 403, session auto-confirmed.
- TODO produksi: isi kredensial payment gateway (Midtrans/Xendit) + webhook callback; SMTP masih blocker (#1).
5. Payment lifecycle complete: auto-paid refresh + dashboards (2026-08-05)
- POST /payments/{id}/refresh: polls Midtrans v2/{order_id}/status; settlement/capture -> payment paid + session confirmed automatically (e2e verified via sandbox simulator: VA paid -> refresh -> paid + confirmed)
- GET /payments/session/{id}: payments for one session, accessible by that session's mentee or mentor (powers both dashboards)
- Session mentor may confirm their own payments (previously admins only) — verified e2e
- FE: /mentoring/my-sessions (mentee) + /mentoring/mentor-dashboard (mentor), both with live payment status and refresh/confirm actions; QRIS step renders real QR from qr_string
6. Materi + AI Agent RAG (2026-08-05, commit 164c186 BE / 7294040 FE)
Fitur yang "harusnya ada" menurut user (Figma hanya berisi Design System, halaman Materi/AI Agent tidak ada di file) — dibangun dari pemahaman alur bisnis mentoring.
Backend — modul materials (imphnen-dimentorin/src/materials/):
app_materialstable: mentor_id, title, slug, category, description, content, cover_url, is_published- Pola articles: domain/repository/service + postgres repo + DTO ZodValidate
- Routes: GET /materials (public, published), /materials/{id|slug|categories}, POST/PUT/DELETE (auth, author-only)
- ENV baru: AI_LLM_BASE_URL/API_KEY/MODEL, AI_EMBEDDING_MODEL, QDRANT_URL
Backend — AI agent RAG (imphnen-dimentorin/src/ai_agent/):
- Chunking materi (700 chars, overlap 80) -> embed via 9router
gemini/gemini-embedding-001(3072 dim!) - Qdrant collection
dimentorin_materi(3072d cosine, point id = u64 dari uuid xor index — Qdrant TOLAK string non-UUID) - Chat: embed question -> search top-4 -> LLM (
text-> gemini-3.1-flash-lite) jawab dengan konteks + sources - Routes: POST /ai/chat, POST /ai/materials/{id}/index, POST /ai/reindex
- 9router chat SELALU SSE-streaming walau tanpa stream:true — parser harus agregate
data:lines
Verified e2e: reindex 3 chunks; chat 'ownership' -> source materi Rust 0.88; chat 'endpoint axum' -> Axum 0.82; browser: list materi, detail, chatbox jawab + sources 84/64/61%.
Pitfall: embedding Gemini = 3072 dim (bukan 768); Qdrant point id harus u64/UUID; model embedding yang berfungsi di 9router = gemini/gemini-embedding-001 (llama-nemotron -> 'No credentials for provider: openai').