Files
yn/backend/main.js
T

215 lines
6.7 KiB
JavaScript

const fs = require('fs')
const os = require('os')
const path = require('path')
const Koa = require('koa')
const bodyParser = require('koa-body')
const static = require('koa-static')
const mime = require('mime')
const request = require('request')
const pty = require('node-pty')
const file = require('./file')
const dataRepository = require('./repository')
const run = require('./run')
const convert = require('./convert')
const plantuml = require('./plantuml')
const result = (status = 'ok', message = '操作成功', data = null) => {
return { status, message, data }
}
const fileContent = async (ctx, next) => {
if (ctx.path === '/api/file') {
if (ctx.method === 'GET') {
ctx.body = result('ok', '获取成功', {
content: file.read(ctx.query.repo, ctx.query.path).toString(),
hash: file.hash(ctx.query.repo, ctx.query.path)
})
} else if (ctx.method === 'POST') {
const oldHash = ctx.request.body.old_hash
if (!oldHash) {
throw new Error('未传递文件hash')
} else if (oldHash === 'new' && file.exists(ctx.request.body.repo, ctx.request.body.path)) {
throw new Error('文件已经存在')
} else if (oldHash !== 'new' && !file.checkHash(ctx.request.body.repo, ctx.request.body.path, oldHash)) {
throw new Error('磁盘文件已经更新,请刷新文件')
}
const hash = file.write(ctx.request.body.repo, ctx.request.body.path, ctx.request.body.content)
ctx.body = result('ok', '保存成功', hash)
} else if (ctx.method === 'DELETE') {
file.rm(ctx.query.repo, ctx.query.path)
ctx.body = result()
} else if (ctx.method === 'PATCH') {
if (file.exists(ctx.request.body.repo, ctx.request.body.newPath)) {
throw new Error('文件已经存在')
}
file.mv(ctx.request.body.repo, ctx.request.body.oldPath, ctx.request.body.newPath)
ctx.body = result()
}
} else if (ctx.path === '/api/tree') {
ctx.body = result('ok', '获取成功', file.tree(ctx.query.repo))
} else {
await next()
}
}
const attachment = async (ctx, next) => {
if (ctx.path.startsWith('/api/attachment')) {
if (ctx.method === 'POST') {
const path = ctx.request.body.fields.path
const repo = ctx.request.body.fields.repo
file.upload(repo, ctx.request.body.files.attachment, path)
ctx.body = result('ok', '上传成功', path)
} else if (ctx.method === 'GET') {
ctx.type = mime.getType(ctx.query.path)
ctx.body = file.read(ctx.query.repo, ctx.query.path)
}
} else {
await next()
}
}
const open = async (ctx, next) => {
if (ctx.path.startsWith('/api/open')) {
if (ctx.method === 'GET') {
file.open(ctx.query.repo, ctx.query.path)
ctx.body = result()
}
} else {
await next()
}
}
const searchFile = async (ctx, next) => {
if (ctx.path.startsWith('/api/search')) {
const search = ctx.query.search
const repo = ctx.query.repo
ctx.body = result('ok', '操作成功', file.search(repo, search))
} else {
await next()
}
}
const repository = async (ctx, next) => {
if (ctx.path.startsWith('/api/repositories')) {
ctx.body = result('ok', '获取成功', dataRepository.list())
} else {
await next()
}
}
const plantumlGen = async (ctx, next) => {
if (ctx.path.startsWith('/api/plantuml/png')) {
ctx.set('cache-control', 'max-age=86400') // 一天过期
ctx.type = 'image/png'
ctx.body = plantuml.generate(ctx.query.data)
} else {
await next()
}
}
const runCode = async (ctx, next) => {
if (ctx.path.startsWith('/api/run')) {
const rst = run.runCode(ctx.request.body.language, ctx.request.body.code)
ctx.body = result('ok', '运行成功', rst)
} else {
await next()
}
}
const convertFile = async (ctx, next) => {
if (ctx.path.startsWith('/api/convert/')) {
const html = ctx.request.body.html
const type = ctx.request.body.type
ctx.type = mime.getType(`file.${type}`)
ctx.body = await convert(html, type)
} else {
await next()
}
}
const proxy = async (ctx, next) => {
if (ctx.path.startsWith('/api/proxy')) {
const url = ctx.query.url
ctx.body = request(url)
} else {
await next()
}
}
const readme = async (ctx, next) => {
if (ctx.path.startsWith('/api/readme')) {
if (ctx.query.path) {
ctx.type = mime.getType(ctx.query.path)
ctx.body = fs.readFileSync(path.join(__dirname, '../', ctx.query.path))
} else {
ctx.body = result('ok', '获取成功', {
content: fs.readFileSync(path.join(__dirname, '../README.md')).toString()
})
}
} else {
await next()
}
}
const app = new Koa()
app.use(static(
path.join(__dirname, '../static')
))
app.use(bodyParser({
multipart: true,
formLimit: '20mb',
jsonLimit: '20mb',
textLimit: '20mb'
}))
const wrapper = async (ctx, next, fun) => {
try {
await fun(ctx, next)
} catch (error) {
ctx.body = result('error', error.message)
}
}
app.use(async (ctx, next) => await wrapper(ctx, next, fileContent))
app.use(async (ctx, next) => await wrapper(ctx, next, attachment))
app.use(async (ctx, next) => await wrapper(ctx, next, open))
app.use(async (ctx, next) => await wrapper(ctx, next, plantumlGen))
app.use(async (ctx, next) => await wrapper(ctx, next, runCode))
app.use(async (ctx, next) => await wrapper(ctx, next, convertFile))
app.use(async (ctx, next) => await wrapper(ctx, next, searchFile))
app.use(async (ctx, next) => await wrapper(ctx, next, repository))
app.use(async (ctx, next) => await wrapper(ctx, next, proxy))
app.use(async (ctx, next) => await wrapper(ctx, next, readme))
const port = 3000
const server = require('http').createServer(app.callback())
const io = require('socket.io')(server, {path: '/ws'})
io.on('connection', socket => {
const ptyProcess = pty.spawn(os.platform() === 'win32' ? 'cmd.exe' : 'bash', [], {
name: 'xterm-color',
cols: 80,
rows: 24,
cwd: path.join(__dirname, '../'),
env: process.env
})
ptyProcess.on('data', data => socket.emit('output', data))
ptyProcess.on('exit', () => socket.disconnect())
socket.on('input', data => ptyProcess.write(data))
socket.on('resize', size => ptyProcess.resize(size[0], size[1]))
socket.on('disconnect', () => ptyProcess.destroy())
})
server.listen(port)
console.log(`访问地址:http://localhost:${port}`)