mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 13:05:32 +09:00
refactor(api): tidy the api and shared packages (#2733)
This commit is contained in:
1 parent
580401d2dc
commit
5afbf67a70
333 files changed
+10180
-7910
No files matched your search
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import type {MiddlewareHandler} from 'hono';
|
||||
import type {Context, MiddlewareHandler} from 'hono';
|
||||
|
||||
export interface CorsOptions {
|
||||
enabled?: boolean;
|
||||
@@ -15,6 +15,14 @@ export interface CorsOptions {
|
||||
const DEFAULT_METHODS = ['GET', 'HEAD', 'PUT', 'PATCH', 'POST', 'DELETE', 'OPTIONS'];
|
||||
const DEFAULT_HEADERS = ['Content-Type', 'Authorization', 'X-Requested-With', 'Accept-Language', 'X-Request-ID'];
|
||||
|
||||
function appendOriginVary(c: Context): void {
|
||||
const vary = c.res.headers.get('Vary');
|
||||
const fields = vary?.split(',').map((field) => field.trim().toLowerCase()) ?? [];
|
||||
if (!fields.includes('*') && !fields.includes('origin')) {
|
||||
c.header('Vary', 'Origin', {append: Boolean(vary)});
|
||||
}
|
||||
}
|
||||
|
||||
export function cors(options: CorsOptions = {}): MiddlewareHandler {
|
||||
const {
|
||||
enabled = true,
|
||||
@@ -36,7 +44,6 @@ export function cors(options: CorsOptions = {}): MiddlewareHandler {
|
||||
c.header('Access-Control-Allow-Origin', '*');
|
||||
} else if (Array.isArray(origins) && requestOrigin && origins.includes(requestOrigin)) {
|
||||
c.header('Access-Control-Allow-Origin', requestOrigin);
|
||||
c.header('Vary', 'Origin');
|
||||
}
|
||||
if (credentials) {
|
||||
c.header('Access-Control-Allow-Credentials', 'true');
|
||||
@@ -52,10 +59,16 @@ export function cors(options: CorsOptions = {}): MiddlewareHandler {
|
||||
if (maxAge !== undefined) {
|
||||
c.header('Access-Control-Max-Age', maxAge.toString());
|
||||
}
|
||||
if (origins !== '*') {
|
||||
appendOriginVary(c);
|
||||
}
|
||||
return c.body(null, 204);
|
||||
}
|
||||
await next();
|
||||
applyCorsHeaders();
|
||||
if (origins !== '*') {
|
||||
appendOriginVary(c);
|
||||
}
|
||||
return;
|
||||
};
|
||||
}
|
||||
@@ -75,10 +75,8 @@ class Histogram {
|
||||
const lines: Array<string> = [];
|
||||
lines.push(`# HELP ${name} ${help}`);
|
||||
lines.push(`# TYPE ${name} histogram`);
|
||||
let cumulative = 0;
|
||||
for (let i = 0; i < this.buckets.length; i++) {
|
||||
cumulative += this.counts[i];
|
||||
lines.push(`${name}_bucket{le="${this.buckets[i]}"} ${cumulative}`);
|
||||
lines.push(`${name}_bucket{le="${this.buckets[i]}"} ${this.counts[i]}`);
|
||||
}
|
||||
lines.push(`${name}_bucket{le="+Inf"} ${this.count}`);
|
||||
lines.push(`${name}_sum ${this.sum}`);
|
||||
@@ -118,12 +116,12 @@ interface MetricsResult {
|
||||
|
||||
export function createMetricsMiddleware(serviceName: string): MetricsResult {
|
||||
const prefix = `fluxer_${serviceName}`;
|
||||
const startTime = Date.now();
|
||||
const startTime = performance.now();
|
||||
|
||||
const requestsTotal = new Counter();
|
||||
const errorsTotal = new Counter();
|
||||
const requestDuration = new Histogram(DEFAULT_BUCKETS);
|
||||
const uptime = new Gauge(() => (Date.now() - startTime) / 1000);
|
||||
const uptime = new Gauge(() => (performance.now() - startTime) / 1000);
|
||||
|
||||
const state: MetricsState = {requestsTotal, errorsTotal, requestDuration, uptime};
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ describe('CORS Middleware', () => {
|
||||
});
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.headers.get('Access-Control-Allow-Origin')).toBeNull();
|
||||
expect(response.headers.get('Vary')).toBe('Origin');
|
||||
});
|
||||
test('does not set origin header when no origin header in request', async () => {
|
||||
const app = new Hono();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {isIP} from 'node:net';
|
||||
import {isIPv4} from 'node:net';
|
||||
import {getIpNetworkKey} from '@fluxer/ip_utils/src/IpAddress';
|
||||
|
||||
interface ValidateOutboundEndpointOptions {
|
||||
name: string;
|
||||
@@ -65,14 +66,14 @@ function isLocalhostHostname(hostname: string): boolean {
|
||||
}
|
||||
|
||||
function isPrivateOrSpecialIpLiteral(hostname: string): boolean {
|
||||
const ipVersion = isIP(hostname);
|
||||
if (!ipVersion) {
|
||||
const address = getIpNetworkKey(hostname, {ipv4PrefixLength: 'exact', ipv6PrefixLength: 'exact'});
|
||||
if (address === null) {
|
||||
return false;
|
||||
}
|
||||
if (ipVersion === 4) {
|
||||
return isPrivateOrSpecialIPv4(hostname);
|
||||
if (isIPv4(address)) {
|
||||
return isPrivateOrSpecialIPv4(address);
|
||||
}
|
||||
return isPrivateOrSpecialIPv6(hostname);
|
||||
return isPrivateOrSpecialIPv6(address);
|
||||
}
|
||||
|
||||
function isPrivateOrSpecialIPv4(hostname: string): boolean {
|
||||
@@ -91,17 +92,10 @@ function isPrivateOrSpecialIPv4(hostname: string): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
function isPrivateOrSpecialIPv6(hostname: string): boolean {
|
||||
const lower = hostname.toLowerCase();
|
||||
if (lower === '::' || lower === '::1') {
|
||||
function isPrivateOrSpecialIPv6(address: string): boolean {
|
||||
if (address === '::' || address === '::1') {
|
||||
return true;
|
||||
}
|
||||
if (lower.startsWith('::ffff:')) {
|
||||
const mapped = lower.slice('::ffff:'.length);
|
||||
return isPrivateOrSpecialIPv4(mapped);
|
||||
}
|
||||
if (lower.startsWith('fe80:')) return true;
|
||||
if (lower.startsWith('fc') || lower.startsWith('fd')) return true;
|
||||
if (lower.startsWith('ff')) return true;
|
||||
return false;
|
||||
const firstGroup = Number.parseInt(address.split(':', 1)[0] || '0', 16);
|
||||
return (firstGroup & 0xffc0) === 0xfe80 || (firstGroup & 0xfe00) === 0xfc00 || (firstGroup & 0xff00) === 0xff00;
|
||||
}
|
||||
Reference in new issue
Block a user