Finish the contract ratchet, the upload bridge and the client leftovers

BODY_CONTRACT_ALLOW_LIST is empty. The last seventeen handlers type their
body with a createZodDto class, so the fail-closed boot gate has nothing left
to forgive; it still refuses a boot for a new handler that reads @Body()
without one.

Several of those contracts describe rather than constrain, and that is the
whole care in this change. The pipe runs before the handler, so a schema
stricter than the handler moves a user-visible answer:

- TagsController.create answers { error: 'Tag name is required' }, pinned
  byte-for-byte in two tests, and the pipe formats as `field: message`. The
  DTO leaves name optional and the handler keeps the check that owns the
  message. Same shape for the booking-import confirm body, which additionally
  gains per-item validation the handler never did.
- PluginsController.activate and install answer 503 when plugins are disabled,
  and check that BEFORE looking at the body. A schema that rejected first
  would turn that into a 400 for an operator whose server has plugins off.
- setEgressHosts reads a non-array `hosts` as "clear the list", which is the
  documented reset. Typing it as string[] would have removed a working admin
  action rather than tightened anything.
- PluginRoutesController.route answers { route: null } at 200 for everything
  it dislikes, so the client falls back to straight lines. A 400 there would
  turn a graceful degradation into a visible error.

files.bridge is gone. The multer options are built by
MulterModule.registerAsync from an AllowedFileTypesService leaf, so the
fileFilter reads the operator's extension list from the container instead of
from a second FilesService built outside it. Nest injects
MULTER_MODULE_OPTIONS with @Optional(), so a token that fails to resolve is
not a boot error: it silently falls back to defaults, with no filter and no
size cap. FILE-020 already proved that for trip files by uploading an .svg
over HTTP and expecting 400; journey uploads had no such case, and now do.
VIDEO_UPLOAD is untouched, including the filename callback that forces .jpg
regardless of what the client sends, which guards the public share proxy.

PluginFrame gets an onError. A frame whose document fails to load left a
blank element at full height, which reads as a hung app rather than as a
plugin that is gone.

