chore: remediate CI blockers for production readiness

This commit is contained in:
Ho Ngoc Hai
2026-05-07 13:08:20 +07:00
parent f82806e06d
commit b35ec55126
32 changed files with 401 additions and 113 deletions

View File

@@ -4,8 +4,10 @@
* Seeds ALL 27 models with realistic Vietnamese real estate data.
* Idempotent: safe to run multiple times (uses upsert + ON CONFLICT).
*
* Default admin account:
* Phone: 0876677771 | Email: hongochai10@icloud.com | Password: Velik@2026
* Seed admin account:
* Phone: 0876677771 | Email: hongochai10@icloud.com
*
* Set SEED_DEFAULT_PASSWORD before running this script.
*/
import * as crypto from 'node:crypto';
@@ -51,8 +53,25 @@ const prisma = new PrismaClient({ adapter });
// Constants
// =============================================================================
const DEFAULT_PASSWORD = 'Velik@2026';
const BCRYPT_ROUNDS = 12;
function getRequiredEnv(name: string): string {
const value = process.env[name]?.trim();
if (!value) {
throw new Error(`${name} must be set before running prisma seed`);
}
return value;
}
function getBcryptRounds(): number {
const raw = process.env['BCRYPT_ROUNDS'] ?? '12';
const rounds = Number.parseInt(raw, 10);
if (!Number.isInteger(rounds) || rounds < 4) {
throw new Error('BCRYPT_ROUNDS must be an integer >= 4');
}
return rounds;
}
const SEED_DEFAULT_PASSWORD = getRequiredEnv('SEED_DEFAULT_PASSWORD');
const BCRYPT_ROUNDS = getBcryptRounds();
const now = new Date();
const oneMonthAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
@@ -133,7 +152,7 @@ async function seedUsers(passwordHash: string) {
});
}
console.log(`${users.length} users seeded (all with password: ${DEFAULT_PASSWORD})`);
console.log(`${users.length} users seeded (password configured via SEED_DEFAULT_PASSWORD)`);
}
// =============================================================================
@@ -429,7 +448,30 @@ async function seedProperties() {
${p.yearBuilt ?? null}, ${p.legalStatus ?? null}, ${p.amenities ?? null}::jsonb, ${null}::jsonb,
${null}, ${p.projectName ?? null}, NOW(), NOW()
)
ON CONFLICT ("id") DO NOTHING
ON CONFLICT ("id") DO UPDATE SET
"propertyType" = EXCLUDED."propertyType",
"title" = EXCLUDED."title",
"description" = EXCLUDED."description",
"address" = EXCLUDED."address",
"ward" = EXCLUDED."ward",
"district" = EXCLUDED."district",
"city" = EXCLUDED."city",
"location" = EXCLUDED."location",
"areaM2" = EXCLUDED."areaM2",
"usableAreaM2" = EXCLUDED."usableAreaM2",
"bedrooms" = EXCLUDED."bedrooms",
"bathrooms" = EXCLUDED."bathrooms",
"floors" = EXCLUDED."floors",
"floor" = EXCLUDED."floor",
"totalFloors" = EXCLUDED."totalFloors",
"direction" = EXCLUDED."direction",
"yearBuilt" = EXCLUDED."yearBuilt",
"legalStatus" = EXCLUDED."legalStatus",
"amenities" = EXCLUDED."amenities",
"nearbyPOIs" = EXCLUDED."nearbyPOIs",
"metroDistanceM" = EXCLUDED."metroDistanceM",
"projectName" = EXCLUDED."projectName",
"updatedAt" = NOW()
`;
}
@@ -742,8 +784,8 @@ async function main() {
console.log('━'.repeat(60));
// Pre-compute password hash
console.log('🔑 Hashing default password...');
const passwordHash = bcrypt.hashSync(DEFAULT_PASSWORD, BCRYPT_ROUNDS);
console.log('🔑 Hashing seed password...');
const passwordHash = bcrypt.hashSync(SEED_DEFAULT_PASSWORD, BCRYPT_ROUNDS);
console.log(` ✓ Password hash computed (bcrypt, ${BCRYPT_ROUNDS} rounds)\n`);
// Phase 1 — Plans
@@ -840,8 +882,8 @@ async function main() {
console.log('\n🔐 Admin Login:');
console.log(' Phone: 0876677771');
console.log(' Email: hongochai10@icloud.com');
console.log(' Password: Velik@2026');
console.log(' (All users share the same password)\n');
console.log(' Password: configured via SEED_DEFAULT_PASSWORD');
console.log(' (All seeded users share the configured seed password)\n');
}
main()