summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorpolwex <polwex@sortug.com>2025-05-29 14:52:38 +0700
committerpolwex <polwex@sortug.com>2025-05-29 14:52:38 +0700
commit490388360a0852bcf8ee054e96fa90e166df5792 (patch)
tree3940097c5505ff1bb09875dddb7ee0e4881beb77 /src
parentf243847216279cbd43879de8b5ef6dcceb3a2f1d (diff)
fucker actually solved the cookies, love ya man
Diffstat (limited to 'src')
-rw-r--r--src/actions/deck.ts3
-rw-r--r--src/actions/login.ts31
-rw-r--r--src/components/Login2.tsx20
-rw-r--r--src/lib/db/seed.ts1
-rw-r--r--src/lib/hooks/useCookie.ts33
-rw-r--r--src/lib/server/cookie.ts47
-rw-r--r--src/lib/server/cookiebridge.ts56
-rw-r--r--src/lib/server/setcookie.ts11
-rw-r--r--src/pages.gen.ts50
-rw-r--r--src/pages/study.tsx53
10 files changed, 145 insertions, 160 deletions
diff --git a/src/actions/deck.ts b/src/actions/deck.ts
index e501f23..5812715 100644
--- a/src/actions/deck.ts
+++ b/src/actions/deck.ts
@@ -1,7 +1,6 @@
"use server";
-import ServerWord from "@/zoom/ServerWord";
-import { analyzeTHWord, segmentateThai } from "@/pages/api/nlp";
import db from "../lib/db";
+import { analyzeTHWord, segmentateThai } from "@/lib/calls/nlp";
export function shuffleDeck(userId: number, lessonId: number) {
const res = db.fetchLesson({ userId, lessonId, random: true });
diff --git a/src/actions/login.ts b/src/actions/login.ts
index ee15fe6..ed96f54 100644
--- a/src/actions/login.ts
+++ b/src/actions/login.ts
@@ -50,20 +50,35 @@ export async function postLogin(
console.log({ res });
if ("error" in res) return { error: res.error };
else {
- setCookie(res.ok as number);
- return { success: true };
+ // Set the cookie
+ await setCookie(res.ok as number);
+
+ // Return success for client-side handling
+ return {
+ success: true,
+ userId: res.ok,
+ redirect: "/"
+ };
}
}
async function setCookie(userId: number) {
+ // Set cookie expiry for 30 days
const COOKIE_EXPIRY = Date.now() + 1000 * 60 * 60 * 24 * 30;
- const COOKIE_OPTS = { expires: new Date(COOKIE_EXPIRY) };
-
+
+ // Generate a secure random token for the cookie
const { randomBytes } = await import("node:crypto");
- const cokistring = randomBytes(32).toBase64();
- const res = db.setCookie(cokistring, userId, COOKIE_EXPIRY);
+ const cookieToken = randomBytes(32).toString("base64");
+
+ // Store the cookie in the database
+ const res = db.setCookie(cookieToken, userId, COOKIE_EXPIRY);
+
+ // Set the cookie in the response
const { setCookie } = useCookies();
- setCookie(cokistring);
- // unstable_redirect("/");
+ setCookie(cookieToken);
+
+ console.log("Cookie set for user ID:", userId);
+
+ // Redirect is managed by client after successful login
}
// export async function postLogout(prev: number) {
diff --git a/src/components/Login2.tsx b/src/components/Login2.tsx
index 6c26efc..7adf09c 100644
--- a/src/components/Login2.tsx
+++ b/src/components/Login2.tsx
@@ -42,17 +42,18 @@ function OOldform({ isReg, toggle }: { isReg: boolean; toggle: () => void }) {
else setStrings(logstrings);
}, [isReg]);
- // const [state, formAction, isPending] = useActionState<FormState, FormData>(
- // isReg ? postRegister : postLogin,
- // { error: "" },
- // "/login",
- // );
const [state, formAction, isPending] = useActionState<FormState, FormData>(
- postLogin,
+ isReg ? postRegister : postLogin,
{ error: "" },
"/login",
);
console.log({ state });
+
+ // Handle redirect after successful login
+ if (state.success && state.redirect) {
+ window.location.href = state.redirect;
+ }
+
return (
<form action={formAction}>
<div className="flex flex-col gap-6">
@@ -87,8 +88,8 @@ function OOldform({ isReg, toggle }: { isReg: boolean; toggle: () => void }) {
{state.password && <p>{state.password}</p>}
</Label>
</div>
- <Button type="submit" className="w-full">
- {strings.button}
+ <Button type="submit" className="w-full" disabled={isPending}>
+ {isPending ? "Loading..." : strings.button}
</Button>
<div className="text-center text-sm">
{strings.toggle}
@@ -101,7 +102,8 @@ function OOldform({ isReg, toggle }: { isReg: boolean; toggle: () => void }) {
</a>
</div>
</div>
- {state.error && <p className="text-red">{state.error}</p>}
+ {state.error && <p className="text-red-500">{state.error}</p>}
+ {state.success && <p className="text-green-500">Login successful! Redirecting...</p>}
</CardContent>
</Card>
<div className="text-balance text-center text-xs text-muted-foreground [&_a]:underline [&_a]:underline-offset-4 [&_a]:hover:text-primary ">
diff --git a/src/lib/db/seed.ts b/src/lib/db/seed.ts
index a196b6a..6c2a9f7 100644
--- a/src/lib/db/seed.ts
+++ b/src/lib/db/seed.ts
@@ -3,7 +3,6 @@ import { getStressedSyllable, getSyllableCount } from "../utils";
import useful from "@/lib/useful_thai.json";
import db from ".";
import pdb from "./prosodydb";
-import * as Sorsyl from "sorsyl";
import { findLemma } from "../calls/nlp";
const SYMBOL_REGEX = new RegExp(/[\W\d]/);
diff --git a/src/lib/hooks/useCookie.ts b/src/lib/hooks/useCookie.ts
index 904738a..cb3a019 100644
--- a/src/lib/hooks/useCookie.ts
+++ b/src/lib/hooks/useCookie.ts
@@ -1,35 +1,12 @@
import { getContext } from "waku/middleware/context";
-import { mergeSetCookies } from "./setcookie";
+
+// This file appears to be unused - all cookie functionality is now in cookiebridge.ts
+// Left as a placeholder in case it's referenced elsewhere
const useCookies = () => {
const ctx = getContext();
- console.log(ctx.req, "cookie bridge");
- const headers = ctx.req.headers;
- console.log({ headers });
- return "hi";
-
- // const headerObj = ctx.headers || {};
- // headerObj["set-cookie"] = mergeSetCookies(
- // headerObj["set-cookie"] || [],
- // (ctx.cookies || []) as ResponseCookie[],
- // );
- // const headers = new Headers(headerObj as Record<string, string>);
- // const reqCookies = new RequestCookies(headers);
- // const resCookies = new ResponseCookies(headers);
-
- // const getCookie: ResponseCookies["get"] = (...args) =>
- // resCookies.get(...args) || reqCookies.get(...args);
- // const setCookie: ResponseCookies["set"] = (...args) => {
- // const updated = resCookies.set(...args);
- // ctx.cookies = updated.getAll();
- // return updated;
- // };
- // const delCookie: ResponseCookies["delete"] = (...args) => {
- // const updated = resCookies.delete(...args);
- // ctx.cookies = updated.getAll();
- // return updated;
- // };
- // return { getCookie, setCookie, delCookie };
+ console.log("WARNING: useCookie.ts is deprecated, use cookiebridge.ts instead");
+ return "DEPRECATED";
};
export { useCookies };
diff --git a/src/lib/server/cookie.ts b/src/lib/server/cookie.ts
index 9a7e632..32894b9 100644
--- a/src/lib/server/cookie.ts
+++ b/src/lib/server/cookie.ts
@@ -1,41 +1,36 @@
-import { getHonoContext } from "waku/unstable_hono";
import cookie from "cookie";
import db from "../db";
-
import type { Middleware } from "waku/config";
const cookieMiddleware: Middleware = () => {
- console.log("cookieMiddleware executed");
return async (ctx, next) => {
+ // Parse incoming cookies
const cookies = cookie.parse(ctx.req.headers.cookie || "");
const coki = cookies.sorlang;
- // if (!coki) {
- // if (ctx.req.url.pathname === "/login") return await next();
- // ctx.res.status = 301;
- // ctx.res.headers = {
- // Location: "/login",
- // };
- // }
+
+ // If cookie exists, fetch user data and set in context
if (coki) {
const userRow = db.fetchCookie(coki);
- // console.log({ userRow });
- if (userRow) ctx.data.user = { id: userRow.id, name: userRow.name };
- // else {
- // if (ctx.req.url.pathname === "/login") return await next();
- // ctx.res.status = 301;
- // ctx.res.headers = {
- // Location: "/login",
- // };
- // }
+ if (userRow) {
+ ctx.data.user = { id: userRow.id, name: userRow.name };
+ console.log("User authenticated:", userRow.name);
+ }
+ }
+
+ // Uncomment to enable redirection for unauthenticated users
+ /*
+ if (!ctx.data.user && ctx.req.url.pathname !== "/login") {
+ ctx.res.status = 302;
+ ctx.res.headers = {
+ Location: "/login",
+ };
+ return;
}
+ */
+
await next();
- const hctx: any = getHonoContext();
- // console.log("hono", hctx.lol);
- // console.log("ctx coki", ctx.data.cookie);
- ctx.res.headers ||= {};
- if (ctx.data.cookie)
- ctx.res.headers["set-cookie"] = ctx.data.cookie as string;
- ctx.res.headers["set-lmao"] = "wtf man";
+
+ // Cookie setting is now handled by setCookieMiddleware
};
};
diff --git a/src/lib/server/cookiebridge.ts b/src/lib/server/cookiebridge.ts
index 778fc2c..613479b 100644
--- a/src/lib/server/cookiebridge.ts
+++ b/src/lib/server/cookiebridge.ts
@@ -1,52 +1,32 @@
import { getContext, getContextData } from "waku/middleware/context";
+import cookie from "cookie";
const useCookies = () => {
const ctx = getContext();
const headers = ctx.req.headers;
- console.log(headers.cookie);
-
- const getCookie = (s: string) => {
- const coki = headers.cookie;
- if (!coki) return {};
- const cokiMap = parseCoki(coki);
- return cokiMap;
+
+ const getCookie = (name: string) => {
+ const cookieHeader = headers.cookie;
+ if (!cookieHeader) return null;
+
+ const cookies = cookie.parse(cookieHeader);
+ return cookies[name];
};
- const setCookie = (s: string) => {
+
+ const setCookie = (value: string) => {
const ctxdata = getContextData();
- // (ctxdata.cokimap as Record<string, string>).sorlang = s;
- // ctxdata.cookie = `sorlang=${s}; Secure`
- ctxdata.cookie = `sorlang=${s};`;
+ // Set cookie with proper attributes for security and functionality
+ ctxdata.cookie = `sorlang=${value}; Path=/; HttpOnly; SameSite=Lax; Max-Age=2592000`;
+ console.log("Cookie value being set:", value);
};
- const delCookie = (s: string) => {
+
+ const delCookie = (name: string) => {
const ctxdata = getContextData();
- delete (ctxdata.cokimap as Record<string, string>).sorlang;
+ // Set an expired cookie to delete it
+ ctxdata.cookie = `${name}=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT`;
};
+
return { getCookie, setCookie, delCookie };
};
export { useCookies };
-
-function parseCoki(s: string) {
- return s
- .split(";")
- .map((v) => v.split("="))
- .reduce((acc: Record<string, string>, v: any) => {
- acc[decodeURIComponent(v[0].trim())] = decodeURIComponent(v[1].trim());
- return acc;
- }, {});
-}
-function parseSetCoki(s: string) {
- return s
- .split(";")
- .map((v) => v.split("="))
- .reduce((acc: Record<string, string>, v: any) => {
- acc[decodeURIComponent(v[0].trim())] = decodeURIComponent(v[1].trim());
- return acc;
- }, {});
-}
-function cokiToString(m: Record<string, string>): string {
- return Object.entries(m).reduce((acc: string, item: [string, string]) => {
- const [key, val] = item;
- return `${acc} ${key}=${val};`;
- }, "");
-}
diff --git a/src/lib/server/setcookie.ts b/src/lib/server/setcookie.ts
index 61da128..10ca489 100644
--- a/src/lib/server/setcookie.ts
+++ b/src/lib/server/setcookie.ts
@@ -3,8 +3,17 @@ import type { Middleware } from "waku/config";
const setCookieMiddleware: Middleware = () => {
return async (ctx, next) => {
await next();
+
+ // Ensure headers object exists
ctx.res.headers ||= {};
- ctx.res.headers["set-cookie"] = ctx.data.cookie as string;
+
+ // Only set the cookie header if we have a cookie to set
+ if (ctx.data.cookie) {
+ ctx.res.headers["set-cookie"] = ctx.data.cookie as string;
+
+ // Debugging
+ console.log("Setting cookie header:", ctx.data.cookie);
+ }
};
};
diff --git a/src/pages.gen.ts b/src/pages.gen.ts
index b4a4bd7..d8c992f 100644
--- a/src/pages.gen.ts
+++ b/src/pages.gen.ts
@@ -4,49 +4,51 @@
import type { PathsForPages, GetConfigResponse } from 'waku/router';
// prettier-ignore
-import type { getConfig as Zoom_getConfig } from './pages/zoom';
+import type { getConfig as File_Zoom_getConfig } from './pages/zoom';
// prettier-ignore
-import type { getConfig as LangSlug_getConfig } from './pages/lang/[slug]';
+import type { getConfig as File_LangSlug_getConfig } from './pages/lang/[slug]';
// prettier-ignore
-import type { getConfig as LessonSlug_getConfig } from './pages/lesson/[slug]';
+import type { getConfig as File_LessonSlug_getConfig } from './pages/lesson/[slug]';
// prettier-ignore
-import type { getConfig as Login_getConfig } from './pages/login';
+import type { getConfig as File_Login_getConfig } from './pages/login';
// prettier-ignore
-import type { getConfig as Parse_getConfig } from './pages/parse';
+import type { getConfig as File_Parse_getConfig } from './pages/parse';
// prettier-ignore
-import type { getConfig as Db_getConfig } from './pages/db';
+import type { getConfig as File_Db_getConfig } from './pages/db';
// prettier-ignore
-import type { getConfig as Form_getConfig } from './pages/form';
+import type { getConfig as File_Form_getConfig } from './pages/form';
// prettier-ignore
-import type { getConfig as Tones_getConfig } from './pages/tones';
+import type { getConfig as File_Tones_getConfig } from './pages/tones';
// prettier-ignore
-import type { getConfig as Picker_getConfig } from './pages/picker';
+import type { getConfig as File_Picker_getConfig } from './pages/picker';
// prettier-ignore
-import type { getConfig as About_getConfig } from './pages/about';
+import type { getConfig as File_About_getConfig } from './pages/about';
// prettier-ignore
-import type { getConfig as LogintestIndex_getConfig } from './pages/logintest/index';
+import type { getConfig as File_LogintestIndex_getConfig } from './pages/logintest/index';
// prettier-ignore
-import type { getConfig as Index_getConfig } from './pages/index';
+import type { getConfig as File_Index_getConfig } from './pages/index';
// prettier-ignore
type Page =
-| ({ path: '/zoom' } & GetConfigResponse<typeof Zoom_getConfig>)
-| ({ path: '/lang/[slug]' } & GetConfigResponse<typeof LangSlug_getConfig>)
-| ({ path: '/lesson/[slug]' } & GetConfigResponse<typeof LessonSlug_getConfig>)
-| ({ path: '/login' } & GetConfigResponse<typeof Login_getConfig>)
-| ({ path: '/parse' } & GetConfigResponse<typeof Parse_getConfig>)
-| ({ path: '/db' } & GetConfigResponse<typeof Db_getConfig>)
+| ({ path: '/zoom' } & GetConfigResponse<typeof File_Zoom_getConfig>)
+| ({ path: '/lang/[slug]' } & GetConfigResponse<typeof File_LangSlug_getConfig>)
+| ({ path: '/lesson/[slug]' } & GetConfigResponse<typeof File_LessonSlug_getConfig>)
+| ({ path: '/login' } & GetConfigResponse<typeof File_Login_getConfig>)
+| ({ path: '/parse' } & GetConfigResponse<typeof File_Parse_getConfig>)
+| ({ path: '/db' } & GetConfigResponse<typeof File_Db_getConfig>)
+| { path: '/study'; render: 'dynamic' }
| { path: '/test/client-modal'; render: 'dynamic' }
| { path: '/test/trigger-modal-button'; render: 'dynamic' }
| { path: '/test'; render: 'dynamic' }
-| ({ path: '/form' } & GetConfigResponse<typeof Form_getConfig>)
-| ({ path: '/tones' } & GetConfigResponse<typeof Tones_getConfig>)
-| ({ path: '/picker' } & GetConfigResponse<typeof Picker_getConfig>)
-| ({ path: '/about' } & GetConfigResponse<typeof About_getConfig>)
+| ({ path: '/form' } & GetConfigResponse<typeof File_Form_getConfig>)
+| ({ path: '/tones' } & GetConfigResponse<typeof File_Tones_getConfig>)
+| ({ path: '/picker' } & GetConfigResponse<typeof File_Picker_getConfig>)
+| { path: '/lessons'; render: 'dynamic' }
+| ({ path: '/about' } & GetConfigResponse<typeof File_About_getConfig>)
| { path: '/logintest/Form'; render: 'dynamic' }
-| ({ path: '/logintest' } & GetConfigResponse<typeof LogintestIndex_getConfig>)
+| ({ path: '/logintest' } & GetConfigResponse<typeof File_LogintestIndex_getConfig>)
| { path: '/logintest/ServerForm'; render: 'dynamic' }
-| ({ path: '/' } & GetConfigResponse<typeof Index_getConfig>);
+| ({ path: '/' } & GetConfigResponse<typeof File_Index_getConfig>);
// prettier-ignore
declare module 'waku/router' {
diff --git a/src/pages/study.tsx b/src/pages/study.tsx
index db7dde7..f818b4b 100644
--- a/src/pages/study.tsx
+++ b/src/pages/study.tsx
@@ -1,3 +1,4 @@
+import { getContextData } from "waku/middleware/context";
import { useState } from "react";
import { getState } from "@/lib/db";
import { startStudySession } from "@/actions/srs";
@@ -8,17 +9,24 @@ import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
// This is a server component that gets the initial data
-export default async function StudyPage({ searchParams }: { searchParams: { lessonId?: string } }) {
- const state = getState(null);
- const userId = state.user?.id;
-
+export default async function StudyPage({
+ searchParams,
+}: {
+ searchParams: { lessonId?: string };
+}) {
+ const { user } = getContextData() as any;
+ // const state = getState(null);
+ const userId = user?.id;
+
// If not logged in, show login required message
if (!userId) {
return (
<div className="container mx-auto py-8">
<Card className="p-6 text-center">
<h1 className="text-2xl font-bold mb-4">Login Required</h1>
- <p className="mb-4">You need to be logged in to use the study session feature.</p>
+ <p className="mb-4">
+ You need to be logged in to use the study session feature.
+ </p>
<Button asChild>
<a href="/login">Login</a>
</Button>
@@ -26,14 +34,16 @@ export default async function StudyPage({ searchParams }: { searchParams: { less
</div>
);
}
-
- const lessonId = searchParams.lessonId ? parseInt(searchParams.lessonId, 10) : null;
-
+
+ const lessonId = searchParams.lessonId
+ ? parseInt(searchParams.lessonId, 10)
+ : null;
+
// If no lesson ID provided, show lesson selector
if (!lessonId) {
return <LessonSelector userId={userId} />;
}
-
+
// Get initial data for the study session
let initialData;
try {
@@ -41,13 +51,15 @@ export default async function StudyPage({ searchParams }: { searchParams: { less
} catch (error) {
console.error("Error starting study session:", error);
}
-
+
return (
<div className="container mx-auto py-8">
<StudySession
userId={userId}
lessonId={lessonId}
- initialData={initialData && !('error' in initialData) ? initialData : undefined}
+ initialData={
+ initialData && !("error" in initialData) ? initialData : undefined
+ }
/>
</div>
);
@@ -56,12 +68,12 @@ export default async function StudyPage({ searchParams }: { searchParams: { less
// Client component for selecting a lesson
function LessonSelector({ userId }: { userId: number }) {
const [lessonId, setLessonId] = useState<string>("");
-
+
return (
<div className="container mx-auto py-8">
<Card className="p-6 max-w-md mx-auto">
<h1 className="text-2xl font-bold mb-6">Start Study Session</h1>
-
+
<form action={`/study?lessonId=${lessonId}`}>
<div className="space-y-4">
<div>
@@ -75,13 +87,13 @@ function LessonSelector({ userId }: { userId: number }) {
required
/>
</div>
-
+
<Button type="submit" className="w-full">
Start Study Session
</Button>
</div>
</form>
-
+
<div className="mt-6 pt-6 border-t border-gray-200">
<h2 className="text-lg font-medium mb-3">Available Lessons</h2>
<p className="text-sm text-gray-500 mb-4">
@@ -110,14 +122,9 @@ function LessonSelector({ userId }: { userId: number }) {
Lesson 5
</Button>
</div>
-
+
<div className="mt-4">
- <Button
- variant="ghost"
- size="sm"
- asChild
- className="text-blue-500"
- >
+ <Button variant="ghost" size="sm" asChild className="text-blue-500">
<a href="/">Back to Home</a>
</Button>
</div>
@@ -125,4 +132,4 @@ function LessonSelector({ userId }: { userId: number }) {
</Card>
</div>
);
-} \ No newline at end of file
+}