kaneo (minimalist kanban) fork to experiment adding a tangled integration
github.com/usekaneo/kaneo
1import { createHash } from "node:crypto";
2import { and, eq, gt, isNull, or } from "drizzle-orm";
3import db, { schema } from "../database";
4
5async function hashApiKey(key: string): Promise<string> {
6 const hash = createHash("sha256").update(key).digest();
7 return hash
8 .toString("base64")
9 .replace(/\+/g, "-")
10 .replace(/\//g, "_")
11 .replace(/=/g, "");
12}
13
14export async function verifyApiKey(key: string) {
15 const hashedKey = await hashApiKey(key);
16
17 const [apiKey] = await db
18 .select()
19 .from(schema.apikeyTable)
20 .where(
21 and(
22 eq(schema.apikeyTable.key, hashedKey),
23 eq(schema.apikeyTable.enabled, true),
24 or(
25 isNull(schema.apikeyTable.expiresAt),
26 gt(schema.apikeyTable.expiresAt, new Date()),
27 ),
28 ),
29 )
30 .limit(1);
31
32 if (!apiKey) {
33 return null;
34 }
35
36 return {
37 valid: true,
38 key: {
39 id: apiKey.id,
40 userId: apiKey.referenceId ?? apiKey.userId ?? "",
41 name: apiKey.name,
42 prefix: apiKey.prefix,
43 start: apiKey.start,
44 enabled: apiKey.enabled ?? false,
45 expiresAt: apiKey.expiresAt,
46 permissions: apiKey.permissions
47 ? (JSON.parse(apiKey.permissions) as Record<string, string[]>)
48 : null,
49 refillInterval: apiKey.refillInterval,
50 refillAmount: apiKey.refillAmount,
51 lastRefillAt: apiKey.lastRefillAt,
52 rateLimitEnabled: apiKey.rateLimitEnabled,
53 rateLimitTimeWindow: apiKey.rateLimitTimeWindow,
54 rateLimitMax: apiKey.rateLimitMax,
55 requestCount: apiKey.requestCount,
56 remaining: apiKey.remaining,
57 lastRequest: apiKey.lastRequest,
58 metadata: apiKey.metadata
59 ? (JSON.parse(apiKey.metadata) as Record<string, unknown>)
60 : null,
61 },
62 };
63}