forked from
slices.network/slices
Highly ambitious ATProtocol AppView service and sdks
1import type { SessionAdapter, SessionData } from "../types.ts";
2
3export class DenoKVAdapter implements SessionAdapter {
4 private kv: Deno.Kv;
5 private keyPrefix: string;
6
7 constructor(kv: Deno.Kv, keyPrefix = "sessions") {
8 this.kv = kv;
9 this.keyPrefix = keyPrefix;
10 }
11
12 static async create(path?: string, keyPrefix = "sessions"): Promise<DenoKVAdapter> {
13 const kv = await Deno.openKv(path);
14 return new DenoKVAdapter(kv, keyPrefix);
15 }
16
17 private getKey(sessionId: string): string[] {
18 return [this.keyPrefix, sessionId];
19 }
20
21 async get(sessionId: string): Promise<SessionData | null> {
22 const result = await this.kv.get<SessionData>(this.getKey(sessionId));
23 return result.value;
24 }
25
26 async set(sessionId: string, data: SessionData): Promise<void> {
27 await this.kv.set(this.getKey(sessionId), data);
28 }
29
30 async update(sessionId: string, updates: Partial<SessionData>): Promise<boolean> {
31 const existing = await this.get(sessionId);
32 if (!existing) return false;
33
34 const updated = { ...existing, ...updates };
35 await this.set(sessionId, updated);
36 return true;
37 }
38
39 async delete(sessionId: string): Promise<void> {
40 await this.kv.delete(this.getKey(sessionId));
41 }
42
43 async cleanup(expiresBeforeMs: number): Promise<number> {
44 let cleanedCount = 0;
45
46 const iter = this.kv.list<SessionData>({ prefix: [this.keyPrefix] });
47
48 for await (const entry of iter) {
49 if (entry.value && entry.value.expiresAt < expiresBeforeMs) {
50 await this.kv.delete(entry.key);
51 cleanedCount++;
52 }
53 }
54
55 return cleanedCount;
56 }
57
58 async exists(sessionId: string): Promise<boolean> {
59 const result = await this.kv.get(this.getKey(sessionId));
60 return result.value !== null;
61 }
62
63 close(): void {
64 this.kv.close();
65 }
66
67 async getSessionsByUser(userId: string): Promise<SessionData[]> {
68 const sessions: SessionData[] = [];
69 const iter = this.kv.list<SessionData>({ prefix: [this.keyPrefix] });
70
71 for await (const entry of iter) {
72 if (entry.value && entry.value.userId === userId) {
73 sessions.push(entry.value);
74 }
75 }
76
77 return sessions;
78 }
79
80 async clear(): Promise<void> {
81 const iter = this.kv.list({ prefix: [this.keyPrefix] });
82
83 for await (const entry of iter) {
84 await this.kv.delete(entry.key);
85 }
86 }
87
88 async count(): Promise<number> {
89 let count = 0;
90 const iter = this.kv.list({ prefix: [this.keyPrefix] });
91
92 for await (const _entry of iter) {
93 count++;
94 }
95
96 return count;
97 }
98}