new version

This commit is contained in:
Anaïs Saraiva
2026-08-16 16:04:26 +02:00
parent a6ff57e5d5
commit c6a96b08c3
7 changed files with 33 additions and 6 deletions
+4
View File
@@ -25,6 +25,10 @@ MAX_STORAGE_USAGE=0.5
# In milliseconds. Default and hard maximum is 48 hours (172800000).
FILE_TTL_MS=172800000
# Length in characters of the upload/download key token (a-z0-9).
# Default 8. Longer keys are harder to brute-force.
KEY_LENGTH=8
# Brute-force protection: rate limit per client IP on download and
# deletion endpoints. Set RATE_LIMIT_ENABLED=0 to disable.
RATE_LIMIT_ENABLED=1
+1
View File
@@ -65,6 +65,7 @@ STORAGE_DIR=/opt/filehost/storage
MAX_FILE_SIZE=10GB # must be <= proxy upload limit
FILE_TTL_MS=172800000 # expire files after 48 hours (hard maximum)
MAX_STORAGE_USAGE=0.5 # block uploads when >50% of the disk is used
KEY_LENGTH=8 # length of the upload/download key token
RATE_LIMIT_ENABLED=1
TRUST_PROXY=1 # proxy sets X-Forwarded-For
```
+1
View File
@@ -51,6 +51,7 @@ cp .env.example .env
| `MAX_FILE_SIZE` | `10GB` | Max upload size. Units: `B`, `KB`, `MB`, `GB`, `TB` (or plain int). |
| `MAX_STORAGE_USAGE` | `0.5` | Max fraction (0.0–1.0) of the storage disk that may be used; uploads are rejected with `503 try again later` above this. |
| `FILE_TTL_MS` | `172800000` | Milliseconds before an upload expires and is deleted (48 hours, hard maximum). |
| `KEY_LENGTH` | `8` | Length in characters of the upload/download key token (a-z0-9). |
| `RATE_LIMIT_ENABLED` | `1` | Enable per-IP rate limiting on `/(key)` and `/api/files/`. |
| `RATE_LIMIT_MAX` | `300` | Max requests per IP per window. |
| `RATE_LIMIT_WINDOW_MS` | `60000` | Rate-limit window length in ms. |
+7
View File
@@ -58,6 +58,12 @@ in
description = "Maximum fraction (0.0-1.0) of the storage filesystem that may be used; uploads are rejected above this.";
};
keyLength = lib.mkOption {
type = lib.types.ints.positive;
default = 8;
description = "Length in characters of the upload/download key token.";
};
publicBaseUrl = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
@@ -134,6 +140,7 @@ in
"MAX_FILE_SIZE=${cfg.maxFileSize}"
"FILE_TTL_MS=${toString cfg.fileTtlMs}"
"MAX_STORAGE_USAGE=${toString cfg.maxStorageUsage}"
"KEY_LENGTH=${toString cfg.keyLength}"
]
++ lib.optionals (cfg.publicBaseUrl != null) [ "PUBLIC_BASE_URL=${cfg.publicBaseUrl}" ]
++ lib.optionals cfg.trustProxy [ "TRUST_PROXY=1" ];
+7
View File
@@ -7,6 +7,7 @@ export interface Config {
maxFileSize: number;
fileTtlMs: number;
maxStorageUsage: number;
keyLength: number;
publicBaseUrl?: string;
rateLimitEnabled: boolean;
rateLimitMax: number;
@@ -80,6 +81,11 @@ export function loadConfig(env: Record<string, string | undefined> = process.env
const trustProxy = boolFromEnv(env.TRUST_PROXY, false);
const keyLength = Number(env.KEY_LENGTH ?? "8");
if (!Number.isInteger(keyLength) || keyLength < 4 || keyLength > 64) {
throw new Error(`Invalid KEY_LENGTH: ${env.KEY_LENGTH}`);
}
let publicBaseUrl: string | undefined;
const rawBase = env.PUBLIC_BASE_URL?.trim();
if (rawBase) {
@@ -97,6 +103,7 @@ export function loadConfig(env: Record<string, string | undefined> = process.env
maxFileSize,
fileTtlMs,
maxStorageUsage,
keyLength,
publicBaseUrl,
rateLimitEnabled: boolFromEnv(env.RATE_LIMIT_ENABLED, true),
rateLimitMax,
+11 -5
View File
@@ -1,11 +1,17 @@
import { createHash, createHmac, randomBytes, scryptSync, timingSafeEqual } from "node:crypto";
export const KEY_LENGTH = 8;
export let KEY_LENGTH = 8;
// Keys are lowercase alphanumeric only (a-z0-9), 8 characters.
export const KEY_PATTERN = /^[a-z0-9]{8}$/;
// The id and the download key are the same 12-char token.
export const ID_PATTERN = KEY_PATTERN;
// Keys are lowercase alphanumeric only (a-z0-9).
export let KEY_PATTERN: RegExp = /^[a-z0-9]{8}$/;
// The id and the download key are the same token.
export let ID_PATTERN = KEY_PATTERN;
export function setKeyLength(length: number): void {
KEY_LENGTH = length;
KEY_PATTERN = new RegExp(`^[a-z0-9]{${length}}$`);
ID_PATTERN = KEY_PATTERN;
}
const KEY_CHARSET = "abcdefghijklmnopqrstuvwxyz0123456789";
const CHARSET_SIZE = KEY_CHARSET.length;
+2 -1
View File
@@ -7,10 +7,11 @@ import { handleAuth } from "./routes/auth";
import { handleDelete } from "./routes/delete";
import { handleDownload } from "./routes/download";
import { handleUpload } from "./routes/upload";
import { KEY_PATTERN } from "./security";
import { KEY_PATTERN, setKeyLength } from "./security";
import { Storage } from "./storage";
const cfg = loadConfig();
setKeyLength(cfg.keyLength);
const storage = new Storage(cfg.storageDir);
await storage.init();