Dashboard with YAML

This commit is contained in:
2026-04-19 18:03:14 +01:00
commit 9ed7f34c44
29 changed files with 26677 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
import { readDb } from '../../utils/yamlDb';
export default defineEventHandler(async (event) => {
const slugParam = event.context.params?.slug;
const slugParts = slugParam ? slugParam.split('/').filter(Boolean) : [];
const items = readDb();
let currentFolderId: string | null = null;
let currentFolder = null;
// Resolve folder path
for (let i = 0; i < slugParts.length; i++) {
const part = slugParts[i];
// Find folder with this slug and parentId
const folder = items.find(item => item.type === 'folder' && item.slug === part && item.parentId === currentFolderId);
if (!folder) {
throw createError({ statusCode: 404, statusMessage: 'Folder not found' });
}
currentFolder = folder;
currentFolderId = folder.id;
}
// Get children of current folder
const children = items
.filter(item => item.parentId === currentFolderId)
.sort((a, b) => a.order - b.order);
return {
folder: currentFolder,
items: children
};
});

View File

@@ -0,0 +1,28 @@
import { readDb, writeDb, DashboardItem } from '../../utils/yamlDb';
import { randomUUID } from 'crypto';
export default defineEventHandler(async (event) => {
const body = await readBody(event);
if (!body.name) {
throw createError({ statusCode: 400, statusMessage: 'Name is required' });
}
let slug = body.name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '');
if (!slug) slug = 'folder';
const newFolder: DashboardItem = {
id: randomUUID(),
type: 'folder',
name: body.name,
slug: slug,
parentId: body.parentId || null,
order: body.order || 0
};
const items = readDb();
items.push(newFolder);
writeDb(items);
return newFolder;
});

View File

@@ -0,0 +1,28 @@
import { readDb, writeDb, DashboardItem } from '../../utils/yamlDb';
import { randomUUID } from 'crypto';
export default defineEventHandler(async (event) => {
const body = await readBody(event);
if (!body.title || !body.url) {
throw createError({ statusCode: 400, statusMessage: 'Title and URL are required' });
}
const newLink: DashboardItem = {
id: randomUUID(),
type: 'link',
parentId: body.folderId || null,
title: body.title,
description: body.description || undefined,
url: body.url,
icon: body.icon || undefined,
color: body.color || undefined,
order: body.order || 0
};
const items = readDb();
items.push(newLink);
writeDb(items);
return newLink;
});

View File

@@ -0,0 +1,23 @@
import { readDb, writeDb } from '../utils/yamlDb';
export default defineEventHandler(async (event) => {
const body = await readBody(event);
if (!body.items || !Array.isArray(body.items)) {
throw createError({ statusCode: 400, statusMessage: 'Invalid items array' });
}
const items = readDb();
// Expecting body.items: { id: string, type: 'folder' | 'link', order: number }[]
for (const updateItem of body.items) {
const existing = items.find(i => i.id === updateItem.id);
if (existing) {
existing.order = updateItem.order;
}
}
writeDb(items);
return { success: true };
});

32
server/utils/yamlDb.ts Normal file
View File

@@ -0,0 +1,32 @@
import fs from 'fs';
import path from 'path';
import yaml from 'js-yaml';
const dbPath = path.resolve(process.cwd(), 'app/dashboard.yml');
export interface DashboardItem {
id: string;
type: 'folder' | 'link';
parentId: string | null;
name?: string;
slug?: string;
title?: string;
url?: string;
description?: string;
icon?: string;
color?: string;
order: number;
}
export function readDb(): DashboardItem[] {
if (!fs.existsSync(dbPath)) {
return [];
}
const fileContents = fs.readFileSync(dbPath, 'utf8');
return (yaml.load(fileContents) as DashboardItem[]) || [];
}
export function writeDb(data: DashboardItem[]) {
const yamlStr = yaml.dump(data);
fs.writeFileSync(dbPath, yamlStr, 'utf8');
}