From e3dec865da3318d8d3923f67afd33301538ebeb7 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Tue, 3 Nov 2026 17:19:39 +0100 Subject: [PATCH] fix: let the webmail build again with the device registry's rate limit out of its route --- app/api/devices/__tests__/route.test.ts | 8 ++++---- app/api/devices/route.ts | 8 +------- lib/devices/registry.ts | 6 ++++++ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/api/devices/__tests__/route.test.ts b/app/api/devices/__tests__/route.test.ts index 3d26acf90..055485d39 100644 --- a/app/api/devices/__tests__/route.test.ts +++ b/app/api/devices/__tests__/route.test.ts @@ -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; diff --git a/app/api/devices/route.ts b/app/api/devices/route.ts index 40748e1fe..3d3be4c91 100644 --- a/app/api/devices/route.ts +++ b/app/api/devices/route.ts @@ -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 ''; diff --git a/lib/devices/registry.ts b/lib/devices/registry.ts index c69d95813..a826ddd23 100644 --- a/lib/devices/registry.ts +++ b/lib/devices/registry.ts @@ -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}$/;