merge webring db,

improve board list sorting and filters
improvelayout of public and global manage board list forms
and add extra homepage stat
merge-requests/341/head
Thomas Lynch 3 years ago
parent d9a0b3b9a3
commit 8892f64bad
  1. 75
      db/boards.js
  2. 1
      db/index.js
  3. 10
      ecosystem.config.js
  4. 12
      gulp/res/css/style.css
  5. 12
      gulpfile.js
  6. 4
      helpers/tasks.js
  7. 16
      migrations/0.1.2.js
  8. 2
      models/forms/actionhandler.js
  9. 2
      models/forms/changeboardsettings.js
  10. 2
      models/forms/create.js
  11. 102
      models/pages/boardlist.js
  12. 4
      package.json
  13. 36
      schedules/tasks/webring.js
  14. 2
      server.js
  15. 80
      views/pages/boardlist.pug
  16. 53
      views/pages/globalmanageboardlist.pug
  17. 2
      views/pages/globalmanagesettings.pug
  18. 13
      views/pages/home.pug

@ -169,10 +169,11 @@ module.exports = {
return listedBoards;
},
boardSort: (skip=0, limit=50, sort={ ips:-1, pph:-1, sequence_value:-1 }, filter={}, showSensitive=false) => {
boardSort: (skip=0, limit=50, sort={ ips:-1, pph:-1, sequence_value:-1 }, filter={}, showSensitive=false, webringSites=false) => {
const addedFilter = {};
const projection = {
'_id': 1,
'uri': 1,
'lastPostTimestamp': 1,
'sequence_value': 1,
'pph': 1,
@ -181,11 +182,22 @@ module.exports = {
'settings.sfw': 1,
'settings.description': 1,
'settings.name': 1,
'settings.tags': 1,
'tags': 1,
'path': 1,
'siteName': 1,
'webring': 1,
'settings.unlistedLocal': 1,
};
if (webringSites) {
addedFilter['siteName'] = {
'$in': webringSites,
};
}
if (!showSensitive) {
addedFilter['settings.unlistedLocal'] = false;
addedFilter['settings.unlistedLocal'] = { '$ne': true };
if (!webringSites) {
addedFilter['webring'] = false;
}
} else {
if (filter.filter_sfw) {
addedFilter['settings.sfw'] = true;
@ -197,12 +209,14 @@ module.exports = {
addedFilter['owner'] = null;
addedFilter['settings.moderators'] = [];
}
addedFilter['webring'] = false;
projection['settings.moderators'] = 1;
projection['owner'] = 1;
}
if (filter.search) {
addedFilter['$or'] = [
{ 'settings.tags': filter.search },
{ 'tags': filter.search },
{ 'uri': filter.search },
{ '_id': filter.search },
];
}
@ -215,7 +229,8 @@ module.exports = {
webringBoards: () => {
return db.find({
'settings.unlistedWebring': false
'webring': false,
'settings.unlistedWebring': false,
}, {
'projection': {
'_id': 1,
@ -227,15 +242,20 @@ module.exports = {
'settings.sfw': 1,
'settings.description': 1,
'settings.name': 1,
'settings.tags': 1,
'tags': 1,
}
}).toArray();
},
count: (filter, showSensitive=false) => {
count: (filter, showSensitive=false, webringSites=false) => {
const addedFilter = {};
if (webringSites) {
addedFilter['siteName'] = {
'$in': webringSites,
};
}
if (!showSensitive) {
addedFilter['settings.unlistedLocal'] = false;
addedFilter['settings.unlistedLocal'] = { $ne: true };
} else {
if (filter.filter_sfw) {
addedFilter['settings.sfw'] = true;
@ -247,21 +267,48 @@ module.exports = {
addedFilter['owner'] = null;
addedFilter['settings.moderators'] = [];
}
addedFilter['webring'] = false;
}
if (filter.search) {
addedFilter['$or'] = [
{ 'settings.tags': filter.search },
{ 'tags': filter.search },
{ 'uri': filter.search },
{ '_id': filter.search },
];
}
return db.countDocuments(addedFilter);
},
webringSites: async () => {
let webringSites = await cache.get('webringsites');
if (!webringSites) {
webringSites = await db.aggregate([
{
'$match': {
'webring': true
},
},
{
'$group': {
'_id': null,
'sites': {
'$addToSet': '$siteName'
}
}
}
])
.toArray()
.then(res => res[0].sites);
cache.set('webringsites', webringSites);
}
return webringSites;
},
totalStats: () => {
return db.aggregate([
{
'$group': {
'_id': null,
'_id': '$webring',
'posts': {
'$sum': '$sequence_value'
},
@ -279,10 +326,14 @@ module.exports = {
'$cond': ['$settings.unlistedLocal', 1, 0]
}
},
//removed ips because sum is inaccurate
}
}
]).toArray().then(res => res[0]);
])
.toArray()
.then(res => {
res.sort((a, b) => a._id ? 1 : -1);
return res;
});
},
exists: async (req, res, next) => {

@ -4,7 +4,6 @@ module.exports = {
Posts: require(__dirname+'/posts.js'),
Boards: require(__dirname+'/boards.js'),
Webring: require(__dirname+'/webring.js'),
Stats: require(__dirname+'/stats.js'),
Accounts: require(__dirname+'/accounts.js'),
Bans: require(__dirname+'/bans.js'),

@ -2,9 +2,9 @@ const numCpus = require('os').cpus().length;
module.exports = {
// Options reference: https://pm2.io/doc/en/runtime/reference/ecosystem-file/
apps : [{
name: 'build-worker',
name: 'dev-build-worker',
script: 'worker.js',
instances: Math.floor(numCpus/2), //if you only have 1 core and floor to 0, 0 just means "all cores" which is correct in that case.
instances: 1,
autorestart: true,
watch: false,
max_memory_restart: '1G',
@ -19,9 +19,9 @@ module.exports = {
NODE_ENV: 'production'
}
}, {
name: 'chan',
name: 'dev-chan',
script: 'server.js',
instances: Math.floor(numCpus/2),
instances: 1,
autorestart: true,
watch: false,
max_memory_restart: '1G',
@ -35,7 +35,7 @@ module.exports = {
NODE_ENV: 'production'
}
}, {
name: 'schedules',
name: 'dev-schedules',
script: 'schedules/index.js',
instances: 1,
autorestart: true,

@ -19,6 +19,12 @@ main.minimal {
height: 100vh;
}
.form-post {
display: flex;
flex-direction: column;
max-width: 100%;
}
.row {
display: flex;
flex-direction: row;
@ -743,12 +749,6 @@ span.captchacheckbox {
display: flex;
}
.form-post {
display: flex;
flex-direction: column;
max-width: 100%;
}
.user-id {
text-shadow: #000 0px 0px 1px, #000 0px 0px 1px, #000 0px 0px 1px, #000 0px 0px 1px, #000 0px 0px 1px, #000 0px 0px 1px;
color: white;

@ -59,14 +59,14 @@ async function wipe() {
const db = Mongo.db;
const collectionNames = ['accounts', 'bans', 'custompages', 'boards', 'captcha', 'files',
'modlog','news', 'posts', 'poststats', 'ratelimit', 'webring', 'bypass'];
'modlog','news', 'posts', 'poststats', 'ratelimit', 'bypass'];
for (const name of collectionNames) {
//drop collection so gulp reset can be run again. ignores error of dropping non existing collection first time
await db.dropCollection(name).catch(e => {});
await db.createCollection(name);
}
const { Webring, Boards, Posts, Captchas, Ratelimits, News, CustomPages,
const { Boards, Posts, Captchas, Ratelimits, News, CustomPages,
Accounts, Files, Stats, Modlogs, Bans, Bypass } = require(__dirname+'/db/');
//wipe db shit
@ -77,7 +77,6 @@ async function wipe() {
Accounts.deleteAll(),
Posts.deleteAll(),
Boards.deleteAll(),
Webring.deleteAll(),
Bans.deleteAll(),
Files.deleteAll(),
Stats.deleteAll(),
@ -89,11 +88,9 @@ async function wipe() {
//add indexes - should profiled and changed at some point if necessary
await Stats.db.createIndex({board:1, hour:1})
await Boards.db.createIndex({ips: 1, pph:1, sequence_value:1})
await Boards.db.createIndex({'settings.tags':1})
await Boards.db.createIndex({tags: 1})
await Boards.db.createIndex({uri: 1})
await Boards.db.createIndex({lastPostTimestamp:1})
await Webring.db.createIndex({uniqueUsers:1, postsPerHour:1, totalPosts:1})
await Webring.db.createIndex({tags:1})
await Webring.db.createIndex({lastPostTimestamp:1})
await Bans.db.dropIndexes()
await Captchas.db.dropIndexes()
await Ratelimits.db.dropIndexes()
@ -234,6 +231,7 @@ async function cache() {
Redis.deletePattern('blacklisted:*'),
Redis.deletePattern('overboard'),
Redis.deletePattern('catalog'),
Redis.deletePattern('webringsites'),
]);
}

@ -242,8 +242,10 @@ module.exports = {
Files.activeContent(), //size ans number of files
News.find(maxRecentNews), //some recent newsposts
]);
const [ localStats, webringStats ] = totalStats;
const { html } = await render('index.html', 'home.pug', {
totalStats,
localStats,
webringStats,
boards,
fileStats,
recentNews,

@ -0,0 +1,16 @@
'use strict';
module.exports = async(db, redis) => {
console.log('setting webring:false to boards');
await db.collection('boards').updateMany({}, {
'$set': {
'webring': false,
}
});
await db.collection('boards').dropIndexes();
await db.collection('boards').createIndex({ips: 1, pph:1, sequence_value:1});
await db.collection('boards').createIndex({tags: 1});
await db.collection('boards').createIndex({uri: 1});
await db.collection('boards').createIndex({lastPostTimestamp:1});
await db.dropCollection('webring');
};

@ -259,7 +259,7 @@ module.exports = async (req, res, next) => {
buildBoards = (await Boards.db.find({
'_id': {
'$in': threadBoards
}
},
}).toArray()).reduce((acc, curr) => {
if (!acc[curr._id]) {
acc[curr._id] = curr;

@ -105,7 +105,6 @@ module.exports = async (req, res, next) => {
'fileR9KMode': numberSetting(req.body.file_r9k_mode, oldSettings.fileR9KMode),
'filterMode': numberSetting(req.body.filter_mode, oldSettings.filterMode),
'filterBanDuration': numberSetting(req.body.ban_duration, oldSettings.filterBanDuration),
'tags': arraySetting(req.body.tags, oldSettings.tags, 10),
'filters': arraySetting(req.body.filters, oldSettings.filters, 50),
'blockedCountries': req.body.countries || [],
'disableAnonymizerFilePosting': booleanSetting(req.body.disable_anonymizer_file_posting),
@ -128,6 +127,7 @@ module.exports = async (req, res, next) => {
await Boards.updateOne(req.params.board, {
'$set': {
'settings': newSettings,
'tags': arraySetting(req.body.tags, oldSettings.tags, 10),
}
});

@ -40,6 +40,7 @@ module.exports = async (req, res, next) => {
const newBoard = {
'_id': uri,
owner,
tags,
'banners': [],
'flags': [],
'sequence_value': 1,
@ -50,7 +51,6 @@ module.exports = async (req, res, next) => {
'settings': {
name,
description,
tags,
'moderators': [],
...boardDefaults
}

@ -1,39 +1,61 @@
'use strict';
const config = require(__dirname+'/../../config.js')
, { Boards, Webring } = require(__dirname+'/../../db/')
, { Boards } = require(__dirname+'/../../db/')
, cache = require(__dirname+'/../../redis.js')
, { relativeColor, relativeString } = require(__dirname+'/../../helpers/timeutils.js')
, pageQueryConverter = require(__dirname+'/../../helpers/pagequeryconverter.js')
, limit = 20;
, limit = 30;
module.exports = async (req, res, next) => {
const { enableWebring } = config.get;
const { meta } = config.get;
const { page, offset, queryString } = pageQueryConverter(req.query, limit);
const direction = req.query.direction && req.query.direction === 'asc' ? 1 : -1;
const search = (typeof req.query.search === 'string' ? req.query.search : null);
let sort = req.query.sort && req.query.sort === 'activity' ? 'activity' : 'popularity';
const localFirst = req.query.local_first != null;
const sortType = req.query.sort && req.query.sort === 'activity' ? 'activity' : 'popularity';
let sort = {};
const cacheQuery = new URLSearchParams({ direction, sort, search, page });
const webringSites = [meta.siteName, ...(await Boards.webringSites())];
const webringSitesSet = new Set(webringSites);
let selectedSites = req.query.sites ? (typeof req.query.sites === 'string' ? [req.query.sites] : req.query.sites) : [];
let validSelectedSites = selectedSites.filter(ss => webringSitesSet.has(ss));
if (validSelectedSites.length === 0) {
validSelectedSites = webringSites;
}
const validSelectedSitesSet = new Set(validSelectedSites);
const cacheQuery = new URLSearchParams({ direction, sort: sortType, search, page, localFirst, selectedSites: [...validSelectedSitesSet] });
cacheQuery.sort();
const cacheQueryString = cacheQuery.toString();
const cached = await cache.get(`boardlist:${cacheQueryString}`);
const { shown, notShown } = webringSites.reduce((acc, ws) => {
if (validSelectedSitesSet.has(ws)) {
acc.shown.push(ws);
} else {
acc.notShown.push(ws)
}
return acc;
}, { shown: [], notShown: [] });
validSelectedSites = validSelectedSites.map(x => x === meta.siteName ? null : x);
let localBoards, webringBoards, localPages, webringPages, maxPage;
if (localFirst && validSelectedSitesSet.has(meta.siteName)) {
//meh.
sort['webring'] = 1;
}
let boards, maxPage;
if (cached) {
({ localBoards, webringBoards, localPages, webringPages, maxPage } = cached);
({ boards, maxPage } = cached);
} else {
if (sort === 'activity') {
sort = {
'lastPostTimestamp': direction
}
if (sortType === 'activity') {
sort['lastPostTimestamp'] = direction;
} else {
sort = {
'ips': direction,
'pph': direction,
'sequence_value': direction
};
sort['ips'] = direction;
sort['pph'] = direction;
sort['sequence_value'] = direction;
}
let filter = {};
@ -44,42 +66,25 @@ module.exports = async (req, res, next) => {
}
try {
[ localBoards, localPages, webringBoards, webringPages ] = await Promise.all([
Boards.boardSort(offset, limit, sort, filter),
Boards.count(filter),
enableWebring ? Webring.boardSort(offset, limit, sort, filter) : null,
enableWebring ? Webring.count(filter) : 0,
[ boards, maxPage ] = await Promise.all([
Boards.boardSort(offset, limit, sort, filter, false, validSelectedSites),
Boards.count(filter, false, validSelectedSites),
]);
localPages = Math.ceil(localPages / limit);
webringPages = Math.ceil(webringPages / limit);
maxPage = Math.max(localPages, webringPages);
maxPage = Math.ceil(maxPage/limit);
} catch (err) {
return next(err);
}
const now = new Date();
if (localBoards) {
for (let i = 0; i < localBoards.length; i++) {
if (localBoards[i].lastPostTimestamp) {
const lastPostDate = new Date(localBoards[i].lastPostTimestamp);
localBoards[i].lastPostTimestamp = {
text: relativeString(now, lastPostDate),
color: relativeColor(now, lastPostDate)
}
}
}
}
if (webringBoards) {
for (let i = 0; i < webringBoards.length; i++) {
if (webringBoards[i].lastPostTimestamp) {
const lastPostDate = new Date(webringBoards[i].lastPostTimestamp);
webringBoards[i].lastPostTimestamp = {
text: relativeString(now, lastPostDate),
color: relativeColor(now, lastPostDate)
}
for (let i = 0; i < boards.length; i++) {
if (boards[i].lastPostTimestamp) {
const lastPostDate = new Date(boards[i].lastPostTimestamp);
boards[i].lastPostTimestamp = {
text: relativeString(now, lastPostDate),
color: relativeColor(now, lastPostDate)
}
}
}
cache.set(`boardlist:${cacheQueryString}`, { localBoards, localPages, webringBoards, webringPages, maxPage }, 60);
cache.set(`boardlist:${cacheQueryString}`, { boards, maxPage }, 60);
}
res
@ -87,18 +92,19 @@ module.exports = async (req, res, next) => {
if (req.path === '/boards.json') {
res.json({
localBoards,
webringBoards,
boards,
page,
maxPage,
});
} else {
res.render('boardlist', {
localBoards,
webringBoards,
boards,
shown,
notShown,
page,
maxPage,
query: req.query,
localFirst,
search,
queryString,
});

@ -1,7 +1,7 @@
{
"name": "jschan",
"version": "0.1.1",
"migrateVersion": "0.1.1",
"version": "0.1.2",
"migrateVersion": "0.1.2",
"description": "",
"main": "server.js",
"dependencies": {

@ -4,7 +4,8 @@ const fetch = require('node-fetch')
, { debugLogs } = require(__dirname+'/../../configs/secrets.js')
, config = require(__dirname+'/../../config.js')
, Mongo = require(__dirname+'/../../db/db.js')
, { Boards, Webring } = require(__dirname+'/../../db/')
, Redis = require(__dirname+'/../../redis.js')
, { Boards } = require(__dirname+'/../../db/')
, { outputFile } = require('fs-extra')
, SocksProxyAgent = require('socks-proxy-agent')
, uploadDirectory = require(__dirname+'/../../helpers/files/uploadDirectory.js')
@ -21,6 +22,7 @@ module.exports = {
const agent = proxy.enabled ? new SocksProxyAgent(require('url').parse(proxy.address)) : null;
const visited = new Map();
const siteNames = new Set();
let known = new Set(following);
let webringBoards = []; //list of webring boards
while (known.size > visited.size) {
@ -51,31 +53,34 @@ module.exports = {
.filter(url => !blacklist.some(x => url.includes(x)) && !url.includes(meta.url))
.forEach(url => known.add(url));
}
siteNames.add(ring.name);
if (ring.boards && ring.boards.length > 0) {
//add some stuff for the boardlist and then add their boards
ring.boards.forEach(board => {
board.siteName = ring.name;
//convert to numbers because old infinity webring plugin returns string
board.totalPosts = parseInt(board.totalPosts);
board.postsPerHour = parseInt(board.postsPerHour);
board.uniqueUsers = parseInt(board.uniqueUsers);
board.sequence_value = parseInt(board.totalPosts);
board.pph = parseInt(board.postsPerHour);
board.ips = parseInt(board.uniqueUsers);
board.settings = {
sfw: !board.nsfw,
name: board.title,
description: board.subtitle,
};
});
webringBoards = webringBoards.concat(ring.boards);
}
}
}
await Boards.db.deleteMany({ webring: true });
if (webringBoards.length > 0) {
//$out from temp collection to replace webring boards
const tempCollection = Mongo.db.collection('tempwebring');
await tempCollection.insertMany(webringBoards);
await tempCollection.aggregate([
{ $out : 'webring' }
]).toArray();
await tempCollection.drop();
} else {
//otherwise none found, so delete them all
await Webring.deleteAll();
webringBoards = webringBoards.map(x => {
x.webring = true;
return x;
});
await Boards.db.insertMany(webringBoards);
await Redis.set('webringsites', [...siteNames]);
}
//update webring.json
@ -96,10 +101,11 @@ module.exports = {
subtitle: b.settings.description,
path: `${meta.url}/${b._id}/`,
postsPerHour: b.pph,
postsPerDay: b.ppd,
totalPosts: b.sequence_value-1,
uniqueUsers: b.ips,
nsfw: !b.settings.sfw,
tags: b.settings.tags,
tags: b.tags,
lastPostTimestamp: b.lastPostTimestamp,
};
}),

@ -38,6 +38,8 @@ const config = require(__dirname+'/config.js')
// disable useless express header
app.disable('x-powered-by');
//query strings
app.set('query parser', 'simple');
// parse forms
app.use(express.urlencoded({extended: false}));
// parse cookies

@ -9,61 +9,59 @@ block content
.flexcenter.mv-10
form.form-post(action='/boards.html' method='GET')
input(type='hidden' value=page)
.row
.label Search
input(type='text' name='search' value=search placeholder='Uri or tags')
.row
.label Sort
select(name='sort')
option(value='popularity') Popularity
option(value='activity' selected=query.sort === 'activity') Latest Activity
.row
.label Order
select(name='direction')
option(value='desc') Descending
option(value='asc' selected=query.direction === 'asc') Ascending
.row.wrap.sb
div(class=`col${enableWebring ? ' mr-5' : ''}`)
.row
.label Search
input(type='text' name='search' value=search placeholder='Uri or tags')
.row
.label Sort
select(name='sort')
option(value='popularity') Popularity
option(value='activity' selected=query.sort === 'activity') Latest Activity
.row
.label Order
select(name='direction')
option(value='desc') Descending
option(value='asc' selected=query.direction === 'asc') Ascending
.row
.label Local First
label.postform-style.ph-5
input(type='checkbox', name='local_first', value='true' checked=localFirst)
if enableWebring
.col
.row
.label Sites
select(name='sites' size='5' multiple)
optgroup(label='Shown')
each site in shown
option(value=site selected=true) #{site}
optgroup(label='Not Shown')
each site in notShown
option(value=site) #{site}
input(type='submit', value='Filter')
if localBoards && localBoards.length > 0
h4.board-description Local Boards
if boards && boards.length > 0
+boardtable(true, true)
each board in localBoards
each board in boards
- const path = board.webring ? board.path : `/${board._id}/index.html`;
tr
td
span.left
if board.webring === true
span.help(title='Webring') &#x1F517;
|
if board.settings.sfw === true
span.help(title='SFW') &#x1F4BC;
|
if board.settings.unlistedLocal === true
span.help(title='Unlisted') &#x1F441;&#xFE0F;
|
a(href=`/${board._id}/index.html`) /#{board._id}/ - #{board.settings.name}
td #{board.settings.description}
a(href=path) #{board.webring ? board.siteName : ''} /#{board.webring ? board.uri : board._id}/ - #{board.settings.name}
td #{board.settings.description || '-'}
td #{board.pph}
td #{board.ppd}
td #{board.ppd || '-'}
td #{board.ips}
td #{board.sequence_value-1}
if board.lastPostTimestamp
td(style=`background-color: ${board.lastPostTimestamp.color}`) #{board.lastPostTimestamp.text}
else
td -
if webringBoards && webringBoards.length > 0
h4.board-description Webring Boards
+boardtable(false, true)
each board in webringBoards
tr
td
span.left
if !board.nsfw
span.help(title='SFW') &#x1F4BC;
|
a(href=board.path) #{board.siteName} /#{board.uri}/ - #{board.title}
td #{board.subtitle || '-'}
td #{board.postsPerHour}
td #{board.uniqueUsers}
td #{board.totalPosts}
if board.lastPostTimestamp
td(style=`background-color: ${board.lastPostTimestamp.color}`) #{board.lastPostTimestamp.text}
else
td -
.pages.text-center.mt-5.mv-0
include ../includes/pages.pug

@ -15,31 +15,34 @@ block content
.flexcenter.mv-10
form.form-post(action='/globalmanage/boards.html' method='GET')
input(type='hidden' value=page)
.row
.label Search
input(type='text' name='search' value=search placeholder='Uri or tags')
.row
.label Sort
select(name='sort')
option(value='popularity') Popularity
option(value='activity' selected=query.sort === 'activity') Latest Activity
.row
.label Order
select(name='direction')
option(value='desc') Descending
option(value='asc' selected=query.direction === 'asc') Ascending
.row
.label Unlisted
label.postform-style.ph-5
input(type='checkbox', name='filter_unlisted', value='1' checked=(query.filter_unlisted != null))
.row
.label SFW
label.postform-style.ph-5
input(type='checkbox', name='filter_sfw', value='1' checked=(query.filter_sfw != null))
.row
.label Abandoned
label.postform-style.ph-5
input(type='checkbox', name='filter_abandoned', value='1' checked=(query.filter_abandoned != null))
.row.wrap.sb
.col.mr-5
.row
.label Search
input(type='text' name='search' value=search placeholder='Uri or tags')
.row
.label Sort
select(name='sort')
option(value='popularity') Popularity
option(value='activity' selected=query.sort === 'activity') Latest Activity
.row
.label Order
select(name='direction')
option(value='desc') Descending
option(value='asc' selected=query.direction === 'asc') Ascending
.col
.row
.label Unlisted
label.postform-style.ph-5
input(type='checkbox', name='filter_unlisted', value='1' checked=(query.filter_unlisted != null))
.row
.label SFW
label.postform-style.ph-5
input(type='checkbox', name='filter_sfw', value='1' checked=(query.filter_sfw != null))
.row
.label Abandoned
label.postform-style.ph-5
input(type='checkbox', name='filter_abandoned', value='1' checked=(query.filter_abandoned != null))
input(type='submit', value='Filter')
if localBoards && localBoards.length > 0
h4.text-center.mv-10 Board List:

@ -548,7 +548,7 @@ block content
.row
.label Use Socks Proxy
label.postform-style.ph-5
input(type='checkbox', name='webring_proxy_enabled', value='true' checked=settings.proxy.enabled)
input(type='checkbox', name='webring_proxy_enabled', value='true' placeholder='socks5h://127.0.0.1:9050' checked=settings.proxy.enabled)
.row
.label Proxy Address
input(type='text', name='webring_proxy_address', value=settings.proxy.address)

@ -44,7 +44,7 @@ block content
td #{board.ppd}
td #{board.ips}
td #{board.sequence_value-1}
if totalStats.total-totalStats.unlisted > boards.length
if localStats.total-localStats.unlisted > boards.length
tr
td(colspan=5)
a.bold(href='/boards.html') More Boards
@ -55,5 +55,14 @@ block content
tr
td
pre.no-m-p
| There are currently #[span.bold #{totalStats.total-totalStats.unlisted}] public boards, #[span.bold #{totalStats.total}] in total. Sitewide, #[span.bold #{totalStats.ppd}] post#{totalStats.ppd === 1 ? ' has' : 's have'} been made in the last day, #[span.bold #{totalStats.pph}] in the last hour, #[span.bold #{totalStats.posts}] in total.
| There are currently #[span.bold #{localStats.total-localStats.unlisted}] public boards, #[span.bold #{localStats.total}] in total. Sitewide, #[span.bold #{localStats.ppd}] post#{localStats.ppd === 1 ? ' has' : 's have'} been made in the last day, #[span.bold #{localStats.pph}] in the last hour, #[span.bold #{localStats.posts}] in total.
| #[span.bold #{fileStats.count}] file#{fileStats.count === 1 ? ' is' : 's are'} being served, totaling #[span.bold #{fileStats.totalSizeString}].
if enableWebring === true
.table-container.flex-center.mv-10.text-center
table(style='max-width:450px')
tr
th Webring Stats
tr
td
pre.no-m-p
| There are currently #[span.bold #{webringStats.total}] boards in the known webring. Across all webring boards, #[span.bold #{webringStats.pph}] post#{webringStats.pph === 1 ? ' has' : 's have'} been made in the last hour, #[span.bold #{webringStats.posts}] in total.

Loading…
Cancel
Save