// Server only: launch-handler.mjs. No npm packages needed. const base_url = "https://scormbox.com/api/v1"; const origin = process.env.LMS_ORIGIN; // e.g. https://lms.example.com const course_id = process.env.SCORMBOX_COURSE_ID; const api_key = process.env.SCORMBOX_API_KEY; const api_secret = process.env.SCORMBOX_API_SECRET; if (!origin || !course_id || !api_key || !api_secret) { throw new Error("Set LMS_ORIGIN, SCORMBOX_COURSE_ID, SCORMBOX_API_KEY and SCORMBOX_API_SECRET."); } const authorization = "Basic " + Buffer.from(api_key + ":" + api_secret).toString("base64"); export async function handleTrainingLaunch(request, response, learner) { const send = (status, body) => { response.writeHead(status, { "Content-Type": "application/json", "Cache-Control": "no-store" }); response.end(JSON.stringify(body)); }; if (request.method !== "POST") return send(405, { error: "Use POST." }); if (request.headers.origin !== origin) return send(403, { error: "Website not allowed." }); if (!learner || typeof learner.name !== "string" || typeof learner.email !== "string") { return send(401, { error: "Sign in to your LMS first." }); } try { const upstream = await fetch(base_url + "/courses/" + encodeURIComponent(course_id) + "/sessions", { method: "POST", redirect: "error", signal: AbortSignal.timeout(20000), headers: { Authorization: authorization, "Content-Type": "application/json" }, body: JSON.stringify({ user: { name: learner.name, email: learner.email }, origin, attempt_mode: "resume" }) }); const session = await upstream.json(); if (!upstream.ok) return send(502, { error: "Unable to prepare your course." }); // Never return API credentials or the management URL. send(200, { launch_token: session.launch_token }); } catch { send(502, { error: "Unable to prepare your course. Please try again." }); } } // In your existing authenticated/authorized POST route: // await handleTrainingLaunch(request, response, signed_in_learner);