Merge remote-tracking branch 'refs/remotes/origin/user-management' into user-management

This commit is contained in:
Tzvetan Trave
2025-10-19 22:02:39 +02:00
4 changed files with 110 additions and 0 deletions

41
doc/user/ping.md Normal file
View File

@ -0,0 +1,41 @@
# ping
Available endpoints:
- POST `/ping`
- GET `/ping/:userId`
Common return:
- 500 with response
```json
{
"error": "Internal server error"
}
```
## POST `/ping`
Used to send a ping and update the lastSeenTime (can be used for activity time)
Input needed : just need a valid token
Can return:
- 200
```json
{
"msg": "last seen time updated successfully"
}
```
## GET `/ping/:userId`
Used to retrive the lastSeenTime of a user
Input needed : just need a valid token
Can return:
- 200
```json
{
"isLogged": "<true/false>"
}
```

View File

@ -21,6 +21,8 @@ import { dMatchHistory } from './dMatchHistory.js';
import { pAvatar } from './pAvatar.js';
import { gAvatar } from './gAvatar.js';
import { dAvatar } from './dAvatar.js';
import { pPing } from './pPing.js';
import { gPing } from './gPing.js';
const env = process.env.NODE_ENV || 'development';
@ -72,6 +74,12 @@ function prepareDB() {
CHECK(date >= 0)
) STRICT
`);
database.exec(`
CREATE TABLE IF NOT EXISTS activityTime (
username TEXT PRIMARY KEY,
time TEXT
) STRICT
`);
}
prepareDB();
@ -85,6 +93,11 @@ const incLossesPong = database.prepare('UPDATE userData SET pongLosses = pongLos
const incWinsTetris = database.prepare('UPDATE userData SET tetrisWins = tetrisWins + 1 WHERE username = ?;');
const incLossesTetris = database.prepare('UPDATE userData SET tetrisLosses = tetrisLosses + 1 WHERE username = ?');
const setAvatarId = database.prepare('UPDATE userData SET avatarId = ? WHERE username = ?;');
const setActivityTime = database.prepare(`
INSERT INTO activityTime (username, time)
VALUES (?, ?)
ON CONFLICT(username) DO UPDATE SET time = excluded.time;
`);
// PATCH
const changeDisplayName = database.prepare('UPDATE userData SET displayName = ? WHERE username = ?;');
@ -100,6 +113,7 @@ const getNumberUsers = database.prepare('SELECT COUNT (DISTINCT username) AS n_u
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 game = ? AND ? IN (player1, player2);');
const getAvatarId = database.prepare('SELECT avatarId FROM userData WHERE username = ?;');
const getActivityTime = database.prepare('SELECT time FROM activityTime WHERE username = ?;')
// DELETE
const deleteUser = database.prepare('DELETE FROM userData WHERE username = ?;');
@ -170,6 +184,9 @@ export default async function(fastify, options) {
fastify.get('/users/:userId/avatar', { preHandler: [fastify.authenticate] }, async (request, reply) => {
return gAvatar(request, reply, fastify, getAvatarId);
});
fastify.get('/ping/:userId', { preHandler: [fastify.authenticate] }, async (request, reply) => {
return gPing(request, reply, fastify, getActivityTime);
});
// POST
fastify.post('/users/:userId', { preHandler: [fastify.authenticateAdmin] }, async (request, reply) => {
@ -184,6 +201,9 @@ export default async function(fastify, options) {
fastify.post('/users/:userId/avatar', { preHandler: [fastify.authenticate] }, async (request, reply) => {
return pAvatar(request, reply, fastify, setAvatarId);
});
fastify.post('/ping', { preHandler: [fastify.authenticate] }, async (request, reply) => {
return pPing(request, reply, fastify, setActivityTime);
})
// PATCH
fastify.patch('/users/:userId/:member', { preHandler: [fastify.authenticate] }, async (request, reply) => {

28
src/api/user/gPing.js Normal file
View File

@ -0,0 +1,28 @@
/**
* @param {import('fastify').FastifyRequest} request
* @param {import('fastify').FastifyReply} reply
* @param {import('fastify').FastifyInstance} fastify
*/
export async function gPing(request, reply, fastify, getActivityTime) {
try {
const user = request.params.userId;
const time = getActivityTime.get(user);
if (!time || !time.time) {
return reply.code(404).send({ error: "User not found or no activity time recorded" });
}
const lastSeenTime = new Date(time.time);
const now = new Date();
const oneMinuteAgo = new Date(now.getTime() - 60000); // 60,000 ms = 1 minute
const isActiveInLastMinute = lastSeenTime >= oneMinuteAgo;
return reply.code(200).send({
isLogged: isActiveInLastMinute
});
} catch (err) {
fastify.log.error(err);
return reply.code(500).send({ error: "Internal server error" });
}
}

21
src/api/user/pPing.js Normal file
View File

@ -0,0 +1,21 @@
/**
* @param {import('fastify').FastifyRequest} request
* @param {import('fastify').FastifyReply} request
* @param {import('fastify').Fastify} fastify
*/
export async function pPing(request, reply, fastify, setActivityTime) {
try {
const user = request.user;
const currentTime = new Date().toISOString();
setActivityTime.run(user, currentTime);
return reply.code(200)
.send({
msg: "last seen time updated successfully"
});
} catch (err) {
fastify.log.error(err);
return reply.code(500).send({ error: "Internal server error" });
}
}