|
|
|
@ -1,31 +1,30 @@
|
|
|
|
|
import { rebaseUpdates, Update } from "@codemirror/collab";
|
|
|
|
|
import { ChangeSet, Text } from "@codemirror/state";
|
|
|
|
|
import cors from "@fastify/cors";
|
|
|
|
|
import { Type, TypeBoxTypeProvider } from "@fastify/type-provider-typebox";
|
|
|
|
|
import websocket, { WebSocket } from "@fastify/websocket";
|
|
|
|
|
import { Type, TypeBoxTypeProvider } from "@fastify/type-provider-typebox";
|
|
|
|
|
import Fastify, { FastifyReply } from "fastify";
|
|
|
|
|
import { nanoid } from "nanoid";
|
|
|
|
|
import { allocateBuffer, getRunner } from "runner";
|
|
|
|
|
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";
|
|
|
|
|
import bcrypt from "bcrypt";
|
|
|
|
|
import { fastifySession } from "@fastify/session";
|
|
|
|
|
import { fastifyCookie } from "@fastify/cookie";
|
|
|
|
|
|
|
|
|
|
const sender = new Push();
|
|
|
|
|
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<string, FastifyReply> = {};
|
|
|
|
|
const generateId = () => nanoid(32);
|
|
|
|
|
|
|
|
|
|
//let updates: Update[] = [];
|
|
|
|
|
//let doc = Text.of(["foo"]);
|
|
|
|
|
|
|
|
|
|
type Room = {
|
|
|
|
|
sockets: WebSocket[];
|
|
|
|
|
updates: Update[];
|
|
|
|
|
doc: Text;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const rooms: Record<string, Room> = {};
|
|
|
|
|
|
|
|
|
@ -43,29 +42,47 @@ const fastify = Fastify({
|
|
|
|
|
type Fastify = typeof fastify;
|
|
|
|
|
await fastify.register(cors, {
|
|
|
|
|
origin: process.env.ALLOW_ORIGIN || "*",
|
|
|
|
|
credentials: true,
|
|
|
|
|
methods: ["GET", "POST", "PUT", "DELETE"],
|
|
|
|
|
});
|
|
|
|
|
fastify.register(fastifyCookie);
|
|
|
|
|
fastify.register(fastifySession, {
|
|
|
|
|
secret: "8jYuS75JZuxb6C72nDtH2cY6hnV4B7i35r5c39gQ3h9G9DApAweBsQ47dU9DGpk5",
|
|
|
|
|
cookie: {
|
|
|
|
|
secure: false,
|
|
|
|
|
sameSite: "none",
|
|
|
|
|
partitioned: true,
|
|
|
|
|
},
|
|
|
|
|
saveUninitialized: false,
|
|
|
|
|
cookieName: "session-id",
|
|
|
|
|
});
|
|
|
|
|
fastify.register(websocket);
|
|
|
|
|
|
|
|
|
|
declare module "fastify" {
|
|
|
|
|
interface Session {
|
|
|
|
|
userKey: string | null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fastify.register(async function (fastify: Fastify) {
|
|
|
|
|
fastify.register(websocket);
|
|
|
|
|
fastify.register(async function(fastify: Fastify) {
|
|
|
|
|
fastify.get(
|
|
|
|
|
"/live/:roomId",
|
|
|
|
|
{
|
|
|
|
|
schema: {
|
|
|
|
|
params: Type.Object({
|
|
|
|
|
roomId: Type.String(),
|
|
|
|
|
})
|
|
|
|
|
params: Type.Object({
|
|
|
|
|
roomId: Type.String(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
websocket: true
|
|
|
|
|
websocket: true,
|
|
|
|
|
},
|
|
|
|
|
(socket, request) => {
|
|
|
|
|
const { roomId } = request.params;
|
|
|
|
|
let room = rooms[roomId];
|
|
|
|
|
if(!room){
|
|
|
|
|
if (!room) {
|
|
|
|
|
room = {
|
|
|
|
|
sockets: [],
|
|
|
|
|
updates: [],
|
|
|
|
|
doc: Text.of([''])
|
|
|
|
|
doc: Text.of([""]),
|
|
|
|
|
};
|
|
|
|
|
rooms[roomId] = room;
|
|
|
|
|
}
|
|
|
|
@ -102,16 +119,33 @@ fastify.register(async function (fastify: Fastify) {
|
|
|
|
|
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("/live", {
|
|
|
|
|
schema: {
|
|
|
|
|
body: Type.Object({
|
|
|
|
|
code: Type.String(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}, (request, reply) => {
|
|
|
|
|
const { code } = request.body;
|
|
|
|
|
let room, roomId;
|
|
|
|
|
do {
|
|
|
|
|
roomId = generateId();
|
|
|
|
|
room = rooms[roomId];
|
|
|
|
|
} while (room);
|
|
|
|
|
|
|
|
|
|
room = {
|
|
|
|
|
sockets: [],
|
|
|
|
|
updates: [],
|
|
|
|
|
doc: Text.of([code]),
|
|
|
|
|
};
|
|
|
|
|
rooms[roomId] = room;
|
|
|
|
|
return roomId;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
fastify.post("/run", {
|
|
|
|
|
schema: {
|
|
|
|
@ -168,12 +202,58 @@ fastify.post(
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
async (request, reply) => {
|
|
|
|
|
const { login, email, password, permissions } = request.body;
|
|
|
|
|
db.insertUser(database, login, email, password, permissions);
|
|
|
|
|
reply.send({ success: true });
|
|
|
|
|
const { login, email, password, permissions } = request.body;
|
|
|
|
|
|
|
|
|
|
// Hashage du mot de passe
|
|
|
|
|
const saltRounds = 10;
|
|
|
|
|
const hashedPassword = await bcrypt.hash(password, saltRounds);
|
|
|
|
|
|
|
|
|
|
const success = await db.insertUser(
|
|
|
|
|
database,
|
|
|
|
|
login,
|
|
|
|
|
email,
|
|
|
|
|
hashedPassword,
|
|
|
|
|
permissions
|
|
|
|
|
);
|
|
|
|
|
reply.send({ success });
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
/* Route pour vérifier si un utilisateur existe */
|
|
|
|
|
fastify.post(
|
|
|
|
|
"/users/login",
|
|
|
|
|
{
|
|
|
|
|
schema: {
|
|
|
|
|
body: Type.Object({
|
|
|
|
|
login: Type.String(),
|
|
|
|
|
password: Type.String(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
async (request, reply) => {
|
|
|
|
|
const { login, password } = request.body;
|
|
|
|
|
const user = await db.verifyUser(database, login);
|
|
|
|
|
|
|
|
|
|
if (user === null || !(await bcrypt.compare(password, user.password))) {
|
|
|
|
|
reply.send({ success: false });
|
|
|
|
|
} else {
|
|
|
|
|
request.session.userKey = generateId();
|
|
|
|
|
reply.send({ success: true });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bcrypt.compare(password, user!.password)
|
|
|
|
|
.then(res => reply.send({ sucess: res }))
|
|
|
|
|
.catch(err => reply.send({ sucess: false }));
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
/* Route pour se déconnecter */
|
|
|
|
|
fastify.get("/users/logout", async (request, reply) => {
|
|
|
|
|
console.log(request.session.userKey);
|
|
|
|
|
request.session.destroy();
|
|
|
|
|
reply.send({ success: true });
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/* Route pour mettre à jour le login d'un utilisateur */
|
|
|
|
|
fastify.put(
|
|
|
|
|
"/users/:id/login",
|
|
|
|
@ -194,7 +274,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 */
|
|
|
|
@ -215,9 +295,13 @@ fastify.put(
|
|
|
|
|
async (request, reply) => {
|
|
|
|
|
const { id } = request.params;
|
|
|
|
|
const { newPassword } = request.body;
|
|
|
|
|
db.updateUserPassword(database, id, newPassword);
|
|
|
|
|
|
|
|
|
|
const saltRounds = 10;
|
|
|
|
|
const hashedPassword = await bcrypt.hash(newPassword, saltRounds);
|
|
|
|
|
|
|
|
|
|
await db.updateUserPassword(database, id, hashedPassword);
|
|
|
|
|
reply.send({ success: true });
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
/* Route pour mettre à jour les permissions d'un utilisateur */
|
|
|
|
@ -240,7 +324,7 @@ fastify.put(
|
|
|
|
|
const { newPermissions } = request.body;
|
|
|
|
|
await db.updateUserPermissions(database, id, newPermissions);
|
|
|
|
|
reply.send({ success: true });
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
/* Route pour mettre à jour l'email d'un utilisateur */
|
|
|
|
@ -289,7 +373,7 @@ fastify.delete(
|
|
|
|
|
const { id } = request.params;
|
|
|
|
|
await db.deleteUserById(database, id);
|
|
|
|
|
reply.send({ success: true });
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
/* Route pour supprimer un utilisateur par son login */
|
|
|
|
@ -306,7 +390,7 @@ fastify.delete(
|
|
|
|
|
const { login } = request.params;
|
|
|
|
|
await db.deleteUserByLogin(database, login);
|
|
|
|
|
reply.send({ success: true });
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
/* Route pour supprimer tous les utilisateurs */
|
|
|
|
@ -335,9 +419,11 @@ fastify.get(
|
|
|
|
|
},
|
|
|
|
|
async (request, reply) => {
|
|
|
|
|
const { id } = request.params;
|
|
|
|
|
const user = await db.selectUserById(database, id);
|
|
|
|
|
reply.send(user);
|
|
|
|
|
},
|
|
|
|
|
if (request.session.userKey) {
|
|
|
|
|
const user = await db.selectUserById(database, id);
|
|
|
|
|
reply.send(user);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
/* Route pour récupérer un utilisateur par son login */
|
|
|
|
@ -354,7 +440,7 @@ fastify.get(
|
|
|
|
|
const { login } = request.params;
|
|
|
|
|
const user = await db.selectUserByLogin(database, login);
|
|
|
|
|
reply.send(user);
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
/* Route pour créer un work */
|
|
|
|
@ -375,7 +461,7 @@ fastify.post(
|
|
|
|
|
const { id_user, link, language, title, code } = request.body;
|
|
|
|
|
await db.insertWork(database, link, id_user, language, title, code);
|
|
|
|
|
reply.send({ success: true });
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
/* Route pour récupérer tous les works */
|
|
|
|
@ -406,7 +492,7 @@ fastify.delete(
|
|
|
|
|
const { id } = request.params;
|
|
|
|
|
db.deleteWork(database, id);
|
|
|
|
|
reply.send({ success: true });
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
/* Route pour récupérer un work par son Link */
|
|
|
|
@ -445,55 +531,6 @@ 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;
|
|
|
|
|
await 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(),
|
|
|
|
|
language: Type.String(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
async (request, reply) => {
|
|
|
|
|
const { id } = request.params;
|
|
|
|
|
const { newContent, language } = request.body;
|
|
|
|
|
await db.updateWorkContent(database, id, newContent, language);
|
|
|
|
|
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) {
|
|
|
|
@ -531,4 +568,4 @@ async function forwardOutput() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Lancer le serveur et la fonction forwardOutput sur le même thread en parallèle */
|
|
|
|
|
await Promise.all([fastify.listen({ port: 3000 }), forwardOutput()]);
|
|
|
|
|
await Promise.all([fastify.listen({ port: 3000, host: '0.0.0.0' }), forwardOutput()]);
|
|
|
|
|