From af269b030f6bf693e6f9207967cd1111d9225f8c Mon Sep 17 00:00:00 2001 From: Sam Chau Date: Wed, 31 Dec 2025 00:58:17 +1030 Subject: [PATCH] feat(cache): implement regex101 cache table and queries for API response caching --- src/lib/server/db/migrations.ts | 4 +- .../migrations/017_create_regex101_cache.ts | 18 +++++++ src/lib/server/db/queries/regex101Cache.ts | 54 +++++++++++++++++++ src/lib/server/db/schema.sql | 12 +++++ src/routes/api/regex101/[id]/+server.ts | 14 +++++ 5 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 src/lib/server/db/migrations/017_create_regex101_cache.ts create mode 100644 src/lib/server/db/queries/regex101Cache.ts diff --git a/src/lib/server/db/migrations.ts b/src/lib/server/db/migrations.ts index 57591a2..0f63f5e 100644 --- a/src/lib/server/db/migrations.ts +++ b/src/lib/server/db/migrations.ts @@ -18,6 +18,7 @@ import { migration as migration013 } from './migrations/013_add_upgrade_dry_run. import { migration as migration014 } from './migrations/014_create_ai_settings.ts'; import { migration as migration015 } from './migrations/015_create_arr_sync_tables.ts'; import { migration as migration016 } from './migrations/016_add_should_sync_flags.ts'; +import { migration as migration017 } from './migrations/017_create_regex101_cache.ts'; export interface Migration { version: number; @@ -251,7 +252,8 @@ export function loadMigrations(): Migration[] { migration013, migration014, migration015, - migration016 + migration016, + migration017 ]; // Sort by version number diff --git a/src/lib/server/db/migrations/017_create_regex101_cache.ts b/src/lib/server/db/migrations/017_create_regex101_cache.ts new file mode 100644 index 0000000..6707c68 --- /dev/null +++ b/src/lib/server/db/migrations/017_create_regex101_cache.ts @@ -0,0 +1,18 @@ +import type { Migration } from '../migrations.ts'; + +export const migration: Migration = { + version: 17, + name: 'Create regex101 cache table', + + up: ` + CREATE TABLE IF NOT EXISTS regex101_cache ( + regex101_id TEXT PRIMARY KEY, + response TEXT NOT NULL, + fetched_at DATETIME DEFAULT CURRENT_TIMESTAMP + ); + `, + + down: ` + DROP TABLE IF EXISTS regex101_cache; + ` +}; diff --git a/src/lib/server/db/queries/regex101Cache.ts b/src/lib/server/db/queries/regex101Cache.ts new file mode 100644 index 0000000..3e6d9a7 --- /dev/null +++ b/src/lib/server/db/queries/regex101Cache.ts @@ -0,0 +1,54 @@ +import { db } from '../db.ts'; + +/** + * Types for regex101_cache table + */ +export interface Regex101Cache { + regex101_id: string; + response: string; + fetched_at: string; +} + +/** + * All queries for regex101_cache table + */ +export const regex101CacheQueries = { + /** + * Get cached response by regex101 ID + */ + get(regex101Id: string): Regex101Cache | undefined { + return db.queryFirst( + 'SELECT * FROM regex101_cache WHERE regex101_id = ?', + regex101Id + ); + }, + + /** + * Store response in cache (insert or replace) + */ + set(regex101Id: string, response: string): void { + db.execute( + 'INSERT OR REPLACE INTO regex101_cache (regex101_id, response) VALUES (?, ?)', + regex101Id, + response + ); + }, + + /** + * Delete a cached entry + */ + delete(regex101Id: string): boolean { + const affected = db.execute( + 'DELETE FROM regex101_cache WHERE regex101_id = ?', + regex101Id + ); + return affected > 0; + }, + + /** + * Clear all cached entries + */ + clear(): number { + return db.execute('DELETE FROM regex101_cache'); + } +}; diff --git a/src/lib/server/db/schema.sql b/src/lib/server/db/schema.sql index 4fd54bd..26b2839 100644 --- a/src/lib/server/db/schema.sql +++ b/src/lib/server/db/schema.sql @@ -383,3 +383,15 @@ CREATE INDEX idx_upgrade_configs_arr_instance ON upgrade_configs(arr_instance_id -- Arr sync indexes (Migration: 015_create_arr_sync_tables.ts) CREATE INDEX idx_arr_sync_quality_profiles_instance ON arr_sync_quality_profiles(instance_id); CREATE INDEX idx_arr_sync_delay_profiles_instance ON arr_sync_delay_profiles(instance_id); + +-- ============================================================================== +-- TABLE: regex101_cache +-- Purpose: Cache regex101 API responses to avoid redundant fetches +-- Migration: 017_create_regex101_cache.ts +-- ============================================================================== + +CREATE TABLE regex101_cache ( + regex101_id TEXT PRIMARY KEY, -- Versioned ID (e.g., "ABC123/1") + response TEXT NOT NULL, -- Full JSON response with test results + fetched_at DATETIME DEFAULT CURRENT_TIMESTAMP +); diff --git a/src/routes/api/regex101/[id]/+server.ts b/src/routes/api/regex101/[id]/+server.ts index cbd06ab..26c779e 100644 --- a/src/routes/api/regex101/[id]/+server.ts +++ b/src/routes/api/regex101/[id]/+server.ts @@ -1,6 +1,7 @@ import { json, error } from '@sveltejs/kit'; import type { RequestHandler } from './$types'; import { logger } from '$logger/logger'; +import { regex101CacheQueries } from '$db/queries/regex101Cache.ts'; export interface Regex101UnitTest { description: string; @@ -89,6 +90,16 @@ export const GET: RequestHandler = async ({ params, fetch }) => { throw error(400, 'Missing regex101 ID'); } + // Check cache first + const cached = regex101CacheQueries.get(id); + if (cached) { + await logger.debug('regex101 cache hit', { + source: 'Regex101API', + meta: { id } + }); + return json(JSON.parse(cached.response)); + } + // Handle ID with optional version (e.g., "ABC123" or "ABC123/1") const [regexId, version] = id.split('/'); @@ -136,6 +147,9 @@ export const GET: RequestHandler = async ({ params, fetch }) => { unitTests: testedUnitTests }; + // Cache the result + regex101CacheQueries.set(id, JSON.stringify(result)); + return json(result); } catch (err) { if (err && typeof err === 'object' && 'status' in err) {