mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 05:25:40 +09:00
fix(api): store branding images as media references (#3313)
This commit is contained in:
1 parent
847d449882
commit
4ecbe9603c
4 files changed
+211
-5
No files matched your search
@@ -0,0 +1,67 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
export const BRANDING_ASSET_FIELDS = ['icon_url', 'symbol_url', 'logo_url', 'wordmark_url', 'favicon_url'] as const;
|
||||
|
||||
export type BrandingAssetField = (typeof BRANDING_ASSET_FIELDS)[number];
|
||||
|
||||
const BRANDING_ASSET_REFERENCE = /^branding\/\d{1,20}\/(?:a_)?[0-9a-f]{8}\.[a-z0-9]{2,5}$/;
|
||||
const BRANDING_ASSET_PATH = /\/(branding\/\d{1,20}\/(?:a_)?[0-9a-f]{8}\.[a-z0-9]{2,5})$/;
|
||||
|
||||
function trimTrailingSlashes(value: string): string {
|
||||
return value.replace(/\/+$/u, '');
|
||||
}
|
||||
|
||||
export function isBrandingAssetReference(value: string): boolean {
|
||||
return BRANDING_ASSET_REFERENCE.test(value);
|
||||
}
|
||||
|
||||
export function brandingAssetReferenceFromOwnUrl(value: string, mediaEndpoint: string): string | null {
|
||||
const prefix = `${trimTrailingSlashes(mediaEndpoint)}/`;
|
||||
if (!value.startsWith(prefix)) return null;
|
||||
const remainder = value.slice(prefix.length);
|
||||
return isBrandingAssetReference(remainder) ? remainder : null;
|
||||
}
|
||||
|
||||
export function brandingAssetReferenceFromAnyUrl(value: string): string | null {
|
||||
let url: URL;
|
||||
try {
|
||||
url = new URL(value);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
if ((url.protocol !== 'https:' && url.protocol !== 'http:') || url.search !== '' || url.hash !== '') return null;
|
||||
return BRANDING_ASSET_PATH.exec(url.pathname)?.[1] ?? null;
|
||||
}
|
||||
|
||||
export function brandingAssetStorageKey(reference: string): string {
|
||||
const slash = reference.lastIndexOf('/');
|
||||
const file = reference.slice(slash + 1);
|
||||
const hash = file.slice(0, file.indexOf('.')).replace(/^a_/u, '');
|
||||
return `${reference.slice(0, slash)}/${hash}`;
|
||||
}
|
||||
|
||||
export function toStoredBrandingAsset(
|
||||
value: string | null | undefined,
|
||||
mediaEndpoint: string,
|
||||
): string | null | undefined {
|
||||
if (value === undefined || value === null) return value;
|
||||
return brandingAssetReferenceFromOwnUrl(value, mediaEndpoint) ?? value;
|
||||
}
|
||||
|
||||
export function resolveBrandingAsset(value: string | null, mediaEndpoint: string): string | null {
|
||||
if (value === null || !isBrandingAssetReference(value)) return value;
|
||||
return `${trimTrailingSlashes(mediaEndpoint)}/${value}`;
|
||||
}
|
||||
|
||||
export function mapBrandingAssets<T extends Partial<Record<BrandingAssetField, string | null | undefined>>>(
|
||||
branding: T,
|
||||
map: (value: string | null | undefined) => string | null | undefined,
|
||||
): T {
|
||||
const next = {...branding};
|
||||
for (const field of BRANDING_ASSET_FIELDS) {
|
||||
if (field in next) {
|
||||
next[field] = map(next[field]) as T[BrandingAssetField];
|
||||
}
|
||||
}
|
||||
return next;
|
||||
}
|
||||
Reference in new issue
Block a user