diff --git a/controllers/albumsController.js b/controllers/albumsController.js index 2a83b01..397bb48 100644 --- a/controllers/albumsController.js +++ b/controllers/albumsController.js @@ -32,7 +32,7 @@ albumsController.list = async (req, res, next) => { let fields = ['id', 'name'] if (req.params.sidebar === undefined) { - fields = fields.concat(fields, ['timestamp', 'identifier', 'editedAt', 'download', 'public']) + fields = fields.concat(fields, ['timestamp', 'identifier', 'editedAt', 'download', 'public', 'description']) } const albums = await db.table('albums') @@ -70,7 +70,7 @@ albumsController.create = async (req, res, next) => { const user = await utils.authorize(req, res) if (!user) { return } - const name = req.body.name + const name = utils.escape(req.body.name) if (name === undefined || name === '') { return res.json({ success: false, description: 'No album name specified.' }) } @@ -102,7 +102,8 @@ albumsController.create = async (req, res, next) => { editedAt: 0, zipGeneratedAt: 0, download: (req.body.download === false || req.body.download === 0) ? 0 : 1, - public: (req.body.public === false || req.body.public === 0) ? 0 : 1 + public: (req.body.public === false || req.body.public === 0) ? 0 : 1, + description: utils.escape(req.body.description) || '' }) return res.json({ success: true, id: ids[0] }) @@ -191,14 +192,14 @@ albumsController.edit = async (req, res, next) => { return res.json({ success: false, description: 'No album specified.' }) } - const name = req.body.name + const name = utils.escape(req.body.name) if (name === undefined || name === '') { return res.json({ success: false, description: 'No name specified.' }) } const album = await db.table('albums') .where({ - name, + id, userid: user.id, enabled: 1 }) @@ -221,7 +222,8 @@ albumsController.edit = async (req, res, next) => { .update({ name, download: Boolean(req.body.download), - public: Boolean(req.body.public) + public: Boolean(req.body.public), + description: utils.escape(req.body.description) || '' }) if (req.body.requestLink) { diff --git a/controllers/utilsController.js b/controllers/utilsController.js index 6fd02d5..efa05e7 100644 --- a/controllers/utilsController.js +++ b/controllers/utilsController.js @@ -54,6 +54,58 @@ utilsController.extname = filename => { return extname + multi } +utilsController.escape = string => { + // MIT License + // Copyright(c) 2012-2013 TJ Holowaychuk + // Copyright(c) 2015 Andreas Lubbe + // Copyright(c) 2015 Tiancheng "Timothy" Gu + + if (!string) { return string } + + const str = '' + string + const match = /["'&<>]/.exec(str) + + if (!match) { return str } + + let escape + let html = '' + let index = 0 + let lastIndex = 0 + + for (index = match.index; index < str.length; index++) { + switch (str.charCodeAt(index)) { + case 34: // " + escape = '"' + break + case 38: // & + escape = '&' + break + case 39: // ' + escape = ''' + break + case 60: // < + escape = '<' + break + case 62: // > + escape = '>' + break + default: + continue + } + + if (lastIndex !== index) { + html += str.substring(lastIndex, index) + } + + lastIndex = index + 1 + html += escape + } + + return lastIndex !== index + ? html + str.substring(lastIndex, index) + : html +} + utilsController.authorize = async (req, res) => { const token = req.headers.token if (token === undefined) { diff --git a/database/db.js b/database/db.js index 656789b..ef24fc2 100644 --- a/database/db.js +++ b/database/db.js @@ -15,6 +15,7 @@ const init = function (db) { table.integer('zipGeneratedAt') table.integer('download') table.integer('public') + table.string('description') }).then(() => {}) } }) diff --git a/database/migration.js b/database/migration.js index 368197c..3ed13d0 100644 --- a/database/migration.js +++ b/database/migration.js @@ -7,7 +7,8 @@ const map = { editedAt: 'integer', zipGeneratedAt: 'integer', download: 'integer', - public: 'integer' + public: 'integer', + description: 'string' }, users: { enabled: 'integer', @@ -23,11 +24,11 @@ migration.start = async () => { const columns = Object.keys(map[table]) return Promise.all(columns.map(async column => { if (await db.schema.hasColumn(table, column)) { - return console.log(`Column "${column}" already exists in table "${table}".`) + return // console.log(`SKIP: ${column} => ${table}.`) } const columnType = map[table][column] return db.schema.table(table, t => { t[columnType](column) }) - .then(() => console.log(`Added column "${column}" to table "${table}".`)) + .then(() => console.log(`OK: ${column} (${columnType}) => ${table}.`)) .catch(console.error) })) })) @@ -45,7 +46,7 @@ migration.start = async () => { console.log(`Updated root's permission to ${perms.permissions.superadmin} (superadmin).`) }) - console.log('Migration finished! Now start lolisafe normally') + console.log('Migration finished! Now you may start lolisafe normally.') process.exit(0) } diff --git a/public/css/album.css b/public/css/album.css index be3f18b..bfd8096 100644 --- a/public/css/album.css +++ b/public/css/album.css @@ -13,6 +13,12 @@ box-shadow: 0 20px 60px rgba(10, 10, 10, 0.05), 0 5px 10px rgba(10, 10, 10, 0.1), 0 1px 1px rgba(10, 10, 10, 0.2); } +@media screen and (max-width: 768px) { + .description { + text-align: center; + } +} + /* This is the same sheets used in dashboard.css, minus the on-hover ones. I should probably put this in a file named _thumbs.css, remove the ones in dashboard.css, diff --git a/public/css/style.css b/public/css/style.css index 78513dd..1b558ff 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -57,19 +57,23 @@ hr { color: #7f8c8d; } -.input::-moz-placeholder { +.input::-moz-placeholder, +.textarea::-moz-placeholder { color: #7f8c8d; } -.input::-webkit-input-placeholder { +.input::-webkit-input-placeholder, +.textarea::-webkit-input-placeholder { color: #7f8c8d; } -.input:-moz-placeholder { +.input:-moz-placeholder, +.textarea:-moz-placeholder { color: #7f8c8d; } -.input:-ms-input-placeholder { +.input:-ms-input-placeholder, +.textarea:-ms-input-placeholder { color: #7f8c8d; } diff --git a/public/js/album.js b/public/js/album.js index 8146e9c..d77af95 100644 --- a/public/js/album.js +++ b/public/js/album.js @@ -10,6 +10,7 @@ const page = { page.getPrettyBytes = num => { // MIT License // Copyright (c) Sindre Sorhus (sindresorhus.com) + if (!Number.isFinite(num)) { return num } const neg = num < 0 diff --git a/public/js/dashboard.js b/public/js/dashboard.js index b9425c2..bb066de 100644 --- a/public/js/dashboard.js +++ b/public/js/dashboard.js @@ -1046,6 +1046,11 @@ page.getAlbums = function () { +
+
+ +
+
@@ -1090,7 +1095,8 @@ page.getAlbums = function () { page.cache.albums[album.id] = { name: album.name, download: album.download, - public: album.public + public: album.public, + description: album.description } const tr = document.createElement('tr') @@ -1139,9 +1145,13 @@ page.editAlbum = function (id) { const div = document.createElement('div') div.innerHTML = `
-
- + +
+
+
+
+
@@ -1186,6 +1196,7 @@ page.editAlbum = function (id) { axios.post('api/albums/edit', { id, name: document.getElementById('swalName').value, + description: document.getElementById('swalDescription').value, download: document.getElementById('swalDownload').checked, public: document.getElementById('swalPublic').checked, requestLink: document.getElementById('swalRequestLink').checked @@ -1265,7 +1276,8 @@ page.submitAlbum = function (element) { page.isLoading(element, true) axios.post('api/albums', { - name: document.getElementById('albumName').value + name: document.getElementById('albumName').value, + description: document.getElementById('albumDescription').value }).then(function (response) { if (!response) { return } @@ -1572,6 +1584,7 @@ page.getPrettyDate = date => { page.getPrettyBytes = num => { // MIT License // Copyright (c) Sindre Sorhus (sindresorhus.com) + if (!Number.isFinite(num)) { return num } const neg = num < 0 diff --git a/public/js/home.js b/public/js/home.js index be557af..5df9f23 100644 --- a/public/js/home.js +++ b/public/js/home.js @@ -384,9 +384,13 @@ page.createAlbum = function () { const div = document.createElement('div') div.innerHTML = `
-
- + +
+
+
+
+
@@ -423,6 +427,7 @@ page.createAlbum = function () { const name = document.getElementById('swalName').value axios.post('api/albums', { name, + description: document.getElementById('swalDescription').value, download: document.getElementById('swalDownload').checked, public: document.getElementById('swalPublic').checked }, { diff --git a/routes/album.js b/routes/album.js index 77c8e1a..bf0e615 100644 --- a/routes/album.js +++ b/routes/album.js @@ -57,6 +57,7 @@ routes.get('/a/:identifier', async (req, res, next) => { return res.render('album', { title: album.name, + description: album.description.replace(/\n/g, '
'), count: files.length, thumb, files, diff --git a/views/_globals.njk b/views/_globals.njk index fc210e1..a21817b 100644 --- a/views/_globals.njk +++ b/views/_globals.njk @@ -15,7 +15,7 @@ v2: Images and config files (manifest.json, browserconfig.xml, etc). v3: CSS and JS files (libs such as bulma, lazyload, etc). #} -{% set v1 = "0mdbHpy0x2" %} +{% set v1 = "9cK64TLE3Z" %} {% set v2 = "Ii3JYKIhb0" %} {% set v3 = "ll7yHY3b2b" %} diff --git a/views/album.njk b/views/album.njk index 1586553..843bd4b 100644 --- a/views/album.njk +++ b/views/album.njk @@ -16,7 +16,7 @@ {% block opengraph %} - + @@ -24,7 +24,7 @@ - + {% endblock %} @@ -37,7 +37,7 @@

- {{ title }} + {{ title | safe }}

@@ -51,7 +51,7 @@

{% if downloadLink -%} - Download album + Download album {%- else -%} Download disabled {%- endif %} @@ -59,12 +59,10 @@

{%- endif %} - {% if generateZips and downloadLink and files.length -%} -
-
- Album archives may be cached by CDN, if the one you have downloaded seems outdated, refresh the page to get the latest download link. -
-
+ {% if description -%} +

+ {{ description | safe }} +

{%- endif %}
{% if files.length -%}