feat: worst-scan fansub web — initial release
Posts engine (SQLite + auto-publisher via poller), public feed with clickable tags, reader, admin panel, submit/search/queue tools. BFF proxy to pipeline API. Clean dark design. Docker-ready.
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
import { SignJWT, jwtVerify } from "jose"
|
||||
|
||||
const SECRET = new TextEncoder().encode(
|
||||
process.env.JWT_SECRET || process.env.WEB_PASSWORD || "worst-scan-web-dev-secret",
|
||||
)
|
||||
|
||||
const COOKIE_NAME = "session"
|
||||
|
||||
export interface SessionPayload {
|
||||
authenticated: boolean
|
||||
timestamp: number
|
||||
}
|
||||
|
||||
export async function createSession(): Promise<string> {
|
||||
return new SignJWT({ authenticated: true, timestamp: Date.now() })
|
||||
.setProtectedHeader({ alg: "HS256" })
|
||||
.setExpirationTime("7d")
|
||||
.sign(SECRET)
|
||||
}
|
||||
|
||||
export async function verifySession(token: string): Promise<SessionPayload | null> {
|
||||
try {
|
||||
const { payload } = await jwtVerify(token, SECRET)
|
||||
return payload as unknown as SessionPayload
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export function sessionCookieOptions(): { name: string; options: { httpOnly: boolean; secure: boolean; sameSite: "lax"; path: string; maxAge: number } } {
|
||||
return {
|
||||
name: COOKIE_NAME,
|
||||
options: {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
sameSite: "lax",
|
||||
path: "/",
|
||||
maxAge: 60 * 60 * 24 * 7,
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user