mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 13:05:32 +09:00
chore(repo): prune dead code and duplicate abstractions (#3413)
This commit is contained in:
1 parent
c17091768c
commit
c9fdf8fead
2937 files changed
+42233
-176793
No files matched your search
@@ -13,7 +13,6 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "catalog:",
|
||||
"@vitest/coverage-v8": "catalog:",
|
||||
"typescript": "catalog:ts7",
|
||||
"vitest": "catalog:"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import type {IpAddressFamily, ParsedIpAddress} from '@fluxer/ip_utils/src/IpAddress';
|
||||
import type {ParsedIpAddress} from '@fluxer/ip_utils/src/IpAddress';
|
||||
import {parseIpAddress} from '@fluxer/ip_utils/src/IpAddress';
|
||||
|
||||
interface ClientIpExtractionOptions {
|
||||
@@ -8,14 +8,6 @@ interface ClientIpExtractionOptions {
|
||||
clientIpHeaderName?: string;
|
||||
}
|
||||
|
||||
type ClientIpSource = 'client-ip-header';
|
||||
|
||||
interface ExtractedClientIp {
|
||||
ip: string;
|
||||
source: ClientIpSource;
|
||||
ipVersion: IpAddressFamily;
|
||||
}
|
||||
|
||||
export class MissingClientIpError extends Error {
|
||||
readonly code = 'FORBIDDEN';
|
||||
readonly status = 403;
|
||||
@@ -26,10 +18,6 @@ export class MissingClientIpError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
interface HeaderReader {
|
||||
get(name: string): string | null;
|
||||
}
|
||||
|
||||
const DEFAULT_CLIENT_IP_HEADER_NAME = 'x-forwarded-for';
|
||||
|
||||
function normalizeHeaderName(headerName: string): string {
|
||||
@@ -47,32 +35,13 @@ function parseClientIpHeaderValue(value: string | null): ParsedIpAddress | null
|
||||
return parseIpAddress(value.split(',', 1)[0]);
|
||||
}
|
||||
|
||||
function extractClientIpDetailsFromReader(
|
||||
headerReader: HeaderReader,
|
||||
options?: ClientIpExtractionOptions,
|
||||
): ExtractedClientIp | null {
|
||||
export function extractClientIp(req: Request, options?: ClientIpExtractionOptions): string | null {
|
||||
if (!options?.trustClientIpHeader) {
|
||||
return null;
|
||||
}
|
||||
const headerName = resolveClientIpHeaderName(options.clientIpHeaderName);
|
||||
const clientIpHeader = parseClientIpHeaderValue(headerReader.get(headerName));
|
||||
if (clientIpHeader) {
|
||||
return {
|
||||
ip: clientIpHeader.normalized,
|
||||
source: 'client-ip-header',
|
||||
ipVersion: clientIpHeader.family,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function extractClientIpDetails(req: Request, options?: ClientIpExtractionOptions): ExtractedClientIp | null {
|
||||
return extractClientIpDetailsFromReader(req.headers, options);
|
||||
}
|
||||
|
||||
export function extractClientIp(req: Request, options?: ClientIpExtractionOptions): string | null {
|
||||
const extracted = extractClientIpDetails(req, options);
|
||||
return extracted?.ip ?? null;
|
||||
return (
|
||||
parseClientIpHeaderValue(req.headers.get(resolveClientIpHeaderName(options.clientIpHeaderName)))?.normalized ?? null
|
||||
);
|
||||
}
|
||||
|
||||
export function requireClientIp(req: Request, options?: ClientIpExtractionOptions): string {
|
||||
|
||||
@@ -331,3 +331,61 @@ export function getSubnet(ip: string): string | null {
|
||||
ipv6PrefixLength: 48,
|
||||
});
|
||||
}
|
||||
|
||||
export function ipToBytes(address: string): {family: IpAddressFamily; bytes: Uint8Array} | null {
|
||||
const normalized = stripIpv6Brackets(address.trim().toLowerCase());
|
||||
if (isIPv4(normalized)) return {family: 'ipv4', bytes: Uint8Array.from(normalized.split('.').map(Number))};
|
||||
if (!isIPv6(normalized)) {
|
||||
return null;
|
||||
}
|
||||
let head = normalized;
|
||||
let embeddedIpv4Octets: Array<number> | null = null;
|
||||
const lastColonIndex = head.lastIndexOf(':');
|
||||
const trailing = head.slice(lastColonIndex + 1);
|
||||
if (trailing.includes('.')) {
|
||||
if (!isIPv4(trailing)) {
|
||||
return null;
|
||||
}
|
||||
embeddedIpv4Octets = trailing.split('.').map((part) => Number.parseInt(part, 10));
|
||||
head = `${head.slice(0, lastColonIndex + 1)}0:0`;
|
||||
}
|
||||
let groups: Array<string>;
|
||||
if (head.indexOf('::') === -1) {
|
||||
groups = head.split(':');
|
||||
if (groups.length !== 8) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
const [beforePart, afterPart] = head.split('::');
|
||||
const before = beforePart.length > 0 ? beforePart.split(':') : [];
|
||||
const after = afterPart.length > 0 ? afterPart.split(':') : [];
|
||||
const missing = 8 - before.length - after.length;
|
||||
if (missing < 1) {
|
||||
return null;
|
||||
}
|
||||
groups = [...before, ...new Array(missing).fill('0'), ...after];
|
||||
}
|
||||
const bytes = new Uint8Array(16);
|
||||
for (let index = 0; index < 8; index += 1) {
|
||||
const value = parseHexGroup(groups[index]);
|
||||
if (value === null) {
|
||||
return null;
|
||||
}
|
||||
bytes[index * 2] = (value >> 8) & 0xff;
|
||||
bytes[index * 2 + 1] = value & 0xff;
|
||||
}
|
||||
if (embeddedIpv4Octets) {
|
||||
bytes[12] = embeddedIpv4Octets[0];
|
||||
bytes[13] = embeddedIpv4Octets[1];
|
||||
bytes[14] = embeddedIpv4Octets[2];
|
||||
bytes[15] = embeddedIpv4Octets[3];
|
||||
}
|
||||
return {family: 'ipv6', bytes};
|
||||
}
|
||||
|
||||
function parseHexGroup(value: string | undefined): number | null {
|
||||
if (!value || !/^[0-9a-f]{1,4}$/.test(value)) {
|
||||
return null;
|
||||
}
|
||||
return Number.parseInt(value, 16);
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import {
|
||||
extractClientIp,
|
||||
extractClientIpDetails,
|
||||
MissingClientIpError,
|
||||
requireClientIp,
|
||||
resolveClientIpHeaderName,
|
||||
@@ -109,25 +108,6 @@ describe('resolveClientIpHeaderName', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('extractClientIpDetails', () => {
|
||||
it('returns extracted ip and source', () => {
|
||||
const request = new Request('http://example.com', {
|
||||
headers: {'X-Forwarded-For': '203.0.113.50'},
|
||||
});
|
||||
expect(extractClientIpDetails(request, {trustClientIpHeader: true})).toEqual({
|
||||
ip: '203.0.113.50',
|
||||
source: 'client-ip-header',
|
||||
ipVersion: 'ipv4',
|
||||
});
|
||||
});
|
||||
it('returns null when trust is disabled', () => {
|
||||
const request = new Request('http://example.com', {
|
||||
headers: {'X-Forwarded-For': '203.0.113.50'},
|
||||
});
|
||||
expect(extractClientIpDetails(request, {trustClientIpHeader: false})).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('requireClientIp', () => {
|
||||
it('returns ip when present and trusted', () => {
|
||||
const request = new Request('http://example.com', {
|
||||
|
||||
Reference in new issue
Block a user