/opt/canhelp/apps/api/src
NameSizeModeActions
lib/-0755rm
middleware/-0755rm
routes/-0755rm
auth.ts156000644editdlrm
db.ts1740644editdlrm
index.ts239420644editdlrm
redis.ts2490644editdlrm
seed.ts830180644editdlrm
socket.ts57540644editdlrm
sync-categories-from-prod.ts27920644editdlrm
translate.ts220300644editdlrm
Edit: /opt/canhelp/apps/api/src/sync-categories-from-prod.ts (2792B)
import 'dotenv/config' import { categories } from '@canhelp/db' import { db } from './db.js' import type { NodePgDatabase } from 'drizzle-orm/node-postgres' const PROD_CATEGORIES_URL = process.env.PROD_CATEGORIES_URL || 'https://api.canhelp.gr/api/categories' type RemoteCategory = { slug: string icon?: string | null names?: { el?: string | null en?: string | null ru?: string | null uk?: string | null } | null order?: number | null isActive?: boolean | null children?: RemoteCategory[] | null } function normalizeName(value: string | null | undefined, fallback: string): string { const v = (value ?? '').trim() return v || fallback } async function fetchProductionCategories(): Promise { const res = await fetch(PROD_CATEGORIES_URL) if (!res.ok) { throw new Error(`Failed to fetch ${PROD_CATEGORIES_URL}: ${res.status} ${res.statusText}`) } const data = await res.json() if (!Array.isArray(data)) { throw new Error('Unexpected categories payload: expected array') } return data as RemoteCategory[] } async function insertTree( tx: NodePgDatabase, nodes: RemoteCategory[], parentId: string | null, ): Promise { let inserted = 0 for (let index = 0; index < nodes.length; index += 1) { const node = nodes[index] if (!node?.slug || typeof node.slug !== 'string') continue const slug = node.slug.trim() if (!slug) continue const names = node.names ?? {} const fallback = slug.replace(/-/g, ' ') const [created] = await tx .insert(categories) .values({ slug, icon: (node.icon ?? '').trim() || '📦', namesEl: normalizeName(names.el, fallback), namesEn: normalizeName(names.en, fallback), namesRu: normalizeName(names.ru, fallback), namesUk: (names.uk ?? '').trim() || null, parentId, order: typeof node.order === 'number' ? node.order : index + 1, isActive: typeof node.isActive === 'boolean' ? node.isActive : true, }) .returning({ id: categories.id }) inserted += 1 const children = Array.isArray(node.children) ? node.children : [] if (children.length > 0) { inserted += await insertTree(tx, children, created.id) } } return inserted } async function main() { console.log(`Syncing categories from: ${PROD_CATEGORIES_URL}`) const tree = await fetchProductionCategories() const total = await db.transaction(async (tx) => { await tx.delete(categories) return insertTree(tx as unknown as NodePgDatabase, tree, null) }) console.log(`Done. Imported categories: ${total}`) } main() .then(() => process.exit(0)) .catch((error) => { console.error('Category sync failed:', error) process.exit(1) })