CoolFace
Apppublic

Peacemanguy/LLMChoice

sourceHugging Faceupdated 1y agoView on Hugging Face
0likes
server.js273 linesDownload Raw Back to root
1// server.js  — one‑vote‑per‑IP edition2const express     = require('express');3const bodyParser  = require('body-parser');4const fs          = require('fs');5const path        = require('path');6const requestIp   = require('request-ip');       // NEW7const crypto      = require('crypto');           // we'll hash IPs before saving8const archiver    = require('./leaderboard_archiver');9const https       = require('https');            // For Hugging Face API requests10 11const PORT         = process.env.PORT || 3000;12const DATA_FILE    = path.join(__dirname, 'data', 'data.json');13const IP_FILE      = path.join(__dirname, 'data', 'ips.json');14 15const CATEGORIES = ["6gb", "12gb", "16gb", "24gb", "48gb", "72gb", "96gb"];16function validateCategory(cat) {17  return CATEGORIES.includes(cat);18}19 20const app = express();21app.use(bodyParser.json());22app.use(requestIp.mw());                         // adds req.clientIp23app.use(express.static(path.join(__dirname, 'public')));24 25/* ---------- tiny helpers ---------- */26function readJson(file, fallback) {27  try { return JSON.parse(fs.readFileSync(file)); }28  catch { return fallback; }29}30function writeJson(file, obj) {31  fs.writeFileSync(file, JSON.stringify(obj, null, 2));32}33function hash(ip) {                              // do not store raw IP34  return crypto.createHash('sha256').update(ip).digest('hex');35}36 37/* ---------- IP‑limit middleware ---------- */38function oneVotePerIP(req, res, next) {39  const ipList = readJson(IP_FILE, {});40  const key    = hash(req.clientIp || 'unknown');41  if (ipList[key]) return res.status(409)42    .json({ error: 'You have already voted from this IP' });43  req._ipKey = key;          // remember for later44  next();45}46 47/* ---------- Ensure IP tracking is properly formatted ---------- */48function ensureValidIpTracking() {49  const ips = readJson(IP_FILE, {});50  let changed = false;51  52  // Convert any string values to objects53  Object.keys(ips).forEach(key => {54    if (typeof ips[key] === 'string') {55      ips[key] = {};56      changed = true;57    }58  });59  60  if (changed) {61    writeJson(IP_FILE, ips);62  }63  64  return ips;65}66 67/* ---------- API ---------- */68app.get('/api/entries', (req, res) => {69  const category = req.query.category;70  const data = readJson(DATA_FILE, {});71  if (!validateCategory(category)) {72    return res.status(400).json({ error: 'Invalid category' });73  }74  const entries = (data[category] || []).sort((a, b) => b.votes - a.votes);75  res.json(entries);76});77 78/*  Add new entry + cast initial vote  */79app.post('/api/add', (req, res) => {80  const name = (req.body.name || '').trim();81  const category = req.body.category;82  if (!name) return res.status(400).json({ error: 'Name required' });83  if (!validateCategory(category)) return res.status(400).json({ error: 'Invalid category' });84 85  const data = readJson(DATA_FILE, {});86  const list = data[category] = data[category] || [];87  if (list.find(e => e.name.toLowerCase() === name.toLowerCase()))88    return res.status(400).json({ error: 'Entry already exists' });89 90  const ips = ensureValidIpTracking();91  const ipKey = hash(req.clientIp || 'unknown');92  if (!ips[ipKey] || typeof ips[ipKey] !== 'object') ips[ipKey] = {};93  const prevVotedId = ips[ipKey][category];94 95  // If user has already voted for another entry, decrement its votes96  if (prevVotedId) {97    const prevItem = list.find(e => e.id === prevVotedId);98    if (prevItem && prevItem.votes > 0) prevItem.votes -= 1;99  }100 101  // Add new entry with 1 vote102  const entry = { id: Date.now().toString(), name, votes: 1 };103  list.push(entry);104  writeJson(DATA_FILE, data);105 106  // Update IP record to new entry id for this category107  ips[ipKey][category] = entry.id;108  writeJson(IP_FILE, ips);109 110  res.json(entry);111});112 113/*  Vote for existing entry  */114app.post('/api/vote', (req, res) => {115  const { id, category } = req.body;116  if (!validateCategory(category)) return res.status(400).json({ error: 'Invalid category' });117 118  const data = readJson(DATA_FILE, {});119  const list = data[category] = data[category] || [];120  const item = list.find(e => e.id === id);121  if (!item) return res.status(404).json({ error: 'Entry not found' });122 123  const ips = ensureValidIpTracking();124  const ipKey = hash(req.clientIp || 'unknown');125  if (!ips[ipKey] || typeof ips[ipKey] !== 'object') ips[ipKey] = {};126  const prevVotedId = ips[ipKey][category];127 128  if (prevVotedId === id) {129    // Already voted for this option130    return res.status(409).json({ error: 'You have already voted for this option' });131  }132 133  // If user has voted for a different option, decrement that vote134  if (prevVotedId) {135    const prevItem = list.find(e => e.id === prevVotedId);136    if (prevItem && prevItem.votes > 0) prevItem.votes -= 1;137  }138 139  // Increment vote for the new option140  item.votes += 1;141  writeJson(DATA_FILE, data);142 143  // Update IP record to new voted id for this category144  ips[ipKey][category] = id;145  writeJson(IP_FILE, ips);146 147  res.json(item);148});149 150/* ---------- Archive API ---------- */151// Get list of archived weeks152app.get('/api/archives/weeks', (req, res) => {153  try {154    const weeks = archiver.getArchivedWeeks();155    res.json(weeks);156  } catch (error) {157    console.error('Error getting archived weeks:', error);158    res.status(500).json({ error: 'Failed to retrieve archived weeks' });159  }160});161 162// Get archived data for a specific week163app.get('/api/archives/week/:weekId', (req, res) => {164  try {165    const { weekId } = req.params;166    const archive = archiver.getArchivedWeek(weekId);167    168    if (!archive) {169      return res.status(404).json({ error: 'Archive not found for the specified week' });170    }171    172    res.json(archive);173  } catch (error) {174    console.error('Error getting archived week:', error);175    res.status(500).json({ error: 'Failed to retrieve archived data' });176  }177});178 179// Get archived data for a specific week and category180app.get('/api/archives/week/:weekId/category/:category', (req, res) => {181  try {182    const { weekId, category } = req.params;183    const archive = archiver.getArchivedWeek(weekId);184    185    if (!archive) {186      return res.status(404).json({ error: 'Archive not found for the specified week' });187    }188    189    if (!validateCategory(category)) {190      return res.status(400).json({ error: 'Invalid category' });191    }192    193    const entries = (archive.data[category] || []).sort((a, b) => b.votes - a.votes);194    res.json(entries);195  } catch (error) {196    console.error('Error getting archived category:', error);197    res.status(500).json({ error: 'Failed to retrieve archived data' });198  }199});200 201// Get archived data for a date range202app.get('/api/archives/range', (req, res) => {203  try {204    const { startDate, endDate } = req.query;205    206    if (!startDate || !endDate) {207      return res.status(400).json({ error: 'Both startDate and endDate are required' });208    }209    210    const archives = archiver.getArchivedRange(startDate, endDate);211    res.json(archives);212  } catch (error) {213    console.error('Error getting archived range:', error);214    res.status(500).json({ error: 'Failed to retrieve archived data for the specified range' });215  }216});217 218/* ---------- Hugging Face API Proxy ---------- */219app.get('/api/huggingface/models', (req, res) => {220  const query = req.query.query;221  222  if (!query || query.length < 2) {223    return res.status(400).json({ error: 'Query must be at least 2 characters' });224  }225  226  const options = {227    hostname: 'huggingface.co',228    path: `/api/models?search=${encodeURIComponent(query)}`,229    method: 'GET',230    headers: {231      'Accept': 'application/json'232    }233  };234  235  const hfRequest = https.request(options, (hfResponse) => {236    let data = '';237    238    hfResponse.on('data', (chunk) => {239      data += chunk;240    });241    242    hfResponse.on('end', () => {243      try {244        const parsedData = JSON.parse(data);245        246        // Format the response to include only necessary information247        const formattedResults = parsedData.map(model => ({248          id: model.id,249          modelId: model.modelId,250          name: model.name || model.id,251          author: model.author?.name || 'Unknown',252          downloads: model.downloads || 0,253          likes: model.likes || 0254        })).slice(0, 10); // Limit to 10 results255        256        res.json(formattedResults);257      } catch (error) {258        console.error('Error parsing Hugging Face API response:', error);259        res.status(500).json({ error: 'Failed to parse Hugging Face API response' });260      }261    });262  });263  264  hfRequest.on('error', (error) => {265    console.error('Error fetching from Hugging Face API:', error);266    res.status(500).json({ error: 'Failed to fetch from Hugging Face API' });267  });268  269  hfRequest.end();270});271 272/* ---------- start ---------- */273app.listen(PORT, () => console.log('Leaderboard running on', PORT));