mirror of
https://github.com/KeyZox71/knl_meowscendence.git
synced 2025-12-31 21:56:41 +01:00
unfinished image API
This commit is contained in:
@ -1,5 +1,5 @@
|
||||
import authDB from '../../utils/authDB';
|
||||
import { authUserRemove } from '../../utils/authUserRemove';
|
||||
import authDB from '../../utils/authDB.js';
|
||||
import { authUserRemove } from '../../utils/authUserRemove.js';
|
||||
|
||||
/**
|
||||
* @param {import('fastify').FastifyRequest} request
|
||||
|
||||
10
src/api/images/dImage.js
Normal file
10
src/api/images/dImage.js
Normal file
@ -0,0 +1,10 @@
|
||||
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" });
|
||||
}
|
||||
}
|
||||
86
src/api/images/default.js
Normal file
86
src/api/images/default.js
Normal file
@ -0,0 +1,86 @@
|
||||
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);
|
||||
});
|
||||
}
|
||||
13
src/api/images/gImage.js
Normal file
13
src/api/images/gImage.js
Normal file
@ -0,0 +1,13 @@
|
||||
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" });
|
||||
}
|
||||
}
|
||||
33
src/api/images/pImage.js
Normal file
33
src/api/images/pImage.js
Normal file
@ -0,0 +1,33 @@
|
||||
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,8 +0,0 @@
|
||||
Todo :
|
||||
- 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. (what are the RGPD requirements ?) ?
|
||||
|
||||
|
||||
|
||||
Always update API doc
|
||||
9
src/api/user/dAvatar.js
Normal file
9
src/api/user/dAvatar.js
Normal file
@ -0,0 +1,9 @@
|
||||
export async function dAvatar(request, reply, fastify, deleteAvatarId) {
|
||||
try {
|
||||
;
|
||||
return reply.code(200).send({ msg: "Avatar deleted successfully" });
|
||||
} catch (err) {
|
||||
fastify.log.error(err);
|
||||
return reply.code(500).send({ error: "Internal server error" });
|
||||
}
|
||||
}
|
||||
@ -2,22 +2,25 @@ import fastifyJWT from '@fastify/jwt';
|
||||
import fastifyCookie from '@fastify/cookie';
|
||||
import Database from 'better-sqlite3';
|
||||
|
||||
import { gUsers } from './gUsers.js'
|
||||
import { gUser } from './gUser.js'
|
||||
import { gNumberUsers } from './gNumberUsers.js'
|
||||
import { gFriends } from './gFriends.js'
|
||||
import { gNumberFriends } from './gNumberFriends.js'
|
||||
import { gMatchHistory } from './gMatchHistory.js'
|
||||
import { gNumberMatches } from './gNumberMatches.js'
|
||||
import { pUser } from './pUser.js'
|
||||
import { pFriend } from './pFriend.js'
|
||||
import { pMatchHistory } from './pMatchHistory.js'
|
||||
import { uMember } from './uMember.js'
|
||||
import { dUser } from './dUser.js'
|
||||
import { dMember } from './dMember.js'
|
||||
import { dFriends } from './dFriends.js'
|
||||
import { dFriend } from './dFriend.js'
|
||||
import { dMatchHistory } from './dMatchHistory.js'
|
||||
import { gUsers } from './gUsers.js';
|
||||
import { gUser } from './gUser.js';
|
||||
import { gNumberUsers } from './gNumberUsers.js';
|
||||
import { gFriends } from './gFriends.js';
|
||||
import { gNumberFriends } from './gNumberFriends.js';
|
||||
import { gMatchHistory } from './gMatchHistory.js';
|
||||
import { gNumberMatches } from './gNumberMatches.js';
|
||||
import { pUser } from './pUser.js';
|
||||
import { pFriend } from './pFriend.js';
|
||||
import { pMatchHistory } from './pMatchHistory.js';
|
||||
import { uMember } from './uMember.js';
|
||||
import { dUser } from './dUser.js';
|
||||
import { dMember } from './dMember.js';
|
||||
import { dFriends } from './dFriends.js';
|
||||
import { dFriend } from './dFriend.js';
|
||||
import { dMatchHistory } from './dMatchHistory.js';
|
||||
import { pAvatar } from './pAvatar.js';
|
||||
import { gAvatar } from './gAvatar.js';
|
||||
import { dAvatar } from './dAvatar.js';
|
||||
|
||||
const env = process.env.NODE_ENV || 'development';
|
||||
|
||||
@ -35,6 +38,7 @@ function prepareDB() {
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT,
|
||||
displayName TEXT,
|
||||
avatarId INTEGER,
|
||||
pongWins INTEGER,
|
||||
pongLosses INTEGER,
|
||||
tetrisWins INTEGER,
|
||||
@ -73,16 +77,18 @@ function prepareDB() {
|
||||
prepareDB();
|
||||
|
||||
// POST
|
||||
const createUser = database.prepare('INSERT INTO userData (username, displayName, pongWins, pongLosses, tetrisWins, tetrisLosses) VALUES (?, ?, 0, 0, 0, 0);');
|
||||
const createUser = database.prepare('INSERT INTO userData (username, displayName, avatarId, pongWins, pongLosses, tetrisWins, tetrisLosses) VALUES (?, ?, -1, 0, 0, 0, 0);');
|
||||
const addFriend = database.prepare('INSERT INTO friends (username, friendName) VALUES (?, ?);');
|
||||
const addMatch = database.prepare('INSERT INTO matchHistory (game, date, player1, player2, matchId) VALUES (?, ?, ?, ?, ?);');
|
||||
const incWinsPong = database.prepare('UPDATE userData SET pongWins = pongWins + 1 WHERE username = ?;');
|
||||
const incLossesPong = database.prepare('UPDATE userData SET pongLosses = pongLosses + 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 setAvatarId = database.prepare('UPDATE userData SET avatarId = ? WHERE username = ?;');
|
||||
|
||||
// PATCH
|
||||
const changeDisplayName = database.prepare('UPDATE userData SET displayName = ? WHERE username = ?;');
|
||||
const changeAvatarId = database.prepare('UPDATE userData SET avatarId = ? WHERE username = ?;');
|
||||
|
||||
// GET
|
||||
const getUserData = database.prepare('SELECT username, displayName, pongWins, pongLosses, tetrisWins, tetrisLosses FROM userData LIMIT ? OFFSET ?;');
|
||||
@ -92,7 +98,8 @@ const getFriend = database.prepare('SELECT friendName FROM friends WHERE usernam
|
||||
const getMatchHistory = database.prepare('SELECT matchId, date FROM matchHistory WHERE game = ? AND ? IN (player1, player2) 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 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 = ?;');
|
||||
|
||||
// DELETE
|
||||
const deleteUser = database.prepare('DELETE FROM userData WHERE username = ?;');
|
||||
@ -101,6 +108,7 @@ const deleteFriends = database.prepare('DELETE FROM friends WHERE username = ?;'
|
||||
const deleteMatchHistory = database.prepare('DELETE FROM matchHistory WHERE game = ? AND ? IN (player1, player2);');
|
||||
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 deleteAvatarId = database.prepare('UPDATE userData SET avatarId = -1 WHERE username = ?;');
|
||||
|
||||
const querySchema = { type: 'object', required: ['iStart', 'iEnd'], properties: { iStart: { type: 'integer', minimum: 0 }, iEnd: { type: 'integer', minimum: 0 } } }
|
||||
const bodySchema = { type: 'object', required: ['opponent', 'myScore', 'opponentScore'], properties: { opponent: { type: 'string' }, myScore: { type: 'integer', minimum: 0 }, opponentScore: { type: 'integer', minimum: 0 } } }
|
||||
@ -159,6 +167,9 @@ export default async function(fastify, options) {
|
||||
fastify.get('/users/:userId/matchHistory/count', { preHandler: [fastify.authenticate], schema: { query: querySchemaMatchHistoryGame } }, async (request, reply) => {
|
||||
return gNumberMatches(request, reply, fastify, getUserInfo, getNumberMatches);
|
||||
});
|
||||
fastify.get('/users/:userId/avatar', { preHandler: [fastify.authenticate] }, async (request, reply) => {
|
||||
return gAvatar(request, reply, fastify, getAvatarId);
|
||||
});
|
||||
|
||||
// POST
|
||||
fastify.post('/users/:userId', { preHandler: [fastify.authenticateAdmin] }, async (request, reply) => {
|
||||
@ -170,10 +181,13 @@ export default async function(fastify, options) {
|
||||
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);
|
||||
});
|
||||
fastify.post('/users/:userId/avatar', { preHandler: [fastify.authenticate] }, async (request, reply) => {
|
||||
return pAvatar(request, reply, fastify, setAvatarId);
|
||||
});
|
||||
|
||||
// PATCH
|
||||
fastify.patch('/users/:userId/:member', { preHandler: [fastify.authenticate] }, async (request, reply) => {
|
||||
return uMember(request, reply, fastify, getUserInfo, changeDisplayName);
|
||||
return uMember(request, reply, fastify, getUserInfo, changeDisplayName, changeAvatarId);
|
||||
});
|
||||
|
||||
// DELETE
|
||||
@ -192,4 +206,7 @@ export default async function(fastify, options) {
|
||||
fastify.delete('/users/:userId/matchHistory', { preHandler: [fastify.authenticate], schema: { query: querySchemaMatchHistoryGame } }, async (request, reply) => {
|
||||
return dMatchHistory(request, reply, fastify, getUserInfo, deleteMatchHistory, deleteStatsPong, deleteStatsTetris);
|
||||
});
|
||||
fastify.delete('/users/:userId/avatar', { preHandler: [fastify.authenticate] }, async (request, reply) => {
|
||||
return dAvatar(request, reply, fastify, deleteAvatarId);
|
||||
});
|
||||
}
|
||||
|
||||
9
src/api/user/gAvatar.js
Normal file
9
src/api/user/gAvatar.js
Normal file
@ -0,0 +1,9 @@
|
||||
export async function gAvatar(request, reply, fastify, getAvatarId) {
|
||||
try {
|
||||
;
|
||||
return reply.code(200).send({ });
|
||||
} catch (err) {
|
||||
fastify.log.error(err);
|
||||
return reply.code(500).send({ error: "Internal server error" });
|
||||
}
|
||||
}
|
||||
13
src/api/user/pAvatar.js
Normal file
13
src/api/user/pAvatar.js
Normal file
@ -0,0 +1,13 @@
|
||||
export async function pAvatar(request, reply, fastify, setAvatarId) {
|
||||
try {
|
||||
/* const res = await fetch('http://localhost:3004/images', { method: "POST", headers: { } });
|
||||
if (!res.ok) {
|
||||
return reply.code(500).send({ error: "Internal server error" });
|
||||
}
|
||||
const data = await res.json();*/
|
||||
return reply.code(200).send({ msg: "Avatar uploaded successfully" });
|
||||
} catch (err) {
|
||||
fastify.log.error(err);
|
||||
return reply.code(500).send({ error: "Internal server error" });
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
export async function uMember(request, reply, fastify, getUserInfo, changeDisplayName) {
|
||||
export async function uMember(request, reply, fastify, getUserInfo, changeDisplayName, changeAvatarId) {
|
||||
try {
|
||||
const userId = request.params.userId;
|
||||
if (!request.user) {
|
||||
|
||||
Reference in New Issue
Block a user