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');