HEX
Server: LiteSpeed
System: Linux houston.panomity.com 6.8.0-100-generic #100-Ubuntu SMP PREEMPT_DYNAMIC Tue Jan 13 16:40:06 UTC 2026 x86_64
User: nudepix (1011)
PHP: 7.4.33
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: //proc/thread-self/root/tmp/enable_vote_categories.mjs
import fs from 'node:fs';

const voteCategories = "['all', 'bikini', 'dating', 'business', 'social']";

let votes = fs.readFileSync('api/src/routes/votes.js', 'utf8');
votes = votes.replace(
`const normalizeCategory = (raw) => {
	const val = String(raw || '').toLowerCase();
	return val === 'bikini' ? 'bikini' : 'all';
};`,
`const normalizeCategory = (raw) => {
	const val = String(raw || '').toLowerCase();
	return ${voteCategories}.includes(val) ? val : 'all';
};`
);
votes = votes.replace(
"'INSERT INTO votes (photo_id, voter_user_id, ip_hash, score) VALUES (?,?,?,?)', [\n\t\t\t\tphotoId, userId, ipHash, score\n\t\t\t]",
"'INSERT INTO votes (photo_id, voter_user_id, ip_hash, score, category) VALUES (?,?,?,?,?)', [\n\t\t\t\tphotoId, userId, ipHash, score, category\n\t\t\t]"
);
fs.writeFileSync('api/src/routes/votes.js', votes, 'utf8');

let photos = fs.readFileSync('api/src/routes/photos.js', 'utf8');
photos = photos.replace(
`const normalizeCategory = (raw) => {
	const val = String(raw || '').toLowerCase();
	return val === 'bikini' ? 'bikini' : 'all';
};`,
`const normalizeCategory = (raw) => {
	const val = String(raw || '').toLowerCase();
	return ${voteCategories}.includes(val) ? val : 'all';
};`
);
fs.writeFileSync('api/src/routes/photos.js', photos, 'utf8');

let leaderboards = fs.readFileSync('api/src/jobs/leaderboards.js', 'utf8');
leaderboards = leaderboards.replace(
`const categories = [
  { key: 'all', where: "p.status='active'" },
  { key: 'bikini', where: "JSON_EXTRACT(p.ai_flags, '$.is_bikini')=true AND p.status='active'" }
];`,
`const categories = [
  { key: 'all', where: "p.status='active'" },
  { key: 'bikini', where: "JSON_EXTRACT(p.ai_flags, '$.is_bikini')=true AND p.status='active'", voteCategory: 'bikini' },
  { key: 'dating', where: "p.status='active'", voteCategory: 'dating' },
  { key: 'business', where: "p.status='active'", voteCategory: 'business' },
  { key: 'social', where: "p.status='active'", voteCategory: 'social' }
];`
);
leaderboards = leaderboards.replace(
`const loadGlobalAverage = async (pool, where, days) => {
  const params = [];
  const timeFilter = days ? 'AND v.created_at >= UTC_TIMESTAMP() - INTERVAL ? DAY' : '';
  if (days) params.push(days);`,
`const loadGlobalAverage = async (pool, category, days) => {
  const params = [];
  const timeFilter = days ? 'AND v.created_at >= UTC_TIMESTAMP() - INTERVAL ? DAY' : '';
  const categoryFilter = category.voteCategory ? 'AND v.category=?' : '';
  if (category.voteCategory) params.push(category.voteCategory);
  if (days) params.push(days);`
);
leaderboards = leaderboards.replace('WHERE ${where} ${timeFilter}`', 'WHERE ${category.where} ${categoryFilter} ${timeFilter}`');
leaderboards = leaderboards.replace(
`const loadCandidates = async (pool, where, days) => {
  const params = [];
  const voteJoin = days
    ? 'LEFT JOIN votes v ON v.photo_id=p.id AND v.created_at >= UTC_TIMESTAMP() - INTERVAL ? DAY'
    : 'LEFT JOIN votes v ON v.photo_id=p.id';
  if (days) params.push(days);`,
`const loadCandidates = async (pool, category, days) => {
  const params = [];
  const categoryJoinFilter = category.voteCategory ? ' AND v.category=?' : '';
  const voteJoin = days
    ? 'LEFT JOIN votes v ON v.photo_id=p.id' + categoryJoinFilter + ' AND v.created_at >= UTC_TIMESTAMP() - INTERVAL ? DAY'
    : 'LEFT JOIN votes v ON v.photo_id=p.id' + categoryJoinFilter;
  if (category.voteCategory) params.push(category.voteCategory);
  if (days) params.push(days);`
);
leaderboards = leaderboards.replace('WHERE ${where}', 'WHERE ${category.where}');
leaderboards = leaderboards.replace('const globalAverage = await loadGlobalAverage(pool, category.where, period.days);', 'const globalAverage = await loadGlobalAverage(pool, category, period.days);');
leaderboards = leaderboards.replace('const candidates = await loadCandidates(pool, category.where, period.days);', 'const candidates = await loadCandidates(pool, category, period.days);');
fs.writeFileSync('api/src/jobs/leaderboards.js', leaderboards, 'utf8');

let leaderRoute = fs.readFileSync('api/src/routes/leaderboards.js', 'utf8');
leaderRoute = leaderRoute.replace(
"const haveDefault = rows.some(r => r.category === 'all' && r.period === 'all') && rows.some(r => r.category === 'bikini' && r.period === 'all');",
"const requiredCategories = ['all', 'bikini', 'dating', 'business', 'social'];\n\t\tconst haveDefault = requiredCategories.every(category => rows.some(r => r.category === category && r.period === 'all'));"
);
leaderRoute = leaderRoute.replace(
"if (!rows.length && period === 'all' && (category === 'all' || category === 'bikini')) {",
"if (!rows.length && period === 'all' && ['all', 'bikini', 'dating', 'business', 'social'].includes(category)) {"
);
fs.writeFileSync('api/src/routes/leaderboards.js', leaderRoute, 'utf8');

let db = fs.readFileSync('db.sql', 'utf8');
db = db.replace(
`  score TINYINT UNSIGNED NOT NULL CHECK (score BETWEEN 1 AND 10),
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id),
  KEY idx_votes_photo (photo_id),`,
`  score TINYINT UNSIGNED NOT NULL CHECK (score BETWEEN 1 AND 10),
  category VARCHAR(32) NOT NULL DEFAULT 'all',
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id),
  KEY idx_votes_photo (photo_id),
  KEY idx_votes_category_photo (category, photo_id),`
);
fs.writeFileSync('db.sql', db, 'utf8');

let top10 = fs.readFileSync('web/src/modules/home/Top10Page.jsx', 'utf8');
top10 = top10.replace(
`const ContextSegmentRail = ({ segments, sourceItems }) => (`,
`const ContextSegmentRail = ({ segments, data }) => (`
);
top10 = top10.replace(
`const items = pickSegmentItems(sourceItems, index);`,
`const realItems = data[buildKey(segment.key, 'all')] || [];
        const items = realItems.length > 0 ? realItems : pickSegmentItems(data[buildKey('all', 'all')] || data[buildKey('all', '7d')] || data[buildKey('all', 'rising')] || [], index);`
);
top10 = top10.replace(
`<ContextSegmentRail segments={contextSegments} sourceItems={(data[buildKey('all', 'all')] || data[buildKey('all', '7d')] || data[buildKey('all', 'rising')] || [])} />`,
`<ContextSegmentRail segments={contextSegments} data={data} />`
);
fs.writeFileSync('web/src/modules/home/Top10Page.jsx', top10, 'utf8');

console.log('vote categories enabled in source');