import { hasPermissions } from "#shared/auth.util"; import useDatabase from '~/composables/useDatabase'; import { explorerContentTable } from '~/db/schema'; import { project, type ProjectItem } from '~/schemas/project'; import { parsePath } from "#shared/general.utils"; import { eq, getTableColumns, sql } from "drizzle-orm"; import type { ExploreContent, TreeItem } from "~/types/content"; import type { TreeItemEditable } from "~/pages/explore/edit/index.vue"; export default defineEventHandler(async (e) => { const { user } = await getUserSession(e); if(!user || !hasPermissions(user.permissions, ['admin', 'editor'])) { throw createError({ statusCode: 401, statusText: 'Unauthorized' }); } const body = await readValidatedBody(e, project.safeParse); if(!body.success) { throw body.error; } const items = buildOrder(body.data) as Array; const db = useDatabase(); const { ...cols } = getTableColumns(explorerContentTable); const full = db.select(cols).from(explorerContentTable).all() as Record[]; for(let i = full.length - 1; i >= 0; i--) { const item = items.find(e => (e.path === '' ? [e.parent, parsePath(e.name === '' ? e.title : e.name)].filter(e => !!e).join('/') : e.path) === full[i].path); if(item) { item.match = i; full[i].include = true; } } db.transaction((tx) => { for(let i = 0; i < items.length; i++) { const item = items[i]; const old = full[item.match!]; const path = [item.parent, parsePath(item.name === '' ? item.title : item.name)].filter(e => !!e).join('/'); tx.insert(explorerContentTable).values({ path: item.path || path, owner: user.id, title: item.title, type: item.type, navigable: item.navigable, private: item.private, order: item.order, content: item.content ?? old?.content ?? null, }).onConflictDoUpdate({ set: { path: path, title: item.title, type: item.type, navigable: item.navigable, private: item.private, order: item.order, timestamp: new Date(), content: item.content ?? old?.content ?? null, }, target: explorerContentTable.path, }).run(); if(item.path !== path && !old) { tx.update(explorerContentTable).set({ content: sql`replace(${explorerContentTable.content}, ${sql.placeholder('old')}, ${sql.placeholder('new')})` }).prepare().run({ 'old': item.path, 'new': path }); } } for(let i = 0; i < full.length; i++) { if(full[i].include !== true) tx.delete(explorerContentTable).where(eq(explorerContentTable.path, full[i].path)).run(); } }); return items.map(e => ({ path: e.path, owner: e.owner, title: e.title, type: e.type, content: e.content, navigable: e.navigable, private: e.private, order: e.order, visit: e.visit, timestamp: e.timestamp, })) as ExploreContent[]; }); function buildOrder(items: ProjectItem[]): ProjectItem[] { items.forEach((e, i) => { e.order = i; if(e.children) e.children = buildOrder(e.children); }); return items.flatMap(e => [e, ...(e.children ?? [])]); }