mvp
This commit is contained in:
+20
-1
@@ -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
@@ -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
@@ -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 => {
|
||||
|
||||
Reference in New Issue
Block a user