fix: various fixes to things + simply app proxy sentry setup
This commit is contained in:
@@ -30,15 +30,10 @@ export interface AppProxyResult {
|
||||
export async function createAppProxyApp(options: CreateAppProxyAppOptions): Promise<AppProxyResult> {
|
||||
const {
|
||||
assetsPath = '/assets',
|
||||
config,
|
||||
cspDirectives,
|
||||
customMiddleware = [],
|
||||
logger,
|
||||
metricsCollector,
|
||||
rateLimitService = null,
|
||||
sentryProxyEnabled = true,
|
||||
sentryProxyPath = '/sentry',
|
||||
sentryProxyRouteEnabled = true,
|
||||
staticCDNEndpoint,
|
||||
staticDir,
|
||||
tracing,
|
||||
@@ -48,13 +43,9 @@ export async function createAppProxyApp(options: CreateAppProxyAppOptions): Prom
|
||||
|
||||
applyAppProxyMiddleware({
|
||||
app,
|
||||
config,
|
||||
customMiddleware,
|
||||
logger,
|
||||
metricsCollector,
|
||||
rateLimitService,
|
||||
sentryProxyEnabled,
|
||||
sentryProxyPath,
|
||||
tracing,
|
||||
});
|
||||
|
||||
@@ -63,9 +54,6 @@ export async function createAppProxyApp(options: CreateAppProxyAppOptions): Prom
|
||||
assetsPath,
|
||||
cspDirectives,
|
||||
logger,
|
||||
sentryProxy: config.sentry_proxy,
|
||||
sentryProxyPath,
|
||||
sentryProxyRouteEnabled,
|
||||
staticCDNEndpoint,
|
||||
staticDir,
|
||||
});
|
||||
|
||||
@@ -22,34 +22,13 @@ import type {SentryConfig, TelemetryConfig} from '@fluxer/config/src/MasterZodSc
|
||||
import type {MetricsCollector} from '@fluxer/hono_types/src/MetricsTypes';
|
||||
import type {TracingOptions} from '@fluxer/hono_types/src/TracingTypes';
|
||||
import type {Logger} from '@fluxer/logger/src/Logger';
|
||||
import type {IRateLimitService} from '@fluxer/rate_limit/src/IRateLimitService';
|
||||
import type {MiddlewareHandler} from 'hono';
|
||||
|
||||
export interface AppProxySentryProxyConfig {
|
||||
project_id: string;
|
||||
public_key: string;
|
||||
target_url: string;
|
||||
path_prefix: string;
|
||||
}
|
||||
|
||||
export interface AppProxyConfig {
|
||||
env: string;
|
||||
port: number;
|
||||
static_cdn_endpoint: string;
|
||||
sentry_proxy_path: string;
|
||||
sentry_report_host: string;
|
||||
sentry_proxy: AppProxySentryProxyConfig | null;
|
||||
assets_dir: string;
|
||||
kv: {
|
||||
url: string;
|
||||
timeout_ms: number;
|
||||
};
|
||||
rate_limit: {
|
||||
sentry: {
|
||||
limit: number;
|
||||
window_ms: number;
|
||||
};
|
||||
};
|
||||
telemetry: TelemetryConfig;
|
||||
sentry: SentryConfig;
|
||||
}
|
||||
@@ -57,7 +36,6 @@ export interface AppProxyConfig {
|
||||
export interface AppProxyContext {
|
||||
config: AppProxyConfig;
|
||||
logger: Logger;
|
||||
rateLimitService: IRateLimitService | null;
|
||||
}
|
||||
|
||||
export interface AppProxyHonoEnv {
|
||||
@@ -69,15 +47,11 @@ export type AppProxyMiddleware = MiddlewareHandler<AppProxyHonoEnv>;
|
||||
export interface CreateAppProxyAppOptions {
|
||||
config: AppProxyConfig;
|
||||
logger: Logger;
|
||||
rateLimitService?: IRateLimitService | null;
|
||||
metricsCollector?: MetricsCollector;
|
||||
tracing?: TracingOptions;
|
||||
customMiddleware?: Array<AppProxyMiddleware>;
|
||||
sentryProxyPath?: string;
|
||||
assetsPath?: string;
|
||||
staticCDNEndpoint?: string;
|
||||
staticDir?: string;
|
||||
cspDirectives?: CSPOptions;
|
||||
sentryProxyEnabled?: boolean;
|
||||
sentryProxyRouteEnabled?: boolean;
|
||||
}
|
||||
|
||||
@@ -17,44 +17,25 @@
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import type {AppProxyConfig, AppProxyHonoEnv, AppProxyMiddleware} from '@fluxer/app_proxy/src/AppProxyTypes';
|
||||
import {createProxyRateLimitMiddleware} from '@fluxer/app_proxy/src/app_proxy/middleware/ProxyRateLimit';
|
||||
import {createSentryHostProxyMiddleware} from '@fluxer/app_proxy/src/app_proxy/middleware/SentryHostProxy';
|
||||
import {proxySentry} from '@fluxer/app_proxy/src/app_proxy/proxy/SentryProxy';
|
||||
import {resolveSentryHost} from '@fluxer/app_proxy/src/app_proxy/utils/Host';
|
||||
import type {AppProxyHonoEnv, AppProxyMiddleware} from '@fluxer/app_proxy/src/AppProxyTypes';
|
||||
import {isExpectedError} from '@fluxer/app_proxy/src/ErrorClassification';
|
||||
import {applyMiddlewareStack} from '@fluxer/hono/src/middleware/MiddlewareStack';
|
||||
import type {MetricsCollector} from '@fluxer/hono_types/src/MetricsTypes';
|
||||
import type {TracingOptions} from '@fluxer/hono_types/src/TracingTypes';
|
||||
import type {Logger} from '@fluxer/logger/src/Logger';
|
||||
import type {IRateLimitService} from '@fluxer/rate_limit/src/IRateLimitService';
|
||||
import {captureException} from '@fluxer/sentry/src/Sentry';
|
||||
import type {Context, Hono} from 'hono';
|
||||
|
||||
interface ApplyAppProxyMiddlewareOptions {
|
||||
app: Hono<AppProxyHonoEnv>;
|
||||
config: AppProxyConfig;
|
||||
customMiddleware: Array<AppProxyMiddleware>;
|
||||
logger: Logger;
|
||||
metricsCollector?: MetricsCollector;
|
||||
rateLimitService: IRateLimitService | null;
|
||||
sentryProxyEnabled: boolean;
|
||||
sentryProxyPath: string;
|
||||
tracing?: TracingOptions;
|
||||
}
|
||||
|
||||
export function applyAppProxyMiddleware(options: ApplyAppProxyMiddlewareOptions): void {
|
||||
const {
|
||||
app,
|
||||
config,
|
||||
customMiddleware,
|
||||
logger,
|
||||
metricsCollector,
|
||||
rateLimitService,
|
||||
sentryProxyEnabled,
|
||||
sentryProxyPath,
|
||||
tracing,
|
||||
} = options;
|
||||
const {app, customMiddleware, logger, metricsCollector, tracing} = options;
|
||||
|
||||
applyMiddlewareStack(app, {
|
||||
requestId: {},
|
||||
@@ -81,7 +62,7 @@ export function applyAppProxyMiddleware(options: ApplyAppProxyMiddlewareOptions)
|
||||
skip: ['/_health'],
|
||||
},
|
||||
errorHandler: {
|
||||
includeStack: !sentryProxyEnabled,
|
||||
includeStack: true,
|
||||
logger: (error: Error, ctx: Context) => {
|
||||
if (!isExpectedError(error)) {
|
||||
captureException(error, {
|
||||
@@ -103,58 +84,4 @@ export function applyAppProxyMiddleware(options: ApplyAppProxyMiddlewareOptions)
|
||||
},
|
||||
customMiddleware,
|
||||
});
|
||||
|
||||
applySentryHostProxyMiddleware({
|
||||
app,
|
||||
config,
|
||||
logger,
|
||||
rateLimitService,
|
||||
sentryProxyEnabled,
|
||||
sentryProxyPath,
|
||||
});
|
||||
}
|
||||
|
||||
interface ApplySentryHostProxyMiddlewareOptions {
|
||||
app: Hono<AppProxyHonoEnv>;
|
||||
config: AppProxyConfig;
|
||||
logger: Logger;
|
||||
rateLimitService: IRateLimitService | null;
|
||||
sentryProxyEnabled: boolean;
|
||||
sentryProxyPath: string;
|
||||
}
|
||||
|
||||
function applySentryHostProxyMiddleware(options: ApplySentryHostProxyMiddlewareOptions): void {
|
||||
const {app, config, logger, rateLimitService, sentryProxyEnabled, sentryProxyPath} = options;
|
||||
const sentryHost = resolveSentryHost(config);
|
||||
const sentryProxy = config.sentry_proxy;
|
||||
|
||||
if (!sentryProxyEnabled || !sentryHost || !sentryProxy) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sentryRateLimitMiddleware = createProxyRateLimitMiddleware({
|
||||
rateLimitService,
|
||||
bucketPrefix: 'sentry-proxy',
|
||||
config: {
|
||||
enabled: config.rate_limit.sentry.limit > 0,
|
||||
limit: config.rate_limit.sentry.limit,
|
||||
windowMs: config.rate_limit.sentry.window_ms,
|
||||
skipPaths: ['/_health'],
|
||||
},
|
||||
logger,
|
||||
});
|
||||
|
||||
const sentryHostMiddleware = createSentryHostProxyMiddleware({
|
||||
sentryHost,
|
||||
rateLimitMiddleware: sentryRateLimitMiddleware,
|
||||
proxyHandler: (c) =>
|
||||
proxySentry(c, {
|
||||
enabled: true,
|
||||
logger,
|
||||
sentryProxy,
|
||||
sentryProxyPath,
|
||||
}),
|
||||
});
|
||||
|
||||
app.use('*', sentryHostMiddleware);
|
||||
}
|
||||
|
||||
@@ -18,9 +18,8 @@
|
||||
*/
|
||||
|
||||
import {resolve} from 'node:path';
|
||||
import type {AppProxyHonoEnv, AppProxySentryProxyConfig} from '@fluxer/app_proxy/src/AppProxyTypes';
|
||||
import type {AppProxyHonoEnv} from '@fluxer/app_proxy/src/AppProxyTypes';
|
||||
import {proxyAssets} from '@fluxer/app_proxy/src/app_proxy/proxy/AssetsProxy';
|
||||
import {proxySentry} from '@fluxer/app_proxy/src/app_proxy/proxy/SentryProxy';
|
||||
import {createSpaIndexRoute} from '@fluxer/app_proxy/src/app_server/routes/SpaIndexRoute';
|
||||
import type {CSPOptions} from '@fluxer/app_proxy/src/app_server/utils/CSP';
|
||||
import type {Logger} from '@fluxer/logger/src/Logger';
|
||||
@@ -31,46 +30,15 @@ interface RegisterAppProxyRoutesOptions {
|
||||
assetsPath: string;
|
||||
cspDirectives?: CSPOptions;
|
||||
logger: Logger;
|
||||
sentryProxy: AppProxySentryProxyConfig | null;
|
||||
sentryProxyPath: string;
|
||||
sentryProxyRouteEnabled: boolean;
|
||||
staticCDNEndpoint: string | undefined;
|
||||
staticDir?: string;
|
||||
}
|
||||
|
||||
export function registerAppProxyRoutes(options: RegisterAppProxyRoutesOptions): void {
|
||||
const {
|
||||
app,
|
||||
assetsPath,
|
||||
cspDirectives,
|
||||
logger,
|
||||
sentryProxy,
|
||||
sentryProxyPath,
|
||||
sentryProxyRouteEnabled,
|
||||
staticCDNEndpoint,
|
||||
staticDir,
|
||||
} = options;
|
||||
const {app, assetsPath, cspDirectives, logger, staticCDNEndpoint, staticDir} = options;
|
||||
|
||||
app.get('/_health', (c) => c.text('OK'));
|
||||
|
||||
app.all(sentryProxyPath, (c) =>
|
||||
proxySentry(c, {
|
||||
enabled: sentryProxyRouteEnabled,
|
||||
logger,
|
||||
sentryProxy,
|
||||
sentryProxyPath,
|
||||
}),
|
||||
);
|
||||
|
||||
app.all(`${sentryProxyPath}/*`, (c) =>
|
||||
proxySentry(c, {
|
||||
enabled: sentryProxyRouteEnabled,
|
||||
logger,
|
||||
sentryProxy,
|
||||
sentryProxyPath,
|
||||
}),
|
||||
);
|
||||
|
||||
if (staticCDNEndpoint) {
|
||||
app.get(`${assetsPath}/*`, (c) =>
|
||||
proxyAssets(c, {
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {HttpStatus} from '@fluxer/constants/src/HttpConstants';
|
||||
import {extractClientIp} from '@fluxer/ip_utils/src/ClientIp';
|
||||
import type {Logger} from '@fluxer/logger/src/Logger';
|
||||
import type {IRateLimitService} from '@fluxer/rate_limit/src/IRateLimitService';
|
||||
import {createRateLimitMiddleware} from '@fluxer/rate_limit/src/middleware/RateLimitMiddleware';
|
||||
|
||||
interface ProxyRateLimitConfig {
|
||||
enabled?: boolean;
|
||||
limit: number;
|
||||
windowMs: number;
|
||||
skipPaths?: Array<string>;
|
||||
}
|
||||
|
||||
export interface CreateProxyRateLimitMiddlewareOptions {
|
||||
rateLimitService: IRateLimitService | null;
|
||||
bucketPrefix: string;
|
||||
config: ProxyRateLimitConfig;
|
||||
logger: Logger;
|
||||
}
|
||||
|
||||
export function createProxyRateLimitMiddleware(options: CreateProxyRateLimitMiddlewareOptions) {
|
||||
const {bucketPrefix, config, logger, rateLimitService} = options;
|
||||
|
||||
return createRateLimitMiddleware({
|
||||
rateLimitService: () => rateLimitService,
|
||||
config: {
|
||||
get enabled() {
|
||||
const hasRateLimitService = Boolean(rateLimitService);
|
||||
return config.enabled !== undefined ? hasRateLimitService && config.enabled : hasRateLimitService;
|
||||
},
|
||||
limit: config.limit,
|
||||
windowMs: config.windowMs,
|
||||
skipPaths: config.skipPaths,
|
||||
},
|
||||
getClientIdentifier: (req) => {
|
||||
const realIp = extractClientIp(req);
|
||||
return realIp || 'unknown';
|
||||
},
|
||||
getBucketName: (identifier, _ctx) => `${bucketPrefix}:ip:${identifier}`,
|
||||
onRateLimitExceeded: (c, retryAfter) => {
|
||||
const identifier = extractClientIp(c.req.raw) || 'unknown';
|
||||
logger.warn(
|
||||
{
|
||||
ip: identifier,
|
||||
path: c.req.path,
|
||||
retryAfter,
|
||||
},
|
||||
'proxy rate limit exceeded',
|
||||
);
|
||||
return c.text('Too Many Requests', HttpStatus.TOO_MANY_REQUESTS);
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {normalizeHost} from '@fluxer/app_proxy/src/app_proxy/utils/Host';
|
||||
import type {Context, Next} from 'hono';
|
||||
|
||||
interface SentryHostProxyMiddlewareOptions {
|
||||
sentryHost: string;
|
||||
// biome-ignore lint/suspicious/noConfusingVoidType: hono middleware may return void
|
||||
rateLimitMiddleware: (c: Context, next: Next) => Promise<Response | undefined | void>;
|
||||
proxyHandler: (c: Context) => Promise<Response>;
|
||||
}
|
||||
|
||||
export function createSentryHostProxyMiddleware(options: SentryHostProxyMiddlewareOptions) {
|
||||
const {proxyHandler, rateLimitMiddleware, sentryHost} = options;
|
||||
|
||||
return async function sentryHostProxy(c: Context, next: Next): Promise<Response | undefined> {
|
||||
const incomingHost = normalizeHost(c.req.raw.headers.get('host') ?? '');
|
||||
if (incomingHost && incomingHost === sentryHost) {
|
||||
const result = await rateLimitMiddleware(c, async () => {
|
||||
c.res = await proxyHandler(c);
|
||||
});
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
return c.res;
|
||||
}
|
||||
|
||||
await next();
|
||||
return undefined;
|
||||
};
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import type {AppProxySentryProxyConfig} from '@fluxer/app_proxy/src/AppProxyTypes';
|
||||
import {createProxyRequestHeaders, forwardProxyRequest} from '@fluxer/app_proxy/src/app_proxy/proxy/ProxyRequest';
|
||||
import {HttpStatus} from '@fluxer/constants/src/HttpConstants';
|
||||
import type {Logger} from '@fluxer/logger/src/Logger';
|
||||
import type {Context} from 'hono';
|
||||
|
||||
export interface ProxySentryOptions {
|
||||
enabled: boolean;
|
||||
sentryProxy: AppProxySentryProxyConfig | null;
|
||||
sentryProxyPath: string;
|
||||
logger: Logger;
|
||||
}
|
||||
|
||||
export async function proxySentry(c: Context, options: ProxySentryOptions): Promise<Response> {
|
||||
const {enabled, logger, sentryProxy, sentryProxyPath} = options;
|
||||
if (!enabled || !sentryProxy) {
|
||||
return c.text('Sentry proxy not configured', HttpStatus.SERVICE_UNAVAILABLE);
|
||||
}
|
||||
|
||||
const targetPath = resolveSentryTargetPath(c.req.path, sentryProxyPath, sentryProxy.path_prefix);
|
||||
const targetUrl = new URL(targetPath, sentryProxy.target_url);
|
||||
const upstreamSentryEndpoint = new URL(sentryProxy.target_url);
|
||||
const headers = createProxyRequestHeaders({
|
||||
incomingHeaders: c.req.raw.headers,
|
||||
upstreamHost: upstreamSentryEndpoint.host,
|
||||
});
|
||||
|
||||
try {
|
||||
return await forwardProxyRequest({
|
||||
targetUrl,
|
||||
method: c.req.method,
|
||||
headers,
|
||||
body: c.req.raw.body,
|
||||
bufferResponseBody: true,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
{
|
||||
path: c.req.path,
|
||||
targetUrl: targetUrl.toString(),
|
||||
error,
|
||||
},
|
||||
'sentry proxy error',
|
||||
);
|
||||
return c.text('Bad Gateway', HttpStatus.BAD_GATEWAY);
|
||||
}
|
||||
}
|
||||
|
||||
function resolveSentryTargetPath(requestPath: string, sentryProxyPath: string, sentryPathPrefix: string): string {
|
||||
let normalizedPath = requestPath;
|
||||
if (normalizedPath.startsWith(sentryProxyPath)) {
|
||||
normalizedPath = normalizedPath.slice(sentryProxyPath.length) || '/';
|
||||
}
|
||||
if (!normalizedPath.startsWith('/')) {
|
||||
normalizedPath = `/${normalizedPath}`;
|
||||
}
|
||||
return `${sentryPathPrefix}${normalizedPath}`;
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export function normalizeHost(value: string): string {
|
||||
const trimmed = value.trim().toLowerCase();
|
||||
if (!trimmed) {
|
||||
return '';
|
||||
}
|
||||
const primary = trimmed.split(',')[0]?.trim() ?? '';
|
||||
if (primary.startsWith('[')) {
|
||||
const end = primary.indexOf(']');
|
||||
return end > 0 ? primary.slice(1, end) : primary;
|
||||
}
|
||||
return primary.split(':')[0] ?? primary;
|
||||
}
|
||||
|
||||
export function resolveSentryHost(config: {
|
||||
sentry_proxy: {target_url: string} | null;
|
||||
sentry_report_host: string;
|
||||
}): string | null {
|
||||
if (!config.sentry_proxy || !config.sentry_report_host) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const normalizedSentryHost = normalizeHostValue(config.sentry_report_host);
|
||||
return normalizedSentryHost || null;
|
||||
}
|
||||
|
||||
function normalizeHostValue(rawHost: string): string {
|
||||
try {
|
||||
return normalizeHost(new URL(rawHost).host);
|
||||
} catch {
|
||||
return normalizeHost(rawHost);
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
|
||||
import {randomBytes} from 'node:crypto';
|
||||
import type {SentryDSN} from '@fluxer/app_proxy/src/app_server/utils/SentryDSN';
|
||||
import {parseSentryDSN} from '@fluxer/app_proxy/src/app_server/utils/SentryDSN';
|
||||
|
||||
export const CSP_HOSTS = {
|
||||
FRAME: [
|
||||
@@ -71,8 +71,6 @@ export const CSP_HOSTS = {
|
||||
'https://*.fluxer.workers.dev',
|
||||
'https://fluxerusercontent.com',
|
||||
'https://fluxerstatic.com',
|
||||
'https://sentry.web.fluxer.app',
|
||||
'https://sentry.web.canary.fluxer.app',
|
||||
'https://fluxer.media',
|
||||
'http://127.0.0.1:21863',
|
||||
'http://127.0.0.1:21864',
|
||||
@@ -95,33 +93,26 @@ export interface CSPOptions {
|
||||
reportUri?: string;
|
||||
}
|
||||
|
||||
export interface SentryProxyConfig {
|
||||
sentryProxy: SentryDSN | null;
|
||||
sentryProxyPath: string;
|
||||
sentryReportHost: string;
|
||||
export interface SentryCSPConfig {
|
||||
sentryDsn: string;
|
||||
}
|
||||
|
||||
export function generateNonce(): string {
|
||||
return randomBytes(16).toString('hex');
|
||||
}
|
||||
|
||||
export function buildSentryReportURI(config: SentryProxyConfig): string {
|
||||
const sentry = config.sentryProxy;
|
||||
export function buildSentryReportURI(config: SentryCSPConfig): string {
|
||||
const sentry = parseSentryDSN(config.sentryDsn);
|
||||
if (!sentry) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const pathPrefix = config.sentryProxyPath.replace(/\/+$/, '');
|
||||
let uri = `${pathPrefix}/api/${sentry.projectId}/security/?sentry_version=7`;
|
||||
let uri = `${sentry.targetUrl}${sentry.pathPrefix}/api/${sentry.projectId}/security/?sentry_version=7`;
|
||||
|
||||
if (sentry.publicKey) {
|
||||
uri += `&sentry_key=${sentry.publicKey}`;
|
||||
}
|
||||
|
||||
if (config.sentryReportHost) {
|
||||
return config.sentryReportHost + uri;
|
||||
}
|
||||
|
||||
return uri;
|
||||
}
|
||||
|
||||
@@ -160,8 +151,13 @@ export function buildCSP(nonce: string, options?: CSPOptions): string {
|
||||
return directives.join('; ');
|
||||
}
|
||||
|
||||
export function buildFluxerCSPOptions(config: SentryProxyConfig): CSPOptions {
|
||||
export function buildFluxerCSPOptions(config: SentryCSPConfig): CSPOptions {
|
||||
const reportURI = buildSentryReportURI(config);
|
||||
const sentry = parseSentryDSN(config.sentryDsn);
|
||||
const connectSrc: Array<string> = [...CSP_HOSTS.CONNECT];
|
||||
if (sentry) {
|
||||
connectSrc.push(sentry.targetUrl);
|
||||
}
|
||||
|
||||
return {
|
||||
scriptSrc: [...CSP_HOSTS.SCRIPT],
|
||||
@@ -169,7 +165,7 @@ export function buildFluxerCSPOptions(config: SentryProxyConfig): CSPOptions {
|
||||
imgSrc: [...CSP_HOSTS.IMAGE],
|
||||
mediaSrc: [...CSP_HOSTS.MEDIA],
|
||||
fontSrc: [...CSP_HOSTS.FONT],
|
||||
connectSrc: [...CSP_HOSTS.CONNECT],
|
||||
connectSrc: Array.from(new Set(connectSrc)),
|
||||
frameSrc: [...CSP_HOSTS.FRAME],
|
||||
workerSrc: [...CSP_HOSTS.WORKER],
|
||||
manifestSrc: [...CSP_HOSTS.MANIFEST],
|
||||
@@ -177,6 +173,6 @@ export function buildFluxerCSPOptions(config: SentryProxyConfig): CSPOptions {
|
||||
};
|
||||
}
|
||||
|
||||
export function buildFluxerCSP(nonce: string, config: SentryProxyConfig): string {
|
||||
export function buildFluxerCSP(nonce: string, config: SentryCSPConfig): string {
|
||||
return buildCSP(nonce, buildFluxerCSPOptions(config));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user