clean up deleted q
This commit is contained in:
@@ -206,16 +206,22 @@
|
||||
confirmBtn.onclick = async () => {
|
||||
confirmBtn.textContent = 'Deleting...';
|
||||
confirmBtn.disabled = true;
|
||||
const res = await post("/api/v2/admin/deletepost", {
|
||||
postid: postid
|
||||
});
|
||||
if (!res.success) {
|
||||
alert(res.msg);
|
||||
try {
|
||||
const res = await post("/api/v2/admin/deletepost", {
|
||||
postid: postid
|
||||
});
|
||||
if (!res.success) {
|
||||
alert(res.msg);
|
||||
confirmBtn.textContent = 'Delete';
|
||||
confirmBtn.disabled = false;
|
||||
} else {
|
||||
closeModal();
|
||||
window.location.href = '/';
|
||||
}
|
||||
} catch (e) {
|
||||
alert('Error: ' + e); // Or e.message
|
||||
confirmBtn.textContent = 'Delete';
|
||||
confirmBtn.disabled = false;
|
||||
} else {
|
||||
closeModal();
|
||||
window.location.href = '/';
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -212,57 +212,59 @@ export default (router, tpl) => {
|
||||
posts,
|
||||
page,
|
||||
pages,
|
||||
stats: { total: posts.length },
|
||||
tmp: null
|
||||
}, req)
|
||||
});
|
||||
});
|
||||
|
||||
const deleteItem = async (id) => {
|
||||
const f0ck = await db`
|
||||
select dest, mime
|
||||
from "items"
|
||||
where
|
||||
id = ${id}
|
||||
limit 1
|
||||
`;
|
||||
|
||||
if (f0ck.length > 0) {
|
||||
console.log(`[ADMIN DENY] Found item, deleting files: ${f0ck[0].dest}`);
|
||||
// Delete files
|
||||
await fs.unlink(`./public/b/${f0ck[0].dest}`).catch(e => console.log('File error pub/b:', e.message));
|
||||
await fs.unlink(`./public/t/${id}.webp`).catch(e => console.log('File error pub/t:', e.message));
|
||||
await fs.unlink(`./deleted/b/${f0ck[0].dest}`).catch(e => console.log('File error del/b:', e.message));
|
||||
await fs.unlink(`./deleted/t/${id}.webp`).catch(e => console.log('File error del/t:', e.message));
|
||||
|
||||
if (f0ck[0].mime.startsWith('audio')) {
|
||||
await fs.unlink(`./public/ca/${id}.webp`).catch(() => { });
|
||||
await fs.unlink(`./deleted/ca/${id}.webp`).catch(() => { });
|
||||
}
|
||||
|
||||
// Delete DB entries
|
||||
console.log('[ADMIN DENY] Deleting DB entries...');
|
||||
try {
|
||||
await db`delete from "tags_assign" where item_id = ${id}`;
|
||||
await db`delete from "favorites" where item_id = ${id}`;
|
||||
await db`delete from "comments" where item_id = ${id}`.catch(() => { });
|
||||
await db`delete from "items" where id = ${id}`;
|
||||
console.log('[ADMIN DENY] Deleted successfully');
|
||||
return true;
|
||||
} catch (dbErr) {
|
||||
console.error('[ADMIN DENY DB ERROR]', dbErr);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
console.log('[ADMIN DENY] Item not found in DB');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
router.get(/^\/admin\/deny\/?/, lib.auth, async (req, res) => {
|
||||
console.log('[ADMIN DENY] Logs initiated');
|
||||
if (req.url.qs?.id) {
|
||||
const id = +req.url.qs.id;
|
||||
console.log(`[ADMIN DENY] Denying ID: ${id}`);
|
||||
|
||||
try {
|
||||
const f0ck = await db`
|
||||
select dest, mime
|
||||
from "items"
|
||||
where
|
||||
id = ${id}
|
||||
limit 1
|
||||
`;
|
||||
|
||||
if (f0ck.length > 0) {
|
||||
console.log(`[ADMIN DENY] Found item, deleting files: ${f0ck[0].dest}`);
|
||||
// Delete files
|
||||
await fs.unlink(`./public/b/${f0ck[0].dest}`).catch(e => console.log('File error pub/b:', e.message));
|
||||
await fs.unlink(`./public/t/${id}.webp`).catch(e => console.log('File error pub/t:', e.message));
|
||||
await fs.unlink(`./deleted/b/${f0ck[0].dest}`).catch(e => console.log('File error del/b:', e.message));
|
||||
await fs.unlink(`./deleted/t/${id}.webp`).catch(e => console.log('File error del/t:', e.message));
|
||||
|
||||
if (f0ck[0].mime.startsWith('audio')) {
|
||||
await fs.unlink(`./public/ca/${id}.webp`).catch(() => { });
|
||||
await fs.unlink(`./deleted/ca/${id}.webp`).catch(() => { });
|
||||
}
|
||||
|
||||
// Delete DB entries
|
||||
console.log('[ADMIN DENY] Deleting DB entries...');
|
||||
try {
|
||||
await db`delete from "tags_assign" where item_id = ${id}`;
|
||||
await db`delete from "favorites" where item_id = ${id}`;
|
||||
await db`delete from "comments" where item_id = ${id}`.catch(() => { });
|
||||
await db`delete from "items" where id = ${id}`;
|
||||
console.log('[ADMIN DENY] Deleted successfully');
|
||||
} catch (dbErr) {
|
||||
console.error('[ADMIN DENY DB ERROR]', dbErr);
|
||||
}
|
||||
} else {
|
||||
console.log('[ADMIN DENY] Item not found in DB');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[ADMIN DENY ERROR]', err);
|
||||
}
|
||||
|
||||
await deleteItem(id);
|
||||
return res.writeHead(302, {
|
||||
"Location": `/admin/approve`
|
||||
}).end();
|
||||
@@ -272,5 +274,22 @@ export default (router, tpl) => {
|
||||
return res.writeHead(302, { "Location": "/admin/approve" }).end();
|
||||
});
|
||||
|
||||
router.post(/^\/admin\/deny-multi\/?/, lib.auth, async (req, res) => {
|
||||
try {
|
||||
const ids = req.post.ids;
|
||||
if (!Array.isArray(ids)) throw new Error('ids must be an array');
|
||||
|
||||
console.log(`[ADMIN DENY MULTI] Denying ${ids.length} items`);
|
||||
for (const id of ids) {
|
||||
await deleteItem(+id);
|
||||
}
|
||||
|
||||
return res.reply({ success: true });
|
||||
} catch (err) {
|
||||
console.error('[ADMIN DENY MULTI ERROR]', err);
|
||||
return res.reply({ success: false, msg: err.message }, 500);
|
||||
}
|
||||
});
|
||||
|
||||
return router;
|
||||
};
|
||||
|
||||
@@ -51,34 +51,142 @@
|
||||
</div>
|
||||
<br>
|
||||
@endif
|
||||
|
||||
<div style="text-align: center; margin-bottom: 20px;">
|
||||
<button id="btn-deny-all" class="badge badge-danger" onclick="window.handleDenyAll(event)"
|
||||
style="font-size: 1.2em; padding: 10px 20px; border: none; cursor: pointer;">Deny All</button>
|
||||
</div>
|
||||
|
||||
<a href="/admin">Back to Admin</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Custom Modal -->
|
||||
<div id="custom-modal"
|
||||
style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.7); justify-content: center; align-items: center; z-index: 1000;">
|
||||
<div
|
||||
style="background: #222; color: #fff; padding: 20px; border-radius: 8px; max-width: 400px; text-align: center; border: 1px solid #444;">
|
||||
<h3 id="modal-title" style="margin-top: 0;">Confirm Action</h3>
|
||||
<p id="modal-text">Are you sure?</p>
|
||||
<div style="display: flex; justify-content: space-around; margin-top: 20px;">
|
||||
<button id="modal-cancel" class="badge badge-secondary"
|
||||
style="border: none; padding: 10px 20px; cursor: pointer;">Cancel</button>
|
||||
<button id="modal-confirm" class="badge badge-danger"
|
||||
style="border: none; padding: 10px 20px; cursor: pointer;">Confirm</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.querySelectorAll('.btn-deny-async').forEach(btn => {
|
||||
btn.addEventListener('click', async e => {
|
||||
e.preventDefault();
|
||||
if (!confirm('Permanently delete this item?')) return;
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Dynamic Button Text
|
||||
const btnDenyAllInit = document.getElementById('btn-deny-all');
|
||||
if (btnDenyAllInit) {
|
||||
const count = document.querySelectorAll('.btn-deny-async').length;
|
||||
btnDenyAllInit.innerText = 'Deny All (' + count + ' visible)';
|
||||
}
|
||||
|
||||
const url = btn.href;
|
||||
const row = btn.closest('tr');
|
||||
const originalText = btn.innerText;
|
||||
btn.innerText = 'Deleting...';
|
||||
const modal = document.getElementById('custom-modal');
|
||||
const modalTitle = document.getElementById('modal-title');
|
||||
const modalText = document.getElementById('modal-text');
|
||||
const btnConfirm = document.getElementById('modal-confirm');
|
||||
const btnCancel = document.getElementById('modal-cancel');
|
||||
|
||||
try {
|
||||
const res = await fetch(url);
|
||||
if (res.ok) {
|
||||
row.style.opacity = '0';
|
||||
setTimeout(() => row.remove(), 500);
|
||||
} else {
|
||||
alert('Delete request failed');
|
||||
btn.innerText = originalText;
|
||||
let pendingAction = null;
|
||||
|
||||
const showModal = (title, text, action) => {
|
||||
modalTitle.innerText = title;
|
||||
modalText.innerText = text;
|
||||
pendingAction = action;
|
||||
modal.style.display = 'flex';
|
||||
|
||||
btnConfirm.onclick = async () => {
|
||||
if (!pendingAction) return;
|
||||
btnConfirm.disabled = true;
|
||||
btnConfirm.innerText = 'Processing...';
|
||||
try {
|
||||
await pendingAction();
|
||||
closeModal();
|
||||
} catch (e) {
|
||||
alert('Error: ' + e.message);
|
||||
} finally {
|
||||
btnConfirm.disabled = false;
|
||||
btnConfirm.innerText = 'Confirm';
|
||||
}
|
||||
} catch (err) {
|
||||
alert('Error: ' + err);
|
||||
btn.innerText = originalText;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const closeModal = () => {
|
||||
modal.style.display = 'none';
|
||||
pendingAction = null;
|
||||
};
|
||||
|
||||
if (btnCancel) btnCancel.onclick = closeModal;
|
||||
|
||||
// Single Deny
|
||||
document.querySelectorAll('.btn-deny-async').forEach(btn => {
|
||||
btn.addEventListener('click', e => {
|
||||
e.preventDefault();
|
||||
const url = btn.getAttribute('href');
|
||||
const row = btn.closest('tr');
|
||||
|
||||
showModal('Deny Item', 'Permanently delete this item?', async () => {
|
||||
const res = await fetch(url);
|
||||
if (res.ok) {
|
||||
row.style.opacity = '0';
|
||||
setTimeout(() => row.remove(), 300);
|
||||
} else {
|
||||
throw new Error('Request failed');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Global handler for Deny All
|
||||
window.handleDenyAll = (e) => {
|
||||
e.preventDefault();
|
||||
console.log('Deny All clicked (Inline)');
|
||||
const allBtn = [...document.querySelectorAll('.btn-deny-async')];
|
||||
|
||||
// Map to {id, element}
|
||||
const targets = allBtn.map(b => {
|
||||
const href = b.getAttribute('href');
|
||||
const match = href ? href.match(/[?&]id=([^&]+)/) : null;
|
||||
if (!match && href) console.log('No ID match for href:', href);
|
||||
return match ? { id: match[1], btn: b } : null;
|
||||
}).filter(item => item);
|
||||
|
||||
const ids = targets.map(t => t.id);
|
||||
console.log('Deny List:', ids);
|
||||
|
||||
if (ids.length === 0) return alert('No items to deny');
|
||||
|
||||
showModal('Deny ALL', 'Permanently delete ' + ids.length + ' visible items?', async () => {
|
||||
const res = await fetch('/admin/deny-multi', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ ids })
|
||||
});
|
||||
const json = await res.json();
|
||||
if (json.success) {
|
||||
closeModal(); // UX Polish: Close modal immediately
|
||||
// Visual Removal
|
||||
targets.forEach(t => {
|
||||
const row = t.btn.closest('tr');
|
||||
if (row) {
|
||||
row.style.opacity = '0';
|
||||
}
|
||||
});
|
||||
|
||||
// Allow transition then reload
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 500);
|
||||
} else {
|
||||
throw new Error(json.msg || 'Failed');
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
</script>
|
||||
@include(snippets/footer)
|
||||
Reference in New Issue
Block a user