Some checks failed
Deploy / Deploy Production (push) Has been skipped
Deploy / Cleanup Preview (push) Has been skipped
Deploy / Deploy Preview (push) Failing after 0s
CI / Scraper / Test (pull_request) Successful in 11s
CI / UI / Build (pull_request) Failing after 14s
CI / Scraper / Lint (pull_request) Successful in 23s
CI / Scraper / Build (pull_request) Successful in 24s
iOS CI / Build (push) Has been cancelled
iOS CI / Test (push) Has been cancelled
iOS CI / Build (pull_request) Failing after 2s
iOS CI / Test (pull_request) Has been skipped
- iOS SwiftUI app (ios/LibNovel/) targeting iOS 17+, generated via xcodegen - Full feature set: auth, home, library, book detail, chapter reader, browse, audio player, profile - Kingfisher for image loading, swift-markdown-ui for chapter rendering - Base URL: https://v2.libnovel.kalekber.cc - SvelteKit JSON API routes (ui/src/routes/api/) for iOS consumption: auth/login, auth/register, auth/me, auth/logout, auth/change-password, home, library, book/[slug], chapter/[slug]/[n], search, ranking, progress/[slug], presign/audio (updated) - Gitea Actions CI: .gitea/workflows/ios.yaml (build + test on macos-latest) - justfile: ios-gen, ios-build, ios-test recipes
49 lines
1.5 KiB
TypeScript
49 lines
1.5 KiB
TypeScript
import { json } from '@sveltejs/kit';
|
|
import type { RequestHandler } from './$types';
|
|
import { listBooks, recentlyAddedBooks, allProgress, getHomeStats } from '$lib/server/pocketbase';
|
|
import { log } from '$lib/server/logger';
|
|
import type { Book, Progress } from '$lib/server/pocketbase';
|
|
|
|
/**
|
|
* GET /api/home
|
|
* Returns home screen data: continue-reading list, recently updated books, and stats.
|
|
* Requires authentication (enforced by layout guard).
|
|
*/
|
|
export const GET: RequestHandler = async ({ locals }) => {
|
|
let allBooks: Book[] = [];
|
|
let recentBooks: Book[] = [];
|
|
let progressList: Progress[] = [];
|
|
let stats = { totalBooks: 0, totalChapters: 0 };
|
|
|
|
try {
|
|
[allBooks, recentBooks, progressList, stats] = await Promise.all([
|
|
listBooks(),
|
|
recentlyAddedBooks(8),
|
|
allProgress(locals.sessionId, locals.user?.id),
|
|
getHomeStats()
|
|
]);
|
|
} catch (e) {
|
|
log.error('api/home', 'failed to load home data', { err: String(e) });
|
|
}
|
|
|
|
const bookMap = new Map<string, Book>(allBooks.map((b) => [b.slug, b]));
|
|
|
|
const continueReading = progressList
|
|
.filter((p) => bookMap.has(p.slug))
|
|
.slice(0, 6)
|
|
.map((p) => ({ book: bookMap.get(p.slug)!, chapter: p.chapter }));
|
|
|
|
const inProgressSlugs = new Set(continueReading.map((c) => c.book.slug));
|
|
const recentlyUpdated = recentBooks.filter((b) => !inProgressSlugs.has(b.slug)).slice(0, 6);
|
|
|
|
return json({
|
|
continue_reading: continueReading,
|
|
recently_updated: recentlyUpdated,
|
|
stats: {
|
|
totalBooks: stats.totalBooks,
|
|
totalChapters: stats.totalChapters,
|
|
booksInProgress: continueReading.length
|
|
}
|
|
});
|
|
};
|