refactor(api): emit moderation events and apply account actions (#3031)

This commit is contained in:
Hampus authored and GitHub committed 2026-09-29 12:24:15 +02:00
1 parent c488906131
commit 364084c819
385 files changed
+8994 -22221

No files matched your search

+3 -32
View File
@@ -194,9 +194,6 @@ function defaultConfig(): MasterConfig {
from_name: 'Fluxer',
app_base_url: '',
},
sms: {
enabled: false,
},
captcha: {
enabled: false,
provider: 'none',
@@ -251,12 +248,9 @@ function defaultConfig(): MasterConfig {
},
},
blocklist_feeds: {},
tor_exit_list: {},
breached_password_check: {},
risk_integration: {
enabled: false,
ipinfo_api_key: '',
account_policy_dsl: undefined,
ipinfo: {
api_key: '',
},
push: {
apns: {
@@ -277,23 +271,6 @@ function defaultConfig(): MasterConfig {
setup: {
configured: false,
},
abuse_policy: {
inbound_phone_country_codes: [],
phone_flagging: {
enabled: true,
exempt_country_codes: [],
},
phone_verification: {
inbound_required_prefixes: [],
},
direct_contact_spam: {
enabled: false,
country_codes: [],
distinct_target_threshold: 25,
target_window_ms: 2 * 60 * 60 * 1000,
action: 'flag_spammer',
},
},
},
dev: {
relax_registration_rate_limits: false,
@@ -590,11 +567,6 @@ function normalizeConfig(config: MasterConfig): MasterConfig {
assertOneOf(config.integrations.captcha.provider, ['hcaptcha', 'turnstile', 'none'], 'FLUXER_CAPTCHA_PROVIDER');
assertOneOf(config.integrations.search.engine, ['elasticsearch', 'meilisearch'], 'FLUXER_SEARCH_ENGINE');
assertOneOf(config.integrations.cache_purge.adapter, CACHE_PURGE_ADAPTER_NAMES, 'FLUXER_CACHE_PURGE_ADAPTER');
assertOneOf(
config.instance.abuse_policy.direct_contact_spam.action,
['flag_spammer', 'suppress_delivery'],
'FLUXER_ABUSE_DIRECT_CONTACT_SPAM_ACTION',
);
validatePostgresConfig(config);
validateCaptchaConfig(config);
validateApiWorkerConfig(config);
@@ -656,7 +628,7 @@ function applyPublicPort(config: MasterConfig, endpoints: DerivedEndpoints): Mas
}
const {bluesky, passkeys} = config.auth;
const {branding} = config.instance;
const {email, sms, voice} = config.integrations;
const {email, voice} = config.integrations;
return {
...config,
domain: {
@@ -696,7 +668,6 @@ function applyPublicPort(config: MasterConfig, endpoints: DerivedEndpoints): Mas
integrations: {
...config.integrations,
email: {...email, app_base_url: normalize(email.app_base_url)},
sms: {...sms, inbound_webhook_public_url: normalizeOptional(sms.inbound_webhook_public_url)},
voice: {...voice, url: normalize(voice.url)},
},
instance: {
+2 -33
View File
@@ -207,15 +207,6 @@ export interface MasterConfig {
secure: boolean;
};
};
sms: {
enabled: boolean;
account_sid?: string;
auth_token?: string;
verify_service_sid?: string;
inbound_challenge_number?: string;
inbound_webhook_auth_token?: string;
inbound_webhook_public_url?: string;
};
captcha: {
enabled: boolean;
provider: 'hcaptcha' | 'turnstile' | 'none';
@@ -288,16 +279,11 @@ export interface MasterConfig {
blocklist_feeds: {
enabled?: boolean;
};
tor_exit_list: {
enabled?: boolean;
};
breached_password_check: {
enabled?: boolean;
};
risk_integration: {
enabled: boolean;
ipinfo_api_key: string;
account_policy_dsl?: unknown;
ipinfo: {
api_key: string;
};
push: {
apns: {
@@ -340,23 +326,6 @@ export interface MasterConfig {
setup: {
configured: boolean;
};
abuse_policy: {
inbound_phone_country_codes: Array<string>;
phone_flagging: {
enabled: boolean;
exempt_country_codes: Array<string>;
};
phone_verification: {
inbound_required_prefixes: Array<string>;
};
direct_contact_spam: {
enabled: boolean;
country_codes: Array<string>;
distinct_target_threshold: number;
target_window_ms: number;
action: 'flag_spammer' | 'suppress_delivery';
};
};
};
dev: {
relax_registration_rate_limits: boolean;
@@ -299,7 +299,6 @@ describe('ConfigLoader', () => {
FLUXER_POSTGRES_PREPARED_STATEMENTS: 'false',
FLUXER_API_WORKER_MODE: 'single_task',
FLUXER_API_WORKER_TASK: 'processStripeWebhook',
FLUXER_ACCOUNT_POLICY_DSL: '{"version":1,"id":"env_policy","rules":[]}',
FLUXER_LIVEKIT_ENABLED: 'true',
FLUXER_LIVEKIT_DEFAULT_REGION:
'{"id":"local","name":"Local","emoji":"LC","latitude":59.3293,"longitude":18.0686}',
@@ -316,11 +315,6 @@ describe('ConfigLoader', () => {
expect(config.database.postgres.prepared_statements).toBe(false);
expect(config.services.api.worker?.mode).toBe('single_task');
expect(config.services.api.worker?.task).toBe('processStripeWebhook');
expect(config.integrations.risk_integration.account_policy_dsl).toEqual({
version: 1,
id: 'env_policy',
rules: [],
});
expect(config.integrations.voice.default_region?.id).toBe('local');
});
@@ -475,7 +469,7 @@ describe('ConfigLoader', () => {
await expect(loadConfig()).rejects.toThrow('FLUXER_POSTGRES_SSL must be true');
});
test('parses self-host branding, setup, abuse policy, and search engine environment variables', async () => {
test('parses self-host branding, setup, and search engine environment variables', async () => {
stubMinimalEnv({
FLUXER_SEARCH_ENGINE: 'meilisearch',
FLUXER_SEARCH_URL: 'http://meilisearch:7700',
@@ -491,15 +485,6 @@ describe('ConfigLoader', () => {
FLUXER_APP_STATUS_PAGE_URL: 'https://status.example',
FLUXER_APP_STATUS_PAGE_INCIDENT_HISTORY_URL: 'https://status.example/history',
FLUXER_INSTANCE_SETUP_CONFIGURED: 'true',
FLUXER_ABUSE_INBOUND_PHONE_COUNTRY_CODES: 'AA,BB',
FLUXER_ABUSE_PHONE_FLAGGING_ENABLED: 'false',
FLUXER_ABUSE_PHONE_FLAGGING_EXEMPT_COUNTRY_CODES: 'CC,DD',
FLUXER_ABUSE_PHONE_INBOUND_REQUIRED_PREFIXES: '+101,+202',
FLUXER_ABUSE_DIRECT_CONTACT_SPAM_ENABLED: 'true',
FLUXER_ABUSE_DIRECT_CONTACT_SPAM_COUNTRY_CODES: 'AA,BB',
FLUXER_ABUSE_DIRECT_CONTACT_SPAM_DISTINCT_TARGET_THRESHOLD: '9',
FLUXER_ABUSE_DIRECT_CONTACT_SPAM_TARGET_WINDOW_MS: '12345',
FLUXER_ABUSE_DIRECT_CONTACT_SPAM_ACTION: 'suppress_delivery',
});
const config = await loadConfig();
@@ -520,23 +505,6 @@ describe('ConfigLoader', () => {
status_page_incident_history_url: 'https://status.example/history',
});
expect(config.instance.setup.configured).toBe(true);
expect(config.instance.abuse_policy).toEqual({
inbound_phone_country_codes: ['AA', 'BB'],
phone_flagging: {
enabled: false,
exempt_country_codes: ['CC', 'DD'],
},
phone_verification: {
inbound_required_prefixes: ['+101', '+202'],
},
direct_contact_spam: {
enabled: true,
country_codes: ['AA', 'BB'],
distinct_target_threshold: 9,
target_window_ms: 12345,
action: 'suppress_delivery',
},
});
});
test('rejects an enabled captcha with no keys for the selected provider', async () => {
@@ -677,22 +645,27 @@ describe('ConfigLoader', () => {
const config = await loadConfig();
expect(config.integrations.tor_exit_list.enabled).toBeUndefined();
expect(config.integrations.breached_password_check.enabled).toBeUndefined();
});
test('reads the optional outbound lookup switches from the environment', async () => {
stubMinimalEnv({
FLUXER_TOR_EXIT_LIST_ENABLED: 'true',
FLUXER_BREACHED_PASSWORD_CHECK_ENABLED: 'false',
});
const config = await loadConfig();
expect(config.integrations.tor_exit_list.enabled).toBe(true);
expect(config.integrations.breached_password_check.enabled).toBe(false);
});
test('reads the IPinfo key from its current name before the previous one', async () => {
stubMinimalEnv({FLUXER_RISK_IPINFO_API_KEY: 'previous'});
expect((await loadConfig()).integrations.ipinfo.api_key).toBe('previous');
resetConfig();
stubMinimalEnv({FLUXER_IPINFO_API_KEY: 'current', FLUXER_RISK_IPINFO_API_KEY: 'previous'});
expect((await loadConfig()).integrations.ipinfo.api_key).toBe('current');
});
test('leaves Bluesky login off with no legal URLs by default', async () => {
stubMinimalEnv();
@@ -823,7 +796,6 @@ describe('ConfigLoader', () => {
FLUXER_GATEWAY_MEDIA_PROXY_ENDPOINT: 'http://localhost/media',
FLUXER_S3_PUBLIC_ENDPOINT: 'http://localhost/s3',
FLUXER_EMAIL_APP_BASE_URL: 'http://localhost',
FLUXER_SMS_INBOUND_WEBHOOK_PUBLIC_URL: 'http://localhost/webhooks/sms',
FLUXER_AUTH_BLUESKY_CLIENT_URI: 'http://localhost',
FLUXER_AUTH_BLUESKY_TOS_URI: 'http://localhost/terms',
FLUXER_APP_ICON_URL: 'http://localhost/icon.png',
@@ -835,7 +807,6 @@ describe('ConfigLoader', () => {
expect(config.services.gateway.media_proxy_endpoint).toBe('http://localhost:8088/media');
expect(config.s3?.presigned_url_base).toBe('http://localhost:8088/s3');
expect(config.integrations.email.app_base_url).toBe('http://localhost:8088');
expect(config.integrations.sms.inbound_webhook_public_url).toBe('http://localhost:8088/webhooks/sms');
expect(config.auth.bluesky.client_uri).toBe('http://localhost:8088');
expect(config.auth.bluesky.tos_uri).toBe('http://localhost:8088/terms');
expect(config.instance.branding.icon_url).toBe('http://localhost:8088/icon.png');
@@ -233,13 +233,6 @@ const NAMED_FLUXER_ENV_OVERRIDES: Record<string, NamedEnvOverride> = {
FLUXER_EMAIL_SMTP_USERNAME: {path: ['integrations', 'email', 'smtp', 'username']},
FLUXER_EMAIL_SMTP_PASSWORD: {path: ['integrations', 'email', 'smtp', 'password']},
FLUXER_EMAIL_SMTP_SECURE: {path: ['integrations', 'email', 'smtp', 'secure'], parse: parseBoolean},
FLUXER_SMS_ENABLED: {path: ['integrations', 'sms', 'enabled'], parse: parseBoolean},
FLUXER_SMS_ACCOUNT_SID: {path: ['integrations', 'sms', 'account_sid']},
FLUXER_SMS_AUTH_TOKEN: {path: ['integrations', 'sms', 'auth_token']},
FLUXER_SMS_VERIFY_SERVICE_SID: {path: ['integrations', 'sms', 'verify_service_sid']},
FLUXER_SMS_INBOUND_CHALLENGE_NUMBER: {path: ['integrations', 'sms', 'inbound_challenge_number']},
FLUXER_SMS_INBOUND_WEBHOOK_AUTH_TOKEN: {path: ['integrations', 'sms', 'inbound_webhook_auth_token']},
FLUXER_SMS_INBOUND_WEBHOOK_PUBLIC_URL: {path: ['integrations', 'sms', 'inbound_webhook_public_url']},
FLUXER_CAPTCHA_ENABLED: {path: ['integrations', 'captcha', 'enabled'], parse: parseBoolean},
FLUXER_CAPTCHA_PROVIDER: {path: ['integrations', 'captcha', 'provider']},
FLUXER_CAPTCHA_HCAPTCHA_SITE_KEY: {path: ['integrations', 'captcha', 'hcaptcha', 'site_key']},
@@ -326,17 +319,11 @@ const NAMED_FLUXER_ENV_OVERRIDES: Record<string, NamedEnvOverride> = {
parse: parseInteger,
},
FLUXER_BLOCKLIST_FEEDS_ENABLED: {path: ['integrations', 'blocklist_feeds', 'enabled'], parse: parseBoolean},
FLUXER_TOR_EXIT_LIST_ENABLED: {path: ['integrations', 'tor_exit_list', 'enabled'], parse: parseBoolean},
FLUXER_BREACHED_PASSWORD_CHECK_ENABLED: {
path: ['integrations', 'breached_password_check', 'enabled'],
parse: parseBoolean,
},
FLUXER_RISK_INTEGRATION_ENABLED: {path: ['integrations', 'risk_integration', 'enabled'], parse: parseBoolean},
FLUXER_RISK_IPINFO_API_KEY: {path: ['integrations', 'risk_integration', 'ipinfo_api_key']},
FLUXER_ACCOUNT_POLICY_DSL: {
path: ['integrations', 'risk_integration', 'account_policy_dsl'],
parse: parseEnvValue,
},
FLUXER_IPINFO_API_KEY: {path: ['integrations', 'ipinfo', 'api_key']},
FLUXER_PUSH_APNS_ENABLED: {path: ['integrations', 'push', 'apns', 'enabled'], parse: parseBoolean},
FLUXER_PUSH_APNS_TEAM_ID: {path: ['integrations', 'push', 'apns', 'team_id']},
FLUXER_PUSH_APNS_KEY_ID: {path: ['integrations', 'push', 'apns', 'key_id']},
@@ -366,41 +353,6 @@ const NAMED_FLUXER_ENV_OVERRIDES: Record<string, NamedEnvOverride> = {
FLUXER_APP_STATUS_PAGE_URL: {path: ['instance', 'branding', 'status_page_url']},
FLUXER_APP_STATUS_PAGE_INCIDENT_HISTORY_URL: {path: ['instance', 'branding', 'status_page_incident_history_url']},
FLUXER_INSTANCE_SETUP_CONFIGURED: {path: ['instance', 'setup', 'configured'], parse: parseBoolean},
FLUXER_ABUSE_INBOUND_PHONE_COUNTRY_CODES: {
path: ['instance', 'abuse_policy', 'inbound_phone_country_codes'],
parse: parseCsv,
},
FLUXER_ABUSE_PHONE_FLAGGING_ENABLED: {
path: ['instance', 'abuse_policy', 'phone_flagging', 'enabled'],
parse: parseBoolean,
},
FLUXER_ABUSE_PHONE_FLAGGING_EXEMPT_COUNTRY_CODES: {
path: ['instance', 'abuse_policy', 'phone_flagging', 'exempt_country_codes'],
parse: parseCsv,
},
FLUXER_ABUSE_PHONE_INBOUND_REQUIRED_PREFIXES: {
path: ['instance', 'abuse_policy', 'phone_verification', 'inbound_required_prefixes'],
parse: parseCsv,
},
FLUXER_ABUSE_DIRECT_CONTACT_SPAM_ENABLED: {
path: ['instance', 'abuse_policy', 'direct_contact_spam', 'enabled'],
parse: parseBoolean,
},
FLUXER_ABUSE_DIRECT_CONTACT_SPAM_COUNTRY_CODES: {
path: ['instance', 'abuse_policy', 'direct_contact_spam', 'country_codes'],
parse: parseCsv,
},
FLUXER_ABUSE_DIRECT_CONTACT_SPAM_DISTINCT_TARGET_THRESHOLD: {
path: ['instance', 'abuse_policy', 'direct_contact_spam', 'distinct_target_threshold'],
parse: parseInteger,
},
FLUXER_ABUSE_DIRECT_CONTACT_SPAM_TARGET_WINDOW_MS: {
path: ['instance', 'abuse_policy', 'direct_contact_spam', 'target_window_ms'],
parse: parseInteger,
},
FLUXER_ABUSE_DIRECT_CONTACT_SPAM_ACTION: {
path: ['instance', 'abuse_policy', 'direct_contact_spam', 'action'],
},
FLUXER_DISCOVERY_ENABLED: {path: ['discovery', 'enabled'], parse: parseBoolean},
FLUXER_DISCOVERY_MIN_MEMBER_COUNT: {path: ['discovery', 'min_member_count'], parse: parseInteger},
FLUXER_DELETION_GRACE_PERIOD_HOURS: {path: ['deletion_grace_period_hours'], parse: parseInteger},
@@ -537,6 +489,7 @@ export function setNestedValue(target: ConfigContainer, keys: Array<ConfigPathKe
const NAMED_FLUXER_ENV_ALIASES: Record<string, string | undefined> = {
FLUXER_INTERNAL_MEDIA_PROXY_ENDPOINT: 'FLUXER_MEDIA_PROXY_ENDPOINT',
FLUXER_NATS_URL: 'FLUXER_NATS_CORE_URL',
FLUXER_IPINFO_API_KEY: 'FLUXER_RISK_IPINFO_API_KEY',
};
export function buildNamedFluxerEnvOverrides(env: NodeJS.ProcessEnv): ConfigObject {