mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 21:15: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
@@ -1,8 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
export type RateLimitScope = 'global' | 'shared' | 'user';
|
||||
type RateLimitAlgorithm = 'leaky_bucket';
|
||||
|
||||
export interface RateLimitResult {
|
||||
allowed: boolean;
|
||||
limit: number;
|
||||
@@ -18,14 +16,12 @@ export interface RateLimitConfig {
|
||||
maxAttempts: number;
|
||||
windowMs: number;
|
||||
identifier: string;
|
||||
algorithm?: RateLimitAlgorithm;
|
||||
}
|
||||
|
||||
export interface BucketConfig {
|
||||
limit: number;
|
||||
windowMs: number;
|
||||
exemptFromGlobal?: boolean;
|
||||
algorithm?: RateLimitAlgorithm;
|
||||
}
|
||||
|
||||
export interface IRateLimitService {
|
||||
|
||||
@@ -7,8 +7,19 @@ import type {
|
||||
RateLimitConfig,
|
||||
RateLimitResult,
|
||||
} from '@pkgs/rate_limit/src/IRateLimitService';
|
||||
import {RateLimitKeyFactory} from '@pkgs/rate_limit/src/internal/RateLimitKeyFactory';
|
||||
import {assertPositiveFiniteNumber} from '@pkgs/rate_limit/src/internal/RateLimitValidation';
|
||||
|
||||
function getIdentifierKey(identifier: string): string {
|
||||
if (identifier.length === 0) throw new Error('identifier must be a non-empty string');
|
||||
return `ratelimit:${identifier}`;
|
||||
}
|
||||
function getBucketKey(bucket: string): string {
|
||||
if (bucket.length === 0) throw new Error('bucket must be a non-empty string');
|
||||
return `ratelimit:bucket:${bucket}`;
|
||||
}
|
||||
function getGlobalKey(identifier: string): string {
|
||||
if (identifier.length === 0) throw new Error('identifier must be a non-empty string');
|
||||
return `ratelimit:global:${identifier}`;
|
||||
}
|
||||
|
||||
interface IRateLimitStore {
|
||||
checkLeakyBucketLimit(key: string, limit: number, windowMs: number, cost: number): Promise<KVRateLimitResult>;
|
||||
@@ -50,7 +61,6 @@ function peekRetryAfterMs(result: KVRateLimitResult, windowMs: number): number {
|
||||
|
||||
export class RateLimitService implements IRateLimitService {
|
||||
private static readonly DEFAULT_GLOBAL_WINDOW_MS = 1000;
|
||||
private readonly keyFactory = new RateLimitKeyFactory();
|
||||
private readonly globalWindowMs: number;
|
||||
|
||||
constructor(
|
||||
@@ -58,17 +68,18 @@ export class RateLimitService implements IRateLimitService {
|
||||
options: RateLimitServiceOptions = {},
|
||||
) {
|
||||
this.globalWindowMs = options.globalWindowMs ?? RateLimitService.DEFAULT_GLOBAL_WINDOW_MS;
|
||||
assertPositiveFiniteNumber(this.globalWindowMs, 'globalWindowMs');
|
||||
if (!Number.isFinite(this.globalWindowMs) || this.globalWindowMs <= 0)
|
||||
throw new Error('globalWindowMs must be a positive finite number');
|
||||
}
|
||||
|
||||
async checkLimit(config: RateLimitConfig): Promise<RateLimitResult> {
|
||||
const key = this.keyFactory.getIdentifierKey(config.identifier);
|
||||
const key = getIdentifierKey(config.identifier);
|
||||
const result = await this.store.checkLeakyBucketLimit(key, config.maxAttempts, config.windowMs, 1);
|
||||
return createRateLimitResult(result);
|
||||
}
|
||||
|
||||
async peekLimit(config: RateLimitConfig): Promise<RateLimitResult> {
|
||||
const key = this.keyFactory.getIdentifierKey(config.identifier);
|
||||
const key = getIdentifierKey(config.identifier);
|
||||
const result = await this.store.checkLeakyBucketLimit(key, config.maxAttempts, config.windowMs, 0);
|
||||
if (result.remaining >= 1) {
|
||||
return createRateLimitResult(result);
|
||||
@@ -82,19 +93,19 @@ export class RateLimitService implements IRateLimitService {
|
||||
}
|
||||
|
||||
async checkBucketLimit(bucket: string, config: BucketConfig): Promise<RateLimitResult> {
|
||||
const key = this.keyFactory.getBucketKey(bucket);
|
||||
const key = getBucketKey(bucket);
|
||||
const result = await this.store.checkLeakyBucketLimit(key, config.limit, config.windowMs, 1);
|
||||
return createRateLimitResult(result);
|
||||
}
|
||||
|
||||
async checkGlobalLimit(identifier: string, limit: number): Promise<RateLimitResult> {
|
||||
const key = this.keyFactory.getGlobalKey(identifier);
|
||||
const key = getGlobalKey(identifier);
|
||||
const result = await this.store.checkLeakyBucketLimit(key, limit, this.globalWindowMs, 1);
|
||||
return createRateLimitResult(result, true);
|
||||
}
|
||||
|
||||
async resetLimit(identifier: string): Promise<void> {
|
||||
const key = this.keyFactory.getIdentifierKey(identifier);
|
||||
const key = getIdentifierKey(identifier);
|
||||
await this.store.del(key);
|
||||
}
|
||||
|
||||
@@ -102,7 +113,7 @@ export class RateLimitService implements IRateLimitService {
|
||||
if (identifierPrefix.length === 0) {
|
||||
throw new Error('identifierPrefix must be non-empty');
|
||||
}
|
||||
const keyPrefix = this.keyFactory.getIdentifierKey(identifierPrefix);
|
||||
const keyPrefix = getIdentifierKey(identifierPrefix);
|
||||
const pattern = `${keyPrefix.replace(/[\\*?[\]]/g, '\\$&')}*`;
|
||||
let totalDeleted = 0;
|
||||
const batchSize = 256;
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {assertNonEmptyString} from '@pkgs/rate_limit/src/internal/RateLimitValidation';
|
||||
|
||||
const RATE_LIMIT_PREFIX = 'ratelimit';
|
||||
const BUCKET_NAMESPACE = 'bucket';
|
||||
const GLOBAL_NAMESPACE = 'global';
|
||||
|
||||
interface IRateLimitKeyFactory {
|
||||
getIdentifierKey(identifier: string): string;
|
||||
getBucketKey(bucket: string): string;
|
||||
getGlobalKey(identifier: string): string;
|
||||
}
|
||||
|
||||
export class RateLimitKeyFactory implements IRateLimitKeyFactory {
|
||||
getIdentifierKey(identifier: string): string {
|
||||
assertNonEmptyString(identifier, 'identifier');
|
||||
return `${RATE_LIMIT_PREFIX}:${identifier}`;
|
||||
}
|
||||
|
||||
getBucketKey(bucket: string): string {
|
||||
assertNonEmptyString(bucket, 'bucket');
|
||||
return `${RATE_LIMIT_PREFIX}:${BUCKET_NAMESPACE}:${bucket}`;
|
||||
}
|
||||
|
||||
getGlobalKey(identifier: string): string {
|
||||
assertNonEmptyString(identifier, 'identifier');
|
||||
return `${RATE_LIMIT_PREFIX}:${GLOBAL_NAMESPACE}:${identifier}`;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
export function assertPositiveFiniteNumber(value: number, fieldName: string): void {
|
||||
if (!Number.isFinite(value) || value <= 0) {
|
||||
throw new Error(`${fieldName} must be a positive finite number`);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertNonEmptyString(value: string, fieldName: string): void {
|
||||
if (value.length === 0) {
|
||||
throw new Error(`${fieldName} must be a non-empty string`);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user