68 lines
3.0 KiB
TypeScript
68 lines
3.0 KiB
TypeScript
// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
|
|
// The stores of the mariadb and mongodb profiles (spec §5.1; profiles/<name>/README.md), built once
|
|
// per web process beside the Postgres pair app.ts builds. One login, no row-level security to bind
|
|
// (AT-D20 is Postgres-only): the repository predicate is the tenant wall, which the adapters' shared
|
|
// contract suites prove. The queue is BullMQ on the linked Valkey, and a request only enqueues.
|
|
//
|
|
// `kit template:prune --profile` keeps this folder for mariadb and mongodb (and the chosen engine's
|
|
// folder); postgres and convex remove it with every `kit:profile-dialect` line.
|
|
|
|
import type { AppEnv } from "@app/config"
|
|
import type { AnyEntityDef, JobQueue } from "@conusai-kit/data"
|
|
import { createBullmqJobQueue } from "@conusai-kit/jobs-bullmq"
|
|
import { Redis } from "ioredis"
|
|
import type { EngineStore } from "../stores"
|
|
import { mariadbStore } from "./mariadb/index" // kit:profile-mariadb
|
|
import { mongodbStore } from "./mongodb/index" // kit:profile-mongodb
|
|
|
|
export interface DialectStores extends EngineStore {
|
|
/** The queue a request enqueues on; it connects on the first enqueue. */
|
|
queue(): JobQueue
|
|
close(): Promise<void>
|
|
}
|
|
|
|
/**
|
|
* The BullMQ key prefix of this app: `kit-jobs-<slug>`, from the project the platform injects, so
|
|
* two apps sharing one Valkey service never meet (the worker's `queueSlug` is the same function).
|
|
*/
|
|
export function queueSlug(env: Pick<AppEnv, "deployment">): string {
|
|
const id = env.deployment.CONUSAI_PROJECT_ID.toLowerCase().replace(/[^a-z0-9-]+/g, "-")
|
|
return `p-${id}`.replace(/-+$/, "")
|
|
}
|
|
|
|
type Collections = ReadonlyArray<{ readonly entity: string; readonly table: string }>
|
|
|
|
function engineStore(env: AppEnv, entities: readonly AnyEntityDef[], collections: Collections): EngineStore | null {
|
|
if (env.mariadb) return mariadbStore(env.mariadb.url, entities) // kit:profile-mariadb
|
|
if (env.mongodb) return mongodbStore(env.mongodb.url, entities, collections) // kit:profile-mongodb
|
|
return null
|
|
}
|
|
|
|
/** The stores of the mariadb or mongodb profile, or null on any other. */
|
|
export function dialectStores(
|
|
env: AppEnv,
|
|
entities: readonly AnyEntityDef[],
|
|
collections: Collections = [],
|
|
): DialectStores | null {
|
|
const store = engineStore(env, entities, collections)
|
|
if (!store) return null
|
|
const redisUrl = env.redis?.url
|
|
if (!redisUrl) throw new Error("the mariadb and mongodb profiles need REDIS_URL for their job queue")
|
|
let connection: Redis | undefined
|
|
let queue: ReturnType<typeof createBullmqJobQueue> | undefined
|
|
return {
|
|
...store,
|
|
queue: () => {
|
|
// BullMQ needs `maxRetriesPerRequest: null` on the connection it is given.
|
|
connection ??= new Redis(redisUrl, { maxRetriesPerRequest: null })
|
|
queue ??= createBullmqJobQueue({ connection, slug: queueSlug(env) })
|
|
return queue
|
|
},
|
|
close: async () => {
|
|
await queue?.close().catch(() => undefined)
|
|
await connection?.quit().catch(() => undefined)
|
|
await store.data.close()
|
|
},
|
|
}
|
|
}
|