const reviewRoot = '../../target'
const settingsRoot = '../../tools/legal-review'
const express = require('express')
const app = express()
const open = require('open')
const fs = require('fs')
const path = require('path')
// The home page that lists available reports.
app.get('/', function (req, res) {
let html = '
Report review
'
const files = fs.readdirSync(reviewRoot)
const reports = files
.map(f => f.match(/^(.*)-report.html$/))
.filter(m => m != null)
.map(m => m[1])
if (reports.length == 0) {
html +=
'No reports found. ' + 'Run enso / gatherLicenses
first.'
} else {
html += 'Select report:'
html += ''
}
res.send(html)
})
// Serves the injection script.
app.use('/static', express.static('static'))
// Serves contents of the given report, injecting the review-mode script.
app.get('/report/:report', function (req, res) {
const report = req.params['report']
console.log('Opening report for ', report)
fs.readFile(path.join(reviewRoot, report + '-report.html'), 'utf-8', (err, data) => {
const injection =
'' +
''
if (err) {
res.status(400).send(err)
} else {
const injected = data.replace('', injection + '')
res.send(injected)
}
})
})
// Appends a line to the setting file.
function addLine(report, package, file, line) {
const dir = path.join(settingsRoot, report, package)
const location = path.join(dir, file)
console.log('Adding ' + line + ' to ' + location)
fs.mkdirSync(dir, {
recursive: true,
})
fs.appendFileSync(location, line + '\n')
}
// Removes a line from the setting file.
function removeLine(report, package, file, line) {
const location = path.join(settingsRoot, report, package, file)
console.log('Removing ' + line + ' from ' + location)
const lines = fs
.readFileSync(location, 'utf-8')
.split(/\r?\n/)
.filter(x => x.length > 0)
const toRemove = lines.filter(x => x == line)
const others = lines.filter(x => x != line)
if (toRemove.length == 0) {
throw (
'Line ' + line + ' was not present in the file. ' + 'Are you sure the report is up to date?'
)
} else {
var newContent = others.join('\n') + '\n'
if (others.length == 0) {
newContent = ''
}
fs.writeFileSync(location, newContent)
}
}
// Handles the requests to add or remove lines.
app.use(express.urlencoded({ extended: true }))
app.post('/modify/:report', function (req, res) {
const report = req.params['report']
const package = req.body['package']
const action = req.body['action']
const file = req.body['file']
let line = req.body['line']
const encodedLine = req.body['encoded_line']
if (encodedLine !== undefined) {
line = Buffer.from(encodedLine, 'base64').toString()
}
try {
if (action == 'add') {
addLine(report, package, file, line)
} else if (action == 'remove') {
removeLine(report, package, file, line)
} else {
throw 'Unknown action'
}
res.send('OK')
} catch (error) {
console.error(error)
res.status(500).send(error)
}
})
// Handles the request to rename a package directory.
app.post('/rename-package/:report', function (req, res) {
const report = req.params['report']
const from = req.body['from']
const to = req.body['to']
try {
const fromPath = path.join(settingsRoot, report, from)
const toPath = path.join(settingsRoot, report, to)
if (!fs.existsSync(fromPath)) {
throw 'The source directory ' + fromPath + ' does not exist...'
}
if (fs.existsSync(toPath)) {
const isTargetEmpty = fs.readdirSync(toPath).length == 0
if (!isTargetEmpty) {
throw (
'The target directory ' +
toPath +
' already exists. Please merge the directories manually using your preferred file explorer.'
)
}
fs.rmdirSync(toPath)
}
fs.renameSync(fromPath, toPath)
res.send('OK')
} catch (error) {
console.error(error)
res.status(500).send(error)
}
})
app.post('/override-custom-license/:report', function (req, res) {
const report = req.params['report']
const package = req.body['package']
const file = req.body['file']
try {
const dir = path.join(settingsRoot, report, package)
const defaultAndCustom = path.join(dir, 'default-and-custom-license')
if (fs.existsSync(defaultAndCustom)) {
fs.unlinkSync(defaultAndCustom)
}
const fileName = file.split('/').pop()
const custom = path.join(dir, 'custom-license')
fs.writeFileSync(custom, fileName + '\n')
res.send('OK')
} catch (error) {
console.error(error)
res.status(500).send(error)
}
})
app.get('/get-known-license-texts', function (req, res) {
const knownLicenses = fs.readdirSync(path.join(settingsRoot, 'license-texts'))
res.send(JSON.stringify(knownLicenses))
})
app.post('/mark-license-as-reviewed/:report', function (req, res) {
const report = req.params['report']
const licenseName = req.body['licenseName']
const licenseTextPath = 'tools/legal-review/license-texts/' + req.body['licenseTextPath']
const location = path.join(settingsRoot, report, 'reviewed-licenses', licenseName)
console.log(licenseTextPath + ' -> ' + location)
if (fs.existsSync(location)) {
res.status(400).send('License already marked as reviewed')
return
}
fs.writeFileSync(location, licenseTextPath)
res.send('Marked ' + licenseName + ' as reviewed: ' + licenseTextPath)
})
/*
* Listens on a random free port, opens a browser with the home page and waits
* for a newline to terminate.
*/
const server = app.listen(0, () => {
const port = server.address().port
console.log('Listening on at ', 'http://localhost:' + port + '/')
open('http://localhost:' + port + '/')
console.log('Press ENTER to stop the server.')
process.stdin.on('data', function (chunk) {
if (chunk.indexOf('\n') >= 0) {
console.log('Good bye')
process.exit(0)
}
})
})