mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 21:15:32 +09:00
feat(api): purge media through the controller edge api (#3454)
This commit is contained in:
1 parent
f509bef1fb
commit
6e2bf3cbe6
13 files changed
+248
-22
No files matched your search
@@ -218,6 +218,11 @@ function defaultConfig(): MasterConfig {
|
||||
token: '',
|
||||
timeout_ms: 10_000,
|
||||
},
|
||||
controller: {
|
||||
endpoint: '',
|
||||
token: '',
|
||||
timeout_ms: 10_000,
|
||||
},
|
||||
},
|
||||
blocklist_feeds: {},
|
||||
breached_password_check: {},
|
||||
@@ -484,26 +489,37 @@ function normalizeAppOriginAliases(config: MasterConfig): void {
|
||||
];
|
||||
}
|
||||
|
||||
function validateCachePurgeConfig(config: MasterConfig): void {
|
||||
const cachePurge = config.integrations.cache_purge;
|
||||
if (cachePurge.adapter !== 'http') {
|
||||
return;
|
||||
}
|
||||
requireString(cachePurge.http.endpoint, 'FLUXER_CACHE_PURGE_HTTP_ENDPOINT');
|
||||
const endpoint = URL.parse(cachePurge.http.endpoint);
|
||||
function validateCachePurgeTarget(
|
||||
target: {endpoint: string; token: string; timeout_ms: number},
|
||||
envPrefix: string,
|
||||
protocols: ReadonlyArray<string>,
|
||||
): void {
|
||||
requireString(target.endpoint, `${envPrefix}_ENDPOINT`);
|
||||
const endpoint = URL.parse(target.endpoint);
|
||||
if (
|
||||
endpoint === null ||
|
||||
(endpoint.protocol !== 'http:' && endpoint.protocol !== 'https:') ||
|
||||
!protocols.includes(endpoint.protocol) ||
|
||||
endpoint.username !== '' ||
|
||||
endpoint.password !== ''
|
||||
) {
|
||||
throw new Error('FLUXER_CACHE_PURGE_HTTP_ENDPOINT must be an absolute http or https URL without credentials');
|
||||
const schemes = protocols.map((protocol) => protocol.slice(0, -1)).join(' or ');
|
||||
throw new Error(`${envPrefix}_ENDPOINT must be an absolute ${schemes} URL without credentials`);
|
||||
}
|
||||
requireString(cachePurge.http.token, 'FLUXER_CACHE_PURGE_HTTP_TOKEN');
|
||||
if (!/^[\x21-\x7e]+$/u.test(cachePurge.http.token)) {
|
||||
throw new Error('FLUXER_CACHE_PURGE_HTTP_TOKEN must contain only visible ASCII characters');
|
||||
requireString(target.token, `${envPrefix}_TOKEN`);
|
||||
if (!/^[\x21-\x7e]+$/u.test(target.token)) {
|
||||
throw new Error(`${envPrefix}_TOKEN must contain only visible ASCII characters`);
|
||||
}
|
||||
assertIntegerInRange(target.timeout_ms, `${envPrefix}_TIMEOUT_MS`, 1_000, 10_000);
|
||||
}
|
||||
|
||||
function validateCachePurgeConfig(config: MasterConfig): void {
|
||||
const cachePurge = config.integrations.cache_purge;
|
||||
if (cachePurge.adapter === 'http') {
|
||||
validateCachePurgeTarget(cachePurge.http, 'FLUXER_CACHE_PURGE_HTTP', ['http:', 'https:']);
|
||||
}
|
||||
if (cachePurge.adapter === 'controller') {
|
||||
validateCachePurgeTarget(cachePurge.controller, 'FLUXER_CACHE_PURGE_CONTROLLER', ['https:']);
|
||||
}
|
||||
assertIntegerInRange(cachePurge.http.timeout_ms, 'FLUXER_CACHE_PURGE_HTTP_TIMEOUT_MS', 1_000, 10_000);
|
||||
}
|
||||
|
||||
function validateStoreProductSlots(
|
||||
|
||||
@@ -5,7 +5,7 @@ import type {DerivedEndpoints} from '@fluxer/config/src/EndpointDerivation';
|
||||
type RuntimeEnv = 'development' | 'production' | 'test';
|
||||
type DatabaseBackend = 'postgres' | 'cassandra';
|
||||
type PublicScheme = 'http' | 'https';
|
||||
export const CACHE_PURGE_ADAPTER_NAMES = ['none', 'http'] as const;
|
||||
export const CACHE_PURGE_ADAPTER_NAMES = ['none', 'http', 'controller'] as const;
|
||||
export type CachePurgeAdapterName = (typeof CACHE_PURGE_ADAPTER_NAMES)[number];
|
||||
export const STORE_PRODUCT_SLOT_NAMES = ['monthly', 'yearly', 'gift_1_month', 'gift_1_year'] as const;
|
||||
export type StoreProductSlotName = (typeof STORE_PRODUCT_SLOT_NAMES)[number];
|
||||
@@ -243,6 +243,11 @@ export interface MasterConfig {
|
||||
token: string;
|
||||
timeout_ms: number;
|
||||
};
|
||||
controller: {
|
||||
endpoint: string;
|
||||
token: string;
|
||||
timeout_ms: number;
|
||||
};
|
||||
};
|
||||
blocklist_feeds: {
|
||||
enabled?: boolean;
|
||||
|
||||
@@ -603,6 +603,46 @@ describe('ConfigLoader', () => {
|
||||
}
|
||||
});
|
||||
|
||||
test('rejects the controller cache purge adapter without an endpoint', async () => {
|
||||
stubMinimalEnv({FLUXER_CACHE_PURGE_ADAPTER: 'controller'});
|
||||
await expect(loadConfig()).rejects.toThrow('FLUXER_CACHE_PURGE_CONTROLLER_ENDPOINT is required');
|
||||
});
|
||||
|
||||
test('rejects a controller cache purge endpoint that is not https', async () => {
|
||||
stubMinimalEnv({
|
||||
FLUXER_CACHE_PURGE_ADAPTER: 'controller',
|
||||
FLUXER_CACHE_PURGE_CONTROLLER_ENDPOINT: 'http://controller.test/v1/orgs/a/sites/b/purge',
|
||||
FLUXER_CACHE_PURGE_CONTROLLER_TOKEN: 'pak_test',
|
||||
});
|
||||
await expect(loadConfig()).rejects.toThrow(
|
||||
'FLUXER_CACHE_PURGE_CONTROLLER_ENDPOINT must be an absolute https URL without credentials',
|
||||
);
|
||||
});
|
||||
|
||||
test('rejects the controller cache purge adapter without a token', async () => {
|
||||
stubMinimalEnv({
|
||||
FLUXER_CACHE_PURGE_ADAPTER: 'controller',
|
||||
FLUXER_CACHE_PURGE_CONTROLLER_ENDPOINT: 'https://controller.test/v1/orgs/a/sites/b/purge',
|
||||
});
|
||||
await expect(loadConfig()).rejects.toThrow('FLUXER_CACHE_PURGE_CONTROLLER_TOKEN is required');
|
||||
});
|
||||
|
||||
test('accepts a complete controller cache purge configuration', async () => {
|
||||
stubMinimalEnv({
|
||||
FLUXER_CACHE_PURGE_ADAPTER: 'controller',
|
||||
FLUXER_CACHE_PURGE_CONTROLLER_ENDPOINT: 'https://controller.test/v1/orgs/a/sites/b/purge',
|
||||
FLUXER_CACHE_PURGE_CONTROLLER_TOKEN: 'pak_test',
|
||||
FLUXER_CACHE_PURGE_CONTROLLER_TIMEOUT_MS: '5000',
|
||||
});
|
||||
const cachePurge = (await loadConfig()).integrations.cache_purge;
|
||||
expect(cachePurge.adapter).toBe('controller');
|
||||
expect(cachePurge.controller).toEqual({
|
||||
endpoint: 'https://controller.test/v1/orgs/a/sites/b/purge',
|
||||
token: 'pak_test',
|
||||
timeout_ms: 5000,
|
||||
});
|
||||
});
|
||||
|
||||
test('leaves the cache purge settings unvalidated when the adapter is none', async () => {
|
||||
stubMinimalEnv({
|
||||
FLUXER_CACHE_PURGE_HTTP_ENDPOINT: 'ftp://purge.internal/purge',
|
||||
|
||||
@@ -239,6 +239,12 @@ const NAMED_FLUXER_ENV_OVERRIDES: Record<string, NamedEnvOverride> = {
|
||||
path: ['integrations', 'cache_purge', 'http', 'timeout_ms'],
|
||||
parse: parseInteger,
|
||||
},
|
||||
FLUXER_CACHE_PURGE_CONTROLLER_ENDPOINT: {path: ['integrations', 'cache_purge', 'controller', 'endpoint']},
|
||||
FLUXER_CACHE_PURGE_CONTROLLER_TOKEN: {path: ['integrations', 'cache_purge', 'controller', 'token']},
|
||||
FLUXER_CACHE_PURGE_CONTROLLER_TIMEOUT_MS: {
|
||||
path: ['integrations', 'cache_purge', 'controller', 'timeout_ms'],
|
||||
parse: parseInteger,
|
||||
},
|
||||
FLUXER_BLOCKLIST_FEEDS_ENABLED: {path: ['integrations', 'blocklist_feeds', 'enabled'], parse: parseBoolean},
|
||||
FLUXER_BREACHED_PASSWORD_CHECK_ENABLED: {
|
||||
path: ['integrations', 'breached_password_check', 'enabled'],
|
||||
|
||||
Reference in new issue
Block a user