cleanip
This commit is contained in:
@@ -12,7 +12,7 @@ import cfg from "../config.mjs";
|
|||||||
import security from "../security.mjs";
|
import security from "../security.mjs";
|
||||||
import crypto from "crypto";
|
import crypto from "crypto";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { getManualApproval, setManualApproval, getMinTags, setMinTags, getRegistrationOpen, setRegistrationOpen, getTrustedUploads, setTrustedUploads, getEnablePdf, setEnablePdf, getLogUserIps, setLogUserIps, getHashUserIps, setHashUserIps, getEnableCleanup, setEnableCleanup, getCleanupStartDate, setCleanupStartDate, getCleanupEndDate, setCleanupEndDate, getShitpostMode } from "../settings.mjs";
|
import { getManualApproval, setManualApproval, getMinTags, setMinTags, getRegistrationOpen, setRegistrationOpen, getTrustedUploads, setTrustedUploads, getEnablePdf, setEnablePdf, getLogUserIps, setLogUserIps, getHashUserIps, setHashUserIps, getEnableCleanup, setEnableCleanup, getCleanupStartDate, setCleanupStartDate, getCleanupEndDate, setCleanupEndDate, getCleanupIncludeEngaged, setCleanupIncludeEngaged, getShitpostMode } from "../settings.mjs";
|
||||||
|
|
||||||
export default (router, tpl) => {
|
export default (router, tpl) => {
|
||||||
router.get(/^\/login(\/)?$/, async (req, res) => {
|
router.get(/^\/login(\/)?$/, async (req, res) => {
|
||||||
@@ -661,6 +661,7 @@ export default (router, tpl) => {
|
|||||||
enable_cleanup: getEnableCleanup(),
|
enable_cleanup: getEnableCleanup(),
|
||||||
cleanup_start_date: getCleanupStartDate(),
|
cleanup_start_date: getCleanupStartDate(),
|
||||||
cleanup_end_date: getCleanupEndDate(),
|
cleanup_end_date: getCleanupEndDate(),
|
||||||
|
cleanup_include_engaged: getCleanupIncludeEngaged(),
|
||||||
totals: await lib.countf0cks(),
|
totals: await lib.countf0cks(),
|
||||||
tmp: null
|
tmp: null
|
||||||
};
|
};
|
||||||
@@ -674,15 +675,18 @@ export default (router, tpl) => {
|
|||||||
try {
|
try {
|
||||||
const cleanup_start_date = req.post.cleanup_start_date || '';
|
const cleanup_start_date = req.post.cleanup_start_date || '';
|
||||||
const cleanup_end_date = req.post.cleanup_end_date || '';
|
const cleanup_end_date = req.post.cleanup_end_date || '';
|
||||||
|
const cleanup_include_engaged = req.post.cleanup_include_engaged === 'true' || req.post.cleanup_include_engaged === 'on' || req.post.cleanup_include_engaged === '1';
|
||||||
|
|
||||||
await db`INSERT INTO site_settings (key, value) VALUES ('cleanup_start_date', ${cleanup_start_date}) ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value`;
|
await db`INSERT INTO site_settings (key, value) VALUES ('cleanup_start_date', ${cleanup_start_date}) ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value`;
|
||||||
await db`INSERT INTO site_settings (key, value) VALUES ('cleanup_end_date', ${cleanup_end_date}) ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value`;
|
await db`INSERT INTO site_settings (key, value) VALUES ('cleanup_end_date', ${cleanup_end_date}) ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value`;
|
||||||
|
await db`INSERT INTO site_settings (key, value) VALUES ('cleanup_include_engaged', ${cleanup_include_engaged ? 'true' : 'false'}) ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value`;
|
||||||
|
|
||||||
setCleanupStartDate(cleanup_start_date);
|
setCleanupStartDate(cleanup_start_date);
|
||||||
setCleanupEndDate(cleanup_end_date);
|
setCleanupEndDate(cleanup_end_date);
|
||||||
|
setCleanupIncludeEngaged(cleanup_include_engaged);
|
||||||
|
|
||||||
if (req.headers['x-requested-with'] === 'XMLHttpRequest') {
|
if (req.headers['x-requested-with'] === 'XMLHttpRequest') {
|
||||||
const body = JSON.stringify({ success: true, enable_cleanup: getEnableCleanup(), cleanup_start_date: getCleanupStartDate(), cleanup_end_date: getCleanupEndDate() });
|
const body = JSON.stringify({ success: true, enable_cleanup: getEnableCleanup(), cleanup_start_date: getCleanupStartDate(), cleanup_end_date: getCleanupEndDate(), cleanup_include_engaged: getCleanupIncludeEngaged() });
|
||||||
return res.writeHead(200, { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) }).end(body);
|
return res.writeHead(200, { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) }).end(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -701,17 +705,23 @@ export default (router, tpl) => {
|
|||||||
router.post(/^\/admin\/cleanup\/run\/?$/, lib.auth, async (req, res) => {
|
router.post(/^\/admin\/cleanup\/run\/?$/, lib.auth, async (req, res) => {
|
||||||
try {
|
try {
|
||||||
// Ensure settings are synced from DB before execution
|
// Ensure settings are synced from DB before execution
|
||||||
const settings = await db`SELECT key, value FROM site_settings WHERE key IN ('enable_cleanup', 'cleanup_start_date', 'cleanup_end_date')`;
|
const settings = await db`SELECT key, value FROM site_settings WHERE key IN ('enable_cleanup', 'cleanup_start_date', 'cleanup_end_date', 'cleanup_include_engaged')`;
|
||||||
const settingsMap = Object.fromEntries(settings.map(s => [s.key, s.value]));
|
const settingsMap = Object.fromEntries(settings.map(s => [s.key, s.value]));
|
||||||
|
|
||||||
const isEnabled = settingsMap['enable_cleanup'] !== undefined ? settingsMap['enable_cleanup'] === 'true' : getEnableCleanup();
|
const isEnabled = settingsMap['enable_cleanup'] !== undefined ? settingsMap['enable_cleanup'] === 'true' : getEnableCleanup();
|
||||||
const startDate = settingsMap['cleanup_start_date'] || '';
|
const startDate = settingsMap['cleanup_start_date'] || '';
|
||||||
const endDate = settingsMap['cleanup_end_date'] || '';
|
const endDate = settingsMap['cleanup_end_date'] || '';
|
||||||
|
|
||||||
|
const reqEngaged = req.post?.include_engaged;
|
||||||
|
const includeEngaged = reqEngaged !== undefined
|
||||||
|
? (reqEngaged === 'true' || reqEngaged === true || reqEngaged === 'on' || reqEngaged === '1')
|
||||||
|
: (settingsMap['cleanup_include_engaged'] === 'true' || getCleanupIncludeEngaged());
|
||||||
|
|
||||||
// Update memory state
|
// Update memory state
|
||||||
setEnableCleanup(isEnabled);
|
setEnableCleanup(isEnabled);
|
||||||
setCleanupStartDate(startDate);
|
setCleanupStartDate(startDate);
|
||||||
setCleanupEndDate(endDate);
|
setCleanupEndDate(endDate);
|
||||||
|
setCleanupIncludeEngaged(includeEngaged);
|
||||||
|
|
||||||
if (!isEnabled) {
|
if (!isEnabled) {
|
||||||
throw new Error('Cleanup is disabled in settings.');
|
throw new Error('Cleanup is disabled in settings.');
|
||||||
@@ -721,7 +731,7 @@ export default (router, tpl) => {
|
|||||||
throw new Error('Please select both a Start Date and an End Date.');
|
throw new Error('Please select both a Start Date and an End Date.');
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`[ADMIN] Starting manual cleanup for period ${startDate} to ${endDate}...`);
|
console.log(`[ADMIN] Starting manual cleanup for period ${startDate} to ${endDate} (includeEngaged: ${includeEngaged})...`);
|
||||||
|
|
||||||
const start_stamp = ~~(new Date(startDate).getTime() / 1000);
|
const start_stamp = ~~(new Date(startDate).getTime() / 1000);
|
||||||
const end_stamp = ~~(new Date(endDate).getTime() / 1000) + 86399; // Include full end day
|
const end_stamp = ~~(new Date(endDate).getTime() / 1000) + 86399; // Include full end day
|
||||||
@@ -737,7 +747,7 @@ export default (router, tpl) => {
|
|||||||
AND i.is_pinned = false
|
AND i.is_pinned = false
|
||||||
AND i.stamp >= ${start_stamp}
|
AND i.stamp >= ${start_stamp}
|
||||||
AND i.stamp <= ${end_stamp}
|
AND i.stamp <= ${end_stamp}
|
||||||
AND (
|
${includeEngaged ? db`` : db`AND (
|
||||||
-- Case 1: Active posts with no engagement (ignoring automatic subscriptions)
|
-- Case 1: Active posts with no engagement (ignoring automatic subscriptions)
|
||||||
(i.active = true AND i.is_deleted = false AND NOT EXISTS (SELECT 1 FROM comments WHERE item_id = i.id) AND NOT EXISTS (SELECT 1 FROM favorites WHERE item_id = i.id))
|
(i.active = true AND i.is_deleted = false AND NOT EXISTS (SELECT 1 FROM comments WHERE item_id = i.id) AND NOT EXISTS (SELECT 1 FROM favorites WHERE item_id = i.id))
|
||||||
OR
|
OR
|
||||||
@@ -746,7 +756,7 @@ export default (router, tpl) => {
|
|||||||
OR
|
OR
|
||||||
-- Case 3: Pending posts (not yet approved) that are old enough
|
-- Case 3: Pending posts (not yet approved) that are old enough
|
||||||
(i.active = false AND i.is_deleted = false)
|
(i.active = false AND i.is_deleted = false)
|
||||||
)
|
)`}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const statsInfo = `(Total items: ${totalCleanable[0].c}, In range: ${withinRange[0].c})`;
|
const statsInfo = `(Total items: ${totalCleanable[0].c}, In range: ${withinRange[0].c})`;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ let enable_pdf = false;
|
|||||||
let enable_cleanup = false;
|
let enable_cleanup = false;
|
||||||
let cleanup_start_date = '';
|
let cleanup_start_date = '';
|
||||||
let cleanup_end_date = '';
|
let cleanup_end_date = '';
|
||||||
|
let cleanup_include_engaged = false;
|
||||||
export const getShitpostMode = () => !!cfg.websrv.shitpost_mode;
|
export const getShitpostMode = () => !!cfg.websrv.shitpost_mode;
|
||||||
export const setShitpostMode = (val) => {}; // No-op, strictly config-based
|
export const setShitpostMode = (val) => {}; // No-op, strictly config-based
|
||||||
|
|
||||||
@@ -29,6 +30,9 @@ export const setCleanupStartDate = (val) => cleanup_start_date = val || '';
|
|||||||
export const getCleanupEndDate = () => cleanup_end_date;
|
export const getCleanupEndDate = () => cleanup_end_date;
|
||||||
export const setCleanupEndDate = (val) => cleanup_end_date = val || '';
|
export const setCleanupEndDate = (val) => cleanup_end_date = val || '';
|
||||||
|
|
||||||
|
export const getCleanupIncludeEngaged = () => cleanup_include_engaged;
|
||||||
|
export const setCleanupIncludeEngaged = (val) => cleanup_include_engaged = !!val;
|
||||||
|
|
||||||
export const getEnablePdf = () => enable_pdf;
|
export const getEnablePdf = () => enable_pdf;
|
||||||
export const setEnablePdf = (val) => enable_pdf = !!val;
|
export const setEnablePdf = (val) => enable_pdf = !!val;
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import { handleMetaExtract } from "./meta_extract_handler.mjs";
|
|||||||
import { handleMetaStrip } from "./meta_strip_handler.mjs";
|
import { handleMetaStrip } from "./meta_strip_handler.mjs";
|
||||||
import { handleCommentUpload, handleCommentUploadCancel } from "./comment_upload_handler.mjs";
|
import { handleCommentUpload, handleCommentUploadCancel } from "./comment_upload_handler.mjs";
|
||||||
import { handleDmAttachmentUpload, handleDmAttachmentDownload, handleDmAttachmentDelete } from "./dm_attachment_handler.mjs";
|
import { handleDmAttachmentUpload, handleDmAttachmentDownload, handleDmAttachmentDelete } from "./dm_attachment_handler.mjs";
|
||||||
import { getManualApproval, setManualApproval, getMinTags, setMinTags, getRegistrationOpen, setRegistrationOpen, getTrustedUploads, setTrustedUploads, getBypassDuplicateCheck, setBypassDuplicateCheck, getProtectFiles, setProtectFiles, getPrivateMessages, setPrivateMessages, getDmAttachments, setDmAttachments, getDmUnencrypted, setDmUnencrypted, getDefaultLayout, setDefaultLayout, getEnablePdf, setEnablePdf, getEnableCleanup, setEnableCleanup, getCleanupStartDate, setCleanupStartDate, getCleanupEndDate, setCleanupEndDate, getLogUserIps, setLogUserIps, getHashUserIps, setHashUserIps, getShitpostMode, setShitpostMode, getAllowCommentDeletion, setAllowCommentDeletion, getNsfpIds, setNsfpIds } from "./inc/settings.mjs";
|
import { getManualApproval, setManualApproval, getMinTags, setMinTags, getRegistrationOpen, setRegistrationOpen, getTrustedUploads, setTrustedUploads, getBypassDuplicateCheck, setBypassDuplicateCheck, getProtectFiles, setProtectFiles, getPrivateMessages, setPrivateMessages, getDmAttachments, setDmAttachments, getDmUnencrypted, setDmUnencrypted, getDefaultLayout, setDefaultLayout, getEnablePdf, setEnablePdf, getEnableCleanup, setEnableCleanup, getCleanupStartDate, setCleanupStartDate, getCleanupEndDate, setCleanupEndDate, getCleanupIncludeEngaged, setCleanupIncludeEngaged, getLogUserIps, setLogUserIps, getHashUserIps, setHashUserIps, getShitpostMode, setShitpostMode, getAllowCommentDeletion, setAllowCommentDeletion, getNsfpIds, setNsfpIds } from "./inc/settings.mjs";
|
||||||
import { updateHallsCache, getHalls } from "./inc/halls_cache.mjs";
|
import { updateHallsCache, getHalls } from "./inc/halls_cache.mjs";
|
||||||
import { createI18n } from "./inc/i18n.mjs";
|
import { createI18n } from "./inc/i18n.mjs";
|
||||||
import security from "./inc/security.mjs";
|
import security from "./inc/security.mjs";
|
||||||
@@ -1239,6 +1239,12 @@ process.on('uncaughtException', err => {
|
|||||||
setCleanupEndDate(endSetting[0].value);
|
setCleanupEndDate(endSetting[0].value);
|
||||||
}
|
}
|
||||||
console.log(`[BOOT] Cleanup End Date: ${getCleanupEndDate()}`);
|
console.log(`[BOOT] Cleanup End Date: ${getCleanupEndDate()}`);
|
||||||
|
|
||||||
|
const engagedSetting = await db`SELECT value FROM site_settings WHERE key = 'cleanup_include_engaged' LIMIT 1`;
|
||||||
|
if (engagedSetting.length > 0) {
|
||||||
|
setCleanupIncludeEngaged(engagedSetting[0].value === 'true');
|
||||||
|
}
|
||||||
|
console.log(`[BOOT] Cleanup Include Engaged: ${getCleanupIncludeEngaged()}`);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(`[BOOT] Cleanup settings fetch failed:`, e.message);
|
console.warn(`[BOOT] Cleanup settings fetch failed:`, e.message);
|
||||||
setEnableCleanup(!!cfg.websrv.enable_cleanup);
|
setEnableCleanup(!!cfg.websrv.enable_cleanup);
|
||||||
@@ -1392,6 +1398,7 @@ process.on('uncaughtException', err => {
|
|||||||
get enable_cleanup() { return getEnableCleanup(); },
|
get enable_cleanup() { return getEnableCleanup(); },
|
||||||
get cleanup_start_date() { return getCleanupStartDate(); },
|
get cleanup_start_date() { return getCleanupStartDate(); },
|
||||||
get cleanup_end_date() { return getCleanupEndDate(); },
|
get cleanup_end_date() { return getCleanupEndDate(); },
|
||||||
|
get cleanup_include_engaged() { return getCleanupIncludeEngaged(); },
|
||||||
matrix_enabled: cfg.clients.find(c => c.type === 'matrix')?.enabled || false,
|
matrix_enabled: cfg.clients.find(c => c.type === 'matrix')?.enabled || false,
|
||||||
telegram_enabled: cfg.clients.find(c => c.type === 'tg')?.enabled || false,
|
telegram_enabled: cfg.clients.find(c => c.type === 'tg')?.enabled || false,
|
||||||
ts: Date.now(),
|
ts: Date.now(),
|
||||||
|
|||||||
@@ -26,6 +26,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-item" style="margin-bottom: 20px;">
|
||||||
|
<label style="display: flex; align-items: center; gap: 10px; cursor: pointer; color: #ff6b6b; font-weight: bold;">
|
||||||
|
<input type="checkbox" id="cleanup_include_engaged" name="cleanup_include_engaged" value="true" @if(cleanup_include_engaged) checked @endif style="width: 18px; height: 18px; cursor: pointer;">
|
||||||
|
<span>Include items with engagement</span>
|
||||||
|
</label>
|
||||||
|
<span style="color: #888; display: block; margin-top: 5px; margin-left: 28px;">If checked, cleanup will purge ALL posts in the selected date range, even if they have comments or favorites.</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div style="display: flex; gap: 10px; align-items: center;">
|
<div style="display: flex; gap: 10px; align-items: center;">
|
||||||
<button type="submit" class="btn-primary" style="width: auto; padding: 12px 40px; font-weight: bold;">Save Configuration</button>
|
<button type="submit" class="btn-primary" style="width: auto; padding: 12px 40px; font-weight: bold;">Save Configuration</button>
|
||||||
<span id="cleanup-status" style="margin-left: 15px; font-weight: bold; display: none;"></span>
|
<span id="cleanup-status" style="margin-left: 15px; font-weight: bold; display: none;"></span>
|
||||||
@@ -101,8 +109,13 @@
|
|||||||
async function runCleanup() {
|
async function runCleanup() {
|
||||||
const btn = document.getElementById('run-cleanup-btn');
|
const btn = document.getElementById('run-cleanup-btn');
|
||||||
const status = document.getElementById('run-status');
|
const status = document.getElementById('run-status');
|
||||||
|
const includeEngaged = document.getElementById('cleanup_include_engaged')?.checked || false;
|
||||||
|
|
||||||
if (!confirm('Are you absolutely sure? This will PERMANENTLY delete files from disk. This action cannot be undone.')) return;
|
const warningMsg = includeEngaged
|
||||||
|
? 'SECURITY CLEANUP WARNING: You have selected to INCLUDE posts WITH engagement (comments/favorites). Are you ABSOLUTELY SURE you want to permanently delete ALL posts in this date range?'
|
||||||
|
: 'Are you absolutely sure? This will PERMANENTLY delete files from disk. This action cannot be undone.';
|
||||||
|
|
||||||
|
if (!confirm(warningMsg)) return;
|
||||||
|
|
||||||
btn.disabled = true;
|
btn.disabled = true;
|
||||||
btn.textContent = 'CLEANING UP...';
|
btn.textContent = 'CLEANING UP...';
|
||||||
@@ -110,12 +123,17 @@
|
|||||||
status.style.color = 'var(--accent)';
|
status.style.color = 'var(--accent)';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const formData = new URLSearchParams();
|
||||||
|
formData.append('include_engaged', includeEngaged ? 'true' : 'false');
|
||||||
|
|
||||||
const res = await fetch('/admin/cleanup/run', {
|
const res = await fetch('/admin/cleanup/run', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'X-Requested-With': 'XMLHttpRequest',
|
'X-Requested-With': 'XMLHttpRequest',
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
'X-CSRF-Token': window.f0ckSession?.csrf_token || '{{ csrf_token }}'
|
'X-CSRF-Token': window.f0ckSession?.csrf_token || '{{ csrf_token }}'
|
||||||
}
|
},
|
||||||
|
body: formData
|
||||||
});
|
});
|
||||||
|
|
||||||
const cleanup_response = await res.json();
|
const cleanup_response = await res.json();
|
||||||
|
|||||||
Reference in New Issue
Block a user