diff --git a/prisma/migrations/20250312230736_add_metadata_providers_to_appconfig/migration.sql b/prisma/migrations/20250312230736_add_metadata_providers_to_appconfig/migration.sql new file mode 100644 index 0000000..54f1097 --- /dev/null +++ b/prisma/migrations/20250312230736_add_metadata_providers_to_appconfig/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "ApplicationSettings" ADD COLUMN "metadataProviders" TEXT[]; diff --git a/prisma/schema/app.prisma b/prisma/schema/app.prisma index a949749..aff80ce 100644 --- a/prisma/schema/app.prisma +++ b/prisma/schema/app.prisma @@ -2,6 +2,7 @@ model ApplicationSettings { timestamp DateTime @id @default(now()) enabledAuthencationMechanisms AuthMec[] + metadataProviders String[] } enum Platform { diff --git a/server/internal/config/application-configuration.ts b/server/internal/config/application-configuration.ts index 2ed585f..ac4f7c4 100644 --- a/server/internal/config/application-configuration.ts +++ b/server/internal/config/application-configuration.ts @@ -3,44 +3,29 @@ import prisma from "../db/database"; class ApplicationConfiguration { // Reference to the currently selected application configuration - private currentApplicationSettings: ApplicationSettings; + private currentApplicationSettings: ApplicationSettings = { + timestamp: new Date(), + enabledAuthencationMechanisms: [], + metadataProviders: [], + }; private applicationStateProxy: object; private dirty: boolean = false; private dirtyPromise: Promise | undefined = undefined; constructor() { - // @ts-expect-error - this.currentApplicationSettings = {}; this.applicationStateProxy = {}; } - private buildApplicationSettingsProxy() { - const appConfig = this; - const proxy = new Proxy(this.currentApplicationSettings, { - get: (target, key: keyof ApplicationSettings) => { - return appConfig.currentApplicationSettings[key]; - }, - set: (target, key: keyof ApplicationSettings, value) => { - if (JSON.stringify(value) === JSON.stringify(appConfig.currentApplicationSettings[key])) return true; - appConfig.currentApplicationSettings[key] = value; + private async save() { + const deepAppConfigCopy: Omit & { + timestamp?: Date; + } = JSON.parse(JSON.stringify(this.currentApplicationSettings)); - const deepAppConfigCopy: Omit & { - timestamp?: Date; - } = JSON.parse(JSON.stringify(appConfig.currentApplicationSettings)); + delete deepAppConfigCopy["timestamp"]; - delete deepAppConfigCopy["timestamp"]; - - appConfig.dirty = true; - appConfig.dirtyPromise = prisma.applicationSettings.create({ - data: deepAppConfigCopy, - }); - return true; - }, - deleteProperty: (target, key: keyof ApplicationSettings) => { - return false; - }, + await prisma.applicationSettings.create({ + data: deepAppConfigCopy, }); - this.applicationStateProxy = proxy; } // Default application configuration @@ -48,11 +33,11 @@ class ApplicationConfiguration { const initialState = await prisma.applicationSettings.create({ data: { enabledAuthencationMechanisms: [AuthMec.Simple], + metadataProviders: [], }, }); this.currentApplicationSettings = initialState; - this.buildApplicationSettingsProxy(); } async pullConfiguration() { @@ -65,18 +50,22 @@ class ApplicationConfiguration { if (!latestState) throw new Error("No application configuration to pull"); this.currentApplicationSettings = latestState; - this.buildApplicationSettingsProxy(); } - async waitForWrite() { - if (this.dirty) { - await this.dirtyPromise; + async set( + key: T, + value: ApplicationSettings[T] + ) { + if (this.currentApplicationSettings[key] !== value) { + this.currentApplicationSettings[key] = value; + + await this.save(); } } - useApplicationConfiguration(): ApplicationSettings { - return this.applicationStateProxy as ApplicationSettings; + get(key: T): ApplicationSettings[T] { + return this.currentApplicationSettings[key]; } } -export const applicationSettings = new ApplicationConfiguration(); \ No newline at end of file +export const applicationSettings = new ApplicationConfiguration(); diff --git a/server/internal/metadata/index.ts b/server/internal/metadata/index.ts index 5fcdada..ea76df6 100644 --- a/server/internal/metadata/index.ts +++ b/server/internal/metadata/index.ts @@ -46,6 +46,18 @@ export class MetadataHandler { this.providers.push(provider, priority); } + + /** + * Returns provider IDs, used to save to applicationConfig + * @returns The provider IDs in order, missing manual + */ + fetchProviderIdsInOrder() { + return this.providers + .values() + .map((e) => e.id()) + .filter((e) => e !== "manual"); + } + async search(query: string) { const promises: Promise[] = []; for (const provider of this.providers.values()) { @@ -236,17 +248,3 @@ export class MetadataHandler { export const metadataHandler = new MetadataHandler(); export default metadataHandler; - -export const enabledMedadataProviders: string[] = []; -const metadataProviders = [GiantBombProvider, ManualMetadataProvider]; - -for(const provider of metadataProviders){ - try { - const prov = new provider; - metadataHandler.addProvider(prov); - enabledMedadataProviders.push(prov.id()); - console.log(`enabled metadata provider: ${prov.name()}`) - }catch(e){ - console.error(`skipping metadata provider setup: ${e}`); - } -} \ No newline at end of file diff --git a/server/internal/metadata/types.d.ts b/server/internal/metadata/types.d.ts index 6476d74..1b2432e 100644 --- a/server/internal/metadata/types.d.ts +++ b/server/internal/metadata/types.d.ts @@ -1,6 +1,6 @@ import { Developer, Publisher } from "@prisma/client"; -import { ObjectReference } from "../objects"; import { ObjectTransactionalHandler, TransactionDataType } from "../objects/transactional"; +import { ObjectReference } from "../objects/objectHandler"; export interface GameMetadataSearchResult { id: string; diff --git a/server/plugins/1.system-init.ts b/server/plugins/01.system-init.ts similarity index 90% rename from server/plugins/1.system-init.ts rename to server/plugins/01.system-init.ts index 65a066a..aebc802 100644 --- a/server/plugins/1.system-init.ts +++ b/server/plugins/01.system-init.ts @@ -8,10 +8,7 @@ export default defineNitroPlugin(async (nitro) => { } else { await applicationSettings.initialiseConfiguration(); } - - nitro.hooks.hookOnce("close", async () => { - await applicationSettings.waitForWrite(); - }); + console.log("initalised application config"); // Ensure system user exists // The system user owns any user-based code diff --git a/server/plugins/2.system-setup.ts b/server/plugins/02.system-setup.ts similarity index 100% rename from server/plugins/2.system-setup.ts rename to server/plugins/02.system-setup.ts diff --git a/server/plugins/03.metadata-init.ts b/server/plugins/03.metadata-init.ts new file mode 100644 index 0000000..862dfa3 --- /dev/null +++ b/server/plugins/03.metadata-init.ts @@ -0,0 +1,49 @@ +import { applicationSettings } from "../internal/config/application-configuration"; +import metadataHandler, { MetadataProvider } from "../internal/metadata"; +import { GiantBombProvider } from "../internal/metadata/giantbomb"; +import { ManualMetadataProvider } from "../internal/metadata/manual"; + +export default defineNitroPlugin(async (nitro) => { + const metadataProviders = [GiantBombProvider]; + + const providers: { [key: string]: MetadataProvider } = {}; + + for (const provider of metadataProviders) { + try { + const prov = new provider(); + const id = prov.id(); + providers[id] = prov; + + console.log(`enabled metadata provider: ${prov.name()}`); + } catch (e) { + console.warn(`skipping metadata provider setup: ${e}`); + } + } + + // Add providers based on their position in the application settings + const configuredProviderList = applicationSettings.get("metadataProviders"); + const max = configuredProviderList.length; + for (const [index, providerId] of configuredProviderList.entries()) { + const priority = max * 2 - index; // Offset by the length --- (max - index) + max + const provider = providers[providerId]; + if (!provider) { + console.warn(`failed to add existing metadata provider: ${providerId}`); + continue; + } + metadataHandler.addProvider(provider, priority); + delete providers[providerId]; + } + + // Add the rest with no position + for (const [providerId, provider] of Object.entries(providers)) { + metadataHandler.addProvider(provider); + } + + metadataHandler.addProvider(new ManualMetadataProvider(), -1000); + + // Update the applicatonConfig + await applicationSettings.set( + "metadataProviders", + metadataHandler.fetchProviderIdsInOrder() + ); +});