initial version of the app
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
import Database from 'better-sqlite3';
|
||||
import bcrypt from 'bcryptjs';
|
||||
|
||||
async function setupDatabase() {
|
||||
const db = new Database('sqlite.db');
|
||||
|
||||
// Enable foreign keys
|
||||
db.pragma('foreign_keys = ON');
|
||||
|
||||
// Create tables
|
||||
db.exec(`
|
||||
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')),
|
||||
created_at INTEGER NOT NULL,
|
||||
updated_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
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
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS settings (
|
||||
id TEXT PRIMARY KEY,
|
||||
key TEXT NOT NULL UNIQUE,
|
||||
value TEXT NOT NULL,
|
||||
description TEXT,
|
||||
created_at INTEGER NOT NULL,
|
||||
updated_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS time_slots (
|
||||
id TEXT PRIMARY KEY,
|
||||
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
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS bookings (
|
||||
id TEXT PRIMARY KEY,
|
||||
user_id TEXT NOT NULL,
|
||||
court_id TEXT NOT NULL,
|
||||
date TEXT NOT NULL,
|
||||
time_slot_id TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'confirmed' CHECK (status IN ('confirmed', 'cancelled', 'pending')),
|
||||
created_at INTEGER NOT NULL,
|
||||
updated_at INTEGER NOT NULL,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id),
|
||||
FOREIGN KEY (court_id) REFERENCES courts(id),
|
||||
FOREIGN KEY (time_slot_id) REFERENCES time_slots(id)
|
||||
);
|
||||
|
||||
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,
|
||||
created_at INTEGER NOT NULL,
|
||||
updated_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS activity_logs (
|
||||
id TEXT PRIMARY KEY,
|
||||
user_id TEXT NOT NULL,
|
||||
action TEXT NOT NULL,
|
||||
details TEXT,
|
||||
created_at INTEGER NOT NULL,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
`);
|
||||
|
||||
console.log('Tables created successfully!');
|
||||
|
||||
// Insert default admin user
|
||||
const now = Date.now();
|
||||
const adminPassword = await bcrypt.hash('admin123', 10);
|
||||
|
||||
try {
|
||||
const stmt = db.prepare(`
|
||||
INSERT OR IGNORE INTO users (id, email, name, surname, password, role, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`);
|
||||
|
||||
stmt.run(
|
||||
'admin-' + crypto.randomUUID(),
|
||||
'admin@ttbooking.com',
|
||||
'Admin',
|
||||
'User',
|
||||
adminPassword,
|
||||
'admin',
|
||||
now,
|
||||
now
|
||||
);
|
||||
|
||||
console.log('Admin user created: admin@ttbooking.com / admin123');
|
||||
} catch (error) {
|
||||
console.log('Admin user might already exist');
|
||||
}
|
||||
|
||||
// Insert default courts
|
||||
try {
|
||||
const courtStmt = db.prepare(`
|
||||
INSERT OR IGNORE INTO courts (id, name, is_active, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
`);
|
||||
|
||||
courtStmt.run('court-1', 'Court 1', 1, now, now);
|
||||
courtStmt.run('court-2', 'Court 2', 1, now, now);
|
||||
|
||||
console.log('Default courts created');
|
||||
} catch (error) {
|
||||
console.log('Courts might already exist');
|
||||
}
|
||||
|
||||
// Insert time slots
|
||||
try {
|
||||
const timeSlotStmt = db.prepare(`
|
||||
INSERT OR IGNORE INTO time_slots (id, start_time, end_time, is_active, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
`);
|
||||
|
||||
const timeSlots = [
|
||||
['09:00', '10:00'],
|
||||
['10:00', '11:00'],
|
||||
['11:00', '12:00'],
|
||||
['12:00', '13:00'],
|
||||
['13:00', '14:00'],
|
||||
['14:00', '15:00'],
|
||||
['15:00', '16:00'],
|
||||
['16:00', '17:00'],
|
||||
['17:00', '18:00'],
|
||||
['18:00', '19:00'],
|
||||
['19:00', '20:00'],
|
||||
['20:00', '21:00'],
|
||||
];
|
||||
|
||||
timeSlots.forEach(([start, end], index) => {
|
||||
timeSlotStmt.run(`slot-${index + 1}`, start, end, 1, now, now);
|
||||
});
|
||||
|
||||
console.log('Time slots created');
|
||||
} catch (error) {
|
||||
console.log('Time slots might already exist');
|
||||
}
|
||||
|
||||
// Insert default settings
|
||||
try {
|
||||
const settingsStmt = db.prepare(`
|
||||
INSERT OR IGNORE INTO settings (id, key, value, description, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
`);
|
||||
|
||||
settingsStmt.run('setting-1', 'booking_advance_days', '7', 'Days in advance users can book', now, now);
|
||||
settingsStmt.run('setting-2', 'max_bookings_per_user', '3', 'Maximum bookings per user per week', now, now);
|
||||
settingsStmt.run('setting-3', 'booking_duration', '60', 'Booking duration in minutes', now, now);
|
||||
|
||||
console.log('Default settings created');
|
||||
} catch (error) {
|
||||
console.log('Settings might already exist');
|
||||
}
|
||||
|
||||
db.close();
|
||||
console.log('Database setup completed!');
|
||||
}
|
||||
|
||||
setupDatabase().catch(console.error);
|
||||
@@ -0,0 +1,263 @@
|
||||
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);
|
||||
@@ -0,0 +1,50 @@
|
||||
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();
|
||||
@@ -0,0 +1,203 @@
|
||||
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();
|
||||
@@ -0,0 +1,133 @@
|
||||
import { db } from '@/lib/db';
|
||||
import { users, courts, timeSlots, settings } from '@/lib/db/schema';
|
||||
import { hashPassword } from '@/lib/auth';
|
||||
import { generateId } from '@/lib/utils';
|
||||
|
||||
async function setupDatabase() {
|
||||
try {
|
||||
console.log('Setting up database...');
|
||||
|
||||
// Create admin user
|
||||
const adminEmail = process.env.ADMIN_EMAIL || 'admin@example.com';
|
||||
const adminPassword = process.env.ADMIN_PASSWORD || 'admin123';
|
||||
|
||||
const hashedAdminPassword = await hashPassword(adminPassword);
|
||||
const now = new Date();
|
||||
|
||||
await db
|
||||
.insert(users)
|
||||
.values({
|
||||
id: generateId(),
|
||||
email: adminEmail,
|
||||
name: 'Admin',
|
||||
surname: 'User',
|
||||
password: hashedAdminPassword,
|
||||
role: 'admin',
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
})
|
||||
.onConflictDoNothing();
|
||||
|
||||
// Create default courts
|
||||
await db
|
||||
.insert(courts)
|
||||
.values([
|
||||
{
|
||||
id: generateId(),
|
||||
name: 'Court 1',
|
||||
isActive: true,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
},
|
||||
{
|
||||
id: generateId(),
|
||||
name: 'Court 2',
|
||||
isActive: true,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
},
|
||||
])
|
||||
.onConflictDoNothing();
|
||||
|
||||
// Create default time slots
|
||||
// Monday (1) and Tuesday (2): 19:00-23:00
|
||||
const mondayTuesdaySlots = [];
|
||||
for (let day of [1, 2]) {
|
||||
for (let hour = 19; hour < 23; hour++) {
|
||||
const hourStr = hour < 10 ? '0' + hour : hour.toString();
|
||||
const nextHourStr = hour + 1 < 10 ? '0' + (hour + 1) : (hour + 1).toString();
|
||||
mondayTuesdaySlots.push({
|
||||
id: generateId(),
|
||||
dayOfWeek: day,
|
||||
startTime: `${hourStr}:00`,
|
||||
endTime: `${nextHourStr}:00`,
|
||||
isActive: true,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Sunday (0): 12:00-17:00
|
||||
const sundaySlots = [];
|
||||
for (let hour = 12; hour < 17; hour++) {
|
||||
const hourStr = hour < 10 ? '0' + hour : hour.toString();
|
||||
const nextHourStr = hour + 1 < 10 ? '0' + (hour + 1) : (hour + 1).toString();
|
||||
sundaySlots.push({
|
||||
id: generateId(),
|
||||
dayOfWeek: 0,
|
||||
startTime: `${hourStr}:00`,
|
||||
endTime: `${nextHourStr}:00`,
|
||||
isActive: true,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
});
|
||||
}
|
||||
|
||||
await db
|
||||
.insert(timeSlots)
|
||||
.values([...mondayTuesdaySlots, ...sundaySlots])
|
||||
.onConflictDoNothing();
|
||||
|
||||
// Create default settings
|
||||
await db
|
||||
.insert(settings)
|
||||
.values([
|
||||
{
|
||||
id: generateId(),
|
||||
key: 'booking_window_days',
|
||||
value: '7',
|
||||
updatedAt: now,
|
||||
},
|
||||
{
|
||||
id: generateId(),
|
||||
key: 'max_bookings_per_user',
|
||||
value: '3',
|
||||
updatedAt: now,
|
||||
},
|
||||
{
|
||||
id: generateId(),
|
||||
key: 'booking_cancellation_hours',
|
||||
value: '2',
|
||||
updatedAt: now,
|
||||
},
|
||||
])
|
||||
.onConflictDoNothing();
|
||||
|
||||
console.log('Database setup completed successfully!');
|
||||
console.log(`Admin user created: ${adminEmail}`);
|
||||
console.log(`Admin password: ${adminPassword}`);
|
||||
} catch (error) {
|
||||
console.error('Database setup error:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Run setup if this file is executed directly
|
||||
if (require.main === module) {
|
||||
setupDatabase()
|
||||
.then(() => process.exit(0))
|
||||
.catch(() => process.exit(1));
|
||||
}
|
||||
|
||||
export { setupDatabase };
|
||||
Reference in New Issue
Block a user