refactor progress

This commit is contained in:
Hampus Kraft
2026-02-17 12:22:36 +00:00
parent cb31608523
commit d5abd1a7e4
8257 changed files with 1190207 additions and 761040 deletions

View File

@@ -0,0 +1,25 @@
/*
* 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 interface S3RateLimitConfig {
enabled: boolean;
maxAttempts: number;
windowMs: number;
skipPaths?: Array<string>;
}

View File

@@ -0,0 +1,71 @@
/*
* 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 {createErrorHandler} from '@fluxer/errors/src/ErrorHandler';
import type {LoggerInterface} from '@fluxer/logger/src/LoggerInterface';
import {S3Error} from '@fluxer/s3/src/errors/S3Error';
import {captureException} from '@fluxer/sentry/src/Sentry';
import type {Hono} from 'hono';
import type {HonoEnv} from '../types/HonoEnv';
interface SetupS3ErrorHandlingOptions {
app: Hono<HonoEnv>;
logger: LoggerInterface;
}
export function setupS3ErrorHandling(options: SetupS3ErrorHandlingOptions): void {
const {app, logger} = options;
const errorHandler = createErrorHandler({
includeStack: false,
responseFormat: 'xml',
logError: (err, ctx) => {
const isExpectedError = err instanceof Error && 'isExpected' in err && err.isExpected;
if (!(err instanceof S3Error || isExpectedError)) {
captureException(err);
}
logger.error(
{
error: err.message,
stack: err.stack,
requestId: ctx.get('requestId'),
},
'Request error',
);
},
customHandler: (err, ctx) => {
if (err instanceof S3Error) {
err.requestId = ctx.get('requestId');
return err.getResponse();
}
return undefined;
},
});
app.onError(errorHandler);
app.notFound((ctx) => {
const s3Error = new S3Error('NoSuchKey', 'The specified resource was not found.', {
requestId: ctx.get('requestId'),
});
return s3Error.getResponse();
});
}

View File

@@ -0,0 +1,89 @@
/*
* 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 {Headers} from '@fluxer/constants/src/Headers';
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 {LoggerInterface} from '@fluxer/logger/src/LoggerInterface';
import type {RateLimitService} from '@fluxer/rate_limit/src/RateLimitService';
import type {S3RateLimitConfig} from '@fluxer/s3/src/app/S3AppConfigTypes';
import type {S3AuthConfig} from '@fluxer/s3/src/middleware/S3AuthMiddleware';
import {createS3AuthMiddleware} from '@fluxer/s3/src/middleware/S3AuthMiddleware';
import type {IS3Service} from '@fluxer/s3/src/s3/S3Service';
import type {HonoEnv} from '@fluxer/s3/src/types/HonoEnv';
import type {Hono} from 'hono';
import {createMiddleware} from 'hono/factory';
interface SetupS3MiddlewareOptions {
app: Hono<HonoEnv>;
logger: LoggerInterface;
s3Service: IS3Service;
authConfig: S3AuthConfig;
metricsCollector?: MetricsCollector;
tracing?: TracingOptions;
rateLimitService?: RateLimitService | null;
rateLimitConfig?: S3RateLimitConfig | null;
}
export function setupS3Middleware(options: SetupS3MiddlewareOptions): void {
const {app, logger, s3Service, authConfig, metricsCollector, tracing, rateLimitService, rateLimitConfig} = options;
const serviceMiddleware = createMiddleware<HonoEnv>(async (ctx, next) => {
ctx.set('s3Service', s3Service);
await next();
});
applyMiddlewareStack(app, {
requestId: {headerName: Headers.X_AMZ_REQUEST_ID},
tracing,
metrics: metricsCollector
? {
enabled: true,
collector: metricsCollector,
skipPaths: ['/_health'],
}
: undefined,
logger: {
log: (data) => {
logger.info(
{
method: data.method,
path: data.path,
status: data.status,
durationMs: data.durationMs,
},
'Request completed',
);
},
},
rateLimit:
rateLimitService && rateLimitConfig?.enabled
? {
enabled: true,
service: rateLimitService,
maxAttempts: rateLimitConfig.maxAttempts,
windowMs: rateLimitConfig.windowMs,
skipPaths: rateLimitConfig.skipPaths ?? ['/_health'],
}
: undefined,
customMiddleware: [serviceMiddleware, createS3AuthMiddleware(authConfig, logger)],
skipErrorHandler: true,
});
}

View File

@@ -0,0 +1,60 @@
/*
* 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 {ICacheService} from '@fluxer/cache/src/ICacheService';
import {KVCacheProvider} from '@fluxer/cache/src/providers/KVCacheProvider';
import type {IKVProvider} from '@fluxer/kv_client/src/IKVProvider';
import {KVClient} from '@fluxer/kv_client/src/KVClient';
import {throwKVRequiredError} from '@fluxer/rate_limit/src/KVRequiredError';
import {RateLimitService} from '@fluxer/rate_limit/src/RateLimitService';
import type {S3RateLimitConfig} from '@fluxer/s3/src/app/S3AppConfigTypes';
interface ResolveS3RateLimitServiceOptions {
kvUrl?: string;
rateLimitService?: RateLimitService | null;
rateLimitConfig?: S3RateLimitConfig | null;
}
export function resolveS3RateLimitService(options: ResolveS3RateLimitServiceOptions): RateLimitService | null {
const {kvUrl, rateLimitService, rateLimitConfig} = options;
if (rateLimitService) {
return rateLimitService;
}
if (!rateLimitConfig?.enabled) {
return null;
}
if (!kvUrl) {
throwKVRequiredError({
serviceName: 'S3 service',
configPath: 'kvUrl option',
fluxerServerHint: 'rateLimitService is passed in as an option',
});
}
const kvProvider = createKVProvider(kvUrl);
const cacheService: ICacheService = new KVCacheProvider({client: kvProvider});
return new RateLimitService(cacheService);
}
function createKVProvider(kvUrl: string): IKVProvider {
return new KVClient({url: kvUrl});
}

View File

@@ -0,0 +1,50 @@
/*
* 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 {Headers} from '@fluxer/constants/src/Headers';
import type {HonoEnv} from '@fluxer/s3/src/types/HonoEnv';
import type {Hono} from 'hono';
export function setupS3ResponseHeadersMiddleware(app: Hono<HonoEnv>): void {
app.use('*', async (ctx, next) => {
await next();
ctx.header(Headers.X_AMZ_ID_2, ctx.get('requestId'));
ctx.header('Server', 'FluxerS3');
const origin = ctx.req.header('origin');
if (origin) {
ctx.header('Access-Control-Allow-Origin', origin);
ctx.header('Access-Control-Allow-Methods', 'GET, PUT, POST, DELETE, HEAD, OPTIONS');
ctx.header(
'Access-Control-Allow-Headers',
'Authorization, Content-Type, X-Amz-Date, X-Amz-Content-Sha256, X-Amz-User-Agent, X-Amz-Security-Token, X-Amz-Meta-*',
);
ctx.header(
'Access-Control-Expose-Headers',
'ETag, x-amz-request-id, x-amz-id-2, x-amz-version-id, x-amz-delete-marker',
);
ctx.header('Access-Control-Max-Age', '3600');
}
});
app.options('*', (ctx) => {
return ctx.body(null, 200);
});
}

View File

@@ -0,0 +1,32 @@
/*
* 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 {BucketController} from '@fluxer/s3/src/s3/BucketController';
import {ObjectController} from '@fluxer/s3/src/s3/ObjectController';
import type {HonoEnv} from '@fluxer/s3/src/types/HonoEnv';
import type {Hono} from 'hono';
export function registerS3Routes(app: Hono<HonoEnv>): void {
app.get('/_health', (ctx) => {
return ctx.json({ok: true}, 200);
});
ObjectController(app);
BucketController(app);
}