|
|
|
@ -2,10 +2,13 @@ const { Controller } = require('libflitter')
|
|
|
|
|
const ncp = require('ncp').ncp
|
|
|
|
|
const fs = require('fs').promises
|
|
|
|
|
const path = require('path')
|
|
|
|
|
const md = require('markdown').markdown
|
|
|
|
|
const rimraf = require('rimraf')
|
|
|
|
|
const uuid = require('uuid/v4')
|
|
|
|
|
|
|
|
|
|
class ExportController extends Controller {
|
|
|
|
|
static get services() {
|
|
|
|
|
return [...super.services, 'models', 'utility']
|
|
|
|
|
return [...super.services, 'models', 'utility', 'upload']
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async get_export_list(req, res, next) {
|
|
|
|
@ -17,13 +20,54 @@ class ExportController extends Controller {
|
|
|
|
|
return res.api(exports)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async download_export(req, res, next) {
|
|
|
|
|
const Export = this.models.get('api:Export')
|
|
|
|
|
const exported = await Export.findOne({ UUID: req.params.ExportId, user_id: req.user.id })
|
|
|
|
|
if ( !exported ) {
|
|
|
|
|
return res.status(404)
|
|
|
|
|
.message('No export with that UUID found.')
|
|
|
|
|
.api()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const File = this.models.get('upload::File')
|
|
|
|
|
const file = await File.findOne({_id: File.ObjectId(exported.file_id)})
|
|
|
|
|
if ( !file ) return res.status(404).message('This export file has been deleted.').api({})
|
|
|
|
|
|
|
|
|
|
return file.send(res)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async export_subtree(req, res, next) {
|
|
|
|
|
const Export = this.models.get('api:Export')
|
|
|
|
|
const format = req.form.format
|
|
|
|
|
const page = req.form.page
|
|
|
|
|
|
|
|
|
|
if ( format === 'html' ) {
|
|
|
|
|
const generated_export = await this.export_subtree_as_html(page, req.user)
|
|
|
|
|
|
|
|
|
|
// Store the generated archive
|
|
|
|
|
const uploader = this.upload.provider()
|
|
|
|
|
const file = await uploader.store({
|
|
|
|
|
temp_path: generated_export,
|
|
|
|
|
original_name: `export-${uuid()}.tar.gz`,
|
|
|
|
|
mime_type: 'application/gzip',
|
|
|
|
|
tag: 'generated_export',
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const exp = new Export({
|
|
|
|
|
user_id: req.user.id,
|
|
|
|
|
format,
|
|
|
|
|
subtree: true,
|
|
|
|
|
file_id: file.id,
|
|
|
|
|
PageId: page.UUID,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
await exp.save()
|
|
|
|
|
return res.api(exp)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return res.status(400)
|
|
|
|
|
.message('Invalid export format!')
|
|
|
|
|
.api()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async export_subtree_as_html(page, user) {
|
|
|
|
@ -75,7 +119,30 @@ class ExportController extends Controller {
|
|
|
|
|
await fs.writeFile(path.resolve(work_dir, item.file_name), item_template)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return work_dir
|
|
|
|
|
// Write the main page redirect
|
|
|
|
|
const redir_html = `
|
|
|
|
|
<!DOCTYPE html>
|
|
|
|
|
<html>
|
|
|
|
|
<head>
|
|
|
|
|
<meta http-equiv="refresh" content="0; url=${flat_tree[0].file_name}">
|
|
|
|
|
</head>
|
|
|
|
|
</html>
|
|
|
|
|
`
|
|
|
|
|
await fs.writeFile(path.resolve(work_dir, 'index.html'), redir_html)
|
|
|
|
|
|
|
|
|
|
// Create the archive
|
|
|
|
|
const tar = require('tar')
|
|
|
|
|
const archive_path = path.resolve(work_dir, '..', `export-${uuid()}.tar.gz`)
|
|
|
|
|
await tar.c({
|
|
|
|
|
gzip: true,
|
|
|
|
|
file: archive_path,
|
|
|
|
|
cwd: path.resolve(work_dir, '..'),
|
|
|
|
|
}, [path.basename(work_dir)])
|
|
|
|
|
|
|
|
|
|
await new Promise(res => {
|
|
|
|
|
rimraf(work_dir, res)
|
|
|
|
|
})
|
|
|
|
|
return archive_path
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async copy_template(from, to) {
|
|
|
|
@ -89,28 +156,43 @@ class ExportController extends Controller {
|
|
|
|
|
|
|
|
|
|
async scratch_dir() {
|
|
|
|
|
const tmp = require('tmp')
|
|
|
|
|
const gen_id = uuid()
|
|
|
|
|
|
|
|
|
|
return new Promise((res, rej) => {
|
|
|
|
|
tmp.dir((err, path) => {
|
|
|
|
|
tmp.dir((err, tmp_path) => {
|
|
|
|
|
if ( err ) rej(err)
|
|
|
|
|
else res(path)
|
|
|
|
|
else {
|
|
|
|
|
fs.mkdir(path.resolve(tmp_path, `export`)).then(() => {
|
|
|
|
|
res(path.resolve(tmp_path, `export`))
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Given a page and a template working directory, render that page as HTML.
|
|
|
|
|
* @param {Page} page
|
|
|
|
|
* @param {string} work_dir
|
|
|
|
|
* @return {Promise<string>}
|
|
|
|
|
*/
|
|
|
|
|
async page_as_html(page, work_dir) {
|
|
|
|
|
const Codium = this.models.get('api:Codium')
|
|
|
|
|
const FileGroup = this.models.get('api:FileGroup')
|
|
|
|
|
const File = this.models.get('upload::File')
|
|
|
|
|
const Database = this.models.get('api:db:Database')
|
|
|
|
|
const DBEntry = this.models.get('api:db:DBEntry')
|
|
|
|
|
|
|
|
|
|
let html = ''
|
|
|
|
|
|
|
|
|
|
const nodes = await page.nodes
|
|
|
|
|
for ( const node of nodes ) {
|
|
|
|
|
// ATM, there are 4 node types: norm, database_ref, files_ref, and code_ref
|
|
|
|
|
// ATM, there are 5 node types: norm, markdown, database_ref, files_ref, and code_ref
|
|
|
|
|
|
|
|
|
|
if ( node.Value.Mode === 'norm' ) {
|
|
|
|
|
html += node.Value.Value
|
|
|
|
|
} else if ( node.Value.Mode === 'markdown' ) {
|
|
|
|
|
html += md.toHTML(node.Value.Value)
|
|
|
|
|
} else if ( node.Type === 'code_ref' ) {
|
|
|
|
|
const code = await Codium.findOne({ UUID: node.Value.Value })
|
|
|
|
|
if ( code ) {
|
|
|
|
@ -132,12 +214,13 @@ class ExportController extends Controller {
|
|
|
|
|
const file = await File.findById(file_id)
|
|
|
|
|
if ( file ) {
|
|
|
|
|
const store_path = file.provider().filepath(file.store_id)
|
|
|
|
|
await this.copy_template(store_path, path.resolve(work_dir, `file-${file.upload_name}`))
|
|
|
|
|
const ext = file.original_name.split('.').reverse()[0]
|
|
|
|
|
await this.copy_template(store_path, path.resolve(work_dir, `file-${file.upload_name}.${ext}`))
|
|
|
|
|
|
|
|
|
|
file_htmls.push(`
|
|
|
|
|
<div class="file">
|
|
|
|
|
<div class="file-name">${file.original_name}</div>
|
|
|
|
|
<a href="file-${file.upload_name}" class="dl-link" target="_blank">▼</a>
|
|
|
|
|
<a href="file-${file.upload_name}.${ext}" class="dl-link" target="_blank">▼</a>
|
|
|
|
|
</div>
|
|
|
|
|
`)
|
|
|
|
|
}
|
|
|
|
@ -148,6 +231,38 @@ class ExportController extends Controller {
|
|
|
|
|
${file_htmls.join('\n')}
|
|
|
|
|
</div>`
|
|
|
|
|
}
|
|
|
|
|
} else if ( node.Type === 'database_ref' ) {
|
|
|
|
|
const grid_id = `dbase-${node.Value.Value}`
|
|
|
|
|
const db = await Database.findOne({ UUID: node.Value.Value })
|
|
|
|
|
|
|
|
|
|
if ( db ) {
|
|
|
|
|
html += `
|
|
|
|
|
<div id="${grid_id}-container" class="database-container">
|
|
|
|
|
<h4>${db.Name}</h4>
|
|
|
|
|
<div id="${grid_id}" class="ag-theme-balham database-ref"></div>
|
|
|
|
|
</div>
|
|
|
|
|
<script src="${grid_id}.js"></script>
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
// generate the column defs
|
|
|
|
|
const cols = await db.get_columns()
|
|
|
|
|
const col_defs = cols.map(col => {
|
|
|
|
|
return {
|
|
|
|
|
field: col.field,
|
|
|
|
|
headerName: col.headerName,
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// generate the data rows
|
|
|
|
|
const entries = await DBEntry.find({ DatabaseId: db.UUID })
|
|
|
|
|
const rows = entries.map(entry => entry.RowData)
|
|
|
|
|
|
|
|
|
|
let dbTemplateContents = await fs.readFile(path.resolve(work_dir, 'database.js'), 'utf-8')
|
|
|
|
|
dbTemplateContents = dbTemplateContents.replace(/{{\s?COLUMN_DEFS\s?}}/g, JSON.stringify(col_defs))
|
|
|
|
|
dbTemplateContents = dbTemplateContents.replace(/{{\s?ROW_DATA\s?}}/g, JSON.stringify(rows))
|
|
|
|
|
dbTemplateContents = dbTemplateContents.replace(/{{\s?GRID_ID\s?}}/g, grid_id)
|
|
|
|
|
await fs.writeFile(path.resolve(work_dir, `${grid_id}.js`), dbTemplateContents)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|