fix(server): consolidate allowed_file_types onto AllowedFileTypesService

This commit is contained in:
jubnl
2026-08-16 09:49:46 +02:00
parent c75d431e4c
commit b72645272b
10 changed files with 45 additions and 20 deletions
+2 -1
View File
@@ -80,6 +80,7 @@ import type { RuntimeEnvService } from '../../../src/nest/app-config/runtime-env
import { __clearVersionCacheForTests } from '../../../src/nest/admin/admin.helpers';
import { makeNotificationsService, makeNotificationPreferencesService } from '../../helpers/notifications';
import { EphemeralTokenService } from '../../../src/nest/auth/ephemeral-token.service';
import { AllowedFileTypesService } from '../../../src/nest/files/allowed-file-types.service';
const dbs = new DatabaseService(testDb);
const realtime = new RealtimeService();
@@ -90,7 +91,7 @@ const userCleanup = new UserCleanupService(dbs, new BudgetService(dbs, permissio
// and the mailer was missing entirely, so `auth` was built with its last four
// collaborators shifted by one. Nothing failed, because none of the cases below
// reach a path that uses them.
const auth = new AuthService(dbs, permissions, new TripMembershipService(dbs), webauthn, userCleanup, new MailerService(dbs), new EphemeralTokenService());
const auth = new AuthService(dbs, permissions, new TripMembershipService(dbs), webauthn, userCleanup, new MailerService(dbs), new EphemeralTokenService(), new AllowedFileTypesService(dbs));
const svc = new AdminService(
dbs,
new AddonsService(dbs),
@@ -59,6 +59,7 @@ import { NotificationsService } from '../../../src/nest/notifications/notificati
import { AdminService } from '../../../src/nest/admin/admin.service';
import { makeNotificationsService, makeNotificationPreferencesService } from '../../helpers/notifications';
import { EphemeralTokenService } from '../../../src/nest/auth/ephemeral-token.service';
import { AllowedFileTypesService } from '../../../src/nest/files/allowed-file-types.service';
const dbs = new DatabaseService(testDb);
const realtime = new RealtimeService();
@@ -69,7 +70,7 @@ const userCleanup = new UserCleanupService(dbs, new BudgetService(dbs, permissio
// and the mailer was missing entirely, so `auth` was built with its last four
// collaborators shifted by one. Nothing failed, because the version-check path
// below never reaches them.
const auth = new AuthService(dbs, permissions, new TripMembershipService(dbs), webauthn, userCleanup, new MailerService(dbs), new EphemeralTokenService());
const auth = new AuthService(dbs, permissions, new TripMembershipService(dbs), webauthn, userCleanup, new MailerService(dbs), new EphemeralTokenService(), new AllowedFileTypesService(dbs));
const svc = new AdminService(
dbs,
new AddonsService(dbs),
@@ -82,6 +82,8 @@ import { WebauthnConfigService } from '../../../src/nest/auth/webauthn-config.se
import { revokeUserSessions } from '../../../src/mcp/sessionManager';
import { MailerService } from '../../../src/nest/notifications/mailer/mailer.service';
import { EphemeralTokenService } from '../../../src/nest/auth/ephemeral-token.service';
import { AllowedFileTypesService } from '../../../src/nest/files/allowed-file-types.service';
import { DEFAULT_ALLOWED_EXTENSIONS } from '../../../src/nest/files/files.constants';
// MailerService is injected since the notifications fold — a stub instead of a
// module mock. sendPasswordResetEmail is the only thing auth reaches for.
@@ -103,6 +105,7 @@ const svc = new AuthService(
new UserCleanupService(new DatabaseService(testDb), new BudgetService(new DatabaseService(testDb), new PermissionsService(new DatabaseService(testDb)), new ExchangeRatesService(), new RealtimeService())),
mailerStub,
new EphemeralTokenService(),
new AllowedFileTypesService(new DatabaseService(testDb)),
);
// ---------------------------------------------------------------------------
@@ -559,6 +562,17 @@ describe('getAppConfig', () => {
vi.unstubAllEnvs();
});
it('AUTH-DB-050b: fresh-install fallback matches DEFAULT_ALLOWED_EXTENSIONS (pkpass/md included)', () => {
// No allowed_file_types row: the config payload must advertise the same
// default list the upload filters actually enforce — the historical
// hardcoded copy dropped pkpass, pkpasses, md and markdown, so the client
// greyed out types the server accepts.
createUser(testDb);
testDb.prepare("DELETE FROM app_settings WHERE key = 'allowed_file_types'").run();
const cfg = svc.getAppConfig(null);
expect(cfg.allowed_file_types).toBe(DEFAULT_ALLOWED_EXTENSIONS);
});
it('AUTH-DB-051: authenticated caller gets the permissions block; app_settings rows flow through', () => {
const { user } = createUser(testDb);
testDb.prepare("INSERT OR REPLACE INTO app_settings (key, value) VALUES ('require_mfa', 'true')").run();
+9 -7
View File
@@ -166,26 +166,28 @@ describe('files.constants', () => {
});
});
// ── getAllowedExtensions ──────────────────────────────────────────────────────
// ── allowed extensions (canonical owner: AllowedFileTypesService) ─────────────
// FilesService.getAllowedExtensions was deleted in the storage slice-2
// consolidation (it had no callers); the same four behaviors are pinned
// against the single remaining query owner.
describe('getAllowedExtensions', () => {
describe('AllowedFileTypesService.get', () => {
it('FILE-SVC-006: returns the app_settings value when set', () => {
setAppSetting(testDb, 'allowed_file_types', 'pdf,txt');
expect(svc.getAllowedExtensions()).toBe('pdf,txt');
expect(new AllowedFileTypesService(new DatabaseService(testDb)).get()).toBe('pdf,txt');
});
it('FILE-SVC-007: returns the default when the row is absent', () => {
expect(svc.getAllowedExtensions()).toBe(DEFAULT_ALLOWED_EXTENSIONS);
expect(new AllowedFileTypesService(new DatabaseService(testDb)).get()).toBe(DEFAULT_ALLOWED_EXTENSIONS);
});
it('FILE-SVC-008: returns the default for an empty value (|| coercion, not ??)', () => {
setAppSetting(testDb, 'allowed_file_types', '');
expect(svc.getAllowedExtensions()).toBe(DEFAULT_ALLOWED_EXTENSIONS);
expect(new AllowedFileTypesService(new DatabaseService(testDb)).get()).toBe(DEFAULT_ALLOWED_EXTENSIONS);
});
it('FILE-SVC-009: returns the default when the query throws (no app_settings table)', () => {
const bareSvc = new FilesService(new DatabaseService(bareDb), permissionsStub, new RealtimeService(), new EphemeralTokenService());
expect(bareSvc.getAllowedExtensions()).toBe(DEFAULT_ALLOWED_EXTENSIONS);
expect(new AllowedFileTypesService(new DatabaseService(bareDb)).get()).toBe(DEFAULT_ALLOWED_EXTENSIONS);
});
});
@@ -103,6 +103,7 @@ import { AuthService } from '../../../src/nest/auth/auth.service';
import { WebauthnConfigService } from '../../../src/nest/auth/webauthn-config.service';
import { UserCleanupService } from '../../../src/nest/auth/user-cleanup.service';
import { EphemeralTokenService } from '../../../src/nest/auth/ephemeral-token.service';
import { AllowedFileTypesService } from '../../../src/nest/files/allowed-file-types.service';
import { OidcService } from '../../../src/nest/oidc/oidc.service';
import { MailerService } from '../../../src/nest/notifications/mailer/mailer.service';
@@ -123,6 +124,7 @@ const auth = new AuthService(
new UserCleanupService(new DatabaseService(testDb), new BudgetService(new DatabaseService(testDb), new PermissionsService(new DatabaseService(testDb)), new ExchangeRatesService(), new RealtimeService())),
mailerStub,
new EphemeralTokenService(),
new AllowedFileTypesService(new DatabaseService(testDb)),
);
const svc = new OidcService(new DatabaseService(testDb), auth, membership);
@@ -84,6 +84,7 @@ import { PasskeyService } from '../../../src/nest/auth/passkey.service';
import { WebauthnConfigService } from '../../../src/nest/auth/webauthn-config.service';
import { UserCleanupService } from '../../../src/nest/auth/user-cleanup.service';
import { EphemeralTokenService } from '../../../src/nest/auth/ephemeral-token.service';
import { AllowedFileTypesService } from '../../../src/nest/files/allowed-file-types.service';
import { MailerService } from '../../../src/nest/notifications/mailer/mailer.service';
// MailerService is injected since the notifications fold — a stub instead of a
@@ -113,6 +114,7 @@ const auth = new AuthService(
new UserCleanupService(new DatabaseService(testDb), new BudgetService(new DatabaseService(testDb), new PermissionsService(new DatabaseService(testDb)), new ExchangeRatesService(), new RealtimeService())),
mailerStub,
new EphemeralTokenService(),
new AllowedFileTypesService(new DatabaseService(testDb)),
);
const svc = new PasskeyService(new DatabaseService(testDb), auth, webauthn);