From 22da9af53d970d3a9b59055a5ddb77a21595b6f8 Mon Sep 17 00:00:00 2001 From: Tzvetan Trave Date: Thu, 2 Oct 2025 11:21:42 +0200 Subject: [PATCH] improved user API, see doc --- doc/user/user.md | 150 ++++++++++++++------ src/api/user/TODO | 45 ++---- src/api/user/default.js | 304 +++++++++++++++++++++++++++------------- 3 files changed, 326 insertions(+), 173 deletions(-) diff --git a/doc/user/user.md b/doc/user/user.md index cdc5779..3c18841 100644 --- a/doc/user/user.md +++ b/doc/user/user.md @@ -5,9 +5,12 @@ Available endpoints: - POST `/users/:userId/friends` - POST `/users/:userId/matchHistory` - GET `/users` +- GET `/users/count` - GET `/users/:userId` - GET `/users/:userId/friends` +- GET `/users/:userId/friends/count` - GET `/users/:userId/matchHistory` +- GET `/users/:userId/matchHistory/count` - PATCH `/users/:userId/:member` - DELETE `/users/:userId` - DELETE `/users/:userId/:member` @@ -54,16 +57,9 @@ Can return: } ``` -## POST `/users/:userId/friends` +## POST `/users/:userId/friends/:friendId` -Used to add a friend - -Input needed : -```json -{ - "friend": "" -} -``` +Used to add a friend to an user Can return: - 200 with response @@ -72,7 +68,7 @@ Can return: "msg": "Friend added successfully" } ``` -- 400 with response (if no user is specified in header, or friend is the user specified in header, or no friend is specified in body) +- 400 with response (if no user is specified in header, or friend is the user specified in header, or friend is already added) ```json { "error": "" @@ -93,7 +89,7 @@ Can return: ## POST `/users/:userId/matchHistory` -Used to add a match result +Used to add a match result to an user Input needed : ```json @@ -130,22 +126,49 @@ Can return: } ``` -## GET `/users` +## GET `/users?iStart=&iEnd=` -Used to get the user list +Used to get the list of users + +Can return: +- 200 with response (list of user objects (between iStart and iEnd)) +```json +{ + "users": + [ + { + "username": "", + "displayName": "", + "wins": , + "losses": + }, + ... + ] +} +``` +- 400 with response (if iStart/iEnd is missing, or iEnd < iStart) +```json +{ + "error": "" +} +``` +- 404 with response (if no users exist in the selected range) +```json +{ + "error": "" +} +``` + +## GET `/users/count` + +Used to get the number of users Always returns: -- 200 with response (list of user objects) +- 200 with response ```json -[ - { - "username": "", - "displayName": "", - "wins": , - "losses": - }, - ... -] +{ + "n_": +} ``` ## GET `/users/:userId` @@ -169,38 +192,57 @@ Can return: } ``` -## GET `/users/:userId/friends` +## GET `/users/:userId/friends?iStart=&iEnd=` -Used to the friends of a user +Used to get the friends of an user Can return: -- 200 with response (list of friend objects) +- 200 with response (list of friend objects (between iStart and iEnd)) ```json -[ - { - "friendName": "" - }, - ... -] +{ + "friends": + [ + { + "friendName": "" + }, + ... + ] +} ``` -- 404 with response (if user does not exist, or user does not have friends) +- 400 with response (if iStart/iEnd is missing, or iEnd < iStart) +```json +{ + "error": "" +} +``` +- 404 with response (if user does not exist, or no friends exist in the selected range) ```json { "error": "" } ``` -## GET `/users/:userId/matchHistory` +## GET `/users/:userId/friends/count` -Used to the match history of a user +Used to get the number of friends of an user -Input needed : +Can return: +- 200 with response ```json { - "iStart": , - "iEnd": + "n_": } ``` +- 404 with response (if user does not exist) +```json +{ + "error": "" +} +``` + +## GET `/users/:userId/matchHistory?iStart=&iEnd=` + +Used to get the match history of an user Can return: - 200 with response (list of matches results (between iStart and iEnd)) @@ -225,7 +267,25 @@ Can return: "error": "" } ``` -- 404 with response (if user does not exist, or user did not play any matches) +- 404 with response (if user does not exist, or no matches exist in the selected range) +```json +{ + "error": "" +} +``` + +## GET `/users/:userId/matchHistory/count` + +Used to get the number of matches an user played + +Can return: +- 200 with response +```json +{ + "n_": +} +``` +- 404 with response (if user does not exist) ```json { "error": "" @@ -234,7 +294,7 @@ Can return: ## PATCH `/users/:userId/:member` -Used to modify the member of a user (only displayName can be modified) +Used to modify a member of an user (only displayName can be modified) Input needed : ```json @@ -271,7 +331,7 @@ Can return: ## DELETE `/users/:userId` -Used to delete a user +Used to delete an user Can return: - 200 with response @@ -289,7 +349,7 @@ Can return: ## DELETE `/users/:userId/:member` -Used to delete a member (only displayName can be deleted) +Used to delete a member of an user (only displayName can be deleted) Can return: - 200 with response @@ -319,7 +379,7 @@ Can return: ## DELETE `/users/:userId/friends` -Used to delete friends +Used to delete the friends of an user Can return: - 200 with response @@ -349,7 +409,7 @@ Can return: ## DELETE `/users/:userId/friends/:friendId` -Used to delete a friend +Used to delete a friend of an user Can return: - 200 with response @@ -379,7 +439,7 @@ Can return: ## DELETE `/users/:userId/matchHistory` -Used to delete the match history +Used to delete the match history of an user Can return: - 200 with response diff --git a/src/api/user/TODO b/src/api/user/TODO index 83a6f8c..653d4f3 100644 --- a/src/api/user/TODO +++ b/src/api/user/TODO @@ -1,39 +1,16 @@ Todo : -- crate a whole image upload API that ensures files are not executables, converts to a single type, stores the image and returns a UID to address them -- create users with all the necessary arguments (assign avatar randomly if none is provided) -- add endpoints to return number of friends and matches -- use more schema in endpoints ? instead of using many checks everywhere +- test matchHistory & wins/losses + +- create users with an avatar (by default) -> POST/GET/PATCH/DELETE avatar +- create a whole image upload API that ensures files are not executables, converts to a single type, stores the image and returns a UID to address them +- add a privacy setting so not anybody can GET friends, match history, etc. ? + + + +- choose where to use == and === +- use more schema in endpoints for querystring and body - split code into files with functions called in the endpoints -- test everything (using Postman) -POST user : -- uploading the avatar involves annoying file handling functions -- avatar must be chosen randomly if not provided - -GET friends : -- should also work with indexes ideally (like GET matchHistory) - -POST friends : -- rework to make work more similarly to POST matchHistory ? - -PATCH : -- changing the avatar involves annoying file handling functions - -DELETE : -- what can be deleted ? --> users --> friends --> user info ? like display name, avatar, or should they just be changeable ? --> match history ? does it need to be deletable to comply with RGPD ? - - - -Known issues : -- When game ends we must ensure only one match result is written to the blockchain -> not an issue if we do the server-side as the server can make the single post, but if it is client-side we must take care not to send two (either by creating an API for the game that will have the necessary protections or by adding these protections directly into the user API) --> Right now POST matchHistory will send the two matches to the blockchain API - --> Users set to private should not appear in the friends lists of other public users - --> Right now the client can only get his own friends. Do we not want any other client to be able to see his friends ? +Always update API doc diff --git a/src/api/user/default.js b/src/api/user/default.js index d95fa8e..ef9cf43 100644 --- a/src/api/user/default.js +++ b/src/api/user/default.js @@ -1,29 +1,33 @@ import fastifyJWT from '@fastify/jwt'; import fastifyCookie from '@fastify/cookie'; import Database from 'better-sqlite3'; -import fs from 'fs'; const env = process.env.NODE_ENV || 'development'; +let database; if (!env || env === 'development') { - const database = new Database(":memory:", { verbose: console.log }); + database = new Database(':memory:', { verbose: console.log }); } else { const dbPath = process.env.DB_PATH || '/db/db.sqlite' - const database = new Database(dbPath); + database = new Database(dbPath); } function prepareDB() { database.exec(` CREATE TABLE IF NOT EXISTS userData ( - username TEXT PRIMARY KEY, + id INTEGER PRIMARY KEY AUTOINCREMENT, + username TEXT, displayName TEXT, - avatar BLOB, wins INTEGER, - losses INTEGER + losses INTEGER, + UNIQUE(username), + CHECK(wins >= 0), + CHECK(losses >= 0) ) STRICT `); database.exec(` CREATE TABLE IF NOT EXISTS friends ( + id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, friendName TEXT, UNIQUE(username, friendName), @@ -42,25 +46,31 @@ function prepareDB() { prepareDB(); // POST -const createUser = database.prepare('INSERT INTO userData (username, displayName, avatar, wins, losses) VALUES (?, ?, ?, 0, 0);'); +const createUser = database.prepare('INSERT INTO userData (username, displayName, wins, losses) VALUES (?, ?, 0, 0);'); const addFriend = database.prepare('INSERT INTO friends (username, friendName) VALUES (?, ?);'); const addMatch = database.prepare('INSERT INTO matchHistory (username, matchId) VALUES (?, ?);'); +const incWins = database.prepare('UPDATE userData SET wins = wins + 1 WHERE username = ?;'); +const incLosses = database.prepare('UPDATE userData SET losses = losses + 1 WHERE username = ?'); // PATCH const changeDisplayName = database.prepare('UPDATE userData SET displayName = ? WHERE username = ?;'); -const changeAvatar = database.prepare('UPDATE userData SET avatar = ? WHERE username = ?;'); // GET -const getUserData = database.prepare('SELECT * FROM userData;'); -const getUserInfo = database.prepare('SELECT * FROM userData WHERE username = ?;'); -const getFriends = database.prepare('SELECT friendName FROM friends WHERE username = ?;'); -const getMatchHistory = database.prepare('SELECT matchId FROM matchHistory WHERE username = ? AND id BETWEEN ? AND ? ORDER BY id ASC;'); +const getUserData = database.prepare('SELECT username, displayName, wins, losses FROM userData LIMIT ? OFFSET ?;'); +const getUserInfo = database.prepare('SELECT username, displayName, wins, losses FROM userData WHERE username = ?;'); +const getFriends = database.prepare('SELECT friendName FROM friends WHERE username = ? LIMIT ? OFFSET ?;'); +const getFriend = database.prepare('SELECT friendName FROM friends WHERE username = ? AND friendName = ?;'); +const getMatchHistory = database.prepare('SELECT matchId FROM matchHistory WHERE username = ? LIMIT ? OFFSET ?;'); +const getNumberUsers = database.prepare('SELECT COUNT (DISTINCT username) AS n_users FROM userData;'); +const getNumberFriends = database.prepare('SELECT COUNT (DISTINCT friendName) AS n_friends FROM friends WHERE username = ?;'); +const getNumberMatches = database.prepare('SELECT COUNT (DISTINCT id) AS n_matches FROM matchHistory WHERE username = ?;') // DELETE const deleteUser = database.prepare('DELETE FROM userData WHERE username = ?;'); const deleteFriend = database.prepare('DELETE FROM friends WHERE username = ? AND friendName = ?;'); const deleteFriends = database.prepare('DELETE FROM friends WHERE username = ?;'); const deleteMatchHistory = database.prepare('DELETE FROM matchHistory WHERE username = ?;'); +const deleteStats = database.prepare('UPDATE userData SET wins = 0, losses = 0 WHERE username = ?;'); /** * @param {import('fastify').FastifyInstance} fastify @@ -75,7 +85,7 @@ export default async function(fastify, options) { }); fastify.register(fastifyCookie); - fastify.decorate("authenticate", async function(request, reply) { + fastify.decorate('authenticate', async function(request, reply) { try { const jwt = await request.jwtVerify(); request.user = jwt.user; @@ -84,11 +94,11 @@ export default async function(fastify, options) { } }); - fastify.decorate("authenticateAdmin", async function(request, reply) { + fastify.decorate('authenticateAdmin', async function(request, reply) { try { const jwt = await request.jwtVerify(); if (jwt.user !== 'admin') { - throw (""); + throw ('You lack administrator privileges'); } } catch (err) { reply.code(401).send({ error: 'Unauthorized' }); @@ -98,17 +108,39 @@ export default async function(fastify, options) { // GET fastify.get('/users', { preHandler: [fastify.authenticate] }, async (request, reply) => { try { - const users = getUserData.all(); + const { iStart, iEnd } = request.query; + if (!iStart || !iEnd) { + return reply.code(400).send({ error: "Please specify both a starting and an ending index" }); + } + if (Number(iEnd) < Number(iStart)) { + return reply.code(400).send({ error: "Starting index cannot be strictly inferior to ending index" }); + } + const users = getUserData.all(Number(iEnd) - Number(iStart), Number(iStart)); + if (!users.length) { + return reply.code(404).send({ error: "No users exist in the selected range" }); + } return reply.code(200).send({ users }); } catch (err) { fastify.log.error(err); return reply.code(500).send({ error: "Internal server error" }); } }); + fastify.get('/users/count', { preHandler: [fastify.authenticate] }, async (request, reply) => { + try { + const row = getNumberUsers.get(); + return reply.code(200).send({ n_users: row.n_users }); + } catch (err) { + fastify.log.error(err); + return reply.code(500).send({ error: "Internal server error" }); + } + }); fastify.get('/users/:userId', { preHandler: [fastify.authenticate] }, async (request, reply) => { try { - const info = getUserInfo.get(request.params.userId); - return reply.code(200).send({ info }); + if (!getUserInfo.get(userId)) { + return reply.code(404).send({ error: "User does not exist" }); + } + const userInfo = getUserInfo.get(request.params.userId); + return reply.code(200).send({ userInfo }); } catch (err) { fastify.log.error(err); return reply.code(500).send({ error: "Internal server error" }); @@ -120,13 +152,31 @@ export default async function(fastify, options) { if (!getUserInfo.get(userId)) { return reply.code(404).send({ error: "User does not exist" }); } - if (userId == request.user || request.user == 'admin') { - const friends = getFriends.all(userId); - if (!friends) { - return reply.code(404).send({ error: "User does not have friends D:" }); - } - return reply.code(200).send({ friends }); + const { iStart, iEnd } = request.query; + if (!iStart || !iEnd) { + return reply.code(400).send({ error: "Please specify both a starting and an ending index" }); } + if (Number(iEnd) < Number(iStart)) { + return reply.code(400).send({ error: "Starting index cannot be strictly inferior to ending index" }); + } + const friends = getFriends.all(userId, Number(iEnd) - Number(iStart), Number(iStart)); + if (!friends.length) { + return reply.code(404).send({ error: "No friends exist in the selected range" }); + } + return reply.code(200).send({ friends }); + } catch (err) { + fastify.log.error(err); + return reply.code(500).send({ error: "Internal server error" }); + } + }); + fastify.get('/users/:userId/friends/count', { preHandler: [fastify.authenticate] }, async (request, reply) => { + try { + const userId = request.params.userId; + if (!getUserInfo.get(userId)) { + return reply.code(404).send({ error: "User does not exist" }); + } + const row = getNumberFriends.get(userId); + return reply.code(200).send({ n_friends: row.n_friends }); } catch (err) { fastify.log.error(err); return reply.code(500).send({ error: "Internal server error" }); @@ -138,78 +188,91 @@ export default async function(fastify, options) { if (!getUserInfo.get(userId)) { return reply.code(404).send({ error: "User does not exist" }); } - if (userId == request.user || request.user == 'admin') { - if (!matchHistory) { - return reply.code(404).send({ error: "User has not participated in any matches yet" }); - } - if (!request.body || !request.body.i_start || !request.body.i_end) { - return reply.code(400).send({ error: "Please specify both a strting and an ending index" }); - } - if (request.body.i_end < request.body.i_start) { - return reply.code(400).send({ error: "Starting index cannot be strictly inferior to ending index" }); - } - const matchHistoryId = getMatchHistory.all(userId, request.body.i_start, request.body.i_end - 1); - const promises = matchHistoryId.map(async (id) => { - const res = await fetch('/' + userId, { method: "GET", headers: { "Content-Type": "application/json" } }); - if (!res.ok) - throw new Error('Failed to fetch item ${id}'); - return res.json(); - }); - const matchHistory = await Promise.all(promises); - return reply.code(200).send({ matchHistory }); + const { iStart, iEnd } = request.query; + if (!iStart || !iEnd) { + return reply.code(400).send({ error: "Please specify both a starting and an ending index" }); } + if (Number(iEnd) < Number(iStart)) { + return reply.code(400).send({ error: "Starting index cannot be strictly inferior to ending index" }); + } + const matchHistoryId = getMatchHistory.all(userId, Number(iEnd) - Number(iStart), Number(iStart)); + if (!matchHistoryId.length) { + return reply.code(404).send({ error: "No matches exist in the selected range" }); + } + const promises = matchHistoryId.map(async (id) => { + const res = await fetch('https://transcendence-api-scoreStore:3000/' + id, { method: "GET", headers: { "Content-Type": "application/json" } }); + if (!res.ok) + throw new Error('Failed to fetch item from blockchain API'); + return res.json(); + }); + const matchHistory = await Promise.all(promises); + return reply.code(200).send({ matchHistory }); } catch (err) { fastify.log.error(err); return reply.code(500).send({ error: "Internal server error" }); } - ); + }); + fastify.get('/users/:userId/matchHistory/count', { preHandler: [fastify.authenticate] }, async (request, reply) => { + try { + const userId = request.params.userId; + if (!getUserInfo.get(userId)) { + return reply.code(404).send({ error: "User does not exist" }); + } + const row = getNumberMatches.get(userId); + return reply.code(200).send({ n_matches: row.n_matches }); + } catch (err) { + fastify.log.error(err); + return reply.code(500).send({ error: "Internal server error" }); + } + }); // POST fastify.post('/users/:userId', { preHandler: [fastify.authenticateAdmin] }, async (request, reply) => { try { const userId = request.params.userId; - if (request.user != 'admin') { + if (!request.user || !request.user.user) { + return reply.code(400).send({ error: "Please specify a user" }); + } + if (request.user.user !== 'admin') { return reply.code(401).send({ error: "Unauthorized" }); } if (getUserInfo.get(userId)) { return reply.code(400).send({ error: "User already exist" }); } if (!request.body || !request.body.displayName) { - return reply.code(400).send({ error: "Please specify a display name and an avatar" }); + return reply.code(400).send({ error: "Please specify a display name" }); } - const avatar; - if (request.body.avatar) { - avatar = request.body.avatar; - } else { - avatar = 1;// randomly chosen avatar - } - createUser.run(userId, request.body.displayName, avatar); - return reply.code(200).send({ msg: "User created sucessfully" }); + createUser.run(userId, request.body.displayName); + return reply.code(200).send({ msg: "User created successfully" }); } catch (err) { fastify.log.error(err); return reply.code(500).send({ error: "Internal server error" }); } }); - fastify.post('/users/:userId/friends', { preHandler: [fastify.authenticate] }, async (request, reply) => { + fastify.post('/users/:userId/friends/:friendId', { preHandler: [fastify.authenticate] }, async (request, reply) => { try { const userId = request.params.userId; - if (request.user != 'admin' && request.user != userId) { - return reply.code(401).send({ error: "Unauthorized" }); - } - if (!request.body || !request.body.user) { + if (!request.user) { return reply.code(400).send({ error: "Please specify a user" }); } + if (request.user !== 'admin' && request.user !== userId) { + return reply.code(401).send({ error: "Unauthorized" }); + } if (!getUserInfo.get(userId)) { return reply.code(404).send({ error: "User does not exist" }); } - if (!getUserInfo.get(request.body.user)) { + const friendId = request.params.friendId; + if (!getUserInfo.get(friendId)) { return reply.code(404).send({ error: "Friend does not exist" }); } - if (request.body.user === userId) { + if (friendId === userId) { return reply.code(400).send({ error: "You can't add yourself :D" }); } - addFriend.run(userId, request.body.user) - return reply.code(200).send({ msg: "Friend added sucessfully" }); + if (getFriend.get(userId, friendId)) { + return reply.code(400).send({ error: "Friend already added" }); + } + addFriend.run(userId, friendId) + return reply.code(200).send({ msg: "Friend added successfully" }); } catch (err) { fastify.log.error(err); return reply.code(500).send({ error: "Internal server error" }); @@ -218,29 +281,37 @@ export default async function(fastify, options) { fastify.post('/users/:userId/matchHistory', { preHandler: [fastify.authenticate] }, async (request, reply) => { try { const userId = request.params.userId; - if (request.user != 'admin' && request.user != userId) { + if (!request.user) { + return reply.code(400).send({ error: "Please specify a user" }); + } + if (request.user !== 'admin' && request.user !== userId) { return reply.code(401).send({ error: "Unauthorized" }); } - if (!request.body || !request.body.user || !request.body.p1Score || !request.body.p2Score) { - return reply.code(400).send({ error: "Please specify the second player and the score of both players" }); + if (!request.body || !request.body.opponent || !request.body.p1Score || !request.body.p2Score) { + return reply.code(400).send({ error: "Please specify the opponent and the score of both players" }); } if (!getUserInfo.get(userId)) { return reply.code(404).send({ error: "User does not exist" }); } - if (!getUserInfo.get(request.body.user)) { - return reply.code(404).send({ error: "Second player does not exist" }); + if (!getUserInfo.get(request.body.opponent)) { + return reply.code(404).send({ error: "Opponent does not exist" }); } - if (request.body.user === userId) { - return reply.code(400).send({ error: "Do you have dementia ? You cannot have played a match against yourself, gramps" }); + if (request.body.opponent === userId) { + return reply.code(400).send({ error: "Do you have dementia ? You cannot have played a match against yourself gramps" }); } if (request.body.p1Score < 0 || request.body.p2Score < 0) { return reply.code(400).send({ error: "A score cannot be strictly negative" }); } - const res = await fetch('/', { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ p1: userId, p2: request.body.user, p1Score: request.body.p1Score, p2Score: request.body.p2Score }) }); + const res = await fetch('http://localhost:3003/', { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ p1: userId, p2: request.body.opponent, p1Score: request.body.p1Score, p2Score: request.body.p2Score }) }); if (!res.ok) return reply.code(500).send({ error: "Internal server error" }); addMatch.run(userId, res.id); - return reply.code(200).send({ msg: "Match history retrieved successfully" }); + if (request.body.p1Score > request.body.p2Score) { + incWins.run(userId); + } else if (request.body.p1Score < request.body.p2Score) { + incLosses.run(userId); + } + return reply.code(200).send({ msg: "Match successfully saved to the blockchain" }); } catch (err) { fastify.log.error(err); return reply.code(500).send({ error: "Internal server error" }); @@ -251,7 +322,10 @@ export default async function(fastify, options) { fastify.patch('/users/:userId/:member', { preHandler: [fastify.authenticate] }, async (request, reply) => { try { const userId = request.params.userId; - if (request.user != 'admin' && request.user != userId) { + if (!request.user || !request.user.user) { + return reply.code(400).send({ error: "Please specify a user" }); + } + if (request.user.user !== 'admin' && request.user.user !== userId) { return reply.code(401).send({ error: "Unauthorized" }); } if (!getUserInfo.get(userId)) { @@ -263,16 +337,7 @@ export default async function(fastify, options) { return reply.code(400).send({ error: "Please specify a displayName" }); } changeDisplayName.run(request.body.displayName, userId); - return reply.code(200).send({ msg: "Display name modified sucessfully" }); - } - if (member === 'avatar') { - if (!request.body || !request.body.avatar) { - return reply.code(400).send({ error: "Please specify an avatar" }); - } - changeAvatar.run(request.body.avatar, userId); - return reply.code(200).send({ msg: "Avatar modified sucessfully" }); - } - return reply.code(400).send({ error: "Avatar does not exist" }) + return reply.code(200).send({ msg: "Display name modified successfully" }); } return reply.code(400).send({ error: "Member does not exist" }) } catch (err) { @@ -282,16 +347,15 @@ export default async function(fastify, options) { }); // DELETE - /** - * @description Can be used to delete a user from the db - */ fastify.delete('/users/:userId', { preHandler: [fastify.authenticateAdmin] }, async (request, reply) => { try { if (!getUserInfo(request.params.userId)) { return reply.code(404).send({ error: "User does not exist" }); } - deleteUser.run(request.params.userId); + deleteMatchHistory.run(request.params.userId); deleteFriends.run(request.params.userId); + deleteUser.run(request.params.userId); + return reply.code(200).send({ msg: "User deleted successfully" }); } catch (err) { fastify.log.error(err); return reply.code(500).send({ error: "Internal server error" }); @@ -299,12 +363,19 @@ export default async function(fastify, options) { }); fastify.delete('/users/:userId/:member', { preHandler: fastify.authenticate }, async (request, reply) => { try { - const user = request.user; + if (!request.user || !request.user.user) { + return reply.code(400).send({ error: "Please specify a user" }); + } + const userId = request.params.userId; + if (!getUserInfo.get(userId)) { + return reply.code(404).send({ error: "User does not exist" }); + } + const user = request.user.user; const member = request.params.member; if (user == 'admin' || user == request.params.userId) { - if (member == 'displayName') { + if (member === 'displayName') { changeDisplayName.run("", request.params.userId); - return reply.code(200).send({ msg: "Display name cleared sucessfully" }); + return reply.code(200).send({ msg: "Display name deleted successfully" }); } return reply.code(400).send({ msg: "Member does not exist" }) } else { @@ -316,18 +387,63 @@ export default async function(fastify, options) { } }); - fastify.delete('/users/:userId/friends/:friendId', { preHandler: [fastify.authenticate] }, async (request, reply) => { + fastify.delete('/users/:userId/friends', { preHandler: [fastify.authenticate] }, async (request, reply) => { try { + if (!request.user || !request.user.user) { + return reply.code(400).send({ error: "Please specify a user" }); + } const userId = request.params.userId; - const friendId = request.params.friendId; if (!getUserInfo.get(userId)) { return reply.code(404).send({ error: "User does not exist" }); } - if (request.user != 'admin' && request.user != userId) { + if (request.user.user != 'admin' && request.user.user != userId) { return reply.code(401).send({ error: "Unauthorized" }); } + deleteFriends.run(userId); + return reply.code(200).send({ msg: "Friends deleted successfully" }); + } catch (err) { + fastify.log.error(err); + return reply.code(500).send({ error: "Internal server error" }); + } + }); + fastify.delete('/users/:userId/friends/:friendId', { preHandler: [fastify.authenticate] }, async (request, reply) => { + try { + if (!request.user || !request.user.user) { + return reply.code(400).send({ error: "Please specify a user" }); + } + const userId = request.params.userId; + if (!getUserInfo.get(userId)) { + return reply.code(404).send({ error: "User does not exist" }); + } + if (request.user.user != 'admin' && request.user.user != userId) { + return reply.code(401).send({ error: "Unauthorized" }); + } + const friendId = request.params.friendId; + if (!getFriend.get(friendId)) { + return reply.code(404).send({ error: "Friend does not exist" }); + } deleteFriend.run(userId, friendId); - return reply.code(200).send({ msg: "Friend remove sucessfully" }); + return reply.code(200).send({ msg: "Friend deleted successfully" }); + } catch (err) { + fastify.log.error(err); + return reply.code(500).send({ error: "Internal server error" }); + } + }); + fastify.delete('/users/:userId/matchHistory', { preHandler: [fastify.authenticate] }, async (request, reply) => { + try { + if (!request.user || !request.user.user) { + return reply.code(400).send({ error: "Please specify a user" }); + } + const userId = request.params.userId; + if (!getUserInfo.get(userId)) { + return reply.code(404).send({ error: "User does not exist" }); + } + if (request.user.user != 'admin' && request.user.user != userId) { + return reply.code(401).send({ error: "Unauthorized" }); + } + deleteMatchHistory.run(userId); + deleteStats.run(userId); + return reply.code(200).send({ msg: "Match history deleted successfully" }); } catch (err) { fastify.log.error(err); return reply.code(500).send({ error: "Internal server error" });