fix: let the webmail build again with the device registry's rate limit out of its route

This commit is contained in:
Linus Rath
2026-11-03 17:19:39 +01:00
parent aad0b61a63
commit e3dec865da
3 changed files with 11 additions and 11 deletions
+4 -4
View File
@@ -18,7 +18,7 @@ vi.mock('@/lib/admin/session', async (original) => ({
getClientIP: () => client.ip,
}));
import { POST as checkIn, REGISTER_PER_HOUR } from '../route';
import { POST as checkIn } from '../route';
import { POST as wiped } from '../wiped/route';
import { GET as adminList, POST as adminAct } from '../../admin/devices/route';
import * as registry from '@/lib/devices/registry';
@@ -120,12 +120,12 @@ describe('POST /api/devices', () => {
it('holds new rows to a much lower limit per address than check-ins, and a 429 changes nothing', async () => {
creds.value = { accountName: 'alice@example.org', serverUrl: 'https://mail.example.org' };
const statuses: number[] = [];
for (let i = 0; i <= REGISTER_PER_HOUR; i += 1) {
for (let i = 0; i <= registry.REGISTER_PER_HOUR; i += 1) {
statuses.push((await checkIn(request('/api/devices', { platform: 'x', appVersion: '1' }))).status);
}
expect(statuses.slice(0, REGISTER_PER_HOUR).every((status) => status === 200)).toBe(true);
expect(statuses.slice(0, registry.REGISTER_PER_HOUR).every((status) => status === 200)).toBe(true);
expect(statuses.at(-1)).toBe(429);
expect((await registry.listDevices()).total).toBe(REGISTER_PER_HOUR);
expect((await registry.listDevices()).total).toBe(registry.REGISTER_PER_HOUR);
// A device that's already registered still hears about its wipe.
const [{ id }] = (await registry.listDevices()).devices;
+1 -7
View File
@@ -1,6 +1,6 @@
import { NextRequest, NextResponse } from 'next/server';
import { getClientIP } from '@/lib/admin/session';
import { checkInDevice, DEVICE_ID_RE, DEVICE_SECRET_RE, registerDevice, TooManyDevicesError } from '@/lib/devices/registry';
import { checkInDevice, DEVICE_ID_RE, DEVICE_SECRET_RE, REGISTER_PER_HOUR, registerDevice, TooManyDevicesError } from '@/lib/devices/registry';
import { logger } from '@/lib/logger';
import { rejectCrossOriginRequest } from '@/lib/security/same-origin';
import { getStore } from '@/lib/shared-store';
@@ -11,12 +11,6 @@ const NO_STORE = { 'Cache-Control': 'no-store' };
const MAX_BODY_BYTES = 2048;
// Per address: a whole office can sit behind one, checking in as it starts up.
const PER_MINUTE = 300;
/**
* A new row is written to the state dir and stays half a year, for anyone with a
* session. Enough for an office turning this on in one morning: a device turned
* away tries again in a quarter of an hour.
*/
export const REGISTER_PER_HOUR = 60;
function cleanText(value: unknown): string {
if (typeof value !== 'string') return '';
+6
View File
@@ -19,6 +19,12 @@ import { settingsId } from '@/lib/settings-sync';
import { getStore, type CollectionOptions } from '@/lib/shared-store';
import type { DeviceView, WipeReason } from './types';
/**
* A new row is written to the state dir and stays half a year, for anyone with a
* session. Enough for an office turning this on in one morning: a device turned
* away tries again in a quarter of an hour.
*/
export const REGISTER_PER_HOUR = 60;
export const DEVICE_ID_RE = /^[0-9a-f]{64}\/[0-9a-f]{32}$/;
export const DEVICE_SECRET_RE = /^[0-9a-f]{64}$/;
const USER_KEY_RE = /^[0-9a-f]{64}$/;