fix(community): robust asset filename matching for existing submissions

Implements a normalization strategy to map stored original filenames (e.g. 'icon (2).png') to PocketBase sanitized filenames (e.g. 'icon_2_....png') to ensure variants display correctly for existing records.
This commit is contained in:
Thomas Camlong
2025-11-20 09:47:39 +01:00
parent 9ad3e48e61
commit 7f6155d661

View File

@@ -16,6 +16,42 @@ function createServerPB() {
return new PocketBase(process.env.NEXT_PUBLIC_POCKETBASE_URL || "http://127.0.0.1:8090")
}
/**
* Helper to find the best matching asset filename for a given original filename
* PocketBase sanitizes filenames and appends a random suffix.
* This function attempts to map the stored original filename (in extras) to the actual sanitized filename (in assets).
*/
function findBestMatchingAsset(originalName: string, assets: string[]): string {
if (!originalName || !assets || assets.length === 0) return originalName
// 1. Exact match
if (assets.includes(originalName)) return originalName
// 2. Normalized match
// Normalize: remove non-alphanumeric, lowercase
const normalize = (s: string) => s.replace(/[^a-z0-9]/gi, "").toLowerCase()
// Remove extension for comparison
const originalBase = originalName.substring(0, originalName.lastIndexOf(".")) || originalName
const normalizedOriginal = normalize(originalBase)
// Check against assets
for (const asset of assets) {
const assetBase = asset.substring(0, asset.lastIndexOf(".")) || asset
const normalizedAsset = normalize(assetBase)
// Check if normalized asset STARTS with normalized original
// PocketBase usually appends `_` + random chars, which normalize removes or appends to end
// "langsmith (2)" -> "langsmith2"
// "langsmith_2_8tf..." -> "langsmith28tf..."
if (normalizedAsset.startsWith(normalizedOriginal)) {
return asset
}
}
return originalName // Fallback to original if no match found
}
/**
* Transform a CommunityGallery item to IconWithName format for use with IconSearch
* For community icons, base is the full HTTP URL to the main icon asset
@@ -29,6 +65,27 @@ function transformGalleryToIcon(item: CommunityGallery): any {
const mainAssetExt = item.assets?.[0]?.split(".").pop()?.toLowerCase() || "svg"
const baseFormat = mainAssetExt === "svg" ? "svg" : mainAssetExt === "png" ? "png" : "webp"
// Process and fix file mappings in extras
const colors = item.extras?.colors ? { ...item.extras.colors } : undefined
if (colors && item.assets) {
Object.keys(colors).forEach((key) => {
const k = key as keyof typeof colors
if (colors[k]) {
colors[k] = findBestMatchingAsset(colors[k]!, item.assets || [])
}
})
}
const wordmark = item.extras?.wordmark ? { ...item.extras.wordmark } : undefined
if (wordmark && item.assets) {
Object.keys(wordmark).forEach((key) => {
const k = key as keyof typeof wordmark
if (wordmark[k]) {
wordmark[k] = findBestMatchingAsset(wordmark[k]!, item.assets || [])
}
})
}
const transformed = {
name: item.name,
status: item.status,
@@ -46,8 +103,8 @@ function transformGalleryToIcon(item: CommunityGallery): any {
name: item.created_by || "Community",
},
},
colors: item.extras?.colors,
wordmark: item.extras?.wordmark,
colors: colors,
wordmark: wordmark,
},
}