|
|
|
@ -1,9 +1,25 @@
|
|
|
|
|
import cors from '@fastify/cors';
|
|
|
|
|
import { Type, TypeBoxTypeProvider } from '@fastify/type-provider-typebox';
|
|
|
|
|
import Fastify, { FastifyReply } from 'fastify';
|
|
|
|
|
import { nanoid } from 'nanoid';
|
|
|
|
|
import { allocateBuffer, IMAGES } from 'runner';
|
|
|
|
|
import { Pull, Push } from 'zeromq';
|
|
|
|
|
import cors from "@fastify/cors";
|
|
|
|
|
import { Type, TypeBoxTypeProvider } from "@fastify/type-provider-typebox";
|
|
|
|
|
import Fastify, { FastifyReply, FastifyRequest } from "fastify";
|
|
|
|
|
import { nanoid } from "nanoid";
|
|
|
|
|
import { allocateBuffer, IMAGES } from "runner";
|
|
|
|
|
import { Pull, Push } from "zeromq";
|
|
|
|
|
|
|
|
|
|
import {
|
|
|
|
|
createDbDirectory,
|
|
|
|
|
openDatabase,
|
|
|
|
|
closeDatabase,
|
|
|
|
|
createRegisteredUserTable,
|
|
|
|
|
insertUser,
|
|
|
|
|
updateUserLogin,
|
|
|
|
|
updateUserPassword,
|
|
|
|
|
updateUserPermissions,
|
|
|
|
|
deleteUser,
|
|
|
|
|
deleteAllUsers,
|
|
|
|
|
selectAllUsers,
|
|
|
|
|
selectUserByLogin,
|
|
|
|
|
selectUserById,
|
|
|
|
|
} from "./database";
|
|
|
|
|
|
|
|
|
|
const sender = new Push();
|
|
|
|
|
await sender.bind(`tcp://127.0.0.1:5557`);
|
|
|
|
@ -17,37 +33,143 @@ const fastify = Fastify({
|
|
|
|
|
logger: true,
|
|
|
|
|
}).withTypeProvider<TypeBoxTypeProvider>();
|
|
|
|
|
await fastify.register(cors, {
|
|
|
|
|
origin: process.env.ALLOW_ORIGIN || '*',
|
|
|
|
|
origin: process.env.ALLOW_ORIGIN || "*",
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
fastify.post('/run', {
|
|
|
|
|
schema: {
|
|
|
|
|
body: Type.Object({
|
|
|
|
|
code: Type.String(),
|
|
|
|
|
language: Type.String(),
|
|
|
|
|
}),
|
|
|
|
|
// Code runner in a container
|
|
|
|
|
|
|
|
|
|
// POST /run : Run code in a container
|
|
|
|
|
|
|
|
|
|
fastify.post(
|
|
|
|
|
"/run",
|
|
|
|
|
{
|
|
|
|
|
schema: {
|
|
|
|
|
body: Type.Object({
|
|
|
|
|
code: Type.String(),
|
|
|
|
|
language: Type.String(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}, (req, reply) => {
|
|
|
|
|
const { code, language } = req.body;
|
|
|
|
|
const jobId = generateId();
|
|
|
|
|
const buffer = allocateBuffer(jobId, code, IMAGES.moshell);
|
|
|
|
|
reply.raw.writeHead(200, {
|
|
|
|
|
'Content-Type': 'text/event-stream',
|
|
|
|
|
Connection: 'keep-alive',
|
|
|
|
|
'Cache-Control': 'no-cache',
|
|
|
|
|
'Access-Control-Allow-Origin': process.env.ALLOW_ORIGIN || '*',
|
|
|
|
|
});
|
|
|
|
|
reply.raw.on('close', () => {
|
|
|
|
|
delete clients[jobId];
|
|
|
|
|
});
|
|
|
|
|
sender.send(buffer).then(() => {
|
|
|
|
|
reply.raw.write('event: connected\n');
|
|
|
|
|
reply.raw.write(`data: ${jobId}\n`);
|
|
|
|
|
reply.raw.write('id: 0\n\n');
|
|
|
|
|
});
|
|
|
|
|
clients[jobId] = reply;
|
|
|
|
|
(req, reply) => {
|
|
|
|
|
const { code, language } = req.body;
|
|
|
|
|
const jobId = generateId();
|
|
|
|
|
const buffer = allocateBuffer(jobId, code, IMAGES.moshell);
|
|
|
|
|
reply.raw.writeHead(200, {
|
|
|
|
|
"Content-Type": "text/event-stream",
|
|
|
|
|
Connection: "keep-alive",
|
|
|
|
|
"Cache-Control": "no-cache",
|
|
|
|
|
"Access-Control-Allow-Origin": process.env.ALLOW_ORIGIN || "*",
|
|
|
|
|
});
|
|
|
|
|
reply.raw.on("close", () => {
|
|
|
|
|
delete clients[jobId];
|
|
|
|
|
});
|
|
|
|
|
sender.send(buffer).then(() => {
|
|
|
|
|
reply.raw.write("event: connected\n");
|
|
|
|
|
reply.raw.write(`data: ${jobId}\n`);
|
|
|
|
|
reply.raw.write("id: 0\n\n");
|
|
|
|
|
});
|
|
|
|
|
clients[jobId] = reply;
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Database
|
|
|
|
|
|
|
|
|
|
// Création du répertoire de la base de données s'il n'existe pas
|
|
|
|
|
createDbDirectory();
|
|
|
|
|
|
|
|
|
|
// Ouvrir la base de données
|
|
|
|
|
const db = openDatabase();
|
|
|
|
|
|
|
|
|
|
// Créer la table registered_user si elle n'existe pas
|
|
|
|
|
createRegisteredUserTable(db);
|
|
|
|
|
|
|
|
|
|
// Route pour créer un utilisateur
|
|
|
|
|
fastify.post<{
|
|
|
|
|
Body: {
|
|
|
|
|
login: string;
|
|
|
|
|
password: string;
|
|
|
|
|
permissions: number;
|
|
|
|
|
};
|
|
|
|
|
}>("/users", async (request, reply) => {
|
|
|
|
|
const { login, password, permissions } = request.body;
|
|
|
|
|
insertUser(db, login, password, permissions);
|
|
|
|
|
reply.send({ success: true });
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Route pour mettre à jour le login d'un utilisateur
|
|
|
|
|
fastify.put<{
|
|
|
|
|
Params: { id: string };
|
|
|
|
|
Body: { newLogin: string };
|
|
|
|
|
}>("/users/:id/login", async (request, reply) => {
|
|
|
|
|
const { id } = request.params;
|
|
|
|
|
const { newLogin } = request.body;
|
|
|
|
|
updateUserLogin(db, parseInt(id), newLogin);
|
|
|
|
|
reply.send({ success: true });
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Route pour mettre à jour le mot de passe d'un utilisateur
|
|
|
|
|
fastify.put<{
|
|
|
|
|
Params: { id: string };
|
|
|
|
|
Body: { newPassword: string };
|
|
|
|
|
}>("/users/:id/password", async (request, reply) => {
|
|
|
|
|
const { id } = request.params;
|
|
|
|
|
const { newPassword } = request.body;
|
|
|
|
|
updateUserPassword(db, parseInt(id), newPassword);
|
|
|
|
|
reply.send({ success: true });
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Route pour mettre à jour les permissions d'un utilisateur
|
|
|
|
|
fastify.put<{
|
|
|
|
|
Params: { id: string };
|
|
|
|
|
Body: { newPermissions: number };
|
|
|
|
|
}>("/users/:id/permissions", async (request, reply) => {
|
|
|
|
|
const { id } = request.params;
|
|
|
|
|
const { newPermissions } = request.body;
|
|
|
|
|
updateUserPermissions(db, parseInt(id), newPermissions);
|
|
|
|
|
reply.send({ success: true });
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Route pour supprimer un utilisateur
|
|
|
|
|
fastify.delete<{
|
|
|
|
|
Params: { id: string };
|
|
|
|
|
}>("/users/:id", async (request, reply) => {
|
|
|
|
|
const { id } = request.params;
|
|
|
|
|
deleteUser(db, parseInt(id));
|
|
|
|
|
reply.send({ success: true });
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Route pour supprimer tous les utilisateurs
|
|
|
|
|
fastify.delete("/users", async (request, reply) => {
|
|
|
|
|
deleteAllUsers(db);
|
|
|
|
|
reply.send({ success: true });
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Route pour récupérer tous les utilisateurs
|
|
|
|
|
fastify.get("/users", async (request, reply) => {
|
|
|
|
|
const users = selectAllUsers(db);
|
|
|
|
|
return users;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Route pour récupérer un utilisateur par son ID
|
|
|
|
|
fastify.get<{
|
|
|
|
|
Params: { id: string };
|
|
|
|
|
}>("/users/:id", async (request, reply) => {
|
|
|
|
|
const { id } = request.params;
|
|
|
|
|
const user = selectUserById(db, parseInt(id));
|
|
|
|
|
return user;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Route pour récupérer un utilisateur par son login
|
|
|
|
|
fastify.get<{
|
|
|
|
|
Params: { login: string };
|
|
|
|
|
}>("/users/login/:login", async (request, reply) => {
|
|
|
|
|
const { login } = request.params;
|
|
|
|
|
const user = selectUserByLogin(db, login);
|
|
|
|
|
return user;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Forward output from the runner to the client
|
|
|
|
|
|
|
|
|
|
async function forwardOutput() {
|
|
|
|
|
for await (const [buff] of receiver) {
|
|
|
|
|
const messageType = buff.readInt8();
|
|
|
|
@ -62,23 +184,23 @@ async function forwardOutput() {
|
|
|
|
|
case 1:
|
|
|
|
|
case 2:
|
|
|
|
|
const text = encodeURIComponent(buff.subarray(33).toString());
|
|
|
|
|
raw.write('event: message\n');
|
|
|
|
|
raw.write("event: message\n");
|
|
|
|
|
if (messageType === 1) {
|
|
|
|
|
raw.write('id: stdout\n');
|
|
|
|
|
raw.write("id: stdout\n");
|
|
|
|
|
} else {
|
|
|
|
|
raw.write('id: stderr\n');
|
|
|
|
|
raw.write("id: stderr\n");
|
|
|
|
|
}
|
|
|
|
|
raw.write(`data: ${text}\n\n`);
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
const exitCode = buff.readUint32BE(33);
|
|
|
|
|
raw.write('event: message\n');
|
|
|
|
|
raw.write('id: exit\n');
|
|
|
|
|
raw.write("event: message\n");
|
|
|
|
|
raw.write("id: exit\n");
|
|
|
|
|
raw.write(`data: ${exitCode}\n\n`);
|
|
|
|
|
raw.end();
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
console.error('Unknown message type', messageType);
|
|
|
|
|
console.error("Unknown message type", messageType);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|