This commit is contained in:
2026-08-07 21:42:12 +02:00
parent c7414e4b21
commit feb408338a
40 changed files with 927 additions and 229 deletions
+81 -44
View File
@@ -302,6 +302,9 @@ export default {
userHallFilter = db`and items.id in (select uha.item_id from user_halls_assign uha where uha.hall_id = ${userHallObj.id})`;
}
const isOwnerOrAdmin = (session && user && typeof user === 'string' && session.user && session.user.toLowerCase() === user.toLowerCase()) || (session && (session.admin || session.is_moderator));
const visibilityFilter = isOwnerOrAdmin ? db`` : db`and coalesce(items.visibility, 0) = 0`;
const cacheKey = buildCountCacheKey({ modequery, tag, user, hall, mime, fav, session, excludedTags, newerThan, minXd, userHallObj, tagger });
let total = getCachedCount(cacheKey);
@@ -313,6 +316,7 @@ export default {
where
${db.unsafe(modequery)}
and items.active = true
${visibilityFilter}
${tagFilter}
${titleFilter}
${fav ? db`and fav_u.user ilike ${user}` : db``}
@@ -355,6 +359,7 @@ export default {
where
${db.unsafe(modequery)}
and items.active = true
${visibilityFilter}
${tagFilter}
${titleFilter}
${fav ? db`and fav_u.user ilike ${user}` : db``}
@@ -385,6 +390,8 @@ export default {
const rows = (await db`
select
items.id,
items.slug,
items.visibility,
items.mime,
items.dest,
items.username as username,
@@ -521,7 +528,17 @@ export default {
if (uhData.length) userHallObj = uhData[0];
}
const mime = (rawMime ?? "");
const itemid = rawItemid ? +rawItemid : null;
const rawIdOrSlug = rawItemid ?? null;
if (rawIdOrSlug === null || rawIdOrSlug === undefined || rawIdOrSlug === '') {
return {
success: false,
message: "404 - upload not found"
};
}
const isNumeric = /^\d+$/.test(String(rawIdOrSlug));
const itemLookup = isNumeric ? db`items.id = ${+rawIdOrSlug}` : db`items.slug = ${String(rawIdOrSlug)}`;
const mimeParts = (mime || "").split(',').filter(m => ['video', 'audio', 'image', 'flash', 'pdf'].includes(m));
const mimeSQL = mimeParts.length > 0
? db`and (${mimeParts.map(m => m === 'flash'
@@ -535,19 +552,12 @@ export default {
const strictParams = ((strict || (tag && tag.includes(','))) && tag) ? tag.split(',').map(t => lib.slugify(t)).filter(t => t) : [];
const isStrict = strictParams.length > 0;
const tmp = { user, tag: isTitleSearch ? _decodedTag : tag, hall, mime, itemid, strict: strict, userHall: userHallObj || userHallSlug, userHallOwner };
const tmp = { user, tag: isTitleSearch ? _decodedTag : tag, hall, mime, itemid: rawIdOrSlug, strict: strict, userHall: userHallObj || userHallSlug, userHallOwner };
const effMode = Number(mode ?? 0);
const multiRatingSQL = (Array.isArray(ratings) && ratings.length > 0) ? lib.getMultiRatingMode(ratings) : null;
const modequery = multiRatingSQL ?? lib.getMode(effMode);
if (itemid === null) {
return {
success: false,
message: "404 - upload not found"
};
}
let tagFilter = db``;
let titleFilter = db``;
if (isTitleSearch && titleQuery) {
@@ -588,6 +598,7 @@ export default {
return db`
${db.unsafe(modequery)}
and items.active = true
and coalesce(items.visibility, 0) = 0
${tagFilter}
${titleFilter}
${hallFilter}
@@ -601,11 +612,9 @@ export default {
};
const startTime = Date.now();
console.log(`[${new Date().toISOString()}] [GETF0CK_OPT] Starting fetch for itemid=${itemid}`);
console.log(`[${new Date().toISOString()}] [GETF0CK_OPT] Starting fetch for rawIdOrSlug=${rawIdOrSlug}`);
// 1. Fetch the main item
// We only apply the active check and global NSFW filter (for guests) here.
// We skip the 'mode' preference filter so that switching modes on an item view doesn't result in a 404 (post not visible).
const items = await db`
select distinct on (items.id)
items.*,
@@ -629,7 +638,7 @@ export default {
left join "user_options" uo on uo.user_id = author_u.id
${user_id ? db`left join user_video_views uvv on uvv.video_id = items.id and uvv.user_id = ${user_id}` : db``}
where
items.id = ${itemid} and
${itemLookup} and
items.active = true
${!session && getGlobalfilter() ? db`and not exists (select 1 from tags_assign where item_id = items.id and (${db.unsafe(getGlobalfilter())}))` : db``}
limit 1
@@ -637,7 +646,41 @@ export default {
const actitem = items[0];
if (actitem && user_id) {
if (!actitem) {
return {
success: false,
message: "404 - upload not found"
};
}
const itemid = actitem.id;
// Check visibility permissions:
const isOwnerOrAdmin = session && (
(session.user && session.user.toLowerCase() === (actitem.username || '').toLowerCase()) ||
session.admin || session.is_moderator
);
// If request was by sequential numeric ID (/123) and item visibility > 0 (unlisted/private):
// Block numeric enumeration unless viewer is owner/admin
if (isNumeric && actitem.visibility > 0 && !isOwnerOrAdmin) {
return {
success: false,
message: "404 - upload not found"
};
}
// If item is Private (visibility === 2):
// Direct link only allowed for owner/admin
if (actitem.visibility === 2 && !isOwnerOrAdmin) {
return {
success: false,
is_private: true,
message: "403 - private upload"
};
}
if (user_id) {
db`
insert into user_video_views (user_id, video_id, view_count, last_viewed)
values (${user_id}, ${itemid}, 1, now())
@@ -646,34 +689,26 @@ export default {
last_viewed = now()
`.catch(e => console.error('Failed to track view:', e));
}
if (!actitem) {
// Item not found or filtered out - check if it exists but was filtered (for OG meta tags)
if (!session && getGlobalfilter()) {
// Guest global filter check if item was filtered out
if (!session && getGlobalfilter() && !actitem) {
const unfilteredItem = await db`
select id from items where id = ${itemid} and active = true limit 1
select id from items where ${itemLookup} and active = true limit 1
`;
if (unfilteredItem[0]) {
// Item exists but was filtered - return minimal data for OG tags with blurred thumbnail
const hallSlug = hall && typeof hall === 'object' ? hall.slug : hall;
return {
success: false,
message: "Sorry, this post is currently not visible.",
item: {
id: itemid,
og_thumbnail: `${cfg.websrv.paths.thumbnails}/${itemid}_blur.webp`,
id: unfilteredItem[0].id,
og_thumbnail: `${cfg.websrv.paths.thumbnails}/${unfilteredItem[0].id}_blur.webp`,
og_url: hallSlug
? `https://${cfg.main.url.domain}/h/${encodeURIComponent(hallSlug)}/${itemid}`
: `https://${cfg.main.url.domain}/${itemid}`,
? `https://${cfg.main.url.domain}/h/${encodeURIComponent(hallSlug)}/${unfilteredItem[0].id}`
: `https://${cfg.main.url.domain}/${unfilteredItem[0].id}`,
og_description: `Content not visible in current mode`
}
};
}
}
return {
success: false,
message: "Sorry, this post is currently not visible."
};
}
// 2. Fetch Next/Prev/Start/End/Cheat in parallel
@@ -685,7 +720,7 @@ export default {
const baseQuery = (whereClause, orderBy, limit = 1) => {
return db`
select items.id
select items.id, items.slug
from items
left join tags_assign on tags_assign.item_id = items.id
left join tags on tags.id = tags_assign.tag_id
@@ -696,7 +731,7 @@ export default {
where
${buildConditions()}
${whereClause}
group by items.id
group by items.id, items.slug
${orderBy}
limit ${limit}
`;
@@ -711,7 +746,7 @@ export default {
const checkFilter = !session && nsfpIds.length > 0;
const query = db`
SELECT ta.item_id as id
SELECT ta.item_id as id, items.slug
FROM tags_assign ta
INNER JOIN items ON items.id = ta.item_id
${checkFilter
@@ -781,24 +816,24 @@ export default {
if (actitem.checksum && actitem.checksum.includes('_bypass_')) {
const baseChecksum = actitem.checksum.split('_bypass_')[0];
const repostRows = await db`
SELECT id, username, stamp FROM items
SELECT id, slug, username, stamp FROM items
WHERE active = true
AND id != ${itemid}
AND (checksum = ${baseChecksum} OR checksum LIKE ${baseChecksum + '_bypass_%'})
ORDER BY id ASC
`;
repostItems = repostRows.map(r => ({ id: r.id, username: r.username, stamp: r.stamp, match_type: 'checksum' }));
repostItems = repostRows.map(r => ({ id: r.id, slug: r.slug, username: r.username, stamp: r.stamp, match_type: 'checksum' }));
} else if (actitem.checksum) {
// Even without bypass, check if other bypass-entries exist with this same hash
const baseChecksum = actitem.checksum;
const repostRows = await db`
SELECT id, username, stamp FROM items
SELECT id, slug, username, stamp FROM items
WHERE active = true
AND id != ${itemid}
AND checksum LIKE ${baseChecksum + '_bypass_%'}
ORDER BY id ASC
`;
repostItems = repostRows.map(r => ({ id: r.id, username: r.username, stamp: r.stamp, match_type: 'checksum' }));
repostItems = repostRows.map(r => ({ id: r.id, slug: r.slug, username: r.username, stamp: r.stamp, match_type: 'checksum' }));
}
// Also find visually-similar items via phash, merging with checksum results
@@ -808,7 +843,7 @@ export default {
const existingIds = new Set(repostItems.map(r => r.id));
for (const pm of phashMatches) {
if (!existingIds.has(pm.id)) {
repostItems.push({ id: pm.id, username: pm.username, stamp: pm.stamp, match_type: 'phash' });
repostItems.push({ id: pm.id, slug: pm.slug, username: pm.username, stamp: pm.stamp, match_type: 'phash' });
existingIds.add(pm.id);
}
}
@@ -843,7 +878,7 @@ export default {
else if (userMode === 4 && (!cfg.enable_nsfl || !isNsfl)) modeBlocked = true; // NSFL mode, item is not NSFL
else if (userMode === 2 && isTagged) modeBlocked = true; // Untagged mode, item has tags
if (modeBlocked) {
if (modeBlocked && !isOwnerOrAdmin && actitem.visibility !== 1) {
const hallSlug = hall && typeof hall === 'object' ? hall.slug : hall;
return {
success: false,
@@ -871,6 +906,8 @@ export default {
},
item: {
id: actitem.id,
slug: actitem.slug || null,
visibility: actitem.visibility !== undefined ? actitem.visibility : 0,
username: actitem.username,
author_id: actitem.author_id,
author_color: actitem.author_color,
@@ -933,13 +970,13 @@ export default {
height: actitem.height || null,
original_filename: actitem.original_filename || null
},
title: `${actitem.id} - ${cfg.websrv.domain}`,
title: `${(cfg.enable_item_slugs !== false && actitem.slug) ? actitem.slug : actitem.id} - ${cfg.websrv.domain}`,
pagination: {
end: endItem[0]?.id || itemid,
start: startItem[0]?.id || itemid,
next: nextItem[0]?.id || null,
prev: prevItem[0]?.id || null,
page: actitem.id,
end: (cfg.enable_item_slugs !== false && endItem[0]?.slug) ? endItem[0].slug : (endItem[0]?.id || itemid),
start: (cfg.enable_item_slugs !== false && startItem[0]?.slug) ? startItem[0].slug : (startItem[0]?.id || itemid),
next: (cfg.enable_item_slugs !== false && nextItem[0]?.slug) ? nextItem[0].slug : (nextItem[0]?.id || null),
prev: (cfg.enable_item_slugs !== false && prevItem[0]?.slug) ? prevItem[0].slug : (prevItem[0]?.id || null),
page: (cfg.enable_item_slugs !== false && actitem.slug) ? actitem.slug : actitem.id,
cheat: cheat
},
phrase: cfg.websrv.phrases[~~(Math.random() * cfg.websrv.phrases.length)],