import { createHmac } from "crypto"; function base64url(buf: Buffer): string { return buf.toString("base64") .replace(/=/g, "") .replace(/\+/g, "-") .replace(/\//g, "_"); } function signJWT(payload: object, secret: string): string { const header = { alg: "HS256", typ: "JWT" }; const headerStr = base64url(Buffer.from(JSON.stringify(header))); const payloadStr = base64url(Buffer.from(JSON.stringify(payload))); const signature = createHmac("sha256", secret) .update(`${headerStr}.${payloadStr}`) .digest(); return `${headerStr}.${payloadStr}.${base64url(signature)}`; } const API_BASE = "http://localhost:3051/api"; async function run() { console.log("=== STEP 1: Login via Auth Route ==="); const loginRes = await fetch(`${API_BASE}/auth/login`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ username: "admin", password: "homaroma123" }) }); const loginData = await loginRes.json() as any; console.log("Login status:", loginRes.status); console.log("Login response:", loginData); if (!loginData.success) { console.error("Login failed!"); return; } const loginToken = loginData.token; console.log("\n=== STEP 2: Test POST /menus with login token ==="); const testMenuPayload = { name: "challenger-test-menu", rootImage: "https://example.com/root.jpg", items: [ { label: "Item 1", url: "/item1", order: 1, columnImage: "https://example.com/column1.jpg", hoverImage: "https://example.com/hover1.jpg" } ] }; const postRes = await fetch(`${API_BASE}/menus`, { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${loginToken}` }, body: JSON.stringify(testMenuPayload) }); console.log("POST /menus with login token status:", postRes.status); const postData = await postRes.json(); console.log("POST /menus response:", postData); // If unauthorized, let's sign tokens with different secret fallbacks to see which works! let workingToken = loginToken; if (postRes.status === 401) { console.log("\n⚠️ Detected JWT Secret Mismatch! Trying manually signed tokens..."); const payload = { id: 1, username: "admin", role: "ADMIN" }; const secrets = [ "homaroma_secret_2026_scent", "homaroma_secret_2026_brand", "fallback-secret" ]; for (const secret of secrets) { const token = signJWT(payload, secret); const testRes = await fetch(`${API_BASE}/menus`, { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${token}` }, body: JSON.stringify(testMenuPayload) }); console.log(`Testing secret '${secret}': status = ${testRes.status}`); if (testRes.status !== 401) { console.log(`✅ Success with secret '${secret}'!`); workingToken = token; break; } } } console.log("\n=== STEP 3: Perform correct Menu creation & retrieval ==="); // Ensure the menu doesn't exist already by trying to query / delete it first // Let's create it clean const menuName = "challenger-test-" + Date.now(); const menuPayload = { name: menuName, rootImage: "https://example.com/challenger-root.png", items: [ { label: "Challenger Link", url: "/challenger-link", order: 2, columnImage: "https://example.com/challenger-column.png", hoverImage: "https://example.com/challenger-hover.png" } ] }; const createRes = await fetch(`${API_BASE}/menus`, { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${workingToken}` }, body: JSON.stringify(menuPayload) }); console.log("Creation status:", createRes.status); const createdMenu = await createRes.json() as any; console.log("Created menu:", JSON.stringify(createdMenu, null, 2)); // Verify DB retrieval via GET /api/menus/:name const getRes = await fetch(`${API_BASE}/menus/${menuName}`); console.log("GET /menus/:name status:", getRes.status); const retrievedMenu = await getRes.json() as any; console.log("Retrieved menu:", JSON.stringify(retrievedMenu, null, 2)); // Verify that rootImage, columnImage, and hoverImage are correctly stored and retrieved const rootImageOk = retrievedMenu.rootImage === menuPayload.rootImage; const item = retrievedMenu.items?.[0]; const columnImageOk = item?.columnImage === menuPayload.items[0].columnImage; const hoverImageOk = item?.hoverImage === menuPayload.items[0].hoverImage; console.log(`\nVerification Results:`); console.log(`- rootImage correct: ${rootImageOk} (Expected: ${menuPayload.rootImage}, Got: ${retrievedMenu.rootImage})`); console.log(`- columnImage correct: ${columnImageOk} (Expected: ${menuPayload.items[0].columnImage}, Got: ${item?.columnImage})`); console.log(`- hoverImage correct: ${hoverImageOk} (Expected: ${menuPayload.items[0].hoverImage}, Got: ${item?.hoverImage})`); console.log("\n=== STEP 4: Perform PUT update menu ==="); const updatePayload = { rootImage: "https://example.com/challenger-root-updated.png", items: [ { id: item?.id, label: "Challenger Link Updated", url: "/challenger-link-updated", order: 5, columnImage: "https://example.com/challenger-column-updated.png", hoverImage: "https://example.com/challenger-hover-updated.png" } ] }; const updateRes = await fetch(`${API_BASE}/menus/${menuName}`, { method: "PUT", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${workingToken}` }, body: JSON.stringify(updatePayload) }); console.log("PUT status:", updateRes.status); const updatedMenu = await updateRes.json() as any; console.log("Updated menu retrieved:", JSON.stringify(updatedMenu, null, 2)); console.log("\n=== STEP 5: Verify Validation Errors for Incorrect Types ==="); const invalidPayload = { name: "challenger-invalid-menu", rootImage: 12345, // Should fail: expected string items: [ { label: "Invalid Link", url: "/invalid", order: "not-a-number", // Should fail: expected number columnImage: true // Should fail: expected string } ] }; const invalidRes = await fetch(`${API_BASE}/menus`, { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${workingToken}` }, body: JSON.stringify(invalidPayload) }); console.log("POST invalid payload status:", invalidRes.status); const invalidData = await invalidRes.json(); console.log("POST invalid payload response (expected validation error):", JSON.stringify(invalidData, null, 2)); } run().catch(console.error);