Switch all db storage to session storage
All checks were successful
Build And Deploy / build-and-deploy (push) Successful in 1m27s
All checks were successful
Build And Deploy / build-and-deploy (push) Successful in 1m27s
Only affects CapAdapter, challenge.ts, and redeem.ts.
This commit is contained in:
parent
1fbcbf772a
commit
6c56b203d3
4 changed files with 56 additions and 87 deletions
|
|
@ -4,7 +4,7 @@ import type { ActionAPIContext } from "astro:actions";
|
|||
import validator from "validator";
|
||||
import SmsClient from "@lib/SmsGatewayClient.ts";
|
||||
import Otp, { verifyOtp } from "@lib/Otp.ts";
|
||||
import CapServer from "@lib/CapAdapter";
|
||||
import { createCap } from "@lib/CapAdapter";
|
||||
import {
|
||||
OTP_SUPER_SECRET_SALT,
|
||||
ANDROID_SMS_GATEWAY_RECIPIENT_PHONE,
|
||||
|
|
@ -81,10 +81,12 @@ const submitActionDefinition = {
|
|||
});
|
||||
}
|
||||
|
||||
const cap = createCap(context.session ?? null);
|
||||
|
||||
if (
|
||||
!(
|
||||
/^[a-fA-F0-9]{16}:[a-fA-F0-9]{30}$/.test(input.captcha) &&
|
||||
(await CapServer.validateToken(input.captcha))
|
||||
(await cap.validateToken(input.captcha))
|
||||
)
|
||||
) {
|
||||
throw new ActionError({
|
||||
|
|
|
|||
|
|
@ -1,85 +1,42 @@
|
|||
import Cap, { type ChallengeData } from "@cap.js/server";
|
||||
import { db, eq, and, gt, lte, Cap_Challenges, Cap_Tokens } from "astro:db";
|
||||
import type { AstroSession } from "astro";
|
||||
|
||||
const cap = new Cap({
|
||||
export function createCap(session: AstroSession<any> | null) {
|
||||
if (!session) {
|
||||
throw new Error("Session context is required");
|
||||
}
|
||||
return new Cap({
|
||||
storage: {
|
||||
challenges: {
|
||||
store: async (token: string, challengeData: ChallengeData) => {
|
||||
const expires = challengeData.expires;
|
||||
const data = challengeData.challenge;
|
||||
await db
|
||||
.insert(Cap_Challenges)
|
||||
.values({ token: token, data: data, expires: expires })
|
||||
.onConflictDoUpdate({
|
||||
target: Cap_Challenges.token,
|
||||
set: { data: data, expires: expires },
|
||||
});
|
||||
session.set(`cap:challenge:${token}`, JSON.stringify(challengeData));
|
||||
},
|
||||
read: async (token) => {
|
||||
const result = await db
|
||||
.select({
|
||||
challenge: Cap_Challenges.data,
|
||||
expires: Cap_Challenges.expires,
|
||||
})
|
||||
.from(Cap_Challenges)
|
||||
.where(
|
||||
and(
|
||||
eq(Cap_Challenges.token, token),
|
||||
gt(Cap_Challenges.expires, Date.now()),
|
||||
),
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
const data = result[0] as ChallengeData;
|
||||
|
||||
return result ? data : null;
|
||||
read: async (token: string) => {
|
||||
const raw = await session.get(`cap:challenge:${token}`);
|
||||
return raw ? (JSON.parse(raw) as ChallengeData) : null;
|
||||
},
|
||||
|
||||
delete: async (token) => {
|
||||
await db.delete(Cap_Challenges).where(eq(Cap_Challenges.token, token));
|
||||
delete: async (token: string) => {
|
||||
session.delete(`cap:challenge:${token}`);
|
||||
},
|
||||
|
||||
deleteExpired: async () => {
|
||||
await db
|
||||
.delete(Cap_Challenges)
|
||||
.where(lte(Cap_Challenges.expires, Date.now()));
|
||||
// no-op: session store handles TTL itself
|
||||
},
|
||||
},
|
||||
tokens: {
|
||||
store: async (tokenKey, expires) => {
|
||||
await db
|
||||
.insert(Cap_Tokens)
|
||||
.values({ key: tokenKey, expires: expires })
|
||||
.onConflictDoUpdate({
|
||||
target: Cap_Tokens.key,
|
||||
set: { expires: expires },
|
||||
});
|
||||
store: async (tokenKey: string, expires: number) => {
|
||||
session.set(`cap:token:${tokenKey}`, String(expires));
|
||||
},
|
||||
|
||||
get: async (tokenKey) => {
|
||||
const result = await db
|
||||
.select({ expires: Cap_Tokens.expires })
|
||||
.from(Cap_Tokens)
|
||||
.where(
|
||||
and(
|
||||
eq(Cap_Tokens.key, tokenKey),
|
||||
gt(Cap_Tokens.expires, Date.now()),
|
||||
),
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
return result ? result[0].expires : null;
|
||||
get: async (tokenKey: string) => {
|
||||
const raw = await session.get(`cap:token:${tokenKey}`);
|
||||
return raw ? Number(raw) : null;
|
||||
},
|
||||
|
||||
delete: async (tokenKey) => {
|
||||
await db.delete(Cap_Tokens).where(eq(Cap_Tokens.key, tokenKey));
|
||||
delete: async (tokenKey: string) => {
|
||||
session.delete(`cap:token:${tokenKey}`);
|
||||
},
|
||||
|
||||
deleteExpired: async () => {
|
||||
await db.delete(Cap_Tokens).where(lte(Cap_Tokens.expires, Date.now()));
|
||||
// no-op: session store handles TTL itself
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default cap;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import type { APIRoute } from "astro";
|
||||
import cap from "@lib/CapAdapter";
|
||||
import { createCap } from "@lib/CapAdapter";
|
||||
export const prerender = false;
|
||||
|
||||
export const POST: APIRoute = async () => {
|
||||
export const POST: APIRoute = async (context) => {
|
||||
try {
|
||||
const cap = createCap(context.session ?? null);
|
||||
return new Response(JSON.stringify(await cap.createChallenge()), {
|
||||
status: 200,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,12 +1,21 @@
|
|||
import type { APIRoute } from "astro";
|
||||
import cap from "@lib/CapAdapter";
|
||||
import { createCap } from "@lib/CapAdapter";
|
||||
export const prerender = false;
|
||||
|
||||
export const POST: APIRoute = async ({ request }) => {
|
||||
const { token, solutions } = await request.json();
|
||||
export const POST: APIRoute = async (context) => {
|
||||
if (!context.session) {
|
||||
return new Response(
|
||||
JSON.stringify({ success: false, error: "Session unavailable." }),
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
|
||||
const { token, solutions } = await context.request.json();
|
||||
if (!token || !solutions) {
|
||||
return new Response(JSON.stringify({ success: false }), { status: 400 });
|
||||
}
|
||||
|
||||
const cap = createCap(context.session);
|
||||
return new Response(
|
||||
JSON.stringify(await cap.redeemChallenge({ token, solutions })),
|
||||
{ status: 200 },
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue