diff --git a/index.js b/index.js
index 11182b4..551fb1c 100644
--- a/index.js
+++ b/index.js
@@ -5,6 +5,8 @@ const Router = require('@koa/router')
const multer = require('@koa/multer')
const logger = require('koa-logger')
const sendfile = require('koa-sendfile')
+const serve = require('koa-static')
+const mount = require('koa-mount')
const { mkdirp } = require('mkdirp')
const fs = require('fs')
const { spawn } = require('child_process')
@@ -76,7 +78,7 @@ function expireKey (key) {
function flash (ctx, data) {
console.log(data)
- ctx.cookies.set('flash', encodeURIComponent(JSON.stringify(data)), {overwrite: true, httpOnly: false})
+ ctx.cookies.set('flash', encodeURIComponent(JSON.stringify(data)), {overwrite: true, httpOnly: false, sameSite: 'strict', maxAge: 10 * 1000})
}
const app = new Koa()
@@ -142,7 +144,8 @@ router.post('/generate', async ctx => {
const info = {
created: new Date(),
agent: agent,
- file: null
+ file: null,
+ urls: []
}
ctx.keys.set(key, info)
expireKey(key)
@@ -200,123 +203,153 @@ router.post('/upload', upload.single('file'), async ctx => {
return
}
- if (!ctx.request.file || ctx.request.file.size === 0) {
- flash(ctx, {
- message: 'Invalid file submitted',
- success: false,
- key: key
- })
- ctx.redirect('back', '/')
- if (ctx.request.file) {
+ const info = ctx.keys.get(key)
+ expireKey(key)
+
+ let url = null
+ if (ctx.request.body.url) {
+ url = ctx.request.body.url.trim()
+ if (url.length > 0 && !info.urls.includes(url)) {
+ info.urls.push(url)
+ }
+ }
+
+ let conversion = null
+ let filename = ""
+
+ if (ctx.request.file) {
+ if (ctx.request.file.size === 0) {
+ flash(ctx, {
+ message: 'Invalid file submitted',
+ success: false,
+ key: key
+ })
+ ctx.redirect('back', '/')
fs.unlink(ctx.request.file.path, (err) => {
if (err) console.error(err)
else console.log('Removed file', ctx.request.file.path)
})
+ return
+ }
+
+ const mimetype = ctx.request.file.mimetype
+
+ const type = await FileType.fromFile(ctx.request.file.path)
+
+ if ((!type || !allowedTypes.includes(type.mime)) && !allowedTypes.includes(mimetype)) {
+ flash(ctx, {
+ message: 'Uploaded file is of an invalid type: ' + ctx.request.file.originalname + ' (' + (type? type.mime : 'unknown mimetype') + ')',
+ success: false,
+ key: key
+ })
+ ctx.redirect('back', '/')
+ fs.unlink(ctx.request.file.path, (err) => {
+ if (err) console.error(err)
+ else console.log('Removed file', ctx.request.file.path)
+ })
+ return
+ }
+
+ let data = null
+ filename = ctx.request.file.originalname
+
+ if (mimetype === TYPE_EPUB && info.agent.includes('Kindle')) {
+ // convert to .mobi
+ conversion = 'kindlegen'
+ const outname = ctx.request.file.path.replace(/\.epub$/i, '.mobi')
+ filename = filename.replace(/\.kepub\.epub$/i, '.epub').replace(/\.epub$/i, '.mobi')
+
+ data = await new Promise((resolve, reject) => {
+ const kindlegen = spawn('kindlegen', [basename(ctx.request.file.path), '-dont_append_source', '-c1', '-o', basename(outname)], {
+ stdio: 'inherit',
+ cwd: dirname(ctx.request.file.path)
+ })
+ kindlegen.once('close', (code) => {
+ fs.unlink(ctx.request.file.path, (err) => {
+ if (err) console.error(err)
+ else console.log('Removed file', ctx.request.file.path)
+ })
+ fs.unlink(ctx.request.file.path.replace(/\.epub$/i, '.mobi8'), (err) => {
+ if (err) console.error(err)
+ else console.log('Removed file', ctx.request.file.path.replace(/\.epub$/i, '.mobi8'))
+ })
+ if (code !== 0) {
+ console.warn('kindlegen error code ' + code)
+ }
+
+ resolve(outname)
+ })
+ })
+
+ } else if (mimetype === TYPE_EPUB && info.agent.includes('Kobo') && ctx.request.body.kepubify) {
+ // convert to Kobo EPUB
+ conversion = 'kepubify'
+ const outname = ctx.request.file.path.replace(/\.epub$/i, '.kepub.epub')
+ filename = filename.replace(/\.kepub\.epub$/i, '.epub').replace(/\.epub$/i, '.kepub.epub')
+
+ data = await new Promise((resolve, reject) => {
+ const kepubify = spawn('kepubify', ['-v', '-u', '-o', basename(outname), basename(ctx.request.file.path)], {
+ stdio: 'inherit',
+ cwd: dirname(ctx.request.file.path)
+ })
+ kepubify.once('close', (code) => {
+ fs.unlink(ctx.request.file.path, (err) => {
+ if (err) console.error(err)
+ else console.log('Removed file', ctx.request.file.path)
+ })
+ if (code !== 0) {
+ reject('kepubify error code ' + code)
+ return
+ }
+
+ resolve(outname)
+ })
+ })
+ } else {
+ // No conversion
+ data = ctx.request.file.path
+ }
+
+ expireKey(key)
+ if (info.file && info.file.path) {
+ await new Promise((resolve, reject) => fs.unlink(info.file.path, (err) => {
+ if (err) return reject(err)
+ else console.log('Removed previously uploaded file', info.file.path)
+ resolve()
+ }))
+ }
+ info.file = {
+ name: filename,
+ path: data,
+ // size: ctx.request.file.size,
+ uploaded: new Date()
}
- return
}
- const mimetype = ctx.request.file.mimetype
+ let messages = []
+ if (ctx.request.file) {
+ messages.push('Upload successful! ' + (conversion ? ' Ebook was converted with ' + conversion + ' and sent' : ' Sent')+' to '+(info.agent.includes('Kobo') ? 'a Kobo device.' : (info.agent.includes('Kindle') ? 'a Kindle device.' : 'a device.')))
+ messages.push('Filename: ' + filename)
+ }
+ if (url) {
+ messages.push("Added url: " + url)
+ }
- const type = await FileType.fromFile(ctx.request.file.path)
-
- if ((!type || !allowedTypes.includes(type.mime)) && !allowedTypes.includes(mimetype)) {
+ if (messages.length === 0) {
flash(ctx, {
- message: 'Uploaded file is of an invalid type: ' + ctx.request.file.originalname + ' (' + (type? type.mime : 'unknown mimetype') + ')',
+ message: 'No file or url selected',
success: false,
key: key
})
ctx.redirect('back', '/')
- fs.unlink(ctx.request.file.path, (err) => {
- if (err) console.error(err)
- else console.log('Removed file', ctx.request.file.path)
- })
return
}
- const info = ctx.keys.get(key)
- expireKey(key)
-
- let data = null
- let filename = ctx.request.file.originalname
- let conversion = null
-
- if (mimetype === TYPE_EPUB && info.agent.includes('Kindle')) {
- // convert to .mobi
- conversion = 'kindlegen'
- const outname = ctx.request.file.path.replace(/\.epub$/i, '.mobi')
- filename = filename.replace(/\.kepub\.epub$/i, '.epub').replace(/\.epub$/i, '.mobi')
-
- data = await new Promise((resolve, reject) => {
- const kindlegen = spawn('kindlegen', [basename(ctx.request.file.path), '-dont_append_source', '-c1', '-o', basename(outname)], {
- stdio: 'inherit',
- cwd: dirname(ctx.request.file.path)
- })
- kindlegen.once('close', (code) => {
- fs.unlink(ctx.request.file.path, (err) => {
- if (err) console.error(err)
- else console.log('Removed file', ctx.request.file.path)
- })
- fs.unlink(ctx.request.file.path.replace(/\.epub$/i, '.mobi8'), (err) => {
- if (err) console.error(err)
- else console.log('Removed file', ctx.request.file.path.replace(/\.epub$/i, '.mobi8'))
- })
- if (code !== 0) {
- console.warn('kindlegen error code ' + code)
- }
-
- resolve(outname)
- })
- })
-
- } else if (mimetype === TYPE_EPUB && info.agent.includes('Kobo') && ctx.request.body.kepubify) {
- // convert to Kobo EPUB
- conversion = 'kepubify'
- const outname = ctx.request.file.path.replace(/\.epub$/i, '.kepub.epub')
- filename = filename.replace(/\.kepub\.epub$/i, '.epub').replace(/\.epub$/i, '.kepub.epub')
-
- data = await new Promise((resolve, reject) => {
- const kepubify = spawn('kepubify', ['-v', '-u', '-o', basename(outname), basename(ctx.request.file.path)], {
- stdio: 'inherit',
- cwd: dirname(ctx.request.file.path)
- })
- kepubify.once('close', (code) => {
- fs.unlink(ctx.request.file.path, (err) => {
- if (err) console.error(err)
- else console.log('Removed file', ctx.request.file.path)
- })
- if (code !== 0) {
- reject('kepubify error code ' + code)
- return
- }
-
- resolve(outname)
- })
- })
- } else {
- // No conversion
- data = ctx.request.file.path
- }
-
- expireKey(key)
- if (info.file && info.file.path) {
- await new Promise((resolve, reject) => fs.unlink(info.file.path, (err) => {
- if (err) return reject(err)
- else console.log('Removed previously uploaded file', info.file.path)
- resolve()
- }))
- }
- info.file = {
- name: filename,
- path: data,
- // size: ctx.request.file.size,
- uploaded: new Date()
- }
-
flash(ctx, {
- message: 'Upload successful!
'+(conversion ? ' Ebook was converted with ' + conversion + ' and sent' : ' Sent')+' to '+(info.agent.includes('Kobo') ? 'a Kobo device.' : (info.agent.includes('Kindle') ? 'a Kindle device.' : 'a device.'))+'
Filename: ' + filename,
+ message: messages.join("
"),
success: true,
- key: key
+ key: key,
+ url: url
})
ctx.redirect('back', '/')
})
@@ -349,28 +382,30 @@ router.get('/status/:key', async ctx => {
file: info.file ? {
name: info.file.name,
// size: info.file.size
- } : null
+ } : null,
+ urls: info.urls
}
})
-router.get('/style.css', async ctx => {
- await sendfile(ctx, 'style.css')
-})
-
router.get('/receive', async ctx => {
- await sendfile(ctx, 'download.html')
+ await sendfile(ctx, 'static/download.html')
})
router.get('/', async ctx => {
const agent = ctx.get('user-agent')
console.log(ctx.ip, agent)
- await sendfile(ctx, agent.includes('Kobo') || agent.includes('Kindle')? 'download.html' : 'upload.html')
+ await sendfile(ctx, agent.includes('Kobo') || agent.includes('Kindle')? 'static/download.html' : 'static/upload.html')
})
app.use(router.routes())
app.use(router.allowedMethods())
+app.use(serve("static"))
+
+app.use(mount("/node_modules/crypto-js", serve("node_modules/crypto-js")))
+
+
fs.rm('uploads', {recursive: true}, (err) => {
if (err) throw err
mkdirp('uploads').then (() => {
diff --git a/package-lock.json b/package-lock.json
index 239ca46..ac21a8a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,7 +14,9 @@
"file-type": "^16.5.4",
"koa": "^2.14.2",
"koa-logger": "^3.2.1",
+ "koa-mount": "^4.0.0",
"koa-sendfile": "^3.0.0",
+ "koa-static": "^5.0.0",
"mkdirp": "^3.0.1",
"multer": "^1.4.5-lts.1",
"transliteration": "^2.3.5"
@@ -1054,6 +1056,62 @@
"node": ">= 7.6.0"
}
},
+ "node_modules/koa-mount": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/koa-mount/-/koa-mount-4.0.0.tgz",
+ "integrity": "sha512-rm71jaA/P+6HeCpoRhmCv8KVBIi0tfGuO/dMKicbQnQW/YJntJ6MnnspkodoA4QstMVEZArsCphmd0bJEtoMjQ==",
+ "dependencies": {
+ "debug": "^4.0.1",
+ "koa-compose": "^4.1.0"
+ },
+ "engines": {
+ "node": ">= 7.6.0"
+ }
+ },
+ "node_modules/koa-send": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/koa-send/-/koa-send-5.0.1.tgz",
+ "integrity": "sha512-tmcyQ/wXXuxpDxyNXv5yNNkdAMdFRqwtegBXUaowiQzUKqJehttS0x2j0eOZDQAyloAth5w6wwBImnFzkUz3pQ==",
+ "dependencies": {
+ "debug": "^4.1.1",
+ "http-errors": "^1.7.3",
+ "resolve-path": "^1.4.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/koa-send/node_modules/depd": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+ "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/koa-send/node_modules/http-errors": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz",
+ "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==",
+ "dependencies": {
+ "depd": "~1.1.2",
+ "inherits": "2.0.4",
+ "setprototypeof": "1.2.0",
+ "statuses": ">= 1.5.0 < 2",
+ "toidentifier": "1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/koa-send/node_modules/statuses": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+ "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/koa-sendfile": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/koa-sendfile/-/koa-sendfile-3.0.0.tgz",
@@ -1066,6 +1124,26 @@
"node": ">= 10"
}
},
+ "node_modules/koa-static": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/koa-static/-/koa-static-5.0.0.tgz",
+ "integrity": "sha512-UqyYyH5YEXaJrf9S8E23GoJFQZXkBVJ9zYYMPGz919MSX1KuvAcycIuS0ci150HCoPf4XQVhQ84Qf8xRPWxFaQ==",
+ "dependencies": {
+ "debug": "^3.1.0",
+ "koa-send": "^5.0.0"
+ },
+ "engines": {
+ "node": ">= 7.6.0"
+ }
+ },
+ "node_modules/koa-static/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
"node_modules/koa/node_modules/http-errors": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz",
@@ -1245,6 +1323,14 @@
"resolved": "https://registry.npmjs.org/passthrough-counter/-/passthrough-counter-1.0.0.tgz",
"integrity": "sha512-Wy8PXTLqPAN0oEgBrlnsXPMww3SYJ44tQ8aVrGAI4h4JZYCS0oYqsPqtPR8OhJpv6qFbpbB7XAn0liKV7EXubA=="
},
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/path-to-regexp": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz",
@@ -1327,6 +1413,58 @@
"node": ">=0.10.0"
}
},
+ "node_modules/resolve-path": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/resolve-path/-/resolve-path-1.4.0.tgz",
+ "integrity": "sha512-i1xevIst/Qa+nA9olDxLWnLk8YZbi8R/7JPbCMcgyWaFR6bKWaexgJgEB5oc2PKMjYdrHynyz0NY+if+H98t1w==",
+ "dependencies": {
+ "http-errors": "~1.6.2",
+ "path-is-absolute": "1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/resolve-path/node_modules/depd": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+ "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/resolve-path/node_modules/http-errors": {
+ "version": "1.6.3",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
+ "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==",
+ "dependencies": {
+ "depd": "~1.1.2",
+ "inherits": "2.0.3",
+ "setprototypeof": "1.1.0",
+ "statuses": ">= 1.4.0 < 2"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/resolve-path/node_modules/inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw=="
+ },
+ "node_modules/resolve-path/node_modules/setprototypeof": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
+ "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
+ },
+ "node_modules/resolve-path/node_modules/statuses": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+ "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
diff --git a/package.json b/package.json
index b069aac..9e49a1f 100644
--- a/package.json
+++ b/package.json
@@ -14,7 +14,9 @@
"file-type": "^16.5.4",
"koa": "^2.14.2",
"koa-logger": "^3.2.1",
+ "koa-mount": "^4.0.0",
"koa-sendfile": "^3.0.0",
+ "koa-static": "^5.0.0",
"mkdirp": "^3.0.1",
"multer": "^1.4.5-lts.1",
"transliteration": "^2.3.5"
diff --git a/static/common.js b/static/common.js
new file mode 100644
index 0000000..60eee2b
--- /dev/null
+++ b/static/common.js
@@ -0,0 +1,41 @@
+'use strict'
+
+function log(str) {
+ var node = document.createElement("div")
+ node.textContent = str
+ logs.appendChild(node)
+}
+window.addEventListener("error", function (event) {
+ log(event.filename + ":" + event.lineno + " " + event.message)
+}, false)
+
+function xhr(method, url, cb) {
+ var x = new XMLHttpRequest()
+ x.onload = function () {
+ cb(x)
+ }
+ x.onerror = function () {
+ cb(x)
+ }
+ x.open(method, url, true)
+ x.send(null)
+}
+
+var isIOS = /iPad|iPhone|iPod/.test(navigator.platform) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)
+
+function getCookies() {
+ var cookieRegex = /([\w\.]+)\s*=\s*(?:"((?:\\"|[^"])*)"|(.*?))\s*(?:[;,]|$)/g
+ var cookies = {}
+ var match
+ while( (match = cookieRegex.exec(document.cookie)) !== null ) {
+ var value = match[2] || match[3]
+ cookies[match[1]] = decodeURIComponent(value)
+ try {
+ cookies[match[1]] = JSON.parse(cookies[match[1]])
+ } catch (err) {}
+ }
+ return cookies
+}
+// function deleteCookie(name) {
+// document.cookie = name + "= ; expires = Thu, 01 Jan 1970 00:00:00 GMT"
+// }
diff --git a/download.html b/static/download.html
similarity index 71%
rename from download.html
rename to static/download.html
index 7daf0a5..84eeaed 100644
--- a/download.html
+++ b/static/download.html
@@ -4,7 +4,8 @@