hgfdhgfd
This commit is contained in:
@@ -10,6 +10,46 @@ const globalfilter = cfg.nsfp?.length ? cfg.nsfp.map(n => `tag_id = ${n}`).join(
|
|||||||
// All MIME types that map to the 'swf' extension in config (e.g. application/x-shockwave-flash, application/vnd.adobe.flash.movie)
|
// All MIME types that map to the 'swf' extension in config (e.g. application/x-shockwave-flash, application/vnd.adobe.flash.movie)
|
||||||
const flashMimes = Object.entries(cfg.mimes || {}).filter(([, ext]) => ext === 'swf').map(([mime]) => mime);
|
const flashMimes = Object.entries(cfg.mimes || {}).filter(([, ext]) => ext === 'swf').map(([mime]) => mime);
|
||||||
|
|
||||||
|
// ── Count cache ─────────────────────────────────────────────────────────────
|
||||||
|
// The COUNT(DISTINCT items.id) in getf0cks is expensive (full filtered scan).
|
||||||
|
// Cache it per unique filter combination for 90 seconds so that navigating
|
||||||
|
// between pages 1→192 with the same filters skips the COUNT entirely.
|
||||||
|
const COUNT_CACHE_TTL_MS = 90_000;
|
||||||
|
const countCache = new Map(); // key → { total, expiresAt }
|
||||||
|
|
||||||
|
function buildCountCacheKey({ modequery, tag, user, hall, mime, fav, session, excludedTags, newerThan, minXd, userHallObj }) {
|
||||||
|
return JSON.stringify([
|
||||||
|
modequery,
|
||||||
|
tag ?? '',
|
||||||
|
user ?? '',
|
||||||
|
hall ?? '',
|
||||||
|
mime ?? '',
|
||||||
|
fav ? 1 : 0,
|
||||||
|
session ? 1 : 0, // guests get globalfilter applied; members don't
|
||||||
|
excludedTags.slice().sort().join(','),
|
||||||
|
newerThan ?? '',
|
||||||
|
minXd,
|
||||||
|
userHallObj?.id ?? ''
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCachedCount(key) {
|
||||||
|
const entry = countCache.get(key);
|
||||||
|
if (!entry) return null;
|
||||||
|
if (Date.now() > entry.expiresAt) { countCache.delete(key); return null; }
|
||||||
|
return entry.total;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setCachedCount(key, total) {
|
||||||
|
countCache.set(key, { total, expiresAt: Date.now() + COUNT_CACHE_TTL_MS });
|
||||||
|
// Prevent unbounded growth — evict all expired entries when cache grows large
|
||||||
|
if (countCache.size > 500) {
|
||||||
|
const now = Date.now();
|
||||||
|
for (const [k, v] of countCache) { if (now > v.expiresAt) countCache.delete(k); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
const processMentions = async (comments) => {
|
const processMentions = async (comments) => {
|
||||||
if (!comments || comments.length === 0) return comments;
|
if (!comments || comments.length === 0) return comments;
|
||||||
|
|
||||||
@@ -192,26 +232,32 @@ export default {
|
|||||||
userHallFilter = db`and items.id in (select uha.item_id from user_halls_assign uha where uha.hall_id = ${userHallObj.id})`;
|
userHallFilter = db`and items.id in (select uha.item_id from user_halls_assign uha where uha.hall_id = ${userHallObj.id})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const totalRows = await db`
|
const cacheKey = buildCountCacheKey({ modequery, tag, user, hall, mime, fav, session, excludedTags, newerThan, minXd, userHallObj });
|
||||||
select count(distinct items.id) as total
|
let total = getCachedCount(cacheKey);
|
||||||
from items
|
|
||||||
${fav ? db`inner join favorites on favorites.item_id = items.id inner join "user" fav_u on fav_u.id = favorites.user_id` : db``}
|
if (total === null) {
|
||||||
where
|
const totalRows = await db`
|
||||||
${db.unsafe(modequery)}
|
select count(distinct items.id) as total
|
||||||
and items.active = true
|
from items
|
||||||
${tagFilter}
|
${fav ? db`inner join favorites on favorites.item_id = items.id inner join "user" fav_u on fav_u.id = favorites.user_id` : db``}
|
||||||
${titleFilter}
|
where
|
||||||
${fav ? db`and fav_u.user ilike ${user}` : db``}
|
${db.unsafe(modequery)}
|
||||||
${!fav && user ? db`and items.username ilike ${user}` : db``}
|
and items.active = true
|
||||||
${mimeSQL}
|
${tagFilter}
|
||||||
${hallFilter}
|
${titleFilter}
|
||||||
${userHallFilter}
|
${fav ? db`and fav_u.user ilike ${user}` : db``}
|
||||||
${!session && globalfilter ? db`and not exists (select 1 from tags_assign where item_id = items.id and (${db.unsafe(globalfilter)}))` : db``}
|
${!fav && user ? db`and items.username ilike ${user}` : db``}
|
||||||
${excludedTags.length > 0 ? db`and not exists (select 1 from tags_assign where item_id = items.id and tag_id = any(${excludedTags}::int[]))` : db``}
|
${mimeSQL}
|
||||||
${newerThan ? db`and items.id > ${newerThan}` : db``}
|
${hallFilter}
|
||||||
${xdFilter}
|
${userHallFilter}
|
||||||
`;
|
${!session && globalfilter ? db`and not exists (select 1 from tags_assign where item_id = items.id and (${db.unsafe(globalfilter)}))` : db``}
|
||||||
const total = Number(totalRows[0].total);
|
${excludedTags.length > 0 ? db`and not exists (select 1 from tags_assign where item_id = items.id and tag_id = any(${excludedTags}::int[]))` : db``}
|
||||||
|
${newerThan ? db`and items.id > ${newerThan}` : db``}
|
||||||
|
${xdFilter}
|
||||||
|
`;
|
||||||
|
total = Number(totalRows[0].total);
|
||||||
|
if (total > 0) setCachedCount(cacheKey, total);
|
||||||
|
}
|
||||||
|
|
||||||
if (!total || total === 0) {
|
if (!total || total === 0) {
|
||||||
return {
|
return {
|
||||||
@@ -1458,5 +1504,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computeXdScore,
|
computeXdScore,
|
||||||
xdScoreMeta
|
xdScoreMeta,
|
||||||
|
// Bust the count cache (call after a new upload is accepted so page totals stay accurate)
|
||||||
|
clearCountCache: () => countCache.clear()
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import path from "path";
|
|||||||
import https from "https";
|
import https from "https";
|
||||||
import { getManualApproval, getMinTags, getTrustedUploads, getBypassDuplicateCheck, getEnablePdf } from "./inc/settings.mjs";
|
import { getManualApproval, getMinTags, getTrustedUploads, getBypassDuplicateCheck, getEnablePdf } from "./inc/settings.mjs";
|
||||||
import { parseMultipart, collectBody } from "./inc/multipart.mjs";
|
import { parseMultipart, collectBody } from "./inc/multipart.mjs";
|
||||||
|
import f0cklib from "./inc/routeinc/f0cklib.mjs";
|
||||||
|
|
||||||
// Helper for JSON response
|
// Helper for JSON response
|
||||||
const sendJson = (res, data, code = 200) => {
|
const sendJson = (res, data, code = 200) => {
|
||||||
@@ -560,6 +561,8 @@ export const handleUpload = async (req, res, self) => {
|
|||||||
|
|
||||||
// Action if auto-approved
|
// Action if auto-approved
|
||||||
if (!manualApproval) {
|
if (!manualApproval) {
|
||||||
|
// Bust the count cache so page totals update immediately
|
||||||
|
f0cklib.clearCountCache();
|
||||||
if (!linkedToExisting) {
|
if (!linkedToExisting) {
|
||||||
// Move logic: Handles both real files and symlinks (reposts) correctly
|
// Move logic: Handles both real files and symlinks (reposts) correctly
|
||||||
const moveSafe = async (src, dst) => {
|
const moveSafe = async (src, dst) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user