From 4ef692363c69a46f52d94c4be202fda101bc0205 Mon Sep 17 00:00:00 2001 From: Vishnunath A Suresh Date: Tue, 31 Mar 2026 15:51:16 +0530 Subject: [PATCH] refactor(bunkx): remove session handling and attendance payload logic accidentally added to the bunkialo-landing page --- .../src/app/api/bunkx/session/[sid]/route.ts | 31 ------- .../src/app/api/bunkx/session/route.ts | 58 ------------- bunkialo-landing/src/app/bunkialo/page.tsx | 85 ------------------- bunkialo-landing/src/lib/bunkx-payload.ts | 68 --------------- .../src/lib/bunkx-session-store.ts | 73 ---------------- 5 files changed, 315 deletions(-) delete mode 100644 bunkialo-landing/src/app/api/bunkx/session/[sid]/route.ts delete mode 100644 bunkialo-landing/src/app/api/bunkx/session/route.ts delete mode 100644 bunkialo-landing/src/app/bunkialo/page.tsx delete mode 100644 bunkialo-landing/src/lib/bunkx-payload.ts delete mode 100644 bunkialo-landing/src/lib/bunkx-session-store.ts diff --git a/bunkialo-landing/src/app/api/bunkx/session/[sid]/route.ts b/bunkialo-landing/src/app/api/bunkx/session/[sid]/route.ts deleted file mode 100644 index 7b0581e..0000000 --- a/bunkialo-landing/src/app/api/bunkx/session/[sid]/route.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { consumeSession } from "@/lib/bunkx-session-store"; -import { NextResponse } from "next/server"; - -export async function GET( - _request: Request, - context: { params: Promise<{ sid: string }> }, -) { - const params = await context.params; - const payload = consumeSession(params.sid); - - if (!payload) { - return NextResponse.json( - { - error: "Session not found or expired", - }, - { - status: 404, - headers: { - "Cache-Control": "no-store", - }, - }, - ); - } - - return NextResponse.json(payload, { - status: 200, - headers: { - "Cache-Control": "no-store", - }, - }); -} diff --git a/bunkialo-landing/src/app/api/bunkx/session/route.ts b/bunkialo-landing/src/app/api/bunkx/session/route.ts deleted file mode 100644 index 2190f5c..0000000 --- a/bunkialo-landing/src/app/api/bunkx/session/route.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { parseBunkxAttendancePayload } from "@/lib/bunkx-payload"; -import { createSession } from "@/lib/bunkx-session-store"; -import { NextResponse } from "next/server"; - -const toSafeErrorMessage = (error: unknown): string => { - if (!(error instanceof Error)) { - return "Invalid request payload"; - } - - const knownValidationMessages = new Set([ - "Payload must be an object", - "attendance_rows must be an array", - "Invalid attendance row", - "Invalid period_date", - "Invalid session_time", - "Invalid course_code", - "Invalid subject_name", - "Invalid faculty", - "Invalid faculty_email", - "Invalid score", - ]); - - if (knownValidationMessages.has(error.message)) { - return error.message; - } - - return "An unexpected error occurred"; -}; - -export async function POST(request: Request) { - try { - const payload = parseBunkxAttendancePayload( - (await request.json()) as unknown, - ); - const session = createSession(payload); - - return NextResponse.json(session, { - status: 201, - headers: { - "Cache-Control": "no-store", - }, - }); - } catch (error) { - const message = toSafeErrorMessage(error); - - return NextResponse.json( - { - error: message, - }, - { - status: 400, - headers: { - "Cache-Control": "no-store", - }, - }, - ); - } -} diff --git a/bunkialo-landing/src/app/bunkialo/page.tsx b/bunkialo-landing/src/app/bunkialo/page.tsx deleted file mode 100644 index 9e11a89..0000000 --- a/bunkialo-landing/src/app/bunkialo/page.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import type { BunkxAttendancePayload } from "@/lib/bunkx-payload"; -import { headers } from "next/headers"; - -interface BunkialoPageProps { - searchParams: Promise<{ sid?: string }>; -} - -export default async function BunkialoPage({ - searchParams, -}: BunkialoPageProps) { - const params = await searchParams; - const sid = params.sid?.trim(); - - if (!sid) { - return ( -
-

Missing session id

-

- Open this page from the app so attendance can be transferred securely. -

-
- ); - } - - const requestHeaders = await headers(); - const host = - requestHeaders.get("x-forwarded-host") ?? requestHeaders.get("host"); - const proto = requestHeaders.get("x-forwarded-proto") ?? "https"; - const fallbackHost = process.env.DEFAULT_HOST?.trim(); - - if (!host && !fallbackHost) { - return ( -
-

Session unavailable

-

- Unable to resolve server host. Please try again in a moment. -

-
- ); - } - - const resolvedHost = host ?? fallbackHost!; - const apiUrl = `${proto}://${resolvedHost}/api/bunkx/session/${encodeURIComponent(sid)}`; - - const response = await fetch(apiUrl, { - cache: "no-store", - }); - - let payload: BunkxAttendancePayload | null = null; - if (response.ok) { - try { - payload = (await response.json()) as BunkxAttendancePayload; - } catch { - payload = null; - } - } - - if (!payload) { - return ( -
-

Session unavailable

-

- This session has expired or was already used. Please launch Bunkx - again from the app. -

-
- ); - } - - const attendanceCount = Array.isArray(payload.attendance_rows) - ? payload.attendance_rows.length - : 0; - - return ( -
-

Bunkialo attendance handoff

-

- Received {attendanceCount} records from the mobile app. -

-
-        {JSON.stringify(payload, null, 2)}
-      
-
- ); -} diff --git a/bunkialo-landing/src/lib/bunkx-payload.ts b/bunkialo-landing/src/lib/bunkx-payload.ts deleted file mode 100644 index f9048fd..0000000 --- a/bunkialo-landing/src/lib/bunkx-payload.ts +++ /dev/null @@ -1,68 +0,0 @@ -export interface BunkxAttendanceRow { - period_date: string; - session_time: string; - course_code: string; - subject_name: string; - faculty: string; - faculty_email: string; - course?: string; - score: string; - record_id?: string; -} - -export interface BunkxAttendancePayload { - attendance_rows: BunkxAttendanceRow[]; - dataset_id?: string; - dataset_expires_at?: string; -} - -const parseString = (value: unknown, field: string): string => { - if (typeof value !== "string") { - throw new Error(`Invalid ${field}`); - } - return value; -}; - -const parseOptionalString = (value: unknown): string | undefined => { - return typeof value === "string" ? value : undefined; -}; - -const isRecord = (value: unknown): value is Record => - typeof value === "object" && value !== null; - -const parseRow = (value: unknown): BunkxAttendanceRow => { - if (!isRecord(value)) { - throw new Error("Invalid attendance row"); - } - - return { - period_date: parseString(value.period_date, "period_date"), - session_time: parseString(value.session_time, "session_time"), - course_code: parseString(value.course_code, "course_code"), - subject_name: parseString(value.subject_name, "subject_name"), - faculty: parseString(value.faculty, "faculty"), - faculty_email: parseString(value.faculty_email, "faculty_email"), - course: parseOptionalString(value.course), - score: parseString(value.score, "score"), - record_id: parseOptionalString(value.record_id), - }; -}; - -export const parseBunkxAttendancePayload = ( - value: unknown, -): BunkxAttendancePayload => { - if (!isRecord(value)) { - throw new Error("Payload must be an object"); - } - - const rows = value.attendance_rows; - if (!Array.isArray(rows)) { - throw new Error("attendance_rows must be an array"); - } - - return { - attendance_rows: rows.map(parseRow), - dataset_id: parseOptionalString(value.dataset_id), - dataset_expires_at: parseOptionalString(value.dataset_expires_at), - }; -}; diff --git a/bunkialo-landing/src/lib/bunkx-session-store.ts b/bunkialo-landing/src/lib/bunkx-session-store.ts deleted file mode 100644 index e9ca8fe..0000000 --- a/bunkialo-landing/src/lib/bunkx-session-store.ts +++ /dev/null @@ -1,73 +0,0 @@ -import type { BunkxAttendancePayload } from "@/lib/bunkx-payload"; - -interface SessionRecord { - payload: BunkxAttendancePayload; - expiresAtMs: number; -} - -interface CreatedSession { - sid: string; - expiresAt: string; -} - -const DEFAULT_TTL_MS = 10 * 60 * 1000; -const sessionStore = new Map(); - -const cleanupExpiredSessions = (): void => { - const now = Date.now(); - for (const [sid, record] of sessionStore.entries()) { - if (record.expiresAtMs <= now) { - sessionStore.delete(sid); - } - } -}; - -const generateSessionId = (): string => { - return crypto.randomUUID(); -}; - -const generateUniqueSessionId = (): string => { - const maxAttempts = 8; - - for (let attempt = 0; attempt < maxAttempts; attempt += 1) { - const sid = generateSessionId(); - if (!sessionStore.has(sid)) { - return sid; - } - } - - throw new Error("Could not generate unique session id"); -}; - -export const createSession = ( - payload: BunkxAttendancePayload, - ttlMs: number = DEFAULT_TTL_MS, -): CreatedSession => { - cleanupExpiredSessions(); - - const now = Date.now(); - const expiresAtMs = now + Math.max(60_000, ttlMs); - const sid = generateUniqueSessionId(); - - sessionStore.set(sid, { - payload, - expiresAtMs, - }); - - return { - sid, - expiresAt: new Date(expiresAtMs).toISOString(), - }; -}; - -export const consumeSession = (sid: string): BunkxAttendancePayload | null => { - cleanupExpiredSessions(); - - const existing = sessionStore.get(sid); - if (!existing) { - return null; - } - - sessionStore.delete(sid); - return existing.payload; -};