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;
-};