Skip to content
This repository was archived by the owner on Mar 28, 2024. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ test_book/book/
# Ignore Vim temporary and swap files.
*.sw?
*~

node_modules
*-lock.json
build
44 changes: 41 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,48 @@

mdBook is a utility to create modern online books from Markdown files.

Check out the **[User Guide]** for a list of features and installation and usage information.
The User Guide also serves as a demonstration to showcase what a book looks like.
This is a modified version that lets you support a custom playground backend other than rust.
Its entirely a "bring your own backend" situation so in the end you are responsible to safeguard against what kind of code
is running on your backend.

If you are interested in contributing to the development of mdBook, check out the [Contribution Guide].
May or may not have some features broken due to mdBook being *VERY* hardcoded with rust in mind but should work just fine.

## Custom playground backend

For a custom playground backend you simply need a webserver with a `POST` route open. Call it whatever you like.

Then in the `.toml` config file of your book set the endpoint:
```toml
[output.html.playground]
editable = false # allows editing the source code
copyable = true # include the copy button for copying code snippets
copy-js = true # includes the JavaScript for the code editor
line-numbers = true # displays line numbers for editable code
runnable = true # displays a run button for rust code
endpoint = "http://localhost:4242/playground/" # send the code to this url for execution
hidden-str = "#" # since different languange use certain chars
```

A clients incoming request looks as follows:
```json
{
"lang": "cpp",
"code": "..."
}
```

[See supported languanges](/guide/src/format/theme/syntax-highlighting.md) for syntax highlighting. As well as the `lang` options for incoming client requests.


A servers outgoing response should look as follows:
```json
{
"result": "Request received!\n",
"error": null
}
```

The client will display the appropriate message depending on the server's response.

## License

Expand Down
3 changes: 3 additions & 0 deletions code-executor/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Code executor

TODO: Re-write this in RUST because yes, for now typescript is faster to implement.
21 changes: 21 additions & 0 deletions code-executor/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "c-playground",
"version": "1.0.0",
"description": "A simple server that takes a post request and executes the C code locally.",
"main": "webserv.js",
"scripts": {
"start": "node build/app.js",
"build": "./node_modules/typescript/bin/tsc"
},
"author": "W2Wizard",
"license": "MIT",
"dependencies": {
"cors": "^2.8.5",
"express": "^4.18.2"
},
"devDependencies": {
"@types/express": "^4.17.15",
"@types/node-fetch": "^2.6.2",
"typescript": "^4.9.4"
}
}
62 changes: 62 additions & 0 deletions code-executor/src/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// -----------------------------------------------------------------------------
// Codam Coding College, Amsterdam @ 2023.
// See README in the root project for more information.
// -----------------------------------------------------------------------------

import cors from "cors";
import express from "express";
import { Request, Response, NextFunction } from "express";
import { Execution } from "./executor";

// Globals
/*============================================================================*/

export const webserv = express();
export const port = 4242;

// Middleware
/*============================================================================*/

webserv.use(cors());
webserv.use(express.json());
webserv.use(express.urlencoded({ extended: true }));
webserv.use((err: any, req: Request, res: Response, next: NextFunction) => {
if (err.statusCode === 400 && "body" in err)
res.status(400).send({ status: 400, message: err.message });
});

// Routes
/*============================================================================*/

webserv.post('/playground/', (req, res) => {
const code = req.body.code;
const flags = req.body.flags;
const languange = req.body.language;

// Check request
if(!req.is("application/json"))
return res.status(400).json({ result: null, error: "Incorrect content type!" });
if (code == null || languange == null || flags == null)
return res.status(400).json({ result: null, error: "Malformed body" });

// TODO: Check from which domain the request came from.
// TODO: Probs add a few more checks here for unwanted requests.

// Find module
const module = Execution.modules[languange];
if (module == undefined)
return res.status(404).json({ result: null, error: "Unsupported Language!" });

Execution.run(module, code, flags, res);

console.log(`[Playground] [${languange}] body:`, code);
return res.json({ result: "Request received!\n", error: null });
});


// Entry point
/*============================================================================*/

webserv.listen(port, () => {
console.log(`[Playground] Running on: ${port}`);
});
52 changes: 52 additions & 0 deletions code-executor/src/executor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// -----------------------------------------------------------------------------
// Codam Coding College, Amsterdam @ 2023.
// See README in the root project for more information.
// -----------------------------------------------------------------------------

import { Response } from "express";
import CExecutor from "./modules/module.c";
import CPPExecutor from "./modules/module.cpp";
import ExecutionModule from "./modules/module.base";

/*============================================================================*/

