···11import postgres from 'postgres';
22import { createHash } from 'crypto';
33-import type { DomainLookup, CustomDomainLookup } from '@wispplace/database';
33+import type { DomainLookup, CustomDomainLookup, SiteCache, SiteSettingsCache } from '@wispplace/database';
4455// Global cache-only mode flag (set by index.ts)
66let cacheOnlyMode = false;
···242242 } catch (err) {
243243 console.error('[DB] Error closing database connections:', err);
244244 }
245245+}
246246+247247+// Site cache queries
248248+249249+export async function getSiteCache(did: string, rkey: string): Promise<SiteCache | null> {
250250+ const result = await sql<SiteCache[]>`
251251+ SELECT did, rkey, record_cid, file_cids, cached_at, updated_at
252252+ FROM site_cache
253253+ WHERE did = ${did} AND rkey = ${rkey}
254254+ LIMIT 1
255255+ `;
256256+ return result[0] || null;
257257+}
258258+259259+export async function getSiteSettingsCache(did: string): Promise<SiteSettingsCache | null> {
260260+ const result = await sql<SiteSettingsCache[]>`
261261+ SELECT did, record_cid, directory_listing, spa_mode, custom_404, index_files, clean_urls, headers, cached_at, updated_at
262262+ FROM site_settings_cache
263263+ WHERE did = ${did}
264264+ LIMIT 1
265265+ `;
266266+ return result[0] || null;
267267+}
268268+269269+export async function listSiteCachesForDid(did: string): Promise<SiteCache[]> {
270270+ return await sql<SiteCache[]>`
271271+ SELECT did, rkey, record_cid, file_cids, cached_at, updated_at
272272+ FROM site_cache
273273+ WHERE did = ${did}
274274+ ORDER BY updated_at DESC
275275+ `;
245276}
246277247278export { sql };
+1-1
apps/hosting-service/src/lib/storage.ts
···1717 S3StorageTier,
1818 type StorageTier,
1919 type StorageMetadata,
2020-} from 'tiered-storage';
2020+} from '@wispplace/tiered-storage';
21212222const CACHE_DIR = process.env.CACHE_DIR || './cache/sites';
2323const HOT_CACHE_SIZE = parseInt(process.env.HOT_CACHE_SIZE || '104857600', 10); // 100MB default
+43
apps/main-app/src/lib/db.ts
···122122 )
123123`;
124124125125+// Site cache table - stores CIDs for cached sites (used by firehose/hosting services)
126126+await db`
127127+ CREATE TABLE IF NOT EXISTS site_cache (
128128+ did TEXT NOT NULL,
129129+ rkey TEXT NOT NULL,
130130+ record_cid TEXT NOT NULL,
131131+ file_cids JSONB NOT NULL DEFAULT '{}',
132132+ cached_at BIGINT DEFAULT EXTRACT(EPOCH FROM NOW()),
133133+ updated_at BIGINT DEFAULT EXTRACT(EPOCH FROM NOW()),
134134+ PRIMARY KEY (did, rkey)
135135+ )
136136+`;
137137+138138+// Site settings cache table - cached place.wisp.settings records
139139+await db`
140140+ CREATE TABLE IF NOT EXISTS site_settings_cache (
141141+ did TEXT PRIMARY KEY,
142142+ record_cid TEXT NOT NULL,
143143+ directory_listing BOOLEAN NOT NULL DEFAULT false,
144144+ spa_mode TEXT,
145145+ custom_404 TEXT,
146146+ index_files JSONB,
147147+ clean_urls BOOLEAN NOT NULL DEFAULT true,
148148+ headers JSONB,
149149+ cached_at BIGINT DEFAULT EXTRACT(EPOCH FROM NOW()),
150150+ updated_at BIGINT DEFAULT EXTRACT(EPOCH FROM NOW())
151151+ )
152152+`;
153153+125154// Create indexes for common query patterns
126155await Promise.all([
127156 // oauth_states cleanup queries
···177206 db`CREATE INDEX IF NOT EXISTS idx_sites_did ON sites(did)`.catch(err => {
178207 if (!err.message?.includes('already exists')) {
179208 console.error('Failed to create idx_sites_did:', err);
209209+ }
210210+ }),
211211+212212+ // site_cache queries by did
213213+ db`CREATE INDEX IF NOT EXISTS idx_site_cache_did ON site_cache(did)`.catch(err => {
214214+ if (!err.message?.includes('already exists')) {
215215+ console.error('Failed to create idx_site_cache_did:', err);
216216+ }
217217+ }),
218218+219219+ // site_cache queries by updated_at (for cleanup/monitoring)
220220+ db`CREATE INDEX IF NOT EXISTS idx_site_cache_updated ON site_cache(updated_at)`.catch(err => {
221221+ if (!err.message?.includes('already exists')) {
222222+ console.error('Failed to create idx_site_cache_updated:', err);
180223 }
181224 })
182225]);