// @ts-nocheck import { mutation, query } from "./_generated/server" import { v } from "convex/values" import { canonicalizeTenantDomain, manualVisibleForTenant, tenantDomainVariants, } from "../lib/manuals-tenant" const manualInput = v.object({ filename: v.string(), path: v.string(), manufacturer: v.string(), category: v.string(), size: v.optional(v.number()), lastModified: v.optional(v.number()), searchTerms: v.optional(v.array(v.string())), commonNames: v.optional(v.array(v.string())), thumbnailUrl: v.optional(v.string()), manualUrl: v.optional(v.string()), hasParts: v.optional(v.boolean()), assetSource: v.optional(v.string()), sourcePath: v.optional(v.string()), sourceSite: v.optional(v.string()), sourceDomain: v.optional(v.string()), siteVisibility: v.optional(v.array(v.string())), importBatch: v.optional(v.string()), }) export const list = query({ args: { domain: v.string(), }, handler: async (ctx, args) => { const tenantDomain = canonicalizeTenantDomain(args.domain) if (!tenantDomain) { return [] } const manuals = await ctx.db.query("manuals").collect() return manuals .filter((manual) => manualVisibleForTenant(manual, tenantDomain)) .sort((a, b) => a.filename.localeCompare(b.filename)) }, }) export const dashboard = query({ args: {}, handler: async (ctx) => { const manuals = await ctx.db.query("manuals").collect() const categories = await ctx.db.query("manualCategories").collect() const manufacturerMap = new Map() const categoryMap = new Map() for (const manual of manuals) { manufacturerMap.set( manual.manufacturer, (manufacturerMap.get(manual.manufacturer) ?? 0) + 1 ) categoryMap.set( manual.category, (categoryMap.get(manual.category) ?? 0) + 1 ) } return { missingManuals: { summary: { total_expected_models: manuals.length, models_missing_all: 0, models_partial: 0, }, }, qaData: [], metadata: manuals, structuredData: [], semanticIndex: { total_chunks: manuals.length, }, acquisitionList: { total_items: 0, high_priority: 0, medium_priority: 0, low_priority: 0, acquisition_list: [], }, nameMapping: categories, } }, }) export const upsertMany = mutation({ args: { manuals: v.array(manualInput), }, handler: async (ctx, args) => { const now = Date.now() const results = [] for (const manual of args.manuals) { const existing = await ctx.db .query("manuals") .withIndex("by_path", (q) => q.eq("path", manual.path)) .unique() if (existing) { await ctx.db.patch(existing._id, { ...manual, updatedAt: now, }) results.push(await ctx.db.get(existing._id)) } else { const id = await ctx.db.insert("manuals", { ...manual, createdAt: now, updatedAt: now, }) results.push(await ctx.db.get(id)) } } return results }, }) export const backfillTenantVisibility = mutation({ args: { domain: v.string(), dryRun: v.optional(v.boolean()), }, handler: async (ctx, args) => { const tenantDomain = canonicalizeTenantDomain(args.domain) if (!tenantDomain) { throw new Error("A valid tenant domain is required.") } const aliases = tenantDomainVariants(tenantDomain) const dryRun = Boolean(args.dryRun) const now = Date.now() const manuals = await ctx.db.query("manuals").collect() let patched = 0 let alreadyCovered = 0 for (const manual of manuals) { const visibilitySet = new Set( (manual.siteVisibility || []) .map((entry) => canonicalizeTenantDomain(entry)) .filter(Boolean) ) const sourceDomain = canonicalizeTenantDomain(manual.sourceDomain) const hasDomain = aliases.some((alias) => visibilitySet.has(alias)) || (sourceDomain ? aliases.includes(sourceDomain) : false) if (hasDomain) { alreadyCovered += 1 continue } const nextVisibility = Array.from( new Set([...visibilitySet, ...aliases]) ).sort() if (!dryRun) { await ctx.db.patch(manual._id, { sourceDomain: sourceDomain || tenantDomain, siteVisibility: nextVisibility, updatedAt: now, }) } patched += 1 } return { domain: tenantDomain, total: manuals.length, patched, alreadyCovered, dryRun, } }, })