234 lines
6.3 KiB
TypeScript
234 lines
6.3 KiB
TypeScript
import Database from 'better-sqlite3';
|
|
import path from 'path';
|
|
import { albums as initialAlbums } from './data';
|
|
import { Album, Purchase } from './types';
|
|
|
|
// Database path
|
|
const dbPath = path.join(process.cwd(), 'data', 'parsa.db');
|
|
|
|
// Initialize database
|
|
let db: Database.Database;
|
|
|
|
export function getDatabase(): Database.Database {
|
|
if (!db) {
|
|
// Create data directory if it doesn't exist
|
|
const fs = require('fs');
|
|
const dataDir = path.join(process.cwd(), 'data');
|
|
if (!fs.existsSync(dataDir)) {
|
|
fs.mkdirSync(dataDir, { recursive: true });
|
|
}
|
|
|
|
db = new Database(dbPath);
|
|
db.pragma('journal_mode = WAL');
|
|
initializeDatabase();
|
|
}
|
|
return db;
|
|
}
|
|
|
|
function initializeDatabase() {
|
|
// Create albums table
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS albums (
|
|
id TEXT PRIMARY KEY,
|
|
title TEXT NOT NULL,
|
|
coverImage TEXT NOT NULL,
|
|
year INTEGER NOT NULL,
|
|
genre TEXT NOT NULL,
|
|
description TEXT NOT NULL,
|
|
price REAL NOT NULL,
|
|
songs TEXT NOT NULL,
|
|
createdAt INTEGER DEFAULT (strftime('%s', 'now')),
|
|
updatedAt INTEGER DEFAULT (strftime('%s', 'now'))
|
|
)
|
|
`);
|
|
|
|
// Create purchases table
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS purchases (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
albumId TEXT NOT NULL,
|
|
transactionId TEXT NOT NULL UNIQUE,
|
|
customerName TEXT,
|
|
email TEXT,
|
|
phoneNumber TEXT,
|
|
txReceipt TEXT,
|
|
purchaseDate INTEGER NOT NULL,
|
|
createdAt INTEGER DEFAULT (strftime('%s', 'now')),
|
|
FOREIGN KEY (albumId) REFERENCES albums(id) ON DELETE CASCADE
|
|
)
|
|
`);
|
|
|
|
// Create indexes
|
|
db.exec(`
|
|
CREATE INDEX IF NOT EXISTS idx_purchases_albumId ON purchases(albumId);
|
|
CREATE INDEX IF NOT EXISTS idx_purchases_transactionId ON purchases(transactionId);
|
|
`);
|
|
|
|
// Seed initial data if albums table is empty
|
|
const count = db.prepare('SELECT COUNT(*) as count FROM albums').get() as { count: number };
|
|
if (count.count === 0) {
|
|
seedInitialData();
|
|
}
|
|
}
|
|
|
|
function seedInitialData() {
|
|
const insert = db.prepare(`
|
|
INSERT INTO albums (id, title, coverImage, year, genre, description, price, songs)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
`);
|
|
|
|
const insertMany = db.transaction((albums: Album[]) => {
|
|
for (const album of albums) {
|
|
insert.run(
|
|
album.id,
|
|
album.title,
|
|
album.coverImage,
|
|
album.year,
|
|
album.genre,
|
|
album.description,
|
|
album.price,
|
|
JSON.stringify(album.songs)
|
|
);
|
|
}
|
|
});
|
|
|
|
insertMany(initialAlbums);
|
|
}
|
|
|
|
// Album operations
|
|
export const albumDb = {
|
|
getAll(): Album[] {
|
|
const db = getDatabase();
|
|
const rows = db.prepare('SELECT * FROM albums ORDER BY year DESC').all();
|
|
return rows.map((row: any) => ({
|
|
...row,
|
|
songs: JSON.parse(row.songs),
|
|
}));
|
|
},
|
|
|
|
getById(id: string): Album | null {
|
|
const db = getDatabase();
|
|
const row = db.prepare('SELECT * FROM albums WHERE id = ?').get(id) as any;
|
|
if (!row) return null;
|
|
return {
|
|
...row,
|
|
songs: JSON.parse(row.songs),
|
|
};
|
|
},
|
|
|
|
create(album: Album): void {
|
|
const db = getDatabase();
|
|
db.prepare(`
|
|
INSERT INTO albums (id, title, coverImage, year, genre, description, price, songs)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
`).run(
|
|
album.id,
|
|
album.title,
|
|
album.coverImage,
|
|
album.year,
|
|
album.genre,
|
|
album.description,
|
|
album.price,
|
|
JSON.stringify(album.songs)
|
|
);
|
|
},
|
|
|
|
update(id: string, album: Album): void {
|
|
const db = getDatabase();
|
|
db.prepare(`
|
|
UPDATE albums
|
|
SET title = ?, coverImage = ?, year = ?, genre = ?, description = ?, price = ?, songs = ?, updatedAt = strftime('%s', 'now')
|
|
WHERE id = ?
|
|
`).run(
|
|
album.title,
|
|
album.coverImage,
|
|
album.year,
|
|
album.genre,
|
|
album.description,
|
|
album.price,
|
|
JSON.stringify(album.songs),
|
|
id
|
|
);
|
|
},
|
|
|
|
delete(id: string): void {
|
|
const db = getDatabase();
|
|
db.prepare('DELETE FROM albums WHERE id = ?').run(id);
|
|
},
|
|
};
|
|
|
|
// Purchase operations
|
|
export const purchaseDb = {
|
|
getAll(): Purchase[] {
|
|
const db = getDatabase();
|
|
const rows = db.prepare('SELECT * FROM purchases ORDER BY purchaseDate DESC').all();
|
|
return rows.map((row: any) => ({
|
|
id: row.id,
|
|
albumId: row.albumId,
|
|
transactionId: row.transactionId,
|
|
customerName: row.customerName,
|
|
email: row.email,
|
|
phoneNumber: row.phoneNumber,
|
|
txReceipt: row.txReceipt,
|
|
purchaseDate: new Date(row.purchaseDate),
|
|
}));
|
|
},
|
|
|
|
getByAlbumId(albumId: string): Purchase[] {
|
|
const db = getDatabase();
|
|
const rows = db.prepare('SELECT * FROM purchases WHERE albumId = ? ORDER BY purchaseDate DESC').all(albumId);
|
|
return rows.map((row: any) => ({
|
|
id: row.id,
|
|
albumId: row.albumId,
|
|
transactionId: row.transactionId,
|
|
customerName: row.customerName,
|
|
email: row.email,
|
|
phoneNumber: row.phoneNumber,
|
|
txReceipt: row.txReceipt,
|
|
purchaseDate: new Date(row.purchaseDate),
|
|
}));
|
|
},
|
|
|
|
getByTransactionId(transactionId: string): Purchase | null {
|
|
const db = getDatabase();
|
|
const row = db.prepare('SELECT * FROM purchases WHERE transactionId = ?').get(transactionId) as any;
|
|
if (!row) return null;
|
|
return {
|
|
id: row.id,
|
|
albumId: row.albumId,
|
|
transactionId: row.transactionId,
|
|
customerName: row.customerName,
|
|
email: row.email,
|
|
phoneNumber: row.phoneNumber,
|
|
txReceipt: row.txReceipt,
|
|
purchaseDate: new Date(row.purchaseDate),
|
|
};
|
|
},
|
|
|
|
create(purchase: Omit<Purchase, 'id'>): Purchase {
|
|
const db = getDatabase();
|
|
const result = db.prepare(`
|
|
INSERT INTO purchases (albumId, transactionId, customerName, email, phoneNumber, txReceipt, purchaseDate)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
`).run(
|
|
purchase.albumId,
|
|
purchase.transactionId,
|
|
purchase.customerName || null,
|
|
purchase.email || null,
|
|
purchase.phoneNumber || null,
|
|
purchase.txReceipt || null,
|
|
purchase.purchaseDate instanceof Date ? purchase.purchaseDate.getTime() : purchase.purchaseDate
|
|
);
|
|
|
|
return {
|
|
id: result.lastInsertRowid as number,
|
|
...purchase,
|
|
};
|
|
},
|
|
|
|
delete(id: number): void {
|
|
const db = getDatabase();
|
|
db.prepare('DELETE FROM purchases WHERE id = ?').run(id);
|
|
},
|
|
};
|