个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证Source code for our website that allows users to vote on the next song to be played.
翻译准备中
取自仓库 README
正在加载完整 README。
查看完整 README ↗A real-time voting website that lets visitors choose which Christmas song plays next at a synchronized light show. Visitors cast votes from their phones, and the results are broadcast to fppscheduler via MQTT to influence the show's playlist.
Visitor Browser
│
│ HTTP / WebSocket (Hapi NES)
▼
┌─────────────┐ MQTT ┌──────────────────┐
│ Node.js │ ◄────────────────►│ MQTT Broker │
│ Hapi Server│ └──────────────────┘
│ :7654 │ │
└─────────────┘ │ /christmas/...
▲ ┌──────┴───────┐
│ Built static files │ fppscheduler │
┌─────────────┐ │ (on FPP box)│
│ Vue 3 + │ └──────────────┘
│ Vite │
│ Frontend │
└─────────────┘
frontend/) — Vue 3 SPA built with Vite. Served as static files by a web server (e.g., nginx). Communicates with the backend via REST and a persistent WebSocket subscription.server/) — Hapi.js server. Manages vote state, pushes real-time updates to all connected clients via Hapi NES WebSockets, and relays votes/status to/from an MQTT broker.nvm use 24)/christmasserver/)Copy the example config and fill in your MQTT broker details:
cp server/greglights_config_example.json server/greglights_config.json
Edit server/greglights_config.json:
{
"username": "your-mqtt-username",
"password": "your-mqtt-password",
"port": 1883,
"host": "192.168.1.x",
"send_enabled": true
}
| Field | Description |
|---|---|
host | Hostname or IP of the MQTT broker |
port | MQTT broker port (1883 for plain, 8883 for TLS) |
username | MQTT username |
password | MQTT password |
send_enabled | Set false to disable publishing votes/queues to MQTT (dry-run mode) |
Note:
greglights_config.jsoncontains credentials — do not commit it. It is listed in.gitignore.
cd server
nvm use 24
npm install
node index.js
The server listens on port 7654 by default. Override with the port environment variable:
port=8000 node index.js
cd server
docker-compose build
docker-compose up -d
The docker-compose.yml binds the server to 127.0.0.1:7654 on the host. Front it with nginx or another reverse proxy for HTTPS.
| Method | Path | Description |
|---|---|---|
GET | /queue | Returns the full data model (songs, names, snowmen, votes remaining for session) |
GET | /model | Returns the full data model (no session info) |
GET/POST | /vote/{id} | Cast a vote for song with the given ID |
DELETE | /vote/{id} | Remove a previously cast vote for song {id} |
GET/POST | /votesnowman/{id} | Vote for a snowman display |
GET | /status | Returns server health status |
GET | /faq | Returns the shared display FAQ as rendered HTML |
GET | /ws | WebSocket endpoint (Hapi NES) — subscribe to /publicData for real-time model pushes |
GET /faqThis server owns the FAQ text for both vote-now.org and thehormanns.net, so the
two sites can never drift apart. The questions and answers live in
server/data/faqs.js; anything that can change — season dates, the song count,
the address, charity names — is written there as a {{token}} and filled in
from server/data/facts.js on each request. Answers come back as ready-to-show
HTML.
| Query | Description |
|---|---|
audience | web (thehormanns.net) or app (the vote-now info page). Omit for everything. |
site | Base URL for links into thehormanns.net. Defaults to https://thehormanns.net; thehormanns.net passes an empty string so its own links stay relative. |
curl 'http://localhost:7654/faq?audience=app'
thehormanns.net calls this at build time (which is what feeds its FAQPage
structured data) and again in the browser, so edits here reach that site without
a redeploy.
Sessions are tracked server-side via a cookie (Hapi Yar). Each session gets 8 votes, replenished by 1 every 2 minutes.
The server subscribes to these topics (all under the /christmas/ prefix):
| Topic | Purpose |
|---|---|
/christmas/nameQueue | Name queue status from fppscheduler |
/christmas/scheduler/status | Scheduler run state (on/off, short list mode, debug) |
/christmas/scheduler/name_estimate | Estimated time until next name is generated |
/christmas/scheduler/all_playlist | Full song list with durations |
/christmas/snowman/status | Available snowman displays and current selection |
/christmas/falcon/player/fpp2/playlist_details | Currently playing song from FPP |
/christmas/clock | Show clock tick |
/christmas/todayPower | Power consumption stats |
/christmas/vote/stats | Aggregated vote statistics |
/christmas/vote/setShortList | Toggle short-list mode |
/christmas/vote/debug | Toggle debug mode |
The server publishes to:
| Topic | Purpose |
|---|---|
/christmas/vote/add | Notify fppscheduler of a new song vote |
/christmas/snowmanvote/add | Notify fppscheduler of a snowman vote |
/christmas/vote/songQueue | Broadcast current song queue (every 2 s) |
/christmas/vote/snowmenQueue | Broadcast current snowman queue (every 2 s) |
/christmas/personsName | Submit a visitor's name to the name display queue |
/christmas/scheduler/requestSongs | Request full playlist from fppscheduler on startup |
/christmas is hard-coded in lib/mymqtt.js.https://vote-now.org) is hard-coded in routes/index.js but only used for a redirect on a 404.mqtt:// is active.frontend/)Vite uses .env files to inject the backend URL at build time. Two files are used:
.env.local — used automatically during npm run dev (never committed):
VITE_API_BASE_URL=http://localhost:7654
VITE_WS_URL=ws://localhost:7654/ws
.env.production — used automatically during npm run build:
VITE_API_BASE_URL=https://your-domain.com/api
VITE_WS_URL=wss://your-domain.com/ws
Update .env.production to match your production server's domain before building.
| Variable | Description |
|---|---|
VITE_API_BASE_URL | Base URL for REST API calls to the Hapi server |
VITE_WS_URL | WebSocket URL for real-time Hapi NES subscription |
cd frontend
nvm use 24
npm install
npm run dev
Starts Vite dev server at http://localhost:8080 (bound to all interfaces via host: true in vite.config.js). API requests proxy to the backend via the URL in .env.local.
npm run build
Outputs static files to frontend/dist/. Copy these to your web server's document root.
| Command | Description |
|---|---|
npm run preview | Preview the production build locally |
npm run lint | Lint and auto-fix with ESLint |
npm run format | Format source files with Prettier |
npm run test | Run unit tests with Vitest |
| Route | Component | Description |
|---|---|---|
/ | HomeView | Song voting — list of available songs with vote buttons |
/names | NameQueue | Submit your name to appear on the light show name display |
/info | InfoView | Show information and FAQ |
/stats | StatsView | Voting and power usage statistics |
/other | OtherDisplaysView | Other display elements (snowmen voting, etc.) |
| Package | Purpose |
|---|---|
| Vue 3 + Vite | UI framework and build tool |
| Pinia | State management |
| Vue Router 4 | Client-side routing |
@hapi/nes | WebSocket client for real-time server pushes |
| Axios | HTTP REST client |
| Bootstrap 5 | UI styling |
Run backend and frontend in separate terminals:
# Terminal 1 — backend
cd server && nvm use 24 && node index.js
# Terminal 2 — frontend
cd frontend && nvm use 24 && npm run dev
The frontend dev server at http://localhost:8080 connects to the backend at http://localhost:7654 as configured in frontend/.env.local.
cd frontend && npm run buildfrontend/dist/ files to your web server document root./api/ and /ws to http://127.0.0.1:7654.cd server && docker-compose up -d.