Align capacity units and scope administrator network edits

This commit is contained in:
2026-10-15 14:20:39 +09:00
parent 9cda9cbe84
commit 60a14e9c6c
14 changed files with 458 additions and 259 deletions

View File

@@ -68,11 +68,31 @@ const fields = {
uploadsPaused: z.boolean(),
maxConcurrentUploads: z.number().int().min(1).max(16),
totalStorageLimitMb: z.number().int().min(1024).max(1048576),
accountBytes: z.number().int().min(1).max(1e12),
deviceBytes: z.number().int().min(1).max(1e12),
devicesTotalBytes: z.number().int().min(1).max(1e12),
perFileBytes: z.number().int().min(1).max(1e12),
minDiskFreeBytes: z.number().int().min(0).max(1e12),
accountBytes: z
.number()
.int()
.min(1)
.max(1024 ** 4),
deviceBytes: z
.number()
.int()
.min(1)
.max(1024 ** 4),
devicesTotalBytes: z
.number()
.int()
.min(1)
.max(1024 ** 4),
perFileBytes: z
.number()
.int()
.min(1)
.max(1024 ** 4),
minDiskFreeBytes: z
.number()
.int()
.min(0)
.max(1024 ** 4),
blockedCountries: z
.array(
z
@@ -101,7 +121,11 @@ const fields = {
autoLoginFailures: z.number().int().min(3).max(1000),
autoRateViolations: z.number().int().min(3).max(1000),
autoUploadRequests: z.number().int().min(3).max(10000),
autoUploadBytes: z.number().int().min(1000000).max(1e12),
autoUploadBytes: z
.number()
.int()
.min(1000000)
.max(1024 ** 4),
autoBlockMinutes: z.number().int().min(1).max(10080),
};
const networkDefaults = {
@@ -113,7 +137,7 @@ const networkDefaults = {
autoLoginFailures: 10,
autoRateViolations: 20,
autoUploadRequests: 30,
autoUploadBytes: 10e9,
autoUploadBytes: 10 * 1024 ** 3,
autoBlockMinutes: 30,
};
const stateSchema = z.object({ revision: z.number().int().min(0), ...fields }).strict();
@@ -154,19 +178,20 @@ export class Administration {
.where(eq(s.adminSettings.name, 'controls'))
.get();
const capacityDefaults = {
maxConcurrentUploads: service.cfg.maxConcurrentUploads,
totalStorageLimitMb: service.cfg.totalStorageQuota / 1048576,
accountBytes: service.cfg.userQuota,
deviceBytes: service.cfg.guestQuota,
devicesTotalBytes: service.cfg.totalGuestQuota,
perFileBytes: service.cfg.maxFileBytes,
minDiskFreeBytes: service.cfg.minFreeBytes,
};
this.capacityDefaults = Object.freeze(capacityDefaults);
this.state = row
? stateSchema.parse({ ...networkDefaults, ...capacityDefaults, ...JSON.parse(row.value) })
: {
revision: 0,
uploadsPaused: false,
maxConcurrentUploads: service.cfg.maxConcurrentUploads,
totalStorageLimitMb: service.cfg.totalStorageQuota / 1048576,
blockedCountries: [],
blockedIps: [],
...networkDefaults,
@@ -371,6 +396,7 @@ export class Administration {
const disk = statfsSync(this.service.cfg.uploadDir, { bigint: true });
return {
settings: { ...this.state },
capacityDefaults: { ...this.capacityDefaults },
limits: {
perFileBytes: this.state.perFileBytes,
accountBytes: this.state.accountBytes,

View File

@@ -102,6 +102,10 @@ test('administrator limits persist, report to clients, reject invalid updates an
a = await f.create(),
b = await f.create();
await f.space(a, b);
const defaults = (await f.call(admin, '/admin/summary')).json().capacityDefaults;
assert.equal(defaults.perFileBytes, 4);
assert.equal(defaults.accountBytes, f.cfg.userQuota);
assert.equal(defaults.totalStorageLimitMb, f.cfg.totalStorageQuota / 1048576);
assert.equal((await request(f, a, b)).json().code, 'TRANSFER_LIMIT');
assert.equal((await own(f, user)).json().code, 'TRANSFER_LIMIT');
const updated = await patch(f, admin, {
@@ -112,6 +116,8 @@ test('administrator limits persist, report to clients, reject invalid updates an
minDiskFreeBytes: 0,
});
const restored = new Administration(f.service);
assert.deepEqual(restored.capacityDefaults, defaults);
assert.deepEqual((await f.call(admin, '/admin/summary')).json().capacityDefaults, defaults);
for (const key of [
'perFileBytes',
'accountBytes',
@@ -151,7 +157,7 @@ test('administrator limits persist, report to clients, reject invalid updates an
{ perFileBytes: 0 },
{ accountBytes: -1 },
{ deviceBytes: 1.2 },
{ devicesTotalBytes: 1e12 + 1 },
{ devicesTotalBytes: 1024 ** 4 + 1 },
{ minDiskFreeBytes: -1 },
]) {
assert.equal(
@@ -169,6 +175,13 @@ test('administrator limits persist, report to clients, reject invalid updates an
),
),
);
await patch(f, admin, { uploadsPaused: true, autoLoginFailures: 7 });
await patch(f, admin, defaults);
for (const [key, value] of Object.entries(defaults))
assert.equal(f.service.admin.state[key], value, key);
assert.equal(f.service.admin.state.uploadsPaused, true);
assert.equal(f.service.admin.state.autoLoginFailures, 7);
assert.equal(f.service.admin.summary().storedFiles, list.length);
} finally {
await f.close();
}

View File

@@ -1,20 +1,27 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { formatSize, formatAdminDate } from '../../web/src/displayFormat.js';
import {
formatSize,
formatAdminDate,
SIZE_KB,
SIZE_MB,
SIZE_GB,
} from '../../web/src/displayFormat.js';
test('shared display sizes use decimal KB below 1MB and switch only at MB/GB boundaries', () => {
test('shared display uses familiar 1024-based KB/MB/GB with comma grouping and readable whole-GB quotas', () => {
for (const [bytes, expected] of [
[0, '0KB'],
[10, '1KB 미만'],
[1000, '1KB'],
[938000, '938KB'],
[999000, '999KB'],
[1000000, '1.0MB'],
[1100000, '1.1MB'],
[1000000000, '1.0GB'],
[1700000000, '1.7GB'],
[7687e9, '7,687.0GB'],
[1234567e9, '1,234,567.0GB'],
[SIZE_KB, '1KB'],
[938 * SIZE_KB, '938KB'],
[1023 * SIZE_KB, '1,023KB'],
[SIZE_MB, '1.0MB'],
[1.1 * SIZE_MB, '1.1MB'],
[SIZE_GB, '1.0GB'],
[1.7 * SIZE_GB, '1.7GB'],
[20 * SIZE_GB, '20.0GB'],
[7687 * SIZE_GB, '7,687.0GB'],
[1234567 * SIZE_GB, '1,234,567.0GB'],
])
assert.equal(formatSize(bytes), expected);
});