feat(enforcement): record decisions and send notices (#3492)

This commit is contained in:
Hampus authored and GitHub committed 2026-10-15 07:12:46 +02:00
1 parent da1842ecab
commit f5c30fdf8f
383 files changed
+29692 -4139

No files matched your search

+4 -8
View File
@@ -7,7 +7,7 @@ export interface ConfiguredLegalUrls {
guidelines_url: string | null;
}
export interface LegalUrls {
interface LegalUrls {
termsUrl: string | null;
guidelinesUrl: string | null;
}
@@ -18,15 +18,11 @@ export function setCachedConfiguredLegalUrls(legal: ConfiguredLegalUrls): void {
cachedConfiguredLegalUrls = {terms_url: legal.terms_url, guidelines_url: legal.guidelines_url};
}
export function resolveLegalUrls(legal: ConfiguredLegalUrls): LegalUrls {
export function getLegalUrls(): LegalUrls {
const hostedUrl = (path: string): string | null =>
Config.instance.selfHosted ? null : `${Config.endpoints.marketing}${path}`;
return {
termsUrl: legal.terms_url ?? hostedUrl('/terms'),
guidelinesUrl: legal.guidelines_url ?? hostedUrl('/guidelines'),
termsUrl: cachedConfiguredLegalUrls.terms_url ?? hostedUrl('/terms'),
guidelinesUrl: cachedConfiguredLegalUrls.guidelines_url ?? hostedUrl('/guidelines'),
};
}
export function getLegalUrls(): LegalUrls {
return resolveLegalUrls(cachedConfiguredLegalUrls);
}
@@ -0,0 +1,85 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {getConfig} from '@app/api/Config';
import {setCassandraQueryExecutorForTesting} from '@app/api/database/CassandraQueryExecution';
import {InstanceConfigRepository} from '@app/api/instance/InstanceConfigRepository';
import {getInstanceProductName, setCachedProductName} from '@app/api/instance/ProductName';
import {InMemoryCassandraQueryExecutor} from '@app/api/test/InMemoryCassandraQueryExecutor';
import {MockKVProvider} from '@pkgs/kv_client/src/testing/MockKVProvider';
import {afterEach, beforeEach, describe, expect, it} from 'vitest';
const WEB_APP_HOST = 'chat.example.org';
describe('getInstanceProductName', () => {
const config = getConfig();
const original = {
selfHosted: config.instance.selfHosted,
productName: config.instance.branding.productName,
webApp: config.endpoints.webApp,
};
beforeEach(() => {
config.endpoints.webApp = `https://${WEB_APP_HOST}:8443/app`;
config.instance.branding.productName = '';
});
afterEach(() => {
config.instance.selfHosted = original.selfHosted;
config.instance.branding.productName = original.productName;
config.endpoints.webApp = original.webApp;
setCachedProductName(null);
});
describe('hosted', () => {
beforeEach(() => {
config.instance.selfHosted = false;
});
it.each([
[null, 'Fluxer'],
['Fluxer', 'Fluxer'],
['Example Chat', 'Example Chat'],
])('resolves %j to %j', (cached, expected) => {
setCachedProductName(cached);
expect(getInstanceProductName()).toBe(expected);
});
});
describe('self-hosted', () => {
beforeEach(() => {
config.instance.selfHosted = true;
});
it.each([null, '', ' ', 'Fluxer', ' fluxer ', 'FLUXER'])('resolves %j to the web app host', (cached) => {
setCachedProductName(cached);
expect(getInstanceProductName()).toBe(WEB_APP_HOST);
});
it('resolves a configured default of Fluxer to the web app host', () => {
config.instance.branding.productName = 'Fluxer';
expect(getInstanceProductName()).toBe(WEB_APP_HOST);
});
it.each(['Fluxer Community', 'Example Chat'])('leaves %j alone', (cached) => {
setCachedProductName(cached);
expect(getInstanceProductName()).toBe(cached);
});
it('resolves a persisted Fluxer name to the web app host', async () => {
setCassandraQueryExecutorForTesting(new InMemoryCassandraQueryExecutor());
const kvProvider = new MockKVProvider();
const writer = new InstanceConfigRepository(kvProvider);
const reader = new InstanceConfigRepository(kvProvider);
try {
await writer.setConfig('app_public_config', JSON.stringify({branding: {product_name: 'Fluxer'}}));
setCachedProductName('Example Chat');
await reader.initialize();
expect((await reader.getAppPublicConfig()).branding.product_name).toBe('Fluxer');
expect(getInstanceProductName()).toBe(WEB_APP_HOST);
} finally {
writer.shutdown();
reader.shutdown();
}
});
});
});
+13 -2
View File
@@ -2,16 +2,27 @@
import {Config} from '@app/api/Config';
const UPSTREAM_PRODUCT_NAME = 'Fluxer';
let cachedProductName: string | null = null;
export function getDefaultProductName(): string {
return Config.instance.branding.productName || 'Fluxer';
return Config.instance.branding.productName || UPSTREAM_PRODUCT_NAME;
}
export function getInstanceProductName(): string {
return cachedProductName ?? getDefaultProductName();
const productName = cachedProductName ?? getDefaultProductName();
if (Config.instance.selfHosted && isUnbrandedProductName(productName)) {
return new URL(Config.endpoints.webApp).hostname;
}
return productName;
}
export function setCachedProductName(productName: string | null): void {
cachedProductName = productName;
}
function isUnbrandedProductName(productName: string): boolean {
const normalized = productName.trim().toLowerCase();
return normalized === '' || normalized === UPSTREAM_PRODUCT_NAME.toLowerCase();
}