Skip to content

Commit

Permalink
add guide to host the backend
Browse files Browse the repository at this point in the history
  • Loading branch information
YieldRay committed Jun 14, 2024
1 parent 45502d3 commit 0d0b5a4
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 33 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,12 @@ Thanks to the following libraries for making this project possible:
- [`xterm.js`](https://github.com/xtermjs/xterm.js): The terminal emulator

Of course, this project also relies on other libraries, which are listed in the package.json file.

## Hosting the Backend

The backend is designed for running untrusted code, so please ensure that security is thoroughly considered.
For example, you may run it in a sandbox environment like Docker.

```sh
deno run -A https://raw.githubusercontent.com/YieldRay/deno-playground/main/backend/main.ts
```
67 changes: 37 additions & 30 deletions backend/main.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,45 @@
/**
* This script should run in a virtual environment,
* as it execute by: deno run `-A`
* This script should run in a virtual (sandbox) environment,
* as it runs untrusted code and execute with: deno run -A main.ts
*/

import { encodeBase64 } from 'jsr:@std/encoding/base64';
import ServeRouter from 'https://esm.sh/serve-router@1';
import ServeRouter from 'https://esm.sh/serve-router@1.1.0';

const PORT = Number(Deno.env.get('PORT')) || 8000;
const TIMEOUT = 10 * 1000;

const app = ServeRouter();
app.all(
'/',
() =>
new Response(`Usage:
app.all('/', () => new Response(`Usage:\n\nPOST /event-stream\ncode`));
POST /event-stream
{code}
GET /event-stream?code={code}
`)
);
app.all('/event-stream', async (req) => {
const code =
req.method === 'GET' ? new URL(req.url).searchParams.get('code') || '' : await req.text();

let child: Deno.ChildProcess;
const body = new ReadableStream({
async start(controller) {
/** https://developer.mozilla.org/docs/Web/API/Server-sent_events */
const send = (event: string, data: string) =>
controller.enqueue(new TextEncoder().encode(`event: ${event}\ndata: ${data}\n\n`));

// [events]
// ready : string
// stdout: base64
// stderr: base64
// exit : string

try {
const filepath = await Deno.makeTempFile({ prefix: 'temp_run' });
const filepath = await Deno.makeTempFile({ prefix: 'deno_playground_' });
await Deno.writeTextFile(filepath, code);
console.log({ date: new Date(), filepath, content: code });

Expand Down Expand Up @@ -54,39 +73,26 @@ app.all('/event-stream', async (req) => {
});

child = cmd.spawn();
send('ready', filepath);

controller.enqueue(new TextEncoder().encode(`event: ready\ndata: ${filepath}\n\n`));
// pipe both stdout stderr
await Promise.all<void>([
(async () => {
for await (const chunk of child.stdout) {
controller.enqueue(
new TextEncoder().encode(`event: stdout\ndata: ${encodeBase64(chunk)}\n\n`)
);
}
for await (const chunk of child.stdout) send('stdout', encodeBase64(chunk));
})(),
(async () => {
for await (const chunk of child.stderr) {
controller.enqueue(
new TextEncoder().encode(`event: stderr\ndata: ${encodeBase64(chunk)}\n\n`)
);
}
for await (const chunk of child.stderr) send('stderr', encodeBase64(chunk));
})()
]);

/**
* note that the `exit` event does NOT encode base64, just normal string
*/
const status = await child.status;
controller.enqueue(
new TextEncoder().encode(
`event: exit\ndata: ${
status.success
? `Normal program termination. Exit status: ${status.code}`
: `Exit status: ${status.code}`
}\n\n`
)
send(
'exit',
status.success
? `Normal program termination. Exit status: ${status.code}`
: `Exit status: ${status.code}`
);

console.log({
date: new Date(),
filepath,
Expand All @@ -95,13 +101,14 @@ app.all('/event-stream', async (req) => {
});
} catch (e) {
console.error(e);
controller.enqueue(new TextEncoder().encode(`event: exit\ndata: Interrupted\n\n`));
send('exit', 'Interrupted');
} finally {
controller.close();
}
},
cancel() {
child?.kill();
// force the process to kill
child?.kill('SIGKILL');
}
});

Expand All @@ -117,5 +124,5 @@ app.all('/event-stream', async (req) => {

Deno.serve({
handler: app.fetch,
port: Number(Deno.env.get('PORT')) || undefined
port: PORT
});
13 changes: 10 additions & 3 deletions src/lib/components/Playground.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
const spinner = ['|', '/', '-', '\\'];
let i = 0;
let isSpinning = true;
const timeout = setInterval(() => {
xterm.write('\r' + spinner[i]);
i = (i + 1) % spinner.length;
Expand All @@ -96,17 +97,23 @@
xterm.write('\x1b[2J\x1b[H');
};
clear();
const stop = () => {
if (isSpinning) {
clearInterval(timeout);
clear();
isSpinning = false;
}
};
const emitter = run(me.getValue());
emitter.on('exit', (s) => {
clearInterval(timeout);
stop();
statusText = s;
(emitter as Emitter<any>).off('*');
});
emitter.on('ready', () => {
clearInterval(timeout);
clear();
stop();
statusText = 'Running...';
emitter.on('stdout', (s) => xterm.write(s));
emitter.on('stderr', (s) => xterm.write(s));
Expand Down

0 comments on commit 0d0b5a4

Please sign in to comment.