···2 * GENERATED CODE - DO NOT MODIFY
3 */
4import { type ValidationResult, BlobRef } from '@atproto/lexicon'
5-import { CID } from 'multiformats/cid'
6import { validate as _validate } from '../../../lexicons'
7import { type $Typed, is$typed as _is$typed, type OmitKey } from '../../../util'
8
···2 * GENERATED CODE - DO NOT MODIFY
3 */
4import { type ValidationResult, BlobRef } from '@atproto/lexicon'
5+import { CID } from 'multiformats'
6import { validate as _validate } from '../../../lexicons'
7import { type $Typed, is$typed as _is$typed, type OmitKey } from '../../../util'
8
+8-86
hosting-service/src/lib/db.ts
···1import postgres from 'postgres';
023const sql = postgres(
4 process.env.DATABASE_URL || 'postgres://postgres:postgres@localhost:5432/wisp',
···21 verified: boolean;
22}
2324-// In-memory cache with TTL
25-interface CacheEntry<T> {
26- data: T;
27- expiry: number;
28-}
2930-const CACHE_TTL_MS = 10 * 60 * 1000; // 10 minutes
31-32-class SimpleCache<T> {
33- private cache = new Map<string, CacheEntry<T>>();
34-35- get(key: string): T | null {
36- const entry = this.cache.get(key);
37- if (!entry) return null;
38-39- if (Date.now() > entry.expiry) {
40- this.cache.delete(key);
41- return null;
42- }
43-44- return entry.data;
45- }
46-47- set(key: string, data: T): void {
48- this.cache.set(key, {
49- data,
50- expiry: Date.now() + CACHE_TTL_MS,
51- });
52- }
53-54- // Periodic cleanup to prevent memory leaks
55- cleanup(): void {
56- const now = Date.now();
57- for (const [key, entry] of this.cache.entries()) {
58- if (now > entry.expiry) {
59- this.cache.delete(key);
60- }
61- }
62- }
63-}
64-65-// Create cache instances
66-const wispDomainCache = new SimpleCache<DomainLookup | null>();
67-const customDomainCache = new SimpleCache<CustomDomainLookup | null>();
68-const customDomainHashCache = new SimpleCache<CustomDomainLookup | null>();
69-70-// Run cleanup every 5 minutes
71-setInterval(() => {
72- wispDomainCache.cleanup();
73- customDomainCache.cleanup();
74- customDomainHashCache.cleanup();
75-}, 5 * 60 * 1000);
7677export async function getWispDomain(domain: string): Promise<DomainLookup | null> {
78 const key = domain.toLowerCase();
7980- // Check cache first
81- const cached = wispDomainCache.get(key);
82- if (cached !== null) {
83- return cached;
84- }
85-86 // Query database
87 const result = await sql<DomainLookup[]>`
88 SELECT did, rkey FROM domains WHERE domain = ${key} LIMIT 1
89 `;
90 const data = result[0] || null;
9192- // Store in cache
93- wispDomainCache.set(key, data);
94-95 return data;
96}
9798export async function getCustomDomain(domain: string): Promise<CustomDomainLookup | null> {
99 const key = domain.toLowerCase();
100101- // Check cache first
102- const cached = customDomainCache.get(key);
103- if (cached !== null) {
104- return cached;
105- }
106-107 // Query database
108 const result = await sql<CustomDomainLookup[]>`
109 SELECT id, domain, did, rkey, verified FROM custom_domains
···111 `;
112 const data = result[0] || null;
113114- // Store in cache
115- customDomainCache.set(key, data);
116-117 return data;
118}
119120export async function getCustomDomainByHash(hash: string): Promise<CustomDomainLookup | null> {
121- // Check cache first
122- const cached = customDomainHashCache.get(hash);
123- if (cached !== null) {
124- return cached;
125- }
126-127 // Query database
128 const result = await sql<CustomDomainLookup[]>`
129 SELECT id, domain, did, rkey, verified FROM custom_domains
130 WHERE id = ${hash} AND verified = true LIMIT 1
131 `;
132 const data = result[0] || null;
133-134- // Store in cache
135- customDomainHashCache.set(hash, data);
136137 return data;
138}
···163 * PostgreSQL advisory locks use bigint (64-bit signed integer)
164 */
165function stringToLockId(key: string): bigint {
166- let hash = 0n;
167- for (let i = 0; i < key.length; i++) {
168- const char = BigInt(key.charCodeAt(i));
169- hash = ((hash << 5n) - hash + char) & 0x7FFFFFFFFFFFFFFFn; // Keep within signed int64 range
170- }
171- return hash;
172}
173174/**
···180 const lockId = stringToLockId(key);
181182 try {
183- const result = await sql`SELECT pg_try_advisory_lock(${lockId}) as acquired`;
184 return result[0]?.acquired === true;
185 } catch (err) {
186 console.error('Failed to acquire lock', { key, error: err });
···195 const lockId = stringToLockId(key);
196197 try {
198- await sql`SELECT pg_advisory_unlock(${lockId})`;
199 } catch (err) {
200 console.error('Failed to release lock', { key, error: err });
201 }
···1import postgres from 'postgres';
2+import { createHash } from 'crypto';
34const sql = postgres(
5 process.env.DATABASE_URL || 'postgres://postgres:postgres@localhost:5432/wisp',
···22 verified: boolean;
23}
24000002500000000000000000000000000000000000000000000002627export async function getWispDomain(domain: string): Promise<DomainLookup | null> {
28 const key = domain.toLowerCase();
2900000030 // Query database
31 const result = await sql<DomainLookup[]>`
32 SELECT did, rkey FROM domains WHERE domain = ${key} LIMIT 1
33 `;
34 const data = result[0] || null;
3500036 return data;
37}
3839export async function getCustomDomain(domain: string): Promise<CustomDomainLookup | null> {
40 const key = domain.toLowerCase();
4100000042 // Query database
43 const result = await sql<CustomDomainLookup[]>`
44 SELECT id, domain, did, rkey, verified FROM custom_domains
···46 `;
47 const data = result[0] || null;
4800049 return data;
50}
5152export async function getCustomDomainByHash(hash: string): Promise<CustomDomainLookup | null> {
00000053 // Query database
54 const result = await sql<CustomDomainLookup[]>`
55 SELECT id, domain, did, rkey, verified FROM custom_domains
56 WHERE id = ${hash} AND verified = true LIMIT 1
57 `;
58 const data = result[0] || null;
0005960 return data;
61}
···86 * PostgreSQL advisory locks use bigint (64-bit signed integer)
87 */
88function stringToLockId(key: string): bigint {
89+ const hash = createHash('sha256').update(key).digest('hex');
90+ // Take first 16 hex characters (64 bits) and convert to bigint
91+ const hashNum = BigInt('0x' + hash.substring(0, 16));
92+ // Keep within signed int64 range
93+ return hashNum & 0x7FFFFFFFFFFFFFFFn;
094}
9596/**
···102 const lockId = stringToLockId(key);
103104 try {
105+ const result = await sql`SELECT pg_try_advisory_lock(${Number(lockId)}) as acquired`;
106 return result[0]?.acquired === true;
107 } catch (err) {
108 console.error('Failed to acquire lock', { key, error: err });
···117 const lockId = stringToLockId(key);
118119 try {
120+ await sql`SELECT pg_advisory_unlock(${Number(lockId)})`;
121 } catch (err) {
122 console.error('Failed to release lock', { key, error: err });
123 }