This commit is contained in:
2024-09-21 18:39:31 +02:00
parent 8d951b0e80
commit 0896799b34
16 changed files with 692 additions and 150 deletions
+20 -1
View File
@@ -31,7 +31,7 @@ async function cleanContainers() {
}
async function createContainer(tag) {
const id = randomUUID()
const id = randomUUID().split('-').at(-1)
await fs.mkdir(getContainerPath('phc', id), { recursive: true })
@@ -50,6 +50,21 @@ async function createContainer(tag) {
containerIds.add(id)
await storeContainers()
await runCommandContainer(id, `echo "echo hello world" > example.sh; chmod +x example.sh`)
// Example file tree
await runCommandContainer(id, `mkdir -p /project/src /project/bin`)
await runCommandContainer(id, `echo 'console.log("Hello from index.js")' > /project/src/index.js`)
await runCommandContainer(id, `echo 'console.log("Hello from app.js")' > /project/src/app.js`)
await runCommandContainer(id, `echo 'echo "Hello from script.sh"' > /project/bin/script.sh`)
await runCommandContainer(id, `chmod +x /project/bin/script.sh`)
await runCommandContainer(id, `echo 'This is the README file.' > /project/README.md`)
await runCommandContainer(id, `echo '{"name": "project", "version": "1.0.0"}' > /project/package.json`)
return id
}
@@ -87,3 +102,7 @@ async function destroyContainer(id) {
containerIds.delete(id)
await storeContainers()
}
async function runCommandContainer(uuid, command) {
return await runCommand('docker', ['exec', '-w', '/project', uuid, '/bin/sh', '-c', command])
}
+51 -5
View File
@@ -8,18 +8,59 @@ import fs from 'fs/promises'
const app = express()
app.use(express.json())
app.use(express.json({ strict: false }))
app.get('/api/status', c => c.json(42))
// 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 files = await runCommand('/bin/ls', [path.join(getContainerPath('phc', uuid), p)])
const dirPath = path.join(getContainerPath('phc', uuid), p)
const fileList = await fs.readdir(dirPath)
res.json(files.trim().split('\n'))
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() })
}
@@ -40,13 +81,18 @@ app.get('/api/container/:uuid([a-zA-Z0-9-]+)/fs/*', async (req, res) => {
})
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 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) {
+10 -1
View File
@@ -4,7 +4,10 @@ export const CONTAINERS_ROOT = '/tmp/phc-run/containers'
export const getContainerPath = (ns, uuid) => `${CONTAINERS_ROOT}/${ns}/${uuid}`
export function runCommand(command, args, options = { stdin: null, capture: ['stdout', 'stderr'] }) {
export function runCommand(command, args, options = {}) {
options.stdin ??= null
options.capture ??= ['stdout', 'stderr']
console.log(`Running: ${command} ${JSON.stringify(args)}`)
return new Promise((resolve, reject) => {
@@ -14,18 +17,24 @@ export function runCommand(command, args, options = { stdin: null, capture: ['st
if (options.capture.includes('stdout')) {
child.stdout.on('data', data => {
console.log('[command]', '[stdout]', JSON.stringify(data.toString()))
output += data.toString()
})
}
if (options.capture.includes('stderr')) {
child.stderr.on('data', data => {
console.log('[command]', '[stderr]', JSON.stringify(data.toString()))
output += data.toString()
})
}
if (options.stdin) {
console.log('[command]', '[stdin]', options.stdin)
child.stdin.setDefaultEncoding('utf8')
child.stdin.write(options.stdin)
child.stdin.end()
}
child.on('close', code => {