Migrate to single DB for both users and tournaments and introduce userService
This commit is contained in:
@@ -7,6 +7,7 @@
|
|||||||
"build": "tsc"
|
"build": "tsc"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"argon2": "^0.44.0",
|
||||||
"axios": "1.13.5",
|
"axios": "1.13.5",
|
||||||
"body-parser": "2.2.1",
|
"body-parser": "2.2.1",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
|
|||||||
@@ -3,10 +3,20 @@ import bodyParser from 'body-parser';
|
|||||||
import 'dotenv/config';
|
import 'dotenv/config';
|
||||||
|
|
||||||
import {TournamentService} from './services/tournament-service';
|
import {TournamentService} from './services/tournament-service';
|
||||||
|
import {UserService} from './services/user-service';
|
||||||
import router from './middlewares/logger';
|
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 port = process.env.PORT || 3000;
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
@@ -63,6 +73,36 @@ app.delete('/tournaments', async (req: Request, res: Response) => {
|
|||||||
res.status(200).send({message: 'Tournament deleted successfully'});
|
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, () => {
|
app.listen(port, () => {
|
||||||
console.log(`server started on port ${port}`);
|
console.log(`server started on port ${port}`);
|
||||||
});
|
});
|
||||||
|
|||||||
6
backend_splatournament_manager/src/models/user.ts
Normal file
6
backend_splatournament_manager/src/models/user.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export interface User {
|
||||||
|
id: number;
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -5,14 +5,10 @@ import {Database, RunResult} from "sqlite3";
|
|||||||
|
|
||||||
export class TournamentService {
|
export class TournamentService {
|
||||||
private csvFilename = 'csv/tournaments.csv';
|
private csvFilename = 'csv/tournaments.csv';
|
||||||
private dbFilename = 'tournaments.sqlite';
|
|
||||||
private db: Database;
|
private db: Database;
|
||||||
|
|
||||||
constructor() {
|
constructor(db: Database) {
|
||||||
if (fs.existsSync(this.dbFilename)) {
|
this.db = db;
|
||||||
fs.unlinkSync(this.dbFilename);
|
|
||||||
}
|
|
||||||
this.db = new Database(this.dbFilename);
|
|
||||||
this.db.serialize(() => {
|
this.db.serialize(() => {
|
||||||
this.db.run(`CREATE TABLE IF NOT EXISTS Tournaments
|
this.db.run(`CREATE TABLE IF NOT EXISTS Tournaments
|
||||||
(
|
(
|
||||||
|
|||||||
65
backend_splatournament_manager/src/services/user-service.ts
Normal file
65
backend_splatournament_manager/src/services/user-service.ts
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user