overhauled structure, leaderboardRoute.ts and userRoute.ts working

This commit is contained in:
j-weissen 2022-12-28 15:39:58 +01:00
parent c2cd74ebe2
commit fff55edf79
25 changed files with 315 additions and 212 deletions

View file

@ -9,8 +9,8 @@
"version": "1.0.0", "version": "1.0.0",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"body-parser": "^1.20.1",
"express": "^4.18.2", "express": "^4.18.2",
"express-validator": "^6.14.2",
"helmet": "^6.0.1", "helmet": "^6.0.1",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"pg-promise": "^10.15.4", "pg-promise": "^10.15.4",
@ -413,6 +413,18 @@
"node": ">= 0.10.0" "node": ">= 0.10.0"
} }
}, },
"node_modules/express-validator": {
"version": "6.14.2",
"resolved": "https://registry.npmjs.org/express-validator/-/express-validator-6.14.2.tgz",
"integrity": "sha512-8XfAUrQ6Y7dIIuy9KcUPCfG/uCbvREctrxf5EeeME+ulanJ4iiW71lWmm9r4YcKKYOCBMan0WpVg7FtHu4Z4Wg==",
"dependencies": {
"lodash": "^4.17.21",
"validator": "^13.7.0"
},
"engines": {
"node": ">= 8.0.0"
}
},
"node_modules/finalhandler": { "node_modules/finalhandler": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
@ -533,6 +545,11 @@
"node": ">= 0.10" "node": ">= 0.10"
} }
}, },
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/make-error": { "node_modules/make-error": {
"version": "1.3.6", "version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
@ -1055,6 +1072,14 @@
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg=="
}, },
"node_modules/validator": {
"version": "13.7.0",
"resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz",
"integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/vary": { "node_modules/vary": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
@ -1408,6 +1433,15 @@
"vary": "~1.1.2" "vary": "~1.1.2"
} }
}, },
"express-validator": {
"version": "6.14.2",
"resolved": "https://registry.npmjs.org/express-validator/-/express-validator-6.14.2.tgz",
"integrity": "sha512-8XfAUrQ6Y7dIIuy9KcUPCfG/uCbvREctrxf5EeeME+ulanJ4iiW71lWmm9r4YcKKYOCBMan0WpVg7FtHu4Z4Wg==",
"requires": {
"lodash": "^4.17.21",
"validator": "^13.7.0"
}
},
"finalhandler": { "finalhandler": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
@ -1495,6 +1529,11 @@
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
}, },
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"make-error": { "make-error": {
"version": "1.3.6", "version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
@ -1857,6 +1896,11 @@
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg=="
}, },
"validator": {
"version": "13.7.0",
"resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz",
"integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw=="
},
"vary": { "vary": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",

View file

@ -11,8 +11,8 @@
"author": "jweissen", "author": "jweissen",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"body-parser": "^1.20.1",
"express": "^4.18.2", "express": "^4.18.2",
"express-validator": "^6.14.2",
"helmet": "^6.0.1", "helmet": "^6.0.1",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"pg-promise": "^10.15.4", "pg-promise": "^10.15.4",

View file

@ -10,10 +10,14 @@ const port = 3000
app.use(helmet()) app.use(helmet())
// configure & use logger // configure & use logger
let morganFormatted = morgan('[:date[iso]] :method :url - :status') let morganFormatted = morgan('[:date[iso]] :method :url - :status')
app.use(morganFormatted); app.use(morganFormatted);
app.use(express.json())
app.use('/leaderboard', leaderboardRoute) app.use('/leaderboard', leaderboardRoute)
app.use('/user', userRoute) app.use('/user', userRoute)
@ -21,6 +25,10 @@ app.get('/helloworld', (req, res) => {
res.json({message: "Hello World!"}) res.json({message: "Hello World!"})
}) })
app.post('/echo', async (req, res) => {
res.json(req.body)
})
app.listen(port, () => { app.listen(port, () => {
console.log(`Server started at http://localhost:3000`); console.log(`Server started at http://localhost:3000`);

View file

@ -1,23 +1,33 @@
import express from 'express'; import express from 'express';
import {Database} from "./Database.js";
import {TimeLeaderboardManager} from "./manager/TimeLeaderboardManager.js"; import {TimeLeaderboardManager} from "./manager/TimeLeaderboardManager.js";
import {TimeLeaderboardPgPromiseManager} from "./manager/pgPromise/TimeLeaderboardPgPromiseManager.js";
import {HighscoreLeaderboardPgPromiseManager} from "./manager/pgPromise/HighscoreLeaderboardPgPromiseManager.js";
import {HighscoreLeaderboardManager} from "./manager/HighscoreLeaderboardManager.js"; import {HighscoreLeaderboardManager} from "./manager/HighscoreLeaderboardManager.js";
import { import {HighscoreLeaderboard, TimeLeaderboard} from "./model/Leaderboard.js";
HighscoreLeaderboardPgPromiseSerializer
} from "./serializer/pgpromise/HighscoreLeaderboardPgPromiseSerializer.js";
import {TimeLeaderboardPgPromiseSerializer} from "./serializer/pgpromise/TimeLeaderboardPgPromiseSerializer.js";
export const leaderboardRoute = express.Router() export const leaderboardRoute = express.Router()
leaderboardRoute.get('/highscore', async (req, res) => { leaderboardRoute.get('/highscore', async (req, res) => {
let data = await Database.db.manyOrNone('SELECT * FROM lb_highscore INNER JOIN "user" ON user_id = id ORDER BY RANK;') try {
const leaderboardManager = new HighscoreLeaderboardManager(data, new HighscoreLeaderboardPgPromiseSerializer); const highscoreLeaderboardManager: HighscoreLeaderboardManager = new HighscoreLeaderboardPgPromiseManager;
res.send(leaderboardManager.content) const highscoreLeaderboard: HighscoreLeaderboard = await highscoreLeaderboardManager.getAll();
res.send(highscoreLeaderboard);
} catch (error) {
// handle errors
console.log(error)
res.status(500).json({ errors: [{msg: "Internal server error"}]})
}
}) })
leaderboardRoute.get('/totalplaytime', async (req, res) => { leaderboardRoute.get('/totalplaytime', async (req, res) => {
let data = await Database.db.manyOrNone('SELECT * FROM lb_total_playtime INNER JOIN "user" ON user_id = id ORDER BY RANK;') try {
const leaderboardManager = new TimeLeaderboardManager(data, new TimeLeaderboardPgPromiseSerializer); const timeLeaderboardManager: TimeLeaderboardManager = new TimeLeaderboardPgPromiseManager;
res.send(leaderboardManager.content) const timeLeaderboard: TimeLeaderboard = await timeLeaderboardManager.getAll();
res.send(timeLeaderboard);
} catch (error) {
// handle errors
console.log(error)
res.status(500).json({ errors: [{msg: "Internal server error"}]})
}
}) })

View file

@ -1,32 +1,6 @@
import {Leaderboard} from "../model/Leaderboard.js"; import {HighscoreLeaderboard} from "../model/Leaderboard.js";
import {Manager} from "./Manager.js"; import {Manager} from "./Manager.js";
import {HighscoreLeaderboardSerializer} from "../serializer/HighscoreLeaderboardSerializer.js";
export class HighscoreLeaderboardManager implements Manager<Leaderboard<number>>{
private _content: Leaderboard<number>;
private _serializer: HighscoreLeaderboardSerializer;
constructor(rawData: any, serializer: HighscoreLeaderboardSerializer) {
this.serializer = serializer;
this._content = this.serializer.serialize(rawData);
}
//region getter&setter
get content() {
return this._content;
}
set content(value: Leaderboard<number>) {
this._content = value;
}
get serializer() {
return this._serializer;
}
set serializer(value: HighscoreLeaderboardSerializer) {
this._serializer = value;
}
//endregion
export abstract class HighscoreLeaderboardManager extends Manager<HighscoreLeaderboard>{
abstract getAll(): Promise<HighscoreLeaderboard>;
} }

View file

@ -1,8 +1,7 @@
import {Serializer} from "../serializer/Serializer.js"; export abstract class Manager<T> {
//region getter&setter
protected abstract serialize(raw: any): T;
export interface Manager<T> { protected abstract deserialize(parsed: T): any;
get content(), //endregion
set content(value: T),
get serializer(),
set serializer(value: Serializer<T>),
} }

View file

@ -1,34 +1,6 @@
import {Manager} from "./Manager.js"; import {Manager} from "./Manager.js";
import {Leaderboard} from "../model/Leaderboard.js"; import {TimeLeaderboard} from "../model/Leaderboard.js";
import {Time} from "../model/Time.js";
import {TimeLeaderboardSerializer} from "../serializer/TimeLeaderboardSerializer.js";
import {TimeLeaderboardPgPromiseSerializer} from "../serializer/pgpromise/TimeLeaderboardPgPromiseSerializer.js";
export class TimeLeaderboardManager implements Manager<Leaderboard<Time>> {
private _content: Leaderboard<Time>;
private _serializer: TimeLeaderboardPgPromiseSerializer
constructor(data: any, serializer: TimeLeaderboardSerializer) {
this._serializer = serializer;
this._content = this._serializer.serialize(data);
}
//region getter&setter
get content() {
return this._content;
}
set content(value: Leaderboard<Time>) {
this._content = value;
}
get serializer(): TimeLeaderboardPgPromiseSerializer {
return this._serializer;
}
set serializer(value: TimeLeaderboardPgPromiseSerializer) {
this._serializer = value;
}
//endregion
export abstract class TimeLeaderboardManager extends Manager<TimeLeaderboard>{
abstract getAll(): Promise<TimeLeaderboard>;
} }

View file

@ -0,0 +1,10 @@
import {Manager} from "./Manager.js";
import {User} from "../model/User.js";
export abstract class UserManager extends Manager<User>{
abstract getById(id: number): Promise<User>;
abstract getByName(name: string): Promise<User>;
abstract withIdExists(userId: number): Promise<boolean>;
abstract withNameExists(username: string): Promise<boolean>
abstract insert(user: Omit<User, 'id'>): Promise<User>;
}

View file

@ -1,33 +1,6 @@
import {UserScores} from "../model/UserScores.js"; import {UserScores} from "../model/UserScores.js";
import { Serializer } from "../serializer/Serializer.js";
import {UserScoresSerializer} from "../serializer/UserScoresSerializer.js";
import {Manager} from "./Manager.js"; import {Manager} from "./Manager.js";
export class UserScoresManager implements Manager<UserScores> { export abstract class UserScoresManager extends Manager<UserScores>{
private _content: UserScores; abstract getById(userId: number): Promise<UserScores>;
private _serializer: UserScoresSerializer;
constructor(rawData: any, serializer: UserScoresSerializer) {
this.serializer = serializer;
this._content = this.serializer.serialize(rawData);
}
//region getter&setter
get content() {
return this._content;
}
set content(value: UserScores) {
this._content = value;
}
get serializer() {
return this._serializer
}
set serializer(value: Serializer<UserScores>) {
this._serializer = value
}
//endregion
} }

View file

@ -0,0 +1,29 @@
import {HighscoreLeaderboardManager} from "../HighscoreLeaderboardManager.js";
import {HighscoreLeaderboard, LeaderboardEntry} from "../../model/Leaderboard.js";
import {Database} from "../../Database.js";
export class HighscoreLeaderboardPgPromiseManager extends HighscoreLeaderboardManager{
async getAll(): Promise<HighscoreLeaderboard> {
const raw: any = await Database.db.manyOrNone(
'SELECT * FROM lb_highscore INNER JOIN "user" ON user_id = id ORDER BY RANK;'
);
return this.serialize(raw);
}
//region serialization
protected serialize(raw: any): HighscoreLeaderboard {
return raw.map((item) => {
let newItem: LeaderboardEntry<number> = {
rank: item.rank,
username: item.name,
score: item.highscore,
}
return newItem;
});
}
protected deserialize(parsed: HighscoreLeaderboard): any {
throw new Error("Mthod not implemented.");
}
//endregion
}

View file

@ -0,0 +1,29 @@
import {TimeLeaderboardManager} from "../TimeLeaderboardManager.js";
import {LeaderboardEntry, TimeLeaderboard} from "../../model/Leaderboard.js";
import {Database} from "../../Database.js";
export class TimeLeaderboardPgPromiseManager extends TimeLeaderboardManager {
async getAll(): Promise<TimeLeaderboard> {
const raw: any = await Database.db.manyOrNone(
'SELECT * FROM lb_total_playtime INNER JOIN "user" ON user_id = id ORDER BY RANK;'
);
return this.serialize(raw);
}
//region serialization
protected deserialize(parsed: TimeLeaderboard): any {
throw new Error("Method not implemented.")
}
protected serialize(raw: any): TimeLeaderboard {
return raw.map((item) => {
let newItem: LeaderboardEntry<string> = {
rank: item.rank,
username: item.name,
score: item.total_playtime,
}
return newItem
});
}
//endregion
}

View file

@ -0,0 +1,53 @@
import {UserManager} from "../UserManager.js";
import {User} from "../../model/User.js";
import {Database} from "../../Database.js";
export class UserPgPromiseManager extends UserManager {
async getById(id: number): Promise<User> {
const raw = await Database.db.oneOrNone(
'SELECT * FROM "user" WHERE id = $1;', id
);
return this.serialize(raw);
}
async getByName(name: string): Promise<User> {
const raw = await Database.db.oneOrNone(
'SELECT * FROM "user" WHERE name = $1;', name
);
return this.serialize(raw);
}
async withIdExists(id: number): Promise<boolean> {
const response = await Database.db.oneOrNone(
'SELECT count(*) AS row_count FROM "user" WHERE id = $1;', id
);
return response.row_count > 0;
}
async withNameExists(name: string): Promise<boolean> {
const response = await Database.db.oneOrNone(
'SELECT count(*) AS row_count FROM "user" WHERE name = $1;', name
);
return response.row_count > 0;
}
async insert(user: Omit<User, 'id'>): Promise<User> {
const raw = await Database.db.oneOrNone(
'INSERT INTO "user" (name) VALUES (${name}) RETURNING *;', user
);
return this.serialize(raw);
}
//region serialization
protected serialize(raw: any): User {
return {
id: raw.id,
name: raw.name
};
}
protected deserialize(parsed: User): any {
throw new Error("Method not implemented.")
}
//endregion
}

View file

@ -0,0 +1,27 @@
import {UserScoresManager} from "../UserScoresManager.js";
import {UserScores} from "../../model/UserScores.js";
import {Database} from "../../Database.js";
export class UserScoresPgPromiseManager extends UserScoresManager {
public async getById(id: number): Promise<UserScores> {
const raw = await Database.db.oneOrNone(
'SELECT * FROM user_scores WHERE user_id = $1;', id
);
return this.serialize(raw);
}
protected serialize(raw: any): UserScores {
return {
userId: raw.user_id,
highscore: raw.highscore,
totalScore: raw.total_score,
totalPlaytime: raw.total_playtime,
averageScore: raw.average_score,
gamesPlayed: raw.games_played,
};
}
protected deserialize(parsed: UserScores): any {
throw new Error("Method not implemented.")
}
}

View file

@ -1,7 +1,10 @@
export type Leaderboard<T> = LeaderboardEntry<T>[]; export type Leaderboard<T> = LeaderboardEntry<T>[];
export type HighscoreLeaderboard = Leaderboard<number>;
export type TimeLeaderboard = Leaderboard<string>;
export interface LeaderboardEntry<T> { export interface LeaderboardEntry<T> {
username: number,
rank: number, rank: number,
username: string,
score: T, score: T,
} }

View file

@ -0,0 +1,4 @@
export interface User {
id?: number,
name: string,
}

View file

@ -1,8 +1,10 @@
import {Time} from "./Time.js";
export interface UserScores { export interface UserScores {
username: string, userId: number,
highscore: number, highscore: number,
totalScore: number, totalScore: number,
totalPlaytime: string, totalPlaytime: Time,
averageScore: number, averageScore: number,
gamesPlayed: number, gamesPlayed: number,
} }

View file

@ -1,4 +0,0 @@
import {Serializer} from "./Serializer.js";
import {Leaderboard} from "../model/Leaderboard.js";
export interface HighscoreLeaderboardSerializer extends Serializer<Leaderboard<number>> {}

View file

@ -1,4 +0,0 @@
export interface Serializer<T> {
serialize(rawData: any): T,
deserialize(parsedData: T): any,
}

View file

@ -1,5 +0,0 @@
import {Serializer} from "./Serializer.js";
import {Leaderboard} from "../model/Leaderboard.js";
import {Time} from "../model/Time.js";
export interface TimeLeaderboardSerializer extends Serializer<Leaderboard<Time>> {}

View file

@ -1,4 +0,0 @@
import {UserScores} from "../model/UserScores.js";
import {Serializer} from "./Serializer.js";
export interface UserScoresSerializer extends Serializer<UserScores>{}

View file

@ -1,20 +0,0 @@
import {HighscoreLeaderboardSerializer} from "../HighscoreLeaderboardSerializer.js";
import {Leaderboard, LeaderboardEntry} from "../../model/Leaderboard.js";
export class HighscoreLeaderboardPgPromiseSerializer implements HighscoreLeaderboardSerializer {
deserialize(parsedData: Leaderboard<number>): any {
throw new Error("Method not implemented.")
}
serialize(rawData: any): Leaderboard<number> {
return rawData.map((item) => {
let newItem: LeaderboardEntry<number> = {
rank: item.rank,
username: item.name,
score: item.highscore,
}
return newItem
});
}
}

View file

@ -1,20 +0,0 @@
import {TimeLeaderboardSerializer} from "../TimeLeaderboardSerializer.js";
import {Leaderboard, LeaderboardEntry} from "../../model/Leaderboard.js";
import {Time} from "../../model/Time.js";
export class TimeLeaderboardPgPromiseSerializer implements TimeLeaderboardSerializer {
deserialize(parsedData: Leaderboard<Time>): any {
throw new Error("Method not implemented.")
}
serialize(rawData: any): Leaderboard<Time> {
return rawData.map((item) => {
let newItem: LeaderboardEntry<Time> = {
rank: item.rank,
username: item.name,
score: item.total_playtime,
}
return newItem
});
}
}

View file

@ -1,20 +0,0 @@
import {UserScores} from "../../model/UserScores.js";
import {UserScoresSerializer} from "../UserScoresSerializer.js";
export class UserScoresPgPromiseSerializer implements UserScoresSerializer {
deserialize(parsedData: UserScores): any {
throw new Error("Method not implemented");
}
serialize(rawData: any): UserScores {
return {
username: rawData.name,
highscore: rawData.highscore,
totalScore: rawData.total_score,
totalPlaytime: rawData.total_playtime,
averageScore: rawData.averageScore,
gamesPlayed: rawData.games_played,
};
}
}

View file

@ -1,30 +1,73 @@
import express from "express"; import express from "express";
import {Database} from "./Database.js"; import { body, param, validationResult } from 'express-validator';
import {UserScoresPgPromiseManager} from "./manager/pgPromise/UserScoresPgPromiseManager.js";
import {UserPgPromiseManager} from "./manager/pgPromise/UserPgPromiseManager.js";
import {UserManager} from "./manager/UserManager.js";
import {UserScoresManager} from "./manager/UserScoresManager.js"; import {UserScoresManager} from "./manager/UserScoresManager.js";
import {UserScoresPgPromiseSerializer} from "./serializer/pgpromise/UserScoresPgPromiseSerializer.js"; import {User} from "./model/User.js";
import bodyParser from "body-parser";
export const userRoute = express.Router() export const userRoute = express.Router()
userRoute.use(bodyParser.json) userRoute.post(
'/register',
userRoute.get('/:username/scores', async (req, res) => { body('name')
let data = await Database.db.oneOrNone( .isString()
'SELECT * FROM user_scores INNER JOIN "user" ON user_scores.user_id = "user".id WHERE "user".name = $1;', .isLength({min: 3, max: 32})
[req.params.username]) .matches('[a-zA-Z0-9_.\\- ]*'),
.catch((error) => console.log(error.message) async (req, res) => {
) try {
let userDataManager: UserScoresManager = new UserScoresManager(data, new UserScoresPgPromiseSerializer); //region validate parameters
res.json(userDataManager.content); const errors = validationResult(req);
}) if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
userRoute.post('/register', async (req, res) => {
if (req.body.name == undefined) {
res.status(400).send("'name' was not defined");
return;
} }
await Database.db.none( //endregion
'INSERT INTO "user" (name) VALUES ($1);', [req.body.name]
); const username: string = req.body.name;
res.status(200).send(); const userManager: UserManager = new UserPgPromiseManager();
})
// check if username already exists
if (await userManager.withNameExists(username)) {
return res.status(400).json({ errors: [{msg: `User with name '${username}' already exists.`}] })
}
// insert & return user
const inserted: User = await userManager.insert({name: username});
res.json(inserted);
} catch (error) {
// handle errors
console.log(error)
res.status(500).json({ errors: [{msg: "Internal server error"}]})
}
}
)
userRoute.get('/:userId/scores',
param('userId').isInt({min: 1}),
async (req, res) => {
//region validate parameters
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
//endregion
const userId: number = req.params.userId;
const userManager: UserManager = new UserPgPromiseManager;
try {
// check if user with given id exists
if (!await userManager.withIdExists(userId)) {
return res.status(400).json({ errors: [{msg: `User with id ${userId} does not exist.`}] })
}
// get & return data
const userScoresManager: UserScoresManager = new UserScoresPgPromiseManager;
const userScores = await userScoresManager.getById(userId);
res.json(userScores);
} catch (error) {
// handle errors
console.log(error)
res.status(500).json({ errors: [{msg: "Internal server error"}]})
}
}
)

View file

@ -4,7 +4,7 @@ CREATE TABLE "user" (
); );
CREATE TABLE user_scores ( CREATE TABLE user_scores (
user_id INT PRIMARY KEY REFERENCES "user", user_id INT PRIMARY KEY,
highscore INT NOT NULL DEFAULT 0, highscore INT NOT NULL DEFAULT 0,
total_score INT NOT NULL DEFAULT 0, total_score INT NOT NULL DEFAULT 0,
total_playtime TIME NOT NULL DEFAULT '00:00:00', total_playtime TIME NOT NULL DEFAULT '00:00:00',