mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
refactor(server): move the stateless helpers into nest/common (#1823)
Eight files in services/ hold no state and touch no database: avatarUrl, conflictResult, demo, passwordPolicy, timezoneService, cookie, and the two crypto helpers. They are imported from 65 places, 62 of them already inside src/nest, so having them live in the legacy layer was pure inertia. They stay plain functions rather than becoming providers. Three importers sit outside the container and would have no way to reach an injectable one: db/migrations.ts runs before the app is built, and middleware/auth.ts and middleware/mfaPolicy.ts run beside it. Wrapping these in DI would buy ceremony and nothing else. mfaCrypto and apiKeyCrypto move together into common/crypto — they read the same ENCRYPTION_KEY, and the directory says so. services/ drops from 44 files to 36, 9,870 lines to 8,493. One trap worth recording: src/app-config and src/nest/app-config both exist. cookie.ts imported '../app-config' from services/, which resolved to the former; after the move the same string silently pointed at the latter, which has no readEnv. It is now explicit about which one it wants. Nothing else changes — no signatures, no behaviour, no tests rewritten beyond their import paths.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { readEnv } from '../app-config';
|
||||
import { encrypt_api_key } from '../services/apiKeyCrypto';
|
||||
import { encrypt_api_key } from '../nest/common/crypto/apiKeyCrypto';
|
||||
|
||||
import Database from 'better-sqlite3';
|
||||
import fs from 'fs';
|
||||
|
||||
@@ -5,7 +5,7 @@ import { db } from '../db/database';
|
||||
import { JWT_SECRET } from '../config';
|
||||
import { AuthRequest, OptionalAuthRequest, User } from '../types';
|
||||
import { applyIdempotency } from './idempotency';
|
||||
import { isDemoEmail } from '../services/demo';
|
||||
import { isDemoEmail } from '../nest/common/demo';
|
||||
|
||||
export function extractToken(req: Request): string | null {
|
||||
// Prefer httpOnly cookie; fall back to Authorization: Bearer (MCP, API clients)
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Request, Response, NextFunction } from 'express';
|
||||
import { readEnv } from '../app-config';
|
||||
import { db } from '../db/database';
|
||||
import { extractToken, verifyJwtAndLoadUser } from './auth';
|
||||
import { DEMO_EMAILS } from '../services/demo';
|
||||
import { DEMO_EMAILS } from '../nest/common/demo';
|
||||
|
||||
/** Paths that never require MFA (public or pre-auth). */
|
||||
export function isPublicApiPath(method: string, pathNoQuery: string): boolean {
|
||||
|
||||
@@ -15,11 +15,11 @@ import { revokeUserSessions, revokeUserSessionsForClient } from '../../mcp/sessi
|
||||
import { invalidateMcpSessions } from '../../mcp';
|
||||
import { emitUserDeleted } from '../../plugin-user-lifecycle';
|
||||
import type { User, Addon } from '../../types';
|
||||
import { maybe_encrypt_api_key, decrypt_api_key } from '../../services/apiKeyCrypto';
|
||||
import { avatarUrl } from '../../services/avatarUrl';
|
||||
import { maybe_encrypt_api_key, decrypt_api_key } from '../common/crypto/apiKeyCrypto';
|
||||
import { avatarUrl } from '../common/avatarUrl';
|
||||
import { prepareLlmAddonConfigForWrite, maskLlmAddonConfig } from '../../services/llmConfig';
|
||||
import { getPhotoProviderConfig } from '../../services/memories/helpersService';
|
||||
import { validatePassword } from '../../services/passwordPolicy';
|
||||
import { validatePassword } from '../common/passwordPolicy';
|
||||
import { deleteUserCompletely } from '../../services/userCleanupService';
|
||||
import { getPreferencesMatrix, setAdminPreferences } from '../../services/notificationPreferencesService';
|
||||
import { DatabaseService } from '../database/database.service';
|
||||
|
||||
@@ -6,7 +6,7 @@ import { RateLimitService } from '../common/rate-limit.service';
|
||||
import { OptionalJwtGuard } from './optional-jwt.guard';
|
||||
import { getClientIp } from '../audit/client-ip';
|
||||
import { AuditService } from '../audit/audit.service';
|
||||
import { willDropSecureCookie } from '../../services/cookie';
|
||||
import { willDropSecureCookie } from '../common/cookie';
|
||||
import type { User } from '../../types';
|
||||
|
||||
const WINDOW = 15 * 60 * 1000;
|
||||
|
||||
@@ -35,7 +35,7 @@ const auth = new AuthService(
|
||||
);
|
||||
|
||||
export { stripUserForClient } from './auth.helpers';
|
||||
export { avatarUrl } from '../../services/avatarUrl';
|
||||
export { avatarUrl } from '../common/avatarUrl';
|
||||
|
||||
export function isDemoUser(userId: number): boolean {
|
||||
return auth.isDemoUser(userId);
|
||||
|
||||
@@ -39,7 +39,7 @@ import { JwtAuthGuard } from './jwt-auth.guard';
|
||||
import { CurrentUser } from './current-user.decorator';
|
||||
import { getClientIp } from '../audit/client-ip';
|
||||
import { AuditService } from '../audit/audit.service';
|
||||
import { isDemoEmail } from '../../services/demo';
|
||||
import { isDemoEmail } from '../common/demo';
|
||||
import type { User } from '../../types';
|
||||
|
||||
const WINDOW = 15 * 60 * 1000;
|
||||
|
||||
@@ -3,7 +3,7 @@ import crypto from 'crypto';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { User } from '../../types';
|
||||
import { decrypt_api_key } from '../../services/apiKeyCrypto';
|
||||
import { decrypt_api_key } from '../common/crypto/apiKeyCrypto';
|
||||
|
||||
/**
|
||||
* Pure half of the auth domain (no DB, no injected deps) — the DB half lives
|
||||
|
||||
@@ -13,9 +13,9 @@ import { DatabaseService } from '../database/database.service';
|
||||
import { PermissionsService } from '../permissions/permissions.service';
|
||||
import { AtlasService } from '../atlas/atlas.service';
|
||||
import { getCountryFromCoords } from '../atlas/atlas-geo';
|
||||
import { validatePassword } from '../../services/passwordPolicy';
|
||||
import { encryptMfaSecret, decryptMfaSecret } from '../../services/mfaCrypto';
|
||||
import { decrypt_api_key, maybe_encrypt_api_key, encrypt_api_key } from '../../services/apiKeyCrypto';
|
||||
import { validatePassword } from '../common/passwordPolicy';
|
||||
import { encryptMfaSecret, decryptMfaSecret } from '../common/crypto/mfaCrypto';
|
||||
import { decrypt_api_key, maybe_encrypt_api_key, encrypt_api_key } from '../common/crypto/apiKeyCrypto';
|
||||
import { createEphemeralToken } from '../../services/ephemeralTokens';
|
||||
// Import from sessionManager directly, NOT the ../../mcp barrel: the barrel pulls
|
||||
// the whole tools fan-out (and via the domain bridges, the Nest services) into
|
||||
@@ -27,11 +27,11 @@ import { emitUserDeleted } from '../../plugin-user-lifecycle';
|
||||
import { getFlightDistanceKm } from '../../services/distanceService';
|
||||
import { verifyJwtAndLoadUser } from '../../middleware/auth';
|
||||
import { User } from '../../types';
|
||||
import { DEMO_EMAIL_PRIMARY, isDemoEmail } from '../../services/demo';
|
||||
import { avatarUrl } from '../../services/avatarUrl';
|
||||
import { DEMO_EMAIL_PRIMARY, isDemoEmail } from '../common/demo';
|
||||
import { avatarUrl } from '../common/avatarUrl';
|
||||
import { joinTripAsMember } from '../../services/tripMembership';
|
||||
import { isPasskeyConfigured } from '../../services/webauthnConfig';
|
||||
import { setAuthCookie, clearAuthCookie } from '../../services/cookie';
|
||||
import { setAuthCookie, clearAuthCookie } from '../common/cookie';
|
||||
import { sendPasswordResetEmail } from '../../services/notifications';
|
||||
import { getAppUrl } from '../../app-config';
|
||||
import {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { RateLimitService } from '../common/rate-limit.service';
|
||||
import { JwtAuthGuard } from './jwt-auth.guard';
|
||||
import { PasskeyEnabledGuard } from './passkey-enabled.guard';
|
||||
import { CurrentUser } from './current-user.decorator';
|
||||
import { setAuthCookie } from '../../services/cookie';
|
||||
import { setAuthCookie } from '../common/cookie';
|
||||
import { getClientIp } from '../audit/client-ip';
|
||||
import { AuditService } from '../audit/audit.service';
|
||||
import { PasskeyService } from './passkey.service';
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
type AuthenticatorTransportFuture,
|
||||
} from '@simplewebauthn/server';
|
||||
import { resolveWebauthnConfig } from '../../services/webauthnConfig';
|
||||
import { avatarUrl } from '../../services/avatarUrl';
|
||||
import { avatarUrl } from '../common/avatarUrl';
|
||||
import { stripUserForClient } from './auth.helpers';
|
||||
import { AuthService } from './auth.service';
|
||||
import { DatabaseService } from '../database/database.service';
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { TrekWsPayload, TrekWsTripEventName } from '@trek/shared';
|
||||
import { RealtimeService } from '../realtime/realtime.service';
|
||||
import { PermissionsService } from '../permissions/permissions.service';
|
||||
import { verifyTripAccess } from '../../services/tripAccess';
|
||||
import { avatarUrl } from '../../services/avatarUrl';
|
||||
import { avatarUrl } from '../common/avatarUrl';
|
||||
import type { User, BudgetItem, BudgetItemMember, BudgetItemPayer } from '../../types';
|
||||
import { ExchangeRatesService } from './exchange-rates.service';
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { TrekWsPayload, TrekWsTripEventName } from '@trek/shared';
|
||||
import { RealtimeService } from '../realtime/realtime.service';
|
||||
import { PermissionsService } from '../permissions/permissions.service';
|
||||
import { verifyTripAccess } from '../../services/tripAccess';
|
||||
import { avatarUrl } from '../../services/avatarUrl';
|
||||
import { avatarUrl } from '../common/avatarUrl';
|
||||
import { checkSsrf, createPinnedDispatcher } from '../../utils/ssrfGuard';
|
||||
import type { CollabNote, CollabPoll, CollabMessage, TripFile, User } from '../../types';
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ import { JwtAuthGuard } from '../auth/jwt-auth.guard';
|
||||
import { CurrentUser } from '../auth/current-user.decorator';
|
||||
import { PLACE_IMAGE_UPLOAD } from '../common/place-image-upload';
|
||||
import { placeImageUrl } from '../../services/placeImage';
|
||||
import { isDemoEmail } from '../../services/demo';
|
||||
import { isDemoEmail } from '../common/demo';
|
||||
import {
|
||||
CollectionCreateDto,
|
||||
CollectionUpdateDto,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { readEnv } from '../app-config';
|
||||
import { SESSION_DURATION_MS, SESSION_DURATION_REMEMBER_MS } from '../config';
|
||||
import { readEnv } from '../../app-config';
|
||||
import { SESSION_DURATION_MS, SESSION_DURATION_REMEMBER_MS } from '../../config';
|
||||
|
||||
const COOKIE_NAME = 'trek_session';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as crypto from 'node:crypto';
|
||||
import { ENCRYPTION_KEY } from '../config';
|
||||
import { ENCRYPTION_KEY } from '../../../config';
|
||||
|
||||
const ENCRYPTED_PREFIX = 'enc:v1:';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import crypto from 'crypto';
|
||||
import { ENCRYPTION_KEY } from '../config';
|
||||
import { ENCRYPTION_KEY } from '../../../config';
|
||||
|
||||
function getKey(): Buffer {
|
||||
return crypto.createHash('sha256').update(`${ENCRYPTION_KEY}:mfa:v1`).digest();
|
||||
@@ -28,7 +28,7 @@ import { CurrentUser } from '../auth/current-user.decorator';
|
||||
import { MAX_FILE_SIZE, MAX_VIDEO_SIZE, BLOCKED_EXTENSIONS, filesDir, isVideoExtension } from './files.constants';
|
||||
import { getAllowedExtensions } from './files.bridge';
|
||||
import { FileUploadDto, FileUpdateDto, FileLinkDto } from './files.dto';
|
||||
import { isDemoEmail } from '../../services/demo';
|
||||
import { isDemoEmail } from '../common/demo';
|
||||
|
||||
const UPLOAD = {
|
||||
storage: diskStorage({
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { Request } from 'express';
|
||||
import type { TrekWsPayload, TrekWsTripEventName } from '@trek/shared';
|
||||
import { RealtimeService } from '../realtime/realtime.service';
|
||||
import { PermissionsService } from '../permissions/permissions.service';
|
||||
import { avatarUrl } from '../../services/avatarUrl';
|
||||
import { avatarUrl } from '../common/avatarUrl';
|
||||
import { consumeEphemeralToken } from '../../services/ephemeralTokens';
|
||||
import { verifyJwtAndLoadUser } from '../../middleware/auth';
|
||||
import type { User, TripFile } from '../../types';
|
||||
|
||||
@@ -9,7 +9,7 @@ import type {
|
||||
} from '@trek/shared';
|
||||
import { readEnv, getAppUrl } from '../../app-config';
|
||||
import { safeFetchFollow, SsrfBlockedError } from '../../utils/ssrfGuard';
|
||||
import { decrypt_api_key } from '../../services/apiKeyCrypto';
|
||||
import { decrypt_api_key } from '../common/crypto/apiKeyCrypto';
|
||||
// ── Photo cache (disk-backed) ────────────────────────────────────────────────
|
||||
import * as placePhotoCache from '../../services/placePhotoCache';
|
||||
import { DatabaseService } from '../database/database.service';
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
} from '../../services/notificationPreferencesService';
|
||||
import { getChannel, listChannels, type ChannelMessage, type ExternalChannel } from '../../services/notifications/channelRegistry';
|
||||
import { getAction } from '../../services/inAppNotificationActions';
|
||||
import { avatarUrl } from '../../services/avatarUrl';
|
||||
import { avatarUrl } from '../common/avatarUrl';
|
||||
import { DatabaseService } from '../database/database.service';
|
||||
import { RealtimeService } from '../realtime/realtime.service';
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Controller, Get, Query, Req, Res } from '@nestjs/common';
|
||||
import type { Request, Response } from 'express';
|
||||
import { readEnv } from '../../app-config';
|
||||
import { OidcService, OIDC_STATE_TTL_MS } from './oidc.service';
|
||||
import { cookieOptions } from '../../services/cookie';
|
||||
import { cookieOptions } from '../common/cookie';
|
||||
|
||||
const OIDC_STATE_COOKIE = 'trek_oidc_state';
|
||||
|
||||
|
||||
@@ -8,9 +8,9 @@ import type { Request, Response } from 'express';
|
||||
import { readEnv, getAppUrl } from '../../app-config';
|
||||
import { JWT_SECRET, SESSION_DURATION_SECONDS } from '../../config';
|
||||
import { User } from '../../types';
|
||||
import { decrypt_api_key } from '../../services/apiKeyCrypto';
|
||||
import { decrypt_api_key } from '../common/crypto/apiKeyCrypto';
|
||||
import { joinTripAsMember } from '../../services/tripMembership';
|
||||
import { setAuthCookie } from '../../services/cookie';
|
||||
import { setAuthCookie } from '../common/cookie';
|
||||
import { AuthService } from '../auth/auth.service';
|
||||
import { DatabaseService } from '../database/database.service';
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
import type { TrekWsPayload, TrekWsTripEventName } from '@trek/shared';
|
||||
import type { User } from '../../types';
|
||||
import { PackingService } from './packing.service';
|
||||
import { isUpdateConflict } from '../../services/conflictResult';
|
||||
import { isUpdateConflict } from '../common/conflictResult';
|
||||
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
|
||||
import { CurrentUser } from '../auth/current-user.decorator';
|
||||
import {
|
||||
|
||||
@@ -3,8 +3,8 @@ import type { TrekWsPayload, TrekWsTripEventName } from '@trek/shared';
|
||||
import { RealtimeService } from '../realtime/realtime.service';
|
||||
import { PermissionsService } from '../permissions/permissions.service';
|
||||
import { verifyTripAccess } from '../../services/tripAccess';
|
||||
import { avatarUrl } from '../../services/avatarUrl';
|
||||
import type { UpdateConflict } from '../../services/conflictResult';
|
||||
import { avatarUrl } from '../common/avatarUrl';
|
||||
import type { UpdateConflict } from '../common/conflictResult';
|
||||
import type { User } from '../../types';
|
||||
import { DatabaseService } from '../database/database.service';
|
||||
|
||||
|
||||
@@ -20,12 +20,12 @@ import { hexColorSchema } from '@trek/shared';
|
||||
import { readEnv } from '../../app-config';
|
||||
import type { User } from '../../types';
|
||||
import { PlacesService } from './places.service';
|
||||
import { isUpdateConflict } from '../../services/conflictResult';
|
||||
import { isUpdateConflict } from '../common/conflictResult';
|
||||
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
|
||||
import { CurrentUser } from '../auth/current-user.decorator';
|
||||
import { PLACE_IMAGE_UPLOAD } from '../common/place-image-upload';
|
||||
import { placeImageUrl } from '../../services/placeImage';
|
||||
import { isDemoEmail } from '../../services/demo';
|
||||
import { isDemoEmail } from '../common/demo';
|
||||
import {
|
||||
PlaceBulkDeleteDto,
|
||||
PlaceBulkUpdateDto,
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
resolveCategoryIdForFolder,
|
||||
} from '../../services/kmlImport';
|
||||
import { searchUnsplashPhotos, getUnsplashKey } from '../../services/unsplashService';
|
||||
import { type UpdateConflict, isUpdateConflict } from '../../services/conflictResult';
|
||||
import { type UpdateConflict, isUpdateConflict } from '../common/conflictResult';
|
||||
import { reclaimPlaceImage } from '../../services/placeImage';
|
||||
import { onPlaceCreated, onPlaceUpdated, onPlaceDeleted } from '../../services/journeyService';
|
||||
import {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common';
|
||||
import type { TrekWsPayload, TrekWsTripEventName } from '@trek/shared';
|
||||
import { readEnv } from '../../../app-config';
|
||||
import { RealtimeService } from '../../realtime/realtime.service';
|
||||
import { isUpdateConflict } from '../../../services/conflictResult';
|
||||
import { isUpdateConflict } from '../../common/conflictResult';
|
||||
import { getWeather } from '../../../services/weatherService';
|
||||
import { BLOCKED_EXTENSIONS, filesDir } from '../../files/files.constants';
|
||||
import { joinTripAsMember } from '../../../services/tripMembership';
|
||||
@@ -17,7 +17,7 @@ import { PermissionsService } from '../../permissions/permissions.service';
|
||||
import { TripsService, NotFoundError, ValidationError } from '../../trips/trips.service';
|
||||
import { PlacesService, type PlaceCreateInput, type PlaceUpdateInput } from '../../places/places.service';
|
||||
import { AddonsService } from '../../addons/addons.service';
|
||||
import { isDemoEmail } from '../../../services/demo';
|
||||
import { isDemoEmail } from '../../common/demo';
|
||||
import { ADDON_IDS } from '../../../addons';
|
||||
import { listJourneys, listEntries as listJournalEntriesSvc, createEntry as createJournalEntrySvc, updateEntry as updateJournalEntrySvc, deleteEntry as deleteJournalEntrySvc, createJourney as createJourneySvc, deleteJourney as deleteJourneySvc, onPlaceCreated, onPlaceUpdated, onPlaceDeleted } from '../../../services/journeyService';
|
||||
import { AtlasService } from '../../atlas/atlas.service';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import crypto from 'node:crypto';
|
||||
import { DatabaseService } from '../database/database.service';
|
||||
import { encrypt_api_key, decrypt_api_key } from '../../services/apiKeyCrypto';
|
||||
import { encrypt_api_key, decrypt_api_key } from '../common/crypto/apiKeyCrypto';
|
||||
import { getAppUrl } from '../../app-config';
|
||||
import { isPrivateIp } from './install/safe-fetch';
|
||||
import { safeFetchLlm } from '../../utils/ssrfGuard';
|
||||
|
||||
@@ -9,7 +9,7 @@ import { readUserSettingsDecrypted, hasRequiredUserSettings } from './plugins.se
|
||||
import { PLUGIN_CHANNEL_EVENTS } from './install/manifest';
|
||||
import { stripEmoji } from './text-sanitize';
|
||||
import { applyStagedPluginTrees, setStagedRestoreApplier } from './plugin-backup';
|
||||
import { decrypt_api_key } from '../../services/apiKeyCrypto';
|
||||
import { decrypt_api_key } from '../common/crypto/apiKeyCrypto';
|
||||
import { PluginSupervisor, type PluginRouteInfo } from './supervisor/plugin-supervisor';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
@@ -3,7 +3,7 @@ import { db } from '../../db/database';
|
||||
import { DatabaseService } from '../database/database.service';
|
||||
import { pluginsEnabled } from './kill-switch';
|
||||
import { devLinkEnabled } from './dev-link';
|
||||
import { maybe_encrypt_api_key, decrypt_api_key } from '../../services/apiKeyCrypto';
|
||||
import { maybe_encrypt_api_key, decrypt_api_key } from '../common/crypto/apiKeyCrypto';
|
||||
import { readAudit } from './host/plugin-audit';
|
||||
import { keyFingerprint } from './signature-status';
|
||||
import { pluginBudgetUsage } from './host/plugin-host-state';
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { TrekWsPayload, TrekWsTripEventName } from '@trek/shared';
|
||||
import { RealtimeService } from '../realtime/realtime.service';
|
||||
import { PermissionsService } from '../permissions/permissions.service';
|
||||
import { verifyTripAccess } from '../../services/tripAccess';
|
||||
import { avatarUrl } from '../../services/avatarUrl';
|
||||
import { avatarUrl } from '../common/avatarUrl';
|
||||
import type { Reservation, User } from '../../types';
|
||||
import { BudgetService } from '../budget/budget.service';
|
||||
import { typeToCostCategory } from '@trek/shared';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { DatabaseService } from '../database/database.service';
|
||||
import { decrypt_api_key, maybe_encrypt_api_key } from '../../services/apiKeyCrypto';
|
||||
import { decrypt_api_key, maybe_encrypt_api_key } from '../common/crypto/apiKeyCrypto';
|
||||
import { MASKED_SETTING_VALUE, normalizeAppearance } from '@trek/shared';
|
||||
|
||||
const ENCRYPTED_SETTING_KEYS = new Set(['webhook_url', 'ntfy_token', 'mapbox_access_token', 'llm_api_key']);
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*/
|
||||
import { haversineKm } from '../../services/distanceService';
|
||||
import type { EndpointInput } from '../reservations/reservations.service';
|
||||
import { localParts, resolveTimeZone } from '../../services/timezoneService';
|
||||
import { localParts, resolveTimeZone } from '../common/timezoneService';
|
||||
import { deriveTransitStats, type TransitItinerary, type TransitLeg } from './transit.helpers';
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
@@ -30,7 +30,7 @@ import { CurrentUser } from '../auth/current-user.decorator';
|
||||
import { getClientIp } from '../audit/client-ip';
|
||||
import { logInfo } from '../audit/audit-log.logger';
|
||||
import { AuditService } from '../audit/audit.service';
|
||||
import { isDemoEmail } from '../../services/demo';
|
||||
import { isDemoEmail } from '../common/demo';
|
||||
import { NotFoundError, ValidationError } from './trips.service';
|
||||
import { TripCreateDto, TripUpdateDto, TripCopyDto, TripAddMemberDto, TripTransferOwnershipDto, TripCreateGuestDto, TripRenameGuestDto } from './trips.dto';
|
||||
import { saveUnsplashCover, isUnsplashCoverUrl } from '../../services/unsplashService';
|
||||
|
||||
@@ -7,10 +7,10 @@ import type { TrekWsPayload, TrekWsTripEventName } from '@trek/shared';
|
||||
import { RealtimeService } from '../realtime/realtime.service';
|
||||
import { PermissionsService } from '../permissions/permissions.service';
|
||||
import type { Trip, User } from '../../types';
|
||||
import { avatarUrl } from '../../services/avatarUrl';
|
||||
import { avatarUrl } from '../common/avatarUrl';
|
||||
import { erasePluginUserData } from '../../services/userCleanupService';
|
||||
import { emitUserDeleted } from '../../plugin-user-lifecycle';
|
||||
import { resolveTimeZone } from '../../services/timezoneService';
|
||||
import { resolveTimeZone } from '../common/timezoneService';
|
||||
import { PlacesService } from '../places/places.service';
|
||||
import { DaysService, addDays } from '../days/days.service';
|
||||
import { PackingService } from '../packing/packing.service';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { localParts } from '../timezoneService';
|
||||
import { localParts } from '../../nest/common/timezoneService';
|
||||
import type { AirtrailAirport, AirtrailFlightRaw, AirtrailNamedCode } from './airtrailClient';
|
||||
import type { AirtrailFlight } from '@trek/shared';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { AirtrailFlight } from '@trek/shared';
|
||||
import { db } from '../../db/database';
|
||||
import { maybe_encrypt_api_key, decrypt_api_key } from '../apiKeyCrypto';
|
||||
import { maybe_encrypt_api_key, decrypt_api_key } from '../../nest/common/crypto/apiKeyCrypto';
|
||||
import { checkSsrf } from '../../utils/ssrfGuard';
|
||||
import { writeAudit } from '../../nest/audit/audit.bridge';
|
||||
import { AirtrailAuthError, AirtrailCreds, AirtrailRequestError, listFlights } from './airtrailClient';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { db, canAccessTrip } from '../db/database';
|
||||
import { avatarUrl } from './avatarUrl';
|
||||
import { avatarUrl } from '../nest/common/avatarUrl';
|
||||
import type { Journey, JourneyEntry, JourneyPhoto, JourneyContributor } from '../types';
|
||||
import { broadcastToUser } from '../websocket';
|
||||
import {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { maybe_encrypt_api_key, decrypt_api_key } from './apiKeyCrypto';
|
||||
import { maybe_encrypt_api_key, decrypt_api_key } from '../nest/common/crypto/apiKeyCrypto';
|
||||
|
||||
/**
|
||||
* Shared types + helpers for the `llm_parsing` addon configuration.
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Readable } from 'node:stream';
|
||||
import { Response } from 'express';
|
||||
import { canAccessTrip, db } from "../../db/database";
|
||||
import { safeFetch, SsrfBlockedError, type SafeFetchOptions } from '../../utils/ssrfGuard';
|
||||
import { decrypt_api_key } from '../apiKeyCrypto';
|
||||
import { decrypt_api_key } from '../../nest/common/crypto/apiKeyCrypto';
|
||||
|
||||
// helpers for handling return types
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Response } from 'express';
|
||||
import { db } from '../../db/database';
|
||||
import { maybe_encrypt_api_key, decrypt_api_key } from '../apiKeyCrypto';
|
||||
import { maybe_encrypt_api_key, decrypt_api_key } from '../../nest/common/crypto/apiKeyCrypto';
|
||||
import { checkSsrf, safeFetch } from '../../utils/ssrfGuard';
|
||||
import { writeAudit } from '../../nest/audit/audit.bridge';
|
||||
import { addTripPhotos} from './unifiedService';
|
||||
|
||||
@@ -7,7 +7,7 @@ import { streamImmichAsset, fetchImmichThumbnailBytes, getAssetInfo as getImmich
|
||||
import { streamSynologyAsset, fetchSynologyThumbnailBytes, getSynologyAssetInfo } from './synologyService';
|
||||
import type { ServiceResult, AssetInfo } from './helpersService';
|
||||
import { fail, success } from './helpersService';
|
||||
import { encrypt_api_key, decrypt_api_key } from '../apiKeyCrypto';
|
||||
import { encrypt_api_key, decrypt_api_key } from '../../nest/common/crypto/apiKeyCrypto';
|
||||
import * as photoCache from './trekPhotoCache';
|
||||
import { ensureLocalThumbnail } from './thumbnailService';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
import { Response } from 'express';
|
||||
import { db } from '../../db/database';
|
||||
import { decrypt_api_key, encrypt_api_key, maybe_encrypt_api_key } from '../apiKeyCrypto';
|
||||
import { decrypt_api_key, encrypt_api_key, maybe_encrypt_api_key } from '../../nest/common/crypto/apiKeyCrypto';
|
||||
import { safeFetch, SsrfBlockedError, checkSsrf } from '../../utils/ssrfGuard';
|
||||
import { addTripPhotos } from './unifiedService';
|
||||
import {
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
Selection,
|
||||
} from './helpersService';
|
||||
import { getOrCreateTrekPhoto, deleteTrekPhotoIfOrphan } from './photoResolverService';
|
||||
import { encrypt_api_key } from '../apiKeyCrypto';
|
||||
import { encrypt_api_key } from '../../nest/common/crypto/apiKeyCrypto';
|
||||
|
||||
|
||||
function _providers(): Array<{id: string; enabled: boolean}> {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { getAppUrl, readEnv } from '../app-config';
|
||||
import { db } from '../db/database';
|
||||
import { checkSsrf, createPinnedDispatcher } from '../utils/ssrfGuard';
|
||||
import { decrypt_api_key } from './apiKeyCrypto';
|
||||
import { decrypt_api_key } from '../nest/common/crypto/apiKeyCrypto';
|
||||
import { logInfo, logDebug, logError } from '../nest/audit/audit-log.logger';
|
||||
// ── Types ──────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { v4 as uuidv4 } from 'uuid';
|
||||
import { readEnv } from '../app-config';
|
||||
import { safeFetch } from '../utils/ssrfGuard';
|
||||
import { db } from '../db/database';
|
||||
import { decrypt_api_key } from './apiKeyCrypto';
|
||||
import { decrypt_api_key } from '../nest/common/crypto/apiKeyCrypto';
|
||||
|
||||
interface UnsplashSearchResponse {
|
||||
results?: {
|
||||
|
||||
@@ -45,7 +45,7 @@ vi.mock('../../src/db/database', () => ({
|
||||
db, canAccessTrip, isOwner: vi.fn(() => true), getPlaceWithTags: vi.fn(), closeDb: () => {}, reinitialize: () => {},
|
||||
}));
|
||||
vi.mock('../../src/websocket', () => ({ broadcast: vi.fn() }));
|
||||
vi.mock('../../src/services/demo', () => ({ isDemoEmail: vi.fn(() => false) }));
|
||||
vi.mock('../../src/nest/common/demo', () => ({ isDemoEmail: vi.fn(() => false) }));
|
||||
|
||||
import { PermissionsService } from '../../src/nest/permissions/permissions.service';
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ vi.mock('../../src/nest/notifications/notifications.bridge', () => ({ send: vi.f
|
||||
// The audit domain is DI-native now: writeAudit runs for real against the temp
|
||||
// db's audit_log table; only the file logger is silenced.
|
||||
vi.mock('../../src/nest/audit/audit-log.logger', () => ({ LOG_LEVEL: 'error', logInfo: vi.fn(), logDebug: vi.fn(), logError: vi.fn(), logWarn: vi.fn() }));
|
||||
vi.mock('../../src/services/demo', () => ({ isDemoEmail: vi.fn(() => false) }));
|
||||
vi.mock('../../src/nest/common/demo', () => ({ isDemoEmail: vi.fn(() => false) }));
|
||||
|
||||
import { PermissionsService } from '../../src/nest/permissions/permissions.service';
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
|
||||
import Database from 'better-sqlite3';
|
||||
import bcrypt from 'bcryptjs';
|
||||
import { encryptMfaSecret } from '../../src/services/mfaCrypto';
|
||||
import { encrypt_api_key } from '../../src/services/apiKeyCrypto';
|
||||
import { encryptMfaSecret } from '../../src/nest/common/crypto/mfaCrypto';
|
||||
import { encrypt_api_key } from '../../src/nest/common/crypto/apiKeyCrypto';
|
||||
|
||||
let _userSeq = 0;
|
||||
let _tripSeq = 0;
|
||||
|
||||
@@ -704,7 +704,7 @@ describe('Synology auth checks', () => {
|
||||
// ── Album sync ────────────────────────────────────────────────────────────────
|
||||
|
||||
import { addAlbumLink } from '../helpers/factories';
|
||||
import { encrypt_api_key } from '../../src/services/apiKeyCrypto';
|
||||
import { encrypt_api_key } from '../../src/nest/common/crypto/apiKeyCrypto';
|
||||
|
||||
describe('Synology syncSynologyAlbumLink', () => {
|
||||
it('SYNO-050 — POST sync happy path: trip owner with album link saves photos to DB', async () => {
|
||||
@@ -1187,7 +1187,7 @@ describe('Synology SSRF blocked error handling', () => {
|
||||
// ── Passphrase persistence fixes ─────────────────────────────────────────────
|
||||
|
||||
import { getOrCreateTrekPhoto, deleteTrekPhotoIfOrphan } from '../../src/services/memories/photoResolverService';
|
||||
import { decrypt_api_key } from '../../src/services/apiKeyCrypto';
|
||||
import { decrypt_api_key } from '../../src/nest/common/crypto/apiKeyCrypto';
|
||||
|
||||
describe('trek_photos passphrase healing (SYNO-090)', () => {
|
||||
it('SYNO-090 — getOrCreateTrekPhoto overwrites an existing bad passphrase when a new one is supplied', () => {
|
||||
|
||||
@@ -35,7 +35,7 @@ vi.mock('../../../src/config', () => ({
|
||||
ENCRYPTION_KEY: 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2',
|
||||
updateJwtSecret: () => {},
|
||||
}));
|
||||
vi.mock('../../../src/services/apiKeyCrypto', () => ({
|
||||
vi.mock('../../../src/nest/common/crypto/apiKeyCrypto', () => ({
|
||||
encrypt_api_key: (v: string) => v,
|
||||
decrypt_api_key: (v: string) => v,
|
||||
maybe_encrypt_api_key: (v: string) => v,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { HttpException } from '@nestjs/common';
|
||||
import type { Request } from 'express';
|
||||
|
||||
vi.mock('../../../src/middleware/auth', () => ({ extractToken: vi.fn(), verifyJwtAndLoadUser: vi.fn() }));
|
||||
vi.mock('../../../src/services/cookie', () => ({ setAuthCookie: vi.fn() }));
|
||||
vi.mock('../../../src/nest/common/cookie', () => ({ setAuthCookie: vi.fn() }));
|
||||
vi.mock('../../../src/nest/audit/client-ip', () => ({ getClientIp: vi.fn(() => '1.2.3.4') }));
|
||||
vi.mock('../../../src/nest/audit/audit-log.logger', () => ({ LOG_LEVEL: 'error', logInfo: vi.fn(), logDebug: vi.fn(), logError: vi.fn(), logWarn: vi.fn() }));
|
||||
|
||||
@@ -35,7 +35,7 @@ import { CurrentUser } from '../../../src/nest/auth/current-user.decorator';
|
||||
import { extractToken, verifyJwtAndLoadUser } from '../../../src/middleware/auth';
|
||||
import type { AuthService } from '../../../src/nest/auth/auth.service';
|
||||
import type { PasskeyService } from '../../../src/nest/auth/passkey.service';
|
||||
import { setAuthCookie } from '../../../src/services/cookie';
|
||||
import { setAuthCookie } from '../../../src/nest/common/cookie';
|
||||
import type { AuditService } from '../../../src/nest/audit/audit.service';
|
||||
import type { User } from '../../../src/types';
|
||||
|
||||
|
||||
@@ -4,14 +4,14 @@ import type { Request, Response } from 'express';
|
||||
|
||||
vi.mock('../../../src/nest/audit/client-ip', () => ({ getClientIp: vi.fn(() => '1.2.3.4') }));
|
||||
vi.mock('../../../src/nest/audit/audit-log.logger', () => ({ LOG_LEVEL: 'error', logInfo: vi.fn(), logDebug: vi.fn(), logError: vi.fn(), logWarn: vi.fn() }));
|
||||
vi.mock('../../../src/services/demo', () => ({ isDemoEmail: vi.fn(() => false) }));
|
||||
vi.mock('../../../src/nest/common/demo', () => ({ isDemoEmail: vi.fn(() => false) }));
|
||||
|
||||
import { AuthPublicController } from '../../../src/nest/auth/auth-public.controller';
|
||||
import { AuthController } from '../../../src/nest/auth/auth.controller';
|
||||
import { RateLimitService } from '../../../src/nest/common/rate-limit.service';
|
||||
import type { AuthService } from '../../../src/nest/auth/auth.service';
|
||||
import type { AuditService } from '../../../src/nest/audit/audit.service';
|
||||
import { isDemoEmail } from '../../../src/services/demo';
|
||||
import { isDemoEmail } from '../../../src/nest/common/demo';
|
||||
import type { User } from '../../../src/types';
|
||||
|
||||
const user = { id: 1, username: 'u', role: 'user', email: 'u@example.test' } as User;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
|
||||
vi.mock('../../../src/config', () => ({ JWT_SECRET: 'test-secret', ENCRYPTION_KEY: '0'.repeat(64) }));
|
||||
vi.mock('../../../src/services/apiKeyCrypto', () => ({
|
||||
vi.mock('../../../src/nest/common/crypto/apiKeyCrypto', () => ({
|
||||
decrypt_api_key: vi.fn((v) => v),
|
||||
maybe_encrypt_api_key: vi.fn((v) => v),
|
||||
encrypt_api_key: vi.fn((v) => v),
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
generateBackupCodes,
|
||||
parseBackupCodeHashes,
|
||||
} from '../../../src/nest/auth/auth.helpers';
|
||||
import { avatarUrl } from '../../../src/services/avatarUrl';
|
||||
import { avatarUrl } from '../../../src/nest/common/avatarUrl';
|
||||
import type { User } from '../../../src/types';
|
||||
|
||||
// ── utcSuffix ────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -43,11 +43,11 @@ vi.mock('../../../src/config', () => ({
|
||||
SESSION_DURATION_REMEMBER_SECONDS: 2592000,
|
||||
updateJwtSecret: () => {},
|
||||
}));
|
||||
vi.mock('../../../src/services/mfaCrypto', () => ({
|
||||
vi.mock('../../../src/nest/common/crypto/mfaCrypto', () => ({
|
||||
encryptMfaSecret: vi.fn((s) => `enc:${s}`),
|
||||
decryptMfaSecret: vi.fn((s: string) => s.replace('enc:', '')),
|
||||
}));
|
||||
vi.mock('../../../src/services/apiKeyCrypto', () => ({
|
||||
vi.mock('../../../src/nest/common/crypto/apiKeyCrypto', () => ({
|
||||
decrypt_api_key: vi.fn((v) => v),
|
||||
maybe_encrypt_api_key: vi.fn((v) => v),
|
||||
mask_stored_api_key: vi.fn((v: string | null | undefined) => (v ? '••••••••' : null)),
|
||||
|
||||
@@ -57,7 +57,7 @@ import { createTables } from '../../../src/db/schema';
|
||||
import { runMigrations } from '../../../src/db/migrations';
|
||||
import { resetTestDb } from '../../helpers/test-db';
|
||||
import { createUser, createTrip } from '../../helpers/factories';
|
||||
import { avatarUrl } from '../../../src/services/avatarUrl';
|
||||
import { avatarUrl } from '../../../src/nest/common/avatarUrl';
|
||||
import { DatabaseService } from '../../../src/nest/database/database.service';
|
||||
import { PermissionsService } from '../../../src/nest/permissions/permissions.service';
|
||||
import { CollabService } from '../../../src/nest/collab/collab.service';
|
||||
|
||||
@@ -5,14 +5,14 @@ import os from 'os';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
|
||||
vi.mock('../../../src/services/demo', () => ({ isDemoEmail: vi.fn(() => false) }));
|
||||
vi.mock('../../../src/nest/common/demo', () => ({ isDemoEmail: vi.fn(() => false) }));
|
||||
|
||||
import { FilesController } from '../../../src/nest/files/files.controller';
|
||||
import { FilesDownloadController } from '../../../src/nest/files/files-download.controller';
|
||||
import { PhotosController } from '../../../src/nest/photos/photos.controller';
|
||||
import type { FilesService } from '../../../src/nest/files/files.service';
|
||||
import type { PhotosService } from '../../../src/nest/photos/photos.service';
|
||||
import { isDemoEmail } from '../../../src/services/demo';
|
||||
import { isDemoEmail } from '../../../src/nest/common/demo';
|
||||
import type { User } from '../../../src/types';
|
||||
|
||||
const user = { id: 1, username: 'u', role: 'user', email: 'u@example.test' } as User;
|
||||
|
||||
@@ -79,7 +79,7 @@ vi.mock('../../../src/utils/ssrfGuard', () => {
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('../../../src/services/apiKeyCrypto', () => ({
|
||||
vi.mock('../../../src/nest/common/crypto/apiKeyCrypto', () => ({
|
||||
decrypt_api_key: (v: string | null) => v,
|
||||
}));
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ vi.mock('../../../src/config', () => ({
|
||||
ENCRYPTION_KEY: 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2',
|
||||
updateJwtSecret: () => {},
|
||||
}));
|
||||
vi.mock('../../../src/services/apiKeyCrypto', () => ({
|
||||
vi.mock('../../../src/nest/common/crypto/apiKeyCrypto', () => ({
|
||||
decrypt_api_key: (v: string | null) => v,
|
||||
maybe_encrypt_api_key: (v: string) => v,
|
||||
encrypt_api_key: (v: string) => v,
|
||||
|
||||
@@ -51,11 +51,11 @@ vi.mock('../../../src/config', () => ({
|
||||
// as auth.service.test.ts) — services/tripMembership is deliberately NOT
|
||||
// mocked: OIDC-SVC-045 asserts the real trip_members row a trip-bound invite
|
||||
// creates.
|
||||
vi.mock('../../../src/services/mfaCrypto', () => ({
|
||||
vi.mock('../../../src/nest/common/crypto/mfaCrypto', () => ({
|
||||
encryptMfaSecret: vi.fn((s) => `enc:${s}`),
|
||||
decryptMfaSecret: vi.fn((s: string) => s.replace('enc:', '')),
|
||||
}));
|
||||
vi.mock('../../../src/services/apiKeyCrypto', () => ({
|
||||
vi.mock('../../../src/nest/common/crypto/apiKeyCrypto', () => ({
|
||||
decrypt_api_key: vi.fn((v) => v),
|
||||
maybe_encrypt_api_key: vi.fn((v) => v),
|
||||
mask_stored_api_key: vi.fn((v: string | null | undefined) => (v ? '••••••••' : null)),
|
||||
@@ -78,8 +78,8 @@ vi.mock('../../../src/app-config', async (importOriginal) => {
|
||||
});
|
||||
|
||||
const { setAuthCookieMock } = vi.hoisted(() => ({ setAuthCookieMock: vi.fn() }));
|
||||
vi.mock('../../../src/services/cookie', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('../../../src/services/cookie')>();
|
||||
vi.mock('../../../src/nest/common/cookie', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('../../../src/nest/common/cookie')>();
|
||||
return { ...actual, setAuthCookie: setAuthCookieMock };
|
||||
});
|
||||
|
||||
|
||||
@@ -43,11 +43,11 @@ vi.mock('../../../src/config', () => ({
|
||||
|
||||
// Construction insurance for the injected AuthService's import graph (same set
|
||||
// as oidc.service.test.ts / auth.service.test.ts).
|
||||
vi.mock('../../../src/services/mfaCrypto', () => ({
|
||||
vi.mock('../../../src/nest/common/crypto/mfaCrypto', () => ({
|
||||
encryptMfaSecret: vi.fn((s) => `enc:${s}`),
|
||||
decryptMfaSecret: vi.fn((s: string) => s.replace('enc:', '')),
|
||||
}));
|
||||
vi.mock('../../../src/services/apiKeyCrypto', () => ({
|
||||
vi.mock('../../../src/nest/common/crypto/apiKeyCrypto', () => ({
|
||||
decrypt_api_key: vi.fn((v) => v),
|
||||
maybe_encrypt_api_key: vi.fn((v) => v),
|
||||
mask_stored_api_key: vi.fn((v: string | null | undefined) => (v ? '••••••••' : null)),
|
||||
|
||||
@@ -35,7 +35,7 @@ vi.mock('../../../src/config', () => ({
|
||||
}));
|
||||
|
||||
// Passthrough crypto — value comes back unchanged for most tests
|
||||
vi.mock('../../../src/services/apiKeyCrypto', () => ({
|
||||
vi.mock('../../../src/nest/common/crypto/apiKeyCrypto', () => ({
|
||||
maybe_encrypt_api_key: (v: string) => v,
|
||||
decrypt_api_key: (v: string) => v,
|
||||
}));
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { Request } from 'express';
|
||||
vi.mock('../../../src/nest/audit/client-ip', () => ({ getClientIp: vi.fn(() => '1.2.3.4') }));
|
||||
vi.mock('../../../src/nest/audit/audit-log.logger', () => ({ LOG_LEVEL: 'error', logInfo: vi.fn(), logDebug: vi.fn(), logError: vi.fn(), logWarn: vi.fn() }));
|
||||
const { isDemoEmail } = vi.hoisted(() => ({ isDemoEmail: vi.fn(() => false) }));
|
||||
vi.mock('../../../src/services/demo', () => ({ isDemoEmail }));
|
||||
vi.mock('../../../src/nest/common/demo', () => ({ isDemoEmail }));
|
||||
// Mock the Unsplash cover internalisation so the controller test never hits the
|
||||
// network; isUnsplashCoverUrl keeps its real (host-based) logic.
|
||||
vi.mock('../../../src/services/unsplashService', () => ({
|
||||
|
||||
@@ -148,7 +148,7 @@ const packingStub = {
|
||||
deleteBag: vi.fn((_tid: number | string, bagId: string) => Number(bagId) !== 404),
|
||||
setBagMembers: vi.fn((_tid: number | string, bagId: string, userIds: number[]) => (Number(bagId) === 404 ? null : userIds.map((u) => ({ user_id: u })))),
|
||||
} as unknown as PackingService;
|
||||
vi.mock('../../../src/services/conflictResult', () => ({ isUpdateConflict: (r: unknown) => !!(r as { conflict?: boolean })?.conflict }));
|
||||
vi.mock('../../../src/nest/common/conflictResult', () => ({ isUpdateConflict: (r: unknown) => !!(r as { conflict?: boolean })?.conflict }));
|
||||
vi.mock('../../../src/services/weatherService', () => ({ getWeather: vi.fn(async (lat: string, lng: string) => ({ lat, lng, temp: 20 })) }));
|
||||
const categoriesStub = { list: vi.fn(() => [{ id: 1, name: 'Food' }]) } as unknown as CategoriesService;
|
||||
const tagsStub = {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
|
||||
vi.mock('../../../src/services/apiKeyCrypto', () => ({
|
||||
vi.mock('../../../src/nest/common/crypto/apiKeyCrypto', () => ({
|
||||
encrypt_api_key: (v: unknown) => (typeof v === 'string' ? `enc:${v}` : v),
|
||||
decrypt_api_key: (v: unknown) => (typeof v === 'string' && v.startsWith('enc:') ? v.slice(4) : v),
|
||||
}));
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
|
||||
// Reversible crypto stub so we can assert encrypt-at-rest without a real key env.
|
||||
vi.mock('../../../src/services/apiKeyCrypto', () => ({
|
||||
vi.mock('../../../src/nest/common/crypto/apiKeyCrypto', () => ({
|
||||
maybe_encrypt_api_key: (v: unknown) => (typeof v === 'string' ? `enc:${v}` : v),
|
||||
decrypt_api_key: (v: unknown) => (typeof v === 'string' && v.startsWith('enc:') ? v.slice(4) : v),
|
||||
}));
|
||||
|
||||
@@ -7,7 +7,7 @@ vi.mock('../../../src/config', () => ({
|
||||
updateJwtSecret: () => {},
|
||||
}));
|
||||
|
||||
import { encrypt_api_key, decrypt_api_key, maybe_encrypt_api_key } from '../../../src/services/apiKeyCrypto';
|
||||
import { encrypt_api_key, decrypt_api_key, maybe_encrypt_api_key } from '../../../src/nest/common/crypto/apiKeyCrypto';
|
||||
|
||||
describe('apiKeyCrypto', () => {
|
||||
const PLAINTEXT_KEY = 'my-secret-api-key-12345';
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* an OIDC `picture` claim (#1399); the helper resolves both to a renderable src.
|
||||
*/
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { avatarUrl } from '../../../src/services/avatarUrl';
|
||||
import { avatarUrl } from '../../../src/nest/common/avatarUrl';
|
||||
|
||||
describe('avatarUrl', () => {
|
||||
it('AVATAR-URL-001: prefixes an uploaded file name with the avatars path', () => {
|
||||
|
||||
@@ -16,7 +16,7 @@ const { testDb, dbMock } = vi.hoisted(() => {
|
||||
});
|
||||
vi.mock('../../../src/db/database', () => dbMock);
|
||||
vi.mock('../../../src/config', () => ({ JWT_SECRET: 'x'.repeat(40), ENCRYPTION_KEY: 'a'.repeat(64), updateJwtSecret: () => {} }));
|
||||
vi.mock('../../../src/services/apiKeyCrypto', () => ({ decrypt_api_key: (v: string) => v, maybe_encrypt_api_key: (v: string) => v, encrypt_api_key: (v: string) => v }));
|
||||
vi.mock('../../../src/nest/common/crypto/apiKeyCrypto', () => ({ decrypt_api_key: (v: string) => v, maybe_encrypt_api_key: (v: string) => v, encrypt_api_key: (v: string) => v }));
|
||||
const { sendMailMock } = vi.hoisted(() => ({ sendMailMock: vi.fn().mockResolvedValue({ accepted: ['a@b.c'] }) }));
|
||||
vi.mock('nodemailer', () => ({ default: { createTransport: vi.fn(() => ({ sendMail: sendMailMock, verify: vi.fn() })) } }));
|
||||
vi.stubGlobal('fetch', vi.fn());
|
||||
|
||||
@@ -38,7 +38,7 @@ import { createTables } from '../../../src/db/schema';
|
||||
import { runMigrations } from '../../../src/db/migrations';
|
||||
import { resetTestDb } from '../../helpers/test-db';
|
||||
import { createUser, createTrip } from '../../helpers/factories';
|
||||
import { isUpdateConflict } from '../../../src/services/conflictResult';
|
||||
import { isUpdateConflict } from '../../../src/nest/common/conflictResult';
|
||||
import { DatabaseService } from '../../../src/nest/database/database.service';
|
||||
import { PackingService } from '../../../src/nest/packing/packing.service';
|
||||
import { PlacesService } from '../../../src/nest/places/places.service';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
|
||||
import { cookieOptions } from '../../../src/services/cookie';
|
||||
import { cookieOptions } from '../../../src/nest/common/cookie';
|
||||
import { SESSION_DURATION_MS, SESSION_DURATION_REMEMBER_MS } from '../../../src/config';
|
||||
|
||||
describe('cookieOptions', () => {
|
||||
|
||||
@@ -7,7 +7,7 @@ vi.mock('../../../src/config', () => ({
|
||||
updateJwtSecret: () => {},
|
||||
}));
|
||||
|
||||
import { encryptMfaSecret, decryptMfaSecret } from '../../../src/services/mfaCrypto';
|
||||
import { encryptMfaSecret, decryptMfaSecret } from '../../../src/nest/common/crypto/mfaCrypto';
|
||||
|
||||
describe('mfaCrypto', () => {
|
||||
const TOTP_SECRET = 'JBSWY3DPEHPK3PXP'; // typical base32 TOTP secret
|
||||
|
||||
@@ -26,7 +26,7 @@ vi.mock('../../../src/config', () => ({
|
||||
ENCRYPTION_KEY: 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2',
|
||||
updateJwtSecret: () => {},
|
||||
}));
|
||||
vi.mock('../../../src/services/apiKeyCrypto', () => ({
|
||||
vi.mock('../../../src/nest/common/crypto/apiKeyCrypto', () => ({
|
||||
decrypt_api_key: (v: string | null) => v,
|
||||
maybe_encrypt_api_key: (v: string) => v,
|
||||
encrypt_api_key: (v: string) => v,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { describe, it, expect, vi, afterEach, afterAll, beforeEach } from 'vites
|
||||
vi.mock('../../../src/db/database', () => ({
|
||||
db: { prepare: () => ({ get: vi.fn(() => undefined), all: vi.fn(() => []) }) },
|
||||
}));
|
||||
vi.mock('../../../src/services/apiKeyCrypto', () => ({
|
||||
vi.mock('../../../src/nest/common/crypto/apiKeyCrypto', () => ({
|
||||
decrypt_api_key: vi.fn((v) => v),
|
||||
maybe_encrypt_api_key: vi.fn((v) => v),
|
||||
}));
|
||||
|
||||
@@ -29,7 +29,7 @@ vi.mock('../../../src/config', () => ({
|
||||
ENCRYPTION_KEY: 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2',
|
||||
updateJwtSecret: () => {},
|
||||
}));
|
||||
vi.mock('../../../src/services/apiKeyCrypto', () => ({
|
||||
vi.mock('../../../src/nest/common/crypto/apiKeyCrypto', () => ({
|
||||
encrypt_api_key: (v: string) => v,
|
||||
decrypt_api_key: (v: string) => v,
|
||||
maybe_encrypt_api_key: (v: string) => v,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { validatePassword } from '../../../src/services/passwordPolicy';
|
||||
import { validatePassword } from '../../../src/nest/common/passwordPolicy';
|
||||
|
||||
describe('validatePassword', () => {
|
||||
// AUTH-006 — Registration with weak password
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { localParts, resolveTimeZone } from '../../../src/services/timezoneService';
|
||||
import { localParts, resolveTimeZone } from '../../../src/nest/common/timezoneService';
|
||||
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ vi.mock('../../../src/utils/ssrfGuard', () => ({ safeFetch }));
|
||||
vi.mock('../../../src/db/database', () => ({
|
||||
db: { prepare: () => ({ get: mockDbGet, all: vi.fn(() => []), run: vi.fn() }) },
|
||||
}));
|
||||
vi.mock('../../../src/services/apiKeyCrypto', () => ({ decrypt_api_key: (v: string | null) => v }));
|
||||
vi.mock('../../../src/nest/common/crypto/apiKeyCrypto', () => ({ decrypt_api_key: (v: string | null) => v }));
|
||||
|
||||
import { searchUnsplashPhotos, getUnsplashKey, saveUnsplashCover, isUnsplashCoverUrl } from '../../../src/services/unsplashService';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user