From 6e36fafe6e31aea9984208ee96d75615e04f9d41 Mon Sep 17 00:00:00 2001 From: Paul Fitzpatrick Date: Wed, 17 Nov 2021 19:46:26 -0500 Subject: [PATCH] refactor a bit --- index.js | 176 +++++++++++++++++++++++++++++++++++++++++++++++------- text.html | 41 +++++++++++++ 2 files changed, 194 insertions(+), 23 deletions(-) create mode 100644 text.html diff --git a/index.js b/index.js index b570f54..ae64ee9 100644 --- a/index.js +++ b/index.js @@ -5,46 +5,176 @@ const childProcess = require('child_process') const multer = require('multer') const uuid = require('uuid') const fs = require ('fs') -const app = express() +const os = require('os') +const path = require('path') -const workDir = '/tmp/api' const port = 3000 +const workDir = '/tmp/makesweet-api' const upload = multer({ dest: workDir }) +const app = express() app.get('/', (req, res) => { res.send('Hello World!') }) -app.post('/make/:template', upload.any('images'), (req, res) => { +app.post('/make/:template', upload.any('images'), handleErrors, (req, res) => { if (!req.files) { - return res.status(400).json({error: "i hunger for images..."}) + throw new ApiError(400, 'i hunger for images...'); } - const template = `templates/${req.params.template}.zip` - if (!fs.existsSync(template)) { - return res.status(404).json({error: "i seek the template everywhere, but it is not to be found. try 'heart-locket'"}) - } - const path=`${workDir}/${uuid.v4()}.gif` + const textHtml = `${process.cwd()}/text.html`; + const output = `${workDir}/${uuid.v4()}.gif`; + const template = getTemplatePathFromName(req.params.template); + const generator = new Generator(); + generator.useTemplate(template); try { - const cwd = process.cwd() - const command = ['run', '-v', `${workDir}:${workDir}`, '-v', `${cwd}:/share`, 'paulfitz/makesweet', '--zip', template, '--in'] - for (const file of req.files) { - command.push(file.path) - } - command.push('--gif', path) - console.log(command) - - const result = childProcess.execFileSync('docker', command).stdout - res.send(fs.readFileSync(path)) + generator.addTexts(req.query.text, textHtml); + generator.addImages(req.files); + generator.setOutput(output); + res.send(generator.apply()); } finally { - for (const file of req.files) { - fs.unlinkSync(file.path) - } - fs.unlinkSync(path) + generator.clean(); } }) +app.use(handleErrors) + app.listen(port, () => { console.log(`Example app listening at http://localhost:${port}`) }) +function execSync(command) { + console.log("Executing: " + command); + childProcess.execSync(command, { stdio: 'inherit' }); +} + +function text2image(htmlTemplate, outputDir, prefix, txt, border) { + console.log("text2image:", txt); + const data = { + text: txt.replace(/[ \n\r\t]+\/\/[ \n\r\t]+/g, '\n').trim() + }; + fs.writeFileSync(`${outputDir}/text.json`, JSON.stringify(data)); + fs.copyFileSync(htmlTemplate, `${outputDir}/text.html`); + console.log("ready"); + execSync(`wkhtmltoimage --enable-local-file-access --transparent --width 3000 --window-status ready_to_print ${outputDir}/text.html ${outputDir}/${prefix}.png`); + console.log("123"); + execSync(`convert ${outputDir}/${prefix}.png -trim -bordercolor none -border ` + border + ` ${outputDir}/${prefix}2.png`); + console.log("234"); + return `${outputDir}/${prefix}2.png`; +} + +function getTemplatePathFromName(name) { + if (!name) { + throw new ApiError(400, 'i must have a template i cannot do anything without a template'); + } + const nameStr = String(name); + if (!nameStr.match(/^[-a-zA-Z0-9]+$/)) { + throw new ApiError(400, 'is that really a template: ' + nameStr); + } + const template = `templates/${nameStr}.zip` + if (!fs.existsSync(template)) { + throw new ApiError(404, 'i seek the template everywhere, but it is not to be found.'); + } + return template; +} + +function handleErrors(err, req, res, next) { + console.error(err); + if (err.code) { + return res.status(err.code).json({error: err.message}) + } else { + res.status(500).json({error: err}) + } +} + +class ApiError extends Error { + constructor(code, message) { + super(message); + this.code = code; + } +} + +class Generator { + constructor() { + this.fnames = []; + this.images = []; + this.texts = []; + this.template = null; + this.output = null; + this.tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'makesweet-api')); + } + useFile(fname) { + this.fnames.push(fname); + } + useTemplate(fname) { + this.useFile(fname); + this.template = fname; + } + addImages(files) { + if (!files) { return; } + for (const file of files) { + this.images.push(file.path); + this.fnames.push(file.path); + } + } + addTexts(textOrTexts, htmlTemplate) { + console.log({textOrTexts, htmlTemplate}); + const texts = !textOrTexts ? [] : + (typeof textOrTexts === 'string') ? [textOrTexts] : + [...textOrTexts]; + console.log("texts", texts) + let i = 0; + for (const txt of texts) { + const fname = text2image(htmlTemplate, this.tmpDir, `text_${i}`, txt, 60); + this.texts.push(fname); + this.fnames.push(fname); + i++; + } + } + setOutput(output) { + this.output = output; + this.fnames.push(output); + } + getVolumes() { + const dirs = new Set(this.fnames.map(fname => path.resolve(path.dirname(fname)))); + return [...dirs].sort(); + } + getCommand() { + const command = ['run']; + for (const volume of this.getVolumes()) { + command.push('-v', `${volume}:${volume}`); + } + command.push('--mount', 'type=tmpfs,destination=/share') + + command.push('paulfitz/makesweet'); + command.push('--zip', path.resolve(this.template)); + command.push('--in'); + for (const image of this.images) { + command.push(image); + } + for (const text of this.texts) { + command.push(text); + } + command.push('--gif', this.output); + return command; + } + apply() { + const command = this.getCommand(); + console.log("command:", command); + childProcess.execFileSync('docker', command); + return fs.readFileSync(this.output); + } + clean() { + try { + fs.rmSync(this.tmpDir, { recursive: true }); + for (const image of this.images) { + fs.unlinkSync(image); + } + if (this.output) { + fs.unlinkSync(this.output); + } + } catch (e) { + console.error(`Error removing tmpDir: ${e}`); + } + } +} diff --git a/text.html b/text.html new file mode 100644 index 0000000..9da776f --- /dev/null +++ b/text.html @@ -0,0 +1,41 @@ + + + + + +

+This is a test
my friend. +

+ + + + +