664 lines
19 KiB
TypeScript
664 lines
19 KiB
TypeScript
import Database from 'better-sqlite3';
|
|
import { drizzle } from 'drizzle-orm/better-sqlite3';
|
|
import * as schema from '../lib/db/schema';
|
|
import { sql, eq } from 'drizzle-orm';
|
|
import { randomUUID } from 'crypto';
|
|
import bcrypt from 'bcryptjs';
|
|
|
|
const dbPath = process.env.DATABASE_URL || './data/sqlite.db';
|
|
const sqlite = new Database(dbPath);
|
|
const db = drizzle(sqlite, { schema });
|
|
|
|
interface SetupOptions {
|
|
reset?: boolean;
|
|
seedData?: boolean;
|
|
verbose?: boolean;
|
|
}
|
|
|
|
async function setupDatabase(options: SetupOptions = {}) {
|
|
const { reset = false, seedData = true, verbose = false } = options;
|
|
|
|
try {
|
|
console.log('🚀 Starting database setup...\n');
|
|
|
|
if (reset) {
|
|
await resetTables(verbose);
|
|
}
|
|
|
|
await createTables(verbose);
|
|
await seedBasicData(verbose);
|
|
|
|
if (seedData) {
|
|
await seedSampleData(verbose);
|
|
}
|
|
|
|
console.log('✅ Database setup completed successfully!\n');
|
|
|
|
// Print summary
|
|
await printDatabaseSummary();
|
|
} catch (error) {
|
|
console.error('❌ Database setup failed:', error);
|
|
throw error;
|
|
} finally {
|
|
sqlite.close();
|
|
}
|
|
}
|
|
|
|
async function resetTables(verbose: boolean) {
|
|
if (verbose) console.log('🗑️ Resetting database tables...');
|
|
|
|
const tables = [
|
|
'activity_logs',
|
|
'metrics',
|
|
'bookings',
|
|
'announcements',
|
|
'time_slots',
|
|
'settings',
|
|
'courts',
|
|
'users',
|
|
'__drizzle_migrations',
|
|
'__old_push_courts',
|
|
'__old_push_users',
|
|
];
|
|
|
|
for (const table of tables) {
|
|
try {
|
|
await db.run(sql.raw(`DROP TABLE IF EXISTS ${table}`));
|
|
if (verbose) console.log(` ✓ Dropped table: ${table}`);
|
|
} catch (error) {
|
|
if (verbose) console.log(` - Table ${table} doesn't exist or error dropping`);
|
|
}
|
|
}
|
|
|
|
console.log('✅ Tables reset complete\n');
|
|
}
|
|
|
|
async function createTables(verbose: boolean) {
|
|
if (verbose) console.log('🏗️ Creating database tables...');
|
|
|
|
// Users table
|
|
await db.run(sql`
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id TEXT PRIMARY KEY,
|
|
email TEXT NOT NULL UNIQUE,
|
|
name TEXT NOT NULL,
|
|
surname TEXT NOT NULL,
|
|
password TEXT NOT NULL,
|
|
role TEXT NOT NULL DEFAULT 'user' CHECK (role IN ('user', 'admin')),
|
|
theme_preference TEXT DEFAULT 'system' CHECK (theme_preference IN ('light', 'dark', 'system')),
|
|
created_at INTEGER NOT NULL,
|
|
updated_at INTEGER NOT NULL
|
|
)
|
|
`);
|
|
|
|
// Courts table
|
|
await db.run(sql`
|
|
CREATE TABLE IF NOT EXISTS courts (
|
|
id TEXT PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
is_active INTEGER NOT NULL DEFAULT 1,
|
|
created_at INTEGER NOT NULL,
|
|
updated_at INTEGER NOT NULL
|
|
)
|
|
`);
|
|
|
|
// Settings table
|
|
await db.run(sql`
|
|
CREATE TABLE IF NOT EXISTS settings (
|
|
id TEXT PRIMARY KEY,
|
|
key TEXT NOT NULL UNIQUE,
|
|
value TEXT NOT NULL,
|
|
updated_at INTEGER NOT NULL
|
|
)
|
|
`);
|
|
|
|
// Time slots table
|
|
await db.run(sql`
|
|
CREATE TABLE IF NOT EXISTS time_slots (
|
|
id TEXT PRIMARY KEY,
|
|
day_of_week INTEGER NOT NULL,
|
|
start_time TEXT NOT NULL,
|
|
end_time TEXT NOT NULL,
|
|
is_active INTEGER NOT NULL DEFAULT 1,
|
|
created_at INTEGER NOT NULL,
|
|
updated_at INTEGER NOT NULL
|
|
)
|
|
`);
|
|
|
|
// Bookings table
|
|
await db.run(sql`
|
|
CREATE TABLE IF NOT EXISTS bookings (
|
|
id TEXT PRIMARY KEY,
|
|
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
court_id TEXT NOT NULL REFERENCES courts(id) ON DELETE CASCADE,
|
|
date TEXT NOT NULL,
|
|
start_time TEXT NOT NULL,
|
|
end_time TEXT NOT NULL,
|
|
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'cancelled')),
|
|
notes TEXT,
|
|
partner_name TEXT,
|
|
created_at INTEGER NOT NULL,
|
|
updated_at INTEGER NOT NULL
|
|
)
|
|
`);
|
|
|
|
// Announcements table
|
|
await db.run(sql`
|
|
CREATE TABLE IF NOT EXISTS announcements (
|
|
id TEXT PRIMARY KEY,
|
|
title TEXT NOT NULL,
|
|
content TEXT NOT NULL,
|
|
is_active INTEGER NOT NULL DEFAULT 1,
|
|
priority TEXT NOT NULL DEFAULT 'medium' CHECK (priority IN ('low', 'medium', 'high')),
|
|
expires_at INTEGER,
|
|
created_at INTEGER NOT NULL,
|
|
updated_at INTEGER NOT NULL
|
|
)
|
|
`);
|
|
|
|
// Activity logs table
|
|
await db.run(sql`
|
|
CREATE TABLE IF NOT EXISTS activity_logs (
|
|
id TEXT PRIMARY KEY,
|
|
user_id TEXT REFERENCES users(id) ON DELETE SET NULL,
|
|
action TEXT NOT NULL,
|
|
entity_type TEXT NOT NULL,
|
|
entity_id TEXT,
|
|
details TEXT,
|
|
ip_address TEXT,
|
|
user_agent TEXT,
|
|
created_at INTEGER NOT NULL
|
|
)
|
|
`);
|
|
|
|
// Metrics table
|
|
await db.run(sql`
|
|
CREATE TABLE IF NOT EXISTS metrics (
|
|
id TEXT PRIMARY KEY,
|
|
metric_type TEXT NOT NULL,
|
|
period TEXT NOT NULL,
|
|
value INTEGER NOT NULL DEFAULT 0,
|
|
created_at INTEGER NOT NULL,
|
|
updated_at INTEGER NOT NULL
|
|
)
|
|
`);
|
|
|
|
if (verbose) console.log(' ✓ All tables created successfully');
|
|
console.log('✅ Database schema ready\n');
|
|
}
|
|
|
|
async function seedBasicData(verbose: boolean) {
|
|
console.log('🌱 Seeding essential data...');
|
|
|
|
const now = Date.now();
|
|
|
|
// Check if users already exist
|
|
const existingUsers = await db.select().from(schema.users);
|
|
|
|
if (existingUsers.length === 0) {
|
|
// Create admin user
|
|
const adminPassword = await bcrypt.hash('admin123', 12);
|
|
const adminId = randomUUID();
|
|
|
|
await db.insert(schema.users).values({
|
|
id: adminId,
|
|
email: 'admin@tabletennis.com',
|
|
name: 'Admin',
|
|
surname: 'User',
|
|
password: adminPassword,
|
|
role: 'admin',
|
|
themePreference: 'system',
|
|
createdAt: new Date(now),
|
|
updatedAt: new Date(now),
|
|
});
|
|
|
|
// Create test user
|
|
const userPassword = await bcrypt.hash('user123', 12);
|
|
const userId = randomUUID();
|
|
|
|
await db.insert(schema.users).values({
|
|
id: userId,
|
|
email: 'user@tabletennis.com',
|
|
name: 'Test',
|
|
surname: 'User',
|
|
password: userPassword,
|
|
role: 'user',
|
|
themePreference: 'system',
|
|
createdAt: new Date(now),
|
|
updatedAt: new Date(now),
|
|
});
|
|
|
|
if (verbose) console.log(' ✓ Created admin and test users');
|
|
} else {
|
|
if (verbose) console.log(' - Users already exist, skipping user creation');
|
|
}
|
|
|
|
// Check if courts already exist
|
|
const existingCourts = await db.select().from(schema.courts);
|
|
|
|
if (existingCourts.length === 0) {
|
|
// Create courts
|
|
const courtIds = [randomUUID(), randomUUID(), randomUUID()];
|
|
await db.insert(schema.courts).values([
|
|
{
|
|
id: courtIds[0],
|
|
name: 'Court 1',
|
|
isActive: true,
|
|
createdAt: new Date(now),
|
|
updatedAt: new Date(now),
|
|
},
|
|
{
|
|
id: courtIds[1],
|
|
name: 'Court 2',
|
|
isActive: true,
|
|
createdAt: new Date(now),
|
|
updatedAt: new Date(now),
|
|
},
|
|
{
|
|
id: courtIds[2],
|
|
name: 'Court 3',
|
|
isActive: true,
|
|
createdAt: new Date(now),
|
|
updatedAt: new Date(now),
|
|
},
|
|
]);
|
|
|
|
if (verbose) console.log(' ✓ Created 3 courts');
|
|
} else {
|
|
if (verbose) console.log(' - Courts already exist, skipping court creation');
|
|
}
|
|
|
|
// Insert system settings
|
|
const defaultSettings = [
|
|
{ key: 'booking_window_days', value: '14' },
|
|
{ key: 'max_booking_duration_hours', value: '2' },
|
|
{ key: 'max_bookings_per_user_per_hour_per_day', value: '1' },
|
|
{ key: 'allow_booking_modifications', value: 'true' },
|
|
{ key: 'booking_modification_hours_before', value: '2' },
|
|
{ key: 'min_booking_duration_minutes', value: '60' },
|
|
{ key: 'booking_start_time', value: '08:00' },
|
|
{ key: 'booking_end_time', value: '22:00' },
|
|
{ key: 'allow_weekend_bookings', value: 'true' },
|
|
{ key: 'facility_name', value: 'Table Tennis Club' },
|
|
{ key: 'facility_email', value: 'info@tabletennis.com' },
|
|
{ key: 'facility_phone', value: '+353-1-234-5678' },
|
|
];
|
|
|
|
for (const setting of defaultSettings) {
|
|
const existingSetting = await db
|
|
.select()
|
|
.from(schema.settings)
|
|
.where(eq(schema.settings.key, setting.key))
|
|
.limit(1);
|
|
|
|
if (existingSetting.length === 0) {
|
|
await db.insert(schema.settings).values({
|
|
id: randomUUID(),
|
|
key: setting.key,
|
|
value: setting.value,
|
|
updatedAt: new Date(now),
|
|
});
|
|
if (verbose) console.log(` ✓ Setting: ${setting.key} = ${setting.value}`);
|
|
}
|
|
}
|
|
|
|
// Check if time slots already exist
|
|
const existingTimeSlots = await db.select().from(schema.timeSlots);
|
|
|
|
if (existingTimeSlots.length === 0) {
|
|
// Create time slots - Operating hours for each day
|
|
const timeSlotData = [
|
|
// Sunday: 12:00 - 17:00 (shorter hours)
|
|
{ dayOfWeek: 0, startTime: '12:00', endTime: '17:00' },
|
|
|
|
// Monday to Thursday: 18:00 - 23:00 (evening sessions)
|
|
{ dayOfWeek: 1, startTime: '18:00', endTime: '23:00' },
|
|
{ dayOfWeek: 2, startTime: '18:00', endTime: '23:00' },
|
|
{ dayOfWeek: 3, startTime: '18:00', endTime: '23:00' },
|
|
{ dayOfWeek: 4, startTime: '18:00', endTime: '23:00' },
|
|
|
|
// Friday: 17:00 - 22:00 (earlier end)
|
|
{ dayOfWeek: 5, startTime: '17:00', endTime: '22:00' },
|
|
|
|
// Saturday: 10:00 - 18:00 (full day weekend)
|
|
{ dayOfWeek: 6, startTime: '10:00', endTime: '18:00' },
|
|
];
|
|
|
|
for (const slot of timeSlotData) {
|
|
await db.insert(schema.timeSlots).values({
|
|
id: randomUUID(),
|
|
dayOfWeek: slot.dayOfWeek,
|
|
startTime: slot.startTime,
|
|
endTime: slot.endTime,
|
|
isActive: true,
|
|
createdAt: new Date(now),
|
|
updatedAt: new Date(now),
|
|
});
|
|
}
|
|
|
|
if (verbose) console.log(' ✓ Created time slots for all days');
|
|
} else {
|
|
if (verbose) console.log(' - Time slots already exist, skipping time slot creation');
|
|
}
|
|
|
|
// Check if announcements already exist
|
|
const existingAnnouncements = await db.select().from(schema.announcements);
|
|
|
|
if (existingAnnouncements.length === 0) {
|
|
// Create essential announcements
|
|
const essentialAnnouncements = [
|
|
{
|
|
id: randomUUID(),
|
|
title: 'Welcome to Table Tennis Booking System!',
|
|
content:
|
|
'Book your court times easily and manage your games efficiently. Check the time slots for availability and remember to arrive 5 minutes early.',
|
|
isActive: true,
|
|
priority: 'high' as const,
|
|
expiresAt: null,
|
|
createdAt: new Date(now),
|
|
updatedAt: new Date(now),
|
|
},
|
|
{
|
|
id: randomUUID(),
|
|
title: 'Booking Guidelines',
|
|
content:
|
|
'Maximum booking duration is 2 hours. Please cancel bookings you cannot attend to allow others to use the courts.',
|
|
isActive: true,
|
|
priority: 'medium' as const,
|
|
expiresAt: null,
|
|
createdAt: new Date(now),
|
|
updatedAt: new Date(now),
|
|
},
|
|
];
|
|
|
|
await db.insert(schema.announcements).values(essentialAnnouncements);
|
|
|
|
if (verbose) console.log(' ✓ Created essential announcements');
|
|
} else {
|
|
if (verbose) console.log(' - Announcements already exist, skipping announcement creation');
|
|
}
|
|
|
|
// Initialize monthly metrics if they don't exist
|
|
const currentMonth = new Date().toISOString().substring(0, 7);
|
|
const existingMetrics = await db
|
|
.select()
|
|
.from(schema.metrics)
|
|
.where(eq(schema.metrics.period, currentMonth))
|
|
.limit(1);
|
|
|
|
if (existingMetrics.length === 0) {
|
|
await db.insert(schema.metrics).values({
|
|
id: randomUUID(),
|
|
metricType: 'monthly_bookings',
|
|
period: currentMonth,
|
|
value: 0,
|
|
createdAt: new Date(now),
|
|
updatedAt: new Date(now),
|
|
});
|
|
|
|
if (verbose) console.log(' ✓ Initialized monthly metrics');
|
|
} else {
|
|
if (verbose) console.log(' - Monthly metrics already exist');
|
|
}
|
|
|
|
console.log('✅ Essential data seeded\n');
|
|
}
|
|
|
|
async function seedSampleData(verbose: boolean) {
|
|
console.log('🎭 Seeding sample data...');
|
|
|
|
const now = Date.now();
|
|
const users = await db.select().from(schema.users);
|
|
const courts = await db.select().from(schema.courts);
|
|
|
|
const adminUser = users.find((u) => u.role === 'admin');
|
|
const regularUser = users.find((u) => u.role === 'user');
|
|
|
|
if (!adminUser || !regularUser) {
|
|
console.log('⚠️ No users found for sample data');
|
|
return;
|
|
}
|
|
|
|
// Sample bookings for the next few days
|
|
const today = new Date();
|
|
const tomorrow = new Date(today.getTime() + 24 * 60 * 60 * 1000);
|
|
const dayAfter = new Date(today.getTime() + 48 * 60 * 60 * 1000);
|
|
|
|
const sampleBookings = [
|
|
{
|
|
id: randomUUID(),
|
|
userId: regularUser.id,
|
|
courtId: courts[0].id,
|
|
date: today.toISOString().split('T')[0],
|
|
startTime: '19:00',
|
|
endTime: '20:00',
|
|
status: 'active' as const,
|
|
notes: 'Regular evening practice session',
|
|
partnerName: 'John Smith',
|
|
createdAt: new Date(now - 2 * 60 * 60 * 1000),
|
|
updatedAt: new Date(now - 2 * 60 * 60 * 1000),
|
|
},
|
|
{
|
|
id: randomUUID(),
|
|
userId: regularUser.id,
|
|
courtId: courts[1]?.id || courts[0].id,
|
|
date: tomorrow.toISOString().split('T')[0],
|
|
startTime: '20:00',
|
|
endTime: '21:00',
|
|
status: 'active' as const,
|
|
notes: 'Tournament preparation',
|
|
partnerName: null,
|
|
createdAt: new Date(now - 1 * 60 * 60 * 1000),
|
|
updatedAt: new Date(now - 1 * 60 * 60 * 1000),
|
|
},
|
|
{
|
|
id: randomUUID(),
|
|
userId: adminUser.id,
|
|
courtId: courts[2]?.id || courts[0].id,
|
|
date: dayAfter.toISOString().split('T')[0],
|
|
startTime: '18:00',
|
|
endTime: '20:00',
|
|
status: 'active' as const,
|
|
notes: 'Staff training session',
|
|
partnerName: 'Staff Team',
|
|
createdAt: new Date(now - 30 * 60 * 1000),
|
|
updatedAt: new Date(now - 30 * 60 * 1000),
|
|
},
|
|
];
|
|
|
|
await db.insert(schema.bookings).values(sampleBookings);
|
|
|
|
// Sample activity logs
|
|
const sampleLogs = [
|
|
{
|
|
id: randomUUID(),
|
|
userId: adminUser.id,
|
|
action: 'login',
|
|
entityType: 'user',
|
|
entityId: adminUser.id,
|
|
details: JSON.stringify({
|
|
email: adminUser.email,
|
|
role: adminUser.role,
|
|
loginMethod: 'password',
|
|
}),
|
|
ipAddress: '192.168.1.100',
|
|
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
|
|
createdAt: new Date(now - 3 * 60 * 60 * 1000),
|
|
},
|
|
{
|
|
id: randomUUID(),
|
|
userId: regularUser.id,
|
|
action: 'create_booking',
|
|
entityType: 'booking',
|
|
entityId: sampleBookings[0].id,
|
|
details: JSON.stringify({
|
|
courtId: courts[0].id,
|
|
courtName: courts[0].name,
|
|
date: today.toISOString().split('T')[0],
|
|
startTime: '19:00',
|
|
endTime: '20:00',
|
|
}),
|
|
ipAddress: '192.168.1.101',
|
|
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15',
|
|
createdAt: new Date(now - 2 * 60 * 60 * 1000),
|
|
},
|
|
];
|
|
|
|
await db.insert(schema.activityLogs).values(sampleLogs);
|
|
|
|
// Additional announcements
|
|
const additionalAnnouncements = [
|
|
{
|
|
id: randomUUID(),
|
|
title: 'New Court Rules',
|
|
content:
|
|
'Please remember to clean up after your sessions and respect the time limits. Equipment should be returned to the storage area.',
|
|
isActive: true,
|
|
priority: 'medium' as const,
|
|
expiresAt: new Date(now + 7 * 24 * 60 * 60 * 1000), // 1 week from now
|
|
createdAt: new Date(now - 4 * 60 * 60 * 1000),
|
|
updatedAt: new Date(now - 4 * 60 * 60 * 1000),
|
|
},
|
|
{
|
|
id: randomUUID(),
|
|
title: 'Tournament Sign-ups Open!',
|
|
content:
|
|
'The annual table tennis tournament sign-ups are now open! Register by the end of this month. Prizes for winners in each category.',
|
|
isActive: true,
|
|
priority: 'high' as const,
|
|
expiresAt: new Date(now + 30 * 24 * 60 * 60 * 1000), // 30 days from now
|
|
createdAt: new Date(now - 24 * 60 * 60 * 1000),
|
|
updatedAt: new Date(now - 24 * 60 * 60 * 1000),
|
|
},
|
|
{
|
|
id: randomUUID(),
|
|
title: 'Equipment Maintenance',
|
|
content:
|
|
'New paddles and balls have been added to the equipment collection. Old equipment will be replaced gradually.',
|
|
isActive: true,
|
|
priority: 'low' as const,
|
|
expiresAt: new Date(now + 14 * 24 * 60 * 60 * 1000), // 2 weeks from now
|
|
createdAt: new Date(now - 6 * 60 * 60 * 1000),
|
|
updatedAt: new Date(now - 6 * 60 * 60 * 1000),
|
|
},
|
|
];
|
|
|
|
await db.insert(schema.announcements).values(additionalAnnouncements);
|
|
|
|
if (verbose) {
|
|
console.log(` ✓ Created ${sampleBookings.length} sample bookings`);
|
|
console.log(` ✓ Created ${sampleLogs.length} activity logs`);
|
|
console.log(` ✓ Created ${additionalAnnouncements.length} additional announcements`);
|
|
}
|
|
|
|
console.log('✅ Sample data seeded\n');
|
|
}
|
|
|
|
async function printDatabaseSummary() {
|
|
console.log('📊 Database Summary:');
|
|
console.log('═══════════════════════════════════════\n');
|
|
|
|
const users = await db.select().from(schema.users);
|
|
const courts = await db.select().from(schema.courts);
|
|
const bookings = await db.select().from(schema.bookings);
|
|
const announcements = await db.select().from(schema.announcements);
|
|
const timeSlots = await db.select().from(schema.timeSlots);
|
|
const settings = await db.select().from(schema.settings);
|
|
|
|
console.log(`👥 Users: ${users.length}`);
|
|
users.forEach((user) => {
|
|
console.log(` • ${user.name} ${user.surname} (${user.email}) - ${user.role}`);
|
|
});
|
|
|
|
console.log(`\n🏓 Courts: ${courts.length}`);
|
|
courts.forEach((court) => {
|
|
console.log(` • ${court.name} - ${court.isActive ? 'Active' : 'Inactive'}`);
|
|
});
|
|
|
|
console.log(`\n📅 Bookings: ${bookings.length}`);
|
|
if (bookings.length > 0) {
|
|
console.log(' Recent bookings:');
|
|
bookings.slice(0, 3).forEach((booking) => {
|
|
console.log(` • ${booking.date} ${booking.startTime}-${booking.endTime} (${booking.status})`);
|
|
});
|
|
}
|
|
|
|
console.log(`\n📢 Announcements: ${announcements.length}`);
|
|
const activeAnnouncements = announcements.filter((a) => a.isActive);
|
|
console.log(` • Active: ${activeAnnouncements.length}`);
|
|
|
|
console.log(`\n⏰ Time Slots: ${timeSlots.length}`);
|
|
const dayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
|
|
timeSlots.forEach((slot) => {
|
|
console.log(` • ${dayNames[slot.dayOfWeek]}: ${slot.startTime}-${slot.endTime}`);
|
|
});
|
|
|
|
console.log(`\n⚙️ Settings: ${settings.length} configured`);
|
|
|
|
console.log('\n💡 Login Credentials:');
|
|
console.log(' Admin: admin@tabletennis.com / admin123');
|
|
console.log(' User: user@tabletennis.com / user123');
|
|
|
|
console.log('\n🚀 Ready to start! Run: npm run dev');
|
|
console.log('═══════════════════════════════════════\n');
|
|
}
|
|
|
|
// Parse command line arguments
|
|
function parseArgs() {
|
|
const args = process.argv.slice(2);
|
|
const options: SetupOptions = {};
|
|
|
|
if (args.includes('--reset') || args.includes('-r')) {
|
|
options.reset = true;
|
|
}
|
|
|
|
if (args.includes('--no-sample-data') || args.includes('--essential-only')) {
|
|
options.seedData = false;
|
|
}
|
|
|
|
if (args.includes('--verbose') || args.includes('-v')) {
|
|
options.verbose = true;
|
|
}
|
|
|
|
if (args.includes('--help') || args.includes('-h')) {
|
|
console.log(`
|
|
Table Tennis Booking System - Database Setup
|
|
|
|
Usage: tsx scripts/setup-database.ts [options]
|
|
|
|
Options:
|
|
--reset, -r Reset/drop all tables before setup
|
|
--no-sample-data Only seed essential data (no sample bookings/logs)
|
|
--essential-only Same as --no-sample-data
|
|
--verbose, -v Show detailed output
|
|
--help, -h Show this help message
|
|
|
|
Examples:
|
|
tsx scripts/setup-database.ts # Full setup with sample data
|
|
tsx scripts/setup-database.ts --reset # Reset and full setup
|
|
tsx scripts/setup-database.ts --essential-only # Only essential data
|
|
tsx scripts/setup-database.ts --reset --verbose # Reset with detailed output
|
|
`);
|
|
process.exit(0);
|
|
}
|
|
|
|
return options;
|
|
}
|
|
|
|
// Main execution
|
|
if (require.main === module) {
|
|
const options = parseArgs();
|
|
|
|
setupDatabase(options)
|
|
.then(() => {
|
|
console.log('🎉 Database setup completed successfully!');
|
|
process.exit(0);
|
|
})
|
|
.catch((error) => {
|
|
console.error('💥 Database setup failed:', error);
|
|
process.exit(1);
|
|
});
|
|
}
|
|
|
|
export { setupDatabase };
|