diff --git a/src/backend/src/modules/puterexec/Judge0Client.js b/src/backend/src/modules/puterexec/Judge0Client.js index 23ad4f66..15c2c556 100644 --- a/src/backend/src/modules/puterexec/Judge0Client.js +++ b/src/backend/src/modules/puterexec/Judge0Client.js @@ -12,6 +12,17 @@ class Judge0Client { return await this.get_('/about'); } + async create_submission ({ lang_id, code }) { + return await this.post_('/submissions', { + language_id: lang_id, + source_code: code, + }); + } + + async get_submission (id) { + return await this.get_(`/submissions/${id}`); + } + async get_ (path) { if ( ! path.startsWith('/') ) { path = `/${path}`; @@ -27,6 +38,24 @@ class Judge0Client { return resp.data; } + + async post_ (path, data) { + if ( ! path.startsWith('/') ) { + path = `/${path}`; + } + + const resp = await axios.request({ + method: 'POST', + url: `${this.baseURL}${path}`, + headers: { + Authorization: `Bearer ${this.token}`, + 'Content-Type': 'application/json', + }, + data, + }); + + return resp.data; + } } module.exports = { Judge0Client }; \ No newline at end of file diff --git a/src/backend/src/modules/puterexec/Judge0Service.js b/src/backend/src/modules/puterexec/Judge0Service.js index 872449d1..bbe0bc9f 100644 --- a/src/backend/src/modules/puterexec/Judge0Service.js +++ b/src/backend/src/modules/puterexec/Judge0Service.js @@ -1,33 +1,90 @@ +const putility = require("@heyputer/putility"); const BaseService = require("../../services/BaseService"); const { Judge0Client } = require("./Judge0Client"); +const { Context } = require("../../util/context"); class Judge0Service extends BaseService { _construct () { this.about_ = {}; } + async _init () { + this.languages = require('./languages/languages'); + this.client = new Judge0Client({ + token: this.config.token, + }); + + // this.submissions_ = {}; + } + static IMPLEMENTS = { ['puter-exec']: { async about () { return this.about ?? (this.about = await this.client.about()); }, async supported () { - return require('./languages/languages'); }, async exec ({ runtime, code }) { - return await this.exec_(runtime, code); + return await this.exec_({ runtime, code }); } } } - async _init () { - this.client = new Judge0Client({ - token: this.config.token, - }); - } + async exec_ ({ runtime, code }) { + const lang_id = (() => { + if ( runtime.startsWith('j0-') ) { + return runtime.slice(3); + } + let lang = this.languages.find((lang) => lang.language === runtime); + if ( lang ) { + return lang.judge0_id; + } + })(); - async exec_ (runtime, code) { - // + if ( !lang_id ) { + throw new Error(`Language or runtime not found: ${runtime}`); + } + + const result = await this.client.create_submission({ + lang_id, + code, + }); + + const submission_done = new putility.libs.promise.TeePromise(); + (async () => { + // Need to poll the submission until it's done + let poll = setInterval(async () => { + const submission = await this.client.get_submission(result.token); + if ( submission.status.id >= 3 ) { + clearInterval(poll); + // this.submissions_[result.id] = submission; + submission_done.resolve(submission); + } + }); + + // Wait for the submission to be done + const submission = await submission_done; + + this.log.noticeme('Submission done', submission); + + // Send event + const svc_event = this.services.get('event'); + svc_event.emit('puter-exec.submission.done', { + id: submission.token, + actor: Context.get('actor'), + output: submission.stdout, + summary: submission.message, + measures: { + time: submission.time, + memory: submission.memory, + }, + aux_outputs: { + compile: submission.compile_output, + }, + }); + })(); + + return result; } } diff --git a/src/backend/src/modules/puterexec/PuterExecModule.js b/src/backend/src/modules/puterexec/PuterExecModule.js index bd00eae3..3730a027 100644 --- a/src/backend/src/modules/puterexec/PuterExecModule.js +++ b/src/backend/src/modules/puterexec/PuterExecModule.js @@ -8,6 +8,11 @@ class PuterExecModule extends AdvancedBase { const ExecInterfaceService = require('./ExecInterfaceService'); services.registerService('__exec-interfaces', ExecInterfaceService); + + if ( !! config?.services?.['judge0'] ) { + const Judge0Service = require('./Judge0Service'); + services.registerService('judge0', Judge0Service); + } } }