A web-based game platform built with React, TypeScript, and Go, featuring both client and admin interfaces.
The project consists of three main components:
client/- Frontend web application for usersadmin/- Administrative dashboardbackend/- Go-based backend server
- React 18 with TypeScript
- Vite for build tooling
- TailwindCSS for styling
- Shadcn UI components
- Redux Toolkit for state management
- React Router for navigation
- Framer Motion for animations
- Go 1.23.2
- Gin web framework
- PostgreSQL database
- Sessions-based authentication
See TESTING.md for full testing instructions. The E2E QA scope and smoke coverage live in e2e/README.md.
- Node.js (Latest LTS version)
- Go 1.23.2 or higher
- PostgreSQL 15+
- Create a PostgreSQL database:
createdb yearendwebgame- Run the migration:
psql -d yearendwebgame -f backend/migrations/001_initial_schema.sql- Copy environment template:
cp backend/.env.example backend/.env- Edit
backend/.envwith your local database credentials:
DB_USER=postgres
DB_NAME=yearendwebgame
DB_PASSWORD=your_password
SESSION_KEY=your-secret-key
The backend is configured to use Railway's DATABASE_URL automatically when deployed. Railway PostgreSQL addon provides:
- Automatic
DATABASE_URLinjection - Internal networking (zero latency)
- Automatic backups
- Install frontend dependencies:
# Client
cd client
npm install
# Admin
cd ../admin
npm install- Start the development servers:
# Backend
cd backend
go run .
# Client (in separate terminal)
cd client
npm run dev
# Admin (in separate terminal)
cd admin
npm run dev- Backend: 8080
- Client: 5173
- Admin: 5174
Admin endpoints are protected with API key authentication. This prevents unauthorized access to game controls.
- Generate an API key:
openssl rand -hex 16- Configure the backend:
Add to your
.envfile (or Railway environment variables):
ADMIN_API_KEY=your-generated-api-key-here
- Configure the admin dashboard:
Add to
admin/.env:
VITE_ADMIN_API_KEY=your-generated-api-key-here
| Method | Endpoint | Description |
|---|---|---|
| POST | /game/create |
Create a new game session |
| POST | /game/end |
End a game session |
| POST | /game/session-config |
Update session configuration |
| POST | /admin/reset-event |
Reset game event |
| GET | /admin/join |
WebSocket connection for admin |
REST API: Include the API key in the Authorization header:
curl -X POST http://localhost:8080/game/create \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{"createdBy": "admin-1"}'WebSocket: Include the API key as a query parameter:
ws://localhost:8080/admin/join?token=your-api-key
- Never commit API keys to source control
- Use different keys for development and production
- Rotate keys periodically
- The admin dashboard automatically includes the API key from environment variables
The platform includes a Kahoot-style PIN-based game session system for easy player joining without authentication.
- Admin Creates Game: Admin clicks "Tạo Mã PIN Mới" in the admin dashboard
- Players Join: Players visit
year-end-web-game.vercel.appand enter the 6-digit PIN - Player Flow: Players enter nickname, select team (or auto-assign), and confirm
- Real-time Updates: Admin sees connected players grouped by team in real-time
| Method | Endpoint | Description |
|---|---|---|
| POST | /game/create |
Create a new game session with PIN (requires auth) |
| POST | /game/validate-pin |
Validate if PIN exists and is active |
| POST | /game/join |
Join a game session with PIN and nickname |
| GET | /game/session?pin=XXX |
Get session info and player count |
| POST | /game/end |
End a game session (requires auth) |
curl -X POST http://localhost:8080/game/create \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{"createdBy": "admin-1"}'Response:
{
"pin": "847291",
"expiresAt": "2026-01-13T04:32:44+07:00",
"createdAt": "2026-01-13T00:32:44+07:00",
"message": "Game session created successfully"
}curl -X POST http://localhost:8080/game/join \
-H "Content-Type: application/json" \
-d '{
"pin": "847291",
"nickname": "Player1",
"teamID": 1
}'Response:
{
"playerId": "9365b751-e49a-488f-8ddf-b5a5e86a0f9a",
"teamID": 1,
"message": "Joined game successfully"
}The PIN validation endpoint is rate-limited to prevent brute-force attacks:
- Maximum 10 attempts per minute per IP
- Returns
429 Too Many Requestswhen exceeded
- Sessions expire after 4 hours of inactivity
- Expired sessions are automatically cleaned up every 5 minutes
- Maximum 200 players per session
This project uses Husky (v9.1.7) and lint-staged (v15.5.3) to enforce code quality before commits.
Pre-commit hooks are automatically installed when you run:
pnpm installThis runs the prepare script which configures Husky hooks in .husky/.
Frontend (Admin & Client) - Per-package lint-staged configs:
- ESLint with
--max-warnings=0- blocks commits on lint errors - Prettier - auto-formats staged files
Backend (Go) - Root lint-staged config:
- golangci-lint with
--fix- lints and auto-formats Go files
You can run pre-commit hooks manually:
# Check staged files only
pnpm exec lint-staged
# Debug mode (shows what would run)
pnpm exec lint-staged --debugTo bypass pre-commit hooks (not recommended):
HUSKY=0 git commit -m "message"- Root:
package.json- lint-staged backend config - Admin:
admin/.lintstagedrc.json- ESLint + Prettier rules - Client:
client/.lintstagedrc.json- ESLint + Prettier rules - Backend:
backend/.golangci.yml- golangci-lint configuration
See backend/.env.example for all required environment variables.
| Variable | Required | Description |
|---|---|---|
| DATABASE_URL | Production | Railway PostgreSQL connection string |
| DB_USER | Local | PostgreSQL username |
| DB_NAME | Local | PostgreSQL database name |
| DB_PASSWORD | Local | PostgreSQL password |
| SESSION_KEY | Yes | Secret for session cookies |
| PORT | No | Server port (default: 8080) |
| GIN_MODE | No | debug or release |
| SENTRY_DSN | No | Sentry error tracking |
Both admin/ and client/ apps need these environment variables configured in Vercel:
| Variable | Required | Default | Description |
|---|---|---|---|
| VITE_API_URL | No | Railway URL | Backend API URL (e.g., https://yearendwebgame-production.up.railway.app) |
| VITE_WS_URL | No | Railway WSS | Backend WebSocket URL (e.g., wss://yearendwebgame-production.up.railway.app) |
| VITE_SENTRY_DSN | No | - | Sentry DSN for frontend error tracking |
Note: If not set, the apps default to the Railway production URL. For local development, copy .env.example to .env and update the URLs to http://localhost:8080 and ws://localhost:8080.
- Client: https://year-end-web-game.vercel.app
- Admin: https://year-end-web-game-admin.vercel.app
- API: https://yearendwebgame-production.up.railway.app
This project uses GitHub Actions for continuous integration and deployment.
| Workflow | File | Trigger | Description |
|---|---|---|---|
| CI | .github/workflows/ci.yml |
PR to main/develop, push to main/develop |
Runs all tests (backend + frontend) |
| Lint | .github/workflows/lint.yml |
PR to main/develop, push to main/develop |
Code quality checks (Go + TypeScript) |
| E2E | .github/workflows/e2e.yml |
PR to main/develop, push to main/develop, manual dispatch |
End-to-end Playwright tests |
Backend Tests:
- Go 1.24.0 with race detector (
-raceflag) - PostgreSQL 15 service container
- Coverage reporting with
go test -cover
Frontend Tests:
- Admin: Vitest with 170+ tests
- Client: Vitest with 390+ tests
- pnpm for package management
Backend:
- golangci-lint v1.63 with custom config (
.golangci.yml) - Security-focused linters enabled
Frontend:
- ESLint with TypeScript support
- Prettier formatting checks
- Playwright tests with Chromium browser
- Backend service with PostgreSQL
- Rate limiting configured for test environment
- Artifacts uploaded on failure (screenshots, traces)
- Manual dispatch with test pattern selection
Branch protection is configured to require:
- ✅
ci-summary- All tests must pass - ✅
lint-summary- All lint checks must pass
You can trigger workflows manually from the GitHub Actions tab:
- Go to Actions → Select workflow
- Click Run workflow
- Choose branch and parameters (for E2E)
Before pushing, ensure all checks pass locally:
# Backend tests
cd backend && go test -race ./...
# Frontend tests
cd admin && pnpm test
cd client && pnpm test
# E2E tests
cd e2e && pnpm test
# Linting
cd backend && golangci-lint run
cd admin && pnpm lint
cd client && pnpm lint- Concurrency: Workflows cancel previous runs on the same branch to save resources
- Timeouts: 10 minutes for CI/Lint, 20 minutes for E2E
- Caching: pnpm store and Go modules are cached for faster builds
Flaky E2E Tests:
- Check rate limiting configuration
- Verify backend health endpoint responds
- Review Playwright traces in artifacts
Backend Race Conditions:
- CI runs with
-raceflag enabled - Check logs for data race warnings
Frontend Test Failures:
- Ensure all dependencies installed with
pnpm install --frozen-lockfile - Check for environment-specific test issues
- Backend: Auto-deploys to Railway on push to
main - Frontend: Auto-deploys to Vercel on push to
main - No manual deployment steps required
See TESTING.md for full testing instructions. The E2E QA scope and smoke coverage live in e2e/README.md.