106 lines
3.6 KiB
TypeScript
106 lines
3.6 KiB
TypeScript
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.util";
|
|
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<TreeItemEditable & { match?: number }>;
|
|
|
|
const db = useDatabase();
|
|
const { ...cols } = getTableColumns(explorerContentTable);
|
|
const full = db.select(cols).from(explorerContentTable).all() as Record<string, any>[];
|
|
|
|
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 ?? [])]);
|
|
} |