feat: cache logos locally, show config_path, rename Sync button (#511)

- logoCacheService.ts: download script logos to public/logos/ for local serving
- cache-logos.ts: build-time script caching 500+ logos from PocketBase
- scripts.ts router: resolve local logo paths, resyncScripts now caches logos
- autoSyncService.js: cache logos during background auto-sync
- ScriptDetailModal: show config_path per install method
- ResyncButton: renamed 'Sync Json Files' to 'Sync Scripts'
- GeneralSettingsModal: updated auto-sync description text
- .gitignore: ignore public/logos/ and data/*.db
This commit is contained in:
CanbiZ (MickLesk)
2026-03-17 16:35:03 +01:00
committed by GitHub
parent d8e92e0445
commit e2a950da58
9 changed files with 229 additions and 15 deletions

View File

@@ -16,6 +16,7 @@ import {
} from "~/server/services/pbScripts";
import type { Script, ScriptCard } from "~/types/script";
import type { Server } from "~/types/server";
import { cacheLogos, getLocalLogoPath } from "~/server/services/logoCacheService";
// ---------------------------------------------------------------------------
// Mapper: PocketBase record → internal Script type (used by scriptDownloader)
@@ -177,7 +178,14 @@ export const scriptsRouter = createTRPCRouter({
.query(async () => {
try {
const cards = await getScriptCards();
return { success: true, cards: cards.map(pbCardToScriptCard) };
return {
success: true,
cards: cards.map((c) => {
const card = pbCardToScriptCard(c);
card.logo = getLocalLogoPath(c.slug, card.logo);
return card;
}),
};
} catch (error) {
console.error('Error in getScriptCards:', error);
return {
@@ -212,7 +220,9 @@ export const scriptsRouter = createTRPCRouter({
if (!pb) {
return { success: false, error: 'Script not found', script: null };
}
return { success: true, script: pbToScript(pb) };
const script = pbToScript(pb);
script.logo = getLocalLogoPath(pb.slug, script.logo);
return { success: true, script };
} catch (error) {
console.error('Error in getScriptBySlug:', error);
return {
@@ -245,7 +255,11 @@ export const scriptsRouter = createTRPCRouter({
try {
// PocketBase already returns category names expanded on each card
const cards = await getScriptCards();
const scriptCards = cards.map(pbCardToScriptCard);
const scriptCards = cards.map((c) => {
const card = pbCardToScriptCard(c);
card.logo = getLocalLogoPath(c.slug, card.logo);
return card;
});
// Also return the category list for the sidebar filter
const metadata = await pbGetMetadata();
@@ -262,15 +276,29 @@ export const scriptsRouter = createTRPCRouter({
}
}),
// PocketBase is always up to date this is a no-op kept for API compatibility
// Sync: cache logos locally from PocketBase script data
resyncScripts: publicProcedure
.mutation(async () => {
return {
success: true,
message: 'Script catalog is served directly from PocketBase and is always up to date.',
count: 0,
error: undefined as string | undefined,
};
try {
const cards = await getScriptCards();
const entries = cards
.filter((c) => c.logo)
.map((c) => ({ slug: c.slug, url: c.logo! }));
const result = await cacheLogos(entries);
return {
success: true,
message: `Logo cache updated: ${result.downloaded} downloaded, ${result.skipped} cached, ${result.errors} errors.`,
count: result.downloaded,
error: undefined as string | undefined,
};
} catch (error) {
return {
success: false,
message: 'Failed to sync logos',
count: 0,
error: error instanceof Error ? error.message : 'Unknown error',
};
}
}),
// Load script files from the community repository