feat(v3): add v3 stack — backend rewrite, renamed env vars, docs
- New Go backend binary (backend + runner) replacing old scraper/ - Rename SCRAPER_API_URL → BACKEND_API_URL in UI env and docker-compose - Rename scraperFetch → backendFetch across all 19 UI server files - Remove SCRAPER_PROXY env var and proxy transport from browser.Config - Add Meilisearch, Valkey, Caddy to docker-compose - Add docs/: api-endpoints.md, request-flow.mermaid.md, data-flow.mermaid.md
This commit is contained in:
47
v3/ui/src/routes/api/presign/audio/+server.ts
Normal file
47
v3/ui/src/routes/api/presign/audio/+server.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { json, error } from '@sveltejs/kit';
|
||||
import type { RequestHandler } from './$types';
|
||||
import { presignAudio } from '$lib/server/minio';
|
||||
import { log } from '$lib/server/logger';
|
||||
import * as cache from '$lib/server/presignCache';
|
||||
|
||||
/**
|
||||
* GET /api/presign/audio?slug=...&n=...&voice=...
|
||||
* Returns a presigned MinIO URL for the audio file so the browser
|
||||
* can stream it directly without going through the server.
|
||||
* Returns 404 when the audio has not been generated yet.
|
||||
*
|
||||
* Results are cached in-process for 50 minutes (MinIO URLs are valid 1 hour)
|
||||
* to avoid a backend + MinIO round-trip on every "Play" click.
|
||||
*/
|
||||
export const GET: RequestHandler = async ({ url }) => {
|
||||
const slug = url.searchParams.get('slug');
|
||||
// Accept both 'n' (web) and 'chapter' (iOS) as the chapter number param
|
||||
const n = parseInt(url.searchParams.get('n') ?? url.searchParams.get('chapter') ?? '', 10);
|
||||
const voice = url.searchParams.get('voice') ?? '';
|
||||
|
||||
if (!slug || !n || n < 1) {
|
||||
error(400, 'Missing slug or n');
|
||||
}
|
||||
|
||||
const cacheKey = cache.audioKey(slug, n, voice);
|
||||
|
||||
// Fast path: return cached URL if still valid.
|
||||
const cached = await cache.get(cacheKey);
|
||||
if (cached) {
|
||||
return json({ url: cached });
|
||||
}
|
||||
|
||||
// Slow path: call backend → MinIO presign.
|
||||
try {
|
||||
const presignedUrl = await presignAudio(slug, n, voice || undefined);
|
||||
await cache.set(cacheKey, presignedUrl);
|
||||
return json({ url: presignedUrl });
|
||||
} catch (e) {
|
||||
const status = (e as { status?: number }).status;
|
||||
if (status === 404) {
|
||||
error(404, 'Audio not found');
|
||||
}
|
||||
log.error('presign', 'presign audio failed', { slug, n, err: String(e) });
|
||||
error(500, `Could not get presigned URL: ${e}`);
|
||||
}
|
||||
};
|
||||
40
v3/ui/src/routes/api/presign/voice-sample/+server.ts
Normal file
40
v3/ui/src/routes/api/presign/voice-sample/+server.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { json, error } from '@sveltejs/kit';
|
||||
import type { RequestHandler } from './$types';
|
||||
import { presignVoiceSample } from '$lib/server/minio';
|
||||
import * as cache from '$lib/server/presignCache';
|
||||
|
||||
/**
|
||||
* GET /api/presign/voice-sample?voice=af_bella
|
||||
* Returns a presigned URL for the voice sample audio file.
|
||||
* Returns 404 if the sample has not been generated yet.
|
||||
*
|
||||
* Results are cached in-process for 50 minutes to avoid a backend + MinIO
|
||||
* round-trip on every voice-selection preview play.
|
||||
*/
|
||||
export const GET: RequestHandler = async ({ url }) => {
|
||||
const voice = url.searchParams.get('voice');
|
||||
if (!voice) {
|
||||
error(400, 'Missing voice parameter');
|
||||
}
|
||||
|
||||
const cacheKey = cache.sampleKey(voice);
|
||||
|
||||
// Fast path: return cached URL if still valid.
|
||||
const cached = await cache.get(cacheKey);
|
||||
if (cached) {
|
||||
return json({ url: cached });
|
||||
}
|
||||
|
||||
// Slow path: call backend → MinIO presign.
|
||||
try {
|
||||
const presignedUrl = await presignVoiceSample(voice);
|
||||
await cache.set(cacheKey, presignedUrl);
|
||||
return json({ url: presignedUrl });
|
||||
} catch (e) {
|
||||
const status = (e as { status?: number }).status;
|
||||
if (status === 404) {
|
||||
error(404, 'Voice sample not found');
|
||||
}
|
||||
error(502, `Failed to presign voice sample: ${e}`);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user