import { createAdminClient } from "npm:@insforge/sdk"; const corsHeaders = { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "POST, OPTIONS", "Access-Control-Allow-Headers": "Content-Type, Authorization", }; function sha256Hex(text: string): Promise { return crypto.subtle .digest("SHA-256", new TextEncoder().encode(text)) .then((buf) => Array.from(new Uint8Array(buf)) .map((b) => b.toString(16).padStart(2, "0")) .join("") ); } function randomSalt(): string { const bytes = new Uint8Array(16); crypto.getRandomValues(bytes); return Array.from(bytes) .map((b) => b.toString(16).padStart(2, "0")) .join(""); } export default async function (req: Request): Promise { if (req.method === "OPTIONS") { return new Response(null, { status: 204, headers: corsHeaders }); } if (req.method !== "POST") { return new Response(JSON.stringify({ error: "Method not allowed" }), { status: 405, headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } try { const { username, password } = await req.json(); if ( typeof username !== "string" || typeof password !== "string" || username.trim().length < 3 || password.length < 6 ) { return new Response( JSON.stringify({ error: "Username min 3 & password min 6 karakter." }), { status: 400, headers: { ...corsHeaders, "Content-Type": "application/json" } } ); } const admin = createAdminClient({ baseUrl: Deno.env.get("INSFORGE_BASE_URL")!, apiKey: Deno.env.get("INSFORGE_API_KEY")!, }); const { count } = await admin.database .from("app_users") .select("id", { count: "exact", head: true }); if ((count ?? 0) > 0) { return new Response( JSON.stringify({ error: "Akun admin sudah dibuat. Pendaftaran ditutup." }), { status: 403, headers: { ...corsHeaders, "Content-Type": "application/json" } } ); } const { data: existing } = await admin.database .from("app_users") .select("id") .eq("username", username.trim()) .maybeSingle(); if (existing) { return new Response(JSON.stringify({ error: "Username sudah dipakai." }), { status: 409, headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } const salt = randomSalt(); const pepper = Deno.env.get("AUTH_PEPPER") ?? ""; const hash = await sha256Hex(`${salt}|${password}|${pepper}`); const { error } = await admin.database.from("app_users").insert([ { username: username.trim(), password_hash: hash, salt }, ]); if (error) { return new Response(JSON.stringify({ error: error.message }), { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } return new Response(JSON.stringify({ ok: true }), { status: 200, headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } catch (err) { return new Response( JSON.stringify({ error: err instanceof Error ? err.message : "Unknown error" }), { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } } ); } }