feat: add admin dashboard schema and seeder
- Add Plan, Subscription, Payment, PaymentMethod, Coupon models - Add ApiKey, Webhook models for API access - Add AppConfig model for dynamic configuration - Add role, suspendedAt fields to User model - Create comprehensive seeder with: - Admin user (dwindi.ramadhana@gmail.com) - Default plans (Free, Pro Monthly, Pro Yearly) - Payment methods (BCA, Mandiri, GoPay) - App config (maintenance mode) - Zero data loss migration strategy
This commit is contained in:
@@ -1,15 +1,237 @@
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
const prisma = new PrismaClient();
|
||||
import * as bcrypt from 'bcrypt';
|
||||
|
||||
const adminSeeder = {
|
||||
email: 'dwindi.ramadhana@gmail.com',
|
||||
password: 'tabungin2k25!@#',
|
||||
}
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
const TEMP_USER_ID =
|
||||
process.env.TEMP_USER_ID || '16b74848-daa3-4dc9-8de2-3cf59e08f8e3';
|
||||
|
||||
const ADMIN_EMAIL = 'dwindi.ramadhana@gmail.com';
|
||||
const ADMIN_PASSWORD = 'tabungin2k25!@#';
|
||||
|
||||
async function main() {
|
||||
console.log('🌱 Starting seed...');
|
||||
|
||||
// ============================================
|
||||
// 1. CREATE ADMIN USER
|
||||
// ============================================
|
||||
console.log('\n👤 Creating admin user...');
|
||||
|
||||
const passwordHash = await bcrypt.hash(ADMIN_PASSWORD, 10);
|
||||
|
||||
const admin = await prisma.user.upsert({
|
||||
where: { email: ADMIN_EMAIL },
|
||||
update: {
|
||||
role: 'admin',
|
||||
passwordHash,
|
||||
emailVerified: true,
|
||||
},
|
||||
create: {
|
||||
email: ADMIN_EMAIL,
|
||||
passwordHash,
|
||||
name: 'Dwindi Ramadhana',
|
||||
role: 'admin',
|
||||
emailVerified: true,
|
||||
},
|
||||
});
|
||||
|
||||
console.log('✅ Admin user created:', admin.email);
|
||||
|
||||
// ============================================
|
||||
// 2. CREATE DEFAULT PLANS
|
||||
// ============================================
|
||||
console.log('\n💰 Creating default plans...');
|
||||
|
||||
const freePlan = await prisma.plan.upsert({
|
||||
where: { slug: 'free' },
|
||||
update: {},
|
||||
create: {
|
||||
name: 'Free',
|
||||
slug: 'free',
|
||||
description: 'Perfect for getting started',
|
||||
price: 0,
|
||||
currency: 'IDR',
|
||||
durationType: 'lifetime',
|
||||
durationDays: null,
|
||||
trialDays: 0,
|
||||
features: {
|
||||
wallets: { limit: 5, label: '5 wallets' },
|
||||
goals: { limit: 3, label: '3 goals' },
|
||||
team: { enabled: false, label: 'No team feature' },
|
||||
api: { enabled: false, label: 'No API access' },
|
||||
support: { level: 'basic', label: 'Basic support' },
|
||||
export: { enabled: true, formats: ['csv'], label: 'CSV export' },
|
||||
},
|
||||
badge: null,
|
||||
sortOrder: 1,
|
||||
isActive: true,
|
||||
isVisible: true,
|
||||
isFeatured: false,
|
||||
maxWallets: 5,
|
||||
maxGoals: 3,
|
||||
maxTeamMembers: 0,
|
||||
apiEnabled: false,
|
||||
apiRateLimit: null,
|
||||
},
|
||||
});
|
||||
|
||||
const proMonthly = await prisma.plan.upsert({
|
||||
where: { slug: 'pro-monthly' },
|
||||
update: {},
|
||||
create: {
|
||||
name: 'Pro Monthly',
|
||||
slug: 'pro-monthly',
|
||||
description: 'Perfect for individuals and small teams',
|
||||
price: 49000,
|
||||
currency: 'IDR',
|
||||
durationType: 'monthly',
|
||||
durationDays: 30,
|
||||
trialDays: 7,
|
||||
features: {
|
||||
wallets: { limit: null, label: 'Unlimited wallets' },
|
||||
goals: { limit: null, label: 'Unlimited goals' },
|
||||
team: { enabled: true, maxMembers: 10, label: 'Team feature (10 members)' },
|
||||
api: { enabled: true, rateLimit: 1000, label: 'API access (1000 req/hr)' },
|
||||
support: { level: 'priority', label: 'Priority support' },
|
||||
export: { enabled: true, formats: ['csv', 'excel', 'pdf'], label: 'All export formats' },
|
||||
},
|
||||
badge: 'Popular',
|
||||
badgeColor: 'blue',
|
||||
highlightColor: '#3B82F6',
|
||||
sortOrder: 2,
|
||||
isActive: true,
|
||||
isVisible: true,
|
||||
isFeatured: true,
|
||||
maxWallets: null,
|
||||
maxGoals: null,
|
||||
maxTeamMembers: 10,
|
||||
apiEnabled: true,
|
||||
apiRateLimit: 1000,
|
||||
},
|
||||
});
|
||||
|
||||
const proYearly = await prisma.plan.upsert({
|
||||
where: { slug: 'pro-yearly' },
|
||||
update: {},
|
||||
create: {
|
||||
name: 'Pro Yearly',
|
||||
slug: 'pro-yearly',
|
||||
description: 'Best value - Save 17% with annual billing',
|
||||
price: 490000,
|
||||
currency: 'IDR',
|
||||
durationType: 'yearly',
|
||||
durationDays: 365,
|
||||
trialDays: 7,
|
||||
features: {
|
||||
wallets: { limit: null, label: 'Unlimited wallets' },
|
||||
goals: { limit: null, label: 'Unlimited goals' },
|
||||
team: { enabled: true, maxMembers: 10, label: 'Team feature (10 members)' },
|
||||
api: { enabled: true, rateLimit: 1000, label: 'API access (1000 req/hr)' },
|
||||
support: { level: 'priority', label: 'Priority support' },
|
||||
export: { enabled: true, formats: ['csv', 'excel', 'pdf'], label: 'All export formats' },
|
||||
discount: { value: '17%', label: 'Save 17% (2 months free)' },
|
||||
},
|
||||
badge: 'Best Value',
|
||||
badgeColor: 'green',
|
||||
highlightColor: '#10B981',
|
||||
sortOrder: 3,
|
||||
isActive: true,
|
||||
isVisible: true,
|
||||
isFeatured: true,
|
||||
maxWallets: null,
|
||||
maxGoals: null,
|
||||
maxTeamMembers: 10,
|
||||
apiEnabled: true,
|
||||
apiRateLimit: 1000,
|
||||
},
|
||||
});
|
||||
|
||||
console.log('✅ Plans created:', [freePlan.name, proMonthly.name, proYearly.name]);
|
||||
|
||||
// ============================================
|
||||
// 3. CREATE DEFAULT PAYMENT METHODS
|
||||
// ============================================
|
||||
console.log('\n💳 Creating default payment methods...');
|
||||
|
||||
const bcaMethod = await prisma.paymentMethod.upsert({
|
||||
where: { id: 'bca-method' },
|
||||
update: {},
|
||||
create: {
|
||||
id: 'bca-method',
|
||||
type: 'bank_transfer',
|
||||
provider: 'BCA',
|
||||
accountName: 'PT Tabungin Indonesia',
|
||||
accountNumber: '1234567890',
|
||||
displayName: 'BCA Virtual Account',
|
||||
logoUrl: '/logos/bca.png',
|
||||
instructions: 'Transfer to the account above and upload proof of payment.',
|
||||
isActive: true,
|
||||
sortOrder: 1,
|
||||
},
|
||||
});
|
||||
|
||||
const mandiriMethod = await prisma.paymentMethod.upsert({
|
||||
where: { id: 'mandiri-method' },
|
||||
update: {},
|
||||
create: {
|
||||
id: 'mandiri-method',
|
||||
type: 'bank_transfer',
|
||||
provider: 'Mandiri',
|
||||
accountName: 'PT Tabungin Indonesia',
|
||||
accountNumber: '9876543210',
|
||||
displayName: 'Mandiri Virtual Account',
|
||||
logoUrl: '/logos/mandiri.png',
|
||||
instructions: 'Transfer to the account above and upload proof of payment.',
|
||||
isActive: true,
|
||||
sortOrder: 2,
|
||||
},
|
||||
});
|
||||
|
||||
const gopayMethod = await prisma.paymentMethod.upsert({
|
||||
where: { id: 'gopay-method' },
|
||||
update: {},
|
||||
create: {
|
||||
id: 'gopay-method',
|
||||
type: 'e-wallet',
|
||||
provider: 'GoPay',
|
||||
accountName: 'Dwindi Ramadhana',
|
||||
accountNumber: '081234567890',
|
||||
displayName: 'GoPay',
|
||||
logoUrl: '/logos/gopay.png',
|
||||
instructions: 'Send payment to the number above and upload proof.',
|
||||
isActive: true,
|
||||
sortOrder: 3,
|
||||
},
|
||||
});
|
||||
|
||||
console.log('✅ Payment methods created:', [bcaMethod.displayName, mandiriMethod.displayName, gopayMethod.displayName]);
|
||||
|
||||
// ============================================
|
||||
// 4. CREATE APP CONFIG (Optional)
|
||||
// ============================================
|
||||
console.log('\n⚙️ Creating app config...');
|
||||
|
||||
await prisma.appConfig.upsert({
|
||||
where: { key: 'MAINTENANCE_MODE' },
|
||||
update: {},
|
||||
create: {
|
||||
key: 'MAINTENANCE_MODE',
|
||||
value: 'false',
|
||||
category: 'general',
|
||||
label: 'Maintenance Mode',
|
||||
description: 'Enable to show maintenance page to users',
|
||||
type: 'boolean',
|
||||
isSecret: false,
|
||||
},
|
||||
});
|
||||
|
||||
console.log('✅ App config created');
|
||||
|
||||
// ============================================
|
||||
// 5. CREATE TEMP USER & WALLET (Legacy)
|
||||
// ============================================
|
||||
console.log('\n🔧 Creating temp user (legacy)...');
|
||||
|
||||
const user = await prisma.user.upsert({
|
||||
where: { id: TEMP_USER_ID },
|
||||
update: {},
|
||||
@@ -19,9 +241,7 @@ async function main() {
|
||||
},
|
||||
});
|
||||
|
||||
// create a sample money wallet if none
|
||||
const existing = await prisma.wallet.findFirst({});
|
||||
|
||||
if (!existing) {
|
||||
await prisma.wallet.create({
|
||||
data: {
|
||||
@@ -32,8 +252,20 @@ async function main() {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
console.log('✅ Temp user created:', user.id);
|
||||
|
||||
console.log('Seed complete. TEMP_USER_ID=', user.id);
|
||||
// ============================================
|
||||
// SUMMARY
|
||||
// ============================================
|
||||
console.log('\n🎉 Seed complete!');
|
||||
console.log('\n📋 Summary:');
|
||||
console.log(' Admin Email:', ADMIN_EMAIL);
|
||||
console.log(' Admin Password:', ADMIN_PASSWORD);
|
||||
console.log(' Plans:', [freePlan.name, proMonthly.name, proYearly.name].join(', '));
|
||||
console.log(' Payment Methods:', [bcaMethod.displayName, mandiriMethod.displayName, gopayMethod.displayName].join(', '));
|
||||
console.log('\n⚠️ IMPORTANT: Change admin password after first login!');
|
||||
console.log('\n🔗 Login at: http://localhost:5174/auth/login');
|
||||
}
|
||||
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user