Files
goodgo-platform/scripts/seed-plans.ts
Ho Ngoc Hai af71270a2e feat: upgrade major dependencies to latest versions
- Prisma 6.19 → 7.7 (driver adapter pattern, prisma.config.ts)
- TypeScript 5.9 → 6.0 (ignoreDeprecations, CSS type declarations)
- Vitest 3.2 → 4.1
- Pino 9.14 → 10.3
- @types/node 22.x → 25.x

All 307 tests pass, typecheck clean, build succeeds.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-08 13:15:36 +07:00

134 lines
3.2 KiB
TypeScript

/**
* Seed subscription plans (FREE, AGENT_PRO, INVESTOR, ENTERPRISE).
*
* Usage: npx tsx scripts/seed-plans.ts
* Idempotent: uses upsert on PlanTier unique constraint.
*/
import { PrismaPg } from '@prisma/adapter-pg';
import { PrismaClient, PlanTier } from '@prisma/client';
import pg from 'pg';
const pool = new pg.Pool({ connectionString: process.env['DATABASE_URL'] });
const adapter = new PrismaPg(pool);
const prisma = new PrismaClient({ adapter });
export const PLANS = [
{
tier: PlanTier.FREE,
name: 'Miễn phí',
priceMonthlyVND: BigInt(0),
priceYearlyVND: BigInt(0),
maxListings: 3,
maxSavedSearches: 5,
features: {
basicSearch: true,
listingPost: true,
maxPhotos: 5,
analytics: false,
prioritySupport: false,
aiValuation: false,
featuredListing: false,
},
},
{
tier: PlanTier.AGENT_PRO,
name: 'Agent Pro',
priceMonthlyVND: BigInt(499_000),
priceYearlyVND: BigInt(4_990_000),
maxListings: 50,
maxSavedSearches: 30,
features: {
basicSearch: true,
listingPost: true,
maxPhotos: 30,
analytics: true,
prioritySupport: true,
aiValuation: true,
featuredListing: true,
leadManagement: true,
agentProfile: true,
},
},
{
tier: PlanTier.INVESTOR,
name: 'Investor',
priceMonthlyVND: BigInt(999_000),
priceYearlyVND: BigInt(9_990_000),
maxListings: 20,
maxSavedSearches: 100,
features: {
basicSearch: true,
listingPost: true,
maxPhotos: 15,
analytics: true,
prioritySupport: true,
aiValuation: true,
featuredListing: false,
marketReports: true,
priceAlerts: true,
portfolioTracking: true,
},
},
{
tier: PlanTier.ENTERPRISE,
name: 'Enterprise',
priceMonthlyVND: BigInt(4_990_000),
priceYearlyVND: BigInt(49_900_000),
maxListings: null,
maxSavedSearches: null,
features: {
basicSearch: true,
listingPost: true,
maxPhotos: 100,
analytics: true,
prioritySupport: true,
aiValuation: true,
featuredListing: true,
leadManagement: true,
agentProfile: true,
marketReports: true,
priceAlerts: true,
portfolioTracking: true,
apiAccess: true,
whiteLabel: true,
dedicatedSupport: true,
},
},
];
async function seedPlans() {
console.log('Seeding subscription plans...\n');
for (const plan of PLANS) {
const _result = await prisma.plan.upsert({
where: { tier: plan.tier },
update: {
name: plan.name,
priceMonthlyVND: plan.priceMonthlyVND,
priceYearlyVND: plan.priceYearlyVND,
maxListings: plan.maxListings,
maxSavedSearches: plan.maxSavedSearches,
features: plan.features,
},
create: plan,
});
const monthly = Number(plan.priceMonthlyVND).toLocaleString('vi-VN');
console.log(` ${plan.tier.padEnd(12)} ${plan.name.padEnd(14)} ${monthly} VND/tháng`);
}
console.log(`\n${PLANS.length} plans seeded.`);
}
if (require.main === module) {
seedPlans()
.catch((e) => {
console.error('Seed plans failed:', e);
process.exit(1);
})
.finally(() => prisma.$disconnect());
}
export { seedPlans };