|
|
@ -1,14 +1,14 @@
|
|
|
|
import cors from "@fastify/cors";
|
|
|
|
import cors from "@fastify/cors";
|
|
|
|
|
|
|
|
import websocket, { WebSocket } from '@fastify/websocket';
|
|
|
|
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 { ChangeSet, Text } from "@codemirror/state";
|
|
|
|
|
|
|
|
import { Update, rebaseUpdates } from "@codemirror/collab";
|
|
|
|
import * as db from "./database";
|
|
|
|
import * as db from "./database";
|
|
|
|
|
|
|
|
|
|
|
|
console.log(IMAGES.logo);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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`);
|
|
|
|
const receiver = new Pull();
|
|
|
|
const receiver = new Pull();
|
|
|
@ -17,14 +17,85 @@ await receiver.bind(`tcp://127.0.0.1:5558`);
|
|
|
|
const clients: Record<string, FastifyReply> = {};
|
|
|
|
const clients: Record<string, FastifyReply> = {};
|
|
|
|
const generateId = () => nanoid(32);
|
|
|
|
const generateId = () => nanoid(32);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let updates: Update[] = [];
|
|
|
|
|
|
|
|
let doc = Text.of(['']);
|
|
|
|
|
|
|
|
const liveClients: WebSocket[] = [];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function send(socket: WebSocket, requestId: number, payload: unknown) {
|
|
|
|
|
|
|
|
const response = {
|
|
|
|
|
|
|
|
_request: requestId,
|
|
|
|
|
|
|
|
payload
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
socket.send(JSON.stringify(response));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const fastify = Fastify({
|
|
|
|
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 || '*',
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
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()})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fastify.post("/run", {
|
|
|
|
|
|
|
|
schema: {
|
|
|
|
|
|
|
|
body: Type.Object({
|
|
|
|
|
|
|
|
code: Type.String(),
|
|
|
|
|
|
|
|
language: Type.String(),
|
|
|
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
}, (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, 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 || "*",
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
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 */
|
|
|
|
/* Création du répertoire de la base de données s'il n'existe pas */
|
|
|
|
db.createDbDirectory();
|
|
|
|
db.createDbDirectory();
|
|
|
|