From 9dd06fa7bcb033e01a8b1775f8aadd52deed2da1 Mon Sep 17 00:00:00 2001
From: alikia2x
Date: Sun, 1 Jun 2025 16:18:01 +0800
Subject: [PATCH] add: set cookie after signing up
---
packages/backend/middleware/index.ts | 17 +++++++++--
packages/backend/routes/user/register.ts | 28 ++++++++++++++-----
packages/backend/src/schema.d.ts | 5 ++++
.../next/app/[locale]/signup/SignUpForm.tsx | 6 +++-
packages/next/app/[locale]/signup/request.tsx | 18 ++++++++++--
packages/next/components/hooks/useCaptcha.ts | 4 +--
6 files changed, 61 insertions(+), 17 deletions(-)
diff --git a/packages/backend/middleware/index.ts b/packages/backend/middleware/index.ts
index 1aa05ca..0cd2a9d 100644
--- a/packages/backend/middleware/index.ts
+++ b/packages/backend/middleware/index.ts
@@ -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);
diff --git a/packages/backend/routes/user/register.ts b/packages/backend/routes/user/register.ts
index ccab0af..0a6a080 100644
--- a/packages/backend/routes/user/register.ts
+++ b/packages/backend/routes/user/register.ts
@@ -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 => {
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>(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(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(response, 201);
} catch (e) {
if (e instanceof ValidationError) {
const response: ErrorResponse = {
diff --git a/packages/backend/src/schema.d.ts b/packages/backend/src/schema.d.ts
index 62ad606..a1a1220 100644
--- a/packages/backend/src/schema.d.ts
+++ b/packages/backend/src/schema.d.ts
@@ -38,6 +38,11 @@ interface CaptchaSessionRawResponse {
t: number;
}
+export interface SignUpResponse {
+ username: string;
+ token: string;
+}
+
export type CaptchaVerificationRawResponse = {
token: string;
}
diff --git a/packages/next/app/[locale]/signup/SignUpForm.tsx b/packages/next/app/[locale]/signup/SignUpForm.tsx
index 23497f5..69d09c0 100644
--- a/packages/next/app/[locale]/signup/SignUpForm.tsx
+++ b/packages/next/app/[locale]/signup/SignUpForm.tsx
@@ -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 = ({ 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 = ({ backendURL }) => {
await startCaptcha();
}
- trigger({
+ await trigger({
data: {
username: usernameInput,
password: passwordInput,
@@ -76,6 +78,8 @@ const SignUpForm: React.FC = ({ backendURL }) => {
setDialogContent,
t
});
+
+ router.push("/");
} finally {
setLoading(false);
}
diff --git a/packages/next/app/[locale]/signup/request.tsx b/packages/next/app/[locale]/signup/request.tsx
index e7b2613..468d035 100644
--- a/packages/next/app/[locale]/signup/request.tsx
+++ b/packages/next/app/[locale]/signup/request.tsx
@@ -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(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
);
+ } else {
+ setShowDialog(true);
+ setDialogContent(
+ setShowDialog(false)} errorCode="UNKNOWN_ERROR">
+ 无法为你注册账户。
+
+ 错误信息:
+
{JSON.stringify(error)}
+
+
+ );
}
}
};
diff --git a/packages/next/components/hooks/useCaptcha.ts b/packages/next/components/hooks/useCaptcha.ts
index 60e5986..c9f99cc 100644
--- a/packages/next/components/hooks/useCaptcha.ts
+++ b/packages/next/components/hooks/useCaptcha.ts
@@ -16,8 +16,6 @@ export function useCaptcha({ backendURL, route }: UseCaptchaOptions) {
const { trigger, data, isMutating, error } = useSWRMutation(
fullUrl,
async (url: string) => {
- setIsUsed(false);
-
const sessionRes = await fetcher(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(resultUrl.toString());
-
+ setIsUsed(false);
return result;
}
);