mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-23 23:31:47 +00:00
fix(maps): bound place-photo cache growth (Wikimedia + Google) (#1174)
The place-photo cache (uploads/photos/google) grew unbounded: a Wikimedia geosearch path cached full-res originals despite requesting a 400px thumb, the writer applied no size guard, nothing reclaimed orphaned files, and backups archived the whole re-derivable cache verbatim. - Prefer the scaled `thumburl` over the full-res `info.url` in the Commons geosearch fallback. - Downscale any cached image to <=800px JPEG via the existing jimp dep, with a safe fallback to the original bytes on decode failure. - Add sweepOrphans() (orphaned meta rows + stray files) wired into the scheduler (startup + nightly), and removeIfUnreferenced() called on place delete for prompt reclamation. - Exclude the re-derivable photo/trek caches from backups; restores self-heal as the cache dirs are recreated at startup.
This commit is contained in:
@@ -14,6 +14,20 @@ import {
|
||||
type KmlImportSummary,
|
||||
} from './kmlImport';
|
||||
import { enrichImportedPlaces, type EnrichablePlace } from './placeEnrichment';
|
||||
import * as placePhotoCache from './placePhotoCache';
|
||||
|
||||
// Reclaim a deleted place's cached marker photo if nothing else references it.
|
||||
// The cache key is the Google place_id, or — for coordinate-only places — the
|
||||
// pseudo-id embedded in the stored proxy URL (/api/maps/place-photo/{id}/bytes).
|
||||
function reclaimPhotoCache(googlePlaceId: string | null, imageUrl: string | null): void {
|
||||
const candidates = new Set<string>();
|
||||
if (googlePlaceId) candidates.add(googlePlaceId);
|
||||
const m = imageUrl?.match(/^\/api\/maps\/place-photo\/(.+)\/bytes$/);
|
||||
if (m) { try { candidates.add(decodeURIComponent(m[1])); } catch { /* malformed url */ } }
|
||||
for (const id of candidates) {
|
||||
try { placePhotoCache.removeIfUnreferenced(id); } catch { /* best-effort */ }
|
||||
}
|
||||
}
|
||||
|
||||
/** Opt-in Places-API enrichment for list imports (#886). */
|
||||
export interface ListImportOptions {
|
||||
@@ -242,25 +256,33 @@ export function updatePlace(
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export function deletePlace(tripId: string, placeId: string): boolean {
|
||||
const place = db.prepare('SELECT id FROM places WHERE id = ? AND trip_id = ?').get(placeId, tripId);
|
||||
const place = db.prepare(
|
||||
'SELECT google_place_id, image_url FROM places WHERE id = ? AND trip_id = ?'
|
||||
).get(placeId, tripId) as { google_place_id: string | null; image_url: string | null } | undefined;
|
||||
if (!place) return false;
|
||||
db.prepare('DELETE FROM places WHERE id = ?').run(placeId);
|
||||
reclaimPhotoCache(place.google_place_id, place.image_url);
|
||||
return true;
|
||||
}
|
||||
|
||||
export function deletePlacesMany(tripId: string, ids: number[]): number[] {
|
||||
if (ids.length === 0) return [];
|
||||
const selectStmt = db.prepare('SELECT id FROM places WHERE id = ? AND trip_id = ?');
|
||||
const selectStmt = db.prepare('SELECT google_place_id, image_url FROM places WHERE id = ? AND trip_id = ?');
|
||||
const deleteStmt = db.prepare('DELETE FROM places WHERE id = ?');
|
||||
const deleted: number[] = [];
|
||||
const reclaimable: { google_place_id: string | null; image_url: string | null }[] = [];
|
||||
const run = db.transaction((list: number[]) => {
|
||||
for (const id of list) {
|
||||
if (!selectStmt.get(id, tripId)) continue;
|
||||
const row = selectStmt.get(id, tripId) as { google_place_id: string | null; image_url: string | null } | undefined;
|
||||
if (!row) continue;
|
||||
deleteStmt.run(id);
|
||||
deleted.push(id);
|
||||
reclaimable.push(row);
|
||||
}
|
||||
});
|
||||
run(ids);
|
||||
// Reclaim after the transaction commits so isReferenced() sees the final place set.
|
||||
for (const row of reclaimable) reclaimPhotoCache(row.google_place_id, row.image_url);
|
||||
return deleted;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user