Merge branch 'main' into sqlite3
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details

pull/2/head
Clément FRÉVILLE 11 months ago
commit 490c9188f5

@ -1,7 +1,7 @@
import sqlite3 from "sqlite3";
import fs from "fs";
import fs from 'fs';
import sqlite3 from 'sqlite3';
const dbDirectory = "./src/db";
const dbDirectory = './src/db';
const dbFilePath = `${dbDirectory}/database.db`;
/* Fonction pour exécuter une requête sur la base de données */
@ -10,7 +10,7 @@ const dbFilePath = `${dbDirectory}/database.db`;
function runDB(
db: sqlite3.Database,
query: string,
params: any[]
params: any[],
): Promise<void> {
return new Promise((resolve, reject) => {
db.run(query, params, (err) => {
@ -40,7 +40,7 @@ function allDB(db: sqlite3.Database, query: string): Promise<any[]> {
function getDB(
db: sqlite3.Database,
query: string,
params: any[]
params: any[],
): Promise<void> {
return new Promise((resolve, reject) => {
db.get(query, params, (err, row: any) => {
@ -64,13 +64,13 @@ function createDbDirectory() {
/* Ouvrir la base de données */
function openDatabase() {
console.log("Ouverture de la connexion à la base de données.");
console.log('Ouverture de la connexion à la base de données.');
return new sqlite3.Database(
dbFilePath,
sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE,
(err: Error | null) => {
if (err) console.error(err.message);
}
},
);
}
@ -80,7 +80,7 @@ function closeDatabase(db: sqlite3.Database) {
if (err) {
console.error(err.message);
}
console.log("Fermeture de la connexion à la base de données.");
console.log('Fermeture de la connexion à la base de données.');
});
}
@ -102,7 +102,8 @@ function createTables(db: sqlite3.Database) {
/* Créer la table registered_user dans la base de données */
function createRegisteredUserTable(db: sqlite3.Database): Promise<void> {
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, []);
}
@ -112,7 +113,7 @@ function insertUser(
db: sqlite3.Database,
login: string,
password: string,
permissions: number
permissions: number,
) {
const insertUserQuery = `INSERT INTO registered_user (login, password, permissions) VALUES (?, ?, ?)`;
@ -130,7 +131,7 @@ function updateUserLogin(db: sqlite3.Database, id: number, newLogin: string) {
function updateUserPassword(
db: sqlite3.Database,
id: number,
newPassword: string
newPassword: string,
) {
const updateUserPasswordQuery = `UPDATE registered_user SET password = ? WHERE id_user = ?`;
@ -141,7 +142,7 @@ function updateUserPassword(
function updateUserPermissions(
db: sqlite3.Database,
id: number,
newPermissions: number
newPermissions: number,
) {
const updateUserPermissionsQuery = `UPDATE registered_user SET permissions = ? WHERE id_user = ?`;
@ -199,7 +200,8 @@ function selectUserById(db: sqlite3.Database, id: number) {
/* Créer la table language dans la base de données */
function createLanguageTable(db: sqlite3.Database): Promise<void> {
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, []);
}
@ -207,7 +209,7 @@ function createLanguageTable(db: sqlite3.Database): Promise<void> {
function insertLanguage(
db: sqlite3.Database,
designation: string,
version: number
version: number,
) {
const insertLanguageQuery = `INSERT INTO language (designation, version) VALUES (?, ?)`;
@ -218,7 +220,7 @@ function insertLanguage(
function updateLanguageDesignation(
db: sqlite3.Database,
id: number,
newDesignation: string
newDesignation: string,
) {
const updateLanguageDesignationQuery = `UPDATE language SET designation = ? WHERE id_language = ?`;
@ -229,7 +231,7 @@ function updateLanguageDesignation(
function updateLanguageVersion(
db: sqlite3.Database,
id: number,
newVersion: number
newVersion: number,
) {
const updateLanguageVersionQuery = `UPDATE language SET version = ? WHERE id_language = ?`;
@ -275,7 +277,8 @@ function selectLanguageById(db: sqlite3.Database, id: number) {
/* Créer la table work dans la base de données */
function createWorkTable(db: sqlite3.Database): Promise<void> {
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, []);
}
@ -285,7 +288,7 @@ 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 (?, ?, ?, ?)`;
@ -322,30 +325,30 @@ function selectWorkById(db: sqlite3.Database, id: number) {
/////////////////////////// Export des fonctions ///////////////////////////
export {
createDbDirectory,
openDatabase,
closeDatabase,
createDbDirectory,
createTables,
insertUser,
updateUserLogin,
updateUserPassword,
updateUserPermissions,
deleteAllLanguages,
deleteAllUsers,
deleteAllWorks,
deleteLanguage,
deleteUserById,
deleteUserByLogin,
deleteAllUsers,
selectAllUsers,
selectUserByLogin,
selectUserById,
deleteWork,
insertLanguage,
updateLanguageDesignation,
updateLanguageVersion,
deleteLanguage,
deleteAllLanguages,
selectAllLanguages,
selectLanguageById,
insertUser,
insertWork,
openDatabase,
selectAllLanguages,
selectAllUsers,
selectAllWorks,
deleteAllWorks,
deleteWork,
selectLanguageById,
selectUserById,
selectUserByLogin,
selectWorkById,
updateLanguageDesignation,
updateLanguageVersion,
updateUserLogin,
updateUserPassword,
updateUserPermissions,
};

@ -1,5 +1,8 @@
export const IMAGES = {
moshell: 'ghcr.io/moshell-lang/moshell:master',
export const RUNNERS = ['bash', 'moshell', 'bun', 'typescript'] as const;
const ALLOWED_LANGUAGES = new Set<string>(RUNNERS);
const aliases: Record<string, typeof RUNNERS[number]> = {
'JavaScript': 'bun',
'TypeScript': 'typescript',
};
/**
@ -20,3 +23,11 @@ export function allocateBuffer(jobId: string, code: string, image: string): Buff
buffer.write(code, cur);
return buffer;
}
export function getRunner(language: string): typeof RUNNERS[number] | null {
language = aliases[language] || language;
if (ALLOWED_LANGUAGES.has(language)) {
return language as typeof RUNNERS[number];
}
return null;
}

@ -1,38 +1,37 @@
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 cors from '@fastify/cors';
import { Type, TypeBoxTypeProvider } from '@fastify/type-provider-typebox';
import Fastify, { FastifyReply } from 'fastify';
import { nanoid } from 'nanoid';
import { allocateBuffer, getRunner } from 'runner';
import { Pull, Push } from 'zeromq';
import {
createDbDirectory,
openDatabase,
closeDatabase,
createDbDirectory,
createTables,
insertUser,
updateUserLogin,
updateUserPassword,
updateUserPermissions,
deleteAllLanguages,
deleteAllUsers,
deleteAllWorks,
deleteLanguage,
deleteUserById,
deleteUserByLogin,
deleteAllUsers,
selectAllUsers,
selectUserByLogin,
selectUserById,
deleteWork,
insertLanguage,
updateLanguageDesignation,
updateLanguageVersion,
deleteLanguage,
deleteAllLanguages,
selectAllLanguages,
selectLanguageById,
insertUser,
insertWork,
openDatabase,
selectAllLanguages,
selectAllUsers,
selectAllWorks,
deleteAllWorks,
deleteWork,
selectLanguageById,
selectUserById,
selectUserByLogin,
selectWorkById,
} from "./database";
updateLanguageDesignation,
updateLanguageVersion,
updateUserLogin,
updateUserPassword,
updateUserPermissions,
} from './database';
const sender = new Push();
await sender.bind(`tcp://127.0.0.1:5557`);
@ -46,15 +45,35 @@ const fastify = Fastify({
logger: true,
}).withTypeProvider<TypeBoxTypeProvider>();
await fastify.register(cors, {
origin: process.env.ALLOW_ORIGIN || "*",
origin: process.env.ALLOW_ORIGIN || '*',
});
/* Code runner in a container */
/* 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();
/* POST /run : Run code in a container */
/* Créer les tables si elles n'existent pas */
createTables(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 });
});
fastify.post(
"/run",
'/run',
{
schema: {
body: Type.Object({
@ -65,55 +84,35 @@ fastify.post(
},
(req, reply) => {
const { code, language } = req.body;
const runner = getRunner(language);
if (runner === null) {
return reply.status(422).send({ error: 'Invalid language' });
}
const jobId = generateId();
const buffer = allocateBuffer(jobId, code, IMAGES.moshell);
const buffer = allocateBuffer(jobId, code, runner);
reply.raw.writeHead(200, {
"Content-Type": "text/event-stream",
Connection: "keep-alive",
"Cache-Control": "no-cache",
"Access-Control-Allow-Origin": process.env.ALLOW_ORIGIN || "*",
'Content-Type': 'text/event-stream',
Connection: 'keep-alive',
'Cache-Control': 'no-cache',
'Access-Control-Allow-Origin': process.env.ALLOW_ORIGIN || '*',
});
reply.raw.on("close", () => {
reply.raw.on('close', () => {
delete clients[jobId];
});
sender.send(buffer).then(() => {
reply.raw.write("event: connected\n");
reply.raw.write('event: connected\n');
reply.raw.write(`data: ${jobId}\n`);
reply.raw.write("id: 0\n\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 les tables si elles n'existent pas */
createTables(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) => {
}>('/users/:id/login', async (request, reply) => {
const { id } = request.params;
const { newLogin } = request.body;
updateUserLogin(db, parseInt(id), newLogin);
@ -124,7 +123,7 @@ fastify.put<{
fastify.put<{
Params: { id: string };
Body: { newPassword: string };
}>("/users/:id/password", async (request, reply) => {
}>('/users/:id/password', async (request, reply) => {
const { id } = request.params;
const { newPassword } = request.body;
await updateUserPassword(db, parseInt(id), newPassword);
@ -135,7 +134,7 @@ fastify.put<{
fastify.put<{
Params: { id: string };
Body: { newPermissions: number };
}>("/users/:id/permissions", async (request, reply) => {
}>('/users/:id/permissions', async (request, reply) => {
const { id } = request.params;
const { newPermissions } = request.body;
await updateUserPermissions(db, parseInt(id), newPermissions);
@ -145,7 +144,7 @@ fastify.put<{
/* Route pour supprimer un utilisateur par son ID */
fastify.delete<{
Params: { id: string };
}>("/users/:id", async (request, reply) => {
}>('/users/:id', async (request, reply) => {
const { id } = request.params;
await deleteUserById(db, parseInt(id));
reply.send({ success: true });
@ -154,20 +153,20 @@ fastify.delete<{
/* Route pour supprimer un utilisateur par son login */
fastify.delete<{
Params: { login: string };
}>("/users/login/:login", async (request, reply) => {
}>('/users/login/:login', async (request, reply) => {
const { login } = request.params;
await deleteUserByLogin(db, login);
reply.send({ success: true });
});
/* Route pour supprimer tous les utilisateurs */
fastify.delete("/users", async (request, reply) => {
fastify.delete('/users', async (request, reply) => {
await deleteAllUsers(db);
reply.send({ success: true });
});
/* Route pour récupérer tous les utilisateurs */
fastify.get("/users", async (request, reply) => {
fastify.get('/users', async (request, reply) => {
const users = await selectAllUsers(db);
reply.send(users);
});
@ -175,7 +174,7 @@ fastify.get("/users", async (request, reply) => {
/* Route pour récupérer un utilisateur par son ID */
fastify.get<{
Params: { id: string };
}>("/users/:id", async (request, reply) => {
}>('/users/:id', async (request, reply) => {
const { id } = request.params;
const user = await selectUserById(db, parseInt(id));
reply.send(user);
@ -184,7 +183,7 @@ fastify.get<{
/* Route pour récupérer un utilisateur par son login */
fastify.get<{
Params: { login: string };
}>("/users/login/:login", async (request, reply) => {
}>('/users/login/:login', async (request, reply) => {
const { login } = request.params;
const user = await selectUserByLogin(db, login);
reply.send(user);
@ -196,7 +195,7 @@ fastify.post<{
designation: string;
version: string;
};
}>("/languages", async (request, reply) => {
}>('/languages', async (request, reply) => {
const { designation, version } = request.body;
insertLanguage(db, designation, parseInt(version));
reply.send({ success: true });
@ -206,7 +205,7 @@ fastify.post<{
fastify.put<{
Params: { id: string };
Body: { newDesignation: string };
}>("/languages/:id/designation", async (request, reply) => {
}>('/languages/:id/designation', async (request, reply) => {
const { id } = request.params;
const { newDesignation } = request.body;
updateLanguageDesignation(db, parseInt(id), newDesignation);
@ -217,7 +216,7 @@ fastify.put<{
fastify.put<{
Params: { id: string };
Body: { newVersion: number };
}>("/languages/:id/version", async (request, reply) => {
}>('/languages/:id/version', async (request, reply) => {
const { id } = request.params;
const { newVersion } = request.body;
updateLanguageVersion(db, parseInt(id), newVersion);
@ -227,14 +226,14 @@ fastify.put<{
/* Route pour supprimer un language */
fastify.delete<{
Params: { id: string };
}>("/languages/:id", async (request, reply) => {
}>('/languages/:id', async (request, reply) => {
const { id } = request.params;
deleteLanguage(db, parseInt(id));
reply.send({ success: true });
});
/* Route pour supprimer tous les languages */
fastify.delete("/languages", async (request, reply) => {
fastify.delete('/languages', async (request, reply) => {
deleteAllLanguages(db);
reply.send({ success: true });
});
@ -242,14 +241,14 @@ fastify.delete("/languages", async (request, reply) => {
/* Route pour récupérer un language par son ID */
fastify.get<{
Params: { id: string };
}>("/languages/:id", async (request, reply) => {
}>('/languages/:id', async (request, reply) => {
const { id } = request.params;
const language = await selectLanguageById(db, parseInt(id));
reply.send(language);
});
/* Route pour récupérer tous les languages */
fastify.get("/languages", async (request, reply) => {
fastify.get('/languages', async (request, reply) => {
const languages = await selectAllLanguages(db);
reply.send(languages);
});
@ -262,20 +261,20 @@ fastify.post<{
id_language: number;
code: string;
};
}>("/works", async (request, reply) => {
}>('/works', async (request, reply) => {
const { id_user, link, id_language, code } = request.body;
insertWork(db, link, id_user, id_language, code);
reply.send({ success: true });
});
/* Route pour récupérer tous les works */
fastify.get("/works", async (request, reply) => {
fastify.get('/works', async (request, reply) => {
const works = await selectAllWorks(db);
reply.send(works);
});
/* Route pour supprimer tous les works */
fastify.delete("/works", async (request, reply) => {
fastify.delete('/works', async (request, reply) => {
deleteAllWorks(db);
reply.send({ success: true });
});
@ -283,7 +282,7 @@ fastify.delete("/works", async (request, reply) => {
/* Route pour supprimer un work par son ID */
fastify.delete<{
Params: { id: string };
}>("/works/:id", async (request, reply) => {
}>('/works/:id', async (request, reply) => {
const { id } = request.params;
deleteWork(db, parseInt(id));
reply.send({ success: true });
@ -292,7 +291,7 @@ fastify.delete<{
/* Route pour récupérer un work par son ID */
fastify.get<{
Params: { id: string };
}>("/works/:id", async (request, reply) => {
}>('/works/:id', async (request, reply) => {
const { id } = request.params;
const work = await selectWorkById(db, parseInt(id));
reply.send(work);
@ -313,23 +312,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);
}
}
}

Loading…
Cancel
Save