Refactor production setup and database management

- Updated Dockerfile for production to ensure proper database permissions and improved startup script.
- Removed outdated PRODUCTION_README.md and consolidated relevant information into other documentation.
- Enhanced deploy.sh script to fix database permissions and streamline deployment process.
- Modified docker-compose configuration to use a dedicated production file and adjusted port mappings.
- Removed legacy docker-compose.yml file to avoid confusion.
- Improved session management by refining secure cookie settings based on environment variables.
- Deleted obsolete Nginx configuration and old seed scripts to clean up the project structure.
- Updated database setup scripts to reflect new structure and removed old seed data scripts.
- Adjusted reset-db script to use environment variable for database path.
- Enhanced setup-database script to provide dynamic admin credentials in the summary.
- Removed unnecessary backup file for SQLite database.
This commit is contained in:
2025-09-28 16:32:31 +01:00
parent 6706ba7f1f
commit 2d31c49235
19 changed files with 69 additions and 1143 deletions
-101
View File
@@ -1,101 +0,0 @@
#!/bin/bash
# Cleanup old seed scripts and organize database utilities
# This script consolidates old individual seed scripts into the new unified system
echo "🧹 Cleaning up old database scripts..."
# Create a backup directory for old scripts
mkdir -p scripts/old-seeds
# Move old individual seed scripts to backup
echo "📦 Moving old seed scripts to scripts/old-seeds/..."
if [ -f "scripts/seed-data.ts" ]; then
mv scripts/seed-data.ts scripts/old-seeds/
echo " ✓ Moved seed-data.ts"
fi
if [ -f "scripts/seed-announcements.ts" ]; then
mv scripts/seed-announcements.ts scripts/old-seeds/
echo " ✓ Moved seed-announcements.ts"
fi
if [ -f "scripts/seed-time-slots.ts" ]; then
mv scripts/seed-time-slots.ts scripts/old-seeds/
echo " ✓ Moved seed-time-slots.ts"
fi
if [ -f "scripts/init-admin-data.ts" ]; then
mv scripts/init-admin-data.ts scripts/old-seeds/
echo " ✓ Moved init-admin-data.ts"
fi
# Remove old reset script if it exists
if [ -f "scripts/reset-database.ts" ]; then
mv scripts/reset-database.ts scripts/old-seeds/
echo " ✓ Moved old reset-database.ts"
fi
# Create a README in the old-seeds directory
cat > scripts/old-seeds/README.md << 'EOF'
# Old Seed Scripts (Archived)
These are the original individual seed scripts that have been consolidated into the new unified database setup system.
## Consolidated Into:
- **setup-database.ts** - Unified setup script with all functionality
- **reset-db.ts** - Improved reset script with safety features
## Original Scripts:
- `seed-data.ts` - Sample bookings and activity logs
- `seed-announcements.ts` - Test announcements
- `seed-time-slots.ts` - Time slot configuration
- `init-admin-data.ts` - Admin dashboard initialization
- `reset-database.ts` - Original reset script
## Migration Notes:
All functionality from these scripts has been intelligently integrated into the new system:
### New Command Equivalents:
| Old Script | New Command |
|------------|-------------|
| `tsx scripts/seed-data.ts` | `npm run db:setup` |
| `tsx scripts/seed-announcements.ts` | Integrated into setup |
| `tsx scripts/seed-time-slots.ts` | Integrated into setup |
| `tsx scripts/init-admin-data.ts` | Integrated into setup |
| `tsx scripts/reset-database.ts` | `npm run db:reset-confirm` |
### Advantages of New System:
1. **Intelligent Setup** - Automatically handles dependencies and order
2. **Flexible Options** - Essential-only or full sample data modes
3. **Safety Features** - Reset confirmation and detailed logging
4. **Better Documentation** - Comprehensive help and summaries
5. **Single Source** - All database setup in one place
These old scripts are kept for reference but should not be used in new development.
EOF
echo " ✓ Created README in old-seeds directory"
echo ""
echo "✅ Cleanup complete!"
echo ""
echo "📋 Summary of changes:"
echo " • Old seed scripts moved to scripts/old-seeds/"
echo " • New unified system active:"
echo " - setup-database.ts (comprehensive setup)"
echo " - reset-db.ts (safe reset with confirmation)"
echo ""
echo "🚀 New database commands:"
echo " npm run db:setup # Full setup with sample data"
echo " npm run db:seed # Essential data only"
echo " npm run db:reset # Safe reset (shows warning)"
echo " npm run db:reset-confirm # Immediate reset"
echo ""
echo "📖 See DATABASE_SETUP.md for detailed documentation"
-40
View File
@@ -1,40 +0,0 @@
# Old Seed Scripts (Archived)
These are the original individual seed scripts that have been consolidated into the new unified database setup system.
## Consolidated Into:
- **setup-database.ts** - Unified setup script with all functionality
- **reset-db.ts** - Improved reset script with safety features
## Original Scripts:
- `seed-data.ts` - Sample bookings and activity logs
- `seed-announcements.ts` - Test announcements
- `seed-time-slots.ts` - Time slot configuration
- `init-admin-data.ts` - Admin dashboard initialization
- `reset-database.ts` - Original reset script
## Migration Notes:
All functionality from these scripts has been intelligently integrated into the new system:
### New Command Equivalents:
| Old Script | New Command |
|------------|-------------|
| `tsx scripts/seed-data.ts` | `npm run db:setup` |
| `tsx scripts/seed-announcements.ts` | Integrated into setup |
| `tsx scripts/seed-time-slots.ts` | Integrated into setup |
| `tsx scripts/init-admin-data.ts` | Integrated into setup |
| `tsx scripts/reset-database.ts` | `npm run db:reset-confirm` |
### Advantages of New System:
1. **Intelligent Setup** - Automatically handles dependencies and order
2. **Flexible Options** - Essential-only or full sample data modes
3. **Safety Features** - Reset confirmation and detailed logging
4. **Better Documentation** - Comprehensive help and summaries
5. **Single Source** - All database setup in one place
These old scripts are kept for reference but should not be used in new development.
-90
View File
@@ -1,90 +0,0 @@
import { db } from '../lib/db';
import { settings, metrics } from '../lib/db/schema';
import { randomUUID } from 'crypto';
import { eq } from 'drizzle-orm';
async function initializeAdminData() {
try {
console.log('Initializing admin dashboard data...');
const now = new Date();
// Initialize default settings
console.log('Setting up default settings...');
const defaultSettings = [
{
id: randomUUID(),
key: 'booking_window_days',
value: '14',
updatedAt: now,
},
{
id: randomUUID(),
key: 'max_booking_duration_hours',
value: '2',
updatedAt: now,
},
{
id: randomUUID(),
key: 'max_bookings_per_user_per_hour_per_day',
value: '1',
updatedAt: now,
},
{
id: randomUUID(),
key: 'allow_booking_modifications',
value: 'true',
updatedAt: now,
},
{
id: randomUUID(),
key: 'booking_modification_hours_before',
value: '2',
updatedAt: now,
},
];
// Insert settings if they don't exist
for (const setting of defaultSettings) {
const existingSetting = await db.select().from(settings).where(eq(settings.key, setting.key)).limit(1);
if (existingSetting.length === 0) {
await db.insert(settings).values(setting);
console.log(`✓ Setting '${setting.key}' initialized with value '${setting.value}'`);
} else {
console.log(`- Setting '${setting.key}' already exists`);
}
}
// Initialize current month's metrics
console.log('Initializing monthly metrics...');
const currentMonth = now.toISOString().substring(0, 7); // "2025-09"
const existingMetric = await db.select().from(metrics).where(eq(metrics.period, currentMonth)).limit(1);
if (existingMetric.length === 0) {
const monthlyMetric = {
id: randomUUID(),
metricType: 'monthly_bookings',
period: currentMonth,
value: 0,
createdAt: now,
updatedAt: now,
};
await db.insert(metrics).values(monthlyMetric);
console.log(`✓ Monthly metrics initialized for ${currentMonth}`);
} else {
console.log(`- Monthly metrics for ${currentMonth} already exist`);
}
console.log('Admin dashboard data initialization complete!');
} catch (error) {
console.error('Error initializing admin data:', error);
throw error;
}
}
initializeAdminData();
-263
View File
@@ -1,263 +0,0 @@
import Database from 'better-sqlite3';
import { drizzle } from 'drizzle-orm/better-sqlite3';
import * as schema from '../lib/db/schema';
import { sql } from 'drizzle-orm';
import { randomUUID } from 'crypto';
import bcrypt from 'bcryptjs';
const sqlite = new Database('./sqlite.db');
const db = drizzle(sqlite, { schema });
async function resetDatabase() {
console.log('Resetting database...');
// Drop all tables
const tables = [
'activity_logs',
'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}`));
console.log(`Dropped table: ${table}`);
} catch (error) {
console.log(`Table ${table} doesn't exist or error dropping:`, error);
}
}
// Create all tables with current schema
console.log('Creating tables...');
// Users table
await db.run(sql`
CREATE TABLE 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')),
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
)
`);
// Courts table
await db.run(sql`
CREATE TABLE 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 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 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 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,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
)
`);
// Announcements table with all required columns
await db.run(sql`
CREATE TABLE 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 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
)
`);
console.log('All tables created successfully!');
// Insert seed data
console.log('Inserting seed data...');
const now = Date.now();
// Create admin user
const hashedPassword = await bcrypt.hash('admin123', 12);
await db.insert(schema.users).values({
id: randomUUID(),
email: 'admin@ttbooking.com',
name: 'Admin',
surname: 'User',
password: hashedPassword,
role: 'admin',
createdAt: new Date(now),
updatedAt: new Date(now),
});
// Create test user
const testPassword = await bcrypt.hash('password123', 12);
await db.insert(schema.users).values({
id: randomUUID(),
email: 'user@test.com',
name: 'Test',
surname: 'User',
password: testPassword,
role: 'user',
createdAt: new Date(now),
updatedAt: new Date(now),
});
// Create courts
const court1Id = randomUUID();
const court2Id = randomUUID();
await db.insert(schema.courts).values([
{
id: court1Id,
name: 'Court 1',
isActive: true,
createdAt: new Date(now),
updatedAt: new Date(now),
},
{
id: court2Id,
name: 'Court 2',
isActive: true,
createdAt: new Date(now),
updatedAt: new Date(now),
},
]);
// Insert default settings
await db.insert(schema.settings).values([
{
id: randomUUID(),
key: 'booking_window_days',
value: '7',
updatedAt: new Date(now),
},
{
id: randomUUID(),
key: 'max_booking_duration_hours',
value: '2',
updatedAt: new Date(now),
},
{
id: randomUUID(),
key: 'min_booking_duration_minutes',
value: '30',
updatedAt: new Date(now),
},
{
id: randomUUID(),
key: 'booking_start_time',
value: '08:00',
updatedAt: new Date(now),
},
{
id: randomUUID(),
key: 'booking_end_time',
value: '22:00',
updatedAt: new Date(now),
},
{
id: randomUUID(),
key: 'allow_weekend_bookings',
value: 'true',
updatedAt: new Date(now),
},
]);
// Create time slots for all days (8 AM to 10 PM)
const timeSlotData = [];
for (let day = 0; day < 7; day++) {
for (let hour = 8; hour < 22; hour += 2) {
timeSlotData.push({
id: randomUUID(),
dayOfWeek: day,
startTime: `${hour.toString().padStart(2, '0')}:00`,
endTime: `${(hour + 2).toString().padStart(2, '0')}:00`,
isActive: true,
createdAt: new Date(now),
updatedAt: new Date(now),
});
}
}
await db.insert(schema.timeSlots).values(timeSlotData);
// Create sample announcement
await db.insert(schema.announcements).values({
id: randomUUID(),
title: 'Welcome to Table Tennis Booking System',
content: 'Book your court times easily and manage your games efficiently.',
isActive: true,
priority: 'high',
expiresAt: null,
createdAt: new Date(now),
updatedAt: new Date(now),
});
console.log('Seed data inserted successfully!');
console.log('Database reset complete!');
sqlite.close();
}
resetDatabase().catch(console.error);
-50
View File
@@ -1,50 +0,0 @@
import { db } from '@/lib/db';
import { announcements } from '@/lib/db/schema';
import { randomUUID } from 'crypto';
async function seedAnnouncements() {
try {
const testAnnouncements = [
{
id: randomUUID(),
title: 'Welcome to the New Booking System!',
content:
'We have upgraded our table tennis booking system with new features including mobile support, partner booking, and booking management. Enjoy your games!',
priority: 'high' as const,
isActive: true,
createdAt: new Date(),
updatedAt: new Date(),
},
{
id: randomUUID(),
title: 'Court Maintenance Schedule',
content:
'Court 2 will be under maintenance this Friday from 2 PM to 4 PM. Please plan your bookings accordingly.',
priority: 'medium' as const,
isActive: true,
createdAt: new Date(),
updatedAt: new Date(),
},
{
id: randomUUID(),
title: 'New Partnership Feature',
content:
'You can now specify your playing partner when making a booking. This helps other players know who will be using the court.',
priority: 'low' as const,
isActive: true,
createdAt: new Date(),
updatedAt: new Date(),
},
];
for (const announcement of testAnnouncements) {
await db.insert(announcements).values(announcement);
}
console.log('Test announcements created successfully!');
} catch (error) {
console.error('Error creating test announcements:', error);
}
}
seedAnnouncements();
-203
View File
@@ -1,203 +0,0 @@
import { db } from '../lib/db';
import { users, courts as courtsTable, bookings, announcements, activityLogs } from '../lib/db/schema';
import { randomUUID } from 'crypto';
import bcrypt from 'bcryptjs';
async function seedData() {
try {
console.log('Starting data seeding...');
// Get existing users to add sample bookings and activities
const existingUsers = await db.select().from(users);
if (existingUsers.length < 2) {
console.log('Not enough users found. Please run the reset-database script first.');
return;
}
const adminUser = existingUsers.find((u) => u.role === 'admin');
const regularUser = existingUsers.find((u) => u.role === 'user');
const courts = await db.select().from(courtsTable);
if (!adminUser || !regularUser || courts.length === 0) {
console.log('Missing admin user, regular user, or courts. Please run reset-database first.');
return;
}
const now = new Date();
const today = now.toISOString().split('T')[0];
const tomorrow = new Date(now.getTime() + 24 * 60 * 60 * 1000).toISOString().split('T')[0];
// Add some sample bookings
console.log('Creating sample bookings...');
const sampleBookings = [
{
id: randomUUID(),
userId: regularUser.id,
courtId: courts[0].id,
date: today,
startTime: '19:00',
endTime: '20:00',
status: 'active' as const,
notes: 'Regular evening practice session',
createdAt: new Date(now.getTime() - 2 * 60 * 60 * 1000), // 2 hours ago
updatedAt: new Date(now.getTime() - 2 * 60 * 60 * 1000),
},
{
id: randomUUID(),
userId: regularUser.id,
courtId: courts[1] ? courts[1].id : courts[0].id,
date: tomorrow,
startTime: '20:00',
endTime: '21:00',
status: 'active' as const,
notes: 'Tournament preparation',
createdAt: new Date(now.getTime() - 1 * 60 * 60 * 1000), // 1 hour ago
updatedAt: new Date(now.getTime() - 1 * 60 * 60 * 1000),
},
];
await db.insert(bookings).values(sampleBookings);
// Add sample activity logs
console.log('Creating 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.getTime() - 3 * 60 * 60 * 1000), // 3 hours ago
},
{
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,
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.getTime() - 2 * 60 * 60 * 1000), // 2 hours ago
},
{
id: randomUUID(),
userId: regularUser.id,
action: 'login',
entityType: 'user',
entityId: regularUser.id,
details: JSON.stringify({
email: regularUser.email,
role: regularUser.role,
loginMethod: 'password',
}),
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.getTime() - 2.5 * 60 * 60 * 1000), // 2.5 hours ago
},
{
id: randomUUID(),
userId: adminUser.id,
action: 'create_announcement',
entityType: 'announcement',
entityId: null,
details: JSON.stringify({
title: 'System Maintenance',
priority: 'high',
action: 'created_via_seed',
}),
ipAddress: '192.168.1.100',
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
createdAt: new Date(now.getTime() - 1 * 60 * 60 * 1000), // 1 hour ago
},
{
id: randomUUID(),
userId: regularUser.id,
action: 'create_booking',
entityType: 'booking',
entityId: sampleBookings[1].id,
details: JSON.stringify({
courtId: courts[1] ? courts[1].id : courts[0].id,
courtName: courts[1] ? courts[1].name : courts[0].name,
date: tomorrow,
startTime: '20:00',
endTime: '21: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.getTime() - 1 * 60 * 60 * 1000), // 1 hour ago
},
{
id: randomUUID(),
userId: adminUser.id,
action: 'update_settings',
entityType: 'settings',
entityId: null,
details: JSON.stringify({
changedSettings: ['booking_window_days', 'max_booking_duration_hours'],
previousValues: { booking_window_days: '7', max_booking_duration_hours: '2' },
newValues: { booking_window_days: '14', max_booking_duration_hours: '3' },
}),
ipAddress: '192.168.1.100',
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
createdAt: new Date(now.getTime() - 30 * 60 * 1000), // 30 minutes ago
},
];
await db.insert(activityLogs).values(sampleLogs);
// Add more announcements for testing
console.log('Creating additional announcements...');
const additionalAnnouncements = [
{
id: randomUUID(),
title: 'New Court Rules',
content: 'Please remember to clean up after your sessions and respect the time limits.',
isActive: true,
priority: 'medium' as const,
expiresAt: new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000), // 1 week from now
createdAt: new Date(now.getTime() - 4 * 60 * 60 * 1000), // 4 hours ago
updatedAt: new Date(now.getTime() - 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.',
isActive: true,
priority: 'high' as const,
expiresAt: new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000), // 30 days from now
createdAt: new Date(now.getTime() - 24 * 60 * 60 * 1000), // 1 day ago
updatedAt: new Date(now.getTime() - 24 * 60 * 60 * 1000),
},
];
await db.insert(announcements).values(additionalAnnouncements);
console.log('Sample data seeding completed successfully!');
console.log(`Created:
- ${sampleBookings.length} sample bookings
- ${sampleLogs.length} activity logs
- ${additionalAnnouncements.length} additional announcements`);
} catch (error) {
console.error('Error seeding data:', error);
}
}
seedData();
-55
View File
@@ -1,55 +0,0 @@
import { db } from '../lib/db';
import { timeSlots } from '../lib/db/schema';
async function seedTimeSlots() {
console.log('🌱 Seeding time slots...');
// Example time slots for different days
const timeSlotData = [
// Sunday: 12:00 - 17:00
{ dayOfWeek: 0, startTime: '12:00', endTime: '17:00' },
// Monday: 19:00 - 23:00
{ dayOfWeek: 1, startTime: '19:00', endTime: '23:00' },
// Tuesday: 19:00 - 23:00
{ dayOfWeek: 2, startTime: '19:00', endTime: '23:00' },
// Wednesday: NO SLOTS (facility closed)
// { dayOfWeek: 3, startTime: '18:00', endTime: '22:00' },
// Thursday: NO SLOTS (facility closed)
// { dayOfWeek: 4, startTime: '19:00', endTime: '23:00' },
// Friday: 18:00 - 22:00
{ dayOfWeek: 5, startTime: '18:00', endTime: '22:00' },
// Saturday: 10:00 - 18:00
{ dayOfWeek: 6, startTime: '10:00', endTime: '18:00' },
];
for (const slot of timeSlotData) {
await db.insert(timeSlots).values({
id: crypto.randomUUID(),
dayOfWeek: slot.dayOfWeek,
startTime: slot.startTime,
endTime: slot.endTime,
isActive: true,
createdAt: new Date(),
updatedAt: new Date(),
});
}
console.log('✅ Time slots seeding completed');
}
// Run the seeding function
seedTimeSlots()
.then(() => {
console.log('Time slots seeding process completed');
process.exit(0);
})
.catch((error) => {
console.error('Error during time slots seeding:', error);
process.exit(1);
});
+2 -1
View File
@@ -3,7 +3,8 @@ import { drizzle } from 'drizzle-orm/better-sqlite3';
import * as schema from '../lib/db/schema';
import { sql } from 'drizzle-orm';
const sqlite = new Database('./sqlite.db');
const dbPath = process.env.DATABASE_URL || './data/sqlite.db';
const sqlite = new Database(dbPath);
const db = drizzle(sqlite, { schema });
interface ResetOptions {
+1 -1
View File
@@ -598,7 +598,7 @@ async function printDatabaseSummary() {
console.log(`\n⚙️ Settings: ${settings.length} configured`);
console.log('\n💡 Login Credentials:');
console.log(' Admin: admin@tabletennis.com / admin123');
console.log(` Admin: ${process.env.ADMIN_EMAIL || 'admin@tabletennis.com'} / ${process.env.ADMIN_PASSWORD || 'admin123'}`);
console.log(' User: user@tabletennis.com / user123');
console.log('\n🚀 Ready to start! Run: npm run dev');