typecheck:tests runs in CI now, with the test tree at zero errors. tz-lookup
is left as it is: the build puts it in a lazy helpers chunk, not the entry
chunk, and the remaining split would trade a synchronous render for an async
one to move 172 kB that is already off the critical path.
This commit is contained in:
Maurice
2026-08-10 19:35:47 +02:00
committed by Maurice
parent 7078ac7a4d
commit ba7d55f999
74 changed files with 620 additions and 196 deletions
+1
View File
@@ -237,6 +237,7 @@ const admin: TranslationStrings = {
'admin.plugins.updateTo': 'Update → v{version}',
'admin.plugins.enabledToggle': 'تفعيل الإضافة',
'plugins.notFound': 'الإضافة غير موجودة',
'plugins.frameLoadFailed': 'This plugin could not be loaded.',
'admin.plugins.title': 'الإضافات الخارجية',
'admin.plugins.subtitle': 'ثبّت وأدر إضافات الجهات الخارجية على خادمك.',
'admin.plugins.disabledTitle': 'الإضافات الخارجية معطّلة',
+1
View File
@@ -217,6 +217,7 @@ const admin: TranslationStrings = {
'admin.plugins.updateTo': 'Atualizar → v{version}',
'admin.plugins.enabledToggle': 'Ativar plugin',
'plugins.notFound': 'Plugin não encontrado',
'plugins.frameLoadFailed': 'This plugin could not be loaded.',
'admin.plugins.title': 'Plugins',
'admin.plugins.subtitle': 'Instale e gerencie plugins de terceiros na sua instância.',
'admin.plugins.disabledTitle': 'Os plugins estão desativados',
+1
View File
@@ -430,6 +430,7 @@ const admin: TranslationStrings = {
'admin.plugins.updateTo': 'Actualitzar → v{version}',
'admin.plugins.enabledToggle': 'Activar el connector',
'plugins.notFound': "No s'ha trobat el connector",
'plugins.frameLoadFailed': 'This plugin could not be loaded.',
'admin.plugins.title': 'Connectors',
'admin.plugins.subtitle': 'Instal·la i gestiona connectors de tercers a la teva instància.',
'admin.plugins.disabledTitle': 'Els connectors estan desactivats',
+1
View File
@@ -218,6 +218,7 @@ const admin: TranslationStrings = {
'admin.plugins.updateTo': 'Update → v{version}',
'admin.plugins.enabledToggle': 'Povolit plugin',
'plugins.notFound': 'Plugin nenalezen',
'plugins.frameLoadFailed': 'This plugin could not be loaded.',
'admin.plugins.title': 'Pluginy',
'admin.plugins.subtitle': 'Instalujte a spravujte pluginy třetích stran na své instanci.',
'admin.plugins.disabledTitle': 'Pluginy jsou zakázány',
+1
View File
@@ -221,6 +221,7 @@ const admin: TranslationStrings = {
'admin.plugins.updateTo': 'Update → v{version}',
'admin.plugins.enabledToggle': 'Plugin aktivieren',
'plugins.notFound': 'Plugin nicht gefunden',
'plugins.frameLoadFailed': 'Dieses Plugin konnte nicht geladen werden.',
'admin.plugins.title': 'Plugins',
'admin.plugins.subtitle': 'Plugins von Drittanbietern auf deiner Instanz installieren und verwalten.',
'admin.plugins.disabledTitle': 'Plugins sind deaktiviert',
+1
View File
@@ -271,6 +271,7 @@ const admin: TranslationStrings = {
'admin.plugins.updateTo': 'Update → v{version}',
'admin.plugins.enabledToggle': 'Enable plugin',
'plugins.notFound': 'Plugin not found',
'plugins.frameLoadFailed': 'This plugin could not be loaded.',
'admin.plugins.title': 'Plugins',
'admin.plugins.subtitle': 'Install and manage third-party plugins on your instance.',
'admin.plugins.disabledTitle': 'Plugins are disabled',
+1
View File
@@ -219,6 +219,7 @@ const admin: TranslationStrings = {
'admin.plugins.updateTo': 'Actualizar → v{version}',
'admin.plugins.enabledToggle': 'Activar plugin',
'plugins.notFound': 'Plugin no encontrado',
'plugins.frameLoadFailed': 'No se ha podido cargar este plugin.',
'admin.plugins.title': 'Plugins',
'admin.plugins.subtitle': 'Instala y gestiona plugins de terceros en tu instancia.',
'admin.plugins.disabledTitle': 'Los plugins están desactivados',
+1
View File
@@ -222,6 +222,7 @@ const admin: TranslationStrings = {
'admin.plugins.updateTo': 'Update → v{version}',
'admin.plugins.enabledToggle': 'Activer le plugin',
'plugins.notFound': 'Plugin introuvable',
'plugins.frameLoadFailed': "Ce plugin n'a pas pu être chargé.",
'admin.plugins.title': 'Plugins',
'admin.plugins.subtitle': 'Installez et gérez des plugins tiers sur votre instance.',
'admin.plugins.disabledTitle': 'Les plugins sont désactivés',
+1
View File
@@ -266,6 +266,7 @@ const admin: TranslationStrings = {
'admin.plugins.updateTo': 'Update → v{version}',
'admin.plugins.enabledToggle': 'Ενεργοποίηση plugin',
'plugins.notFound': 'Το plugin δεν βρέθηκε',
'plugins.frameLoadFailed': 'This plugin could not be loaded.',
'admin.plugins.title': 'Plugins',
'admin.plugins.subtitle': 'Εγκαταστήστε και διαχειριστείτε plugins τρίτων στην εγκατάστασή σας.',
'admin.plugins.disabledTitle': 'Τα plugins είναι απενεργοποιημένα',
+1
View File
@@ -220,6 +220,7 @@ const admin: TranslationStrings = {
'admin.plugins.updateTo': 'Frissítés → v{version}',
'admin.plugins.enabledToggle': 'Plugin engedélyezése',
'plugins.notFound': 'A plugin nem található',
'plugins.frameLoadFailed': 'This plugin could not be loaded.',
'admin.plugins.title': 'Pluginok',
'admin.plugins.subtitle': 'Harmadik féltől származó pluginok telepítése és kezelése a példányodon.',
'admin.plugins.disabledTitle': 'A pluginok le vannak tiltva',
+1
View File
@@ -262,6 +262,7 @@ const admin: TranslationStrings = {
'admin.plugins.updateTo': 'Update → v{version}',
'admin.plugins.enabledToggle': 'Aktifkan plugin',
'plugins.notFound': 'Plugin tidak ditemukan',
'plugins.frameLoadFailed': 'This plugin could not be loaded.',
'admin.plugins.title': 'Plugins',
'admin.plugins.subtitle': 'Pasang dan kelola plugin pihak ketiga di instans kamu.',
'admin.plugins.disabledTitle': 'Plugin dinonaktifkan',
+1
View File
@@ -219,6 +219,7 @@ const admin: TranslationStrings = {
'admin.plugins.updateTo': 'Aggiorna → v{version}',
'admin.plugins.enabledToggle': 'Abilita plugin',
'plugins.notFound': 'Plugin non trovato',
'plugins.frameLoadFailed': 'Impossibile caricare questo plugin.',
'admin.plugins.title': 'Plugin',
'admin.plugins.subtitle': 'Installa e gestisci plugin di terze parti sulla tua istanza.',
'admin.plugins.disabledTitle': 'I plugin sono disabilitati',
+1
View File
@@ -251,6 +251,7 @@ const admin: TranslationStrings = {
'admin.plugins.updateTo': '更新 → v{version}',
'admin.plugins.enabledToggle': 'プラグインを有効にする',
'plugins.notFound': 'プラグインが見つかりません',
'plugins.frameLoadFailed': 'This plugin could not be loaded.',
'admin.plugins.title': 'プラグイン',
'admin.plugins.subtitle': 'サードパーティ製プラグインをインスタンスにインストール・管理します。',
'admin.plugins.disabledTitle': 'プラグインは無効になっています',
+1
View File
@@ -256,6 +256,7 @@ const admin: TranslationStrings = {
'admin.plugins.updateTo': '업데이트 → v{version}',
'admin.plugins.enabledToggle': '플러그인 활성화',
'plugins.notFound': '플러그인을 찾을 수 없습니다',
'plugins.frameLoadFailed': 'This plugin could not be loaded.',
'admin.plugins.title': '플러그인',
'admin.plugins.subtitle': '인스턴스에 서드파티 플러그인을 설치하고 관리하세요.',
'admin.plugins.disabledTitle': '플러그인이 비활성화되어 있습니다',
+1
View File
@@ -220,6 +220,7 @@ const admin: TranslationStrings = {
'admin.plugins.updateTo': 'Update → v{version}',
'admin.plugins.enabledToggle': 'Plugin inschakelen',
'plugins.notFound': 'Plugin niet gevonden',
'plugins.frameLoadFailed': 'Deze plug-in kon niet worden geladen.',
'admin.plugins.title': 'Plugins',
'admin.plugins.subtitle': 'Installeer en beheer plugins van derden op je instantie.',
'admin.plugins.disabledTitle': 'Plugins zijn uitgeschakeld',
+1
View File
@@ -213,6 +213,7 @@ const admin: TranslationStrings = {
'admin.plugins.updateTo': 'Update → v{version}',
'admin.plugins.enabledToggle': 'Włącz wtyczkę',
'plugins.notFound': 'Nie znaleziono wtyczki',
'plugins.frameLoadFailed': 'This plugin could not be loaded.',
'admin.plugins.title': 'Wtyczki',
'admin.plugins.subtitle': 'Instaluj wtyczki innych autorów i zarządzaj nimi na swojej instancji.',
'admin.plugins.disabledTitle': 'Wtyczki są wyłączone',
+1
View File
@@ -220,6 +220,7 @@ const admin: TranslationStrings = {
'admin.plugins.updateTo': 'Обновить → v{version}',
'admin.plugins.enabledToggle': 'Включить плагин',
'plugins.notFound': 'Плагин не найден',
'plugins.frameLoadFailed': 'This plugin could not be loaded.',
'admin.plugins.title': 'Плагины',
'admin.plugins.subtitle': 'Устанавливайте сторонние плагины и управляйте ими на вашем экземпляре.',
'admin.plugins.disabledTitle': 'Плагины отключены',
+1
View File
@@ -276,6 +276,7 @@ const admin: TranslationStrings = {
'admin.plugins.updateTo': 'Uppdatera → v{version}',
'admin.plugins.enabledToggle': 'Aktivera plugin',
'plugins.notFound': 'Pluginet hittades inte',
'plugins.frameLoadFailed': 'This plugin could not be loaded.',
'admin.plugins.title': 'Plugins',
'admin.plugins.subtitle': 'Installera och hantera tredjepartsplugins på din instans.',
'admin.plugins.disabledTitle': 'Plugins är inaktiverade',
+1
View File
@@ -264,6 +264,7 @@ const admin: TranslationStrings = {
'admin.plugins.updateTo': 'Güncelle → v{version}',
'admin.plugins.enabledToggle': 'Eklentiyi etkinleştir',
'plugins.notFound': 'Eklenti bulunamadı',
'plugins.frameLoadFailed': 'This plugin could not be loaded.',
'admin.plugins.title': 'Plugins',
'admin.plugins.subtitle': 'Örneğinizde üçüncü taraf eklentileri yükleyin ve yönetin.',
'admin.plugins.disabledTitle': 'Eklentiler devre dışı',
+1
View File
@@ -219,6 +219,7 @@ const admin: TranslationStrings = {
'admin.plugins.updateTo': 'Оновити → v{version}',
'admin.plugins.enabledToggle': 'Увімкнути плагін',
'plugins.notFound': 'Плагін не знайдено',
'plugins.frameLoadFailed': 'This plugin could not be loaded.',
'admin.plugins.title': 'Плагіни',
'admin.plugins.subtitle': 'Встановлюйте сторонні плагіни та керуйте ними на вашому інстансі.',
'admin.plugins.disabledTitle': 'Плагіни вимкнено',
+1
View File
@@ -275,6 +275,7 @@ const admin: TranslationStrings = {
'admin.plugins.updateTo': 'Cập nhật → v{version}',
'admin.plugins.enabledToggle': 'Bật plugin',
'plugins.notFound': 'Không tìm thấy plugin',
'plugins.frameLoadFailed': 'This plugin could not be loaded.',
'admin.plugins.title': 'Plugins',
'admin.plugins.subtitle': 'Cài đặt và quản lý các plugin bên thứ ba trên hệ thống của bạn.',
'admin.plugins.disabledTitle': 'Plugin đang bị tắt',
+1
View File
@@ -251,6 +251,7 @@ const admin: TranslationStrings = {
'admin.plugins.updateTo': '更新 → v{version}',
'admin.plugins.enabledToggle': '啟用外掛',
'plugins.notFound': '找不到外掛',
'plugins.frameLoadFailed': 'This plugin could not be loaded.',
'admin.plugins.title': '外掛',
'admin.plugins.subtitle': '在你的執行個體上安裝並管理第三方外掛。',
'admin.plugins.disabledTitle': '外掛已停用',
+1
View File
@@ -211,6 +211,7 @@ const admin: TranslationStrings = {
'admin.plugins.updateTo': '更新 → v{version}',
'admin.plugins.enabledToggle': '启用插件',
'plugins.notFound': '未找到插件',
'plugins.frameLoadFailed': 'This plugin could not be loaded.',
'admin.plugins.title': '插件',
'admin.plugins.subtitle': '在你的实例上安装和管理第三方插件。',
'admin.plugins.disabledTitle': '插件功能已禁用',