69
This commit is contained in:
@@ -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)],
|
||||
|
||||
Reference in New Issue
Block a user