Initial commit
This commit is contained in:
		
						commit
						b2a04ba399
					
				
							
								
								
									
										5
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					[*.{js,jsx,ts,tsx,vue}]
 | 
				
			||||||
 | 
					indent_style = space
 | 
				
			||||||
 | 
					indent_size = 2
 | 
				
			||||||
 | 
					trim_trailing_whitespace = true
 | 
				
			||||||
 | 
					insert_final_newline = true
 | 
				
			||||||
							
								
								
									
										23
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					.DS_Store
 | 
				
			||||||
 | 
					node_modules
 | 
				
			||||||
 | 
					/dist
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# local env files
 | 
				
			||||||
 | 
					.env.local
 | 
				
			||||||
 | 
					.env.*.local
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Log files
 | 
				
			||||||
 | 
					npm-debug.log*
 | 
				
			||||||
 | 
					yarn-debug.log*
 | 
				
			||||||
 | 
					yarn-error.log*
 | 
				
			||||||
 | 
					pnpm-debug.log*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Editor directories and files
 | 
				
			||||||
 | 
					.idea
 | 
				
			||||||
 | 
					.vscode
 | 
				
			||||||
 | 
					*.suo
 | 
				
			||||||
 | 
					*.ntvs*
 | 
				
			||||||
 | 
					*.njsproj
 | 
				
			||||||
 | 
					*.sln
 | 
				
			||||||
 | 
					*.sw?
 | 
				
			||||||
							
								
								
									
										24
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					# codebox
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Project setup
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					yarn install
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Compiles and hot-reloads for development
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					yarn serve
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Compiles and minifies for production
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					yarn build
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Lints and fixes files
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					yarn lint
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Customize configuration
 | 
				
			||||||
 | 
					See [Configuration Reference](https://cli.vuejs.org/config/).
 | 
				
			||||||
							
								
								
									
										4
									
								
								backend/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								backend/index.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					import setup from './src/setup.js'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					setup()
 | 
				
			||||||
 | 
					console.log('started')
 | 
				
			||||||
							
								
								
									
										13
									
								
								backend/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								backend/package.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "name": "codebox-backend",
 | 
				
			||||||
 | 
					  "version": "1.0.0",
 | 
				
			||||||
 | 
					  "main": "index.js",
 | 
				
			||||||
 | 
					  "license": "MIT",
 | 
				
			||||||
 | 
					  "type": "module",
 | 
				
			||||||
 | 
					  "dependencies": {
 | 
				
			||||||
 | 
					    "body-parser": "^1.19.0",
 | 
				
			||||||
 | 
					    "express": "^4.17.1",
 | 
				
			||||||
 | 
					    "node-pty": "^0.10.1",
 | 
				
			||||||
 | 
					    "ws": "^8.2.3"
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										6
									
								
								backend/src/config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								backend/src/config.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					export default {
 | 
				
			||||||
 | 
					  port: process.env.PORT || 1234,
 | 
				
			||||||
 | 
					  dev: process.env.DEV || false,
 | 
				
			||||||
 | 
					  containerBinary: 'podman',
 | 
				
			||||||
 | 
					  containerLabel: 'codebox-worker'
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										34
									
								
								backend/src/containers.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								backend/src/containers.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					import { execFile as execFileC } from 'child_process'
 | 
				
			||||||
 | 
					import { promisify } from 'util'
 | 
				
			||||||
 | 
					import config from './config.js'
 | 
				
			||||||
 | 
					import pty from 'node-pty'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const execFile = promisify(execFileC)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const exec = async (binary, args = []) => {
 | 
				
			||||||
 | 
					  return (await execFile(binary, args)).stdout
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function containerExists (containerId) {
 | 
				
			||||||
 | 
					  return (await runningContainers()).some(c => c.Id === containerId)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function runningContainers () {
 | 
				
			||||||
 | 
					  return JSON.parse((await exec(config.containerBinary,
 | 
				
			||||||
 | 
					    ['ps', '--filter', `label=${config.containerLabel}`, '--format=json']
 | 
				
			||||||
 | 
					  )))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function getContainerShell (containerId, shell = 'sh') {
 | 
				
			||||||
 | 
					  return pty.spawn(config.containerBinary, ['exec', '-it', containerId, shell], {
 | 
				
			||||||
 | 
					    name: 'xterm-color',
 | 
				
			||||||
 | 
					    cols: 30,
 | 
				
			||||||
 | 
					    rows: 40,
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function startContainer (image = 'alpine', cmd = ['sh', '-c', 'while true; do sleep 1d; done']) {
 | 
				
			||||||
 | 
					  console.log(['run', '-d', '-l', config.containerLabel, image, ...cmd])
 | 
				
			||||||
 | 
					  return exec(config.containerBinary, ['run', '--rm', '-d', '-l', config.containerLabel, image, ...cmd])
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										14
									
								
								backend/src/cors.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								backend/src/cors.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					export default (app, origin = 'http://localhost:8080') => {
 | 
				
			||||||
 | 
					  app.use((req, res, next) => {
 | 
				
			||||||
 | 
					    res
 | 
				
			||||||
 | 
					      .header('Access-Control-Allow-Origin', origin)
 | 
				
			||||||
 | 
					      .header('Access-Control-Allow-Method', 'POST')
 | 
				
			||||||
 | 
					      .header('Access-Control-Allow-Headers', 'content-type')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    next()
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  app.options('*', (req, res) => {
 | 
				
			||||||
 | 
					    res.status(204). send()
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										51
									
								
								backend/src/http.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								backend/src/http.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,51 @@
 | 
				
			|||||||
 | 
					import { runningContainers, startContainer } from './containers.js'
 | 
				
			||||||
 | 
					import bodyParser from 'body-parser'
 | 
				
			||||||
 | 
					import config from './config.js'
 | 
				
			||||||
 | 
					import express from 'express'
 | 
				
			||||||
 | 
					import cors from './cors.js'
 | 
				
			||||||
 | 
					import http from 'http'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default (sessions) => {
 | 
				
			||||||
 | 
					  const app = express()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  app.use(bodyParser.json())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (config.dev) {
 | 
				
			||||||
 | 
					    cors(app)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  app.get('/containers', async (req, res) => {
 | 
				
			||||||
 | 
					    return res.json(
 | 
				
			||||||
 | 
					      (await runningContainers())
 | 
				
			||||||
 | 
					        .map(c => ({ id: c.Id, image: c.Image, labels: c.Labels }))
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  app.post('/containers', async (req, res) => {
 | 
				
			||||||
 | 
					    if ((req.body.image && typeof req.body.image !== 'string') || (req.body.cmd && Array.isArray(req.body.cmd))) {
 | 
				
			||||||
 | 
					      return res.send('invalid arguments').status(401)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const container = await startContainer(req.body.image, req.body.cmd)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    res.send(container)
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  app.post('/containers/:container/:session/resize', (req, res) => {
 | 
				
			||||||
 | 
					    const session = sessions[req.params.session]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!session || session.container !== req.params.container) {
 | 
				
			||||||
 | 
					      return res.send('invalid session').status(401)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!req.body.cols || !req.body.rows) {
 | 
				
			||||||
 | 
					      return res.send('missing arguments').status(401)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    session.term.resize(req.body.cols, req.body.rows)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    res.send('ok')
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return http.createServer(app)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										14
									
								
								backend/src/setup.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								backend/src/setup.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					import http from './http.js'
 | 
				
			||||||
 | 
					import websocket from './websocket.js'
 | 
				
			||||||
 | 
					import config from './config.js'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default () => {
 | 
				
			||||||
 | 
					  const sessions = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const server = http(sessions)
 | 
				
			||||||
 | 
					  websocket(server, sessions)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  server.listen(config.port)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return { sessions, server }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										63
									
								
								backend/src/websocket.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								backend/src/websocket.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,63 @@
 | 
				
			|||||||
 | 
					import { containerExists, getContainerShell } from './containers.js'
 | 
				
			||||||
 | 
					import { WebSocketServer } from 'ws'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default (server, sessions) => {
 | 
				
			||||||
 | 
					  const wss = new WebSocketServer({ noServer: true })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  server.on('upgrade', async (request, socket, head) => {
 | 
				
			||||||
 | 
					    const forbidden = () => {
 | 
				
			||||||
 | 
					      socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
 | 
				
			||||||
 | 
					      socket.destroy();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const path = request.url.substr(1).split('/')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (path.length !== 3 || path[0] !== 'ws') {
 | 
				
			||||||
 | 
					      return forbidden()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const [_, container, sessionId] = path
 | 
				
			||||||
 | 
					    const session = sessions[sessionId]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (session && session.container !== container) {
 | 
				
			||||||
 | 
					      console.log('wrong session')
 | 
				
			||||||
 | 
					      return forbidden()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!(await containerExists(container))) {
 | 
				
			||||||
 | 
					      console.log('no container')
 | 
				
			||||||
 | 
					      return forbidden()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!session) {
 | 
				
			||||||
 | 
					      sessions[sessionId] = { container }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    request.session = sessions[sessionId]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    wss.handleUpgrade(request, socket, head, ws => {
 | 
				
			||||||
 | 
					      wss.emit('connection', ws, request);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  wss.on('connection', (ws, req) => {
 | 
				
			||||||
 | 
					    if (!req.session.term) {
 | 
				
			||||||
 | 
					      req.session.term = getContainerShell(req.session.container)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ws.on('message', message => {
 | 
				
			||||||
 | 
					      const decoded = message.toString()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      req.session.term.write(decoded)
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    req.session.term.onData(data => {
 | 
				
			||||||
 | 
					      ws.send(data)
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    req.session.term.onExit(exit => {
 | 
				
			||||||
 | 
					      ws.send(`Process terminated with code ${exit.exitCode}`)
 | 
				
			||||||
 | 
					      ws.close()
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										385
									
								
								backend/yarn.lock
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										385
									
								
								backend/yarn.lock
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,385 @@
 | 
				
			|||||||
 | 
					# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
 | 
				
			||||||
 | 
					# yarn lockfile v1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					accepts@~1.3.7:
 | 
				
			||||||
 | 
					  version "1.3.7"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd"
 | 
				
			||||||
 | 
					  integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    mime-types "~2.1.24"
 | 
				
			||||||
 | 
					    negotiator "0.6.2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					array-flatten@1.1.1:
 | 
				
			||||||
 | 
					  version "1.1.1"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
 | 
				
			||||||
 | 
					  integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					body-parser@1.19.0, body-parser@^1.19.0:
 | 
				
			||||||
 | 
					  version "1.19.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a"
 | 
				
			||||||
 | 
					  integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    bytes "3.1.0"
 | 
				
			||||||
 | 
					    content-type "~1.0.4"
 | 
				
			||||||
 | 
					    debug "2.6.9"
 | 
				
			||||||
 | 
					    depd "~1.1.2"
 | 
				
			||||||
 | 
					    http-errors "1.7.2"
 | 
				
			||||||
 | 
					    iconv-lite "0.4.24"
 | 
				
			||||||
 | 
					    on-finished "~2.3.0"
 | 
				
			||||||
 | 
					    qs "6.7.0"
 | 
				
			||||||
 | 
					    raw-body "2.4.0"
 | 
				
			||||||
 | 
					    type-is "~1.6.17"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bytes@3.1.0:
 | 
				
			||||||
 | 
					  version "3.1.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
 | 
				
			||||||
 | 
					  integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					content-disposition@0.5.3:
 | 
				
			||||||
 | 
					  version "0.5.3"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd"
 | 
				
			||||||
 | 
					  integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    safe-buffer "5.1.2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					content-type@~1.0.4:
 | 
				
			||||||
 | 
					  version "1.0.4"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
 | 
				
			||||||
 | 
					  integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					cookie-signature@1.0.6:
 | 
				
			||||||
 | 
					  version "1.0.6"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
 | 
				
			||||||
 | 
					  integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					cookie@0.4.0:
 | 
				
			||||||
 | 
					  version "0.4.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba"
 | 
				
			||||||
 | 
					  integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					debug@2.6.9:
 | 
				
			||||||
 | 
					  version "2.6.9"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
 | 
				
			||||||
 | 
					  integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    ms "2.0.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					depd@~1.1.2:
 | 
				
			||||||
 | 
					  version "1.1.2"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
 | 
				
			||||||
 | 
					  integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					destroy@~1.0.4:
 | 
				
			||||||
 | 
					  version "1.0.4"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
 | 
				
			||||||
 | 
					  integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ee-first@1.1.1:
 | 
				
			||||||
 | 
					  version "1.1.1"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
 | 
				
			||||||
 | 
					  integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					encodeurl@~1.0.2:
 | 
				
			||||||
 | 
					  version "1.0.2"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
 | 
				
			||||||
 | 
					  integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					escape-html@~1.0.3:
 | 
				
			||||||
 | 
					  version "1.0.3"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
 | 
				
			||||||
 | 
					  integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					etag@~1.8.1:
 | 
				
			||||||
 | 
					  version "1.8.1"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
 | 
				
			||||||
 | 
					  integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					express@^4.17.1:
 | 
				
			||||||
 | 
					  version "4.17.1"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134"
 | 
				
			||||||
 | 
					  integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    accepts "~1.3.7"
 | 
				
			||||||
 | 
					    array-flatten "1.1.1"
 | 
				
			||||||
 | 
					    body-parser "1.19.0"
 | 
				
			||||||
 | 
					    content-disposition "0.5.3"
 | 
				
			||||||
 | 
					    content-type "~1.0.4"
 | 
				
			||||||
 | 
					    cookie "0.4.0"
 | 
				
			||||||
 | 
					    cookie-signature "1.0.6"
 | 
				
			||||||
 | 
					    debug "2.6.9"
 | 
				
			||||||
 | 
					    depd "~1.1.2"
 | 
				
			||||||
 | 
					    encodeurl "~1.0.2"
 | 
				
			||||||
 | 
					    escape-html "~1.0.3"
 | 
				
			||||||
 | 
					    etag "~1.8.1"
 | 
				
			||||||
 | 
					    finalhandler "~1.1.2"
 | 
				
			||||||
 | 
					    fresh "0.5.2"
 | 
				
			||||||
 | 
					    merge-descriptors "1.0.1"
 | 
				
			||||||
 | 
					    methods "~1.1.2"
 | 
				
			||||||
 | 
					    on-finished "~2.3.0"
 | 
				
			||||||
 | 
					    parseurl "~1.3.3"
 | 
				
			||||||
 | 
					    path-to-regexp "0.1.7"
 | 
				
			||||||
 | 
					    proxy-addr "~2.0.5"
 | 
				
			||||||
 | 
					    qs "6.7.0"
 | 
				
			||||||
 | 
					    range-parser "~1.2.1"
 | 
				
			||||||
 | 
					    safe-buffer "5.1.2"
 | 
				
			||||||
 | 
					    send "0.17.1"
 | 
				
			||||||
 | 
					    serve-static "1.14.1"
 | 
				
			||||||
 | 
					    setprototypeof "1.1.1"
 | 
				
			||||||
 | 
					    statuses "~1.5.0"
 | 
				
			||||||
 | 
					    type-is "~1.6.18"
 | 
				
			||||||
 | 
					    utils-merge "1.0.1"
 | 
				
			||||||
 | 
					    vary "~1.1.2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					finalhandler@~1.1.2:
 | 
				
			||||||
 | 
					  version "1.1.2"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d"
 | 
				
			||||||
 | 
					  integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    debug "2.6.9"
 | 
				
			||||||
 | 
					    encodeurl "~1.0.2"
 | 
				
			||||||
 | 
					    escape-html "~1.0.3"
 | 
				
			||||||
 | 
					    on-finished "~2.3.0"
 | 
				
			||||||
 | 
					    parseurl "~1.3.3"
 | 
				
			||||||
 | 
					    statuses "~1.5.0"
 | 
				
			||||||
 | 
					    unpipe "~1.0.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					forwarded@0.2.0:
 | 
				
			||||||
 | 
					  version "0.2.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
 | 
				
			||||||
 | 
					  integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fresh@0.5.2:
 | 
				
			||||||
 | 
					  version "0.5.2"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
 | 
				
			||||||
 | 
					  integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					http-errors@1.7.2:
 | 
				
			||||||
 | 
					  version "1.7.2"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f"
 | 
				
			||||||
 | 
					  integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    depd "~1.1.2"
 | 
				
			||||||
 | 
					    inherits "2.0.3"
 | 
				
			||||||
 | 
					    setprototypeof "1.1.1"
 | 
				
			||||||
 | 
					    statuses ">= 1.5.0 < 2"
 | 
				
			||||||
 | 
					    toidentifier "1.0.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					http-errors@~1.7.2:
 | 
				
			||||||
 | 
					  version "1.7.3"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06"
 | 
				
			||||||
 | 
					  integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    depd "~1.1.2"
 | 
				
			||||||
 | 
					    inherits "2.0.4"
 | 
				
			||||||
 | 
					    setprototypeof "1.1.1"
 | 
				
			||||||
 | 
					    statuses ">= 1.5.0 < 2"
 | 
				
			||||||
 | 
					    toidentifier "1.0.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					iconv-lite@0.4.24:
 | 
				
			||||||
 | 
					  version "0.4.24"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
 | 
				
			||||||
 | 
					  integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    safer-buffer ">= 2.1.2 < 3"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					inherits@2.0.3:
 | 
				
			||||||
 | 
					  version "2.0.3"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
 | 
				
			||||||
 | 
					  integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					inherits@2.0.4:
 | 
				
			||||||
 | 
					  version "2.0.4"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
 | 
				
			||||||
 | 
					  integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ipaddr.js@1.9.1:
 | 
				
			||||||
 | 
					  version "1.9.1"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
 | 
				
			||||||
 | 
					  integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					media-typer@0.3.0:
 | 
				
			||||||
 | 
					  version "0.3.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
 | 
				
			||||||
 | 
					  integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					merge-descriptors@1.0.1:
 | 
				
			||||||
 | 
					  version "1.0.1"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
 | 
				
			||||||
 | 
					  integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					methods@~1.1.2:
 | 
				
			||||||
 | 
					  version "1.1.2"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
 | 
				
			||||||
 | 
					  integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mime-db@1.50.0:
 | 
				
			||||||
 | 
					  version "1.50.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.50.0.tgz#abd4ac94e98d3c0e185016c67ab45d5fde40c11f"
 | 
				
			||||||
 | 
					  integrity sha512-9tMZCDlYHqeERXEHO9f/hKfNXhre5dK2eE/krIvUjZbS2KPcqGDfNShIWS1uW9XOTKQKqK6qbeOci18rbfW77A==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mime-types@~2.1.24:
 | 
				
			||||||
 | 
					  version "2.1.33"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.33.tgz#1fa12a904472fafd068e48d9e8401f74d3f70edb"
 | 
				
			||||||
 | 
					  integrity sha512-plLElXp7pRDd0bNZHw+nMd52vRYjLwQjygaNg7ddJ2uJtTlmnTCjWuPKxVu6//AdaRuME84SvLW91sIkBqGT0g==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    mime-db "1.50.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mime@1.6.0:
 | 
				
			||||||
 | 
					  version "1.6.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
 | 
				
			||||||
 | 
					  integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ms@2.0.0:
 | 
				
			||||||
 | 
					  version "2.0.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
 | 
				
			||||||
 | 
					  integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ms@2.1.1:
 | 
				
			||||||
 | 
					  version "2.1.1"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
 | 
				
			||||||
 | 
					  integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					nan@^2.14.0:
 | 
				
			||||||
 | 
					  version "2.15.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee"
 | 
				
			||||||
 | 
					  integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					negotiator@0.6.2:
 | 
				
			||||||
 | 
					  version "0.6.2"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
 | 
				
			||||||
 | 
					  integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					node-pty@^0.10.1:
 | 
				
			||||||
 | 
					  version "0.10.1"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.10.1.tgz#cd05d03a2710315ec40221232ec04186f6ac2c6d"
 | 
				
			||||||
 | 
					  integrity sha512-JTdtUS0Im/yRsWJSx7yiW9rtpfmxqxolrtnyKwPLI+6XqTAPW/O2MjS8FYL4I5TsMbH2lVgDb2VMjp+9LoQGNg==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    nan "^2.14.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					on-finished@~2.3.0:
 | 
				
			||||||
 | 
					  version "2.3.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
 | 
				
			||||||
 | 
					  integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    ee-first "1.1.1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					parseurl@~1.3.3:
 | 
				
			||||||
 | 
					  version "1.3.3"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
 | 
				
			||||||
 | 
					  integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					path-to-regexp@0.1.7:
 | 
				
			||||||
 | 
					  version "0.1.7"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
 | 
				
			||||||
 | 
					  integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					proxy-addr@~2.0.5:
 | 
				
			||||||
 | 
					  version "2.0.7"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
 | 
				
			||||||
 | 
					  integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    forwarded "0.2.0"
 | 
				
			||||||
 | 
					    ipaddr.js "1.9.1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					qs@6.7.0:
 | 
				
			||||||
 | 
					  version "6.7.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
 | 
				
			||||||
 | 
					  integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					range-parser@~1.2.1:
 | 
				
			||||||
 | 
					  version "1.2.1"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
 | 
				
			||||||
 | 
					  integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					raw-body@2.4.0:
 | 
				
			||||||
 | 
					  version "2.4.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332"
 | 
				
			||||||
 | 
					  integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    bytes "3.1.0"
 | 
				
			||||||
 | 
					    http-errors "1.7.2"
 | 
				
			||||||
 | 
					    iconv-lite "0.4.24"
 | 
				
			||||||
 | 
					    unpipe "1.0.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					safe-buffer@5.1.2:
 | 
				
			||||||
 | 
					  version "5.1.2"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
 | 
				
			||||||
 | 
					  integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"safer-buffer@>= 2.1.2 < 3":
 | 
				
			||||||
 | 
					  version "2.1.2"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
 | 
				
			||||||
 | 
					  integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					send@0.17.1:
 | 
				
			||||||
 | 
					  version "0.17.1"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8"
 | 
				
			||||||
 | 
					  integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    debug "2.6.9"
 | 
				
			||||||
 | 
					    depd "~1.1.2"
 | 
				
			||||||
 | 
					    destroy "~1.0.4"
 | 
				
			||||||
 | 
					    encodeurl "~1.0.2"
 | 
				
			||||||
 | 
					    escape-html "~1.0.3"
 | 
				
			||||||
 | 
					    etag "~1.8.1"
 | 
				
			||||||
 | 
					    fresh "0.5.2"
 | 
				
			||||||
 | 
					    http-errors "~1.7.2"
 | 
				
			||||||
 | 
					    mime "1.6.0"
 | 
				
			||||||
 | 
					    ms "2.1.1"
 | 
				
			||||||
 | 
					    on-finished "~2.3.0"
 | 
				
			||||||
 | 
					    range-parser "~1.2.1"
 | 
				
			||||||
 | 
					    statuses "~1.5.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					serve-static@1.14.1:
 | 
				
			||||||
 | 
					  version "1.14.1"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9"
 | 
				
			||||||
 | 
					  integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    encodeurl "~1.0.2"
 | 
				
			||||||
 | 
					    escape-html "~1.0.3"
 | 
				
			||||||
 | 
					    parseurl "~1.3.3"
 | 
				
			||||||
 | 
					    send "0.17.1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					setprototypeof@1.1.1:
 | 
				
			||||||
 | 
					  version "1.1.1"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683"
 | 
				
			||||||
 | 
					  integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"statuses@>= 1.5.0 < 2", statuses@~1.5.0:
 | 
				
			||||||
 | 
					  version "1.5.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
 | 
				
			||||||
 | 
					  integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					toidentifier@1.0.0:
 | 
				
			||||||
 | 
					  version "1.0.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
 | 
				
			||||||
 | 
					  integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type-is@~1.6.17, type-is@~1.6.18:
 | 
				
			||||||
 | 
					  version "1.6.18"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
 | 
				
			||||||
 | 
					  integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    media-typer "0.3.0"
 | 
				
			||||||
 | 
					    mime-types "~2.1.24"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					unpipe@1.0.0, unpipe@~1.0.0:
 | 
				
			||||||
 | 
					  version "1.0.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
 | 
				
			||||||
 | 
					  integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					utils-merge@1.0.1:
 | 
				
			||||||
 | 
					  version "1.0.1"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
 | 
				
			||||||
 | 
					  integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					vary@~1.1.2:
 | 
				
			||||||
 | 
					  version "1.1.2"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
 | 
				
			||||||
 | 
					  integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ws@^8.2.3:
 | 
				
			||||||
 | 
					  version "8.2.3"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba"
 | 
				
			||||||
 | 
					  integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==
 | 
				
			||||||
							
								
								
									
										5
									
								
								frontend/babel.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								frontend/babel.config.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					module.exports = {
 | 
				
			||||||
 | 
					  presets: [
 | 
				
			||||||
 | 
					    '@vue/cli-plugin-babel/preset'
 | 
				
			||||||
 | 
					  ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										50
									
								
								frontend/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								frontend/package.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,50 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "name": "codebox",
 | 
				
			||||||
 | 
					  "version": "0.1.0",
 | 
				
			||||||
 | 
					  "private": true,
 | 
				
			||||||
 | 
					  "scripts": {
 | 
				
			||||||
 | 
					    "serve": "vue-cli-service serve",
 | 
				
			||||||
 | 
					    "build": "vue-cli-service build",
 | 
				
			||||||
 | 
					    "lint": "vue-cli-service lint"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "dependencies": {
 | 
				
			||||||
 | 
					    "core-js": "^3.6.5",
 | 
				
			||||||
 | 
					    "vue": "^3.0.0",
 | 
				
			||||||
 | 
					    "xterm": "^4.14.1",
 | 
				
			||||||
 | 
					    "xterm-addon-attach": "^0.6.0",
 | 
				
			||||||
 | 
					    "xterm-addon-fit": "^0.5.0"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "devDependencies": {
 | 
				
			||||||
 | 
					    "@vue/cli-plugin-babel": "~4.5.0",
 | 
				
			||||||
 | 
					    "@vue/cli-plugin-eslint": "~4.5.0",
 | 
				
			||||||
 | 
					    "@vue/cli-service": "~4.5.0",
 | 
				
			||||||
 | 
					    "@vue/compiler-sfc": "^3.0.0",
 | 
				
			||||||
 | 
					    "@vue/eslint-config-standard": "^5.1.2",
 | 
				
			||||||
 | 
					    "babel-eslint": "^10.1.0",
 | 
				
			||||||
 | 
					    "eslint": "^6.7.2",
 | 
				
			||||||
 | 
					    "eslint-plugin-import": "^2.20.2",
 | 
				
			||||||
 | 
					    "eslint-plugin-node": "^11.1.0",
 | 
				
			||||||
 | 
					    "eslint-plugin-promise": "^4.2.1",
 | 
				
			||||||
 | 
					    "eslint-plugin-standard": "^4.0.0",
 | 
				
			||||||
 | 
					    "eslint-plugin-vue": "^7.0.0"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "eslintConfig": {
 | 
				
			||||||
 | 
					    "root": true,
 | 
				
			||||||
 | 
					    "env": {
 | 
				
			||||||
 | 
					      "node": true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "extends": [
 | 
				
			||||||
 | 
					      "plugin:vue/vue3-essential",
 | 
				
			||||||
 | 
					      "@vue/standard"
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "parserOptions": {
 | 
				
			||||||
 | 
					      "parser": "babel-eslint"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "rules": {}
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "browserslist": [
 | 
				
			||||||
 | 
					    "> 1%",
 | 
				
			||||||
 | 
					    "last 2 versions",
 | 
				
			||||||
 | 
					    "not dead"
 | 
				
			||||||
 | 
					  ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								frontend/public/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								frontend/public/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 4.2 KiB  | 
							
								
								
									
										17
									
								
								frontend/public/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								frontend/public/index.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					<!DOCTYPE html>
 | 
				
			||||||
 | 
					<html lang="">
 | 
				
			||||||
 | 
					  <head>
 | 
				
			||||||
 | 
					    <meta charset="utf-8">
 | 
				
			||||||
 | 
					    <meta http-equiv="X-UA-Compatible" content="IE=edge">
 | 
				
			||||||
 | 
					    <meta name="viewport" content="width=device-width,initial-scale=1.0">
 | 
				
			||||||
 | 
					    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
 | 
				
			||||||
 | 
					    <title><%= htmlWebpackPlugin.options.title %></title>
 | 
				
			||||||
 | 
					  </head>
 | 
				
			||||||
 | 
					  <body>
 | 
				
			||||||
 | 
					    <noscript>
 | 
				
			||||||
 | 
					      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
 | 
				
			||||||
 | 
					    </noscript>
 | 
				
			||||||
 | 
					    <div id="app"></div>
 | 
				
			||||||
 | 
					    <!-- built files will be auto injected -->
 | 
				
			||||||
 | 
					  </body>
 | 
				
			||||||
 | 
					</html>
 | 
				
			||||||
							
								
								
									
										14
									
								
								frontend/src/App.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								frontend/src/App.vue
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <WsTerm container="fc4854871b5d9ad97f8f8c5daec81e5452bd1bb59fb50ea5cbcdac6787600d53" session="random-string"/>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script>
 | 
				
			||||||
 | 
					import WsTerm from './components/WsTerm.vue'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default {
 | 
				
			||||||
 | 
					  name: 'App',
 | 
				
			||||||
 | 
					  components: {
 | 
				
			||||||
 | 
					    WsTerm
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
							
								
								
									
										58
									
								
								frontend/src/components/WsTerm.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								frontend/src/components/WsTerm.vue
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,58 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <div ref="renderEl"></div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script>
 | 
				
			||||||
 | 
					import 'xterm/css/xterm.css'
 | 
				
			||||||
 | 
					import { Terminal } from 'xterm'
 | 
				
			||||||
 | 
					import { AttachAddon } from 'xterm-addon-attach'
 | 
				
			||||||
 | 
					import { onMounted, ref } from 'vue'
 | 
				
			||||||
 | 
					import { FitAddon } from 'xterm-addon-fit'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default {
 | 
				
			||||||
 | 
					  name: 'WsTerm',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  props: {
 | 
				
			||||||
 | 
					    container: String,
 | 
				
			||||||
 | 
					    session: String
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  setup (props) {
 | 
				
			||||||
 | 
					    const socket = new WebSocket(`ws://localhost:1234/ws/${props.container}/${props.session}`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const renderEl = ref(null)
 | 
				
			||||||
 | 
					    const term = new Terminal()
 | 
				
			||||||
 | 
					    const attachAddon = new AttachAddon(socket)
 | 
				
			||||||
 | 
					    const fitAddon = new FitAddon()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    term.loadAddon(fitAddon)
 | 
				
			||||||
 | 
					    term.loadAddon(attachAddon)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const resize = () => {
 | 
				
			||||||
 | 
					      fitAddon.fit()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      fetch(`http://localhost:1234/containers/${props.container}/${props.session}/resize`, {
 | 
				
			||||||
 | 
					        method: 'post',
 | 
				
			||||||
 | 
					        headers: { 'content-type': 'application/json' },
 | 
				
			||||||
 | 
					        body: JSON.stringify({
 | 
				
			||||||
 | 
					          cols: term.cols,
 | 
				
			||||||
 | 
					          rows: term.rows
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    onMounted(() => {
 | 
				
			||||||
 | 
					      const resizeObserver = new ResizeObserver(() => {
 | 
				
			||||||
 | 
					        resize()
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      resizeObserver.observe(renderEl.value)
 | 
				
			||||||
 | 
					      term.open(renderEl.value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      resize()
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return { renderEl }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
							
								
								
									
										4
									
								
								frontend/src/main.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								frontend/src/main.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					import { createApp } from 'vue'
 | 
				
			||||||
 | 
					import App from './App.vue'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					createApp(App).mount('#app')
 | 
				
			||||||
							
								
								
									
										8931
									
								
								frontend/yarn.lock
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8931
									
								
								frontend/yarn.lock
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user