add: set cookie after signing up
This commit is contained in:
parent
bb7f846305
commit
9dd06fa7bc
@ -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);
|
||||
|
@ -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> = {
|
||||
|
5
packages/backend/src/schema.d.ts
vendored
5
packages/backend/src/schema.d.ts
vendored
@ -38,6 +38,11 @@ interface CaptchaSessionRawResponse {
|
||||
t: number;
|
||||
}
|
||||
|
||||
export interface SignUpResponse {
|
||||
username: string;
|
||||
token: string;
|
||||
}
|
||||
|
||||
export type CaptchaVerificationRawResponse = {
|
||||
token: string;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user