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