mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-21 23:05:46 +00:00
feat(themes): fresher store refresh + external-services notice (#1016)
* feat(themes): fresher store refresh + external-services notice Manual refresh now fetches the registry from GitHub raw first (~5 min cache) and falls back to the jsDelivr CDN, so a freshly merged theme shows up without waiting on — or purging — the shared CDN @main edge (up to 12 h). Normal loads still use the CDN. Adds a notice on the Theme Store that the catalogue and previews load from external services (jsDelivr / GitHub), in line with the app's network transparency. i18n ×9. * docs(themes): note the store-refresh PR in the Theme Store entry Now that #1015 is merged, fold PR #1016 into the still-unreleased Theme Store changelog and credits entry.
This commit is contained in:
@@ -84,4 +84,31 @@ describe('fetchRegistry', () => {
|
||||
expect(r.registry.generatedAt).toBe('fresh2');
|
||||
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('force-refresh tries GitHub raw first', async () => {
|
||||
const calls: string[] = [];
|
||||
vi.stubGlobal('fetch', vi.fn(async (url: string) => { calls.push(url); return okRes(reg('forced')); }));
|
||||
|
||||
await fetchRegistry({ force: true });
|
||||
expect(calls[0]).toContain('raw.githubusercontent.com');
|
||||
});
|
||||
|
||||
it('force-refresh falls back to jsDelivr when raw fails', async () => {
|
||||
const fetchMock = vi.fn(async (url: string) =>
|
||||
url.includes('raw.githubusercontent.com') ? failRes() : okRes(reg('cdn')));
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
|
||||
const r = await fetchRegistry({ force: true });
|
||||
expect(r.registry.generatedAt).toBe('cdn');
|
||||
expect(fetchMock).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('non-force load uses the CDN only (not raw)', async () => {
|
||||
const calls: string[] = [];
|
||||
vi.stubGlobal('fetch', vi.fn(async (url: string) => { calls.push(url); return okRes(reg('cdn')); }));
|
||||
|
||||
await fetchRegistry();
|
||||
expect(calls).toHaveLength(1);
|
||||
expect(calls[0]).not.toContain('raw.githubusercontent.com');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
|
||||
const CDN_BASE = 'https://cdn.jsdelivr.net/gh/Psysonic/psysonic-themes@main';
|
||||
const REGISTRY_URL = `${CDN_BASE}/registry.json`;
|
||||
// GitHub raw serves with a ~5-minute cache (vs jsDelivr's up-to-12h @main edge)
|
||||
// and permissive CORS. Used only on a manual refresh so freshly merged themes
|
||||
// appear without waiting on — or purging — the shared CDN edge.
|
||||
const RAW_REGISTRY_URL = 'https://raw.githubusercontent.com/Psysonic/psysonic-themes/main/registry.json';
|
||||
const CACHE_KEY = 'psysonic_theme_registry_cache';
|
||||
const TTL_MS = 12 * 60 * 60 * 1000; // 12h — matches jsDelivr's @main edge cache
|
||||
|
||||
@@ -81,17 +85,23 @@ export async function fetchRegistry(opts?: { force?: boolean }): Promise<FetchRe
|
||||
const cached = readCache();
|
||||
if (cached && Date.now() - cached.ts < TTL_MS) return { registry: cached.registry, stale: false };
|
||||
}
|
||||
try {
|
||||
const res = await fetch(REGISTRY_URL, { cache: 'no-cache' });
|
||||
if (!res.ok) throw new Error(`registry fetch failed: ${res.status}`);
|
||||
const registry = (await res.json()) as Registry;
|
||||
writeCache(registry);
|
||||
return { registry, stale: false };
|
||||
} catch (err) {
|
||||
const cached = readCache();
|
||||
if (cached) return { registry: cached.registry, stale: true };
|
||||
throw err;
|
||||
// On a manual refresh, try GitHub raw first (fresher) then fall back to the
|
||||
// jsDelivr CDN; normal loads use the CDN only.
|
||||
const sources = opts?.force ? [RAW_REGISTRY_URL, REGISTRY_URL] : [REGISTRY_URL];
|
||||
for (const url of sources) {
|
||||
try {
|
||||
const res = await fetch(url, { cache: 'no-cache' });
|
||||
if (!res.ok) continue;
|
||||
const registry = (await res.json()) as Registry;
|
||||
writeCache(registry);
|
||||
return { registry, stale: false };
|
||||
} catch {
|
||||
// try the next source
|
||||
}
|
||||
}
|
||||
const cached = readCache();
|
||||
if (cached) return { registry: cached.registry, stale: true };
|
||||
throw new Error('registry fetch failed');
|
||||
}
|
||||
|
||||
/** Fetch a single theme's CSS text from the CDN (repo-relative path). */
|
||||
|
||||
Reference in New Issue
Block a user