mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 13:05:32 +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
@@ -7,12 +7,23 @@ import {executeConditional, fetchMany, fetchOne, upsertOne} from '@app/api/datab
|
||||
import {Db, type PreparedQuery} from '@app/api/database/CassandraTypes';
|
||||
import type {InstanceConfigurationRow} from '@app/api/database/types/InstanceConfigTypes';
|
||||
import {syncChannelThreadsConfig} from '@app/api/experiment/ChannelThreadsGate';
|
||||
import type {IStorageService} from '@app/api/infrastructure/IStorageService';
|
||||
import {
|
||||
type AccountIdentity,
|
||||
resolveAccountIdentity,
|
||||
type StoredAccountIdentity,
|
||||
setCachedAccountIdentity,
|
||||
} from '@app/api/instance/AccountIdentityModeCache';
|
||||
import {
|
||||
BRANDING_ASSET_FIELDS,
|
||||
type BrandingAssetField,
|
||||
brandingAssetReferenceFromAnyUrl,
|
||||
brandingAssetStorageKey,
|
||||
isBrandingAssetReference,
|
||||
mapBrandingAssets,
|
||||
resolveBrandingAsset,
|
||||
toStoredBrandingAsset,
|
||||
} from '@app/api/instance/BrandingAssetReferences';
|
||||
import {
|
||||
getDefaultDateOfBirthCollection,
|
||||
setCachedDateOfBirthCollection,
|
||||
@@ -684,11 +695,26 @@ const StoredInstanceAppPublicSchema = InstanceAppPublicSchema.extend({
|
||||
});
|
||||
|
||||
function parseStoredAppPublicConfig(raw: string | null): InstanceAppPublicConfig {
|
||||
return buildAppPublicConfig(
|
||||
salvageStoredConfig(StoredInstanceAppPublicSchema, readStoredConfigValue(raw, 'app public'), 'app public'),
|
||||
return resolveAppPublicConfig(
|
||||
buildAppPublicConfig(
|
||||
salvageStoredConfig(StoredInstanceAppPublicSchema, readStoredConfigValue(raw, 'app public'), 'app public'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function resolveAppPublicConfig(config: InstanceAppPublicConfig): InstanceAppPublicConfig {
|
||||
return {
|
||||
...config,
|
||||
branding: mapBrandingAssets(config.branding, (value) =>
|
||||
resolveBrandingAsset(value ?? null, Config.endpoints.media),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
function toStoredBranding<T extends Partial<Record<BrandingAssetField, string | null>>>(branding: T): T {
|
||||
return mapBrandingAssets(branding, (value) => toStoredBrandingAsset(value, Config.endpoints.media));
|
||||
}
|
||||
|
||||
function buildAppPublicConfig(config: z.infer<typeof StoredInstanceAppPublicSchema>): InstanceAppPublicConfig {
|
||||
const defaults = getDefaultAppPublicConfig();
|
||||
const {branding = {}, setup = {}, legal = {}, registration = {}} = config;
|
||||
@@ -1658,8 +1684,8 @@ export class InstanceConfigRepository {
|
||||
StoredInstanceAppPublicSchema,
|
||||
{
|
||||
branding: {
|
||||
...current.branding,
|
||||
...(config.branding ?? {}),
|
||||
...toStoredBranding(current.branding),
|
||||
...toStoredBranding(config.branding ?? {}),
|
||||
premium_product_name: premiumProductName,
|
||||
},
|
||||
setup: {
|
||||
@@ -1677,13 +1703,68 @@ export class InstanceConfigRepository {
|
||||
},
|
||||
'app public',
|
||||
);
|
||||
return {value: JSON.stringify(merged), result: buildAppPublicConfig(merged)};
|
||||
return {value: JSON.stringify(merged), result: resolveAppPublicConfig(buildAppPublicConfig(merged))};
|
||||
});
|
||||
await this.publishRefresh(cache.sourceId);
|
||||
setCachedDateOfBirthCollection(next.registration.collect_date_of_birth);
|
||||
return next;
|
||||
}
|
||||
|
||||
async normalizeStoredBrandingAssets(storageService: IStorageService): Promise<number> {
|
||||
const cache = this.configCache;
|
||||
await cache.getSnapshot();
|
||||
cache.assertActive();
|
||||
const raw = await this.fetchConfigFromDatabase(APP_PUBLIC_CONFIG_KEY);
|
||||
if (raw === null) return 0;
|
||||
const stored = salvageStoredConfig(
|
||||
StoredInstanceAppPublicSchema,
|
||||
readStoredConfigValue(raw, 'app public'),
|
||||
'app public',
|
||||
);
|
||||
const rewrites = new Map<string, string>();
|
||||
for (const field of BRANDING_ASSET_FIELDS) {
|
||||
const value = stored.branding?.[field];
|
||||
if (typeof value !== 'string' || isBrandingAssetReference(value) || rewrites.has(value)) continue;
|
||||
const reference = toStoredBrandingAsset(value, Config.endpoints.media);
|
||||
if (reference !== value && typeof reference === 'string') {
|
||||
rewrites.set(value, reference);
|
||||
continue;
|
||||
}
|
||||
const candidate = brandingAssetReferenceFromAnyUrl(value);
|
||||
if (candidate === null) continue;
|
||||
const metadata = await storageService.getObjectMetadata(
|
||||
Config.s3.buckets.cdn,
|
||||
brandingAssetStorageKey(candidate),
|
||||
);
|
||||
if (metadata !== null) {
|
||||
rewrites.set(value, candidate);
|
||||
}
|
||||
}
|
||||
if (rewrites.size === 0) return 0;
|
||||
const {result} = await this.compareAndSetStoredValue(cache, APP_PUBLIC_CONFIG_KEY, (current) => {
|
||||
const latest = salvageStoredConfig(
|
||||
StoredInstanceAppPublicSchema,
|
||||
readStoredConfigValue(current, 'app public'),
|
||||
'app public',
|
||||
);
|
||||
if (!latest.branding) return {value: null, result: 0};
|
||||
let changed = 0;
|
||||
const branding = mapBrandingAssets(latest.branding, (value) => {
|
||||
const rewrite = typeof value === 'string' ? rewrites.get(value) : undefined;
|
||||
if (rewrite === undefined) return value;
|
||||
changed++;
|
||||
return rewrite;
|
||||
});
|
||||
if (changed === 0) return {value: null, result: 0};
|
||||
const next = validateStoredConfig(StoredInstanceAppPublicSchema, {...latest, branding}, 'app public');
|
||||
return {value: JSON.stringify(next), result: changed};
|
||||
});
|
||||
if (result > 0) {
|
||||
await this.publishRefresh(cache.sourceId);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async getInstancePolicyConfig(): Promise<InstancePolicyConfig> {
|
||||
const raw = await this.getConfig(INSTANCE_POLICY_CONFIG_KEY);
|
||||
return parseStoredInstancePolicyConfig(raw);
|
||||
|
||||
Reference in new issue
Block a user