import { Injectable, Logger } from "@nestjs/common"; import { FanavaranClientConfigService } from "src/fanavaran/fanavaran-client-config.service"; import { OfflineInquirySettingsViewDto, SystemSettingsResponseDto, UpdateOfflineInquirySettingsDto, UpdateSystemSettingsDto, } from "./dto/system-settings.dto"; import { SystemSettingsDbService } from "./entities/db-service/system-settings.db.service"; @Injectable() export class SystemSettingsService { private readonly logger = new Logger(SystemSettingsService.name); private cache: Record | null = null; private cacheAt = 0; private readonly cacheTtlMs = 15_000; constructor( private readonly db: SystemSettingsDbService, private readonly fanavaranClientConfigService: FanavaranClientConfigService, ) {} private async loadGlobal(): Promise> { const now = Date.now(); if (this.cache && now - this.cacheAt < this.cacheTtlMs) { return this.cache; } let doc = await this.db.findGlobal(); if (!doc) { doc = await this.db.upsertGlobal({ $set: { externalApis: { sandHubUseLiveApi: false }, offlineInquiry: { enabled: true }, }, }); this.logger.log( "Created default system_settings document (sandHubUseLiveApi=false, offlineInquiry.enabled=true)", ); } this.cache = doc; this.cacheAt = now; return doc; } private invalidateCache(): void { this.cache = null; this.cacheAt = 0; } private readOfflineInquiryEnabled(doc: Record): boolean { const offlineInquiry = doc.offlineInquiry as | { enabled?: boolean } | undefined; // Unset → enabled (preserve prior always-on seed behavior). return offlineInquiry?.enabled !== false; } /** * Whether SandHub/Tejarat live HTTP should run (`system_settings` collection). */ async isSandHubLiveEnabled(): Promise { const doc = await this.loadGlobal(); const externalApis = doc.externalApis as | { sandHubUseLiveApi?: boolean } | undefined; return externalApis?.sandHubUseLiveApi === true; } /** * Whether seeded offline plate inquiries may short-circuit live/mock inquiry. */ async isOfflineInquiryEnabled(): Promise { const doc = await this.loadGlobal(); return this.readOfflineInquiryEnabled(doc); } async getOfflineInquirySettings(): Promise { return { enabled: await this.isOfflineInquiryEnabled() }; } async getSettingsView(): Promise { const doc = await this.loadGlobal(); const externalApis = doc.externalApis as | { sandHubUseLiveApi?: boolean } | undefined; const sandHubUseLiveApi = externalApis?.sandHubUseLiveApi === true; return { key: String(doc.key ?? "global"), externalApis: { sandHubUseLiveApi }, offlineInquiry: { enabled: this.readOfflineInquiryEnabled(doc) }, sandHubMode: sandHubUseLiveApi ? "live" : "mock", }; } async updateSettings( body: UpdateSystemSettingsDto, ): Promise { const $set: Record = {}; if (body.externalApis?.sandHubUseLiveApi !== undefined) { $set["externalApis.sandHubUseLiveApi"] = body.externalApis.sandHubUseLiveApi; } if (body.offlineInquiry?.enabled !== undefined) { $set["offlineInquiry.enabled"] = body.offlineInquiry.enabled; } if (Object.keys($set).length > 0) { await this.db.upsertGlobal({ $set }); this.invalidateCache(); this.logger.log(`System settings updated: ${JSON.stringify($set)}`); } return this.getSettingsView(); } async updateOfflineInquirySettings( body: UpdateOfflineInquirySettingsDto, ): Promise { await this.db.upsertGlobal({ $set: { "offlineInquiry.enabled": body.enabled }, }); this.invalidateCache(); this.logger.log( `Offline inquiry master switch set to enabled=${body.enabled}`, ); return { enabled: body.enabled }; } /** * Re-read `fanavaranClientConfigs` into the in-memory profile cache. * Auth fingerprint changes invalidate cached Fanavaran tokens. */ async reloadFanavaranConfigCache(): Promise<{ ok: true; message: string }> { await this.fanavaranClientConfigService.reloadCache(); this.logger.log("Fanavaran client config cache reloaded via system-settings API"); return { ok: true, message: "Fanavaran client config cache reloaded", }; } }