You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

150 lines
3.8 KiB
JavaScript

import express from 'express'
import { WebSocketServer } from 'ws'
import { createContainerPty } from './docker.js'
import { getContainerPath, runCommand } from './utils.js'
import path from 'path'
import fs from 'fs/promises'
const app = express()
app.use(express.json({ strict: false }))
// TODO: Convert to spawn + websocket
app.get('/api/container/:uuid([a-zA-Z0-9-]+)/exec/shell', async (req, res) => {
const uuid = req.params.uuid
/** @type {string} */
const command = JSON.parse(req.query.command)
try {
const output = await runCommand('docker', ['exec', uuid, '/bin/sh', '-c', command])
res.json(output)
} catch (e) {
res.json({ error: e.toString() })
}
})
// app.get('/api/container/:uuid([a-zA-Z0-9-]+)/exec/ls/*', async (req, res) => {
// const uuid = req.params.uuid
// const p = path.normalize(req.params[0])
// try {
// const files = await runCommand('/bin/ls', [path.join(getContainerPath('phc', uuid), p)])
// res.json(files.trim().split('\n'))
// } catch (e) {
// res.json({ error: e.toString() })
// }
// })
app.get('/api/container/:uuid([a-zA-Z0-9-]+)/exec/ls/*', async (req, res) => {
const uuid = req.params.uuid
const p = path.normalize(req.params[0])
try {
const dirPath = path.join(getContainerPath('phc', uuid), p)
const fileList = await fs.readdir(dirPath)
const result = await Promise.all(
fileList.map(async name => {
const fullPath = path.join(dirPath, name)
const stats = await fs.lstat(fullPath)
return {
type: stats.isDirectory() ? 'folder' : 'file',
name,
}
})
)
result.sort((e1, e2) => e2.type.localeCompare(e1.type))
res.json(result)
} catch (e) {
res.json({ error: e.toString() })
}
})
app.get('/api/container/:uuid([a-zA-Z0-9-]+)/fs/*', async (req, res) => {
const uuid = req.params.uuid
const p = path.normalize(req.params[0])
console.log('Getting', p)
try {
const content = await fs.readFile(path.join(getContainerPath('phc', uuid), p), 'utf8')
res.json(content)
} catch (e) {
res.json({ error: e.toString() })
}
})
app.put('/api/container/:uuid([a-zA-Z0-9-]+)/fs/*', async (req, res) => {
const uuid = req.params.uuid
const p = path.normalize(req.params[0])
const content = req.body
console.log(content)
try {
// await fs.writeFile(path.join(getContainerPath('phc', uuid), p), content)
await runCommand('docker', ['exec', '-i', uuid, '/usr/bin/tee', `/project/${p}`], {
stdin: content,
})
res.json('ok')
} catch (e) {
res.json({ error: e.toString() })
}
})
const server = app.listen(5432, () => {
console.log('Starting API server on :5432...')
})
const wss = new WebSocketServer({ server })
wss.on('connection', async (ws, req) => {
const tag = req.url.split('/').at(-2)
console.log(`Client connected to "${tag}"`)
const container = await createContainerPty(tag, {
onData(data) {
ws.send(
JSON.stringify({
type: 'pty',
data,
})
)
},
onExit(e) {
ws.send(
JSON.stringify({
type: 'destroyed',
...e,
})
)
ws.close()
},
})
ws.send(
JSON.stringify({
type: 'created',
id: container.id,
})
)
ws.on('message', data => {
container.pty.write(data)
})
ws.on('close', () => {
container.pty.kill()
console.log(`Client disconnected`)
})
})