feat: profile page, admin pages, infinite scroll on browse
Some checks failed
Deploy / Deploy Production (push) Has been skipped
Deploy / Cleanup Preview (push) Has been skipped
Deploy / Deploy Preview (push) Failing after 1s
CI / UI / Build (pull_request) Successful in 16s
CI / Scraper / Test (pull_request) Successful in 16s
CI / Scraper / Lint (pull_request) Successful in 19s
CI / Scraper / Build (pull_request) Successful in 16s

- Add /profile page with reading settings (voice, speed, auto-next) and password change form
- Add /admin/scrape page showing scraping task history with live status polling and trigger controls
- Add /admin/audio page showing audio cache entries with client-side search filter
- Add changePassword(), listAudioCache(), listScrapingTasks() to pocketbase.ts
- Add /api/admin/scrape and /api/browse-page server-side proxy routes
- Replace browse page pagination with IntersectionObserver infinite scroll
- Update nav: username becomes a /profile link; admin users see Scrape and Audio cache links
This commit is contained in:
Admin
2026-03-06 18:58:24 +05:00
parent 08d4718245
commit 8f0a2f7e92
11 changed files with 845 additions and 38 deletions

View File

@@ -0,0 +1,23 @@
import { json, error } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
import { env } from '$env/dynamic/private';
const SCRAPER_URL = env.SCRAPER_API_URL ?? 'http://localhost:8080';
/**
* GET /api/admin/scrape/status
* Admin-only proxy to the Go scraper's /api/scrape/status endpoint.
*/
export const GET: RequestHandler = async ({ locals }) => {
if (!locals.user || locals.user.role !== 'admin') {
throw error(403, 'Forbidden');
}
try {
const res = await fetch(`${SCRAPER_URL}/api/scrape/status`);
if (!res.ok) return json({ running: false });
const data = await res.json();
return json({ running: data.running ?? false });
} catch {
return json({ running: false });
}
};

View File

@@ -0,0 +1,37 @@
import { json, error } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
import { env } from '$env/dynamic/private';
import { log } from '$lib/server/logger';
const SCRAPER_URL = env.SCRAPER_API_URL ?? 'http://localhost:8080';
/**
* GET /api/browse-page?page=2&genre=all&sort=popular&status=all
*
* Thin proxy to the Go scraper's /api/browse endpoint.
* Used by the infinite-scroll browse page to append subsequent pages
* without a full SSR navigation.
*/
export const GET: RequestHandler = async ({ url }) => {
const page = url.searchParams.get('page') ?? '1';
const genre = url.searchParams.get('genre') ?? 'all';
const sort = url.searchParams.get('sort') ?? 'popular';
const status = url.searchParams.get('status') ?? 'all';
const params = new URLSearchParams({ page, genre, sort, status });
const apiURL = `${SCRAPER_URL}/api/browse?${params.toString()}`;
try {
const res = await fetch(apiURL);
if (!res.ok) {
log.error('browse-page', 'scraper returned error', { status: res.status });
throw error(502, `Browse fetch failed: ${res.status}`);
}
const data = await res.json();
return json(data);
} catch (e) {
if (e instanceof Error && 'status' in e) throw e;
log.error('browse-page', 'network error', { err: String(e) });
throw error(502, 'Could not reach browse service');
}
};