返回 DeepSeek-Reasonix
sessions.ts
根目录 / workers / accounts / src / db / sessions.ts
1 import type { UserRow } from "../types";
2 import { generateToken, hashToken } from "../auth/crypto";
3 import { SESSION_TTL_MS } from "../config";
4
5 export interface NewSession {
6 kind?: "web" | "cli";
7 userAgent?: string;
8 ttlMs?: number;
9 }
10
11 export class SessionRepo {
12 constructor(
13 private readonly db: D1Database,
14 private readonly pepper: string,
15 ) {}
16
17 // Persists sha256(pepper:token) and returns the raw token for the cookie.
18 async create(userId: number, opts: NewSession = {}): Promise<string> {
19 const token = generateToken();
20 const tokenHash = await hashToken(this.pepper, token);
21 const now = new Date();
22 const expires = new Date(now.getTime() + (opts.ttlMs ?? SESSION_TTL_MS));
23 await this.db
24 .prepare(
25 `INSERT INTO sessions (token_hash, user_id, kind, user_agent, created_at, last_seen_at, expires_at)
26 VALUES (?1, ?2, ?3, ?4, ?5, ?5, ?6)`,
27 )
28 .bind(tokenHash, userId, opts.kind ?? "web", (opts.userAgent ?? "").slice(0, 256), now.toISOString(), expires.toISOString())
29 .run();
30 return token;
31 }
32
33 // Resolves a cookie token to its active user, pruning the row if expired.
34 async resolve(token: string): Promise<UserRow | null> {
35 const tokenHash = await hashToken(this.pepper, token);
36 const row = await this.db
37 .prepare(
38 `SELECT u.*, s.expires_at AS s_expires
39 FROM sessions s JOIN users u ON u.id = s.user_id
40 WHERE s.token_hash = ?1`,
41 )
42 .bind(tokenHash)
43 .first<UserRow & { s_expires: string }>();
44 if (!row) return null;
45 if (row.s_expires <= new Date().toISOString()) {
46 await this.deleteByHash(tokenHash);
47 return null;
48 }
49 if (row.status !== "active") return null;
50 const { s_expires: _ignored, ...user } = row;
51 return user;
52 }
53
54 async deleteByToken(token: string): Promise<void> {
55 await this.deleteByHash(await hashToken(this.pepper, token));
56 }
57
58 async deleteAllForUser(userId: number): Promise<void> {
59 await this.db.prepare("DELETE FROM sessions WHERE user_id = ?1").bind(userId).run();
60 }
61
62 private async deleteByHash(tokenHash: string): Promise<void> {
63 await this.db.prepare("DELETE FROM sessions WHERE token_hash = ?1").bind(tokenHash).run();
64 }
65 }
66
66 lines TYPESCRIPT