diff --git a/backend/prisma/seed.ts b/backend/prisma/seed.ts new file mode 100644 index 0000000..d7daa04 --- /dev/null +++ b/backend/prisma/seed.ts @@ -0,0 +1,597 @@ +import { PrismaClient } from "@prisma/client"; +import { subDays, subHours } from "date-fns"; +import { hashPassword } from "../src/lib/password.js"; + +const prisma = new PrismaClient(); +const now = new Date("2026-06-12T12:00:00Z"); +const defaultPasswordHash = hashPassword("correct-password"); +const twoFactorPasswordHash = hashPassword("2fa-password"); + +async function main() { + console.log("Cleaning database..."); + // Clear tables in reverse sequence to avoid constraint violations + await prisma.auditEntry.deleteMany(); + await prisma.notification.deleteMany(); + await prisma.automation.deleteMany(); + await prisma.backup.deleteMany(); + await prisma.securityFinding.deleteMany(); + await prisma.metricPoint.deleteMany(); + await prisma.resourceUsage.deleteMany(); + await prisma.mailbox.deleteMany(); + await prisma.dnsRecord.deleteMany(); + await prisma.domain.deleteMany(); + await prisma.website.deleteMany(); + await prisma.accountUser.deleteMany(); + await prisma.userPermission.deleteMany(); + await prisma.session.deleteMany(); + await prisma.hostingAccount.deleteMany(); + await prisma.user.deleteMany(); + await prisma.server.deleteMany(); + + console.log("Seeding Servers..."); + await prisma.server.createMany({ + data: [ + { + id: "srv-dal-01", + name: "Dallas Web 01", + hostname: "srv-dal-01.coaxys.net", + os: "AlmaLinux 9.6", + panelVersion: "CoaxysPanel 2026.6", + load1: 0.38, + load5: 0.44, + load15: 0.41, + ipAddress: "198.51.100.24", + status: "healthy", + location: "Dallas, US", + }, + { + id: "srv-nyc-02", + name: "New York Apps 02", + hostname: "srv-nyc-02.coaxys.net", + os: "Ubuntu 24.04 LTS", + panelVersion: "CoaxysPanel 2026.6", + load1: 1.12, + load5: 0.91, + load15: 0.78, + ipAddress: "203.0.113.22", + status: "warning", + location: "New York, US", + }, + ], + }); + + console.log("Seeding Users..."); + const adminUser = await prisma.user.create({ + data: { + id: "usr-1", + name: "Account Owner", + email: "owner@example.com", + passwordHash: defaultPasswordHash, + role: "owner", + avatarInitials: "AO", + twoFactorEnabled: true, + lastLogin: subHours(now, 2), + }, + }); + + const twoFactorUser = await prisma.user.create({ + data: { + id: "usr-2fa", + name: "Two Factor Owner", + email: "owner-2fa@example.com", + passwordHash: twoFactorPasswordHash, + role: "owner", + avatarInitials: "TO", + twoFactorEnabled: true, + lastLogin: subHours(now, 4), + }, + }); + + const developerUser = await prisma.user.create({ + data: { + id: "usr-dev", + name: "Developer User", + email: "developer@example.com", + passwordHash: defaultPasswordHash, + role: "developer", + avatarInitials: "DU", + lastLogin: subHours(now, 6), + }, + }); + + const auditorUser = await prisma.user.create({ + data: { + id: "usr-audit", + name: "Auditor User", + email: "auditor@example.com", + passwordHash: defaultPasswordHash, + role: "auditor", + avatarInitials: "AU", + lastLogin: subHours(now, 8), + }, + }); + + console.log("Seeding Permissions..."); + const permissionsList = [ + "dashboard_read", + "tools_read", + "websites_manage", + "domains_manage", + "files_manage", + "databases_manage", + "email_manage", + "metrics_read", + "security_manage", + "software_manage", + "advanced_manage", + "servers_manage", + "users_manage", + "resellers_manage", + "backups_manage", + "automation_manage", + "notifications_read", + "audit_read", + "support_manage", + "settings_manage", + ] as const; + const developerPermissions = [ + "dashboard_read", + "tools_read", + "websites_manage", + "domains_manage", + "files_manage", + "databases_manage", + "metrics_read", + "advanced_manage", + ] as const; + const auditorPermissions = [ + "dashboard_read", + "tools_read", + "metrics_read", + "audit_read", + ] as const; + + await prisma.userPermission.createMany({ + data: [ + ...permissionsList.map((perm) => ({ userId: adminUser.id, permission: perm })), + ...permissionsList.map((perm) => ({ userId: twoFactorUser.id, permission: perm })), + ...developerPermissions.map((perm) => ({ userId: developerUser.id, permission: perm })), + ...auditorPermissions.map((perm) => ({ userId: auditorUser.id, permission: perm })), + ], + }); + + console.log("Seeding Hosting Accounts..."); + await prisma.hostingAccount.createMany({ + data: [ + { + id: "acct-main", + username: "coaxys", + primaryDomain: "example.com", + homeDirectory: "/home/coaxys", + sharedIp: "198.51.100.24", + dedicatedIp: "203.0.113.15", + theme: "light", + }, + { + id: "acct-shop", + username: "storeops", + primaryDomain: "store.example.com", + homeDirectory: "/home/storeops", + sharedIp: "198.51.100.31", + theme: "system", + }, + ], + }); + + console.log("Seeding Account Users..."); + await prisma.accountUser.createMany({ + data: [ + { userId: adminUser.id, accountId: "acct-main" }, + { userId: adminUser.id, accountId: "acct-shop" }, + { userId: twoFactorUser.id, accountId: "acct-main" }, + { userId: developerUser.id, accountId: "acct-main" }, + { userId: auditorUser.id, accountId: "acct-main" }, + ], + }); + + console.log("Seeding Websites..."); + await prisma.website.createMany({ + data: [ + { + id: "alpha-site", + name: "Alpha Marketing", + domain: "example.com", + runtime: "PHP", + status: "healthy", + ssl: "healthy", + repository: "git@example.com:alpha/marketing.git", + environment: "production", + cpuLimit: "2 vCPU", + memoryLimit: "2 GB", + lastDeployment: subHours(now, 5), + accountId: "acct-main", + }, + { + id: "docs-hub", + name: "Docs Hub", + domain: "docs.example.com", + runtime: "Node_js", + status: "healthy", + ssl: "healthy", + repository: "git@example.com:alpha/docs.git", + environment: "production", + cpuLimit: "1 vCPU", + memoryLimit: "1 GB", + lastDeployment: subDays(now, 1), + accountId: "acct-main", + }, + { + id: "preview-api", + name: "Preview API", + domain: "api.preview.example.com", + runtime: "Docker", + status: "warning", + ssl: "warning", + environment: "staging", + cpuLimit: "1 vCPU", + memoryLimit: "768 MB", + lastDeployment: subHours(now, 9), + accountId: "acct-main", + }, + ], + }); + + console.log("Seeding Domains..."); + await prisma.domain.createMany({ + data: [ + { + id: "dom-1", + name: "example.com", + type: "primary", + documentRoot: "/home/coaxys/public_html", + sslStatus: "healthy", + dnssec: true, + propagation: 98, + accountId: "acct-main", + websiteId: "alpha-site", + }, + { + id: "dom-2", + name: "docs.example.com", + type: "subdomain", + documentRoot: "/home/coaxys/docs", + sslStatus: "healthy", + dnssec: false, + propagation: 100, + accountId: "acct-main", + websiteId: "docs-hub", + }, + { + id: "dom-3", + name: "legacy-example.net", + type: "alias", + documentRoot: "/home/coaxys/public_html", + sslStatus: "warning", + dnssec: false, + propagation: 73, + accountId: "acct-main", + websiteId: "alpha-site", + }, + ], + }); + + console.log("Seeding DNS Records..."); + await prisma.dnsRecord.createMany({ + data: [ + { + id: "dns-1", + name: "example.com.", + ttl: 14400, + type: "A", + record: "198.51.100.24", + domainId: "dom-1", + }, + { + id: "dns-2", + name: "www.example.com.", + ttl: 14400, + type: "CNAME", + record: "example.com.", + domainId: "dom-1", + }, + { + id: "dns-3", + name: "example.com.", + ttl: 14400, + type: "MX", + record: "mail.example.com.", + priority: 10, + domainId: "dom-1", + }, + { + id: "dns-4", + name: "example.com.", + ttl: 3600, + type: "TXT", + record: "v=spf1 mx include:_spf.coaxys.net ~all", + domainId: "dom-1", + }, + { + id: "dns-5", + name: "_dmarc.example.com.", + ttl: 3600, + type: "TXT", + record: "v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com", + domainId: "dom-1", + }, + ], + }); + + console.log("Seeding Mailboxes..."); + await prisma.mailbox.createMany({ + data: [ + { + id: "mail-1", + address: "admin@example.com", + used: 1200000000n, // BigInt suffix + quota: 5000000000n, + lastLogin: subHours(now, 1), + forwarding: true, + spamScore: 1.8, + authStatus: "healthy", + domainId: "dom-1", + accountId: "acct-main", + }, + { + id: "mail-2", + address: "support@example.com", + used: 4600000000n, + quota: 5000000000n, + lastLogin: subDays(now, 2), + forwarding: false, + spamScore: 4.1, + authStatus: "warning", + domainId: "dom-1", + accountId: "acct-main", + }, + ], + }); + + console.log("Seeding Resource Usage..."); + await prisma.resourceUsage.createMany({ + data: [ + { + id: "usage-disk", + key: "disk", + label: "Disk usage", + used: 38.4, + limit: 100, + unit: "GB", + status: "healthy", + accountId: "acct-main", + }, + { + id: "usage-bandwidth", + key: "bandwidth", + label: "Bandwidth", + used: 412, + limit: 1000, + unit: "GB", + status: "healthy", + accountId: "acct-main", + }, + { + id: "usage-cpu", + key: "cpu", + label: "CPU", + used: 42, + limit: 100, + unit: "%", + status: "healthy", + accountId: "acct-main", + }, + { + id: "usage-memory", + key: "memory", + label: "Memory", + used: 6.8, + limit: 16, + unit: "GB", + status: "warning", + accountId: "acct-main", + }, + { + id: "usage-inodes", + key: "inodes", + label: "Inodes", + used: 182000, + limit: 500000, + unit: "files", + status: "healthy", + accountId: "acct-main", + }, + { + id: "usage-databases", + key: "database_storage", + label: "Database storage", + used: 8.6, + limit: 25, + unit: "GB", + status: "healthy", + accountId: "acct-main", + }, + ], + }); + + console.log("Seeding Metrics..."); + await prisma.metricPoint.createMany({ + data: Array.from({ length: 48 }, (_, index) => { + const phase = index / 5; + return { + id: `metric-${index + 1}`, + timestamp: subHours(now, 47 - index), + cpu: Math.round(32 + Math.sin(phase) * 10 + (index % 7)), + memory: Math.round(48 + Math.cos(phase) * 8 + (index % 5)), + disk: Math.round(37 + index * 0.08), + bandwidth: Math.round(220 + Math.sin(phase) * 60 + index * 3), + requests: Math.round(1200 + Math.cos(phase) * 240 + index * 16), + accountId: "acct-main", + }; + }), + }); + + console.log("Seeding Security Findings..."); + await prisma.securityFinding.createMany({ + data: [ + { + id: "sec-1", + severity: "high", + title: "Two certificates expire within 14 days", + target: "legacy-example.net", + status: "open", + recommendation: "Renew or replace the certificate before expiration.", + accountId: "acct-main", + }, + { + id: "sec-2", + severity: "medium", + title: "SSH password authentication is enabled", + target: "srv-dal-01", + status: "acknowledged", + recommendation: "Prefer key-only authentication for administrative access.", + accountId: "acct-main", + }, + { + id: "sec-3", + severity: "low", + title: "Directory listing is enabled on one path", + target: "/home/coaxys/public_html/assets", + status: "open", + recommendation: "Disable index listing unless intentionally public.", + accountId: "acct-main", + }, + ], + }); + + console.log("Seeding Backups..."); + await prisma.backup.createMany({ + data: [ + { + id: "bak-1", + name: "Full account nightly", + scope: "full", + destination: "Local storage", + size: 42_800_000_000n, + status: "healthy", + encrypted: true, + createdAt: subHours(now, 7), + accountId: "acct-main", + }, + { + id: "bak-2", + name: "Database pre-deploy", + scope: "database", + destination: "S3-compatible storage", + size: 8_200_000_000n, + status: "healthy", + encrypted: true, + createdAt: subDays(now, 1), + accountId: "acct-main", + }, + ], + }); + + console.log("Seeding Automations..."); + await prisma.automation.createMany({ + data: [ + { + id: "auto-1", + name: "Renew expiring SSL certificates", + trigger: "Daily at 03:10 UTC", + status: "healthy", + lastRun: subHours(now, 9), + successRate: 99, + accountId: "acct-main", + }, + { + id: "auto-2", + name: "Deploy docs on Git push", + trigger: "Git webhook", + status: "healthy", + lastRun: subHours(now, 5), + successRate: 96, + accountId: "acct-main", + }, + ], + }); + + console.log("Seeding Notifications..."); + await prisma.notification.createMany({ + data: [ + { + id: "notif-1", + title: "Nightly backup verified", + category: "backup", + read: false, + createdAt: subHours(now, 2), + body: "The full account backup completed and passed verification.", + accountId: "acct-main", + }, + { + id: "notif-2", + title: "Certificate renewal recommended", + category: "ssl", + read: false, + createdAt: subHours(now, 4), + body: "legacy-example.net has a certificate entering the renewal window.", + accountId: "acct-main", + }, + { + id: "notif-3", + title: "Memory usage is elevated", + category: "resource", + read: true, + createdAt: subHours(now, 6), + body: "Memory usage has remained above the warning threshold for 25 minutes.", + accountId: "acct-main", + }, + ], + }); + + console.log("Seeding Audit logs..."); + await prisma.auditEntry.createMany({ + data: [ + { + id: "aud-1", + actor: "Account Owner", + action: "created DNS record", + target: "api.example.com A", + ipAddress: "192.0.2.44", + createdAt: subHours(now, 2), + requestId: "req_8f172a", + accountId: "acct-main", + }, + { + id: "aud-2", + actor: "Account Owner", + action: "downloaded backup", + target: "bak-2026-06-12", + ipAddress: "192.0.2.44", + createdAt: subHours(now, 3), + requestId: "req_9b72cf", + accountId: "acct-main", + }, + ], + }); + + console.log("Database seeded successfully!"); +} + +main() + .catch((e) => { + console.error(e); + process.exit(1); + }) + .finally(async () => { + await prisma.$disconnect(); + });