f1b1aa5975
Define full Prisma schema (10 models), run initial migration, build core services (auth, user, group, app, board, permission), Zod validators, type definitions, API response envelope, constants, and seed script.
276 lines
6.4 KiB
TypeScript
276 lines
6.4 KiB
TypeScript
import { PrismaClient } from '@prisma/client';
|
|
import bcrypt from 'bcryptjs';
|
|
|
|
const prisma = new PrismaClient();
|
|
|
|
async function main() {
|
|
console.log('Seeding database...');
|
|
|
|
// --- System Settings ---
|
|
const settings = await prisma.systemSettings.upsert({
|
|
where: { id: 'singleton' },
|
|
update: {},
|
|
create: {
|
|
id: 'singleton',
|
|
authMode: 'local',
|
|
registrationEnabled: true,
|
|
defaultTheme: 'dark',
|
|
defaultPrimaryColor: '#6366f1',
|
|
healthcheckDefaults: JSON.stringify({
|
|
interval: 300,
|
|
timeout: 5000,
|
|
method: 'GET',
|
|
expectedStatus: 200
|
|
})
|
|
}
|
|
});
|
|
console.log(' Created system settings:', settings.id);
|
|
|
|
// --- Admin User ---
|
|
const adminPassword = await bcrypt.hash('admin123', 12);
|
|
const admin = await prisma.user.upsert({
|
|
where: { email: 'admin@localhost' },
|
|
update: {},
|
|
create: {
|
|
email: 'admin@localhost',
|
|
password: adminPassword,
|
|
displayName: 'Administrator',
|
|
role: 'admin',
|
|
authProvider: 'local'
|
|
}
|
|
});
|
|
console.log(' Created admin user:', admin.email);
|
|
|
|
// --- Groups ---
|
|
const adminGroup = await prisma.group.upsert({
|
|
where: { name: 'admin' },
|
|
update: {},
|
|
create: {
|
|
name: 'admin',
|
|
description: 'Administrators with full system access',
|
|
isDefault: false
|
|
}
|
|
});
|
|
console.log(' Created group:', adminGroup.name);
|
|
|
|
const userGroup = await prisma.group.upsert({
|
|
where: { name: 'user' },
|
|
update: {},
|
|
create: {
|
|
name: 'user',
|
|
description: 'Default group for all registered users',
|
|
isDefault: true
|
|
}
|
|
});
|
|
console.log(' Created group:', userGroup.name);
|
|
|
|
// --- User-Group memberships ---
|
|
await prisma.userGroup.upsert({
|
|
where: { userId_groupId: { userId: admin.id, groupId: adminGroup.id } },
|
|
update: {},
|
|
create: { userId: admin.id, groupId: adminGroup.id }
|
|
});
|
|
await prisma.userGroup.upsert({
|
|
where: { userId_groupId: { userId: admin.id, groupId: userGroup.id } },
|
|
update: {},
|
|
create: { userId: admin.id, groupId: userGroup.id }
|
|
});
|
|
console.log(' Added admin to groups');
|
|
|
|
// --- Sample Apps ---
|
|
const apps = [
|
|
{
|
|
name: 'Plex',
|
|
url: 'http://plex.local:32400',
|
|
icon: 'plex',
|
|
iconType: 'simple',
|
|
description: 'Media server for streaming movies, TV shows, and music',
|
|
category: 'Media',
|
|
tags: 'media,streaming,movies,tv',
|
|
healthcheckEnabled: true
|
|
},
|
|
{
|
|
name: 'Nextcloud',
|
|
url: 'http://nextcloud.local',
|
|
icon: 'nextcloud',
|
|
iconType: 'simple',
|
|
description: 'Self-hosted file sync, sharing, and collaboration platform',
|
|
category: 'Productivity',
|
|
tags: 'files,sync,cloud,office',
|
|
healthcheckEnabled: true
|
|
},
|
|
{
|
|
name: 'Gitea',
|
|
url: 'http://gitea.local:3000',
|
|
icon: 'gitea',
|
|
iconType: 'simple',
|
|
description: 'Lightweight self-hosted Git service',
|
|
category: 'Development',
|
|
tags: 'git,code,development,ci',
|
|
healthcheckEnabled: true
|
|
},
|
|
{
|
|
name: 'Home Assistant',
|
|
url: 'http://homeassistant.local:8123',
|
|
icon: 'homeassistant',
|
|
iconType: 'simple',
|
|
description: 'Open-source home automation platform',
|
|
category: 'Home Automation',
|
|
tags: 'home,automation,iot,smart-home',
|
|
healthcheckEnabled: true
|
|
},
|
|
{
|
|
name: 'Grafana',
|
|
url: 'http://grafana.local:3000',
|
|
icon: 'grafana',
|
|
iconType: 'simple',
|
|
description: 'Analytics and monitoring dashboards',
|
|
category: 'Monitoring',
|
|
tags: 'monitoring,analytics,dashboards,metrics',
|
|
healthcheckEnabled: true
|
|
}
|
|
];
|
|
|
|
const createdApps = [];
|
|
for (const appData of apps) {
|
|
const app = await prisma.app.upsert({
|
|
where: { id: appData.name.toLowerCase().replace(/\s+/g, '-') },
|
|
update: {},
|
|
create: {
|
|
...appData,
|
|
createdById: admin.id
|
|
}
|
|
});
|
|
createdApps.push(app);
|
|
console.log(' Created app:', app.name);
|
|
}
|
|
|
|
// --- Default Board ---
|
|
const board = await prisma.board.upsert({
|
|
where: { id: 'default-board' },
|
|
update: {},
|
|
create: {
|
|
id: 'default-board',
|
|
name: 'Dashboard',
|
|
icon: 'layout-dashboard',
|
|
description: 'Default application dashboard',
|
|
isDefault: true,
|
|
isGuestAccessible: true,
|
|
createdById: admin.id
|
|
}
|
|
});
|
|
console.log(' Created board:', board.name);
|
|
|
|
// --- Sections ---
|
|
const mediaSection = await prisma.section.upsert({
|
|
where: { id: 'section-media' },
|
|
update: {},
|
|
create: {
|
|
id: 'section-media',
|
|
boardId: board.id,
|
|
title: 'Media & Entertainment',
|
|
icon: 'tv',
|
|
order: 0,
|
|
isExpandedByDefault: true
|
|
}
|
|
});
|
|
console.log(' Created section:', mediaSection.title);
|
|
|
|
const infraSection = await prisma.section.upsert({
|
|
where: { id: 'section-infra' },
|
|
update: {},
|
|
create: {
|
|
id: 'section-infra',
|
|
boardId: board.id,
|
|
title: 'Infrastructure & Tools',
|
|
icon: 'server',
|
|
order: 1,
|
|
isExpandedByDefault: true
|
|
}
|
|
});
|
|
console.log(' Created section:', infraSection.title);
|
|
|
|
// --- Widgets ---
|
|
// Plex widget in media section
|
|
await prisma.widget.upsert({
|
|
where: { id: 'widget-plex' },
|
|
update: {},
|
|
create: {
|
|
id: 'widget-plex',
|
|
sectionId: mediaSection.id,
|
|
type: 'app',
|
|
order: 0,
|
|
appId: createdApps[0].id,
|
|
config: JSON.stringify({ showStatus: true, openInNewTab: true })
|
|
}
|
|
});
|
|
|
|
// Nextcloud widget in infra section
|
|
await prisma.widget.upsert({
|
|
where: { id: 'widget-nextcloud' },
|
|
update: {},
|
|
create: {
|
|
id: 'widget-nextcloud',
|
|
sectionId: infraSection.id,
|
|
type: 'app',
|
|
order: 0,
|
|
appId: createdApps[1].id,
|
|
config: JSON.stringify({ showStatus: true, openInNewTab: true })
|
|
}
|
|
});
|
|
|
|
// Gitea widget in infra section
|
|
await prisma.widget.upsert({
|
|
where: { id: 'widget-gitea' },
|
|
update: {},
|
|
create: {
|
|
id: 'widget-gitea',
|
|
sectionId: infraSection.id,
|
|
type: 'app',
|
|
order: 1,
|
|
appId: createdApps[2].id,
|
|
config: JSON.stringify({ showStatus: true, openInNewTab: true })
|
|
}
|
|
});
|
|
|
|
// Home Assistant widget in infra section
|
|
await prisma.widget.upsert({
|
|
where: { id: 'widget-homeassistant' },
|
|
update: {},
|
|
create: {
|
|
id: 'widget-homeassistant',
|
|
sectionId: infraSection.id,
|
|
type: 'app',
|
|
order: 2,
|
|
appId: createdApps[3].id,
|
|
config: JSON.stringify({ showStatus: true, openInNewTab: true })
|
|
}
|
|
});
|
|
|
|
// Grafana widget in infra section
|
|
await prisma.widget.upsert({
|
|
where: { id: 'widget-grafana' },
|
|
update: {},
|
|
create: {
|
|
id: 'widget-grafana',
|
|
sectionId: infraSection.id,
|
|
type: 'app',
|
|
order: 3,
|
|
appId: createdApps[4].id,
|
|
config: JSON.stringify({ showStatus: true, openInNewTab: true })
|
|
}
|
|
});
|
|
|
|
console.log(' Created widgets for all apps');
|
|
console.log('Seeding complete!');
|
|
}
|
|
|
|
main()
|
|
.catch((e) => {
|
|
console.error('Seed error:', e);
|
|
process.exit(1);
|
|
})
|
|
.finally(async () => {
|
|
await prisma.$disconnect();
|
|
});
|