Migrate to single DB for both users and tournaments and introduce userService

This commit is contained in:
2026-03-06 08:41:49 +01:00
parent 44e7ea9f73
commit 46467c457a
5 changed files with 115 additions and 7 deletions

View File

@@ -3,10 +3,20 @@ import bodyParser from 'body-parser';
import 'dotenv/config';
import {TournamentService} from './services/tournament-service';
import {UserService} from './services/user-service';
import router from './middlewares/logger';
import {Database} from 'sqlite3';
import fs from "fs";
const tournamentService = new TournamentService();
const dbFilename = 'tournaments.sqlite';
if (fs.existsSync(dbFilename)){
fs.unlinkSync(dbFilename);
}
const db = new Database(dbFilename);
const tournamentService = new TournamentService(db);
const userService = new UserService(db);
const port = process.env.PORT || 3000;
const app = express();
@@ -63,6 +73,36 @@ app.delete('/tournaments', async (req: Request, res: Response) => {
res.status(200).send({message: 'Tournament deleted successfully'});
});
// Auth routes
app.post('/register', async (req: Request, res: Response) => {
const { username, password } = req.body;
if (!username || !password) {
return res.status(400).send({ error: 'Username and password are required' });
}
try {
const user = await userService.register(username, password);
res.status(201).send(user);
} catch (err: any) {
if (err.message?.includes('UNIQUE constraint failed')) {
return res.status(409).send({ error: 'Username already exists' });
}
res.status(500).send({ error: 'Registration failed' });
}
});
app.post('/login', async (req: Request, res: Response) => {
const { username, password } = req.body;
if (!username || !password) {
return res.status(400).send({ error: 'Username and password are required' });
}
try {
const user = await userService.login(username, password);
res.status(200).send(user);
} catch (err: any) {
res.status(401).send({ error: err.message || 'Login failed' });
}
});
app.listen(port, () => {
console.log(`server started on port ${port}`);
});

View File

@@ -0,0 +1,6 @@
export interface User {
id: number;
username: string;
password: string;
}

View File

@@ -5,14 +5,10 @@ import {Database, RunResult} from "sqlite3";
export class TournamentService {
private csvFilename = 'csv/tournaments.csv';
private dbFilename = 'tournaments.sqlite';
private db: Database;
constructor() {
if (fs.existsSync(this.dbFilename)) {
fs.unlinkSync(this.dbFilename);
}
this.db = new Database(this.dbFilename);
constructor(db: Database) {
this.db = db;
this.db.serialize(() => {
this.db.run(`CREATE TABLE IF NOT EXISTS Tournaments
(

View File

@@ -0,0 +1,65 @@
import { User } from '../models/user';
import { Database } from 'sqlite3';
import * as argon2 from 'argon2';
export class UserService {
private db: Database;
constructor(db: Database) {
this.db = db;
this.db.run(`CREATE TABLE IF NOT EXISTS Users
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL
)`);
}
register(username: string, password: string): Promise<{ id: number; username: string }> {
return new Promise(async (resolve, reject) => {
try {
const hash = await argon2.hash(password);
this.db.run(
'INSERT INTO Users (username, password) VALUES (?, ?)',
[username, hash],
function (err: Error | null) {
if (err) {
return reject(err);
}
resolve({ id: this.lastID, username });
}
);
} catch (err) {
reject(err);
}
});
}
login(username: string, password: string): Promise<{ id: number; username: string }> {
return new Promise((resolve, reject) => {
this.db.get(
'SELECT * FROM Users WHERE username = ?',
[username],
async (err: Error | null, user: User | undefined) => {
if (err) {
return reject(err);
}
if (!user) {
return reject(new Error('User not found'));
}
try {
const valid = await argon2.verify(user.password, password);
if (!valid) {
return reject(new Error('Invalid password'));
}
resolve({ id: user.id, username: user.username });
} catch (e) {
reject(e);
}
}
);
});
}
}