"use strict"; const express = require('express'), morgan = require('morgan'), cookieParser = require('cookie-parser'), bodyParser = require('body-parser'), http = require('http'), config = require('config'), app = express(), { timestamp } = require('./timestamp'), fs = require('fs'), util = require('util'), mkdir = util.promisify(fs.mkdir), unlink = util.promisify(fs.unlink), path = require('path'), fetch = require('node-fetch'), Promise = require('bluebird'), url = require('url'), { exec } = require('child_process'); fetch.Promise = Promise; const basePath = "/" + config.get("http.base").replace(/^\/*/, "").replace(/\/*$/, "") + "/", dataPath = "/" + config.get("dataPath").replace(/^\/*/, "").replace(/\/*$/, "") + "/"; /* */ if (!config.has("auth.idsid") || !config.has("auth.password")) { console.error("You need to provide credentials to connect to ubit-gfx in config/local.json"); console.error(' "auth": { "idsid": "USERNAME", "password": "PASSWORD" }'); process.exit(-1); } app.use(morgan('common')); app.use(bodyParser.json({ verify: function(req,res,buf) { req.rawBody = buf; } })); app.use(bodyParser.urlencoded({ extended: false })); app.use(cookieParser()); /* Routes: * /api/v1/publish Publish content to repository */ app.get("/*", (req, res, next) => { /* */ return res.status(400).send({ usage: `POST ${basePath}api/v1/publish/:distro/:releaseStream/:url` }); }); const auth = new Buffer(config.get("auth.idsid") + ":" + config.get("auth.password"), 'ascii').toString('base64'); app.post(basePath + 'api/v1/publish/:distro/:releaseStream/:url', function (req, res, next) { const distro = req.params.distro, releaseStream = req.params.releaseStream, remoteUrl = req.params.url; let filename; try { filename = path.basename(url.parse(remoteUrl).pathname); } catch (error) { return res.status(400).send({ error: `Unparsable URL: ${remoteUrl}` }); } if (distro.match(/\//) || releaseStream.match(/\//)) { return res.status(400).send({ error: "Neither distro nor releaseStream may contain '/'" }); } console.log(`POST publish/${distro}-${releaseStream}/${filename}`); const filepath = `${dataPath}${distro}-${releaseStream}`; return mkdir(filepath, { recursive: true }, () => { const pathname = `${filepath}/${filename}`; if (fs.existsSync(pathname)) { return res.status(409).send({ message: `'${distro}-${releaseStream}/${filename}' already exists.` }); } return fetch(remoteUrl, { method: "GET", headers: { 'Authorization': `Basic ${auth}` } }).then(result => { const dest = fs.createWriteStream(pathname); dest.on('finish', () => { exec(`./update-repository.sh ${distro}-${releaseStream}`, { cwd: ".." , shell: "/bin/bash" }, (error, stdout, stderr) => { if (error) { return unlink(pathname).catch(() => { console.error(`Unable to remove ${pathname} after update-repository.sh failed.`); }).then(() => { return res.status(500).send({ message: "Error while updating aptly database.", error: error, stderr: stderr, stdout: stdout }); }); } return res.status(200).send({ message: "OK", stdout: stdout || "", stderr: stderr || "" }); }); }); result.body.pipe(dest); }).catch((error )=> { const message = `Unable to download ${remoteUrl}: ${error}`; console.error(message); return res.status(500).send({ message: message }); }); }).catch((error) => { const message = `Unable to mkdir ${filepath}: ${error}`; console.error(message); return res.status(500).send({ message: message }); }); }); app.post("/*", (req, res, next) => { /* */ return res.status(400).send({ usage: `POST /${basePath}/api/v1/publish/:distro/:releaseStream/:url` }); }); const server = http.createServer(app), port = config.has("port") ? config.get("port") : 6543; server.listen(port); server.on('listening', function() { let addr = server.address(); let bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port; console.log(timestamp() + ` Now serving ${basePath} on ${bind}`); }); module.exports = server;