From 46467c457aa24493fdad6ff494731eb75499ead4 Mon Sep 17 00:00:00 2001 From: tikaiz Date: Fri, 6 Mar 2026 08:41:49 +0100 Subject: [PATCH] Migrate to single DB for both users and tournaments and introduce userService --- backend_splatournament_manager/package.json | 1 + backend_splatournament_manager/src/app.ts | 42 +++++++++++- .../src/models/user.ts | 6 ++ .../src/services/tournament-service.ts | 8 +-- .../src/services/user-service.ts | 65 +++++++++++++++++++ 5 files changed, 115 insertions(+), 7 deletions(-) create mode 100644 backend_splatournament_manager/src/models/user.ts create mode 100644 backend_splatournament_manager/src/services/user-service.ts diff --git a/backend_splatournament_manager/package.json b/backend_splatournament_manager/package.json index c24dc1c..e5efb1a 100644 --- a/backend_splatournament_manager/package.json +++ b/backend_splatournament_manager/package.json @@ -7,6 +7,7 @@ "build": "tsc" }, "dependencies": { + "argon2": "^0.44.0", "axios": "1.13.5", "body-parser": "2.2.1", "dotenv": "^17.2.3", diff --git a/backend_splatournament_manager/src/app.ts b/backend_splatournament_manager/src/app.ts index 076f0c0..1ddb853 100644 --- a/backend_splatournament_manager/src/app.ts +++ b/backend_splatournament_manager/src/app.ts @@ -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}`); }); diff --git a/backend_splatournament_manager/src/models/user.ts b/backend_splatournament_manager/src/models/user.ts new file mode 100644 index 0000000..c9b704d --- /dev/null +++ b/backend_splatournament_manager/src/models/user.ts @@ -0,0 +1,6 @@ +export interface User { + id: number; + username: string; + password: string; +} + diff --git a/backend_splatournament_manager/src/services/tournament-service.ts b/backend_splatournament_manager/src/services/tournament-service.ts index 77bfed2..b5f2536 100644 --- a/backend_splatournament_manager/src/services/tournament-service.ts +++ b/backend_splatournament_manager/src/services/tournament-service.ts @@ -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 ( diff --git a/backend_splatournament_manager/src/services/user-service.ts b/backend_splatournament_manager/src/services/user-service.ts new file mode 100644 index 0000000..bfc958f --- /dev/null +++ b/backend_splatournament_manager/src/services/user-service.ts @@ -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); + } + } + ); + }); + } +} + +