From 203b47bd125c08bac618269ed57408bb8caa6c5a Mon Sep 17 00:00:00 2001 From: Bastien OLLIER Date: Mon, 3 Jun 2024 15:03:31 +0200 Subject: [PATCH 1/9] Implement private user rooms (#4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: bastien ollier Reviewed-on: https://codefirst.iut.uca.fr/git/sandkasten/labyrinth/pulls/4 Reviewed-by: Clément FRÉVILLE Co-authored-by: Bastien OLLIER Co-committed-by: Bastien OLLIER --- package.json | 1 + src/database.ts | 33 ++++++----- src/runner.ts | 4 +- src/server.ts | 148 ++++++++++++++++++++++++++++++++---------------- 4 files changed, 119 insertions(+), 67 deletions(-) diff --git a/package.json b/package.json index 44ed1b8..d2e192b 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@fastify/type-provider-typebox": "^4.0.0", "@fastify/websocket": "^10.0.1", "@sinclair/typebox": "^0.32.9", + "dprint": "^0.46.1", "fastify": "^4.27.0", "nanoid": "^5.0.4", "sqlite3": "^5.1.7", diff --git a/src/database.ts b/src/database.ts index c8b3d48..3aa61be 100644 --- a/src/database.ts +++ b/src/database.ts @@ -10,7 +10,7 @@ const dbFilePath = `${dbDirectory}/database.db`; export function runDB( db: sqlite3.Database, query: string, - params: any[] + params: any[], ): Promise { return new Promise((resolve, reject) => { db.run(query, params, (err) => { @@ -26,7 +26,7 @@ export function runDB( /* Fonction pour récupérer plusieurs lignes de la base de données */ export function allDB( db: sqlite3.Database, - query: string + query: string, ): Promise { return new Promise((resolve, reject) => { db.all(query, (err, rows) => { @@ -43,7 +43,7 @@ export function allDB( export function getDB( db: sqlite3.Database, query: string, - params: any[] + params: any[], ): Promise { return new Promise((resolve, reject) => { db.get(query, params, (err, row: any) => { @@ -73,7 +73,7 @@ export function openDatabase() { sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, (err: Error | null) => { if (err) console.error(err.message); - } + }, ); } @@ -105,7 +105,8 @@ export function createTables(db: sqlite3.Database) { /* Créer la table registered_user dans la base de données */ export function createRegisteredUserTable(db: sqlite3.Database): Promise { - const tableRegisteredUser = `CREATE TABLE IF NOT EXISTS registered_user (id_user INTEGER PRIMARY KEY AUTOINCREMENT, login TEXT NOT NULL, password TEXT NOT NULL, permissions INTEGER NOT NULL, UNIQUE (login))`; + const tableRegisteredUser = + `CREATE TABLE IF NOT EXISTS registered_user (id_user INTEGER PRIMARY KEY AUTOINCREMENT, login TEXT NOT NULL, password TEXT NOT NULL, permissions INTEGER NOT NULL, UNIQUE (login))`; return runDB(db, tableRegisteredUser, []); } @@ -115,7 +116,7 @@ export function insertUser( db: sqlite3.Database, login: string, password: string, - permissions: number + permissions: number, ) { const insertUserQuery = `INSERT INTO registered_user (login, password, permissions) VALUES (?, ?, ?)`; @@ -126,7 +127,7 @@ export function insertUser( export function updateUserLogin( db: sqlite3.Database, id: number, - newLogin: string + newLogin: string, ) { const updateUserLoginQuery = `UPDATE registered_user SET login = ? WHERE id_user = ?`; @@ -137,7 +138,7 @@ export function updateUserLogin( export function updateUserPassword( db: sqlite3.Database, id: number, - newPassword: string + newPassword: string, ) { const updateUserPasswordQuery = `UPDATE registered_user SET password = ? WHERE id_user = ?`; @@ -148,7 +149,7 @@ export function updateUserPassword( export function updateUserPermissions( db: sqlite3.Database, id: number, - newPermissions: number + newPermissions: number, ) { const updateUserPermissionsQuery = `UPDATE registered_user SET permissions = ? WHERE id_user = ?`; @@ -206,7 +207,8 @@ export function selectUserById(db: sqlite3.Database, id: number) { /* Créer la table language dans la base de données */ export function createLanguageTable(db: sqlite3.Database): Promise { - const tableLanguage = `CREATE TABLE IF NOT EXISTS language (id_language INTEGER PRIMARY KEY AUTOINCREMENT, designation TEXT NOT NULL, version INTEGER NOT NULL)`; + const tableLanguage = + `CREATE TABLE IF NOT EXISTS language (id_language INTEGER PRIMARY KEY AUTOINCREMENT, designation TEXT NOT NULL, version INTEGER NOT NULL)`; return runDB(db, tableLanguage, []); } @@ -214,7 +216,7 @@ export function createLanguageTable(db: sqlite3.Database): Promise { export function insertLanguage( db: sqlite3.Database, designation: string, - version: number + version: number, ) { const insertLanguageQuery = `INSERT INTO language (designation, version) VALUES (?, ?)`; @@ -225,7 +227,7 @@ export function insertLanguage( export function updateLanguageDesignation( db: sqlite3.Database, id: number, - newDesignation: string + newDesignation: string, ) { const updateLanguageDesignationQuery = `UPDATE language SET designation = ? WHERE id_language = ?`; @@ -236,7 +238,7 @@ export function updateLanguageDesignation( export function updateLanguageVersion( db: sqlite3.Database, id: number, - newVersion: number + newVersion: number, ) { const updateLanguageVersionQuery = `UPDATE language SET version = ? WHERE id_language = ?`; @@ -282,7 +284,8 @@ export function selectLanguageById(db: sqlite3.Database, id: number) { /* Créer la table work dans la base de données */ export function createWorkTable(db: sqlite3.Database): Promise { - const tableWork = `CREATE TABLE IF NOT EXISTS work (id_work INTEGER PRIMARY KEY AUTOINCREMENT, link CHAR(36) NOT NULL, user_id INTEGER REFERENCES registered_user(id_user), language_id INTEGER NOT NULL REFERENCES language(id_language), content TEXT NOT NULL)`; + const tableWork = + `CREATE TABLE IF NOT EXISTS work (id_work INTEGER PRIMARY KEY AUTOINCREMENT, link CHAR(36) NOT NULL, user_id INTEGER REFERENCES registered_user(id_user), language_id INTEGER NOT NULL REFERENCES language(id_language), content TEXT NOT NULL)`; return runDB(db, tableWork, []); } @@ -292,7 +295,7 @@ export function insertWork( link: string, user_id: number, language_id: number, - content: string + content: string, ) { const insertWorkQuery = `INSERT INTO work (link, user_id, language_id, content) VALUES (?, ?, ?, ?)`; diff --git a/src/runner.ts b/src/runner.ts index 290e8bb..1a8567b 100644 --- a/src/runner.ts +++ b/src/runner.ts @@ -19,11 +19,11 @@ export const IMAGES = { export function allocateBuffer( jobId: string, code: string, - image: string + image: string, ): Buffer { let cur = 0; const buffer = Buffer.allocUnsafe( - jobId.length + image.length + code.length + 9 + jobId.length + image.length + code.length + 9, ); cur = buffer.writeUInt8(0, cur); cur += buffer.write(jobId, cur); diff --git a/src/server.ts b/src/server.ts index 8690712..51d8de6 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,12 +1,12 @@ +import { rebaseUpdates, Update } from "@codemirror/collab"; +import { ChangeSet, Text } from "@codemirror/state"; import cors from "@fastify/cors"; -import websocket, { WebSocket } from '@fastify/websocket'; import { Type, TypeBoxTypeProvider } from "@fastify/type-provider-typebox"; +import websocket, { WebSocket } from "@fastify/websocket"; import Fastify, { FastifyReply } from "fastify"; import { nanoid } from "nanoid"; import { allocateBuffer, getRunner } from "runner"; import { Pull, Push } from "zeromq"; -import { ChangeSet, Text } from "@codemirror/state"; -import { Update, rebaseUpdates } from "@codemirror/collab"; import * as db from "./database"; const sender = new Push(); @@ -14,17 +14,25 @@ await sender.bind(`tcp://127.0.0.1:5557`); const receiver = new Pull(); await receiver.bind(`tcp://127.0.0.1:5558`); + const clients: Record = {}; const generateId = () => nanoid(32); -let updates: Update[] = []; -let doc = Text.of(['']); -const liveClients: WebSocket[] = []; +//let updates: Update[] = []; +//let doc = Text.of(["foo"]); + +type Room = { + sockets: WebSocket[]; + updates: Update[]; + doc: Text; +} + +const rooms: Record = {}; function send(socket: WebSocket, requestId: number, payload: unknown) { const response = { _request: requestId, - payload + payload, }; socket.send(JSON.stringify(response)); } @@ -32,39 +40,79 @@ function send(socket: WebSocket, requestId: number, payload: unknown) { const fastify = Fastify({ logger: true, }).withTypeProvider(); +type Fastify = typeof fastify; await fastify.register(cors, { - origin: process.env.ALLOW_ORIGIN || '*', + origin: process.env.ALLOW_ORIGIN || "*", }); fastify.register(websocket); -fastify.get("/live", { websocket: true }, (socket, req) => { - liveClients.push(socket); - socket.on("message", message => { - const data = JSON.parse(message.toString()); - const requestId = data._request; - if (data.type === "pullUpdates") { - send(socket, requestId, updates.slice(data.version)) - } else if (data.type === "pushUpdates") { - let received = data.updates.map((json: any) => ({ - clientID: json.clientID, - changes: ChangeSet.fromJSON(json.changes) - })) - if (data.version != updates.length) { - received = rebaseUpdates(received, updates.slice(data.version)) - } - for (let update of received) { - updates.push(update) - doc = update.changes.apply(doc) + + +fastify.register(async function (fastify: Fastify) { + fastify.get( + "/live/:roomId", + { + schema: { + params: Type.Object({ + roomId: Type.String(), + }) + }, + websocket: true + }, + (socket, request) => { + const { roomId } = request.params; + let room = rooms[roomId]; + if(!room){ + room = { + sockets: [], + updates: [], + doc: Text.of(['']) + }; + rooms[roomId] = room; } - send(socket, requestId, received.map((update: any) => ({ - clientID: update.clientID, - changes: update.changes.toJSON() - }))); - } else if (data.type == "getDocument") { - send(socket, requestId, {version: updates.length, doc: doc.toString()}) - } - }) + room.sockets.push(socket); + socket.on("message", message => { + const data = JSON.parse(message.toString()); + const requestId = data._request; + + if (data.type === "pullUpdates") { + send(socket, requestId, room.updates.slice(data.version)); + } else if (data.type === "pushUpdates") { + let received = data.updates.map((json: any) => ({ + clientID: json.clientID, + changes: ChangeSet.fromJSON(json.changes), + })); + + if (data.version != room.updates.length) { + received = rebaseUpdates(received, room.updates.slice(data.version)); + } + + for (let update of received) { + room.updates.push(update); + room.doc = update.changes.apply(room.doc); + } + send( + socket, + requestId, + received.map((update: any) => ({ + clientID: update.clientID, + changes: update.changes.toJSON(), + })), + ); + } else if (data.type === "getDocument") { + send(socket, requestId, { version: room.updates.length, doc: room.doc.toString() }); + } + }); + }); }) +/* Route pour créer une room */ +fastify.post( + "/live", + async (request, reply) => { + return generateId(); + }, +); + fastify.post("/run", { schema: { body: Type.Object({ @@ -122,7 +170,7 @@ fastify.post( const { login, password, permissions } = request.body; db.insertUser(database, login, password, permissions); reply.send({ success: true }); - } + }, ); /* Route pour mettre à jour le login d'un utilisateur */ @@ -145,7 +193,7 @@ fastify.put( const { newLogin } = request.body; db.updateUserLogin(database, id, newLogin); reply.send({ success: true }); - } + }, ); /* Route pour mettre à jour le mot de passe d'un utilisateur */ @@ -168,7 +216,7 @@ fastify.put( const { newPassword } = request.body; db.updateUserPassword(database, id, newPassword); reply.send({ success: true }); - } + }, ); /* Route pour mettre à jour les permissions d'un utilisateur */ @@ -191,7 +239,7 @@ fastify.put( const { newPermissions } = request.body; await db.updateUserPermissions(database, id, newPermissions); reply.send({ success: true }); - } + }, ); /* Route pour supprimer un utilisateur par son ID */ @@ -210,7 +258,7 @@ fastify.delete( const { id } = request.params; await db.deleteUserById(database, id); reply.send({ success: true }); - } + }, ); /* Route pour supprimer un utilisateur par son login */ @@ -227,7 +275,7 @@ fastify.delete( const { login } = request.params; await db.deleteUserByLogin(database, login); reply.send({ success: true }); - } + }, ); /* Route pour supprimer tous les utilisateurs */ @@ -258,7 +306,7 @@ fastify.get( const { id } = request.params; const user = await db.selectUserById(database, id); reply.send(user); - } + }, ); /* Route pour récupérer un utilisateur par son login */ @@ -275,7 +323,7 @@ fastify.get( const { login } = request.params; const user = await db.selectUserByLogin(database, login); reply.send(user); - } + }, ); /* Route pour créer un language */ @@ -293,7 +341,7 @@ fastify.post( const { designation, version } = request.body; db.insertLanguage(database, designation, version); reply.send({ success: true }); - } + }, ); /* Route pour mettre à jour la désignation d'un language */ @@ -316,7 +364,7 @@ fastify.put( const { newDesignation } = request.body; db.updateLanguageDesignation(database, id, newDesignation); reply.send({ success: true }); - } + }, ); /* Route pour mettre à jour la version d'un language */ @@ -339,7 +387,7 @@ fastify.put( const { newVersion } = request.body; db.updateLanguageVersion(database, id, newVersion); reply.send({ success: true }); - } + }, ); /* Route pour supprimer un language */ @@ -358,7 +406,7 @@ fastify.delete( const { id } = request.params; db.deleteLanguage(database, id); reply.send({ success: true }); - } + }, ); /* Route pour supprimer tous les languages */ @@ -383,7 +431,7 @@ fastify.get( const { id } = request.params; const language = await db.selectLanguageById(database, id); reply.send(language); - } + }, ); /* Route pour récupérer tous les languages */ @@ -409,7 +457,7 @@ fastify.post( const { id_user, link, id_language, code } = request.body; db.insertWork(database, link, id_user, id_language, code); reply.send({ success: true }); - } + }, ); /* Route pour récupérer tous les works */ @@ -440,7 +488,7 @@ fastify.delete( const { id } = request.params; db.deleteWork(database, id); reply.send({ success: true }); - } + }, ); /* Route pour récupérer un work par son ID */ @@ -459,7 +507,7 @@ fastify.get( const { id } = request.params; const work = await db.selectWorkById(database, id); reply.send(work); - } + }, ); /* Forward output est une fonction asynchrone qui permet de récupérer les messages envoyés par le container et de les renvoyer au client */ -- 2.36.3 From b47dbb65fe8d19e971a28b5a81c0146dcfdd5c42 Mon Sep 17 00:00:00 2001 From: clfreville2 Date: Tue, 28 May 2024 16:12:27 +0200 Subject: [PATCH 2/9] Add an email to User and title, date to Work --- src/database.ts | 41 +++++++++++++++++++++++++++++++++-------- src/server.ts | 34 ++++++++++++++++++++++++++++++---- 2 files changed, 63 insertions(+), 12 deletions(-) diff --git a/src/database.ts b/src/database.ts index 3aa61be..4cfe03c 100644 --- a/src/database.ts +++ b/src/database.ts @@ -98,6 +98,7 @@ export function createTables(db: sqlite3.Database) { // CREATE TABLE registered_user ( // id_user SERIAL PRIMARY KEY, // login VARCHAR(64) NOT NULL, +// email VARCHAR(64) NOT NULL, // password VARCHAR(72) NOT NULL, // permissions INT NOT NULL, // UNIQUE (login) @@ -105,8 +106,13 @@ export function createTables(db: sqlite3.Database) { /* Créer la table registered_user dans la base de données */ export function createRegisteredUserTable(db: sqlite3.Database): Promise { - const tableRegisteredUser = - `CREATE TABLE IF NOT EXISTS registered_user (id_user INTEGER PRIMARY KEY AUTOINCREMENT, login TEXT NOT NULL, password TEXT NOT NULL, permissions INTEGER NOT NULL, UNIQUE (login))`; + const tableRegisteredUser = `CREATE TABLE IF NOT EXISTS registered_user ( + id_user INTEGER PRIMARY KEY AUTOINCREMENT, + login TEXT NOT NULL, + email TEXT NOT NULL, + password TEXT NOT NULL, + permissions INTEGER NOT NULL, + UNIQUE (login))`; return runDB(db, tableRegisteredUser, []); } @@ -115,12 +121,13 @@ export function createRegisteredUserTable(db: sqlite3.Database): Promise { export function insertUser( db: sqlite3.Database, login: string, + email: string, password: string, permissions: number, ) { - const insertUserQuery = `INSERT INTO registered_user (login, password, permissions) VALUES (?, ?, ?)`; + const insertUserQuery = `INSERT INTO registered_user (login, email, password, permissions) VALUES (?, ?, ?, ?)`; - return runDB(db, insertUserQuery, [login, password, permissions]); + return runDB(db, insertUserQuery, [login, email, password, permissions]); } /* Modifier le login d'un utilisateur dans la table registered_user */ @@ -156,6 +163,15 @@ export function updateUserPermissions( return runDB(db, updateUserPermissionsQuery, [newPermissions, id]); } +export function updateUserEmail( + db: sqlite3.Database, + id: number, + newEmail: string +) { + const updateUserEmailQuery = `UPDATE registered_user SET email = ? WHERE id_user = ?`; + return runDB(db, updateUserEmailQuery, [newEmail, id]); +} + /* Supprimer un utilisateur de la table registered_user par son ID */ export function deleteUserById(db: sqlite3.Database, id: number) { const deleteUserQuery = `DELETE FROM registered_user WHERE id_user = ?`; @@ -279,13 +295,20 @@ export function selectLanguageById(db: sqlite3.Database, id: number) { // link CHAR(36) NOT NULL, // user_id INT REFERENCES registered_user(id_user), // language_id INT NOT NULL REFERENCES language(id_language) +// title VARCHAR(30) NOT NULL, // content TEXT NOT NULL, +// date TIMESTAMP DEFAULT CURRENT_TIMESTAMP // ); /* Créer la table work dans la base de données */ export function createWorkTable(db: sqlite3.Database): Promise { - const tableWork = - `CREATE TABLE IF NOT EXISTS work (id_work INTEGER PRIMARY KEY AUTOINCREMENT, link CHAR(36) NOT NULL, user_id INTEGER REFERENCES registered_user(id_user), language_id INTEGER NOT NULL REFERENCES language(id_language), content TEXT NOT NULL)`; + const tableWork = `CREATE TABLE IF NOT EXISTS work ( + id_work INTEGER PRIMARY KEY AUTOINCREMENT, + link CHAR(36) NOT NULL, user_id INTEGER REFERENCES registered_user(id_user), + language_id INTEGER NOT NULL REFERENCES language(id_language), + title TEXT NOT NULL, + content TEXT NOT NULL, + date TIMESTAMP DEFAULT CURRENT_TIMESTAMP)`; return runDB(db, tableWork, []); } @@ -295,11 +318,13 @@ export function insertWork( link: string, user_id: number, language_id: number, + title: string, content: string, + date: string ) { - const insertWorkQuery = `INSERT INTO work (link, user_id, language_id, content) VALUES (?, ?, ?, ?)`; + const insertWorkQuery = `INSERT INTO work (link, user_id, language_id, title, content, date) VALUES (?, ?, ?, ?, ?, ?)`; - return runDB(db, insertWorkQuery, [link, user_id, language_id, content]); + return runDB(db, insertWorkQuery, [link, user_id, language_id, title, content, date]); } /* Sélectionner tous les works de la table work */ diff --git a/src/server.ts b/src/server.ts index 51d8de6..b89c714 100644 --- a/src/server.ts +++ b/src/server.ts @@ -161,14 +161,15 @@ fastify.post( schema: { body: Type.Object({ login: Type.String(), + email: Type.String(), password: Type.String(), permissions: Type.Number(), }), }, }, async (request, reply) => { - const { login, password, permissions } = request.body; - db.insertUser(database, login, password, permissions); + const { login, email, password, permissions } = request.body; + db.insertUser(database, login, email, password, permissions); reply.send({ success: true }); }, ); @@ -242,6 +243,29 @@ fastify.put( }, ); +/* Route pour mettre à jour l'email d'un utilisateur */ +fastify.put( + "/users/:id/email", + { + schema: { + params: Type.Object({ + id: Type.Number({ + minimum: 0, + }), + }), + body: Type.Object({ + newEmail: Type.String(), + }), + }, + }, + async (request, reply) => { + const { id } = request.params; + const { newEmail } = request.body; + await db.updateUserEmail(database, id, newEmail); + reply.send({ success: true }); + } +); + /* Route pour supprimer un utilisateur par son ID */ fastify.delete( "/users/:id", @@ -449,13 +473,15 @@ fastify.post( id_user: Type.Number(), link: Type.String(), id_language: Type.Number(), + title: Type.String(), code: Type.String(), + date: Type.String(), }), }, }, async (request, reply) => { - const { id_user, link, id_language, code } = request.body; - db.insertWork(database, link, id_user, id_language, code); + const { id_user, link, id_language, title, code, date } = request.body; + db.insertWork(database, link, id_user, id_language, title, code, date); reply.send({ success: true }); }, ); -- 2.36.3 From fc1cfbfc31b694b62d7dfa57e3f819843ffba102 Mon Sep 17 00:00:00 2001 From: cofrizot Date: Tue, 28 May 2024 16:23:42 +0200 Subject: [PATCH 3/9] Add the update of the title and the content --- src/database.ts | 24 ++++++++++++++++++++++++ src/server.ts | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/src/database.ts b/src/database.ts index 4cfe03c..a6c2754 100644 --- a/src/database.ts +++ b/src/database.ts @@ -354,3 +354,27 @@ export function selectWorkById(db: sqlite3.Database, id: number) { return getDB(db, selectWorkByIdQuery, [id]); } + +/* Update the work title by its ID */ +export function updateWorkTitle( + db: sqlite3.Database, + id: number, + newTitle: string +) { + const updateWorkTitleQuery = `UPDATE work SET title = ? WHERE id_work = ?`; + + return runDB(db, updateWorkTitleQuery, [newTitle, id]); +} + +/* Update the work content by its ID */ +export function updateWorkContent( + db: sqlite3.Database, + id: number, + newContent: string +) { + const updateWorkContentQuery = `UPDATE work SET content = ? WHERE id_work = ?`; + + return runDB(db, updateWorkContentQuery, [newContent, id]); +} + + diff --git a/src/server.ts b/src/server.ts index b89c714..3e781d7 100644 --- a/src/server.ts +++ b/src/server.ts @@ -536,6 +536,54 @@ fastify.get( }, ); +/* Update the work title by its ID */ +fastify.put( + "/works/:id/title", + { + schema: { + params: Type.Object({ + id: Type.Number({ + minimum: 0, + }), + }), + body: Type.Object({ + newTitle: Type.String(), + }), + }, + }, + async (request, reply) => { + const { id } = request.params; + const { newTitle } = request.body; + db.updateWorkTitle(database, id, newTitle); + reply.send({ success: true }); + } +); + +/* Update the work content by its ID */ +fastify.put( + "/works/:id/content", + { + schema: { + params: Type.Object({ + id: Type.Number({ + minimum: 0, + }), + }), + body: Type.Object({ + newContent: Type.String(), + }), + }, + }, + async (request, reply) => { + const { id } = request.params; + const { newContent } = request.body; + db.updateWorkContent(database, id, newContent); + reply.send({ success: true }); + } +); + + + /* Forward output est une fonction asynchrone qui permet de récupérer les messages envoyés par le container et de les renvoyer au client */ async function forwardOutput() { for await (const [buff] of receiver) { -- 2.36.3 From 99b29432cd74d08e6eb9b34ea8206609ed900cc1 Mon Sep 17 00:00:00 2001 From: cofrizot Date: Mon, 3 Jun 2024 15:07:50 +0200 Subject: [PATCH 4/9] Add await to the calls and add the date by default --- src/database.ts | 3 +-- src/server.ts | 16 +++++++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/database.ts b/src/database.ts index a6c2754..7aa6704 100644 --- a/src/database.ts +++ b/src/database.ts @@ -320,11 +320,10 @@ export function insertWork( language_id: number, title: string, content: string, - date: string ) { const insertWorkQuery = `INSERT INTO work (link, user_id, language_id, title, content, date) VALUES (?, ?, ?, ?, ?, ?)`; - return runDB(db, insertWorkQuery, [link, user_id, language_id, title, content, date]); + return runDB(db, insertWorkQuery, [link, user_id, language_id, title, content, new Date().toISOString()]); } /* Sélectionner tous les works de la table work */ diff --git a/src/server.ts b/src/server.ts index 3e781d7..eb92b16 100644 --- a/src/server.ts +++ b/src/server.ts @@ -261,6 +261,13 @@ fastify.put( async (request, reply) => { const { id } = request.params; const { newEmail } = request.body; + + // Check if the ID relates to an existing ID. + const user = await db.selectUserById(database, id); + if (!user) { + reply.status(404).send({ error: "User not found" }); + return; + } await db.updateUserEmail(database, id, newEmail); reply.send({ success: true }); } @@ -475,13 +482,12 @@ fastify.post( id_language: Type.Number(), title: Type.String(), code: Type.String(), - date: Type.String(), }), }, }, async (request, reply) => { - const { id_user, link, id_language, title, code, date } = request.body; - db.insertWork(database, link, id_user, id_language, title, code, date); + const { id_user, link, id_language, title, code } = request.body; + await db.insertWork(database, link, id_user, id_language, title, code); reply.send({ success: true }); }, ); @@ -554,7 +560,7 @@ fastify.put( async (request, reply) => { const { id } = request.params; const { newTitle } = request.body; - db.updateWorkTitle(database, id, newTitle); + await db.updateWorkTitle(database, id, newTitle); reply.send({ success: true }); } ); @@ -577,7 +583,7 @@ fastify.put( async (request, reply) => { const { id } = request.params; const { newContent } = request.body; - db.updateWorkContent(database, id, newContent); + await db.updateWorkContent(database, id, newContent); reply.send({ success: true }); } ); -- 2.36.3 From aa0516dc728a70c015515f5fe249c3e88346697c Mon Sep 17 00:00:00 2001 From: "matis.mazingue" Date: Tue, 11 Jun 2024 17:07:20 +0200 Subject: [PATCH 5/9] selectWorkById et selectLastWorkByUserId --- src/database.ts | 35 +++++++++++++++++++++++++++-------- src/server.ts | 19 +++++++++++++++++++ 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/src/database.ts b/src/database.ts index 7aa6704..dd1f6fb 100644 --- a/src/database.ts +++ b/src/database.ts @@ -27,9 +27,10 @@ export function runDB( export function allDB( db: sqlite3.Database, query: string, + params?: any[] ): Promise { return new Promise((resolve, reject) => { - db.all(query, (err, rows) => { + db.all(query, params, (err, rows) => { if (err) { reject(err); } else { @@ -333,6 +334,31 @@ export function selectAllWorks(db: sqlite3.Database): Promise { return allDB(db, selectAllWorksQuery); } +/* Sélectionner tous les works de la table work */ +export function selectWorksByUserId(db: sqlite3.Database, user_id: number): Promise { + const selectWorksByUserIdQuery = `SELECT * FROM work + WHERE user_id = ?`; + + return allDB(db, selectWorksByUserIdQuery, [user_id]); +} + +/* Sélectionner un work par son ID */ +export function selectWorkById(db: sqlite3.Database, id: number) { + const selectWorkByIdQuery = `SELECT * FROM work WHERE id_work = ?`; + + return getDB(db, selectWorkByIdQuery, [id]); +} + +/* Sélectionner le dernier work par l'id de l'utilisateur */ +export function selectLastWorkByUserId(db: sqlite3.Database, user_id: number) { + const selectLastWorkByUserIdQuery = `SELECT * FROM work + WHERE user_id = ? + ORDER BY date DESC + LIMIT 1`; + + return getDB(db, selectLastWorkByUserIdQuery, [user_id]); +} + /* Supprimer tous les works de la table work */ export function deleteAllWorks(db: sqlite3.Database) { const deleteAllWorksQuery = `DELETE FROM work`; @@ -347,13 +373,6 @@ export function deleteWork(db: sqlite3.Database, id: number) { return runDB(db, deleteWorkQuery, [id]); } -/* Sélectionner un work par son ID */ -export function selectWorkById(db: sqlite3.Database, id: number) { - const selectWorkByIdQuery = `SELECT * FROM work WHERE id_work = ?`; - - return getDB(db, selectWorkByIdQuery, [id]); -} - /* Update the work title by its ID */ export function updateWorkTitle( db: sqlite3.Database, diff --git a/src/server.ts b/src/server.ts index eb92b16..a685276 100644 --- a/src/server.ts +++ b/src/server.ts @@ -542,6 +542,25 @@ fastify.get( }, ); +/* Route pour récupérer un work par l'id de l'utilisateur */ +fastify.get( + "/works/user/:user_id", + { + schema: { + params: Type.Object({ + user_id: Type.Number({ + minimum: 0, + }), + }), + }, + }, + async (request, reply) => { + const { user_id } = request.params; + const work = await db.selectLastWorkByUserId(database, user_id); + reply.send(work); + }, +); + /* Update the work title by its ID */ fastify.put( "/works/:id/title", -- 2.36.3 From b22caee2024be031bddb3d33985b35b55c8a441a Mon Sep 17 00:00:00 2001 From: clfreville2 Date: Mon, 17 Jun 2024 08:35:23 +0200 Subject: [PATCH 6/9] Remove trailing whitespaces --- src/database.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/database.ts b/src/database.ts index dd1f6fb..0731d70 100644 --- a/src/database.ts +++ b/src/database.ts @@ -351,9 +351,9 @@ export function selectWorkById(db: sqlite3.Database, id: number) { /* Sélectionner le dernier work par l'id de l'utilisateur */ export function selectLastWorkByUserId(db: sqlite3.Database, user_id: number) { - const selectLastWorkByUserIdQuery = `SELECT * FROM work - WHERE user_id = ? - ORDER BY date DESC + const selectLastWorkByUserIdQuery = `SELECT * FROM work + WHERE user_id = ? + ORDER BY date DESC LIMIT 1`; return getDB(db, selectLastWorkByUserIdQuery, [user_id]); -- 2.36.3 From d9ffd7331361f19541c315ab4f4aff6fa553320d Mon Sep 17 00:00:00 2001 From: "matis.mazingue" Date: Mon, 17 Jun 2024 08:43:04 +0200 Subject: [PATCH 7/9] get last work's link by user id --- src/database.ts | 10 ++++++++++ src/server.ts | 23 +++++++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/database.ts b/src/database.ts index dd1f6fb..4271e0e 100644 --- a/src/database.ts +++ b/src/database.ts @@ -359,6 +359,16 @@ export function selectLastWorkByUserId(db: sqlite3.Database, user_id: number) { return getDB(db, selectLastWorkByUserIdQuery, [user_id]); } +/* Sélectionner le link du dernier work par l'id de l'utilisateur */ +export function selectLinkLastWorkByUserId(db: sqlite3.Database, user_id: number) { + const selectLinkLastWorkByUserIdQuery = `SELECT link FROM work + WHERE user_id = ? + ORDER BY date DESC + LIMIT 1`; + + return getDB(db, selectLinkLastWorkByUserIdQuery, [user_id]); +} + /* Supprimer tous les works de la table work */ export function deleteAllWorks(db: sqlite3.Database) { const deleteAllWorksQuery = `DELETE FROM work`; diff --git a/src/server.ts b/src/server.ts index a685276..54b1c97 100644 --- a/src/server.ts +++ b/src/server.ts @@ -542,9 +542,9 @@ fastify.get( }, ); -/* Route pour récupérer un work par l'id de l'utilisateur */ +/* Route pour récupérer le dernier work par l'id de l'utilisateur */ fastify.get( - "/works/user/:user_id", + "/works/last-work/:user_id", { schema: { params: Type.Object({ @@ -561,6 +561,25 @@ fastify.get( }, ); +/* Route pour récupérer le dernier work par l'id de l'utilisateur */ +fastify.get( + "/works/last-link/:user_id", + { + schema: { + params: Type.Object({ + user_id: Type.Number({ + minimum: 0, + }), + }), + }, + }, + async (request, reply) => { + const { user_id } = request.params; + const linkWork = await db.selectLinkLastWorkByUserId(database, user_id); + reply.send(linkWork); + }, +); + /* Update the work title by its ID */ fastify.put( "/works/:id/title", -- 2.36.3 From 1240b3ebe732e892d805cf6d9f74f08faa6712e1 Mon Sep 17 00:00:00 2001 From: "matis.mazingue" Date: Mon, 17 Jun 2024 10:37:07 +0200 Subject: [PATCH 8/9] delete table language + delete getLastLinkWorkByUSerId --- src/database.ts | 96 ++------------------------------- src/server.ts | 139 ++---------------------------------------------- 2 files changed, 8 insertions(+), 227 deletions(-) diff --git a/src/database.ts b/src/database.ts index 32b5c7b..a7af7dc 100644 --- a/src/database.ts +++ b/src/database.ts @@ -91,7 +91,6 @@ export function closeDatabase(db: sqlite3.Database) { /* Create all the tables in the database */ export function createTables(db: sqlite3.Database) { createRegisteredUserTable(db); - createLanguageTable(db); createWorkTable(db); } @@ -215,87 +214,12 @@ export function selectUserById(db: sqlite3.Database, id: number) { return getDB(db, selectUserByIdQuery, [id]); } -/////////////////////////// Gestion des Languages /////////////////////////// -// CREATE TABLE language ( -// id_language SERIAL PRIMARY KEY, -// designation VARCHAR(30) NOT NULL, -// version INT NOT NULL, -// ); - -/* Créer la table language dans la base de données */ -export function createLanguageTable(db: sqlite3.Database): Promise { - const tableLanguage = - `CREATE TABLE IF NOT EXISTS language (id_language INTEGER PRIMARY KEY AUTOINCREMENT, designation TEXT NOT NULL, version INTEGER NOT NULL)`; - return runDB(db, tableLanguage, []); -} - -/* Insérer un language dans la table language */ -export function insertLanguage( - db: sqlite3.Database, - designation: string, - version: number, -) { - const insertLanguageQuery = `INSERT INTO language (designation, version) VALUES (?, ?)`; - - return runDB(db, insertLanguageQuery, [designation, version]); -} - -/* Modifier la designation d'un language dans la table language */ -export function updateLanguageDesignation( - db: sqlite3.Database, - id: number, - newDesignation: string, -) { - const updateLanguageDesignationQuery = `UPDATE language SET designation = ? WHERE id_language = ?`; - - return runDB(db, updateLanguageDesignationQuery, [newDesignation, id]); -} - -/* Modifier la version d'un language dans la table language */ -export function updateLanguageVersion( - db: sqlite3.Database, - id: number, - newVersion: number, -) { - const updateLanguageVersionQuery = `UPDATE language SET version = ? WHERE id_language = ?`; - - return runDB(db, updateLanguageVersionQuery, [newVersion, id]); -} - -/* Supprimer un language de la table language par son ID */ -export function deleteLanguage(db: sqlite3.Database, id: number) { - const deleteLanguageQuery = `DELETE FROM language WHERE id_language = ?`; - - return runDB(db, deleteLanguageQuery, [id]); -} - -/* Supprimer tous les languages de la table language */ -export function deleteAllLanguages(db: sqlite3.Database) { - const deleteAllLanguagesQuery = `DELETE FROM language`; - - return runDB(db, deleteAllLanguagesQuery, []); -} - -/* Sélectionner tous les languages de la table language */ -export function selectAllLanguages(db: sqlite3.Database): Promise { - const selectAllLanguagesQuery = `SELECT * FROM language`; - - return allDB(db, selectAllLanguagesQuery); -} - -/* Sélectionner un language par son ID */ -export function selectLanguageById(db: sqlite3.Database, id: number) { - const selectLanguageByIdQuery = `SELECT * FROM language WHERE id_language = ?`; - - return getDB(db, selectLanguageByIdQuery, [id]); -} - /////////////////////////// Gestion des works /////////////////////////// // CREATE TABLE work ( // id_work SERIAL PRIMARY KEY, // link CHAR(36) NOT NULL, // user_id INT REFERENCES registered_user(id_user), -// language_id INT NOT NULL REFERENCES language(id_language) +// language TEXT NOT NULL, // title VARCHAR(30) NOT NULL, // content TEXT NOT NULL, // date TIMESTAMP DEFAULT CURRENT_TIMESTAMP @@ -306,7 +230,7 @@ export function createWorkTable(db: sqlite3.Database): Promise { const tableWork = `CREATE TABLE IF NOT EXISTS work ( id_work INTEGER PRIMARY KEY AUTOINCREMENT, link CHAR(36) NOT NULL, user_id INTEGER REFERENCES registered_user(id_user), - language_id INTEGER NOT NULL REFERENCES language(id_language), + language TEXT NOT NULL, title TEXT NOT NULL, content TEXT NOT NULL, date TIMESTAMP DEFAULT CURRENT_TIMESTAMP)`; @@ -318,13 +242,13 @@ export function insertWork( db: sqlite3.Database, link: string, user_id: number, - language_id: number, + language: string, title: string, content: string, ) { - const insertWorkQuery = `INSERT INTO work (link, user_id, language_id, title, content, date) VALUES (?, ?, ?, ?, ?, ?)`; + const insertWorkQuery = `INSERT INTO work (link, user_id, language, title, content, date) VALUES (?, ?, ?, ?, ?, ?)`; - return runDB(db, insertWorkQuery, [link, user_id, language_id, title, content, new Date().toISOString()]); + return runDB(db, insertWorkQuery, [link, user_id, language, title, content, new Date().toISOString()]); } /* Sélectionner tous les works de la table work */ @@ -359,16 +283,6 @@ export function selectLastWorkByUserId(db: sqlite3.Database, user_id: number) { return getDB(db, selectLastWorkByUserIdQuery, [user_id]); } -/* Sélectionner le link du dernier work par l'id de l'utilisateur */ -export function selectLinkLastWorkByUserId(db: sqlite3.Database, user_id: number) { - const selectLinkLastWorkByUserIdQuery = `SELECT link FROM work - WHERE user_id = ? - ORDER BY date DESC - LIMIT 1`; - - return getDB(db, selectLinkLastWorkByUserIdQuery, [user_id]); -} - /* Supprimer tous les works de la table work */ export function deleteAllWorks(db: sqlite3.Database) { const deleteAllWorksQuery = `DELETE FROM work`; diff --git a/src/server.ts b/src/server.ts index 54b1c97..813ff5a 100644 --- a/src/server.ts +++ b/src/server.ts @@ -357,120 +357,6 @@ fastify.get( }, ); -/* Route pour créer un language */ -fastify.post( - "/languages", - { - schema: { - body: Type.Object({ - designation: Type.String(), - version: Type.Number(), - }), - }, - }, - async (request, reply) => { - const { designation, version } = request.body; - db.insertLanguage(database, designation, version); - reply.send({ success: true }); - }, -); - -/* Route pour mettre à jour la désignation d'un language */ -fastify.put( - "/languages/:id/designation", - { - schema: { - params: Type.Object({ - id: Type.Number({ - minimum: 0, - }), - }), - body: Type.Object({ - newDesignation: Type.String(), - }), - }, - }, - async (request, reply) => { - const { id } = request.params; - const { newDesignation } = request.body; - db.updateLanguageDesignation(database, id, newDesignation); - reply.send({ success: true }); - }, -); - -/* Route pour mettre à jour la version d'un language */ -fastify.put( - "/languages/:id/version", - { - schema: { - params: Type.Object({ - id: Type.Number({ - minimum: 0, - }), - }), - body: Type.Object({ - newVersion: Type.Number(), - }), - }, - }, - async (request, reply) => { - const { id } = request.params; - const { newVersion } = request.body; - db.updateLanguageVersion(database, id, newVersion); - reply.send({ success: true }); - }, -); - -/* Route pour supprimer un language */ -fastify.delete( - "/languages/:id", - { - schema: { - params: Type.Object({ - id: Type.Number({ - minimum: 0, - }), - }), - }, - }, - async (request, reply) => { - const { id } = request.params; - db.deleteLanguage(database, id); - reply.send({ success: true }); - }, -); - -/* Route pour supprimer tous les languages */ -fastify.delete("/languages", async (request, reply) => { - db.deleteAllLanguages(database); - reply.send({ success: true }); -}); - -/* Route pour récupérer un language par son ID */ -fastify.get( - "/languages/:id", - { - schema: { - params: Type.Object({ - id: Type.Number({ - minimum: 0, - }), - }), - }, - }, - async (request, reply) => { - const { id } = request.params; - const language = await db.selectLanguageById(database, id); - reply.send(language); - }, -); - -/* Route pour récupérer tous les languages */ -fastify.get("/languages", async (request, reply) => { - const languages = await db.selectAllLanguages(database); - reply.send(languages); -}); - /* Route pour créer un work */ fastify.post( "/works", @@ -479,15 +365,15 @@ fastify.post( body: Type.Object({ id_user: Type.Number(), link: Type.String(), - id_language: Type.Number(), + language: Type.String(), title: Type.String(), code: Type.String(), }), }, }, async (request, reply) => { - const { id_user, link, id_language, title, code } = request.body; - await db.insertWork(database, link, id_user, id_language, title, code); + const { id_user, link, language, title, code } = request.body; + await db.insertWork(database, link, id_user, language, title, code); reply.send({ success: true }); }, ); @@ -561,25 +447,6 @@ fastify.get( }, ); -/* Route pour récupérer le dernier work par l'id de l'utilisateur */ -fastify.get( - "/works/last-link/:user_id", - { - schema: { - params: Type.Object({ - user_id: Type.Number({ - minimum: 0, - }), - }), - }, - }, - async (request, reply) => { - const { user_id } = request.params; - const linkWork = await db.selectLinkLastWorkByUserId(database, user_id); - reply.send(linkWork); - }, -); - /* Update the work title by its ID */ fastify.put( "/works/:id/title", -- 2.36.3 From f9b0fb5e10b38ef1e82c562e16c776e72c6f6e38 Mon Sep 17 00:00:00 2001 From: cofrizot Date: Mon, 17 Jun 2024 11:29:18 +0200 Subject: [PATCH 9/9] Change the get and add link --- src/database.ts | 17 ++++++++--------- src/server.ts | 17 ++++++++--------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/database.ts b/src/database.ts index a7af7dc..4a0f351 100644 --- a/src/database.ts +++ b/src/database.ts @@ -260,17 +260,15 @@ export function selectAllWorks(db: sqlite3.Database): Promise { /* Sélectionner tous les works de la table work */ export function selectWorksByUserId(db: sqlite3.Database, user_id: number): Promise { - const selectWorksByUserIdQuery = `SELECT * FROM work - WHERE user_id = ?`; + const selectWorksByUserIdQuery = `SELECT * FROM work WHERE user_id = ?`; return allDB(db, selectWorksByUserIdQuery, [user_id]); } /* Sélectionner un work par son ID */ -export function selectWorkById(db: sqlite3.Database, id: number) { - const selectWorkByIdQuery = `SELECT * FROM work WHERE id_work = ?`; - - return getDB(db, selectWorkByIdQuery, [id]); +export function selectWorkByLink(db: sqlite3.Database, link: string) { + const selectWorkByLinkQuery = `SELECT * FROM work WHERE link = ?`; + return getDB(db, selectWorkByLinkQuery, [link]); } /* Sélectionner le dernier work par l'id de l'utilisateur */ @@ -312,11 +310,12 @@ export function updateWorkTitle( export function updateWorkContent( db: sqlite3.Database, id: number, - newContent: string + newContent: string, + language: string, ) { - const updateWorkContentQuery = `UPDATE work SET content = ? WHERE id_work = ?`; + const updateWorkContentQuery = `UPDATE work SET content = ?, language = ? WHERE id_work = ?`; - return runDB(db, updateWorkContentQuery, [newContent, id]); + return runDB(db, updateWorkContentQuery, [newContent, language, id]); } diff --git a/src/server.ts b/src/server.ts index 813ff5a..d93d053 100644 --- a/src/server.ts +++ b/src/server.ts @@ -409,21 +409,19 @@ fastify.delete( }, ); -/* Route pour récupérer un work par son ID */ +/* Route pour récupérer un work par son Link */ fastify.get( - "/works/:id", + "/works/:link", { schema: { params: Type.Object({ - id: Type.Number({ - minimum: 0, - }), + link: Type.String(), }), }, }, async (request, reply) => { - const { id } = request.params; - const work = await db.selectWorkById(database, id); + const {link} = request.params; + const work = await db.selectWorkByLink(database, link); reply.send(work); }, ); @@ -482,13 +480,14 @@ fastify.put( }), body: Type.Object({ newContent: Type.String(), + language: Type.String(), }), }, }, async (request, reply) => { const { id } = request.params; - const { newContent } = request.body; - await db.updateWorkContent(database, id, newContent); + const { newContent, language } = request.body; + await db.updateWorkContent(database, id, newContent, language); reply.send({ success: true }); } ); -- 2.36.3