diff --git a/backend/scripts/setup-test-mitra-otp.mjs b/backend/scripts/setup-test-mitra-otp.mjs new file mode 100644 index 0000000..379baa9 --- /dev/null +++ b/backend/scripts/setup-test-mitra-otp.mjs @@ -0,0 +1,85 @@ +// Dev helper: provision a STATIC-OTP mitra login for local/QA testing. +// +// Uses the existing test-OTP-bypass allowlist (app_config.test_otp_bypass), +// the same mechanism shipped for Apple-reviewer QA. It: +// 1. ensures an ACTIVE mitra exists with the test phone (mitras default to +// is_active=false, which the mitra verify route rejects with 403), +// 2. adds a phone-scoped, mitra-scoped static OTP entry (bcrypt-hashed), +// 3. flips the global bypass kill-switch on. +// +// After running, log into the mitra app with PHONE + OTP below — no Fazpass, +// no console code-reading. Re-running is idempotent. +// +// Usage (from backend/): node scripts/setup-test-mitra-otp.mjs +// Override defaults: TEST_MITRA_PHONE=+628... TEST_MITRA_OTP=123456 node scripts/setup-test-mitra-otp.mjs + +import 'dotenv/config' +import { getDb } from '../src/db/client.js' +import { + getTestOtpBypass, + addTestOtpBypassEntry, + setTestOtpBypassEnabled, +} from '../src/services/config.service.js' + +const PHONE = process.env.TEST_MITRA_PHONE || '+6281200000001' +const OTP = process.env.TEST_MITRA_OTP || '123456' +const LABEL = process.env.TEST_MITRA_LABEL || 'Dev static OTP (mitra)' +const DISPLAY_NAME = process.env.TEST_MITRA_NAME || 'Test Bestie' +// Far-future expiry — the allowlist requires a future expires_at per entry. +const EXPIRES_AT = '2099-01-01T00:00:00.000Z' + +const sql = getDb() + +async function main () { + // 1. Ensure an ACTIVE mitra with this phone (raw SQL — avoids importing + // mitra.service, which pulls in the valkey plugin and would leave an open + // handle keeping this script alive). + const [existing] = await sql`SELECT id, is_active FROM mitras WHERE phone = ${PHONE}` + if (!existing) { + const [m] = await sql` + INSERT INTO mitras (phone, display_name, is_active) + VALUES (${PHONE}, ${DISPLAY_NAME}, true) + RETURNING id + ` + console.log(` created active mitra ${m.id} (${PHONE})`) + } else if (!existing.is_active) { + await sql`UPDATE mitras SET is_active = true WHERE id = ${existing.id}` + console.log(` mitra ${existing.id} existed — activated`) + } else { + console.log(` mitra ${existing.id} already exists and active`) + } + + // 2. Add the static-OTP allowlist entry (skip if one already exists for this + // phone+mitra — addTestOtpBypassEntry throws on duplicate). + const current = await getTestOtpBypass() + const exists = current.entries.some(e => e.phone === PHONE && e.user_type === 'mitra') + if (exists) { + console.log(' bypass entry already present for this phone+mitra — leaving as is') + console.log(' (to rotate the OTP: delete the entry in CC → Settings, then re-run)') + } else { + await addTestOtpBypassEntry({ + phone: PHONE, + otp: OTP, + user_type: 'mitra', + label: LABEL, + expires_at: EXPIRES_AT, + }) + console.log(` added bypass entry: ${PHONE} → otp ${OTP} (mitra)`) + } + + // 3. Global kill-switch ON. + await setTestOtpBypassEnabled(true) + console.log(' bypass allowlist ENABLED') + + console.log('\n✅ Static mitra OTP ready:') + console.log(` phone: ${PHONE}`) + console.log(` otp: ${exists ? '(unchanged — set on a previous run)' : OTP}`) +} + +main() + .then(() => sql.end({ timeout: 5 })) + .catch(async (err) => { + console.error('FAILED:', err.message) + await sql.end({ timeout: 5 }) + process.exit(1) + })