Compare commits

..

2 Commits

Author SHA1 Message Date
d9e50a4235 chore: bump version to 1.20.1
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 1m13s
2026-03-21 13:13:39 +01:00
5f6eb5a5cb perf: add selective fetch caching for stable API endpoints
Make apiFetch support Next.js revalidate option instead of
hardcoding cache: no-store on every request. Stable endpoints
(libraries, settings, stats, series statuses) now use time-based
revalidation while dynamic data (books, search, jobs) stays uncached.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 13:13:28 +01:00
4 changed files with 18 additions and 16 deletions

8
Cargo.lock generated
View File

@@ -64,7 +64,7 @@ checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
[[package]] [[package]]
name = "api" name = "api"
version = "1.20.0" version = "1.20.1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"argon2", "argon2",
@@ -1232,7 +1232,7 @@ dependencies = [
[[package]] [[package]]
name = "indexer" name = "indexer"
version = "1.20.0" version = "1.20.1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"axum", "axum",
@@ -1771,7 +1771,7 @@ dependencies = [
[[package]] [[package]]
name = "parsers" name = "parsers"
version = "1.20.0" version = "1.20.1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"flate2", "flate2",
@@ -2906,7 +2906,7 @@ dependencies = [
[[package]] [[package]]
name = "stripstream-core" name = "stripstream-core"
version = "1.20.0" version = "1.20.1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"serde", "serde",

View File

@@ -9,7 +9,7 @@ resolver = "2"
[workspace.package] [workspace.package]
edition = "2021" edition = "2021"
version = "1.20.0" version = "1.20.1"
license = "MIT" license = "MIT"
[workspace.dependencies] [workspace.dependencies]

View File

@@ -141,7 +141,7 @@ export function config() {
export async function apiFetch<T>( export async function apiFetch<T>(
path: string, path: string,
init?: RequestInit, init?: RequestInit & { next?: { revalidate?: number; tags?: string[] } },
): Promise<T> { ): Promise<T> {
const { baseUrl, token } = config(); const { baseUrl, token } = config();
const headers = new Headers(init?.headers || {}); const headers = new Headers(init?.headers || {});
@@ -150,10 +150,12 @@ export async function apiFetch<T>(
headers.set("Content-Type", "application/json"); headers.set("Content-Type", "application/json");
} }
const { next: nextOptions, ...restInit } = init ?? {};
const res = await fetch(`${baseUrl}${path}`, { const res = await fetch(`${baseUrl}${path}`, {
...init, ...restInit,
headers, headers,
cache: "no-store", ...(nextOptions ? { next: nextOptions } : { cache: "no-store" as const }),
}); });
if (!res.ok) { if (!res.ok) {
@@ -168,7 +170,7 @@ export async function apiFetch<T>(
} }
export async function fetchLibraries() { export async function fetchLibraries() {
return apiFetch<LibraryDto[]>("/libraries"); return apiFetch<LibraryDto[]>("/libraries", { next: { revalidate: 30 } });
} }
export async function createLibrary(name: string, rootPath: string) { export async function createLibrary(name: string, rootPath: string) {
@@ -356,7 +358,7 @@ export async function fetchAllSeries(
} }
export async function fetchSeriesStatuses(): Promise<string[]> { export async function fetchSeriesStatuses(): Promise<string[]> {
return apiFetch<string[]>("/series/statuses"); return apiFetch<string[]>("/series/statuses", { next: { revalidate: 300 } });
} }
export async function searchBooks( export async function searchBooks(
@@ -421,7 +423,7 @@ export type ThumbnailStats = {
}; };
export async function getSettings() { export async function getSettings() {
return apiFetch<Settings>("/settings"); return apiFetch<Settings>("/settings", { next: { revalidate: 60 } });
} }
export async function updateSetting(key: string, value: unknown) { export async function updateSetting(key: string, value: unknown) {
@@ -432,7 +434,7 @@ export async function updateSetting(key: string, value: unknown) {
} }
export async function getCacheStats() { export async function getCacheStats() {
return apiFetch<CacheStats>("/settings/cache/stats"); return apiFetch<CacheStats>("/settings/cache/stats", { next: { revalidate: 30 } });
} }
export async function clearCache() { export async function clearCache() {
@@ -442,7 +444,7 @@ export async function clearCache() {
} }
export async function getThumbnailStats() { export async function getThumbnailStats() {
return apiFetch<ThumbnailStats>("/settings/thumbnail/stats"); return apiFetch<ThumbnailStats>("/settings/thumbnail/stats", { next: { revalidate: 30 } });
} }
// Status mappings // Status mappings
@@ -453,7 +455,7 @@ export type StatusMappingDto = {
}; };
export async function fetchStatusMappings(): Promise<StatusMappingDto[]> { export async function fetchStatusMappings(): Promise<StatusMappingDto[]> {
return apiFetch<StatusMappingDto[]>("/settings/status-mappings"); return apiFetch<StatusMappingDto[]>("/settings/status-mappings", { next: { revalidate: 60 } });
} }
export async function upsertStatusMapping(provider_status: string, mapped_status: string): Promise<StatusMappingDto> { export async function upsertStatusMapping(provider_status: string, mapped_status: string): Promise<StatusMappingDto> {
@@ -558,7 +560,7 @@ export type StatsResponse = {
}; };
export async function fetchStats() { export async function fetchStats() {
return apiFetch<StatsResponse>("/stats"); return apiFetch<StatsResponse>("/stats", { next: { revalidate: 30 } });
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View File

@@ -1,6 +1,6 @@
{ {
"name": "stripstream-backoffice", "name": "stripstream-backoffice",
"version": "1.20.0", "version": "1.20.1",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev -p 7082", "dev": "next dev -p 7082",