diff --git a/.env.example b/.env.example index f46c8c7..821e903 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,5 @@ POSTGRES_PORT=5432 EXPRESS_PORT=3000 -FRONTEND_PORT=8080 POSTGRES_USER=postgres POSTGRES_PASSWORD=postgres diff --git a/README.md b/README.md index 82d8404..71ff3b6 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,163 @@ # RaspberryRocketeer -## How to run +## Class Diagram of all classes -### Copy .env -First you need to copy the `.env.example`. +```mermaid +classDiagram +direction BT +class Collidable { + collides(o: Entity) boolean +} +class Database { + any _db + any db +} +class Entity { + constructor(position: Position, width: number, height: number, fill: number) + Position _position + number _width + number _height + number fill + boolean _showHitbox + update() void + draw() void + Position position + number width + number height + boolean showHitbox +} +class Game { + number id + number score + string playtime + Date date + number userId +} +class GamePgPromiseRepository { + insert(game: Game) Promise~Game~ + serialize(raw: any) Game +} +class GameRepository { + insert(game: Game) Promise~Game~ +} +class HighscoreLeaderboard +class HighscoreLeaderboardPgPromiseRepository { + getAll() Promise~HighscoreLeaderboard~ + serialize(raw: any) HighscoreLeaderboard +} +class HighscoreLeaderboardRepository { + getAll() Promise~HighscoreLeaderboard~ +} +class Leaderboard +class LeaderboardEntry { + number username + number rank + T score +} +class Obstacle { + constructor(position: Position, obstacleWidth: number, obstacleHeight: number, pipeImagePath: string) + Pipe pipeTop + Pipe pipeBottom + number padding + number speed + number _distanceBetweenPipes + number _startX + resetPosition() void + randomizeHeight() void + randomRange(min: number, max: number) number + update() void + draw() void + collides(o: Entity) boolean + any startX + any distanceBetweenPipes +} +class Pipe { + constructor(positionX: number, width: number, height: number) + any _image + update() void + draw() void + move(speed: number) void + collides(o: Entity) boolean + any image +} +class Position { + constructor(x: number, y: number) + number _x + number _y + number x + number y +} +class Raspberry { + constructor(image: string) + number lift + number gravity + number _velocity + any _image + Position position + number maxVelocity + number WIDTH + number HEIGHT + number FILL + update() void + applyGravity() void + forceBoundaries() void + boost() void + draw() void + number velocity + any image +} +class TimeLeaderboard +class TimeLeaderboardPgPromiseRepository { + getAll() Promise~TimeLeaderboard~ + serialize(raw: any) TimeLeaderboard +} +class TimeLeaderboardRepository { + getAll() Promise~TimeLeaderboard~ +} +class User { + number id + string name +} +class UserPgPromiseRepository { + getById(id: number) Promise~User~ + getByName(name: string) Promise~User~ + withIdExists(id: number) Promise~boolean~ + withNameExists(name: string) Promise~boolean~ + insert(user: Omit~User, "id"~) Promise~User~ + serialize(raw: any) User +} +class UserRepository { + getById(id: number) Promise~User~ + getByName(name: string) Promise~User~ + withIdExists(userId: number) Promise~boolean~ + withNameExists(username: string) Promise~boolean~ + insert(user: Omit~User, "id"~) Promise~User~ +} +class UserScores { + number userId + number highscore + number totalScore + string totalPlaytime + number averageScore + number gamesPlayed +} +class UserScoresPgPromiseRepository { + getById(id: number) Promise~UserScores~ + serialize(raw: any) UserScores +} +class UserScoresRepository { + getById(userId: number) Promise~UserScores~ +} + +GamePgPromiseRepository --> GameRepository +HighscoreLeaderboardPgPromiseRepository --> HighscoreLeaderboardRepository +Obstacle ..> Collidable +Obstacle --> Entity +Pipe ..> Collidable +Pipe --> Entity +Raspberry --> Entity +TimeLeaderboardPgPromiseRepository --> TimeLeaderboardRepository +UserPgPromiseRepository --> UserRepository +UserScoresPgPromiseRepository --> UserScoresRepository -```shell -cp .env.example .env ``` - -Note: It is recommended to change the values for the database user. - -### Install node packages -Go into the frontend folder using -```shell -cd frontend -``` -and run: -```shell -npm install -``` - -### Start the container (in the project root) - -```shell -docker compose up --build -``` - -You can then access the website on `localhost:8080` diff --git a/backend/api/src/gameRoute.ts b/backend/api/src/gameRoute.ts index 0de3e2c..2ac9175 100644 --- a/backend/api/src/gameRoute.ts +++ b/backend/api/src/gameRoute.ts @@ -9,9 +9,6 @@ export const gameRoute = express.Router() gameRoute.use(express.json()) -/** - * Test - */ gameRoute.post( '/add', body('playtime') @@ -21,8 +18,6 @@ gameRoute.post( body('userId') .isInt({min: 1}) .custom(userWithIdExists), - body('score') - .isInt({min: 0}), /** * After processing the errors of express-validator, inserts the game into the DB * @param req diff --git a/backend/api/src/leaderboardRoute.ts b/backend/api/src/leaderboardRoute.ts index 699180c..e77fb2e 100644 --- a/backend/api/src/leaderboardRoute.ts +++ b/backend/api/src/leaderboardRoute.ts @@ -1,5 +1,4 @@ import express from 'express'; -import {query, validationResult} from 'express-validator'; import {TimeLeaderboardRepository} from "./repositories/TimeLeaderboardRepository.js"; import {TimeLeaderboardPgPromiseRepository} from "./repositories/pgPromise/TimeLeaderboardPgPromiseRepository.js"; import {HighscoreLeaderboardPgPromiseRepository} from "./repositories/pgPromise/HighscoreLeaderboardPgPromiseRepository.js"; @@ -9,9 +8,6 @@ import {HighscoreLeaderboard, TimeLeaderboard} from "./model/Leaderboard.js"; export const leaderboardRoute = express.Router() leaderboardRoute.get('/highscore', - query('pagination').toBoolean(), - query('entriesPerPage').optional().isInt({min: 1}).toInt(), - query('page').optional().isInt({min: 0}).toInt(), /** * Returns the highscore leaderboard as JSON response, fetched from DB * @param req @@ -19,21 +15,8 @@ leaderboardRoute.get('/highscore', */ async (req, res) => { try { - //region validate parameters - const errors = validationResult(req); - if (!errors.isEmpty()) { - return res.status(400).json({ errors: errors.array() }); - } - //endregion const highscoreLeaderboardRepo: HighscoreLeaderboardRepository = new HighscoreLeaderboardPgPromiseRepository; - let highscoreLeaderboard: HighscoreLeaderboard; - if (req.query.pagination == true) { - const entriesPerPage = req.query.entriesPerPage; - const page = req.query.page; - highscoreLeaderboard = await highscoreLeaderboardRepo.getPage(entriesPerPage, page); - } else { - highscoreLeaderboard = await highscoreLeaderboardRepo.getAll(); - } + const highscoreLeaderboard: HighscoreLeaderboard = await highscoreLeaderboardRepo.getAll(); res.send(highscoreLeaderboard); } catch (error) { // handle errors @@ -43,9 +26,6 @@ leaderboardRoute.get('/highscore', }) leaderboardRoute.get('/totalplaytime', - query('pagination').toBoolean(), - query('entriesPerPage').optional().isInt({min: 1}).toInt(), - query('page').optional().isInt({min: 0}).toInt(), /** * Returns the total playtime leaderboard as JSON response, fetched from DB * @param req @@ -53,23 +33,8 @@ leaderboardRoute.get('/totalplaytime', */ async (req, res) => { try { - console.log(req.query) - //region validate parameters - const errors = validationResult(req); - if (!errors.isEmpty()) { - return res.status(400).json({ errors: errors.array() }); - } - //endregion const timeLeaderboardRepo: TimeLeaderboardRepository = new TimeLeaderboardPgPromiseRepository; - let timeLeaderboard: TimeLeaderboard; - console.log(req.query) - if (req.query.pagination == true) { - const entriesPerPage = req.query.entriesPerPage; - const page = req.query.page; - timeLeaderboard = await timeLeaderboardRepo.getPage(entriesPerPage, page); - } else { - timeLeaderboard = await timeLeaderboardRepo.getAll(); - } + const timeLeaderboard: TimeLeaderboard = await timeLeaderboardRepo.getAll(); res.send(timeLeaderboard); } catch (error) { // handle errors diff --git a/backend/api/src/repositories/HighscoreLeaderboardRepository.ts b/backend/api/src/repositories/HighscoreLeaderboardRepository.ts index d70d3d5..204feff 100644 --- a/backend/api/src/repositories/HighscoreLeaderboardRepository.ts +++ b/backend/api/src/repositories/HighscoreLeaderboardRepository.ts @@ -2,5 +2,4 @@ import {HighscoreLeaderboard} from "../model/Leaderboard.js"; export abstract class HighscoreLeaderboardRepository { abstract getAll(): Promise; - abstract getPage(entriesPerPage: number, page: number): Promise } \ No newline at end of file diff --git a/backend/api/src/repositories/TimeLeaderboardRepository.ts b/backend/api/src/repositories/TimeLeaderboardRepository.ts index 8c02717..ef9d8fe 100644 --- a/backend/api/src/repositories/TimeLeaderboardRepository.ts +++ b/backend/api/src/repositories/TimeLeaderboardRepository.ts @@ -2,5 +2,4 @@ import {TimeLeaderboard} from "../model/Leaderboard.js"; export abstract class TimeLeaderboardRepository { abstract getAll(): Promise; - abstract getPage(entriesPerPage: number, page: number); } \ No newline at end of file diff --git a/backend/api/src/repositories/pgPromise/HighscoreLeaderboardPgPromiseRepository.ts b/backend/api/src/repositories/pgPromise/HighscoreLeaderboardPgPromiseRepository.ts index 36c9043..8a7747c 100644 --- a/backend/api/src/repositories/pgPromise/HighscoreLeaderboardPgPromiseRepository.ts +++ b/backend/api/src/repositories/pgPromise/HighscoreLeaderboardPgPromiseRepository.ts @@ -5,15 +5,7 @@ import {Database} from "../../Database.js"; export class HighscoreLeaderboardPgPromiseRepository extends HighscoreLeaderboardRepository { async getAll(): Promise { 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); - } - - async getPage(entriesPerPage, page): Promise { - const raw: any = await Database.db.manyOrNone( - 'SELECT * FROM lb_highscore INNER JOIN "user" ON user_id = id ORDER BY rank LIMIT $1 OFFSET $2;', - [entriesPerPage, page * entriesPerPage] + 'SELECT * FROM lb_highscore INNER JOIN "user" ON user_id = id ORDER BY RANK;' ); return this.serialize(raw); } diff --git a/backend/api/src/repositories/pgPromise/TimeLeaderboardPgPromiseRepository.ts b/backend/api/src/repositories/pgPromise/TimeLeaderboardPgPromiseRepository.ts index b2b2ca8..1345562 100644 --- a/backend/api/src/repositories/pgPromise/TimeLeaderboardPgPromiseRepository.ts +++ b/backend/api/src/repositories/pgPromise/TimeLeaderboardPgPromiseRepository.ts @@ -5,15 +5,7 @@ import {Database} from "../../Database.js"; export class TimeLeaderboardPgPromiseRepository extends TimeLeaderboardRepository { async getAll(): Promise { 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); - } - - async getPage(entriesPerPage: number, page: number): Promise { - const raw: any = await Database.db.manyOrNone( - 'SELECT * FROM lb_total_playtime INNER JOIN "user" ON user_id = id ORDER BY rank LIMIT $1 OFFSET $2;', - [entriesPerPage, page * entriesPerPage] + 'SELECT * FROM lb_total_playtime INNER JOIN "user" ON user_id = id ORDER BY RANK;' ); return this.serialize(raw); } diff --git a/backend/api/src/userRoute.ts b/backend/api/src/userRoute.ts index 4061969..e87056f 100644 --- a/backend/api/src/userRoute.ts +++ b/backend/api/src/userRoute.ts @@ -84,30 +84,3 @@ userRoute.get('/:userId/scores', } } ) - -userRoute.get('/:name', - param('name') - .isString() - .isLength({min: 3, max: 32}) - .matches(USERNAME_VALIDATION_REGEX), - - async (req, res) => { - const errors = validationResult(req); - if (!errors.isEmpty()) { - return res.status(400).json({ errors: errors.array() }); - } - - const name: string = req.params.name; - console.log(name) - - try { - // get & return data - const userRepo: UserPgPromiseRepository = new UserPgPromiseRepository(); - const user = await userRepo.getByName(name); - res.json(user); - } catch (error) { - // handle errors - console.log(error) - res.status(500).json({ errors: [{msg: "Internal server error"}]}) - } - }) diff --git a/backend/db/initScripts/loadMockData.sql b/backend/db/initScripts/loadMockData.sql new file mode 100644 index 0000000..7735cc9 --- /dev/null +++ b/backend/db/initScripts/loadMockData.sql @@ -0,0 +1,40 @@ +insert into "user" (name) values ('dpettus0'); +insert into "user" (name) values ('egreetland1'); +insert into "user" (name) values ('smontford2'); +insert into "user" (name) values ('idagwell3'); +insert into "user" (name) values ('lgagan4'); +insert into "user" (name) values ('acarmont5'); +insert into "user" (name) values ('kjermyn6'); +insert into "user" (name) values ('dokieran7'); +insert into "user" (name) values ('pdrinkel8'); + +insert into game (user_id, score, playtime, date) values ('1', 74, '19:59', '2022-07-19'); +insert into game (user_id, score, playtime, date) values ('1', 86, '20:32', '2022-11-24'); +insert into game (user_id, score, playtime, date) values ('1', 68, '10:41', '2022-03-24'); +insert into game (user_id, score, playtime, date) values ('2', 39, '5:55', '2022-06-01'); +insert into game (user_id, score, playtime, date) values ('2', 20, '9:23', '2022-03-12'); +insert into game (user_id, score, playtime, date) values ('2', 28, '23:45', '2022-04-01'); +insert into game (user_id, score, playtime, date) values ('2', 44, '18:43', '2022-06-24'); +insert into game (user_id, score, playtime, date) values ('3', 92, '14:54', '2022-11-06'); +insert into game (user_id, score, playtime, date) values ('3', 73, '0:45', '2022-07-26'); +insert into game (user_id, score, playtime, date) values ('3', 27, '2:49', '2022-02-03'); +insert into game (user_id, score, playtime, date) values ('4', 26, '2:32', '2022-07-19'); +insert into game (user_id, score, playtime, date) values ('4', 12, '17:03', '2022-04-25'); +insert into game (user_id, score, playtime, date) values ('4', 6, '8:49', '2021-12-03'); +insert into game (user_id, score, playtime, date) values ('4', 22, '22:27', '2022-03-02'); +insert into game (user_id, score, playtime, date) values ('5', 94, '1:04', '2022-10-19'); +insert into game (user_id, score, playtime, date) values ('5', 2, '2:14', '2022-04-06'); +insert into game (user_id, score, playtime, date) values ('5', 21, '17:18', '2022-06-03'); +insert into game (user_id, score, playtime, date) values ('6', 33, '16:01', '2022-02-02'); +insert into game (user_id, score, playtime, date) values ('6', 27, '7:03', '2022-02-06'); +insert into game (user_id, score, playtime, date) values ('6', 62, '0:45', '2022-11-15'); +insert into game (user_id, score, playtime, date) values ('7', 12, '8:54', '2022-06-29'); +insert into game (user_id, score, playtime, date) values ('7', 63, '16:01', '2022-11-05'); +insert into game (user_id, score, playtime, date) values ('7', 29, '0:46', '2022-10-01'); +insert into game (user_id, score, playtime, date) values ('8', 67, '1:27', '2022-09-29'); +insert into game (user_id, score, playtime, date) values ('8', 84, '10:37', '2021-12-18'); +insert into game (user_id, score, playtime, date) values ('8', 14, '19:14', '2022-01-31'); +insert into game (user_id, score, playtime, date) values ('9', 21, '19:04', '2022-03-08'); +insert into game (user_id, score, playtime, date) values ('9', 46, '2:34', '2022-04-18'); +insert into game (user_id, score, playtime, date) values ('9', 78, '9:33', '2022-09-10'); +insert into game (user_id, score, playtime, date) values ('9', 82, '11:19', '2022-11-29'); \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 2672685..f21adcc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,32 +1,23 @@ -version: '3.1' - -services: - db: - build: backend/db - container_name: postgres-db - environment: - POSTGRES_DB: ${POSTGRES_DB} - POSTGRES_USER: ${POSTGRES_USER} - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} - ports: - - "${POSTGRES_PORT}:5432" - volumes: - - ./backend/pgdata:/var/lib/postgresql/data - - api: - build: backend/api - depends_on: - - db - container_name: express-api - ports: - - "${EXPRESS_PORT}:3000" - - vue: - build: frontend - container_name: frontend - ports: - - "${FRONTEND_PORT}:8080" - - - volumes: - - ./frontend:/app +version: '3.1' + +services: + db: + build: backend/db + container_name: postgres-db + environment: + POSTGRES_DB: ${POSTGRES_DB} + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + ports: + - "${POSTGRES_PORT}:5432" + volumes: + - ./backend/pgdata:/var/lib/postgresql/data + + api: + build: backend/api + depends_on: + - db + container_name: express-api + ports: + - "${EXPRESS_PORT}:3000" + diff --git a/docs/classes/Entity.Entity.html b/docs/classes/Entity.Entity.html index 0824cc2..39432b8 100644 --- a/docs/classes/Entity.Entity.html +++ b/docs/classes/Entity.Entity.html @@ -28,7 +28,7 @@
  • Pipe
  • Raspberry
  • +
  • Defined in Entity.ts:4
  • @@ -88,7 +88,7 @@

    Returns Entity

    +
  • Defined in Entity.ts:105
  • Properties

    @@ -97,35 +97,35 @@

    Height.

    +
  • Defined in Entity.ts:21
  • _position: Position

    Position.

    +
  • Defined in Entity.ts:9
  • _showHitbox: boolean

    Whether the hitbox (rectangular surrounding) is shown, or not.

    +
  • Defined in Entity.ts:33
  • _width: number

    Width.

    +
  • Defined in Entity.ts:15
  • fill: number

    Color.

    +
  • Defined in Entity.ts:27
  • Accessors

    @@ -137,7 +137,7 @@

    Returns number

    +
  • Defined in Entity.ts:70
  • set height(value: number): void
  • Set height.

    @@ -149,7 +149,7 @@
    value: number
  • Returns void

    +
  • Defined in Entity.ts:78
  • set position(value: Position): void
  • Set position.

    @@ -171,7 +171,7 @@
    value: Position
  • Returns void

    +
  • Defined in Entity.ts:48
  • set showHitbox(value: boolean): void
  • Set the hitbox's visibility.

    @@ -193,7 +193,7 @@
    value: boolean
  • Returns void

    +
  • Defined in Entity.ts:93
  • set width(value: number): void
  • Set width.

    @@ -215,7 +215,7 @@
    value: number
  • Returns void

    +
  • Defined in Entity.ts:63
  • Methods

    @@ -227,7 +227,7 @@

    Returns void

    +
  • Defined in Entity.ts:121
  • +
  • Defined in Entity.ts:116
  • Returns void

    +
  • Defined in Obstacle.ts:13
  • Methods

    @@ -275,7 +275,7 @@

    Returns boolean

    +
  • Defined in Obstacle.ts:89
    • @@ -296,7 +296,7 @@
      pipeImagePath: string

    Returns void

    +
  • Defined in Obstacle.ts:41
  • +
  • Defined in Obstacle.ts:80
    • @@ -328,7 +328,7 @@

    Returns number

    +
  • Defined in Obstacle.ts:70
  • +
  • Defined in Obstacle.ts:59
    • @@ -349,7 +349,7 @@ Randomises the height of the pipes using the padding variable

      Returns void

    +
  • Defined in Obstacle.ts:50
    • @@ -360,7 +360,7 @@ Randomises the height of the pipes using the padding variable

      Returns void

    +
  • Defined in Obstacle.ts:74
  • Returns void

    +
  • Defined in Pipe.ts:23
  • set position(value: Position): void
  • Set position.

    @@ -173,7 +173,7 @@

    Returns void

  • +
  • Defined in Entity.ts:48
  • set showHitbox(value: boolean): void
  • Set the hitbox's visibility.

    @@ -197,7 +197,7 @@

    Returns void

  • +
  • Defined in Entity.ts:93
  • set width(value: number): void
  • Set width.

    @@ -221,7 +221,7 @@

    Returns void

  • +
  • Defined in Entity.ts:63
  • Methods

    @@ -241,7 +241,7 @@

    Returns boolean

    +
  • Defined in Pipe.ts:68
  • +
  • Defined in Pipe.ts:48
    • @@ -269,7 +269,7 @@

    Returns void

    +
  • Defined in Pipe.ts:60
  • +
  • Defined in Pipe.ts:43
  • Returns Position

    +
  • Defined in Position.ts:55
  • Properties

    @@ -75,14 +75,14 @@

    X coordinate.

    +
  • Defined in Position.ts:9
  • _y: number

    Y coordinate.

    +
  • Defined in Position.ts:15
  • Accessors

    @@ -94,7 +94,7 @@

    Returns number

    +
  • Defined in Position.ts:22
  • set x(value: number): void
  • Set x.

    @@ -106,7 +106,7 @@
    value: number
  • Returns void

    +
  • Defined in Position.ts:30
  • set y(value: number): void
  • Set y.

    @@ -128,7 +128,7 @@
    value: number
  • Returns void

    +
  • Defined in Position.ts:45
  • Returns void

    +
  • Defined in Raspberry.ts:87
  • set position(value: Position): void
  • Set position.

    @@ -227,7 +227,7 @@

    Returns void

  • +
  • Defined in Entity.ts:48
  • set showHitbox(value: boolean): void
  • Set the hitbox's visibility.

    @@ -251,7 +251,7 @@

    Returns void

  • +
  • Defined in Entity.ts:93
  • set velocity(value: number): void
  • Sets the velocity.

    @@ -273,7 +273,7 @@
    value: number
  • Returns void

    +
  • Defined in Raspberry.ts:72
  • set width(value: number): void
  • Set width.

    @@ -297,7 +297,7 @@

    Returns void

  • +
  • Defined in Entity.ts:63
  • Methods

    @@ -309,7 +309,7 @@

    Returns void

    +
  • Defined in Raspberry.ts:114
  • +
  • Defined in Raspberry.ts:153
  • +
  • Defined in Raspberry.ts:143
  • +
  • Defined in Raspberry.ts:132
  • +
  • Defined in Raspberry.ts:160
  • +
  • Defined in Raspberry.ts:188
  • +
  • Defined in Raspberry.ts:172
  • +
  • Defined in Raspberry.ts:180
  • +
  • Defined in Raspberry.ts:123
  • +
  • Defined in Raspberry.ts:198
  • +
  • Defined in Raspberry.ts:106
  • Returns boolean

    +
  • Defined in Collidable.ts:9