Sistema completo tipo gubernamental con conteo en tiempo real por WebSocket.
- Cliente: HTML, CSS, JS puro
- Servidor: PHP 8+, Ratchet (WebSocket), Firebase PHP-JWT (JWT)
- BD: MySQL (PDO, patrón Singleton)
- Dependencias: Composer (
cboden/ratchet,firebase/php-jwt,guzzlehttp/guzzle)
gov-voting-php/
├─ app/
│ ├─ Config/
│ ├─ Controllers/
│ ├─ Helpers/
│ ├─ Middleware/
│ ├─ Router/
│ └─ Services/
├─ bin/
│ └─ ws-server.php
├─ public/
│ ├─ api/
│ │ └─ index.php
│ ├─ assets/
│ │ ├─ css/styles.css
│ │ └─ js/{app.js,admin.js,vote.js,results.js}
│ ├─ admin.html
│ ├─ vote.html
│ ├─ results.html
│ └─ index.html
├─ schema.sql
├─ seed/
│ ├─ seed.sql
│ └─ seed.php
├─ composer.json
└─ .env.example
- Clonar/copiar este proyecto.
- Crear archivo
.enva partir de.env.exampley ajustar credenciales. - Instalar dependencias:
composer install
- Crear BD e importar esquema:
mysql -u root -p < schema.sqlSi prefieres usar tu propio
database.sqlsubido previamente, también funciona. El campopartidoencandidateses opcional y el backend lo ignora. - (Opcional) Ejecutar
seed/seed.phppara crear el usuario admin con bcrypt si tu BD no lo tiene aún:php seed/seed.php
- Iniciar servidor WebSocket:
php bin/ws-server.php
- En otra terminal, iniciar servidor PHP (document root
public/):php -S localhost:8000 -t public
- Abrir:
- Admin: http://localhost:8000/admin.html
- Votación: http://localhost:8000/vote.html
- Resultados: http://localhost:8000/results.html
- email: [email protected]
- password: admin123
Si importaste
seed/seed.sqlo corristeseed/seed.php, tendrás este usuario con contraseñabcrypt.
Si estás usando otro dump donde la contraseña está con SHA-256, el backend también la soporta de forma retrocompatible.
POST /api/auth/loginGET|POST|PUT|DELETE /api/eventsGET|POST|PUT|DELETE /api/candidatesGET|POST|PUT|DELETE /api/votersGET|POST|PUT|DELETE /api/sitesPOST /api/votes(idempotente; registra IP, geolocalización, UA; dispara broadcast)GET /api/results/{event_id}(totales)
- Servidor en
ws://localhost:8080 - Canal por evento:
event.{event_id}(suscripción implícita por query?event_id=ID) - Mensaje:
{ "event_id": 1, "totals": [ { "candidate_id": 1, "votes": 10 }, ... ] }
- Login en Admin, crear evento/candidatos/votantes/sitios o usar
seed.sql. - Abrir
results.html(elige evento) y déjalo abierto. - En
vote.html, ingresa documento, sitio y vota. Verás actualización en vivo.
- JWT para usuarios internos.
- 1 persona = 1 voto por evento (índice único
(event_id, voter_id)). - Validación de votante activo y asignado al sitio.
- Auditoría completa en
audit_logs. - IP pública o local + ubicación geográfica (IP geolocation) registradas automáticamente.
- Ajusta
.envcon URL/puertos reales. - Detrás de proxy, asegúrate de pasar
X-Forwarded-ForyX-Real-IPhacia PHP. - Considera HTTPS (wss://) para WebSocket en producción.