feat(mvp): phase 8 - integration, testing & deployment
Fix all build/type/lint errors (zod 3.25 compat wrapper, Svelte 5 fixes), write 115 unit tests across 10 test files, expand seed script with demo data, update Docker config with migration on startup.
This commit is contained in:
+108
-31
@@ -41,6 +41,21 @@ async function main() {
|
||||
});
|
||||
console.log(' Created admin user:', admin.email);
|
||||
|
||||
// --- Regular User ---
|
||||
const userPassword = await bcrypt.hash('user123', 12);
|
||||
const regularUser = await prisma.user.upsert({
|
||||
where: { email: 'user@localhost' },
|
||||
update: {},
|
||||
create: {
|
||||
email: 'user@localhost',
|
||||
password: userPassword,
|
||||
displayName: 'Demo User',
|
||||
role: 'user',
|
||||
authProvider: 'local'
|
||||
}
|
||||
});
|
||||
console.log(' Created regular user:', regularUser.email);
|
||||
|
||||
// --- Groups ---
|
||||
const adminGroup = await prisma.group.upsert({
|
||||
where: { name: 'admin' },
|
||||
@@ -75,10 +90,15 @@ async function main() {
|
||||
update: {},
|
||||
create: { userId: admin.id, groupId: userGroup.id }
|
||||
});
|
||||
console.log(' Added admin to groups');
|
||||
await prisma.userGroup.upsert({
|
||||
where: { userId_groupId: { userId: regularUser.id, groupId: userGroup.id } },
|
||||
update: {},
|
||||
create: { userId: regularUser.id, groupId: userGroup.id }
|
||||
});
|
||||
console.log(' Added users to groups');
|
||||
|
||||
// --- Sample Apps ---
|
||||
const apps = [
|
||||
const appDefinitions = [
|
||||
{
|
||||
name: 'Plex',
|
||||
url: 'http://plex.local:32400',
|
||||
@@ -128,15 +148,36 @@ async function main() {
|
||||
category: 'Monitoring',
|
||||
tags: 'monitoring,analytics,dashboards,metrics',
|
||||
healthcheckEnabled: true
|
||||
},
|
||||
{
|
||||
name: 'Portainer',
|
||||
url: 'http://portainer.local:9000',
|
||||
icon: 'portainer',
|
||||
iconType: 'simple',
|
||||
description: 'Container management UI for Docker and Kubernetes',
|
||||
category: 'Infrastructure',
|
||||
tags: 'docker,containers,kubernetes,management',
|
||||
healthcheckEnabled: true
|
||||
},
|
||||
{
|
||||
name: 'Pi-hole',
|
||||
url: 'http://pihole.local/admin',
|
||||
icon: 'pihole',
|
||||
iconType: 'simple',
|
||||
description: 'Network-wide ad blocking DNS sinkhole',
|
||||
category: 'Network',
|
||||
tags: 'dns,adblock,network,privacy',
|
||||
healthcheckEnabled: true
|
||||
}
|
||||
];
|
||||
|
||||
// Create apps using create (delete existing first for idempotency)
|
||||
const createdApps = [];
|
||||
for (const appData of apps) {
|
||||
const app = await prisma.app.upsert({
|
||||
where: { id: appData.name.toLowerCase().replace(/\s+/g, '-') },
|
||||
update: {},
|
||||
create: {
|
||||
for (const appData of appDefinitions) {
|
||||
// Delete existing app with same name if present (for re-seeding)
|
||||
await prisma.app.deleteMany({ where: { name: appData.name } });
|
||||
const app = await prisma.app.create({
|
||||
data: {
|
||||
...appData,
|
||||
createdById: admin.id
|
||||
}
|
||||
@@ -190,12 +231,36 @@ async function main() {
|
||||
});
|
||||
console.log(' Created section:', infraSection.title);
|
||||
|
||||
// --- Widgets ---
|
||||
// Plex widget in media section
|
||||
await prisma.widget.upsert({
|
||||
where: { id: 'widget-plex' },
|
||||
const networkSection = await prisma.section.upsert({
|
||||
where: { id: 'section-network' },
|
||||
update: {},
|
||||
create: {
|
||||
id: 'section-network',
|
||||
boardId: board.id,
|
||||
title: 'Network & Security',
|
||||
icon: 'shield',
|
||||
order: 2,
|
||||
isExpandedByDefault: true
|
||||
}
|
||||
});
|
||||
console.log(' Created section:', networkSection.title);
|
||||
|
||||
// --- Widgets ---
|
||||
// Delete existing seed widgets for idempotency
|
||||
const seedWidgetIds = [
|
||||
'widget-plex',
|
||||
'widget-nextcloud',
|
||||
'widget-gitea',
|
||||
'widget-homeassistant',
|
||||
'widget-grafana',
|
||||
'widget-portainer',
|
||||
'widget-pihole'
|
||||
];
|
||||
await prisma.widget.deleteMany({ where: { id: { in: seedWidgetIds } } });
|
||||
|
||||
// Media section widgets
|
||||
await prisma.widget.create({
|
||||
data: {
|
||||
id: 'widget-plex',
|
||||
sectionId: mediaSection.id,
|
||||
type: 'app',
|
||||
@@ -205,11 +270,9 @@ async function main() {
|
||||
}
|
||||
});
|
||||
|
||||
// Nextcloud widget in infra section
|
||||
await prisma.widget.upsert({
|
||||
where: { id: 'widget-nextcloud' },
|
||||
update: {},
|
||||
create: {
|
||||
// Infrastructure section widgets
|
||||
await prisma.widget.create({
|
||||
data: {
|
||||
id: 'widget-nextcloud',
|
||||
sectionId: infraSection.id,
|
||||
type: 'app',
|
||||
@@ -219,11 +282,8 @@ async function main() {
|
||||
}
|
||||
});
|
||||
|
||||
// Gitea widget in infra section
|
||||
await prisma.widget.upsert({
|
||||
where: { id: 'widget-gitea' },
|
||||
update: {},
|
||||
create: {
|
||||
await prisma.widget.create({
|
||||
data: {
|
||||
id: 'widget-gitea',
|
||||
sectionId: infraSection.id,
|
||||
type: 'app',
|
||||
@@ -233,11 +293,8 @@ async function main() {
|
||||
}
|
||||
});
|
||||
|
||||
// Home Assistant widget in infra section
|
||||
await prisma.widget.upsert({
|
||||
where: { id: 'widget-homeassistant' },
|
||||
update: {},
|
||||
create: {
|
||||
await prisma.widget.create({
|
||||
data: {
|
||||
id: 'widget-homeassistant',
|
||||
sectionId: infraSection.id,
|
||||
type: 'app',
|
||||
@@ -247,11 +304,8 @@ async function main() {
|
||||
}
|
||||
});
|
||||
|
||||
// Grafana widget in infra section
|
||||
await prisma.widget.upsert({
|
||||
where: { id: 'widget-grafana' },
|
||||
update: {},
|
||||
create: {
|
||||
await prisma.widget.create({
|
||||
data: {
|
||||
id: 'widget-grafana',
|
||||
sectionId: infraSection.id,
|
||||
type: 'app',
|
||||
@@ -261,6 +315,29 @@ async function main() {
|
||||
}
|
||||
});
|
||||
|
||||
await prisma.widget.create({
|
||||
data: {
|
||||
id: 'widget-portainer',
|
||||
sectionId: infraSection.id,
|
||||
type: 'app',
|
||||
order: 4,
|
||||
appId: createdApps[5].id,
|
||||
config: JSON.stringify({ showStatus: true, openInNewTab: true })
|
||||
}
|
||||
});
|
||||
|
||||
// Network section widgets
|
||||
await prisma.widget.create({
|
||||
data: {
|
||||
id: 'widget-pihole',
|
||||
sectionId: networkSection.id,
|
||||
type: 'app',
|
||||
order: 0,
|
||||
appId: createdApps[6].id,
|
||||
config: JSON.stringify({ showStatus: true, openInNewTab: true })
|
||||
}
|
||||
});
|
||||
|
||||
console.log(' Created widgets for all apps');
|
||||
console.log('Seeding complete!');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user