export namespace Execution {
export type ModuleEntry = {
executor: typeof ExecutionModule;
extensions: string;
}

/** Map to associate languange with the correct executionModule */
export const modules: { [name: string]: ModuleEntry } = {
"c": {
executor: CExecutor,
extensions: ".c"
},
"cpp": {
executor: CPPExecutor,
extensions: ".cpp",
}
};

/**
* Spawns a child process for the given module and executes the code.
* @param module The specified module to run
*/
export function run(module: ModuleEntry, code: string, flags: string, response: Response) {
try {
const executor = new module.executor(code, flags);

executor.execute((err, stderr, stdout) => {
if (err)
response.status(500).json({ result: null, error: err });
else if (stderr != "")
response.status(204).json({ result: stderr, error: null });
else
response.status(204).json({ result: stdout, error: null });
});
} catch (error) {
return response.status(500).json({ result: null, error: error });
}
return;
}
}
37 changes: 37 additions & 0 deletions code-executor/src/modules/module.base.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// -----------------------------------------------------------------------------
// Codam Coding College, Amsterdam @ 2023.
// See README in the root project for more information.
// -----------------------------------------------------------------------------

import Shell from "child_process"

/*============================================================================*/

/**
* An execution module describes the way a language should be compiled and run.
*
* For example in C you need to compile the language and then run the out file.
*/
class ExecutionModule {
protected code: string;
protected flags: string;

/**
* Creates a new execution module.
* @param code The code to execute.
* @param flags Additional compiler flags
*/
constructor(code: string, flags: string) {
this.code = code;
this.flags = flags;
}

/**
* Spawn a child process and
*/
execute(cb: (err: Shell.ExecException, stderr: string, stdout: string) => void): void {
cb(new Error("Invalid module"), "", "");
}
}

export default ExecutionModule;
32 changes: 32 additions & 0 deletions code-executor/src/modules/module.c.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// -----------------------------------------------------------------------------
// Codam Coding College, Amsterdam @ 2023.
// See README in the root project for more information.
// -----------------------------------------------------------------------------

import Shell from "child_process"
import ExecutionModule from "./module.base";

/*============================================================================*/

class CExecutor extends ExecutionModule {
constructor(code: string, flags: string) {
super(code, flags)
}

/**
* Compiles and executes the code
*/
public execute(cb: (err, stderr, stdout) => void): void {

// Create file with code in it.
// ...

// Compile it
Shell.exec(`gcc ${this.flags} -o`, { timeout: 10000 }, (err, stdout: string, stderr: string) => cb(err, stderr, stdout));

// Run it
Shell.execFile(``, { timeout: 10000 }, (err, stdout, stderr) => cb(err, stderr, stdout));
}
}

export default CExecutor;
23 changes: 23 additions & 0 deletions code-executor/src/modules/module.cpp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// -----------------------------------------------------------------------------
// Codam Coding College, Amsterdam @ 2023.
// See README in the root project for more information.
// -----------------------------------------------------------------------------

import ExecutionModule from "./module.base";

/*============================================================================*/

class CPPExecutor extends ExecutionModule {
constructor(code: string, flags: string) {
super(code, flags)
}

/**
* Executes the code
*/
public execute(cb: (err, stderr, stdout) => void): void {

}
}

export default CPPExecutor;
30 changes: 30 additions & 0 deletions code-executor/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"compilerOptions": {
"target": "esnext",
"module": "commonjs",
"allowJs": false,
"outDir": "./build/",
"rootDir": "./src/",
"removeComments": true,
"downlevelIteration": false,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"noImplicitThis": true,
"alwaysStrict": true,
"noUnusedLocals": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"noPropertyAccessFromIndexSignature": true,
"esModuleInterop": true,
"preserveSymlinks": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"exclude": [
"node_modules/",
],
"include": ["src/"]
}
8 changes: 8 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,10 @@ pub struct Playground {
pub line_numbers: bool,
/// Display the run button. Default: `true`
pub runnable: bool,
/// Submits the code to the following endpoint for running.
pub endpoint: String,
/// The string used to mark lines as hidden.
pub hidden_str: String
}

impl Default for Playground {
Expand All @@ -638,6 +642,8 @@ impl Default for Playground {
copy_js: true,
line_numbers: false,
runnable: true,
endpoint: "https://play.rust-lang.org/evaluate.json".to_string(),
hidden_str: "!!".to_string()
}
}
}
Expand Down Expand Up @@ -783,6 +789,8 @@ mod tests {
copy_js: true,
line_numbers: false,
runnable: true,
endpoint: "".to_string(),
hidden_str: "".to_string()
};
let html_should_be = HtmlConfig {
curly_quotes: true,
Expand Down
Loading