Merge branch 'new-dev' into 'master'

0.1.9

Closes #403, #402, #398, and #397

See merge request fatchan/jschan!233
indiachan-spamvector v0.1.9
Thomas Lynch 3 years ago
commit c80919d34b
  1. 7
      CHANGELOG.md
  2. 2
      configs/nginx/nginx.sh
  3. 8
      gulp/res/css/style.css
  4. BIN
      gulp/res/img/defaultbanner.png
  5. 2
      gulp/res/js/csstoggles.js
  6. 4
      gulp/res/js/filters.js
  7. 3
      gulp/res/js/viewfulltext.js
  8. 2
      gulp/res/js/yous.js
  9. 10
      helpers/filemiddlewares.js
  10. 8
      helpers/files/mimetypes.js
  11. 4
      helpers/imagehash.js
  12. 2
      helpers/tasks.js
  13. 9
      models/forms/addassets.js
  14. 5
      models/forms/addflags.js
  15. 22
      models/forms/changeglobalsettings.js
  16. 9
      models/forms/makepost.js
  17. 9
      models/forms/uploadbanners.js
  18. 20496
      package-lock.json
  19. 7
      package.json
  20. 8
      schedules/tasks/webring.js
  21. 8
      views/mixins/catalogtile.pug
  22. 2
      views/mixins/fileform.pug
  23. 2
      views/mixins/post.pug

@ -68,3 +68,10 @@
- Multiple files & post flags now shown in catalog view
- Faster, more efficient global settings changes
- Add option for board owner to prevent OP deleting threads that are too old or have too many replies
### 0.1.9
- Fix "improved" global settings changes not regenerating custom pages properly
- Postmenu dropdown for filters/moderate added to catalog tiles
- Notifications are no longer sent for posts when clicking "view full text"
- Make handling files with no/incorrect extension better
- Image count from OP is included in catalog tiles file count

@ -173,7 +173,7 @@ fi
if [ "$ROBOTS_TXT_DISALLOW" == "y" ]; then
#add path / (all) to disallow to make robots.txt block all robots instead of allowing
sudo sed -d "s|Disallow:|Disallow: /|g" /etc/nginx/snippets/jschan_common_routes.conf
sudo sed -i "s|Disallow:|Disallow: /|g" /etc/nginx/snippets/jschan_common_routes.conf
fi
if [ "$GEOIP" == "y" ]; then

