Spaces:
Configuration error
Configuration error
Download server.js from AITestingWorkSpace/BOU_2026: direct link, hf CLI and curl.
- Browser
- Download file 5.22 kB
-
https://huggingface.co/spaces/AITestingWorkSpace/BOU_2026/resolve/main/server.js
- Command line
-
hf download hf://spaces/AITestingWorkSpace/BOU_2026/server.js
-
curl -L -o server.js https://huggingface.co/spaces/AITestingWorkSpace/BOU_2026/resolve/main/server.js
5.22 kB
| // BOU 2026 - Best Project voting app | |
| // Serves the voting page and logs every vote (Name, UID, Email, Project, Timestamp) | |
| // into an Excel file on disk, one row per voter (re-voting updates their row). | |
| const path = require("path"); | |
| const fs = require("fs"); | |
| const express = require("express"); | |
| const ExcelJS = require("exceljs"); | |
| const PORT = process.env.PORT || 3000; | |
| const ADMIN_KEY = process.env.ADMIN_KEY || "change-me"; // set a real secret before deploying | |
| const VOTES_FILE = path.join(__dirname, "data", "votes.xlsx"); | |
| const PROJECTS = [ | |
| { id: "1", name: "Global Account – DAS (Adecco)", who: "Sathiaseelan Arumugam" }, | |
| { id: "2", name: "PCB & IC Package Team", who: "Syam" }, | |
| { id: "3", name: "Product Engineering / Embedded", who: "Munajat Abdul Hadi" }, | |
| { id: "4", name: "CIS", who: "Shunmuganainar Chockalingam" }, | |
| { id: "5", name: "ATE", who: "Kother" }, | |
| { id: "6", name: "Manufacturing POD", who: "Rohan Chowdhary" }, | |
| { id: "7", name: "Manufacturing", who: "Hasrul Hasni" }, | |
| ]; | |
| const PROJECT_IDS = new Set(PROJECTS.map((p) => p.id)); | |
| const HEADERS = ["Name", "UID", "Email", "Project ID", "Project Name", "Timestamp"]; | |
| // --- tiny write queue so concurrent votes never corrupt the Excel file --- | |
| let writeQueue = Promise.resolve(); | |
| function withWriteLock(fn) { | |
| const result = writeQueue.then(fn, fn); | |
| writeQueue = result.catch(() => {}); | |
| return result; | |
| } | |
| async function loadWorkbook() { | |
| const wb = new ExcelJS.Workbook(); | |
| if (fs.existsSync(VOTES_FILE)) { | |
| await wb.xlsx.readFile(VOTES_FILE); | |
| } else { | |
| fs.mkdirSync(path.dirname(VOTES_FILE), { recursive: true }); | |
| const ws = wb.addWorksheet("Votes"); | |
| ws.addRow(HEADERS); | |
| ws.getRow(1).font = { bold: true }; | |
| } | |
| const ws = wb.getWorksheet("Votes") || wb.addWorksheet("Votes"); | |
| if (ws.rowCount === 0) { | |
| ws.addRow(HEADERS); | |
| ws.getRow(1).font = { bold: true }; | |
| } | |
| return { wb, ws }; | |
| } | |
| async function recordVote({ name, uid, email, projectId }) { | |
| return withWriteLock(async () => { | |
| const { wb, ws } = await loadWorkbook(); | |
| const project = PROJECTS.find((p) => p.id === projectId); | |
| // find an existing row for this UID (column 2) so re-voting updates it | |
| let existingRow = null; | |
| ws.eachRow((row, rowNumber) => { | |
| if (rowNumber === 1) return; | |
| if (String(row.getCell(2).value) === String(uid)) existingRow = row; | |
| }); | |
| const values = [name, uid, email, project.id, project.name, new Date().toISOString()]; | |
| if (existingRow) { | |
| existingRow.values = [, ...values]; // keep 1-based column alignment | |
| } else { | |
| ws.addRow(values); | |
| } | |
| await wb.xlsx.writeFile(VOTES_FILE); | |
| }); | |
| } | |
| async function tallyResults() { | |
| const { ws } = await loadWorkbook(); | |
| const counts = {}; | |
| let total = 0; | |
| const voters = []; | |
| ws.eachRow((row, rowNumber) => { | |
| if (rowNumber === 1) return; | |
| const projectId = String(row.getCell(4).value || ""); | |
| if (!projectId) return; | |
| counts[projectId] = (counts[projectId] || 0) + 1; | |
| total += 1; | |
| voters.push({ | |
| name: row.getCell(1).value, | |
| uid: row.getCell(2).value, | |
| email: row.getCell(3).value, | |
| projectId, | |
| }); | |
| }); | |
| return { counts, total, voters }; | |
| } | |
| // --- app --- | |
| const app = express(); | |
| app.use(express.json()); | |
| app.use(express.static(path.join(__dirname, "public"))); | |
| app.get("/api/projects", (req, res) => res.json(PROJECTS)); | |
| app.post("/api/vote", async (req, res) => { | |
| const { name, uid, email, projectId } = req.body || {}; | |
| if (!name || !uid || !email || !projectId) { | |
| return res.status(400).json({ ok: false, error: "Name, UID, email and a project choice are all required." }); | |
| } | |
| if (!/^\S+@\S+\.\S+$/.test(email)) { | |
| return res.status(400).json({ ok: false, error: "That email address doesn't look valid." }); | |
| } | |
| if (!PROJECT_IDS.has(String(projectId))) { | |
| return res.status(400).json({ ok: false, error: "Unknown project." }); | |
| } | |
| try { | |
| await recordVote({ name: String(name).trim(), uid: String(uid).trim(), email: String(email).trim(), projectId: String(projectId) }); | |
| res.json({ ok: true }); | |
| } catch (err) { | |
| console.error(err); | |
| res.status(500).json({ ok: false, error: "Could not save your vote. Please try again." }); | |
| } | |
| }); | |
| function requireAdmin(req, res, next) { | |
| const key = req.query.key || req.headers["x-admin-key"]; | |
| if (key !== ADMIN_KEY) return res.status(401).json({ ok: false, error: "Invalid admin key." }); | |
| next(); | |
| } | |
| app.get("/api/results", requireAdmin, async (req, res) => { | |
| try { | |
| const { counts, total } = await tallyResults(); | |
| res.json({ ok: true, projects: PROJECTS, counts, total }); | |
| } catch (err) { | |
| console.error(err); | |
| res.status(500).json({ ok: false, error: "Could not load results." }); | |
| } | |
| }); | |
| app.get("/api/export", requireAdmin, async (req, res) => { | |
| if (!fs.existsSync(VOTES_FILE)) { | |
| return res.status(404).json({ ok: false, error: "No votes recorded yet." }); | |
| } | |
| res.download(VOTES_FILE, "bou2026-votes.xlsx"); | |
| }); | |
| app.listen(PORT, () => { | |
| console.log(`BOU 2026 voting app listening on port ${PORT}`); | |
| if (ADMIN_KEY === "change-me") { | |
| console.warn("WARNING: set a real ADMIN_KEY environment variable before sharing this app."); | |
| } | |
| }); | |