mirror of
https://github.com/SrIzan10/makesweet-api.git
synced 2026-05-01 10:55:14 +00:00
refactor a bit
This commit is contained in:
176
index.js
176
index.js
@@ -5,46 +5,176 @@ const childProcess = require('child_process')
|
|||||||
const multer = require('multer')
|
const multer = require('multer')
|
||||||
const uuid = require('uuid')
|
const uuid = require('uuid')
|
||||||
const fs = require ('fs')
|
const fs = require ('fs')
|
||||||
const app = express()
|
const os = require('os')
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
const workDir = '/tmp/api'
|
|
||||||
const port = 3000
|
const port = 3000
|
||||||
|
|
||||||
|
const workDir = '/tmp/makesweet-api'
|
||||||
const upload = multer({ dest: workDir })
|
const upload = multer({ dest: workDir })
|
||||||
|
const app = express()
|
||||||
|
|
||||||
app.get('/', (req, res) => {
|
app.get('/', (req, res) => {
|
||||||
res.send('Hello World!')
|
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) {
|
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`
|
const textHtml = `${process.cwd()}/text.html`;
|
||||||
if (!fs.existsSync(template)) {
|
const output = `${workDir}/${uuid.v4()}.gif`;
|
||||||
return res.status(404).json({error: "i seek the template everywhere, but it is not to be found. try 'heart-locket'"})
|
const template = getTemplatePathFromName(req.params.template);
|
||||||
}
|
const generator = new Generator();
|
||||||
const path=`${workDir}/${uuid.v4()}.gif`
|
generator.useTemplate(template);
|
||||||
try {
|
try {
|
||||||
const cwd = process.cwd()
|
generator.addTexts(req.query.text, textHtml);
|
||||||
const command = ['run', '-v', `${workDir}:${workDir}`, '-v', `${cwd}:/share`, 'paulfitz/makesweet', '--zip', template, '--in']
|
generator.addImages(req.files);
|
||||||
for (const file of req.files) {
|
generator.setOutput(output);
|
||||||
command.push(file.path)
|
res.send(generator.apply());
|
||||||
}
|
|
||||||
command.push('--gif', path)
|
|
||||||
console.log(command)
|
|
||||||
|
|
||||||
const result = childProcess.execFileSync('docker', command).stdout
|
|
||||||
res.send(fs.readFileSync(path))
|
|
||||||
} finally {
|
} finally {
|
||||||
for (const file of req.files) {
|
generator.clean();
|
||||||
fs.unlinkSync(file.path)
|
|
||||||
}
|
|
||||||
fs.unlinkSync(path)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
app.use(handleErrors)
|
||||||
|
|
||||||
app.listen(port, () => {
|
app.listen(port, () => {
|
||||||
console.log(`Example app listening at http://localhost:${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}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
41
text.html
Normal file
41
text.html
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-size: 240px;
|
||||||
|
}
|
||||||
|
#target {
|
||||||
|
text-align: center;
|
||||||
|
white-space: pre-line;
|
||||||
|
text-shadow: 0px 0px 8px #fff, 0px 0px 8px #fff, 0px 0px 8px #fff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p id="target">
|
||||||
|
This is a test<br>my friend.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function callback(txt) {
|
||||||
|
document.getElementById("target").innerHTML = JSON.parse(txt).text;
|
||||||
|
window.status = "ready_to_print";
|
||||||
|
}
|
||||||
|
function loadJSON() {
|
||||||
|
|
||||||
|
var xobj = new XMLHttpRequest();
|
||||||
|
xobj.overrideMimeType("application/json");
|
||||||
|
xobj.open('GET', './text.json', true);
|
||||||
|
xobj.onreadystatechange = function () {
|
||||||
|
if (xobj.readyState == 4) {
|
||||||
|
// Required use of an anonymous callback as .open will NOT return a value but simply returns undefined in asynchronous mode
|
||||||
|
callback(xobj.responseText);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
xobj.send(null);
|
||||||
|
}
|
||||||
|
loadJSON();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user