Phase 1: Forward Assist initial build

Multi-tenant AI help desk SaaS for the firearms industry.
Full monorepo: API (Express/Prisma), Worker (BullMQ), Frontend (React/Vite/Tailwind).
PostgreSQL 16 + pgvector, Redis 7, JWT auth, RLS tenant isolation.
Dark Armory theme with tactical branding throughout.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Eric Jungbauer
2026-03-20 01:45:13 +00:00
parent 0bae347e65
commit 05aad75272
56 changed files with 11815 additions and 0 deletions
+147
View File
@@ -0,0 +1,147 @@
import { PrismaClient } from '@prisma/client';
import bcrypt from 'bcryptjs';
const prisma = new PrismaClient();
async function main() {
console.log('Seeding Forward Assist database...');
// Create plans
const trialPlan = await prisma.plan.upsert({
where: { slug: 'trial' },
update: {},
create: {
name: 'Trial',
slug: 'trial',
maxUsers: 2,
maxEmailAccounts: 1,
maxTicketsPerMonth: 100,
aiDraftsEnabled: true,
knowledgeBaseEnabled: false,
priceMonthly: 0,
priceYearly: 0,
},
});
await prisma.plan.upsert({
where: { slug: 'starter' },
update: {},
create: {
name: 'Starter',
slug: 'starter',
maxUsers: 5,
maxEmailAccounts: 2,
maxTicketsPerMonth: 1000,
aiDraftsEnabled: true,
knowledgeBaseEnabled: false,
priceMonthly: 49,
priceYearly: 470,
},
});
await prisma.plan.upsert({
where: { slug: 'pro' },
update: {},
create: {
name: 'Pro',
slug: 'pro',
maxUsers: 20,
maxEmailAccounts: 10,
maxTicketsPerMonth: 10000,
aiDraftsEnabled: true,
knowledgeBaseEnabled: true,
priceMonthly: 149,
priceYearly: 1430,
},
});
await prisma.plan.upsert({
where: { slug: 'enterprise' },
update: {},
create: {
name: 'Enterprise',
slug: 'enterprise',
maxUsers: 999,
maxEmailAccounts: 50,
maxTicketsPerMonth: 999999,
aiDraftsEnabled: true,
knowledgeBaseEnabled: true,
priceMonthly: 499,
priceYearly: 4790,
},
});
// Create test tenant
const tenant = await prisma.tenant.upsert({
where: { slug: 'demo-armory' },
update: {},
create: {
name: 'Demo Armory',
slug: 'demo-armory',
planId: trialPlan.id,
settings: {
timezone: 'America/Chicago',
businessHours: {
monday: { enabled: true, start: '09:00', end: '17:00' },
tuesday: { enabled: true, start: '09:00', end: '17:00' },
wednesday: { enabled: true, start: '09:00', end: '17:00' },
thursday: { enabled: true, start: '09:00', end: '17:00' },
friday: { enabled: true, start: '09:00', end: '17:00' },
saturday: { enabled: false, start: '10:00', end: '14:00' },
sunday: { enabled: false, start: '10:00', end: '14:00' },
},
autoReplyEnabled: false,
autoReplyMessage: 'Thank you for contacting us. We will respond within 24 hours.',
aiDraftEnabled: false,
aiTone: 'professional',
ticketPrefix: 'FA',
maxTicketsPerDay: 100,
},
},
});
// Create admin user (password: "admin123")
const passwordHash = await bcrypt.hash('admin123', 12);
const adminUser = await prisma.user.upsert({
where: { tenantId_email: { tenantId: tenant.id, email: 'admin@demo-armory.com' } },
update: {},
create: {
tenantId: tenant.id,
email: 'admin@demo-armory.com',
passwordHash,
name: 'Range Master',
role: 'owner',
isActive: true,
},
});
// Create notification preferences for admin
await prisma.notificationPreference.upsert({
where: { userId: adminUser.id },
update: {},
create: {
userId: adminUser.id,
tenantId: tenant.id,
newTicket: true,
ticketAssigned: true,
ticketReply: true,
aiDraftReady: true,
dailyDigest: false,
emailNotifications: true,
},
});
console.log('Seed complete!');
console.log(` Plans: trial, starter, pro, enterprise`);
console.log(` Tenant: ${tenant.name} (${tenant.slug})`);
console.log(` Admin: ${adminUser.email} / admin123`);
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});