{"id":51130682,"url":"https://github.com/suvanwita/eventpulse","last_synced_at":"2026-06-25T12:01:07.427Z","repository":{"id":365068879,"uuid":"1266143496","full_name":"Suvanwita/EventPulse","owner":"Suvanwita","description":"Campus event operations platform for capacity-safe registrations, QR-based entry, automated waitlists, venue scheduling, duplicate check-in prevention, and real-time crowd-flow tracking.","archived":false,"fork":false,"pushed_at":"2026-06-22T19:00:25.000Z","size":436,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-22T20:12:33.622Z","etag":null,"topics":["campus-management","concurrency","interval-scheduling","kafka","nextjs","nodejs","postgresql","rate-limiting","real-time","redis","socket-io","synchronization","web-sockets"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Suvanwita.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-11T11:04:05.000Z","updated_at":"2026-06-22T19:00:36.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/Suvanwita/EventPulse","commit_stats":null,"previous_names":["suvanwita/eventpulse"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Suvanwita/EventPulse","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Suvanwita%2FEventPulse","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Suvanwita%2FEventPulse/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Suvanwita%2FEventPulse/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Suvanwita%2FEventPulse/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Suvanwita","download_url":"https://codeload.github.com/Suvanwita/EventPulse/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Suvanwita%2FEventPulse/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34773843,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-25T02:00:05.521Z","response_time":101,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["campus-management","concurrency","interval-scheduling","kafka","nextjs","nodejs","postgresql","rate-limiting","real-time","redis","socket-io","synchronization","web-sockets"],"created_at":"2026-06-25T12:01:06.211Z","updated_at":"2026-06-25T12:01:07.421Z","avatar_url":"https://github.com/Suvanwita.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# EventPulse\n\nEventPulse is a full-stack campus event platform for capacity-aware registrations, waitlists, QR passes, live entry scanning, analytics, and real-time organizer/volunteer dashboards.\n\n## Structure\n\n- `frontend/` - Next.js App Router app.\n- `backend/` - Node.js, Express, Prisma, PostgreSQL, Redis, Kafka, Socket.IO API.\n- `docker-compose.yml` - Local PostgreSQL, Redis, Kafka/Zookeeper, frontend, and backend services.\n\n## Backend Setup\n\n```bash\ncd backend\nnpm install\ncp .env.example .env\nnpm run prisma:generate\nnpm run prisma:migrate -- --name init_eventpulse_schema\nnpm run seed\nnpm run dev\n```\n\nThe backend runs on `http://localhost:4000` by default.\n\n## Docker\n\n```bash\ndocker compose up -d postgres redis kafka zookeeper\ndocker compose up frontend backend\n```\n\nPostgreSQL is mapped to host port `5433` to avoid common local conflicts. Inside Docker, services use `postgres:5432`.\n\n## Prisma\n\n```bash\ncd backend\nnpm run prisma:generate\nnpm run prisma:migrate\nnpm run prisma:studio\nnpm run seed\n```\n\n## Backend Tests\n\nRun the RBAC/ABAC authorization suite with:\n\n```bash\ncd backend\nnpm run test:authz\n```\n\nThe suite uses Jest and Supertest against the real Express app, JWT auth middleware, CASL policies, route middleware, and Prisma-backed service ownership checks. It seeds isolated admin, organizer, volunteer, and student users plus organizer-owned events, then verifies student restrictions, organizer ownership boundaries, volunteer scan scope, and admin access. Run it against a migrated test database; the test data uses unique `authz-*` emails and cleans itself up after completion.\n\nTo avoid accidental writes to shared databases, the suite runs only when `DATABASE_URL` points to localhost, the database name clearly contains `eventpulse_test`, or `AUTHZ_TEST_DATABASE_URL` is provided:\n\n```bash\nAUTHZ_TEST_DATABASE_URL=postgresql://eventpulse:eventpulse@localhost:5433/eventpulse_test npm run test:authz\n```\n\nRemote database runs require explicit opt-in with `AUTHZ_ALLOW_REMOTE_DATABASE=true`.\n\n## API Routes\n\nAuth:\n- `POST /api/auth/register`\n- `POST /api/auth/login`\n- `GET /api/auth/me`\n\nVenues:\n- `POST /api/venues`\n- `GET /api/venues`\n- `GET /api/venues/:id`\n- `PATCH /api/venues/:id`\n- `DELETE /api/venues/:id`\n- `GET /api/venues/:id/schedule`\n\nEvents:\n- `POST /api/events`\n- `GET /api/events`\n- `GET /api/events/:id`\n- `PATCH /api/events/:id`\n- `DELETE /api/events/:id`\n\nRegistrations and waitlist:\n- `POST /api/events/:id/register`\n- `POST /api/events/:id/cancel`\n- `GET /api/events/:id/registration-status`\n- `GET /api/events/:id/waitlist`\n- `POST /api/events/:id/promote-next`\n\nPasses and check-in:\n- `GET /api/events/:id/pass`\n- `POST /api/checkin/scan`\n- `GET /api/events/:id/checkins`\n\nAnalytics:\n- `GET /api/analytics/events/:id`\n- `GET /api/analytics/venues`\n- `GET /api/analytics/checkins`\n\nNotifications:\n- `GET /api/notifications`\n- `GET /api/notifications/unread-count`\n- `PATCH /api/notifications/:id/read`\n- `PATCH /api/notifications/read-all`\n\nHealth:\n- `GET /health`\n\n## Auth Flow\n\nUsers register or log in with email/password. Passwords are hashed with `bcryptjs`. Login returns a JWT with:\n\n```json\n{\n  \"userId\": \"...\",\n  \"role\": \"STUDENT\"\n}\n```\n\nProtected routes use `Authorization: Bearer \u003ctoken\u003e`. Public registration can create `STUDENT`, `ORGANIZER`, and `VOLUNTEER`; `ADMIN` cannot be created through public registration.\n\n## Request Validation\n\nBackend route inputs are validated at the API boundary with Zod. Shared schemas live in `backend/src/validation/requestSchemas.js` and are applied through `backend/src/middleware/validateRequest.middleware.js` for request bodies, route params, and query strings. Invalid requests return `400` with structured field-level details before reaching service logic.\n\n## Security Headers\n\nThe backend uses Helmet to apply security response headers before CORS and API routes. The policy includes a conservative Content Security Policy, `X-Frame-Options`/`frame-ancestors` deny framing, `X-Content-Type-Options: nosniff`, `Referrer-Policy: no-referrer`, and cross-origin isolation/resource policies suitable for an API backend.\n\n`Strict-Transport-Security` is enabled only when `NODE_ENV=production` so local HTTP development keeps working. `FRONTEND_URL` is used in the CSP `connect-src` allowlist alongside the backend origin.\n\n## Auth Rate Limiting\n\nEventPulse uses Redis-backed fixed-window rate limits for sensitive routes. Login and registration are limited by IP and email to slow brute-force attempts. Authenticated limits protect QR scans, special entry, pass generation, notification mutations, and admin/organizer write routes. Responses include `RateLimit-Limit`, `RateLimit-Remaining`, and `RateLimit-Reset` headers.\n\nDefault limits can be tuned with environment variables:\n\n```bash\nRATE_LIMIT_AUTH_LOGIN_IP_LIMIT=20\nRATE_LIMIT_AUTH_LOGIN_IP_WINDOW_SECONDS=900\nRATE_LIMIT_AUTH_LOGIN_EMAIL_LIMIT=8\nRATE_LIMIT_AUTH_LOGIN_EMAIL_IP_LIMIT=5\nRATE_LIMIT_AUTH_REGISTER_IP_LIMIT=10\nRATE_LIMIT_AUTH_REGISTER_IP_WINDOW_SECONDS=3600\nRATE_LIMIT_QR_SCAN_USER_LIMIT=60\nRATE_LIMIT_QR_SCAN_USER_WINDOW_SECONDS=60\nRATE_LIMIT_PASS_GENERATION_USER_LIMIT=30\nRATE_LIMIT_NOTIFICATION_MUTATION_USER_LIMIT=60\nRATE_LIMIT_ADMIN_WRITE_USER_LIMIT=60\n```\n\n## Authorization Policies\n\nBackend authorization is centralized with CASL in `backend/src/authorization/ability.js`. Route middleware uses coarse abilities such as `create Event`, `read GateFlow`, and `manage Venue`, while service methods use object-aware policies for ownership-sensitive records such as events, analytics, waitlists, crew access, check-ins, passes, registrations, and notifications.\n\nAdmins can manage all resources. Organizers can create events and manage owned event resources. Volunteers can scan and read operational entry data. Students can register, cancel, read their own passes, and read their own notifications. PostgreSQL remains the source of truth for ownership checks before object-level CASL authorization is evaluated.\n\n## Idempotency Keys\n\nHigh-risk write endpoints require an `Idempotency-Key` header so client retries do not repeat side effects. EventPulse stores the key, authenticated user, route, method, request fingerprint, response status/body, and expiry in PostgreSQL. Matching retries replay the original successful response with `Idempotency-Replayed: true`; reusing the same key with a different request returns `409`.\n\nProtected endpoints using idempotency:\n- `POST /api/events/:id/register`\n- `POST /api/events/:id/cancel`\n- `POST /api/events/:id/promote-next`\n- `POST /api/events/:id/checkins/scan`\n- `POST /api/events/:id/checkins/special-entry`\n- `POST /api/events/:id/crew`\n- `PATCH /api/events/:id/crew/:crewAccessId`\n- `DELETE /api/events/:id/crew/:crewAccessId`\n- `PATCH /api/notifications/:id/read`\n- `PATCH /api/notifications/read-all`\n\nExample:\n\n```bash\ncurl -X POST http://localhost:4000/api/events/event-id/register \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Idempotency-Key: register-event-id-user-id-1\"\n```\n\nKeys expire after `IDEMPOTENCY_KEY_TTL_SECONDS` seconds, defaulting to 24 hours. Clean expired rows with:\n\n```bash\ncd backend\nnpm run cleanup:idempotency\n```\n\n## Registration Flow\n\nStudents register for `OPEN` or `LIVE` events before `registrationDeadline`. Registration uses Redis lock `lock:event:{eventId}:registration` and a Prisma transaction to prevent overbooking. If capacity is available, the system allocates the first available seat and creates a `CONFIRMED` registration.\n\n## Waitlist Flow\n\nIf an event is full and waitlist capacity remains, the student receives a `WAITLISTED` registration and a `WaitlistEntry` with the next queue position. Cancelling a confirmed registration releases the seat and promotes the first `WAITING` waitlist entry by `position ASC`.\n\n## QR Check-In Flow\n\nConfirmed students fetch `GET /api/events/:id/pass`. The backend signs a QR payload using HMAC with `JWT_SECRET`, stores only the token hash, and returns a QR image data URL. Volunteers, organizers, and admins scan via `POST /api/checkin/scan`. The scan path uses rate limits, QR token verification, Redis scan locks, and the unique `CheckIn.registrationId` constraint to reject duplicate entry.\n\n## WebSocket Events\n\nClients join event rooms with:\n- `join-event-room`\n- `leave-event-room`\n\nRoom format:\n- `event:{eventId}`\n\nServer emitted events:\n- `capacity-updated`\n- `registration-updated`\n- `waitlist-updated`\n- `checkin-updated`\n- `entry-rate-updated`\n- `no-show-released`\n- `notification-created`\n- `notification-read`\n\n## Redis Usage\n\nRedis is used for:\n- registration locks\n- QR scan locks\n- fixed-window rate limits\n- fast live counters:\n  - `event:{eventId}:registeredCount`\n  - `event:{eventId}:checkedInCount`\n  - `event:{eventId}:waitlistCount`\n\nPostgreSQL remains the source of truth. Redis counters can be rebuilt with database sync helpers.\n\n## In-App Notifications\n\nEventPulse stores per-user notifications for registration confirmations, waitlist joins and promotions, cancellations, check-ins, and crew access changes. Authenticated clients can fetch `/api/notifications`, read the navbar badge count through `/api/notifications/unread-count`, and mark one or all notifications as read. Socket.IO pushes `notification-created` and `notification-read` events to each authenticated user room so the badge and `/notifications` center update live.\n\n## Kafka Usage\n\nKafka is used for async streams, not source-of-truth storage. Publish failures are logged and do not crash API requests.\n\nDomain services write Kafka messages through a transactional outbox table before Kafka publish. State changes such as registrations, cancellations, waitlist promotions, check-ins, no-show releases, and crew access updates create `OutboxEvent` rows in the same Prisma transaction as the PostgreSQL mutation. A separate publisher worker reads pending rows, publishes them to Kafka, and marks them as published only after Kafka acknowledges the send.\n\nRun the outbox publisher locally with:\n\n```bash\ncd backend\nnpm run publisher:outbox\n```\n\nOutbox retries use exponential backoff. Configure them with:\n\n```bash\nOUTBOX_MAX_ATTEMPTS=10\nOUTBOX_PUBLISH_BATCH_SIZE=25\nOUTBOX_PUBLISH_POLL_INTERVAL_MS=2500\nOUTBOX_PUBLISH_BASE_BACKOFF_MS=1000\nOUTBOX_PUBLISH_MAX_BACKOFF_MS=60000\nOUTBOX_PROCESSING_TIMEOUT_MS=300000\n```\n\nPublished Kafka messages preserve the existing topic names and JSON payload shape. The publisher adds Kafka headers `eventpulse-outbox-id` and `eventpulse-outbox-attempt` so consumers can de-duplicate if needed. Delivery is at least once: if the process crashes after Kafka accepts a message but before PostgreSQL marks the row as published, the publisher may retry that same outbox row.\n\nTopics:\n- `eventpulse.registration.created`\n- `eventpulse.waitlist.joined`\n- `eventpulse.waitlist.promoted`\n- `eventpulse.registration.cancelled`\n- `eventpulse.checkin.completed`\n- `eventpulse.security.scan_failed`\n- `eventpulse.no_show.released`\n- `eventpulse.crew.access_granted`\n- `eventpulse.crew.access_updated`\n- `eventpulse.crew.access_revoked`\n- `eventpulse.crew.special_entry_used`\n\nKafka consumers run in dedicated consumer groups:\n- `eventpulse-registration-consumer`\n- `eventpulse-checkin-consumer`\n- `eventpulse-crew-consumer`\n\nRun them locally with:\n\n```bash\ncd backend\nnpm run consumer:kafka\n```\n\nConsumer processing keeps PostgreSQL as the source of truth. Handlers verify referenced records, resync Redis counters for capacity-affecting topics, and write event-scoped audit logs.\n\nRetry topics:\n- `eventpulse.retry.registration`\n- `eventpulse.retry.checkin`\n- `eventpulse.retry.crew`\n\nDead-letter topics:\n- `eventpulse.dlq.registration`\n- `eventpulse.dlq.checkin`\n- `eventpulse.dlq.crew`\n\nFailed messages are wrapped with the original topic, original payload, original timestamp, attempt count, retry timestamp, consumer group, and serialized error. After `KAFKA_CONSUMER_MAX_ATTEMPTS`, the message is moved to the matching DLQ topic.\n\nKafka message contracts are centralized in `backend/src/utils/kafkaSchemas.js` and enforced with AJV before publishing and before consumer handlers run. Domain messages keep the existing JSON shape:\n\n```json\n{\n  \"eventId\": \"event-id\",\n  \"userId\": \"user-id-or-null\",\n  \"registrationId\": \"registration-id-or-null\",\n  \"timestamp\": \"2026-06-22T12:00:00.000Z\",\n  \"metadata\": {}\n}\n```\n\n`eventpulse.security.scan_failed` allows `eventId` to be omitted because rejected scans may contain unusable or missing event data. Other domain topics require `eventId`, `timestamp`, and `metadata`. Validation failures are not published on the producer path; consumer-side validation failures are routed through the retry/DLQ flow with structured error details.\n\n## Concurrency And Synchronization\n\nRegistration, cancellation, waitlist promotion, no-show release, and QR scans are protected with Redis locks and Prisma transactions. Database unique constraints provide a final safety net for one registration per user/event, one waitlist entry per user/event, and one check-in per registration.\n\nFenwick Tree is used for efficient time-range check-in analytics over bucketed event entry data. The optimized time-range path powers `GET /api/analytics/events/:id/time-range` without replacing PostgreSQL as the source of truth.\n\nTrie is used for fast autocomplete across events, venues, zones, and categories.\n\nGraph utilities are used to model venue zones and gate loads, enabling least-crowded gate recommendations and simple crowd-flow routing.\n\n## Event Crew Access\n\nOrganizers can assign selected students as event-specific organizers, crew members, performers, speakers, volunteer helpers, or VIP entries. Access is scoped to a single event and does not change the student’s global `STUDENT` role. Each crew access record can specify a special gate and an optional note that volunteers can see during scanning. Crew access updates emit live Socket.IO events to the event room and publish best-effort Kafka stream events.\n\n## Workers\n\nRun manual workers or queue-backed workers:\n\n```bash\ncd backend\nnpm run worker:noshow\nnpm run worker:analytics\nnpm run consumer:kafka\nnpm run scheduler:bullmq\nnpm run worker:bullmq\n```\n\n`worker:noshow` marks unscanned confirmed registrations as `NO_SHOW` after a grace period, releases seats, promotes waitlisted users, updates counters, and publishes Kafka events.\n\n`worker:analytics` computes aggregate event, venue, and check-in metrics and logs them.\n\n## BullMQ Usage\n\nBullMQ is used for scheduled and retryable background jobs on top of the existing Redis service. Kafka remains the async domain-event stream; BullMQ handles jobs that need retries, delays, or repeat schedules.\n\nQueues:\n- `eventpulse-event-lifecycle`\n- `eventpulse-notifications`\n- `eventpulse-analytics`\n\nJob types:\n- `event-no-show-release` runs after event start plus `NO_SHOW_GRACE_MINUTES`.\n- `process-no-shows` repeats every `NO_SHOW_REPEAT_MS` as a safety sweep.\n- `registration-deadline-reminder` creates organizer in-app reminder jobs before registration closes.\n- `event-start-reminder` creates attendee in-app reminder jobs before event start.\n- `create-notification` writes in-app notifications through the notification service.\n- `analytics-aggregate` repeats every `ANALYTICS_REPEAT_MS`.\n\nRun `npm run scheduler:bullmq` once during deployment or local boot to register repeatable jobs and delayed jobs for upcoming events. Run `npm run worker:bullmq` as a long-running worker process. Jobs use exponential backoff, bounded completed/failed retention, and stable job IDs for event reminders so event updates replace pending schedules.\n\n## Observability\n\nThe backend uses Pino for structured JSON logs and OpenTelemetry for traces. Local development works without an OpenTelemetry collector because tracing is disabled by default.\n\nEnvironment variables:\n\n```bash\nLOG_LEVEL=info\nOTEL_ENABLED=false\nOTEL_SERVICE_NAME=eventpulse-backend\nOTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318\nOTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4318/v1/traces\nOTEL_DIAG_LOGGING=false\n```\n\nWhen `OTEL_ENABLED=true`, EventPulse auto-instruments Node HTTP/Express, PostgreSQL, Redis/ioredis, KafkaJS, Socket.IO, and Pino where supported by the OpenTelemetry auto-instrumentation package. The backend also creates manual spans for Kafka publish/consume operations, BullMQ job processing, and Socket.IO emits. Logs include request IDs and active trace/span IDs when a trace context exists.\n\nFor local traces, start an OTLP-compatible collector and set `OTEL_ENABLED=true` plus `OTEL_EXPORTER_OTLP_ENDPOINT`. Without an endpoint, tracing can still be enabled for local context propagation, but spans are not exported.\n\n## Prometheus Metrics\n\nThe backend exposes Prometheus metrics at `GET /metrics` using `prom-client`.\n\nMetrics include default Node.js process metrics plus:\n- HTTP request count and duration by method, normalized route pattern, and status.\n- Domain counters for registrations, waitlist joins/promotions, cancellations, check-ins, scan failures, crew access changes, notifications, and no-show releases.\n- Kafka publish success/failure counters by topic.\n- Outbox enqueue/publish/retry/failure counters, current outbox status gauges, and oldest pending outbox age.\n- BullMQ job completed/failed counters by queue and job name.\n- Optional BullMQ queue state gauges when `PROMETHEUS_BULLMQ_QUEUE_GAUGES=true`.\n- Idempotency started/completed/replayed/conflict/in-flight/failure counters.\n\nMetric labels are intentionally low-cardinality and do not include user IDs, event IDs, request IDs, or raw request paths.\n\nLocal Prometheus scrape example:\n\n```yaml\nscrape_configs:\n  - job_name: eventpulse-backend\n    metrics_path: /metrics\n    static_configs:\n      - targets: [\"localhost:4000\"]\n```\n\n## Demo Credentials\n\nAll seeded users use password `password123`.\n\n- `admin@iiita.ac.in` - `ADMIN`\n- `organizer@iiita.ac.in` - `ORGANIZER`\n- `volunteer@iiita.ac.in` - `VOLUNTEER`\n- `student1@iiita.ac.in` - `STUDENT`\n- `student2@iiita.ac.in` - `STUDENT`\n- `student3@iiita.ac.in` - `STUDENT`\n- `student4@iiita.ac.in` - `STUDENT`\n- `student5@iiita.ac.in` - `STUDENT`\n\n## Frontend Setup\n\n```bash\ncd frontend\nnpm install\ncp .env.example .env.local\nnpm run dev\n```\n\nFrontend environment variables:\n\n```bash\nNEXT_PUBLIC_API_URL=http://localhost:5000\nNEXT_PUBLIC_SOCKET_URL=http://localhost:5000\n```\n\nThe frontend keeps the futuristic EventPulse spatial operations UI while integrating with the backend APIs. Login and registration store the JWT and user profile in local storage, attach `Authorization: Bearer \u003ctoken\u003e` to API requests, and update navigation by role.\n\nIntegrated pages:\n- `/login`\n- `/register`\n- `/events`\n- `/events/[id]`\n- `/notifications`\n- `/pass/[eventId]`\n- `/organizer/dashboard`\n- `/organizer/events/new`\n- `/organizer/events/[id]`\n- `/volunteer/scan`\n- `/admin/venues`\n- `/admin/analytics`\n\nRole routing:\n- `STUDENT` -\u003e `/events`\n- `ORGANIZER` -\u003e `/organizer/dashboard`\n- `VOLUNTEER` -\u003e `/volunteer/scan`\n- `ADMIN` -\u003e `/admin/venues`\n\nUnauthorized users are sent to `/login`. Authenticated users with the wrong role see an access-denied operations panel.\n\n## Frontend Demo Flow\n\n1. Login as `organizer@iiita.ac.in`.\n2. Create an event from `/organizer/events/new`.\n3. Login as `student1@iiita.ac.in`.\n4. Register from `/events/[id]`.\n5. Open `/pass/[eventId]` and copy the demo QR token.\n6. Login as `volunteer@iiita.ac.in`.\n7. Scan the copied token from `/volunteer/scan`.\n8. Scan it again to verify duplicate-entry rejection.\n9. Login as `admin@iiita.ac.in`.\n10. View `/admin/venues` and `/admin/analytics`.\n11. Open `/organizer/events/[id]` to watch live capacity and check-in updates.\n\nWebSocket behavior:\n- Organizer event control rooms and the volunteer scanner use `socket.io-client`.\n- Clients join rooms with `join-event-room` and leave with `leave-event-room`.\n- Live events update capacity, registration, waitlist, check-in, entry-rate, and no-show signals.\n\nCommon frontend fixes:\n- If API calls fail, confirm `NEXT_PUBLIC_API_URL` points to the backend.\n- If sockets stay offline, confirm `NEXT_PUBLIC_SOCKET_URL` points to the backend Socket.IO server.\n- If protected pages redirect, login again and confirm the demo user has the expected role.\n- If QR scan fails, generate a fresh pass because QR tokens are signed and expire at event end time.\n\n## Manual Testing Checklist\n\n1. Register/login.\n2. Create IIITA venue.\n3. Create IIITA event.\n4. Detect venue conflict.\n5. Register student.\n6. Fill event capacity.\n7. Move users to waitlist.\n8. Cancel confirmed registration.\n9. Auto-promote waitlisted user.\n10. Generate QR pass.\n11. Scan QR.\n12. Reject duplicate QR scan.\n13. Check live counters.\n14. Verify Kafka logs.\n15. Run no-show worker.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsuvanwita%2Feventpulse","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsuvanwita%2Feventpulse","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsuvanwita%2Feventpulse/lists"}