@ -1398,6 +1398,14 @@ row.wrap.sb .col {
}
}
@media only screen and (min-width: 600px) {
/* not-completely-shit way to "fix" short text squishing on single file posts */
.post-files:not(.fn) + .post-message {
min-width: 40em;
}
}
@media only screen and (max-width: 600px) {
.ct-r2 .catalog-thumb.small {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

@ -42,7 +42,7 @@ class CssToggle {
};
//define the css
const hidePostStubsCss = `.post-container.hidden, .catalog-tile.hidden { visibility: hidden;margin-top: -1.5em;height: 0; }`;
const hidePostStubsCss = `.post-container.hidden, .catalog-tile.hidden { display: none;margin-top: -1.5em;height: 0; }`;
const hideThumbnailsCss = `.file-thumb, .catalog-thumb { visibility: hidden !important; }`;
const hideRecursiveCss = `.op.hidden ~ .anchor, .op.hidden ~ .post-container { display: none; }`;
const heightUnlimitCss = `img, video { max-height: unset; }`;

@ -77,9 +77,7 @@ const togglePostsHidden = (posts, state, single) => {
} else {
elem.classList['add']('hidden');
}
if (!isCatalog) {
elem.querySelector('.postmenu').children[0].textContent = (showing ? 'Hide' : 'Show');
}
}
};
@ -239,7 +237,7 @@ const moderatePost = (postContainer) => {
}
const postMenuChange = function(e) {
const postContainer = this.parentElement.parentElement.parentElement;
const postContainer = this.closest(isCatalog ? '.catalog-tile': '.post-container');
const postDataset = postContainer.dataset
const filterType = this.value;
const hiding = !postContainer.classList.contains('hidden');

@ -62,7 +62,8 @@ window.addEventListener('DOMContentLoaded', (event) => {
detail: {
post: parentPost,
postId: postJson.postId,
json: postJson
json: postJson,
viewfulltext: true,
}
});
window.dispatchEvent(updatePostMessageEvent);

@ -62,7 +62,7 @@ const handleNewYous = (e) => {
//toggle for any quotes in a new post that quote (you)
toggleQuotes(youHoverQuotes, yousEnabled);
//if not a hover newpost, and enabled/for yous, send notification
if (!e.detail.hover && notificationsEnabled && !isYou) {
if (!e.detail.viewfulltext && !e.detail.hover && notificationsEnabled && !isYou) {
if (notificationYousOnly && !quotesYou) {
return; //only send notif for (you) if setting
}

@ -12,6 +12,13 @@ const { debugLogs } = require(__dirname+'/../configs/secrets.js')
'redirect': req.headers.referer
});
}
, missingExtensionLimitFunction = (req, res, next) => {
return dynamicResponse(req, res, 400, 'message', {
'title': 'Bad Request',
'message': 'Missing file extensions',
'redirect': req.headers.referer
});
}
, updateHandlers = () => {
const { globalLimits, filterFileNames, spaceFileNameReplacement } = require(__dirname+'/../config.js').get;
['flag', 'banner', 'asset', 'post'].forEach(fileType => {
@ -36,8 +43,9 @@ const { debugLogs } = require(__dirname+'/../configs/secrets.js')
fileSize: fileSizeLimit.max,
files: fileNumLimit.max,
},
numFilesLimitHandler: fileNumLimitFunction,
limitHandler: fileSizeLimitFunction,
numFilesLimitHandler: fileNumLimitFunction,
extensionLimitHandler: missingExtensionLimitFunction,
useTempFiles: true,
tempFileDir: __dirname+'/../tmp/'
});

@ -53,10 +53,12 @@ module.exports = {
realMimeCheck: async (file) => {
const supposedMimeType = file.mimetype;
const realMimeType = await FileType.fromFile(file.tempFilePath);
if (!realMimeType) {
return config.get.allowMimeNoMatch;
if (realMimeType) {
//note the correct file extension in case it is incorrect/missing
file.extension = `.${realMimeType.ext}`;
return supposedMimeType === realMimeType.mime;
}
return supposedMimeType === realMimeType.mime;
return config.get.allowMimeNoMatch;
},
image, animatedImage, video, audio, other

@ -1,6 +1,6 @@
'use strict';
const imageHash = require('node-image-hash').hash
const imageHash = require('imghash').hash
, config = require(__dirname+'/../config.js');
module.exports = async (req, res, next) => {
@ -11,7 +11,7 @@ module.exports = async (req, res, next) => {
const mainType = req.files.file[i].mimetype.split('/')[0];
if (mainType === 'image' || mainType === 'video') {
hashPromises.push(imageHash(req.files.file[i].tempFilePath, 8, 'hex').then(res => {
req.files.file[i].phash = res.hash;
req.files.file[i].phash = res;
}));
}
}

@ -239,7 +239,7 @@ module.exports = {
let [ totalStats, boards, fileStats, recentNews ] = await Promise.all([
Boards.totalStats(), //overall total posts ever made
Boards.boardSort(0, 20), //top 20 boards sorted by users, pph, total posts
Files.activeContent(), //size ans number of files
Files.activeContent(), //size and number of files
News.find(maxRecentNews), //some recent newsposts
]);
const [ localStats, webringStats ] = totalStats;

@ -48,11 +48,10 @@ module.exports = async (req, res, next) => {
const filenames = [];
for (let i = 0; i < res.locals.numFiles; i++) {
const file = req.files.file[i];
const filename = file.sha256 + path.extname(file.name);
file.filename = filename;
file.filename = file.sha256 + file.extension;
//check if already exists
const exists = await pathExists(`${uploadDirectory}/asset/${req.params.board}/${filename}`);
const exists = await pathExists(`${uploadDirectory}/asset/${req.params.board}/${file.filename}`);
if (exists) {
await remove(file.tempFilePath);
@ -60,10 +59,10 @@ module.exports = async (req, res, next) => {
}
//add to list after checking it doesnt already exist
filenames.push(filename);
filenames.push(file.filename);
//then upload it
await moveUpload(file, filename, `asset/${req.params.board}`);
await moveUpload(file, file.filename, `asset/${req.params.board}`);
//and delete the temp file
await remove(file.tempFilePath);

@ -54,6 +54,7 @@ module.exports = async (req, res, next) => {
for (let i = 0; i < res.locals.numFiles; i++) {
const file = req.files.file[i];
let noExt = path.parse(file.name).name;
file.filename = noExt + file.sha256 + file.extension;
//match case for real country flags
if (noExt.length === 2 && countryCodesSet.has(noExt.toUpperCase())) {
@ -61,10 +62,10 @@ module.exports = async (req, res, next) => {
}
//add to list after checking it doesnt already exist
newFlags[noExt] = file.name;
newFlags[noExt] = file.filename;
//then upload it
await moveUpload(file, file.name, `flag/${req.params.board}`);
await moveUpload(file, file.filename, `flag/${req.params.board}`);
//and delete the temp file
await remove(file.tempFilePath);

@ -16,25 +16,25 @@ const { Boards, Posts, Accounts } = require(__dirname+'/../../db/')
, template = require(__dirname+'/../../configs/template.js.example')
, settingChangeEntries = Object.entries({
//doesnt seem like it would be much different transforming this to be tasks: [settings] or this way, so this way it is
'globalAnnouncement.raw': ['deletehtml'],
'globalAnnouncement.raw': ['deletehtml', 'custompages'],
'meta.siteName': ['deletehtml', 'scripts', 'custompages'],
'meta.url': ['deletehtml', 'scripts', 'custompages'],
'captchaOptions.type': ['deletehtml', 'css', 'scripts'],
'archiveLinksURL': ['deletehtml'],
'reverseImageLinksURL': ['deletehtml'],
'enableWebring': ['deletehtml'],
'captchaOptions.type': ['deletehtml', 'css', 'scripts', 'custompages'],
'archiveLinksURL': ['deletehtml', 'custompages'],
'reverseImageLinksURL': ['deletehtml', 'custompages'],
'enableWebring': ['deletehtml', 'custompages'],
'thumbSize': ['deletehtml', 'css', 'scripts'],
'previewReplies': ['deletehtml'],
'stickyPreviewReplies': ['deletehtml'],
'maxRecentNews': ['deletehtml'],
'previewReplies': ['deletehtml', 'custompages'],
'stickyPreviewReplies': ['deletehtml', 'custompages'],
'maxRecentNews': ['deletehtml', 'custompages'],
'themes': ['scripts'],
'codeThemes': ['scripts'],
'globalLimits.postFiles.max': ['deletehtml'],
'globalLimits.postFilesSize.max': ['deletehtml'],
'globalLimits.postFiles.max': ['deletehtml', 'custompages'],
'globalLimits.postFilesSize.max': ['deletehtml', 'custompages'],
//these will make it easier to keep updated and include objects where any/all property change needs tasks
//basically, it expands to all of globalLimits.fieldLength.* or frontendScriptDefault.*
//it could be calculated in compareSettings with *, but im just precompiling it now. probably a tiny bit faster not doing it each time
...includeChildren(template, 'globalLimits.fieldLength', ['deletehtml']),
...includeChildren(template, 'globalLimits.fieldLength', ['deletehtml', 'custompages']),
...includeChildren(template, 'frontendScriptDefault', ['scripts']),
});

@ -236,18 +236,17 @@ ${res.locals.numFiles > 0 ? req.files.file.map(f => f.name+'|'+(f.phash || '')).
//upload, create thumbnails, get metadata, etc.
for (let i = 0; i < res.locals.numFiles; i++) {
const file = req.files.file[i];
let extension = path.extname(file.name) || file.name.substring(file.name.indexOf('.'));
file.filename = file.sha256 + extension;
file.filename = file.sha256 + file.extension;
//get metadata
let processedFile = {
filename: file.filename,
spoiler: (res.locals.permLevel >= 4 || userPostSpoiler) && req.body.spoiler && req.body.spoiler.includes(file.sha256),
hash: file.sha256,
filename: file.filename, //could probably remove since we have hash and extension
originalFilename: req.body.strip_filename && req.body.strip_filename.includes(file.sha256) ? file.filename : file.name,
mimetype: file.mimetype,
size: file.size,
extension,
extension: file.extension,
};
//phash
@ -580,7 +579,7 @@ ${res.locals.numFiles > 0 ? req.files.file.map(f => f.name+'|'+(f.phash || '')).
const projectedPost = {
'_id': postMongoId,
'u': data.now,
'u': data.u,
'date': data.date,
'name': data.name,
'country': data.country,

@ -68,11 +68,10 @@ module.exports = async (req, res, next) => {
const filenames = [];
for (let i = 0; i < res.locals.numFiles; i++) {
const file = req.files.file[i];
const filename = file.sha256 + path.extname(file.name);
file.filename = filename;
file.filename = file.sha256 + file.extension;
//check if already exists
const exists = await pathExists(`${uploadDirectory}/banner/${req.params.board}/${filename}`);
const exists = await pathExists(`${uploadDirectory}/banner/${req.params.board}/${file.filename}`);
if (exists) {
await remove(file.tempFilePath);
@ -80,10 +79,10 @@ module.exports = async (req, res, next) => {
}
//add to list after checking it doesnt already exist
filenames.push(filename);
filenames.push(file.filename);
//then upload it
await moveUpload(file, filename, `banner/${req.params.board}`);
await moveUpload(file, file.filename, `banner/${req.params.board}`);
//and delete the temp file
await remove(file.tempFilePath);

20496
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -1,6 +1,6 @@
{
"name": "jschan",
"version": "0.1.8",
"version": "0.1.9",
"migrateVersion": "0.1.8",
"description": "",
"main": "server.js",
@ -14,7 +14,7 @@
"del": "^6.0.0",
"dnsbl": "^3.2.0",
"express": "^4.17.1",
"express-fileupload": "git+https://gitgud.io/fatchan/express-fileupload.git#b2c0d9c0868fed3b4bbd5c0318cb162ee81146b4",
"express-fileupload": "git+https://gitgud.io/fatchan/express-fileupload.git#ded0b1e69c222d1102fb2d8415996973e74a03fe",
"express-session": "^1.17.2",
"file-type": "^16.5.3",
"fluent-ffmpeg": "^2.1.2",
@ -33,12 +33,11 @@
"highlight.js": "^11.2.0",
"i18n-iso-countries": "^6.8.0",
"iconv-lite": "^0.6.3",
"imghash": "0.0.9",
"imghash": "^0.0.9",
"ioredis": "^4.28.0",
"ip6addr": "^0.2.3",
"mongodb": "^4.0.1",
"node-fetch": "^2.6.5",
"node-image-hash": "^1.1.0",
"path": "^0.12.7",
"pm2": "^5.1.0",
"pug": "^3.0.2",

@ -59,12 +59,12 @@ module.exports = {
ring.boards.forEach(board => {
board.siteName = ring.name;
//convert to numbers because old infinity webring plugin returns string
board.sequence_value = parseInt(board.totalPosts);
board.pph = parseInt(board.postsPerHour);
board.sequence_value = parseInt(board.totalPosts) || 0;
board.pph = parseInt(board.postsPerHour) || 0;
if (board.postsPerDay != null) {
board.ppd = parseInt(board.postsPerDay);
board.ppd = parseInt(board.postsPerDay) || 0;
}
board.ips = parseInt(board.uniqueUsers);
board.ips = parseInt(board.uniqueUsers) || 0;
board.settings = {
sfw: !board.nsfw,
name: board.title,

@ -37,10 +37,16 @@ mixin catalogtile(post, index, overboard=false)
span(class=`flag flag-${post.country.code.toLowerCase()}` title=post.country.name alt=post.country.name)
|
a.no-decoration.post-subject(href=postURL) #{post.subject || 'No subject'}
|
select.jsonly.postmenu
option(value='single') Hide
option(value='fsub') Filter Subject
if !overboard
option(value='moderate') Moderate
br
span(title='Replies') R: #{post.replyposts}
| /
span(title='Files') F: #{post.replyfiles}
span(title='Files') F: #{post.replyfiles + post.files.length}
if !overboard
| /
span(title='Page') P: #{Math.ceil(index/10)}

@ -31,7 +31,7 @@ mixin fileform(name, max, total, addPath, deletePath, checkName, fileList, nameL
input(type='checkbox' name=checkName value=nameList[index])
img(class=imageClass src=`${filePath}/${file}` loading='lazy')
if showName
small #{file.substring(0, file.lastIndexOf('.'))}
small #{nameList[index]}
if showLink
a(href=`${filePath}/${file}`) Link
input(type='submit', value='delete')

@ -154,7 +154,7 @@ mixin post(post, truncate, manage=false, globalmanage=false, ban=false, overboar
- const ompo = post.omittedposts;
- const omfi = post.omittedfiles;
| #{ompo} repl#{ompo > 1 ? 'ies' : 'y'}
| #{omfi > 0 ? ` and ${omfi} image${omfi > 1 ? 's' : ''}` : ''} omitted.
| #{omfi > 0 ? ` and ${omfi} file${omfi > 1 ? 's' : ''}` : ''} omitted.
| #[a(href=postURL) View the full thread]
if post.previewbacklinks != null
if post.previewbacklinks.length > 0

Loading…
Cancel
Save