mirror of
https://github.com/KeyZox71/knl_meowscendence.git
synced 2025-12-31 21:56:41 +01:00
merged image API in user API
This commit is contained in:
4
Justfile
4
Justfile
@ -11,10 +11,6 @@ set dotenv-load
|
|||||||
@user $FASTIFY_LOG_LEVEL="info" $FASTIFY_PRETTY_LOGS="true":
|
@user $FASTIFY_LOG_LEVEL="info" $FASTIFY_PRETTY_LOGS="true":
|
||||||
fastify start src/api/user/default.js
|
fastify start src/api/user/default.js
|
||||||
|
|
||||||
# For launching the images api
|
|
||||||
@images $FASTIFY_LOG_LEVEL="info" $FASTIFY_PRETTY_LOGS="true":
|
|
||||||
fastify start src/api/images/default.js
|
|
||||||
|
|
||||||
@scoreStore $FASTIFY_LOG_LEVEL="info" $FASTIFY_PRETTY_LOGS="true":
|
@scoreStore $FASTIFY_LOG_LEVEL="info" $FASTIFY_PRETTY_LOGS="true":
|
||||||
fastify start src/api/scoreStore/default.js
|
fastify start src/api/scoreStore/default.js
|
||||||
|
|
||||||
|
|||||||
120
doc/user/avatar.md
Normal file
120
doc/user/avatar.md
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
# Avatar
|
||||||
|
|
||||||
|
Available endpoints:
|
||||||
|
- POST `/users/:userId/avatar`
|
||||||
|
- GET `/users/:userId/avatar`
|
||||||
|
- PATCH `/users/:userId/avatar`
|
||||||
|
- DELETE `/users/:userId/avatar`
|
||||||
|
|
||||||
|
Common return:
|
||||||
|
- 500 with response
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"error": "Internal server error"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## POST /users/:userId/avatar
|
||||||
|
|
||||||
|
Used to upload an avatar
|
||||||
|
|
||||||
|
Input needed :
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
<FormData object containing the file>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Can return:
|
||||||
|
- 200 with response
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"msg": "Avatar uploaded successfully"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- 400 with response (if the file is too large, or file is missing, or it is missing a file name, or it is missing a mime type)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"error": "<corresponding error>"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- 404 with response (if the user does not exist)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"error": "<corresponding error>"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## GET /users/:userId/avatar
|
||||||
|
|
||||||
|
Used to download an avatar
|
||||||
|
|
||||||
|
Input needed :
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
<FormData object containing the file>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Can return:
|
||||||
|
- 200 with response
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"msg": "Avatar uploaded successfully"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- 404 with response (if the user does not exist, or the user does not have an assigned avatar, or the image does not exist)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"error": "<corresponding error>"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## PATCH /users/:userId/avatar
|
||||||
|
|
||||||
|
Used to modify an avatar
|
||||||
|
|
||||||
|
Input needed :
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
<FormData object containing the file>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Can return:
|
||||||
|
- 200 with response
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"msg": "Avatar modified successfully"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- 400 with response (if the file is too large, or file is missing, or it is missing a file name, or it is missing a mime type)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"error": "<corresponding error>"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- 404 with response (if the user does not exist)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"error": "<corresponding error>"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## DELETE /users/:userId/avatar
|
||||||
|
|
||||||
|
Used to delete an avatar
|
||||||
|
|
||||||
|
Can return:
|
||||||
|
- 200 with response
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"msg": "Avatar deleted successfully"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- 404 with response (if the user does not exist, or the user does not have an assigned avatar)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"error": "<corresponding error>"
|
||||||
|
}
|
||||||
|
```
|
||||||
@ -1,10 +0,0 @@
|
|||||||
export async function dImage(request, reply, fastify, deleteImage) {
|
|
||||||
try {
|
|
||||||
const imageId = Number(request.params.imageId);
|
|
||||||
deleteImage.run(imageId);
|
|
||||||
return reply.code(200).send({ msg: "Image deleted successfully" });
|
|
||||||
} catch (err) {
|
|
||||||
fastify.log.error(err);
|
|
||||||
return reply.code(500).send({ error: "Internal server error" });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,86 +0,0 @@
|
|||||||
import fastifyJWT from '@fastify/jwt';
|
|
||||||
import fastifyCookie from '@fastify/cookie';
|
|
||||||
import Database from 'better-sqlite3';
|
|
||||||
import multipart from '@fastify/multipart';
|
|
||||||
|
|
||||||
import { gImage } from './gImage.js';
|
|
||||||
import { pImage } from './pImage.js';
|
|
||||||
import { dImage } from './dImage.js';
|
|
||||||
|
|
||||||
const env = process.env.NODE_ENV || 'development';
|
|
||||||
|
|
||||||
let database;
|
|
||||||
if (!env || env === 'development') {
|
|
||||||
database = new Database(':memory:', { verbose: console.log });
|
|
||||||
} else {
|
|
||||||
const dbPath = process.env.DB_PATH || '/db/db.sqlite'
|
|
||||||
database = new Database(dbPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
function prepareDB() {
|
|
||||||
database.exec(`
|
|
||||||
CREATE TABLE IF NOT EXISTS images (
|
|
||||||
imageId INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
fileName TEXT,
|
|
||||||
mimeType TEXT,
|
|
||||||
data BLOB
|
|
||||||
) STRICT
|
|
||||||
`);
|
|
||||||
}
|
|
||||||
|
|
||||||
prepareDB();
|
|
||||||
|
|
||||||
// POST
|
|
||||||
const postImage = database.prepare('INSERT INTO images (fileName, mimeType, data) VALUES (?, ?, ?);');
|
|
||||||
|
|
||||||
// GET
|
|
||||||
const getImage = database.prepare('SELECT fileName, mimeType, data FROM images WHERE imageId = ?;');
|
|
||||||
|
|
||||||
// DELETE
|
|
||||||
const deleteImage = database.prepare('DELETE FROM images WHERE imageId = ?;');
|
|
||||||
|
|
||||||
export default async function(fastify, options) {
|
|
||||||
fastify.register(fastifyJWT, {
|
|
||||||
secret: process.env.JWT_SECRET || '123456789101112131415161718192021',
|
|
||||||
cookie: {
|
|
||||||
cookieName: 'token',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
fastify.register(fastifyCookie);
|
|
||||||
fastify.register(multipart, { limits: { fileSize: 2 * 1024 * 1024 } });
|
|
||||||
|
|
||||||
fastify.decorate('authenticate', async function(request, reply) {
|
|
||||||
try {
|
|
||||||
const jwt = await request.jwtVerify();
|
|
||||||
request.user = jwt.user;
|
|
||||||
} catch (err) {
|
|
||||||
reply.code(401).send({ error: 'Unauthorized' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
fastify.decorate('authenticateAdmin', async function(request, reply) {
|
|
||||||
try {
|
|
||||||
const jwt = await request.jwtVerify();
|
|
||||||
if (jwt.user !== 'admin') {
|
|
||||||
throw ('You lack administrator privileges');
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
reply.code(401).send({ error: 'Unauthorized' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// GET
|
|
||||||
fastify.get('/images/:imageId', { preHandler: [fastify.authenticate] }, async (request, reply) => {
|
|
||||||
return gImage(request, reply, fastify, getImage);
|
|
||||||
});
|
|
||||||
|
|
||||||
// POST
|
|
||||||
fastify.post('/images', { preHandler: [fastify.authenticate] }, async (request, reply) => {
|
|
||||||
return pImage(request, reply, fastify, postImage);
|
|
||||||
});
|
|
||||||
|
|
||||||
// DELETE
|
|
||||||
fastify.delete('/images/:imageId', { preHandler: [fastify.authenticate] }, async (request, reply) => {
|
|
||||||
return dImage(request, reply, fastify, deleteImage);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
export async function gImage(request, reply, fastify, getImage) {
|
|
||||||
try {
|
|
||||||
const imageId = Number(request.params.imageId);
|
|
||||||
const image = getImage.get(imageId);
|
|
||||||
if (!image) {
|
|
||||||
return reply.code(404).send({ error: "Image does not exist" });
|
|
||||||
}
|
|
||||||
return reply.code(200).type(image.mimeType).header('Content-Disposition', `inline; filename="${image.fileName}"`).send(image.data);
|
|
||||||
} catch (err) {
|
|
||||||
fastify.log.error(err);
|
|
||||||
return reply.code(500).send({ error: "Internal server error" });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
export async function pImage(request, reply, fastify, postImage) {
|
|
||||||
try {
|
|
||||||
const parts = request.parts();
|
|
||||||
for await (const part of parts) {
|
|
||||||
if (part.file) {
|
|
||||||
const chunks = [];
|
|
||||||
for await (const chunk of part.file) {
|
|
||||||
chunks.push(chunk);
|
|
||||||
}
|
|
||||||
const buffer = Buffer.concat(chunks);
|
|
||||||
if (!part.filename || part.filename.trim() === '') {
|
|
||||||
return reply.code(400).send({ error: "Missing filename" });
|
|
||||||
}
|
|
||||||
if (!part.mimetype || part.mimetype.trim() === '') {
|
|
||||||
return reply.code(400).send({ error: "Missing mimetype" });
|
|
||||||
}
|
|
||||||
const ext = part.filename.toLowerCase().substring(part.filename.lastIndexOf('.'));
|
|
||||||
if (ext !== 'webp') {
|
|
||||||
return reply.code(400).send({ error: "Wrong file extension" });
|
|
||||||
}
|
|
||||||
// check size max here ?
|
|
||||||
// convert image to webp using sharp
|
|
||||||
//sharp(buffer, ).toFile();
|
|
||||||
const id = postImage.run(part.filename, part.mimetype, buffer);
|
|
||||||
return reply.code(200).send({ msg: "Image uploaded successfully", imageId: id.lastInsertRowid });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return reply.code(400).send({ error: "No file uploaded" });
|
|
||||||
} catch (err) {
|
|
||||||
fastify.log.error(err);
|
|
||||||
return reply.code(500).send({ error: "Internal server error" });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,9 +1,14 @@
|
|||||||
export async function dAvatar(request, reply, fastify, getUserInfo, deleteAvatarId) {
|
export async function dAvatar(request, reply, fastify, getUserInfo, getAvatarId, deleteAvatarId, deleteImage) {
|
||||||
try {
|
try {
|
||||||
const userId = request.params.userId;
|
const userId = request.params.userId;
|
||||||
if (!getUserInfo.get(userId)) {
|
if (!getUserInfo.get(userId)) {
|
||||||
return reply.cose(404).send({ error: "User does not exist" });
|
return reply.cose(404).send({ error: "User does not exist" });
|
||||||
}
|
}
|
||||||
|
const imageId = getAvatarId.get(userId);
|
||||||
|
if (imageId.avatarId === -1) {
|
||||||
|
return reply.code(404).send({ error: "User does not have an avatar" });
|
||||||
|
}
|
||||||
|
deleteImage.run(imageId.avatarId);
|
||||||
deleteAvatarId.run(userId);
|
deleteAvatarId.run(userId);
|
||||||
return reply.code(200).send({ msg: "Avatar deleted successfully" });
|
return reply.code(200).send({ msg: "Avatar deleted successfully" });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import fastifyJWT from '@fastify/jwt';
|
import fastifyJWT from '@fastify/jwt';
|
||||||
import fastifyCookie from '@fastify/cookie';
|
import fastifyCookie from '@fastify/cookie';
|
||||||
import Database from 'better-sqlite3';
|
import Database from 'better-sqlite3';
|
||||||
|
import multipart from '@fastify/multipart';
|
||||||
|
|
||||||
import { gUsers } from './gUsers.js';
|
import { gUsers } from './gUsers.js';
|
||||||
import { gUser } from './gUser.js';
|
import { gUser } from './gUser.js';
|
||||||
@ -20,6 +21,7 @@ import { dFriend } from './dFriend.js';
|
|||||||
import { dMatchHistory } from './dMatchHistory.js';
|
import { dMatchHistory } from './dMatchHistory.js';
|
||||||
import { pAvatar } from './pAvatar.js';
|
import { pAvatar } from './pAvatar.js';
|
||||||
import { gAvatar } from './gAvatar.js';
|
import { gAvatar } from './gAvatar.js';
|
||||||
|
import { uAvatar } from './uAvatar.js';
|
||||||
import { dAvatar } from './dAvatar.js';
|
import { dAvatar } from './dAvatar.js';
|
||||||
import { pPing } from './pPing.js';
|
import { pPing } from './pPing.js';
|
||||||
import { gPing } from './gPing.js';
|
import { gPing } from './gPing.js';
|
||||||
@ -80,6 +82,14 @@ function prepareDB() {
|
|||||||
time TEXT
|
time TEXT
|
||||||
) STRICT
|
) STRICT
|
||||||
`);
|
`);
|
||||||
|
database.exec(`
|
||||||
|
CREATE TABLE IF NOT EXISTS images (
|
||||||
|
imageId INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
fileName TEXT,
|
||||||
|
mimeType TEXT,
|
||||||
|
data BLOB
|
||||||
|
) STRICT
|
||||||
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareDB();
|
prepareDB();
|
||||||
@ -93,6 +103,7 @@ const incLossesPong = database.prepare('UPDATE userData SET pongLosses = pongLos
|
|||||||
const incWinsTetris = database.prepare('UPDATE userData SET tetrisWins = tetrisWins + 1 WHERE username = ?;');
|
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 incLossesTetris = database.prepare('UPDATE userData SET tetrisLosses = tetrisLosses + 1 WHERE username = ?');
|
||||||
const setAvatarId = database.prepare('UPDATE userData SET avatarId = ? WHERE username = ?;');
|
const setAvatarId = database.prepare('UPDATE userData SET avatarId = ? WHERE username = ?;');
|
||||||
|
const postImage = database.prepare('INSERT INTO images (fileName, mimeType, data) VALUES (?, ?, ?);');
|
||||||
const setActivityTime = database.prepare(`
|
const setActivityTime = database.prepare(`
|
||||||
INSERT INTO activityTime (username, time)
|
INSERT INTO activityTime (username, time)
|
||||||
VALUES (?, ?)
|
VALUES (?, ?)
|
||||||
@ -113,6 +124,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 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 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 getAvatarId = database.prepare('SELECT avatarId FROM userData WHERE username = ?;');
|
||||||
|
const getImage = database.prepare('SELECT fileName, mimeType, data FROM images WHERE imageId = ?;');
|
||||||
const getActivityTime = database.prepare('SELECT time FROM activityTime WHERE username = ?;')
|
const getActivityTime = database.prepare('SELECT time FROM activityTime WHERE username = ?;')
|
||||||
|
|
||||||
// DELETE
|
// DELETE
|
||||||
@ -123,6 +135,7 @@ const deleteMatchHistory = database.prepare('DELETE FROM matchHistory WHERE game
|
|||||||
const deleteStatsPong = database.prepare('UPDATE userData SET pongWins = 0, pongLosses = 0 WHERE username = ?;');
|
const deleteStatsPong = database.prepare('UPDATE userData SET pongWins = 0, pongLosses = 0 WHERE username = ?;');
|
||||||
const deleteStatsTetris = database.prepare('UPDATE userData SET tetrisWins = 0, tetrisLosses = 0 WHERE username = ?;');
|
const deleteStatsTetris = database.prepare('UPDATE userData SET tetrisWins = 0, tetrisLosses = 0 WHERE username = ?;');
|
||||||
const deleteAvatarId = database.prepare('UPDATE userData SET avatarId = -1 WHERE username = ?;');
|
const deleteAvatarId = database.prepare('UPDATE userData SET avatarId = -1 WHERE username = ?;');
|
||||||
|
const deleteImage = database.prepare('DELETE FROM images WHERE imageId = ?;');
|
||||||
|
|
||||||
const querySchema = { type: 'object', required: ['iStart', 'iEnd'], properties: { iStart: { type: 'integer', minimum: 0 }, iEnd: { type: 'integer', minimum: 0 } } }
|
const querySchema = { type: 'object', required: ['iStart', 'iEnd'], properties: { iStart: { type: 'integer', minimum: 0 }, iEnd: { type: 'integer', minimum: 0 } } }
|
||||||
const querySchemaMatchHistory = { type: 'object', required: ['game', 'iStart', 'iEnd'], properties: { game: { type: 'string' }, iStart: { type: 'integer', minimum: 0 }, iEnd: { type: 'integer', minimum: 0 } } }
|
const querySchemaMatchHistory = { type: 'object', required: ['game', 'iStart', 'iEnd'], properties: { game: { type: 'string' }, iStart: { type: 'integer', minimum: 0 }, iEnd: { type: 'integer', minimum: 0 } } }
|
||||||
@ -137,6 +150,7 @@ export default async function(fastify, options) {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
fastify.register(fastifyCookie);
|
fastify.register(fastifyCookie);
|
||||||
|
fastify.register(multipart, { limits: { fileSize: 2 * 1024 * 1024 + 1 } });
|
||||||
|
|
||||||
fastify.decorate('authenticate', async function(request, reply) {
|
fastify.decorate('authenticate', async function(request, reply) {
|
||||||
try {
|
try {
|
||||||
@ -181,7 +195,7 @@ export default async function(fastify, options) {
|
|||||||
return gNumberMatches(request, reply, fastify, getUserInfo, getNumberMatches);
|
return gNumberMatches(request, reply, fastify, getUserInfo, getNumberMatches);
|
||||||
});
|
});
|
||||||
fastify.get('/users/:userId/avatar', { preHandler: [fastify.authenticate] }, async (request, reply) => {
|
fastify.get('/users/:userId/avatar', { preHandler: [fastify.authenticate] }, async (request, reply) => {
|
||||||
return gAvatar(request, reply, fastify, getUserInfo, getAvatarId);
|
return gAvatar(request, reply, fastify, getUserInfo, getAvatarId, getImage);
|
||||||
});
|
});
|
||||||
fastify.get('/ping/:userId', { preHandler: [fastify.authenticate] }, async (request, reply) => {
|
fastify.get('/ping/:userId', { preHandler: [fastify.authenticate] }, async (request, reply) => {
|
||||||
return gPing(request, reply, fastify, getActivityTime);
|
return gPing(request, reply, fastify, getActivityTime);
|
||||||
@ -197,14 +211,17 @@ export default async function(fastify, options) {
|
|||||||
fastify.post('/users/:userId/matchHistory', { preHandler: [fastify.authenticate], schema: { body: bodySchemaMatchHistory } }, async (request, reply) => {
|
fastify.post('/users/:userId/matchHistory', { preHandler: [fastify.authenticate], schema: { body: bodySchemaMatchHistory } }, async (request, reply) => {
|
||||||
return pMatchHistory(request, reply, fastify, getUserInfo, addMatch, incWinsPong, incLossesPong, incWinsTetris, incLossesTetris);
|
return pMatchHistory(request, reply, fastify, getUserInfo, addMatch, incWinsPong, incLossesPong, incWinsTetris, incLossesTetris);
|
||||||
});
|
});
|
||||||
fastify.post('/users/:userId/avatar', { preHandler: [fastify.authenticate] }, async (request, reply) => {
|
fastify.post('/users/:userId/avatar',/* { preHandler: [fastify.authenticate] },*/ async (request, reply) => {
|
||||||
return pAvatar(request, reply, fastify, getUserInfo, setAvatarId);
|
return pAvatar(request, reply, fastify, getUserInfo, setAvatarId, postImage);
|
||||||
});
|
});
|
||||||
fastify.post('/ping', { preHandler: [fastify.authenticate] }, async (request, reply) => {
|
fastify.post('/ping', { preHandler: [fastify.authenticate] }, async (request, reply) => {
|
||||||
return pPing(request, reply, fastify, setActivityTime);
|
return pPing(request, reply, fastify, setActivityTime);
|
||||||
})
|
})
|
||||||
|
|
||||||
// PATCH
|
// PATCH
|
||||||
|
fastify.patch('/users/:userId/avatar', { preHandler: [fastify.authenticate] }, async (request, reply) => {
|
||||||
|
return uAvatar(request, reply, fastify, getUserInfo, setAvatarId, getAvatarId, deleteAvatarId, postImage, deleteImage);
|
||||||
|
});
|
||||||
fastify.patch('/users/:userId/:member', { preHandler: [fastify.authenticate] }, async (request, reply) => {
|
fastify.patch('/users/:userId/:member', { preHandler: [fastify.authenticate] }, async (request, reply) => {
|
||||||
return uMember(request, reply, fastify, getUserInfo, changeDisplayName, changeAvatarId);
|
return uMember(request, reply, fastify, getUserInfo, changeDisplayName, changeAvatarId);
|
||||||
});
|
});
|
||||||
@ -226,6 +243,6 @@ export default async function(fastify, options) {
|
|||||||
return dMatchHistory(request, reply, fastify, getUserInfo, deleteMatchHistory, deleteStatsPong, deleteStatsTetris);
|
return dMatchHistory(request, reply, fastify, getUserInfo, deleteMatchHistory, deleteStatsPong, deleteStatsTetris);
|
||||||
});
|
});
|
||||||
fastify.delete('/users/:userId/avatar', { preHandler: [fastify.authenticate] }, async (request, reply) => {
|
fastify.delete('/users/:userId/avatar', { preHandler: [fastify.authenticate] }, async (request, reply) => {
|
||||||
return dAvatar(request, reply, fastify, getUserInfo, deleteAvatarId);
|
return dAvatar(request, reply, fastify, getUserInfo, getAvatarId, deleteAvatarId, deleteImage);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,27 +1,18 @@
|
|||||||
export async function gAvatar(request, reply, fastify, getUserInfo, getAvatarId) {
|
export async function gAvatar(request, reply, fastify, getUserInfo, getAvatarId, getImage) {
|
||||||
try {
|
try {
|
||||||
const userId = request.params.userId;
|
const userId = request.params.userId;
|
||||||
if (!getUserInfo.get(userId)) {
|
if (!getUserInfo.get(userId)) {
|
||||||
return reply.code(404).send({ error: "User does not exist" });
|
return reply.code(404).send({ error: "User does not exist" });
|
||||||
}
|
}
|
||||||
const imageId = 1;//getAvatarId.get(userId);
|
const imageId = getAvatarId.get(userId);
|
||||||
if (imageId === -1) {
|
if (imageId.avatarId === -1) {
|
||||||
;// return random kanel image
|
return reply.code(404).send({ error: "User does not have an avatar" });
|
||||||
}
|
}
|
||||||
const res = await fetch(`http://localhost:3004/images/${imageId}`, { method: "GET" });
|
const image = getImage.get(imageId.avatarId);
|
||||||
if (!res.ok) {
|
if (!image) {
|
||||||
console.log("====================================\nAn error on the image API has occured");
|
return reply.code(404).send({ error: "Avatar does not exist" });
|
||||||
return reply.code(500).send({ error: "Internal server error" });
|
|
||||||
}
|
}
|
||||||
for (const [key, value] of res.headers) {
|
return reply.code(200).type(image.mimeType).send(image.data);
|
||||||
reply.header(key, value);
|
|
||||||
}
|
|
||||||
if (res.body) {
|
|
||||||
reply.code(res.statusCode).send(res.body);
|
|
||||||
} else {
|
|
||||||
reply.code(res.statusCode).send();
|
|
||||||
}
|
|
||||||
//return reply.code(200).type(res.header).send(res.body);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
fastify.log.error(err);
|
fastify.log.error(err);
|
||||||
return reply.code(500).send({ error: "Internal server error" });
|
return reply.code(500).send({ error: "Internal server error" });
|
||||||
|
|||||||
@ -1,17 +1,37 @@
|
|||||||
export async function pAvatar(request, reply, fastify, getUserInfo, setAvatarId) {
|
import sharp from 'sharp';
|
||||||
|
|
||||||
|
export async function pAvatar(request, reply, fastify, getUserInfo, setAvatarId, postImage) {
|
||||||
try {
|
try {
|
||||||
const userId = request.params.userId;
|
const userId = request.params.userId;
|
||||||
if (!getUserInfo.get(userId)) {
|
if (!getUserInfo.get(userId)) {
|
||||||
return reply.cose(404).send({ error: "User does not exist" });
|
return reply.cose(404).send({ error: "User does not exist" });
|
||||||
}
|
}
|
||||||
console.log("====================================\n", request.headers);//==========
|
const parts = request.parts();
|
||||||
const res = await fetch('http://localhost:3004/images', { method: "POST", headers: { "Content-Type": "image/webp" }, body: request.body ? JSON.stringify(request.body) : undefined });
|
for await (const part of parts) {
|
||||||
if (!res.ok) {
|
if (part.file) {
|
||||||
return reply.code(500).send({ error: "Internal server error" });
|
let size = 0;
|
||||||
|
const chunks = [];
|
||||||
|
for await (const chunk of part.file) {
|
||||||
|
size += chunk.length;
|
||||||
|
chunks.push(chunk);
|
||||||
|
}
|
||||||
|
if (size === 2 * 1024 * 1024 + 1) {
|
||||||
|
return reply.code(400).send({ error: "File too large" });
|
||||||
|
}
|
||||||
|
const buffer = Buffer.concat(chunks);
|
||||||
|
if (!part.filename || part.filename.trim() === '') {
|
||||||
|
return reply.code(400).send({ error: "Missing filename" });
|
||||||
|
}
|
||||||
|
if (!part.mimetype || part.mimetype.trim() === '') {
|
||||||
|
return reply.code(400).send({ error: "Missing mimetype" });
|
||||||
|
}
|
||||||
|
const webpBuffer = await sharp(buffer).toFormat('webp').toBuffer();
|
||||||
|
const imageId = postImage.run(part.filename, part.mimetype, webpBuffer);
|
||||||
|
setAvatarId.run(imageId.lastInsertRowid, userId);
|
||||||
|
return reply.code(200).send({ msg: "Avatar uploaded successfully" });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const data = await res.json();
|
return reply.code(400).send({ error: "No avatar uploaded" });
|
||||||
setAvatarId.run(data.imageId, userId);
|
|
||||||
return reply.code(200).send({ msg: "Avatar uploaded successfully" });
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
fastify.log.error(err);
|
fastify.log.error(err);
|
||||||
return reply.code(500).send({ error: "Internal server error" });
|
return reply.code(500).send({ error: "Internal server error" });
|
||||||
|
|||||||
45
src/api/user/uAvatar.js
Normal file
45
src/api/user/uAvatar.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import sharp from 'sharp';
|
||||||
|
|
||||||
|
export async function uAvatar(request, reply, fastify, getUserInfo, setAvatarId, getAvatarId, deleteAvatarId, postImage, deleteImage) {
|
||||||
|
try {
|
||||||
|
const userId = request.params.userId;
|
||||||
|
if (!getUserInfo.get(userId)) {
|
||||||
|
return reply.cose(404).send({ error: "User does not exist" });
|
||||||
|
}
|
||||||
|
deleteAvatarId.run(userId);
|
||||||
|
const parts = request.parts();
|
||||||
|
for await (const part of parts) {
|
||||||
|
if (part.file) {
|
||||||
|
let size = 0;
|
||||||
|
const chunks = [];
|
||||||
|
for await (const chunk of part.file) {
|
||||||
|
size += chunk.length;
|
||||||
|
chunks.push(chunk);
|
||||||
|
}
|
||||||
|
if (size === 2 * 1024 * 1024 + 1) {
|
||||||
|
return reply.code(400).send({ error: "File too large" });
|
||||||
|
}
|
||||||
|
const buffer = Buffer.concat(chunks);
|
||||||
|
if (!part.filename || part.filename.trim() === '') {
|
||||||
|
return reply.code(400).send({ error: "Missing filename" });
|
||||||
|
}
|
||||||
|
if (!part.mimetype || part.mimetype.trim() === '') {
|
||||||
|
return reply.code(400).send({ error: "Missing mimetype" });
|
||||||
|
}
|
||||||
|
const webpBuffer = await sharp(buffer).toFormat('webp').toBuffer();
|
||||||
|
const imageId = postImage.run(part.filename, part.mimetype, webpBuffer);
|
||||||
|
const oldImageId = getAvatarId.get(userId);
|
||||||
|
if (oldImageId.avatarId !== -1) {
|
||||||
|
deleteImage.run(oldImageId.avatarId);
|
||||||
|
deleteAvatarId.run(userId);
|
||||||
|
}
|
||||||
|
setAvatarId.run(imageId.lastInsertRowid, userId);
|
||||||
|
return reply.code(200).send({ msg: "Avatar modified successfully" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return reply.code(400).send({ error: "No avatar modified" });
|
||||||
|
} catch (err) {
|
||||||
|
fastify.log.error(err);
|
||||||
|
return reply.code(500).send({ error: "Internal server error" });
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/start.js
11
src/start.js
@ -1,7 +1,6 @@
|
|||||||
import Fastify from 'fastify';
|
import Fastify from 'fastify';
|
||||||
import authApi from './api/auth/default.js';
|
import authApi from './api/auth/default.js';
|
||||||
import userApi from './api/user/default.js';
|
import userApi from './api/user/default.js';
|
||||||
import imagesApi from './api/images/default.js';
|
|
||||||
import scoreApi from './api/scoreStore/default.js';
|
import scoreApi from './api/scoreStore/default.js';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
@ -69,16 +68,6 @@ async function start() {
|
|||||||
servers.push(score);
|
servers.push(score);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target === 'images' || target === 'all') {
|
|
||||||
const images = Fastify({ logger: loggerOption('images') });
|
|
||||||
images.register(imagesApi);
|
|
||||||
const port = target === 'all' ? 3004 : 3000;
|
|
||||||
const host = target === 'all' ? '127.0.0.1' : '0.0.0.0';
|
|
||||||
await images.listen({ port, host });
|
|
||||||
console.log(`Images API listening on http://${host}:${port}`);
|
|
||||||
servers.push(images);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Graceful shutdown on SIGINT
|
// Graceful shutdown on SIGINT
|
||||||
process.on('SIGINT', async () => {
|
process.on('SIGINT', async () => {
|
||||||
console.log('SIGINT received, closing servers...');
|
console.log('SIGINT received, closing servers...');
|
||||||
|
|||||||
Reference in New Issue
Block a user