add: set cookie after signing up

This commit is contained in:
alikia2x (寒寒) 2025-06-01 16:18:01 +08:00
parent bb7f846305
commit 9dd06fa7bc
Signed by: alikia2x
GPG Key ID: 56209E0CCD8420C6
6 changed files with 61 additions and 17 deletions

View File

@ -1,4 +1,4 @@
import { Hono } from "hono";
import { Context, Hono } from "hono";
import { Variables } from "hono/types";
import { bodyLimitForPing } from "./bodyLimits.ts";
import { pingHandler } from "routes/ping";
@ -8,10 +8,21 @@ import { logger } from "./logger.ts";
import { timing } from "hono/timing";
import { contentType } from "./contentType.ts";
import { captchaMiddleware } from "./captcha.ts";
import { cors } from 'hono/cors';
import { cors } from "hono/cors";
export function configureMiddleWares(app: Hono<{ Variables: Variables }>) {
app.all("*", cors());
app.use("*", async (c, next) => {
if (c.req.path === "/user") {
const corsMiddlewareHandler = cors({
origin: c.req.header("Origin"),
credentials: true
});
return corsMiddlewareHandler(c, next);
}
const corsMiddlewareHandler = cors();
return corsMiddlewareHandler(c, next);
});
app.use("*", contentType);
app.use(timing());
app.use("*", preetifyResponse);

View File

@ -4,9 +4,10 @@ import { object, string, ValidationError } from "yup";
import type { Context } from "hono";
import type { Bindings, BlankEnv, BlankInput } from "hono/types";
import { sqlCred } from "@core/db/dbNew.ts";
import { ErrorResponse, StatusResponse } from "src/schema";
import { ErrorResponse, SignUpResponse } from "src/schema";
import { generateRandomId } from "@core/lib/randomID";
import { getUserIP } from "@/middleware/rateLimiters";
import { setCookie } from "hono/cookie";
const RegistrationBodySchema = object({
username: string().trim().required("Username is required").max(50, "Username cannot exceed 50 characters"),
@ -25,14 +26,15 @@ export const userExists = async (username: string) => {
return result.length > 0;
};
const createLoginSession = async (uid: number, ua?: string, ip?: string) => {
const createLoginSession = async (uid: number, ua?: string, ip?: string): Promise<string> => {
const ip_address = ip || null;
const user_agent = ua || null;
const id = generateRandomId(16);
const id = generateRandomId(24);
await sqlCred`
INSERT INTO login_sessions (id, uid, expire_at, ip_address, user_agent)
VALUES (${id}, ${uid}, CURRENT_TIMESTAMP + INTERVAL '1 year', ${ip_address}, ${user_agent})
`;
return id;
};
const getUserIDByName = async (username: string) => {
@ -91,13 +93,25 @@ export const registerHandler = createHandlers(async (c: ContextType) => {
return c.json<ErrorResponse<string>>(response, 500);
}
createLoginSession(uid, c.req.header("User-Agent"), getUserIP(c));
const sessionID = await createLoginSession(uid, c.req.header("User-Agent"), getUserIP(c));
const response: StatusResponse = {
message: `User '${username}' registered successfully.`
const response: SignUpResponse = {
username: username,
token: sessionID
};
return c.json<StatusResponse>(response, 201);
const A_YEAR = 365 * 86400;
const isDev = process.env.NODE_ENV === "development";
setCookie(c, "session_id", sessionID, {
path: "/",
maxAge: A_YEAR,
domain: process.env.DOMAIN,
secure: isDev ? false : true,
sameSite: "Lax"
});
return c.json<SignUpResponse>(response, 201);
} catch (e) {
if (e instanceof ValidationError) {
const response: ErrorResponse<string> = {

View File

@ -38,6 +38,11 @@ interface CaptchaSessionRawResponse {
t: number;
}
export interface SignUpResponse {
username: string;
token: string;
}
export type CaptchaVerificationRawResponse = {
token: string;
}

View File

@ -13,6 +13,7 @@ import { requestSignUp } from "./request";
import { FilledButton } from "@/components/ui/Buttons/FilledButton";
import { ErrorDialog } from "./ErrorDialog";
import { ApiRequestError } from "@/lib/net";
import { useRouter } from "next/navigation";
setLocale({
mixed: {
@ -49,6 +50,7 @@ const SignUpForm: React.FC<RegistrationFormProps> = ({ backendURL }) => {
route: "POST-/user"
});
const { trigger } = useSWRMutation(`${backendURL}/user`, requestSignUp);
const router = useRouter();
const translateErrorMessage = (item: LocalizedMessage | string, path?: string) => {
if (typeof item === "string") {
@ -63,7 +65,7 @@ const SignUpForm: React.FC<RegistrationFormProps> = ({ backendURL }) => {
await startCaptcha();
}
trigger({
await trigger({
data: {
username: usernameInput,
password: passwordInput,
@ -76,6 +78,8 @@ const SignUpForm: React.FC<RegistrationFormProps> = ({ backendURL }) => {
setDialogContent,
t
});
router.push("/");
} finally {
setLoading(false);
}

View File

@ -1,6 +1,6 @@
import { Dispatch, JSX, SetStateAction } from "react";
import { ApiRequestError, fetcher } from "@/lib/net";
import type { CaptchaVerificationRawResponse, ErrorResponse } from "@backend/src/schema";
import type { CaptchaVerificationRawResponse, ErrorResponse, SignUpResponse } from "@backend/src/schema";
import Link from "next/link";
import { LocalizedMessage } from "./SignUpForm";
import { ErrorDialog } from "./ErrorDialog";
@ -91,8 +91,9 @@ export const requestSignUp = async (url: string, { arg }: { arg: RequestSignUpAr
throw err;
}
setCaptchaUsedState(true);
const registrationResponse = await fetcher(url, {
const registrationResponse = await fetcher<SignUpResponse>(url, {
method: "POST",
withCredentials: true,
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${captchaResult!.token}`
@ -105,7 +106,7 @@ export const requestSignUp = async (url: string, { arg }: { arg: RequestSignUpAr
});
return registrationResponse;
} catch (error) {
if (error instanceof ApiRequestError) {
if (error instanceof ApiRequestError && error.response) {
const res = error.response as ErrorResponse;
setShowDialog(true);
setDialogContent(
@ -134,6 +135,17 @@ export const requestSignUp = async (url: string, { arg }: { arg: RequestSignUpAr
</p>
</ErrorDialog>
);
} else {
setShowDialog(true);
setDialogContent(
<ErrorDialog closeDialog={() => setShowDialog(false)} errorCode="UNKNOWN_ERROR">
<p></p>
<p>
<br />
<pre className="break-all">{JSON.stringify(error)}</pre>
</p>
</ErrorDialog>
);
}
}
};

View File

@ -16,8 +16,6 @@ export function useCaptcha({ backendURL, route }: UseCaptchaOptions) {
const { trigger, data, isMutating, error } = useSWRMutation<CaptchaVerificationRawResponse, Error>(
fullUrl,
async (url: string) => {
setIsUsed(false);
const sessionRes = await fetcher<CaptchaSessionRawResponse>(url, {
method: "POST",
headers: {
@ -37,7 +35,7 @@ export function useCaptcha({ backendURL, route }: UseCaptchaOptions) {
resultUrl.searchParams.set("ans", ans.result.toString());
const result = await fetcher<CaptchaVerificationRawResponse>(resultUrl.toString());
setIsUsed(false);
return result;
}
);