PulseBoard Hackathon · Rankings

Live polls for feedback. Submissions evaluated head-to-head by AI judges, then a single-elimination bracket decides the final standings.

165
Evaluated
11
In Bracket
10/10
Matches Played
2
Eliminated

BracketSingle-elimination · 11 entrants · re-judged head-to-head by AI

GF
Champion
crowned at end of GF
pulse-board
Sayantan
GF
Grand Final
1 of 1 played
GF-m1FINAL
pulse-board
Sayantan
VS
ChaiPoll
Ishaan Satapathy
ADVANCES →
SF
Semifinals
2 of 2 played
SF-m1FINAL
Quibli
Affan Khan
VS
pulse-board
Sayantan
← ADVANCES
SF-m2FINAL
ChaiPoll
Ishaan Satapathy
VS
PollNow
Paramveer
ADVANCES →
QF
Quarterfinals
4 of 4 played
QF-m1FINAL
Quibli
Affan Khan
VS
POLL-STAR
HiteshDhayal
H
ADVANCES →
QF-m2FINAL
ChaiPoll
Ishaan Satapathy
VS
Pollinate
Abhinav Bist
ADVANCES →
QF-m3FINAL
Pulse Board
Shivam Goyal
VS
PollNow
Paramveer
← ADVANCES
QF-m4FINAL
pulse-board
Sayantan
VS
Pulse
Purakhnath Jyani
ADVANCES →
16
Round of 16
3 of 3 played
R16-m1FINAL
PollNow
Paramveer
VS
PulseBoard
Pallab Karmakar
ADVANCES →
R16-m2FINAL
Pollinate
Abhinav Bist
VS
PollForge
Ashaaf Khan
ADVANCES →
R16-m3FINAL
S
Versus
Saumya Agrawal
VS
POLL-STAR
HiteshDhayal
H
← ADVANCES

Leaderboardall evaluated submissions · click any row for AI judge feedback

RANK
PROJECT & SUBMITTER
SCORE
1
pulse-board 🏆 Champion
Sayantan · @sayantanbal
61
pulse-board takes the Grand Final 61/80. Its decisive strengths are the production-grade response submission flow (two-tier anonymous dedup via anon_session cookie + IP/UA hash fallback + AnonResponseClaim distributed mutex with exponential backoff), deep analytics with leaderboard and view tracking, and genuinely functional real-time integration where delta/snapshot events are consumed by every relevant page (CreatorLivePage, AnalyticsPage, PublicResultsPage) without HTTP fallback. The auth implementation with MongoDB transaction-based token rotation is solid; the main gaps are no Helmet/CSRF middleware and no email-verification or password-reset flows. UI is functional but the monolithic 900+ line page components are a maintainability concern. Against ChaiPoll, pulse-board's functional correctness on the core polling features (working real-time data path, editable polls, production-grade dedup) outweighs ChaiPoll's superior visual polish and broader auth feature set.
Authentication & Access Control7 / 10
Dual-token JWT with 15-min access + 7-day refresh, bcrypt 12 rounds, MongoDB transaction-based cryptographic token rotation (auth.service.ts:142-209), httpOnly/secure/sameSite cookies, rate limiting on auth endpoints. AuthProvider with Axios interceptor for concurrent-request deduplication on refresh. Anonymous vs authenticated poll modes enforced server-side (publicPoll.service.ts:183-210). Anon session cookie (30-day) for respondent identification. CONFIRMED GAPS: No Helmet middleware (app.ts:15-22 — only cors, express.json, cookieParser), no CSRF protection, no email verification or password reset, `req.user!` non-null assertions in 8+ route handlers, `as Date` type assertion in requireAuth.ts:42. Score 7 — solid core auth with transaction-based rotation, but missing Helmet, CSRF, and account recovery flows are real security concerns.
Poll Creation & Question Management8 / 15
React Hook Form + Zod validation (pollFormSchema from shared package). Dynamic question/option add/remove via useFieldArray, mandatory/optional toggle per question, anonymous/authenticated response mode, datetime-local expiry picker, timer modes (none/attached/detached, 0-3600s), allowCreatorResponses/allowResponseChanges toggles, status selection (draft/active). Server-side Zod validation enforces min 1 question, min 2 options/question. Edit mode with dirty-field detection — buildDirtyUpdatePayload() sends only changed fields in PATCH. Questions can be edited before first response (poll.service.ts:74-81). Timer-attached mode recomputes expiry server-side. CONFIRMED: Single question type (radio only — no checkbox/multi-select, no text), 919-line monolithic PollBuilderPage component, no reorder capability for questions. Score 8 — solid builder with timer modes and dirty-field PATCH optimization, but lack of multiple choice/question types limits real-world usability.
Response Collection Flow9 / 15
Exceptional defense-in-depth response submission. Server validates poll state (draft→404, published→409, expired→410), answer integrity (unknown question/option, duplicate questions, required completeness) at publicPoll.service.ts:127-181. Anonymous dedup is the standout feature: primary strategy uses httpOnly anon_session cookie (30-day UUID), fallback uses IP+UA hash (publicPoll.service.ts:294-297). AnonResponseClaim model with unique index acts as a distributed mutex — retry loop handles up to 8 concurrent anonymous submissions with 25ms exponential backoff without race conditions (lines 308-456). Authenticated dedup via unique partial compound index on (pollId, respondentId). Transaction-based writes for aggregate updates and response creation. Response-change support with old aggregate decrement. Client-side partial save via navigator.sendBeacon on beforeunload/pagehide AND blur. Timer auto-submit in detached mode. Rate-limited at 30 requests per 10 minutes. CONFIRMED: no individual answer-level validation error messages returned to client (returns generic "validation failed"). Score 9 — clearly the best response submission flow with production-grade anonymous dedup being the key differentiator.
Analytics & Feedback Dashboard8 / 15
Deep analytics: total/complete/partial response counts, completion/drop-off rate calculations, per-question option counts with percentages, per-question drop-off tracking from partial responses (analytics.service.ts:202-284). Builds response time series with day/hour granularity and IANA timezone support via MongoDB $dateTrunc + luxon (buildResponseTimeSeries, lines 114-192). Leaderboard with composite accuracy+speed scoring — weighted composite (65% accuracy + 35% speed) with fallback to speed-only (analytics.service.ts:399-499). View tracking: total views, unique visitors, device breakdown (ua-parser-js), bot detection (13 crawlers + 9 suspicious patterns), top-10 countries via MaxMind GeoLite2. IP masking for privacy. CreatorLivePage full-screen dashboard with SVG timer ring and animated bar charts. CONFIRMED GAPS: No CSV/JSON export functionality, no individual response viewing capability. Non-atomic recomputeAggregates uses deleteMany+insertMany (analytics.service.ts:306-319 — could lose data between operations). Score 8 — deepest analytics with leaderboard, geolocation, and bot detection, offset by no export and non-atomic recompute.
Frontend Experience7 / 10
12 routes with React Router, dark/light theme with localStorage + OS preference, Sonner toasts, ErrorBoundary, skeleton loading states, Lucide icons, copy-to-clipboard with execCommand fallback, native share sheet, Framer Motion bar-chart animations, canvas-confetti on submission, 4 distinct empty-state variants in PublicResultsPage, TanStack Query for caching. The monolithic 900+ line page components (PollBuilderPage, PublicResultsPage) and CSS-in-JS approach are internal-code-organization issues that affect maintainability but not the user-facing experience — the deployed app (https://pulseboard.sayantanbal.in/) is functional and responsive. Score adjusted up accordingly.
Backend Architecture & API Design7 / 15
Clean layered architecture: routes→services→repositories→domain with shared Zod schemas package. Centralized error handler with HttpError class and error codes. MongoDB transactions for token refresh rotation (auth.service.ts:142-209) and response submission. Good indexing: TTL indexes on AnonResponseClaim (1h) and ViewHistory (90d), unique partial compound on Response, compound indices for queries. Zod-validated env vars at startup (env.ts). CORS with explicit origin + credentials, cookie security, trust proxy for Cloud Run, 1MB body limit, rate limiting. RESTful semantics. Health/ready endpoints. Internal cron for expiry sweep. CONFIRMED GAPS: No Helmet middleware (app.ts — only cors, express.json, cookieParser), no input sanitization on user-generated content (XSS risk). Duplicated code: toPollWire in 2 files (poll.service.ts:17-48, publicPoll.service.ts:61-91), applyLazyStatusUpdate in 2 files, getPollIdParam in 4 files. No pagination on poll listing. Non-atomic recomputeAggregates deleteMany+insertMany pattern. Score 7 — solid MongoDB-native architecture but no Helmet and code duplication are concerns.
Real-Time Updates Using WebSockets8 / 10
Socket.IO server on /analytics namespace with proper room-based pub/sub. Two event types — delta (incremental count update) and snapshot (full replacement) — emitted on response submission, response change, and after error recovery. Client singleton consumed by CreatorLivePage, AnalyticsPage, AND PublicResultsPage — all correctly apply delta/snapshot to local state without HTTP round-trip. PublicResultsPage connects socket even for anonymous visitors. Reconnection room re-join handled via connect event. Genuinely functional real-time data path on a clean 107-line implementation — score adjusted up accordingly. Remaining gap is lack of authenticated socket handshake.
Code Quality & Project Structure7 / 10
Monorepo with pnpm workspaces (frontend, backend, shared). TypeScript strict mode with zero `any` in production code. Property-based testing with fast-check on ipHasher, ipMasker, botDetector, deviceDetector, viewAnalytics (19 test files total). Consistent error handling (HttpError + central errorHandler). Zod validation at all I/O boundaries. Shared package exports inferred types via z.infer. Dockerfile for production, CI workflow (ci.yml), Firebase+Cloud Run deployment config. CONFIRMED GAPS: Significant code duplications — getPollIdParam defined identically in 4 files, toPollWire in 2 files (with intentional isCorrect diff), applyLazyStatusUpdate in 2 files. No business logic tests for core services (0% for poll.service, publicPoll.service, analytics.service, auth.service — tests only cover utility functions). Large monolithic page files: PollBuilderPage 919 lines, PublicResultsPage 915 lines. `req.user!` non-null assertions in 8+ route locations. `as unknown as` type casts in production code (poll.service.ts:261). No .env.example file. No sub-package READMEs. Score 7 — good infrastructure and property-based tests but core business logic untested and significant code duplication.
2
ChaiPoll GF Finalist
Ishaan Satapathy · @ishaansatapathy09_3c3a58db
58
ChaiPoll places 2nd as Grand Final runner-up at 58/80. Strengths are the polished 'mission control' dark theme with Framer Motion / RoughNotation annotations (the best UI of the bracket), the most comprehensive auth package of any submission (dual-token JWT, Google OAuth, 2FA via email OTP, password reset with OTP + magic link, Helmet + CSRF, rate limiting), and the best engineering hygiene (TypeScript strict, 84 tests across 22 files, CI pipeline, Swagger docs). Two structural flaws kept it from winning the Grand Final: (1) the Socket.io layer is decorative — the client socket file (26 lines) sets up a connection but does not listen for data events on most pages, falling back to HTTP refetch (a critical gap for a real-time polling app); (2) polls are immutable after creation — updatePoll only handles title/description/visibility/expiresAt, with no logic for editing questions or options. Against pulse-board, ChaiPoll wins on visual polish and security middleware but loses on functional completeness of the core polling features.
Authentication & Access Control8 / 10
ChaiPoll has the most comprehensive auth of the two: dual-token JWT (15-min access + 30-day refresh with separate secrets, verified in generateToken.js:5-14) in HTTP-only cookies, Google OAuth with avatar sync (passport.js:30-74), 2FA via email OTP with toggle (authController.js:96-119, 328-382), password reset with OTP + magic link dual modes (authController.js:140-246), CSRF via X-Requested-With + JSON bypass (csrf.js:25-34), rate limiting on auth endpoints (rateLimiters.js:6-23), and Axios interceptor with refresh subscriber queue (api.ts:61-104). Weaknesses: signup issues tokens before email verification (authController.js:69 — generateToken called immediately without waiting for verification), passport serializeUser/deserializeUser is dead code (session:false throughout), and optionalProtect exists but is unused (inline optionalAuth used in votes.js:10-22 instead). These are real but minor gaps relative to the overall auth depth.
Poll Creation & Question Management7 / 15
Solid creation flow with dynamic question CRUD, single/multiple choice toggle, mandatory/optional toggle, correct option marking, express-validator rules (min 1 question, 2 options), text sanitization via sanitizeText(), and auto-generated 6-char hex poll codes with up to 10 collision retries (Poll.js:110-130). CRITICAL FLAW confirmed: updatePoll (pollController.js:363-391) only destructures {title, description, visibility, expiresAt} and CANNOT edit questions or options after creation. The controller has zero logic for updating question text, options, isMandatory, or type. Additionally, raw req.body values assigned without sanitizeText() (lines 380-382) and no express-validator middleware on the PUT route (routes/polls.js:24 has only `protect`). This means polls are essentially immutable after creation — a significant functional gap. The 466-line CreatePoll with imperative useState (no form library) also adds friction.
Response Collection Flow8 / 15
Defense-in-depth approach confirmed: banned user check, poll existence (404), isActive gate, expiry check, anonymous enforcement (401 if auth required), mandatory question check via validateResponsesAgainstPoll() (pollVoteIncrement.js:2-36). Atomic vote persistence with MongoDB multi-document transactions on replica sets, graceful fallback with rollback (persistVoteSubmission.js:14-31, 40-56). buildPollVoteIncrement() constructs single atomic $inc with arrayFilters. Duplicate prevention via dual partial unique indexes (Vote.js:42-49): one for authenticated users, one for anonymous IPs. SHA-256 IP hashing (voteController.js:11-13). MongoDB 11000 error caught for "already submitted" message (voteController.js:86-91). PollView handles loading/error/expired/auth-required states. Gaps: no client-side localStorage pre-check, users behind same NAT share IP hash, no PollView component tests. Strong overall, but PollNow edges ahead with Redis locking.
Analytics & Feedback Dashboard8 / 15
Comprehensive analytics confirmed: stat console (total responses, response velocity 24h vs 48h, completion rate, avg response time, poll age), MongoDB aggregation pipeline for time-series with cumulative totals and daily grouping (pollController.js:218-237), Recharts AreaChart + BarChart with percentages, question insight cards (Strongest Consensus, Most Engaged, Highest Abandonment), paginated participant list with voter details, per-option expandable voter lists, CSV export (pollController.js:288-332), QR code with embedded logo. Features breadth is excellent. Weaknesses: N+1 query pattern (polls list + per-poll analytics fetch), velocity computed only from first page of recentVotes (potentially inaccurate), Analytics.jsx at 761 lines is excessively monolithic, no date range filtering, no individual response drill-down. Feature-rich but architecturally compromised; scores equal with PollNow whose analytics are leaner but better engineered (cache stampede vs N+1).
Frontend Experience9 / 10
The best visual presentation of the two candidates. Exceptional "mission control" dark theme (Tailwind + custom CSS gradients), Framer Motion throughout (page transitions, AnimatePresence on question add/remove, animated progress bars), RoughNotation hand-drawn annotations on dashboard metrics, custom spotlight cursor effect on homepage. 14 routes with PublicLayout vs AppLayout + ProtectedRoute separation. Comprehensive state coverage: Skeleton/CardSkeleton/AnalyticsSkeleton loading states, error banners with recovery, EmptyAnalytics empty state, expired poll component, 404 page. QR code generation with embedded logo + copy-to-clipboard feedback. Responsive layout with mobile FAB. Weaknesses: no form library (466 lines of imperative useState in CreatePoll), no data caching layer (refetches on every navigation), no confirmation dialogs for destructive actions. The animation and visual design elevate this above typical hackathon quality and clearly beat PollNow's heavier inline-styled approach without transitions.
Backend Architecture & API Design7 / 15
Clean MVC architecture confirmed: routes→controllers→services→models with factory pattern (createApp() in app.js:21-109). Comprehensive middleware stack: CORS, Helmet, Winston request logger, global rate limiter, express-validator on all endpoints (7 rule sets), CSRF protection, targeted auth rate limiters, debounced lastActive tracker. MongoDB: dual partial unique indexes on Vote, auto-generated poll codes, dual-mode transaction with fallback. SHA-256 IP hashing. Swagger docs at /api-docs (363 lines). 84 test cases across 22 files (Vitest + Playwright + MongoMemoryReplSet — verified count). True MERN stack. Gaps: updatePoll bypasses sanitization and validation (pollController.js:380-382, no validator on PUT route), DUPLICATE route mounting at /api/v1/ AND /api/ confirmed (app.js:73-82 — doubles attack surface), dead TypeScript types (server/types/index.ts — never imported, out of sync with actual schemas), Poll.createdBy missing index, E2E tests mix /create and /create-poll routes inconsistently. Scores equal with PollNow: ChaiPoll wins on tests + MERN compliance, PollNow wins on cleaner caching + rate limiting architecture.
Real-Time Updates Using WebSockets4 / 10
The agent's own findings concluded the Socket.io implementation is a 'structural failure' — server emits 5 event types (pollUpdated, new_participation, etc.) but the client socket (src/socket/index.js, 26 lines) only sets up a connection with a debug logger. It does NOT listen for data events on most pages, falling back to HTTP refetch in Analytics/Results. Some pages partially integrate (Analytics.jsx does listen), but the overall real-time layer is decorative not functional. For a real-time polling app this is a critical gap, not a minor one — score adjusted down accordingly.
Code Quality & Project Structure7 / 10
Well-organized monorepo with server/ and src/ separation confirmed. Consistent naming conventions (controllers/routes/models/middleware/utils/services/sockets). JSDoc @desc/@route/@access on all controller functions. ES modules throughout backend. Factory pattern (createApp()). ESLint 9 flat config + Prettier. CI pipeline with parallel server/client/e2e jobs (GitHub Actions). TypeScript strict mode in frontend. Comprehensive README. 84 test cases across 22 files — the only candidate with tests. Gaps confirmed: duplicate ErrorBoundary component (components/error/ vs components/utils/ — 104 vs 99 lines, nearly identical), ownership check pattern repeated 7x in pollController.js, error logging duplicated 35x across controllers, pagination boilerplate repeated 3x+, unused Vite aliases (@hooks/@utils/@types don't exist), dead server/types/index.ts, E2E tests use inconsistent routes (/create vs /create-poll). Despite these issues, ChaiPoll clearly beats PollNow on code quality due to TypeScript + tests + ESLint + CI.
3
PollNow Semifinalist
Paramveer · @piscesparamveer_99c2215d
59
PollNow distinguishes itself with exceptional backend engineering depth: defense-in-depth response collection (11 validation layers), production-grade Redis cache stampede pattern, and comprehensive Socket.IO abuse prevention. The dual-token JWT auth with replay detection is well-implemented, though missing password reset and email verification. The Bauhaus-inspired UI with 5 themes and QR code sharing is distinctive, but the ~60% code duplication between CreatePollPage and EditPollPage and lack of TypeScript hold back code quality scores. Overall a genuinely impressive hackathon project whose backend architecture shows production-thinking well beyond typical expectations. Compared to PulseBoard: PollNow wins on response submission robustness, rate limiting, cache engineering, and socket architecture. PulseBoard wins on auth completeness (password reset, email verification), UI polish (Framer Motion, theming), and TypeScript safety. [Quarter-Final] Rank #1
Authentication & Access Control7 / 10
Previous: 8. Downgraded to 7 due to missing password reset flow (no forgot/reset-password endpoints exist anywhere) and missing email verification — these are significant auth feature gaps for a production-style platform. Strengths: bcryptjs 12 rounds, JWT dual-token (15min access in JS memory + 7d httpOnly refresh), Google OAuth with issuer check + email_verified check + account merging conflict detection (auth.services.js:42-87), refresh token rotation with replay detection via atomic UPDATE WHERE-clause matching (auth.queries.js:31-38), authMiddleware + optionalAuth, AuthContext with concurrent refresh deduplication via _refreshPromise (api/index.js:19-39), Helmet + CORS, ProtectedRoute + PublicOnlyRoute, rate limiting on auth endpoints. Gap: anonymous token returned in response body stored in JS memory (not httpOnly cookie), making it XSS-vulnerable; ANON_COOKIE_OPTS constant defined but never used (dead code); no CSRF tokens.
Poll Creation & Question Management6 / 15
Previous: 6. Score maintained. Transactional creation via Drizzle (poll.queries.js:34-64) and transactional updates via delete+recreate pattern (lines 71-98). Zod schemas with proper constraints (2-10 options, 1-20 questions, slug format). Auto-slug generation with nanoid(6) suffix (slugify.js:12). publishOn scheduling + results visibility (all/respondents/private). Gaps: no form library — individual useState hooks throughout CreatePollPage (480 lines) with ~60% code duplication vs EditPollPage (456 lines); option IDs client-generated via uuid().slice(0,8) (CreatePollPage.jsx:10); no drag-and-drop reordering; no slug collision handling beyond single retry; EditPollPage uses Math.random() for IDs (weaker than uuid).
Response Collection Flow9 / 15
Previous: 8. Upgraded to 9 for genuinely exceptional defense-in-depth. Verified 11 distinct validation layers in submitResponse (poll.services.js:196-270): (1) Zod schema validation, (2) poll status gate (active only), (3) expiry gate, (4) anonymous access enforcement, (5) anonymous JWT token verification with extractAnonId, (6) Redis distributed lock via SET NX EX 15 preventing concurrent submissions, (7) DB duplicate check query, (8) mandatory question validation with specific missing IDs in error, (9) option validity via Set-based per-question lookup preventing fake option injection, (10) transactional insert (response + answers atomic), (11) DB partial unique indexes (schema.js:79-86) as ultimate guard. Lock released in finally block (line 268). Rate limited at 5/min on submit endpoint. Cache invalidation + Socket.IO emit post-commit. Pre-submission GET /:slug/submission for returning voters. Client-side mandatory validation with inline error feedback. Minor: Redis lock TTL fixed at 15s could theoretically race with slow DB commits, but DB unique index is ultimate guard.
Analytics & Feedback Dashboard8 / 15
Previous: 8. Score maintained. Verified the cache stampede pattern in analytics.service.js:72-99 — this is a textbook production-grade implementation: fresh cache check (30s TTL), distributed lock (SET NX EX 10), lock winner computes and stores both fresh (30s) + stale (120s) copies, lock losers poll every 100ms for up to 3s, stale fallback, last-resort recompute. 4 parallel DB queries via Promise.all (analytics.service.js:15-21). 3-tier results visibility enforced in getPublishedAnalytics (poll.services.js:147-169): private requires ownership, respondents requires prior submission, all is open. AnalyticsPage renders 4 stat cards, per-question Recharts bar charts with VoteBreakdown progress bars, participation-over-time area chart. CSV export with proper escaping (commas, quotes, newlines) — client-side only (AnalyticsPage.jsx:23-59). Live viewer count, socket-connected indicator. Gaps: no individual response browsing/viewing, participation timeline hardcoded to 7 days (analytics.queries.js:35), no question-to-question drop-off funnel, no server-side CSV export endpoint.
Frontend Experience7 / 10
Previous: 7. Score maintained. Distinctive Bauhaus-inspired design with CSS custom properties (index.css:202 lines, themes.js:336 lines with 5 themes), Tailwind CSS v3, JetBrains Mono typography. 11 routes with React.lazy code splitting. Comprehensive state coverage: loading skeletons, error banners, empty states with CTAs, auth-required redirects, toast notifications, confirmation modals. Recharts with custom tooltips, QR code sharing (QRShare.jsx). Responsive grid layouts. Dashboard organized by poll status with filtering. PollVotePage handles all read-only states with dedicated banners (submitted, expired, published, auth-required). Gaps: no mobile drawer navigation, heavy inline styling limits CSS class reusability, no form library on create/edit pages (plain useState), no Framer Motion or smooth page transitions, anonymous voting UX requires extra round-trip (issueAnonToken before submit) without user-facing awareness.
Backend Architecture & API Design8 / 15
Previous: 8. Score maintained. Clean layered architecture: routes→controllers→services→queries with strict separation (no DB in controllers, no Redis in queries). Drizzle ORM with PostgreSQL: 5 tables with enums, relations, cascade deletes, partial unique indexes (responses_user_poll_unique + responses_session_poll_unique), strategic indexing. Zod validation on all inputs via safeParse middleware (validate.js). Redis cache: poll cache (60s TTL), analytics cache (30s fresh + 120s stale) with stampede mutex pattern. 4-tier rate limiting via Lua INCR+EXPIRE script with fail-open (rateLimiter.js): auth 20/5min, submit 5/min, read 120/min, analytics 60/min. Lifecycle worker (5s interval) for auto-activation via publishOn and auto-expiry via expiresAt. DB transactions for create, update, response insert. Centralized error handler with Zod error formatting + production-safe 500s. Standard response envelope. Graceful shutdown disconnects HTTP, Redis, DB pool (server.js:33-44). Gaps: PostgreSQL/Drizzle not MongoDB as MERN track expects; missing indexes on polls.expiresAt and polls.publishOn (lifecycle worker queries these every 5s); no pagination on GET /polls; in-process socket state doesn't scale horizontally; rate limiter fails open on Redis error.
Real-Time Updates Using WebSockets8 / 10
Previous: 7. Upgraded to 8 for superior real-time architecture. Verified dual-room architecture (socket/index.js:6-7): public rooms for voters + admin rooms for creators. 6 server-emitted events: analytics-updated, response-count-updated, viewer-count-updated, poll-expired, poll-published, poll-status-changed. Socket auth middleware verifies JWT on connection (handlers.js:50-58). Abuse prevention: 20 room join limit per socket lifetime, 100 events/sec rate limit with disconnect enforcement, poll ID validation (handlers.js:5-27,40-48). Duplicate join prevention via Set tracking. Viewer count broadcast on join/leave/disconnect with setImmediate. Frontend useSocket hook: Ref-based handlers avoid stale closures, auto join/leave, proper cleanup. AnalyticsPage: 500ms throttle on updates, falls back to 5s HTTP polling on disconnect. Dashboard: PollStatusWatcher on each poll. Lifecycle worker emits status-changed on auto-activate/expire. Gaps: in-process Maps don't scale horizontally; frontend socket reconnects through connect event which re-emits join but without explicit room re-subscription logic; no debouncing on server-side emits.
Code Quality & Project Structure6 / 10
Previous: 6. Score maintained. Comprehensive README (954 lines) covering architecture diagrams, DB design, Redis key map, auth flows, lifecycle state machine, analytics pipeline, Socket.IO rooms, rate limiting, endpoint flows, file-by-file references. Clean module organization with clear boundaries (auth/, poll/, analytics/, socket/, lifecycle/, common/). Consistent naming conventions: *.controller.js, *.services.js, *.queries.js, *.schemas.js. Centralized utilities: response helpers, ApiError with isOperational flag, token logic, slugify. .env.example for both packages. Gaps: no TypeScript (pure JavaScript throughout — zero type safety); zero automated tests; ~60% code duplication between CreatePollPage (480 lines) and EditPollPage (456 lines) with nearly identical form structure; no linter configuration for backend; inline styling pattern blocks CSS reuse across components; Create/Edit use different ID generation strategies (uuid vs Math.random); fail() in response.js is dead code.
4
Quibli Semifinalist
Affan Khan · @affan
59
Quibli delivers a polished full-stack polling platform with standout features: the draft→active→closed→published state machine, cookie-authenticated Socket.IO with admin/public room isolation plus optimistic cache merging, and 905 lines of integration tests covering core business flows. The custom JWT implementation using crypto-js (tokens.ts) rather than a standard library is the single biggest technical concern. The anonymous response deduplication gap (ipHash stored but never enforced at responses.service.ts:121-146) is a critical functional issue for a polling platform. The UI component library and Tailwind v4 design system are well-executed, though the Dashboard VelocityCard hardcoded data and "coming soon" placeholders are minor polish gaps. Strong overall but edged out by pulse-board's superior anonymous dedup strategy and deeper analytics (leaderboard, view tracking, geolocation). [Semi-Final] Rank #2
Authentication & Access Control7 / 10
Dual auth (bcrypt-12 credentials + Sentinel OIDC/PKCE) with rotating refresh tokens in HTTP-only cookies. Refresh reuse detection with full session revocation (auth.service.ts:129-141) is a standout feature. requireAuth (requireAuth.ts:16-46) and optionalAuth middleware, rate limiting on login/register (10/min), Helmet + CORS with explicit origins. Frontend ProtectedShell/AuthShell route guards with automatic 401 refresh deduplication via singleton promise pattern (api.ts:47-65). Gaps confirmed in my review: (1) Custom JWT implementation using crypto-js HMAC-SHA256 (tokens.ts:37-39) instead of a standard library like jsonwebtoken or jose — this is a real security concern since custom crypto implementations are prone to subtle bugs. (2) No email verification for credential users (emailVerified field exists in schema but never set outside OIDC path). (3) No password reset flow. (4) No CSRF protection beyond Helmet defaults. Score stays at 7 — the custom JWT is the main concern holding this back from 8.
Poll Creation & Question Management8 / 15
Full poll creation with dynamic questions — add/remove/reorder via splice (PollEditor.tsx:150-158), per-question isRequired toggle via Switch component (QuestionEditor.tsx:196-211), per-question options (2-10 min/max), response mode toggle (anonymous/authenticated) via Segmented component (PollEditor.tsx:484-501), expiry presets (1h/24h/7d/none) + datetime-local picker with 2-min tolerance matching (PollEditor.tsx:616-665). Save-as-draft AND activate flows with confirmation dialog and share link/QR code. Preview dialog (PollEditor.tsx:667-733). Locked read-only state for non-draft polls. Server-side Zod validation: 1-50 questions, 2-10 options each, future-date enforcement (polls.validation.ts:30-36). Activation validates min 1 question + 2 options/question (polls.service.ts:319-335). Slug collision avoidance with 5-retry nanoid (polls.service.ts:34-44). Gaps: questions deleted/recreated on update (loses question IDs), no form library (custom state management), no slug customization UI, no timer modes or allowResponseChanges features. Score stays at 8 — solid implementation with draft/activate state machine as key differentiator.
Response Collection Flow7 / 15
Defense-in-depth validation chain: poll status gate ("active" only), expiry check (responses.service.ts:54-59), authenticated mode enforcement (L61-66), question existence validation (L91-95), option-question relationship check (L97-101), duplicate answer per question detection (L103-109), required question enforcement (L112-119). Authenticated duplicate prevention at app-level + DB partial unique index (schema.ts:144-147, WHERE respondentId IS NOT NULL). IP hashing via HMAC-SHA256 (ip.ts:14). Transactional write (L140-158). Client-side required-field validation with smooth scroll to first error (PublicRespond.tsx:39-57). Comprehensive humanized error messages (L319-337). Success confirmation, expired/closed GoneState with differentiated UI. CRITICAL GAP confirmed: Anonymous polls have NO duplicate prevention. ipHash is computed and stored (responses.service.ts:146) but never checked for uniqueness — unlimited anonymous submissions are possible (L121-138 only checks authenticated mode). This is a fundamental issue for a polling platform. Also: no rate limiting on the respond endpoint. Score drops from 8 to 7 — the anonymous dedup gap is a real, verified, citable issue that materially impacts the core response submission flow.
Analytics & Feedback Dashboard8 / 15
Comprehensive analytics: total responses + unique respondents with correct distinct counting (polls.service.ts:379-384), per-question option breakdowns with counts/percentages (L406-425), completion rate computation (PollAnalytics.tsx:174-186 — actual calculation, not hardcoded), response velocity timeseries via SQL date_trunc with empty bucket filling (polls.service.ts:465-516). Key insight derivation with winner analysis (PollAnalytics.tsx:378-391). CSV export with proper cell escaping handling commas/quotes/newlines (L393-421). Published results page with executive summary and key insights (PublicResults.tsx). Frontend charts via CSS bars with framer-motion animation (AnalyticsChart.tsx). Gaps: timeseries granularity/window hardcoded to day/7d on frontend (PollAnalytics.tsx:45-46), no individual response viewer, no export beyond CSV, no leaderboard/scoring, no geographic/demographic insights, no view tracking. Score stays at 8 — strong analytic foundations with real time-series and CSV export.
Frontend Experience7 / 10
Polished UI with Tailwind CSS v4, full dark/light mode via ThemeProvider with localStorage persistence and prefers-color-scheme respect (theme-context.tsx). Custom component library: Button (5 variants/4 sizes/loading spinner), Input/Textarea/Field (with aria-invalid), Badge (6 tones), Switch, Segmented, Dialog (framer-motion, Escape key, body scroll lock), Dropdown, Tabs, Skeleton, Card family — 10+ components. Framer Motion animations, 11 routes with layout shells (ProtectedShell/AuthShell/PublicWrapper/MarketingShell). All pages handle loading (Skeleton), error (GoneState, error banners), empty (EmptyState component). Responsive layout. TanStack Query with smart retry, Sonner toast, prefers-reduced-motion support. Gaps confirmed: no React error boundary, Dashboard VelocityCard uses hardcoded bar heights [40,55,72,60,85,95,82] (Dashboard.tsx:349-404 — decorative only), "coming soon" disabled ConfigSwitch features in PollEditor (lines 512-526), Landing page uses hardcoded preview data. Score stays at 7 — polished with minor placeholder/data issues.
Backend Architecture & API Design7 / 15
Clean three-layer architecture (controller→service→db) with feature slices (modules/auth, modules/polls). Zod validation on all inputs (auth.validation.ts, polls.validation.ts: 6 schemas). AppError class hierarchy with machine-readable codes (AppError.ts). Global error handler for Zod/AppError/unknown (errorHandler.ts). Drizzle ORM with 8 well-designed tables, proper FK cascades, indexes on all foreign keys, partial unique index for authenticated response dedup (schema.ts). Transaction-based mutations throughout. Helmet + CORS with explicit origins. express.json({ limit: "256kb" }) body size limit. Gaps confirmed: custom JWT implementation (tokens.ts, crypto-js HMAC-SHA256 — not jsonwebtoken/jose) is a real concern, duplicate readBearer in requireAuth/optionalAuth (requireAuth.ts:9-14 vs optionalAuth.ts:8-12 — identical logic in two files), duplicate fetchQuestionsWithOptions/fetchQuestionsWithOptionsTx (~50 lines near-identical), ZodError details exposed to client in all environments (errorHandler.ts:16), PostgreSQL/Drizzle instead of MongoDB (documented as intentional but diverges from track spec). Score stays at 7 — solid architecture but custom JWT and code duplication are real issues.
Real-Time Updates Using WebSockets8 / 10
Best-in-class Socket.IO implementation. Cookie-based JWT auth on handshake using cookie npm package (io.ts:42-59) — this is proper authentication for WebSocket connections. Two-room pattern: poll:{id} (public totals + status) and poll-admin:{id} (creator-only per-option deltas) — proper security isolation (events.ts:32-36). Server emits response:created (public), analytics:update (admin), poll:status (both) on state transitions. Frontend usePollLive hook (usePollLive.ts) with connection status tracking (connecting/live/offline) and LiveBadge UI. Optimistic cache merging of analytics deltas directly into TanStack Query cache (mergeAnalytics, L34-65) — applies option count changes client-side without waiting for HTTP. Coalesced 2.5s safety refetch for server-only data (L103-110). Proper cleanup on unmount. Auto-reconnect with infinite attempts (socket.ts). Gaps: no socket event Zod validation server-side, analytics deltas don't include uniqueAuthRespondents/timeseries data. Score stays at 8 — clearly the best real-time implementation with auth, room isolation, and optimistic merging.
Code Quality & Project Structure7 / 10
TypeScript strict mode throughout with noUncheckedIndexedAccess/exactOptionalPropertyTypes, zero explicit `any` usage in entire codebase (single occurrence is in a comment only). Feature-slice architecture with clean module boundaries. 905 lines of integration tests across 5 files (auth, polls CRUD, public responses, analytics, realtime) with production guard in test DB helper (db.ts:16-29) — tests run against real DB with truncation between tests. Comprehensive README with setup, env docs, architecture notes, screenshots. Clean component library with consistent API patterns. .env.example present with all 8 variables documented. Gaps confirmed: duplicate readBearer in two middleware files (requireAuth.ts:9-14, optionalAuth.ts:8-12 — 6 lines each), duplicate fetchQuestionsWithOptions/fetchQuestionsWithOptionsTx (~50 lines copied), near-duplicate KPI card components (Kpi/KpiCard/KpiTile across 3 pages), frontend types manually duplicated from backend (no shared package), no frontend tests, no React error boundary, no CI/CD config, hardcoded placeholder data in Dashboard VelocityCard. Score stays at 7 — good engineering with tests, but duplication and lack of frontend tests hold it back.
5
H
POLL-STAR Quarterfinalist
HiteshDhayal · @hiteshdhayal
58
POLL-STAR is a well-executed full-stack polling platform with a distinctive Japanese-minimalist design aesthetic. The backend architecture is clean with layered separation (routes→controllers→services), thorough Zod validation on all endpoints, dual-token JWT auth with Google OAuth, Prisma ORM with PostgreSQL, and Socket.IO real-time updates. The frontend delivers 12 routes with consistent state handling (loading/error/empty/auth), a polished multi-step poll creation wizard using React Hook Form, and responsive Recharts-based analytics. Both frontend (Vercel) and backend (Railway) are deployed and verified working. Key strengths: authentication depth, validation rigor, well-designed database schema, and real-time Socket.IO integration. Main gaps: a critical bug where polls require publishing before responses can be submitted (isPublished gate in polls.service.ts:64), zero automated tests, some schema duplication between client/server, production .env committed, and analytics lacks time-series/trend metrics. Overall a solid 58/100 — above average quality with a few impactful issues.
Authentication & Access Control8 / 10
Full auth system with email/password registration (bcrypt 12, Resend email verification), Google OAuth with CSRF state, dual JWT (15m access + 7d refresh with server-side rotation via bcrypt-hashed tokens), httpOnly/secure/sameSite cookies, authenticate + optionalAuth middleware, ProtectedRoute client gating, Zustand auth store, Axios interceptor with concurrent refresh deduplication. Zod validation on all endpoints, auth rate limiting (20/15m). Missing: no password reset flow (validators defined but no routes/service), production .env committed to repo, no CSRF beyond OAuth state.
Poll Creation & Question Management8 / 15
Multi-step wizard (Details → Questions → Review, 384-line CreatePollPage.tsx) using React Hook Form + useFieldArray + Zod resolver. Per-question mandatory/optional toggle, anonymous/authenticated mode, datetime expiry picker, option limits (2-6), duplicate option detection, share link with clipboard copy. Full CRUD via polls.service.ts with Prisma transactions, auto-expiry detection, owner-only gates. EditPollPage.tsx supports question editing. Gaps: edit page fetches poll via analytics API (loses description/isAnonymous/expiresAt values on load), schema duplicated between server (polls.validators.ts) and client (CreatePollPage.tsx lines 13-56), no draft status.
Response Collection Flow6 / 15
RespondPage.tsx (252 lines) handles loading/error/expired/submitted/active states with good UX. Server-side validation chain is thorough: poll existence check, expiry auto-update, status gate, duplicate prevention (compound unique index for auth users + sessionId/IP hash for anonymous), required question validation, option-to-question membership verification. Transaction-based submission with socket emission. Critical bug: polls.service.ts line 64 gates public access on `isPublished` — polls are created unpublished, so respondents cannot access polls until creator publishes first, contradicting the intended flow where publishing should happen AFTER response collection. This breaks the entire submission path.
Analytics & Feedback Dashboard7 / 15
AnalyticsPage.tsx with ParticipationStats (total responses, questions, participation rate) + QuestionBreakdown (per-question option counts with percentages, CSS progress bars) + ResponseBarChart (Recharts with custom tooltip). Socket.io real-time updates via usePollSocket hook with fallback 30s polling. Publish button, copy link, status badges, live connection indicator. Server analytics.service.ts computes option counts+percentages via Prisma includes. Dashboard shows aggregate response counts per poll. Gaps: participation rate hardcoded to 100%, no time-series/trend data, no auth/anon breakdown, no export, no individual response viewing, public results page doesn't reuse analytics service, copy link uses pollId instead of shareToken.
Frontend Experience7 / 10
12 React Router routes covering landing, auth (3 pages), dashboard, create/edit polls, respond, public results, analytics. Consistent Japanese-minimalist design language (Playfair Display + DM Sans, cream/charcoal/crimson palette). All pages handle loading (spinners/skeletons), error, empty, and auth states. Multi-step poll creation with progress indicators. Responsive layout, CSS animations (fade-up, transitions). TanStack Query for data management. Custom UI component library (Button, Input, Badge, Modal, Skeleton, LiveBadge). Landing page with live demo simulation. Gaps: error handling uses alert() instead of toast UI (CreatePollPage:101, EditPollPage:78), no confirmation dialogs for destructive actions, `as any` casts on Zod resolvers (3 instances), some `any` types in client (6 occurrences), no dark mode, no mobile hamburger menu.
Backend Architecture & API Design8 / 15
Clean layered architecture (routes → controllers → services → Prisma) with domain-driven modules (auth/polls/responses/analytics). All inputs validated via Zod schemas on every endpoint. Prisma ORM with well-normalized relational schema (User/Poll/Question/QuestionOption/Response/ResponseAnswer) and cascading deletes. Transactions for atomic operations (poll creation, response submission, question replacement). Envalid for startup env validation. Centralized error handler distinguishing Zod/AppError/generic errors. asyncHandler utility. Security: helmet, CORS, express-rate-limit (global 100/15m + auth 20/15m). Graceful shutdown with Prisma disconnect. Gaps: CORS uses `origin: true` (too permissive), debug console.log in production (index.ts:27), no pagination on getMyPolls, no request body size limits, uses PostgreSQL/Prisma not MongoDB as track suggests.
Real-Time Updates Using WebSockets7 / 10
Socket.IO server integrated with HTTP server (socket.ts:8-25), room-based pattern (join/leave `poll:{pollId}` rooms). Two server-emitted events: `response:new` (with totalResponses + per-question option counts via Prisma groupBy aggregation, responses.service.ts:103-134) and `poll:published` (polls.service.ts:182-184). usePollSocket hook manages lifecycle (connect, join room, listen, leave room, cleanup). Fallback polling every 30s in analytics page. LiveBadge shows connection status. Gaps: client ignores pushed data from `response:new` — only invalidates TanStack query for HTTP refetch (AnalyticsPage.tsx:34-37), socket on public results page is connected but never triggers refetch (explicitly noted in comment at PublicResultsPage.tsx:25-29), no reconnect room re-join, no socket auth, no typed events, useSocket hook is dead code.
Code Quality & Project Structure7 / 10
Clean monorepo (client/server) with TypeScript strict: true on server, noUnusedLocals/Parameters on client. Domain-driven folder structure with consistent naming. Zero `any` in server code, 6 occurrences in client (error handlers, public results page). Custom UI component library with consistent API. Comprehensive README with architecture overview, API docs, env setup. Well-normalized Prisma schema. ESLint configured on client. Gaps: zero automated tests, Zod schema duplicated between server (polls.validators.ts) and client (CreatePollPage.tsx lines 13-56), client .env.example missing, production .env committed, `as any` casts on Zod resolvers (3 instances), unused code (useSocket hook, refreshSchema/forgotPasswordSchema/resetPasswordSchema validators with no routes), server-setup.sh script in root, no pre-commit hooks or CI/CD.
6
Pulse Board Quarterfinalist
Shivam Goyal · @byte2code
55
PulseBoard is a feature-rich polling platform (~12.7K LOC TypeScript/TSX) with impressive breadth: AI poll generation via GPT-4o-mini, browser fingerprint deduplication, Kafka event pipeline with DLQ and replay, comprehensive analytics with health scoring and AI insights, dynamic Open Graph images, and dual-token JWT auth with Google OAuth. The frontend is polished with Framer Motion animations, dark/light theme, TanStack Query, and comprehensive state coverage. However, the critical loadEnv bug (env.ts:25 returns unvalidated process.env despite running safeParse) is a genuine severity issue that undermines the entire TypeScript safety story — the app could silently crash with missing secrets at runtime. Compared to PollNow: PulseBoard wins on auth completeness (password reset + email verification), UI polish, TypeScript/CI-CD infrastructure, and poll creation UX (drag-and-drop, AI generation). PollNow wins decisively on response submission robustness (11-layer defense-in-depth vs 5), rate limiting (4 tiers vs none), cache engineering (stampede pattern vs basic TTL), and socket architecture (abuse prevention, polling fallback, viewer counts). [Quarter-Final] Rank #2
Authentication & Access Control7 / 10
Previous: 8. Downgraded to 7. The env validation function (config/env.ts:25) returns raw `process.env as unknown as Env` instead of parsed data — meaning JWT_ACCESS_SECRET and JWT_REFRESH_SECRET may be undefined at runtime, silently breaking the entire auth system. Combined with non-null assertions on secrets (auth/service.ts:8-9, middleware/auth.ts:18), this creates a brittle auth foundation. No rate limiting on any auth endpoint (login, register, refresh, forgot-password). Otherwise strong: bcrypt 12 rounds, dual-token JWT with 15-min access + 7-day rotating refresh stored as SHA-256 hash, httpOnly/secure/sameSite cookies (auth/router.ts:22-27), Google OAuth via Passport with auto account linking (config/passport.ts:27-38), forgot/reset password flow with 32-byte random token + SHA-256 hashing + 1-hour expiry (auth/service.ts:50-73), Zustand auth store with Axios 401 interceptor for silent token refresh (api/client.ts:19-53), ProtectedRoute + session bootstrapping.
Poll Creation & Question Management8 / 15
Previous: 8. Score maintained. React Hook Form + Zod (useFieldArray) for dynamic questions/options with add/remove/duplicate (PollBuilder.tsx:238-274). Drag-and-drop reordering via DnD Kit with PointerSensor and SortableContext (lines 307-318). Per-question mandatory/optional toggle, per-option input fields. Expiry scheduling with 4 preset shortcuts (1d/3d/7d/30d). Anonymous mode and show-results toggles. AI poll draft generator via GPT-4o-mini with structured JSON output + hardcoded fallback (polls/service.ts:110-234). Edit mode with question locking when responses exist. Live preview panel (PollPreview.tsx). Server-side: DB transaction creating poll+questions+options atomically (polls/service.ts:20-54), Zod createPollSchema/updatePollSchema with min/max constraints. Gaps: no slug/permalink generation (polls are shared by CUID); no server-side question editing (PATCH is metadata-only, polls/schema.ts:26-32); no validation that expiresAt is in the future (schema.ts:18 + service.ts:29); question locking prevents any editing even for zero-response polls.
Response Collection Flow7 / 15
Previous: 8. Downgraded to 7. Multi-layer validation is present: required question check (responses/service.ts:37-44), option-to-question verification (lines 46-51), poll expiry auto-deactivation (lines 29-34). Browser fingerprint deduplication via Redis SET NX with 24-hour TTL (lines 53-57) is atomic and correct. DB transaction for atomic response+answers insertion (lines 63-83). Step-by-step respond UI with AnimatePresence, progress bar, countdown timer, confetti. 5 distinct UI states. But compared to PollNow's 11-layer defense-in-depth, PulseBoard is notably weaker: no rate limiting on submit endpoint; fingerprint sent as HTTP header (x-fingerprint) is trivially bypassable by any client; no DB-level unique constraint on responses (schema has fingerprint index but no unique constraint); Kafka publish is fire-and-forget after DB write (lines 85-99) — if Kafka send fails, no retry; no Redis lock to prevent concurrent submissions from same identity (fingerprint dedup alone can be raced).
Analytics & Feedback Dashboard8 / 15
Previous: 8. Score maintained. Comprehensive analytics dashboard (AnalyticsDashboard.tsx, 580 lines): 4 metric tiles with animated counts, response velocity timeline via hourly-bucketed area chart, drop-off funnel showing per-question completion drop, health score (0-100) computed server-side (40% completion + 30% unique ratio + 30% velocity). AI-powered insights via GPT-4o-mini generating 5 actionable bullet points from aggregate-only data (polls/service.ts:236-306), cached in Redis for 1 hour. Per-question deep-dive with bar/donut chart toggle. DLQ panel with Kafka offset stats and replay endpoint. Public results page (ResultsPage.tsx, 368 lines). Analytics cached in Redis for 30 seconds (analytics/service.ts:119). Gaps: N+1 query for response counts in dashboard list (polls/router.ts:37-42 — each poll triggers a separate count query); option percentages divide by totalResponses not per-question respondent count (polls/service.ts:268), deflating percentages for optional questions; no CSV/export functionality; no individual response browsing; health score formula uses daysActive = max(1, daysSinceCreation) which doesn't reflect actual active days (analytics/service.ts:91-95).
Frontend Experience8 / 10
Previous: 8. Score maintained. 13 routes with comprehensive state coverage: loading (Skeleton variants: title/card/chart), error (toast notifications + redirect), empty (EmptyState component), auth (ProtectedRoute with skeleton guard), not-found (404 page). Dark/light theme toggle via Zustand store with warm off-white (#F5F4F0) light mode and CSS custom properties (design/tokens.ts). Framer Motion animations: page transitions, section stagger, question card slide, option selection feedback, animated counts. React Hook Form + Zod for poll builder with field-level error display. TanStack Query v5 with staleTime 30s, retry logic, 60s auto-refetch on analytics. Responsive layout: sidebar hidden on mobile, mobile FAB for actions, grid layouts adapt across breakpoints. 13-section landing page with hero, features, pricing, FAQ, testimonials, architecture diagram. Custom toast styling per variant. Google OAuth branded button. Copy-to-clipboard on share. Gaps: no PWA/offline support; form UX on create page is scroll-based (not wizard-like); mobile sidebar hidden but no mobile bottom-nav replacement; landing page sections add significant bundle size for a polling app.
Backend Architecture & API Design5 / 15
Previous: 6. Downgraded to 5 due to a critical, verified bug: loadEnv() at config/env.ts:25 returns `process.env as unknown as Env` — the result of safeParse is stored in `parsed` but NEVER used in the return value; only `console.warn` is emitted on validation failure (line 23). The app starts with potentially missing secrets and crashes at runtime with opaque errors. Additional issues: no rate limiting on any endpoint (auth, AI generation, response submission, analytics — unlike PollNow's 4-tier rate limiting); Redis KEYS command on line 46 of responseConsumer.ts is an O(N) production anti-pattern that blocks the Redis event loop; no graceful shutdown of PostgreSQL pool or Redis connection (index.ts:76-87 only disconnects Kafka producer); error handler uses fragile string-matching on err.message (errorHandler.ts); refresh token race condition possible on concurrent requests. On the positive side: clean layered module architecture (router→service→schema), Drizzle ORM on PostgreSQL, 8 tables with proper FKs + CASCADE deletes + CUID2 PKs + composite indexes, Zod validation middleware used consistently, Helmet + CORS + cookieParser, Kafka event pipeline with DLQ + retry, Swagger/OpenAPI docs. Note: PostgreSQL used instead of MongoDB as MERN track specifies — same issue as PollNow.
Real-Time Updates Using WebSockets6 / 10
Previous: 7. Downgraded to 6. Socket.IO server with JWT-optional auth extracting userId from token (socket/index.ts:12-26). Room-based pub/sub: clients join/leave poll:{pollId} rooms (lines 28-34). Two server-emitted events: analytics:update + response:new, both pushed via Kafka consumer pipeline (realtimeConsumer.ts:21-28). Client-side useSocket hook manages connect/disconnect, emits join/leave on pollId change, tracks connected state (useSocket.ts:10-48). AnalyticsDashboard merges base HTTP-fetched analytics with live socket data via mergeAnalytics (AnalyticsDashboard.tsx:68-93), with useAnimatedCount for smooth transitions. LiveBadge shows green pulsing dot. Gaps compared to PollNow: no live response counts on the public voting page (only analytics dashboard); no reconnection room re-join logic (socket reconnects silently lose room subscriptions); analytics data pushed from Redis counters rather than DB-authoritative source; Redis counter keys have no TTL (permanent memory leak as polls age); no typed Socket.IO events; no debouncing on server-side broadcasts; no abuse prevention (no rate limiting, no connection limits).
Code Quality & Project Structure6 / 10
Previous: 6. Score maintained. Clean monorepo structure (apps/api + apps/web) with npm workspaces. TypeScript strict mode across both packages — zero `any` usage verified. Consistent naming conventions and module patterns throughout. Comprehensive README (303 lines) with architecture diagrams, AI docs, setup guide, deployment instructions. Docker Compose for dev/infra/production environments. GitHub Actions CI (lint + typecheck + Docker build) and deploy workflow. ESLint configured in both apps. .env.example files for both packages. Component colocation (pages/builder/, pages/analytics/). Gaps: critical loadEnv bug (config/env.ts:25 returns unvalidated env) undermines the TypeScript safety claim — the Env type is a complete lie at runtime; escapeHtml/escapeXml 100% duplicated across og/router.ts and og/render.ts; owner authorization check copy-pasted 4 times (polls/service.ts:64,88,101 and analytics/router.ts:14); poll expiry deactivation duplicated in 4 locations (index.ts cron, polls/router.ts:57-62, responses/service.ts:29-34, queries/polls.ts); OptionCountRow interface defined identically in two files; no automated tests of any kind; no graceful shutdown of PostgreSQL pool or Redis connection; removeQuestion in PollBuilder doesn't cascade-delete related answer data.
7
Pollinate Quarterfinalist
Abhinav Bist · @abhinavbist05_1a1e20ef
54
Pollinate is a well-designed live-quiz polling platform with standout features: per-question timers enabling host-controlled progression, 4-strategy voter key deduplication, and comprehensive Socket.IO live state management with auto-expiry timers. The backend uses modern tooling (Zod/Drizzle/TypeScript) with clean module separation. However, critical issues hold it back: a verified vote-limit enforcement bug at public/controller.ts:397-404 that the previous reviewer missed, zero automated tests anywhere in the codebase, massive code duplication (~350 lines of leaderboard and form logic), missing security features (no CSRF, no email verification, no password reset, no OAuth state parameter, token in localStorage), and in-memory-only live state that's lost on server restart. Compared to ChaiPoll, Pollinate wins on poll editing flexibility and the innovative live-quiz model with per-question timers, but loses decisively on authentication depth, analytics richness, UI polish, test coverage, and code deduplication. [Quarter-Final] Rank #2
Authentication & Access Control7 / 10
Email/password registration with bcrypt 10 rounds (auth/controller.ts L41). JWT with 7-day tokens (L12-16). Google OAuth with full authorization code exchange flow (L133-208), proper token exchange and userinfo fetching. Zustand auth store with hydrate pattern, Axios 401 interceptor redirecting to login (api.ts L25-46). TanStack beforeLoad route protection (polls.new.tsx L29). Helmet + CORS allowlist (origins.ts). Rate limiting on submit endpoint (public/routes.ts L11-19). Gaps verified: no refresh token rotation, single JWT_SECRET for all tokens, token in localStorage (XSS risk), no CSRF protection anywhere, no email verification flow, no password reset flow, OAuth flow lacks state parameter (controller.ts L122-128 — URLSearchParams omits state entirely), hardcoded dev JWT secret fallback "pollinate-jwt-secret-dev" (secrets.ts), no rate limiting on login/register endpoints, no ban/suspension mechanism.
Poll Creation & Question Management8 / 15
Full poll CRUD with Zod-validated CreatePollSchema (polls/models.ts L29-39) and UpdatePollSchema (L41-51). Three question types (radio/checkbox/text), mandatory/optional toggle (isRequired), per-question timers (timeLimit 1-600s), correct answer marking (isCorrect), anonymous toggle, vote limits (1-10). Dynamic questions/options add/remove in builder (polls.new.tsx:354 lines) and edit (polls.$id_.edit.tsx:378 lines). Drizzle transactions for atomic poll creation (polls/controller.ts L71-119) and updates (L193-260). nanoid(12) share IDs with QR code display. Unique feature: timer per question enables live-quiz mode. Gaps verified: edit uses destructive replace strategy (deletes ALL questions/options and re-inserts — L226-233), no custom slug/URL for sharing, no bulk/mass option operations.
Response Collection Flow7 / 15
Strong defense-in-depth design: getEffectiveStatus gates draft/ended/scheduled polls (public/controller.ts L19-38), live state validation checks isActive/acceptingResponses/timer expiry (L354-380), answers verified for active question only (L372-379), per-question option validation prevents cross-question injection (L450-469), voter key system with 4 strategies (user/participant/session/IP-based — L45-61), transactional write with answer cleanup via upsert semantics (L472-522). Frontend timer countdown with auto-close (p.$shareId.tsx L94-114), required field validation (L176-179), comprehensive error states. Rate limiting at 30/min. CRITICAL BUG VERIFIED at L397-404: vote limit enforcement uses `!existingResponses[0] && existingResponses.length >= poll.voteLimitPerSession` — the `!existingResponses[0]` negates the check. When voter has existing responses, condition becomes false regardless of count, so vote limit is NEVER enforced for returning voters. This is a logic error missed by previous reviewer who scored 9.
Analytics & Feedback Dashboard6 / 15
Results endpoint provides per-question option counts with percentages, top-option identification, ranking, and text answer listing (results/controller.ts L62-110). Leaderboard with scored ranking: correctAnswers/total, scorePercent, ties broken by submission time (L143-186). Analytics adds completion rate via LEFT JOIN (L229-236) and daily response timeline (L219-227). Public leaderboard available after completion. Frontend analytics page is basic: 3 stat cards (total responses, completion rate, active days) + a date bar chart rendered as inline divs without a chart library (polls.$id_.analytics.tsx:97 lines). Results page shows leaderboard + per-question bars (197 lines). Gaps verified: no CSV export capability, no individual response viewing, no anonymous vs authenticated breakdown, no device/geo/location breakdown, no trend visualization beyond simple daily bars, no health scoring or insight cards, no participation feed.
Frontend Experience7 / 10
Consistent dark theme using Tailwind CSS v4 + shadcn/ui components (Button, Card, Badge, Input, Textarea, Select). Dark/light mode toggle persisted in localStorage (theme-store.ts). Responsive layout from mobile to desktop (root layout with sticky nav). All major states handled: loading ("Loading..." text on all pages), error (red error banners with messages), empty (no-polls CTA on dashboard L83-99), auth-required (redirects with return-to URL). Landing page with hero section and CTA (index.tsx). QR code display, timer countdown animation, progress bar, leaderboard viewing. Public poll page (p.$shareId.tsx:541 lines) is comprehensive with all poll lifecycle states. Gaps: loading states are plain "Loading..." text (no spinners, no skeleton screens), no toast/notification system for success actions, no confirmation dialogs for destructive actions (delete poll, publish), Google SVG icon duplicated verbatim in login.tsx and register.tsx (43 lines each), no form library (manual useState hooks for complex forms), many pages lack error recovery UI beyond a red text message.
Backend Architecture & API Design7 / 15
Well-organized module structure (auth/polls/results/public/socket) with clean controller-route separation. All inputs validated via Zod (6 schemas across models.ts files). Drizzle ORM schema with 5 main tables (users/polls/questions/options/responses), 2 junction tables (options/answers), proper foreign keys with CASCADE deletes, and 7 indexes (schema.ts). Transactions for poll creation, update, and response submission. Security: Helmet (app.ts L49), CORS with explicit origin allowlist (origins.ts L24-56), express-rate-limit on submit endpoint, JWT middleware with DB user lookup. Differentiated HTTP status codes (400/401/403/404/409/410/425/429). Gaps: single JWT_SECRET for all tokens, no token versioning/revocation mechanism, no structured error handling middleware (all errors handled inline per-controller), no API versioning prefix, route collision risk (resultsRouter and pollRouter both mounted at /api/polls, Express first-match dependent), ZERO automated tests anywhere in codebase (no test files, no test framework dependencies in either package.json), in-memory live state lost on server restart.
Real-Time Updates Using WebSockets7 / 10
Innovative Socket.IO live quiz implementation. Server-side: room-based pattern with poll:join/poll:leave events (socket/index.ts L77-89), live state management via in-memory Map with PollLiveState interface, timer auto-expiry via setTimeout broadcasting closed state to all room clients (L168-184), owner-only control verified via JWT token + DB poll ownership lookup (L42-55), participant count via io.in(room).fetchSockets() (L72-75), disconnecting cleanup (L91-98), response:new emitted on successful submission (public/controller.ts L524). Client connects on mount, joins room, handles live-state/participant-count/response-new events (p.$shareId.tsx L66-92). Timer overflow protection: acceptingResponses only when endsAt is in future. Gaps: all live state is in-memory only (lost on server restart — no DB persistence for timers or question state), pure invalidation model (data not pushed over socket, client refetches HTTP), no typed Socket.IO events despite TypeScript throughout, no explicit client-side reconnection re-join logic, no server-side heartbeat or connection health checks.
Code Quality & Project Structure5 / 10
Clean pnpm monorepo with client/ and server/ separation, TypeScript strict mode in both packages, ESLint + Prettier + Husky pre-commit hook. Consistent naming conventions and path aliases (#/*). Good function extraction (canControlPoll, normalizeOrigin, voterKeyFor, getEffectiveStatus). Only 1 server-side `any` (auth/controller.ts L205 catch). VERIFIED CODE DUPLICATION: Leaderboard scoring logic (scoredQuestionIds building + correctOptionIdsByQuestion map + responses iteration + scoring + sorting + ranking) duplicated verbatim between results/controller.ts L37-186 (~150 lines) and public/controller.ts L173-278 (~105 lines) — character-for-character identical scoring algorithm. Question form CRUD functions (addQuestion, removeQuestion, updateQuestion, addOption, updateOption, toggleCorrectOption, removeOption) fully duplicated between polls.new.tsx and polls.$id_.edit.tsx — ~200+ lines of identical JSX and handler logic. Google SVG icon (43 lines) duplicated in login.tsx and register.tsx. ZERO automated tests anywhere (no vitest, jest, mocha, or any test framework in either package.json). Frontend uses `any` extensively (10+ occurrences in route files). No typed Socket.IO events despite full TypeScript stack. No .env.example at root level.
8
Pulse Quarterfinalist
Purakhnath Jyani · @purakhnath_jyani
54
Pulse is a remarkably well-engineered full-stack polling platform demonstrating production-grade architecture with Helmet, structured logging, BullMQ job pipelines, Redis caching with SWR+mutex stampede protection, and 4-layer anonymous identity resolution. The frontend offers polished UX with Tailwind CSS, a custom component library, drag-and-drop poll builder, one-question carousel, Recharts dashboards, and infinite scroll. However, several critical functional gaps were verified that differentiate it from pulse-board: (1) poll questions can NEVER be edited after creation — updatePollSchema excludes questions, making edit mode non-functional for structural changes; (2) SocketProvider only connects for authenticated users (SocketProvider.tsx:44), preventing anonymous poll viewers from receiving real-time updates; (3) no time-series analytics, leaderboard, or view/bot/geo tracking; (4) IP hashing uses static 'salt' string instead of env secret; (5) zero automated tests. These gaps, especially the socket restriction and poll edit limitation, materially impact product functionality. [Quarter-Final] Rank #2
Authentication & Access Control7 / 10
Google OAuth 2.0/OIDC via openid-client with PKCE flow (auth.controller.ts:34-77). JWT access tokens (15min) + rotating refresh tokens with family-based reuse detection and Redis-backed family revocation (auth.service.ts:79-126). Dual middleware (requireAuth + optionalAuth) with extractToken supporting cookies and Bearer headers (auth.ts:6-75). requireOwnership factory for poll owner checks. httpOnly/secure/sameSite cookies. Auth rate limiting at 10/min with 5-min block via Redis sliding window. Axios interceptor with 401 queueing and automatic silent refresh. Family-based reuse detection is sophisticated. Gaps: CSRF_SECRET env variable defined but never used in any middleware — no CSRF protection on any endpoint despite being configured. No custom email/password registration (Google OAuth only), limiting accessibility for users without Google accounts.
Poll Creation & Question Management6 / 15
PollBuilderPage.tsx uses React Hook Form with Zod validation, useFieldArray for dynamic questions/options, and @dnd-kit for drag-and-drop question reordering. Mandatory/optional toggle, anonymous/authenticated mode toggle, datetime-local expiry picker in settings sidebar. Drizzle transaction creates poll+questions+options atomically (polls.service.ts:28-70). Auto slug generation with random hex suffix. Poll listing with pagination and per-poll response counts via grouped query. Critical gap verified: updatePollSchema (polls.schemas.ts:25-32) only accepts title, description, responsesMode, publishResults, expiresAt, status — questions and options CANNOT be edited at any point after creation. The PATCH endpoint (polls.service.ts:159-186) spreads `...input` into the update, but the schema doesn't allow questions, making the PollBuilderPage edit mode non-functional for structural changes. This is stricter than pulse-board which allows question edits pre-responses.
Response Collection Flow7 / 15
Comprehensive defense-in-depth validation in responses.service.ts: poll existence check, status gates (expired/inactive), auth mode enforcement (authenticated requires user), duplicate prevention for both authenticated (DB lookup) and anonymous (Redis lookup via hasAnonResponded), required question validation with per-question option existence checks, and transactional response+answers insert. 4-layer anonymous identity system (anon.ts): HMAC-signed httpOnly cookie (30-day TTL), device fingerprint (UA/language/platform/headers), Redis session token, hashed IP fallback. Rate limiting on submit (5/min, 2-min block). Non-blocking BullMQ analytics enqueue + debounced socket broadcast after submission. Frontend one-question-at-a-time carousel with progress bar, Framer Motion transitions, all states covered. Gaps: IP hashing uses static salt 'salt' in responses.service.ts:21 instead of env secret; no server-side enforcement of single-option-per-question (radio buttons enforced only on frontend); no sendBeacon partial save mechanism like pulse-board has.
Analytics & Feedback Dashboard6 / 15
LiveAnalyticsPage.tsx with 3 stat cards (total responses with AnimatedCounter, active viewers with ping animation, completion rate) and per-question horizontal Recharts bar charts with colored bars and tooltips. Publish Results flow with modal showing public link + copy-to-clipboard. Backend analytics service: computeFullAnalytics aggregates answer counts per question/option via grouped SQL queries, cachedFetch with stale-while-revalidate and local mutex stampede protection, in-flight deduplication, and fast Redis count endpoint. PublishWorker via BullMQ computes analytics, upserts published_results, updates poll flags, invalidates cache, broadcasts socket event. AnalyticsWorker buffers events for 2s, does atomic Redis counter increments, periodic DB upserts. DashboardPage with infinite scroll via IntersectionObserver and status filtering. Gaps vs pulse-board: no CSV/export, no individual response viewing, no trend/time-series data whatsoever, no leaderboard, no view/device/bot/geo analytics, non-functional "Settings" button on analytics page.
Frontend Experience8 / 10
Polished UI with Tailwind CSS v3, dark mode support (ThemeProvider + dark: classes throughout), and custom component library (Button, Card, Skeleton, EmptyState, Modal, ToggleSwitch, AnimatedCounter, LiveBadge). Framer Motion animations: page transitions, question carousel, drag-and-drop, success celebration, chart animations. React Router 7 with 9 routes, lazy loading via Suspense+PageLoader. Three layout variants: AppLayout (sidebar nav), AuthLayout, PublicLayout. Comprehensive state coverage: loading (Skeletons everywhere), error (toast+EmptyState), empty (contextual EmptyState with CTAs), auth (redirects+prompts), 404. TanStack React Query for server state, Zustand for client state. One-question-at-a-time carousel on poll response. Infinite scroll dashboard. Copy-to-clipboard with feedback. Stronger than pulse-board on UI with Tailwind and a proper component library. Gaps: console.log debug line in PublicPollPage.tsx:17, no confirmation dialogs for destructive actions, no PWA/offline support, settings sidebar fields use individual registers rather than consistent Controller pattern.
Backend Architecture & API Design8 / 15
Production-grade Express architecture with comprehensive middleware: helmet, CORS with origin allowlist, cookieParser(signed), compression, pino-http structured logging, correlation IDs. Controller-Service-Route pattern across 7 feature modules (auth, polls, responses, analytics, results, users, health). All inputs Zod-validated: env (19 fields, fail-fast on startup), request bodies (5 schemas for polls alone), query params, URL params. Drizzle ORM + PostgreSQL: 10 tables with proper relationships, 18+ indexes, foreign keys with cascade, custom enums. JWT via jose with HS256, separate secrets, proper expiration parsing. Error class hierarchy: AppError base with 15 specific subclasses. Redis-backed sliding-window rate limiting (4 tiers). BullMQ with 4 queues, 3 workers with exponential backoff. Graceful shutdown (SIGTERM/SIGINT/uncaughtException/unhandledRejection). Stronger than pulse-board on production infrastructure (Helmet, structured logging, BullMQ, correlation IDs). Gaps: CSRF_SECRET in env never used, no automated tests, no HTTPS enforcement middleware.
Real-Time Updates Using WebSockets6 / 10
Socket.io server with JWT auth middleware on handshake (sockets/index.ts:16-42), connection rate limiting (10 per IP, lines 46-61), Redis-backed presence tracking via SET/SREM/SCARD (lines 65-76), and debounced analytics broadcast (500ms, lines 80-104). 5 server events: response:count, presence:update, poll:expired, poll:published, pong. 3 client events: poll:join, poll:leave, ping. SocketProvider on frontend listens to all events, updates Zustand stores, invalidates React Query caches. useSocketRoom hook handles room join/leave with reconnection support (socket.on('connect', join)). useRealtimeAnalytics hook does optimistic cache updates + debounced refetch (2s for BullMQ delay). PublishWorker and ExpiryWorker broadcast socket events. The backend socket auth middleware correctly allows unauthenticated connections (lines 21-24). Critical gap: SocketProvider only connects when isAuthenticated (SocketProvider.tsx:44 — `if (!isAuthenticated) return;`), so anonymous poll viewers NEVER receive real-time updates despite backend support. This is a significant functional regression vs pulse-board where PublicResultsPage connects socket for all users.
Code Quality & Project Structure6 / 10
Clean monorepo structure (pulse-backend/ + pulse-frontend/) with well-organized module-based architecture. TypeScript throughout with strong typing, Zod env validation, shared error classes, and dedicated type files. Consistent naming: controllers/services/routes/schemas per feature module. Custom hooks encapsulate business logic. Tailwind class merging utility. ESLint configured for frontend. Comprehensive README (402 lines) with architecture diagrams, API summary, socket events table, env vars guide, and setup instructions. .env.example files for both packages. Docker Compose + Dockerfile for infrastructure. Gaps: zero automated tests anywhere in the project (verified — no .test.ts/.spec.ts files exist), no ESLint on backend, dead/commented code (PollBuilderPage.tsx:55), console.log debug statement (PublicPollPage.tsx:17), and the unused CSRF_SECRET env variable. pulse-board has 19 test files including property-based tests; Pulse has none.
9
PulseBoard Round of 16
Pallab Karmakar · @pallabdev
57
PulseBoard is a full-stack polling platform (MERN + Socket.io + TypeScript) with thorough implementation across all parameters. Deployed and accessible at two URLs with a working backend API. Authentication is excellent with email verification, refresh token rotation, dual JWT cookies, and rate limiting. Poll builder supports rich text, drag-and-drop reordering, and full CRUD with comprehensive Zod validation. Response collection uses Socket.io with defense-in-depth validation, duplicate prevention via unique indexes, atomic increments, and rate limiting. Analytics features Recharts bar charts, participant summaries with pagination, and result publishing with state machine enforcement. Frontend is polished with dark SaaS theme, comprehensive state handling, and Framer Motion animations. Code architecture is clean with consistent module separation across 8,700+ lines of TypeScript. Main gaps: no tests, some code duplication (StripHtml, CountdownTimer), no CSV export, and poll duration capped at 30 minutes. Overall: 57/100 — a strong submission ranked near the top of the leaderboard.
Authentication & Access Control8 / 10
Thorough auth implementation across 209 lines in auth/services.ts. Registration with hashed email verification tokens (crypto.randomBytes(32), 15-min expiry), login with dual-token JWT (15-min access + 7-day refresh in httpOnly/secure/sameSite cookies), bcryptjs 10 rounds, forgot/reset password with hashed tokens, refresh token rotation with hash comparison (auth/services.ts:174-208). verifyUser middleware extracts token from Authorization header, cookies, or body (auth/middleware.ts:9-42). optionalVerifyUser for public routes. AuthContext with isLoading/authReady pattern, Axios interceptor for auto-refresh with concurrent-request queue deduplication (api.ts:15-77). Helmet, CORS, auth-specific rate limiting (150/15min). Anonymous vs authenticated mode enforced server-side in vote services (vote/services.ts:53-58). Gaps: no CSRF protection, cookie httpOnly disabled in development, no env validation for JWT secrets (uses non-null assertion).
Poll Creation & Question Management7 / 15
PollBuilder.tsx (684 lines) supports full CRUD with create and edit modes. Dynamic questions via drag-and-drop (@hello-pangea/dnd), add/remove, mandatory/optional toggle per question. Options 2-4 per question with add/remove and rich text (tiptap). Poll-level: name, description (rich text), duration (1-30 min), anonymous toggle, status (draft/active). Server-side Zod validation: createPollSchema (poll/validator.ts:64-78), createQuestionSchema (lines 103-124) with unique/continuous order enforcement, updateQuestionSchema (lines 126-160) with option ID tracking and voted-option protection (poll/services.ts:104-110), updateQuestionOrderSchema (lines 162-193). sanitize-html on all rich text. Question deletion cascades votes and recalculates participant counts. Save-in-flight guard prevents double submission. Copy share link and analytics buttons. Gaps: 30-min duration cap is restrictive, no QR code, no slug customization, individual useState hooks (no form library), stripHtml uses DOM API (SSR-incompatible).
Response Collection Flow8 / 15
Response submission via Socket.io with ack callbacks (PublicPoll.tsx:251-264). Server-side validation in submitSocketVotesService (vote/services.ts:29-150): poll existence, active status check, expiry auto-enforcement (sets status='ended'), anonymous mode enforcement per poll (isAnonymousAllowed check), anonymous voter identity requirements, duplicate question IDs per answer rejected, option belonging to question validated, required questions enforced, existing participant votes checked via explicit query plus MongoDB unique compound indexes (vote/model.ts:52-66). Atomic option vote increment via bulkWrite $inc (lines 118-129). Vote count and participant recalculation after each submission. Socket vote rate limiting at 30/min per IP (socket.ts:20-34). Client-side mandatory question validation, identity form for anonymous voters, browser fingerprint via localStorage. UI states: expired, requires signin, draft, published, submitted, identity form, countdown timer. Gaps: vote submission is socket-only (no HTTP fallback), fingerprint is non-cryptographic (fp-{timestamp}-{random}).
Analytics & Feedback Dashboard7 / 15
LiveAnalytics.tsx (389 lines) with Recharts horizontal bar charts, stat cards (Total Votes, Participants, Access with animated Framer Motion numbers), per-question percentage breakdowns with colored indicators, countdown timer. Result publishing via PATCH (updatePollService enforces poll must be ended first, poll/services.ts:296-302). Published results render publicly on PollResults component (PublicPoll.tsx:66-118) with progress bars and vote counts. ParticipantSummary.tsx (180 lines) with paginated individual respondent list, expandable answer details per question, user name/email/type. Backend computes analytics via getPublicAnalyticsSnapshotByCode (public/services.ts:105-140) with per-question option votes and getParticipantSummaryByAnalyticsCode (lines 142-251) with aggregation pipeline. Dashboard shows poll cards with status badges. Real-time socket pushes full snapshot on vote. Gaps: no CSV/Excel export, no time-series trend data, no anonymous vs authenticated breakdown metric, no peak activity tracking, no individual response audit trail beyond summary.
Frontend Experience7 / 10
Consistent dark zinc-themed UI with Shadcn UI + Tailwind CSS across 11 React Router routes. Comprehensive state handling: loading (Loader2 spinners on all pages), error (not-found pages for polls, analytics), empty (no polls CTA, no questions notice, no votes state), auth-required (sign-in prompt with return-to redirect on PublicPoll), expired/published/draft status banners. Responsive layouts: DashboardLayout hides sidebar on mobile, grid adapts from 1→2→3 columns. Rich text editor (tiptap) for poll descriptions, questions, and options. Countdown timers with live second updates. Framer Motion animations on analytics stats. Drag-and-drop question reordering. Copy-to-clipboard with sonner toasts. 60KB+ landing page with animated navbar, hero section, feature cards. Dashboard with poll cards showing status badges, analytics links, share buttons, delete confirmation dialog. Gaps: no dark/light mode toggle, CountdownTimer duplicated across 2 files, PollBuilder at 684 lines could use extraction, frontend README is unmodified Vite starter.
Backend Architecture & API Design7 / 15
Clean layered architecture: routes→controllers→services→models across auth, poll, public, vote modules. TypeScript throughout. 15 Zod validation schemas across all inputs. MongoDB/Mongoose with proper schema design: User (indexed email, hashed tokens), Poll (unique shareCode/analyticsCode, indexed createdBy, state machine draft/active/ended), Question (separate collection, indexed pollId, embedded options with votes, 2-4 options validator), Vote (two sparse unique compound indexes on {pollId,questionId,userId} and {pollId,questionId,userFingerPrint}). 19 REST + 1 health endpoint. Security: Helmet, CORS from env, express-rate-limit (API 600/15min, auth 150/15min), express.json 250kb limit, sanitize-html on rich text, ApiError/ApiResponse classes, centralized error handler. Cascading deletes. Atomic vote increments via bulkWrite. Poll auto-expiry via expireActivePolls. Result publishing state enforcement. Docker compose for local DB. Gaps: no request logging, JWT secrets use non-null assertion, repetitive safeParse pattern in controllers, no pagination on getMyPolls.
Real-Time Updates Using WebSockets7 / 10
Socket.io server integrated with HTTP server (server.ts:10). Room-based architecture: poll:{shareCode} and analytics:{analyticsCode} rooms. Client events: public:poll:join, public:analytics:join (with auth via resolveSocketUserId checking 4 token sources including cookie/header/handshake/body, socket.ts:58-106), public:vote:submit (single), public:votes:submit (batch). Server emits poll:update and analytics:update on vote submission (vote/services.ts:142-143) and poll update (poll/controller.ts:239-240). Socket rate limiting: 30 submissions/min per IP. Client connects with WebSocket+polling fallback, joins rooms on connect, listens for updates with state setSnapshot (LiveAnalytics.tsx:128-131, PublicPoll.tsx:177-181). Cleanup via disconnect on unmount. Socket ready indicator with animated ping dot. Gaps: no explicit reconnection room re-join (Socket.io loses rooms on reconnect), no socket auth middleware (auth at join only), no typed socket events, public poll page has no socket status indicator.
Code Quality & Project Structure6 / 10
Monorepo with backend (2,949 TS lines) and frontend (5,754 TS/TSX lines). TypeScript strict mode. Module-based organization: auth/poll/public/vote each with controller/services/model/validator/routes. Common utilities: ApiError, ApiResponse, tokens, sanitizeHtml, Mail, socket. Clean service/controller separation. Proper React patterns: useEffect cleanup, useRef for mutable refs, useCallback, AuthContext with window events, custom usePolls hook. Axios interceptor with token refresh queue. sample.env and .env.example for both packages. Custom Express type augmentation. Gaps: zero automated tests, StripHtml duplicated 3 times (PollBuilder.tsx:65-70, LiveAnalytics.tsx:31-36, ParticipantSummary.tsx:8-12), CountdownTimer duplicated identically in LiveAnalytics.tsx and PublicPoll.tsx, PollBuilder.tsx at 684 lines without sub-component extraction, Home.tsx is 60KB of static content, no backend ESLint config, comment "// Removed unused React import" in App.tsx line 1 with React import still present, some `as any` type assertions in services.
10
S
Versus Round of 16
Saumya Agrawal · @saumyagrawal
57
Versus is a polished, full-stack polling platform with strong implementation across all criteria. The project features better-auth OAuth (Google+GitHub), a sophisticated 3-step poll creation flow with draft editing, defense-in-depth response collection with DB-level duplicate prevention, comprehensive real-time analytics with adaptive time bucketing and multiple chart types, Socket.io room-based live updates, and a beautiful landing page with parallax animations. The monorepo structure with a shared @versus/shared package demonstrates good engineering discipline. Primary areas for improvement: add automated tests, implement typed Socket.IO events, add CSRF protection, remove `any` types, add reconnection room re-join for WebSocket, and add pagination to the dashboard. Versus advances clearly over POLL-STAR due to: (1) no critical functional bugs vs POLL-STAR's broken isPublished gate, (2) substantially better analytics with adaptive time bucketing vs POLL-STAR's hardcoded 100% participation rate, (3) defense-in-depth response validation vs POLL-STAR's thinner approach, and (4) shared validation package vs POLL-STAR's duplicated schemas with different error messages. [Round of 16] Rank #1
Authentication & Access Control7 / 10
better-auth integration with Google + GitHub OAuth (auth.ts, 33 lines). requireAuth middleware verifies sessions and attaches req.user (auth.middleware.ts, 42 lines). optionalAuth allows unauthenticated access gracefully. ProtectedRoute component with loading spinner and login redirect preserving return URL (ProtectedRoute.tsx, 21 lines). Anonymous vs authenticated poll enforcement server-side (vs.controller.ts:374-376). Innovative admin key system for anonymous creators with SHA-256 hashing + nanoid keys, plus poll claiming via POST /api/vs/claim. Gaps: authLimiter middleware defined but never used anywhere in routes, authInstance typed as `any` (auth.ts:7), no email/password fallback — OAuth-only limits the audience, no CSRF tokens.
Poll Creation & Question Management8 / 15
Multi-step creation flow (Edit→Review→Go Public) in CreatePoll.tsx (624 lines). Dynamic questions with add/remove, dynamic options per question with add/remove and min-2 guard. Mandatory/optional toggle per question. Settings: anonymous/authenticated toggle, show creator name, vote toasts, custom slug, expiry datetime. Client-side validation (title, expiry future check, slug format, question text, min 2 valid options per question). Server-side Zod validation via createPollSchema (poll.validator.ts, 106 lines) shared via @versus/shared monorepo package — strong DRY advantage. Draft editing via PATCH /api/vs/:id with full question/option replacement and admin key auth for anonymous polls. Slug collision detection with abandoned draft auto-cleanup (1-hour threshold). Minor gap: no auto-slug generation, uses individual useState hooks rather than a form library (React Hook Form would be cleaner for 624-line component).
Response Collection Flow8 / 15
Defense-in-depth response collection (vs.controller.ts:358-488). Poll state gating rejects draft/expired/closed polls with specific 410/403 codes. Auto-expiry via checkExpiry on every fetch. Anonymous vs authenticated enforcement at lines 374-376. Duplicate prevention: DB-level unique compound indexes (response.model.ts:47-54) for both auth (poll+respondent) and anon (poll+fingerprint) plus explicit findOne pre-checks at lines 379-393. Mandatory question validation (lines 398-413) iterates poll.questions to verify all required answers present. Option ownership validation checks each submitted questionId/optionId against the poll document (lines 415-434). IP+UA SHA-256 fingerprinting for anonymous dedup (fingerprint.ts). UA parser for device/browser/OS. Client-side mandatory validation before submit (PollPage.tsx:169-172). Minor gap: server does not enforce single-answer-per-question (malicious client could submit duplicate questionIds in answers array).
Analytics & Feedback Dashboard8 / 15
Comprehensive creator-only analytics (Analytics.tsx, 400 lines; vs.controller.ts:668-775). Stat cards: total responses, views, completion rate, response rate with pulse animation on new votes. Response momentum area chart via Recharts with adaptive time bucketing: per-minute (<6h), per-hour (<48h), per-day (>48h) using MongoDB $dateToString aggregation — this is genuinely sophisticated. Device breakdown donut chart (mobile/desktop/tablet) via Recharts PieChart. Per-question horizontal bar charts with consensus indicators (clear_winner/tight_race/split_decision). Optional question engagement tracking with progress bars. Platform (browser) breakdown. Live activity feed (last 20 events). Peak time detection. Poll lifecycle controls: close, publish (irreversible), delete with confirmation dialogs. Published results visible publicly on poll page. Dashboard (Dashboard.tsx, 288 lines) with poll list, status filtering, response counts. Gaps: no CSV/PDF export, no individual response viewing.
Frontend Experience7 / 10
7 client-side routes with consistent dark theme (Tailwind CSS v4, oklch color space). Landing page (Landing.tsx, 476 lines) with wave SVG background, parallax 3D tilt cards using perspective/rotateX/Y on mousemove, count-up animations via IntersectionObserver, animated poll bars, step cards — visually impressive. Comprehensive state coverage: skeleton loaders (SkeletonPoll, SkeletonCard, shimmer animations), empty states (no polls, no data), error states (404, auth required), login-required banners with redirect. Poll page handles all response states: can respond, already voted, needs auth, poll closed, results view. ProtectedRoute shows spinner during auth loading then redirects with return URL. Sonner toast notifications for all actions (correct library choice for production). Copy-to-clipboard with confirmation feedback. Gaps: only 7 routes vs competitor's 12, no QR code sharing, no confirmation dialogs beyond browser window.confirm for destructive actions.
Backend Architecture & API Design7 / 15
Clean layered Express architecture: app.ts (47 lines) sets up helmet, cors, morgan, cookie-parser, express.json with trust proxy. Routes→controllers pattern (vs.routes.ts, 22 lines wiring 10 endpoints). Zod validation middleware with safeParse (validate.middleware.ts, 15 lines). Rate limiting via express-rate-limit on create/respond/get endpoints (rateLimiter.middleware.ts, 21 lines). MongoDB with Mongoose: embedded question/option subdocuments (poll.model.ts, 93 lines), sparse unique compound indexes for duplicate prevention (response.model.ts, 56 lines). Aggregation pipelines for analytics (computeQuestionSummaries, device/platform breakdowns, response velocity). Shared @versus/shared package with Zod schemas, TypeScript types, and error codes consumed by both apps — strong engineering. Centralized error handling via ApiError class and globalErrorHandler. Gaps: env.ts uses `!` non-null assertions without Zod/env validation, no pagination on dashboard/list endpoints, no request body size limits, no service layer separation (controllers mix business logic).
Real-Time Updates Using WebSockets6 / 10
Socket.io server integrated with Express HTTP server (server.ts:20-22). Room-based pub/sub pattern: clients join `vs:{pollId}` rooms via join:poll event, leave via leave:poll (socket/index.ts, 31 lines). 5 server-emitted events: response:new (with full totalResponses+questionSummaries payload), poll:closed, poll:published, poll:deleted, toast:vote:updates. Full-state broadcast design (server recomputes and sends complete summary — good pattern). Client usePollSocket hook (useSocket.ts, 22 lines) joins/leaves rooms on mount/unmount with proper cleanup. PollPage listens for all events updating live results (PollPage.tsx:121-165). Analytics page listens for response:new updating analytics + activity feed + pulse animation (Analytics.tsx:96-128). Graceful degradation: emits wrapped in try/catch. Critical gap: no reconnection room re-join — hook does NOT listen for Socket.io `reconnect` event to re-emit `join:poll`, so after any connection blip the client silently misses all real-time updates. No socket authentication (anyone can join any room by pollId). No typed Socket.IO events.
Code Quality & Project Structure6 / 10
Monorepo with pnpm workspaces: apps/backend, apps/frontend, packages/shared. Shared package provides Zod validators, TypeScript types, and error codes consumed by both apps — excellent DRY architecture. TypeScript throughout with ESLint configured. Clean module organization: common/{config,middleware,models,utils}, modules/{socket,vs}, shared types. Consistent API patterns (sendSuccess/throwApiError, ApiError class, controller+next(error) async pattern). Barrel exports in shared/src/index.ts. Comprehensive README (442 lines) with architecture docs, API reference, setup instructions, design decisions. .env.example for both services. 74 total source files, ~4,200 TS/TSX lines. Issues: authInstance typed as `any` (auth.ts:7), 2 `as any` casts in frontend, authLimiter defined but unused, console.log in mongoose.ts:8, "for ex 5" leftover comment in server.ts:17, no automated tests, no Husky/CI, env.ts uses `!` non-null assertions instead of Zod validation, rate limiter uses in-memory store (not horizontally scalable).
11
PollForge Round of 16
Ashaaf Khan · @ashaafkhan
56
PollForge is a well-executed full-stack polling platform with Firebase OAuth + JWT dual-token auth, drag-and-drop poll builder with conditional skip logic, defense-in-depth response collection, rich Recharts analytics dashboard with real-time Socket.io updates, and a feature-rich notification/milestone system. Key strengths: conditional logic engine (both UI and server-side validation), comprehensive analytics (5 stat cards, 3 chart types, health score, activity feed), notification system with milestones, and gamification (creatorScore, badges). Primary weaknesses: ~200 lines of PollBuilder/PollEdit duplication, zero automated tests, no TypeScript, hardcoded dark colors in 8 files that break the light theme toggle, 14 production console.log statements, unused dependencies, and no server-side ESLint. Scores: 7,8,8,8,6,7,7,5 = 56/100. Pollinate advances due to superior response submission defense-in-depth, cleaner code quality with TypeScript, and consistent theming — though PollForge has meaningfully better analytics. [Round of 16] Rank #2
Authentication & Access Control7 / 10
Solid dual-auth system: Firebase OAuth + email/password registration, JWT dual-token strategy (15m access + 7d refresh), httpOnly refresh cookie with secure/sameSite, auth middleware + optionalAuth middleware, AuthContext with silent refresh interceptor (api.js:26-49), anonymous/authenticated poll modes enforced server-side (responseController.js:106-112). Helmet, CORS with specific origin, dual rate limiting. Gaps confirmed: no email verification, no password reset functionality, access token stored in localStorage (XSS risk), no CSRF protection despite sameSite:none in production, no token versioning/revocation mechanism. The refresh token via httpOnly cookie is a plus over Pollinate's single-token approach.
Poll Creation & Question Management8 / 15
Comprehensive poll creation: PollBuilder.jsx (322 lines) with dynamic questions/options, drag-and-drop reordering via dnd-kit, per-question required toggle, poll-level settings (anonymous, requireAuth, response limit, expiry date). PollEdit.jsx (553 lines) adds full question editing with conditional skip logic UI (lines 440-525). Server-side validateQuestions on create/update (pollController.js:25-40), slug auto-generation with collision avoidance (pollController.js:14-23). Gaps confirmed: PollBuilder lacks conditional logic configuration (only in PollEdit), no form library (individual useState hooks for 8 form fields), significant PollBuilder/PollEdit code duplication (~200 lines including createId, emptyQuestion, validateQuestions, CRUD helpers). The dnd-kit integration and conditional logic are strong differentiators.
Response Collection Flow8 / 15
Thorough response collection with defense-in-depth: server-side validateAnswers (responseController.js:29-73) checks required questions, option existence per question, and conditional logic visibility. Dual duplicate prevention via respondent-based + IP-hash-based lookups (lines 121-133). Expiry enforcement (pollIsExpired, lines 18-21), response cap enforcement (hasReachedCap, lines 23-27), auth mode enforcement (lines 106-112). IP hashing via SHA-256 for anonymous tracking. Completion time tracking with rolling average. Per-poll rate limit of 10 req/hour (responseRoutes.js:8-15). Client-side validation with progress bar and conditional visibility. Differentiated HTTP status codes (400/401/404/409/410/423). Gamification adds creatorScore, badges, and milestone notifications. Gaps confirmed: question/option shuffling settings defined in Poll schema but never implemented in response flow.
Analytics & Feedback Dashboard8 / 15
Rich analytics dashboard: 5 stat cards (Responses, Views, Completion Rate, Avg Time, Health Score), area chart timeline via Recharts, pie chart for participation breakdown (anonymous vs authenticated), bar chart for device usage, per-question detailed breakdown with horizontal bar charts and leading option callout, recent activity feed. 6-factor poll health score computation (pollController.js:377-384): completion rate (20pts), avg response time (15pts), required answer rate (15pts), auth ratio (10pts), day spread (10pts), dedupe rate (10pts). Published results accessible to non-creators via ResultsDisplay.jsx. Real-time Socket.io updates to analytics. Gaps confirmed: no CSV/export functionality, no individual response viewing, analytics computation uses N+1 query pattern (fetches all responses then maps in JavaScript), health score computed server-side only. Significantly richer than Pollinate's basic analytics.
Frontend Experience6 / 10
Polished frontend with 10 page components, all lazy-loaded via React.lazy + Suspense (App.jsx:6-15). Dark/light theme toggle via ThemeContext with CSS variables and localStorage persistence. Comprehensive state coverage: loading spinners, SkeletonCard/SkeletonStat/SkeletonText components, toast notifications for errors/success, empty states ("No polls yet" CTA), expired poll view, success confirmation. Responsive layout. Drag-and-drop via dnd-kit. QR code modal, confirmation dialogs, notification bell with Socket.io. Critical gap verified: PollEdit.jsx, NotificationBell.jsx, ConfirmModal.jsx, QRModal.jsx, Skeleton.jsx, NotFound.jsx, App.jsx (LoadingScreen), and main.jsx (Toaster) ALL use hard-coded dark hex colors (e.g., bg-[#0A0A0F], text-slate-100, border-[#1E1E2E]) that DO NOT respond to the theme toggle. This is a real bug — the light mode doesn't work on ~40% of the UI. Main pages (Dashboard, PollBuilder, PollPublic, Analytics, Landing, Login, Register) are properly theme-aware.
Backend Architecture & API Design7 / 15
Clean MVC architecture with routes→controllers→services separation. Good security posture: Helmet.js, CORS with specific CLIENT_URL origin, dual rate limiting (global 100/15min + response 10/hr), JWT dual-token, Firebase Admin, bcrypt (10 rounds), body size limit (1mb), Morgan logging. Mongoose schemas with proper compound indexes (Poll.js:72-73, Response.js:28-30, Notification.js:18). 22 consistent REST endpoints. Centralized error handler. Gaps confirmed: express-validator only on basic fields (title length, name/email/password), no Zod on backend, settings subdocument (Poll.js:49-57) has no formal Mongoose schema allowing arbitrary keys, duplicated utility code (detectDevice in pollController.js:243 and responseController.js:75, validateQuestions in 3 files, optionalAuth in 2 middleware files), vestigial passport-google-oauth20 dependency still in package.json.
Real-Time Updates Using WebSockets7 / 10
Room-based Socket.io architecture: poll:join/leave and user:join/leave rooms (pollSocket.js:32-49). Debounced 5-second emission with payload merging via emitDebounced (pollSocket.js:4-30). Four event types: response:new (to poll room), notification:new (to user room), poll:milestone (at 10/50/100/500 responses), poll:published (to creator's user room). Analytics page updates all stats locally on response:new events (Analytics.jsx:82-113) with local computation of percentages, leader, and time-series. Notification bell receives real-time notifications (NotificationBell.jsx:43-46). Milestone system comprehensive: first 5 responses + every 5th generate notifications, milestones at key thresholds. Gaps confirmed: no authentication on socket connections (anyone can join any room), no room re-join on reconnect, analytics client-side recomputation could diverge from server state over time, 5-second debounce is lengthy for "real-time" — undermines the real-time claim.
Code Quality & Project Structure5 / 10
Clean monorepo structure (client/src + server/) with sensible MVC organization and excellent README with setup instructions and architecture diagram. Consistent React patterns (useEffect cleanup with active flag, centralized API client with Axios interceptors). However, significant quality issues verified: PollBuilder and PollEdit share ~200 lines of duplicated code (createId, emptyQuestion, validateQuestions, addQuestion, removeQuestion, updateQuestion, updateOption, addOption, removeOption all duplicated verbatim). detectDevice duplicated across pollController.js:243 and responseController.js:75. validateQuestions in 3 files with inconsistent thoroughness. optionalAuth middleware duplicated in 2 separate files. isQuestionVisible duplicated in server (responseController.js:41-47) and client (PollPublic.jsx:51-56). Zero automated tests (no test deps). 14 console.log/error/warn calls in production code across 8 files. No TypeScript anywhere. 3 unused imports (lazy/Suspense in main.jsx, Poll in notificationController.js, useState in Register.jsx). Vestigial passport-google-oauth20 dependency. PollEdit and 7 other files use hardcoded dark colors breaking theme toggle. 1.4MB logo PNG in repo root. No server-side ESLint config at all.
BRACKET CUTOFF · prelim-only scores below
Top 11 above were re-judged head-to-head in the bracket. Their scores reflect the deeper review. Entries below have only the AI's prelim score and were never head-to-head re-evaluated, so a 57 below the cutoff isn't directly comparable to a 58 above it.
12
Voxly
Saurav Jha · @srvjha
56
Voxly is a well-built full-stack polling platform with Clerk authentication, a polished 3-step poll creation wizard, comprehensive response collection with database-level duplicate prevention, and a solid analytics dashboard with charts and regional breakdown. The codebase has clean architecture with controller/service separation, thorough Zod validation, tiered rate limiting, and functional Socket.io real-time updates. Key weaknesses are zero tests, some code duplication, weaker backend TypeScript configuration, no security headers (Helmet), and the real-time layer is limited to a single event type with no data pushed over sockets. The deployment at https://voxly.srvjha.in is accessible and working. Overall score: 56/80.
Authentication & Access Control7 / 10
Clerk-based auth with webhook sync (Svix-verified user.created/updated/deleted at auth.controller.ts:35-82), dual middleware (loadDbUser returns 401, loadOptionalDbUser silently continues at middleware/auth.ts:36-78), anonymous vs authenticated poll modes enforced server-side (polls.service.ts:399-409), ProtectedRoute frontend guard with sign-in redirect (ProtectedRoute.tsx:4-13), anonToken via crypto.randomUUID() in localStorage (anonToken.ts). Gaps: entirely Clerk-dependent with no custom registration/login/password-reset, no Helmet/CSRF protection, anonToken dedup bypassable by clearing localStorage or different browser.
Poll Creation & Question Management8 / 15
Three-step wizard (PollBuilder.tsx, 925 lines) using react-hook-form with useFieldArray for dynamic question/option add/remove, per-question mandatory/optional checkbox, anonymous/signed-in mode toggle, expiry datetime-local picker with future-date validation, live preview sidebar showing respondent view, review step, and edit mode for draft polls. Server-side Zod validation (polls.schema.ts:1-47): questions min 1/max 50, options min 2/max 20, title 1-500 chars, expiresAt must be future. Transactional creation with questions+options tree insertion (polls.service.ts:206-228). Edit locks on non-draft polls and polls with responses (ensureCreatorAndEditable, polls.service.ts:158-176). Gaps: no slug/QR generation, no password protection or response limits, no drag-to-reorder questions.
Response Collection Flow8 / 15
Defense-in-depth validation: client-side mandatory question enforcement (PollPublic.tsx:167-172), server-side question existence check (polls.service.ts:411-422), option membership validation per question (416-421), mandatory question enforcement (424-429), poll status gating (active only, 410 for expired, 386-393). DB-level duplicate prevention via partial unique index on poll_responses(poll_id, respondent_id) WHERE respondent_id IS NOT NULL (schema.ts:77-79) and unique index on question_answers(response_id, question_id) for single-choice enforcement (schema.ts:92-93). Transaction-based submission (432-464), Postgres error code 23505 caught as 409 conflict. localStorage tracking for anonymous duplicates (anonToken.ts:24-32). Lazy expiry via maybeExpire (polls.service.ts:143-156). Rate-limited at 65/hr. Gaps: localStorage-based anon dedup is bypassable, no CAPTCHA/bot protection.
Analytics & Feedback Dashboard7 / 15
Analytics page (Analytics.tsx, 723 lines) with KPI cards (Total Responses, Status, Completion %, Total Votes Cast), per-question Recharts BarChart with option breakdowns and percentages, Recharts PieChart donut for top question distribution with center "Leading" label, Regional Participation card with country flag emojis from geoip-lite and Clerk session country resolution (polls.service.ts:467-587, computeTallies with 50+ COUNTRY_NAMES mapping). Insights section auto-generates leading option, highest/lowest engagement findings. Dashboard (Dashboard.tsx, 445 lines) with stat cards, tabbed created/participated views, and empty states. Published results view with animated ResultBars. Gaps: no CSV/JSON export, no individual response viewing, no trend/time-series data, void Legend hack on line 617.
Frontend Experience7 / 10
Seven routes (App.tsx) with consistent card surface design language across all pages. Dark/light mode toggle via Zustand store (theme.ts) with system preference detection and flash-prevention inline script. Three-step wizard with validated step navigation, clickable step indicator, and live preview sidebar. All pages handle loading (skeleton cards in Dashboard.tsx:176-184, spinners), error (icon+message cards differentiated by HTTP status in PollPublic.tsx:197-216), empty (reusable EmptyState component at Dashboard.tsx:377-406), and auth states (sign-in prompt, ProtectedRoute redirect). Mobile-responsive grid layouts. Animated result bars and pulse effects. Gaps: cardSurface CSS string duplicated in 3 files (Dashboard.tsx:22-25, PollBuilder.tsx:81-84, Analytics.tsx:45-48), no PWA/offline support, no confirmation dialogs beyond window.confirm().
Backend Architecture & API Design7 / 15
Clean layered architecture: routes→controllers→services (polls.routes.ts→polls.controller.ts→polls.service.ts). Six-table normalized PostgreSQL schema (schema.ts) with PostgreSQL enum for poll_status, partial unique indexes for database-enforced invariants, cascade deletes with SET NULL for user references, and order_index columns. Comprehensive Zod validation on all inputs via generic validate middleware (validate.ts:8-33, safeParse with structured error responses). Tiered rate limiting (global: 450/15min, write: 85/15min, submit: 65/hr in rate-limit.ts). ApiError class with 8 static factory methods (api-error.ts), ApiResponse standardized envelope, global error handler (error-handler.ts:6-24). Environment validation at startup via Zod (env.ts). Gaps: no Helmet/security headers, no request body size limits, backend tsconfig has noUnusedLocals/noUnusedParameters/noImplicitReturns/noFallthroughCasesInSwitch all commented out, no backend ESLint, uses PostgreSQL (not MongoDB referenced in criteria).
Real-Time Updates Using WebSockets6 / 10
Functional Socket.io integration: typed server (realtime/io.ts) with ServerToClientEvents/ClientToServerEvents interfaces, room-based pattern (poll:<id>), UUID validation on poll IDs before room join (isValidPollId via uuid library). Server broadcasts poll:update via broadcastPollUpdate (io.ts:50-53) after each accepted response (polls.service.ts:456). Client usePollRoom hook (socket.ts:30-56) with lazy singleton Socket.io client, useRef callback pattern for stale closure prevention, auto-reconnect re-subscription (connect event listener), and proper cleanup on unmount (unsubscribe + listener removal). CORS shared between HTTP and Socket.io. Gaps: only one event type (poll:update on response), no data pushed (pure invalidation signal triggers HTTP refetch), no socket auth (anyone can join any room), only used on Analytics page (not on public poll page for live response counts), single-process only.
Code Quality & Project Structure6 / 10
Clean monorepo structure (backend/ + frontend/) with standard conventions. TypeScript throughout with well-defined types (types.ts:106 lines of shared interfaces). Frontend has strict TypeScript with zero `any` types found. Consistent naming: kebab-case files, PascalCase components, camelCase functions. Good separation: controllers thin, services thick. Comprehensive README (403 lines) with architecture diagram, API reference, database schema docs, setup instructions, and deployment notes. Gaps: zero automated tests (no test infrastructure whatsoever), cardSurface CSS duplicated in 3 files, String.fromCharCode(65+oIdx) duplicated 5+ times, backend tsconfig weaker than frontend (noUnusedLocals/noUnusedParameters/noFallthroughCasesInSwitch commented out), no backend ESLint, void Legend hack (Analytics.tsx:617), console.log in production (index.ts:13, db/index.ts:9,12, error-handler.ts:18), unused icons.svg in public/, frontend/README.md is unmodified Vite template, auto-generated migration name.
13
Opinion
Atharv Dange · @atharvdange618
55
Opinion is a well-built polling platform with a custom OIDC auth system (Kleis IdP), comprehensive poll creation/editing with Zod validation, multi-layered response collection with Turnstile anti-spam and IP fingerprinting, and rich analytics with Recharts visualizations. The monorepo architecture with shared types is clean, and the Tailwind v4 + shadcn/ui frontend is polished. Key weaknesses: the real-time Socket.IO implementation is minimal (only 1 event, no reconnect logic, no debouncing), there's significant form code duplication between create and edit pages, no automated tests exist, and some backend gaps (no rate limiting, no transactions, N+1 queries in analytics). Deployment is fully working on both frontend and API domains.
Authentication & Access Control7 / 10
Custom OIDC auth with self-built Kleis IdP implementing Authorization Code Flow with PKCE (oidcService.ts:25-95). Auth middleware provides both `requireAuth` and `optionalAuth` (auth.ts:36-54), with dual token resolution via Bearer header (JWKS verification) and session cookie (HS256 JWT). Anonymous vs authenticated poll modes correctly enforced at both route level (public.ts uses optionalAuth for submit) and service level (publicService.ts:142-203 checks mode + auth). Frontend AuthProvider detects session via server-side cookie check in layout.tsx:66-67 then calls GET /api/auth/me. Gaps: no registration flow (relies entirely on external IdP), no auth on Socket.IO connections, the isLoaded state is set to true immediately when hasSession=false without API verification.
Poll Creation & Question Management7 / 15
Full-featured poll creator at polls/create/page.tsx (384 lines) using react-hook-form with useFieldArray for dynamic multi-question building: add/remove/reorder questions, add/remove options (2-10), mandatory checkbox per question. Shared Zod schema (schemas/poll.ts) validates title, description, questions (min 1, max 20), options (min 2, max 10). LocalStorage draft saving with 500ms debounce auto-restore (create/page.tsx:62-87). Edit page (polls/[id]/edit/page.tsx, 373 lines) allows full question editing, blocked only after responses exist (pollService.ts:205-209). Gaps: ~200 lines of form JSX duplicated between create and edit pages; createPollSchema uses .transform() which doesn't compose cleanly with .partial() for update; no custom slug support.
Response Collection Flow8 / 15
Comprehensive response collection with defense-in-depth. Server validates expiry (publicService.ts:108-113), poll status (active only, :115-117), mandatory questions (:121-128), question existence (:130-134), and option validity (:135-140). Anonymous mode: Cloudflare Turnstile verification (verifyService.ts:25-44) + SHA-256 IP fingerprinting with configurable salt (publicService.ts:222-228) + UUID cookie-based respondentId + duplicate prevention via findOne on (poll, respondentId) (:156-162). Authenticated mode: OIDC user required, duplicate check on (poll, respondent) (:186-192). Separate verify page with Turnstile widget and JWT verification cookie (poll/[slug]/verify/page.tsx). Client-side mandatory validation via react-hook-form. Gaps: no transaction wrapping for insertMany across questions.
Analytics & Feedback Dashboard8 / 15
Rich analytics dashboard at polls/[id]/analytics/page.tsx (513 lines). Stat cards show total responses, anonymous/authenticated breakdown with animated progress bar, peak activity hour+day, poll duration, and Q1→Qn drop-off rate. Timeline area chart using Recharts for daily response velocity. Per-question bar charts with colored bars, leading option identification, and answer counts. Engagement stats compute unique respondents, first/last response timestamps, peak activity (pollService.ts:247-288). Poll health metrics include hours since creation, duration, and per-question votes with drop-off rates (pollService.ts:290-338). Results publishing via PATCH /api/polls/:id/publish (pollService.ts:176-184), with public results page showing charts. Dashboard (dashboard/page.tsx, 200 lines) lists polls with response counts and status badges. Gaps: no CSV/export, no individual response viewing, analytics computation has N+1 query issue.
Frontend Experience7 / 10
Next.js 16 App Router with polished Tailwind CSS v4 + shadcn/ui design system. Responsive layouts throughout with dark/light theme support. Landing page (page.tsx) has Hero, Features, AnimatedFeatures, HowItWorks, UseCases, FAQ, CTA sections. Loading skeletons for all async states (analytics, dashboard, poll page, edit page). Error/empty/not-found states handled comprehensively. Form UX uses react-hook-form with animated radio option selection on poll page. Toast notifications via sonner for publish and copy actions. GlobalNav adapts to auth state (signed in vs signed out). SEO metadata, OpenGraph, Twitter cards, sitemap, robots.txt, JSON-LD structured data. Gaps: ~200 lines of form JSX duplicated between create and edit pages; document.title set via useEffect rather than metadata API on client pages; some `any` type escapes; no PWA offline support beyond manifest.
Backend Architecture & API Design7 / 15
Clean layered architecture: routes→controllers→services→models with Express 5 + TypeScript. Middleware stack: requireAuth/optionalAuth, Zod validate, centralized errorHandler with custom AppError hierarchy (errors.ts:1-33). Shared Zod schemas and TypeScript interfaces in @opinion/shared package used by both API and web. RESTful API with consistent resource naming (public.ts, polls.ts). Security: Helmet + HPP middleware (index.ts:29-30), CORS with credentials, httpOnly cookies, Turnstile verification. MongoDB schemas: separate User, Poll, Question, Response collections with compound indexes on Response (poll+respondentId, poll+question). Input validation on all mutation routes via validate middleware. Gaps: no rate limiting, no request body size limits, no MongoDB transactions for multi-collection operations, analytics does N+1 queries per question, env vars use non-null assertions (KLEIS_IDP_URL!), createPollSchema.transform() breaks .partial() for update validation.
Real-Time Updates Using WebSockets5 / 10
Socket.IO server attached to HTTP server at startup (index.ts:20-21) with room-based pub/sub pattern. Server listens for `join:poll` and `leave:poll` client events to manage `poll:${pollId}` rooms (socket/index.ts:12-19). Only 1 server-emitted event exists: `analytics:update`, fired from submitResponse controller (public.ts:58) after each response submission. Client connects via singleton pattern (socket.ts:9-17) with WebSocket + polling fallback. Analytics page joins room on mount, listens for `analytics:update`, invalidates React Query cache to trigger HTTP refetch, and cleans up properly on unmount (analytics/page.tsx:86-98). Gaps: only covers new response notifications to analytics page — no events for poll publishing, expiry, or deletion; no socket integration on public voting page; no data pushed over socket (pure signal, client refetches); no debouncing/batching; no reconnect room re-join logic (socket reconnects silently lose subscription); no authentication on socket connections; no typed Socket.IO events.
Code Quality & Project Structure6 / 10
Monorepo with pnpm workspaces: @opinion/shared, @opinion/api, @opinion/web with proper build ordering. TypeScript throughout with separate tsconfig.json per package. Tooling: ESLint flat config with typescript-eslint, eslint-plugin-perfectionist, eslint-plugin-unicorn; Prettier; commitlint with conventional commits; Husky pre-commit hooks running lint-staged + typecheck. Consistent naming: camelCase variables, kebab-case files, PascalCase components. Clean folder separation: routes/controllers/services/models/middleware. Shared types package centralizes Zod schemas and TypeScript interfaces. Gaps: no automated tests at all (no Jest, Vitest, or any test files); ~200 lines of form JSX duplicated verbatim between polls/create/page.tsx and polls/[id]/edit/page.tsx; some `any` escapes and eslint-disable comments in form code; non-null assertions on environment variables; the shared package is very thin (just types/schemas).
14
P
PollFlow
Preet Jain · @preetjain2200
55
PollFlow is a well-executed full-stack polling platform with strong backend architecture (consistent 4-layer pattern), production-grade auth (dual-token JWT with blocklist, token rotation, silent refresh), thorough response validation (3-level defense-in-depth), and comprehensive analytics (MongoDB $facet pipelines with Recharts visualization). The Socket.io implementation has proper room architecture with admin authentication. The frontend is clean with good state coverage. Main weaknesses: significant code duplication (analytics pipeline 3x, config constants, brand headers), no tests, the 1450-line LandingPage, immutable questions after creation, and the WebSocket analytics pattern uses signal-then-refetch rather than pushing data. Deployed and accessible at pollflow.jdevs.codes with working API health check.
Authentication & Access Control8 / 10
Production-grade dual-token JWT (access 15m + refresh 7d httpOnly cookie) with jti-based blocklist, token type confusion prevention, and atomic replay detection via MongoDB E11000 (auth.service.ts:68-79). bcryptjs hashing with configurable salt rounds. Anti-enumeration login (same error for wrong email/password, auth.service.ts:42-48). Forgot/reset password with SHA-256 hashed tokens and Resend email. requireAuth middleware with Bearer+blocklist check (authenticate.middleware.ts:12-35), optionalAuth for anonymous+authenticated on same endpoint. Frontend: Zustand memory-only token, bootstrapAuth on page load (bootstrapAuth.ts:16-23), Axios interceptor with silent refresh + concurrent request queue (axios.ts:91-147), ProtectedRoute/PublicOnlyRoute guards. Zod env validation (env.ts:9-46) enforces min 32-char secrets. Gaps: no CSRF protection, no email verification on registration, cookie maxAge hardcoded instead of sourced from env.
Poll Creation & Question Management7 / 15
CreatePollPage (529+ lines) uses React Hook Form + Zod with nested useFieldArray for dynamic questions and options (CreatePollPage.tsx:124-128). ToggleSwitch for isRequired/requiresAuth/isAnonymous, per-question collapse/expand, up/down reorder. MAX_OPTIONS=6 limit on both client and server. EditPollPage for metadata editing. Poll duplicate feature (poll.service.ts:119-144). Server-side Zod validation: createPollSchema enforces min 1 question, min 2 options (poll.dto.ts:32-63), updatePollSchema for metadata-only edits (poll.dto.ts:66-88). Gaps: questions are immutable after creation — EditPollPage renders them as read-only cards with explanatory note; no slug/permalink for sharing (raw ObjectId); no anonymous/authenticated per-question toggle (poll-level only).
Response Collection Flow8 / 15
Comprehensive defense-in-depth: 9-step submission flow in ResponseService.submitResponse (response.service.ts:12-82). Lazy expiry check via assertPollAcceptsResponses gate (active/expired/published blocking, poll.service.ts:235-258). requiresAuth enforcement before duplicate check to avoid information leakage. Dual-layer duplicate prevention: app-level hasUserResponded/hasIpResponded check + DB-level dual sparse unique indexes {pollId, respondentId} and {pollId, ipHash} (response.schema.ts:111-131). 3-level validateAnswers: question existence, option validity per question, required question enforcement with human-readable errors (response.service.ts:95-150). IP hashing via SHA-256 for anonymous dedup. Atomic $inc totalResponses. RespondPage has 7 distinct UI states: loading, error, published, expired, auth-required, submitted, urgency warning with useCountdown timer. Gaps: SHA-256 of IP is reversible for IPv4 (no salt); no per-question response limit enforcement at DB level.
Analytics & Feedback Dashboard7 / 15
MongoDB $facet aggregation pipeline computes totalResponses, per-question option counts/percentages, daily timeline, and anonymous/identified breakdown in a single query (analytics.service.ts:38-173). Completion rate calculated from required questions only (lines 197-205). Published results endpoint for public view (analytics.service.ts:363-492). AnalyticsPage (1000+ lines) with animated StatCards (requestAnimationFrame + ease-out cubic, lines 58-86), Recharts BarChart per question and LineChart for daily timeline. html2canvas export as shareable PNG results card (useResultsCardExport.ts) with dynamic import, font preloading, oklch() workaround. QR code generation via qrcode library. TanStack Query cache invalidation on WebSocket events. Gaps: aggregation pipeline duplicated across 3 methods (~260 lines); no CSV/JSON export; no individual response viewing; no peak activity metrics; no caching layer for repeated analytics queries.
Frontend Experience6 / 10
Clean UI with shadcn/ui + Tailwind CSS v4. Dark/light mode via next-themes with class strategy and system default. Responsive layout (grid-cols-1 sm:2 lg:3, mobile hamburger menu AppLayout.tsx:159-196). Loading skeletons on Dashboard (PollCardSkeleton 3 cards), Analytics (6 stat card skeletons), PollResults (ResultsSkeleton). Error states with contextual messages on all pages. Empty states with CTAs (Dashboard: "No polls yet", Analytics: "No responses yet" with share link). 7 distinct StatusScreen states on RespondPage. Polished LandingPage with mock components, marquee, feature showcase. Form UX: spinner on submit buttons, inline Zod validation errors. QR code modal with download. Gaps: LandingPage is ~1450 lines (static content, not respecting theme toggle); no form library on create (individual useState); brand header duplicated across 8 pages; no confirmation dialog for publish action; generic empty catch blocks in some handlers.
Backend Architecture & API Design7 / 15
Consistent 4-layer modular architecture (Routes→Controller→Service→Repository) across all 4 modules. asyncHandler wrapper eliminates try/catch boilerplate (async-handler.ts:9-13). Zod validation on all POST/PATCH endpoints (12 server-side schemas). Helmet + CORS (origin-locked to CLIENT_URL) + 6 rate limiters (global 300/15m, login 10/15m, register 5/hr, etc.). Mongoose schemas with proper indexing: sparse unique compound indexes for response dedup (response.schema.ts:111-131), TTL index on blocklist (token-blocklist.schema.ts:35), compound indexes on polls for creator query ({createdBy, createdAt}) and expiry sweep ({status, expiresAt}). Embedded questions/options in poll document (correct for 16MB limit). ApiError class with factory methods, ApiResponse for consistent envelope. Graceful shutdown (HTTP→Socket.io→MongoDB). Gaps: no unit/integration tests; analytics service directly calls Mongoose models (no repository); analytics pipeline duplicated 3 times; route params never Zod-validated; no analytics repository.
Real-Time Updates Using WebSockets6 / 10
Socket.io server with fully typed 4-generic-parameter events (socket.ts:8-42). Dual isolated room namespace: public:poll:{id} (anyone, count+status events) and poll:admin:{id} (creator only, full analytics). Admin room requires 3-step verification: JWT verify + blocklist check + poll ownership verification (socket.ts:120-153). 5 server-emitted events: poll:response-count, poll:analytics-update, poll:published, poll:expired, room:joined. Connection state recovery enabled (2-min max disconnection). Frontend useSocket singleton with reference counting and per-handler cleanup via socket.off(event, handler) (useSocket.ts:191-208). Emit helpers guard against uninitialized io. Gaps: analytics "update" is signal-then-refetch (no data pushed over socket); RespondPage doesn't consume poll:response-count for live counter updates (only poll:published/poll:expired); admin room errors silently caught without logging; joinedRooms Set resets on reconnection; no typed Socket.io schemas at runtime (compile-time only).
Code Quality & Project Structure6 / 10
Clean monorepo with client/server separation. TypeScript strict mode with noUncheckedIndexedAccess, noUnusedLocals, noUnusedParameters on both packages. Prettier formatting on both, ESLint on client. Comprehensive README (849 lines) with architecture diagrams, full API reference, security measures, and deployment instructions. Consistent 4-layer module pattern with no exceptions. .env.example files with clear documentation. Zod env validation at startup. Named barrel exports throughout. Gaps: no tests at all; analytics aggregation pipeline duplicated 3 times (~260 lines); LandingPage.tsx is ~1450 lines (should be split into components); BASE_URL duplicated in 3 files (axios.ts, bootstrapAuth.ts, polls.ts); STATUS_CONFIG duplicated in 2 files; brand header duplicated in 8 pages; MAX_OPTIONS=6 duplicated in CreatePollPage and EditPollPage; no TanStack Query key factory; subscriberCount could go negative in useSocket cleanup.
15
R
PollSnap
Rith Banerjee · @rithb2004
55
PollSnap is a well-built full-stack polling platform with significant depth. The backend features a clean layered architecture with 38 endpoints, comprehensive Zod validation, solid Drizzle ORM schema design with 6 migrations, and proper error handling infrastructure. The frontend delivers polished UX with shadcn/ui, animations, comprehensive state coverage across all pages, and real-time updates via Socket.IO with Redis-based horizontal scaling. Key strengths: thorough response validation with defense-in-depth, rich analytics (per-question, global, geographic, device), proper auth with email/OAuth/password-reset, and good TypeScript discipline. Key weaknesses: questions controller has a bug where 6 validation error paths throw plain Error instead of ApiError (causing 500s instead of 400s), no rate limiting anywhere, significant code duplication in services, frontend lacks strict TypeScript mode, no automated tests, and Math.random() generates fake analytics data. Overall a solid 55/80 — strong implementation with some polish gaps.
Authentication & Access Control7 / 10
Better Auth integration (backend/src/lib/auth.ts:1-37) with email+password, Google OAuth, and Resend-based password reset. requireAuth middleware (middlewares/requireAuth.ts:11-32) validates Bearer tokens via getSession; requireCreator (middlewares/requireCreator.ts:12-49) enforces poll ownership with soft-delete check. Frontend Zustand auth-store (auth-store.ts:22-124) persists user state in localStorage with login/register/logout/checkSession. ProtectedRoute component guards authenticated routes. Anonymous vs authenticated mode enforced server-side in submitResponse (responses.service.ts:36-38). Gaps: no rate limiting on auth endpoints, no email verification (verification table exists in schema but no sendVerificationEmail implementation found), auth token passed via query string for Socket.io creator namespace, users.controller.ts:16 returns ApiResponse.Success with 401 code instead of throwing ApiError.unauthorized().
Poll Creation & Question Management7 / 15
Full CRUD for polls, questions, and options across 22 dedicated endpoints. PollWizard component (PollWizard.tsx, ~598 lines) provides create and edit modes with Zod client-side validation (pollBasicInfoSchema) for title, description, anonymous toggle, ISO datetime expiry, and response goal. QuestionBlock handles per-question option editing. usePollDraft hook (use-poll-draft.ts) provides localStorage auto-save with 30s interval. 4 pre-built poll templates (templates.ts). Server-side activation validation (polls.service.ts:238-281) checks min 1 question, each with 2+ options, and expiry date. Status machine enforces draft→active→closed transitions. Questions can be reordered via dedicated endpoint. Gaps: questions created separately after poll (not inline), no form library (manual useState), expiry only validates ISO format not "must be future", massive single-file PollWizard component.
Response Collection Flow8 / 15
Comprehensive defense-in-depth in submitResponse (responses.service.ts:8-179): validates poll existence, status=active, expiry not passed, anonymous/authenticated mode enforcement, duplicate submission prevention via both DB UNIQUE constraints and explicit pre-check lookup, mandatory question enforcement with missing-question naming in error, per-answer question existence validation, per-answer option-exists-and-belongs-to-question validation. Client-side in PublicPoll.tsx (lines 91-128): mandatory question check before submit, API error toast with descriptive messages. Question-by-question navigation with back/next. Duplicate prevention via UNIQUE on (poll_id, respondent_id) and (poll_id, session_token). IP/device/country tracking on responses. Confetti on successful submission. Results view with live-updating percentages. Gaps: no rate limiting on response endpoint, no CAPTCHA, sessionToken generation must happen client-side but isn't visible in PublicPoll component.
Analytics & Feedback Dashboard7 / 15
Per-poll analytics (analytics.service.ts:72-241): per-question option counts/percentages, total responses, goal progress, completion rate, top-10 recent votes with respondent identity, geographic breakdown, device type breakdown. Public results endpoint (getPollResults, lines 243-322) for published polls. Global analytics module (global-analytics.service.ts): response trends by 30/7/90/365 days, device/browser/OS breakdown from User-Agent parsing, top-5 poll leaderboard, 7-day x 8-bucket heatmap, geographic distribution. Dashboard with 5 endpoints: stats, trends, recent-activity, audience-insights, plan-usage. Frontend: Analytics page with Recharts AreaChart/PieChart, Framer Motion animations, PollAnalytics page with per-question breakdowns. Gaps: N+1 query pattern (per-option counts via individual queries), global-analytics.service.ts:158 uses Math.random() for fake completion rate data, no CSV/export, no individual response viewing, dashboard module breaks controller-service pattern (all inline in routes file).
Frontend Experience7 / 10
56 shadcn/ui components with Tailwind CSS v4 and extensive custom styling. 11 distinct routes (main.tsx:32-52) with proper layout nesting (RootLayout, AuthLayout, DashboardLayout). Polished landing page with hero, process steps, bento features, trust grid, FAQ accordion, CTA, and footer. Framer Motion animations (motion/react) on analytics and dashboard. Comprehensive state coverage: loading spinners (PublicPoll.tsx:135-141), error states with AlertCircle (lines 144-153), draft notice (lines 155-165), closed poll notice with "View Results" button (lines 384-391), submission success morphing state (lines 282-289), live results view with option bars (lines 188-278). Toast notifications via sonner, confetti on submit, copy-to-clipboard with feedback, ConnectionStatus WebSocket indicator. Responsive design with useIsMobile hook. SWR with typed cache keys for data fetching. Gaps: App.tsx is dead code (returns <div>App</div>), no dark/light mode toggle, Hero1 and FooterSection appear unused alongside HeroRevamp/FooterRevamp, no form library for poll creation.
Backend Architecture & API Design7 / 15
Clean layered architecture: routes→controllers→services→DB across 7 modules (polls, questions, responses, analytics, global-analytics, users, health, dashboard). 38 API endpoints. All inputs validated with Zod safeParse (11 schemas: polls, questions, responses, users). Drizzle ORM with 9 tables, proper foreign keys with cascading behavior, 7 indexes, unique constraints, enums, 6 migrations. env.ts: Zod validation at startup with process.exit(1) on failure and Object.freeze. ApiError class hierarchy with 7 factory methods (api-error.ts:1-55). AsyncHandler wrapper for error propagation (async-handler.ts). Helmet security headers, CORS with explicit origin, morgan logging, pino logger with sensitive data redaction, graceful shutdown with 10s force timeout. Swagger documentation. Critical bug: questions.controller.ts uses throw new Error() instead of throw ApiError.badRequest() for Zod validation failures (lines 23, 58, 94, 118, 148, 180 in 6 controller methods), causing 500 status codes instead of 400 for validation errors. Other gaps: no rate limiting anywhere, no request body size limits, dashboard module breaks MVC pattern (all handlers inline in routes file), services re-check ownership already verified by middleware (defense-in-depth but redundant).
Real-Time Updates Using WebSockets6 / 10
Socket.IO with two namespaces: /creator (authenticated, creator.ts:1-100) and /respondent (public, respondent.ts:1-34). Optional Redis adapter via @socket.io/redis-adapter for horizontal scaling (socket/index.ts:21-31). Room-based broadcasting: poll:{id}:creator and poll:{id}:respondent patterns (rooms.ts). 4 typed event constants (events.ts): response:new, vote:update, poll:closed, poll:published. Dedicated socket-emitter.ts module decouples HTTP handlers from socket infrastructure. Client useSocket hook (use-socket.ts:20-137): reconnection with 5 attempts/1s delay, listener registry for reconnection re-attachment, proper cleanup on unmount. PublicPoll.tsx listens for poll:closed to update live status (line 44-49) and vote:update to refresh results (lines 59-65). Gaps: events are pure invalidation signals (client refetches HTTP, no data pushed over socket), poll:published event defined but never emitted anywhere (dead code), no heartbeat/ping mechanism, no debouncing on client-side event handling, frontend socket store joinedPolls array can grow unbounded across navigations, no reconnect room re-join logic (relies on new connection triggering join_poll emit).
Code Quality & Project Structure6 / 10
Clean monorepo structure with separate frontend/ and backend/ packages. TypeScript throughout with zero 'any' in hand-written source. Backend tsconfig strict: true, consistent module pattern (controllers/services/routes/schemas) across 7 domains. ESLint configured on frontend, commitlint on backend. .env.example for both packages, proper README. Barrel exports, DTO types in api.ts (19 interfaces), Zustand store with persist middleware using partialize. Issues: frontend tsconfig lacks strict: true (no strictNullChecks, noImplicitAny), API client auth header logic duplicated 4 times in api.ts (post/patch/delete + fetcher), poll ownership + draft check pattern duplicated in 8+ questions service methods, analytics aggregation logic duplicated between getPollAnalytics and getPollResults (~100 lines each), dashboard module breaks controller-service pattern, App.tsx is dead code (returns static <div>), questions controller throws plain Error for validation (6 occurrences), no automated tests anywhere, Hero1.tsx and HeroRevamp.tsx appear redundant, FooterSection.tsx and FooterRevamp.tsx appear redundant.
16
S
Pollify
Shivam Yadav · @shivam_yadav25
55
Pollify is a well-built, full-stack MERN polling platform with strong implementation across all required areas. The backend features a clean MVC+Service architecture with comprehensive security (Helmet, rate limiting, CORS whitelist, JWT with blocklisting, httpOnly cookies). The frontend has polished UI with a custom design system, Framer Motion animations, and complete state coverage. Analytics are computed efficiently via MongoDB aggregation pipelines with an engagement score. Socket.IO provides real-time updates with room-based isolation across 3 room types. The response collection flow is the strongest parameter (8/10) with atomic transactions, dual duplicate prevention, and comprehensive validation. Key weaknesses: backend deployment is inaccessible (Railway hibernation), some code duplication (StatusTag, CHART_HEX, CustomTooltip), no TypeScript, no automated tests, and questions cannot be edited after poll creation.
Authentication & Access Control7 / 10
Dual JWT (15-min access + 7-day refresh tokens with jti blocklisting), bcrypt cost 12, httpOnly/secure/sameSite cookies, protect and optionalAuth middleware with differentiated error codes, AuthStore with in-memory token storage (authStore.js:20-21), Axios interceptor with concurrent request deduplication queue (api.js:34-43), anonymous vs authenticated poll mode enforced server-side (responseController.js:21-23). Gaps: no email verification, no password reset, no OAuth, no CSRF protection beyond sameSite cookies, Socket.IO auth doesn't handle token refresh during connection.
Poll Creation & Question Management7 / 15
CreatePollPage (428 lines) with dynamic questions/options add/remove, mandatory/optional toggle per question, anonymous/authenticated toggle, preset expiry (1h-7d) + custom datetime-local picker, 3 pre-built templates (NPS, Retrospective, Event Feedback). Server-side Zod validation (optionSchema, questionSchema with duplicate option detection via refine, createPollSchema enforcing future expiry). Mongoose schema validates 1-20 questions, 2-10 options each. PollDetailPage shows question-summary with option chips. Duplicate endpoint copies poll with reset counts. Gaps: questions/options cannot be edited after creation (PATCH only updates title/description/expiresAt), no draft status, no form library.
Response Collection Flow8 / 15
TakePollPage (448 lines) with full response form, countdown timer (1s-interval live countdown), real-time Socket.IO listener for liveCount and poll expiry/publish events. Server-side validation chain: expiry check → auth check → duplicate prevention (by user + by IP, respecting allowMultipleSubmissions) → mandatory question validation → answer-to-question and answer-to-option validation (responseController.js:6-103). Atomic MongoDB transaction in pollService.submitResponse (pollService.js:215-254) with standalone MongoDB fallback. Completion time tracking (seconds). Comprehensive UI state coverage: loading, expired (410), already-responded (409), auth-required (401), submitted success, main form with progress bar. Differentiated HTTP status codes throughout.
Analytics & Feedback Dashboard7 / 15
AnalyticsPage (392 lines): 5 stat cards, 7-day trend line chart (Recharts), per-question progress bars + donut charts with leading option detection, anonymous/authenticated bar chart, recent submissions list, engagement score (composite 0-100 formula), publish-ready prompt, live Socket.IO-driven silent refetch. DashboardPage (209 lines): creator stats (4 metric cards), recent polls, milestone toast notifications. ResultsPage (286 lines): public published results with per-question donut charts, leading option, count+percentage display. Backend: single MongoDB $aggregate + $facet pipeline (5 sub-pipelines) computing 12 metrics (pollService.js:9-175). CSV export endpoint with per-response detail (pollController.js:220-246). Gaps: no individual response viewing, no date range filtering, engagement score formula is somewhat arbitrary.
Frontend Experience7 / 10
Tailwind CSS with custom design system (485-line index.css with CSS custom properties, consistent warm palette, typography). Framer Motion animations across all pages (fadeUp, AnimatePresence for question add/remove, animated progress bars, spring transitions). Responsive layout with mobile hamburger menu (Navbar.jsx:181-228). 11 routes with proper layout composition (Layout, AuthLayout, ProtectedRoute). Comprehensive state coverage: LoadingSpinner everywhere, toast error handling, empty states (no polls CTA), auth gating. Custom toggle switches, progress bars, live-dot animation, option cards with selected state. Landing page with hero, features grid, how-it-works, CTA section. AuthLayout with decorative panel and stat cards. Gaps: no form library (useState hooks for all form state), StatusTag component duplicated in 3 files (DashboardPage, MyPollsPage, PollDetailPage), no data caching layer.
Backend Architecture & API Design7 / 15
Clean MVC + Service layer + Validator architecture. 22 REST API endpoints across 3 route files. 4 Mongoose models with appropriate indexes (creator, status, expiresAt, compound poll+respondent, compound poll+ip, compound poll+submittedAt, TTL index on BlacklistedToken). Security: Helmet, CORS with origin whitelist (server.js:30-46), rate limiting at two tiers (200/15min global + 10/min for submissions), httpOnly cookies, trust proxy (server.js:61). Zod validation on poll creation, express-validator on auth. MongoDB aggregation pipeline for efficient analytics (single $facet query). Atomic transactions with standalone MongoDB fallback (pollService.js:254-288). Centralized error handler (CastError, 11000, ValidationError → JSON). Graceful shutdown (SIGTERM/SIGINT). Gaps: two different validation libraries used inconsistently, bulk expiry updateQuery duplicated between controller and service, no automated tests, no API versioning, no request body size limits beyond express.json(10mb).
Real-Time Updates Using WebSockets6 / 10
Socket.IO server with JWT auth middleware (server.js:74-90). Room-based isolation: 3 room types (poll:{code} public, creator:{userId} notifications, poll:admin:{code} isolated analytics). 4 server-emitted events: response:new (with analytics payload), poll:expired, poll:published, milestone:reached. 7 server-listened events: join/leave for all 3 room types + disconnect. Client-side singleton socket with reconnection (5 attempts, 1s delay, socket.js:10-22). Cleanup: named listener .off(), leave rooms on unmount. Analytics page silently refetches on response:new (analytics.tsx:101-103). Dashboard shows live response count and milestone toasts. TakePollPage responds to live count + navigates on expiry/publish. Gaps: no reconnection room re-join logic (rooms lost on reconnect), no data pushed over socket (signal-only, clients HTTP refetch), no socket auth token refresh during long sessions, no debouncing on response:new.
Code Quality & Project Structure6 / 10
Monorepo structure with backend/ and frontend/ separation, root package.json scripts for dev/build. Backend organized into controllers/, middleware/, models/, routes/, services/, validators/, utils/ with clear separation. Frontend organized into pages/, components/common/, components/layout/, lib/, store/. CSS design system with 30+ custom properties. .env.example with documented variables. vercel.json for deployment. ESLint configured on frontend. Consistent conventions throughout. Issues: no TypeScript; StatusTag component duplicated in 3 files (DashboardPage.jsx:20-23, MyPollsPage.jsx:14-17, PollDetailPage.jsx:17-20); CHART_HEX array duplicated in 2 files; CustomTooltip near-duplicate in 2 files; field() render helper duplicated in LoginPage and RegisterPage; commented-out dead code in pollController.js:260-262; unused @types/react devDependencies; no automated tests; token expiry durations hardcoded (15m/7d).
17
Pulse Board
Saad · @saad
55
Pulse Board is a well-architected full-stack polling platform with Express 5 + React 19 + Sequelize/PostgreSQL. Strengths: comprehensive session-auth with dual Passport strategies and CSRF protection, full election lifecycle (launch/end/publish/unpublish), defense-in-depth response validation with session+IP-hash+localStorage vote prevention, room-based Socket.IO real-time updates, consistent newspaper-themed UI with React Query + React Hook Form + Zod, 38 RESTful endpoints with Zod validation on every input, exceptional documentation (README, CLAUDE.md, Starlight docs). Main weaknesses: no TypeScript (JS only), the clearCookie("connect.sid") logout bug, thin test coverage (13 tests), widespread code duplication in controllers, relatively shallow analytics (no time-series, no export), and no service layer abstraction. Overall score: 55/80, a solid "good" submission above the median (43).
Authentication & Access Control7 / 10
Comprehensive session-based auth with dual Passport strategies (admin-local + voter-local), signup/login/logout/forgot-password/reset-password, profile update, CSRF double-submit cookie protection on all mutations, requireAdmin/requireElectionOwner/requireVoter middleware, deserializeUser re-hydrates from DB on every request, Zod-validated inputs, anonymous vs authenticated poll modes enforced server-side. Key gaps: clearCookie uses "connect.sid" instead of the configured "pulse.sid" (auth.controller.js:51, public.controller.js:130) — a real logout bug; bcrypt SALT_ROUNDS=10 (adequate but low for 2026); no email verification on signup; no Google OAuth.
Poll Creation & Question Management8 / 15
Full election CRUD with separate launch/end/publish/unpublish lifecycle; question CRUD with isRequired toggle, title+description; option CRUD per question; voter management for authenticated mode; auto-generated or manual custom URL slugs with collision handling (-2 through -50 sequence); live slug availability checker with canonical form suggestion and debounced (280ms) validation; launch validation checks 1+ questions, 2+ options/question, 1+ voters for authenticated mode; update wall after launch (only expiresAt mutable). Frontend: QuestionsTab with React Hook Form + Zod, inline question/option editing, auto-focus new question's option input, incomplete-option warnings. Gaps: "Cannot edit launched election" guard duplicated 6 times across controllers; questions/options/voters fully locked after launch.
Response Collection Flow7 / 15
Defense-in-depth with multiple vote-prevention layers: session-based tracking (survives reload/tab close), IP-hash secondary check (HMAC-SHA256, not raw IP), and localStorage client-side record for instant revisit short-circuit. Server validates election is accepting responses (launched && !ended && !expired), verifies question/option ownership (cross-question injection prevented), enforces required questions, prevents duplicate answers per question, and uses Sequelize transaction for Response+Voter write. BallotPage with radio selection, inline missing-required validation, and 409-to-thanks redirect. Gaps: monolithic 95-line submitResponse function in public.controller.js:136-231; no UI countdown timer for expiry; no per-question single-option enforcement on server (relies on frontend radio buttons).
Analytics & Feedback Dashboard6 / 15
Working analytics with per-question option tallies, total response count, participation rate for authenticated mode (responses/eligible with percentage), leading option identification. Chart.js bar charts with custom newspaper-styled design. WebSocket-driven live invalidation on new responses. Public results page with same charts, 404-gated until resultsPublished=true, NotifyCTA for subscription. Gaps: no time-series/trend data (no per-hour/per-day velocity), no individual response viewing, no CSV/PDF export, no drop-off rates between questions, brute-force in-memory tally for all responses (analytics.controller.js:24-30), no peak activity metrics, no response timeline visualization.
Frontend Experience7 / 10
14 frontend routes with React Router 7, ProtectedRoute for admin pages, React.lazy code splitting for heavy pages. Consistent newspaper/polling aesthetic with custom typography, "Stamp" accents, LiveDot animations, TallyMark SVG. React Hook Form + Zod on all forms. Comprehensive state handling: loading (skeleton text on every page), error (Banner/PublicError with status-code mapping), empty states ("No polls yet" with CTA), auth-required redirects carrying from-state. ShareLink with QR code generation (copy URL, copy QR as image, Web Share API, download PNG). NotifyCTA with 4 contextual variants. Smart 404 with context-aware exit links. Gaps: no dark mode; some forms use plain useState instead of React Hook Form (Dashboard create flow); no custom confirmation dialogs (uses browser confirm()); no PWA/offline support.
Backend Architecture & API Design7 / 15
Clean layered MVC: routes assemble middleware chains → validate(Zod) → guards → controllers. 38 RESTful API endpoints across 6 route files. 7 validator files with ~21 Zod schemas (params, bodies, queries validated everywhere). Security stack: Helmet, csrf-csrf double-submit cookie, express-rate-limit on auth+submit endpoints, httpOnly/secure/sameSite session cookies. Environment validated at startup via Zod (exits on missing secrets). CASCADE deletes at DB level. Election.isAcceptingResponses() domain method. Centralized error handler with CSRF-specific 403, stack stripping in production. Gaps: no TypeScript; no service layer (business logic lives in controllers); PostgreSQL/Sequelize not MongoDB; clearCookie("connect.sid") logout bug; no structured logging; no process-level uncaughtException handler.
Real-Time Updates Using WebSockets7 / 10
Socket.IO server shares express-session middleware for authenticated connections (sockets/index.js:16). Dual-room pattern: election:<id>:public (everyone) and election:<id>:admin (owner only, gated by session.user.id check). response:new emitted to admin room on each submission with totalResponses+newAnswers. election:status emitted to both rooms on launch/end/publish/unpublish/expiry with public-safe field filtering. Emitter abstraction (controllers call emitResponseNew/emitElectionStatus, never io directly). Frontend: useElectionSocket hook joins/leaves rooms + cleans up listeners; AnalyticsPage invalidates React Query on events; LandingPage auto-redirects on live status changes. Gaps: no reconnection logic (room re-join on reconnect); pure invalidation signal (client refetches via HTTP, no data pushed over socket); no debouncing on rapid response:new events; no TypeScript event typing.
Code Quality & Project Structure6 / 10
Clean npm workspaces monorepo (backend+frontend+docs) with consistent module structure (controllers/routes/validators/models/middleware/sockets/lib). All files use "use strict". ESLint on both workspaces. Husky pre-commit hook. Exceptional documentation: comprehensive README with deploy guide, CLAUDE.md with architecture overview and conventions, Starlight docs site. Docker multi-stage build, Kamal deploy config, GitHub Actions CI (manual dispatch). .env.example files for both root and backend. 10 backend + 3 frontend tests that cover core flows well. Gaps: no TypeScript in application code (JS only); widespread try/catch/next boilerplate (~35 instances); "Cannot edit launched election" guard duplicated 6 times; tally-building logic duplicated in analytics.controller.js and public.controller.js; identical Question+Option eager-loading query duplicated 4 times; RESERVED slug set initialized as empty (dead code); notifyOn:"all" enum value on Subscriber has no handler; SALT_ROUNDS=10 duplicated in 2 files; inline require("http-errors") in error.js:27; only 13 tests for a project with 7 models and 6 controllers.
18
S
ZenPoll
SUPRABHAT · @suprabhat
55
ZenPoll is a well-executed full-stack polling platform with 26 API endpoints, comprehensive auth (email+OTP, Google OAuth, refresh rotation), rich poll creation with multiple question types, defense-in-depth response validation with both auth and fingerprint deduplication, detailed analytics with demographic breakdowns and announce/email flow, and a polished crimson-branded frontend with thorough UI state coverage. The backend shows strong engineering with Zod validation, Prisma transactions, and proper security middleware. Key weaknesses: frontend code quality suffers from pervasive `any` types, significant code duplication, and monolithic components; Socket.io is functional but limited to owner-room invalidation signals (no pushed data, no participant rooms, no socket auth); the announce email contains hardcoded localhost URLs and "VibePoll" branding inconsistencies; and there are zero tests. Overall a solid submission in the 55-point range.
Authentication & Access Control8 / 10
Comprehensive auth system with email/password sign-up (bcrypt 12 rounds), OTP verification via Resend email (auth.service.ts:70-127), Google OAuth with CSRF state in httpOnly cookie (auth.google.ts, auth.routes.ts:56-65), JWT access+refresh token rotation with DB-hashed refresh tokens and revocation on rotation (auth.service.ts:129-172, auth.service.ts:174-186), password reset with silent email handling and transactional atomicity (auth.service.ts:188-252), requireAuth/optionalAuth middleware (require-auth.ts:24-43), AuthProvider context with refreshSession/updateAuth/signOut and auth:expired event listener (AuthProvider.tsx), ProtectedRoute with loading+redirect (ProtectedRoute.tsx), ANONYMOUS/AUTHENTICATED mode enforcement server-side (public.service.ts:174-176), and httpOnly/secure/sameSite cookies (auth.utils.ts). Gaps: no rate limiting on auth endpoints (brute-force vulnerability), custom error messages for non-whitelisted origins (app.ts:35).
Poll Creation & Question Management7 / 15
Functional poll creation form at CreatePollPage.tsx (419 lines) with dynamic questions (add/remove), per-question isRequired toggle, per-question option add/remove (min 2 enforced), response mode toggle (ANONYMOUS/AUTHENTICATED), isPublic visibility toggle, expiry with 5 preset durations + custom days input, maxResponses cap with toggle, and category dropdown. Server-side Zod validation enforces 1-30 questions, 2-300 char text, 2-10 options, 1-120 char labels (polls.schemas.ts). Update endpoint replaces questions in transaction (polls.service.ts:155-186). Auto-slug generation with random suffix (polls.service.ts:27-37). Gaps: no form library (individual useState hooks), no question reordering/drag-and-drop, update destroys all questions and recreates them, category stored as string-in-description not a proper field, key={qIndex} anti-pattern, client validation is manual rather than shared Zod.
Response Collection Flow8 / 15
Defense-in-depth response collection: isPublished/expiry/maxResponses gates (public.service.ts:161-171), AUTHENTICATED mode enforcement requiring user (line 174-176), required question validation with specific error message (lines 184-187), per-option validity check against question's options (lines 189-191), authenticated user dedup via DB unique constraint [pollId, respondentUserId] + explicit check (lines 203-209, schema.prisma:147), anonymous fingerprint dedup via fingerprintId DB lookup (lines 212-218), client-side crypto.randomUUID() fingerprint in localStorage (polls-api.ts), voted-polls Set in localStorage, transactional submission with atomic answer creation (lines 220-243), differentiated HTTP codes (401/404/409/410/400), silent token refresh on 401 with retry, and auth:expired CustomEvent. Frontend PollDetailsPage.tsx (353 lines) handles option selection, required question tracking, post-vote results with percentage bars and avatar stacks, and auth wall for AUTHENTICATED polls. Gap: fingerprint can be cleared client-side to bypass dedup.
Analytics & Feedback Dashboard8 / 15
Rich analytics: total/authenticated/anonymous counts (polls.service.ts:342-344), per-question answeredCount/skippedCount (lines 368-372), per-option count/percentage to 2 decimals (lines 420-432), per-option voterCards with name/image/gender/ageGroup for ALL voters (lines 352-389), per-option voterPreviews (first 8 for avatar stacks, lines 436-440), gender breakdown per option (MALE/FEMALE/NON_BINARY/PREFER_NOT_TO_SAY, lines 391-396), age group breakdown (Under 18 through 55+, lines 399-403), demographics suppressed when <3 authenticated voters (line 347), announce-results endpoint with Resend email to all voters (lines 229-304), public results page with demographics when announced (public.service.ts:246-393). Frontend PollAnalyticsPage.tsx (415 lines) has stat cards, per-question OptionCards with voter grids and gender/age tags, show-all-voters expand, DemoBar animated progress bars, demographic suppression warning, and no-votes empty state. Gaps: announce email hardcodes http://localhost:5173 (line 258), email references "VibePoll" not "ZenPoll" (lines 279,289), no CSV/export, no time-series trends, email failures silently logged.
Frontend Experience6 / 10
Clean crimson-branded design with 18 routes, responsive layouts, and comprehensive UI state coverage: loading skeletons (Dashboard, Explorer, Poll Details), error states with messages and retry links, empty states with CTAs (Dashboard, Explorer, Analytics), and success transitions (create poll → live screen with share URL). Landing page has Hero, Features, HowItWorks, TrendingPolls (with skeleton cards), and CTA sections. ProtectedRoute handles loading/redirect. Copy-to-clipboard with "Copied" feedback. Status badges with color coding. Avatar stacks with overflow. Confirmation dialogs for destructive actions. Gaps: pervasive `any` types across all page components (PollDetailsPage poll/results are `any`, PollAnalyticsPage res is `any` on line 232), large monolithic page components (353-419 lines), no form library on create poll, duplicated resolveAvatar/genderLabel/DemoBar between PollDetailsPage and PollAnalyticsPage, empty catch blocks suppress errors silently (PollDetailsPage.tsx:115,149), console.error instead of user-visible errors on dashboard.
Backend Architecture & API Design7 / 15
Clean modular architecture: 6 modules (auth, polls, public, users, uploads, health) with routes→service→schema separation. 26 REST endpoints. All inputs Zod-validated (10 schemas). Prisma schema (9 tables) with proper relations, cascade deletes, composite unique constraints on [pollId,orderIndex], [questionId,orderIndex], [pollId,respondentUserId], [submissionId,questionId], and targeted indexes for public listing and token cleanup queries. Transactions for poll create/update, response submission, and password reset. Centralized error handling with HttpError class (error-handler.ts). Helmet, CORS with dynamic allowlist, rate limiting on /api/public (120/min), graceful shutdown. TypeScript strict, noUncheckedIndexedAccess, exactOptionalPropertyTypes in backend. createApp() factory pattern for testability. Gaps: Socket.io shared via mutable singleton breaking DI (socket.ts:3-6), route handler directly calls getSocketServer() mixing concerns (public.routes.ts:59), email sending in poll service not a notification module, error handler leaks generic Error messages to client (line 46-51), no auth rate limiting, hardcoded localhost URL in production, category embedded in description string, no pagination.
Real-Time Updates Using WebSockets5 / 10
Functional Socket.io with room-based owner pattern: server emits responses:count and analytics:update to poll:{pollId}:owner rooms on each submission (public.routes.ts:59-68). Frontend connects via singleton client (realtime.ts), DashboardPage joins owner rooms for all polls (DashboardPage.tsx:29-33), applies 250ms debounced refresh (lines 36-43), and properly cleans up on unmount (lines 48-58). Gaps: only 2 event types (both on submission, none on publish/close/announce), no public participant rooms for live vote counts on poll page, data pushed is just invalidation signals (client refetches HTTP, no actual analytics payload over socket), no socket authentication (any connected client can join any owner room), no reconnect room re-join logic, no typed Socket.io events on client, module-level socket singleton rather than DI. Limited scope — only covers dashboard analytics refresh.
Code Quality & Project Structure6 / 10
Monorepo structure with frontend/backend separation, clean backend module pattern (auth/polls/public/users/health), consistent *.routes.ts/*.service.ts/*.schemas.ts naming, TypeScript with strict settings in backend, well-commented design decisions (server.ts:38-41 graceful shutdown explanation), thorough backend and frontend documentation with READMEs and example.env files. Gaps: pervasive `any` types in frontend (PollResponse type = {data:any}, all page states typed as any), significant code duplication (getSingleParam 2x, resolveAvatar 2x, genderLabel 2x with variation, DemoBar 2x verbatim, age calculation 2x), large monolithic page components (353-419 lines), zero test files across entire project (backend package.json: "test": "echo \"No tests configured\""), empty catch blocks (3x), hardcoded localhost URL and "VibePoll" branding inconsistency in email template (polls.service.ts:258,279,289), and no top-level repo README explaining monorepo setup.
19
R
MaYu
Raghav Verma · @raghav2103rv_78b4ca17
54
MaYu is a well-engineered full-stack polling platform with strong backend architecture (48 test cases, 26 endpoints, RS256 JWT auth with refresh rotation, 4 question types, thorough response validation with Redis dedup rollback), polished Japanese minimalist frontend design (custom CSS design system, Framer Motion animations, comprehensive state handling), and Kafka+Redis+Socket.io event infrastructure. However, it's held back by two critical issues: (1) the analytics dashboard renders hardcoded mock data for key metrics (timeline chart, audience mix, completion rate, response time) instead of actual analytics, and (2) the WebSocket real-time pipeline is disconnected from the analytics frontend — the backend correctly emits events but the dashboard never connects. Only the frontend is deployed (mayu-six.vercel.app); no production backend URL exists. Total: 54/80.
Authentication & Access Control7 / 10
Strong JWT auth with RS256 asymmetric keys, dual-token strategy (15-min access + 7-day refresh with httpOnly/secure/sameSite cookies), family-based refresh token reuse detection (token.service.ts:86-124), Google OAuth2 with manual PKCE S256 (google.service.ts:46-69), email verification + password reset flows, bcrypt 12 rounds, Helmet + CORS + Redis rate limiting on auth endpoints (5 req/15min). Frontend ProtectedRoute/PublicRoute gating, Axios 401 interceptor with automatic token refresh (axios.ts:21-49). requireAuth and optionalAuth middleware distinguish authenticated vs anonymous poll modes (polls.service.ts enforces requiresAuth field). Gaps: no CSRF protection, access token in localStorage (XSS surface), JTI blacklist mechanism exists but never populated (token.service.ts:45-46), no account lockout/failed-login tracking, no email normalization.
Poll Creation & Question Management7 / 15
Comprehensive poll builder with 4 question types (MULTIPLE_CHOICE, RATING, TEXT, RANKING) via CreatePoll.tsx (629 lines). React Hook Form + useFieldArray for dynamic questions/options with add/remove. Zod superRefine cross-field validation (option min 2 for MC/RANKING), mandatory/optional toggle per question, anonymous/authenticated mode toggles, expiry datetime picker. Server-side createPollSchema validates nested questions, min 1 question, min 2 options per MC/RANKING question (polls.schema.ts:11-12, 37-45). Poll status state machine (DRAFT→ACTIVE→EXPIRED→PUBLISHED) with guards. Auto-expiry cron every 60s (server.ts:41-46). Slug generation with nanoid. Live respondent preview in builder. Gaps: questions/options cannot be edited after creation — PATCH only updates poll metadata (polls.service.ts:182-199), no template/clone system.
Response Collection Flow8 / 15
Thorough multi-stage validation pipeline (responses.service.ts:23-178): poll existence+status check (DRAFT/EXPIRED/PUBLISHED rejected), auth requirement enforcement, Redis SET NX deduplication with 30-day TTL, per-question-type answer validation (MC option ownership, TEXT value presence, RATING range, RANKING completeness+order), mandatory question enforcement. Critical: dedup key rolled back via redis.del at EVERY validation failure path (lines 92-150), preventing poisoned state. Dual write path: Kafka async (202) or sync DB (201) with fallback. Frontend PublicPoll.tsx (461 lines) handles loading, error, not-found, expired, DRAFT, auth-required, success, submit-error states. Anonymous sessionToken via crypto.randomUUID() in localStorage. Client-side mandatory question validation with progress indicator. Transaction-based DB writes with Answer uniqueness constraint (schema @@unique([responseId, questionId])). Gaps: no response submission rate limiting, no server-side optionId belonging verification for the specific poll beyond service layer.
Analytics & Feedback Dashboard5 / 15
Backend analytics are well-implemented: Prisma groupBy for option counts, Redis cache with 30s TTL, cache invalidation on response write, public results endpoint with PUBLISHED guard + owner override, per-question computation of percentages/skipped counts/rating averages (analytics.service.ts:18-94, analytics.ts:44-164). However, the frontend AnalyticsDashboard.tsx (454 lines) has a critical flaw: the timeline AreaChart uses hardcoded static data (lines 55-62: `const timelineData = [{ label: '09:00', responses: 32 }, ...]`), the audience mix BarChart uses fabricated categories (lines 64-69: "Highly aligned"/"Mostly aligned"/etc), the completion rate is hardcoded "91%" (line 235), and median response time is hardcoded "01:42" (line 242). Only total responses and per-question breakdowns render real data. The "Export" button has no onClick handler. The "Responses syncing live" badge is purely cosmetic (CSS animation only, no WebSocket connection). These fabricated metrics undermine the analytics dashboard's credibility.
Frontend Experience7 / 10
Polished Japanese minimalist design system with 27 CSS custom properties mapped to Tailwind tokens (index.css lines 6-53), consistent typography (Noto Serif JP headings, Inter body), 4-level shadow system, custom utility classes (.paper-surface, .ink-wash, .focus-ring). Framer Motion animations on landing page, modals, and toasts with prefers-reduced-motion respect (index.css:160-169). Accessible components: useId for label association, role=switch+aria-checked on Toggle, keyboard navigation on QuestionListItem (CreatePoll.tsx:445-449). 10 routes with comprehensive state handling — PublicPoll alone handles 8 distinct states. Responsive layout: dark sidebar on desktop, mobile bottom nav at lg:hidden breakpoint. Copy-to-clipboard with toast feedback. Gaps: Dashboard has no error rendering on fetch failure (silently shows empty state), analytics charts render hardcoded mock data, dead UI controls (Filter view button, 3 nav items labeled "Soon"), Export button has no handler, no confirmation dialogs for destructive actions (except publish modal).
Backend Architecture & API Design8 / 15
Excellent layered MVC architecture with consistent module structure (controller/service/schema/routes) across all 4 domain modules. 26 REST endpoints covering full CRUD + status transitions + auth + analytics. 8 Prisma models with proper relations, composite unique constraints (pollId+order, responseId+questionId, pollId+respondentId), cascade deletes, and strategic indexes. 19 Zod validation schemas covering all request bodies/params/queries. 6 middleware functions: requireAuth (Bearer JWT verification), optionalAuth, validate (Zod safeParse), rateLimiter (Redis INCR+PEXPIRE), errorHandler (AppError routing), requestId. Helmet + CORS + cookie-parser in app.ts. 48 test cases across 7 files (4 integration suites + 3 unit suites) with test helpers (factories, db cleaner, supertest wrapper). Redis used for rate limiting, auth blacklist, dedup, analytics cache, OAuth state. KafkaJS with idempotent producer and conditional enable. Winston logger with structured metadata. Zod env validation crashes on startup for missing config. Gaps: uses PostgreSQL/Prisma not MongoDB as spec suggests, JTI blacklist mechanism never populated, optionalAuth.ts is 7-line re-export, no request body size limits, 2/3 Kafka topics have no consumers.
Real-Time Updates Using WebSockets5 / 10
Backend WebSocket infrastructure is well-designed: 2 namespaces (/polls public, /analytics JWT-authenticated via socket.handshake.auth.token), Redis adapter for horizontal scaling (socket.server.ts:30-32), room-based pub/sub with join/leave handlers on analytics namespace. Kafka consumer (responseConsumer.ts:28-55) reads from mayu.response.submitted, writes to DB, and emits 3 events (analytics:update, response:count, poll:status) to appropriate rooms. Frontend socket hooks have proper React cleanup (useSocket.ts: socket.off/socket.disconnect in useEffect returns). However, the analytics frontend is completely disconnected from real-time: AnalyticsDashboard.tsx never imports or uses useAnalyticsSocket — it fetches only via HTTP useQuery. The useAnalyticsSocket hook is exported but has zero usages. The "Responses syncing live" badge is cosmetic CSS only. Hardcoded poll:status value (always 'ACTIVE' in responseConsumer.ts:73). Orphaned poll:expired client listener (server never emits it). No leave handler on /polls namespace. disconnectSockets() exported but never called. 2/3 Kafka topics have no consumers. No debouncing on socket emissions.
Code Quality & Project Structure7 / 10
Clean monorepo with backend/frontend separation and well-organized module structure. Full TypeScript throughout with strict configs and proper type narrowing in catch blocks. Consistent naming conventions and JSDoc on most backend functions. Shared utility patterns (pagination, slugify, crypto, logger) reduce duplication. Custom AppError class with 14 typed error codes used uniformly. 1194 lines of test code with factory functions and DB cleanup helper. Frontend uses consistent component patterns (forwardRef with displayName on all UI primitives). Gaps: tooltipStyle object duplicated identically across 3 files (Landing.tsx:676-681, Dashboard.tsx:238-243, AnalyticsDashboard.tsx:368-373), question result rendering duplicated across ResultsPage and AnalyticsDashboard, Google OAuth button duplicated in SignIn/SignUp. Dead code: disconnectSockets() (socket.ts:35, never called), optionalAuth.ts (7 lines re-exporting from auth.ts), useAnalyticsSocket export never imported, uninteracted UI controls (Filter view, 3 nav items). No frontend tests, no CI/CD configuration. Access token stored in localStorage. Frontend .env committed to repo.
20
S
PollVault
Sourav Kumar · @souravkumarverma56_d3b68210
54
PollVault is a well-constructed full-stack polling platform with strong auth (refresh token versioning, httpOnly cookies), a polished React frontend (dark mode, 7-section landing page, comprehensive state coverage), and a clean Express/MongoDB backend (MVC layers, TTL indexes, rate limiting). Socket.IO integration with room-based broadcasting and reconnect handling is solid. The major architectural decision to require authentication for ALL poll responses deviates from the spec's "anonymous or authenticated" requirement — "anonymous" mode only hides identity but doesn't allow unauthenticated voting. Code duplication (7x ownership checks, duplicated aggregation logic) and lack of TypeScript hold back engineering quality. Deployed and accessible at https://poll-vault.onrender.com/. Total score: 54/80.
Authentication & Access Control7 / 10
Robust dual-token JWT auth: 15-min access + 30-day refresh tokens with version-based rotation for session revocation (authController.js:84-88, 113-121), httpOnly/secure/sameSite cookie storage (generateToken.js:18-34). Registration gate via SystemSettings.allowRegistrations (authController.js:13-18). Client AuthContext with auto-session validation, Axios 401 interceptor with request queuing for silent refresh (axios.js:24-71), ProtectedRoute with admin gate (App.jsx:21-40), password strength meter on register (Register.jsx:12-163). Gaps: no email verification, forgot/reset password is a placeholder only, and the "anonymous" response mode still requires authentication — it hides identity from creators but blocks unauthenticated users entirely.
Poll Creation & Question Management7 / 15
3-step poll creation form (CreatePoll.jsx:31-244) with dynamic QuestionBuilder component supporting single/multiple/text types, per-question required toggle via Switch, option add/remove with min-2 enforcement, and type-switching auto-clear (QuestionBuilder.jsx:28-36, 15-195). Expiry presets (1d/7d/30d/3m/1y) using date-fns. Server-side validation via express-validator: title max 200, description max 1000, responseMode enum, expiresAt future, questions min 1 with per-question validation (validate.js:46-86). EditPoll page mirrors create flow, blocks editing published polls (pollController.js:240-242), re-opens if expiry extended (pollController.js:253). shareId auto-generated with nanoid(10). Gaps: no draft status, question _ids regenerate on edit potentially breaking analytics references, no form library (imperative useState throughout).
Response Collection Flow6 / 15
Multi-layered validation: client-side validation with per-type checks and scroll-to-first-error (ResponseForm.jsx:33-61), server-side gates for published (403), closed/expired (410), required-question checking with per-type enforcement, and option-value validation preventing cross-question injection (pollController.js:397-458). Duplicate prevention via active findOne check plus DB sparse unique compound index {poll, user} with partialFilterExpression (Response.js:48-52). XSS sanitization on text answers (pollController.js:431-432). 7 distinct UI states handled (PublicRespond.jsx:60-204). 10/min rate limit on submissions. Critical gap: ALL respondents must be logged in — the `protect` middleware on POST /api/polls/:pollId/responses (pollRoutes.js:43) means there's no true anonymous/public response flow, contradicting the spec's "anonymous or authenticated" requirement. The "anonymous" mode only hides identity from the creator.
Analytics & Feedback Dashboard7 / 15
MongoDB $facet aggregation pipeline in aggregateAnalytics (pollController.js:10-92) computes single-choice counts, multiple-choice counts (with $unwind), text answer grouping, and per-question response counts. LiveAnalytics component (267 lines) renders total responses card with "Live" badge, per-question Bar + Doughnut chart tabs via Chart.js with 8 theme-aware colors, text answer lists with XSS-safe sanitize(), and named-poll respondent name badges (LiveAnalytics.jsx:246-257). Publish/unpublish flow locks/unlocks responses and exposes results publicly (pollController.js:310-353). Dashboard summary stats: total/active polls + total responses (aggregation with $lookup, Dashboard.jsx:96-125). Gaps: no time-series data, no CSV/export, no individual response viewing beyond roll-call names, no peak activity metrics, respondentMap computed with JS loop over all responses (N+1 pattern avoided for counts but not for respondent names).
Frontend Experience7 / 10
10 routes with ProtectedRoute/admin gate separation (App.jsx:42-58), dark/light mode via next-themes with system default, responsive mobile navbar with animated hamburger menu (Navbar.jsx:150-233). Comprehensive state coverage: loading skeletons (Dashboard 6 cards, PublicRespond, ProtectedRoute spinner), error states with retry buttons, empty states with differentiated messaging (no polls vs no filter matches with CTAs). Landing page with 7 sections (Hero, PollShowcase, SocialProof, Features, Testimonials, FAQ, FinalCTA, Footer) and Framer Motion scroll-triggered animations. Polish details: password strength meter (5-level scoring), character counters with 90% warnings, estimated completion time, named-poll warning badge, QR code generation via qrcode.react with copy-to-clipboard, confirmation dialogs for publish/close/delete. Gaps: no form library for poll creation (imperative useState hooks), ResponseMode UI duplicated between CreatePoll and EditPoll, Chart.js dual-registration issue.
Backend Architecture & API Design7 / 15
Clean MVC layered architecture: controllers (591-line pollController.js with 11 endpoints), thin route wiring (pollRoutes.js:45 lines), 4 Mongoose models with schema validation and indexes. Strong DB design: Response model has sparse unique compound index {poll, user} for dedup plus TTL index for 30-day auto-cleanup (Response.js:48-56). Security: Helmet with CSP directives (server.js:38-49), CORS with credential support, 3 rate limiters (auth/submission/general, rateLimiter.js). Centralized error handler for ValidationError/CastError/duplicate key/JWT errors with production message redaction (errorHandler.js:1-60). 5 test suites with 16 tests using mongodb-memory-server + supertest. Gaps: creator ownership check repeated 7 times in pollController.js, response count aggregation duplicated between pollController and adminController, no validation middleware on PATCH /api/auth/me or admin routes, no text index for search (unindexed $regex), missing compound index on {creator, createdAt}, low test coverage relative to codebase size.
Real-Time Updates Using WebSockets7 / 10
Socket.IO server with cookie-based JWT auth middleware that allows unauthenticated connections gracefully (socketHandler.js:31-56). Room-based pattern: authenticated creators join poll_${id} room with authorization check (creator match verified against DB), auto-join user_${userId} personal room. Server emits response:new with full analytics payload to poll room on each submission (pollController.js:468-474), and dashboard:update to creator's personal room (pollController.js:476-481). Client SocketProvider: singleton with exponential backoff (1s→30s, 10 attempts), WebSocket-first transport, DEV direct-connect bypass (SocketContext.jsx:15-58). LiveAnalytics component: real-time state merging of incoming questionStats into existing data via setState updater, reconnect room re-join on every 'connect' event (joinedRef reset), socket error recovery (LiveAnalytics.jsx:65-129). Dashboard listens for dashboard:update to increment counters (Dashboard.jsx:47-69). Gaps: no events for publish/close/delete, no socket integration on public respond page, no debouncing on events, analytics pushed as full payload each time.
Code Quality & Project Structure6 / 10
Clean monorepo with client/server separation and root orchestration. Consistent naming: camelCase for server modules, PascalCase for React components, kebab-case for shadcn/ui primitives. JSDoc comments on key functions. Comprehensive README with architecture diagram, setup instructions, Dockerfile, and render.yaml. No commented-out code or TODO markers. However: no TypeScript (zero compile-time type safety), 7x repeated creator ownership check in pollController.js, response count aggregation duplicated across controllers, ResponseMode UI fully duplicated between CreatePoll/EditPoll pages, two independent CHART_COLORS arrays, trial font committed despite .gitignore exclusion, ThemeProvider double-wrapped in both main.jsx and App.jsx, low test coverage (16 tests for a codebase with 4 models and 3 controllers), stale reference in migration script (normalizeResponseMode helper no longer exists).
21
PulseBoard - Fast, Lightweight, Open-Source Polling
Arman · @armanthakur200814_797a04fb
54
PulseBoard is a well-implemented full-stack polling platform with Clerk authentication, dynamic quiz builder, thorough response validation, analytics dashboard, and Socket.IO real-time updates. The frontend is deployed and accessible at https://pulse-board-seven.vercel.app, but the backend has no deployed URL (the client VITE_API_URL defaults to localhost:3000), so the full application cannot be tested in production. Strengths: clean layered architecture with Zod validation, MongoDB transactions, proper indexing, defense-in-depth validation on submissions, consistent error handling with typed state machines, polished neo-brutalist UI handling all edge case states, and real-time analytics via Socket.IO with debounced silent refetch. Key gaps: no backend deployment, no automated tests, questions cannot be edited after creation (only metadata updates), code duplication in a few places, the serviceCode error pattern uses untyped assertions, and the Socket.IO scope is limited to analytics pages.
Authentication & Access Control7 / 10
Solid Clerk-based authentication with well-implemented access control: anonymous vs authenticated poll modes enforced at both client and server (quiz.controller.ts:78-91, quiz.tsx:130-145), creator-only gates on update/delete (quiz.service.ts:349-375), unpublished analytics restricted to creators (quiz.controller.ts:205-217). Auth sync via GET /api/auth (auth.controller.ts). Login gates on quiz and analytics pages with programmatic sign-in flow. Missing: no backend server deployed so auth can't be verified live; entirely Clerk-dependent with no custom registration/login.
Poll Creation & Question Management6 / 15
Working quiz builder at /create with dynamic questions and options (create.tsx:146-196), mandatory/optional toggle per question, status selection (draft/active/expired), anonymous/authenticated toggle, expiry date picker. Server-side Zod validation enforces min 1 question and min 2 options per question (quiz.dto.ts:5-12, 38-44). Slug auto-generation with collision avoidance (quiz.service.ts:68-92), QR code in success modal (create.tsx:42-117). Significant gap: questions cannot be edited after creation — the PATCH endpoint only updates poll metadata (title, status, isPublished, isAnonymousPoll, expiresAt) but not questions/options (quiz.service.ts:339-365, quiz.dto.ts:82-92).
Response Collection Flow8 / 15
Thorough response collection with defense-in-depth: server validates required questions (quiz.service.ts:199-204), option index ranges (quiz.service.ts:215-220), question existence (quiz.service.ts:207-213), and quiz state gates (draft/expired/published blocking — quiz.service.ts:185-193). Client-side validation for required questions and empty submissions (quiz.tsx:104-121). Duplicate vote prevention via MongoDB sparse unique compound index on {pollId, voterId} (quiz.schema.ts:144-147) plus localStorage tracking (quiz.tsx:23-33). Transaction-based submission (quiz.service.ts:223-243). Anonymous voters get crypto.randomUUID() stored in localStorage; authenticated voters identified via Clerk userId (quiz.controller.ts:78-91). Differentiated HTTP status codes (400/401/403/404/409).
Analytics & Feedback Dashboard7 / 15
Analytics page shows total responses, per-question option breakdowns with counts and percentages, top-pick identification (analytics.tsx:596-600), stat cards (responses, questions, avg/Q, created date). MongoDB aggregation pipeline for efficient counting (quiz.service.ts:263-277). Creator-only ManagePollPanel (analytics.tsx:721-966) for status changes, expiry management, toggling anonymous mode, publishing results, and deleting polls. Published polls make analytics publicly shareable (quiz.controller.ts:204-205). Animated CSS progress bars with double-RAF technique (analytics.tsx:86-89). Empty state UI handled. Missing: no trend data, no export functionality, no individual response viewing.
Frontend Experience7 / 10
Consistent neo-brutalist design language throughout (heavy borders, hard shadows, bold typography) with Tailwind CSS v4. All five routes handle loading, error, login-required, forbidden, and empty states explicitly. Responsive layout from mobile to desktop. Animated progress bars on analytics (700ms CSS transition), QR code in creation success modal, copy-to-clipboard with "Copied!" feedback (1800ms timeout), status badges colored by state. TanStack Router provides SPA routing with scroll restoration. Missing: no offline/PWA support, form UX on create page is functional but basic (individual useState hooks rather than form library), no confirmation dialogs for destructive actions beyond the delete confirmation flow.
Backend Architecture & API Design7 / 15
Clean layered architecture with routes→controllers→services→schemas→DTOs separation. All inputs validated via Zod schemas with safeParse (quiz.dto.ts, 11 schemas). MongoDB transactions for quiz creation, submission, and cascading deletion (3 transaction sites). Proper indexing strategy including unique slug, compound {pollId, voterId} sparse unique, and indexed foreign keys (quiz.schema.ts). Clerk middleware for auth, CORS configured for both Express and Socket.IO. RESTful API design with consistent resource naming. Issues: serviceCode pattern uses unsafe type assertions (quiz.service.ts:173-174), poll fetched twice in getQuizBySlug controller (quiz.controller.ts:146,164), no rate limiting, no request body size limits, MONGODB_URI uses non-null assertion (connectDB.ts:5), double error handling for DB connection (connectDB.ts:10 + app.ts:11-13).
Real-Time Updates Using WebSockets6 / 10
Socket.IO server integrated with Express HTTP server (server/src/index.ts:11-17). Room-based pattern: clients join/leave `analytics:{slug}` rooms (analyticsRealtime.ts:14-47). Server emits analytics:changed on response submission, poll update, and poll deletion (quiz.controller.ts:94,257,294). Client connects, joins room on mount, listens with 800ms debounce, and triggers silent refetch (analytics.tsx:113-151). Cleanup properly closes socket and leaves room on unmount. The implementation uses event-driven invalidation + refetch rather than server-pushed analytics state (acknowledged in README). Limited scope: only covers analytics pages, not quiz participation pages for live response counts.
Code Quality & Project Structure6 / 10
Monorepo with separate client/ and server/ packages plus root tooling. TypeScript strict mode with noUncheckedIndexedAccess across all tsconfigs. Zero `any` in hand-written code, proper `error: unknown` with narrowing in all catch blocks. Consistent stale-closure prevention via `cancelled` flag in all useEffect hooks. Discriminated union types for state machines (FetchState, SubmitState, SyncState). Husky pre-commit hook with ESLint + Prettier. Issues: apiOrigin() duplicated in lib/api.ts and routes/home.tsx; statusStyles duplicated with slight inconsistencies in home.tsx and quiz.tsx; ACCENTS/OPTION_ACCENTS arrays duplicated across 3 files; no automated tests at all; root index.ts is dead code; analytics.tsx at 1004 lines and quiz.tsx at 627 lines without sub-file component extraction; no README.md-based .env.example for server.
22
PulseBoard: Live Polls For Feedback
Omkar Gupta · @omkarg419_94f8d4a6
54
PulseBoard is a solid MERN-stack polling platform with Firebase authentication, Socket.io real-time updates, and a polished dark-themed interface. The backend architecture is clean with proper service-controller-route separation, good error handling, and thorough validation. The frontend has good UX patterns with loading/empty/error states and inline form validation. Key gaps: no true anonymous/unauthenticated response submission (all responses require Firebase auth), only Google OAuth with no email/password registration, no deployment URL is provided, socket connections lack authentication, missing features include pagination, rate limiting, and tests. Overall a well-structured project that covers the core requirements with some significant limitations in the auth/anonymous response flow.
Authentication & Access Control6 / 10
Google Firebase Auth via GoogleAuthProvider works for sign-in (Login.jsx, AuthContext.jsx) with token verification in auth.middleware.js. ProtectedRoute wraps dashboard routes, and public poll is accessible unauthenticated. However, there is no email/password registration — only Google OAuth — and the README claims "registration" while offering none. The "anonymous" poll mode still requires authentication for response submission (response.routes.js applies authMiddleware to all routes), so there is no truly unauthenticated response flow. This significantly limits the auth scope.
Poll Creation & Question Management7 / 15
CreatePoll.jsx (238 lines) with PollForm, QuestionCard, and OptionInput implements full dynamic form management: add/remove questions, add/remove options (minimum 2 enforced), mandatory/optional toggle per question, datetime-local expiry picker, and anonymous toggle. Client-side validation checks titles, future dates, and option completeness. Server-side validation in poll.validation.js (144 lines) is thorough with normalizeQuestions/normalizeOptions/parseBoolean and aggregated error messages. The payload mapping strips frontend client-side UUIDs for backend submission. However, there is no edit-poll UI despite having updatePoll service and endpoint, and only single-choice is supported (as specified).
Response Collection Flow7 / 15
PublicPoll.jsx (385 lines) renders the response form with radio buttons, validates required questions with inline error feedback, and constructs a proper answers payload. Server-side validateAnswers in response.service.js (lines 33-91) checks required question answers, validates selected options exist in the poll, prevents duplicate question answers, and returns normalized answers. Expiry enforcement runs via markExpiredIfNeeded on both fetch and submission. Duplicate response prevention uses both a unique compound index (response.model.js line 57) and an explicit findOne check. However, ALL responses require Firebase auth — there is no path for unauthenticated submission, contradicting the "anonymous responses" feature claim.
Analytics & Feedback Dashboard7 / 15
analytics.service.js (124 lines) computes question summaries with per-option counts and percentages via buildQuestionSummaries, and participation insights (total/anonymous/identified responses, completion rate, average answers, anonymous rate) via buildParticipationInsights. The Analytics.jsx page renders four StatsCards (total responses, anonymous count, completion rate, average answers), a Recharts bar chart for the first question via ResponseChart.jsx, and per-question AnalyticsCards with option cards showing votes and percentages. Published poll results (PublicPoll.jsx lines 196-277) display visual percentage bars. However, the chart only visualizes one question (responseChart shows summary[0]), and there are no time-series trends or export capabilities.
Frontend Experience7 / 10
Dark-themed UI built with Tailwind CSS featuring responsive layouts, two layout variants (MainLayout with Navbar for public routes, DashboardLayout with Sidebar for authenticated routes), and consistent component patterns. Loading states (Loader.jsx with spinner), empty states (EmptyState.jsx with action links), error banners, and react-hot-toast notifications are used throughout. Form UX includes inline validation errors, toggle switches for boolean fields, and a datetime-local picker. The published results view shows visual percentage bars and option vote counts. Router handles 404 via NotFound.jsx. However, there are no confirmation dialogs for destructive actions, no pagination for large poll lists, and no accessibility attributes.
Backend Architecture & API Design7 / 15
Clean modular architecture with service-controller-route separation across auth, poll, response, and analytics modules. Express 5 with CORS, JSON body parsing, and centralized error middleware (error.middleware.js). Firebase Admin SDK for token verification in auth.middleware.js. Mongoose schemas with embedded sub-documents (questions contain options), proper indexes (unique compound on Response, creator+createdAt on Poll), and built-in validators. Custom ApiError class with factory methods (badRequest, unauthorized, forbidden, notFound, conflict, unprocessableEntity), ApiResponse class with static helpers, and asyncHandler wrapper. Docker Compose for MongoDB. However, there is no rate limiting, no request logging, no pagination on list endpoints, no input sanitization beyond validation, and parseBoolean/normalizeBoolean are duplicated across poll.validation.js and response.service.js.
Real-Time Updates Using WebSockets6 / 10
Socket.io server is created in server.js (lines 17-38) with join-poll/leave-poll room management. The response.service.js emits response-update and analytics-update events to poll rooms after each submission (lines 183-188), and poll.service.js emits poll-published on publish (line 146). The client useSocket.js hook (97 lines) manages connection, room join/leave, and event handlers with proper cleanup (off/detach/disconnect). The Analytics.jsx page receives real-time analytics updates via onAnalyticsUpdate callback, and PublicPoll.jsx refreshes on analytics/poll-published events. However, socket connections have no authentication (any client can join any room), there is no reconnection strategy beyond Socket.io defaults, and updates are sent only to the poll room (not individual authenticated client verification).
Code Quality & Project Structure7 / 10
Well-organized project with clear separation: server has modules/{auth,poll,response,analytics} with model/service/controller/routes per module, common/{config,middleware,utils,constants} for shared code. Client has pages/components/hooks/services/routes/layouts/utils directories. Consistent naming conventions (kebab-case files, camelCase functions, PascalCase components). Reusable common utilities (ApiError, ApiResponse, asyncHandler). Client service layer (api.js with axios interceptor) cleanly separates API calls. However, client/README.md is the untouched Vite template boilerplate (not project-specific), there is code duplication (parseBoolean/normalizeBoolean in two files), inconsistent indentation in Navbar.jsx (lines 20-21 mix tabs/spaces), and no tests exist anywhere in the project. No ESLint or Prettier config at the project root level.
23
P
pixpolls
pixpolls · @tr6056538_9b8a97e6
54
Pixpolls is a solid, full-stack MERN polling application with both frontend (Vercel) and backend (Railway) deployed and working. The codebase demonstrates clean architectural patterns across all layers — domain-separated backend modules with proper model/service/controller/route/dto separation, and a frontend with well-organized pages/components/hooks/context. Strengths: custom auth with bcryptjs JWT and well-designed protect/optionalProtect middleware, thorough server-side response validation with clever partial unique index for duplicate prevention (allowing multiple anonymous responses while enforcing one per authenticated user), 3-type Socket.IO real-time events with well-designed usePollRoom hook using refs for stale closure avoidance, cohesive CSS brand design system (898 lines with custom properties), and consistent error/loading/empty state handling across all pages. Key weaknesses: critical production security omissions (no Helmet, no rate limiting, REST CORS is origin:true), questions cannot be edited after poll creation, JWT stored in localStorage, zero test coverage, analytics is basic (no trends, no CSV/export, no time-series data), and no TypeScript. The overall architecture is well-structured but the feature depth and production hardening are at a solid intermediate level. Deployment: Frontend (Vercel) and Backend (Railway) both verified working. Total: ~54/80.
Authentication & Access Control7 / 10
Solid custom auth implementation: register/login with bcryptjs (12 rounds), JWT token signing with configurable expiry, protect/optionalProtect middleware (auth.middleware.js:1-53), ProtectedRoute component with redirect-back state (ProtectedRoute.jsx), AuthProvider with localStorage persistence and lazy hydration (AuthProvider.jsx:20-48). Anonymous vs authenticated poll modes enforced server-side via optionalProtect + service-layer checks (response.service.js:58-60, getPollById visibility gates at poll.service.js:29-32). Password select:false on User model (auth.model.js:30). Gaps: no Helmet, no rate limiting on auth endpoints, JWT in localStorage (XSS-vulnerable), no email verification or password reset, CORS is origin:true on REST (permissive).
Poll Creation & Question Management7 / 15
Functioning poll creation form at CreatePollPage.jsx (291 lines) with dynamic question/option add/remove, is_required toggle per question, poll-level settings (is_private, allow_anonymous, is_published, expires_at datetime-local). Joi validation via CreatePollDto (create-poll.dto.js:1-25): title 3-100 chars, min 1 question, min 2 options per question, expires_at > 'now'. Client-side validateClient() function (CreatePollPage.jsx:84-101) checks title/question/option lengths. Owner settings panel on PollPage (PollPage.jsx:353-469) allows updating metadata via PATCH. Key gap: questions/options cannot be edited after creation — UpdatePollDto (update-poll.dto.js:1-15) only accepts metadata fields (title, booleans, expires_at), no questions array. No slug/permalink or QR code for sharing.
Response Collection Flow7 / 15
Thorough response validation: validateAnswersAgainstPoll (response.service.js:11-40) checks question exists, option exists in question, one-answer-per-question enforcement, required question enforcement. Poll state gates: is_published check, results_published check, expiry check via isPollExpired (response.service.js:7-9), anonymous/auth enforcement (service.js:58-60). Elegant duplicate prevention: MongoDB compound unique index with partialFilterExpression on {poll_id, user_id} where user_id is objectId (response.model.js:37-43), allowing unlimited anonymous responses while enforcing one-per-authenticated-user, plus 11000 error catch at service.js:77-78. Client-side validation in onVote (PollPage.jsx:194-229) mirrors server checks. Radio button selection UI with visual feedback. Gaps: no rate limiting on submission, no IP-based dedup for anonymous users (infinite anonymous submissions possible).
Analytics & Feedback Dashboard6 / 15
Functional analytics: getPollResults (response.service.js:84-145) computes totalResponses via countDocuments, per-question/per-option vote counts via MongoDB aggregation pipeline ($match→$unwind→$group→$group), participation stats (question_count, required/optional counts, is_closed, expired flags). Frontend PollResultsView component (109 lines) renders stat cards (total responses, questions, required, optional), per-question bar charts with vote counts and percentages via CSS stat-bar fills. ResultsPage.jsx (197 lines) provides dedicated analytics dashboard with publish button. normalizePollResults/buildCountsByQuestion utilities for data normalization. Gaps: no CSV/export, no individual response viewing, no trend/time-series data (response velocity, peak activity), no pie charts (pure CSS bars, no chart library), no authenticated vs anonymous breakdown in analytics, aggregation doesn't flag winning options explicitly.
Frontend Experience7 / 10
Distinctive, cohesive brand design: 898-line index.css with CSS custom properties (pretty-blue, party-gold palette), Fredoka display font, rotated hero burst text (index.css:314-346), "P" background pattern (inline SVG), sticky header with nav-pill (Layout.jsx:74 lines). All 8 routes (App.jsx:26-43) handle loading (spinner-text), error (banner--error), empty (empty-state), and auth-redirect states. Responsive layout with fluid typography (clamp()), flexible grid. Interactive elements: radio choices with :has(input:checked) highlighting, hero-bar input with action button, copy-to-clipboard with 2-second "Copied!" feedback, live-connection pill on analytics. Transition animations on buttons and bar fills. Gaps: no form library (individual useState), no chart library, no data caching (refetches on navigation), PollPage.jsx at 545 lines is too large with 20+ state variables, no dark mode, no confirmation dialogs except window.confirm for delete.
Backend Architecture & API Design7 / 15
Clean layered architecture: modules/auth, modules/poll, modules/response each with model/service/controller/routes/dto. Custom ApiError class (api-error.js:1-29) with 5 status-code factory methods (400/401/403/404/409). ApiResponse class with ok/created/noContent helpers. BaseDto + validate middleware pipeline using Joi with abortEarly:false and stripUnknown:true (base.dto.js:1-21). protect/optionalProtect middleware with clean error forwarding (auth.middleware.js:1-53). Socket.IO CORS uses isOriginAllowed() with vercel preview support (cors-origins.js:1-27). Frontend apiFetch wrapper with Bearer token and error normalization (client.js:22-63). Well-designed response model with partial unique index for dedup. Gaps: no Helmet (zero security headers), no rate limiting on any endpoint, REST CORS uses origin:true (permissive) while Socket.IO validates origins — inconsistency, no request body size limits, Mongoose ValidationError not caught in global handler (returns 500), no transactions, no TTL index on expires_at, no pagination, no graceful shutdown.
Real-Time Updates Using WebSockets7 / 10
Solid Socket.IO implementation: server attached to HTTP server (server.js:13), room-based pattern with poll:join/poll:leave events (socket-hub.js:1-57), validates pollId as ObjectId before room join (line 33-35), 3 server-emitted events: poll:responses, poll:updated, poll:deleted via emitToPoll helper. Client-side usePollRoom hook (usePollRoom.js:1-44) properly uses useLayoutEffect + refs to avoid stale closures, handles reconnect (joins room on socket connect), leaves room on unmount, registers all 3 event handlers. getPollSocket singleton (pollSocket.js:1-34) with env-based URL resolution and WebSocket+long-polling transports. Live connection indicator pill on analytics. ownerActivityHint banner for real-time notifications to owner. realtimeTick counter pattern triggers HTTP refetches on events. Gaps: pure invalidation signal (no data pushed over socket, clients refetch HTTP), no socket authentication, no debouncing/batching, no typed events, no socket integration on voting page for live response counts.
Code Quality & Project Structure6 / 10
Clean monorepo (backend/frontend) with consistent module structure and naming. Backend follows model-service-controller-route-dto per domain. Frontend separates pages/components/hooks/context/api/utils/socket. .env.example files in both packages. ESLint configured in frontend. Good React patterns: useEffect cleanup with AbortController everywhere, useRef for mutable refs, useCallback/useMemo for memoization, ref-based stale-closure avoidance in usePollRoom, loadKeyRef pattern for navigation-change detection. BaseDto validation pattern is clean and extensible. Gaps: no TypeScript (all plain JS), zero tests (backend: "Error: no test specified"), PollPage.jsx at 545 lines needs component extraction, generateResetToken() dead code in jwt.utils.js, AbortController pattern repeated verbatim in 4 files, error handling pattern (e instanceof ApiError ? e.message : 'Failed to ...') repeated 8+ times, copy-to-clipboard with feedback timeout duplicated in PollPage and ResultsPage, @types/react devDependencies unused, no PropTypes for components, REST/Socket.IO CORS inconsistency.
24
S
PollNode
Sameer Bhagtani · @sameerbhagtani
53
PollNode is a well-built full-stack polling platform with Clerk authentication, Socket.IO real-time updates, and a polished dark-themed UI. The backend features clean modular architecture with thorough Zod validation, proper Mongoose schema design with strategic indexes, and good error handling via ApiError/ApiResponse classes. The frontend handles all states (loading, error, empty, auth-required, expired, published) gracefully across 6 routes. Response collection is particularly well-implemented with crypto-based anonymous tracking, partial unique indexes for duplicate prevention, and httpOnly cookies. Key weaknesses: no poll editing or deletion endpoints, no security middleware (Helmet, rate limiting), zero automated tests, no CSV export for analytics, and some code quality issues (duplicated schemas, production console.logs, silent error swallowing in emitter). Total: 53/100.
Authentication & Access Control7 / 10
Clerk-based auth with `@clerk/express` middleware (app.ts:4,27), user sync via GET /api/auth/me auto-creating MongoDB records (auth/controller.ts:20-27), requireAuth middleware extracting Clerk userId and attaching req.user (requireAuth.ts:7-28), ProtectedRoute component with sign-in prompt (App.tsx:43-73), Clerk token forwarding via Axios interceptor (api.ts:18-33), anonymous vs authenticated poll modes enforced server-side (responses/controller.ts:57-61), and poll access control with owner/published/anonymous/auth checks (polls/controller.ts:63-75). Gaps: no rate limiting, no Helmet/CSRF, entirely Clerk-dependent with no custom registration/password-reset flows.
Poll Creation & Question Management7 / 15
React Hook Form with Zod resolver for poll creation (CreatePollPage.tsx, 370 lines), useFieldArray for dynamic questions (lines 92-99) and nested useFieldArray for options per question (lines 274-281), per-question isRequired toggle (lines 308-318), anonymous/authenticated access dropdown (lines 179-189), datetime-local expiry picker (lines 196-199), server-side Zod validation with min 2 options, min 1 question, future expiry, at least one required question (polls/schemas.ts:53-128). Gaps: no poll editing (PATCH endpoint), no slug/permalink, no QR code, no draft status.
Response Collection Flow8 / 15
Server-side validatePollAnswers (responses/service.ts:10-49) checks question existence, option existence within question, and required question coverage. Expired and published poll gates (responses/controller.ts:37-43). Duplicate prevention via partial unique MongoDB indexes on {poll, respondent} and {poll, anonymousTokenHash} (responses/model.ts:50-68). Crypto-based anonymous tokens: randomBytes(32) with SHA-256 hashing stored in httpOnly/sameSite cookies (responses/controller.ts:65-83, utils.ts:3-9). User re-attachment to anonymous responses (controller.ts:112-117). Client-side validation via react-hook-form Controller rules (PublicPollPage.tsx:301-304) and empty-answer filtering (lines 234-239). Comprehensive UI states: loading, error, sign-in required, expired, published, submitted, requiresAuth. Minor gap: no MongoDB transaction for race-condition-safe duplicate checking.
Analytics & Feedback Dashboard7 / 15
computeAnalytics (analytics/service.ts:6-95) computes per-question option counts/percentages, total/authenticated/anonymous response breakdowns, participation percentages. Recharts BarChart visualization (AnalyticsDashboard.tsx:283-335) with colored bars, stat cards for total responses/questions/anonymous ratio (lines 183-244), per-question progress bars (lines 336-365), required/optional badges (lines 256-275). Publish workflow: PATCH /api/polls/:pollId/publish (polls/controller.ts:126-156), publish button on dashboard PollCard (PollCard.tsx:91-100), public results view via PublicPollPage redirecting to AnalyticsDashboard (PublicPollPage.tsx:176-184). Analytics accessible by owner or public when published (analytics/controller.ts:43-46). Gaps: no CSV/JSON export, no time-series data, no individual response viewing, brute-force O(n*m) computation instead of aggregation pipeline.
Frontend Experience6 / 10
6 routes (Home, Dashboard, Create, Public Poll, Analytics, 404) via React Router (App.tsx:82-121). Dark-first theme with full CSS variable light/dark system (index.css:38-93). Sticky header with gradient branding (Header.tsx:6-51). Landing page with hero section, gradient text, and feature steps (HomePage.tsx:15-167). Skeleton loading on dashboard (DashboardPage.tsx:63-70). Toasts via Sonner throughout. Responsive grid layouts. Copy-to-clipboard on poll cards (PollCard.tsx:40-43). Status badges (active/expired/published). Empty states for dashboard (DashboardPage.tsx:100-113). All error states handled gracefully. Gaps: no dark/light mode toggle (dark-only), no confirmation dialogs, no mobile hamburger menu, no option selection animations, no drag-drop question ordering.
Backend Architecture & API Design6 / 15
Modular architecture: auth/, polls/, responses/, analytics/ each with controller/routes/schemas (where applicable). ApiError class with static factories for 400/401/403/404/409/500 (ApiError.ts:1-34). ApiResponse with success/created/noContent (ApiResponse.ts:1-27). Zod validation on all inputs with detailed custom error messages (polls/schemas.ts:53-128, responses/schemas.ts:17-49). Centralized env validation via Zod (env.ts:4-23). Mongoose schemas with strategic indexes: creator+createdAt, expiresAt, poll+order unique, partial unique indexes for duplicate prevention. TypeScript strict mode with noUncheckedIndexedAccess and exactOptionalPropertyTypes (server/tsconfig.json:21-22). Express type augmentation for req.user/req.validated (types/express.d.ts). Docker compose for local dev. Gaps: no Helmet/rate limiting, no request body size limits, no poll deletion endpoint, no poll editing, emitter silently swallows errors with empty catch blocks (emitter.ts:7,17), req.validated uses unsafe type assertions, no pagination.
Real-Time Updates Using WebSockets6 / 10
Socket.IO integrated with Express HTTP server (index.ts:13-14), room-based pattern with clients joining poll:{pollId} rooms (handlers.ts:7-9). Two server-emitted events: poll:analytics:update with full analytics snapshot on response submission (emitter.ts:3-8) and poll:publish with publishedAt timestamp (emitter.ts:10-18). useSocket hook (useSocket.ts:14-76) with proper lifecycle management, stable ref pattern, auth token forwarding, and cleanup on unmount. AnalyticsDashboard receives push updates directly without HTTP refetch (AnalyticsDashboard.tsx:76-88). PublicPollPage listens for poll:publish and transitions to results (PublicPollPage.tsx:72-104). Gaps: no server-side socket auth verification, no typed Socket.IO events, no live response count on the voting page, emitter silently swallows all errors with empty catch blocks, no debouncing on analytics updates.
Code Quality & Project Structure6 / 10
Clean module-based structure on both client (modules/) and server (src/modules/). TypeScript throughout: server has strict, noUncheckedIndexedAccess, exactOptionalPropertyTypes (server/tsconfig.json:21-22,33); client has noUnusedLocals, noUnusedParameters (tsconfig.app.json:19-20). Only 2 uses of `any` in entire codebase. Zero TODOs/FIXMEs. Consistent naming (handleXxx, PascalCase models). Comprehensive README with setup, API routes, socket events, DB schemas. .env.example files for both packages. Card/Button/Input components with forwardRef and displayName. clsx+tailwind-merge utility. Gaps: zero automated tests (no test scripts), no ESLint on server, no pre-commit hooks/CI, pollIdParamSchema duplicated identically in polls/schemas.ts:122-128 and analytics/schemas.ts:4-8, console.log in production (5 sites on server), setTimeout(0) workaround in AnalyticsDashboard.tsx:66-73, emitter.ts empty catch blocks, no root package.json or monorepo tooling.
25
Probably A Poll App
Aditya Pratap · @aditya24
53
Probably A Poll App is a well-engineered full-stack polling platform using Fastify + PostgreSQL/Drizzle ORM + Better Auth + React/shadcn-ui + Socket.IO. The architecture is clean with proper handler/repo/schema layering, transactional database operations, comprehensive JSON Schema validation, and thoughtful state management across both client and server. Strengths include the anonymous session system (invisible to users), real-time WebSocket updates with private results filtering, a polished event lifecycle state machine (pending→running→completed→published), and excellent UI state coverage. However, it has notable gaps: no rate limiting or security headers, unrelated dead code from another project (Deezer music search proxy), no tests, no dark mode, and the initial poll creation flow is bare (only title input). The project demonstrates strong engineering fundamentals but falls short of production readiness. Total: 53/100.
Authentication & Access Control6 / 10
Better Auth with Google OAuth + email/password auth (server/src/auth/index.ts:14-27), anonymous sessions via Better Auth plugin (auth/index.ts:38-49), session cookies with httpOnly/secure/sameSite:none (auth/index.ts:31-37), silent session bootstrap on client (ensure-hidden-session.ts:20-25), 401 retry with auto anonymous sign-in (api-fetch.ts:19-28), Login/Register/ForgotPassword/ResetPassword pages (4 pages in client/src/pages/), creator-only middleware (resolve-creator.ts:9-13), and authOnly event gating (responses.handler.ts:21-22). Gaps: no rate limiting on auth endpoints, no Helmet security headers, no CSRF protection, no explicit email verification flow.
Poll Creation & Question Management7 / 15
Full event CRUD with Zod validation (events.schema.ts, 8 endpoints), item/question creation with mandatory/optional toggle (items.schema.ts:18-27, ManageTab.tsx:467-474), dynamic option add/remove with debounced auto-save (ManageTab.tsx:182-206), expiry date picker with time selector (ManageTab.tsx:330-432), event lifecycle state machine pending→running→completed (events.repo.ts:71-98), questions/options locked when running (items.handler.ts:17-21), transactional itemCount tracking (items.repo.ts:8-25). Manage tab is comprehensive (ManageTab.tsx, 582 lines). However, initial creation only takes title - all settings require editing after creation, and no slug/QR sharing.
Response Collection Flow7 / 15
Response handler validates event status= running (responses.handler.ts:35-37), expiry date check (L39-41), duplicate prevention via submittedAt check (L43-46) + DB unique constraint on (eventId, sessionToken) (schema.ts:168-171), mandatory question validation (L49-57), authOnly gating for authenticated-only events (L21-22), atomic transaction for submission + vote increment (responses.repo.ts:53-97), localStorage tracking for already-submitted state (EventDetailPage.tsx:66-69), and approval mode with pending/approved/rejected (ParticipateTab.tsx:80-116). Comprehensive UI state coverage: completed/running/pending/responded/waiting/rejected/blocked. Gaps: no server-side validation that submitted optionIds belong to event items, unsafe type cast on isAnonymous check.
Analytics & Feedback Dashboard7 / 15
Per-question analytics with vote counts, percentages, and text response grouping (responses.repo.ts:114-181, ResultsTab.tsx:35-46), total response card (ResultsTab.tsx:95-103), color-coded progress bars per option (L134-163), results visibility gating public/private with creator override (responses.handler.ts:104-109), publish feature (events.repo.ts:91-98), real-time analytics via WebSocket (EventDetailPage.tsx:180-213), and client-side analytics builder (build-analytics-view.ts). Gaps: no time-series/trend data, no peak activity metrics, no CSV/export, no individual response viewing, N+1 query pattern in getAnalytics (3 separate queries), percentage uses totalResponses which skews for multi-question polls.
Frontend Experience6 / 10
Clean modern UI with shadcn/ui + Tailwind CSS v4, responsive layout, 7 React Router routes (App.tsx:119-127), comprehensive state handling: loading skeletons (EventDetailPage.tsx:419-425), error toasts via sonner throughout, empty states for all list views, auth states (sign-in required, private results), auto-save on settings with 1s debounce (ManageTab.tsx:116-159), confirmation dialog for delete (ManageTab.tsx:249-271), copy link button (EventDetailPage.tsx:412-415). Gaps: no dark mode, no PWA/offline, initial create form bare (only title input), leftover dead code (searchDeezerArtists in events-api.ts, proxy routes for Deezer), unused assets (hero.png, react.svg, vite.svg), inconsistent branding (POLL.IO vs repo name).
Backend Architecture & API Design7 / 15
Clean layered architecture: routes→handlers→repos→DB schema across 4 resource modules (events/items/options/responses), Fastify with JSON Schema validation on all inputs (6 schema files), Drizzle ORM with proper PostgreSQL schema: 5 tables with foreign keys, cascading deletes, and indexes (schema.ts:33-212), transactions for critical ops (response submission, item creation), custom error handler catching PG constraint codes (error-handler.ts:23-50), Zod env validation (validate-env.ts), CORS with origin allowlist (origins.ts). Gaps: no rate limiting anywhere, no Helmet/security headers, no request body size limits, unrelated Deezer proxy code (proxy.routes.ts) dead code, no graceful shutdown, unsafe type casts (any).
Real-Time Updates Using WebSockets7 / 10
Socket.IO server with typed events (socket/index.ts:9-38), room-based pub/sub per event (L92), socket auth middleware verifying Better Auth session (L50-66), response:new and response:count events emitted on submission (responses.handler.ts:66-93), private results filtering - only emits to creator's socket (L72-78, L86-91), participant:status_updated for approval flow (L144), client useEventRealtime hook with connection status tracking (live/offline/private/connecting), proper cleanup on unmount (L134-139), 800ms debounced analytics refresh (EventDetailPage.tsx:90-104). Gaps: signal-only (no data pushed, client refetches HTTP), no reconnect room re-join logic, no events for poll state changes (start/complete/publish), fetchSockets() is O(n) per room.
Code Quality & Project Structure6 / 10
Clean monorepo structure (client/server separation), TypeScript throughout with consistent handler/repo/schema layering, proper use of Drizzle ORM patterns, comprehensive README with full API reference (406 lines), clean apiFetch wrapper with 401 retry logic (api-fetch.ts), useEventRealtime hook with stale-closure prevention via refs (useEventRealtime.ts:46-58), debounced operations across settings/options/analytics. Issues: dead code from unrelated music project (Deezer proxy, searchDeezerArtists function in events-api.ts:271-274), Google SVG icon duplicated across Login/Register pages, unused template assets (hero.png, react.svg, vite.svg), no tests anywhere, no ESLint on server, unsafe type casts with `any`, items schema has unused correctAnswer field.
26
G
PulseBoard
Gagan Sharma · @gagansharma
53
PulseBoard is a solid full-stack polling platform built as a Turborepo monorepo with React/TypeScript frontend and Express/PostgreSQL/Drizzle backend. It delivers working Clerk-based authentication with webhook-synced user persistence, a React Hook Form + Zod poll creator with dynamic questions/options and status lifecycle management, thorough multi-layer response validation with rate limiting, per-question analytics with animated visualizations, a polished dark theme UI, Socket.io real-time updates via Redis adapter, and clean backend architecture. Key weaknesses: the shared package is dead code (schemas triplicated instead), questions cannot be edited after creation, analytics are basic (activity chart shows poll creation not response activity), Socket.io covers only 1 event type with no data pushing, anonymous duplicate prevention is localStorage-only, and there are zero tests. Deployment: frontend confirmed working at pulseboard-web-mzcg.onrender.com; API deployment URL could not be verified (404 on inferred URLs).
Authentication & Access Control7 / 10
Solid Clerk-based authentication with dual guard middleware (requireAuth + optionalAuth in auth.guard.ts:6-26), Svix-signed webhook sync to local users table (webhooks.router.ts:22-67), anonymous vs authenticated poll mode enforcement server-side (responses.service.ts:42-44), and tiered rate limiting (5/hr anon, 20/hr auth). DashboardLayout redirects unauthenticated users (dashboard-layout.tsx:77-79). Gaps: completely Clerk-dependent with no custom registration/login, no socket authentication, no CSRF protection beyond Helmet defaults.
Poll Creation & Question Management7 / 15
Full poll creation form (create-poll.tsx, 422 lines) using React Hook Form + Zod with dynamic questions/options via useFieldArray, mandatory/optional toggle per question, anonymous toggle, expiry datetime picker. Server-side Zod validation enforces min 1 question, 2-10 options, datetime format (polls.controller.ts:6-24). Complete status lifecycle: draft→active→closed→published with state transition validation (polls.service.ts:60-107). Gaps: questions cannot be edited after creation — PATCH endpoint only updates metadata (polls.service.ts:109-132); QuestionBuilder props are typed as `any` (create-poll.tsx:327-329); no slug/permalink generation.
Response Collection Flow7 / 15
Thorough response submission with multi-layer server-side validation (responses.service.ts:30-115): draft/closed/published state gating, expiry checking, anonymous vs authenticated enforcement, required question validation, per-question option integrity checks, and duplicate prevention via DB lookup for authenticated users. Transaction-based submission (responses.repository.ts:30-45). Client-side required-question validation (respond.tsx:147-158) and localStorage duplicate tracking (respond.tsx:29-38). Comprehensive UI states: loading, error, success, already-voted, poll-ended, auth-required. Gaps: no DB-level unique constraint on response_answers(response_id, question_id); anonymous duplicate prevention is localStorage-only (trivially bypassed); no IP-based dedup for anonymous users.
Analytics & Feedback Dashboard6 / 15
Working analytics dashboard with 4 stat cards (Total Responses, Questions, Status, Expires/Created), per-question option breakdowns with vote counts, percentages, animated progress bars, and TOP badge (analytics.tsx, 325 lines). Backend analytics via Drizzle aggregation (analytics.repository.ts:17-62) with JOIN on response_answers grouped by questionId+optionId. Results publishing with public access gating (analytics.service.ts:13-18). Dashboard includes 7-day activity trend chart (Recharts) and poll stat summaries. Gaps: the 7-day chart shows polls created, NOT response activity — no engagement trends; no CSV/JSON export; no individual response viewing; no peak activity time or completion rate metrics; progress bar is hardcoded to cap at 100 for display.
Frontend Experience7 / 10
Polished dark glassmorphic theme with Tailwind CSS v4 and shadcn/ui components throughout. 8 React Router routes with two layouts (RootLayout + DashboardLayout with responsive mobile sidebar/drawer). Framer Motion animations on cards, progress bars, and page transitions. Comprehensive state handling: LoadingSpinner component, error states with icons+CTAs, EmptyState component, auth-required cards, poll-ended/already-voted states. QR code generation, copy-to-clipboard with toast notifications, and date-fns human-readable timestamps. Gaps: console.log leftover in use-socket.ts:23; 6 `any` type usages in frontend; no data caching layer (no staleTime on QueryClient in App.tsx); react.svg/vite.svg boilerplate remaining in assets.
Backend Architecture & API Design7 / 15
Clean Controller→Service→Repository architecture across 5 modules with 14 REST endpoints. PostgreSQL/Drizzle with 6 well-related tables, proper foreign keys (CASCADE/SET NULL), and enum type for poll_status. Strong middleware stack: Helmet, CORS with explicit origin, request ID tracing (request-interceptor.middleware.ts), centralized error handling (ApiError class + ZodError + 500 fallback), rate limiting with Redis (rate-limiter.middleware.ts:5-17). Environment validation at startup (env.ts:6-24), graceful shutdown (main.ts:19-44), structured logging with pino. Gaps: shared package (packages/shared/) is built but never imported — schemas/types triplicated across backend controllers and frontend form; no explicit DB indexes beyond PKs; no unique constraint on response_answers for dedup; no pagination on getMyPolls.
Real-Time Updates Using WebSockets6 / 10
Socket.io server with Redis adapter for horizontal scaling (socket.ts:28). Room-based pattern: clients join/leave `poll:{pollId}` rooms via subscribe_poll/unsubscribe_poll events (socket.ts:34-42). Server emits `poll_updated` on response submission (responses.service.ts:62-65). Client useSocket hook (use-socket.ts:4-38) manages connection lifecycle, subscribes on connect, and triggers React Query cache invalidation for live dashboard updates. Socket.io auto-reconnect with re-subscription via connect handler. Gaps: only 1 event type (no broadcasts for poll status changes, publish, or delete); no data pushed over socket (pure invalidation signal); no debouncing; no socket authentication; console.log leftover; analytics page is the only consumer — respond page has no live updates.
Code Quality & Project Structure6 / 10
Clean Turborepo monorepo structure with apps/api, apps/web, and packages/shared. Consistent naming conventions and folder organization. Backend has zero `any` types with strict TypeScript. ESLint configured for frontend, multi-stage Dockerfile, render.yaml, docker-compose.yml, comprehensive README with setup/deployment guides. TanStack Query with typed key factories (use-polls.ts:12-19). Gaps: shared package is built but never imported (verified via grep — zero imports from "shared"); Zod schemas triplicated across shared package, backend controllers, and frontend form; frontend has 6 `any` usages (QuestionBuilder props in create-poll.tsx:327-329, error handlers); StatusBadge configs, QR dialog, and copyLink duplicated across files; console.log in use-socket.ts:23; zero automated tests; no pre-commit hooks.
27
PulsePoll
Chetan Chauhan · @chetanchauhan1930_7a712310
53
PulsePoll is a working full-stack polling platform with a solid Express/MongoDB backend and a visually polished React/TanStack frontend. The project delivers all core requirements: JWT authentication with cookie security, multi-question poll creation with dynamic option management, thorough response collection with multi-layered validation and database-level duplicate prevention, analytics with area/bar/donut charts, results publishing, and real-time Socket.IO updates. The backend architecture is clean MVC with consistent Zod validation, proper middleware application, and security measures (Helmet, CORS, rate limiting). However, the project has clear quality issues: significant code duplication (~870 lines between create/edit forms), dead code (unused TanStack Query hooks, unused react-hook-form), no TypeScript on the server, placeholder UI elements that appear functional but aren't (notifications, settings), dual auth stores creating confusing state flow, and Socket.IO is limited to a single event type without reconnection handling. Scores reflect a solidly built but somewhat rushed project that delivers the requirements but needs refactoring to reach production quality.
Authentication & Access Control7 / 10
Solid auth with registration/login/logout via Zod-validated endpoints, bcrypt (SALT_ROUNDS=12), JWT in httpOnly cookie with configurable secure/sameSite (authController.js:7-13). Dual auth middleware: `protect` (cookie+Bearer header, JWT error handling) and `optionalAuth` for public routes (auth.js:10-88). Client AuthContext with localStorage hydration, cross-tab sync via CustomEvent, and auth-gated dashboard layout (dashboard.tsx:137-142). Anonymous vs authenticated poll modes enforced server-side in submitResponse (responseController.js:22-24). Rate limiting on auth routes (20/15min). Gaps: no CSRF protection, no email verification, no password reset, no refresh token rotation, and dual auth-store/api-store creates confusing data flow (use-polls.ts with TanStack Query hooks exists but is never imported).
Poll Creation & Question Management7 / 15
Working multi-question poll builder at dashboard.create.tsx (456 lines) with dynamic question add/remove/reorder, per-question required toggle, option add/remove (min 2 enforced), IP tracking toggle with confirmation dialog, calendar+time expiry picker, and live preview sidebar. Server-side Zod createPollSchema with `.strict()` and `superRefine` for case-insensitive duplicate option detection (poll.js:43-56) plus future-date refinement. Edit capability via edit-poll-dialog.tsx (414 lines) and PUT endpoint with updatePollSchema. Client-side Zod pollBuilderSchema matches server. Gaps: react-hook-form installed but unused (raw useState for 456 lines), ~870 lines of duplicated create/edit logic, polls can be modified after responses exist creating analytics inconsistency, no draft status, no slug/QR generation.
Response Collection Flow8 / 15
Thorough multi-layered validation in submitResponse (responseController.js:9-106): Zod structural validation, duplicate questionId detection (lines 29-33), required-question enforcement by cross-referencing poll document (lines 42-55), valid-option enforcement via lookup map (lines 58-66), expiry check before submission (line 17). Duplicate prevention via MongoDB partial unique compound indexes ({pollId, userId} and {pollId, ipAddress}) at Response.js:51-60, with 409 CONFLICT on duplicate key (line 101-103). Rate limiting (10/15min). Client-side: localStorage draft persistence with auto-save (p.$id.tsx:39-49), already-voted detection (line 33-35), dynamic Zod schema for required-question validation (validation.ts:83-98), post-submission spring-animated success state. Gaps: no duplicate prevention for anonymous voters on default (non-IP-tracked) polls, localStorage voted state can be bypassed.
Analytics & Feedback Dashboard7 / 15
Comprehensive analyticsService.js (157 lines): per-question vote tallying, option percentages, mostSelected identification, participation rate (required-question completion), response timeline grouped by calendar day. Analytics page (dashboard.polls.$id.tsx, 589 lines): 4 stat cards (total responses, time-remaining ticker, top option, questions), response timeline area chart with gradient fill, per-question horizontal bar chart + donut chart side by side, percentage breakdown, collapsible voter list for non-anonymous/IP-tracked polls. Dashboard overview: 14-day trend area chart, filter tabs, sort, search. Results publishing via PATCH /api/polls/:id/publish with public results page at /r/$id. Real-time LIVE badge with ping animation. Gaps: no CSV export, analytics computed live every time (no caching), poll modification after responses breaks analytics consistency (analytics recomputed against current poll state), no funnel/drop-off analysis.
Frontend Experience6 / 10
Visually polished with custom CSS design system (OKLCH colorspace, light/dark variants, custom utilities like gradient-primary, glass, shadow-soft — styles.css:197 lines). Framer Motion throughout: staggered entrances, AnimatePresence with popLayout, spring animations, floating shapes. Responsive: mobile sheet sidebar, stacked grids, mobile search toggle. All 10 routes handle loading (skeletons), empty (contextual CTAs), and error states (ErrorBoundary class). Time-remaining ticker, LIVE ping badge, copy-to-clipboard feedback. Gaps: react-hook-form installed but unused (raw useState for 456-line poll builder — no field-level errors), significant duplication (floatingShapes array duplicated verbatim in login.tsx:37-42 and signup.tsx:37-42), placeholder UI elements that look functional but aren't (notifications bell, settings menu, upgrade CTA), dead links (# for Terms/Privacy), hardcoded "admin@chetan.com" placeholder, two parallel data-fetching patterns (api-store vs unused tanstack-query hooks).
Backend Architecture & API Design7 / 15
Clean MVC-layered architecture: routes→controllers→services→models→middleware across 22 source files. All 5 body-accepting endpoints validated with Zod `.strict()` schemas, including sophisticated superRefine for duplicate options (poll.js:43-56) and future-date refinement. MongoDB partial unique compound indexes for race-condition-safe duplicate prevention (Response.js:51-60). JWT auth with cookie+Bearer hybrid (auth.js:10-88). Security: Helmet, CORS with explicit origin allowlist, 3-tier rate limiting (global/auth/response), body size limit (1mb). Centralized error handler for malformed JSON, Mongoose ValidationError, duplicate key (11000), CastError (error.js:6-42). Consistent sendSuccess/sendError format. Environment validation at startup blocks example keys in production (env.js:12-27). Tests: 9 Zod validation cases. Gaps: no CSRF protection, Socket.IO auth code duplicates protect middleware logic, polls modifiable after responses, no scheduled expiry job, cookie maxAge hardcoded instead of derived from JWT_EXPIRES_IN, only 9 unit tests (no integration/controller tests).
Real-Time Updates Using WebSockets6 / 10
Working Socket.IO integration with room-based pub/sub pattern. Server: `poll:{pollId}` rooms, `join_poll` with ObjectId validation, poll existence check, user auth via getSocketUser (cookie parsing + JWT verify), access control (resultsPublished or owner), and `leave_poll` handling (socket/index.js:70-111). Server emits `poll_updated` with full analytics payload from responseController.js:88-96 after each submission. getSocketUser caches auth on socket.data (lines 22-48) to avoid repeated DB lookups. Client: singleton socket factory (socket.ts:1-25), usePollSocket hook with stable callback ref pattern (use-poll-socket.ts:1-51), LIVE badge with ping animation. Gaps: only 1 event type (no events for publish/delete/expiry), no reconnection room re-join logic (socket disconnect loses room membership), no data pushed over socket (pure invalidation signal), no socket integration on public voting page (p.$id.tsx), no authentication at socket connection (only at join_poll).
Code Quality & Project Structure5 / 10
Clean client/server monorepo with consistent naming and well-organized MVC structure. TypeScript on client with strict checking, ESLint+Prettier configured. README with setup instructions, env documentation, and production checklist. .env.example files for both packages. Good patterns: Zod `.strict()` everywhere, consistent response format, proper async error handling. Gaps: no TypeScript on server (pure JS), significant code duplication (apiRequest in lib/api.ts and lib/api-store.tsx; create.tsx and edit-poll-dialog.tsx share ~80% structural overlap; floatingShapes+stagger animations duplicated in login/signup), dead code (use-polls.ts with unused TanStack Query hooks, unused react-hook-form dependency, unused useIsMobile, unused disconnectSocket), dual auth stores with confusing data flow, monolithic api-store context (16 values causing whole-tree re-renders), only 9 schema unit tests with no integration tests, console.log in socket/index.js:99, hardcoded "admin@chetan.com" in forms.
28
P
Pulseloop
Punyansh Singla · @punyanshcoder
53
Pulseloop is a well-rounded polling platform with 26 API endpoints, 18 frontend routes, 6 MongoDB models, and 6 external service integrations. Standout features include AI-powered poll generation via Google Gemini, comprehensive analytics with GeoIP demographics and growth metrics, and a polished step-by-step voting flow with Framer Motion + confetti. Better-Auth provides solid authentication with email verification and Google OAuth. The backend follows a modular MVC pattern with Zod validation, rate limiting, and transactional updates. Key weaknesses: no Helmet/security headers, Socket.io lacks authentication, dead code (4+ unused files), misspelled directory names, zero automated tests, and client-side form validation is toast-only. Overall: a feature-rich, production-minded submission with some rough edges in security, testing, and code cleanliness. Score: 53/100.
Authentication & Access Control7 / 10
Better-Auth integration with email+password registration/login, Google OAuth, email verification flow, forgot/reset password, and change password (ProfileSection.tsx:15-91, reset-password-form.tsx:26-99). authMiddleware (auth.middleware.ts:4-31) blocks unauth requests with 401; optionalAuthMiddleware (optional-auth.middleware.ts) proceeds as anonymous. AuthGuard (auth-guard.tsx:10-29) wraps protected routes. Anonymous vs authenticated poll mode enforced server-side in vote controller (polls.controller.ts:128-150) checking allowAnonymous flag. Gaps: no Helmet security headers, Socket.io rooms lack auth (socket.service.ts:29-46 accepts any join:dashboard request), no CSRF protection, (req as any).user type casts.
Poll Creation & Question Management8 / 15
Dynamic multi-question builder with add/remove (create-poll.tsx:27-42, QuestionSlider.tsx:26-165), per-question mandatory/optional toggle, min 2 options per question enforced server-side via Zod pollSchema (poll.types.ts:1-33), expiry and scheduling dates, visibility (public/private), anonymous and multiple-submission toggles. Full poll editing with transactional question/option add/update/delete (polls.service.ts:307-412 uses mongoose sessions with rollback). AI-powered poll generation via Google Gemini (ai.service.ts:24-32, PollAIModal.tsx:17-128) is a differentiating feature. Gaps: client validation is toast-only with no per-field inline errors, no form library (individual useState hooks), dead create-poll-form.tsx (220 lines).
Response Collection Flow7 / 15
Step-by-step voting UI with Framer Motion transitions and confetti (public-poll.tsx:77-86, 365-543). Server-side vote flow (polls.controller.ts:111-244): status check (line 135-138), expiry check (lines 141-144), auth mode enforcement (lines 147-150), duplicate prevention via compound indexes (Response.ts:97-102) + pre-check query (lines 153-179), mandatory question enforcement (lines 183-194). Server-side SHA-256 fingerprint (fingerprint.ts). Differentiated HTTP status codes (400/401/403/404). 11000 duplicate key race condition handler (lines 235-241). Gaps: no Zod validation on vote request body, race condition between pre-check and save, fingerprint/IP not fully covered by compound indexes in $or queries.
Analytics & Feedback Dashboard7 / 15
Dashboard KPIs with period-over-period growth metrics (analytics.service.ts:10-123), custom SVG responses-over-time chart with interactive tooltips (responses-chart.tsx:5-209), Recharts pie/donut charts for user type breakdown (user-type-breakdown.tsx:6-117), device/browser/OS demographics with progress bars (device-breakdown.tsx:4-71), GeoIP country breakdown (polls.service.ts:564-605 using geoip-lite), 7-day voting timeline with zero-filling (polls.service.ts:608-651), top-performing poll spotlight, activity feed (last 5 votes). Results publishing with public results page (poll-results.tsx:24-455 with AreaChart, PieChart, demographics). Gaps: no CSV/export, no individual response viewing.
Frontend Experience7 / 10
React 19 + React Router v7 + TanStack Query + Framer Motion + shadcn/ui + Tailwind CSS v4. Dark mode via next-themes (theme-toggle.tsx:5-20). Landing page with 10+ sections (Hero, Features, HowItWorks, Analytics, Security, CTA, etc.). Consistent loading states (skeletons in kpis.tsx:46-51, spinners), error boundary (error-boundary.tsx:16-97), empty states, auth redirects. Responsive layout with mobile sidebar (sidebar.tsx:38-53). Demo credentials button for testing (signin-form.tsx:28-32). QR code generation, confetti celebrations. Gaps: no form library (useState-based), toast-only client validation, dead code (create-poll-form.tsx 220L, view-poll.tsx 301L), some `any` types.
Backend Architecture & API Design6 / 15
MVC module architecture (routes→controllers→services→models). Zod env validation at startup (env.ts:4-35 exits on misconfiguration). MongoDB connection with 5-retry logic (db.ts:31-43). Centralized error middleware handling ZodError/CastError/11000/ValidationError (error.middleware.ts). Rate limiting on vote (10/15min) and poll creation (5/hr). Transactional poll updates with mongoose sessions (polls.service.ts:307-412). Server-side SHA-256 fingerprinting (fingerprint.ts). Atomic view tracking with upsert on unique index. Gaps: no Helmet/security headers, Socket.io lacks authentication, ai.controller.ts bypasses error middleware (direct res.status(500)), dead code (ApiError, asyncHandler never used), missing indexes on Question.pollId/Option.questionId/Poll.createdBy, no Zod validation on vote body, debug console.logs in production (5+ locations).
Real-Time Updates Using WebSockets6 / 10
Socket.io integrated with HTTP server (socket.service.ts:17-55). Room-based pattern: poll:{pollId} and dashboard:{userId}. Server emits 4 event types: poll:updated, vote:cast, analytics:update, poll:published from vote/publish/update/view controllers. Client integration via singleton SocketClient class (socket.ts:1-60) with lazy connection, polling fallback transport. Custom hooks (use-polls.ts:37-55, use-analytics.ts:12-28) join rooms and invalidate query caches on events. Gaps: no socket authentication (any client can join any dashboard:{userId} room), purely invalidation-based (no data pushed, clients refetch via HTTP), no reconnection room re-join logic, no debouncing, inconsistent update patterns (invalidateQueries vs setQueryData).
Code Quality & Project Structure5 / 10
Monorepo with client/server separation. TypeScript throughout with ESLint on client. Good component decomposition: 43 domain components, 14 UI components. Custom hooks pattern (use-polls.ts, use-analytics.ts), TanStack Query for server state, clean API client abstraction (api.ts:5-89). Gaps: dead code (create-poll-form.tsx 220L, view-poll.tsx 301L, ApiError class, asyncHandler wrapper), misspelled directory name "tempelates" (consistent across 2 files + imports), duplicated getDeviceInfo() logic, duplicated aggregation pipeline patterns, debug console.log in production code (5+ sites including models/User.ts:33 on import), zero automated tests (package.json: "echo no test specified"), some `any` types.
29
S
WISE Poll
Shivam Verma · @theadroitdev
53
WISE Poll is a solid full-stack polling platform with deployed frontend (https://wisepoll.theadroitdev.com/) and backend API. Strongest areas: response collection with defense-in-depth validation (param-3: 8), real-time Socket.IO with room-based pub/sub and viewer tracking (param-7: 7), thorough auth with JWT refresh rotation (param-1: 7), feature-rich poll creation with quiz mode and scheduled opens (param-2: 7), and Recharts-powered analytics with winner detection and public results (param-4: 7). Main weaknesses: significant code duplication across components, no automated tests, unused rate limiting dependency, console.log in production, no password reset despite configured SMTP vars, and no form library for complex forms. Total: 53/80 — a well-implemented submission that delivers all core requirements with good quality but lacks polish in code organization and missing security/production hardening.
Authentication & Access Control7 / 10
Solid JWT auth with dual-token refresh rotation: register/login/logout/refresh/me/deleteAccount endpoints (auth.controller.ts, 6 handlers). bcryptjs 12 rounds, httpOnly/secure/sameSite cookies for refresh token, refresh token rotation with storage in MongoDB (auth.service.ts:79-80). authenticationMiddleware reads Bearer token and attaches req.user (auth.middleware.ts:14-38), restrictToAuthenticatedUser enforces auth on protected routes (auth.middleware.ts:41-48). Anonymous vs authenticated poll modes enforced server-side (response.service.ts:59-66). Frontend AuthContext with isLoading/login/register/logout + Axios interceptor with automatic refresh and concurrent-request deduplication (axios.ts:20-75). ProtectedRoute component with loading spinner state. Gaps: no password reset/forgot password flow (despite SMTP env vars configured in env.ts), no email verification, no CSRF protection, access token in localStorage (XSS concern), express-rate-limit listed as dependency but never imported/used in any source file.
Poll Creation & Question Management7 / 15
CreatePollPage.tsx (674 lines) and EditPollPage.tsx (688 lines) with full dynamic question/option management: add/remove questions and options, mandatory toggles, opinion vs quiz mode with correctOptionIndex, scheduled opens with custom DatePicker/TimePicker components, thumbnail upload via Cloudinary. Custom client-side validation function (58 lines) checks title/description/question/option lengths, minimum questions/options, duplicate detection, quiz-specific validation, and schedule-before-expiry. Server-side Zod validation (poll.schema.ts, 124 lines) with futureDateString, duplicate option detection, quiz superRefine, and schedule-expiry ordering. Server updatePoll supports partial updates with ownership checks (poll.service.ts:168-216). Custom multipart form parsing middleware (poll.route.ts:29-44). Character counters with color warnings. LIMITS config enforces max 20 questions, 8 options. Gaps: no slug/permalink generation (just MongoDB IDs), no form library (466 lines of imperative useState hooks per form), no poll duplication feature.
Response Collection Flow8 / 15
PollRespondPage.tsx (475 lines) covers all states: loading, error, submitted (with quiz score), expired, auth-required (with Lock icon + login redirect with return-to URL), scheduled countdown (with auto-refresh on open), and full response form. Live countdown timer (useCountdown hook, 14 lines). Server-side defense-in-depth (response.service.ts:137 lines): poll existence check, dynamic status sync (expiry detection), active-only gate with differentiated messages (expired vs published vs scheduled), anonymous/authenticated enforcement with duplicate prevention for authenticated users (findOne check + compound index on {poll,respondent} in response.model.ts:33), question index range validation, mandatory question validation, option existence verification per answer, quiz scoring computation. Zod validation (response.schema.ts, 24 lines) validates questionIndex, selectedOption, and prevents duplicate question indices. Socket emit on submission for real-time count + analytics push. Tab visibility tracking pauses socket. Gaps: no CAPTCHA for anonymous submissions, no rate limiting on submission endpoint.
Analytics & Feedback Dashboard7 / 15
AnalyticsPage.tsx (290 lines) with 4 gradient stat cards, per-question PieChart+BarChart via Recharts, winner/runner badges with Trophy/Medal icons, progress bars per option, publish button with ConfirmDialog, share link copy, print-friendly Download PDF section, live indicator with real-time response count via socket. Server uses MongoDB aggregation pipeline ($match→$unwind→$group→$group→$sort) for efficient computation (analytics.service.ts:237 lines), includes ALL options even with 0 votes, computes mandatory completion rate and 1-decimal percentages. PublicResultsPage.tsx (229 lines) for published results public viewing. AdminPage.tsx (289 lines) with platform-wide stats, recent polls, 7-day response trend aggregation. Dashboard with search/filter (all/live/scheduled/expired/published), QR code modal. Gaps: no CSV/Excel export (only print CSS), no individual response drill-down viewing, no time-series per-poll trend data.
Frontend Experience6 / 10
12 page components across 3 layouts (RootLayout, DashboardLayout, AuthLayout). Custom design system with CSS custom properties (index.css:100 lines), sage-green theme, dark/light mode toggle, consistent spacing/borders/transitions. Reusable UI components: Button, Input, Logo, ThemeToggle, DatePicker, TimePicker, FileUpload, ConfirmDialog. Landing page with 4 sections (Hero, Features, HowItWorks, CTA). Loading spinners on all pages, empty states with CTAs, error states with contextual icons. Auth required gate with return-to URL. Expired/scheduled poll states with countdown timers. Animated progress bars, staggered fade-in-up on cards. Responsive grid layouts. QR code generation. Toast notifications. Print-friendly styles. Open Graph social preview via server-side HTML (app.ts:43-87). Gaps: no form library (individual useState hooks), many inline event handler styles, no skeleton loaders (just spinners), no PWA/offline support, no data caching layer.
Backend Architecture & API Design6 / 15
Clean modular architecture with auth/poll/response/analytics/admin modules, each following model→schema→service→controller→route separation. Common utilities: ApiResponse (ok/created/noContent), ApiError (badRequest/unauthorized/notFound/conflict/forbidden with proper HTTP codes), jwtUtils, uploadToCloudinary. Common middleware: error.middleware (centralized, distinguishes ApiError from generic), validateRequest (Zod safeParseAsync), upload (multer with 5MB limit). 3 Mongoose models with proper validation and compound index on Response. Helmet + CORS with env-specified origin. Zod env validation (env.ts:7-39). MongoDB aggregation for analytics. Transactions for account deletion (auth.service.ts:118-129). TypeScript throughout. Gaps: express-rate-limit is a dependency but never imported in any source file (no rate limiting implemented), no request body size limits on express.json(), no express-mongo-sanitize, no pagination on getMyPolls or getPollResponses, console.log in production code (sockets.ts:4 calls, db.ts:1, server.ts:1), no .env.example file, no graceful shutdown handlers.
Real-Time Updates Using WebSockets7 / 10
Socket.IO server with singleton pattern via getIO() (sockets.ts:68-71). Room-based pub/sub: clients join/leave `pollId` rooms. 3 server-emitted events: poll:response:count (total after submission), poll:analytics:update (full analytics object pushed), poll:viewers:count (live viewer count via room.size with 10-second interval ticker, auto-cleans when room empties). Client socket.ts with autoConnect:false, transports priority, credentials, connect_error logging. useSocket hook (68 lines) with automatic join on mount/leave on unmount, event filtering by pollId, enabled flag for tab visibility, proper cleanup. PollRespondPage shows live viewer count badge. AnalyticsPage receives live response count + full analytics updates via socket. Viewer ticker with automatic cleanup (setInterval managed per pollId). Gaps: no socket authentication, no typed Socket.IO events, no reconnect room re-join logic, analytics updates not debounced on server.
Code Quality & Project Structure5 / 10
Clean monorepo (client/server) with consistent module organization. TypeScript throughout with explicit types. API layer abstraction (5 API modules). Shared types (types/index.ts, 100 lines). Custom hooks (useTheme, useSocket, useScrollReveal). Good README with setup, API docs, structure, and design system. Gaps: zero automated tests. Significant code duplication: Section component duplicated in CreatePollPage.tsx and EditPollPage.tsx, QuestionCard/GradientStatCard duplicated in AnalyticsPage.tsx and PublicResultsPage.tsx, CHART_COLORS array duplicated across 3 files, useCountdown hook duplicated in DashboardPage.tsx and PollRespondPage.tsx. Console.log in server production code (6 places). Unused dependency express-rate-limit. Dead code: generateResetToken (jwtUtils.ts:48-51), SMTP env vars configured but no email functionality. No .env.example file. Large component files (674-688 lines). No pre-commit hooks or lint-staged.
30
V
PollForge Re-scored
Vinit Kumar · @vinitastrak04_5e9f1f16
52
PollForge (repo dev-Astrak/Polling-App) is a real MERN polling platform with bcryptjs+JWT auth (token in localStorage), single-choice questions with mandatory/optional flags, public-vs-authenticated polling modes, robust response deduplication (userId OR clientIp, both checked against existing responses), expiry enforcement, and an admin panel + bootstrap admin role. Socket.io broadcasts poll:stats on each response and poll:status on publish, with PublicPoll auto-redirecting to results when published. Solid backend MVC with env-validation hard-exits on missing MONGO_URI/JWT_SECRET; CORS, 1MB JSON limits configured. Significant gaps: only single-choice question type (no multi-select, text, rating, scale), no edit after create, no charts in analytics (text-only badges), GitHub OAuth scaffolding in utils/passport.js is dead code (never mounted in Express, AuthCallback page has no route), no helmet/rate-limit/tests, IP-based dedup is bypassable behind NAT/VPN.
Authentication & Access Control6 / 10
bcryptjs+JWT register/login (server/src/routes/auth.js). Admin role + bootstrapAdmin guard (utils/bootstrap.js). AuthContext on client, ProtectedRoute, router-level requireAuth on polls.js. Negative: GitHub OAuth strategy in utils/passport.js is dead code — index.js never requires it, no /auth/github route, no passport.initialize(); AuthCallback page imported in App.jsx but never routed. Token in localStorage (XSS-exposed). No rate-limit on auth endpoints.
Poll Creation & Question Management6 / 15
server/routes/polls.js creates polls with title/description/responseMode/expiresAt/questions/required. Poll.js model has anonymous mode, expiry, isPublished. Single question type only (radio/single-choice on PublicPoll.jsx:122). >=2 options validation. POST/DELETE/publish endpoints, no edit-after-create. No multi-select/text/rating types.
Response Collection Flow10 / 15
public.js with sophisticated dedup — checks user.id OR clientIp against existing responses, enforces expiry, checks isPublished lockout (auth-mode gated), validates per-question option choice and required flag, returns clear 400s for missing/invalid. Strong response logic for the rubric. Negative: IP-based dedup is naive (NAT collisions, VPN bypass). No rate-limit.
Analytics & Feedback Dashboard6 / 15
Per-question/option counts, total responses, auth-vs-anon breakdown, first/last response timestamps, completion rate (analytics.js). /publish endpoint exposes results to the public PublishedResults.jsx view. ZERO charts — text 'X votes' badges only (PollAnalytics.jsx:166-171, PublishedResults.jsx:50-56). No Recharts/Chart.js, no per-option percentages, no progression visualization.
Frontend Experience5 / 10
AuthContext + ProtectedRoute, multiple pages (Dashboard, CreatePoll, PublicPoll, PollAnalytics, PublishedResults, ExplorePolls, AdminPanel, AuthCallback), dark-mode toggle via ThemeContext, sort/filter on dashboard, share links. Negative: inline-styled tables in AdminPanel, no charting library, no skeletons, no mobile-tested layouts, AuthCallback page imported but no route renders it.
Backend Architecture & API Design7 / 15
Clean MVC (routes/models/middleware/utils), env validation hard-exits on missing MONGO_URI/JWT_SECRET (index.js:55-63), router-level requireAuth on polls.js:9, CORS with credentials, 1MB JSON limit. Negative: NO helmet, NO rate-limiter, NO request-validation library (zod/joi), generic 500 errors swallow stack, no global error middleware, no request id/logger.
Real-Time Updates Using WebSockets7 / 10
socket.io server (index.js:34-49), room-per-poll (`poll:${pollId}`), client joins on mount, emits poll:stats on each response submission (public.js:202-205) and poll:status on publish (polls.js:144-147), PublicPoll auto-redirects to results when published (PublicPoll.jsx:34-38), Analytics live-updates votes. Solid working implementation. No socket auth (anyone can join any room).
Code Quality & Project Structure5 / 10
env-based secrets, README + DEPLOYMENT.md, monorepo split via concurrently, client ESLint configured. Negative: NO tests anywhere (root test script literally 'echo Error && exit 1'), NO helmet, NO rate-limit, dead GitHub-OAuth code is misleading, server has no lint script, no Prettier config, console.error in production paths, no CI config.
31
P
PollMan
PollMan · @lalit79
52
PollMan is a well-executed full-stack polling platform built with React/TypeScript, Express/Node.js, MongoDB, and Socket.IO. The poll creation form (PollFormBuilder) is the standout feature — comprehensive React Hook Form with dynamic questions, per-question mandatory/optional controls, and extensive settings. Response collection is thorough with defense-in-depth validation on both client and server. The analytics system provides meaningful dashboards with Recharts visualizations. Authentication uses Google OAuth with JWT tokens. The platform is deployed and working at pollman.onrender.com. Key weaknesses: backend is entirely JavaScript (no TypeScript), no input validation library (Zod/Joi placeholder is a stub), no tests at all, no security middleware (Helmet/rate limiting), no database transactions, duplicate controller/service naming, and large monolithic component files. Total score: 52/100, placing it solidly in the "good" range with room for improvement on backend polish and code quality.
Authentication & Access Control6 / 10
Google OAuth 2.0 via Passport.js (passport.js, 69 lines) with auto user creation; JWT access+refresh tokens generated on callback (jwt.service.js, 44 lines); authMiddleware verifies Bearer token + looks up user (auth.js:4-43); optionalAuthMiddleware handles unauthenticated access (auth.js:45-69); ProtectedRoute redirects to /login (ProtectedRoute.tsx, 26 lines); AuthContext with localStorage persistence and logout (AuthContext.tsx, 143 lines). Anonymous vs authenticated poll modes enforced server-side via poll.isAnonymous field. Critical gaps: tokens in localStorage (XSS risk), no Helmet, no CSRF protection, no rate limiting, no email verification, no httpOnly cookies for JWT.
Poll Creation & Question Management8 / 15
PollFormBuilder.tsx (795 lines) uses React Hook Form with useFieldArray for dynamic questions and options; per-question isRequired toggle (mandatory/optional), allowOpinionText toggle, option add/remove; poll-level settings: isAnonymous, allowResultsPublish, passwordProtected with hash, isResponseLimited with count, expiresAt datetime picker; client-side validateValues (62 lines) checks title, questions, options (min 2), expiry date; server-side validation in handleCreatePoll (poll.controller.js:19-38) validates title, questions array, expiry; poll update (poll.service.js:178-279) preserves existing option IDs and counts on edit. Framer Motion animations on question add/remove. Rare gap: no slug/permalink for easy sharing.
Response Collection Flow7 / 15
submitPollResponse (poll.service.js:358-503, 145 lines) performs defense-in-depth: poll existence, creator cannot answer own poll, expiry check, response limit check, duplicate prevention by userId (authenticated) and IP (anonymous), required questions validation, completion percentage calculation, atomic option count updates via arrayFilters, totalResponses increment, access logging, and Socket.IO emission. PublicPoll.tsx (895 lines) handles 9 distinct UI states: loading, not-found, not-published, expired, limit-reached, already-responded, submitted, password-protected, and active; localStorage-based duplicate prevention client-side; live response count via WebSocket. Gaps: no database transactions for response+stats update (race condition risk), IP-based dedup not enforced via unique index, selectedOption stored as string not ObjectId.
Analytics & Feedback Dashboard7 / 15
getPollAnalytics (poll.service.js:506-616) computes per-question breakdowns with option counts/percentages, completion rate, average completion, hourly+daily timeline buckets, and full response list with user info. getDashboardOverview (poll.service.js:618-697) aggregates stats (total/active/draft polls, total responses), 7-day activity timeline from PollAccessLog, recent polls with response counts. PollDetails.tsx (965 lines) renders stat cards, per-question option vote breakdowns, individual response viewer dialog (with avatars/IP/timestamps), QR code generation (via qrserver API), share link with copy, and publish/delete controls. DashboardHome.tsx (384 lines) shows 4 stat cards, Recharts AreaChart for activity timeline, recent polls list, and activity feed. Gaps: no CSV export, no Recharts charts on PollDetails page itself, no peak hour/day metrics, question analytics mapped by array index (fragile).
Frontend Experience7 / 10
16 React pages with 3 layouts (RootLayout, DashboardLayout, AuthLayout) and clean React Router v7 configuration (routes.tsx, 73 lines). Comprehensive state handling across all pages: loading spinners, error toasts, empty states (dashed borders), not-found, expired, limit-reached, already-responded, password-protected states. Framer Motion animations (AnimatePresence, motion.div) on question transitions, page entries. 30+ shadcn/ui components, Tailwind CSS v4, MUI, Radix UI. Dashboard has Recharts AreaChart. QR code generation with download. Sonner toast notifications. Responsive layout with lg breakpoints. Gaps: several `as any` TypeScript casts, large component files (PollDetails 965 lines, PublicPoll 895 lines), no data caching layer (refetches on every navigation), dark-mode only (no light theme).
Backend Architecture & API Design6 / 15
Clean MVC separation: routes (poll.routes.js, 59 lines with 12 endpoints) → controllers (poll.controller.js, 283 lines + polls.controller.js, 146 lines) → services (poll.service.js, 697 lines + polls.service.js, 111 lines). authMiddleware + optionalAuthMiddleware are well-designed (auth.js, 69 lines). JWT service is clean and reusable (jwt.service.js, 44 lines). errorHandler middleware with env-sensitive stack traces (errorHandler.js, 14 lines). Morgan logging, CORS, cookieParser, express-session configured. Mongoose schemas well-indexed (Poll has 6 indexes, Response has 4 indexes). Socket.IO integrated with server. Critical gaps: poll.validator.js is a complete placeholder stub ("Add zod/joi validation here"), no Zod/Joi/express-validator anywhere, no Helmet, no rate limiting, no request body size limits, raw error.message leaked to clients in 6+ catch blocks, no transactions in submitPollResponse (response save + question updates + poll update not atomic), duplicate naming (poll.controller.js vs polls.controller.js).
Real-Time Updates Using WebSockets6 / 10
Socket.IO server attached to HTTP server (server.js:15-20) with CORS. Room-based pattern: clients join `poll:{pollId}` and `creator:{userId}` rooms (socket/index.js:39-53). Server emits 4 events: poll:response:new (on submission), poll:analytics:update (to creator), poll:created (broadcast), poll:published (broadcast). Client singleton socket (socketClient.ts, 41 lines) with WebSocket transport. PublicPoll joins poll room and increments live response count on poll:response:new (PublicPoll.tsx:135-153). PollDetails and DashboardHome join creator room and listen for analytics:update to trigger HTTP refetch (PollDetails.tsx:248-285, DashboardHome.tsx:64-105). Cleanup properly disconnects on unmount. Gaps: no reconnect room re-join (socket reconnects silently lose subscriptions), pure invalidation signal with full HTTP refetch (no data pushed), websocket-only transport (no polling fallback), no socket authentication, only covers analytics updates (not response-level live data).
Code Quality & Project Structure5 / 10
Good monorepo structure (client/ + server/) with clear separation. Comprehensive README (666 lines) with setup instructions, architecture docs, and screenshots. Frontend uses TypeScript with well-typed interfaces. Consistent file naming conventions. React Hook Form integration is clean. useEffect cleanup properly implemented in all components. Critical issues: entire backend is plain JavaScript (no TypeScript) — a major missed opportunity. poll.validator.js is a literal placeholder stub. Duplicate naming pattern: poll.controller.js + polls.controller.js and poll.service.js + polls.service.js. Zero automated tests. No ESLint config or Prettier config found. console.log/warn statements in production code (poll.service.js:149, auth.controller.js emits raw user object in URL). Large component files (PollDetails 965 lines, PublicPoll 895 lines, PollFormBuilder 795 lines) without proper sub-file extraction. Several `as any` casts in TypeScript code.
32
Polling App
Manish Rawat · @manish
52
PollWave — full-stack MERN polling app. Frontend: React 19 + Vite 7 + Tailwind v4 + Shadcn UI + TanStack Query + Framer Motion + Recharts + Socket.IO client. Backend: Express 5 + TypeScript + MongoDB/Mongoose + JWT (access + refresh) + Zod validation + email verification + Socket.IO + Winston. Clean modular architecture (dto/service/controller/route). Live at https://polling.cohortteam.com. [NOTE: This entry was manually re-scored. The AI agent originally scored this 0/80 because the submission URL pointed to a non-default branch (.../tree/Polling) and the clone logic only fetched main (empty readme). After manual review of the actual Polling branch + deployed app, this submission has been re-graded to reflect its real work.]
Authentication & Access Control7 / 10
JWT-based auth with both access tokens (15m TTL, Authorization header) and refresh tokens (7d, httpOnly cookie). Email verification flow via Nodemailer/Resend with token-based account activation. AuthGuard component wraps protected routes (frontend/src/components/auth) and redirects unauthenticated users to /login. authenticate middleware validates JWT on protected backend routes. bcryptjs password hashing. Zod-validated registration body (min 8 chars). Solid implementation; missing: rate limiting on auth endpoints, account lockout, no MFA.
Poll Creation & Question Management6 / 15
POST /api/polls creates polls with title, description, dynamic questions array (each with text + options array), optional expiresAt. Zod schema validates the body. PATCH /api/polls/update/:id for status changes (e.g. COMPLETED). DELETE /api/polls/:id with owner-only check. Standard CRUD. Did not verify min/max bounds on questions or option counts; no visible support for question types beyond multiple-choice.
Response Collection Flow6 / 15
Vote submission endpoint paired with Socket.IO real-time broadcast — clients in the poll's room receive live vote count updates. Cannot verify duplicate-vote prevention (one-per-user logic), anonymous-vs-auth split, or rate limiting from README alone. Working deployed app (polling.cohortteam.com) confirms voting flow functional end-to-end.
Analytics & Feedback Dashboard6 / 15
GET /api/polls/:id/analytics returns per-poll vote analytics. Frontend uses Recharts bar charts to render completion analytics on the poll detail page. Standard aggregation pattern; no obvious advanced metrics (no time-series, no peak-activity, no per-question breakdown verified).
Frontend Experience7 / 10
Modern React 19 + Vite 7 + Tailwind CSS v4 + Shadcn UI stack. Multi-page app: landing (hero/features/use-cases/CTA sections), login, signup, verify-email, home (dashboard with search), poll-detail, create-poll, not-found. Light/dark theme via next-themes. Framer Motion entrance animations. Sonner toasts. Merriweather font. AuthLayout vs AppLayout split. Deployed URL returns proper HTML with title 'PollWave — Create & Vote on Polls' — confirms working frontend.
Backend Architecture & API Design7 / 15
Clean modular Express 5 architecture: app/common/middleware (auth, error, http logger, validate) + app/modules/{auth,polling} each with dto/controller/service/route layers. Zod-validated environment config (env.ts) — server refuses to start if env is malformed. Winston logging. Global error handler middleware. Express app factory pattern (app.ts) separating HTTP setup from server bootstrapping. Mongoose for MongoDB. Postman collection included for manual testing. Solid foundation; missing: no centralized request body size limits visible in README, no rate limiter, no Helmet security headers documented.
Real-Time Updates Using WebSockets6 / 10
Socket.IO attached to the same HTTP server as Express. Clients subscribe to per-poll rooms and receive live vote events without REST polling. Standard implementation pattern; not verified: reconnection room re-join, socket-level auth, typed events.
Code Quality & Project Structure7 / 10
TypeScript strict mode (tsconfig). pnpm-based monorepo with separate frontend/server packages. ESLint + Prettier configured. Detailed READMEs in both frontend/ and server/ documenting tech stack, project structure, routes, env variables, and architecture notes. Postman collection for API testing. .env.example files for both. .gitignore. .prettierignore. Professional project hygiene.
33
A
Ripple - Polling made beautiful
Ayush · @ayush64sharma
52
Ripple is a well-built full-stack polling platform (React/Express/PostgreSQL/Socket.IO) with ~6,800 lines of source code. The project demonstrates solid engineering: dual-token JWT auth with rotation and denylist, multi-question wizard builder, defense-in-depth response validation with dedup, Socket.IO real-time updates with JWT-gated creator rooms, and a polished custom CSS design system. The deployment is live and serving the frontend (though API routing appears to be serving SPA HTML fallback for all paths). Key strengths: thorough response validation (question/option ownership checks, mandatory enforcement, dedup), transactional DB writes, well-organized backend module structure, comprehensive UI state coverage, and a clear README. Key weaknesses: no Helmet/CSRF security headers, console.log leaks in production, shared types defined but unused, component/icon duplication, no automated tests, poll editing not supported, and the anonymous/authenticated poll mode flag is never enforced at response collection. Several rough edges in production readiness but overall a competent implementation.
Authentication & Access Control7 / 10
Dual-token JWT (15-min access + 7-day refresh with JTI) stored in httpOnly/secure/sameSite cookies (auth.controller.ts:22-27). bcrypt-12 hashing (auth.ts:5-14). Access token in Zustand memory, not localStorage. Refresh rotation with JTI denylist in PostgreSQL (auth.controller.ts:72-111). Axios interceptor auto-refreshes on 401 with retry dedup (api.ts:18-41). requireAuth + optionalAuth middleware (middleware.ts:11-42). ProtectedRoute with authHydrated pattern prevents flash redirects (App.tsx:25-34). Auth rate limiter at 10/15min (rateLimit.ts:53-57). Gaps: no Helmet, no CSRF, no email verification, no password reset, anonymous/authenticated poll mode flag exists on schema but not enforced at response collection.
Poll Creation & Question Management7 / 15
3-step wizard builder (Poll Details → Questions → Review) across 681 lines in CreatePoll.tsx. Dynamic question add/remove with per-question mandatory toggle (star icon, lines 497-510). Per-question option add/remove with min 2 options enforced (lines 88-93). Expiry date picker with 3 preset buttons (5min/15min/1hr, lines 296-320). Anonymous toggle switch. Review step shows all questions/options/meta. Transactional poll+questions+options creation in DB (polls.service.ts:13-43). Zod validation: title, future expiry, 2-4 options per question, min 1 question (polls.schema.ts:4-16). Gaps: no poll edit endpoint, no drag-and-drop reorder (claims "Drag to reorder" in UI but no handler), no slugs/QR, no draft status, no form library.
Response Collection Flow8 / 15
Step-by-step response UX with progress bar, slide animations between questions, option cards with fill animation (PollPage.tsx:155-281). FingerprintJS for anonymous visitor identity (fingerprint.ts). Server defense-in-depth: poll status check (active only, line 190), mandatory question enforced (lines 245-252), question ID must belong to poll (lines 217-221), option ID must exist AND belong to its question (lines 224-241), dedup via both app-level check + unique compound indexes on (poll,respondent) and (poll,visitor) (schema.ts:48-50, service lines 194-209), transactional response+answers insert (lines 258-273). checkPollExpiry middleware auto-closes (middleware.ts:44-68). Rate limiting at 5/10min (rateLimit.ts:59-63). UI covers loading/not-found/closed/published/error states. Gaps: isAnonymous flag never enforced at response time, no client-side mandatory validation across all questions, client error messages generic.
Analytics & Feedback Dashboard6 / 15
Analytics dashboard (Analytics.tsx, 286 lines): animated stat cards with easing, per-question option breakdowns with colored percentage bars, top option detection, hourly response timeline using DATE_TRUNC (polls.service.ts:406-413), live counter with socket pulse animation. Creator-only access enforced (polls.service.ts:365-367). Published results page (PublishedResults.tsx, 250 lines) shows public results with vote counts, percentages, "Top" badges, share button, animated bars. Dashboard (Dashboard.tsx) lists user polls with response counts calculated via SQL JOIN+GROUP BY (polls.service.ts:57-78). Gaps: no CSV export, no individual response viewing, no anonymous vs authenticated breakdown in analytics, published results has hardcoded "3 questions" text (line 124), no peak activity metrics, no per-question drop-off rates.
Frontend Experience6 / 10
Clean consistent design with custom CSS system (index.css, 480 lines): color tokens, button variants (coral/ghost/danger), input fields with focus states, 15+ animation keyframes, option card fill animation, toggle switch, progress bars. 10 routes with page transitions. All pages cover loading/error/empty/auth states. Toast notifications (react-hot-toast). Copy-to-clipboard with feedback. Dark sidebar with active state. Home page has skeleton cards, live poll feed, feature cards, CTA banner. ThankYou page with animated SVG checkmark and live stats. Gaps: all inline styles (no CSS-in-JS lib), no mobile-responsive sidebar (no hamburger menu), no form library on create poll, console.log left in production (Dashboard.tsx:36, PollPage.tsx:141), no dark mode, Ripple logo SVG duplicated across 5+ files.
Backend Architecture & API Design7 / 15
Clean module architecture (auth/polls with routes→controller→service layers). PostgreSQL/Drizzle ORM with 7 tables, proper FK cascades, 10 indexes including 2 unique compound indexes, custom poll_status enum (schema.ts:4-6). Zod validation at API boundary with safeParse (polls.schema.ts). ApiError class hierarchy with factory methods (errors.ts). Centralized error handler (index.ts:53-62). JWT denylist table (refreshTokenDenylist). Transactional writes for poll creation and response submission. In-memory sliding window IP rate limiter (rateLimit.ts). Auto-close expired polls at 60s interval. CORS configured. Gaps: no Helmet/security headers, no request body size limits, non-null assertions on req.user (polls.controller.ts:35,37,118), shared types defined but never imported by client or server, no pagination on any GET endpoints, deletePoll lacks transaction wrapper.
Real-Time Updates Using WebSockets6 / 10
Socket.IO integrated with Express HTTP server (index.ts:21-27). Room-based architecture with three room types: poll:{id} (public status), poll:{id}:creator (JWT-gated via verifyAccessToken on join, index.ts:83-95), poll:{id}:thankyou (public stats). Events: poll:response_received to creator room (polls.service.ts:285-288), poll:stats_update to thankyou room (polls.service.ts:290-292), poll:status_changed on close (middleware.ts:59) and publish (polls.service.ts:177). Best-effort socket emits with try/catch to prevent DB rollback (polls.service.ts:278-296). Client joins rooms and refetches on events (Analytics.tsx:117-141, PollPage.tsx:65-77, ThankYou.tsx:52-57). Socket disconnect on unmount. Gaps: events are pure invalidation signals (no data pushed over socket), no reconnection/room rejoin handling, no typed events, no socket authentication beyond creator join, no events on poll deletion.
Code Quality & Project Structure5 / 10
TypeScript strict mode across all packages. pnpm monorepo (client/server/shared). Consistent naming and module separation. ESLint on client. README is thorough with architecture, API summary, tech stack, quick start, and deployment info (README.md:114 lines). Docker Compose for local Postgres dev. Gaps: no automated tests (root package.json: "Error: no test specified"), shared types in shared/ package defined but never imported by client or server — both redefine interfaces independently. Spinner component duplicated 3 times (Login.tsx:202, CreatePoll.tsx:661, Register.tsx:322). Ripple logo SVG duplicated across 5 files. Console.log statements left in production code in 5+ files. No .env.example files exist despite README claiming they should. Deprecated z.iso.datetime() used in poll schema. Several eslint-disable comments for any types.
34
Votora
Ayush Panda · @pandaayush25305_ef3d577f
52
Votora is a well-built MERN polling platform with impressive attention to UX (Framer Motion animations, comprehensive state coverage, dark theme) and solid backend architecture. Notable highlights: anti-cheat quiz mode with tab-switch detection, dual timer system (expiry + manual), MongoDB aggregation analytics with live Recharts visualization, Socket.IO room-based real-time updates, Google OAuth support, and a clean MVC pattern. The main weaknesses are: dual token storage (localStorage + cookie) undermining security claims, duplicate/dead code files, no TypeScript, minimal test coverage, and no Helmet/CSRF protection. A strong hackathon project that delivers on most requirements with good production polish.
Authentication & Access Control6 / 10
Solid auth: bcrypt 12 rounds, JWT in httpOnly cookies with secure/sameSite config (constants/index.js:43-48), protect + optionalAuth middleware (auth.middleware.js), Google OAuth with google-auth-library (auth.service.js:36-65), signup/login/googleAuth endpoints with express-validator (auth.validator.js). Anonymous vs authenticated poll modes enforced server-side (response.service.js:12-14, poll.service.js:128-129). Key gap: token also stored in localStorage (authSlice.js lines 8,18,73) alongside httpOnly cookie, undermining XSS protection claim. Missing: no Helmet, CSRF, email verification, password reset, or refresh token rotation. authLimiter and apiLimiter both set to 500 (rateLimit.middleware.js) — auth should be stricter.
Poll Creation & Question Management7 / 15
Dynamic multi-question builder (CreatePollPage.jsx, 485 lines) with add/remove questions, per-question mandatory/optional toggle, per-question options with add/remove, quiz mode with correct answer marking, anti-cheat toggle, dual time system (expiry datetime picker + timer duration). Edit poll page (EditPollPage.jsx, 204 lines) with full form management. Duplicate/clone poll (poll.service.js:108-144). Backend validation via express-validator (poll.validator.js) and XSS sanitization (poll.service.js:21-22). Gaps: no form library (manual useState), no question reordering, no rich text, questions identified by array index rather than stable IDs.
Response Collection Flow7 / 15
Defense-in-depth: expiry check (expiry.middleware.js + cron job in expiry.job.js), timer system with waiting-room state (PublicPollPage.jsx:216-238), duplicate prevention by userId AND IP (response.service.js:34-41), mandatory question enforcement both client-side (PublicPollPage.jsx:165-172) and server-side (response.service.js:44-55), anti-cheat tab detection auto-submit (PublicPollPage.jsx:106-129), atomic $inc for response count (response.service.js:72), auto-submit on timer expiry (PublicPollPage.jsx:89-103). Gaps: no server-side validation that submitted optionId exists in poll's questions (validator only checks questionIndex is integer — response.validator.js), IP-based duplicate prevention is weak.
Analytics & Feedback Dashboard7 / 15
MongoDB aggregation pipeline analytics (calculateAnalytics.js, 97 lines) computing per-question option counts, percentages, answered/skipped. Analytics dashboard (AnalyticsPage.jsx, 324 lines) with BarChart + PieChart per question via Recharts, animated progress bars, stat cards (total responses, participants, questions, poll code), respondents grid with quiz scores, CSV export (lines 56-79). Public results page (PublicResultsPage.jsx, 192 lines) with published bar charts. Publish toggle (poll.service.js:96-106) makes results public. Presentation mode (PresentationPage.jsx) for screen-sharing. Gaps: no individual response drill-down, no time-series trends, no peak activity metrics, no PDF export, analytics always recalculated on fetch.
Frontend Experience7 / 10
Polished dark theme UI (Tailwind + custom CSS) with Framer Motion animations throughout (page transitions, AnimatePresence for question add/remove, animated progress bars). 16+ routes with ProtectedRoute/PublicRoute wrappers (AppRoutes.jsx). Comprehensive state coverage: loading spinners, SkeletonCard (SkeletonCard.jsx), error states (PublicPollPage shows expired/not-found/auth-required/waiting-room/submitted/quiz-results), empty states (DashboardPage), onboarding flow. Responsive DashboardLayout with mobile sidebar (DashboardLayout.jsx, 164 lines). Reusable components: Button, Input, Modal, Badge, Tooltip, Spinner, Logo, CenteredToast, ContextualHelp. Landing page with feature cards and testimonials (LandingPage.jsx). Gaps: no form library (all useState), no TypeScript, zustand in package.json but unused, some inline styles.
Backend Architecture & API Design6 / 15
Clean MVC architecture: routes→controllers→services→models across 5 route files, 5 controllers, 7 services. express-validator for input validation (3 validators), global error middleware handling CastError/ValidationError/duplicate key/JWT errors (error.middleware.js, 47 lines), ApiError class for consistent JSON errors, asyncHandler wrapper on all controllers, rate limiting via express-rate-limit, XSS sanitization on poll title/description (poll.service.js), cookie-based JWT, cron job for auto-expiry. Gaps: duplicate error middleware file (middlewares/error.middleware.js unused), empty dead files (config/oidc.js, services/oidc.service.js), no Helmet, identical rate limits for auth and API (500 each), debug console.logs in production (poll.controller.js:21, poll.service.js:147,152), no MongoDB transactions used, no request body size limits.
Real-Time Updates Using WebSockets7 / 10
Socket.IO with room-based pattern: poll rooms for participants (poll.socket.js, 57 lines) and analytics rooms for dashboard (analytics.socket.js, 14 lines). Six event types: JOIN_POLL, LEAVE_POLL, NEW_RESPONSE, ANALYTICS_UPDATE, PARTICIPANT_COUNT, POLL_EXPIRED, POLL_PUBLISHED, START_TIMER, TIMER_STARTED. Participant counting with join/leave/disconnect tracking. Timer start via socket (creator clicks in AnalyticsPage, server emits TIMER_STARTED to all poll room members). Socket service abstraction (socket.service.js, 47 lines) for clean emits from anywhere. Client listens on PublicPollPage and AnalyticsPage with proper cleanup. Poll expiry broadcast via cron job (expiry.job.js:27-28). Gaps: no socket auth, analytics handler uses hardcoded 'subscribe_analytics' strings instead of SOCKET_EVENTS constants, no debouncing on analytics updates, full analytics data pushed over socket rather than delta.
Code Quality & Project Structure5 / 10
Clean feature-based structure (client: features/auth, features/polls, features/analytics, features/publicPoll, server: routes/controllers/services/models/middleware/validators/sockets/utils). Redux Toolkit with createAsyncThunk (pollSlice.js, authSlice.js). Custom hooks (useDebounce, useLocalStorage, useTheme). Consistent naming conventions. Comprehensive README with setup instructions and compliance checklist. Constants for shared enums. Gaps: duplicate error middleware file (middlewares/error.middleware.js unused), empty dead config files (config/oidc.js exports {}), scratch directory with leftover test, debug console.logs in multiple server files, no TypeScript (entirely JavaScript), only 1 test file (quiz.test.js with 3 basic unit tests), no ESLint on server, token storage duality (localStorage + cookie), zustand listed but unused.
35
Open Polls
Anand · @anandxdj
51
Open Polls is a full-stack polling platform built with Next.js (frontend), Express.js (backend), MongoDB, Redis, Socket.IO, and Docker. Two working deployments verified. The project has strong architecture with clean module separation, comprehensive auth (JWT + Google OAuth + email verification), AI-assisted poll creation via Google Gemini, Redis-buffered analytics with real-time WebSocket updates, and a polished dark-themed UI with Framer Motion animations. Primary gaps: missing security hardening (no Helmet, no rate limiting), 27 'as any' type assertions, hardcoded respondentId defeating authenticated tracking, no root README, and minimal test coverage. Total: 51/80.
Authentication & Access Control7 / 10
Comprehensive auth system with email/password registration (bcrypt 12 rounds), Google OAuth with PKCE flow, JWT access+refresh tokens with httpOnly cookie, email verification via Resend, forgot/reset password, and auth middleware checking Bearer token. Frontend Zustand store with auto-refresh interceptor. Notable gaps: no rate limiting on auth endpoints, no Helmet security headers, access token persisted in localStorage (XSS risk), auth middleware cookie fallback reads wrong cookie name ('token' vs 'refreshToken'), and response submission endpoint doesn't enforce authenticated vs anonymous mode.
Poll Creation & Question Management7 / 15
PollBuilder.tsx (680 lines) supports dynamic questions with add/remove, mandatory/optional toggle, anonymous/authenticated toggle, expiry datetime picker, and publish/draft status. AI-assisted generation via Google Gemini + LangChain StateGraph. Server-side Zod validation enforces min 1 question and min 2 options per question. Draft auto-save with cleanup on unmount. Backend syncs analytics counts when questions are updated. Missing: no slug/permalink for sharing, no form library (manual useState hooks), no QR code, questions use array index-based navigation rather than IDs.
Response Collection Flow6 / 15
StepByStepForm.tsx provides one-question-at-a-time UX with Framer Motion animations, progress bar, and device ID-based anonymous voting via localStorage. Server validates: poll existence, closed/expired gates, mandatory question enforcement, and option index ranges. Redis-based duplicate vote prevention (24hr deviceId lock). Analytics buffered in Redis hashes for performance. Gaps: respondentId hardcoded to 'mock-respondent-123' in responses.service.ts:68 defeating authenticated tracking, no per-question duplicate answer prevention on server, frontend always submits all questions regardless of isMandatory flag, no CSRF token on submission, no server-side single-option enforcement for individual questions.
Analytics & Feedback Dashboard6 / 15
Analytics dashboard at /analytics/[id] with total response count, per-question option breakdowns with bar/pie/donut visualization choice, top choice highlighting, and published results public view. Redis-buffered analytics worker (2-second flush via bulkWrite) with cache layer (1hr TTL). Recovery logic recreates missing analytics docs. Real-time updates via Socket.IO. Missing: no CSV/PDF export, no individual response viewing, no trend/time-series data, no drop-off rates between questions, no anonymous vs. authenticated breakdown, no peak activity metrics, no poll duration/health stats.
Frontend Experience7 / 10
14 production URL patterns with Next.js App Router, shadcn/ui components, dark theme with custom gradients/glows, Framer Motion animations throughout (page transitions, option selection, progress bars). All states covered: loading skeletons, error banners, empty states, not-found handling, auth redirects. Polished landing page with hero animation, BentoGrid features, steps, pricing, FAQ, and logo marquee. Issues: frontend/README.md is unmodified Vercel starter template, app/test/ directory with experimental code committed, no form library on poll creation, 1,927-line time-picker component, accessToken in localStorage.
Backend Architecture & API Design6 / 15
Clean module-based architecture (routes→controllers→services→models) with Zod validation middleware on all endpoints. 24 API endpoints across auth/polls/responses/ai/health. Redis used for deduplication, analytics buffering, caching, and pub/sub. MongoDB with 4 collections and embedded schemas. ApiError/ApiResponse standardized patterns. Docker compose for dev infrastructure. Significant gaps: no Helmet/security headers, no rate limiting on any endpoint, 27 'as any' type assertions (15 in route handlers alone), missing indexes on creatorId/verificationToken/resetPasswordToken, no MongoDB sanitization, no request body size limits, response service hardcodes respondentId as mock value.
Real-Time Updates Using WebSockets6 / 10
Socket.IO server integrated with Express, room-based pattern (poll:join/poll:leave from client). Redis pub/sub bridge: analytics worker publishes to Redis channel, dedicated subscriber client relays to Socket.IO rooms. Frontend useSocketListener hook handles connect/join/listen/leave/disconnect lifecycle. Analytics payload pushed as real-time data to rooms every 2 seconds on vote flush. Gaps: no socket authentication (any client can join any room), no reconnect logic on Redis subscriber, no debouncing on frontend updates, only one event type (analytics:update — no poll status/close/publish events), no real-time integration on public voting page, sPop processes only one poll per tick (serial bottleneck).
Code Quality & Project Structure6 / 10
Well-organized monorepo with backend/frontend separation, feature-based module organization, consistent naming conventions, TypeScript strict mode with noUncheckedIndexedAccess. Zustand stores with clean patterns. ApiError factory and ApiResponse wrapper are well-designed. Issues: no root README.md (only backend/README.md and unchanged Vercel starter in frontend/README.md), 27 'as any' type assertions throughout backend, PollCard.tsx is dead code but PollCardV2.tsx duplicates ~70 lines from it, handleShare function duplicated in 3 files, console.log debug statements remain in analytics page, app/test/ experimental code committed, 1,927-line time-picker.tsx is maintenance burden, only 2 brittle smoke tests with monkey-patching.
36
PollSphere
himanshu Anand · @himanand2020_b73b3532
51
PollSphere is a full-stack polling platform with Clerk authentication, Socket.IO real-time updates, and a strong neo-brutalist frontend. Strengths: thorough response collection with defense-in-depth (expiry, auth mode, duplicate prevention, mandatory checks), comprehensive analytics with Recharts and MongoDB aggregation, Framer Motion animations throughout, and proper Socket.IO room-based architecture with Redis adapter. Weaknesses: the service layer is mostly empty stubs, 25+ files are empty/unimplemented, questions cannot be edited after creation, no database-level unique vote index (deliberately removed), App.tsx is dead Vite boilerplate, no tests, no CI/CD, and several hardcoded URLs. Total score 51.
Authentication & Access Control7 / 10
Clerk-based auth with proper requireAuth middleware (auth.middleware.ts:9) and looseAuth for public routes (public.routes.ts:13). Auth sync endpoint (auth.controller.ts:5-38) syncs Clerk users to MongoDB. Frontend uses ClerkProvider (main.tsx:22) with Axios interceptor auto-attaching tokens (axios.ts:11-21). Anonymous vs authenticated enforcement on both server (response.controller.ts:74-84) and client (PollPage.tsx:102-115). Gaps: entirely Clerk-dependent with no custom registration/login, empty clerk.ts lib file (1 line), no CSRF protection, no session management.
Poll Creation & Question Management6 / 15
Two-step poll creation: CreatePollForm.tsx (97 lines) with React Hook Form + Zod for poll metadata, QuestionForm.tsx (158 lines) with useFieldArray + Zod for dynamic options (add/remove). Supports mandatory/optional toggle per question. Draft resuming via URL search params (CreatePollPage.tsx:31-45). Backend validates via Zod (poll.validator.ts, question.validator.ts). Gaps: questions cannot be edited (only delete+recreate, CreatePollPage.tsx:103-114), no atomic poll+questions creation, options cannot be modified after creation (updateQuestionSchema explicitly excludes them), no slug/permalink generation (uses raw MongoDB _id).
Response Collection Flow8 / 15
Excellent defense-in-depth in submitResponse (response.controller.ts:45-165): strict expiry check with auto-status update (lines 59-72), auth mode enforcement (lines 74-84), duplicate prevention via voterId for authenticated users and IP-based for anonymous users (lines 86-116), mandatory question validation (lines 118-135). Rate limiting on vote submission: 10 req/60s (public.routes.ts:19). Zod validation for answers array (response.validator.ts). Redis-based rate limiter (rateLimiter.ts). Frontend handles all states: loading, error, auth-required, expired (PollPage.tsx:63-115). Socket.IO broadcast on submission. Gaps: no server-side validation that optionIds belong to poll's questions, database unique index deliberately removed (server.ts:39-45), no transaction for response creation, no frontend localStorage duplicate tracking.
Analytics & Feedback Dashboard7 / 15
MongoDB aggregation pipeline for per-option vote counting and timeline data (analytics.service.ts:11-69, 91 lines). AnalyticsPage.tsx (418 lines) with stat cards (total responses, live status, peak performance), animated progress bars via Framer Motion, per-question Recharts PieChart with donut visualization, AreaChart for timeline "vote flux" with alternating color segments. PublishedResultPage.tsx (352 lines) for public results. Public results endpoint checks poll.status === 'published' (analytics.controller.ts:36-61). Most-voted option detection. Gaps: no CSV/export, no individual response viewing, no response rate metrics, no demographic breakdown, timeline limited to 10 data points, no date filtering.
Frontend Experience8 / 10
Strong neo-brutalist design language throughout (heavy borders, hard shadows, bold typography) with Tailwind CSS v4. 1350-line HomePage with 16 animated feature sections, interactive live demo with simulated voting, FAQ accordion. Framer Motion animations: page transitions, scroll-triggered reveals, animated progress bars, vote flux visualization. Comprehensive UI library (Button, Card, Input, Badge, RadioGroup, Skeleton, Loader, Modal). Dark/light mode with ThemeProvider (next-themes). All pages cover loading/error/empty/auth states. Responsive mobile layout with hamburger menu. Sonner toast notifications. Gaps: App.tsx is 122 lines of unused Vite boilerplate, App.css is dead code, no confirmation dialogs beyond window.confirm, manual URL slug generation.
Backend Architecture & API Design5 / 15
Clean routing structure (routes→controllers→models) with Zod validation middleware (validate.middleware.ts). Proper Mongoose schema separation (Poll, Question, Option, Response, User). Security: Helmet (app.ts:13), Morgan (app.ts:14), CORS (app.ts:12), Redis-based rate limiting (rateLimiter.ts). MongoDB aggregation for analytics. Docker support. Gaps: service layer is mostly empty stubs (poll.service.ts, response.service.ts, auth.service.ts, etc. all 1 line), no centralized error middleware (error.middleware.ts is empty), no request body size limits, env.ts is empty (no Zod env validation), dynamic require() anti-pattern in response.controller.ts:149, index-drop hack in server.ts:39-45, raw error.message leaked to clients in multiple catch blocks, publish.controller.ts is empty.
Real-Time Updates Using WebSockets6 / 10
Socket.IO server integrated with Express (server.ts:22-28) using Redis adapter for horizontal scaling (server.ts:19,23). Room-based pattern: clients join/leave `poll_<id>` rooms (sockets/index.ts:14-24). Server emits `poll_updated` with full analytics on vote submission (response.controller.ts:153-156) and `room_count_update` for active viewer counts (sockets/index.ts:4-8). Frontend socket client with autoConnect:false (socket.ts:7-9) connects on PollPage/AnalyticsPage mount, leaves room on unmount. Proper cleanup with socket.off + disconnect. Disconnecting handler updates room counts (sockets/index.ts:26-37). Gaps: analytics.socket.ts and poll.socket.ts are empty stubs (1 line each), no typed Socket.IO events, no socket authentication, pushes full analytics over wire rather than invalidation signal, no client-side reconnection room re-join logic, only 2 event types, hardcoded production URL in socket.ts.
Code Quality & Project Structure4 / 10
Clean monorepo with backend/frontend separation, TypeScript throughout, Docker support. Well-organized component categories (ui/, layout/, poll/, analytics/). React Hook Form + Zod for forms. ESLint configured on frontend. Gaps are severe: 25+ empty/1-line files across both projects (poll.service.ts, response.service.ts, analytics.socket.ts, poll.socket.ts, useAnalytics.ts, useAuth.ts, usePoll.ts, useSocket.ts, ResponseForm.tsx, DashboardPage.tsx, etc.), App.tsx is 122 lines of dead Vite boilerplate code, App.css is unused starter CSS, `as any` type assertions scattered (QuestionForm.tsx:28-29), hardcoded production URLs in socket.ts and axios.ts, no automated tests anywhere, no CI/CD, dynamic require() in TypeScript (response.controller.ts:149), no pre-commit hooks, console.log in production code, no barrel exports beyond routes/index.ts.
37
Pollflow
Mayank Saini · @mayanksaraswal
51
Pollflow is an ambitious Next.js polling + live quiz platform with substantial feature depth: 18 pages, 43 components, 16 API routes, 14 database models, 16 Socket.IO events, and an AI-powered quiz generator. The response collection flow (param-3) is the standout with thorough defense-in-depth validation. The Socket.IO implementation (param-7) is rich with two namespaces covering both polls and live quiz sessions. The frontend (param-5) is polished with a consistent dark theme and thoughtful one-question-at-a-time polling UX. However, the project is held back by: no tests, boilerplate README, PostgreSQL instead of MongoDB (track requirement), questions not being editable after creation, dead code and duplication, unused dependencies, missing email verification/password reset, and an incorrect analytics participation rate metric. The quiz engine is impressive bonus work but may have spread implementation effort thin across the core polling features. Deployed and accessible at pollflow-eight.vercel.app.
Authentication & Access Control6 / 10
NextAuth with 3 providers (Google OAuth, GitHub OAuth, Credentials with bcrypt 12 rounds). Registration endpoint at `/api/auth/register/route.ts` with Zod validation and email uniqueness check. JWT session strategy with PrismaAdapter. voterMode (ANYONE/AUTHENTICATED_ONLY) enforced in both response route (respond/route.ts:46-48) and public poll page (page.tsx:71-76). isAnonymous flag controls respondent identity recording. Auth-gated dashboard routes via inline `auth()` checks. Gaps: no email verification, no password reset, no registration rate limiting, `proxy.ts` is dead code at wrong path (not `middleware.ts`), no CSRF beyond Next.js defaults, no Helmet/security headers.
Poll Creation & Question Management6 / 15
React Hook Form with Zod resolver in poll-builder.tsx (423 lines). useFieldArray for dynamic questions (max 20) and options (min 2, max 10). Mandatory/optional toggle per question via Asterisk button. Poll metadata: title, description, voterMode toggle, isAnonymous switch, expiresAt datetime-local picker. Server-side creation in polls/route.ts with nested question/option creation in one Prisma call, nanoid(10) shareToken, rate limiting (5/10min). Poll listing with pagination and auto-expiry. Critical gap: questions/options cannot be edited after creation — PATCH endpoint only updates metadata (title, description, voterMode, etc.), confirmed in updatePollSchema which extends partial of createPollSchema. QuestionBlock sub-component uses `any` types (lines 277-279). Duplicate question builder implementations (QuestionBlock vs QuestionBuilder). No draft status.
Response Collection Flow8 / 15
Exceptional defense-in-depth in respond/route.ts (137 lines): IP-based rate limiting (10/min via Upstash), poll existence/status/expiry checks with auto-expire, voterMode auth gate, Zod body validation with CUID for questionId/optionId, mandatory question enforcement returning specific missing IDs, per-answer validation that option belongs to correct question (lines 69-78), duplicate prevention for both authenticated (compound unique index + explicit check, line 81-85) and anonymous users (ipHash, lines 86-92), transactional write via Prisma $transaction (line 101-115). PollForm.tsx (324 lines) provides one-question-at-a-time UX with progress bar, mandatory validation with jump-to-missing, option selection with radio indicators, success state. Differentiated HTTP status codes (400/401/404/409/410/429). Already-responded detection at page load and API level. Real-time socket broadcast on submission.
Analytics & Feedback Dashboard6 / 15
Analytics endpoint (analytics/route.ts, 105 lines) with creator-only access, Redis caching (15s TTL), Prisma groupBy for answer aggregation, per-question option stats with counts and percentages, hourly activity timeline (last 24 hours), total response count. AnalyticsDashboard component (262 lines) with LiveCounter (pulse animation), KPI cards (responses, questions, mode, access), status pill with live indicator, Question Breakdown with ResponseChart per question, empty state ("Waiting for responses"), last update ticker, expiry info, copy link/publish actions, real-time socket updates. Public results page at `/p/[shareToken]/results/page.tsx` (172 lines) with hero, stats, per-question option breakdowns, CTA. Gaps: no CSV export for polls (only quiz has it), no individual response viewing, no anonymous/authenticated breakdown, `participationRate` calculation is conceptually wrong (computes mandatory ratio, not actual user participation rate at line 70-72), no pie charts or overall summary visualization.
Frontend Experience7 / 10
Consistent dark theme (Tailwind CSS, `#09090B`/`#111113` palette). 18 pages across marketing, auth, public polls, dashboard, and quiz. Landing page (320 lines) with hero, stats bar, 2×3 features grid, how-it-works section, CTA banner. PollForm uses one-question-at-a-time UX with progress bar, radio indicators, dot navigation, animation classes. 43 reusable components (22 shadcn UI primitives, 7 poll-specific, 6 shared, 4 analytics, 3 layout, 1 upload). Loading states via loading.tsx, Skeleton, LoadingSpinner. Error states via error.tsx, ErrorBoundary, sonner toasts. Empty states via EmptyState component and analytics "Waiting for responses." All auth/public/dashboard states handled with redirects. Responsive layout (Sheet sidebar on mobile). Gaps: no confirmation dialogs for destructive actions (beyond delete), no QR code generation for sharing, landing page uses fake static stats ("10K+ polls created"), `any` types in QuestionBlock sub-component, no PWA/offline support.
Backend Architecture & API Design6 / 15
PostgreSQL + Prisma ORM (not MongoDB as the MERN track specifies). Well-designed schema with 14 models, 6 enums, proper foreign keys, cascading deletes, and indexes (schema.prisma, 265 lines). 16 API route files following consistent pattern: auth check → validation → business logic → standardized apiSuccess/apiError response. Four Zod validation schema files (12 schemas). Redis caching with TTLs for polls, analytics, user polls. Three Upstash rate limiters (poll submission, poll creation, quiz generation). api-response.ts (63 lines) sanitizes server errors, hashes IPs with SHA-256+secret. Graceful Redis degradation throughout. Gaps: uses PostgreSQL instead of MongoDB, no middleware.ts (proxy.ts is dead code), no Helmet/security headers, no request body size limits, default dev-secret for socket auth in two locations, duplicate Redis client in quiz/generate/route.ts, no service layer (all logic inline in route handlers), no API versioning, no graceful shutdown.
Real-Time Updates Using WebSockets7 / 10
Standalone socket-server (index.ts, 154 lines) with Express + Socket.IO. Two namespaces: default (polls) with join:poll/leave:poll rooms and `/quiz` namespace with quiz:join/quiz:leave + admin rooms. 16 custom events: 3 poll events (response:new, poll:published, poll:expired) + 8 quiz events (lobby_update, question_start/end, answer_ack, admin_answer_tick, leaderboard, quiz:end, admin_stats). Room-based pattern: `poll:{pollId}`, `quiz:{sessionId}`, `quiz:{sessionId}:admin`. Internal HTTP endpoint for backend-to-socket bridge with Bearer token auth. Client hooks: useSocket with reconnection (5 attempts, 1s delay), callback refs for stable listeners, proper join/leave lifecycle and cleanup on unmount. useQuizSocket for quiz real-time. Redis-based quiz state management with HSET, sorted sets for leaderboards, atomic HSETNX for answer dedup. Gaps: socket bridge uses HTTP instead of true pub/sub (adds latency, no built-in retry), no authentication on WebSocket connections themselves, default dev-secret for internal endpoint, no socket integration on public poll answering page.
Code Quality & Project Structure5 / 10
TypeScript throughout with Next.js 16 App Router. Clean folder structure: src/app (routes/pages), src/components (UI + feature), src/hooks, src/lib, src/types. Consistent naming conventions. TanStack Query for data fetching, React Hook Form for forms. Standardized API response pattern with error sanitization. ESLint + Prettier configured. Major issues: zero test files anywhere, README is unmodified Next.js starter boilerplate (no project documentation), no .env.example (12+ required env vars undocumented). Dead code: lib/socket.ts (never imported), proxy.ts (wrong path, never executed), hashIp in utils.ts (base64, reversible, never called), CACHE_KEYS/QUIZ_EVENTS in constants.ts (duplicates redis.ts). 10 instances of `any` types. Duplicate: two question builders, two CACHE configs, two Redis clients. Unused dependencies: dotenv, @base-ui/react, ioredis, @types/ioredis. Dependencies miscategorized: @types/bcryptjs and @types/ioredis in deps instead of devDeps. duplicate tailwindcss-animate + tw-animate-css. quiz/generate/route.ts breaks API response contract (raw NextResponse.json). participationRate metric is conceptually incorrect. SOCKET_INTERNAL_SECRET defaults to 'dev-secret'.
38
T
Pollify
Tamal Sarkar · @dt89
51
Pollify is a solid full-stack polling platform with a polished dark-themed React frontend, comprehensive JWT-based auth (registration, email verification, refresh rotation, password reset), proper poll creation with dynamic multi-question support, and real-time Socket.io updates. The 6-table PostgreSQL/Drizzle schema is well-normalized. Frontend features Framer Motion animations throughout with comprehensive state handling (loading, error, empty, auth). Key strengths: clean architecture, good validation coverage, working real-time updates with room-based Socket.io, and mature auth implementation. Main weaknesses: no duplicate vote prevention, no poll editing, no CSV export, zero tests, `any` types and console.logs in production code, no Helmet/rate limiting, and some environment config inconsistencies. Overall a well-executed hackathon project with production-quality polish in the frontend but missing some backend robustness features.
Authentication & Access Control7 / 10
Full auth with registration (email + bcrypt), email verification via Nodemailer, JWT dual-token (15min access + 7day refresh in httpOnly/secure/sameSite cookies), refresh rotation with SHA-256 hash comparison (auth/service.ts:83-105), forgot/reset password, authenticate + optionalAuthenticate middlewares. Auth-protected poll routes (create, analytics). Anonymous vs authenticated mode enforced server-side (polls/services.ts:84-86). Frontend AuthContext with Axios interceptor for auto-refresh (axios.ts:14-36). Protected route guard in _authenticated.tsx. Gaps: no Helmet, no rate limiting, no CSRF, access token also stored in localStorage alongside cookies, no frontend UI for email verification or password reset, forbidden status code is 412 instead of 403 (api-errors.ts:22).
Poll Creation & Question Management7 / 15
Zod-validated CreatePollDto enforces title (min 3), isAnonymous, expiresAt, questions array (min 1) each with text (min 1), isMandatory, and options (min 2 strings). Transaction-based creation inserts poll→questions→options (services.ts:18-52). Client-side: 438-line create form (polls.create.tsx) with TanStack React Form, dynamic question add/remove with AnimatePresence, per-question option add/remove, mandatory/optional toggle, anonymous toggle, datetime-local expiry picker with min constraint. Client-side validation checks title, expiry, question text, and minimum 2 options (lines 59-67). Gaps: no poll editing endpoint or UI, no slug/permalink, no QR code, no draft status.
Response Collection Flow6 / 15
Server-side validation chain: poll existence check (services.ts:78), expiry enforcement (line 80-82), anonymous/authenticated mode enforcement (lines 84-86), mandatory question validation (lines 88-96). Transaction-based submission creates response + answers atomically (lines 98-117). Option counts computed within same transaction. Client-side: 364-line response page (polls.$pollId.tsx) with 6 UI states (loading, error, submitted, published, expired, auth-required, active), progress bar, live response/viewer counts via Socket.io, animated option selection. Gaps: no duplicate vote prevention (no unique index on {pollId, respondentId} and no localStorage tracking — same user can submit unlimited responses), no server-side validation that submitted optionIds belong to the correct questions, no per-question single-option enforcement server-side.
Analytics & Feedback Dashboard6 / 15
Server analytics (services.ts:156-181) returns totalResponses + optionCounts grouped by questionId/optionId via SQL aggregation with creator-only access check. Results endpoint (lines 220-278) with published-gate, per-question option counts, total responses. Publish sets isPublished=true + expiresAt=now() (lines 184-193). Analytics page (491 lines): animated SVG ResponseRing, 4 stat cards, per-question OptionBar with Framer Motion animations, top-option checkmark, live indicator, "New response" flash notification, copy-to-clipboard. Results page (220 lines): public view with animated progress bars, viewer count. Dashboard (389 lines): polls list with status badges, response counts, publish/delete actions. Gaps: no CSV export, no individual response viewing, no trend/time-series data, no peak activity metrics, no anonymous vs authenticated breakdown, no charts (bar/pie/line).
Frontend Experience7 / 10
Polished dark theme (zinc-950/900/800 + red accent) with Framer Motion animations throughout (page transitions, staggered cards, progress bars, hero parallax, nav pill animation). Landing page (707 lines) with interactive LivePollWidget/AnalyticsWidget, animated counters, FAQ accordion, feature grid. TanStack Router with file-based routing, protected route guard. TanStack React Form + Zod for form validation. Comprehensive state handling: loading spinners, error states, empty states, auth-required redirects on all pages. shadcn/ui components (Button, Input, Label, Badge, Card, etc.). Sonner toast notifications, copy-to-clipboard, responsive mobile layout with hamburger menu. Dark/light theme toggle (theme-provider.tsx:231 lines custom implementation). Navbar with layoutId animation, user dropdown with click-away. Gaps: no forgot-password/reset-password/email-verify UI pages (API endpoints exist but no client pages), explore "popular" sort uses poll ID desc instead of response count (services.ts:299), console.log left in 5 locations across production code.
Backend Architecture & API Design6 / 15
Clean controller→service→route separation across auth and polls modules. Zod-based DTO validation via BaseDto pattern (base.dto.ts). 6-table PostgreSQL/Drizzle schema with proper normalization: users, polls, questions, options, responses, answers with foreign keys and cascade deletes. Transaction usage for poll creation and response submission. ApiResponse/ApiError utility classes for consistent response format. Refresh token rotation with hash comparison. Optional auth middleware properly implemented (catches errors silently). Gaps: no global error handler middleware (ApiErrors thrown in controllers lack catch formatting), no Helmet security headers, no rate limiting, no request body size limits, CORS hardcoded to localhost:5173, `forbidden` status returns 412 instead of 403, pollId typed as `any` in multiple service functions, no pagination on getMyPolls, DATABASE_URL with non-null assertion, @ts-ignore in jwt-utils.ts, env var naming mismatch between env.example (EMAIL_*) and code (SMTP_*).
Real-Time Updates Using WebSockets7 / 10
Socket.io server attached to HTTP server (socket/index.ts) with room-based pattern: poll:<pollId> (response/analytics) and feed (explore page). Server emits new-response with totalResponses+optionCounts, feed-activity with pollTitle+totalResponses, and viewers-update with per-room viewer count. Viewer presence tracking via Map<string, Set<string>> with proper disconnect cleanup (lines 52-62). Response page (polls.$pollId.tsx): listens for new-response to update live counts, viewers-update for viewing count. Analytics page: separate socket connection for React Query cache invalidation with 30s polling fallback. Results page: re-joins on reconnect, invalidates query cache. Explore page: joins feed room, shows toast notifications. Gaps: analytics page creates duplicate Socket.io connection instead of using shared singleton from lib/socket.ts, no data pushed over socket (invalidation-only, client refetches HTTP), no socket authentication, no typed Socket.io events, no debouncing on analytics refetch, usePollSocket hook connects to wrong port (3000 vs 8080).
Code Quality & Project Structure5 / 10
Clean monorepo with client/server separation. Consistent module organization (dto/controller/service/routes on server; api/components/hooks/lib/routes on client). TypeScript throughout with Zod validation on both sides. Prettier + ESLint configured. Comprehensive README with setup instructions, API reference, DB schema diagram, real-time events docs, and .env.example. Docker compose for PostgreSQL. Consistent naming conventions. Gaps: zero automated tests anywhere (no test files, no test scripts in package.json), console.log in 7 places across production code (polls.$pollId.tsx:106, _authenticated.tsx:9-10, dashboard.tsx:58,89, app/index.ts:23, socket/index.ts:15), `any` types in multiple client components, @ts-ignore in jwt-utils.ts (lines 7,18), server package.json name is "express-typescript-auth-remake" (template artifact), client/README.md is Vite starter boilerplate, analytics page creates duplicate Socket.io connection, env var naming mismatch (EMAIL_* vs SMTP_* between env.example and code), no client .env.example, usePollSocket connects to wrong port (3000 vs 8080).
39
K
Pollit
Khushal kedawat · @khushal.kedawat
51
Pollit is a well-architected full-stack polling platform with strong backend engineering and solid Socket.io integration. The PostgreSQL schema with partial unique indexes for duplicate prevention and transaction-based operations show real database engineering skill. The response collection flow has genuine defense-in-depth validation. The frontend has a consistent, accessible design with comprehensive state coverage. Key gaps preventing higher scores: no TypeScript or tests (zero), anonymous duplicate prevention gap in response collection, no security hardening (Helmet, rate limiting), and some code duplication. Overall a solid 51/100 — a genuine, working application with good architecture that lacks polish and production hardening.
Authentication & Access Control6 / 10
Solid JWT auth: register/login/me endpoints with bcryptjs (10 rounds), constant-time-friendly login error messages, and Zod validation on both client and server (auth.schemas.js, validators.js). requireAuth middleware (requireAuth.js:7-24) extracts Bearer token, verifies JWT with DB user lookup for freshness. optionalAuth middleware for public routes. AuthContext (AuthContext.jsx:7-55) manages user state with localStorage token, loading flag, login/register/logout, and socket re-auth on auth change. Axios interceptor injects token and auto-clears on 401. Anonymous vs authenticated poll modes enforced server-side at public.controller.js:86-88, with Socket.io creator room verification (sockets/index.js:22-29). ProtectedRoute component gates dashboard routes. Gaps: no refresh tokens (7-day access tokens only), no email verification, no password reset, no rate limiting on auth endpoints, no Helmet/security headers, tokens in localStorage (XSS-vulnerable), and login/register pages ignore the redirect query parameter (broken return-to flow).
Poll Creation & Question Management7 / 15
Well-implemented poll creation with React Hook Form + Zod resolver (PollCreate.jsx:28-57). Dynamic questions and options via useFieldArray with nested QuestionEditor sub-component managing its own useFieldArray for options (PollCreate.jsx:175-257). Per-question mandatory/optional toggle via isRequired checkbox, anonymous/authenticated response mode via Radix Switch component, expiry datetime picker with 24h default. Min 2 options enforced (can't delete below 2), min 1 question guard. Server-side Zod validation (poll.schemas.js:3-26) validates title, description, responseMode enum, expiresAt future date (.refine), questions 1-50, options 2-20 per question. Transaction-based insert in polls.controller.js:55-90 inserts poll + questions + options atomically. Slug auto-generation (nanoid 10 chars). Gaps: questions/options cannot be edited after creation (PATCH only updates metadata — updatePollSchema at poll.schemas.js:28-33 has no questions field), the GripVertical drag handle is decorative only (no reordering), and updatePollSchema lacks a future-date check on expiresAt.
Response Collection Flow7 / 15
Defense-in-depth response collection: client-side required question check with inline errors (PublicPoll.jsx:60-69), server-side validation at public.controller.js:90-127 checks question existence, option-to-question membership, and required questions with specific error codes (INVALID_QUESTION, INVALID_OPTION, REQUIRED_MISSING). Expiry enforcement returns 410 (line 80-81), published polls blocked with 409 (line 83-84), auth mode enforcement at line 86-88. Transaction-based insert (lines 130-151) wraps response + answers atomically. Duplicate prevention via PostgreSQL partial unique indexes — one on (poll_id, respondent_user_id) WHERE respondent_user_id IS NOT NULL and one on (poll_id, respondent_token) WHERE respondent_token IS NOT NULL (schema.js:93-99) — race-condition safe via Postgres 23505 error mapping (lines 152-157). Client-side respondentToken generation per poll slug via crypto.randomUUID() with localStorage submission tracking (respondentToken.js). CountdownTimer shows remaining time. Beautiful state coverage: expired card, published view, submitted checkmark confirmation, auth-required sign-in prompt. Critical gap: anonymous users who don't provide a respondentToken can submit unlimited responses since the Zod schema marks respondentToken as optional (poll.schemas.js:36) and the partial unique index only fires when respondent_token IS NOT NULL.
Analytics & Feedback Dashboard6 / 15
Functional analytics service (analytics.service.js:1-87) computes total response count, per-question option vote tallies with percentages (rounded to 1 decimal), and skip counts. AnalyticsDashboard component (AnalyticsDashboard.jsx:7-84) renders 3 stat cards (total responses, question count, status with live indicator), and per-question Recharts BarChart with colored bars, tooltips showing count+percentage, and per-question metadata (answered count, skip count, required badge). Published results flow: public page switches from response form to AnalyticsDashboard when poll.state === 'published' (PublicPoll.jsx:98-114). Dashboard listing shows response counts via inline SQL subquery (polls.controller.js:111). Live analytics push via Socket.io on each submission updates the creator's dashboard. Gaps: no CSV/export, no individual response viewing, no trend/time-series data, no peak activity metrics, no anonymous vs authenticated respondent breakdown, computeAnalytics makes 5 sequential DB queries with no caching. Matches the 6-7 reference quality but lacks the richer analytics (drop-off rates, peak hours, animated progress bars) seen in the 8 reference (Opinion).
Frontend Experience6 / 10
Clean, consistent design: custom CSS tokens in HSL (warm cream background, deep navy foreground, electric blue primary), JetBrains Mono used systematically for labels and data, Radix UI primitives for accessible interactions (RadioGroup, Switch, Label, Slot). Landing page has custom CSS decorations: corner brackets using pseudo-elements and dynamic grid backdrop pattern. Responsive layouts throughout (SM/MD/LG breakpoints, max-w constraints). Comprehensive state coverage: loading, empty dashboard with CTA card (Dashboard.jsx:44-53), poll not found card, expired card, published with analytics, submitted confirmation with checkmark icon, auth-required sign-in prompt with proper redirect link. Accessibility: aria-labels on icon-only buttons, htmlFor on labels, autoComplete on form fields, focus-visible rings, disabled states. Sonner toasts with richColors for error feedback. Gaps: loading states are bare text ("Loading…") with no skeletons/spinners, 404 page is a bare `<p>Not found.</p>`, auth flash on initial load (AppShell renders unauthenticated nav before auth resolves), no dark mode support, GripVertical icon in PollCreate is purely decorative (no drag reorder), and window.confirm() used instead of modal dialogs for publish/delete.
Backend Architecture & API Design7 / 15
Clean layered architecture: routes → controllers → services with middleware layer (app.js:34-39). Custom HttpError class with status/code/details + centralized errorHandler mapping Postgres 23505 to 409 (errorHandler.js:13-16). Generic validate(schema) middleware factory using Zod safeParse (validate.js:3-12). All controllers follow consistent try/catch/next pattern. Database transactions for multi-table operations: createPoll (polls.controller.js:55-90) and submitResponse (public.controller.js:130-151). PostgreSQL schema (schema.js) with 6 tables, proper foreign keys with cascade deletes, Postgres ENUM type for response_mode, 2 partial unique indexes for race-condition-safe duplicate prevention, and 8 strategic indexes. Drizzle ORM with pg.Pool connection pooling. Production auto-migration with crash-early pattern (server.js:11-18), dev manual migration. Graceful shutdown on SIGINT/SIGTERM (server.js:33-38). CORS configured with single-origin restriction, request body limited to 256kb. IO dependency injection via app.set/get + middleware. Gaps: no Helmet (zero security headers), no rate limiting, no request logging framework, no HTML/XSS sanitization on stored text content, database SSL with rejectUnauthorized:false.
Real-Time Updates Using WebSockets7 / 10
Socket.io server attached to Express HTTP server (sockets/index.js:8). Room-based architecture with poll:{pollId} naming convention. Two custom events: poll:analytics (full computed analytics pushed on each response submission — public.controller.js:160-162) and poll:published (signal emitted on publish — polls.controller.js:179). Creator auth verification on join_poll with JWT + DB ownership check (sockets/index.js:22-29) using ack callback pattern. Client hook (usePollSocket.js:4-43) handles full lifecycle: joins room on mount + connect event, leaves room on unmount, listens for disconnect event, and re-joins on reconnect. Defensive event filtering (payload.pollId === pollId check at line 21). Connected state tracking for UI "Live" indicator. Socket singleton with lazy auth callback (socket.js:5-10) and refreshSocketAuth for login/logout. Vite dev proxy forwards /socket.io with ws:true (vite.config.js:17-20). Gaps: only 2 event types (no deletion notification, no online-user count), poll:published is pure signal (no data pushed), no reconnection backoff strategy, no typed Socket.io events, auth refresh uses blunt disconnect/reconnect cycle.
Code Quality & Project Structure5 / 10
Clean monorepo structure with client/ and server/ separation, well-organized directory hierarchy (db/, routes/, controllers/, middleware/, validators/, services/, sockets/, utils/). Consistent naming conventions and ESM usage throughout. Excellent README (291 lines) with architecture diagram, API reference, database schema docs, Socket.io protocol, deployment guide, and manual E2E verification checklist. Zod as single source of truth for validation on both sides. No leftover template/starter code detected. All npm dependencies actively used. Docker compose for local dev, Render blueprint for one-click production deployment. Gaps: no TypeScript (plain JS/JSX with no checkJs), zero automated tests (no test files anywhere), no PropTypes or JSDoc type annotations, Login.jsx and Register.jsx share ~90% code duplication (identical form patterns, try/catch, toast logic), pollStateBadge logic duplicated across 3 files (Dashboard, PollDetail, AnalyticsDashboard), broken redirect flow (login pages hardcode navigate('/dashboard') ignoring the redirect query param set by ProtectedRoute), minor dead code (unused pool export in db/index.js, unused CardFooter from card.jsx, unused responses import in polls.controller.js), no ESLint on server side, no pre-commit hooks or CI/CD.
40
Pulse Board
Ravi Mistry · @ravidotexe
51
Pulse Board is a well-built full-stack polling platform with React 19/TanStack Start frontend and Express 5/Drizzle ORM backend. Core features are fully implemented: JWT auth with anonymous/authenticated poll modes, dynamic multi-question poll creation with React Hook Form, defense-in-depth response validation with duplicate prevention, per-question Recharts analytics with Redis caching, and Socket.IO real-time updates with Redis Pub/Sub adapter. The dark-themed UI is polished with consistent design language and comprehensive state coverage. Key weaknesses: the Render deployment only serves the frontend SPA (API returns HTML, not JSON — backend not running), the poll update operation deletes and recreates all questions destroying existing response data via cascade, there is significant code duplication across multiple files (toApiError, FormBlock, background gradient CSS), no security middleware (Helmet, rate limiting), no automated tests, and tokens are stored in localStorage rather than httpOnly cookies. Scores range from 5-7, reflecting solid implementation with notable gaps in deployment, security, and code reuse.
Authentication & Access Control6 / 10
Solid JWT auth with registration (bcrypt 12 rounds, Zod password complexity rules), login, and two-tier middleware (authenticationMiddleware attaches req.user globally, restrictToAuthenticatedUser guards routes). Server enforces anonymous vs authenticated poll modes in response controller (response.controller.ts:72-87). Frontend has workspace auth guard (workspace.tsx:6-9), login/signup redirects, Axios interceptor for Bearer token. Gaps: JWT stored in localStorage not httpOnly cookies (security concern), no CSRF protection, no rate limiting, no token refresh/rotation, logout is client-only no-op on server (auth.controller.ts:99-101), no Helmet, password complexity rules oddly applied to signin schema too.
Poll Creation & Question Management7 / 15
React Hook Form with useFieldArray for dynamic questions and options (workspace.create.tsx, 694 lines). Per-question required/optional toggle, response mode radio cards, custom DateTimePicker with min/future validation. Server Zod validation: min 1/max 20 questions, min 2/max 10 options per question, datetime future check (poll.schema.ts:12-29). Transaction-based creation (poll.controller.ts:41-84). Full editing via workspace.$pollId.tsx (884 lines) with same form patterns. Delete endpoints for questions and options with minimum-enforcement and reordering. Share link display with copy. Critical gap: update operation deletes and recreates all questions/options (poll.controller.ts:273-275), which cascades to delete existing responses — editing a poll with responses loses all data.
Response Collection Flow7 / 15
Defense-in-depth response validation: poll existence check, published/expired gates with auto-publish on expiry (response.controller.ts:49-70), response mode enforcement (AUTHENTICATED requires login, ANONYMOUS requires anonymousIdentifier, blocks authenticated users from anonymous — lines 72-87), required questions validated (lines 91-105), option ownership verified per question (lines 107-123), duplicate prevention via explicit DB lookup AND DB-level unique compound indexes (responses_poll_user_unique, responses_poll_anonymous_unique in schema.ts:102-107). Transaction-based submission (lines 167-195). Redis counter increments + Socket.IO broadcast after submission. Client-side: required question check before submit, comprehensive UI state coverage (loading, submitted, auth-required, blocked, expired). Gap: only validates required questions server-side, doesn't reject extra/spurious questionIds in answers array.
Analytics & Feedback Dashboard6 / 15
Analytics endpoint (analytics.controller.ts) returns totalResponses and per-question option breakdowns with vote counts and percentages, served from Redis cache with lazy PostgreSQL hydration (analytics.redis.ts). Creator-only access while live, public after expiry (lines 57-63). Frontend: Recharts BarChart per question with vote-count labels, per-option stat cards (text, votes, percentage), total responses stat card, share link display (PollAnalyticsView.tsx, 171 lines). Workspace dashboard shows poll list with meta stats, status badges, question previews (workspace.index.tsx). Gaps: no CSV/export, no individual response viewing, no trend data or time-series, no peak activity metrics, percentages based on total responses (not per-question), no response drop-off analysis, no leader/animated progress.
Frontend Experience7 / 10
Consistent dark theme (zinc/cyan palette) with complex CSS backgrounds and centered glow effects across all pages. 8 frontend routes with TanStack Router file-based routing. All routes handle loading (skeleton cards, spinning indicators), error (AlertDialog), empty, and auth-blocked states explicitly. Responsive grid layouts. React Hook Form with icon-labeled inputs, radio card pattern for visibility, checkbox for required toggle. Custom DateTimePicker component. Landing page with feature cards, step cards, CTA sections. Copy-to-clipboard for share links. TanStack Devtools integrated. Polish issues: same background gradient CSS block duplicated across 5+ page files, FormBlock/GuideTile components redefined in multiple files rather than extracted, no light mode, no toast notifications.
Backend Architecture & API Design6 / 15
Clean Express 5 module organization (controller/route/schema per domain). Drizzle ORM with well-designed PostgreSQL schema: 6 tables, proper foreign keys with cascade, unique compound indexes for duplicate prevention, indexed lookups (schema.ts, 193 lines). Zod validation on env vars and all request inputs across 4 schema files. 12 REST endpoints with distinct HTTP status codes (400/401/403/404). Transaction-based operations for create/submit/delete. Global auth middleware chain (optional + required). Issues: no Helmet security headers, no rate limiting, all 12 catch blocks silently swallow errors without logging, inconsistent Zod error formats (some raw parsed.error vs .flatten()), non-null assertions on env vars (DATABASE_URL!, REDIS_URL!), no centralized error handler, no service layer, no pagination, update poll deletes+recreates questions destroying response data via cascade.
Real-Time Updates Using WebSockets7 / 10
Socket.IO integrated with Redis Pub/Sub adapter for horizontal scaling (socket.ts:23-29). Room-based pattern: clients join/leave `poll:{pollId}` rooms (socket.events.ts:4-14). Server emits `poll_response_update` with full payload (totalResponses + per-question option vote counts) after each response submission (response.controller.ts:203-225). Client connects via socket.io-client, joins room on mount, merges live data into analytics display via mergeAnalyticsWithRealtime(), and cleans up on unmount (poll.$pollId.tsx:230-267). Data is pushed over socket (not just invalidation signal). Gaps: only one event type, no events for poll publishing/deletion/expiry, no socket on workspace dashboard for live response counts, no reconnection room re-join logic, no authentication on socket connections, no debouncing.
Code Quality & Project Structure5 / 10
Clean monorepo with client/server separation, full TypeScript, ESLint + Prettier configured, Docker setup with multi-stage Dockerfile and docker-compose, comprehensive README with schema diagrams and API docs. Significant code duplication: `toApiError()` function duplicated verbatim across 4 service files (authServices.ts, pollServices.ts, responseServices.ts, analyticsServices.ts), FormBlock component redefined in 2 route files (workspace.create.tsx and workspace.$pollId.tsx), GuideTile/InsightTile duplicated across 3 files, identical background CSS gradient block (~25 lines) pasted into 5+ page files. `@ts-ignore` in auth.middleware.ts:30 (unnecessary with existing express.d.ts type augmentation). `icon: any` in FeatureCard (index.tsx:280). Console.log left in socket and Redis code (6 instances in production paths). No automated tests. env.ts throws raw Zod error on validation failure without formatting.
41
Poll Khol
Aniket Dey · @aniketdey
50
Poll Khol is a well-executed full-stack polling platform with a polished dark-themed UI, clean backend architecture, and working real-time updates. The 6,037-line codebase demonstrates solid understanding of MERN stack concepts. Strengths: thorough poll creation with dynamic question builder and preview mode, defense-in-depth response collection with IP-hash duplicate prevention, good analytics dashboard with Recharts bar charts and individual response viewing, room-based Socket.io with proper client cleanup, and a beautiful custom design system with glassmorphism and Framer Motion animations. The main weaknesses are: no TypeScript, no automated tests, missing production security middleware (Helmet, rate limiting), tokens in localStorage instead of httpOnly cookies, duplicated analytics/chart code, and extensive inline styles instead of using the CSS class system. Deployment verified working at https://poll-khol.aniketdey.in/. Total score: 50/100.
Authentication & Access Control6 / 10
Custom registration/login with bcryptjs (12 rounds, user.model.js:33-36) and JWT (7d, auth.service.js:5-11). Auth middleware (auth.js:3-36) verifies Bearer token with TokenExpiredError handling. optionalAuth middleware (auth.js:39-55) for public routes. Zod validation on auth inputs (auth.schema.js). Frontend: Zustand store with login/register/logout/checkAuth + persist (authStore.js), Axios interceptor with 401 redirect (axios.js:23-37), ProtectedRoute with loading/auth states (ProtectedRoute.jsx). Server-side enforcement of anonymous vs authenticated poll modes (public.service.js:49-51). Gaps: tokens in localStorage (XSS risk), no Helmet/CSRF, no rate limiting on auth endpoints, no refresh token rotation, no email verification or password reset, no OAuth/social login.
Poll Creation & Question Management7 / 15
Dynamic question builder in CreatePoll.jsx (562 lines) supports add/remove/reorder questions with up/down buttons, add/remove options (min 2 enforced), type selection (single/multiple via custom Select component), isRequired toggle, and preview/edit mode toggle. Custom DateTimePicker component (237 lines) with calendar grid + time selects. EditPoll.jsx (252 lines) fetches and repopulates all fields with questionId/optionId preservation. Server-side Zod validation in createPollSchema (poll.schema.js:4-27) validates title, future expiry, min 1 question, min 2 options per question, type enum. updatePollSchema (poll.schema.js:30-55) supports partial updates. Mongoose embedded subdocuments with nanoid IDs (poll.model.js:4-48). Published polls blocked from editing (poll.service.js:61-63). Gaps: no slug generation, no QR/share features, no draft creation flow, raw useState (no React Hook Form despite it being a dependency), client-side IDs use Date.now().
Response Collection Flow7 / 15
Solid defense-in-depth response flow. Server validates poll existence, expiry/closed/draft status gates, and anonymous vs authenticated requirement (public.service.js:38-58). IP-hash based duplicate prevention using SHA-256 of IP+pollId (ipHash.js) with MongoDB compound unique index on {poll, ipHash} (response.model.js:48). Required question validation loops through requiredQuestions (public.service.js:61-67). Custom ApiError static factories for differentiated HTTP status codes (400/401/403/404/409/410). Frontend PublicPoll.jsx (412 lines) handles all UI states: loading spinner, expired/closed (410), login-required gate, submitted thank-you, option selection for single/multiple with visual feedback, per-question inline error display (red banner with AlertTriangle), and duplicate/closed error toasts. Gaps: no server-side validation that submitted optionIds actually belong to the poll, no single-choice enforcement (max 1 option) server-side, no rate limiting on submit endpoint, dev IP randomization means duplicate prevention only truly enforced in production.
Analytics & Feedback Dashboard7 / 15
Good analytics dashboard at PollAnalytics.jsx (502 lines). Stats cards show Total Responses and Poll Questions with styled icons. Per-question Recharts BarChart (vertical layout) with custom colored bars, percentage labels, and formatted CustomTooltip. Individual responses section (lines 405-497) shows respondent name/email/avatar or "Anonymous Participant," submission timestamp, and per-answer breakdown in grid cards. Server computes: totalResponses, per-question optionCounts with totalAnswered, and hourly timeline aggregation via MongoDB $group pipeline (poll.service.js:103-171). Publish workflow with confirmation dialog and copy-results-link. PublicResults.jsx (251 lines) renders same charts for published polls with "Not Published" (403) state. Socket integration merges live analytics:update and response:new events into state (lines 108-133). Gaps: timeline data computed server-side but never rendered in UI, no CSV export, no anonymous vs authenticated breakdown stat, no pagination on individual responses, analytics recomputation is O(n*m) brute force, no peak activity/top-option leader summary.
Frontend Experience6 / 10
Polished dark "command center" theme with custom CSS design system (index.css, 541 lines): CSS variables for colors/shadows/radii/fonts, glassmorphism cards with hover effects, gradient buttons, custom toggle switch, styled badges, custom scrollbar, atmospheric background gradients. Framer Motion on landing page hero/features/CTA sections with fadeUp variants. Custom UI components: Toast (60 lines, 3 types, auto-dismiss), Select (125 lines, click-outside, keyboard), DateTimePicker (240 lines, animated calendar+time). All 8 pages handle loading (spinner), error (toast), empty, and auth states. PublicPoll handles expired/closed/login-required/submitted states with distinct UIs. Responsive grid layouts. Navbar auto-hides on landing/public routes. Gaps: no TypeScript, no form library used (562 lines of manual useState in CreatePoll despite react-hook-form being a dependency), extensive inline styles bypassing the CSS class system, no skeleton loaders, no dark/light mode toggle, no offline/PWA, no data caching layer (refetches on every navigation), confirmation uses window.confirm() instead of custom dialogs.
Backend Architecture & API Design6 / 15
Well-organized modular backend with consistent routes→controller→service→schema→model separation across 5 modules (auth, polls, public, responses, users). Zod validation on all inputs (5 schemas total: register, login, createPoll, updatePoll, submitResponse). Custom ApiError class with static factory methods for 400/401/403/404/409/410/500 (ApiError.js), standardized ApiResponse helper, and global error middleware (app.js:49-58). Mongoose schemas well-designed: User with bcrypt pre-save + select:false on password, Poll with embedded questions/options (nanoid IDs, isExpired virtual), Response with compound unique index {poll, ipHash}. 13 REST endpoints across 3 route groups. Express 5, CORS with credentials, body parser 10mb limit, morgan logging, cookie-parser. Socket.io integrated with HTTP server. Gaps: no Helmet (security headers), no rate limiting on any endpoint, no input sanitization (express-mongo-sanitize), no pagination on any list endpoint, analytics computation is brute-force O(n*m) with no MongoDB aggregation pipeline for per-question stats, duplicate analytics logic in public.controller.js (L48-70) and poll.service.js (L116-148), MongoDB connection failure silently continues (server starts without DB), no graceful shutdown, no request body size limits beyond 10mb.
Real-Time Updates Using WebSockets6 / 10
Room-based Socket.io implementation. Server (socket.js:40 lines): connection handler with join:poll and leave:poll room management, participant:count broadcast on room join/leave. Controllers emit 3 event types: analytics:update with full question-level analytics + totalResponses (public.controller.js:72-76), response:new with populated response (line 81), and poll:published with pollId/shareToken (poll.controller.js:88-92). Client useSocket hook (82 lines): singleton connection (autoConnect:false), joins room on connect/mount, leaves on unmount with proper event listener cleanup. PollAnalytics.jsx merges live analytics into state (lines 108-117) and appends new responses with dedup (lines 119-133). PublicResults and PublicPoll also consume socket data (participant count, analytics updates). Gaps: no reconnect room re-join logic (socket silently loses subscriptions after disconnect), server-side analytics recomputation on every submission is expensive (full O(n*m) re-scan of all responses), no debouncing on server emits, no data pushed — only invalidation signals, no socket.io authentication, no typed events, participant count may leak on disconnect edge cases (no leave:poll emitted on network drop).
Code Quality & Project Structure5 / 10
Clean monorepo structure (client/ and server/) with consistent module organization. Good README with screenshots, tech stack, setup instructions, and project structure. ESM modules throughout (type: module). JSDoc on key files (ApiError.js, ApiResponse.js, validate.js, socket.js, ipHash.js). .env.example files for both packages. ESLint configured on client. Consistent naming conventions. Gaps: no TypeScript (entirely JS/JSX), zero automated tests, no backend ESLint. Duplicated code: CHART_COLORS and CustomTooltip in both PollAnalytics.jsx and PublicResults.jsx, duplicate analytics computation in public.controller.js vs poll.service.js. Naming inconsistency: localStorage key "pollflow_token" vs project name "Poll Khol", server package.json name "pollflow-server". Extensive inline styles throughout pages bypass CSS class system. No barrel exports in module directories. No component extraction from large pages (PollAnalytics 502 lines, CreatePoll 562 lines, PublicPoll 412 lines). Icons.svg has unused starter template icons. Description in index.html says "PollFlow" not "Poll Khol".
42
Pollinkr
Asghar Ali · @asgharali97fs
50
Pollinkr is a solid MERN-stack polling platform with dual-JWT authentication (bcrypt-hashed refresh rotation in DB), a well-built CreatePoll form (React Hook Form + Zod + DnD-kit drag-and-drop), thorough response validation with dual partial uniqueness indexes and fingerprint-based deduplication, and custom evILCharts visualization library. Deployed on Vercel (frontend) and Render (backend) with proxy-based API routing. Key gaps: no helmet/rate limiting, TanStack Query installed but unused, duplicated code (animation hooks x3, OPTION_COLORS x2), broken frontend links (/register, /Signup vs /signup), no logout button in UI, socket connections lack authentication, and no automated tests. Total: 50/80.
Authentication & Access Control7 / 10
Dual JWT auth with 15-min access + 7-day refresh tokens in httpOnly/secure/sameSite cookies, bcrypt(12) password hashing, and refresh token rotation with bcrypt hash stored in DB (auth.service.ts:92-109). Auth middleware (auth.middleware.ts:7-38) verifies JWT and attaches req.user. Optional-auth middleware for public routes. Anonymous vs authenticated poll modes enforced via responseMode field with server-side gating (poll.service.ts:176-178). Axios interceptor auto-refreshes on 401 (api.ts:11-42). Gaps: no Helmet, no rate limiting on auth endpoints, no CSRF beyond sameSite, no logout button in UI (clearAuth exists but never called), no password complexity beyond min(8), and empty catch block in logoutSession (auth.service.ts:87-89).
Poll Creation & Question Management7 / 15
CreatePoll.tsx (497 lines) uses React Hook Form + Zod resolver with useFieldArray for dynamic questions and nested useFieldArray per question for options (min 2). Features: mandatory/optional toggle per question, anonymous/authenticated toggle with Controller, expiry date calendar picker via shadcn, DnD-kit drag-and-drop question reordering, and dual-submit (Save Draft / Publish). Server-side Zod validation (poll.dto.ts:5 schemas) enforces min 1 question, min 2 options. Structural integrity guard prevents question/option changes after responses begin (poll.service.ts:76-83). Gaps: edit mode uses same component but doesn't load existing poll data (no useEffect for id param), no slug/QR code generation, `any` types on SortableQuestion props (CreatePoll.tsx:331-334).
Response Collection Flow8 / 15
Thorough defense-in-depth: server validates answers belong to poll questions, enforces mandatory questions, and rejects unknown question IDs (poll.service.ts:254-284). Expiry auto-synced on every access via syncExpiredPoll (lines 232-241). Status gates block draft/expired/published polls from accepting responses (lines 170-174). Duplicate prevention via dual MongoDB partial unique indexes — one for authenticated users {poll, respondentUser} and one for anonymous {poll, respondentFingerprint} (response.model.ts:103-121). SHA-256 fingerprinting combines pollId:ip:userAgent with FINGERPRINT_SECRET for anonymous deduplication. Client-side validates mandatory questions with error banner and scroll-to-first-unanswered (PollResponse.tsx:70-80). Auth gating dialog shown when authenticated poll accessed anonymously. Gap: responseCount uses non-atomic save() instead of $inc (potential race condition).
Analytics & Feedback Dashboard6 / 15
Analytics computes per-question option counts, total answers, leading option detection, and participation rate (poll.analytics.ts:20-63, building question map from responses). Frontend Analytics.tsx (334 lines) renders 3 stat cards (total responses, participation rate, questions), per-question EvilBarChart and percentage progress bars with leading answer label, plus response distribution pie chart. Socket.io live updates with "Live" badge flash. PublishedResults.tsx (268 lines) provides public results view. Dashboard lists polls with response counts and status badges. Gaps: analytics computation is brute-force O(n*m) iteration over all responses (no aggregation pipeline), no CSV/export functionality, no individual response viewing, no time-series/trend data, and N+1 query pattern (fetches all responses then iterates). No peak activity metrics.
Frontend Experience5 / 10
Clean UI with Tailwind CSS v4 + OKLCH design tokens, custom evILCharts library (bar-chart, pie-chart, evil-brush, tooltip, legend — ~2,700 lines of chart components), dark/light mode via next-themes. CreatePoll form is well-built (RHF+Zod+DnD-kit). PollResponse handles all states: loading, closed, published, submitted, auth-required. Dashboard differentiates "no polls" vs "no matching" empty states. Gaps: TanStack Query installed but completely unused (zero useQuery/useMutation — all data fetching is raw useEffect+useState), triple-copied useInView/FadeIn animation hooks across 3 landing page components, broken links (/register route doesn't exist but Hero.tsx and CTA.tsx link to it, Login links to /Signup with wrong case), no logout button anywhere, useIsMobile hook defined but never imported, no skeleton loaders (plain "Loading..." text), Sidebar is fixed width with no collapse on mobile.
Backend Architecture & API Design6 / 15
Clean module-based vertical-slice architecture (auth/poll/response with controller-service-routes-model-dto per module). TypeScript strict mode with noUncheckedIndexedAccess, exactOptionalPropertyTypes. All inputs validated via 9 Zod schemas with validateRequest middleware (validate-request.ts:45 lines). ApiError class hierarchy with factory methods for 400/401/403/404/409/500. ApiResponse utility for consistent {success, message, data} envelope. Centralized error handler with dev stack traces. Mongoose schemas with proper indexing (6 partial unique indexes). express.json body limit (1mb). Zod env validation with safeParse and graceful abort. Gaps: no Helmet middleware, no rate limiting on any endpoint, redundant custom CORS header injection alongside cors package (potential conflict at app.ts:10-33), manual cookie parsing without cookie-parser, 2 empty catch blocks, ObjectId regex duplicated in poll.dto.ts and response.dto.ts, non-null assertions (req.user!.id) in poll.controller.ts, race condition on responseCount increment.
Real-Time Updates Using WebSockets6 / 10
Socket.io server at sockets/index.ts (30 lines) uses room-based pattern: clients join poll:<pollId> rooms via poll:join, leave via poll:leave. Server emits poll:update with full analytics payload to room on response submission (poll.service.ts:214). Server pushes actual data (not just invalidation signal). Client Analytics.tsx (lines 60-80) connects on mount, joins room, listens for updates with 800ms "Live" flash indicator, properly cleans up on unmount. Dashboard.tsx also joins all active poll rooms. Gaps: no socket authentication (any WebSocket client can join any poll room and receive analytics), Dashboard creates inefficient socket churn by using polls.map(...).join("|") as useEffect dependency (reconnects on every status change), only one server-emitted event type, no socket integration on public poll response page, no typed Socket.IO events.
Code Quality & Project Structure5 / 10
Clean monorepo separation (backend/ and front-end/), TypeScript strict mode in backend, well-organized vertical-slice module structure, Zod env validation, proper .env.example files, comprehensive README with setup instructions and API overview. Gaps: triple-copied useInView/FadeIn animation hooks across 3 landing components (~45 lines each), OPTION_COLORS array duplicated in Analytics.tsx and PublishedResults.tsx, cn utility duplicated in src/lib/utils.ts and lib/utils.ts, TanStack Query installed but never used (substantial unused dependency), dead code toObjectId() in poll.analytics.ts (exported but never imported), `any` types in SortableQuestion component props, empty catch blocks (2 instances), no automated tests anywhere, no linting configuration for frontend, no graceful shutdown in backend, analytics computation is brute-force O(n*m) over all responses rather than using MongoDB aggregation.
43
R
PulseBoard - Live Polls for Feedback
Rajat Jaiswal · @rajat_jas
50
PulseBoard is a solid full-stack polling platform with working frontend and backend deployments on Render. The auth system is thorough (account lockout, rate limiting, optional auth for public polls), the poll creation UI has a live preview and edit mode, response collection has defense-in-depth validation, analytics provide per-question breakdowns with anonymous/auth splits, and the UI is polished with dark mode and consistent design. However, the Socket.io implementation has a significant production flaw (dead SocketContext, hardcoded io('/')), code quality suffers from no TypeScript, no tests, dead code, duplicated components, and unused dependencies. The backend lacks a service layer, Sequelize associations, and migrations. Overall: well-executed application features with solid product thinking, but the engineering fundamentals (testing, typing, code reuse) need improvement.
Authentication & Access Control7 / 10
Solid auth with registration/login (express-validator, bcrypt 12 rounds), JWT tokens, account lockout after 5 failed attempts with 15-min lock (User.js:5-31), auth-specific rate limiting (10/15min, auth.js:15-21), protect + optionalAuth middleware (auth.js:7-68), AuthContext with localStorage persistence and auto-verify on mount (AuthContext.jsx:10-29), and Axios 401 interceptor (api.js:22-33). Gaps: JWT in localStorage not httpOnly cookies, no refresh tokens, no email verification, no password reset, pass minimum 6 chars only.
Poll Creation & Question Management7 / 15
CreatePoll.jsx (492 lines) features dynamic questions/options with add/remove, mandatory/optional toggle per question, requireAuth switch, datetime-local expiry picker with future-date validation, live sticky preview panel, and full edit mode via /poll/:id/edit. Server-side express-validator checks title, expiresAt (ISO8601 + future), questions (min 1 array), question text, options (min 2 per question) at polls.js:75-94. ShareId auto-generated via crypto.randomBytes(6) and question _id via UUID hooks in Poll.js:74-94. Gaps: no form library (manual useState), no draft status concept, no QR code, overwrites all questions on update without partial option preservation.
Response Collection Flow7 / 15
Defense-in-depth validation: expiry check (responses.js:38-42), published check (45-48), auth requirement check (52-57), explicit duplicate authenticated response findOne (60-68) + UniqueConstraintError catch (129-133), mandatory question enforcement with specific question-name error messages (79-88), option existence validation (91-100). PublicPoll.jsx (322 lines) handles all UI states: loading, not-found, expired, auth-required, submitted (thank-you), and published (auto-redirects to results). CountdownBadge updates every second (22-47). Progress bar shows answered/total. Composite unique index on (pollId, respondentId) uses PostgreSQL NULL-as-distinct for anonymous multi-voting (Response.js:37-42). Gaps: no localStorage duplicate prevention for anonymous users, no client-side validation beyond required questions.
Analytics & Feedback Dashboard7 / 15
computeAnalytics() (polls.js:9-54) shared across route files computes totalResponses, anonymous/authenticated split, per-question optionCounts, responseCount, skippedCount. Analytics.jsx (277 lines) shows stat cards (total responses with anonymous/auth breakdown, completion rate, poll status), per-question OptionBar breakdowns, Live/Offline indicator with ping animation, empty state with CTA, publish button with confirmation dialog, and copy link. PublishedResults.jsx (174 lines) shows public results with stats grid, per-question breakdown, winner highlighting (CheckCircle2 icon), and creator CTA. Dashboard shows polling stats and expiring-polls warning banner. Gaps: no CSV export, no individual response viewing, no trend/time-series data, unused recharts dependency.
Frontend Experience7 / 10
Consistent papaya-orange design system with CSS custom properties (index.css), dark/light mode via ThemeContext with OS-preference detection, responsive sidebar-to-drawer conversion (Sidebar.jsx), PublicLayout vs AuthLayout separation (App.jsx:19-68), all 8 pages handle loading/error/empty/auth states, copy-to-clipboard with "Copied!" feedback (2s timeout), live countdown timer on public poll, animated progress bars (CSS transitions), demo credentials banner on login, password show/hide toggle, react-hot-toast notifications, Plus Jakarta Sans typography. Gaps: no form library (plain useState), window.confirm for destructive actions (not a styled modal), unused motion and recharts dependencies in package.json, 401 redirect uses window.location.href instead of React Router navigation.
Backend Architecture & API Design6 / 15
Express/Sequelize/PostgreSQL stack with proper middleware ordering (CORS→helmet→rate limiter→body parser), 14 REST endpoints, express-validator on all mutations, comma-separated CORS origin allowlisting (index.js:25-41, functions as allowlist), JSON body size limit 10kb, global + auth-specific rate limiting, UniqueConstraintError handling (responses.js:129), composite unique index on responses, JSONB with hooks for _id generation. Substantial gaps: NO Sequelize associations defined (no hasMany/belongsTo), no DB-level FK constraints, no migration system (sync() only), no service layer (all logic in route handlers), SSL rejectUnauthorized: false in production (db.js:7), no pagination, err.stack logged to console in error handler.
Real-Time Updates Using WebSockets5 / 10
Socket.io server with room-based pattern (poll-{id}), three event types: analytics-update (full data push), new-response (count only), poll-published (socket/index.js:1-17). Server emits on response submission (responses.js:118-121) and publish (polls.js:237-238). Analytics page connects via io(), joins room on mount, listens for all three events with toast notifications, shows Live/Offline status with ping animation, cleans up on unmount (Analytics.jsx:69-93). Critical gaps: SocketContext.jsx is dead code (never mounted, never imported), Analytics page hardcodes io('/') ignoring VITE_API_URL (broken in production where backend is on separate origin), no socket authentication, no reconnection room re-join logic beyond defaults, no debouncing, no real-time updates on the public voting page.
Code Quality & Project Structure4 / 10
Clean backend/frontend separation with meaningful folder structure, good README.md and DATABASE.md documentation, CSS design token system, consistent naming conventions. BUT: zero TypeScript (all .js/.jsx), zero automated tests, dead code (passport.js empty config, SocketContext.jsx never mounted), unused dependencies (motion, recharts in package.json but never imported), unused Inter font preconnect in index.html, OptionBar component duplicated in Analytics.jsx:25-41 and PublishedResults.jsx:10-31, field() helper duplicated in Login and Register, no ESLint/Prettier config, Bearer token extraction duplicated in auth.js protect and optionalAuth, all business logic in route handlers with no service layer, no centralized logging, hardcoded Vite proxy target (localhost:5001).
44
PulseBoard-Live Polls for Feedback
Akash Kumar Singh · @akashkr28
50
PulseBoard is a solid full-stack polling platform with a well-implemented React frontend and Express/MongoDB backend. The frontend features a polished dark theme with custom CSS (2,681 lines), Framer Motion animations, Recharts analytics, and comprehensive state coverage across 8 routes. The backend has clean architecture with 12 REST endpoints, proper Mongoose schema validation, JWT auth with bcrypt, and Socket.IO real-time updates via room-based pub/sub. Total codebase: 5,877 lines (3,196 app + 2,681 CSS). Key strengths: thorough response validation (validateAnswers checks question/option existence, duplicates, mandatory enforcement), polished analytics dashboard with LineChart/PieChart and real-time updates, clean auth flow with booting state and return-to redirect, and good UI state coverage (loading/empty/error/submitted/published/expired/auth-required). Key weaknesses: backend not deployed (Render returns JSON Server default page, not PulseBoard API), no TypeScript, zero tests, no input validation library (all manual), no rate limiting anywhere, no CSRF protection, no poll edit endpoint (can't modify questions after creation), tokens in localStorage rather than httpOnly cookies, formatDate/copyLink logic duplicated across 3 files, Socket.IO lacks reconnection room re-join and debouncing. The Vercel frontend is accessible but cannot function without a working backend.
Authentication & Access Control6 / 10
Solid auth with registration (name/email/password, bcrypt 12 rounds), login, JWT (7d expiry), and GET /me endpoint (auth.routes.js:25-85). optionalAuth middleware extracts Bearer token, verifies JWT, attaches req.user (auth.js:11-33). requireAuth gates protected routes with 401 (auth.js:35-45). Frontend AuthProvider manages booting/authenticated/logged-out states with localStorage token persistence and cancelled useEffect cleanup (AuthContext.jsx:12-39). ProtectedRoute preserves return-to path via location state (ProtectedRoute.jsx:6-13). Anonymous vs authenticated poll modes enforced server-side (public.routes.js:129-142) and reflected in client UI. Gaps: token in localStorage not httpOnly cookie, no CSRF, no rate limiting on auth endpoints, no email verification or password reset, single JWT with no refresh token rotation.
Poll Creation & Question Management7 / 15
Dynamic poll builder (CreatePollPage.jsx, 382 lines) supports add/remove questions and options with per-question mandatory/optional toggle. Settings panel includes title (max 120), description (max 600), response mode segmented control (anonymous/authenticated), and expiry datetime-local with min constraint. Client-side validation via useMemo questionIssues array (lines 66-77) catches empty question text, insufficient options; plus title and expiry checks. Server-side normalizeQuestions (poll.routes.js:10-44) validates text, min 2 options, option trimming, question count. Builder stats (questions/mandatory/options) computed via useMemo. Framer Motion on question add/remove. Gaps: no poll edit/update endpoint (PATCH only for publish, cannot modify questions after creation), no draft status, no slug customization, no QR code, no form library (individual useState).
Response Collection Flow7 / 15
Public poll page (PublicPollPage.jsx, 264 lines) renders radio-button choices with answer progress bar (answered/M total, % bar). Client-side mandatory check via useMemo requiredMissing array (lines 30-33). Server-side validateAnswers (public.routes.js:39-87) validates question existence via Map lookup, option existence via Mongoose .id(), duplicate answers via Set, and mandatory question enforcement with missingRequired array. Gate checks: isPublished (409), isExpired (410), authenticated mode requiring user (401), explicit duplicate check via Response.exists for authenticated polls (line 134-142). Duplicate prevention: compound unique sparse index on {poll, respondent} with partialFilterExpression (Response.js:48-53). IP hash via SHA-256 + user agent metadata. Socket.IO emits poll:submitted + poll:analytics on submission. Differentiated HTTP status codes (401/404/409/410/422). Frontend covers all states: loading, submitted confirmation, published results, expired/closed notice, auth-required banner, error. Gap: no rate limiting on submit endpoint, no localStorage anonymous duplicate tracking.
Analytics & Feedback Dashboard7 / 15
buildPollAnalytics (analytics.js, 118 lines) computes totalResponses, per-question option counts/percentages, anonymous/authenticated split, completionRate, averageAnsweredQuestions, and daily timeline via bucketByDay. ResultSummary (ResultSummary.jsx, 209 lines) renders 4 metric cards (total, avg answered, completion%, last response), daily participation LineChart via Recharts with CartesianGrid/Tooltip, donut PieChart for identity mix with colored legend, and per-question option bar charts with counts/percentages. AnalyticsPage (189 lines) integrates StatusPill, copy link (clipboard API with "Copied" feedback), publish button with POST state transition, live strip indicator, and Socket.IO real-time updates. Publish flow: PATCH /api/polls/:id/publish sets isPublished/publishedAt and broadcasts poll:published (poll.routes.js:151-173). Dashboard (264 lines) has 4 summary stat cards, filter tabs (all/live/published/closed) with useMemo filtering, inline delete confirmation with optimistic removal. Gaps: no CSV/export, no individual response viewing, timeline is day-granularity only, computation is iterative O(n*m) not aggregation pipeline.
Frontend Experience7 / 10
8 React Router v6 routes with SPA catch-all redirect (App.jsx). Custom dark theme via CSS variables (2,681-line styles.css) with gradient backgrounds, subtle grid overlay, cohesive color palette, and responsive layout using CSS Grid. Framer Motion animations throughout: page transitions, staggered card reveals, letter-by-letter hero animation on landing page. Landing page (315 lines) features animated hero, 3 feature cards, 4-step workflow section, CTA strip, and footer. All UI states covered: booting splash screen (App.jsx:15-22), loading skeleton (DashboardPage:121-126), empty state with CTA (DashboardPage:127-136), error alerts on all pages, submitting/saving/publishing/deleting states, submitted confirmation (PublicPollPage:165-177), published results view, expired/closed notice, auth-required banner with return-to login link. Recharts integration for analytics. Dashboard: filter tabs, inline delete confirmation with cancel/confirm. Gaps: no form library (individual useState), no data caching layer, no confirmation for publish action, no PWA/offline support.
Backend Architecture & API Design6 / 15
Clean layered architecture with routes→models→utils separation in 12 REST endpoints plus health check. Mongoose schemas with proper validators: required fields, maxlength, enum, custom validators (min 2 options, min 1 question), compound unique sparse index on Response for duplicate prevention (Response.js:48-53), indexed fields on owner/publicId/expiresAt. Security: helmet (index.js:36), CORS with allowlist from CLIENT_URL env (index.js:19-21), express.json 1mb limit (index.js:37), bcrypt 12 rounds, JWT verification. Error handling: notFound + errorHandler middleware (errorHandler.js:1-19) with MongoDB duplicate key (11000) handling, asyncHandler wrapper (http.js:8-12). MongoDB aggregate for poll list response counts (poll.routes.js:86-91). Gaps: no input validation library (all manual ad-hoc string checks), no rate limiting anywhere, no CSRF protection, no request sanitization (express-mongo-sanitize), no pagination on poll list, no poll edit endpoint, JWT_SECRET accessed directly from process.env without config module.
Real-Time Updates Using WebSockets5 / 10
Socket.IO server with room-based pattern: clients join/leave rooms via poll:join/poll:leave with string validation (index.js:44-56). Server emits 4 events: poll:submitted (response count), poll:analytics (full analytics object), poll:published, poll:deleted. Client handles 3 events: poll:submitted updates liveCount badge on public page (PublicPollPage.jsx:66-68), poll:analytics rerenders full analytics state (AnalyticsPage.jsx:56-63), poll:published transitions both pages to published view (PublicPollPage.jsx:69-83, AnalyticsPage.jsx:64-78). Proper cleanup: poll:leave emit + socket.disconnect() on unmount in both pages. Gaps: no debouncing on client handlers, no reconnection room re-join logic (socket reconnects silently lose room subscription), no socket authentication, full analytics object retransmitted each time instead of incremental data, no Socket.IO event on poll creation for dashboard updates, no typed events, no error handling for socket connection failures.
Code Quality & Project Structure5 / 10
Clean monorepo with client/server workspaces + root concurrently dev script (package.json). All ES modules via "type": "module". ESLint configured for client with React/Hooks/Refresh plugins. Consistent patterns: asyncHandler wrapping, httpError factory, cancelled flags in useEffect cleanup. Auth context separated into createContext + Provider + hook files. Gaps: no TypeScript (all JS/JSX), zero automated tests (no test scripts in either package.json), formatDate function duplicated verbatim in 3 files (PublicPollPage.jsx:10, AnalyticsPage.jsx:11, DashboardPage.jsx:19), copyLink clipboard logic duplicated in DashboardPage and AnalyticsPage, createSocket creates new connection per mount (no singleton), console.log in production (server index.js:71), no root .env.example, no pre-commit hooks or CI, no README-based documentation for VITE_SOCKET_URL variable, total project is 3,196 lines of app code + 2,681 lines CSS.
45
Swift Polls
Parth Munjal · @parthmunjal07
50
SwiftPolls (aka PulseBoard) is a full-stack polling platform with React 19 + Express 5 + PostgreSQL + Redis. Overall score: 50/100. Strengths: rich poll builder with draft/publish flow, dual auth (email+Google OAuth) with JWT refresh rotation, Redis-backed analytics with BullMQ, 10-table PostgreSQL schema, polished responsive UI with dark/light mode, comprehensive state coverage in frontend. Key weaknesses: backend not deployed, auth endpoints lack input validation despite Zod schemas existing, socket.io events have zero authorization allowing anyone to control live sessions, getPollBySlug returns broken response for inactive polls, naming inconsistency (PulseBoard vs SwiftPolls), code duplication (6+ patterns), no tests, access token in localStorage, dead code and unused schemas. Solid implementation overall but needs security hardening and cleanup before production use.
Authentication & Access Control6 / 10
Auth system has email/password registration+login with bcrypt (10 rounds), Google OAuth via Passport.js, JWT dual-token strategy (15min access + 7d HttpOnly refresh cookie with rotation at auth.controller.ts:149-171), AuthContext with localStorage+Axios interceptor for auto-refresh (axios.ts:25-55), PrivateRoute gating, and anonymous-vs-authenticated poll mode enforced at response.controller.ts:48-53. However: auth Zod schemas (auth.validate.ts:3-12) are defined but never imported/used by auth.controller.ts — signup/login have zero input validation. Access token stored in localStorage (XSS risk). No Helmet CSRF, no email verification, no rate limiting on auth endpoints.
Poll Creation & Question Management7 / 15
Rich poll builder (PollBuilderPage.tsx, 406 lines) using React Hook Form + useFieldArray for dynamic questions/options, dual-purpose Zod validation (frontend pollBuilderSchema + backend createPollSchema with superRefine for draft-vs-published), mandatory/optional toggle per question, anonymous toggle, expiry datetime picker for async polls, live/async mode toggle, frontend client-side validation, backend DB transaction for atomic creation (polls.controller.ts:82-152). Draft save with smart defaults via normalizeDraftCreatePoll (lines 19-63). Poll editing exists (updatePoll at line 316) and publishing (publishPoll at line 418). Gaps: updatePoll only modifies metadata (title/description/is_anonymous/expires_at), not questions/options — the `questions` field in updatePollSchema is defined but never used. Slug auto-generation via nanoid(8) but no QR code or permalink preview.
Response Collection Flow7 / 15
Async response submission (response.controller.ts:15-131) validates poll existence, is_active, expiry, anonymous/authenticated mode enforcement, duplicate prevention via both DB query (lines 56-76, by user_id or session_token) and localStorage (AsyncPollPage.tsx:20-21), mandatory question enforcement server-side (lines 83-93), DB transaction for atomic insert + Redis counter increment. Frontend AsyncPollPage.tsx (272 lines) covers all states: loading, error/not-found, expired, login-required, already-submitted, submitted (ThankYouPage), and results view with bar charts. Client-side mandatory validation before submit (lines 164-172). Gaps: does NOT validate submitted optionIds belong to the poll's questions, published_at check is commented out (line 44-46), getPollBySlug returns broken/incomplete response for inactive polls (lines 278-285 omit actual poll data), no per-question single-option enforcement server-side.
Analytics & Feedback Dashboard7 / 15
Analytics dashboard (PollAnalyticsPage.tsx, 317 lines) with bar charts and pie charts (Recharts), accordion per-question drill-down with leading option identification, stat cards (total responses, created date), status badge (draft/active/ended), publish button. Backend analytics (analytics.controller.ts, 422 lines) provides 4 endpoints: getPollAnalytics (per-question option counts with percentages), getPollSummary (total responses/questions/last response), getResponseTrend (24h/30d raw SQL), getSessionAnalytics (per-session scoped). Redis-to-Postgres flush via BullMQ every 10 seconds for real-time + persistent analytics. Gaps: response trend endpoint exists but is NOT wired to the frontend UI, no CSV/export, no individual response viewing, some `any` types in tooltip/data iteration, analytics WebSocket listener in frontend uses event name that server never emits.
Frontend Experience7 / 10
Polished, responsive UI with Tailwind CSS v4 and dark/light theme toggle (ThemeContext). Comprehensive state coverage: loading spinners (Loader, FullPageLoader, inline spinners), error states with contextual messages, empty states ("No async polls found"), expired/not-found/login-required/already-submitted views on AsyncPollPage. 14 routes with PrivateRoute auth gating. Dashboard with live/async tabs, stat overview cards, poll grid with status badges and contextual actions (start session, share link, view results, edit, publish). Modal-based session creation with room code display and copy-to-clipboard with "Copied!" feedback. Consistent custom UI components (Button with variants and loading state, Card with subcomponents, Modal with portal, Input with error display). Sticky headers, hover effects, decorative gradients. Gaps: ~18 `any` types, alert() used for error in AudiencePage, QuestionBuilder and statusColors duplicated, frontend/README.md is unmodified Vite scaffolding.
Backend Architecture & API Design6 / 15
Clean layered backend with routes→controllers→validations separation across 24 source files. PostgreSQL via Drizzle ORM with 10 well-normalized tables (schema.ts, 113 lines). Helmet, CORS (permissive origin:true), cookie-parser configured. Zod validation on polls/responses/sessions with safeParse, discriminated unions, and superRefine. DB transactions for atomic operations (poll creation, deletion, response submission, analytics flush). BullMQ with Redis for background jobs (expiry + periodic analytics flush). JWT middleware with two-tier pattern (authenticate + restrict). Rate limiting on response routes (5 req/min). Gaps: auth endpoints lack validation despite Zod schemas existing, getPollBySlug returns broken incomplete response for inactive polls (lines 278-285), no service layer (controllers mix DB queries, validation, business logic), socket events have zero authorization, no graceful shutdown, no pagination, no request body size limits, inconsistent JSON response shapes, backend not deployed.
Real-Time Updates Using WebSockets5 / 10
Socket.IO integrated with HTTP server (server.ts:23-32) with room-based architecture via room_code. 14 events: 7 incoming (join_room, next_question, prev_question, open_answers, close_answers, set_results_visible, end_session) + 7 outgoing (question_changed, answers_opened/closed, results_visible_changed, session_ended, poll_closed, vote_update). Live vote_update after each live response with Redis-derived counts (response.controller.ts:171-187). poll_closed emitted from BullMQ expiry worker (workers.ts:36). Frontend SocketContext with auto-connect and joinRoom helper. PresenterPage/AudiencePage use real-time for session control. Critical gaps: ALL socket events have zero authorization (any connected client can emit end_session, next_question etc. — restrictToSessionHost middleware exists but unused), analytics page WebSocket listener uses event name `poll:${pollId}:update` that server never emits (server only emits vote_update to room_code rooms), no typed Socket.IO events, no reconnect room re-join logic, useAuth imported in SocketContext but destructured to empty {}.
Code Quality & Project Structure5 / 10
Monorepo with clean backend/frontend separation, TypeScript strict mode throughout, comprehensive 374-line README with setup instructions, architecture diagrams, API tables, and WebSocket event docs. Good patterns: Axios interceptor for JWT refresh with _retry dedup (axios.ts:25-55), singleton Socket.IO via setIO/getIO (io.ts), Zod superRefine for conditional validation. Clean folder structure. Issues: README calls project "PulseBoard" but app is "SwiftPolls" — naming confusion throughout. Frontend README.md is unmodified Vite scaffolding. 18+ `any` types, 3 @ts-ignore directives, 6+ code duplication patterns (QuestionBuilder, statusColors, Redis flush), dead code (4 unused Zod schemas, restrictToSessionHost middleware, 6+ API functions for nonexistent endpoints), zero tests, no .env.example committed, no ESLint on backend, access token in localStorage, socket authorization gap.
46
PollForge
Subransu Sekhar Maharana · @maharanasubhransu6_08f9bfdd
49
PollForge is a solid full-stack polling platform with working authentication (JWT + bcrypt), dynamic poll creation (choice/text questions, mandatory/optional, expiry), thorough response validation with multiple layers of duplicate prevention, real-time analytics via Socket.IO, and a polished dark-themed UI. The submission covers all core requirements: anonymous/authenticated poll modes, public shareable links, expiry enforcement, analytics dashboard with per-question breakdowns and charts, results publishing, and real-time updates. Main weaknesses: no poll editing after creation, no email verification or password reset, authenticated duplicate check is non-atomic (race condition), no helmet/security headers, no automated tests, significant code duplication, and monolithic poll routes file. Deployed and accessible at https://pollforge-nine.vercel.app. Overall score: 49/80, placing it in the solid mid-range of submissions.
Authentication & Access Control6 / 10
Registration with bcryptjs (12 rounds) and login with constant-time comparison (controller.ts:8-70), JWT signing with env-based secret that fails fast if missing (middleware.ts:18-30), optionalAuth + requireAuth middleware enforcing anonymous vs authenticated poll modes server-side (routes.ts:159-173). Frontend AuthContext with localStorage, protected routes (App.tsx:38-45), and react-hook-form auth forms. Gaps: no auth-specific rate limiting (only global 100/5min shared with all /api), no token invalidation/logout mechanism, no password complexity beyond min 6 chars (models.ts:7), no email verification or password reset, /api/auth/me returns stale JWT data without DB re-fetch, no helmet/X-Powered-By suppression. Solid hackathon-level auth, missing production features.
Poll Creation & Question Management7 / 15
Dynamic PollBuilder (256 lines) with react-hook-form + zodResolver, supporting title/description/responseMode/thankYou customization, datetime-local expiry picker defaulting to 24h, and QuestionEditor (184 lines) with add/remove questions, choice/text types, mandatory/optional toggle, single/multiple select mode, and dynamic option add/remove (min 2 enforced). Both client-side Zod validation (validation.ts:41-59) and server-side Zod + Mongoose validation (routes.ts:13-46, model.ts:18-23) for questions and options. Gaps: no poll editing after creation (no PATCH/PUT endpoint exists), no slug/QR code, no draft status concept, key={index} anti-pattern in question rendering (PollBuilder.tsx:219), no drag-and-drop despite importing GripVertical. Functional creation flow, well-validated.
Response Collection Flow7 / 15
POST /api/polls/:id/responses (routes.ts:143-296) with defense-in-depth: Zod schema validation, poll existence check (404), published/expired gates (403), auth mode enforcement (401/400), required question check, unknown question ID validation, text non-empty check, option validation (known IDs, no duplicates, single/multiple choice rules). Duplicate prevention: DB-level unique partial index for anonymous (model.ts:74-77), application check for authenticated + cooldowns (routes.ts:183-194, 264-272), client-side localStorage flag (PublicPoll.tsx:33,132). Rate limiting: 10/min on response endpoint (routes.ts:59-64). Gaps: authenticated duplicate check uses non-unique index (race condition possible vs atomic anonymous path), no MongoDB transactions. Comprehensive validation and anti-spam measures.
Analytics & Feedback Dashboard6 / 15
buildAnalytics (service.ts:1-85) computes totalResponses, per-question answered/skipped/textResponses/option counts+percentages, participation (authenticated/anonymous breakdown, completion rate). AnalyticsPanel (358 lines) renders MetricCard grid (Total/Completion/Anonymous/Avg), hero panel with leading option, insight strip, expandable per-question cards with CSS animated progress bars and Recharts BarCharts, text response lists, and rule-based "AI summary"/"Sentiment" labels. Publish flow: POST /api/polls/:id/publish (routes.ts:298-312) toggles isPublished, results shown on public page via questionsOnly mode. Gaps: no CSV/export, no individual response viewing, no time-series/trend data, analytics loads ALL responses into memory (service.ts:9), N+1 in /mine (routes.ts:102-106), misleading "AI" labeling (rule-based thresholds at 40%/65%).
Frontend Experience6 / 10
6 routes (/ ,/signin, /signup, /dashboard, /p/:pollId, *) with dark-only theme and custom CSS animations (orbit, cardRise, pulseGlow). shadcn/ui v4 with 17 components + custom Typography abstraction. All states covered: loading skeletons (Dashboard.tsx:204-207, PublicPoll.tsx:166-175), error alerts, empty states ("Select or create a poll"), auth-required states ("Sign In Required" card), expired/published handling on public poll. react-hook-form for auth and poll creation. Share link copy with Check/Copy toggle + toast. Sidebar with poll list. Gaps: no confirmation dialogs beyond publish, key={index} patterns (PollBuilder.tsx:219, AnalyticsPanel.tsx:115), hardcoded developer names "Subransu"/"Maharana" in RegisterForm placeholders (lines 61,78), LoginForm/RegisterForm ~80% structural duplication, unused ERROR_MESSAGES/SUCCESS_MESSAGES constants, empty catch blocks, dark-only (no light mode).
Backend Architecture & API Design6 / 15
Backend (904 lines TS across 14 files) with routes→controller/service separation (class-based auth controller, inline poll handlers). TypeScript strict mode + noUncheckedIndexedAccess. Zod validation on all inputs (createPollSchema, submitResponseSchema, SignUpPayloadModel, SignInPayloadModel). Custom rate limiter (rateLimit.ts:101 lines) with sliding window, X-RateLimit-* headers, cooldown system, garbage collection. CORS with configurable origins (cors.ts). Global error handler with dev/prod message control (app/index.ts:32-39). Good Mongoose indexes: compound {poll, anonymousDeviceId} unique partial, {poll, respondent} non-unique. Gaps: no helmet (no security headers at all), X-Powered-By not suppressed (app/index.ts), no request logging, poll routes.ts is a monolith (315 lines with routes+validation+handlers all inline), 8 `any` type assertions (service.ts:25,48,49; routes.ts:70,281), in-memory rate limiting (lost on restart, single-process), no service layer for polls beyond single buildAnalytics function.
Real-Time Updates Using WebSockets6 / 10
Socket.IO server (index.ts:17-45) with room-based pattern: poll:join/poll:leave events with ObjectId validation (line 30), connectionStateRecovery (2min max), pingInterval/pingTimeout configured. Server emits poll:analytics with full payload on response submission and publish (realtime.ts:15-16). Client usePollSocket hook (55 lines) uses ref-callback pattern to avoid stale closures, autoConnect:false with manual connect after listeners, websocket+polling transport fallback, 8 reconnection attempts with 500ms-5s backoff, proper cleanup (leave room, off listeners, disconnect). Used in Dashboard (analytics update + poll list refresh) and PublicPoll (analytics only). Gaps: no Socket.IO authentication (anyone can join any poll room), module-level singleton io (realtime.ts:4), no error handling in emitPollAnalytics, no debouncing on client, full analytics rebuild per emit (queries all responses), no typed events.
Code Quality & Project Structure5 / 10
Clean backend/frontend monorepo with TypeScript strict mode throughout. Reasonable directory structure: backend/src/ (app/auth, app/polls, db), frontend/src/ (components, hooks, utils, types, context). React patterns mostly correct (useRef for callbacks, useEffect cleanup with isCurrent flags). Custom hooks (useApi, usePolls, usePollSocket) with proper abstractions. Gaps: zero automated tests (no test script or test files anywhere), no ESLint on backend, significant code duplication (LoginForm/RegisterForm ~80% overlap, auth dispatch in App.tsx:67-73 and PublicPoll.tsx:151-157, error message extraction repeated 12x), unused constants file (ERROR_MESSAGES/SUCCESS_MESSAGES in constants/index.ts:19-36 never imported), 8 `any` type assertions in backend, non-null assertions (!) scattered across frontend, empty catch blocks (LoginForm.tsx:38, RegisterForm.tsx:38), dead Vite starter assets (react.svg, vite.svg), hardcoded developer names as placeholders, no .env.example files, no root-level orchestration scripts.
47
S
PollStack
Sintu Gupta · @sintugupta108
49
PollStack is a functioning full-stack polling platform with React+Vite frontend and Express+MongoDB backend. Both deployments are live (Vercel frontend, Render backend). The application implements the core polling workflow: auth with email/password and Google OAuth via JWT, 3-step poll creation wizard with Zod validation, question-by-question response submission with fingerprint-based deduplication, analytics dashboard with live WebSocket updates, and published results view. Key strengths: robust poll lifecycle state machine (active/inactive/published/expired), clean WebSocket hook with heartbeat and auto-reconnect, "stale-while-revalidate" pattern for live vs. fetched data, optional auth middleware for anonymous/authenticated responses, and a consistent Tailwind CSS design. Key weaknesses: no input validation library on backend (manual ad-hoc checks only), no security middleware (no helmet, rate limiting, CSRF), raw error messages leaked to clients, hardcoded production hostnames, no poll edit/delete capabilities, significant code duplication (schemas, modals, copy logic duplicated across files), several dead/unused components, and no automated tests. Backend architecture is functional but lacks production hardening. Score: 49/100.
Authentication & Access Control6 / 10
Solid auth implementation: JWT-based login/register with bcrypt (10 rounds), Google OAuth with server-side ID token verification and intelligent account linking (authController.js:59-68 upgrades local users to Google auth). Mandatory auth middleware (auth.js) and optionalAuth middleware (optionalAuth.js) correctly separate protected and public routes. Frontend LoginPage (158 lines) supports email/password and Google OAuth with session restore via fetchMe. Gaps: no Helmet, no CSRF, no rate limiting, no email/password validation, JWT expiry hardcoded as '7d' (JWT_EXPIRES_IN env var unused), empty catch blocks mask error types in auth.js, token stored in localStorage (XSS vulnerable), auth Zod schemas defined but never consumed on frontend, and a routing bug where logged-in users see AdminApp instead of /poll/:link (App.jsx:36).
Poll Creation & Question Management7 / 15
Well-implemented 3-step poll creation wizard in CreatePollPage.jsx (565 lines): Step 1 collects title/description/expiry/anonymous toggle, Step 2 provides dynamic question builder with add/remove questions, add/remove options (min 2), and mandatory toggle per question, Step 3 shows review. Frontend uses Zod validation (pollSchema.js, 22 lines) on submission plus manual validation at each step transition. Success screen shows generated share link with copy button. Template system (TemplatesPage.jsx, 283 lines) allows saving and reusing question sets. Server-side validates title, questions.length, expiresAt (pollController.js:5-9). Polls support anonymous/authenticated modes. Gaps: no poll edit or delete endpoints, no server-side question structure validation (relies on Mongoose), no customization of share link slug.
Response Collection Flow7 / 15
Robust response collection: server validates poll existence, active status, expiry (responseController.js:40-44), checks mandatory questions (lines 54-58), and prevents duplicates via compound unique index {poll, fingerprint} (Response.js:19) plus an explicit findOne pre-check and 11000 error catch for race conditions (lines 86-87). Fingerprint uses user ID for authenticated or IP+User-Agent for anonymous (lines 29-36). Frontend TakePollPage.jsx (182 lines) presents questions one-at-a-time with progress tracking, Zod validation via submitResponseSchema, mandatory question enforcement with inline errors, and per-question progress broadcasting. checkStatus endpoint prevents re-submission attempts. SharedPollPage.jsx (203 lines) implements a 7-state view machine handling all lifecycle states. Gaps: no server-side validation of answer structure (questionId/selectedOption existence in the actual poll), getPublicResults lacks try/catch, and IP+User-Agent fingerprint is fragile for anonymous users.
Analytics & Feedback Dashboard6 / 15
Functional analytics across 3 pages: DashboardPage (338 lines) with 5 stat cards, live activity feed sidebar, question-wise breakdown table with colored bar segments; AnalyticsPage (117 lines) with summary cards, per-question progress bars with percentages and crown-icon top-option detection; ResponsesPage (113 lines) with summary table. PollResultsPage (119 lines) shows published results with sorted options and progress bars. computeAnalytics function (responseController.js:6-26) correctly aggregates per-question option counts. WebSocket data takes priority over API data via stale-while-revalidate pattern. Creator-only access enforcement on analytics endpoint. Gaps: no trend/time-series data, no CSV/export, no individual response viewing, no charting library (all custom div/SVG), decorative non-data-driven SVG chart on dashboard, PollDetailPage.jsx contains only hardcoded mock data, ResponsesChart.jsx and DeviceChart.jsx are static mock visualizations, no peak activity time or engagement metrics computation.
Frontend Experience6 / 10
Consistent Tailwind CSS design with responsive grid layouts, color-coded badges, and animated progress bars. All async operations have loading states with spinners. All failure modes have error states (not found, expired, auth required, already submitted, server error). Empty states for no polls, no analytics. Confirmation modals for destructive actions. Live connection indicator with green pulsing dot. Landing page has 7 well-designed sections. Gaps: no React Router (manual regex-based routing in App.jsx, admin pages cannot be deep-linked), 5 unused UI components (Modal, Stepper, RadioGroup, AppLayout, Table), several dead pages (UserApp.jsx, SettingsPage.jsx stub, PollDetailPage.jsx mock), duplicate sidebar implementations, inline modals repeated 6 times instead of using the Modal component, no dark mode, empty search bar placeholder in admin header, auth forms lack client-side Zod validation despite schemas existing.
Backend Architecture & API Design5 / 15
Functional MVC architecture with Express routes→controllers→Mongoose models separation across 19 REST endpoints. Mongoose schemas are clean (Poll.js:33 lines with embedded questions/options, Response.js:22 lines with compound unique index). Two-tier auth middleware (mandatory + optional) is well-designed. Poll state machine (active/inactive/published) with auto-expiry is solid. But significant gaps: no input validation library at all (no Joi/Zod/express-validator) — all validation is manual ad-hoc property checks; no security middleware (no helmet, no rate limiting, no express-mongo-sanitize, no request body size limits); raw err.message leaked to clients in 6 catch blocks exposing internal errors; hardcoded production hostname 'pollstack-zadw.onrender.com' in server.js:41 and ws.js:18; no centralized error handler; no service layer; unused dependency @react-oauth/google in backend; two .env.example variables (JWT_EXPIRES_IN, GOOGLE_CLIENT_SECRET) declared but never consumed in code; no poll delete/edit endpoints; no pagination on getMyPolls; no graceful shutdown.
Real-Time Updates Using WebSockets7 / 10
Well-implemented real-time system. Backend ws.js (93 lines) uses room-based Map<pollId, Set<ws>> pattern with 30-second ping/pong heartbeat, dead socket cleanup in broadcast, and graceful error handling for send failures. Two event types: ANSWER_PROGRESS (per-question live updates via broadcastProgress, responseController.js:93-115) and NEW_RESPONSE (full analytics push on submission, lines 67-82) plus POLL_PUBLISHED. Frontend useWebSocket hook (91 lines) provides auto-reconnect with 3-second delay, mount safety via mountedRef, intentional-close detection by nulling onclose before close(), connection status in Redux, and JSON parsing. LiveSlice manages rolling feed of 30 events. Stale-while-revalidate pattern prioritizes WebSocket data over API data across DashboardPage, AnalyticsPage, and ResponsesPage. Gaps: no authentication on WebSocket connections (anyone with a pollId can subscribe), hardcoded URL base for parsing, fixed reconnect delay (no exponential backoff), no message queuing for offline periods.
Code Quality & Project Structure5 / 10
Acceptable structure: clean Backend/Frontend separation, consistent naming conventions, Redux Toolkit with createAsyncThunk pattern, Zod validation schemas (3 total), component barrel exports, .env.example files for both packages, and an informative README with API overview. But significant quality issues: no TypeScript; schema duplication in Poll.js and Template.js (identical optionSchema/questionSchema lines 3-11); token extraction logic duplicated in auth.js and optionalAuth.js; inline modal pattern repeated 6 times; copy-to-clipboard logic duplicated 3 times; active-poll selection duplicated in 3 pages; poll-summary table rendering duplicated in 3 pages; near-duplicate Sidebar.jsx and AppSidebar.jsx; 5 unused UI components (Modal, Stepper, RadioGroup, AppLayout, Table); dead code (UserApp.jsx, SettingsPage.jsx stub, PollDetailPage.jsx mock, PollListPage.jsx mock); auth Zod schemas defined but never imported; App.css (187 lines) is unused Vite boilerplate; no automated tests; no ESLint on backend; console.log active in production; unused dependency in backend; stale .env.example variables.
48
S
PollSync
Sanghita Seal · @sanghitasea2005
49
PollSync is a well-implemented full-stack polling platform with solid JWT authentication (dual tokens, refresh rotation, httpOnly cookies), thorough vote validation with DB-level duplicate prevention, Socket.IO realtime analytics with full data push, and a polished dark glass-morphism frontend. The backend architecture is clean with proper separation of concerns, Joi DTO validation, and consistent error handling. Key gaps: no Helmet/rate-limiting/CSRF security middleware, email verification is bypassed, questions cannot be edited/deleted after creation, analytics lack trends/exports, no TypeScript, no tests, and console.log debug statements remain in production code (including a reset token leak). The custom CSS theming is impressive but no form library or UI framework is used. Total score: 49/80.
Authentication & Access Control6 / 10
Solid custom JWT auth with dual-token strategy: 15-min access token + 7-day refresh token in httpOnly/secure/sameSite cookie (auth.controller.js:15-20), bcrypt 12 rounds, SHA-256 hashed refresh token rotation in DB (auth.service.js:69-81). authenticate + optionalAuthenticate middleware (auth.middleware.js:6-52). Anonymous/authenticated poll mode enforced server-side (vote.service.js:27-29). Frontend AuthContext with bootstrap, ProtectedRoute, Axios interceptors for 401 auto-refresh (axiosClient.js:35-67). Forgot/reset password flows with hashed tokens + Nodemailer. Gaps: email verification bypassed (isVerified:true hardcoded, auth.service.js:22), access tokens in localStorage (XSS risk), no Helmet/CSRF/rate limiting, console.log leaks reset tokens (auth.service.js:122).
Poll Creation & Question Management6 / 15
Two-step poll creation: POST /api/polls creates metadata (CreatePollDto: name 3-100, desc 3-500, duration 1-60min, anonymous toggle), then POST /api/polls/:pollId/question adds questions with Joi-validated options (2-4 items, order uniqueness). UpdatePollDto PATCH for metadata and status transitions (draft→active sets start/end times). PollBuilder.jsx (469 lines) has dynamic options add/remove (2-4 limit), mandatory/optional toggle, start/end/publish controls, copy vote/analytics/result links. Status badge and question list with vote counts shown. Gaps: questions can only be added, not edited or deleted (no PATCH/DELETE question endpoints despite update-question.dto.js existing); no form library (imperative useState); no slug generation (ObjectId string as shareCode); no bulk question creation.
Response Collection Flow7 / 15
Defense-in-depth vote validation: poll existence→expiry→status=active→anonymous allowed→question existence→option existence checks (vote.service.js:15-43). Duplicate prevention at DB level via TWO partial unique compound indexes ({pollId,questionId,userId} and {pollId,questionId,userFingerPrint}, vote.model.js:45-59) plus application-level check. Anonymous voter fingerprint via crypto.randomUUID() stored in localStorage (poll.utils.js:21-31). Required question enforcement client-side (PublicPoll.jsx:30-32). Comprehensive UI states: loading, error, results-published, not-accepting-votes, already-voted, active voting form with radio buttons. skipAuth/skipRefresh Axios config for anonymous voting path. Gaps: no MongoDB transaction for vote submission (option increment + vote create + counters not atomic); race condition on option.votes increment (vote.service.js:70-71); required questions validated only client-side; unused `crypto` npm package (Node has built-in).
Analytics & Feedback Dashboard6 / 15
Creator-only analytics (GET /api/polls/:pollId/analytics) and public analytics (GET /api/public/analytics/:analyticsCode) share buildAnalyticsPayload (public.service.js:53-94) returning poll summary (totalVotes, totalParticipants) and per-question option vote counts. RealtimePollUpdates.jsx (235 lines) renders BarChart + PieChart via Recharts per question with stat cards (votes, participants, connection status). PollAnalytics.jsx (109 lines) shows public analytics with percentage bars. Results publishing via PATCH /api/polls/:pollId/publish-results, validates status=ended, sets isResultPublished. Dashboard.jsx (233 lines) lists polls with status badges, copy links, and contextual actions. Gaps: no trend data or time-series; no peak activity/drop-off tracking; no CSV/export; no MongoDB aggregation pipeline (brute-force O(n) in buildAnalyticsPayload); no individual response viewing; PollAnalytics only has percentage bars (no charts).
Frontend Experience6 / 10
Polished dark "glass morphism" theme via custom CSS (theme.css 1482 lines + Home.css 683 lines) with CSS custom properties, backdrop-filter, gradients, and smooth transitions. 11 React Router v7 routes covering landing, auth (5 pages), dashboard, create, builder, realtime, public poll, and analytics. Responsive at 3 breakpoints (900px, 620px, 1040px) with prefers-reduced-motion support. Reusable UI primitives: GlassCard, GradientButton, StatusBadge, EmptyState, LoadingSpinner, AnimatedModal, PageWrapper, Sidebar. Comprehensive state handling (loading/error/empty/filled) on all data-dependent pages. Toast notification system via context. Professional landing page with animated dashboard preview mockup, stats, features grid, testimonials, and pricing sections. Gaps: no CSS framework or component library (all custom); no form library (individual useState hooks); no data caching layer; no confirmation dialogs for destructive actions; hardcoded fake marketing stats on homepage.
Backend Architecture & API Design6 / 15
Clean module architecture: auth/poll/vote/public each with model/service/controller/routes/dto layers, common utils (ApiError class hierarchy with static factory methods for 400/401/403/404/409, ApiResponse, JWT utils). Joi-based DTO validation via BaseDto pattern + validate middleware on all mutation endpoints (14 DTO files). Global error handler catches all errors (app.js:48-55). Poll expiry via setInterval (60s with unref) + per-request expirePollIfNeeded. Proper Mongoose indexing (createdBy, pollId, compound unique for dedup). Separate Poll/Question collections (not embedded). CORS with credentials, cookie-parser, Express 5.x. Gaps: NO Helmet, NO rate limiting, NO CSRF, NO request body size limits; console.log in 8 production locations including reset token leak (auth.service.js:122); CORS origin:true too permissive; no MongoDB connection retry; no graceful shutdown; no pagination on list endpoints; unused import from 'console' (auth.service.js:1); stale e-commerce email template (sendOrderConfirmationEmail); no tests at all.
Real-Time Updates Using WebSockets6 / 10
Socket.IO 4.8.3 integrated with Express HTTP server, room-based pattern (poll:<pollId>). Server emits poll-vote-updated with FULL analytics payload (not just invalidation signal) after each vote (vote.controller.js:13-15). Creator authorization on socket join: JWT token from handshake.auth verified, poll ownership checked (server.js:46-74). Client connects with auth token, joins room on mount, listens for analytics pushes and updates Recharts charts in realtime (RealtimePollUpdates.jsx:84-123). HTTP fallback polling at 2.5s interval. Proper cleanup: leave-poll-room + disconnect on unmount. join/leave/error/joined socket events handled. Vite proxy for /socket.io with WS support. Gaps: only covers creator realtime analytics page, not public voting page for live response counts; 2.5s fallback polling is redundant overhead alongside socket; no debouncing; no events for poll publish/delete/expire; no typed Socket.IO events (JS not TS).
Code Quality & Project Structure6 / 10
Clean monorepo (backend/ + frontend/) with consistent module architecture. Backend follows model-service-controller-routes-dto pattern uniformly. Frontend organized by features (auth/polls), shared components (ui/layout), utilities, and api layer. Consistent naming (camelCase functions, PascalCase classes/components). ESLint configured on frontend. Comprehensive README with setup instructions, API table, route table, and deployment notes. Docker Compose for local dev. .gitignore properly configured. Gaps: No TypeScript (plain JS); zero automated tests (backend test script is stub); console.log in 8 production locations; duplicate token extraction logic in authenticate + optionalAuthenticate middleware; unused Joi import in auth.model.js; Hindi comment in poll.model.js:117 ("hoga"); stale sendOrderConfirmationEmail from template; isVerified:true hardcoded bypassing email flow; commented-out stale code in polls.service.js lines 4-12.
49
Pulse Board
Saurabh Ravte · @saurabhravte
49
PulseBoard is a well-implemented full-stack polling platform with solid architectural foundations. The backend features clean layered MVC with Drizzle ORM, Zod validation, JWT auth with refresh rotation, rate limiting, and Socket.IO real-time updates. The frontend has a custom Tailwind v4 design system with comprehensive state handling across 10 routes. Key strengths: thorough response validation (question/option membership checks, single-choice enforcement, mandatory gating), transactional DB operations, dual auth (custom JWT + Clerk OAuth sync), room-based WebSocket with data pushed, and robust analytics with SQL aggregation and respondent tracking. Primary weaknesses: Clerk sync endpoint has a security vulnerability (no token verification), no poll editing after creation, no automated tests, hardcoded JWT fallback secrets, notable code duplication (5+ patterns), and WebSocket lacks reconnect room re-join logic. The deployed Vercel frontend is accessible but no remote backend URL is configured, so the full stack cannot be verified live.
Authentication & Access Control6 / 10
Solid dual JWT auth with bcrypt 12 rounds, access+refresh token strategy with httpOnly refresh cookie and SHA-256 hash rotation (auth.service.ts:50-82). authenticate and optionalAuth middleware properly structured (auth.middleware.ts:20-50). Anonymous vs authenticated poll modes enforced server-side (polls.service.ts:130-134). Frontend authStore with localStorage persistence, 401 interceptor with concurrent-refresh deduplication (api.ts:24-49). Critical gap: Clerk-sync endpoint (auth.service.ts:94-115) accepts unverified clerkId/email/name from request body with zero Clerk token verification — anyone can impersonate any user. JWT secrets have hardcoded fallbacks "dev-access-secret-change-me" / "dev-refresh-secret-change-me" (jwt.utils.ts:5-8). No email verification, password reset, Helmet, or CSRF protection.
Poll Creation & Question Management6 / 15
Working multi-question poll builder with dynamic add/remove questions (capped at 50) and options per question (capped at 10, min 2) in polls.new.tsx (421 lines). Per-question mandatory/optional toggle (line 269-278), single/multi-select toggle (line 309-319), anonymous/authenticated response mode, and optional expiry datetime picker. Server-side Zod validation via CreatePollDto (create-poll.dto.ts) validates title, responseMode enum, questions array 1-50 items with nested option validation 2-10 per question. Transactional creation in PollModel.createWithQuestions (polls.model.ts:29-64). Client-side validation checks title, prompts, and option labels (polls.new.tsx:137-151). Significant gap: no poll editing after creation — no PATCH endpoint to modify questions/options. Individual useState hooks throughout (no form library). Slug collision retry silently fails after 5 attempts.
Response Collection Flow7 / 15
Thorough server-side validation in submitResponse (polls.service.ts:122-210): poll existence check, isAcceptingResponses gate (active status + expiry), authenticated mode enforcement (line 130-134), duplicate prevention by respondentId or ipHash (SHA-256), question existence validation with Map index, option-belongs-to-question check (line 179-182), single-choice duplicate enforcement (line 184-186), and mandatory question enforcement (line 190-194). Transaction-based submission in PollModel.recordResponse (polls.model.ts:185-228) with atomic response+answer insert + live count aggregation. Proper HTTP status codes (401/403/404/409/410). Client-side validation names exact question prompts (p.$slug.tsx:203-211). Comprehensive UI states: submitted, closed, published, auth-required, expired, not found. Minor gaps: no DB-level unique constraint (race condition possible on dedup), IP-based dedup bypassable via VPN, no localStorage client-side dedup tracking.
Analytics & Feedback Dashboard6 / 15
Solid SQL aggregation in analytics.service.ts (173 lines) computing per-question option counts with percentages, total response count, and response time-series grouped by day. Respondent list with named users (name/email/timestamp) and anonymous count via LEFT JOIN on usersTable (lines 136-155). Frontend analytics page (polls.$pollId.analytics.tsx, 443 lines) displays stat cards (total responses, questions, avg answers/Q), per-question percentage bars with counts, BarChart component for distribution, respondent list with signed-in/anonymous breakdown, and public link card. Results publishing via POST /api/polls/:pollId/publish and public results page (p.$slug_.results.tsx). Creator-only gating enforced. Gaps: response time-series data is fetched but never rendered in the UI (responsesOverTime exists in DTO but no chart consumes it), no CSV/export functionality, no individual response answer viewing (only respondent names not their actual selections), published results page is simplified compared to creator analytics (no time-series, no respondents, no stat cards).
Frontend Experience6 / 10
Custom Tailwind v4 design system with semantic CSS token layer (--pb-* variables, index.css:186 lines) supporting light/dark mode toggle persisted to localStorage (theme.ts:18). Consistent component library: Button, Card, Badge, Input, Textarea, Label, Spinner, FullPageLoader, Countdown, BarChart, FloatingNavbar (15 components). All 10 routes handle loading (FullPageLoader), error (danger-colored cards), empty (CTA cards), and auth states. Responsive layout from mobile to desktop. Visual flourishes: Spotlight SVG glow, BackgroundBeams animation, animated progress bars (CSS transitions), countdown timer. Floating navbar with scroll-hide behavior. No leftover template code. Gaps: no form library on poll creation (individual useState hooks, 420 lines), Roboto Mono-only typography (no fallback body font), confirmation dialogs are basic window.confirm(), copy-to-clipboard has no visual "Copied!" feedback, motion dependency unused in package.json.
Backend Architecture & API Design7 / 15
Clean layered MVC architecture with module-based separation: routes→controllers→services→models. Drizzle ORM schema (schema.ts, 251 lines) with 6 tables, 3 PostgreSQL enums, 7 relation definitions, proper indexes, and FK cascades. Zod validation with abstract BaseDto pattern and reusable validate middleware (Validate.middleware.ts). Two multi-statement DB transactions (poll creation and response recording). Custom ApiError class hierarchy with HTTP status factory methods and centralized error middleware (error.middleware.ts). In-memory token-bucket rate limiter with 3 presets (apiLimiter 120/60s, authLimiter 30/15m, submitResponseLimiter 10/60s) and standard headers. Application factory pattern (createApplication in server.ts). Graceful shutdown with SIGINT/SIGTERM (index.ts:23-31). JWT refresh token rotation with SHA-256 hash comparison. CORS configured for both Express and Socket.IO. Gaps: Clerk-sync accepts unverified identity, default JWT secrets hardcoded, no DB-level dedup constraint, no route param validation, no ESLint on server, no automated tests.
Real-Time Updates Using WebSockets6 / 10
Socket.IO server integrated with Express HTTP server, room-based pattern with poll:join/poll:leave client events for `poll:{pollId}` room management (sockets/index.ts:14-26). Three server-emitted events: poll:response (carries pollId, totalResponses, optionCounts per option), poll:published, poll:closed. Client properly subscribes/unsubscribes on analytics page mount/unmount (polls.$pollId.analytics.tsx:116-153), patches analytics state in-place with incoming optionCounts, and background-refetches for fresh respondent data. Null-safe emit helpers. WebSocket + polling transport fallback. Typed event interfaces on both client and server. Gaps: no reconnect room re-join logic (Socket.io auto-reconnects but rooms are lost), no socket on the voting page (voters don't see live updates), no authentication on socket connections, poll:response pushes live counts bypassing resultsPublished gating, no debouncing on client event handlers, no socket error/reconnect UI on client.
Code Quality & Project Structure5 / 10
Clean monorepo with client/ and server/ separation, consistent naming conventions. TypeScript used throughout with strict mode on server (noUncheckedIndexedAccess) and most strict flags on client. Layered architecture with common/ shared modules (db, dto, sockets, middleware, utils). CSS design token system with --pb-* namespace. Custom 18-line pub/sub store for auth/theme state. Proper useEffect cleanup patterns with cancellation flags in all data-fetching effects. Comprehensive README with setup instructions, env vars table, and project structure. Gaps: zero automated tests anywhere, no ESLint on server, no Prettier, 5+ duplication patterns (option bars in analytics + results, Clerk sync across 2 components, poll ownership check 4 times, findFullById/findFullBySlug nearly identical, try/catch→next boilerplate in all controllers, Spinner SVG duplicated), unused motion dependency in client/package.json, @ts-ignore on every route file export, noUnusedLocals:false on server tsconfig.
50
Pulse Pro
Aaudh Kumar · @aaudhkumar
49
Pulse Pro is a visually ambitious full-stack polling platform with strong backend engineering (Redis atomic vote operations, retry logic, JWT with blocklist, Socket.IO with Redis adapter). The vote service implementation (Redis Lua scripts, dual-layer deduplication, rollback) is the standout feature at param-3 score 8. Authentication and backend architecture are solid at 7 each. However, the frontend has critical bugs (broken imports in PollCreator, undefined references in ResultsPage, unbound form inputs) that would prevent core flows from working, pulling down param-2 (5), param-5 (5), and param-8 (5). Analytics is decent at 6 but lacks time-series and export. Real-time WebSocket is functional but limited (invalidation-only, 1 event type). No working deployment was found. Overall: a strong backend with an ambitious but buggy frontend that shows genuine engineering skill but doesn't fully deliver a working product.
Authentication & Access Control7 / 10
JWT-based auth (bcryptjs 12 rounds) with Google OAuth via Passport.js. Registration/login Zod validation (shared/schemas.js:4-24), Redis-backed JWT jti blocklist for logout (auth.controller.js:34-46), auth middleware with blocklist check (auth.middleware.js:22-33). Anonymous/authenticated poll modes enforced via requireAuth flag in vote.service.js:133-135. Rate limiting on auth (10 req/15min). Zustand auth store with sessionStorage persistence. AuthGuard protected routes with loading state. Gaps: no refresh token rotation, no email verification, no password reset, no CSRF protection, tokens in sessionStorage not httpOnly cookies.
Poll Creation & Question Management5 / 15
Zod poll schema validates title, questions (min 2 options each), future expiry (shared/schemas.js:39-50). Poll model with embedded questions, isMandatory, options with uid counts (Poll.js:23-49). QuestionBlock component (78 lines) with dynamic options add/remove and mandatory toggle. SettingsPanel with isAnonymous, expiresAt datetime, isPublished toggles. However, PollCreator.jsx has broken imports (imports from './hooks/' and './components/' while being IN components/ directory itself) and doesn't pass required props (question, onUpdate, onAddOption) to QuestionBlock. Title input unbound to state. No poll editing endpoint exists. These are real bugs that would prevent the creation flow from working despite solid underlying architecture.
Response Collection Flow8 / 15
Sophisticated vote service with Redis atomic Lua script for duplicate prevention (vote.service.js:38-66), dual-layer deduplication via Redis SET + MongoDB compound unique indexes (Response.js:39-54). IP-based and user-based voter identification. Cache-first poll metadata lookup with MongoDB fallback (lines 78-109). Mandatory question validation on client (useVoteSubmission.js:38-42) and server (lines 156-179). Expiry enforcement (line 129-131), requireAuth check (line 133-135), duplicate answer detection (lines 146-154). Retry logic for MongoDB persistence (50/200/800ms delays) with Redis rollback on failure (lines 245-283). Rate limiting on vote endpoint (5 req/min). Gaps: no per-question single-option enforcement on client, responsive count increment uses arrayFilters that could race.
Analytics & Feedback Dashboard6 / 15
AnalyticsDashboard (418 lines) with animated KPI cards (Total Votes, Questions, Anonymous, Authenticated), per-question bar breakdown with animated bars and SVG donut chart toggle (AnalyticsDashboard.jsx:189-361). MiniQuestionCard sidebar with leading option identification. DashboardSkeleton loading state. getRealTimeStats reads counts from Redis hash per question (analytics.service.js:8-42). ResultsPage at /results/:slug for public results with animated progress bars. Publish endpoint via PATCH /:pollId/publish. Gaps: no time-series/trend data, no CSV/export, no individual response viewing, ResultsPage has runtime bug (api imported but undefined), getSyncFromDB is an empty stub, anonymous/authenticated breakdown uses poll-level settings rather than per-vote data, no peak activity or completion rate metrics.
Frontend Experience5 / 10
Beautiful dark-themed UI with Tailwind CSS v4, Framer Motion animations throughout. React Router v7 with 10 routes, lazy loading, ErrorBoundary, Suspense. Loading spinners, error states (VotePage.jsx:62-93), empty states (OverviewPage.jsx:142-151), skeleton loading. Custom UI components (Button, Card, Badge, Input, Toggle, SocialButton). Responsive layout with mobile hamburger menu. Landing page (1103 lines) is extensive with mock dashboards. However, the app has critical bugs: PollCreator.jsx has broken relative imports (imports from './hooks/' from within components/), QuestionBlock called without required props, title input unbound, SettingsPanel creates separate usePollCreator instance. ResultsPage references undefined `api`. These would prevent core flows from functioning.
Backend Architecture & API Design7 / 15
Clean layered architecture: routes→controllers→services→models with separation of concerns. Zod validation middleware (validate.js), Redis-backed rate limiting (rate-limit.js), Helmet with CSP/HSTS/referrerPolicy (app.js:21-43), CORS with configurable origins, JWT auth middleware with Redis blocklist, structured AppError class with isOperational flag (errors.js), global error handler with Mongoose error handling + dev/prod modes (error.middleware.js). Pino logger with sensitive field redaction. Docker Compose for MongoDB+Redis. Env validation at startup with production HTTPS checks (server.js:9-47). Poll model with compound index, Response model with partial unique indexes for deduplication. Gaps: no TypeScript, analytics.getSyncFromDB is empty stub, broadcastStatsUpdate defined but unused, no pagination on getMyPolls, main field in package.json says "server.ts" but files are .js.
Real-Time Updates Using WebSockets6 / 10
Socket.IO with Redis adapter for multi-node scaling (ConnectionManager.js:34). JWT auth on handshake (lines 38-60), creator-only room authorization via MongoDB lookup (poll.gateway.js:14-24). JOIN_POLL_ANALYTICS/LEAVE_POLL_ANALYTICS room management with ObjectId validation. BroadcastEngine.notifyPollUpdate emits STATS_UPDATED after vote submission (vote.service.js:287-289). Client useSocket hook with token auth, 5 retry attempts (useSocket.js:25-31). useRealTimeStats with Zod validation of socket payloads (lines 13-54), custom 500ms throttle (lines 56-97), HTTP refetch fallback on {update:true} signal (lines 144-148). Proper cleanup on unmount. Gaps: only 1 event type, pushes invalidation signal not data, no real-time on voting page, no reconnection room re-join logic, no events for poll publish/delete.
Code Quality & Project Structure5 / 10
Clean monorepo structure (Backend/, client/) with feature-based organization. Backend has clear MVC layers, client uses features/auth, features/poll-creator, features/poll-voter, features/analytics. Custom hooks (useSocket, useDebounce, useClickOutside, useHasHydrated), Zustand stores (useAuthStore, usePollStore, useSocketStore, useUIStore). .env.example for both packages, Docker Compose for infra, ESLint configured. However: no TypeScript anywhere, zero automated tests, critical runtime bugs (PollCreator broken imports, ResultsPage undefined api, unbound form inputs), useAuthStore.js.bak committed, dead code (broadcastStatsUpdate unused, getSyncFromDB empty stub), client and server use different zod major versions (v4 vs v3) making shared/schemas.js potentially incompatible, no CI/CD, no root-level README with setup instructions.
51
PulseBoard
Krish · @skull_coder
49
PulseBoard is a full-stack MERN polling platform with solid backend architecture and real-time Socket.IO updates. Strengths: comprehensive authentication (JWT with refresh rotation, Google OAuth, email verification), thorough response validation with multi-layer duplicate prevention (Redis atomic locks + MongoDB indexes), clean layered backend architecture, and Redis pub/sub for real-time analytics. Key weaknesses: no deployment (not verifiable live), zero tests anywhere, significant code duplication across both frontend and backend, plain JavaScript without TypeScript or JSDoc, no poll editing after creation, analytics lack exports/charts/trends, localStorage token storage (XSS risk), and missing security headers (no Helmet). Overall a functional project with sound engineering fundamentals but lacking production polish.
Authentication & Access Control6 / 10
Comprehensive auth implementation: register with email verification via Mailtrap, login with JWT access+refresh tokens, Google OAuth, refresh token rotation, logout with Redis blacklisting (auth.service.js:17-266). Protected route guard in Dashboard.jsx:6-10. Authenticated middleware checks Redis blacklist (auth.middleware.js:18-21). Anonymous vs authenticated poll modes enforced at response service level (response.service.js:124-232). Creator-only gates for publish/delete/analytics (poll.service.js:62,195; socket.js:102). Gaps: no deployed backend to verify live; tokens in localStorage (XSS-risk, README-acknowledged line 255); Dashboard only checks token existence, not validity; no Helmet security headers.
Poll Creation & Question Management6 / 15
Functional poll creation form at CreatePoll.jsx (282 lines) with dynamic question/option add/remove controls (up to 5 questions, 2-4 options each), required/optional toggle, response mode, and expiry duration selection. Server-side Zod validation via PollDto (poll.dto.js:6-31) enforces title (1-200 chars), questions array (1-5 items, 2-4 options each), responseMode enum, and expiryDuration enum. MongoDB schema also validates question/option counts (poll.model.js:30-43,58-67). Client-side validation checks empty fields before submission (CreatePoll.jsx:83-100). Significant gap: questions and options cannot be edited after poll creation — no PATCH endpoint exists for updating questions, only publish (poll.controller.js:17-28) and delete (poll.controller.js:68-79).
Response Collection Flow7 / 15
Thorough multi-layer response validation: server validates poll existence and expiry (response.service.js:37-49), validates every questionId/selectedOptionId against actual poll document with option membership checks (lines 55-87), enforces required questions (lines 93-105), differentiates authenticated vs anonymous flows (lines 124-232). Defense-in-depth duplicate prevention: atomic Redis SET NX (lines 150-157, 201-207) + MongoDB fallback check (lines 164-173, 215-224) + dual partial unique compound indexes (response.model.js:70-96). Redis-based analytics increment on submission with Redis pub/sub push to Socket.IO. Client-side required-question validation before submit (PollVote.jsx:51-58). FingerprintJS for anonymous dedup (PollVote.jsx:73-76). Clean UI states: loading, error, expired-unpublished, published-results, submitted-success, active-voting. Gap: ResponseDto Zod schema doesn't enforce non-empty answers array or ObjectId format on IDs.
Analytics & Feedback Dashboard6 / 15
Working analytics dashboard (PollAnalytics.jsx, 227 lines) shows total responses, per-question option breakdowns with vote counts and percentages, animated CSS progress bars, stat cards (responses, status, mode). Result publishing: after expiry, creator can publish (poll.service.js:52-73), then public link shows final outcome with all vote counts and percentages (PollVote.jsx:124-167). MongoDB aggregation pipeline for vote counting (poll.service.js:134-138). Redis HINCRBY for real-time per-option vote counting (response.service.js:261-269) with HGETALL reads. Creator-only analytics access enforced client-side (PollAnalytics.jsx:25-28) and server-side (socket.js:102). Gaps: no export functionality (CSV/PDF), no trend data or time-series visualization, no individual response viewing, getPoll returns inconsistent response shapes based on poll state, no charts — only progress bars.
Frontend Experience5 / 10
Eight routes with React Router v7 (App.jsx): register, login, verify-email, public poll vote, dashboard layout with nested my-polls, create-poll, and analytics. All components handle loading, error, and empty states explicitly. PollVote handles 5 distinct UI states: loading, error, expired-unpublished, published-results, submitted-success, and active-voting. Axios interceptor for auto token refresh (api.js:32-81). Copy-to-clipboard share link (PollAnalytics.jsx:116-118). Weaknesses: forms use manual useState rather than react-hook-form (listed in package.json but not used), no confirmation dialogs for destructive actions, login/signup forms have minimal validation, Dashboard redirect uses localStorage check without token validity verification, login redirect uses window.location.href bypassing React Router, basic CSS without animations beyond progress bars, no toast notifications, no PWA/offline support.
Backend Architecture & API Design7 / 15
Clean layered architecture with routes→controllers→services→models→DTOs separation across three modules (auth, poll, response). All inputs validated via Zod schemas using BaseDto pattern with validateSchema middleware (validate.middleware.js). Custom ApiError class with static factories for all HTTP status codes (api.error.js:1-42), centralized errorHandler middleware (errorHandler.middleware.js:1-29). Rate limiting on auth routes (rateLimitter.middleware.js:4-14). CORS configured to FRONTEND_URL. MongoDB Response model has sophisticated XOR validation (response.model.js:53-62) and partial unique compound indexes for duplicate vote prevention (lines 70-96). Redis used for caching, pub/sub, token blacklisting, and atomic duplicate prevention. Consistent ApiResponse wrapper. Gaps: missing index on Poll.createdBy (causes full collection scans on myPolls), no Helmet security headers, hardcoded Mailtrap config (mail.js:9-10), expiryMap and Zod enum must be manually synced (poll.service.js:15-19 vs poll.dto.js:30), dead code `hashed` function (auth.service.js:15), silent catch swallows JWT errors (poll.controller.js:41), verifyEmail DTO doesn't validate hex format.
Real-Time Updates Using WebSockets7 / 10
Socket.IO server integrated with Express (socket.js, 225 lines). Room-based pattern: creators join `creator:{userId}:poll:{pollId}` rooms with ownership verification (socket.js:91-108). Redis pub/sub for horizontal scaling (POLL_UPDATES channel, lines 41-77, 203-225). Server emits `analytics-init` with full snapshot on dashboard join (lines 164-174) and `poll-updated` on each new vote submission (lines 67-75). Client connects via JWT-authenticated socket, joins dashboard room on connect, listens for updates (PollAnalytics.jsx:45-81). Server-side: publishes totalResponses + per-question option vote counts via Redis HINCRBY/HGETALL (response.service.js:261-289). MongoDB aggregation fallback for cold Redis cache (socket.js:131-162). Live badge with visual indicator when connected. Cleanup on unmount (PollAnalytics.jsx:78-80). Gaps: real-time updates only go to creator dashboard, not voting page; no `poll-expired` or `poll-published` lifecycle events; socket error handling only logs to console; no explicit room leave on disconnect; no reconnection strategy configured on client.
Code Quality & Project Structure5 / 10
Clean monorepo structure with client/server separation. Consistent module pattern across three modules (controller, service, model, routes, dto). Section comments aid navigation in complex files. Negligible static data (~2% of codebase). Proper .env.example files for both client and server. Critical issues: zero tests of any kind (unit, integration, or E2E); plain JavaScript throughout with no TypeScript, PropTypes, or JSDoc annotations; significant duplication — password regex duplicated across 3 files (register.dto.js:16-19, login.dto.js:10-13, Signup.jsx:106-109), SHA-256 hashing pattern repeated in 4 locations, identical error-handling catch blocks across 7+ components, localStorage token access pattern duplicated across 6 files; dead code `hashed` function (auth.service.js:15); debug string "POLL:" left in controller response (poll.controller.js:50); no ESLint/Prettier configuration beyond client's eslint.config.js.
52
D
PulseBoard
DARSHIL LAKHANI · @darshillakhani95
49
PulseBoard by darshil-lakhani is a solid full-stack polling platform (deployed at pulseboard.darshillakhani.com) with React/TypeScript frontend and Express/Prisma/PostgreSQL backend. Strengths: typeform-like one-question-at-a-time poll flow with 9-state machine, dynamic poll builder with RHF + Zod, thorough server-side response validation (7 checkpoints), Recharts analytics with timeline, room-based Socket.io real-time updates, clean layered API architecture, comprehensive README. Key weaknesses: no anonymous duplicate vote prevention (critical gap allowing unlimited submissions), missing security layers (no Helmet, no rate limiting), socket analytics leaked before publish, poll editing destroys existing response data, and zero automated tests. Score 49/95 — above the 43.0 median, placing in the upper-third of submissions.
Authentication & Access Control6 / 10
Custom JWT auth with register/login endpoints (auth.service.ts, 49 lines), bcrypt 12-round hashing (password.ts), 7-day JWT tokens (jwt.ts), requireAuth + optionalAuth middleware pair (auth.middleware.ts, 44 lines). Frontend: Zustand authStore with login/register/logout/loadUser + localStorage persistence (authStore.ts, 59 lines), ProtectedRoute with loading/authenticated/unauthenticated states, AuthLayout redirects authenticated users from login pages, auth-aware PublicPollPage with auth-required state + redirect-to sign-in link (PublicPollPage.tsx:53-54, 142-163). Anonymous vs authenticated poll modes enforced both server-side (response.service.ts:30-35) and client-side. Gaps: no refresh token mechanism (single 7d JWT), tokens in localStorage, no rate limiting on auth endpoints, registration leaks email existence (409 "Email already registered" vs login's generic 401), no Helmet security headers, no email verification or password reset.
Poll Creation & Question Management7 / 15
Full-featured poll builder (CreatePollPage.tsx, 519 lines) with React Hook Form + useFieldArray for dynamic questions/options, per-question required/optional toggle (ToggleLeft/ToggleRight UI), response mode toggle (ANONYMOUS/AUTHENTICATED), expiry datetime-local picker with 7-day default, poll preview mode (editor/preview toggle). Order normalization before submit (CreatePollPage.tsx:274-282). Question reorder via swap (moveUp/moveDown buttons visible on hover), add/remove questions/options with min 2 options enforced. Dual Zod validation: backend createPollSchema (poll.validator.ts, 50 lines) validates title, questions (1-50, min 2 options each, max 20), future expiry; frontend poll.schema.ts mirrors it. PATCH update endpoint exists (poll.service.ts:58-105). Backend create operation creates questions+options in nested Prisma create. Gaps: poll editing deletes all existing questions and recreates (poll.service.ts:73) - destroys response data; @dnd-kit dependency included but unused (only click-based reorder); QuestionCard/OptionField props typed as `any` (CreatePollPage.tsx:91-100); preview uses array index as React key.
Response Collection Flow6 / 15
Response submission with 7-layer server-side defense (response.service.ts, 103 lines): poll existence (404), isActive check (410), expiry check (410), auth mode enforcement (401), duplicate authenticated user check via compound unique index (409), required questions validation (422), cross-reference validation - option belongs to question and question belongs to poll (422, lines 67-86). Frontend: typeform-like one-question-at-a-time flow with 9 explicit UI states (loading/intro/questions/submitting/success/expired/error/results/auth-required) at PublicPollPage.tsx:26. Progress bar animation, per-question radio-card option selection with Framer Motion, canProceed() client-side check. Critical gap: NO duplicate prevention for anonymous responses - the @@unique([pollId, userId]) constraint treats null as distinct, and the duplicate check at response.service.ts:38-48 only runs when userId is truthy. Anonymous users can submit unlimited responses to the same poll.
Analytics & Feedback Dashboard6 / 15
Analytics dashboard (AnalyticsPage.tsx, 387 lines) with: total responses stat card, per-question option breakdowns with counts + percentages (1-decimal precision, computed at response.service.ts:128-148), response timeline AreaChart (7-day daily aggregation via Recharts), per-question BarChart with colored bars, animated percentage bars via Framer Motion, 4 stat cards (Total Responses/Questions/Response Mode/Status). DashboardPage shows aggregated creator stats: total/active/expired polls, total responses, recent 5 polls (poll.service.ts:131-153). Publish flow with irreversible confirmation modal (AnalyticsPage.tsx:330-360), public results view (PublicResultsView in PublicPollPage.tsx:377-478). Gaps: no CSV/PDF export, no individual response viewing, no anonymous/authenticated response breakdown, no peak activity metrics, no "leading option" callout, no response velocity data beyond basic timeline, `isActive` field used in analytics header but type may not always be populated.
Frontend Experience7 / 10
8 routes with clean auth separation (App.tsx): public poll, auth routes, protected dashboard routes with layouts. Consistent Tailwind CSS v4 design system with custom design tokens (cn utility). Framer Motion throughout: page transitions, AnimatePresence for question/option add/remove, option selection feedback, animated progress bars, success checkmark spring animation. Comprehensive state coverage: loading (PageSpinner in 5 locations), error (EmptyState with AlertCircle + Retry in 3 locations), empty (EmptyState with CTA in 2 locations), success states. Responsive: sidebar on desktop, bottom icon bar on mobile (DashboardLayout.tsx). Component library: Button (5 variants, 3 sizes, loading state), Input, Textarea, Card, Badge (6 colors), Modal (backdrop blur, spring animation, body scroll lock), EmptyState, Spinner. Landing page with hero, features, comparison table, mock dashboard. Gaps: no lazy loading/code splitting (706-line LandingPage loads on every route), `any` types in form subcomponents, no draft persistence on form refresh, landing page advertises fictional features (SSO, Slack/Zoom integrations, sentiment analysis).
Backend Architecture & API Design6 / 15
Clean layered architecture: routes->controllers->services->Prisma (21 source files, ~835 lines of logic). RESTful API with 15 endpoints, proper HTTP verbs and status codes (201/401/403/404/409/410/422). Zod validation middleware HOF (validate.middleware.ts:5-19), 8 backend schemas across validators/. Consistent {success, data, error} response envelope via apiResponse.ts helpers. Prisma schema with 6 models + ResponseMode enum, cascade deletes correctly specified, compound unique constraints on Question(pollId,order), Option(questionId,order), Response(pollId,userId), Answer(responseId,questionId). Auth middleware pair (requireAuth + optionalAuth) with proper Express type augmentation. Gaps: no Helmet (zero security headers), no rate limiting on any endpoint, critical anonymous duplicate vote gap, editing poll deletes all responses (deleteMany+recreate), dynamic import anti-pattern in poll.controller.ts:92, dead code (getIO(), analytics-update type), non-null assertions on env vars, err:any in all catch blocks, Object.assign(new Error(...)) instead of custom AppError class, no pagination, no background expiry job.
Real-Time Updates Using WebSockets5 / 10
Socket.io integrated with HTTP server (sockets/index.ts, 44 lines), room-based pattern: clients join/leave `poll:{id}` rooms. Server emits `poll-update` event with full analytics payload on each response submission (public.controller.ts:27-30). Client singleton socket with WebSocket + polling fallback (socket.ts, 37 lines), AnalyticsPage joins room on mount and updates analytics state in-place on new-response event (AnalyticsPage.tsx:79-91), proper cleanup on unmount (leave room + remove listener). Typed event payload with type + payload. Gaps: only covers analytics page (no live updates on public voting page), no socket authentication (any client can join any room and receive analytics for unpublished polls - emitPollUpdate emits regardless of isPublished), no reconnection state sync, only 1 event type emitted, dead code: getIO() function exported but never called (line 32-35), "analytics-update" type declared but never emitted (line 38), disconnect handler is empty.
Code Quality & Project Structure6 / 10
Clean monorepo (backend/ + frontend/) with Docker Compose, multi-stage Dockerfiles, Nginx reverse proxy config. TypeScript strict mode on both, ESLint on frontend, .env.example files, comprehensive README with API docs and deployment guide. Consistent naming conventions and layered architecture. Gaps: zero automated tests across entire codebase; `any` types in CreatePollPage form subcomponents (QuestionCard/OptionField props typed as any, lines 91-100); status badge logic duplicated 3 times (DashboardPage, MyPollsPage, AnalyticsPage); percentage bar rendering duplicated in AnalyticsPage and PublicPollPage; Login/Register pages share ~80% skeleton; no ESLint on backend; dynamic import anti-pattern for static dependency (poll.controller.ts:92); dead code (getIO(), analytics-update type, disconnect handler); README documents directories that don't exist (hooks/, components/poll/, components/analytics/); db:seed script references nonexistent prisma/seed.ts; array index as React key in PollPreview.
53
I
Reflecto
imohit1o1 · @imohit1o1
49
Reflecto is a full-stack polling platform with 3 verified deployments (frontend + API + health endpoint). The project has 159+ distinct features across a well-organized MERN stack with TypeScript, Socket.io, and Docker/CI-CD. Strengths include comprehensive auth (registration, email verification, JWT with refresh rotation), rich poll creation (dynamic questions, scheduling, cloning), good analytics (MongoDB aggregation pipelines, Recharts visualization, device/browser/OS breakdowns), and a modern shadcn/ui frontend with consistent state coverage. Critical issues: forgotPassword returns raw reset token bypassing email verification (P0 security bug), analytics controller crashes for unauthenticated users (null reference), responseMode is never enforced during submission, and the Socket.io frontend integration reads from non-existent localStorage token. No tests exist. Overall score: 49/80, placing it in the strong "above average" range with genuine engineering but several implementation gaps that prevent production readiness.
Authentication & Access Control6 / 10
Solid auth infrastructure: register with email verification (bcrypt 12 rounds), login/logout, JWT access+refresh tokens with rotation via Session model, httpOnly/secure/sameSite cookies, authMiddleware + optionalAuthMiddleware extracting from cookies or Bearer header, Axios interceptor with single-flight refresh queue (api.ts:33-76), Zustand store with correct partialize, and DashboardLayout/AuthLayout route gating. However, two critical flaws: (1) forgotPassword returns the raw reset token in the API response (auth.service.ts:169) and never calls sendResetPasswordEmail — bypassing email verification entirely; (2) responseMode (anonymous/authenticated) is stored on polls but NEVER enforced during response submission — authenticated polls accept anonymous responses. authorize.middleware.ts is a dead no-op stub. No CSRF protection.
Poll Creation & Question Management8 / 15
Comprehensive poll creation: react-hook-form with Zod validation (createPollSchema), dynamic QuestionBuilder using useFieldArray with QuestionCard and QuestionOptions sub-components, per-question required/mandatory toggle, per-question option add/remove. PollConfigSidebar has responseMode toggle, duration presets (2m/5m/10m/1h/24h/custom), advanced scheduling with date range. Save as Draft + Publish workflow. Full editing page loads existing poll data via getById. Poll cloning with deep copy of questions/options (poll.service.ts:203-240). Additional operations: archive, manual expire, cancel schedule. Poll listing with TanStack Table, status filtering, pagination, and delete confirmation. Slug generation with random suffix. Minor gap: frontend sends status in payload but backend always overrides to 'draft' for non-scheduled polls.
Response Collection Flow5 / 15
Response flow has good client-side features: FingerprintJS for anonymous identification, required-question validation with inline errors, radio-button selection UI, PollSuccess confirmation, and PublicResults for expired published polls. Server-side: IP hash + fingerprint + userId duplicate prevention (response.service.ts:30-50), rate limiting (response.router.ts:10), transactional vote count increment, and Socket.io broadcast on submission. However, significant gaps: (1) responseMode is NEVER enforced server-side — authenticated polls accept anonymous responses because no auth middleware is on the response route; (2) no server-side validation that submitted questionIds/optionIds actually belong to the poll (Zod only validates structure); (3) no server-side mandatory question enforcement; (4) no poll status/expiry check in the submission service (only duplicate check); (5) optionalAuthMiddleware is not applied on the response route, so req.user is always undefined.
Analytics & Feedback Dashboard6 / 15
Good analytics features: getPollAnalytics provides per-question option breakdowns via MongoDB aggregation with device/browser/OS distribution (analytics.service.ts:144-233). getDashboardAnalytics adds 7-day response trend with gap-filling, poll status breakdown, and cross-poll device/browser/OS aggregation (analytics.service.ts:5-142). Frontend: StatsGrid (total responses + donut chart), TrafficBreakdown (browser bars + OS radial chart), QuestionResultCard (Recharts bar charts with percentages), ChartResponseTrend, SectionCards with poll metrics. Results publishing via publishResults endpoint. However: analytics.controller.ts:25 crashes for unauthenticated users accessing published poll analytics (req.user.userId is undefined with optionalAuthMiddleware); empty analytics.schema.ts provides no type safety; totalStarted/totalCompleted in poll mapper reference non-existent model fields; 4 separate aggregation pipelines per analytics fetch (N+1); no CSV export or individual response viewing.
Frontend Experience7 / 10
Modern, polished UI with Tailwind CSS v4, 31 shadcn/ui components, and consistent design language. React Router with lazy loading and AuthLayout/DashboardLayout separation. All pages handle loading (spinners, Skeleton), error (error cards with retry/navigate-back), and empty states. Landing page with 6 sections (Navbar, Hero, Features, HowItWorks, CTA, Footer). Toast notifications (gooeyToaster). QR code sharing with copy-to-clipboard. Question cards with radio selection and required indicators. Poll creation uses sidebar layout with PollConfigSidebar. Analytics has 4 specialized components. However: 11 console.log/console.error in 7 frontend files; window.location.href used instead of React Router navigation in 3 places (view/page.tsx:139,184,207); frontend PollStatus type omits "cancelled" from backend enum; no dark mode toggle despite dark: classes present.
Backend Architecture & API Design6 / 15
Well-structured modular architecture: controller→service→model with router, schema, and mapper per module (auth, polls, responses, analytics). All inputs validated via Zod (validate middleware). Zod output validation in services as defense-in-depth. ApiError class hierarchy with proper HTTP status codes. Winston logger, helmet, CORS, cookie-parser. Mongoose schemas with embedded sub-documents, proper indexes, and TTL cleanup on sessions. JWT with refresh rotation via Session model. Background email sending (non-blocking). Cron job for auto-expiry. Docker multi-stage builds + CI/CD. However: forgotPassword returns raw token (P0); responseMode unenforced; authorize.middleware.ts is dead code; analytics.schema.ts is empty; unused deps (crypto-js, ioredis); no request body size limits; no CSRF; token extraction duplicated in auth/optionalAuth middleware; no cascading poll response deletion; no .env.example file; no pagination on analytics endpoints.
Real-Time Updates Using WebSockets5 / 10
Functional Socket.io implementation: server integrated with HTTP server (socket-server.ts), JWT auth via cookie extraction middleware, creator room joining with ownership verification (socket-server.ts:51-72), analytics broadcast via socketStore singleton on response submission (response.service.ts:76-81). Frontend usePollSocket hook connects, joins room, listens for analytics_update events, and cleans up on unmount. Socket event constants shared between frontend/backend. However: limited scope — only covers analytics page for creators, no live updates on public voting page; only response submission triggers events (not publish/expire/delete); no reconnection handling or room re-join logic; no debouncing on updates; frontend sends empty token string (reads localStorage which never has httpOnly cookie token), though server cookie fallback may work; no typed Socket.io events; console.log statements in socket hook.
Code Quality & Project Structure6 / 10
Clean monorepo with backend/frontend separation and consistent module organization (controller/service/model/schema/router/mapper per module). TypeScript with good type coverage, Zod on both sides, ApiError hierarchy, centralized message constants, barrel exports, and mapper pattern for DTO transformation. Multi-stage Docker builds, GitHub Actions CI/CD, Postman collections. However: zero automated tests anywhere; no .env.example file; 11 console.log/console.error in 7 frontend files; empty stub files (analytics.schema.ts, authorize.middleware.ts); unused dependencies (crypto-js, ioredis); duplicated token extraction between auth and optionalAuth middleware; 9 `as any` type assertions; window.location.href instead of React Router in 3 places; no ESLint configuration on backend; README lacks deployment URL.
54
IonPoll
Gagan · @gagan_0108
48
IonPoll is a Clerk-powered polling platform with a clean architecture, strong response validation, and solid analytics. The backend uses DI, Zod validation, PostgreSQL/Drizzle, and transactions well. The frontend has a polished dark theme with good state coverage. Key weaknesses: Socket.io is implemented but deactivated (USE_SOCKET=false), questions can't be edited after creation, no root README exists, no TypeScript or tests, and several rough UI edges (native confirm(), dead links, non-functional contact form). Deployed frontend at ionpoll.gagandev.online works but backend is not verifiably accessible. Overall score: 48/80 — a solid partial implementation with good engineering in most areas but with real-time features not functional and documentation gaps.
Authentication & Access Control6 / 10
Clerk-based authentication with SignIn/SignUp components in AuthPage.jsx, auth middleware chain (requireClerkAuth → ensureUser in auth.middleware.js:38-39), DB user record upsert from Clerk claims in AuthService (auth.service.js:52-64), authenticated vs anonymous poll modes enforced at both client (PollView.jsx:107) and server (response.controller.js:18-19), and cookie-based beforeLoad guard in router.jsx:89-101 for dashboard routes. Gaps: no Helmet/rate-limiting/CSRF, the client-side auth guard uses a weak cookie heuristic rather than a proper auth check, entirely Clerk-dependent with no custom registration/login, and no email verification or password reset flows outside Clerk.
Poll Creation & Question Management6 / 15
Multi-step creation wizard (CreatePoll.jsx, 475 lines): Details → Questions → Preview → Success with auto-copy link. Dynamic questions with add/remove, dynamic options per question with add/remove, mandatory/optional toggle, mode selection (authenticated/anonymous), expiry select (1h-7d). Server-side Zod validation (poll.dto.js:7-22) enforces min 1 question, min 2 options per question, min 1 char per option. Transactional creation in poll.service.js:198-241 with plan limit enforcement. Major gap: PATCH /api/polls/:id only updates metadata (title, description, mode, maxResponses, expiresIn) — questions/options cannot be edited after creation (poll.service.js:254-280). No form library used (individual useState handlers), GripVertical icon present but reordering not functional.
Response Collection Flow7 / 15
Well-implemented response flow with defense-in-depth validation chain in response.service.js: expiry enforcement (lines 28-31), max response limit (lines 34-40), IP-based duplicate prevention for anonymous (lines 44-53), user-based duplicate prevention for authenticated (lines 56-65), AND database-level unique index on (pollId, respondentId) in response.schema.js:19-22. Question/option existence and option index range validation (lines 68-122). Mandatory question enforcement with proper skip for optional (lines 103-109). Transactional insertion of response + answers (lines 125-147). Socket events emitted on submission. Client-side: locked notice for authenticated polls, mandatory check before enabling submit, online countdown timer, expired/extired notice, loading/submitting/success/error states. Gap: no localStorage duplicate tracking on client side.
Analytics & Feedback Dashboard7 / 15
Comprehensive analytics: DashboardAnalytics.jsx (274 lines) with stat cards (total responses, avg response time, completion rate, active polls), monthly trend line chart, poll performance bar chart, participation mode donut/pie chart, and per-question breakdown with option percentages. PollResults.jsx (164 lines) shows public per-question bar charts (Recharts) with winner highlight/trophy, percentage progress bars, and text breakdowns. Server-side AnalyticsService (212 lines): getOverview with participation by mode and avg response time, getPollAnalytics with responses grouped by day, getQuestionAnalytics with per-option counts + percentages, and getTrends with monthly polls + responses roll-up. DashboardPolls.jsx has search/filter/delete with capacity progress bars. Gaps: no CSV/JSON export, no individual response viewing, no peak activity hour/day analysis.
Frontend Experience6 / 10
Consistent dark theme with Tailwind CSS v4, 12 TanStack Router routes with proper layout separation (public vs dashboard), loading spinners on all data-fetching pages, error states with retry buttons, empty states ("No polls yet", "No data available"), auth gates (LockedNotice), toast notification system (4 variants), multi-step create wizard with preview, responsive dashboard with mobile bottom tab bar (DashboardLayout.jsx). Auth page has polished split layout with animated features. Landing page (LandingPage.jsx) has hero, features, how-it-works, CTA, and footer sections. HoverPreview component with 6 preview variants. Gaps: native confirm() for delete instead of custom dialog, Contact form is non-functional (sets submitted=true locally, ContactPage.jsx:34), footer links (Privacy/Terms/Changelog/Blog) all dead links to "#", no root README, leftover package copy.json, some hardcoded mock data in dashboardMock.js.
Backend Architecture & API Design7 / 15
Clean layered architecture: routes→controllers→services→schemas→DTOs with dependency injection throughout (createApp in app.js:49-103). Express 5, Drizzle ORM with PostgreSQL (7 tables: users, polls, questions, options, responses, answers, user_activity), 3 enums. Zod validation on all mutation endpoints (createPollSchema, updatePollSchema, submitResponseSchema). Proper indexing (polls_user_created_idx, polls_status_idx, questions_poll_order_idx, responses_poll_submitted_idx, plus unique partial index on poll+respondent). Transactional operations for poll creation and response submission. AppError class with status codes, global error handler aware of ZodError formatting. CORS middleware, request body size limit (1mb), IP extraction handling proxies. Gaps: no Helmet security headers, no rate limiting, no request logging middleware, getById and getPublic in PollService have identical implementations (code duplication), no pagination, no tests.
Real-Time Updates Using WebSockets4 / 10
Server-side implementation is solid: Socket.io server in server.js:28-40 with room-based pattern (poll:join/poll:leave), SocketService (socket.service.js, 53 lines) with 4 emit methods (emitResponse, emitResponseCount, emitStatusUpdate, emitAnalyticsUpdate) called from response.service.js and poll.service.js. Client-side hooks exist: useSocket, usePollRoom, usePollStatus, usePollTimer (useSocket.js, 168 lines) with proper cleanup and room leave on unmount. CRITICAL GAP: USE_SOCKET = false on useSocket.js:13 — the entire Socket.io client is implemented but DEACTIVATED. Even if enabled, the client connects to VITE_API_URL which is http://localhost:3001 in production, meaning real-time wouldn't work in the deployed frontend. No socket authentication, no reconnection room re-join logic, no UI connection indicator.
Code Quality & Project Structure5 / 10
Clean project structure with Server/Client separation, well-organized Server modules (auth/poll/response/analytics/user each with schema/service/controller/routes), consistent ESM modules, DI pattern, ESLint configured on client, .env.example with all variables, docker-compose.yml for PostgreSQL, Dockerfile. Common utilities well-extracted (app-error, async-handler, ip, time, validate middleware). Gaps: NO root README.md (only empty Client/README.md) — documentation score cannot exceed 3 without root README; duplicate API client code in services/api.js and hooks/useApi.js; leftover package copy.json; no TypeScript; no automated tests; console.log in production error middleware (error.js:4); .env committed to repo with Clerk publishable key; getById and getPublic methods in PollService are identical duplicates.
55
Pollify Re-scored
Soumik Talukder · @soumiktalukder03_a66e0e99
48
Pollify is a true multi-question MERN polling app — Poll/Question/Option/Response/Answer schemas with proper Mongoose refs, mandatory-question enforcement, anonymous vs authenticated submission, expiry/closed gating, and is_published flag for separating creator analytics from public results. The CreatePoll page (multi-question + multi-option add/remove UX) and TakePoll/Results/Analytics frontend pages cover the full lifecycle. Real-time uses socket.io rooms with new_response events triggering analytics refetch on the creator dashboard. Weaknesses: bcrypt salt-rounds=10 (industry baseline is 12), no duplicate-response prevention in the response endpoint, delete-poll cascade uses wrong field names so questions and options orphan, no charts (text-only counts), no helmet/rate-limit/tests, server is a single index.js with side-effecting setIO.
Authentication & Access Control5 / 10
bcrypt 10-round hashing + JWT 7d (Server/src/routes/auth.js). Returns both cookie and token in response body. $or username/email check on login. AuthContext (client/src/context/AuthContext.jsx) + ProtectedRoute on client. No refresh token, no rate-limit, no password validation regex. Salt rounds=10 is below the 12-round modern standard.
Poll Creation & Question Management9 / 15
Real multi-question polling — Poll has many Questions, each has many Options (Server/src/db/schema.js with proper Mongoose refs). Creator sets title, description, is_anonymous, expires_at. Questions have text, is_mandatory, order_index. Options have text + order_index. CreatePoll.jsx supports add/remove questions and add/remove options per question. PATCH /polls/:id/publish toggles is_published. No edit-after-create, no question types beyond single-choice options.
Response Collection Flow6 / 15
POST /responses/:pollId validates mandatory questions answered, checks is_closed and expires_at, allows anonymous if no Authorization header (decodes JWT optionally if present). Saves Response + multiple Answer rows. NO duplicate-response prevention — the same user/IP can submit unlimited times. No rate-limit. Real-time emit via socketInstance.getIO() on success.
Analytics & Feedback Dashboard6 / 15
GET /analytics/:pollId returns per-question per-option counts via Answer.countDocuments aggregation in Promise.all, total responses, ownership check. Frontend Analytics.jsx renders percentages with bar fills (no Recharts/Chart.js). Publish button to expose Results.jsx publicly via /results/:id route. Reasonable coverage but no graph visualization.
Frontend Experience6 / 10
Landing page with Syne-font brand styling, Login/Register, Dashboard, multi-question CreatePoll editor with add/remove UX, TakePoll respondent flow with success state, Results, Analytics with publish flow, ProtectedRoute, AuthContext, useSocket hook. Inline-styled (no Tailwind/UI library). Functional handling of empty/error/success states.
Backend Architecture & API Design7 / 15
Four modular routers (auth, poll, response, analytics) + middleware/auth.js + 6 Mongoose models with proper refs. Single setIO/getIO pattern for cross-file io access. CORS open, no helmet. Bug: routes/poll.js delete handler uses Question.find({ poll: poll._id }) instead of poll_id, so questions and options orphan instead of cascading. No env validation, no global error middleware, console-only logging.
Real-Time Updates Using WebSockets5 / 10
socket.io initialized in index.js, join_poll room event in socket/event.js, emit 'new_response' on response submission (routes/response.js). Client useSocket hook subscribes and refetches analytics via callback. setIO/getIO pattern for module-level io access. Works but emits only an event name (no payload), forcing client to refetch instead of pushing diff. No socket auth.
Code Quality & Project Structure4 / 10
No tests, no helmet, no rate-limit, no zod/joi validation library, no ESLint config visible at top level. CORS wide-open. console.error in client. Hardcoded localhost in useSocket hook. Bengali/casual comments are fine. Delete-cascade bug (wrong field names) ships in production paths.
56
Pollify
Nausheen Faiyaz · @codexninjadev
48
Pollify is a working full-stack polling platform with a custom OIDC authentication system, multi-question poll creation, thorough response validation, real-time Socket.IO analytics, and a publishable results view. The backend architecture is clean (routes→controllers→services→models) with Zod validation, proper security middleware (Helmet, CORS, rate limiting), and good MongoDB indexing. The frontend has 6 routes with responsive design, quiz-style response flow, and dashboard management. The OIDC implementation with PKCE and server-side token exchange is particularly well-done. Key weaknesses: no poll editing, no MongoDB transactions, duplicated UI code (result bars, getApiError), `any` types in the analytics page, no tests, and the client lacks strict TypeScript mode. Overall score: 48/100, placing it above the current cohort median (43) and consistent with a solid but not exceptional implementation.
Authentication & Access Control7 / 10
Custom OIDC implementation with PKCE (S256), server-side authorization code exchange, and Access+Refresh token handling. JWKS-based JWT verification via `jose` library with issuer/audience validation (auth.ts:18-32). Refresh tokens stored in httpOnly cookies with adaptive sameSite/secure settings (auth.routes.ts:21-32), access tokens in localStorage. Token refresh deduplication via singleton promise pattern in Axios interceptor (client.ts:17-38). `requireAuth` and `optionalAuth` middleware covering both protected and public routes. ProtectedRoute component with silent session restore (ProtectedRoute.tsx:11-27). Three response modes enforced server-side: anonymous-only, authenticated-only, both (poll.controller.ts:104-110). Return-to-path after login (oidc.ts:79-98). Gaps: no CSRF protection, access tokens in localStorage (XSS risk), no email verification or password reset, hardcoded default TokenShinobi URL.
Poll Creation & Question Management6 / 15
React Hook Form with `@hookform/resolvers/zod` for client-side validation (CreatePollPage.tsx:25-33). `useFieldArray` for dynamic question add/remove and per-question option add/remove (lines 152, 66-69). Per-question required/optional toggle via checkbox (line 90). Response mode selector (anonymous/authenticated/both) and expiry datetime picker with 6 quick-expiry pills (lines 35-42, 227-261). Character counters for title (max 200) and description (max 1000). Server-side Zod validation enforces 1-30 questions, 2-10 options per question, future expiry (poll.controller.ts:12-47). Auto-generated 8-char hex slug. Gaps: no poll edit/update endpoint exists (PATCH only toggles isPublished, no question/option modification), no draft status, no slug customization, no delete endpoint.
Response Collection Flow7 / 15
Defense-in-depth response validation: server-side Zod schema (submitResponseSchema, poll.controller.ts:26-29), expiry enforcement (line 99), response mode gates (lines 104-110), required-question validation iterating all questions (lines 114-121), option index bounds checking (lines 119-121). Duplicate prevention via MongoDB partial unique compound index on {poll, responderSub} (Response.ts:23) PLUS explicit pre-insert findOne checks for both authenticated and anonymous paths (lines 127-132). Anonymous session token generation via crypto.randomUUID per poll slug stored in localStorage (submissionToken.ts:3-11). Quiz-style step-by-step question UI with Back/Next navigation (PublicPollPage.tsx:258-328). Existing response restoration on page load (lines 69-78). Client-side expiry timer with setTimeout (lines 89-116). AuthRequiredPanel for authenticated-only polls (lines 206-213). Proper cleanup (socket disconnect, timer clear). Gaps: no MongoDB transaction for submission, no per-question single-option enforcement (client uses radio buttons but server accepts multiple answers per question - could submit duplicates for same questionId).
Analytics & Feedback Dashboard6 / 15
Analytics service computes participation breakdown (total/anonymous/authenticated counts) and per-question option tallies with counts (analytics.service.ts:5-46). Three-card stat summary on analytics page (PollAnalyticsPage.tsx:49-53). Per-question bar charts with CSS-proportional width bars, percentage labels, letter badges (A/B/C), and color cycling via CSS variable palette (lines 55-79). Public results view renders identical visualization when poll is published or creator views expired poll (PublicPollPage.tsx:216-255). Publish endpoint (PATCH /polls/:slug/publish) with owner-only gate (poll.controller.ts:159-171). Dashboard with filter tabs (all/live/expired/published) and sort modes (newest/oldest/expiring-soon). Real-time analytics update via Socket.IO on both analytics and public pages. Gaps: computation uses brute-force O(n*m) JS iteration (no MongoDB aggregation pipeline), `any` types throughout PollAnalyticsPage.tsx (lines 12, 55-56), no CSV export, no trend/time-series data, no peak activity metrics, no individual response viewing, result bar rendering duplicated between PollAnalyticsPage and PublicPollPage.
Frontend Experience5 / 10
Clean CSS design system with CSS custom properties and graph-paper background pattern (App.css:1-22). 6 routes with React Router (App.tsx:51-56). Mobile-responsive hamburger navigation (Navbar.tsx:25-33). Quiz-style step-through question UI with radio buttons, letter badges, and Back/Next navigation (PublicPollPage.tsx:258-328). Dashboard with tabbed filtering, sort controls, poll cards with status pills, copy-link, open-poll, and analytics actions (DashboardPage.tsx:49-97). Landing page with hero section, feature cards, and how-it-works flow (HomePage.tsx). Toast notifications with custom neo-brutalist styling (App.tsx:29-48). Page transition CSS animations (App.tsx:49). Copy-to-clipboard button with toast feedback (CopyLinkButton.tsx). Loading and empty states on all pages. Gaps: no dark mode, no skeleton loaders, no confirmation dialogs for destructive actions, no form library abstraction (server auth routes have inline handlers), create page is functional but basic with plain inputs, no error boundaries, `any` types in analytics page degrade type safety.
Backend Architecture & API Design6 / 15
Clean layered architecture: routes→controllers→services→models with separate middleware and utilities (30 source files, ~780 server LOC). All API inputs validated via Zod schemas with safeParse (poll.controller.ts:12-29, 36-38). Custom ApiError class with static factory methods for 400/401/403/404/409/500 (ApiError.ts:14-36). ApiResponse envelope with send/ok/created methods (ApiResponse.ts). asyncHandler middleware eliminates try/catch boilerplate (asyncHandler.ts:3-7). Helmet, CORS with origin allowlist, cookie-parser, express-rate-limit (200 req/min), and 1mb JSON body limit configured (index.ts:27-31). JWKS-based remote key verification (auth.ts:7-8). Proper MongoDB indexing: unique slug, createdBy, expiresAt, compound unique {poll, responderSub} with partial filter (Response.ts:23). Mongoose schema-level validation (min 2 options per question, Poll.ts:16-19). Gaps: no MongoDB transactions (used in calibration's 8-level examples), auth.routes.ts has inline route handlers instead of controller separation, empty response.routes.ts dead code file, raw OIDC error text leaked to client on token exchange failure (auth.routes.ts:68-69), no request logging middleware, no tests, no graceful shutdown, no .env.example files, no input sanitization beyond Zod.
Real-Time Updates Using WebSockets6 / 10
Socket.IO server integrated with Express HTTP server (index.ts:17-25). Room-based pattern: clients join `poll:{slug}` rooms via `poll:join` event, leave via `poll:leave` (socketHandler.ts:8-14). Server emits `analytics:update` with full analytics payload on response submission (poll.controller.ts:144-145) and poll publish (lines 167-168). Client integrates Socket.IO on both PollAnalyticsPage (lines 17-24) AND PublicPollPage (lines 118-146) — analytics page replaces full state on update, public page merges analytics + tracks isPublished flag. Proper cleanup on both pages: `poll:leave` emit, event listener removal (`socket.off`), and `socket.disconnect()` in useEffect return. CORS configured for Socket.IO with client origin allowlist (index.ts:19-24). Gaps: no debouncing on client updates, no authentication on socket connections, no reconnection room re-join logic (socket reconnects silently drop room subscription), no typed Socket.IO events, only 1 event type (no poll-expired, poll-deleted, or new-response events), pure invalidation pattern (pushes full analytics rather than incremental data).
Code Quality & Project Structure5 / 10
TypeScript used on both client and server with clean modular structure (client/ and server/ separation). Server uses strict mode (tsconfig.json), client does not. Consistent naming conventions: camelCase functions, PascalCase components, kebab-case files, UPPER_SNAKE_CASE constants. Comprehensive README with API reference, env var docs, project structure, and setup instructions. Clean code with no leftover Vite/React boilerplate — everything is poll-specific. Good patterns: `useSyncExternalStore` for auth state, singleton refresh promise for concurrency deduplication, PKCE using Web Crypto API, proper useEffect cleanup on all effects. Gaps: no tests anywhere, 4 identified duplication sites (getApiError in 2 files, result bar JSX in 2 files, resultPalette in 2 files, token exchange fetch patterns in auth.routes.ts), dead code (empty response.routes.ts, unused isActive virtual in Poll.ts:40-42, unused ApiResponse.noContent method, commented import in main.tsx), client lacks strict TypeScript mode with 2 `any` usages in PollAnalyticsPage.tsx, no .env.example files, no ESLint on server, no automated linting in CI, no shared Analytics type between PublicPollPage and PollAnalyticsPage.
57
Pollr Re-scored
ADITYA TOMAR · @aditya07879_b92d92fc
48
Pollr is a working MERN polling app with bcryptjs(12 rounds) + 7-day JWT auth, slug-based poll URLs, and Socket.io real-time vote updates broadcast via joinPoll/leavePoll rooms. The frontend has a polished themed dashboard (Overview/MyPolls/Insights/Analytics), Recharts pie+bar visualizations, skeleton loading, dark/light theme, countdown timer, and PublicOnlyRoute/ProtectedRoute guards via AuthContext. Scope is limited to single-question multiple-choice polls (2-6 options, with Yes/No and This-or-That templates) — no multi-question, text, or rating types. Vote deduplication uses an x-voter-token header or IP fallback, which is bypassable. Backend is clean Express MVC but lacks helmet, rate-limit, request-validation library, refresh tokens, and tests. Two commits were pushed after the deadline; the pipeline correctly judged the on-time commit only.
Authentication & Access Control6 / 10
bcryptjs 12-round hashing + JWT 7d expiry (server/controllers/authController.js:6-22, register/login). AuthContext (client/src/context/AuthContext.jsx) with token persistence in localStorage and axios default-header injection. PublicOnlyRoute + ProtectedRoute guards. Bearer-token verification in server/middleware/auth.js. No refresh token, no password reset, no email verification, no rate-limit on /register or /login.
Poll Creation & Question Management5 / 15
Single-question multiple-choice only (server/controllers/pollController.js:createPoll, options validated 2-6, expiry must be in future). Slug auto-generated via uuid. CreatePoll.jsx exposes POLL_TEMPLATES (Blank, Yes/No, This-or-That) for quick start. No multi-question support, no question types (text, rating, scale), no mandatory/optional flag, no edit-after-create.
Response Collection Flow7 / 15
vote() uses x-voter-token header OR req.ip as voter identifier (pollController.js:getVoterIdentifier), checks expiry, checks if already in poll.voters array, validates optionIndex bounds, atomic save via mongoose. Emits voteUpdate over socket on success. Custom header is trivially bypassable. No rate-limit. No anonymous-vs-authenticated mode distinction (every poll is single open public form).
Analytics & Feedback Dashboard6 / 15
Dashboard with 4 sub-pages — Overview, MyPolls, Insights, Analytics (client/src/pages/dashboard/). Recharts pie + bar charts in Analytics.jsx. Insights computes mostPopular and mostDecisive via client-side reduce. No per-poll backend analytics endpoint — all aggregation is client-side over the user's own polls. No public 'publish results' view, no per-question/option breakdown (single-question polls).
Frontend Experience7 / 10
Polished themed UI — dark/light theme via ThemeContext, animated skeletons (shimmer), countdown timer (utils/useCountdown.jsx), dashboard with sidebar nav, Navbar with theme toggle, share-link copy with copied-state feedback, vote bar percentages with maxVotes highlight. Mobile-responsive layout. PublicOnlyRoute redirects logged-in users away from signin/signup.
Backend Architecture & API Design6 / 15
Clean Express MVC — routes/, controllers/, models/, middleware/, sockets/, config/. Mongoose models with timestamps and virtual fields. CORS configured against CLIENT_URL env. Socket attached to req via middleware. No helmet, no rate-limit, no request-validation library (no joi/zod), no env-var validation, no global error middleware, console.log in socket initialization.
Real-Time Updates Using WebSockets7 / 10
Socket.IO server in server/sockets/pollSocket.js with rooms keyed by poll slug. joinPoll/leavePoll events with client subscribing in PollPage.jsx via socketRef on mount and disconnecting on unmount. Vote handler emits voteUpdate to the room with the full updated poll. Works correctly end-to-end. No auth on socket (anyone can join any room).
Code Quality & Project Structure4 / 10
try/catch on all controllers returning generic 500s. No tests, no helmet, no rate-limit, no lint config, no env validation, no refresh token. Two commits pushed after the submission deadline (pipeline correctly rewound to the on-time commit, so the late changes were not evaluated). Reasonable .env handling.
58
Ratiod
Satpalsinh Rana · @satpalsinhrana
48
Ratiod is a full-stack polling app (React 19 + Express 5 + Drizzle ORM + Better Auth + Socket.IO) with solid core functionality but significant gaps in polish and completeness. The backend has a well-designed PostgreSQL schema and thorough response validation, but 50/68 source files are empty stubs. The frontend delivers an attractive landing page and functional dashboard despite being plain JavaScript (no TypeScript). The deployment has the frontend live but the backend API isn't reachable through the same domain (frontend hardcoded to localhost:3000). No project README exists. Zero tests. Strengths: thorough response submission validation, good analytics with Recharts, Socket.IO room-based real-time updates, proper auth with Google OAuth. Key gaps: no poll editing/deletion, no email/password registration, no security middleware (Helmet/rate limiting/CSRF), fragile error handling based on string matching, 10 unused frontend components, no .env.example for backend.
Authentication & Access Control6 / 10
Better Auth with Google OAuth only (lib/auth.ts:7-19). Session-based auth with requireAuth middleware (auth.middleware.ts:5-21) that validates via auth.api.getSession and returns 401. ProtectedRoute on frontend (ProtectedRoute.jsx:4-20) redirects to / if no session. Anonymous vs authenticated poll modes enforced in submitResponse (polls.service.ts:266-268). Socket.IO session auth middleware (sockets/index.ts:21-28). Gaps: no email/password registration, no email verification, no password reset, no CSRF, no Helmet, no rate limiting, req.auth!.user.id non-null assertions in controller (8 call sites), only one OAuth provider, window.alert for auth errors (useAuth.jsx:61,72).
Poll Creation & Question Management6 / 15
Working poll creation with dynamic questions/options: add/remove questions, mandatory/optional toggle, anonymous/authenticated mode, datetime expiry picker, 8-char slug generation with 4-attempt collision retry (polls.service.ts:40-42,154-159). Server-side validation via normalizeCreateInput (polls.service.ts:52-96) checks title, mode enum, min 1 question, min 2 options per question, expiry date validity. Frontend DashboardPage has full creation form with createQuestion/createDraft helpers. Gaps: no poll editing (no PATCH/PUT endpoint), no poll deletion, no maxResponses/allowDuplicateResponses toggles in UI (they exist in DB schema but aren't exposed), no form library (manual useState), no slug display/QR sharing.
Response Collection Flow7 / 15
Thorough server-side validation in submitResponse (polls.service.ts:253-370): poll active check, expiry with auto-status-update, authenticated mode enforcement, maxResponses limit, per-question required answer enforcement, per-answer option-ownership validation. Duplicate prevention for both authenticated (respondentId check, lines 303-316) and anonymous (anonymousSessionToken check, lines 318-331). Anonymous token via crypto.randomUUID() persisted in localStorage. Frontend PollPage (PollPage.jsx:13-231) renders radio buttons, shows required/optional, handles auth-gating, and displays post-submission analytics. Gaps: no DB-level unique compound index for deduplication (application-level only, race condition vulnerable), no transaction wrapping around response + answers insert (lines 334-364 are sequential), no response editing.
Analytics & Feedback Dashboard7 / 15
calculateAnalytics (polls.service.ts:372-452): per-poll totalResponses, uniqueRespondents, participationRate, per-question summaries with per-option count/percentage, answeredCount, skippedCount. Analytics snapshot persistence via upsert into analyticsSnapshots table. getOverallAnalytics (lines 532-606): totalPolls, totalResponses, status/mode breakdowns, pollBreakdown top-8. getPollVoters (lines 608-645): voter list with per-voter answer breakdown for authenticated polls. Frontend PollAnalytics component (PollAnalytics.jsx:199-254) renders metric tiles, PollChartPanel with PieStatChart/BarStatChart using Recharts, per-question option bars. OverallAnalyticsCharts (PollAnalytics.jsx:164-197) for cross-poll dashboard. Gaps: no CSV/export, no time-series, all-analytics computation is brute-force JS filtering (N+1 pattern), no peak activity metrics.
Frontend Experience6 / 10
4 routes: Landing, Poll, Dashboard (protected), 404 (AppRoutes.jsx). Attractive landing with 7 sections (Hero, Stats, Showcase, Story, Workflow, Insight, CTA). Dashboard with 3-tab view + voters sub-view. Public poll page with responsive voting form + results pane. State coverage: loading spinners, empty states, error banners, auth gating, published/closed poll states. isMounted cleanup pattern in all effects. Socket.IO live integration on both pages. No React form library (manual useState in DashboardPage.jsx, ~100 lines of form state management). Frontend is plain JavaScript (no TypeScript). 10 unused components (~600 LOC of dead code). window.alert used for auth errors. Missing icon definition (PollPreview.jsx:60 references `check` icon that doesn't exist in Icon.jsx).
Backend Architecture & API Design5 / 15
Express 5 + Drizzle ORM (PostgreSQL/Neon) + Better Auth. Well-designed DB schema: 13 tables with proper foreign keys (cascade/set-null), composite indexes, enum types (polls.ts, responses.ts, analytics.ts). Clean module structure: routes→controller→service. 12 API endpoints under /api/polls. TypeScript strict:true with zero `any` types. CORS configured. BUT: 50 out of ~68 backend source files are completely empty stubs. All business logic in a single 646-line file (polls.service.ts). No input validation library (manual ad-hoc in normalizeCreateInput). Fragile sendError maps HTTP status from error message substrings (polls.controller.ts:23-32). No Helmet, no rate limiting, no CSRF, no request body size limits. No centralized error middleware (file empty). No async handler wrapper (file empty). req.auth!.user.id non-null assertions throughout controller. No pagination. Duplicate route registrations (with/without trailing slashes). No graceful shutdown.
Real-Time Updates Using WebSockets7 / 10
Socket.IO server attached to HTTP server (server.ts:9). Room-based pub/sub: clients join/leave poll:{pollId} rooms via poll:join/poll:leave events (sockets/index.ts:30-42). Input validation checks pollId is non-empty string. Server emits poll:response-count (totalResponses) and poll:analytics (full analytics object) to room on response submission and result publishing (polls.service.ts:521-529). Client connects on DashboardPage (DashboardPage.jsx:89-100) and PollPage (PollPage.jsx:46-65), emits join, listens for poll:analytics to update state, cleans up with leave + disconnect. Socket.IO session auth middleware validates connection (sockets/index.ts:21-28). Gaps: no reconnection room re-join logic, no debouncing/batching, only covers analytics pages (not live response counts on voting page), no typed events, no heartbeat.
Code Quality & Project Structure4 / 10
Clean pnpm monorepo with apps/* workspace. ESLint + Prettier configured at root. Backend TypeScript strict:true, zero `any` types. Consistent module structure. isMounted cleanup pattern used throughout. BUT: 50 empty/stub files (~75% of backend source). No README at project root (only Vite starter README in frontend/). Zero automated tests. Frontend is plain JavaScript despite @types packages in devDeps. 10 unused frontend components (~600 LOC of dead code). Missing icon definition causes rendering bug. No .env.example for backend (only single-variable .env-example for frontend). docker-compose.yml references non-existent dockerfile. Duplicate route registrations. Inline styles everywhere bypassing CSS custom properties. window.alert for error display. No JSDoc comments. db/scehma.ts has a typo in filename and is empty.
59
ChaiPoll – Real-Time Poll & Feedback Platform
Dipanshu Kumar Sah · @kumardipanshu983542_b3eb5440
47
ChaiPoll is a well-designed MERN polling platform with a polished dark-themed UI and comprehensive feature set covering auth, poll creation, response collection, analytics, and basic Socket.IO. The frontend stands out with 47 custom CSS utility classes, consistent loading/error/empty state handling across all pages, and a cohesive visual identity. Core CRUD and analytics flows work end-to-end. However, the backend has significant engineering gaps: 6 empty stub files, no security middleware (Helmet, rate limiting), unused dependencies, and no input validation library in use despite express-validator being declared. The Socket.IO implementation is functional but minimal with only 1 event type. Code quality suffers from duplications, unused components, zero tests, and no TypeScript. The README over-claims features (Recharts, Framer Motion, Zod) not actually present. Total score: 47/100.
Authentication & Access Control6 / 10
Working JWT auth with registration/login (auth.controller.js:1-81), bcryptjs 10-round hashing, Bearer token middleware (auth.middleware.js:1-41), optionalAuth for anonymous/authenticated mode enforcement (optionalAuth.middleware.js:1-28, response.controller.js:26-30). Frontend has ProtectedRoute redirect (ProtectedRoute.jsx:9-12), AuthContext with localStorage persistence (AuthContext.jsx:1-50), and axios interceptor (axios.js:8-16). Anonymous vs authenticated poll mode enforced server-side. Critical gaps: no Helmet, no rate limiting on auth routes, tokens in localStorage (XSS risk), no refresh token rotation, no email verification, no password reset, CORS hardcoded to localhost:5173 instead of env var, social login buttons are UI-only placeholders.
Poll Creation & Question Management7 / 15
Poll creation at CreatePoll.jsx (222 lines) supports dynamic questions with add/remove, dynamic options per question with min-2 enforcement, mandatory/optional toggle per question, anonymous/authenticated response mode toggle, expiry datetime picker. Client-side validation checks title, expiry (future-date), question text, and min 2 options per question (CreatePoll.jsx:46-57). Server-side validation in poll.controller.js:30-45 validates title, questions array, each question's text, filled options (min 2), and converts string options to {text} schema format. Poll code auto-generated via nanoid(7) (generatePollCode.js). Significant gap: no poll editing endpoint exists — only create/get/delete, no PATCH for question/option updates after creation.
Response Collection Flow6 / 15
Response submission at response.controller.js:7-87 validates: poll existence by code, expiry check, anonymous/authenticated mode enforcement, required question validation (lines 33-44), submitted questionId existence in poll, and submitted option text existence in question options (lines 47-60). Frontend PollPage.jsx (324 lines) handles loading/error/submitted/auth-required states with progress tracking. Socket.IO emits `response_submitted` on submission for real-time updates. Critical gaps: no duplicate vote prevention (no unique compound index on {poll,userId}, no localStorage tracking for anonymous users), no response body size limits, token passed redundantly in manual headers instead of relying on axios interceptor.
Analytics & Feedback Dashboard6 / 15
Analytics controller (analytics.controller.js:1-153) computes: totalResponses, per-question optionCounts with winner detection, answered/skipped per question. Private analytics (creator-only, lines 6-69) with access control check. Result publishing via PATCH /analytics/:pollId/publish (lines 73-95). Public results endpoint (lines 99-150) accessible after publishing. Frontend Analytics.jsx (323 lines) shows stat cards (total responses, questions, completion rate, status), per-question bar charts with percentages and trophy winner, live indicator. Dashboard (Dashboard.jsx, 219 lines) has poll list with 4 stat cards (total/active/expired/published). PublicResults.jsx (158 lines) renders published outcomes. Gaps: no MongoDB aggregation pipeline (O(n*m) brute force), no trend/time-series data, no CSV export, no individual response viewing, no peak activity metrics, Recharts claimed in README but not in package.json (charts are CSS-only).
Frontend Experience7 / 10
Cohesive dark "chai" theme with 47 custom CSS classes in a well-organized @layer components system (index.css, 288 lines), CSS custom properties/tokens, gradient backgrounds, noise texture overlay, glass-morphism effects. All pages handle loading (spinners), error (contextual messaging), empty states (no polls, no responses), and auth-required states. Responsive design throughout. CSS animations (fadeUp, fadeIn, livePulse, stagger). Progress bar on poll response page. Copy-to-clipboard with toast. PollPage has progress tracking (X of N answered). React Hook Form on login/register. Weaknesses: NotFound page is bare "NotFound" div, DashboardLayout/PollCard/Loader components exist but are never imported, Recharts and Framer Motion claimed in README but not in package.json (animations are pure CSS), form-less creation page (466 lines of imperative useState).
Backend Architecture & API Design5 / 15
Clean MVC separation: controllers/ (4 files), models/ (3 files with embedded sub-schemas), routes/ (4 files), middleware/ (4 files), config/ (socket singleton, db), utils/ (token gen, poll code gen). Consistent try/catch error handling across all controllers. RESTful API design with correct route ordering (my-polls before :code to avoid Express collision). Creator-only access enforcement on analytics, delete, and publish (string comparison on creator ID). Critical issues: 6 empty stub files (0 bytes each — all 3 validation files, error.middleware.js, poll.socket.js, validateExpiry.js), express-validator declared in package.json but never imported, no Helmet security headers, no rate limiting, no request body size limits, no centralized error middleware in use (validate.middleware.js has code but never imported by app.js), CORS origin hardcoded instead of using CLIENT_URL env, 3 unused backend dependencies (dayjs, cookie-parser, express-validator), no MongoDB indexes beyond schema-level defaults, no pagination, raw error.message leaked to clients in some catch blocks.
Real-Time Updates Using WebSockets5 / 10
Socket.IO server integrated with HTTP server (server.js:17-23), singleton pattern via config/socket.js for cross-module io access. Server emits 1 event: `response_submitted` to poll-code room (response.controller.js:72-76). Client connects as singleton (socket/socket.js), PollPage joins room on mount (PollPage.jsx:34-42), Analytics page joins room and listens for `response_submitted` to trigger HTTP refetch (Analytics.jsx:27-28). Both pages handle reconnection via `connect` event. Gaps: only 1 event type, pure invalidation signal with no data pushed (client refetches HTTP), no events for publish/delete/expiry, no room leave on unmount, no debouncing, no socket authentication, no typed events, CORS hardcoded to localhost:5173, empty socket handler file (poll.socket.js is 0 bytes), Analytics room join has race condition (depends on async analytics fetch completing first).
Code Quality & Project Structure5 / 10
Clean monorepo with backend/frontend separation, consistent naming conventions, organized folder structure. .env.example files for both packages, .gitignore configured, ESLint configured for frontend. Good README with setup instructions and API route table. Weaknesses: no TypeScript or PropTypes anywhere, 6 files are 0-byte empty stubs (3 validation files, error.middleware.js, poll.socket.js, validateExpiry.js), 3 React components never imported (PollCard, Loader, DashboardLayout), unused App.css (185 lines of Vite boilerplate), 5 unused npm deps (dayjs, cookie-parser, express-validator, zod, @hookform/resolvers), timeLeft() duplicated in Dashboard.jsx and PollPage.jsx, socket room-join pattern duplicated, option results rendering duplicated in Analytics.jsx and PublicResults.jsx, zero automated tests, console.log left in production code at 5 sites, comments are personal Hindi-English fix notes, README documents Recharts/FramerMotion/Zod which are not actually used.
60
N
PollKar
Namra Suthar · @work.namrasuthar7728
47
PollKar is a functional full-stack polling platform with a clean layered architecture (PostgreSQL/Drizzle ORM, Express, React/Vite). It has working auth (register/login/JWT), dynamic poll creation with mandatory/optional questions, response collection with question/option validation, analytics with per-question option counts and percentages, published results, Socket.IO real-time updates, and a distinctive neo-brutalist UI with dark mode. However, it's held back by several critical bugs (optionalAuth middleware broken due to payload.sub vs payload.id mismatch, no expiry enforcement on response submission, socket responseCount always undefined), missing security features (no rate limiting, no CSRF, no body size limit, localStorage tokens), significant code duplication across frontend and backend, and lacking polish (no TypeScript, zero tests, no form library, no trend analytics). Total: 47/100 — a solid working application with good architecture but notable bugs and omissions that prevent a higher score.
Authentication & Access Control5 / 10
Core auth (register/login/protected routes/JWT with bcrypt 10 rounds) is functional with Zod validation on inputs, ProtectedRoute/GuestRoute wrappers, and Axios 401 interceptor. But the optionalAuth middleware has a critical bug: it reads `payload.sub` (line 16 of optional-auth.middleware.js) while the JWT payload uses `id` (auth.service.js:22, 47), so `req.user.id` is always undefined. No refresh tokens, no CSRF protection, no rate limiting on auth endpoints, tokens stored in localStorage, and email uniqueness enforced at app-level only (plain index, not unique index in users.schema.js:32).
Poll Creation & Question Management7 / 15
Solid poll creation: dynamic questions (min 1, max 20) and options (min 2, max 8) with Zod validation (poll.validation.js), mandatory/optional toggle (isRequired boolean), anonymous/authenticated response mode, expiry datetime, slug auto-generation with collision handling (slug.js), and transactional writes (poll.repository.js:35-61). Frontend CreatePollPage.jsx (357 lines) has client-side validation, add/remove for questions and options. Major gap: questions and options CANNOT be edited after creation — the update endpoint (poll.validation.js:25-35) only handles metadata (title, description, responseMode, expiresAt). No question-level CRUD post-creation.
Response Collection Flow6 / 15
Response submission has solid validation: poll existence check (404), published check (409), auth requirement enforcement (401), duplicate prevention via userId/sessionToken lookup (response.service.js:30-38), required question enforcement (lines 47-51), and question/option membership validation (lines 55-74). Transactional write atomically inserts response + answers + increments responseCount via SQL (response.repository.js:70-103). Frontend PublicPollPage.jsx (294 lines) handles loading, error, published, expired, submitted, and auth-required states. Critical gap: NO expiry enforcement on submission (response.service.js never checks poll.expiresAt). The answer Map silently deduplicates duplicate questionIds (last-write-wins) and the optional-auth bug means authenticated mode is broken.
Analytics & Feedback Dashboard6 / 15
Working analytics: SQL aggregation for per-question option counts with percentages via GROUP BY (analytics.repository.js:61-73), summary DTO with totalResponses/completionStatus/isExpired (analytics.dto.js), public results publishing (PATCH /api/polls/:id/publish-results then GET /api/analytics/public/:slug). Frontend AnalyticsPage.jsx (249 lines) has OptionBar component with count/percentage/visual bar, stat cards (total responses, status, expiry), and live Socket.IO banner. PublicResultsPage.jsx (113 lines) shows published results. Gaps: no trend data (time-series, velocity), no CSV export, no individual response viewing, completionStatus doesn't account for expired-unpublished polls (stays "collecting"), analytics query fetches ALL options across ALL polls then filters in JS.
Frontend Experience6 / 10
Coherent neo-brutalist design with consistent shadows, thick borders, custom CSS grid pattern (index.css), dark/light mode with system preference detection (useTheme.js:12-14), and responsive layout (sidebar hidden on mobile, md breakpoints). Nine reusable components (Button with 4 variants/3 sizes/loading spinner, Input, Select, Textarea, Card, Badge, BrandLogo, ThemeToggle, DashboardLayout). PublicPollPage handles 7 distinct states (loading/error/published/expired/submitted/active/auth-required). Gaps: getPollStatus() and formatDate() duplicated 3x each across pages, Login/Register pages are ~90% identical code, OptionBar component not reused in PublicResultsPage (inline duplication), theme prop-drilled through 3 layers, no form library (357-line CreatePollPage with manual useState), text-only loading states, no toast notifications.
Backend Architecture & API Design7 / 15
Well-structured layered architecture: routes→controllers→services→repositories with consistent separation across all 4 modules. Drizzle ORM with 6 PostgreSQL tables, 15 indexes, 8 FK constraints, and 1 enum (response_mode). Universal Zod validation on all inputs via validate middleware with structured body/params/query schemas. Centralized error handling (AppError class, error middleware that masks 500 errors). Helmet+CORS+Morgan middleware stack. Environment validation at startup with fast-fail (env.js). bcrypt password hashing (rounds=10), JWT with configurable secret/expiry. DTO pattern for API response shaping. Transactions for poll creation and response submission. Gaps: no rate limiting anywhere, express.json() with no body size limit, email index is plain index (not uniqueIndex), optionalAuth middleware bug (payload.sub vs payload.id), and getQuestionsWithOptions duplicated across 3 repositories with inefficient all-options query pattern.
Real-Time Updates Using WebSockets5 / 10
Room-based Socket.IO with proper join/leave lifecycle (socket.js:16-31), central event constants (socket-event.js), and pushed event data (pollId, responseId, responseCount, submittedAt) rather than just invalidation signals. Frontend AnalyticsPage.jsx connects socket, joins poll room, listens for "poll:response-created", reloads analytics on events, and properly cleans up on unmount (lines 92-114). Live message banner shows "New response received. Total responses: N". Bugs: the `result.responseCount` in socket emission (response.service.js:86) is undefined because the repository returns `{ response, poll }` (response.repository.js:98-101) — it should be `result.poll.responseCount`. No socket authentication, no guard on pollId format, only one event type (no publish/delete/expiry events), socket only used on analytics page (not on public response page).
Code Quality & Project Structure5 / 10
Clean pnpm monorepo with feature-based modular structure (auth/polls/responses/analytics), shared common/ utilities, and consistent kebab-case/PascalCase naming. Thorough README with setup instructions, API overview, realtime events, database design, demo flow, and troubleshooting. Docker Compose for local PostgreSQL. Drizzle migrations committed. ESLint configured on frontend. However: significant code duplication — getPollStatus() duplicated 3x (PollListPage, PollDetailPage, AnalyticsListPage), formatDate() duplicated 3x (PollDetailPage, PublicPollPage, AnalyticsPage), getQuestionsWithOptions duplicated across 3 repositories, LoginPage and RegisterPage are ~90% identical code. No TypeScript anywhere (plain JS). Zero automated tests. Theme prop-drilled instead of using React Context. Auth state not reactive (direct localStorage reads). No JSDoc or code documentation comments.
61
PulseBoard: Live Polls For Feedback
SUSHRUTO MAJUMDAR · @sushruto
47
PulseBoard: Live Polls For Feedback is a solid full-stack polling platform built with React/Vite, Express, Drizzle ORM, PostgreSQL, Redis, and Socket.IO. Scores 47/80 (mean of 5.9). Strengths: proper email/password auth with JWT cookies and optional OIDC, comprehensive Zod validation, transaction-based poll creation and response submission, duplicate prevention via both query checks and DB unique indexes, 6-table schema with proper foreign keys and cascading deletes, 15 API endpoints, working Socket.IO real-time updates, and clean middleware chain. Frontend deployed at Vercel (verified working), though backend-only at that origin. Key weaknesses: no poll editing or deletion, no tests, schema/logic duplicated across files, monolithic CSS, no TypeScript, no password reset/email verification, no CSV export, minimal Socket.IO implementation (4 events), SSL rejectUnauthorized:false, no analytics time-series or individual response viewing. Solid hackathon submission with core features working but missing depth and polish.
Authentication & Access Control7 / 10
Solid authentication: email/password registration (server/routes/auth.js:42-76) and login (lines 78-95) with bcrypt cost 12, JWT-signed httpOnly/sameSite/secure cookies (services/tokens.js:4-11), attachUser + requireAuth middleware (middleware/auth.js), GET /me, logout, optional OIDC with JWT-signed state/nonce CSRF protection (auth.js:113-144), ProtectedRoute component with return-to redirect (components/ProtectedRoute.jsx). Anonymous vs AUTHENTICATED poll modes enforced at server-side (publicPolls.js:122-123). AuthContext with login/register/logout/refreshUser. Gaps: no password reset, no email verification, no CSRF tokens beyond SameSite, no refresh token rotation, dev JWT fallback hardcoded in env.js:19.
Poll Creation & Question Management6 / 15
Working poll creation: dynamic questions with add/remove (max 20) and per-question options with add/remove (min 2, max 8) in CreatePoll.jsx (330 lines). Mandatory/optional toggle, anonymous/authenticated response mode switcher, expiry datetime picker, title/description fields. Client-side validateClient() checks all fields (lines 99-108). Server-side Zod validation (polls.js:13-31) covers title/description/responseMode/expiresAt/questions/options. Transaction-based creation with atomic poll+questions+options insert (polls.js:114-155). Slug generation with random hex suffix (services/slug.js:3-13). Sidebar "Ready check" summary. Critical gaps: no poll editing endpoint (PATCH /:pollId/publish only updates publishedAt, not questions/options), no poll deletion, no draft status, no slug collision handling.
Response Collection Flow7 / 15
Defense-in-depth response flow: Zod schema validates answer structure (publicPolls.js:19-28). Mandatory question enforcement with per-question error messages (lines 130-150). Option validity check ensures submitted optionId belongs to the question (lines 140-144). Expiry check rejects non-active polls with 409 (lines 117-119). Auth mode enforcement returns 401 for unauthenticated AUTHENTICATED polls (lines 122-123). Duplicate prevention via both pre-insert query check AND unique DB composite index (lines 156-170 + schema.js:94). Transactional insert of response + answers (lines 174-195). Anonymous ID generation via crypto.randomBytes (services/slug.js:15-17). WebSocket broadcast on submit (lines 197-203). Rate limited at 20/5min (line 107). Frontend mandatory check. UI covers success/expired/locked/published states. Gaps: no timer countdown, no IP-based anonymous dup prevention.
Analytics & Feedback Dashboard6 / 15
Solid analytics: services/analytics.js computes totalResponses, authenticatedResponses, anonymousResponses, latestResponseAt, completionRate, per-question totalAnswers/skipped, per-option count/percentage. Analytics.jsx (175 lines) renders 4 stat cards (Responses, Completion, Anonymous, Authenticated), per-question breakdown cards with CSS bar charts (ResultBars component). Poll state badges, expiry countdown, response mode display. Publish endpoint (polls.js:207-234) sets publishedAt, recomputes analytics, broadcasts via WebSocket. Published results render on PublicPoll.jsx with same bar chart format (PublishedResults component, lines 10-41). Dashboard aggregates total polls/responses/live/published counts client-side. Gaps: no CSV/JSON export, no time-series/trend data, no peak activity metrics, no leading option identification per question, no individual response viewing, analytics query fetches all rows without aggregation efficiency.
Frontend Experience5 / 10
Functional UI with 6 routes (App.jsx:17-32): /, /login, /dashboard, /polls/new, /polls/:pollId, /p/:slug. Loading spinner on all pages via Loading component. Error banners on all pages. Empty state on Dashboard ("No polls yet" CTA). Success/expired/locked/published states on PublicPoll (lines 124-211). Busy/submitting on all forms. Copy link with "Copied" feedback (Analytics.jsx:59-63). Segmented login/register toggle. ProtectedRoute with redirect to login and return-to location. Shell with topbar nav. Consistent CSS design tokens. But: 1126-line monolithic app.css, no Tailwind, no form library (individual useState), no dark mode toggle, no skeleton loading, no confirmation dialogs for destructive actions, no QR code sharing, no pagination on dashboard.
Backend Architecture & API Design6 / 15
Clean Express architecture: helmet→cors→morgan→json(1mb)→cookieParser→attachUser→apiLimiter middleware chain (server/index.js:35-50). Drizzle ORM with PostgreSQL; 6 tables with proper foreign keys (CASCADE/SET NULL), 10 indexes including 5 unique constraints (schema.js:1-129). Zod validation on all 4 mutable endpoints. Transactions for poll creation (polls.js:114-155) and response submission (publicPolls.js:174-195). Redis-backed rate limiting with graceful in-memory fallback (rateLimit.js:36-55). Cookie-based JWT with proper httpOnly/sameSite/secure flags. Centralized error handler. Graceful shutdown (SIGINT/SIGTERM). Gaps: schema duplicated in raw DDL (db/init.js) and Drizzle, question-option loading pattern duplicated in 3 files (polls.js:33-66, publicPolls.js:30-62, analytics.js:28-65), rejectUnauthorized:false for DB SSL (db/client.js:8), raw error.message leaked to clients (index.js:74), no tests, no TypeScript loses Drizzle type inference.
Real-Time Updates Using WebSockets5 / 10
Working Socket.IO implementation: server registered on HTTP server (index.js:25-30), room-based with poll:{pollId} for creator analytics and public:{slug} for public pages (sockets/index.js:1-15). Client emits poll:join/poll:leave on mount/unmount. Server emits poll:analytics (full payload) on response submission and publish (publicPolls.js:198, polls.js:229). Server emits poll:submitted with totalResponses to public room (publicPolls.js:199-203). Client cleanup properly calls socket.leave and socket.disconnect (Analytics.jsx:46-55, PublicPoll.jsx:64-73). Gaps: only 4 events total, no reconnection room re-join logic, no authentication on socket connections, no debouncing on analytics push, no events for poll expiry or deletion, no data pushed inline (client refetches), public room only gets response count not analytics.
Code Quality & Project Structure5 / 10
Clean monorepo with server/src separation, consistent naming, no unused imports, no dead code, all functions small and focused. ESLint configured. .env.example with thorough comments. README with setup, API table, socket events, deployment notes. Proper React patterns: useMemo/useCallback, useEffect cleanup on all effects, loading→error→empty→content state patterns. Issues: no TypeScript (plain JS/JSX loses Drizzle type safety), schema duplication (db/schema.js and db/init.js define same schema differently), question-option loading logic duplicated verbatim in 3 server files, monolithic 1126-line CSS file with no sections, no automated tests at all, no service/repository layer separation, rejectUnauthorized:false SSL config (security), inconsistent error propagation (next vs return next).
62
S
Haiya Click - Create realtime polls in few clicks
Sandipan Chakraborty · @shaanch04
46
Haiya.click is a full-stack real-time polling platform with a distinctive terminal/ASCII aesthetic. The backend shows strong engineering in its response collection flow — server-side fingerprint generation, Valkey-based deduplication locks, atomic tally counting, and an async queue worker for MongoDB persistence. WebSocket implementation is thorough with the Valkey pub/sub adapter, late-joiner catchup, and comprehensive event coverage across the poll lifecycle. Email verification with custom HTML templates and results notification emails add polish. However, security gaps (no CSRF, no rate limiting, no helmet), credential leaks in source code, non-rotating refresh tokens, and the lack of any tests are notable concerns. The frontend has creative interactive elements but suffers from inline styles throughout, 6 unused dependencies, extensive code duplication, and monolithic component files. Overall: a functionally complete, creative submission with solid real-time infrastructure that needs additional security hardening and code cleanup.
Authentication & Access Control6 / 10
JWT dual-token strategy with bcrypt 12 rounds and SHA-256 hashed refresh tokens in httpOnly/secure/sameSite cookies (auth.services.js:42-69, auth.controller.js:12-17). Email verification via Mailtrap with custom HTML templates (email.utils.js:13-105). Three-tier auth middleware: authenticate (strict), authenticateOptional (silent), and userCheck with mode flag (auth.middleware.js:6-49, poll.middleware.js:6-36). Anonymous vs authenticated poll modes enforced at REST API (poll.services.js:95/102), submission (response.services.js:12), and WebSocket (socket.js:38-49) layers. Server-side fingerprint overrides client input (response.controller.js:10-13). Gaps: no CSRF protection despite sameSite:none in production, no rate limiting on any auth endpoint, refresh tokens never rotated (same 7-day token throughout), forgotPassword service exists but has no route wired, verification token passed as URL query parameter.
Poll Creation & Question Management5 / 15
Poll creation form (Dashboard.tsx:348-403) uses native HTML form submission with fields for title, duration (mins+secs), isAnonymous, and requireAuth. Questions added one-at-a-time after poll creation with dynamic options — starts with 4 empty slots, add/remove supported, isRequired toggle (Dashboard.tsx:563-622). Question CRUD fully implemented backend-side with Joi DTO validation (questions.dto.js: min 2 options, text 1-500 chars). Creator-only gates on question operations (question.services.js:9,55,69). Gaps: no batch multi-question creation, no expiry date picker (expiry derived from duration at start time), react-hook-form + zod installed but unused, validation uses alert() not inline errors, no slug/QR sharing UI, option IDs are sequential (o1,o2) risking collisions on remove-then-add.
Response Collection Flow7 / 15
Comprehensive defense-in-depth in response.services.js: poll existence check, requireAuth enforcement, isStarted/isPublished gates, answer array validation, per-question option ID validation (lines 8-55). Valkey-based per-question-per-user locking for deduplication with 7-day TTL (lines 42-46: lock:poll:{pollId}:q:{questionId}:u:{submitterId}). Server-side SHA-256 fingerprint from IP+UserAgent overrides client-provided fingerprint (response.controller.js:6-13). Real-time tally via atomic Valkey HINCRBY with VOTE_PULSE broadcast (lines 48-54). Async queue-based submission processing via background worker (submission.worker.js: 3s drain, 2-pass bulkWrite). Client: per-question immediate submission with 600ms auto-advance, skip on non-required (PollView.tsx:406-432), countdown timer with color transitions (lines 371-398), multilayered auth gate with inline login. Gap: worker re-queues entire batch on failure causing potential poison-document loop.
Analytics & Feedback Dashboard6 / 15
OverallAnalytics (analytics.services.js:73-123) provides 8 stat cards: total polls, published, live, drafts, total votes, total views, avg voters/poll, best poll, plus polls-over-time bar chart and top-3 ranking. PollAnalytics (lines 9-68) delivers per-question breakdowns with option counts, percentages, winner detection, voters-over-time 10-bucket timeline, concurrent user tracking, and duration used. Frontend renders custom CSS bar charts (AnalyticsPanel.tsx:58-226) — no charting library used despite recharts being in deps. Results publishing flow (poll.services.js:114-214) drains Valkey queue, flushes tally to MongoDB, counts unique voters via lock keys, sends email notification to creator with HTML template (email.utils.js:107-196), broadcasts RESULTS_READY via socket. Dashboard polls /results every 2s until ready. Gaps: no CSV/JSON export despite being advertised in FeatureGrid, no individual response viewing, no date-range filtering, voter timeline fixed at 10 buckets regardless of poll duration, no caching on analytics queries.
Frontend Experience5 / 10
Distinctive dark theme with monospace "Space Mono" typography and ASCII-art/terminal aesthetic — bracket headers like [ DASHBOARD ], [ CREATE NEW POLL ]. Creative interactive elements: custom typewriter hook (HeroSection.tsx:4-38), dodging "NO" button with collision avoidance (lines 177-212), ASCII wave background with sine/cosine animation. 6 routes with react-router, responsive mobile sidebar (Dashboard.tsx:692-713). All poll states handled: loading (pulse animation), error, waiting-to-start (spinner), live voting, submitted, published, auth-gate with inline login. Countdown timer with color transitions (PollView.tsx:371-398). Significant drawbacks: ALL styles are inline React.CSSProperties objects — Tailwind is fully configured but completely unused in application code. 50+ shadcn/ui components installed but virtually none used. Monolithic pages: Dashboard.tsx (719 lines), PollView.tsx (811 lines). Validation errors use alert() instead of toast or inline feedback. No skeleton loaders (just pulsing text). Native confirm() for destructive actions.
Backend Architecture & API Design6 / 15
Clean layered MVC with DTOs: routes→controllers→services→models separation across 30 backend files. DTO validation system uses BaseDto pattern with Joi abortEarly:false + stripUnknown, consistent validate middleware (validate.middleware.js). ApiError class hierarchy (400/401/403/404/409/500) + ApiResponse format. Socket.io with @socket.io/redis-adapter for multi-instance pub/sub (socket.js:20-26). Background submission worker with 3s drain intervals using bulkWrite for efficiency (submission.worker.js). Good indexing: slug unique, pollId indexed on Questions. Valkey used for slug→ID cache, live tally, submission queue, dedup locks. CORS with credentials+dynamic origins. Notable gaps: no rate limiting on any endpoint, no security middleware (helmet/mongo-sanitize), no CSRF protection, no request body size limits, Response model lacks indexes on pollId/userId, in-process setTimeout for auto-publish (lost on restart), global error handler exposes raw err.message to clients, credentials hardcoded in package.json (CapRover deploy token) and valkey.render.js (Redis password), DRY violations in auth middleware (token extraction pattern repeated 3x).
Real-Time Updates Using WebSockets7 / 10
Socket.io server with Valkey/Redis pub/sub adapter for cross-instance scaling (socket.js:20-26). Room-based architecture with join-poll handler that validates requireAuth, broadcasts USERS_COUNT, tracks maxConcurrentUsers in MongoDB, and replays QUESTION_PUBLISHED to late-joining sockets (lines 32-77). Seven event types covering the full poll lifecycle: join-poll, QUESTION_PUBLISHED, VOTE_PULSE (with per-question-option newCount), POLL_PUBLISHED, RESULTS_READY, USERS_COUNT, JOIN_REJECTED. Server emits from response submission (tally update), poll start (QUESTION_PUBLISHED), publish flow (POLL_PUBLISHED + RESULTS_READY), and disconnecting (updated USERS_COUNT). Client PollView.tsx handles 3 socket branches: waiting-to-start with late-joiner catchup (lines 239-315), live voting with VOTE_PULSE listener (lines 317-350), published with results polling. Retry logic for question fetching on QUESTION_PUBLISHED with backoff delays [0,400,1000,2000]ms (lines 285-291) — handles race condition between socket event and DB write propagation. Proper cleanup on unmount and state transitions. Gaps: no reconnect room re-join logic, no socket-level authentication (auth only on join-poll), no client-side debouncing on VOTE_PULSE.
Code Quality & Project Structure4 / 10
Decoupled frontend/backend separation, clean backend structure (configs/controllers/dto/middleware/models/routes/services/utils/workers), .env.example files for both packages, authFetch utility with automatic JWT refresh (frontend/src/utils/auth.ts:9-54). TypeScript on frontend with tsconfig strict, ESLint configured. README with architecture overview, setup instructions, and deployment guidance. Major quality issues: backend is pure JavaScript (no TypeScript), zero tests anywhere in the project, 29 `any` type usages across 4 frontend files. AnimatedAsciiBarChart duplicated verbatim between HeroSection.tsx and PollView.tsx (108 lines each). Glow effect mouse-follow logic duplicated in 3 components. Pulse/fadeIn keyframes duplicated across multiple inline `<style>` tags. 6 unused npm dependencies (recharts, react-hook-form, zod, @hookform/resolvers, date-fns, next-themes). App.css is dead Vite boilerplate. Frontend README.md is unmodified Vite template. Orphaned components: ASCIICanvas.tsx (237 lines never imported), DualStreamText.tsx (91 lines never imported). 50+ shadcn/ui components installed but unused. Credentials committed to source: CapRover deploy token in package.json:9, Redis password in valkey.render.js:6. Typo "Autheticated" in auth.middleware.js:12.
63
M
Pollzy
Manas Saha · @matrixnas
46
Pollzy is a full-stack polling platform (~9,300 LOC) with Express 5/PostgreSQL/Redis backend and React 19/TanStack Router frontend deployed on Vercel. Strengths include: dual-token JWT auth with refresh rotation, guest token system for anonymous voting, Zod validation on all inputs, Redis pub/sub for Socket.IO scaling, clean dark-themed UI with comprehensive state coverage, and Recharts-powered analytics with pie charts. Key weaknesses: no security hardening (no Helmet, no rate limiting), zero automated tests, inconsistent userId type handling (17 as-any casts), publishPoll swallows errors into generic internal errors, Socket.IO lacks authentication and reconnection handling, response submission doesn't validate option-to-question relationships or poll status server-side, and multiple spelling errors across the codebase (viwe, Responce, Erorr, SERECT). Total score: 46/100.
Authentication & Access Control7 / 10
Custom JWT auth with dual-token strategy: 15-min access token + 7-day refresh token stored in httpOnly/secure/sameSite cookies (auth.controller.ts:88-94). Refresh token rotation with bcrypt comparison (auth.controller.ts:202-210). Guest token system for anonymous users with 30-day UUID cookie (auth.controller.ts:272-288). Three-tier auth middleware: authorization (required), optionalAuthorization (soft), isAuthorizationRequired (poll-aware public/private check in poll.middleware.ts:19-47). Frontend: Auth guard beforeLoad redirects on 4 protected routes, Axios interceptor for automatic token refresh with concurrent-request deduplication (axios.ts:35-90), Zustand store for user info. Registration with firstName/lastName/email/password + bcrypt(10). Gaps: no Helmet/security headers, no rate limiting on any auth endpoint, no email verification on registration (infrastructure exists but unused), inconsistent userId type (payload object vs payload.userId string across auth.middleware.ts:47,82), forgot password UI is non-functional text only.
Poll Creation & Question Management6 / 15
Two-phase creation flow: CreateNewPollPage (517 lines) uses React Hook Form + Zod for title, description, isPublic, closedAt with 6 duration presets and Public/Private visibility toggle. DraftPoll editor (139 lines) supports dynamic questions with add/remove (max 20), per-question isRequired toggle, per-option text editing, add/remove options (min 2, max 10). Server-side validation via addQuestionsDto (poll.validation.ts:14-37) enforces min 1 question, min 2 options per question, max 10 options, max 20 questions. publishPoll endpoint inserts questions + options + updates status to active (poll.controller.ts:117-181). Gaps: questions cannot be edited after publishing (only during draft), publishPoll swallows all errors with try/catch → ApiError.internal() (line 178-179), no transaction wrapping for the multi-step publish operation (questions + options + status update), no slug/permalink generation, dead import `import {title} from "node:process"` in poll.validation.ts:3.
Response Collection Flow5 / 15
Step-by-step voting interface with prev/next navigation, progress bar, per-question single-option selection (PollInterface.tsx:491 lines). Client-side mandatory question enforcement (canGoNext blocks at line 110-111). Duplicate vote prevention at 3 levels: (1) frontend pre-checks is-already-voted on mount (PollInterface.tsx:75), (2) application-level check in submitVote (poll.controller.ts:411-428), (3) database uniqueIndex on (userId, questionId) in votes.schema.ts:38. Anonymous voting via guestToken cookie, authenticated via userId. Zod validation on answers array (submiteVoteDto). Expiry computed at runtime: `closedAt < new Date()` (poll.controller.ts:333-334). Gaps: submitVote does NOT validate optionId belongs to question, does NOT check if poll is expired/closed/draft before accepting votes, the duplicate prevention only checks first answer's questionId (answers[0]?.questionId at line 417), no transaction wrapping for vote insertion, no server-side enforcement of single-option-per-question constraint.
Analytics & Feedback Dashboard6 / 15
Creator dashboard (PollAnalysisDashboard.tsx:141 lines) with StatsGrid showing Total Views, Total Votes, Questions, Options. VoteDistributionChart (207 lines) renders per-question Recharts pie charts with percentage distribution, vote counts, and animated progress bars. PollDetailsCard with metadata (views, votes, visibility, dates, status). Results page (PollResultsScreen.tsx:260 lines) shows per-question vote breakdowns with VoteProgress bars and Required/Optional badges. 3 result visibility modes: "always", "after_vote", "creator_only" (poll.schema.ts:19-23). Real-time Socket.IO updates increment dashboard counts live. Empty state for "No votes yet" with icon. Gaps: no CSV/export functionality, no individual response viewing, no trend/time-series data, no peak activity metrics, no anonymous vs authenticated breakdown, dashboard data computed via Map iteration over joined rows rather than aggregation pipeline, no publishing controls on dashboard.
Frontend Experience6 / 10
Consistent dark theme ("mission control" aesthetic) across all 12 routes using Tailwind CSS 4 + shadcn/ui. TanStack Router with SPA navigation, scroll restoration, preloading. Responsive layout (mobile through desktop). All page states handled: loading (PageLoader, PollSkeleton), error (ErrorState with retry), empty (multiple EmptyState variants), auth-guarded (beforeLoad redirects). PollInterface covers 5 states: voting, already-voted (PollCompleteScreen), poll-closed (PollClosedScreen), completed, saving. Form validation with inline error messages (React Hook Form + Zod). Submitting states with spinners + disabled buttons. Save/bookmark toggle with optimistic UI. Gaps: no dark/light mode toggle, forgot password is non-functional text only (LoginPage.tsx:97-102), console.log in production (LoginPage:50, PollAnalysisDashboard:58), inline styles proliferate throughout components bypassing Tailwind class system, no toast/notification system.
Backend Architecture & API Design6 / 15
Clean layered MVC-lite architecture: routes → validate middleware → auth middleware → controller → Drizzle ORM → PostgreSQL. Zod validation on all 22 API endpoints via BaseDto pattern (baseDto.ts:7-27). Environment validation at startup exits if vars missing (envValidate.ts:20-26). Centralized error hierarchy: ApiError class with static factory methods (apiError.ts:2-36), errorHandler middleware for consistent responses (errorHandler.ts:5-36), asyncHandler wrapper for async error propagation. 8 database tables with proper foreign keys (ON DELETE CASCADE), unique indexes, and enum types. Redis pub/sub for cross-instance Socket.IO scaling. Refresh token rotation with bcrypt. Proper HTTP status codes (201/401/403/404/409/500). Gaps: NO Helmet security headers, NO rate limiting anywhere, NO request body size limit on express.json(), inconsistent error throwing (throw new Error() in poll.controller.ts:70 creates opaque 500s instead of proper ApiError), publishPoll try/catch swallows all errors into generic ApiError.internal() (line 178-179), no transaction wrapping for publishPoll multi-step operation, pollId params not validated as UUIDs, "viwe" misspelling in schema/routes/controller.
Real-Time Updates Using WebSockets5 / 10
Socket.IO server attached to HTTP server (index.ts:11) with Redis pub/sub for cross-instance broadcasting (socket.io.ts:3-22). Room-based pattern: clients join/leave `poll:{pollId}` rooms. Two server-emitted events: vote_updated (carries pollData with questionId/optionId) and view_updated (signal). Client in PollAnalysisDashboard connects, joins room, listens for both events with client-side state increment (PollAnalysisDashboard.tsx:66-104). Cleanup properly calls socket.off(), emit leave_poll, socket.disconnect(). Client-side socket emit on vote submission in PollInterface (line 148). CORS configured on Socket.IO. Gaps: NO authentication on Socket.IO connections (any client can join any room), no reconnection room re-join logic, vote_updated fires BEFORE API call succeeds (optimistic, can cause stale data if API fails), no typed Socket.IO events, no debouncing, no events for poll publishing/deletion/status changes, only covers analytics dashboard (not voting page for live response counts).
Code Quality & Project Structure5 / 10
Monorepo with clean backend/frontend separation. TypeScript strict mode both sides with noUncheckedIndexedAccess on backend (tsconfig.json). Consistent module pattern: each feature has routes/controller/middleware/validation files. Feature-based frontend organization. Import aliases (#/, @/) via Vite config. BaseDto pattern for validation reuse. Axios interceptor with token refresh queue. Drizzle ORM with typed schemas. ESLint + Prettier on frontend. .env.example.txt with all required variables. Gaps: zero automated tests (0 test/spec files across ~9,300 LOC), "viwe" misspelling in 3 files (schema/routes/controller), "apiResponce" and "ErorrState" misspellings, "SERECT" misspelling throughout JWT env vars, dead import `import {title} from "node:process"` in poll.validation.ts:3, 17 instances of `(req as any).userId` with no Express Request augmentation, cookie options duplicated 4 times in auth.controller.ts, console.log in production code, inline styles everywhere bypassing Tailwind class system, no CI/CD, no README setup instructions.
64
Voicora
Rupesh · @rupesh
46
Voicora is a well-polished full-stack polling platform with a consistent dark-themed UI, multi-step poll creation, animated question-by-question response flow, and Socket.IO-powered live updates. Both frontend (https://voicoraa.vercel.app) and backend (https://voicora-backend.onrender.com) are live. Strengths: clean backend architecture with Zod validation, thorough error handling, duplicate response prevention, good state coverage across UI pages, and real-time Socket.IO integration. Key gaps: per-question mandatory/optional toggling is completely absent (a core requirement), analytics are shallow with no trends/exports, security middleware (helmet, rate-limit) are installed but unused, no TypeScript on frontend, no tests, and the analytics endpoint provides basic counts only. Overall: competent hackathon project with good UX but missing depth in several key areas.
Authentication & Access Control6 / 10
JWT-based auth with bcrypt (12 rounds) for register/login (auth.service.ts:15-71), Zod validation on inputs (auth.validation.ts), authenticate and optionalAuth middleware (auth.middleware.ts:10-48), AuthContext with localStorage token persistence (AuthContext.jsx:6-86), ProtectedRoute component (ProtectedRoute.jsx), and responseMode enforcement for anonymous vs authenticated polls (response.service.ts:26-28). Gaps: helmet and express-rate-limit dependencies are installed but NEVER imported/used (package.json only), no Helmet/CSRF protection, tokens stored in localStorage rather than httpOnly cookies, no refresh token rotation, no email verification or password reset.
Poll Creation & Question Management5 / 15
Multi-step poll creation form (CreatePoll.jsx, 468 lines) with dynamic questions/options using add/remove, live preview sidebar, sticky bottom actions, settings for anonymous/authenticated mode, expiry datetime-local picker. Backend Zod validation requires min 1 question, min 2 options per question (poll.validation.ts:15-26). Slug auto-generation for sharing (helpers.ts:7-9). CRITICAL GAP: per-question mandatory/optional toggle is completely missing — the frontend `required: true` field (CreatePoll.jsx:41) is hardcoded, never sent to backend, and has no UI toggle; the Poll model has no `isRequired` or `required` field on questions; no enforcement anywhere. Also: the `updatePoll` service supports question editing via Object.assign (poll.service.ts:42-61) but the frontend has no edit-poll page.
Response Collection Flow6 / 15
One-question-at-a-time response flow with progress bar (PollResponse.jsx:498-571). Server-side validation checks poll is active and not expired (poll.service.ts:120-127), answers match valid questions/options (response.service.ts:42-56), and responseMode enforcement (response.service.ts:26-28). Duplicate prevention via compound sparse unique indexes {pollId, userId} and {pollId, anonymousId} (response.model.ts:58-66) plus explicit findOne checks. localStorage tracking for anonymous voters (PollResponse.jsx:43,89). Auth modal flow for authenticated-mode polls. Covers loading, error, expired, draft, closed, submitted, show-results, and thank-you states. Gap: no server-side enforcement of mandatory questions (model has no such field), response submission is not atomic (separate vote increment and response creation ops).
Analytics & Feedback Dashboard5 / 15
Server analytics endpoint returns per-question option vote counts and percentages (response.service.ts:120-146). Frontend PollAnalytics page (PollAnalytics.jsx, 303 lines) shows stat cards (total responses, questions, status), per-question option breakdowns with animated progress bars, and live Socket.IO updates. Dashboard shows poll cards with response counts and search/filter. PollResults page renders published results publicly. Copy-link and publish-results actions available. Gaps: no time-series/trend data, no CSV export, no individual response viewing, no anonymous vs authenticated breakdown, no peak activity metrics, Recharts AreaChart imported but unused (preview only), analytics are computed from embedded vote counts rather than aggregated from responses (no participation insights beyond raw numbers).
Frontend Experience7 / 10
Consistent dark theme with Tailwind CSS v4 design tokens (index.css:1-50, 15 semantic color variables). Framer Motion animations throughout (page transitions, option selection, progress bars, animated bars, mobile menu). 7 routes: Landing, PollResponse, PollResults, Dashboard, CreatePoll, PollAnalytics. Responsive layout with collapsible mobile sidebar (AppLayout.jsx). Live preview sidebar on poll creation. Step indicator with checkmarks. Loading spinners on every data-fetching page. Error/not-found/empty/draft/expired/closed states all handled explicitly. React Hot Toast for notifications. Auth modal with login/register toggle. Issues: no TypeScript on frontend, no form library (plain useState for complex multi-step form), landing page uses hardcoded fake stats, no dark/light mode toggle, no confirmation dialogs for destructive actions.
Backend Architecture & API Design6 / 15
Clean layered architecture: routes → controllers → services → models with module-based separation (auth/poll/response). TypeScript strict mode enabled (tsconfig.json:9). Zod validation middleware with generic schema support (validate.middleware.ts). Custom AppError class hierarchy with 6 error types (errors.ts), each mapped to correct HTTP status codes. Comprehensive error handler covering Mongoose validation/duplicate/CastError and JWT errors (error.middleware.ts). Async handler wrapper eliminates try/catch in controllers. MongoDB schemas with embedded sub-documents (questions/options), compound sparse unique indexes for duplicate prevention. CORS configured with credentials. GAPS: helmet and express-rate-limit installed but never used in app.ts, `as any` cast in auth.service.ts:11, ensurePollAcceptsResponses parameter typed as `any`, no transactions for response+vote atomicity, no pagination, no request body size limits, no input sanitization beyond Zod.
Real-Time Updates Using WebSockets6 / 10
Socket.IO server initialized on HTTP server with CORS (socket.ts:7-16). Room-based pub/sub pattern: clients emit `poll:join`/`poll:leave` to manage `poll:{id}` rooms (socket.ts:22-31). Server emits `vote:update` event after each response submission with per-question vote counts (response.controller.ts:14-17). Client singleton socket pattern (socket.js) with autoConnect:false, connect/disconnect/join/leave helpers, and reconnection-safe join via `socket.once('connect')` (socket.js:34-36). Three pages use Socket.IO: PollAnalytics, PollResponse (after voting), PollResults — all listen for `vote:update` and trigger HTTP re-fetch (invalidation pattern, not data pushing). Proper cleanup in all useEffect returns. Gaps: only one event type, no debouncing on analytics re-fetch (fires per question), console.log debug messages left in production (socket.ts:19,24,30,35,40), no socket authentication, no broadcast on poll state changes (activate/close/publish).
Code Quality & Project Structure5 / 10
Clean monorepo with backend/ and frontend/ separation. Backend: TypeScript with strict config, consistent module structure (modules/auth, modules/poll, modules/response), typed interfaces (types/index.ts, 105 lines), CSS design token system (15 semantic colors). GAPS: No TypeScript on frontend (entirely .jsx/.js files despite project claiming TypeScript in tech stack), no automated tests anywhere, no .env.example for backend, README in subdirectory not repo root, `statusConfig` duplicated in Dashboard.jsx:21-25 and PollAnalytics.jsx:177-181, the `required` field in CreatePoll question state is dead code (set but never used), no ESLint/Prettier on frontend, no form library or useReducer for complex multi-step form state, `as any` cast in auth.service.ts:11, unused imports in PollAnalytics.jsx (AreaChart/Area/XAxis etc.).
65
Hibiki
Vishal Patil · @vishal
45
Hibiki is a solid full-stack polling platform with Clerk authentication, dynamic poll creation/editing, validated response collection, Recharts-powered analytics with Socket.IO real-time updates, and a polished landing page. The backend architecture is clean with TypeScript strict mode, Joi validation, and proper MVC separation. Key strengths: proper anonymous/authenticated poll mode enforcement, good response validation with multi-layer checks, full analytics snapshot push over WebSockets (not just invalidation signals), comprehensive UI state coverage. Key weaknesses: massive code duplication between CreatePoll/EditPoll, frontend has zero TypeScript, no tests, dead code files (clerk.middleware.ts, cn.js utility), anonymous vote prevention relies on weak IP-based checks, no CSV export or individual response viewing in analytics. Overall: a well-rounded submission with functional completeness but engineering quality gaps.
Authentication & Access Control6 / 10
Clerk-based authentication with clerkMiddleware() applied globally (app/index.ts:25), protect middleware verifying getAuth(req).userId (user.middleware.ts:5-19), and ProtectedRoute wrapper in React (App.jsx:13-20) redirecting unsigned-in users. Anonymous vs authenticated poll modes enforced via requireAuth flag at both client (PollPage.jsx:179-191) and server (public.controller.ts:25-27, 52-54) levels. Axios interceptor attaches Clerk JWT to all requests (AxiosInterceptorProvider.jsx:9-22), SyncUser component persists Clerk user to MongoDB on sign-in (SyncUser.jsx:11-47). Gaps: no custom registration/login forms (entirely Clerk-dependent), clerk.middleware.ts is dead code (never imported), protect middleware bypasses global error handler with manual response construction, anonymous mode uses req.ip for duplicate prevention (easily bypassable), no CSRF protection, no rate limiting on sync endpoint.
Poll Creation & Question Management5 / 15
CreatePoll.jsx (304 lines) and EditPoll.jsx (328 lines) support dynamic questions with add/remove, per-question required toggle via checkbox, anonymous/authenticated toggle via radio buttons, and datetime-local expiry picker. Server-side Joi validation in CreatePollDto validates title, questions (min 1), options (min 2 per question), expiresAt as date, and status enum (create.dto.ts:5-37). EditPoll preserves existing MongoDB ObjectIds for questions/options during update, and backend pollService.update handles re-activation of expired polls when expiry date is extended (polls.service.ts:67-71). Major gaps: ~70% code duplication between CreatePoll and EditPoll (no shared PollForm component), no form library (raw useState with imperative handler functions), minimal client-side validation (only title + expiry existence checks at CreatePoll.jsx:78-79), no draft creation from UI (status always hardcoded to "active"), no slug/QR code for sharing.
Response Collection Flow6 / 15
PollPage.jsx (268 lines) implements radio button selection per question with client-side answers state tracking. Server validates: poll existence, active status (public.controller.ts:56-62), expiry (line 56-58), requireAuth gate (line 52-54), per-answer questionId/optionId validity (lines 75-93), and required question enforcement (lines 95-107). Joi RespondDto validates answers array with min 1 item (respond.dto.ts:5-15). Rate limiting at 100 req/15min (public.routes.ts:10-19). Duplicate prevention via backend respondentId check (public.controller.ts:66-71) + frontend localStorage (PollPage.jsx:39). Atomic option count increment via MongoDB bulkWrite with $inc and arrayFilters (public.controller.ts:121-143). Comprehensive UI states: loading spinner, differentiated error (401/404/400), submitted success with Framer Motion animation, expired banner, auth-required banner. Gaps: no unique compound index on {pollId, respondentId} for DB-level duplicate prevention, anonymous dedup uses req.ip (bypassable), bulkWrite failures silently swallowed (public.controller.ts:144-146) risking data inconsistency.
Analytics & Feedback Dashboard6 / 15
Analytics.jsx (301 lines) renders per-question Recharts bar charts and donut pie charts with colored segments, stat cards for total responses and question count, copy-link button with "Copied!" feedback, and publish button with loading state. MongoDB aggregation pipeline (analytics.service.ts:24-37) efficiently computes answer counts via $unwind + $group. Public results page (ResultsPage.jsx, 169 lines) shows bar charts with percentage progress bars, vote counts, and per-option breakdowns with animated progress bar widths. Empty state when no responses (Analytics.jsx:207-216) with illustration and CTA. Dashboard (Dashboard.jsx, 154 lines) lists polls with status badges, Framer Motion staggered cards, edit/delete actions, and empty state with "Get started" link. Gaps: no CSV/JSON export, no individual response viewing (only aggregated), no trend/time-series data (response velocity, peak activity), no respondent breakdown by auth type (authenticated vs anonymous), no per-question completion/drop-off rates, no publish confirmation dialog.
Frontend Experience5 / 10
Seven routes with proper layout nesting (DashboardLayout/PublicLayout with Outlet), ProtectedRoute wrapper handling loading/signed-out states (App.jsx:13-20). Consistent Tailwind CSS design language with glass-card, rounded-xl, and custom color tokens. Framer Motion animations: staggered dashboard cards (Dashboard.jsx:83-88), success state (PollPage.jsx:152-167), landing page sections. All data pages handle loading (spinners), error (inline alert or dedicated UI), and empty states (Dashboard.jsx:66-79, Analytics.jsx:207-216). Responsive grid layout (grid-cols-1 md:grid-cols-2 lg:grid-cols-3). Landing page with Hero, 9-feature grid, 4-step workflow, FAQ accordion, and footer CTA. Gaps: no form library (raw useState with imperative handlers), no toast/notification system, no loading skeletons, no TypeScript on frontend (all .jsx/.js), repeated inline spinner markup across 5 files, delete uses window.confirm rather than styled modal, dashboard doesn't display errors to user (only console.error), no dark mode.
Backend Architecture & API Design6 / 15
Clean MVC architecture with module-based separation: routes→controllers→services→models per domain (polls, responses, users, analytics). TypeScript strict mode with noUncheckedIndexedAccess and exactOptionalPropertyTypes (tsconfig.json:24-25,36). Joi DTO validation via BaseDto class with generic validate middleware factory (validate.middleware.ts). ApiError class with static factory methods (badRequest/unauthorized/forbidden/notFound/conflict/internal) and centralized error handler (app/index.ts:39-52). Helmet security headers, CORS with env-based origin, express-rate-limit on respond endpoint. MongoDB bulkWrite for atomic vote count increments with arrayFilters. Dual indexes on Response model (pollId and compound {pollId, respondentId}). Express app factory function for testability (app/index.ts:10). Gaps: clerk.middleware.ts is dead code (never imported), protect middleware bypasses global error handler returning manual 401 instead of throwing ApiError, 10 instances of any type assertions, "i": "^0.3.7" typo package in devDependencies, only respond endpoint has rate limiting, no unique compound index for DB-level dedup, socket emit failures silently swallowed risking inconsistency.
Real-Time Updates Using WebSockets6 / 10
Socket.IO server with room-based pattern: clients join `poll:<id>` rooms via socket.on("poll:join") and leave via "poll:leave" (socket.ts:33-43). Server emits poll:analyticsUpdated with full analytics snapshot on response submission (public.controller.ts:148-155), poll:statusUpdated on poll update/publish (polls.controller.ts:71-77, 103), and poll:published on publish (polls.controller.ts:102). Analytics page handles connect/reconnect with room re-join (Analytics.jsx:31-40), listens for all three event types and updates state (lines 42-70). ResultsPage listens for analytics updates (ResultsPage.jsx:35-42). Redis adapter for multi-instance scaling when REDIS_URL is set (socket.ts:16-29). Socket cleanup on component unmount with socket.off. Gaps: poll:expired event listened on frontend (PollPage.jsx:73) but NEVER emitted by backend (dead event), no socket authentication (anyone can join any room), no debouncing on analytics updates, ResultsPage and PollPage lack connect-event re-join logic, no data validation on socket payloads.
Code Quality & Project Structure5 / 10
Monorepo with separate frontend/backend packages, consistent naming conventions (kebab-case backend files, PascalCase frontend components), ESLint + Prettier at root level. Modular backend architecture with clean separation per domain. Comprehensive README with tech stack tables, setup instructions, and API docs link. API_DOCS.md with all endpoints and WebSocket events documented. .env.example files for both packages. TypeScript strict mode on backend. Gaps: frontend is entirely JavaScript (no TypeScript at all) — major inconsistency with backend's strict TS posture, zero automated tests (root package.json: "test": "echo Error: no test specified"), ~70% code duplication between CreatePoll.jsx and EditPoll.jsx, cn.js utility defined but never imported anywhere, "i": "^0.3.7" typo package in devDependencies, three landing-page chart components (PieChart/BarChart/CompletionRing) only used by static DashboardPreview mockup, repeated inline loading spinner across 5 files, repeated logo markup across 4 files, frontend/README.md is a one-line stub, no CI/CD configuration.
66
Narad Muni
Arnab Samanta · @loveumearnab
45
Narad Muni (Naarad Sabha) is a mythology-themed polling platform with a React+Vite+TypeScript frontend and Express+PostgreSQL+Drizzle backend. The project demonstrates solid engineering in several areas: thorough response collection with defense-in-depth validation, well-structured Socket.IO real-time updates with server-pushed analytics data, and a distinctive neo-brutalist UI. However, critical gaps exist: no poll editing or deletion endpoints, reliance on Clerk for all auth with no Helmet/CSRF security, missing tests, and several code hygiene issues (leaked credential comment, console.log in production, as any casts). Deployed at https://naradmuni.arnabsamanta.in and verified working. Overall, a functional but incomplete platform that covers core polling flows well but lacks production-grade completeness in security, editing capabilities, and testing.
Authentication & Access Control6 / 10
Clerk-based authentication with ProtectedRoute and AdminRoute wrappers (App.tsx:17-44). Server-side getSessionUser auto-creates users from Clerk profile and auto-promotes admin emails (lib/http.ts:41-88). requireSessionUser/requireAdminUser middleware for route protection. Anonymous vs authenticated poll modes enforced at submitPoll (poll.controller.ts:512-520) with IP-based rate limiting (poll.request-utils.ts:32-50). Auth token injected via useApiClient getToken() on every API call (api.ts:17-28). Gaps: no Helmet security headers, no CSRF protection, entirely Clerk-dependent with no custom registration/login flow, no rate limiting on auth endpoints.
Poll Creation & Question Management5 / 15
Working poll creation form at /createPolls with dynamic questions and options via QuestionCard (question-card.tsx:21-106), PollSettingsForm with title/slug/description/category/tags/cover-photo/expiry/anonymous-toggle/live-results/completion-message (poll-settings-form.tsx:18-158). useCreatePolls hook manages form state with add/remove question/option (use-createPolls.ts:14-170). Server-side Zod validates questions (min 1, max 20), options (min 2, max 8), question text (min 4, max 500) and all metadata (poll.validation.ts:1-38). Custom slug with collision avoidance (poll.service.ts:193-214). Gap: NO poll editing or deletion endpoints exist — the poll.routes.ts has only create/read/status-change, no PUT/DELETE, making this a significant functional gap.
Response Collection Flow7 / 15
Defense-in-depth response collection at poll.controller.ts:501-642. Server validates accepting-responses state (active + not expired), enforces anonymous/authenticated mode, checks all mandatory questions answered, validates question IDs and option IDs exist in poll. Triple duplicate prevention: (1) DB unique index one_response_per_user_per_poll_idx (schema.ts:134-137), (2) explicit SELECT check for authenticated users (line 550-562), (3) submission token + DB check for anonymous users (lines 564-580) with PostgreSQL unique constraint catch (lines 618-629). Anonymous IP-based sliding-window rate limit (5/60s). Transactional submission with question_responses. Client-side mandatory question validation via useMemo (use-public-poll.ts:57-62). 6 UI state branches on public poll page (published/expired/closed/auth-blocked/form/success). Gap: anonymous rate limit is in-memory only, resets on restart.
Analytics & Feedback Dashboard6 / 15
buildAnalytics in poll.service.ts:114-191 computes totalResponses, authenticatedResponses, anonymousResponses, first/lastResponseAt, completionRate, and per-question option counts with percentages. Dashboard page (dashboard-page.tsx:194-337) shows 4 metric cards, QuestionAnalytics with bar charts (question-analytics.tsx:4-31), individual responses list, CSV export (exportResponses at poll.controller.ts:304-388), and QR code download. Results publishing via PATCH status to 'published' with Socket.IO broadcast. Public results view via ResultsPanel component. Admin overview page with global stats. Gaps: no trend/time-series data, buildAnalytics uses N+1 queries (3 separate queries), no pagination on responses, no peak activity metrics.
Frontend Experience5 / 10
5 React Router routes with ProtectedRoute/AdminRoute wrappers (App.tsx:13-47). Consistent neo-brutalist Tailwind CSS design across all pages. All pages cover loading (Loader2 spinner), error (error panel), and empty states. Public poll page handles 6 distinct states: loading, not-found, published results, expired, closed, auth-required, and response form with success/error messages (public-poll-page.tsx:18-131). Dashboard has metric cards, question breakdowns, individual responses, and side panel. Dark/light theme toggle (use-theme.ts). Copy-link button with feedback, QR code, CSV download. Gaps: no form library (plain useState hooks on builder), console.log(poll) left in production (public-poll-page.tsx:39), no confirmation dialogs for destructive actions (close/publish), no pagination on poll lists.
Backend Architecture & API Design5 / 15
Clean module separation: routes→controllers→services→validation (poll.routes.ts, poll.controller.ts, poll.service.ts, poll.validation.ts). 19 REST endpoints across 3 route groups. PostgreSQL with Drizzle ORM, 5 tables + 2 enums, 3 migrations with proper foreign keys, indexes, unique constraints (schema.ts:1-164). Zod validation on all inputs plus env vars (env.ts:3-22). HttpError class + asyncHandler + centralized errorHandler (lib/http.ts:8-128). Transaction-based poll creation and response submission. Background expiry job (jobs/expire-polls.ts). Cloudinary integration for cover photos. Gaps: no Helmet, no general rate limiting, no request body size limits, Drizzle db typed as generic NodePgDatabase without schema (db/index.ts:16), leaked credential comment (db/index.ts:20-21), no poll edit/delete endpoints, currentUser lives in poll controller (layering issue).
Real-Time Updates Using WebSockets6 / 10
Socket.IO server integrated with Express HTTP server at index.ts:17-24. Room-based pattern: clients join per-poll rooms via poll:join/poll:leave events (socket/index.ts:3-19). Server emits 5 event types: analytics:update (on response submission), poll:published, poll:closed, poll:reopened (on status changes), and poll:expired (from background job). Client usePollSocket hook dynamically loads socket.io script, joins room, listens for all 5 events, and updates Zustand store (use-poll-socket.ts:21-74). Proper cleanup on unmount with poll:leave + disconnect. Server pushes full analytics data (not just invalidation signals) — poll.controller.ts:631-635. Gaps: no reconnection room re-join logic, no socket authentication, no typed Socket.IO events, no socket integration on public poll response page for live counts.
Code Quality & Project Structure5 / 10
Clean monorepo with client/server separation. TypeScript strict mode in both projects. Feature-based component organization (auth/builder/dashboard/home/layout/polls/public-poll/ui). Zustand store with typed selectors. Custom hooks for API/socket/poll-state/theme. .env.example files for both packages. Prettier + ESLint configured. Zod env validation at startup. Gaps: leaked credential comment in db/index.ts:20-21 (security hygiene failure), 8 console.log instances in production code (api.ts:51, public-poll-page.tsx:39, poll.controller.ts:672, etc.), 4 as any casts (createPolls-page.tsx:39-52), duplicated stringParam function (request-utils.ts + admin.controller.ts), zero tests, git history is AI-agent-generated (5 commits reference subagent dispatch/OpenRouter), Winston logger set up but mostly unused, hardcoded Docker credentials (ADMIN/ADMIN in docker-compose.yml).
67
Polra
Abdul Samad · @abdulsamad
45
Polra/Pollify is a solid full-stack polling platform with Clerk authentication, MongoDB/Mongoose backend, and a polished React frontend. The response collection flow stands out with thorough server-side validation (poll state, auth, answer validity, mandatory questions, duplicate prevention at both application and database levels). The analytics dashboard offers per-question breakdowns with ranked options, voter avatars, and a notable Deep Insights view with respondent-level drill-down and question/option filtering. Socket.io provides real-time updates with both server-emitted events and client-relayed live option toggling. Key weaknesses: no poll editing or deletion, no TypeScript, no automated tests, dead code (Submission model), db connection error handling issues, Socket.io lacks authentication and proper reconnection handling, no CSV export in analytics, and inconsistent naming (Pollify/Polra/polling-app). Total: ~3,300 lines of code. Frontend deployed and accessible at https://my-polra.vercel.app.
Authentication & Access Control6 / 10
Clerk-based auth with proper webhook sync (clerk.service.js:10-47, clerk.controller.js:6-37 with Svix verification). getOrCreateUser in poll.service.js:8-35 syncs Clerk users to MongoDB. Auth checks on all protected endpoints in poll.controller.js (create:20-23, creator-polls:77, publish:106, close:133). requiresAuth enforced in submitVoteService:86-88. Anonymous votes tracked by IP with DB-level sparse unique indexes (Vote.schema.js:35-38). Frontend uses Clerk Show/SignInButton/SignUpButton/UserButton pattern. Gaps: entirely Clerk-dependent (no custom auth), no Helmet CSRF config, no email verification or password reset (relies on Clerk), no poll deletion endpoint.
Poll Creation & Question Management5 / 15
CreatePoll.jsx (355 lines) supports dynamic questions/options with add/remove, mandatory toggle, auth/anonymous toggles, expiry datetime picker. PollDto Joi validation (poll.dto.js:5-23) enforces title min 3, questions min 1/max 50, options min 2/max 30, and ISO date. Poll schema (Poll.schema.js) has all required fields. Creation success shows QR code + copy link. Critical gaps: NO poll editing endpoint exists (no PATCH/PUT route), NO delete endpoint, no form library (individual useState hooks), no slug/permalink, no draft status, isPublished defaults to true in schema but createPollService hardcodes it to false.
Response Collection Flow7 / 15
SubmitVoteService (poll.service.js:74-137) has thorough validation: poll existence, isActive check, expiresAt check, requiresAuth check, duplicate answer detection per question, questionId/optionId validity verification, mandatory question enforcement, and duplicate vote prevention (checking userId+clerkUserId OR ipAddress with DB-level sparse unique indexes on Vote.schema.js:35-38). VoteDto Joi validation (vote.dto.js). Frontend VotePoll.jsx (262 lines) handles: published/results view, voted state, expired/closed, auth-required, loading, error states. localStorage votedPolls tracking as secondary duplicate check. Gap: no explicit single-choice-per-question enforcement in DTO/server-side (though duplicate questionId check prevents multi-select).
Analytics & Feedback Dashboard6 / 15
PollAnalytics.jsx (474 lines) has dual-tab view: Leaderboard (ranked options with progress bars, counts, percentages, voter avatars up to 5) and Deep Insights (filter by question→option, respondent list, individual respondent detail showing all answers including "skipped"). Server-side analytics (poll.service.js:139-214) computes totalResponses, per-question option counts with voter details, respondent list sorted by date. Publish/close with confirmation dialogs. Authorization gated (creator-only for unpublished, public for published). Community results page (CommunityResults.jsx:142 lines) with search. Gaps: no CSV export, no trend/time-series data, no peak activity metrics, no per-question drop-off rates, brute-force O(n*m) computation, no summary stat cards beyond total responses.
Frontend Experience6 / 10
React 19 + Vite + Tailwind CSS 4 with 6 React Router routes. Consistent green/cream design language with custom CSS animations. LandingPage.jsx (404 lines) has hero, feature grid, how-it-works, interactive demo poll, stats section. All pages handle loading (spinners), error (messages + navigation), empty states. Responsive layout with mobile hamburger Navbar (Navbar.jsx:161 lines). QR code generation with copy/download. Toast notifications via react-hot-toast. Auth-gated views with sign-in prompts. Gaps: no TypeScript, no form library on CreatePoll, some Tailwind v3 utilities (animate-in, slide-in-from-bottom-4) may not work in v4, no skeleton loading, no dark mode toggle, naming inconsistency (Pollify vs Polra).
Backend Architecture & API Design5 / 15
Clean MVC separation: routes→controllers→services→models with 8 REST endpoints. DTO pattern with Joi validation via BaseDto (base.dto.js:20 lines). Custom ApiError class with factory methods (api-error.js:33 lines). Centralized error handler catches ApiError, Mongoose CastError, defaults to 500. ApiResponse for consistent formatting. CORS restricted to https://my-polra.vercel.app. Helmet + global rate limiting + auth rate limiter on webhooks. Mongoose schemas with compound sparse unique indexes. Issues: dead Submission.schema.js model (never imported), No poll edit or delete endpoints, dbConnect.js catch doesn't re-throw (connection failures silently succeed), error messages leaked to clients via generic ApiError.badRequest(error.message), Socket.io CORS is wildcard "*", no MongoDB sanitization, no .env.example, no pagination beyond 100-item limit, no input sanitization beyond Joi stripUnknown.
Real-Time Updates Using WebSockets5 / 10
Socket.io room-based pub/sub: joinPoll/leavePoll events (socket.js:16-24), pollUpdated emitted on vote submission (poll.controller.js:62), liveToggle relays client option selection/deselection (socket.js:26-29). Frontend: VotePoll.jsx emits liveToggle with user info on option toggle; PollAnalytics.jsx joins room, listens for pollUpdated (triggers HTTP refetch), handles liveOptionUpdate for optimistic UI. Proper cleanup on unmount. Gaps: no socket authentication, only one server-emitted event (pollUpdated with no data payload — just a message string), no events for poll close/publish lifecycle, no reconnection room re-join logic (joinPoll not re-sent on reconnect), liveToggle optimistic updates uncoordinated with server state, Socket.io CORS wildcard "*", no debouncing on events.
Code Quality & Project Structure5 / 10
Clean monorepo structure (backend/ and frontend/). Backend: modules pattern with polls/webhooks/health separation, common/ for middleware/config/utils/dto. Frontend: pages/components/services separation. Consistent naming conventions throughout. ~960 lines backend JS, ~2,336 lines frontend JSX/JS/CSS. ESLint configured for frontend. Vercel deployment with SPA rewrites. Issues: no TypeScript anywhere, no automated tests (npm test echoes "Error: no test specified"), Submission.schema.js is dead code, .env file committed with Clerk key, no .env.example, Poll schema isPublished defaults to true but service code overrides to false (misleading schema default), QR code handling duplicated between CreatePoll.jsx and MyPolls.jsx, naming inconsistency (README says "Pollify", deployed as "Polra", repo is "polling-app"), no Prettier config.
68
PulseBoard
Sahil Thakur · @tsahil6231_3244a379
45
PulseBoard is a solid but incomplete polling app built with React, Node, PostgreSQL/Drizzle, and Clerk auth. The project has ~4,340 lines of TypeScript across 39 source files with clean monorepo organization, TypeScript, Zod validation, proper migrations, and WebSocket real-time updates. Key strengths: Well-architected backend with transaction-based operations, good input validation, responsive frontend with comprehensive loading/error/empty state coverage, dark mode, and real-time vote updates via Socket.IO. Critical gaps: The core requirement of "multiple questions per poll" is not implemented — polls have only flat options. Anonymous voting requires authentication (despite the allowAnonymous flag in the schema). No poll editing endpoint. No tests. Some function duplication across pages. Total score: 45/80. This is a well-built project that covers many requirements but misses the fundamental multi-question feature and has the anonymous voting path broken.
Authentication & Access Control6 / 10
Clerk-based auth with SignInButton/SignUpButton/UserButton in navbar (navbar.tsx:176-197), backend clerkMiddleware + requireAuth (app.ts:25, auth.routes.ts:8-9). AuthSyncProvider syncs Clerk user to backend DB via POST /auth/create-user (auth-sync-provider.tsx:63-98) with email validation. getCurrentUser helper (current-user.ts:8-24) extracts user from Clerk auth with proper 401/404 errors. Frontend BackendClient attaches Clerk JWT token to all requests (use-backend-client.ts:9-49). Critical gap: ALL voting requires auth (requireAuth on POST /:slug/vote — polls.routes.ts:20), making the allowAnonymous flag non-functional despite being in schema/db. No CSRF, rate limiting, or Helmet.
Poll Creation & Question Management4 / 15
CreatePollPage.tsx (192 lines) supports dynamic options with add/remove (min 2 enforced client-side), title, description, allowAnonymous toggle, expiration hours input. Zod validation (polls.validation.ts:16-37) enforces min 2/max 20 options, positive expiration, title max 200 chars. Slug auto-generated with slugify+timestamp (polls.controller.ts:52-55). Transaction-based creation (polls.controller.ts:61-87). MAJOR GAP: No questions concept exists — the system has flat options per poll, not multiple questions with options-per-question. No mandatory/optional question toggles (no questions to mark). No poll editing endpoint (only create/read/delete/publish). No form library used (individual useState hooks). Score 4: functional option creation but missing the core multi-question requirement.
Response Collection Flow5 / 15
Vote controller (votes.controller.ts:14-90) validates poll exists, not expired (line 27-29), status is active+unpublished (line 31-33), option exists and belongs to poll (lines 35-45). Duplicate vote prevention: unique compound index on {pollId, userId} (responses.schema.ts:42) plus explicit pre-check (lines 47-53). Atomic transaction: insert response + increment votesCount via SQL (lines 55-69). Client-side optimistic update with rollback on failure (poll-detail.tsx:134-173). Key gaps: ALL voting requires authentication (requireAuth middleware on vote route), making allowAnonymous flag useless — no path for unauthenticated voting exists. No question-level response structure (flat option model). No per-question validation (no questions exist). Score 5: solid validation and dedup but broken anonymous voting.
Analytics & Feedback Dashboard6 / 15
Three analytics surfaces: AnalyticsPage (429 lines) with stat cards (total votes, live polls, avg votes, top poll), filterable by status and metric, performance breakdown with colored bars; ResultsPage (389 lines) with per-poll option breakdowns (percent/votes toggle, sort by votes/original), leading option highlight, live WebSocket updates; HomePage (747 lines) doubles as dashboard with spotlight poll, stat cards, recent polls table, top-by-votes sidebar. All pages compute totalVotes, percentages, leading options client-side. WebSocket integration updates analytics in real-time. Missing: no per-question analytics (no questions exist), no trend/time-series data, no CSV export, no individual response viewing, N+1 hydration pattern (each poll individually fetched for vote counts). Score 6: good per-option analytics but limited by flat option model.
Frontend Experience6 / 10
6 routes via TanStack Router (router.tsx): /, /polls, /polls/create, /polls/$slug, /results, /analytics. Responsive navbar with mobile hamburger menu, active route highlighting, Clerk auth controls (navbar.tsx). Dark/light mode toggle with CSS variables + Tailwind 4 (theme-provider.tsx, index.css:1-114). All pages handle loading (spinners with Loader2), error (destructive-colored messages), and empty states (contextual CTAs). shadcn/ui Button component. Copy-to-clipboard with "Copied!" 2s feedback. Optimistic UI on vote. Gaps: no form library on create page (individual useState), no confirmation dialogs for destructive actions (delete is immediate), no skeleton loading, pages are functional but plain (responsive grid layouts, no animations beyond CSS transitions). Score 6: solid UX with comprehensive state coverage but lacks polish.
Backend Architecture & API Design7 / 15
Clean layered backend: routes→controllers with Zod validation, Drizzle ORM with PostgreSQL. Schemas well-designed: polls with status enum (draft/active/expired/published), options with votesCount, responses with unique compound index (responses.schema.ts). Two migrations: initial schema + production hardening (indexes, default fixes). Custom HttpError class (errors.ts), asyncHandler wrapper (async-handler.ts), CORS middleware with origin validation and OPTIONS preflight (middleware.ts:18-54), security headers (X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Permissions-Policy). Body size limit 1mb, x-powered-by disabled, trust proxy. Transaction-based poll creation and voting. Zod validation for all inputs. Env validation at startup (env.ts:7-35). Missing: no service layer (business logic in controllers), no rate limiting, no pagination, no request logging, no Helmet. Score 7: solid architecture with good validation and schema design.
Real-Time Updates Using WebSockets5 / 10
Socket.IO server attached to HTTP server (server.ts:11-16) with CORS wildcard. Singleton pattern via getSocketServer/setSocketServer (socket.ts). Server emits "vote-updated" on vote submission with pollSlug, optionId, and per-option vote counts (votes.controller.ts:76-84). Frontend socket connects to backendBaseUrl (use-vote-updates.ts:7-9). useAllVoteUpdates hook listens globally for "vote-updated" (lines 35-45), used by home.tsx, poll-detail.tsx, analytics.tsx, results.tsx. useVoteUpdates (per-slug filter, lines 11-33) is defined but never used. Vite proxy for /socket.io WebSocket (vite.config.ts:20-24). Gaps: no room-based pattern, no events for publish/status changes, no reconnection room re-join logic, no authentication on socket connections, only one event type. Score 5: basic but functional WebSocket with global broadcast only.
Code Quality & Project Structure6 / 10
Clean monorepo with backend/ (17 source files) and frontend/ (22 source files). TypeScript throughout with strict config (tsconfig.json). Consistent naming: PascalCase components, camelCase functions, barrel exports (global.schema.ts). Zod env validation at startup (env.ts). .env.example files for both packages. README with setup instructions and tech stack docs. Issues: no automated tests anywhere (zero test files); pollTotalVotes function duplicated 4x across home.tsx, polls.tsx, analytics.tsx, results.tsx; hydratePolls pattern duplicated 3x across pages; getOptionVotes with flexible type handling (5 different vote count field formats) is clever but suggests inconsistent API shapes; frontend .env committed to repo with actual Clerk key; no ESLint configured for backend; no root workspace/package.json. Score 6: good structure and typing but significant duplication and no tests.
69
SocketPoll
Rajarshi Chakraborty · @rajarshi2005
45
SocketPoll is a functional full-stack polling application with a well-structured Express/PostgreSQL backend and React frontend. The strongest area is the response collection flow (param-3: 7), which has 12 server-side validation checks and transactional vote submission. The poll creation (param-2: 6), analytics (param-4: 6), frontend UX (param-5: 6), and WebSocket implementation (param-7: 6) are solid but have notable gaps. Authentication (param-1: 5) and backend architecture (param-6: 5) are weaker due to security gaps (no Helmet, no rate limiting, SSL verification disabled). Code quality (param-8: 4) is dragged down by pervasive `any` usage, dead code, debug logs in production, minimal documentation, and no tests. Total: 45/100, placing slightly above the cohort median (43). The app works end-to-end and has good fundamentals, but needs security hardening, code cleanup, and polish to reach production quality.
Authentication & Access Control5 / 10
Registration (POST /api/auth/register) and login (POST /api/auth/login) with bcryptjs 10-round hashing and JWT Bearer token extraction from Authorization header (auth.middleware.ts:21-28). authenticate middleware protects routes, optionalAuthenticate for public routes. ProtectedRoute component (ProtectedRoute.tsx:1-24) redirects to /login. Gaps: no refresh token rotation, tokens in localStorage (auth.store.ts:28), no Helmet (listed as dependency but never imported in app.ts), no CSRF protection, no email verification or password reset, no rate limiting on auth endpoints, no Google OAuth (despite .env.example suggesting it), no httpOnly cookies for JWT.
Poll Creation & Question Management6 / 15
CreatePollPage.tsx (303 lines) has dynamic questions/options with add/remove, mandatory/optional toggle, anonymous/auth-required toggles, and expiry datetime picker with min-date enforcement. Server-side Zod validation (poll.validator.ts:21-40) enforces: min 5 char title, min 1 question, min 2 options per question (max 10 options, max 20 questions), duplicate question/option detection, future-date expiry. Transactional creation (poll.repository.ts:9-48) creates poll + questions + options atomically. Significant gap: questions/options cannot be edited after creation — updatePollService (poll.service.ts:44-72) only updates metadata (title, description, isAnonymous, requireAuth, expiresAt), not questions/options. No slug/QR sharing. No form library (manual useState hooks). Auto-publishes immediately after creation without draft review.
Response Collection Flow7 / 15
VotePage.tsx (247 lines) renders radio-button options, validates required questions client-side, constructs answers payload, and generates anonymousId client-side for anonymous polls. Server-side castVoteService (vote.service.ts:17-100) performs 12 validation steps: poll existence, isPublished, isClosed, expiry, auth requirement (requireAuth), anonymousId requirement, duplicate vote detection via findExistingResponseRepository, required question checking, invalid question ID detection, option-to-question ownership validation via validateOptionOwnershipRepository, then transactional submission (insert response → insert response_answers → increment option vote counts via sql template). Comprehensive UI state coverage: loading skeleton, error state, submitted success, closed/expired/draft states. Gap: anonymousId is regenerated on every page load (VotePage.tsx:80), so the same person can vote multiple times by refreshing. Also has leftover console.log debugging (VotePage.tsx:135-141).
Analytics & Feedback Dashboard6 / 15
AnalyticsPage.tsx (310 lines) shows 4 stat cards (total responses, questions, anonymous, created date), per-question option breakdowns with vote counts, percentages, and custom Progress bars sorted by votes. Publish/Close action buttons with confirm dialogs (except publish). Live WebSocket connection indicator. DashboardPage.tsx (210 lines) with poll list, status badges (Live/Draft/Closed/Expired), stats strip (Total/Live/Drafts), delete with confirm(), and copy-link. Server-side analytics: getPollResultsRepository computes per-option percentages, getVoteTimelineRepository provides daily vote buckets, getTrendingPollsRepository uses recent vote activity scoring. Empty state UI and loading skeletons handled. Gaps: no CSV/export, no individual response viewing, no trend charts (Recharts dependency is unused), no peak activity metrics, no drop-off rates, timeline data fetched but not displayed in UI, no published results public view separate from analytics.
Frontend Experience6 / 10
Clean UI with Tailwind CSS and custom design system (Card, Badge, Button, Progress, Separator components in components/ui/index.tsx). Dark/light mode via Zustand store (theme.store.ts) with localStorage persistence and prefers-color-scheme detection. Responsive layout throughout. All 6 routes handle loading (skeleton pulses), error (card with AlertCircle icon), empty state (CTA to create poll), and auth states. Navbar (Navbar.tsx:63 lines) is auth-aware and sticky with backdrop blur. Custom toast notification system (toast.tsx) with success/error/info variants and auto-dismiss. Copy-to-clipboard with toast feedback. CSS animations (fade-in, scale-in, staggered delays). Gaps: console.log left in VotePage (lines 135-141) and App.tsx (lines 102-110), Login/Register pages duplicate background decoration and logo layout, auto-publish on poll creation without user confirmation, no confirmation dialogs beyond browser confirm() for delete/close, form UX is basic (manual useState hooks, no form library), no TanStack Router or data caching.
Backend Architecture & API Design5 / 15
Layered architecture: routes → controllers → services → repositories with DTOs and Zod validators per module. Drizzle ORM with PostgreSQL (8 tables, proper foreign keys with cascade deletes, relations defined in relations.ts). ApiError class hierarchy (UnauthorizedError, NotFoundError, ForbiddenError, BadRequestError, ConflictError) with centralized errorHandler (errorHandler.ts). Winston logging configured. asyncHandler wrapper for async route error propagation. Gaps: no Helmet usage (dependency listed but never imported in app.ts; line 1-59 has no helmet import), no rate limiting on any endpoint including /vote, SSL verification disabled in production (db.config.ts:12: rejectUnauthorized: false), CORS origins hardcoded instead of using env vars (app.ts:24-25), missing indexes on foreign key columns (responses.poll_id, responses.user_id, questions.poll_id etc. — would cause sequential scans), cross-module repository imports violate service layer (analytics.service.ts imports getPollOwnerRepository directly from poll.repository), dead code in 9+ files (~190 lines including BaseDTO, ApiResponse, bcrypt.ts, http.config.ts, error.middleware.ts), unused database tables (poll_rooms, room_participants), PostgreSQL used instead of MongoDB as specified in MERN stack track.
Real-Time Updates Using WebSockets6 / 10
Socket.IO server (socket.ts:74 lines) with JWT auth middleware extracting tokens from handshake.auth, room-based pub/sub (join_poll/leave_poll events, rooms named poll:POLL_ID). Server emits 3 event types: vote_update (fetches full analytics from DB including totalVotes + per-question results), poll_closed, and poll_published (poll.event.ts:50 lines). Client-side: getSocket() singleton (socket.ts:29 lines), joinPollRoom/leavePollRoom helpers. AnalyticsPage joins poll room on mount, listens for vote_update to set results/totalVotes state, poll_closed updates isClosed, and shows live connection indicator (Wifi/WifiOff). emitVoteUpdate pushes full data (not just a signal), which triggers silent UI update. Gaps: VotePage does NOT use WebSocket at all (no join_poll, no live updates for voters), no reconnection room re-join logic (socket reconnects silently drop subscriptions), socket connection in App.tsx only on authenticated user (blocks anonymous socket usage), hardcoded WebSocket URL (socket.ts:7), console.error used instead of Winston in poll.event.ts, no debouncing on client-side vote_update handler, analytics page has duplicate socket.on handlers due to missing cleanup of old listeners on re-render.
Code Quality & Project Structure4 / 10
Monorepo with Client/Server separation and TypeScript throughout (strict mode on server). Clean folder structure with module-based organization (auth/poll/vote/analytics modules). Zustand stores are focused and small (2 stores: auth, theme). Custom component library with reusable Card/Badge/Button/Progress components. Gaps are significant: ~18 `any` usages in server code across poll.repository.ts, analytics.repository.ts, vote.service.ts; `Request | any` anti-pattern defeats type checking in 4 controller files; duplicated axios clients (axios.ts duplicates api.ts with fewer features); dead code across 9+ files (~190 lines including BaseDTO, ApiResponse, bcrypt.ts, http.config.ts, error.middleware.ts); console.log/console.error in 9+ production code locations (poll.controller.ts, poll.service.ts, poll.event.ts, errorHandler.ts, VotePage.tsx, App.tsx); root README is 1 line ("Its a Polling Application Uses WebSocket"); no automated tests; no ESLint on client (only Prettier); package name mismatch ("Pulseboard-Server" in server/package.json vs "SocketPoll" elsewhere); typo in filename (room-participents.schema.ts); JWT secrets in .env.example are weak placeholders; .env.example has 8 unused variables (REDIS_URL, GOOGLE_CLIENT_ID, etc.) suggesting features that were planned but never built.
70
online-polls
Vishal Bisht · @bishtv773_aea963f9
45
Online-polls is a functional full-stack polling application with React/Tailwind frontend and Express/Prisma/PostgreSQL backend. The submission covers all core requirements: JWT authentication with anonymous/authenticated poll modes, dynamic poll creation with multiple questions and options, thorough response validation chain, analytics dashboard with per-question/per-option breakdowns, real-time updates via Socket.io room-based broadcasting, and results publishing. The frontend has good state coverage (loading/error/empty/submitted/expired/published) across pages. However, quality is hampered by 4 empty stub files indicating incomplete implementation areas, dead code (ApiError, asyncHandler), duplicated patterns (error status mapping, ownership verification), no TypeScript or tests, and the backend being unreachable (Render free tier likely spun down). The frontend is deployed and accessible on Vercel. Score: 45/100.
Authentication & Access Control6 / 10
JWT-based auth with registration/login/logout, bcrypt (10 rounds), httpOnly/secure/sameSite cookies, dual token extraction (cookie + Bearer header), required and optional auth middleware, auth context with session restore via GET /me. Anonymous vs authenticated poll modes enforced server-side in response.service.js:121. Frontend ProtectedRoute with loading/redirect states. Gaps: auth.service.js is empty (all logic in controller), no Helmet, no CSRF protection, no rate limiting, no email verification or password reset, no refresh token rotation, JWT_SECRET has no startup validation.
Poll Creation & Question Management6 / 15
Dynamic poll creation form with add/remove questions (min 1) and options per question (min 2 enforced). Mandatory/optional toggle, anonymous/authenticated mode selector, expiry datetime input. Slug auto-generation with uniqueness suffix (poll.service.js:40-47). Server-side Zod validation (createPollSchema: min 1 question, min 2 options each). Single Prisma transaction for nested create. Significant gap: questions and options cannot be edited after creation — updatePoll only changes metadata (title, description, isAnonymous, expiresAt). No form library (raw useState), key-based rendering uses positional indices.
Response Collection Flow7 / 15
Thorough server-side validation chain: poll existence → publication check → expiry check → auth requirement for non-anonymous → duplicate question detection → mandatory question enforcement with question text in error → question/option existence validation → duplicate response prevention for authenticated users (response.service.js:98-189). Client-side mandatory check (PollPage.jsx:50-54). Multiple UI states: submitted, expired, login-required, not-accepting, published results, loading, error. Gap: no submit button loading state enables double clicks, anonymous users can submit multiple responses (database @@unique can't handle null respondentId), no localStorage/IP-based anonymous duplicate prevention.
Analytics & Feedback Dashboard6 / 15
Analytics computes per-question answer/skipped counts, per-option votes with percentages, total responses, completion rate, authenticated vs anonymous breakdown, mandatory/optional counts, and latest response timestamp (response.service.js:24-88). Dashboard shows poll list with response counts. Results publishing with public URL. Public results view via socket push on publish. MetricCard and ResultBar components with CSS transitions. Gaps: all analytics computed in-memory by iterating all responses (no database aggregation), no CSV/export, no individual response viewing in UI, no trend/time-series data, Dashboard silently swallows fetch errors (Dashboard.jsx:17-23 has no .catch), Home page uses hardcoded fake stats.
Frontend Experience5 / 10
7 pages with 8 routes, consistent Tailwind CSS styling, responsive layout (sm:/lg: breakpoints), loading/error/empty states in most pages. PollPage handles 6 distinct view states. Consistent button/card styling. CSS transitions on interactive elements. Gaps: no mobile hamburger menu (Navbar always inline), no entrance/page-transition animations, Dashboard silently swallows fetch errors, no submit loading state on PollPage, unused boilerplate assets (hero.png, react.svg, etc.), HTML title is still "frontend" (Vite default), Login/Register pages ~70% duplicated, Home page shows hardcoded fake stats, no dark mode, no confirmation dialogs for destructive actions.
Backend Architecture & API Design5 / 15
MVC pattern for poll/response modules. Prisma schema has 6 well-normalized models with cascade deletes, unique constraints, and foreign key indexes. Zod validation middleware with 6 schemas. Dual token extraction, environment-aware cookies, CORS whitelist. 16 API endpoints with health check and 404 handler. Gaps: auth.service.js is empty (all auth logic in controller), error.middleware.js is empty (no centralized error handler), ApiError class defined but never used, asyncHandler defined but never used, string-matching error status mapping is fragile (duplicated across controllers), ownership verification duplicated 3 times, no Helmet/rate limiting/body size limits, console.error logging only, raw error messages leaked to clients, publishPollSchema defined but not wired, poll.socket.js is empty, include patterns duplicated across services. Uses PostgreSQL/Prisma, not MongoDB as track spec mentions.
Real-Time Updates Using WebSockets6 / 10
Socket.io server with CORS, room-based pattern (poll-{pollId}), emitPollUpdate helper (socket.js:32-39). Server emits poll-published on publish (poll.controller.js:121) and response-update on submission (response.controller.js:39), both with full analytics payload. Client joins/leaves rooms on mount/unmount with proper cleanup (PollPage.jsx:31-44, Analytics.jsx:44-56). autoConnect:false with explicit lifecycle. Analytics listens for both events and updates state live. Gaps: no socket authentication (any client can join any room), no reconnect room re-join logic, poll.socket.js handler file is empty, no typed events, no debouncing on client updates, full analytics payload pushed over socket instead of lightweight invalidation signal.
Code Quality & Project Structure4 / 10
Clean frontend/backend separation with component/pages/context/hooks organization. Consistent naming conventions, .env.example files, ESLint for frontend, README with setup instructions. However: 4 empty stub files (auth.service.js, error.middleware.js, poll.socket.js, config/db.js). Dead code elsewhere: ApiError class, asyncHandler wrapper, refreshUser function all defined but never used. Error status mapping duplicated across poll and response controllers. Ownership verification pattern duplicated 3 times in poll.service.js. Login/Register pages ~70% duplicated. axios baseURL has double /api bug. No TypeScript (plain JS), no automated tests, console.log/error in production. Unused boilerplate assets (hero.png, react.svg, vite.svg, icons.svg). HTML title is "frontend". Frontend/README.md is Vite starter template. Hardcoded fake data on Home page.
71
pulseboard-live-polls
Rajesh Kumar · @rajesh_kumar71
45
PulseBoard is a functional MERN polling platform with custom JWT authentication, dynamic poll creation with client/server validation, thorough response collection with defense-in-depth validation, basic analytics with progress bars, Socket.IO real-time updates, and a consistent dark-themed custom CSS design. No deployment URL is provided. The code is clean and well-organized but lacks TypeScript, tests, rate limiting, poll editing/deletion, and has some code duplication. Overall score: 45/100, landing near the median of the cohort (43.0).
Authentication & Access Control6 / 10
Custom JWT auth with registration and login (authController.js:18-93), bcryptjs 12 rounds (User.js:38), protect/optionalProtect middleware with Bearer token extraction (authMiddleware.js:4-58), AuthContext with localStorage persistence and Axios interceptor (AuthContext.jsx:10-31, axios.js:10-18), anonymous vs authenticated poll mode enforced server-side (responseController.js:73-77), duplicate prevention via unique compound index {poll, respondentUser} with partial filter (Response.js:47-55), ProtectedRoute with auth check and loading state (ProtectedRoute.jsx). Gaps: token in localStorage (XSS vulnerable, acknowledged in README), no refresh token rotation, no CSRF protection, no rate limiting on auth endpoints, no email verification or password reset.
Poll Creation & Question Management6 / 15
CreatePoll.jsx (399 lines) implements dynamic questions/options with add/remove, mandatory/optional checkbox toggle per question, response mode selector (anonymous/authenticated), datetime-local expiry picker, and client-side validation with specific error messages (CreatePoll.jsx:130-158). Server-side validatePollPayload (pollController.js:31-71) validates title, response mode enum, future expiry, min 1 question, min 2 options per question, non-empty option text. Success state shows created poll with public link and copy-to-clipboard. Gaps: no poll editing/update endpoint exists, no poll deletion, no draft status support, no form library (individual useState hooks), no slug collision retry.
Response Collection Flow7 / 15
Defense-in-depth validation in responseController.js: poll existence check (line 57-63), expiry enforcement via poll.isExpired() + status check (lines 66-71), authenticated mode enforcement (lines 73-77), duplicate submission prevention via DB query + unique compound index (lines 80-92, Response.js:47-55), comprehensive answer validation checking required questions, option ownership per question, and duplicate answers (validateAnswers, lines 6-41), answer cleaning to filter invalid questionIds (cleanAnswers, lines 43-54). Client-side required question validation (PublicPoll.jsx:75-83). UI handles published/expired/auth-required/submitted states. Gaps: no duplicate prevention for anonymous users (no IP/session/cookie tracking), no rate limiting on submission endpoint.
Analytics & Feedback Dashboard6 / 15
Analytics computed in buildPollAnalytics (pollController.js:163-231) covering: total responses, per-question option counts with percentages, answered/skipped counts, anonymous vs authenticated participation breakdown. Result publishing via PATCH /api/polls/:pollId/publish (pollController.js:254-283) with public results endpoint checking isPublished gate (pollController.js:285-308). PollAnalytics.jsx (251 lines) renders stat cards, per-question progress bars with vote counts, and publish button. PublicResults.jsx (92 lines) shows published results to visitors. Gaps: analytics computed via O(n*m) brute-force iteration over all responses (no MongoDB aggregation pipeline), no CSV/PDF export, no trend/time-series data, no peak activity metrics, no individual response viewing, no response counts on dashboard poll list.
Frontend Experience5 / 10
Custom CSS design system (index.css, 688 lines) with dark theme, purple/cyan gradient accents, responsive breakpoints at 760px, consistent card-based layout. All 8 routes handle loading, error, empty, and auth states explicitly (loading text, errorMessage display, empty poll CTA, login-required prompt). Features copy-to-clipboard, status pills with color coding, progress bars for analytics, live update indicator pill. Gaps: HTML title is "client" (unset), no animations/transitions beyond hover transform, no loading skeletons (just text), no component library, no confirmation dialogs, no toast notifications, no dark/light mode, no favicon customization, home page is minimal static landing.
Backend Architecture & API Design5 / 15
Clean MVC architecture with controllers/route separation (app.js:30-32), Mongoose schemas with built-in validation (minlength/maxlength, custom validators for min options/questions), bcrypt pre-save hook with password select:false (User.js:33-43), unique compound index with partial filter for duplicate prevention (Response.js:47-55), security middleware (Helmet, CORS with credentials, JSON body limit 100kb), differentiated HTTP status codes (400/401/403/404/409), error middleware with env-aware messaging (errorMiddleware.js), .env.example files for both packages. Gaps: no input validation library (manual validation only), no rate limiting anywhere, no express-mongo-sanitize, no centralized async error wrapper, no poll edit/delete endpoints, no pagination on getMyPolls, no request logging, no graceful shutdown.
Real-Time Updates Using WebSockets5 / 10
Socket.IO server attached to HTTP server (server.js:15-22) with room-based pattern: clients join poll:{pollId} rooms via "poll:join" event (socketServer.js:6-13). Server emits two events on response submission: "poll:response-submitted" with totalResponses and "poll:analytics-updated" with full analytics payload (responseController.js:119-127). PublicPoll.jsx listens for live response count (lines 46-66), PollAnalytics.jsx listens for full analytics object to replace state (lines 36-56). Client cleanup properly disconnects on unmount. Gaps: no reconnect room re-join logic, no socket authentication, no server-side room cleanup on disconnect (handler empty at socketServer.js:15), no events for poll publish/delete, no debouncing of analytics updates, no typed Socket.IO events.
Code Quality & Project Structure5 / 10
Clean monorepo with client/server separation, organized by function (controllers/models/routes/middleware/sockets/utils for server, api/components/context/pages/sockets for client). Consistent naming conventions throughout. Comprehensive README with setup instructions, API documentation, security notes, and deployment plan. .gitignore properly configured. .env.example files for both packages. Gaps: no TypeScript (plain JS), no automated tests, ESLint on client only (not server), some code duplication (analytics layout duplicated between PollAnalytics.jsx and PublicResults.jsx, token extraction logic duplicated in protect and optionalProtect middleware), HTML title is "client", console.log in server.js:26 and db.js:13, errorHandler uses res.statusCode as fallback (errorMiddleware.js:8).
72
NM Polling
Nikhil Mali · @nikhilmali3141_5257a2d1
44
NM Polling is a functional full-stack polling platform (MERN + Socket.IO) deployed on Vercel (frontend) and Render (backend). The backend has solid JWT auth with refresh token rotation, account lockout, and comprehensive Mongoose schema design with good indexes. Response collection has triple-layer duplicate prevention (user, device, IP). Socket.IO provides real-time updates on the public poll page. The frontend features polished custom CSS with dark/light mode and responsive design. However, the project is undermined by ~300+ lines of completely unused Zod DTOs and aggregation pipeline methods (dead code), a dead app.js file, no TypeScript or tests, significant gaps in analytics (no trends/export), no Socket.IO on the dashboard for creators, a folder name typo ("Fronted"), and monolithic components. Score: 44/100 — a working prototype with solid backend models but significant unfinished aspects and engineering quality issues.
Authentication & Access Control6 / 10
Solid JWT dual-token auth (15-min access + 7-day refresh) with cryptographic rotation stored in MongoDB user documents (user.controller.js:137-140). bcryptjs 10-round hashing, account lockout after 5 failed attempts with 15-min window (user.controller.js:82-86), httpOnly/secure/sameSite cookies in production. protect and optionalAuth middleware (auth.js) check token, user status, and password-changed-after. However, ~107 lines of Zod DTOs (user.dto.js) are NEVER imported by any controller — all validation is manual ad-hoc checks. Tokens also stored in localStorage (authStorage.js) creating XSS exposure. No rate limiting, no CSRF, email verification/password reset exist as schema infrastructure only with no working endpoints.
Poll Creation & Question Management6 / 15
Working poll builder (DashboardPage.jsx PollBuilder, ~160 lines) with dynamic questions (add/remove), dynamic options per question (add/remove, min 2 enforced), mandatory/optional toggle, anonymous/authenticated toggle, expiry datetime picker. Server-side validatePollQuestions() (poll.controller.js:23-52) validates question text, min 2 options per question, option text. PollLink.createLink() with collision avoidance (5 attempts). But ~220 lines of Zod poll DTOs (poll.dto.js) are completely unused dead code. No poll editing endpoint exists (PATCH only for publish/close, not modifying questions/options). Pure imperative React forms without a form library, no slug/permalink, no QR code.
Response Collection Flow7 / 15
Thorough response validation: poll status gate (poll.controller.js:313-314), anonymous/authenticated mode enforcement (line 317-318), answers array validation, question existence check, duplicate question answer prevention, mandatory question enforcement, option-belonging-to-question validation (lines 354-361). Triple-layer duplicate vote prevention: authenticated users via unique compound index {pollId, userId, questionId}, deviceId via partial unique index, IP via sparse index, with explicit pre-checks for all three (lines 363-375). Expiry auto-applies on poll fetch. Frontend disables form after submission. Gaps: no transaction wrapping for the for-loop response inserts (partial failure possible mid-submission), Zod DTOs unused, no client-side mandatory validation before enabling submit.
Analytics & Feedback Dashboard5 / 15
Dashboard shows 5 stat cards (Total, Active, Responses, Published, Top Choice %), CSS conic-gradient donut ring for leading option (PollResults.jsx:58-64), per-question option bar charts with percentages and animated fills (ResultQuestion.jsx). Backend computes percentages in buildPollView() (poll.controller.js:68-72). Per-question vote totals displayed. PollList in sidebar shows response counts per poll. However: Response model's aggregation pipelines (getPollStatistics, getOptionStatistics — ~50 lines) are never called from any controller. No time-series/trend data, no response velocity, no peak activity, no CSV/export, no individual response viewing, no anonymous vs authenticated breakdown. totalResponses on Poll incremented once per submission (not per answer). Frontend stats computed client-side in getDashboardStats() instead of using backend aggregations.
Frontend Experience6 / 10
Polished custom CSS (1570 lines, styles.css) with CSS variable-based light/dark theming and localStorage persistence. Three pages: Landing (hero, flow tabs, feature cards, CTA), Dashboard (sidebar, stats, builder, results), PublicPoll (vote + results side-by-side). Responsive breakpoints at 1120px and 720px. State handling present: loading (text "Loading poll"), empty (sidebar "Your polls will appear here"), error (react-hot-toast with custom styling), auth-gated (modal overlay with backdrop blur), published/unpublished results toggle. CSS animations (slideUp, floatPanel, fadeIn), prefers-reduced-motion support. Gaps: no routing library (manual pathname regex), no form library (imperative useState), DashboardPage.jsx is monolithic at 653 lines, no loading spinners/skeletons, no confirmation dialogs for destructive actions, no offline/PWA support, folder name typo "Fronted".
Backend Architecture & API Design5 / 15
Clean modular structure: modules/User, Poll, Question, Option, Response, PollLink with common middleware and utils. Good Mongoose schema design with indexes, instance methods, static methods. helmet, cors, cookie-parser, express.json with 100kb limit. PollLink separate model with token collision avoidance. RESTful API design. However: ~300 lines of Zod DTOs across user.dto.js and poll.dto.js are completely unused in any controller — all validation is manual ad-hoc. Backend/src/app.js is dead code (unused Express app creation). No service layer — all logic in 433-line poll.controller.js. No rate limiting, no pagination on GET /api/polls, no DELETE endpoint, inconsistent error handling (some use error.statusCode, others plain 500), console.log in production (server.js:73-90), no .env.example, no centralized async error wrapper.
Real-Time Updates Using WebSockets5 / 10
Socket.IO integrated with Express HTTP server (server.js:19-29). Room-based pub/sub with `poll-{token}` rooms. Server emits poll_results_updated (on submission), poll_published_notification (on publish), poll_closed (on close) with full poll data via buildPollView. Client (PublicPollPage.jsx:29-38) connects, joins room, listens for all three events and updates state, cleans up on unmount (leave room + disconnect). However: no Socket.IO integration on DashboardPage — creators don't see live analytics updates. Socket created via useMemo but disconnect() in cleanup permanently destroys connection rather than just leaving room. No authentication on socket connections, no debounce/throttle, no typed events, no reconnection room re-join logic, only console.log for socket errors/disconnects.
Code Quality & Project Structure4 / 10
Clean module-based structure with consistent ES modules and API response format {success, error, data}. Good README with API overview, env vars, setup instructions. Mongoose models well-commented. But significant quality issues: ~300 lines of Zod DTOs and aggregation pipeline methods that are never imported by any controller — substantial dead code. Backend/src/app.js (12 lines) is dead code. No TypeScript, no automated tests, no ESLint/Prettier. Folder typo "Fronted" instead of "Frontend". .gitignore references wrong directory names (backend/frontend vs actual Backend/Fronted). DashboardPage.jsx monolithic at 653 lines with embedded sub-components. No .env.example. console.log in production code. Backend package.json depends on "http" npm package unnecessarily. demoPoll has hardcoded localhost URL.
73
PULSEPOLL: Pooling web App
Abhishek Yadav · @2516abhi43_22f08265
44
PULSEPOLL is a full-stack polling platform with a visually polished futuristic dark UI. The project delivers on all core requirements: JWT authentication with anonymous/authenticated modes, dynamic poll creation with multi-question support, step-by-step response collection with duplicate prevention, analytics dashboard with bar charts and real-time Socket.IO updates, and publish results flow. The backend has a clean MVC structure with Mongoose schemas and express-validator. However, the project has significant gaps across all criteria: no TypeScript, hardcoded JWT secret fallbacks, wildcard CORS, no rate limiting, no Helmet, leftover Vite boilerplate, no tests, and several validation gaps in response submission. The Socket.IO implementation is functional with room-based pub/sub and data pushing but lacks reconnection room re-join. Total score: 44/100, slightly above the median (43) for this cohort.
Authentication & Access Control5 / 10
JWT auth with bcryptjs (10 rounds) in User model (User.js:31-38), register/login endpoints with express-validator (authRoutes.js:10-28), protect and optionalAuth middleware (auth.js:5-52), AuthContext with login/register/logout + localStorage token persistence (AuthContext.jsx:122 lines), ProtectedRoute with redirect-to-login with return-to state (ProtectedRoute.jsx:23 lines), anonymous vs authenticated poll mode enforced server-side (responseController.js:32-35) and shown in UI (PublicPoll.jsx:296-326). Critical gaps: JWT secret hardcoded as fallback in 2 files (auth.js:17, authController.js:6), no refresh token mechanism, token stored in localStorage (XSS-vulnerable), no CSRF/Helmet, no email verification or password reset, no rate limiting on auth endpoints, CORS wildcard.
Poll Creation & Question Management6 / 15
CreateEditPoll.jsx (537 lines) supports dynamic questions with add/remove via AnimatePresence, dynamic options per question with add/remove (min 2 enforced client-side), per-question mandatory/optional toggle, title/description/expiry datetime-local picker, anonymous/authenticated toggle, and draft vs publish mode. Server validates via express-validator (pollRoutes.js:20-29). Edit mode restores existing poll data (CreateEditPoll.jsx:53-102). Backend supports full CRUD + duplicate (pollController.js:154-191). Gaps: no React Hook Form on the create form (uses individual useState hooks), no slug/permalinks, no QR code, no option reordering, no question reordering, no client-side Zod validation on the create form.
Response Collection Flow6 / 15
PublicPoll.jsx (553 lines) has step-by-step voting UI with progress bar (Framer Motion animated), per-question mandatory validation (PublicPoll.jsx:149-155), countdown timer (lines 111-135), and comprehensive UI states (loading, error, expired, needsAuth, submitted/success). Server-side: expiry check via virtual isExpired (responseController.js:26-29), anonymous/auth access control (lines 32-35), mandatory question validation (lines 53-59), and duplicate prevention via unique compound index {poll, voterToken} + explicit findOne (Response.js:42, responseController.js:42-50). LocalStorage duplicate tracking (PublicPoll.jsx:54-57, 196-197). Gaps: no server-side validation that submitted optionIds/questionIds belong to the poll, no per-question single-option enforcement (can submit multiple answers to same question), no transaction for response, voterToken in localStorage is bypassable.
Analytics & Feedback Dashboard6 / 15
Analytics page (461 lines, Analytics.jsx) with Recharts BarChart per question, per-option percentage breakdown with colored animated bars, leading option highlight, stat cards (total responses, questions count), and real-time updates via Socket.IO (Analytics.jsx:91-122). Server uses MongoDB aggregation pipeline to compute option-wise vote counts (pollController.js:251-263), with access control for creator or published polls (lines 242-245). publishResults endpoint toggles isPublished with Socket.io broadcast (pollController.js:196-227). Dashboard shows response counts per poll (Dashboard.jsx:45-49). Gaps: percentage calculated as votes/totalResponses (flawed for multi-question polls where each question has different response counts), no time-series/trend data, no CSV export, no individual response viewing, no anonymous vs authenticated breakdown, N+1 query pattern in getMyPolls.
Frontend Experience5 / 10
Consistent dark futuristic theme with Tailwind CSS custom colors (tailwind.config.js), glassmorphism panels (index.css:31-48), Framer Motion animations throughout (page transitions, AnimatePresence, hover effects), floating animated background (AnimatedBackground.jsx), custom Loader component, toast notification system (AuthContext.jsx:105-110), responsive layout (mobile to desktop), landing page with interactive demo poll. All pages handle loading, error, and empty states. Gaps: App.css (185 lines) is leftover Vite boilerplate CSS completely unused, unused react.svg/vite.svg assets, no dark/light mode toggle, no TypeScript, create form uses individual useState hooks instead of React Hook Form, landing page testimonials are hardcoded fake data, no PWA/offline support, overwrought UI copy hurts usability.
Backend Architecture & API Design5 / 15
MVC structure with routes→controllers→models separation. Mongoose schemas with embedded subdocuments (Poll.js:82 lines), express-validator middleware with custom validate wrapper (validate.js:26 lines), protect/optionalAuth middleware, centralized error handler handling CastError/duplicate key/ValidationError (error.js:30 lines), MongoDB aggregation pipeline for analytics, Socket.IO integrated with HTTP server. Gaps: no service layer (all logic in controllers), CORS set to wildcard origin '*' (server.js:24), JWT secret hardcoded as fallback in 2 files (auth.js:17, authController.js:6), MONGODB_URI hardcoded fallback (db.js:5), no rate limiting anywhere, no Helmet security headers, no request body size limits, dotenv.config() after imports (potential race condition), no pagination on getMyPolls, no graceful shutdown, single JWT with no refresh token mechanism, error stack traces leaked in development.
Real-Time Updates Using WebSockets6 / 10
Socket.IO server integrated with HTTP server (server.js:28), room-based pub/sub with joinPollRoom/leavePollRoom events (socket.js:21-45), live viewer count tracking per room with liveViewersUpdate events. Server pushes computed analytics data (not just invalidation signals) via newResponse event with totalResponses and full analytics array computed via aggregation pipeline (responseController.js:74-119). Client SocketContext provides singleton connection with reconnection config (SocketContext.jsx:69 lines). Analytics page and PublicPoll page listen for newResponse, pollPublished, liveViewersUpdate with proper cleanup (Analytics.jsx:91-122, PublicPoll.jsx:87-108). Navbar shows live connection indicator (Navbar.jsx:32-35). Gaps: no reconnection room re-join logic (socket reconnects silently lose subscriptions), full aggregation recomputation on every response is inefficient, no socket authentication, no debouncing on client, no events for poll deletion or edit.
Code Quality & Project Structure5 / 10
Clean monorepo structure (client/ and server/ separation), consistent naming conventions (pages, components, context, services, controllers, models, middleware, routes), React Context pattern for auth/socket state management, custom hooks (useAuth, useSocket), eslint.config.js on client. Gaps: no TypeScript anywhere (all .js/.jsx), no automated tests (zero test files or test scripts), App.css is leftover Vite boilerplate (185 lines of unused CSS), unused assets (react.svg, vite.svg), .env file committed to repo, no .env.example for client, server has no dev script (same "node server.js" for start and dev), JWT secret fallback duplicated in 2 files, no nodemon/dev tooling on server, no barrel exports, components are large (Analytics.jsx: 461 lines, PublicPoll.jsx: 553 lines, CreateEditPoll.jsx: 537 lines without sub-component extraction), fake testimonials in data, no README at server level.
74
Pollify
Yashika Agrawal · @yashika29
44
Pollify is a functional full-stack polling platform with React 19 + Vite frontend and Express 5 + Prisma + PostgreSQL backend. Implements JWT auth with refresh tokens, dynamic multi-question poll creation, response collection with server-side validation, analytics with Recharts pie charts, Socket.io real-time notifications, and public result publishing. The frontend has a polished dark UI with comprehensive state coverage across all pages. Key limitations: backend appears down on Render, no poll editing/deleting, no anonymous duplicate prevention, no TypeScript or tests, and minimal Socket.io integration (single notification event, no data pushed). The codebase is organized and readable but lacks production hardening.
Authentication & Access Control6 / 10
Complete JWT auth with access (15-min) and refresh (7-day httpOnly cookie) tokens (generateToken.js:1-17), bcryptjs 10-round hashing (auth.controller.js:115), registration/login/me/refresh/logout endpoints. protect middleware (auth.middleware.js:17-40) and optionalProtect for anonymous/authenticated modes. Anonymous vs authenticated poll mode enforced server-side (poll.controller.js:146-165) with duplicate submission check for authenticated users (lines 154-165). AuthContext with loading state, auto-refresh on 401 (api.js:36-48), and ProtectedRoute component. Gaps: no CSRF protection, JWT stored in localStorage, CORS reflects any origin (index.js:15-16), no rate limiting, no Helmet, no email verification or password reset.
Poll Creation & Question Management6 / 15
CreatePoll.jsx (318 lines) supports dynamic question/option add/remove, mandatory/optional toggle per question, anonymous/authenticated toggle, and expiry time input with client-side validation. Server validates title, questions array (min 1), min 2 options per question with trimmed text, isAnonymous field, and expiry time > 0 (poll.controller.js:8-41). Poll link generated via nanoid(10). Success banner with copy-to-clipboard and navigation to poll/analytics/publish. Critical gap: no poll editing/deleting endpoints exist — polls are immutable after creation. No slug-based sharing, no draft status, no form library (raw useState handlers).
Response Collection Flow6 / 15
PollPage.jsx (212 lines) renders radio options per question with client-side mandatory check before submit. Server-side submitPoll (poll.controller.js:113-231) validates: poll exists, not expired, anonymous/authenticated mode enforcement, per-question option ownership checking (lines 183-204), mandatory question completion (lines 167-180), and duplicate prevention for authenticated users (lines 154-165). Response batch inserted via createMany. Expiry checked on both fetch and submit. UI handles loading, not-found, already-submitted, and submitted states. Gaps: no anonymous duplicate prevention (no localStorage/cookie/session tracking), no rate limiting on submissions, no transaction wrapping for response insertion.
Analytics & Feedback Dashboard5 / 15
Analytics endpoint (poll.controller.js:389-454) computes per-question option votes/percentages and total unique participants. Analytics.jsx (196 lines) renders Recharts donut pie charts per question with animated progress bars, participant count stat card, and live status indicator. Dashboard.jsx lists all user polls with live/ended/published badges and response counts. ResultsPage.jsx (181 lines) shows per-question vote breakdown with winner detection ("Top" badge) and progress bars, publicly accessible after publishing. PublishPoll.jsx restricts publishing to creator and enforces expiry-first. Gaps: no trend/time-series data, no CSV export, no individual response viewing, no anonymous/authenticated breakdown, fixed 4-color palette, simple vote/totalParticipants percentage (should use per-question voter count).
Frontend Experience7 / 10
Consistent dark theme across all 9 routes with custom fonts (Syne, DM Sans), gradient text, grid background, and amber accent color. Every page handles loading (spinners with descriptive text), error (toast + inline banners), empty (Dashboard CTA), and edge states (poll not-found, already-submitted, results-unavailable). Protected routes show loading spinner and redirect to login. Landing page has hero with animated pulse indicator, features grid, how-it-works steps, and CTA banner. Custom styled checkboxes, radio buttons, and status badges. Responsive layout with mobile hamburger menu. Uses react-hot-toast for notifications and lucide-react icons. Gaps: no form library (raw useState for all form fields), no confirmation dialogs, no dark/light toggle, some route duplication (/poll/ and /polls/ prefixes).
Backend Architecture & API Design5 / 15
Clean MVC structure: routes→controllers→Prisma with centralized error handler (AppError class + error.middleware.js) and standardized ApiResponse format. Prisma schema with 5 properly-related tables (User, Poll, Question, Option, Response) and 2 tracked migrations. Auth middleware with protect/optionalProtect separation. JWT access+refresh token strategy. 7 REST endpoints. Gaps: no Helmet or security headers, no rate limiting, no request body size limits, no input validation library (manual ad-hoc validation only), no poll delete/edit endpoints, no pagination on getMyPolls, CORS set to reflect any origin, raw console.log for errors, Express 5 bleeding edge, no graceful shutdown, no database service layer.
Real-Time Updates Using WebSockets4 / 10
Socket.io server integrated with HTTP server (index.js:33-49), setIo/getIo singleton pattern for sharing io instance (socket.js:1-14). Client joins room by pollLink on Analytics page mount (Analytics.jsx:33). Server emits "pollUpdated" to room on response submission (poll.controller.js:222-226). Client listens for event and refetches analytics data, cleans up on unmount (Analytics.jsx:34-35). Gaps: only 1 event type and it's a pure notification signal (no data pushed, client refetches HTTP), no socket integration on poll-answering page for live participant count, no reconnection room re-join logic, no debouncing on client listener, no socket authentication, no broadcast on publish/delete actions.
Code Quality & Project Structure5 / 10
Clean monorepo structure with backend/ and frontend/ separation. Backend organized into controllers/, middlewares/, routes/, prisma/, utils/. Frontend has pages/, components/, context/, utils/. Consistent naming conventions and file organization. README with features, setup, and deployment instructions. docker-compose.yaml for local PostgreSQL, .env.example with required vars, ESLint configured for frontend. Gaps: no TypeScript anywhere, zero automated tests (backend test script is "echo 'Error: no test specified'"), JSON test fixture files in src/utils/ (poll.json, response.json, results.json — dead code), no service layer separation, console.log left in production, route duplication in App.jsx, no backend ESLint, some copy-pasted fetch patterns across pages.
75
Polling Heroes
Ved Pandey · @ved_is_dev
44
Polling Heroes is a functional full-stack polling platform built with React/TypeScript frontend and Express/PostgreSQL/Drizzle backend. The project demonstrates solid foundational engineering with clean architecture, TypeScript throughout, Zod validation, and proper React patterns. Key strengths: well-structured modular codebase, consistent dark UI with good state coverage, transaction-based poll creation and response submission, proper database schema with foreign keys and partial unique indexes, working email verification flow, Socket.io real-time updates, and result publishing with proper authorization gates. Key weaknesses: no security hardening (no Helmet, rate limiting, or CSRF protection), polls cannot be edited after creation (questions/options are immutable), anonymous vote prevention is trivially bypassed via localStorage, no test coverage, N+1 query performance issues in analytics, no export functionality, socket implementation lacks authentication and reconnection handling, and several production-readiness gaps (no graceful shutdown, no CI/CD, raw SQL type assertions). Overall score: 44/95 (46%). This is a solid "working prototype" level submission — the core flows work end-to-end with decent quality, but it lacks the depth and hardening expected of a production-grade application.
Authentication & Access Control5 / 10
Registration with firstName/lastName/email/password + bcryptjs (10 rounds) + email verification (crypto.randomBytes token, 24h expiry). Login returns 7-day JWT in httpOnly/secure/sameSite cookie. requireAuth middleware extracts JWT from cookie, fetches user on every request (auth.middleware.ts:21-55). optionalAuth gracefully handles mixed auth/anonymous endpoints (lines 57-86). Anonymous vs Authenticated poll modes enforced server-side (response.service.ts:25-36). Frontend AuthProvider with login/register/logout/refreshUser + 401 interceptor via custom events. Gaps: no Helmet, no rate limiting on auth endpoints, no refresh token (single 7-day JWT with no revocation), unverified users get full JWT immediately, no password reset flow, anonymous sessionToken is client-forged localStorage UUID (trivially bypassed), bcryptjs 10 rounds (low), no CSRF token.
Poll Creation & Question Management5 / 15
CreatePollPage.tsx (250 lines) uses React Hook Form with nested useFieldArray for dynamic questions/options, mandatory/optional toggle per question, Anonymous/Authenticated participant type selector, optional datetime-local expiry input. Server-side Zod validation (poll.schema.ts) enforces title, description max 10k, min 1 question, min 2 options per question, text lengths. Transaction-based creation with atomic question+option inserts and unique 8-char hex slug with 20-attempt collision retry (poll.service.ts:49-90). Dashboard shows polls with submissionCount via correlated subquery. CRUD: Create/Read/Update(metadata only)/Delete/End/Publish. Gaps: questions/options are immutable after creation (updatePoll only patches title/desc/expiresAt/participantType, poll.schema.ts:23-28), no edit poll UI page, N+1 query in getPollQuestionsAndOptions (poll.service.ts:131-139), no pagination/filtering on dashboard listing, no draft status, no custom slug, client-server validation mismatch (question text max 5000, option text max 2000 on server but unbounded on client), dashboard listing doesn't call checkAndExpirePoll so expired polls show as Active.
Response Collection Flow6 / 15
POST /:slug/respond with optionalAuth + Zod middleware (poll.routes.ts:16-21). Server validates: poll existence, status="Active" (410 Gone otherwise), Authenticated poll auth check, Anonymous poll sessionToken requirement, required questions answered (response.service.ts:71-76), submitted options belong to correct question within correct poll (lines 78-87). Duplicate prevention via application-level SELECT (lines 47-55) + database partial unique indexes on submissions (schema.ts:158-165). Transaction-based submission (submissions + responses inserted atomically, lines 89-109). Frontend handles 6+ UI states: not found, loading, auth redirect, ended/published redirect, thank-you, already-responded, error, active form with radio buttons and client-side required validation (PollRespondPage.tsx). Per-poll localStorage session tokens for anonymous mode (utils.ts:23-29). Gaps: duplicate check SELECT is outside the DB transaction (race condition possible, caught by DB index but surfaces as 500), no rate limiting on submission endpoint, anonymous sessionToken is client-forgeable, no poll status re-verification within transaction, count for socket emit queried outside transaction.
Analytics & Feedback Dashboard6 / 15
Analytics endpoint (analytics.service.ts) returns: totalSubmissions, uniqueResponders (via SQL COALESCE), per-question option counts with percentages (rounded to 1 decimal), and hourly-bucketed timeline. Three Recharts chart types: BarChart (vertical bars), PieChart (donut), LineChart (timeline). KPI cards for submissions/questions/responders (PollAnalyticsPage.tsx:152-165). Result publishing enforced: must be "Ended" first, sets isPublished=true (poll.service.ts:178-191). Public results at /poll/:slug/results with 403 gate if not published (analytics.service.ts:97-104). Dashboard listing shows per-poll submissionCount via subquery. Socket.io triggers analytics refetch on submission/end/publish events. Gaps: N+1 query for option counts (one COUNT per option, analytics.service.ts:19-23), no export (CSV/PDF/JSON), no poll health metrics (drop-off, peak activity, completion rate), no time-range filtering, public results page discards timeline and uniqueResponders data (API returns full payload, UI shows only bar charts), pie chart limited to 6 colors, bar chart Y-axis clips at 140px, no data table view, no debouncing on socket-triggered refetches, double-fetch on page load.
Frontend Experience6 / 10
Consistent dark monochrome Tailwind CSS v4 theme. 10 routes with React Router 7: landing page, login, register, verify-email, dashboard, create poll, analytics, poll respond, poll results, 404. React Hook Form on create page with nested useFieldArray for dynamic questions/options (CreatePollPage.tsx). All async pages show loading spinners, error states, and empty states. PollRespondPage handles 6+ UX states (not found, loading, auth redirect, ended, thank-you, already-responded, active form). Landing page is well-designed with hero, feature cards with images, and CTA (HomePage.tsx, 352 lines). Responsive 2-column grid on dashboard. Post-creation success modal with copy link + analytics navigation. Copy-to-clipboard utility. Status badges colored by state. Auth redirect with return-to state. Email verification warning on dashboard and create page. Gaps: only browser confirm() for delete (no custom dialog), no toast notification system, dark-only (no light mode), no page transitions, landing page claims "Exportable reports" but export isn't implemented, no skeleton loading states, no form autosave, no PWA/offline support.
Backend Architecture & API Design6 / 15
Clean layered architecture: routes→middleware(validate+auth)→controller→service→DB. TypeScript throughout with Drizzle ORM. All inputs validated via Zod (registerSchema, loginSchema, createPollSchema, updatePollSchema, submitResponseSchema). Structured error handling (ApiError class with status codes, asyncHandler wrapper, error.middleware.ts). Consistent ApiResponse format. Database schema (schema.ts, 210 lines) with 5 tables, proper foreign keys with cascade deletes, unique indexes (url, poll+user partial, poll+session partial, submission+question). Transaction-based poll creation (poll.service.ts:51-89) and response submission (response.service.ts:89-109). Middleware chain: cors→morgan→bodyParser(50kb)→cookieParser→routes→404→errorHandler (app.ts). modular separation: auth/poll/response/analytics/socket modules. Health check endpoint. Lazy poll expiry via checkAndExpirePoll. Room-based Socket.io with singleton pattern. Gaps: no Helmet security headers, no rate limiting, no CSRF protection, N+1 queries in analytics and poll fetching, JWT secret checking duplicated (auth.middleware.ts:13-19 vs auth.service.ts:16-20), raw SQL with unsafe type casting in analytics.service.ts, no graceful shutdown, no pagination on any list endpoint.
Real-Time Updates Using WebSockets5 / 10
Socket.io server integrated with Express HTTP server via singleton pattern (socket.ts). Room-based: clients join/leave poll:{pollId} rooms via poll:join/poll:leave events. Three server-emitted events: response:new (with pollId + totalSubmissions), poll:ended, poll:published — emitted at correct points after DB operations. Frontend SocketProvider wraps app (SocketContext.tsx), with joinPoll/leavePoll/onResponseNew/onPollEnded/onPollPublished callbacks. PollAnalyticsPage consumes events: joins room on mount, listens for all 3 events, triggers full analytics refetch on each, cleans up on unmount (PollAnalyticsPage.tsx:47-67). Consistent event naming (domain:action). Client uses withCredentials + WebSocket+Polling transport fallback. Gaps: no socket authentication (any client can join any poll room), no reconnection room rejoin (disconnected clients silently lose updates), no debouncing on response:new handler (every submission triggers full refetch), frontend uses full refetch pattern (no incremental data push), no connection state tracking in UI, countSubmissions called outside transaction (race condition between commit and emit), only analytics page uses sockets (no real-time on respond or results pages), no connectionStateRecovery, no adapter for scaling.
Code Quality & Project Structure5 / 10
Clean monorepo with backend/ and frontend/ separation. TypeScript throughout with proper type annotations on all components, services, and API modules. Modular backend: src/modules/{auth,poll,response,analytics,socket} with shared common/ (db, middlewares, utils). Consistent naming (camelCase functions, PascalCase components, kebab-case files). Zod validation on both client (validators.ts) and server (poll.schema.ts, auth.schema.ts, response.schema.ts). Drizzle ORM with typed schema definitions. React patterns: AuthProvider/SocketProvider contexts, useCallback/useMemo, useEffect cleanup on all effects. Props interfaces on all components. Comprehensive README with setup instructions, env variables, tech stack. .env.example files for both packages. ESLint configured on frontend. Gaps: no automated tests (no test scripts), no ESLint on backend, JWT secret retrieval duplicated (auth.middleware.ts:13-19, auth.service.ts:16-20), findUserByEmail and findUserWithPasswordByEmail are identical (auth.service.ts:67-98), useAuth hook is 1-line re-export, VerifyEmailPage calls raw axios instead of shared api instance, response.api.ts has answers:unknown instead of typed, raw SQL type assertions in analytics.service.ts, frontend/.env committed to repo, no Husky/pre-commit hooks, no CI configuration, assertPollOwned exported but unused internally (poll.service.ts:199-203).
76
Pulse
Shreyash Koshta · @therealshreyash
44
Pulse is a single-question polling platform with Iris OAuth authentication, Express+Bun+Drizzle+PostgreSQL backend, React+TanStack Router+Tailwind CSS frontend, and Socket.io real-time updates. Deployed at pulse.shreyxsh.me with working frontend and backend. Strengths: polished dark-theme UI with comprehensive state coverage, solid duplicate vote prevention via fingerprinting+unique index, clean backend architecture with Zod validation and JWKS-based JWT, working Socket.io room-based pub/sub for live vote counts, velocity sparkline analytics. Weaknesses: no multi-question support (single-title-only polls), OAuth-only authentication with no custom registration/login, empty root README and no .env.example, no auto-expiry enforcement, no rate limiting or security headers, hardcoded description field, no automated tests. Total score: 44/100.
Authentication & Access Control5 / 10
Uses Iris OAuth exclusively — no custom email/password registration or login. auth.routes.ts defines /iris-login, /iris-signup, /callback, /refresh-token, /me, /userinfo endpoints. JWT access+refresh tokens stored in httpOnly/secure/sameSite cookies (auth.controller.ts:50-61). authenticate() middleware verifies JWT via JWKS RSA public key (token.ts:12-18), pollAuthenticate() allows optional auth for public polls. Frontend authenticate() calls /me and auto-refreshes on 401 (auth.ts:11-31). Anonymous vs authenticated polling enforced at respond() in poll.services.ts:179-181. Gaps: no logout endpoint, no Helmet/CSRF/rate limiting, no email verification or password reset, entirely dependent on third-party Iris OAuth provider.
Poll Creation & Question Management4 / 15
Create poll form at /create (create.tsx, 309 lines) with dynamic options add/remove (2-10), title textarea, anonymous toggle, show-live-results toggle, expiry select, draft/live submit. Zod validation in poll.models.ts: title 1-280 chars, options 2-10 items each 1-120 chars, status DRAFT/LIVE, expiresAt ISO datetime. Backend stores poll+options in DB (poll.services.ts:15-61). CRITICAL GAP: no multi-question support — only a single 'title' field serves as the sole question, with no separate per-question mandatory/optional toggles. Description field is hardcoded to "TEST" in create.tsx:123. No poll editing (PATCH only changes status to PUBLISHED/ENDED, never modifies questions/options). No slug/permalink for easy sharing.
Response Collection Flow6 / 15
Response page at /poll/$pollId (poll/$pollId.tsx, 279 lines) renders clickable option bars with vote button, handles LIVE/ENDED/PUBLISHED states, auth-blocked prompt for non-anonymous polls. Server-side respond() (poll.services.ts:161-223) validates: poll exists, status===LIVE, auth required if !isAnonymous. Duplicate prevention via check of existing vote by userId (authenticated) or SHA256 fingerprint of IP+UA+Accept-Language (anonymous), plus unique compound DB index on (pollId, userId). hasVoted endpoint confirms status. Socket emits vote update for real-time counts. Gaps: no auto-expiry enforcement on server (no cron to mark polls ENDED), optionId belonging to pollId not explicitly validated, expired polls only blocked indirectly by status===LIVE check.
Analytics & Feedback Dashboard6 / 15
Analytics at /analytics/$pollId (analytics/$pollId.tsx, 248 lines, creator-only): 3 stat cards (total responses, top option %, time left), per-option breakdown with AnalyticsBar showing counts/percentages and leading option highlight (green), response velocity Sparkline chart (SVG-based with hourly aggregation from backend). Published results view shows winner callout, all options with bars, downloadable share image via html-to-image (ShareCard.tsx). Dashboard lists all user polls with response counts, status badges, copy-link. Backend getPoll() computes option counts via LEFT JOIN, totalResponses, velocity via hourly date_trunc (poll.services.ts:63-109). Gaps: no CSV export, no auth vs anonymous breakdown, no peak activity metrics, no individual response viewing, no publish event over WebSocket.
Frontend Experience7 / 10
Polished dark theme with custom CSS design tokens (bg-0/1/2/3, ink-1/2/3, green-acc/bar/dim) in index.css (110 lines). 6 routes via TanStack Router with beforeLoad auth guards. Landing page (index.tsx) has scroll-reveal animations, hero poll bar animation, hamburger menu. UI components: Button (4 variants), Badge (5 status variants + live blink), Toggle switch, TopBar with back/avatar/share. CSS animations: blink, fadeIn, slideUp, bar-transition. Comprehensive state coverage: loading (submitting/creating/publishing), errors, empty states, auth-blocked states. Copy-to-clipboard with feedback, share-as-image feature. Gaps: description hardcoded to "TEST", no form library (individual useState), no confirmation dialogs for close/publish, no data caching layer (no TanStack Query).
Backend Architecture & API Design6 / 15
Clean layered architecture: routes→controllers→services→models with Drizzle ORM+PostgreSQL (not MongoDB). Schema: users, polls, options, votes tables with foreign keys and cascade deletes (schema.ts, 69 lines). Zod validation on env vars (config.ts, 24 lines) and request payloads (poll.models.ts, createPollPayloadModel+responsePayloadModel). JWT/JWKS-based token verification using jwks-rsa with caching (token.ts, 28 lines). ApiError class with factory methods, ApiResponse with consistent {success, message, data} format. Socket.io integrated cleanly. Gaps: no rate limiting, no Helmet/security headers, no request body size limits, ApiError.forbidden() ignores passed message, internalError() hardcodes message, user type is `any` in AuthenticatedRequest, no centralized error middleware, no auto-expiry cron, no pagination, console.log debug in auth.services.ts:54.
Real-Time Updates Using WebSockets6 / 10
Socket.io server with CORS (socket/index.ts, 29 lines), room-based pub/sub on pollId. Clients emit client:poll:join and client:poll:leave events. Server emits server:poll:update (counts[], total) on vote and server:poll:closed on poll close via emitter singleton pattern (emitter.ts, 16 lines). Frontend socket.io-client with autoConnect:false and withCredentials (lib/socket.ts, 8 lines). usePollSocket hook (usePollSocket.ts, 53 lines) connects, joins room, listens for updates with pollId filtering, cleans up on unmount (leave room + off listeners + disconnect). Analytics and poll pages receive real-time vote counts and poll closure. Gaps: socket.disconnect() on every cleanup is too aggressive (disconnects socket globally, not just this hook), no reconnection room re-join logic, no debouncing, no authentication on socket connections, limited events (no publish event, no connection status tracking).
Code Quality & Project Structure4 / 10
Monorepo with backend/ and frontend/ separation, TypeScript throughout with strict mode. Consistent module organization: backend uses modules/auth and modules/poll pattern; frontend uses routes/, components/ui and components/poll, hooks/, lib/, services/. ESLint configured for frontend. Proper React patterns: useEffect cleanup, custom hooks, useRef. Issues: root README.md is empty (0 bytes) — no project documentation at all. Backend README is 'bun init' boilerplate, frontend README is Vite template boilerplate. No .env.example files. No automated tests. Hardcoded "TEST" description in create.tsx:123. calcPcts utility duplicated in poll/$pollId.tsx:27 and ShareCard.tsx:4. ApiError factory bugs (forbidden ignores message, internalError hardcodes). user typed as any. Console.log in auth.services.ts:54. No gitignore entries for .env files. 5,902 total lines across 75 files.
77
PulseBoard
Rajib Sardar · @rajib1504_00fa4b42
44
PulseBoard is a working MERN polling platform with 11 REST endpoints, JWT auth with refresh tokens, dynamic poll creation, FingerprintJS-based anonymous voting, Recharts analytics with publishable results, and Socket.IO real-time updates. The UI is polished with dark/light mode and comprehensive state handling. However, significant gaps exist: no poll editing/deletion, no server-side validation that response answers match poll questions, no MongoDB transactions or unique compound indexes, no Helmet/rate limiting, access tokens in localStorage, no TypeScript, no tests, and the README makes inaccurate claims about tech usage (Zod vs Joi, React Hook Form vs useState). Scores well on the core user journey but has engineering gaps that prevent higher marks.
Authentication & Access Control6 / 10
Auth system works well: registration with bcrypt(12 rounds) + password schema, login returns JWT access+refresh tokens, refresh token rotation with SHA-256 hash stored in DB (auth.service.js:36-38), verifyJWT middleware validates Bearer token and attaches user (auth.middleware.js:6-32), optionalAuth middleware silently identifies users (optional.middleware.js:4-19). Frontend: login/register use React Hook Form + Zod, AuthContext with localStorage persistence, Axios interceptor for auto-refresh on 401 (api.js:21-66). Anonymous vs authenticated poll modes enforced server-side (response.service.js:20-22) and client-side (PollDetail.jsx:85-87). Gaps: access tokens in localStorage (XSS risk), cookie missing sameSite/maxAge, no Helmet, no CSRF, no rate limiting, no email verification or password reset.
Poll Creation & Question Management5 / 15
CreatePoll.jsx (281 lines) implements dynamic questions/options with add/remove, required/optional toggle per question, anonymous/authenticated voting toggle, and expiry datetime picker. Client-side validation checks title, expiry, question text, and option text. Server-side Joi validation via CreatePollDto validates title, expiry, questions (min 1), options (min 2 per question), and isAnonymous flag. Critical gap: no poll edit or delete endpoints exist at all — only Create + Read. No React Hook Form used on creation (contrary to README claim); uses individual useState manually. No slug/QR link for sharing. isPublished field defaults to true in schema but naming is confusing (it controls active state, not results publishing).
Response Collection Flow6 / 15
PollDetail.jsx (299 lines) renders radio-button style option selection with client-side required question validation (lines 78-82), FingerprintJS device ID, expiry detection, and confetti on success. Server-side response.service.js (46 lines) checks poll existence, isPublished, expiry, anonymous/authenticated mode enforcement, and duplicate vote prevention by userId or deviceId findOne query. Joi SubmitResponseDto validates answer structure (hex ObjectId format). Empty/loading/expired states covered. Gaps: no server-side validation that submitted questionIds/optionIds actually belong to the poll (only structural Joi check); no mandatory question enforcement server-side; no MongoDB unique compound index on {pollId, voterId} or {pollId, deviceId}; no transaction wrapping for submission.
Analytics & Feedback Dashboard6 / 15
Analytics dashboard at PollAnalytics.jsx (269 lines) uses Recharts horizontal BarChart per question with colored bars, percentage tooltips, and animated bars. MongoDB aggregation pipeline (poll.service.js:72-84) counts votes per {questionId, optionId}. Per-question summaries show option counts, percentages, and total votes. Publish/unpublish toggle via PATCH endpoint with creator-only gate. PublicResults.jsx (269 lines) shows results to anyone when published, with html-to-image export and QR share modal. MyPolls.jsx lists polls with response counts and status badges. Gaps: no time-series data, no peak activity metrics, no anonymous vs authenticated breakdown, no CSV/export from analytics, no individual response viewing, analytics data duplicated in code (getAnalytics and getPublicResults have near-identical aggregation logic).
Frontend Experience6 / 10
Well-polished UI with Tailwind CSS v4, dark/light mode toggle (ThemeContext.jsx:34 lines), consistent gradient accents. TanStack Router provides 7 file-based routes. All pages handle loading (spinners, skeleton cards in ActivePollsGrid.jsx), error (toast notifications), and empty states (e.g., "No active polls yet", "You haven't created any polls"). Auth-gated pages show login redirect. Landing page has Hero, FeatureShowcase, ActivePollsGrid, HowItWorks, CtaFooter sections. CountdownTimer component, ShareModal with QR code, confetti animation. Gaps: CreatePoll uses imperative useState arrays (NOT React Hook Form as claimed in README), navbar hash links don't hook into actual scroll behavior, no confirmation dialogs for destructive actions, active polls grid has no pagination.
Backend Architecture & API Design5 / 15
Modular structure separates auth/Poll/response with controller-service-model-route-DTO layers. Joi validation with BaseDto class pattern (abortEarly:false, stripUnknown:true). Custom ApiError class with 5 static factory methods (400/401/403/404/409). Global error handler with dev/prod stack trace control. CORS configured with allowlist + credentials. Mongoose schemas with embedded subdocuments, indexes on {creator} and {isPublished, expiresAt}. Gaps: no Helmet, no rate limiting, no request body size limits, no CSRF. Schema bug in poll.model.js: 'require: true' instead of 'required: true' on title and creator fields (lines 39,46) — these fields have NO database-level enforcement. No MongoDB transactions. No poll edit or delete endpoints. Poll route path capitalized as "/api/v1/Polls" (inconsistent). No graceful shutdown. No .env.example. Socket CORS set to wildcard "*". Manual route ordering to avoid param conflicts.
Real-Time Updates Using WebSockets5 / 10
Socket.IO server (socket.js:30 lines) uses singleton io pattern with getIo(). Server emits single "voteUpdated" event to poll-room after each vote (response.controller.js:16-20). Client connects on PollDetail (lines 54-65), PollAnalytics (lines 49-72), and PublicResults (lines 49-64) pages, each joining "joinPollRoom" and listening to "voteUpdated". Reconnection enabled. Gaps: pure invalidation signal pattern (no data pushed over socket, clients refetch HTTP); only 1 event type (no publish/expiry/deletion events); no room leave events; no socket authentication; no event validation/Zod; no debouncing on client-side analytics refetch; CORS wildcard on socket server; PollDetail manipulates totalVotes optimistically (prev+1) without verifying against server.
Code Quality & Project Structure5 / 10
Clean monorepo with client/server separation. Server follows MVC-lite pattern with modular separation (auth/Poll/response). Client uses TanStack Router file-based routing. React patterns decent: useEffect cleanup on all effects, error boundaries via try-catch, Axios interceptor for token refresh. No TypeScript anywhere (entire codebase is plain JS). No automated tests. README claims "Zod" and "React Hook Form" but server actually uses Joi and CreatePoll uses imperative useState. Poll model has 'require: true' bug (not 'required: true'). Console.log active in production (socket.js:14,23). Client package.json has unused deps "install" and "npm". No ESLint on backend. Capitalized "Poll/" directory breaks JS convention. No .env.example files. ~3,442 total lines of hand-written code across the project.
78
O
Voxly Live Polling
Om Raj Panday · @omraj0627
44
Voxly Live Polling Platform is a vanilla JS + Express + PostgreSQL polling app deployed on Railway. It delivers a functional SPA with 11 pages, dark/light theme, custom cursor, and page transitions built entirely without frameworks — an impressive feat for vanilla JS. The backend has clean route/data separation, parameterized SQL, and thorough response validation with dual duplicate prevention. Key strengths: engagement rate analytics, both-mode response logic, custom datetime picker, and comprehensive profile/settings management. Critical issues: OIDC auth is broken at runtime (AuthCodes/RefreshTokens not exported from db.js), Socket.io events are emitted but the client has ZERO Socket.io code making real-time updates non-functional, JWT secrets hardcoded in multiple places with different values, no .env.example, no tests, and a dead mock analytics render function. Total: 44/80.
Authentication & Access Control6 / 10
Solid authentication: register (bcrypt 12 rounds, server.js:33-46), login (bcrypt compare, server.js:48-60), JWT with authenticate/optionalAuth middleware (middleware.js:4-25), protected routes on all poll creation/analytics/publish endpoints, profile management (name, email, password, avatar, account deletion), and poll mode enforcement (anonymous/authenticated/both at server.js:179,195). However: token stored in localStorage (XSS risk), OIDC endpoints are broken at runtime (auth.js:4 imports AuthCodes/RefreshTokens but db.js:182 doesn't export them), JWT secret hardcoded in 2 files with different fallback values (middleware.js:2 'pollify-jwt-secret-dev' vs auth.js:37 'voxly-jwt-secret-dev'), no email verification, no rate limiting, no CSRF/Helmet.
Poll Creation & Question Management6 / 15
Working poll creation with 3-step wizard (Setup→Questions→Publish, app.js:563-651), dynamic question add/remove with mandatory toggle per question (app.js:543-561), dynamic option add/remove (letter-labeled A-H), mode selection (anonymous/authenticated/both), custom datetime picker with DD/MM/YYYY HH:MM AM/PM (app.js:227-237), starts-at scheduling support, preview step before publish (app.js:599-607), client-side validation for title/expiry/questions, server-side validation (server.js:166-168). UUIDs assigned server-side for questions and options (db.js:106-107). Significant gaps: no poll edit or delete endpoints, no question reordering, no option minimum enforced server-side, no slug/permalink generation, no draft status support.
Response Collection Flow7 / 15
Thorough response collection: server validates mandatory questions with missing IDs returned (server.js:199-202), poll status gating rejects expired/non-active polls (server.js:194), authenticated mode enforcement returns 401 for anonymous on auth-only polls (server.js:195), duplicate prevention for both authenticated (by user_id, db.js:170-173) and anonymous (by IP+UA fingerprint salted with pollId, server.js:25-29, db.js:175-178) returning 409. Both-mode logic correctly routes signed-in users to authenticated path (server.js:206). Client-side mirror validation with shake animation on missing mandatory questions (app.js:817-828), option selection with visual feedback (app.js:796-804), progress tracking (app.js:806-811). Gaps: no server-side optionId ownership validation (doesn't verify option belongs to question), response + count increment not in a transaction (potential race condition db.js:153-158), no per-question single-option server enforcement.
Analytics & Feedback Dashboard6 / 15
Good analytics: total responses, per-question option counts with percentages (server.js:234-237), engagement rate = average % of questions answered per respondent (server.js:242-255, a non-trivial metric), anonCount/authCount breakdown (server.js:256-257), per-question bar charts with colored bars (app.js:191-199), results publishing endpoint (server.js:269-278), public results view with published-only gate (server.js:280-292), dashboard with summary stats (active/published/total polls and responses, app.js:678-695), poll list with status badges and action buttons (app.js:697-710). Gaps: no individual response viewing, no CSV/export, no trend data/time-series, no peak activity metrics, dead `renderAnalytics()` function (app.js:712-783) with Math.random()-based mock data instead of real analytics, no animated bar transitions on analytics page.
Frontend Experience7 / 10
Strong vanilla JS SPA: 11 distinct pages (landing, login, signup, dashboard, create, public poll, analytics, success, results, profile, settings) all in single index.html with section-based routing (app.js:319-364), smooth page transitions via scaleY curtain animation (app.js:311-317), dark/light theme with CSS custom properties and localStorage persistence (styles.css:3-59, app.js:271), custom cursor with dot+ring trailing effect (app.js:273-282), scroll-reveal animations (app.js:900-918), toast notification system (app.js:960-963), responsive design (breakpoints at 900/580px), password strength meter (app.js:457-465), button loading states (app.js:444-455), avatar upload with client-side canvas resize (app.js:1020-1038), share modal with copy-to-clipboard (app.js:920-958). Gaps: no loading spinners/skeletons, no PWA support, the dead `renderAnalytics()` mock function and cosmetic hero live counter use Math.random() rather than real data.
Backend Architecture & API Design5 / 15
Good architecture patterns: clean separation of routes (server.js) → data layer (db.js), all SQL parameterized (no injection), consistent JSON error shape, semantic HTTP status codes, cascading delete with BEGIN/COMMIT/ROLLBACK transaction (db.js:86-99), idempotent migrations (db.js:21,48), trust proxy configured, body size limit (2mb). However: OIDC endpoints are broken at runtime — auth.js:4 imports AuthCodes/RefreshTokens from ./db but db.js:182 exports only {Users, Polls, Responses, init, pool}, causing TypeError on any OIDC flow. JWT secret fallback hardcoded in 2 files with DIFFERENT values. No input validation library (all ad-hoc checks), no rate limiting, no Helmet/security headers, no centralized error handler, no service layer, error messages leaked to client (e.message in catch blocks), no graceful shutdown.
Real-Time Updates Using WebSockets3 / 10
Server-side Socket.io infrastructure is correctly set up: integrated with Express HTTP server (server.js:16), room-based pattern with poll:<id> rooms (server.js:296-297), emits response:new with updated counts on submission (server.js:220-223), emits poll:published on publish (server.js:275), emits poll:state on room join for late joiners (server.js:298). However, the CLIENT-SIDE Socket.io code is completely missing: grep for socket.io in public/ returns zero matches — no io() import, no client script tag, no connection, no event listeners anywhere in app.js or index.html. The real-time events are emitted into the void and never consumed by any frontend code. Score 3 for correct server infrastructure without functional client integration.
Code Quality & Project Structure4 / 10
Clean file separation (server.js for routes, db.js for queries, middleware.js for auth, auth.js for tokens/OIDC), all SQL parameterized, consistent HTML escaping via escapeHtml() helper (app.js:91-94). But significant issues: no TypeScript, zero automated tests, no ESLint/Prettier, no .env.example file despite README referencing one, OIDC broken at runtime (AuthCodes/RefreshTokens imported but not exported from db.js), JWT secret hardcoded in 3 locations across 2 files with inconsistent values, OIDC client secret hardcoded (auth.js:27), dead/mock renderAnalytics() function (app.js:712-783) duplicates loadAnalytics() with random data, app.js is a 1135-line single file with no module system, no .env or node_modules in .gitignore for .env.*.local only. README is comprehensive and transparent about limitations.
79
pollforge
Abhiran Das · @abhirandas
44
PollForge is a functional full-stack polling platform (2,893 lines) with React/TypeScript frontend and Express/Drizzle ORM/PostgreSQL backend. The frontend is deployed and accessible on Vercel, but the Railway backend returns 502. Strengths: clean dark-themed UI with comprehensive state coverage across 7 routes, well-designed Drizzle schema with 6 tables and proper relations, solid analytics dashboard with Recharts visualization (bar/line charts, participation trends, per-question breakdowns), and proper Zod validation on all API inputs. WebSockets are properly architected with room-based pub/sub but the Socket.io client connects to the wrong origin in production. Weaknesses: no service layer (business logic in route handlers), no security middleware (Helmet/rate limiting), tokens in localStorage, no tests, Math.random-based slug generation, no question editing after poll creation, and no anonymous duplicate vote prevention. Score 44/100 reflects a solid but incomplete implementation that covers core requirements with decent quality but significant gaps in security, testing, and production readiness.
Authentication & Access Control5 / 10
Registration/login with Zod validation + bcrypt hashing (10 rounds), JWT token (7d expiry) returned on auth success, and `/me` endpoint for session validation (auth.ts:85-109). Two-tier middleware: `authenticate` for protected routes and `optionalAuth` for public routes that may need auth (middleware/auth.ts:9-52). Frontend AuthProvider with localStorage token persistence, ProtectedRoute component with loading state (main.tsx:15-24, useAuth.tsx:16-75). Anonymous vs authenticated poll modes enforced at submission (responses.ts:40-43) and reflected in UI (PollView.tsx:277-289). Critical gaps: tokens stored in localStorage (not httpOnly cookies), no refresh token rotation, no rate limiting on auth endpoints, no Helmet/CSRF protection, no email verification or password reset, JWT_SECRET! non-null assertion in 4 call sites across 3 files.
Poll Creation & Question Management6 / 15
Dynamic poll builder (CreatePoll.tsx, 405 lines) with add/remove questions with multiple options, mandatory/optional toggle per question, anonymous/authenticated mode selection with visual toggle cards, and expiry datetime-local picker with min validation (line 147-149). Client-side validation checks all questions have text and min 2 options (lines 111-121). Server-side Zod schema validates title min 3 chars, questions array min 1, options array min 2 per question (polls.ts:11-26). Slug auto-generated via custom nanoid (utils.ts:3-9). PUT /:id endpoint exists for updating poll metadata (title, description, isActive, expiresAt) but cannot edit questions after creation (polls.ts:145-178). Gaps: no transaction wrapping for sequential question/option inserts, no slug collision avoidance (Math.random-based), no draft status, no QR code or custom slug support, and N+1 query pattern for inserts.
Response Collection Flow6 / 15
Response submission (responses.ts:21-128) validates: poll existence (404), isActive + expiry check (400), anonymous/authenticated mode enforcement (401), duplicate prevention for authenticated users via unique respondent check (409), mandatory questions answered (400 with specific error messages), and options belonging to declared questions (400). Client-side validation checks mandatory questions and displays inline errors (PollView.tsx:76-82). Uses Zod for answer map validation (z.record). Returns differentiated HTTP status codes (201/400/401/403/404/409). Socket.io emits response:new + analytics:update after each submission (lines 115-125). Gaps: no duplicate prevention for anonymous users (acknowledged in README), no rate limiting on submission endpoint, no transaction wrapping for response+answers insert, IP captured but only for non-anonymous polls.
Analytics & Feedback Dashboard7 / 15
Comprehensive analytics (Analytics.tsx, 396 lines) with: stat cards showing total responses, question count, avg response rate, and status (lines 209-260); Recharts bar charts per question with horizontal layout and custom tooltip (lines 349-388); participation-over-time LineChart with 7-day window (lines 263-293); per-question breakdowns with option counts, percentages, skipped count, response rate, and leading option identification (responses.ts:171-194); animated progress bars on option percentages (Analytics.tsx:380-384). Published results view renders analytics publicly through the same poll link (PollView.tsx:126-184). Dashboard shows response counts and question counts per poll (Dashboard.tsx:170-178). Server-side analytics computation includes question stats with option aggregation (responses.ts:150-221). Gaps: no CSV/JSON export, no individual response viewing, no peak activity hour/day metrics, no question drop-off rate between Q1→Qn, analytics computed via in-memory filter/map (O(n*m)) rather than SQL aggregation.
Frontend Experience6 / 10
Consistent dark "forge" theme with custom forges/ink color palette (tailwind.config.js:12-36), Syne display font for headings and DM Sans for body text, Tailwind CSS with 4 custom animations (fade-in, slide-up, slide-in, pulse2). All 7 routes handle: loading (spinner in center), error (banner with AlertCircle icon), empty (dashboard CTA), auth-required (login prompt), poll-closed/expired (informational card), published results (redirected view), and submission success (checkmark confirmation with live count). Responsive layout uses sm: breakpoints throughout. Copy-to-clipboard with "Copied!" 2s timeout feedback. Noise texture background via SVG filter (index.css:67-77). Custom scrollbar styling. Gaps: no form library (individual useState hooks in CreatePoll), native confirm()/alert() instead of custom modal dialogs for delete/publish confirmation, no toast notification system, no dark/light mode toggle, no skeleton loading states (only spinners).
Backend Architecture & API Design5 / 15
Well-designed Drizzle ORM schema with 6 tables (users, polls, questions, options, pollResponses, answers), proper foreign keys with cascading deletes, and full relation definitions (schema.ts, 145 lines). Zod validation on all input payloads (register, login, createPoll, updatePoll, submitResponse). Express route separation (auth/polls/responses) with JWT middleware. CORS configured but with `origin: true` — allows any origin (security concern). Drizzle parameterized queries prevent SQL injection. Socket.io integrated with HTTP server. Gaps: no service layer (all business logic in route handlers, N+1 poll fetch in analytics), no Helmet middleware, no rate limiting anywhere, no request body size limits, no centralized error handler, `console.log` in production (db/index.ts:7), JWT_SECRET! non-null assertion used in 4 call sites across 3 files, custom nanoid uses Math.random (not cryptographically secure), no graceful shutdown, backend deployment returns 502 (Railway is down).
Real-Time Updates Using WebSockets4 / 10
Socket.io server (socket/index.ts, 31 lines) supports room-based pub/sub with join:poll and leave:poll by slug, plus disconnect handling. Server emits two events: `response:new` (with pollSlug, totalResponses, responseId) and `analytics:update` (with full analytics object) after each submission (responses.ts:115-125). Client hook usePollSocket (useSocket.ts:19-40) properly joins room, sets up listeners, and cleans up (leave:poll + socket.off) on unmount. Both PollView and Analytics pages consume live updates for response counts and analytics. Critical gaps: socket.io client connects to `io("/")` (useSocket.ts:8) — in production the Vercel frontend would attempt to connect to the Vercel origin, not the Railway backend, making WebSockets non-functional in production. Backend is also returning 502 (down). No reconnection room re-join logic, no socket authentication, no debouncing on analytics updates, no typed socket events.
Code Quality & Project Structure5 / 10
TypeScript throughout with proper types and interfaces (types/index.ts, 90 lines). Clean monorepo structure (client/ and server/ workspaces). Consistent naming conventions (kebab-case files, camelCase functions, PascalCase components). Drizzle ORM with typed schema exports. Typed API client with method grouping (api.ts:27-61). Tailwind CSS component classes in @layer components for design cohesion. Good README with API reference table and deployment instructions. .env.example files for both packages. Gaps: zero automated tests (no test files anywhere), no ESLint or Prettier configuration, single commit ("api commit"), no service layer separation, `console.log(process.env.DATABASE_URL)` in db/index.ts:7 (leaks DB URL in production logs), custom nanoid using Math.random (not crypto), JWT_SECRET! non-null assertions in 4 call sites, no TypeScript strict mode verification in tsconfig.json, stale-closure vulnerability in useAuth useEffect (empty deps array with token dependency).
80
voxyl
ankit kumar · @lemisin2004_718757cf
44
Voxly (also named PollFlow in README) is a functional full-stack polling platform with solid backend architecture and good feature coverage, but suffers from significant dead code bloat. The active implementation includes JWT auth with proper middleware patterns, dynamic poll creation with question/option management, a well-validated response collection flow with duplicate prevention, analytics with MongoDB aggregation pipelines, and Socket.io real-time updates with room-based broadcasting. However, the frontend is basic (no per-question mandatory/optional toggle in active code, no edit UI, basic analytics visualization), no backend is deployed (only the Netlify frontend works), and ~1,400 lines of dead code with duplicate page implementations, empty stubs, and broken imports pollute the codebase. Scores total 44/80, placing it near the cohort median of 43.
Authentication & Access Control6 / 10
Solid JWT authentication with register, login, and getMe endpoints (authController.js, 87 lines). express-validator chains on registration (auth.js routes:12-16) validate username/email/password. bcryptjs at 12 rounds (User.js:35), findByEmail static with +password select (User.js:46-48). Two middleware: protect (auth.js:4-27) for mandatory auth and optionalAuth (auth.js:29-54) that gracefully sets req.user=null. Client AuthContext (contexts/AuthContext.jsx, 46 lines) manages user state with localStorage persistence, login/logout functions, and auto-validation on mount via getMe. ProtectedRoute checks auth before rendering (router/index.jsx:15-19). Axios interceptor auto-attaches Bearer token and redirects on 401 (axiosInstance.js:5-21). Anonymous vs authenticated poll modes enforced server-side via requiresAuth field (publicController.js:36-38, 75-77). Gaps: no refresh token rotation (single 7-day JWT), tokens in localStorage (XSS-vulnerable), no email verification, no password reset, no logout endpoint on server, no CSRF protection beyond helmet defaults.
Poll Creation & Question Management5 / 15
Active CreatePoll.jsx (198 lines) supports dynamic questions with add/remove (min 1 enforced), dynamic options per question with add/remove (min 2 enforced), duration selector (1h/24h/3d/1w/30d dropdown), and public/private access toggle. Client-side validation via validatePoll() (utils/validation.js:3-12) checks title, questions have text, options count and text. Server-side createPoll (pollController.js:5-59) validates expiry date, questions array, question text, option count/text, and generates shareToken via nanoid(10). Poll update endpoint PUT /api/polls/:pollId exists with guards against editing polls with responses or published polls (pollController.js:82-140). QuestionBuilder component exists only in dead code (not used by active router). Gaps: no per-question mandatory/optional toggle in active CreatePoll (all questions effectively required), no question reordering (move up/down), no slug/permalink, no draft status, no QR code, client-side EditPoll.jsx is empty stub (0 lines), no form library (plain useState), isRequired field accepted by server but never exposed in active client form.
Response Collection Flow6 / 15
PollResponse.jsx (147 lines) fetches poll by shareToken, renders radio-button options per question, validates all questions answered before submission, generates anonymous token via crypto.randomUUID() stored in localStorage, and listens for poll_expired socket events for auto-redirect. Server-side submitResponse (publicController.js:58-144) performs defense-in-depth: validates answers is array, checks poll exists and isActive, enforces requiresAuth, prevents duplicates via checkDuplicateResponse (pollUtils.js:4-14) using userId for authenticated or IP for anonymous users, validates all required questions answered (returns missingQuestions array), verifies each questionId and selectedOptionId belongs to the poll, saves response with respondentId/ipAddress/userAgent, increments totalResponses counter, and emits socket event. Rate limited at 5/hour per IP+shareToken (rateLimiter.js:11-20). Differentiated HTTP status codes (400/401/403/404/409/410/422). Gaps: no per-question mandatory/optional client-side (all required), no completion progress bar, no countdown timer, no option clearing for optional questions, anonymous poll server enforcement but client always creates anonToken regardless.
Analytics & Feedback Dashboard5 / 15
Server analyticsController.js (125 lines) computes comprehensive analytics: responsesByDate aggregation pipeline (lines 44-52), optionCounts aggregation with $unwind (lines 54-65), completion rate via $size query (lines 67-72), per-question breakdown with totalAnswered/skipped/option counts/percentages (lines 74-95), and zero-response handling with skeleton structure (lines 10-42). Active Analytics.jsx (117 lines) renders per-question horizontal bar charts via Recharts with colored cells and tooltips, shows total votes counter, and integrates live socket updates with toast notifications. Dashboard.jsx (127 lines) shows poll grid with status badges (DRAFT/PUBLISHED/CLOSED), response/question counts, copy-link, delete, and analytics links plus empty state. PollResults.jsx (129 lines) renders public results with percentage bars, live socket updates incrementally updating vote counts, and CTA section. Poll publish endpoint blocks if zero responses (pollController.js:157-158). Gaps: no publish button in active Analytics page, no stat cards for completion rate or response velocity, no CSV/export, no individual response viewing, no trend line chart in active code (exists only in dead PollAnalytics.jsx), analytics are basic bar charts and total count only, participationByDate from server never rendered in active client.
Frontend Experience5 / 10
Custom design system with CSS utility classes (glass, voxly-glow, brand-gradient, text-headline-xl), dark/light theme toggle persisted to localStorage (Layout.jsx:6-16), responsive grid layouts, 10 routes via React Router v6 createBrowserRouter (router/index.jsx), Material Symbols icons throughout, and consistent spacing/typography tokens. All pages handle loading states and error states via react-hot-toast. Empty states present in Dashboard (empty inbox icon with CTA). Protected routes show loading indicator during auth check. Hover effects on cards and buttons, animated pulse/gradient backgrounds. Navbar with active-state styling, user greeting, and theme toggle. Landing page with multiple sections (hero, feature grids, demos, testimonials, pricing). Gaps: no form library (plain useState hooks), no skeleton loaders (only text-based "Loading..." states), no confirmation dialogs beyond window.confirm(), tokens in localStorage instead of httpOnly cookies, no inline client-side validation on login/register forms, massive dead code (~1,400 lines) shipping unused duplicate components, "Polls" and "Analytics" nav links redirect to /dashboard (router:28-29), logo URLs point to external Google-hosted images.
Backend Architecture & API Design7 / 15
Clean layered architecture: routes→controllers→models with middleware for cross-cutting concerns. express-validator used properly in routes/auth.js (lines 12-16, 22-27) and routes/polls.js (lines 12-17, 29-34) with custom handleValidationErrors middleware (validateRequest.js, 17 lines) producing consistent 422 responses. Three Mongoose models: User.js (51 lines, bcrypt pre-save 12 rounds, findByEmail static, comparePassword method), Poll.js (119 lines, embedded QuestionSchema/OptionSchema with validators, virtuals isExpired/isActive, indexes on shareToken/createdBy/expiresAt), Response.js (56 lines, AnswerSchema subdocument, compound indexes for dedup queries). Middleware: protect (JWT verify+user lookup), optionalAuth (graceful null handling), pollOwner (loads poll once, attaches to req), rate limiting (authLimiter 10/15min, respondLimiter 5/hr keyed by IP+shareToken). Security: helmet (default), CORS restricted to CLIENT_URL, morgan in dev only. Global error handler with conditional stack trace (server.js:37-44). 12 RESTful endpoints with differentiated status codes (201/200/400/401/403/404/409/410/422). Aggregation pipelines for analytics. Gaps: no service layer (controllers use Models directly), no explicit MongoDB transactions, response submit route lacks express-validator chain (manual only), JWT_SECRET has no fallback check, no pagination on getMyPolls, no graceful shutdown, no request sanitization beyond Mongoose validation.
Real-Time Updates Using WebSockets6 / 10
Socket.io server (socket/index.js, 71 lines) configured with CORS, pingTimeout/pingInterval, and three client events: join_poll_room (joins room, tracks member count, emits joined_room confirmation), leave_poll_room, and disconnect/error handlers. Auto-expire interval runs every 60s scanning for polls expired in the last minute, emitting poll_expired to rooms (lines 46-65). Three server-emitted event types: response_update with lastAnswer+totalResponses (publicController.js:129-133), poll_published with pollId (pollController.js:169), and poll_expired from interval checker. Client useSocket.js hook (83 lines) has full reconnection config (5 attempts, 1s delay), isConnected state, connectionError tracking, joinPollRoom/leavePollRoom callbacks, and typed listener helpers (onResponseUpdate/onPollPublished/onPollExpired) with cleanup. Analytics.jsx listens for response_update with toast, PollResponse.jsx listens for poll_expired for auto-redirect, PollResults.jsx computes incremental vote updates from response_update data. Gaps: two socket connections created (SocketContext via SocketProvider creates one, useSocket hook creates another via useRef - SocketContext connection is never consumed), no authentication on socket connections, no typed events, no client-side debouncing, room re-join on reconnect relies on Socket.io defaults rather than explicit handling in component remounts.
Code Quality & Project Structure4 / 10
Clean monorepo structure with client/ and server/ directories, well-organized server (routes/controllers/models/middleware/socket/utils) and client (api/components/contexts/hooks/pages/utils/router). README.md (112 lines) with setup instructions, API docs table, environment variables, project structure, and deployment guidance. .env.example files for both client and server. Consistent file naming conventions throughout. However, significant quality issues: ~1,400 lines of dead/unused code with two competing frontend architectures (App.jsx with its own BrowserRouter, duplicate pages in pages/auth/, pages/polls/, pages/dashboard/, pages/public/ — none imported by the active router/index.jsx). Seven empty stub files (EditPoll.jsx, Footer.jsx, CountdownTimer.jsx, ErrorMessage.jsx, PollCard.jsx, OptionInput.jsx, PieChartQuestion.jsx). Dual auth contexts (context/AuthContext.jsx vs contexts/AuthContext.jsx). Dead files reference non-existent import (api/axios.js). No TypeScript, no automated tests, no ESLint/Prettier configuration, no PropTypes. Console.log statements in production code (socket connections, MongoDB connection). Hardcoded external Google image URLs for logo in Layout.jsx. CSS design tokens undocumented. No graceful shutdown. Clean active code structure but the dead code situation nearly doubles the codebase with unused implementation.
81
R
Consensus Public
Ruhit Arman · @armanruhit
43
Consensus is a full-stack polling platform deployed at consensus.armanruhit.dev. It implements all core features: JWT auth with httpOnly cookies, dynamic poll creation with multi-question support, response collection with validation and rate limiting, analytics dashboard with per-question option counts, results publishing, and Socket.io real-time updates. The project uses a clean architecture (Express 5 + Prisma + PostgreSQL backend, React 19 + Tailwind CSS v4 + TypeScript frontend) with ~2,500 lines of code. Strengths: Complete OpenAPI/Swagger documentation, comprehensive Zod validation, clean layered backend architecture, all routes handle loading/error/empty states, good README. Weaknesses: No tests, missing security headers (no Helmet/CSRF), missing DB-level duplicate response prevention, no poll editing, basic analytics without time-series/export, signal-only WebSocket approach, hardcoded insecure cookie settings, and several code quality issues (duplicate types, dead files, console.log in production). Total: 43/100, right at the cohort median.
Authentication & Access Control6 / 10
Full registration/login/logout flow with bcrypt (12 rounds), JWT in httpOnly cookies (auth.service.ts:7-16), auth middleware reading from req.cookies.token (auth.middleware.ts:9-29), ProtectedRoute/GuestRoute guards in App.tsx:10-22, authenticated poll mode enforced at route level (poll.routes.ts:43-56). Key gaps: no CSRF protection, no Helmet, hardcoded `secure: false` cookie regardless of environment (auth.service.ts:12), single 7-day token with no refresh rotation, no email verification or password reset, JWT_SECRET uses non-null assertion.
Poll Creation & Question Management6 / 15
Dynamic question/option add/remove in Dashboard.tsx CreatePollForm (lines 77-272), mandatory/optional checkbox per question, anonymous/authenticated mode select, expiry datetime picker. Client validation: title required, future expiry, at least 1 question with text, min 2 options per question (Dashboard.tsx:121-136). Server Zod validation: title, description, responseMode, future expiresAt, questions array with min 1 question, each with min 2 options (poll.validator.ts:13-19). Significant gap: no poll editing whatsoever — questions/options cannot be modified after creation. No draft status, no form library.
Response Collection Flow6 / 15
Client validates mandatory questions before submit (PollPage.tsx:54-60). Server validates required questions, option existence, poll state, and expiry (response.service.ts:31-73). Rate limiting with user/IP key differentiation (rateLimiter.middleware.ts:8-16). Authenticated mode enforces login + one-response-per-user via findFirst (response.service.ts:43-53). Differentiated status codes (400/401/409). Gaps: no DB-level unique constraint on Response(poll_id, respondent_id) — only app-level check with race condition (schema.prisma:80 has TODO comment), no anonymous duplicate prevention, no transaction wrapping for response+answers creation.
Analytics & Feedback Dashboard5 / 15
Creator analytics endpoint (analytics.service.ts:3-72) with per-question option counts/percentages via Prisma groupBy, total responses, poll metadata. Results publishing (poll.service.ts:94-120) with public PollResults component showing bar charts (PollPage.tsx:176-218). Live response count updates on Dashboard via Socket.io with flash animation (Dashboard.tsx:314-321, css:7-11). Gaps: no time-series/timeline analytics, no CSV/JSON export, no individual response viewing, no anonymous/authenticated breakdown, no peak activity or drop-off metrics, basic CSS progress bars instead of charting library.
Frontend Experience5 / 10
Clean Tailwind CSS v4 design system with consistent border/color/spacing tokens across 5 routes. Loading states (Dashboard.tsx:378), error states (PollPage.tsx:20-24), empty states (Dashboard.tsx:382-385), success confirmations (PollPage.tsx:137-162), copy-to-clipboard with "Copied!" feedback (Dashboard.tsx:7-18). Custom CSS animations for flash (index.css:3-11) and fade-in (index.css:13-20). Gaps: no dark mode, no form library (all useState), no confirmation dialogs for destructive publish/close actions, no pagination on poll list, no skeleton loaders, no toast system, no accessibility considerations.
Backend Architecture & API Design5 / 15
Clean routes→services→middleware architecture with Prisma ORM + PostgreSQL connection pooling (prisma.ts:7-12), Zod validation on all inputs (3 validator files, 4 schemas), complete OpenAPI/Swagger spec (openapi.ts:231 lines), configurable CORS + rate limiting, graceful SIGINT shutdown (index.ts:15-18). Gaps: no Helmet/security headers, no CSRF protection, no request body size limits, `secure: false` hardcoded in cookie settings, missing DB-level unique constraint on responses (TODO in schema.prisma:80), Prisma query logging active in production (prisma.ts:11), error middleware leaks raw err.message to clients (error.middleware.ts:5), dynamic imports in poll.routes.ts:48-49 is an anti-pattern.
Real-Time Updates Using WebSockets5 / 10
Socket.io with room-based pub/sub (socket.ts:7-41), two server-emitted events: response:new on submission (response.service.ts:89-96) and poll:state on publish/close (poll.service.ts:117,145). Dashboard joins all owned poll rooms for live count updates + flash animation (Dashboard.tsx:304-352). PollManage joins room, refetches on new response (PollManage.tsx:29-47). Proper room leave + disconnect cleanup. Gaps: signal-only approach (no data pushed, clients HTTP refetch), no reconnection room re-join logic, no socket authentication, no debouncing on analytics refetch, console.log in production socket code (lines 16,20,24,29), no live updates on public response form page.
Code Quality & Project Structure5 / 10
Clean client/server separation, TypeScript strict mode with noUncheckedIndexedAccess/noUnusedLocals/noUnusedParameters (tsconfig.json:9-17), consistent naming, well-written README (263 lines) with setup instructions/API table/architecture notes, .env.example provided, OpenAPI spec, PRD document. Gaps: zero tests (no test scripts or frameworks), duplicate type definitions in api.ts (AnalyticsOption/AnalyticsQuestion/PollAnalytics defined twice at lines 155-166 and 168-195), empty types/index.ts dead file, build artifacts committed (prisma.config.js, prisma.config.js.map), ESLint only on client, no pre-commit hooks, console.log in socket.ts, dynamic imports in route handlers, `as any` type assertion in rateLimiter.
82
POLLRANGE
Satya Prangya Sootar · @satyasootar
43
POLLRANGE is a full-stack polling platform with a well-designed frontend (neo-brutalist aesthetic, Framer Motion, 16 routes, comprehensive state coverage) and a clean backend module architecture (TypeScript, Mongoose, Express). It implements the full feature cycle: auth (register/login/Google OAuth/JWT rotation), poll creation wizard, public response flow, analytics dashboard, Socket.IO real-time updates, and result publishing. Strengths: polished UI with consistent design language, atomic vote counting, comprehensive E2E test suite (39 cases), token rotation on refresh, room-based Socket.IO, word cloud with stop-word filtering. Critical weaknesses: (1) Zod schemas exist for polls/responses but are NEVER used at runtime — poll creation, update, and response submission all accept raw req.body without validation, enabling arbitrary MongoDB field injection. (2) Analytics endpoint has no ownership check — any authenticated user can read any poll's analytics. (3) Poll edit page is broken — it doesn't pre-fill questions into the builder. (4) Duplicate response detection is disabled outside production and has a race condition (no unique compound index). Total: 43/100. Median in cohort: 42. Deployment: frontend works at pollrange.vercel.app, backend (pollrange-backend.onrender.com) is offline.
Authentication & Access Control6 / 10
Comprehensive auth system with register/login (bcrypt 10 rounds), Google OAuth via Passport, JWT access+refresh token pair with rotation (auth.service.ts:150 deletes old session), email verification + password reset via Resend (auth.service.ts:55-237), protected/guest route guards with Zustand rehydration guard (protected-route.tsx:39). Backend enforces anonymous vs authenticated poll modes (response.service.ts:33-35). Gaps: tokens returned in response body alongside cookies (auth.controller.ts:27), Google OAuth puts tokens in URL query params (auth.controller.ts:148-149), same JWT_SECRET for both token types (auth.service.ts:25,30), password reset doesn't revoke sessions (auth.service.ts:223-237), no Helmet/CSRF, no email verification enforcement before login.
Poll Creation & Question Management5 / 15
4-step wizard (Settings→Questions→Review→Success) with Framer Motion transitions, dynamic question/option add/remove via Zustand store (use-poll-builder-store.ts, 138 lines with 14 actions), mandatory/optional toggle, 10-option limit, client-side validation on settings and questions. Backend supports embedded question/option subdocuments (poll.model.ts) with shareToken, expiry, status state machine. CRITICAL GAPS: (1) No Zod validation at runtime — poll.controller.ts:12 passes raw req.body with no parsing (pollZodSchema defined but never used), enabling arbitrary field injection. (2) Edit page broken — edit.tsx:31-38 only calls updateMeta(), never pre-fills questions into builder store. (3) showProgressBar toggle commented out in step-settings.tsx:146. (4) No drag-and-drop reorder despite reorderQuestions in store and GripVertical icon in UI.
Response Collection Flow5 / 15
Public poll page (poll.tsx, 326 lines) handles 6 distinct states (loading/spinner, not found/PollErrorState, closed/PollClosedState, already responded/AlreadyRespondedState, submitted/ThankYouState, active form) with client-side mandatory validation. Server validates poll existence, active status, and expiry (response.service.ts:19-29), enforces authenticated-only mode (response.service.ts:33-35), and uses atomic $inc with arrayFilters for vote counting (response.service.ts:66-88). CRITICAL GAPS: (1) No Zod validation — response.controller.ts:18 passes raw req.body. (2) Duplicate detection disabled outside production (response.service.ts:38 if config.ENVIRONMENT==="production"). (3) Race condition in duplicate check — no unique compound index on {pollId, respondentId}. (4) No server-side mandatory question enforcement. (5) No validation that submitted optionIds belong to the poll.
Analytics & Feedback Dashboard6 / 15
Analytics dashboard (analytics.tsx, 229 lines) with overview cards (total, completion rate, live badge), per-question Recharts bar charts with top-option detection, timeline line chart, custom word cloud widget (word-cloud-widget.tsx), CSV+JSON export (analytics.controller.ts:116-152), snapshot dialog with PNG export and social sharing (snapshot-dialog.tsx, 445 lines). Backend computes daily timeline aggregation (30 days via aggregation pipeline, analytics.service.ts:73-87), browser breakdown from User-Agent, word cloud with 62 stop words, and per-question vote percentages. CRITICAL GAPS: (1) Analytics authorization bypass — getPollAnalyticsSnapshot (analytics.service.ts:61) uses Poll.findById() with NO ownership check; any authenticated user can read any poll's analytics. (2) anonymousCount/authenticatedCount hardcoded to 0/totalResponses (analytics.controller.ts:74-75). (3) Question-mapping logic duplicated verbatim in getFullAnalytics and getPublicResults (~30 lines each). (4) Snapshot dialog fabricates "85% of similar surveys" text.
Frontend Experience6 / 10
Consistent neo-brutalist design language (heavy borders, monospace labels, uppercase tracking) with Tailwind + shadcn/ui. Dark/light/system theme with keyboard shortcut and cross-tab sync (theme-provider.tsx). Framer Motion animations centralized in lib/animations.ts with fadeUp, stagger, slideInRight, numberPulse variants. 16 routes with ProtectedRoute/GuestRoute separation (App.tsx:33-77). State coverage: 6 skeleton/loading states, error boundaries (limited), empty states on dashboard/history, 6 distinct states on public poll page. Toasts via Sonner, optimistic poll deletion. Gaps: landing page navbar has no mobile hamburger menu (nav links hidden below md, navbar.tsx:19), edit page returns null during loading, analytics page returns null on missing data, no top-level error boundary for routes, dashboard timeUntilExpiry text never updates, "QR code" and "Embed" features claimed in landing page but not implemented.
Backend Architecture & API Design5 / 15
Clean layered module architecture (routes→controllers→services→models) with TypeScript strict mode. Mongoose schemas with proper indexing (compound creatorId+createdAt, unique shareToken, status, expiresAt, isDeleted, TTL on Session). Atomic $inc operators for concurrent vote counting (response.service.ts:66-88). Centralized error handler with ZodError normalization (error.middleware.ts:5-35). CRITICAL FLAWS: (1) 18 Zod schemas defined but only auth endpoints use them at runtime — poll creation (poll.controller.ts:12), update (poll.controller.ts:50), and response submission (response.controller.ts:18) all pass raw req.body with zero validation, enabling arbitrary MongoDB field injection. (2) Analytics service has no ownership check (analytics.service.ts:61). (3) Controller-layer leaks: Poll.findOne() called directly from controllers (poll.controller.ts:91, analytics.controller.ts:31). (4) Response model lacks unique compound index for duplicate prevention. (5) poll.service.ts:48 passes user input to $regex without escaping.
Real-Time Updates Using WebSockets5 / 10
Socket.IO server with room-based pub/sub (socket/index.ts:11-51): clients join poll_${pollId} rooms, server emits analytics-update on response submission. Client-side socket singleton with JWT auth and auto-reconnection (lib/socket.ts, 1s-5s backoff, 10 attempts). usePollSocket hook (79 lines) uses handlersRef pattern to avoid stale closures, joinedRef to prevent double-joining, proper cleanup on unmount (leaves room, removes listeners). Analytics page invalidates React Query cache on updates and shows "Live" badge when connected. Gaps: only response submission triggers broadcasts — no events for publish/close/reopen/delete. Socket push is pure invalidation (no data pushed, client refetches HTTP). No socket authentication (anyone can join any room). SOCKET_EVENTS.NEW_RESPONSE and POLL_STATUS_CHANGE are defined but never emitted by server. Only the analytics page uses sockets — dashboard doesn't get live updates.
Code Quality & Project Structure5 / 10
Clean monorepo with backend/frontend separation, TypeScript strict mode, consistent module-per-feature organization. E2E test suite with 39 test cases (test/e2e-test.mjs, 584 lines). Shared animation variants, utility functions, typed API layer. Gaps: significant code duplication — question-mapping logic copy-pasted in two controllers (~30 lines each), already-responded check duplicated in poll.service.ts:134-143 and 299-309, form boilerplate in 3 auth form components, stepper UI duplicated in create.tsx and edit.tsx. Dead code: login-form/register-form/forgot-password-form unused by their pages, ProtectedRoute has dead useEffect and double useQuery, ColorBends (339 lines) appears unused. `(req as any).user` in 10 controller call sites. Zod schemas defined but never used at runtime for polls/responses — wasted engineering effort. Verification email says "PollCraft" not "PollRange" (auth.service.ts:70). No .env.example for backend.
83
Poll Platform
Ravindra Dhadave · @ravi8555_3ffa6a6b
43
Poll Platform is a full-stack MERN polling app with 52+ backend features and 64+ response/analytics/socket features enumerated. The functional core is solid: local + OIDC authentication with JWT cookies, dynamic poll creation with React Hook Form + Zod, multi-layered response validation with expiry/anonymous-auth enforcement, comprehensive analytics with Recharts visualization (line/bar/pie), Socket.IO room-based real-time updates, CSV export, and result publishing. However, the codebase is significantly undermined by quality issues: ~40% is commented-out dead code, ~20+ files are empty stubs, TypeScript strict mode is completely disabled, live production secrets are committed in .env, two route paths shadow each other making PublicResultsPage unreachable, the CSV export has a runtime bug (accessing wrong property name), cookie configuration is hardcoded for localhost only, and there are zero automated tests. The project demonstrates functional breadth across all 8 evaluation criteria but falls short on depth, polish, and production-readiness. Total score: 43/100, landing at exactly the cohort median.
Authentication & Access Control5 / 10
Local + OIDC registration/login with JWT httpOnly cookies (auth.controller.ts:413-443, 446-481, 289-399). Protected routes with redirect to /login (ProtectedRoute.tsx:36-61). OptionalAuth middleware (optionalAuth.ts:1-46) enables anonymous+authenticated poll modes, enforced server-side (response.controller.ts:136-149). One-response-per-user partial unique index (Response.model.ts:78-81). Solid foundation but critical flaws: hardcoded cookie domain:'localhost' and secure:false (auth.controller.ts:257-265) will break production, JWT secret falls back to 'secret' in 4 locations, Zod auth DTOs defined but never wired in routes, no ID token JWKS signature validation despite jose import, no CSRF protection, no email verification or password reset, hardcoded OIDC token endpoint (oidc.service.ts:320).
Poll Creation & Question Management6 / 15
CreatePollPage (694 lines) uses React Hook Form + useFieldArray with dynamic add/remove/reorder questions and add/remove options (CreatePollPage.tsx:408-422, 568-592, 413-422). Mandatory/optional toggle per question (CreatePollPage.tsx:613-622), anonymous/authenticated toggle (CreatePollPage.tsx:501-526), expiry datetime picker (CreatePollPage.tsx:528-541). Client-side Zod validation + backend CreatePollDto (poll.dto.ts:17-23) with min 1 question and min 2 options per question. Shareable link auto-generated (generateLink.ts:3-8). Key gaps: EditPollPage.tsx is empty stub (no editing UI), backend updatePoll only touches top-level fields (poll.service.ts:188-198), cannot edit questions/options after creation, no share link display after creation, PollCreator/QuestionBuilder/OptionBuilder all empty stubs.
Response Collection Flow7 / 15
Defense-in-depth: server validates expiry (response.service.ts:72-74), isActive status (response.service.ts:72-74), authentication mode (response.service.ts:66-69), mandatory questions (response.service.ts:91-99), and Zod submission schema (response.controller.ts:112-117). Duplicate prevention via service-level check + DB partial unique index for authenticated users (response.service.ts:77-88, Response.model.ts:78-81). Client-side mandatory check (PublicPollPage.tsx:53-59). Comprehensive error states: expired, not-found, auth-required with inline login modal (PublicPollPage.tsx:119-168, 212-254). IP/UA capture, atomic $inc of option/poll counts (response.service.ts:119-130). Missing: no duplicate prevention for anonymous voters, no MongoDB transaction (explicitly noted in code).
Analytics & Feedback Dashboard7 / 15
Full analytics: per-question option percentages, response rates, timeline aggregation via MongoDB $group pipeline (analytics.service.ts:51-114, 190-222). Four stat cards (responses, completion rate, questions, publish status), LineChart for timeline, BarChart + PieChart per question, data table with percentage bars (AnalyticsPage.tsx:219-347). Publish/unpublish toggle (AnalyticsPage.tsx:114-128), CSV export with Blob download (AnalyticsPage.tsx:130-145), copy poll link with clipboard API (AnalyticsPage.tsx:147-156). Public results page (PublicResultsPage.tsx:1-64). Live update badges. Gaps: CSV export has runtime bug (analytics.controller.ts:79 uses option.responseCount but interface has option.votes — will output undefined), PublicResultsPage shadowed by duplicate route in App.tsx:116-117, all analytics sub-components are empty stubs, no individual response viewing.
Frontend Experience5 / 10
React Router v6 with proper protected/public route separation (App.tsx:104-143). All pages handle loading (animated spinners), error (toast notifications via react-hot-toast), and empty states (e.g., DashboardPage.tsx:133-136 "No polls yet"). React Hook Form + Zod resolver for poll creation form (CreatePollPage.tsx:359-375). Tailwind CSS with responsive grid layouts. Heroicons for UI elements. Dashboard shows poll cards with live update ping animation (DashboardPage.tsx:143-151). Gaps: Navbar hidden entirely for unauthenticated users (Navbar.tsx:58), no public navigation, duplicate /results/:shareableLink route shadows PublicResultsPage, ~15 empty stub component files, no dark mode, homepage has empty features section, no confirmation dialogs (only browser confirm()), basic Tailwind without distinctive design system.
Backend Architecture & API Design5 / 15
Clean routes→controllers→services→models separation. Zod DTOs for all inputs (4 DTO files). Well-designed MongoDB indexes including partial unique compound (Response.model.ts:78-81) and compound indexes for analytics queries. Helmet, CORS (cors.ts), rate limiting (rateLimiter.ts), graceful shutdown (server.ts:136-158), Winston logging. However, TypeScript strict mode is completely disabled (tsconfig.json:10-18: strict:false, noImplicitAny:false, strictNullChecks:false), ~50+ any usages, ~1,380 lines commented-out code across 12 files (~40% of codebase), getPollByLink duplicated verbatim between PollService and ResponseService (~50 lines), middleware registered twice (helmet, json, urlencoded each applied twice in server.ts:59-75), error handler never receives err.status so all errors become 500, OIDC initialized twice (auth.controller.ts:278 + server.ts:121), auth controller bypasses service layer, all 5 database migrations empty.
Real-Time Updates Using WebSockets4 / 10
Socket.IO server with JWT authentication middleware on handshake (poll.socket.ts:13-25). Room-based pattern: clients emit join-poll/leave-poll to join poll_${pollId} rooms (poll.socket.ts:31-44). Server emits response-count-update on submission (response.controller.ts:164-167, poll.socket.ts:70-83). Frontend SocketService singleton with connection guard (socketService.ts:19-28), decoupled listener Map (socketService.ts:92-112), and proper useEffect cleanup (AnalyticsPage.tsx:81-91, DashboardPage.tsx:67-73). Key issues: count field hardcoded as string 'update' not actual number (poll.socket.ts:77), only one event type emitted, no analytics data pushed (pure invalidation), config/socket.ts, SocketContext.tsx, useSocket.ts all empty stubs, no reconnection room re-join logic, no debouncing/throttling, no analytics-update event from server side.
Code Quality & Project Structure4 / 10
Clean monorepo structure (backend/frontend/docs/database) with proper folder separation. TypeScript frontend, ESLint configured (frontend/eslint.config.js), documentation present (README.md + API_DOCS.md + DEPLOYMENT.md + USER_GUIDE.md). Undermined by massive issues: ~1,380 lines of commented-out code (~40% of codebase across 12 files), ~20+ empty stub files with zero implementation, TypeScript strict mode completely disabled in backend, .env committed to git with live production credentials (OIDC client secret + MongoDB Atlas URI with password), AnalyticsData interface defined identically twice in types.ts (lines 42-73 and 96-127), getPollByLink duplicated verbatim (~50 lines), double middleware registration, generateToken.ts misleadingly named (contains link generation not tokens), no automated tests anywhere, hardcoded URLs (oidc.service.ts:320, cors.ts:3, auth.controller.ts:263). No .env.example files.
84
D
Poll-X
Dev Vaghela · @vagheladev25
43
Poll-X is a solidly-built polling application with a neo-brutalist design aesthetic. Strengths include thorough response collection validation (defense-in-depth with 5 independent deduplication layers), clean backend architecture with proper middleware and transactions, consistent TypeScript usage, and a polished frontend with comprehensive UI state handling. The most significant gaps are: (1) HMAC-SHA256 for password hashing instead of bcrypt, (2) WebSockets entirely disabled with only a shallow REST polling fallback, (3) missing mandatory/optional toggle in the poll creation UI despite full backend support, (4) broken timeline bar scaling in analytics, and (5) no poll editing capability. The project demonstrates competent full-stack engineering but feels like a strong MVP that stopped before rounding out creator-facing management features and production security hardening. Deployed and functional at pollx-beta.vercel.app.
Authentication & Access Control6 / 10
Solid auth with custom JWT implementation (jwt.ts:61-87 correctly verifies HMAC-SHA256 signatures, validates exp/nbf/sub), refresh token rotation (auth.service.ts:83-125), httpOnly cookie for refresh tokens (auth.controller.ts:26-33), requireAuth/optionalAuth middleware (auth.middleware.ts:16-33), server-side anonymous/authenticated poll enforcement (responses.service.ts:31-36), and Axios interceptor for auto-refresh (api.ts:21-50). Key gaps: HMAC-SHA256 for password hashing instead of bcrypt (auth.service.ts:12-13), no Helmet security headers, no auth rate limiting on login/register, access token stored in localStorage (XSS risk), no frontend route guards.
Poll Creation & Question Management5 / 15
Multi-question/option creation with react-hook-form useFieldArray (create-poll.tsx:137-140), dual-layer Zod validation (schemas.ts:19-40, polls.dto.ts:7-23), transactional creation (polls.service.ts:23-52), expiry with quick-select presets (create-poll.tsx:10-34), and database CHECK constraint for min 2 options (schema.ts:60-63). Significant gaps: mandatory/optional toggle missing from UI (hardcoded to true at create-poll.tsx:125,236 despite full backend/db support), no poll editing exists, delete function exists in service (polls.service.ts:166-173) but no route or UI mounts it, no slug-based sharing (UUID only), no max bounds on questions/options.
Response Collection Flow7 / 15
Strong defense-in-depth: Zod boundary validation (responses.dto.ts:3-18), service-level checks for exampll existence/expiry/published status (responses.service.ts:22-25), anonymous/authenticated mode enforcement (lines 31-36), required question enforcement with structured error data (lines 53-58), option index bounds checking (line 47), transactional response+answers+counter insertion (lines 80-118), three DB unique constraints (schema.ts:84-86), IP+UA duplicate detection for anonymous polls (lines 62-77), and rate limiting 1/IP/poll/hour (rate-limit.middleware.ts:8-17). Gaps: no client-side validation for required questions on vote form, TOCTOU between pre-checks and transaction, frontend discards structured error metadata from server.
Analytics & Feedback Dashboard5 / 15
SQL-based aggregation for totals with authenticated/anonymous split (analytics.service.ts:64-71), per-question option counts via DB join (lines 79-90), hourly voting timeline via date_trunc (lines 46-58), geo IP distribution via geoip-lite (lines 22-44), Chart.js bar charts (chart.tsx), and snapshot-on-publish with JSONB storage (polls.service.ts:125-149). Notable issues: timeline bar scaling is mathematically broken (item.count * 10 capped at 100% — $pollId.analytics.tsx:222), no caching — full analytics snapshot recomputed every 5s polling interval, no export/CSV, no individual response viewing, no device/browser analytics despite data collection, geo labels use ISO codes instead of country names (analytics.service.ts:41), dashboard is just a poll list with no cross-poll insights.
Frontend Experience6 / 10
Consistent neo-brutalist design language across all pages (index.css:24-41, brutal-shadow/border-4/bold typography), 7 routes via TanStack Router, reactive form validation via react-hook-form+zodResolver (create-poll.tsx:118-119), all pages handle loading/error/empty states (22 distinct UI states counted), Chart.js visualizations, sonner toasts, responsive navbar with auth-aware rendering (navbar.tsx:24-44), and a polished landing page (index.tsx). Gaps: no frontend route guards (routes render before auth check, rely on API 401), landing page nav items (Features/How it works/Pricing/Templates/Resources) are non-functional buttons, no dark mode, "View Templates" button on homepage is dead, no confirmation dialogs for destructive actions.
Backend Architecture & API Design6 / 15
Clean layered architecture: routes→controllers→services with middleware chain (app.ts:35-44). TypeScript throughout with Zod validation on all inputs (9 schemas across auth/polls/responses/analytics). Drizzle ORM with well-designed PostgreSQL schema including CHECK constraints and composite unique indexes (schema.ts:1-103). Transaction-based operations for poll creation and response submission. Centralized ApiError class with structured error middleware (error.middleware.ts:5-20). Good auth middleware with Bearer token extraction (auth.middleware.ts:5-14). Gaps: no pagination on any list endpoint, no Helmet or request body size limits, no auth rate limiting, custom JWT instead of audited library, unused `socket.io` dependency in backend, `buildLiveCounts` (analytics.service.ts:141-156) is dead code never called by any controller.
Real-Time Updates Using WebSockets3 / 10
WebSocket (Socket.IO) is entirely disabled/commented out for Vercel serverless compatibility (socket/index.ts:1-33, all commented code). The server.ts explicitly comments out initSocket (server.ts:5,10). The only "real-time" mechanism is a REST polling fallback at 5-second intervals (socket.ts:21-59) that hits the full /analytics/:pollId/live endpoint — which calls the expensive buildAnalyticsSnapshot including geo lookup every cycle. No debouncing, no conditional short-circuit for unchanged data, no reconnection handling. The README acknowledges this as a Vercel limitation. Raw WebSocket code exists in comments with room-based pattern and emitPollEvent helper, suggesting it worked in local development but was disabled for deployment, yielding 3 for functional-but-shallow polling fallback.
Code Quality & Project Structure5 / 10
Clean monorepo with backend/frontend separation, TypeScript strict mode in both packages, consistent naming conventions (controller/service/route/dto per module), .env.example files for both packages, Docker Compose for local dev, and ESLint configured on frontend. Notable issues: no ESLint on backend, no automated tests anywhere, custom JWT implementation (jwt.ts:1-88) instead of jsonwebtoken library, dead code (buildLiveCounts, commented-out WebSocket blocks in multiple files), non-null assertion on user in api.ts:42, duplicate status color logic across files (dashboard.tsx:51-62, analytics.tsx:159-165), frontend and backend Zod schemas are duplicated rather than shared, unused dependencies (socket.io, socket.io-client needed only for commented code).
85
PollSync
Rochak Tiwari · @rochaktiwari
43
PollSync is a full-stack polling platform with live deployments (Vercel frontend, Render backend) and functional core features: auth with JWT dual-token flow, poll creation with multi-question support via React Hook Form, comprehensive server-side response validation, real-time Socket.IO updates on the dashboard, and public result publishing. However, the submission is marred by several critical bugs: the analytics API endpoint is completely non-functional (routes not mounted + broken router), auth routes leak under /api/polls/*, poll controller has two data bugs (slug/expiry inverted), and the logout flow is broken (wrong localStorage key). The codebase shows signs of AI-generated code without human review (10+ hallucinated imports from nonexistent exports), has zero tests, and contains 10 console.log statements including security-sensitive data. Total: 43/100 — a working but buggy implementation at the average quality level.
Authentication & Access Control6 / 10
Registration (bcrypt 10 rounds), login with JWT access token (15min) + httpOnly refresh cookie (7 day), Bearer token middleware, optionalAuth middleware for response routes, ProtectedRoute component checking localStorage, anonymous vs authenticated poll modes enforced server-side (response.service.js:42-57), poll ownership checks on publish/delete/getMyPolls, refresh endpoint. Gaps: logout removes wrong localStorage key ("token" vs "accessToken" — Navbar.jsx:9 vs LoginPage.jsx:16), no refresh token DB rotation, tokens in localStorage (XSS risk), no CSRF protection, no rate limiting, no email verification, no password reset, optionalAuth swallows all errors silently (optionalAuth.middleware.js:21-23).
Poll Creation & Question Management6 / 15
CreatePollPage.jsx (171 lines) uses React Hook Form with useFieldArray for dynamic questions/options, QuestionBlock.jsx (121 lines) as isolated sub-component, mandatory/optional toggle per question, anonymous submission toggle, expiry datetime-local picker. Server-side Zod validation (poll.validator.js:34 lines) enforces title min 3, date coercion, boolean coercion, options min 2. Slug auto-generation. Gaps: no poll editing endpoint exists, no client-side validation feedback (form submits with empty questions), onSubmit swallows errors with `console.log(error)` (CreatePollPage.jsx:37), no loading state during submission, no success redirect, no min 1 question server-side enforcement, no draft/active status.
Response Collection Flow7 / 15
SubmitResponseService (response.service.js:152 lines) has comprehensive defense-in-depth: poll existence check, published status gate, expiry check, anonymous/authenticated mode enforcement with duplicate submission prevention for authenticated polls (lines 42-57), required question validation (lines 61-77), invalid questionId rejection (lines 79-88), invalid optionId rejection (lines 90-101). PublicPollPage.jsx (201 lines) handles loading (spinner), error (error card), published (closed voting UI), and expired states. Toast feedback on success/error. Gaps: no client-side required-question validation before submit, submit button always enabled, no anonymous duplicate prevention (localStorage/browser fingerprint), no pending/loading state during submission, poll controller has 2 data bugs affecting frontend: slug set to description (poll.controller.js:42) and isExpired set to published field (poll.controller.js:46).
Analytics & Feedback Dashboard5 / 15
Analytics service (analytics.service.js:77 lines) computes per-question per-option counts with ownership verification. PublicResultsPage.jsx (143 lines) shows results with progress bars, percentage calculations, leader detection (green highlights), and proper states. DashboardPage.jsx (269 lines) displays polls grid with real-time response counts via Socket.IO, publish/delete actions, and status badges. CRITICAL BUG: analytics routes file uses `router` without creating it (Router() imported but never called - analytics.routes.js:1,8) and analytics module is never mounted in app.js (line 35) — the GET /api/analytics/:pollId endpoint returns 404. AnalyticsPage.jsx is unreachable (no route defined — Dashboard links to `/dashboard/poll/:id/analytics` which matches no route). Public results and dashboard polling work, but dedicated analytics page is broken.
Frontend Experience5 / 10
Consistent Tailwind design system (blue #097FE8, slate palette), 7 routes with React Router, separate AppLayout/PublicLayout with sidebar+navigation+breadcrumbs, all pages handle loading (spinners with descriptive text), error (styled error cards), and empty states with CTAs. Landing page has 10+ sections (Hero, Features, WhyPollSync, UseCases, Workflow, Screenshots, Testimonial, CTA, FAQ, ProblemSolution, Footer). Responsive design with mobile hamburger menu. Gaps: page title is "client" (Vite default), no form validation feedback on CreatePollPage, published results link broken (PublicPollPage.jsx:166 links to /results/:slug instead of /poll/:slug/results), no confirmation dialogs on delete, no loading state on submit buttons, no redirect after login to original URL, analytics page not accessible via navigation.
Backend Architecture & API Design5 / 15
Clean controller/service/validator/routes per-module architecture across auth, poll, response, analytics, socket. Prisma schema (schema.prisma:86 lines) with 6 models, cascade deletes, unique constraints, proper relations. Zod validation on all input boundaries (4 schemas). Dual JWT system with Bearer middleware + optional auth middleware. CORS with credentials. CRITICAL BUGS: analytics routes not mounted in app.js and routes file itself is broken (router undefined), auth routes reuse poll router instance (auth.routes.js:1 imports from poll.routes.js instead of creating Router), poll controller data bugs (slug=description, isExpired=published). No rate limiting, no Helmet, no body size limits, no centralized error handler, errors always return 400, no pagination, no graceful shutdown.
Real-Time Updates Using WebSockets5 / 10
Socket.IO integrated with HTTP server (server.js:15-31), room-based pub/sub pattern with `join-poll` client event (socket.handler.js:17-21), server emits `analytics-updated` (full analytics object) and `new-response` (pollId + totalResponses) on response submission (response.service.js:130-150). DashboardPage joins all user's polls and updates response counts in real-time (DashboardPage.jsx:20-25, 35-57). AnalyticsPage joins poll room and listens for analytics-updated (AnalyticsPage.jsx:25-34). Cleanup via socket.off on unmount. setIO/getIO singleton pattern. Gaps: only 1 incoming event and 2 emitted events, no publish/delete/status events, no leave-poll or room cleanup, no reconnect room re-join logic, no debouncing, no socket authentication, analytics page updates useless without working API, no socket on public voting page.
Code Quality & Project Structure4 / 10
Clean monorepo with client/server separation, consistent module pattern, but substantial quality issues: 10+ LLM hallucination import artifacts (id/email from "zod/v4/locales", text from "express", date/includes from "zod" — all nonexistent exports), duplicate getPublicPoll across auth.api.jsx and poll.api.jsx, dead breadcrumbMap.jsx file (Breadcrumbs.jsx has inline duplicate), 10 console.log statements in production (including JWT decoded payload at auth.controller.js:88 and raw cookies at line 74), "Invalid creditals" typo (auth.service.js:53), "hadleDelete" typo (DashboardPage.jsx:73), "createdAT" key misspelled (auth.controller.js:17), zero automated tests, no .env.example files, bcrypt AND bcryptjs both installed, App.jsx exports unused App component, logout bug (Navbar.jsx:9). Consistent module architecture and Prisma schema design prevent score from being lower.
86
Pollify - Live Polls for Feedback
Chittaranjan Shit · @chittaranjanshit2_412ca1f0
43
Pollify is a functional full-stack polling application (702 lines backend + 2747 lines frontend) with working deployments on both Vercel frontend and Railway backend. It implements all core requirements: JWT auth, poll creation with multi-question support, response collection with validation, analytics dashboard, result publishing, and Socket.IO real-time updates. The implementation is solid but not deep — all features work on the happy path but lack production hardening: no rate limiting, no Helmet, hardcoded secret fallbacks, no poll editing, no service layer, no tests, and no TypeScript. Total score: 43/100 (median).
Authentication & Access Control6 / 10
Solid JWT-based auth: register/login with bcrypt (10 rounds), auth middleware (authenticate, optionalAuth), AuthContext with token persistence and axios interceptor, ProtectedRoute/GuestRoute frontend guards. Backend auth routes at /api/auth (register, login, me). Gaps: no logout endpoint on backend (client calls non-existent POST /api/auth/logout at auth.route.js: only has register/login/me), JWT_SECRET has hardcoded 'secret' fallback in auth.middleware.js:13 and :33, no rate limiting on auth endpoints, no refresh tokens (single 7d token), no Helmet/CSRF, tokens stored in localStorage.
Poll Creation & Question Management6 / 15
Functional poll creation at CreatePoll.js (260 lines): dynamic questions/options with add/remove, mandatory/optional toggle, anonymous/requiresAuth toggles, expiry datetime picker, client-side validation (empty title/text checks). Server validates title + min 1 question + min 2 options + non-empty option texts (polls.controller.js:15-24). Option count capped at 8. Gaps: no poll editing endpoint exists (only create, no update), no slug generation (uses UUID shareCode), no draft status, no form library (raw useState).
Response Collection Flow6 / 15
Solid response submission flow: validates active/expired/published state (pollResponse.controller.js:17-25), checks requiresAuth, validates mandatory questions (line 41-47), validates option indices (lines 50-58), prevents duplicate auth-user responses (lines 33-38). Frontend handles 5 view modes (respond, submitted, expired, results, error) with loading states. Gaps: no database-level unique index for deduplication, ipAddress stored but never checked for anonymous dedup, no transactions (option count updates separate from response creation), no rate limiting, shareCode passed in request body rather than URL params.
Analytics & Feedback Dashboard5 / 15
Analytics endpoint computes question-wise option counts with percentages, top-option ID, total responses, and recent 5 responses (polls.controller.js:115-183). Frontend (PollAnalytics.js, 240 lines) shows stat cards, per-question progress bars, live counter, copy-link, and publish button. Published results publicly viewable via shared link with bar charts. Gaps: no CSV/JSON export, no individual response browsing, no trend data or time-series, option counts stored on Poll document (not computed from Response collection), shareCode missing from analytics response (hacked via separate getPollFromId call), no peak activity metrics, no N+1 query optimization.
Frontend Experience6 / 10
Consistent dark theme with CSS variables (App.css, 296 lines), 7 React Router v6 routes + 404 fallback with ProtectedRoute/GuestRoute guards. Handles loading (spinner), error (alert banners), empty states, and auth-required states on all pages. Responsive layout with hamburger menu (Navbar.js:72-74), fade-in animations, copy-to-clipboard with "Copied!" feedback (2000ms), progress bar gradients. Gaps: no form library, no toast system (uses alert()), only 1 git commit, no pagination, no icon library (relies on emojis), no data caching layer.
Backend Architecture & API Design5 / 15
Clean module structure (auth/polls/pollResponse) with route/controller/model separation and Mongoose schemas with validation + virtuals. CORS configured, middleware-based auth. Gaps: no input validation library (manual ad-hoc validation everywhere), no rate limiting, no Helmet/security headers, no centralized error handling, raw error.message leaked to clients in 6 controllers, JWT_SECRET hardcoded as 'secret' fallback in auth.middleware.js (2 locations), no logout endpoint, no pagination on getMyPolls, no graceful shutdown, no service layer (all logic in controllers), no .env.example file.
Real-Time Updates Using WebSockets5 / 10
Socket.IO server with room-based pub/sub (socket.config.js, 19 lines): join-poll/leave-poll events. Server emits new-response on submission (pollResponse.controller.js:83-90) and poll-published on publish (polls.controller.js:218). Client useSocket hook (41 lines) with singleton pattern, join/leave cleanup, and event listener management. Gaps: only 2 event types, no socket authentication, no reconnection with room re-join, pure invalidation signal (client refetches HTTP, no data pushed), no debouncing (every event triggers full analytics HTTP refetch), no typed events, no fallback polling.
Code Quality & Project Structure4 / 10
Clean client/server separation with consistent naming conventions. CSS design system with variables. Good README with setup instructions and tech stack. Gaps: no TypeScript, no automated tests, no ESLint/Prettier config, no .env.example file, console.log in 5 production server locations, JWT_SECRET hardcoded as 'secret' fallback in middleware but not controllers (inconsistency), duplicate JWT sign logic in register/login controllers, client logout calls non-existent backend endpoint, only 1 git commit, no service layer, CSS distributed across 6 files with some duplication.
87
PulseBoard
Trilok · @triloksh07
43
PulseBoard is a functional full-stack polling platform (MERN + Socket.IO) with solid core architecture. The server-side is well-layered with TypeScript, comprehensive Zod validation, good security middleware, and detailed response validation. The client has clean UI with good state coverage. Strengths: authentication flow with JWT httpOnly cookies, thorough server-side response validation, decent analytics computation, clean project structure. Weaknesses: critical Socket.IO event name mismatches (underscore vs colon) meaning real-time features likely don't work end-to-end, client is pure JavaScript while server is TypeScript, several dead code files, console.log debug statements, client-side uses imperative useState instead of the claimed React Hook Form, and zero automated tests. Score: 43/80, aligned with the cohort median of 43.
Authentication & Access Control6 / 10
Auth system has register/login/logout/me endpoints with JWT (httpOnly/secure/sameSite cookies, 7-day maxAge), bcryptjs 12-round hashing, Zod input validation (auth.validation.ts, 6-16 chars). requireAuth middleware checks cookie+Bearer header, fetches user from DB (auth.middleware.ts:22-26). optionalAuth enables anonymous+authenticated response modes enforced server-side (response.service.ts:127-129). Zustand authStore with ready flag and Axios 401 interceptor (api.js:15-22). ProtectedRoute gate with loading state (ProtectedRoute.jsx:11-24). Gaps: no refresh token rotation (single JWT, dev fallback "dev-secret-change-me" at env.ts:9), no email verification, no password reset, no CSRF protection, no rate limiting on auth endpoints specifically.
Poll Creation & Question Management6 / 15
PollBuilderPage.jsx (505 lines) supports dynamic questions with add/remove, per-question required/allowMultiple toggles, dynamic options with add/remove (min 2 enforced), points for quiz mode, correctAnswer marking. Poll-level settings: title, type (poll/quiz), responseMode (anonymous/authenticated), expiresAt datetime picker. Server-side Zod validation (poll.validation.ts:30-47) enforces 3-160 char title, future expiry, min 1 question, min 2 options/question, with superRefine for single-choice correctAnswers. QR code generation (QRCodeCanvas) with copy-to-clipboard and download. Gaps: no form library (imperative useState instead of claimed React Hook Form), no draft status (polls created as "active"), no client-side field-level validation, poll update overwrites all questions on save.
Response Collection Flow6 / 15
Response submission has thorough server-side defense: poll existence check (response.service.ts:104-107), published/draft/expired status gates (109-125), authenticated mode enforcement (127-129), duplicate prevention for authenticated users (131-139 via Response.exists), answer payload validation covering required questions, single-choice enforcement, valid option verification, and invalid question detection (54-96), plus quiz score calculation (24-52). Expired polls auto-save status back to DB. Zod validation (response.validation.ts) validates params+BODY. PublicPollPage.jsx (241 lines) handles loading/error/not-found/success/unavailable states. Gaps: no duplicate prevention for anonymous users (only checks when user truthy), race condition in duplicate check (no unique compound index), no client-side mandatory question validation before submission, authenticated mode just shows warning text rather than gating submission.
Analytics & Feedback Dashboard6 / 15
Analytics service (analytics.service.ts, 148 lines) computes: total responses, per-question option counts/percentages (lines 13-59), hourly timeline buckets (62-73), quiz scores (max/average/highest/lowest, 104-146), participation rates. AnalyticsPage.jsx (149 lines) has Recharts BarChart (option distribution), LineChart (timeline), stat cards, and leaderboard. ResultsPage.jsx (176 lines) renders per-question bar charts with progress bars for public published results. DashboardPage.jsx (128 lines) lists polls with stats and empty state. Publish endpoint (poll.service.ts:142-152) toggles published=true. Gaps: the analytics BarChart aggregates ALL options across ALL questions into one flat chart (misleading), no CSV/export, no individual response viewing, completionRate is hardcoded to 100% (analytics.service.ts:122), no per-question bar charts on analytics page.
Frontend Experience5 / 10
8 routes via React Router with AppShell (authenticated) and PublicShell layouts, custom Button/Card/Field/Badge/Toast UI components with variant system, consistent ocean/ink/slate color scheme via Tailwind. All pages handle loading, error, empty, and edge-case states (expired poll, unpublished, auth required). Responsive layout with Tailwind breakpoints. QR code sharing with download and copy-to-clipboard. Gaps: no form library (README claims React Hook Form but code uses imperative useState throughout), console.log debug lines in authStore.js:10, PollBuilderPage.jsx:60-71, PublicPollPage.jsx:33; Toast component defined but unused; no dark mode; no Framer Motion or page transitions; skeleton loaders absent (just "Loading..." text); no confirmation dialogs; analytics aggregated bar chart is a misleading visualization.
Backend Architecture & API Design6 / 15
Clean TypeScript architecture (strict mode, NodeNext) with routes→controller→service→model layering across 5 modules (auth/polls/responses/analytics/leaderboard). Security middleware: helmet, CORS with credential allowlist (app.ts:18-24), express-rate-limit (500/15min, line 28-35), JSON body limit 1mb. All endpoints validated via Zod schemas (6 schema files). Mongoose schemas with proper indexes: shareCode unique, createdBy compound, pollId+submittedAt compound, pollId+score+completionTime compound. Custom HttpError class, centralized errorHandler with ZodError handling. bcryptjs 12 rounds, JWT with configurable expiry. Gaps: no MongoDB transactions, @ts-expect-erro/@ts-ignore in password.ts:1-2, user typed as `any` in middleware (auth.middleware.ts:11), hash.ts utility unused, base.dto.ts unused, expired poll save side effect during validation (response.service.ts:122-124), no API versioning.
Real-Time Updates Using WebSockets4 / 10
Socket.IO infrastructure is well-architected: typed SOCKET_EVENTS constant (events.ts, 10 events), room system (rooms.ts), poll join/leave handlers with ack pattern (poll.handlers.ts:10-78), leaderboard request handler (leaderboard.handlers.ts:7-31), separate emitter layer (poll.emitters.ts, analytics.emitters.ts). Server integration: response controller emits responseCountUpdated after submission (response.controller.ts:11-14), poll controller emits pollPublished (poll.controller.ts:49-53). Client singleton socket (socket.js), AnalyticsPage listens for live updates. CRITICAL BUGS: event name mismatches between client and server — client uses underscore_separated names (`join_poll`, `analytics_updated`) while server uses colon:separated names (`poll:join`, `analytics:updated`) across 4 events. `emitAnalyticsUpdated` defined but never called — only response counts emitted. No socket authentication (anyone can join any room). console.log and commented-out test code in poll.handlers.ts:24-38.
Code Quality & Project Structure4 / 10
Monorepo with client/ and server/ separation, Docker support (docker-compose.yml, Dockerfile, .dockerignore), ESLint configured for both packages. Server uses TypeScript strict mode. Both packages have .env.example files. However: client is pure JS/.jsx (no TypeScript) while server is TS — inconsistent. Dead code: useCurrentUser.js hook never imported, Toast.jsx component never used, hash.ts utility unused, base.dto.ts unused, commented-out error function in apiResponse.ts:27-33. Sloppy typing: `@ts-expect-erro` typo + `@ts-ignore` in password.ts, `user?: any` in middleware (auth.middleware.ts:11), pervasive `any` in analytics service (6 occurrences). console.log debug lines in 4 files. Commented-out code in 5 files. Zero automated tests. Socket event name mismatches between client/server are a quality bug. 1,612 server LOC + 1,771 client LOC.
88
SmartPoll Web App
Saksham Agrawal · @sakshamagrawal560_795aeedf
43
SmartPoll is a working MERN polling platform with JWT authentication, multi-step poll creation wizard, response collection with mandatory question enforcement, basic analytics dashboard, and Socket.IO real-time updates. The frontend has good visual polish with dark/light theme, responsive design, and thorough state coverage. However, the implementation has several critical gaps: no publish-results feature (even though the Poll schema defines a "published" status), no input validation on poll/response endpoints, no rate limiting or security hardening (Helmet missing, tokens in localStorage), wrong HTTP status codes (412 used for 403/404), a structural bug in App.js where the 404 handler is nested inside the health-check route, empty env.example and stale unused files, and no automated tests. Solid foundation but lacks production-grade hardening and misses key project requirements around result publishing.
Authentication & Access Control6 / 10
JWT dual-token auth (access 15m + refresh 7d) with bcrypt 12 rounds and SHA256-hashed refresh token storage (auth.service.js:18-98). httpOnly/secure/sameSite:strict refresh cookie (auth.controller.js:17-22). Frontend AuthContext with token persistence and auto-verification via /auth/me (AuthContext.jsx:11-23). Axios interceptor for automatic 401 refresh with concurrent-request deduplication (api.js:19-60). Bearer token auth middleware (auth.middleware.js:6-26) and optional-auth middleware (optional-auth.middleware.js:8-33). Missing: no Helmet, no rate limiting on auth endpoints, tokens in localStorage, no CSRF protection, empty env.example (1 blank line), email verification/forgot-password all commented out, register sets isVerified=true immediately bypassing the verification flow.
Poll Creation & Question Management6 / 15
4-step wizard UI (CreatePollPage.jsx, 505 lines) with dynamic questions/options add/remove, per-step client-side validation (title required, question text required, min 2 non-empty options, expiry required), anonymous toggle, datetime-local picker, and review step. EditPollPage.jsx (473 lines) mirrors the same wizard for updating polls via PUT /api/poll/:id. Backend poll model supports questions with text/options/isMandatory subdocuments (poll.model.js:36-42) and auto-access code generation via generatePollCode(). Gaps: no server-side input validation on poll creation (no Joi/Zod on POST /poll route), isMandatory toggle has no UI control (always defaults to true), no draft status usage, no slug/permalink, no form library (all useState), no max/min question count enforcement server-side.
Response Collection Flow6 / 15
Defense-in-depth: server checks poll status !== "active" and expiresAt < Date.now() before accepting responses (response.service.js:10-17). Duplicate vote prevention for authenticated users via Response.findOne (lines 20-29). Mandatory question validation iterates poll.questions checking isMandatory flags (lines 32-42). Answer normalization handles both selectedOptions[] and selectedOption payload shapes (lines 47-55). Frontend VotePage.jsx (297 lines) validates anonymous poll restrictions, mandatory questions, guest name, and shows differentiated states (loading/error/submitted/expired/unauthenticated). Gaps: no duplicate prevention for anonymous voters, no validation that submitted options belong to the poll, no MongoDB unique compound index for race-condition safety, no rate limiting on submissions, poll model's "published" status exists but never used - no publish-results feature or endpoint.
Analytics & Feedback Dashboard5 / 15
Analytics endpoint computes per-question option vote tallies via brute-force iteration over all responses (analytics.service.js:60-115). Frontend AnalyticsPage.jsx (219 lines) renders stat cards (Total Votes, Questions, Status), per-question bar charts with colored gradients, percentage calculations, leading-option trophy indicator, and empty state for zero votes. Dashboard (DashboardPage.jsx, 357 lines) lists polls with status badges, copy-link, edit/analytics/close/delete actions, and stats bar (Total/Active/Questions). Gaps: no publish-results feature (project requirement unmet), analytics page is publicly accessible to anyone who knows the MongoDB _id (no creator-only restriction), no MongoDB aggregation pipeline (O(n*m) JS iteration), no CSV/export, no individual response viewing, no trend/time-series data, no peak activity metrics, dashboard does not show per-poll response counts.
Frontend Experience6 / 10
Consistent glass-morphism dark/light theme with 15 CSS custom properties in each mode (index.css), ThemeContext with localStorage persistence, ThemeToggle component with sun/moon icons. Responsive design with mobile hamburger navbar, kebab menus with click-outside-to-close (DashboardPage.jsx:48-68), and sm/md/lg breakpoints across components. Lenis smooth scrolling (lenis.js). Thorough state coverage: Spinner/PageLoader (11 loading states), EmptyState for no polls (DashboardPage.jsx:170-176) and no votes (AnalyticsPage.jsx:140-157), error screens with differentiated messages (VotePage.jsx:108-138), ConfirmModal for destructive actions (components/ui/index.jsx:58-76). Toast notifications throughout. Password strength indicator bar on registration. Gaps: no form library (all imperative useState), no skeleton loaders, isMandatory toggle not in UI, EditPollPage has less polish than Create, no PWA/offline support, and app.get("/") handler in server/src/App.js has the catch-all 404 route nested inside it (structural bug).
Backend Architecture & API Design5 / 15
Modular architecture with clean auth/poll/response/analytics module separation, service layer pattern, and Joi DTO validation on auth endpoints (register.dto.js, login.dto.js). Mongoose schemas with proper field types, constraints, and pre-save hooks (auth.model.js bcrypt hash). ApiError class with static factory methods (api-error.js). Background poll expiry job via setInterval (pollExpiry.job.js). CORS restricted to single frontend origin. Key gaps: no input validation on poll creation or response submission endpoints (no Joi/Zod, raw req.body passes through), no rate limiting anywhere, no Helmet security middleware, ApiError.forbidden() and ApiError.notfound() both use HTTP 412 instead of 403/404 (api-error.js:19-24), server/src/App.js has catch-all 404 handler nested inside GET("/") callback (lines 41-50, structural bug), no MongoDB sanitization, analytics uses brute-force O(n*m) iteration instead of aggregation pipeline, inconsistent error handling (poll.service uses raw `new Error` for ownership checks while auth.service uses ApiError), no pagination on getMyPolls, empty env.example file.
Real-Time Updates Using WebSockets5 / 10
Socket.IO server integrated with HTTP server (server.js:12-37) using room-based pub/sub: clients emit "join-poll" to join pollId room, server emits "analytics-update" with {pollId, totalResponses} after each response submission (response.controller.js:14-21). Frontend socket singleton (socket.js) with lazy init, room join on AnalyticsPage mount, listener for analytics-update that triggers HTTP refetch for full breakdown (AnalyticsPage.jsx:47-58). Live indicator with green pulse dot and last-ping timestamp (lines 101-112). Proper disconnect cleanup on unmount (leaveSocket). Gaps: only one event type (analytics-update), no data pushed over socket (client refetches via HTTP), no socket integration on VotePage for live vote counts, no debouncing, no reconnection room re-join logic, no socket authentication, no room leave event, no events for poll publish/close/delete, no validation of pollId on join-poll event.
Code Quality & Project Structure4 / 10
Clean client/server separation with modular backend (auth/poll/response/analytics). ES modules throughout. README with API overview and setup instructions. But significant quality issues: no TypeScript, zero automated tests (Glob found no test files), env.example is completely empty (1 blank line), server/src/App.js has the catch-all 404 handler syntactically nested inside the GET("/") callback (lines 41-50 — blocking structure bug), analytics.model.js is an empty file (0 bytes), components/ui/Loaders.jsx contains stale duplicate Spinner/PageLoader/EmptyState definitions never imported anywhere, create and edit poll pages share nearly identical ~400+ lines of wizard code duplicated across files, inconsistent error handling (ApiError vs raw `new Error`), ApiError uses wrong HTTP status codes (412 for 403/404), console.log left in production socket handlers, multiple commented-out code blocks throughout, no ESLint/Prettier config for server, no .env.example with real variable names.
89
U
Voices
uday k · @krishnauday200301
43
Voices (aka PollPulse) is a full-stack polling platform with React/Tailwind frontend and Express/Mongoose/Socket.IO backend, deployed and working at https://voices-1-kixy.onrender.com/. Covers all core requirements: JWT auth with registration/login, multi-question poll creation with mandatory/optional toggling, response collection with expiry/auth validation, analytics dashboard with per-question breakdowns and publishing, Socket.IO real-time updates, and a polished Tailwind UI with dark mode. Solid fundamentals but missing depth across all areas: no rate limiting or security hardening, no poll editing, no exports, no tests/TypeScript/linting, embedded responses instead of separate collection, and a broken response rate calculation. Overall score 43/80 places it at the median of the cohort.
Authentication & Access Control5 / 10
Functional auth with registration (bcrypt 12 rounds), login, JWT tokens (7d expiry), and protected routes via requireAuth middleware (backend/src/middleware/auth.js). Frontend AuthContext with localStorage token persistence, ProtectedRoute component, and login/register UI in Login.jsx (210 lines). Anonymous vs authenticated poll mode enforced server-side in submitResponse (backend/src/controllers/pollController.js:113). Critical gaps: no Helmet, no rate limiting on auth endpoints, no CSRF protection, JWT stored in localStorage (XSS vulnerable), hardcoded JWT secret fallback 'dev-pollpulse-secret-change-me' in config/env.js:14, no email verification or password reset.
Poll Creation & Question Management6 / 15
CreatePoll.jsx (327 lines) supports dynamic multi-question creation with add/remove, dynamic options per question with add/remove, mandatory/optional toggle per question via checkbox, response mode selection (anonymous/authenticated), and expiry datetime picker. Client-side validation checks title (min 3 chars), expiry in future, question text (min 3), and options (min 2 non-blank). Server-side validatePollInput (pollController.js:11-29) validates all fields with descriptive error messages. Mongoose schema enforces min 1 question, min 2 options per question (Poll.js:30-37, 122-128). Significant gaps: no poll editing endpoint (no PATCH/PUT exists), no slug/permalink generation, no draft status, no preview capability.
Response Collection Flow6 / 15
PublicPoll.jsx (329 lines) renders response form with radio buttons per question, validates required questions client-side with inline error messages, and constructs proper {answers: [{questionId, optionId}]} payload. Server-side submitResponse (pollController.js:107-138) enforces: poll existence, published/expired gating, authenticated mode requirement, required question validation via validateAnswers (checks option ownership in poll), and authenticated duplicate prevention (checks existing responses with same user ID). Returns analytics in response. Critical gaps: no duplicate prevention for anonymous voters, responses embedded in poll document (not separate collection), no rate limiting on submission, submitted state exposes full analytics to any respondent (bypassing publish-results requirement), no IP-based uniqueness.
Analytics & Feedback Dashboard5 / 15
Dashboard.jsx (312 lines) shows poll list sidebar with response counts and analytics panel. AnalyticsSummary.jsx (95 lines) displays stat cards (total/anonymous/authenticated responses, response rate), per-question option breakdowns with counts, percentages, and CSS progress bars, and recent responses list. Server-side buildAnalytics (analyticsService.js:32-83) computes per-question option tallies, percentage distributions, and answered/skipped counts. Publish endpoint (POST /:pollId/publish) sets published flag and publishedAt. Published results become publicly visible. Critical gaps: response rate calculation is nonsensical (`totalResponses / Math.max(totalResponses + 1, 1) * 100` always ~50%), no CSV/export, no time-series or trend data, no chart library (only CSS bars), no peak activity metrics, no individual response viewing.
Frontend Experience6 / 10
Professional Tailwind CSS design with dark/light mode toggle respecting system preferences (Layout.jsx:7-24). Six routes with React Router: landing, login, dashboard, create, public poll, 404 redirect. All pages handle loading spinners, error states, and empty states. Responsive layout with sticky header and footer. Landing page (261 lines) has hero, features grid, and CTA sections. Dashboard has sidebar poll list with main analytics panel. Copy-to-clipboard for share links. Gaps: no form library (manual useState), no animation library, uses alert() for clipboard feedback instead of toast, no confirmation dialogs, no data caching layer, broken "Response Rate" calculation displayed in UI.
Backend Architecture & API Design5 / 15
Clean MVC structure with routes→controllers→services→models separation across 15 source files. Mongoose schemas with embedded subdocuments (Question, Option, Answer, Response within Poll model) and field-level validation (Poll.js:146 lines, User.js:38 lines). Auth middleware with optionalAuth (attaches req.user or null) and requireAuth (401 gate). Centralized error handler for ValidationError and duplicate key errors (errorHandler.js:30 lines). CORS with dynamic origin checking. Six RESTful poll endpoints + three auth endpoints + health check. Critical gaps: no input validation library (manual string-based validation only), no rate limiting, no Helmet, no pagination on GET /polls, responses embedded in poll document (potential document size issues), no poll edit/delete endpoints, hardcoded JWT secret fallback, no request logging middleware.
Real-Time Updates Using WebSockets6 / 10
Socket.IO server integrated with HTTP server in socket/index.js (54 lines). Room-based pattern: clients join `poll:{pollId}` rooms via poll:subscribe, servers emit analytics:update with full buildAnalytics payload on response submission (pollController.js:133) and publish (pollController.js:164). Client connections in Dashboard.jsx (lines 22-67) and PublicPoll.jsx (lines 43-68) with reconnect params, proper cleanup on unmount (unsubscribe + disconnect), and reconnect re-subscription in 'connect' handler. HTTP polling fallback at 2-second intervals on Dashboard (lines 70-94). Gaps: only one event type (full analytics push, no deltas), no socket authentication, duplicated socket setup code across two pages, no debouncing, no events for poll status changes, limited to analytics only.
Code Quality & Project Structure4 / 10
Clean monorepo with backend/ and frontend/ separation, consistent naming conventions (PascalCase components, camelCase functions, kebab-case CSS classes). API client module (client.js:122 lines) with interceptor pattern, token management helpers, and convenience methods. Custom hooks (useAsync, useForm, useDebounce) in hooks/useAsync.js (106 lines). .env.example files for both packages, .gitignore, render.yaml deployment config, and RENDER_DEPLOY.md guide. Total ~2,729 lines of source across all files. Critical gaps: no TypeScript, no automated tests (no test scripts), no ESLint or Prettier config, socket connection code duplicated verbatim in Dashboard.jsx and PublicPoll.jsx, useDebounce missing useEffect import, Poll model has commented-out indexes (dead code), console.log debug statements in production, no README at project root level.
90
P
feedloop
Piyush Kumar · @piyush
43
feedloop has a strong, well-architected backend with thorough auth (bcrypt timing-safe, JWT + refresh rotation, Google OAuth), robust response validation, comprehensive analytics computation, and well-designed WebSocket infrastructure. The Prisma schema is clean with proper relationships and enum-based status/visibility tiers. However, the frontend is significantly incomplete: the analytics dashboard is a placeholder, the results page route doesn't exist, the Dashboard and Explore pages are single-line stubs, and the WebSocket backend has zero frontend client connection. Deployment is not accessible (Vercel 404). The core polling flow (create → respond) is functional, but analytics visualization and real-time updates — key requirements — are backend-only with no user-facing surface.
Authentication & Access Control6 / 10
Full registration (bcrypt cost 12) + login (timing-safe dummy hash) + Google OAuth (verified ID tokens, auto-account creation) in auth.service.ts:65-268. JWT access (jose HS256) + refresh token rotation with atomic revoke/create (auth.service.ts:143-155). Logout revokes tokens. Auth middleware attaches req.user (authenticate.ts:20-46). Soft-auth pattern on public endpoints (poll.router.ts:38-49, 88-95; response.router.ts:14-21). Frontend AuthProvider (context/auth.tsx) + Axios interceptor with refresh queuing (client.ts:32-113). Anonymous/authenticated poll modes enforced server-side (response.service.ts:191-196, 206). Gaps: no Helmet, CORS wide open (app.ts:13), tokens in localStorage (client.ts:13-17), no CSRF, no email verification flow, credentials in committed .env file.
Poll Creation & Question Management5 / 15
CreatePoll.tsx (444 lines) supports dynamic question/option add/remove with Framer Motion animations. Client-side validation checks title, questions (min 1), options (min 2), option text (lines 133-146). Settings: isAnonymous toggle, statsVisibility dropdown, maxResponses, expiresAt datetime picker (lines 354-416). Server-side Zod validation with nested question/option schemas enforcing min 1 question, min 2-10 options per question, text length limits (poll.schema.ts:5-42). Unique slug generation with collision avoidance loop (poll.service.ts:84-93). Transactional creation. Status machine: DRAFT→ACTIVE→CLOSED→PUBLISHED. Critical gaps: questions/options cannot be edited after creation (update only changes metadata in poll.service.ts:182-205), mandatory/optional toggle not exposed in UI (all questions hardcoded isRequired:true at line 155), no form library (plain useState hooks).
Response Collection Flow7 / 15
Thorough server-side defense-in-depth: active poll check (response.service.ts:56-58), expiry check (lines 59-61), required question validation (lines 97-103), option-to-question cross-validation via Map lookup (lines 106-118), duplicate question check (lines 88-94). Auth gate for requiresAuth polls (lines 191-195). Duplicate prevention: DB unique constraint {pollId, respondentId} + pre-check for authenticated (lines 131-140), IP-based best-effort for anonymous (lines 143-154). Anonymous mode respects poll.isAnonymous (line 206). Auto-close on maxResponses (lines 161-178). Request metadata capture (IP, UA, device, geo via requestMeta.ts). Transactional response+answers creation (line 202). Fire-and-forget WebSocket broadcasts after submit (response.controller.ts:37-39). Frontend validates all questions answered (PollView.tsx:91-94), handles loading/error/submitted states, shows status-specific UI (closed card, draft notice). Gaps: no client-side localStorage tracking for "already voted", no option-membership validation duplicated on client.
Analytics & Feedback Dashboard4 / 15
Backend analytics service (analytics.service.ts, 354 lines) is thorough: per-option counts+percentages via Prisma groupBy (lines 92-104), daily response timeline via raw SQL DATE_TRUNC (lines 138-153), device/country/region/city/browser/OS breakdowns with top-20 caps (lines 159-177). Three-tier stats visibility (VOTES_ONLY/BASIC/FULL) with conditional data fetching (lines 207-228). Public results endpoint with status gating (CLOSED shows question-text preview, PUBLISHED shows full results — lines 185-204). Owner-only full analytics endpoint (lines 268-349). Parallel data fetching via Promise.all. However: NO frontend analytics page exists — the Dashboard.tsx is a 3-line placeholder showing "Dashboard" text only, no route for /poll/:slug/results despite PollView linking to it (router.tsx has no such route), the usePollResults and usePollAnalytics hooks exist in api/analytics.ts but no component consumes them. The backend is strong but the feature is essentially unusable to end users.
Frontend Experience5 / 10
Polished landing page with scroll-triggered Framer Motion animations, dark gradient hero, 6-feature grid with icons, footer with links (Landing.tsx, 278 lines). CreatePoll page has dynamic form with animations, field validation, toggle/select/datetime controls (CreatePoll.tsx, 444 lines). PollView handles 5 states: loading spinner, poll-not-found error, submitted confirmation with checkmark animation, closed state with lock card, owner draft preview (PollView.tsx, 257 lines). Login/Register pages with Google OAuth buttons and Zod validation. Consistent shadcn-style UI with 40+ Base UI components. react-router v7 with 8 routes. Gaps: Dashboard.tsx and Explore.tsx are single-line placeholders, no analytics page UI, no results page route, App.tsx is dead code, index.html title says "frontend" not "feedloop". The app feels polished where implemented but approximately half the pages are stubs.
Backend Architecture & API Design7 / 15
Clean modular architecture: core/ (config, errors, middleware, utils, ws, cron) + modules/ (auth, poll, response, analytics) with controller-service-router separation. All routes validated via Zod schemas (auth.schema.ts, poll.schema.ts, response.schema.ts) with validate() middleware. Custom error hierarchy (AppError base + 5 subclasses in AppError.ts). Consistent API response envelope (apiResponse.ts). Structured pino logging with PII redaction (logger.ts). Rate limiting (200 req/15min global, 10 req/15min auth in production). Cron job for auto-expiry (expirePolls.ts). Prisma PostgreSQL schema well-designed with proper relations, enums (PollStatus, StatsVisibility, Role, AuthProvider), composite unique constraints, and cascade deletes (schema.prisma). asyncHandler wrapper catches async errors. Graceful shutdown on SIGINT/SIGTERM (index.ts:33-47). Gaps: no Helmet security headers, CORS unrestricted (app.ts:13), no tests, validate() returns only first Zod error (validate.ts:13 — poor UX), soft-auth pattern duplicated 3 times without extraction.
Real-Time Updates Using WebSockets4 / 10
Backend WebSocket implementation is thorough: Socket.io server with room-based pub/sub (io.ts:14-27), public room for anonymous viewers and owner room with JWT authentication + ownership verification (pollHandler.ts:70-102). 8 events: poll:subscribe/unsubscribe, poll:subscribe:owner/unsubscribe:owner, poll:stats_update (totalResponses+remaining for ACTIVE), poll:results_update (full results for PUBLISHED), poll:analytics_update (full analytics for owner), poll:error. Immediate hydration on subscribe (emitPublicUpdate/emitOwnerUpdate, lines 52-108). Broadcast helpers check for active subscribers before querying (lines 162-229). Fire-and-forget broadcasts from response controller (response.controller.ts:37-39). However: NO frontend WebSocket client exists — socket.io-client not in package.json, no connection code or event listeners anywhere in the frontend. The landing page advertises "real-time results" but the feature is non-functional end-to-end. Backend is ~8 quality but entirely unused.
Code Quality & Project Structure5 / 10
Backend TypeScript strict mode with noUncheckedIndexedAccess and exactOptionalPropertyTypes (tsconfig.json). Zero `any` types across both codebases. Clean folder separation (backend: core/modules, frontend: api/components/pages/context/hooks/layouts). Production-grade patterns: refresh token rotation with atomic Prisma transaction (auth.service.ts:143-155), Axios interceptor with concurrent-request deduplication queue (client.ts:32-113), poll state machine with VALID_TRANSITIONS map (poll.service.ts:20-25). Gaps: no root README (only Vite starter in frontend/), frontend tsconfig missing strict:true (tsconfig.app.json), no tests anywhere, parseZodErrors duplicated verbatim in Login.tsx and Register.tsx, error extraction pattern repeated 3 times, frontend .env committed to repo (contains Google Client ID), App.tsx and App.css dead code, Dashboard.tsx and Explore.tsx are 3-line stubs, 15+ shadcn UI components (accordion, calendar, drawer, pagination, etc.) imported but never used.
91
B
pollX
Bhargav · @bhargavshekhar
43
pollX is a solid full-stack polling platform with working authentication (JWT access+refresh), poll creation with dynamic questions/options, response collection with duplicate prevention, analytics dashboard, and Socket.io real-time updates — all deployed on AWS EC2. The auth implementation is the strongest feature with dual-token rotation and httpOnly cookies. However, several gaps hold it back: no poll editing, no server-side mandatory question enforcement, broken dashboard response counts (allPolls doesn't include vote counts), no rate limiting or security headers, no confirmation dialogs, no tests, and limited Socket.io scope (single event type). The codebase is clean and well-structured but lacks production polish. Total: 43/100, fitting the cohort median.
Authentication & Access Control7 / 10
Custom JWT auth with signup/signin (Zod-validated, bcrypt 10 rounds), dual-token strategy: 5-min access token + 7-day refresh token with SHA-256 hash rotation stored in DB (auth.services.ts:30-67). Refresh token in httpOnly/secure/sameSite cookie. Axios interceptor (api.ts:21-57) auto-refreshes on 401 with retry deduplication. Global authenticationMiddleware attaches req.user on Bearer token, requireAuth middleware gates protected routes (auth-middleware.ts). Anonymous vs authenticated poll modes enforced server-side in vote method (poll.services.ts:206). ProtectedRoute component gates frontend routes (ProtectedRoute.tsx). Gaps: access tokens in localStorage (TokenStore.ts), no rate limiting on auth endpoints, no Helmet/CSRF, no email verification or password reset, Google OAuth button is permanently disabled placeholder, ProtectedRoute only checks token presence not validity.
Poll Creation & Question Management6 / 15
CreatePoll.tsx (354 lines) implements dynamic question/option management: add/remove questions (max 20, enforced), add/remove options per question (min 2, max 10), mandatory/optional toggle per question, anonymous/authenticated mode toggle, expiry datetime picker with min 1-minute-ahead constraint. Client-side validation checks title, expiry date, question text, and min 2 filled options. Server-side Zod validation (poll.models.ts:9-17) enforces title, questions array, option count, expiry not in past. Transaction-based creation inserts poll + questions + options atomically (poll.services.ts:20-52). Significant gaps: no poll editing endpoint exists at all — cannot modify questions/options/title after creation; no slug/permalink for sharing; no draft status; individual useState hooks instead of form library.
Response Collection Flow6 / 15
Response flow with defense-in-depth: client validates mandatory questions (VoteView.tsx:17-22), generates crypto.randomUUID() sessionId for anonymous voters stored in localStorage (VoteView.tsx:25-31). Server validateAnswers (poll.services.ts:105-147) checks poll existence, question existence, option belongs to question, and no duplicate question in submission. Vote method checks expiry (line 202), published status (line 204), anonymous mode vs auth requirement (line 206). Duplicate prevention via in-memory check + DB unique constraints on (pollId, userId) and (pollId, sessionId). Transactional vote insertion (lines 149-168, 170-190). Socket.io emits on successful vote. Differentiated HTTP status codes (400/401/403/404/409). Critical gap: validateAnswers does NOT enforce mandatory question flag server-side — only stored during creation, never checked during submission. In-memory duplicate check fetches all votes before checking. Hardcoded "Already Voted" error message in client ignores server error details.
Analytics & Feedback Dashboard4 / 15
Analytics page (Analytics.tsx, 224 lines) shows total responses, question count, and completion rate in stat cards. Per-question breakdowns via QuestionCard component with option counts, percentages, winner detection (star icon), and skip counts for optional questions. Published results view (ResultView.tsx, 97 lines) shows per-question bar charts with percentages and winner highlighting. Dashboard has active/ended tab filtering. Significant gaps: allPolls() endpoint (poll.services.ts:65-70) doesn't include vote counts in query — dashboard response counts always show 0. Completion rate formula (Analytics.tsx:188-196) is convoluted and hard to interpret. No CSV/export, no individual response viewing, no time-series/trend data, no peak activity metrics. All analytics computed client-side from nested poll data — no server-side aggregation queries.
Frontend Experience5 / 10
Consistent dark theme with cohesive design across all 6 pages. Loading spinners on all data-fetching pages. Empty states handled (Dashboard: "No active/ended polls" with CTAs). Error states (poll not found, toast notifications on failures). Success states (vote submitted confirmation view, poll created toast). Expired/published poll views for public link. Password strength meter and match indicator on registration. Copy-to-clipboard with toast feedback. Sticky footers on CreatePoll and VoteView. Live indicator with pulsing animation on analytics. Responsive layout (dashboard: 1-2 column grid). Gaps: dark theme only despite ThemeProvider being set up; no confirmation dialogs for destructive actions (delete, publish); Google OAuth button permanently disabled placeholder; no pagination on dashboard; `Dashbord.tsx` filename typo; no form library (individual useState); some routes redirect to /dashboard instead of 404.
Backend Architecture & API Design5 / 15
Clean MVC architecture: routes→controllers→services with Zod validation on all inputs (7 schemas total). PostgreSQL schema via Drizzle ORM with 5 tables, proper foreign keys with cascading deletes, 8 indexes including 2 unique constraints. Transactional writes for poll creation and vote submission. ApiError class with 6 typed HTTP status codes (api-error.ts). ApiResponse class with consistent JSON format. Two-tier auth middleware (optional + required). CORS configured with credentials. Gaps: no rate limiting anywhere; no Helmet security headers; no request body size limits; no pagination on any endpoint; `console.error(error.stack)` leaks stack traces in production (app/index.ts:33); no poll editing functionality at all; error response format inconsistent (ApiError wraps message in object, unknown errors use plain string); no graceful shutdown; no logging library; Bearer token splitting uses `||` without array length validation (auth-middleware.ts:11).
Real-Time Updates Using WebSockets5 / 10
Socket.io integrated with same HTTP server (server.ts:13). Room-based pattern: server listens for join:poll/leave:poll client events, server emits vote:new on vote submission with pollId + totalVotes (poll.services.ts:211-214). Client connects on analytics page mount, joins poll room, listens for vote:new to trigger full HTTP refetch, properly cleans up on unmount (Analytics.tsx:49-70). autoConnect:false with connect_error logging (socket.ts). Live pulsing green indicator UI on analytics page. Gaps: only one custom event (vote:new) — no publish/delete/expiry events; no data pushed over socket (pure invalidation signal → HTTP refetch); no debouncing on event handler; no reconnection room re-join logic (socket reconnects silently lose subscriptions); no authentication on socket connections; no typed Socket.io events; socket only used on analytics page, not on voting page for live counts.
Code Quality & Project Structure5 / 10
Clean monorepo with client/ and server/ separation, TypeScript throughout both packages. Consistent naming conventions. Drizzle ORM with proper schema/relations separation. Environment variable examples for both packages (.env.example files). ESLint + Prettier configs for client. DTO types derived from Zod via z.infer. Service layer separation from controllers. DRY component reuse (QuestionCard in both analytics and results views). Gaps: formatDate/timeLeft functions duplicated between Analytics.tsx and PollCard.tsx; Dashboard defines separate local Poll type instead of using shared types; `Dashbord.tsx` filename has typo; NotFound.tsx component exists but never used in any route; no automated tests; TokenStore stores user as UUID string but types as UserType object; publish field allowed in createPoll payload but client always sends false; Google OAuth SVG duplicated in Login.tsx and Register.tsx (identical 5-path inline SVG); no .prettierrc or eslint for server.
92
A
AXEPOLL
AXEPOLL · @rushil.gorasia_17198aa7
42
AXEPOLL is a functional full-stack polling platform with a polished dark-themed UI, solid session-based authentication (local + Google OIDC), and working Socket.io integration. Deployed at axepoll.axemoth.com (both frontend and backend verified accessible). The app handles core flows: create polls with questions/options, share via link/QR, vote, and view analytics with bar/pie charts. However, several critical features are missing or incomplete: no publish results feature (isPublished field unused), no duplicate vote prevention, the mandatory/optional question toggle is absent from the create form UI, no input validation library is actually used (Zod installed but never imported), no tests, no TypeScript, and the Socket.io implementation is limited to a single new_vote event on the analytics page only. Overall: good frontend polish and architecture with substantial backend engineering gaps.
Authentication & Access Control7 / 10
Solid session-based auth with email/password (bcryptjs 10 rounds, auth.js:14-68) + Google OIDC with PKCE/S256 code challenge (auth.js:76-158). PostgreSQL-backed sessions via connect-pg-simple with httpOnly/secure/sameSite cookies (app.js:60-76). Middleware enforces requireSession (middleware/requireSession.js) and requirePollOwner with DB ownership check (middleware/requirePollOwner.js). Anonymous vs authenticated poll modes enforced server-side (responses.js:33-34). Rate limiting on auth + vote endpoints (app.js:26-37). AuthContext provides login/signup/logout with ProtectedRoute wrapper (AuthContext.jsx, App.jsx:13-20). Gaps: no email verification, no password reset, no Helmet/CSRF, Zod installed but unused for validation, .env.example has mismatched OIDC variable names.
Poll Creation & Question Management5 / 15
CreatePoll.jsx (323 lines) has dynamic questions with add/remove, dynamic options (min 2), anonymous toggle, datetime-local expiry picker defaulting to 24h. Server-side transaction-based creation (polls.js:20-50) with nanoid slug generation and nested questions+options inserts. PUT /api/polls/:id for metadata updates, DELETE with cascade. Key gaps: NO mandatory/optional toggle in CreatePoll UI — isMandatory field exists in DB schema but is never exposed in the form or sent in payload (CreatePoll.jsx:87-91). NO poll editing UI exists in frontend (PUT endpoint has no client). Zero input validation library usage (Zod is a dependency but never imported). No question/option field validation server-side. No draft status.
Response Collection Flow5 / 15
Vote.jsx (265 lines) renders poll by slug with option selection buttons, Framer Motion animations, and all UI states (loading, 404, auth-required redirect card, success/voted screen). Client-side checks all questions answered (all-or-nothing, ignoring isMandatory). Server validates poll existence, isActive, expiresAt, and authenticated-only mode (responses.js:16-35). Transaction-based submission with Socket.io broadcast. Critical gaps: NO duplicate vote prevention — same user can submit unlimited responses (no unique index on {pollId, respondentId}, no localStorage tracking). sessionToken column exists but NEVER populated (responses.js:43-46). No server-side validation that questionIds/optionIds belong to the poll. No mandatory question enforcement server-side. No per-question single-option enforcement.
Analytics & Feedback Dashboard5 / 15
Analytics.jsx (564 lines) with 3 stat cards (total responses, questions, answer rows), participation insights section, question-wise summary table with leading option percentages, per-question gradient bar charts (evilcharts), donut pie charts, animated option breakdown bars with Framer Motion. SQL aggregation for option counts (analytics.js:22-30). Zero-state handled (no responses). Critical gap: NO publish results feature — isPublished column exists in DB schema (schema.js:25) but no endpoint sets it and no UI exists for publishing. Analytics is creator-only with no public results view. No CSV export, no individual response viewing, no trend/time-series data, no anonymous vs authenticated breakdown. textResults always empty (answers table has no text field).
Frontend Experience6 / 10
Polished dark theme (zinc-950 + cyan accent) with glassmorphic cards and Framer Motion throughout (page transitions, question add/remove via AnimatePresence, option selection feedback, animated progress bars). 6 routes with ProtectedRoute, comprehensive state handling: loading spinners, 404 errors, empty states (dashboard CTA, analytics "no responses"), auth-required redirect with return-to state, success states ("Response Captured"), toast notifications (sonner). Responsive layout, QR code + copy-to-clipboard sharing, animated landing page with stats counter. Gaps: no form library (individual useState hooks in CreatePoll), no form validation for questions/options, browser confirm() only for delete (no custom dialog), evilcharts "use client" directives are Next.js patterns in Vite, no dark/light toggle, no mobile hamburger menu.
Backend Architecture & API Design5 / 15
Clean Express 5 architecture with route files (auth, polls, responses, analytics), well-defined Drizzle ORM schema (106 lines with 6 tables, relations, indexes, foreign keys with cascade), transaction-based writes for poll creation and response submission. Rate limiting (general/auth/vote tiers), CORS with allowed origins, session-based middleware (requireSession, requirePollOwner). Cron job for auto-expiry every minute (expiryJob.js). RESTful API design. Critical gaps: Zod is listed as dependency but NEVER imported — zero input validation anywhere. No server-side validation of request bodies beyond basic existence checks. No Helmet/security headers. No body size limits. Analytics endpoint has N+1 query issue. Response submission doesn't validate that questionIds/optionIds belong to the poll. No health check endpoint. No graceful shutdown.
Real-Time Updates Using WebSockets4 / 10
Socket.io server initialized on HTTP server (socket.js, 59 lines) with room-based pattern: clients join/leave `poll_${pollId}` rooms. Server emits `new_vote` event with pollId+responseId after each submission (responses.js:63-67). Analytics page (Analytics.jsx:72-88) connects, joins room, listens for new_vote, refetches HTTP analytics on event, and properly cleans up on unmount. Live connection indicator badge in header. Gaps: only 1 event type (new_vote) — no broadcast on poll expiry, publishing, or deletion. Pure invalidation signal (client refetches HTTP, no data pushed). No reconnection room re-join logic. No socket authentication. Only analytics page uses sockets (Vote page has none). No debouncing on refetch. Cron expiry job updates DB silently without Socket.io broadcast.
Code Quality & Project Structure5 / 10
Clean monorepo with client/ and server/ separation, well-organized folders (server: db/lib/middleware/routes/jobs, client: pages/components/context). Consistent naming, documented Drizzle schema with inline comments and relations. ESLint on client, .gitignore, docker-compose.yml, GitHub Actions CI/CD. vercel.json for SPA routing. README.md with setup instructions. Gaps: no TypeScript (all JS/JSX). No tests anywhere (zero test files, no test scripts). Zod listed in dependencies but completely unused. BarChart.jsx (857 lines) and PieChart.jsx (292 lines) share highly similar gradient/mask/glow patterns (duplication). "use client" directives in evilcharts components are unnecessary Next.js patterns. Unused dependencies (next-themes, @base-ui/react). Share dialog copy-pasted between Dashboard and Analytics.
93
S
Lequorum - A real-time polling platform
Srujanee Nayak · @snayak52006
42
Lequorum is a functional full-stack polling platform with solid fundamentals: clean MVC architecture, working JWT auth with smart passthroughAuth pattern, dynamic poll creation with questions/options using transactions, proper response validation chain, Recharts-based analytics dashboard with Socket.io live updates, and polished Chakra UI v3 frontend with comprehensive state coverage. The init-factory pattern for Sequelize models is a standout design choice. However, critical gaps hold it back: no duplicate response prevention undermines analytics integrity, no poll editing makes data immutable after creation, the log endpoint has a remote DoS vulnerability (logger.fatal kills the server), sequelize.sync({alter:true}) runs unconditionally in production, and there are no indexes or unique constraints on key database columns. The project is well-scoped and complete in breadth but lacks depth in each area. Score 42/100.
Authentication & Access Control5 / 10
Functional registration/login with bcrypt(10)+JWT (auth.controller.js:7-54, authentication.service.js:9-20). Two-tier auth middleware: requireAuth (authentication.middleware.js:16 lines) and passthroughAuth (passthrough.middleware.js:14 lines) for mixed auth endpoints — a well-designed pattern. Anonymous vs authenticated poll modes enforced server-side (response.controller.js:35-38) and client-side (PollView.jsx:70-81). Auth guards on protected/guest routes via TanStack Router beforeLoad (App.jsx:30-44). Uniform "Invalid credentials" error prevents user enumeration. JWT uses proper iss/aud/sub/jti claims. Gaps: no refresh token rotation, no email verification, no password reset, no rate limiting on auth endpoints, no Helmet/CSRF, token stored in localStorage (AuthContext.jsx:16), 401 redirect uses window.location.href bypassing SPA router (axios.js:25), no cross-tab sync, no user-existence check in auth middleware.
Poll Creation & Question Management6 / 15
CreatePoll.jsx (165 lines) with dynamic QuestionBuilder.jsx (181 lines) supports adding/removing questions and options with mandatory/optional toggle. Minimum constraints enforced in UI: at least 1 question, at least 2 options per question (QuestionBuilder.jsx:94,140). Server-side Joi validation requires min 1 question, min 2 options, title 3-100 chars, expiresAt > now (poll.validator.js:22 lines). Transaction-based atomic creation of poll + questions + options (poll.controller.js:97-139). Re-fetches complete object after creation. Gaps: NO poll EDIT/UPDATE endpoint exists — polls are immutable after creation (no PUT /api/polls/:id). Client-side validation failure is silent (CreatePoll.jsx:49-51 returns without user feedback). No form library (manual useState). No slug/permalink. No draft status.
Response Collection Flow5 / 15
PollView.jsx (199 lines) renders radio groups per question with client-side mandatory enforcement (line 98: mandatoryIds.every). Comprehensive UI states: expired, login-required, submitted confirmation, published redirect. Server-side validation chain (response.controller.js:99 lines): poll existence, published rejection (403), expiry check (410), auth requirement enforcement, mandatory question validation (422 with missing IDs), question ID validation. Transaction-based atomic submission of Response + Answer rows (lines 67-78). Proper HTTP status codes throughout. Gaps: NO duplicate response prevention — same user can submit unlimited responses (no unique constraint on responses(userId, pollId) or answers(responseId, questionId), no localStorage/cookie tracking). Submitted optionIds are NOT validated against parent questions — any UUID is accepted for any question (respondSchema only checks UUID format, not ownership). No rate limiting on submission.
Analytics & Feedback Dashboard6 / 15
analytics.service.js (71 lines) computes totalResponses, per-question option counts with percentages (1 decimal via Math.round). Analytics.jsx (174 lines) displays 3 stat cards (total responses, questions, participation), per-question bar charts via AnalyticsChart.jsx (Recharts horizontal bar chart with percentage labels), publish button with state guards, copy-link with clipboard API, and empty state. PublishedResults.jsx (97 lines) provides public results view with stat cards and charts. publishPoll controller (poll.controller.js:141-172) enforces ownership, expiry, and already-published checks. useFeed hook provides live Socket.io invalidation of analytics. Gaps: no CSV/data export, no individual response viewing, no trend/time-series data, no anonymous vs authenticated breakdown, no peak activity metrics. Analytics computation fetches all response IDs into memory (analytics.service.js:22) — not scalable. No pagination on analytics data.
Frontend Experience5 / 10
Chakra UI v3 with custom design tokens (system.js:70 lines, custom fonts DM Serif Display/DM Sans). 8 routes with comprehensive loading/error/empty state coverage across all pages. Consistent warm off-white (#F5F4F1) aesthetic with polished visual identity. TanStack Router with proper auth guards (beforeLoad redirects). TanStack Query for data fetching. Responsive grid layouts on Home and Dashboard pages. All UI states handled: spinners, ErrorState component, empty states with CTAs, login gate, expired poll, submitted confirmation, not-found. Gaps: no form library (manual useState throughout), silent client-side validation failure in CreatePoll (lines 49-51), duplicated style objects across 4 pages (cardStyle/sectionStyle), hardcoded hex colors repeated throughout instead of semantic tokens, duplicated setField pattern in Login/Register, no confirmation dialogs, no dark mode, 401 interceptor uses window.location.href bypassing SPA router (axios.js:25), PollView double-joins socket room (lines 44-49).
Backend Architecture & API Design5 / 15
Clean MVC architecture with routes→controllers→models separation. Sequelize init-factory pattern (each model exports function receiving sequelize instance) avoids circular dependencies — a notable design choice (models/init.js:38 lines). Joi validation middleware (validation.middleware.js:11 lines, generic higher-order function with abortEarly:false). Pino structured logging throughout with conditional dev/prod formatting. 12 REST endpoints with appropriate HTTP methods and status codes. Transaction-based operations for poll creation and response submission. passthroughAuth elegantly handles mixed auth. Gaps: NO poll UPDATE endpoint. No rate limiting anywhere. No Helmet/security headers. sequelize.sync({alter:true}) runs unconditionally (index.js:29) — dangerous in production. Log controller has remote DoS vulnerability (log.controller.js:6: logger[level] with no validation allows level:"fatal" to crash server). No database indexes on creatorId, expiresAt, or responses(pollId). No unique constraints on answers(responseId, questionId) or responses(userId, pollId). No centralized error handler — catch blocks repeat the same 2-line pattern across 6 controllers. JWT secret loaded at module scope without startup validation. getUserPolls has no pagination.
Real-Time Updates Using WebSockets5 / 10
Socket.io server integrated with HTTP server (socket.config.js:31 lines). Room-based pub/sub pattern: clients emit join_poll/leave_poll to manage room membership by pollId (lines 15-21). Dual event emission on response submission: update_analytics (full payload) and update_count (total only) with fault isolation — socket failures don't affect HTTP response (response.controller.js:82-89). Frontend SocketContext manages single socket lifecycle (SocketContext.jsx:20 lines). useFeed hook (32 lines) elegantly combines React Query caching with Socket.io push via setQueryData invalidation. LiveCounter widget displays animated response count with ping CSS animation (LiveCounter.jsx:50 lines). Gaps: no socket authentication. No reconnection room-rejoin logic (Socket.io reconnects but event handlers don't re-join rooms). PollView double-joins the socket room — once in PollView.jsx:44-49 and again in useFeed/LiveCounter. Full analytics recomputed on every response (no delta updates). Only 2 server-emitted events — no events for poll publish/delete state changes. No debouncing/batching. Initial LiveCounter count always 0 until first socket event.
Code Quality & Project Structure5 / 10
Clean monorepo with separate lequorum-api/ and lequorum-ui/ packages. Well-organized directory structure: controllers, services, models, routes, validators, middlewares, configs on backend; pages, forms, charts, layouts, widgets, ui, contexts, hooks, clients on frontend. Consistent naming conventions throughout. README.md (114 lines) with setup instructions, environment variables table, and tech stack overview. ESLint configured on both packages. Docker support with Dockerfile and GitHub Actions workflow (docker-push.yml). Vite aliases for clean imports. init-factory pattern for Sequelize models prevents circular dependencies — a strength. Gaps: no TypeScript (plain JS throughout). Zero automated tests. Duplicated code: emptyQuestion factory in CreatePoll.jsx + QuestionBuilder.jsx, cardStyle objects across 4 pages, Bearer token extraction in 2 middlewares, setField pattern in Login+Register. Hardcoded hex colors throughout frontend (15+ occurrences of #E7E5E0, #576F6A, etc.) instead of theme tokens. No .env.example files. No pre-commit hooks. The log.controller.js fatal vulnerability (level:"fatal" can kill server) is a quality concern.
94
R
PulseBoard
Rupesh Pradhan · @merupeshpradhan
42
PulseBoard is a functional full-stack polling platform built with the MERN stack. It has working authentication (JWT + bcrypt), poll creation (single-question only in frontend despite multi-question backend support), response collection with duplicate prevention and expiry handling, analytics with Recharts bar charts, publishable results, Socket.io real-time notifications, and a clean responsive UI with Tailwind CSS. The project is deployed and working at pulseboard.rupeshpradhan.com. Total score: 42/100. Primary strengths: solid backend architecture with proper layering, good validation patterns, clean code organization. Primary weaknesses: single-question-only creation UI (despite backend supporting multi-question), missing authorization checks on analytics/publish endpoints, no route protection on frontend, hardcoded localhost URLs preventing proper production deployment, minimal Socket.io implementation (broadcast-only, no rooms), and no tests or TypeScript.
Authentication & Access Control6 / 10
Solid JWT authentication with register, login, logout, and get-me endpoints. auth.middleware.js (43 lines) extracts Bearer token, verifies JWT, attaches req.user; optionalAuth middleware (41 lines) supports anonymous poll submission. bcrypt with 10 rounds and select:false on password field (auth.model.js:21,33). Axios interceptor auto-attaches token (api.js:16-24). Navbar is auth-aware with desktop/mobile menus. Gaps: no refresh token rotation, token in localStorage (XSS-risk), no Helmet/CSRF, no email verification, no rate limiting on auth routes, logout is a no-op (auth.service.js:62-64), JWT verification errors return 500 instead of 401 due to missing ApiError wrapping in verifyAccessToken (jwt.utils.js:14), missing ownership authorization on analytics/publish endpoints, hardcoded localhost:4000 baseURL in api.js:9.
Poll Creation & Question Management4 / 15
Backend model and DTOs fully support multi-question polls: poll.model.js:56 defines questions as [questionSchema] array, CreatePollDto (create-poll.dto.js:13-22) validates Joi.array().min(1) of {question, required, options}. But CreatePoll.jsx (267 lines) only has ONE question input field, hardcodes questions as single-element array (line 103-111: questions: [{ question, required: true, options: cleanedOptions }]), hardcodes allowAnonymous: true (line 99) and required: true (line 107) with no UI toggles. No poll editing or deletion endpoints exist. Users cannot create multi-question polls in practice despite backend readiness. Score 4 for partial implementation with significant UX gaps vs requirements.
Response Collection Flow6 / 15
Solid response collection in PollPage.jsx (231 lines): renders questions with radio buttons, validates all questions answered before submit (lines 71-83), and handles expiry with disabled UI. Server-side submitResponse (poll.service.js:92-166) checks poll existence, expiry (line 113-115), auth requirement for non-anonymous polls (line 120-122), and prevents duplicate voting via response.user lookup (lines 128-136). Client-side duplicate prevention via localStorage voted_poll_X key (PollPage.jsx:25-27, 92). Joi validation via SubmitResponseDto. Loading/submitting/success states all handled. Gaps: no server-side validation that submitted questionIds belong to the poll, no mandatory question enforcement server-side, no validation that selectedOption matches actual poll options, responses embedded in poll document (not separate collection).
Analytics & Feedback Dashboard5 / 15
Creator analytics (Analytics.jsx, 137 lines) uses Recharts BarChart per question with per-option vote counts via getAnalytics service (poll.service.js:176-218) that computes optionCounts by iterating responses. Dashboard (217 lines) lists polls with response counts, publish/draft badges, share/analytics/publish action buttons. publishResults (poll.service.js:248-269) sets isPublished:true; public ResultsPage (164 lines) shows per-question bar charts. Gaps: no percentages, no trend/time-series data, no CSV/export, no individual response viewing, results page re-computes analytics client-side from raw responses (privacy concern), no real-time updates on analytics page, no peak activity metrics, no leading option identification, no sorting by votes. Total codebase includes 2 separate analytics computation implementations (server + client duplicate logic).
Frontend Experience6 / 10
Clean Tailwind CSS design with consistent gradient backgrounds across all 8 pages. Responsive layout with mobile hamburger menu (Navbar.jsx, 209 lines). Loading spinners on all data-fetching pages, error toasts via react-hot-toast, empty state on dashboard ("No polls created yet"). Password show/hide toggle on login/register. Copy-to-clipboard share link. Submit success page with bounce animation. Auth-aware navigation hides links on public poll pages. Gaps: no frontend route protection (no auth guards redirecting unauthenticated users), hardcoded localhost:4000 in api.js:9 and socket.js:5, no form library (individual useState in CreatePoll), no global auth state management (Navbar refetches user on every navigation via location.pathname dependency at Navbar.jsx:57), no Framer Motion or animation library, no confirmation dialogs for destructive actions.
Backend Architecture & API Design6 / 15
Clean layered architecture: server/src/app.js mounts authRoutes and pollRoutes under /api prefix. Module-based structure with controllers→services→models separation across auth/ and poll/ modules. Joi validation on all 4 mutation endpoints via validate middleware factory (validate.middleware.js:3-17). Global error middleware with ApiError class hierarchy (api-error.js, 6 static factories). CORS configured via env CLIENT_URL (app.js:22-27). Poll schema supports multi-question with embedded questionSchema and responses array (poll.model.js:56-77). Gaps: missing authorization checks (any authenticated user can view analytics or publish any poll via poll.service.js:176,248), no Helmet/rate limiting/body size limits, token extraction duplicated in auth.middleware.js:11-14 and optional-auth.middleware.js:10-13, JWT errors become 500s (jwt.utils.js:14 doesn't wrap in ApiError), plain Error thrown at poll.controller.js:23, logout service is dead code, no .env.example, no test scripts, Socket.io broadcasts to all clients instead of rooms.
Real-Time Updates Using WebSockets4 / 10
Socket.io server wired at server.js:22-27 with custom CORS config. Single custom event "poll-response-updated" emitted via io.emit() (broadcast to ALL clients, not room-targeted) after vote submission carrying {pollId, totalResponses} (poll.service.js:154-162). Client listeners: Dashboard.jsx:35-56 shows toast "New response received" and optimistically increments response count using fake Date.now() IDs rather than actual response data; PollPage.jsx:48-52 only resets a justVoted flag. Socket connection has no authentication. No events for poll publishing, deletion, or expiry. No room joining/leaving pattern, no reconnect rejoin logic. Socket cleanup on unmount (socket.off). Hardcoded localhost:4000 in socket.js:5. Pure invalidation signal with no actual data pushed - score 4 for minimal but functional Socket.io integration.
Code Quality & Project Structure5 / 10
Clean project structure: client/ and server/ separation, 24 server files totaling 906 lines and 12 client source files totaling 1544 lines. Consistent module architecture with proper separation of concerns. Good function documentation comments throughout (header blocks). DRY patterns used: ApiError/ApiResponse utilities, validate middleware factory, shared auth middleware. Gaps: no TypeScript (entirely JavaScript), zero automated tests in either package.json, token extraction duplicated in auth.middleware.js:11-14 and optional-auth.middleware.js:10-13, analytics computation duplicated in Analytics.jsx and ResultsPage.jsx, hardcoded localhost URLs in api.js:9 and socket.js:5 (would break production deployments), dead code: commented-out expiresIn line (jwt.utils.js:7), commented-out expiry check (poll.service.js:53-58), no-op logout service (auth.service.js:62-64), dead req.user?._id fallback (poll.controller.js:21), no .env.example file, backend has no ESLint config.
95
PulseBoard
Saurabh Jagtap · @saurabh004
42
PulseBoard is a functioning polling platform with Clerk authentication, poll creation, response collection with strong validation, analytics dashboard, and Socket.IO real-time updates. Deployed on Vercel (frontend) and Render (backend). Strengths: strong response validation with defense-in-depth, good visual design with custom CSS tokens and Framer Motion, clean backend architecture, comprehensive UI state coverage. Weaknesses: entirely Clerk-dependent auth (no custom registration/login), no security middleware (Helmet, rate limiting, CSRF), uses PostgreSQL/Drizzle instead of MongoDB as specified, significant dead code (4 components, 2 DTOs), code duplication (getPollId ×3), no tests, Socket.IO is signal-only with no reconnection handling, and poll editing is completely absent. Overall score: 42/100, near the calibration mean of 41.
Authentication & Access Control5 / 10
Clerk-based auth with proper requireAuth/optionalAuth middleware (requireAuth.ts:11-18, optionalAuth.ts:4-8), user sync endpoint (user.controller.ts:8-25), and ProtectedRoute in App.tsx:13-18. Anonymous vs authenticated poll modes enforced server-side (response.services.ts:33-38). Session token via crypto.randomUUID() for anonymous dedup (PollRespond.tsx:31-39). Entirely Clerk-dependent — no custom registration/login. Missing: Helmet, rate limiting, CSRF protection, email verification. useAuthFetch creates new axios instance on every getToken change (useAuthFetch.ts:8-26).
Poll Creation & Question Management6 / 15
CreatePoll.tsx (764 lines) supports dynamic questions/options with add/remove/reorder, mandatory/optional toggle, anonymous toggle, expiry picker, progress stepper, and client-side validation. Zod validation (poll.dto.ts:17-23) enforces title (3-200), min 1 question, min 2 options per question, ISO datetime format. Transaction-based creation in poll.services.ts:9-49. Significant gaps: no poll editing endpoint (updatePollSchema defined but unused), no slug/permalink, no QR code, no draft status, QuestionBuilder.tsx component is dead code.
Response Collection Flow7 / 15
Strong defense-in-depth in response.services.ts (176 lines): expiry check (L28-29), auth mode enforcement (L33-38), synced user check (L41-51), question ownership validation (L66-73), mandatory enforcement (L78-90), cross-question injection prevention (L94-104), duplicate answer check (L107-112), duplicate submission via respondentId/sessionToken (L115-138), transaction-based save (L141-163). Zod DTO with refine for no duplicate questionIds (response.dto.ts:8-17). Comprehensive UI states: loading, error, expired, published, auth-required, success with confetti. Gap: no DB-level unique constraint for duplicate prevention (race condition potential).
Analytics & Feedback Dashboard6 / 15
Analytics computes total responses, per-question option counts with percentages (analytics.services.ts:7-70). Analytics.tsx (1162 lines) provides animated counters, progress bars, question breakdowns, conic-gradient donut chart, live badge, stat cards, publish button, copy link, and loading/error states. PollResults.tsx (471 lines) for public results with animated bars and 15s auto-refresh. Dashboard lists polls with copy/publish/delete. Publish endpoint (poll.services.ts:69-83) with public results retrieval (poll.services.ts:85-135). Gaps: no CSV/JSON export, no individual response viewing, no engagement metrics (peak hours, velocity), no anonymous/authenticated breakdown, pie chart combines options across questions.
Frontend Experience5 / 10
7 routes with React Router, custom CSS design system with light/dark tokens (AppTheme.css, 50+ lines of tokens), Syne + DM Sans fonts, Framer Motion throughout. State coverage: loading spinners/skeletons, error banners, empty states with geometric illustrations, expired/closed states, login wall. Copy-to-clipboard with feedback, CountDownTimer with urgency levels, progress stepper. Gaps: 4 dead components (PollCard.tsx, LandingPage.tsx in components/, QuestionBuilder.tsx, LiveCounter.tsx), no form library (imperative useState in 764-line CreatePoll), no PWA, no mobile nav, Analytics.tsx at 1162 lines without sub-file extraction.
Backend Architecture & API Design5 / 15
Clean routes→controllers→services→DTOs architecture. 6 Drizzle ORM tables with proper foreign keys and cascade deletes (schema.ts:1-130). Zod validation on all inputs (3 DTO files). ApiError/ApiResponse utility classes. CORS with dynamic origin check (app.ts:19-35). Transactions for poll creation and response submission. Differentiated HTTP status codes. However: no Helmet, no rate limiting, no body size limits, no CSRF protection. Uses PostgreSQL/Drizzle (not MongoDB as track specifies). getPollId helper duplicated identically in 3 controllers. updatePollSchema and analyticsQuerySchema defined but unused. No .env.example.
Real-Time Updates Using WebSockets4 / 10
Socket.IO server integrated with HTTP server (index.ts:7-9). Room-based pub/sub: join/leave `poll:{pollId}` rooms (socket/index.ts:25-31). Server emits `response:new` with totalResponses count after submission (response.services.ts:171-173). Client useSocket hook connects, joins room, listens, cleans up (useSocket.ts:4-23). Analytics page consumes socket to trigger refetch + flash animation (Analytics.tsx:669-677). Gaps: pure invalidation signal (no data pushed, client refetches HTTP), only one event type, no socket authentication, no reconnection room re-join, no debouncing, no typed events, no socket on response page.
Code Quality & Project Structure4 / 10
TypeScript in both packages, clean layered architecture, CSS design system with tokens, Docker compose, Drizzle ORM with relations, ESLint configured. However: 4 dead components + 2 unused DTOs (updatePollSchema, analyticsQuerySchema), getPollId duplicated identically in 3 controller files (24 lines), two LandingPage implementations, Analytics.tsx at 1162 lines without component extraction, commented-out code in useAuthFetch.ts (lines 29-72), no automated tests, no .env.example, inconsistent naming patterns, large inline component definitions in page files.
96
M
poll Khoool
Mohd_Sameer · @mohd_sameer
42
Poll Khoool is a functional full-stack polling and live quiz platform built with React/Vite frontend and Node.js/Express/MongoDB backend. The app implements authentication (JWT register/login), dynamic poll creation with multiple questions and mandatory/optional toggles, response collection with expiry and duplicate prevention, per-question analytics with Recharts bar charts, and Socket.io-based real-time updates for active users and live responses. The project also includes a gamified quiz mode with timed questions, speed-based scoring, and live leaderboards — going beyond core requirements. However, quality is held back by missing security middleware (Helmet, rate limiting), no input validation library, no poll edit/delete endpoints, duplicated Countdown component, no TypeScript, no tests, and tokens stored in localStorage. Two deployment URLs are verified working and serve identical content. Score: 42/80.
Authentication & Access Control5 / 10
Registration (POST /api/auth/register with bcrypt 10 rounds), login (bcrypt compare), and JWT token generation (30-day expiry) work correctly (auth.js:15-68). protect middleware validates Bearer tokens and attaches req.user (middleware/auth.js:4-23), optionalAuth middleware allows anonymous/authenticated paths (middleware/auth.js:25-37). Server-side enforces requiresAuth on response submission (routes/poll.js:68-70). Frontend AuthContext manages token in localStorage, provides login/register/logout, and auto-fetches /me on mount (AuthContext.jsx). Critical gaps: tokens stored in localStorage (no httpOnly cookies), no refresh token rotation, no Helmet/CSRF protection, no rate limiting on auth endpoints, no email verification or password reset, and no ProtectedRoute wrapper on frontend — unauthenticated users can navigate to Dashboard/CreatePoll routes.
Poll Creation & Question Management5 / 15
CreatePoll.jsx (327 lines) supports dynamic questions with add/remove, dynamic options per question (min 2 enforced client-side), mandatory/optional toggle per question, quiz mode with per-question time limit and correct option selector, expiry datetime picker, and requiresAuth/isPublished toggles. Server creates polls with 6-char nanoid shortCodes and embedded questions/options in a single document (routes/poll.js:11-32). Critical gaps: NO poll edit endpoint exists (no PATCH/PUT for modifying questions/options), NO delete endpoint, no server-side validation of individual question/option fields (e.g., min 2 options per question), no slug/permalink generation, no form library (raw useState spread operators), client-side validation uses alert() instead of inline errors, no draft/autosave functionality.
Response Collection Flow6 / 15
POST /api/polls/:id/response checks poll existence (line 62), expiry enforcement (line 65-66), requiresAuth gating (line 68-70), and duplicate prevention by userId (if logged in) or ipAddress (if anonymous) at lines 72-81. Client-side validates mandatory questions are answered before submission (TakePoll.jsx:99-103). Quiz mode has time-limit enforcement with 2-second tolerance, speed-based scoring (500-1000 points), and per-question duplicate answer prevention (routes/poll.js:225-255). Frontend redirects to results on success, shows error banners on failure. Gaps: no server-side validation that submitted optionIds actually belong to the poll/questions, no unique compound DB index for deduplication (only application-level check), no rate limiting on response submission, IP-based dedup is fragile (shared IPs/VPNs), no per-question single-option enforcement on server.
Analytics & Feedback Dashboard6 / 15
Analytics endpoint (GET /api/polls/:id/analytics) uses MongoDB aggregation pipelines ($match+$unwind+$group) with separate paths for polls and quizzes (routes/poll.js:130-163). Visibility gating: creator always sees results, non-creators need isPublished=true AND expired (lines 118-128). Returns {totalResponses, results} with per-question/per-option counts. Frontend PollResults.jsx (285 lines) renders per-question Recharts BarChart, per-option breakdown with counts/percentages/animated progress bars, total response stat card, active viewer counts via Socket.io, QR code + short code for sharing, and results-hidden/waiting-for-expiry state with countdown. HostQuiz.jsx (305 lines) adds live leaderboard (top 10) with ranked display. Dashboard.jsx lists polls with creation dates and short codes. Gaps: no CSV/export, no individual response viewing, no trend/time-series data, no peak activity metrics, no auth/anon breakdown, no question drop-off analysis, no publish-after-creation toggle endpoint.
Frontend Experience5 / 10
8 routes defined (App.jsx:17-26): Home, Login, Register, Dashboard, CreatePoll, TakePoll, PollResults, HostQuiz. Consistent dark "glass" theme with custom Tailwind component classes (btn-primary, card, glass-card, input-field — index.css:18-39), Lucide icons, and CSS animations (blob, slide-up, fade-in, pulse-slow, gradient-flow — index.css:57-97). Responsive layout adapts to mobile. Home page features animated bar chart demo, leaderboard demo, and interactive mock quiz. Loading states shown as text, error states with AlertCircle banners, empty state on Dashboard. Gaps: no ProtectedRoute component (unauthenticated users navigate to Dashboard/CreatePoll with broken UI), no form library on CreatePoll (327 lines of raw useState), alert() for validation errors, no confirmation dialogs, no mobile hamburger menu, Countdown component duplicated across 4 files with subtle differences, no dark/light toggle, no skeleton loading states, unused GripVertical import.
Backend Architecture & API Design5 / 15
Clean architecture with server.js (routes mount), routes/ (auth.js 74 lines, poll.js 291 lines), middleware/ (auth.js 37 lines with protect+optionalAuth), and models/ (User, Poll with embedded questions/options, Response with embedded answers, QuizScore). Mongoose schemas are well-structured with proper field types, defaults, and a compound unique index on QuizScore (QuizScore.js:46). MongoDB aggregation pipelines for analytics. Socket.io integrated with HTTP server. Gaps: NO input validation library (no Joi/Zod/express-validator — all manual ad-hoc checks), no Helmet/rate-limiting/body-size-limit/CSRF, no centralized error handler (every route has its own try/catch returning 500), unused 'redis' dependency in package.json, runtime mongoose.model() call in poll.js:240 (anti-pattern), no .env.example file, no poll edit/delete endpoints, no pagination on getCreatorPolls, no meaningful indexes on Response collection, CORS falls back to "*".
Real-Time Updates Using WebSockets6 / 10
Socket.io server integrated with HTTP server (server.js:14-19). Room-based pattern: clients join `poll_${pollId}_active` rooms via join_poll event. Unique visitor tracking using nested Map structure (activePollVisitors: pollId→visitorId→Set<socketId>) with proper cleanup on leave_poll and disconnecting (server.js:36-108). Server emits 4 event types: active_users_count (on join/leave/disconnect), new_response (on poll submission, routes/poll.js:91), question_published (on quiz question publish with endTime, routes/poll.js:200-204), new_quiz_response (on quiz answer, routes/poll.js:268). Client-side: TakePoll displays active user count; PollResults refetches analytics on new_response; HostQuiz refetches on new_quiz_response; PlayQuiz listens for question_published to start timed quiz. Proper cleanup on unmount with isMounted flag and socket.disconnect(). Gaps: event-driven invalidation (signal + HTTP refetch) rather than pushed state, no debouncing on rapid events, no reconnection room re-join logic, no socket authentication, visitor tracking in memory only (lost on server restart), no events for poll expiry/deletion/status changes.
Code Quality & Project Structure4 / 10
Clean monorepo with backend/ and frontend/ separation, consistent naming conventions (models/User.js, routes/auth.js, middleware/auth.js), proper React patterns (useEffect cleanup, useCallback memoization, isMounted guards). Vite proxy config for dev, Tailwind component classes in @layer, CSS animation keyframes. ESLint configured on frontend. .gitignore present. Significant issues: no TypeScript (all plain JS), Countdown component duplicated 4 times across TakePoll/PollResults/HostQuiz/PlayQuiz with subtle behavioral differences, no automated tests (no test scripts in either package.json), unused 'redis' dependency in backend, unused GripVertical import in CreatePoll, no ESLint on backend, console.log in production (socket connections, errors), no .env.example files, no consistent error response format, single-file large components (Home.jsx 369 lines, CreatePoll.jsx 327 lines) without sub-file extraction, runtime mongoose.model() call in poll.js when QuizScore already imported, dead code in PlayQuiz.jsx:51-52.
97
P
pulse
priyanshu pandey · @priyanshupandat32
42
Pulse is a full-stack polling platform (React+Express+MongoDB) with 4,090 lines of TypeScript code. The project delivers all core requirements: JWT dual-token auth with cookie storage, poll creation with multi-question support, response collection with duplicate prevention and validation, analytics dashboard with per-question breakdowns, Socket.io real-time updates, and a polished neo-brutalist UI. However, significant gaps exist across all dimensions — no security middleware (Helmet, rate limiting, CSRF), no poll editing/deletion endpoints, components duplicated across multiple files, no automated tests, and file name typos. Total score: 42/100, close to the cohort mean (41.87) but below the median (49.00).
Authentication & Access Control6 / 10
JWT dual-token auth (access 15m + refresh 7d) with httpOnly/secure/sameSite cookies (auth.controller.ts:6-11). bcrypt with 12 salt rounds on User pre-save hook (user.model.ts:40-43). Refresh token rotation verifies against DB, issues new token (auth.services.ts:115-147). Optional auth middleware for public endpoints (responseauth.middlware.ts:6-23). Axios 401 interceptor auto-refreshes and retries (axios.ts:12-51). Anonymous vs authenticated poll modes enforced server-side (response.services.ts:32-47). Frontend Zustand store with setUser/logout + ProtectedRoute/PublicRoute wrappers. Missing: no Helmet, no CSRF protection, no rate limiting, no email verification, no password reset flow.
Poll Creation & Question Management6 / 15
Two-step poll creation wizard: Step 1 (title, desc, expiry datetime-local, anonymous toggle), Step 2 (dynamic questions with add/remove). Per-question controls: text input, required/optional toggle with helper text, dynamic options with letter labels A-J, min 2 max 10 enforced (CreatePoll.tsx:44-149). Client validation for empty title, missing/future expiry, and per-question text + option count (lines 85-115). Server-side: expiry date future check (poll.services.ts:10-11), poll ownership gate, active status check before adding questions (question.services.ts:16-23). Missing: no poll edit endpoint (PATCH only for publish), no question editing/deletion, no slug or QR code generation, no draft status, no server-side Zod validation on question data, backend `questionText` field is optional in schema.
Response Collection Flow6 / 15
Defense-in-depth server validation in submitResponseService (response.services.ts:16-65): poll existence check, active status gate, future expiry check, duplicate prevention — IP-based for anonymous polls, userId-based for authenticated polls (with login requirement). Required question validation ensures all mandatory questions are answered (lines 49-65). MongoDB unique compound indexes on {pollId, userId} and {pollId, ipAddress} with partial filter expressions for DB-level dedup (response.model.ts:37-52). IP extracted from x-forwarded-for header for proxy support (response.controller.ts:14-17). Socket.io emission on successful submission. Frontend: option selection UI, required question check before submit, loading spinner, success confirmation, live response count via WebSocket. Missing: no validation that selectedOption exists in question.options array, no questionId existence check against the poll, no duplicate questionId prevention in answers array.
Analytics & Feedback Dashboard5 / 15
Creator-only analytics dashboard at /analytics/:pollId (analytics.controller.ts) computing: total responses, authenticated vs anonymous counts, per-question option vote tallies with winner detection (analytics.services.ts:28-58). Frontend renders 3 stat cards (Total/Authenticated/Anonymous), per-question breakdown with OptionBar component showing counts and percentages, and live/closed/published status badges (Analytics.tsx:222-298). Publish button on closed polls triggers status change (poll.services.ts:92-110). Published results view (PollResults in PollPage.tsx:43-134) shows option bars and winners publicly. Dashboard lists polls with response counts computed client-side. Missing: no CSV/export, no individual response viewing, no trend data or time-series, no peak activity metrics, computation is brute-force O(n*m) with flatMap+filter, no leader summary at top, no animated progress bars (CSS-only).
Frontend Experience5 / 10
Consistent neo-brutalist design (heavy borders, black/white, bold typography) using Tailwind CSS v4. 9 routes with ProtectedRoute/PublicRoute gating. Animated loading screen with pulsing bars (App.tsx:18-63). Landing page with scroll-aware navbar, marquee animation, feature cards, how-it-works steps (landingPage.tsx:570 lines). All pages handle loading (spinners/bars), error (red border boxes), empty (no polls CTA), and auth states. Toast notification system with 3s auto-dismiss (Dashboard.tsx, Analytics.tsx). Responsive grid layouts. Post-submission success screen. Copy-to-clipboard share link. Visual toggles for anonymous/auth and required/optional. Missing: no form library (manual useState in CreatePoll), Icon component duplicated in 6 files, Toast duplicated in 2 files, StatusBadge styling duplicated in 3 files, analytics service file misspelled "anylatics.services.ts", no dark mode, no PWA.
Backend Architecture & API Design5 / 15
Clean layered architecture: routes→controllers→services→models with 13 REST endpoints. TypeScript throughout with proper type definitions. MongoDB/Mongoose schemas with indexes (partial unique compounds on Response, unique email on User). 3 middleware: auth (Bearer+cookie), optionalAuth (cookie-only, non-blocking), Zod validation wrapper. Custom ApiError class with static factories for 400/401/404/409/412 (error.ts). JWT utility (generateToken/verifyToken) with null-safe error handling. CORS + cookieParser configured. Missing: no Helmet security headers, no rate limiting, no body size limits, no express-mongo-sanitize, ApiResponse utility class (response.ts) defined but NEVER used by any controller, response submission has no Zod validation, poll creation has no Zod validation, forbidden status code is 412 (should be 403), no poll editing or deletion endpoints, no question editing/deletion, no MongoDB transactions for poll+question creation, console.log in multiple backend files.
Real-Time Updates Using WebSockets5 / 10
Socket.io server integrated with Express HTTP server with CORS config (socket.ts:7-35). Room-based architecture: clients join/leave `pollId` rooms via `join-poll`/`leave-poll` events. Server emits `new-response` with {totalResponses} after each response submission (response.services.ts:75-78). Frontend singleton socket with lazy init (socket.ts:4-16), useSocket hook for room join/leave lifecycle with cleanup (useSocket.ts:4-27). Used in PollPage.tsx for live response count display and in Analytics.tsx for live updates triggering full refetch. Missing: only 1 server-emitted event — no events for poll publishing, status changes, or expiry; no socket authentication (any client can join any room); no reconnection room re-join logic — silent subscription loss on disconnect; pure invalidation signal pattern (no data pushed, client refetches); no debouncing; console.log in socket handlers; no typed Socket.io events.
Code Quality & Project Structure4 / 10
Monorepo with separate backend/frontend packages, TypeScript throughout, clear module separation (routes/controllers/services/models/middleware/utils/types). Consistent naming conventions. Zustand for state, Axios with interceptors, Tailwind CSS. Several quality issues: Icon component duplicated across 6 files (should be shared component), Toast duplicated in 2 files, StatusBadge styling duplicated across 3 files. File name typos: "anylatics.services.ts" (analytics), "responseauth.middlware.ts" (middleware). poll.services.ts imports both raw axios and api instances. ApiResponse utility class defined but unused. Zero automated tests. No ESLint on backend. Console.log in production code (socket.ts, index.ts, PollPage.tsx). .env files committed to repo. No root package.json or workspace config. No CI/CD. Comments mix Hindi and English. `as any` cast in jwt.ts:8.
98
PollGame
Shivanshu Agrawal · @shivanshu483501_513452e0
41
PollGame is a functional full-stack polling platform with solid foundational architecture. The backend follows clean MVC+Service patterns with 13 REST endpoints, 5 Joi DTOs, centralized error handling, and a well-implemented JWT auth system (access+refresh tokens with httpOnly cookies and SHA-256 hashed storage). The frontend has 7 routes with Tailwind CSS, all states covered, and a working Socket.IO integration for real-time analytics. However, the submission is incomplete compared to production standards: no poll editing/deletion, no root README, zero tests, no TypeScript, significant security gaps (no Helmet, rate limiting, or CSRF), the auth controller lacks catchAsync wrapping, the validate middleware throws synchronously instead of calling next(error), and there's substantial dead code (~438 lines of unused Vite template assets, an orphaned page component, and e-commerce leftovers). Overall a respectable effort that would need polish and security hardening for production use. Total score: 41/80.
Authentication & Access Control6 / 10
Solid JWT auth with register, login, logout, forgot/reset password (8 features). Access+refresh token strategy with httpOnly cookie for refresh token and SHA-256 hashed storage for invalidation (auth.service.js:44-46). Joi validation on all auth DTOs with password complexity rules. Frontend AuthContext with loading guard and ProtectedRoute wrapper. Axios interceptor with token refresh queue pattern for concurrent 401 deduplication (api.js:37-78). Gaps: auth controller (7 handlers) doesn't use catchAsync — unhandled rejections won't reach error middleware; access token stored in localStorage (XSS risk); no Helmet, rate limiting, or CSRF; email verification infrastructure exists but never called (isVerified:true by default); unused authorize middleware.
Poll Creation & Question Management5 / 15
Working poll creation form at CreatePollPage.jsx (327 lines) with dynamic add/remove questions and options, mandatory/optional toggle, anonymous/authenticated response mode, and expiresAt datetime picker with min-constraint. Client-side validateForm() checks title, question text, min 2 options, option text. Server-side Joi validation (dto/create-poll.dto.js) enforces title 3-150 chars, 1-20 questions with 2-10 options each, responseMode enum, future expiry date. POST /api/polls correctly attaches creator from req.user. Gaps: no poll editing/update endpoint exists, no poll deletion endpoint, no slug/permalink (raw ObjectId in URL), CopyLinkButton has a URL bug — constructs `/polls/${pollId}` but route is `/poll/:pollId`.
Response Collection Flow6 / 15
Thorough server-side validation in submitResponse (poll.service.js:144-211): checks poll existence, isPublished gate, expiresAt gate, responseMode enforcement (authenticated requires login), mandatory question enforcement iterating all poll.questions, per-answer questionId/optionId validation against actual poll data using a Map lookup. Response model (response.model.js:30-33) has a compound unique index on {poll, respondent} with partialFilterExpression allowing anonymous unlimited responses while preventing authenticated duplicates. Client-side PollPage.jsx renders radio inputs with required={q.isMandatory}, handles success message after submission. Gaps: no client-side validation beyond HTML5 required attribute; no server-side single-option enforcement per question (answers array accepts multiple per questionId); no anonymous dedup tracking (intentional by design).
Analytics & Feedback Dashboard5 / 15
AnalyticsPage.jsx (153 lines) displays total responses count, per-question bar charts with percentages and vote counts, publish button with window.confirm dialog, published status badge, and empty state for zero responses. GET /api/polls/:id/analytics uses MongoDB $unwind+$group aggregation pipeline (_getAnalytics in poll.service.js:27-83) with Map-based efficient lookup for matching aggregated counts to poll questions. Creator-only access enforced by comparing poll.creator to req.user.id. PATCH /api/polls/:id/publish toggles isPublished; published results display publicly via PollPage.jsx with identical bar chart rendering. Dashboard lists polls with creation dates and analytics links. Gaps: no CSV/PDF export, no individual response viewing, no trend/time-series data, no peak activity metrics, no engagement stats beyond raw counts, analytics bar chart rendering duplicated between PollPage.jsx:83-108 and AnalyticsPage.jsx:112-136.
Frontend Experience5 / 10
7 routes with React Router, ProtectedRoute wrapping, consistent Tailwind CSS v4 emerald theme. 29 error/loading/empty/success states handled across all pages. HomePage has hero section, 4 feature cards, 3-step how-it-works, CTA, and footer. Sticky navbar with backdrop-blur and conditional auth display. CopyLinkButton with clipboard API and 2-second "Copied!" feedback. Responsive container layout. Gaps: no form library (CreatePollPage uses 466 lines of individual useState hooks); React imported unnecessarily in 14 JSX files (new JSX transform); 184 lines of dead Vite template App.css never referenced; HTML title is "frontend" (index.html:7); leftover Vite assets (icons.svg, hero.png, react.svg, vite.svg); URL bug in CopyLinkButton; no animations/transitions; no mobile hamburger menu; no dark mode; no skeleton loaders; no confirmation dialogs except publish.
Backend Architecture & API Design5 / 15
Clean MVC+Service layer architecture with modular structure (auth/, poll/, common/). 13 REST endpoints with consistent naming. 5 Joi DTO validation schemas. Centralized error handler (error.middleware.js) handles ApiError, Mongoose CastError/ValidationError/duplicate key, JWT errors with proper HTTP codes. 3 well-designed Mongoose models with compound unique index using partialFilterExpression on Response.model.js. catchAsync utility for async error propagation. JWT utils with separate access/refresh token generation. Socket.IO integrated with HTTP server. Docker Compose for local MongoDB. Gaps: auth controller doesn't use catchAsync (7 handlers); validate middleware throws synchronously instead of next(error); no Helmet, rate limiting, CSRF protection, or express.json() size limit; incomplete env.example (missing 9 variables); hardcoded MongoDB credentials in docker-compose.yml; e-commerce leftover sendOrderConfirmationEmail in email.js; empty duplicate create-poll.dto.js; console.log debug statements in getPollAnalytics (poll.service.js:226-231); no poll delete or edit endpoints.
Real-Time Updates Using WebSockets5 / 10
Socket.IO server with JWT authentication middleware on handshake (socket-handler.js:4-24), per-poll room subscription pattern (joinPollRoom listener at line 36-39), and analyticsUpdated emission after response submission pushing full analytics payload to room (poll.service.js:210). Client-side useSocket hook (useSocket.js) creates connection with access token, emits joinPollRoom on connect, listens for analyticsUpdated to update AnalyticsPage state via setAnalytics callback, and cleans up on unmount. Gaps: only 1 custom server-emitted event (analyticsUpdated); no events for poll publishing, expiry, or deletion; socket URL hardcoded as "http://localhost:5000" instead of env variable; no Zod/validation on socket events; no debouncing; socket integration only on analytics page, not on poll-taking page; useSocket hook reconnects on every onAnalyticsUpdated callback change due to useEffect dependency.
Code Quality & Project Structure4 / 10
Clean backend/frontend separation with well-organized module structure (common/, modules/auth/, modules/poll/). Consistent naming conventions and good JSDoc on service functions. Frontend service layer abstracts API calls. ESLint configured on frontend. Gaps: no root README.md (only Vite default in frontend/); no TypeScript (plain JS throughout both packages); zero test files despite jest/supertest in devDependencies and "test" script in backend/package.json; 14 JSX files import React unnecessarily; 184 lines of dead Vite template App.css; 156 lines of dead PollResponsePage.jsx (never routed); empty duplicate create-poll.dto.js; e-commerce leftover in email.js; duplicated analytics bar chart rendering (PollPage.jsx and AnalyticsPage.jsx); duplicated password regex; duplicated user object construction in auth.middleware.js; unused Vite template assets; unused authorize middleware; HTML title is "frontend"; hardcoded socket URL; no .env.example for frontend.
99
S
PollSync
Soumyaditya Sinha · @soymya_ditya
41
PollSync is a full-stack polling platform with JWT + Google OAuth authentication, poll CRUD with real-time Socket.IO updates, and a polished Tailwind CSS frontend. Deployed on Vercel (verified working). Key strengths: comprehensive frontend state handling across all pages, room-based Socket.IO with 5 event types, clean modular backend architecture, and solid auth flow. Critical gaps: the poll model only supports a flat single-question with options (no multiple questions with mandatory/optional handling as required), no security hardening (no rate limiting, no Helmet, no CSRF), vote counting is non-atomic, analytics are shallow counters only, and significant code quality issues (no TypeScript, no tests, dead code, console.log in production). Total score 41/100.
Authentication & Access Control6 / 10
Solid JWT auth with proper Bearer token extraction (authMiddleware.js:9-63), protect + optionalAuth middleware, Google OAuth via Passport (passport.js:1-23), bcryptjs password hashing (10 rounds), and session-based anonymous tracking (sessionMiddleware.js:1-21). Frontend has login/register forms, OAuth callback, token management, 401 interceptor, ProtectedRoute guard, and anonymous vs authenticated polling mode (PollBoothPage.jsx:159-179). However: no Helmet, no rate limiting, no CSRF protection, JWT passed as URL query param on OAuth callback (authController.js:52), session secret equals JWT secret, token stored in localStorage, dead verification/reset fields in User schema, and 'visibility: private' has zero enforcement (getPollByIdService returns private polls to unauthenticated users).
Poll Creation & Question Management3 / 15
The requirements call for "multiple questions" with "mandatory or optional" per-question handling. PollSync implements a flat single-question-per-poll model — the Poll schema (Poll.js:68-70) has only a top-level `options` array with no question entity. CreatePollPage.jsx (388 lines) supports dynamic option add/remove (2-8 limit), timer duration, and poll settings toggles, with Joi validation (poll.validator.js:3-81: title, min 2 options, timerDuration). Share code auto-generation and QR code in success modal are nice touches. But the core multi-question/requirement is entirely absent — there are no question objects with their own option sets, no mandatory/optional toggling, and no per-question management. This is a flat single-question poll model, which fundamentally misses the spec.
Response Collection Flow5 / 15
Vote service (voteService.js:27-172) provides a functional flow: poll existence check, ended-status gate, option validation, duplicate vote prevention via voter array scanning (userId or sessionId matching), vote counting, analytics increment, and Socket.IO emission. Client-side localStorage tracking prevents re-voting (PollBoothPage.jsx:26,102). However: no expiry-time checking at vote time — only 'ended' status blocks voting, meaning timer-expired polls with status still 'active' accept votes (the cron job lags up to 10s); the read-modify-save cycle is non-atomic creating duplicate vote race conditions; `allowMultipleVotes` schema field is never consulted in voteService.js; `uniqueParticipants` is a misnamed simple counter (line 121-122 increments on every vote, not deduplicated); vote validator only checks optionId is a string, not valid ObjectId.
Analytics & Feedback Dashboard5 / 15
Analytics endpoint (pollAnalyticsService.js:11-46) returns totalVotes, authenticatedVotes, anonymousVotes, views, shares — all simple counters from the poll document with no time-series, no velocity, no per-question breakdown (structural limitation of flat option model). AnalyticsPage.jsx (184 lines) renders voter type breakdown (auth/anon percentages), vote distribution bars with leading indicator, and participation summary grid. ResultsPage.jsx has animated percentage bars and leaderboard section. LiveDashboardPage.jsx (416 lines) with activity feed/vote chart/analytics/leaderboard panels. Leaderboard service resolves user names but uses N+1 queries (individual User.findById per voter, leaderboardService.js:78-123). Poll results are visible when poll.status === 'ended' (pollResult.dto.js:8-12), but there's no explicit 'publish' action — ending the poll is the implicit publish mechanism. `analytics.views` field is never incremented anywhere in the backend.
Frontend Experience7 / 10
Well-structured React SPA with 10+ routes (landing, auth, dashboard, create, poll booth, analytics, live dashboard, results, leaderboard, share). Custom UI design system (Button, Card, Modal, Spinner, Timer, VoteBar, QRCodeCard, LiveBadge, DataPill) with Tailwind CSS. Every page handles loading, error, empty, and auth states explicitly. Dark/light theme with View Transitions API (useThemeToggle.js). NProgress loading bar. Animated demo poll on landing page. QR code with copy-to-clipboard. Staggered result animations. Body scroll lock in Modal (Modal.jsx:4-13). Responsive throughout. Weaknesses: no form library (individual useState hooks on create page), dead Input.jsx component (30 lines, never imported), inconsistent data flow (LiveDashboardPage bypasses Redux), some JSX duplication (logo, error states). No offline/PWA support.
Backend Architecture & API Design5 / 15
Clean three-layer architecture (controllers/services/models) with custom error hierarchy (ApiError → BadRequestError/NotFoundError/UnauthorizedError/ValidationError), ApiResponse wrapper, asyncHandler for error boundaries, and DTO pattern throughout. Joi validation middleware for auth and poll creation. Cron-based auto-expiration every 10s (pollExpiration.job.js). Well-indexed Mongoose schemas with embedded options. However: NO rate limiting anywhere, NO Helmet security headers, NO CSRF protection, NO request body size limits, CORS includes() check is overly broad and duplicated (app.js:25-39 matches socketServer.js:10-26), route conflict risk (timer routes registered at /api/polls alongside poll routes), no pagination on getMyPolls, no path param validation, `visibility:private` has zero enforcement at service layer, vote counting is non-atomic, dead schema fields (verification, password reset), unused dependency socket.io-client on server, and console.log debug statements in 4 production files.
Real-Time Updates Using WebSockets6 / 10
Socket.IO server (socketServer.js:28-89) with room-based pub/sub (rooms named poll:{pollId}). Five server-emitted events: poll-update, poll-ended, vote-update, analytics-update, leaderboard-update. Client integration across 6 pages: PollBoothPage, ResultsPage, AnalyticsPage, LeaderboardPage, LiveDashboardPage, DashboardPage — each connecting, joining rooms, listening to events, and properly cleaning up on unmount. Realtime DTO (pollRealtime.dto.js) correctly subsets poll data to avoid leaking voter arrays. Leaderboard updates conditionally emitted (showLeaderboard check). Cron-expired polls broadcast poll-ended. Key gap: NO reconnection room re-join logic — Socket.IO auto-reconnects but room subscriptions are lost. No socket authentication. console.log statements in event handlers (analyticsRealtime.handler.js:21-23). No debouncing/batching. vote-update payload includes full poll object which grows with voter arrays.
Code Quality & Project Structure4 / 10
Clean monorepo structure with frontend/backend separation and feature-based module organization. Consistent naming conventions. Redux Toolkit used idiomatically with createAsyncThunk. DTOs, validation, and API layers separated per feature. ESLint configured on frontend. Good patterns: Promise.allSettled for graceful degradation (LiveDashboardPage), cancellation flags for async effects (SharePage, LiveDashboardPage), client-side validation before dispatch, and comprehensive state handling across pages. However: NO TypeScript anywhere, ZERO automated tests, dead code (Input.jsx 30 lines unused, authApi.js 3/4 functions unused, collectParticipantInfo dead form state), code duplication (consumeReturnUrl in 2 files, logo JSX 3+ times, error state pattern in 5 pages), console.log in 4+ production files, misleading field name (uniqueParticipants is actually a simple counter), schema fields without business logic (allowMultipleVotes, pollType, visibility:private), inconsistent data flow (LiveDashboardPage bypasses Redux), no .env.example files, and unused server dependency socket.io-client.
100
Pollify
Aman Sagar · @sagaraman369_b453d2e9
41
Pollify is a functional full-stack polling application with a polished frontend (9 routes, Tailwind CSS), JWT-based authentication, dynamic poll creation, response collection with server-side validation, Recharts-powered analytics, Socket.io real-time updates, and published results. The backend (~950 lines) and frontend (~2,350 lines) total ~3,300 lines of hand-written code. Major weaknesses: backend server is NOT deployed (only the Vite frontend on Vercell), no root README, App.jsx is dead Vite starter boilerplate, no input validation library, CORS wildcard, no security middleware, analytics endpoint lacks owner checks, no poll edit/delete endpoints, React Hook Form claimed but not used, no TypeScript, no tests. Strengths: clean MVC architecture, comprehensive response validation (expiry/required/valid-option/duplicate checks), good client state handling (loading/error/empty/auth-required states on all pages), anonymous vs authenticated poll mode enforced both client and server-side.
Authentication & Access Control6 / 10
Solid JWT auth with register/login (bcryptjs 10 rounds), getMe profile endpoint, protect middleware (authMiddleware.js:35 lines), optionalAuth middleware (optionalAuth.js:40 lines) that silently passes for public endpoints. Anonymous vs authenticated poll mode enforced server-side (responseController.js:34-39 checks !poll.isAnonymous && !req.user → 401). Client AuthContext (AuthContext.jsx:109 lines) with persistent token in localStorage, ProtectedRoute redirects to /login. Gaps: no refresh tokens (single 7d token), tokens in localStorage (XSS risk), no httpOnly cookies, no email verification, no password reset, no rate limiting, no Helmet/CSRF, CORS set to wildcard (*).
Poll Creation & Question Management5 / 15
CreatePoll.jsx (428 lines) supports dynamic questions with add/remove, dynamic options per question with add/remove (min 2), per-question isRequired checkbox with custom styled toggle, isAnonymous radio toggle, datetime-local expiry picker. Client-side getValidationError (CreatePoll.jsx:75-103) validates title, expiry, question text, and option emptiness. Server createPoll (pollController.js:5-60) validates title, expiresAt, questions array non-empty, each question has questionText and min 2 options. Gaps: no poll EDIT endpoint (PATCH only for publishResults, questions cannot be modified after creation), no DELETE endpoint, no slug/permalink generation, React Hook Form listed in tech stack but NOT used (manual useState hooks instead), no poll status (draft/active).
Response Collection Flow7 / 15
Response controller (responseController.js:137 lines) has defense-in-depth: poll existence check (404), expiry check (400), anonymous/authenticated mode enforcement (401), duplicate prevention for authenticated users via Response.findOne (responseController.js:44-58), required question validation looping poll.questions (responseController.js:62-78), valid option check against question.options array (responseController.js:82-92), resultsPublished blocking (responseController.js:94-99), and Socket.io emission to poll room. Client PublicPoll.jsx (388 lines) handles loading, not-found, expired, requires-login (with redirect to login/register), and submitted-success states with client-side required-answer and expiry checks. Gaps: no duplicate prevention for anonymous users (no anonymousId in localStorage), no compound unique index on Response model for {pollId, userId}, backend server NOT running at deployment URL.
Analytics & Feedback Dashboard5 / 15
Analytics endpoint (analyticsController.js:157 lines) computes totalResponses and per-question optionCounts by brute-force iterating all responses (O(n*m)). Client AnalyticsPage.jsx (243 lines) shows total responses in large display, per-question PieChart + BarChart via Recharts, vote breakdown with animated progress bars and percentages. PublicResults.jsx (190 lines) shows published poll results with colored progress bars. MyPolls.jsx augments poll list with analytics response counts (N+1 fetch per poll). Gaps: analytics endpoint has NO owner check (any authenticated user can view any poll's analytics via /api/analytics/:pollId), no anonymous vs authenticated breakdown, no time-series/trend data, no peak activity metrics, no CSV export, no individual response viewing, brute-force computation without MongoDB aggregation pipeline.
Frontend Experience5 / 10
HomePage.jsx (341 lines) has a polished marketing landing page with features, workflow, use-cases sections using consistent Tailwind design (emerald/slate/stone, heavy font-weight, animated progress bars in demo card). 9 routes defined in main.jsx. All pages handle loading (animated spinners in styled cards), error (red bordered error cards with CTA), empty (MyPolls "No polls yet" CTA card), and auth-required states (PublicPoll redirect-to-login card). Toast notification system with timed auto-dismiss. Responsive layout with clamp() spacing. Gaps: App.jsx (122 lines) is COMPLETELY DEAD Vite starter boilerplate (counter, hero image, React/Vite docs links) — never used in routing; App.css (292 lines) is Vite starter CSS with dead counter styles; no shared navigation component (header duplicated across pages); React Hook Form listed in tech stack but NOT used — all forms use manual useState; no mobile hamburger menu; no dark mode.
Backend Architecture & API Design5 / 15
Clean MVC structure: routes (4 files) → controllers (4 files) → models (3 files), with separate middleware layer (protect + optionalAuth). 9 REST endpoints covering auth, polls, responses, and analytics. Mongoose schemas are clean: Poll.js (65 lines) with embedded question sub-schema and timestamps, Response.js (38 lines) with answer sub-schema storing questionId+selectedOption, User.js (25 lines) with unique email index. Cookie-parser imported but unused. Gaps: NO input validation library (no Joi/Zod/express-validator — all validation is manual ad-hoc checks), NO security middleware (no helmet, no rate limiting, no express-mongo-sanitize, no body size limits), CORS set to "*" (completely open), error.message leaked directly to clients in ALL 9 catch blocks across 4 controllers, no centralized error handler, no analytics ownership check (any authenticated user can access any poll's analytics), no poll edit or delete endpoints, no pagination on getMyPolls, console.log active in server.js:50,55,61, no .env.example, no tests.
Real-Time Updates Using WebSockets4 / 10
Socket.io server integrated with Express HTTP server (server.js:22-29), room-based pattern: clients emit "join_poll" with pollId to join room (server.js:52-57). Server emits "response_submitted" event to poll room after each response (responseController.js:112-120). Client AnalyticsPage connects via io(url) constructor (AnalyticsPage.jsx:18-23), joins room on mount, listens for response_submitted to trigger analytics refetch (AnalyticsPage.jsx:59-68), cleans up with socket.off on unmount. Gaps: signal-only approach (pushes no actual data — client re-fetches via HTTP), ONLY 1 event type (response_submitted), no socket events for poll publishing/expiry/deletion, no reconnect room re-join logic, no socket authentication, socket connection NOT cleaned up on unmount (only event listener removed), no debouncing for rapid submissions, no Socket.io on public voting page (only analytics page), backend server NOT actually running at deployment URL so real-time updates are code-only.
Code Quality & Project Structure4 / 10
Clean client/server monorepo with consistent naming conventions (PascalCase components, camelCase functions, kebab-case files). Server follows routes→controllers→models→middleware pattern. Client has pages/context/routes/services separation. Gaps: NO root README.md (only client/README.md which is unmodified Vite starter template — 17 lines about HMR and ESLint), App.jsx (122 lines) is COMPLETELY DEAD Vite starter code (counter component referencing reactLogo, viteLogo, heroImg — never imported by main.jsx), App.css (292 lines) is unused Vite starter CSS, COLORS constant duplicated identically in AnalyticsPage.jsx:24 and PublicResults.jsx:6, no TypeScript, no automated tests, no .env.example, console.log in production (server.js:3 instances, MyPolls.jsx:2 instances), no Axios interceptor for token injection (manual localStorage.getItem in every API call), no shared UI components (all JSX inline in page files), no ESLint on backend.
101
Polling Application
Bhupesh Joshi · @bhupesh.coding_9fa72b92
41
Polling Application by Bhupesh Joshi is a full-stack polling platform with React/TypeScript frontend and Express/MongoDB backend. The project demonstrates solid core functionality: JWT-based authentication (delegated to a separate auth microservice), poll creation with multi-question support, response collection with thorough server-side validation, analytics with per-question option breakdowns, and a publish results flow. The backend code is clean MVC with Zod validation and proper Socket.io setup. However, the project has significant gaps: both frontend and polling API deployments are down (404 not found on Vercel), the real-time WebSocket feature is broken on the frontend (uses polling instead of Socket.io client), the frontend contains significant leftover template code from another project ("PortfolioAudit"), there is no poll edit functionality, no anonymous duplicate response prevention, no security middleware (Helmet, rate limiting), and the backend lacks TypeScript and tests. Overall: a functional proof-of-concept with decent core implementation but incomplete production polish.
Authentication & Access Control6 / 10
Backend has functional JWT verification via extractUser/requireAuth middleware (auth.js:9-44) that parses Bearer tokens, attaches req.user, and gates protected routes. Anonymous vs authenticated poll modes enforced server-side (response/controller.js:26-28). Frontend auth-context.tsx (161 lines) provides full login/signup/logout/forgot-password/reset-password flow with useReducer state machine, token persistence in localStorage (auth-route.ts:16-20), and session restore via /auth/me. Dashboard auth guard prevents unauthenticated access (dashboard.tsx:99-103). Gaps: auth is entirely dependent on external auth-service microservice (not in this repo), no Helmet/CSRF/rate limiting, no token refresh/rotation, tokens stored in localStorage without httpOnly cookies.
Poll Creation & Question Management6 / 15
Full poll creation form at dashboard.polls.create.tsx (335 lines) with dynamic questions (add/remove), dynamic options per question (min 2 enforced), mandatory/optional toggle per question, anonymous/authenticated toggle, and datetime-local expiry picker with min constraint. Client-side validation checks title, expiry, question text, and option text completeness. Backend Zod validation (poll/controller.js:5-21) enforces min 1 question, min 2 options per question, text required for both, and expiresAt must be in future (controller.js:40-42). Significant gap: no poll edit/update functionality exists — the PATCH /:id/publish endpoint only toggles isPublished, and there is no endpoint or UI for modifying questions, options, or poll metadata after creation.
Response Collection Flow6 / 15
Response submission (response/controller.js:16-89) validates: poll existence (404), expiry (410), auth requirement for non-anonymous polls (401), Zod schema (400), mandatory question completion (400), questionId+optionId existence in poll (400), and duplicate response prevention for authenticated users via explicit findOne check (409). Response model has unique compound index with partialFilterExpression {respondentId:{$ne:null}} (model.js:17-20) as defense-in-depth. Hooks increment totalResponses counter and emit Socket.io analytics:update. Proper HTTP status codes throughout. Gaps: no duplicate prevention for anonymous respondents (null respondentId bypasses partial index), no MongoDB transaction for atomic response creation + counter update, no request body size limits.
Analytics & Feedback Dashboard6 / 15
Analytics dashboard at dashboard.polls.$pollId.analytics.tsx (226 lines) shows stat cards (total responses, questions, required count), per-question breakdowns with option bars (counts, percentages, 700ms animated progress bars, top-option highlighting with primary color), and answered/skipped counts per question. buildAnalytics function (response/controller.js:92-141) computes option counts, percentages, and per-question metrics. Publish button with confirmation dialog, and published polls make analytics publicly viewable via the same /poll/$pollId link (ResultsView component in poll.$pollId.tsx:16-72). Gaps: no CSV/JSON export, no individual response viewing, no time-series/trend data, analytics computation uses N+1 nested loops filtering responses array, no anonymous vs authenticated breakdown.
Frontend Experience5 / 10
Clean UI with shadcn/ui + Tailwind CSS v4, dark/light mode toggle (theme-context.tsx:34 lines), responsive layout (sidebar hidden on mobile). All poll pages handle loading (spinners), error (toast messages), empty (no polls CTA), and auth states. Poll response page covers expired/published/submitted/auth-required states. Landing page has feature cards and gradient hero. However: significant leftover "PortfolioAudit" template code — audits.ts (72 lines of hardcoded sample data), dashboard.new.tsx, dashboard.samples.tsx, dashboard.feedback.$id.tsx (all unrelated to polls). Login title says "PortfolioAudit", signup subtitle says "Start auditing portfolios." No form library on poll creation (individual useState hooks). No confirmation dialogs except delete.
Backend Architecture & API Design5 / 15
Clean MVC architecture: routes (poll/routes.js:22 lines, response/routes.js:13 lines) → controllers (poll/controller.js:145 lines, response/controller.js:158 lines) → Mongoose models (poll/model.js:36 lines, response/model.js:22 lines). Zod validation on all inputs (createPollSchema 5-21, submitSchema 11-13). CORS configured with dynamic origin checking (index.js:48-58). Proper Mongoose schemas with validators, timestamps, unique compound index with partialFilterExpression. Vercel serverless entry point (api/index.js:80 lines) with DB connection middleware. Socket.io attached to HTTP server. Gaps: no rate limiting, no Helmet security headers, no request body size limits, no pagination on getMyPolls, raw err.message exposed in global error handler (index.js:74), dotenv.js has unused mongoUri() function, no MongoDB transactions for atomic operations.
Real-Time Updates Using WebSockets2 / 10
Backend has proper Socket.io setup on HTTP server (index.js:15-45) with room-based pub/sub pattern: clients join `poll:{pollId}` rooms, server emits analytics:update with full analytics payload on each response submission (response/controller.js:81-86). Server also handles leave:poll and disconnect cleanup. However, the frontend analytics page (dashboard.polls.$pollId.analytics.tsx:117-123) uses a 5-second setInterval polling via React Query cache invalidation instead of an actual Socket.io client connection. The wsRef is declared (line 101) but never connected to Socket.io. The socket.io-client is not in frontend package.json dependencies. The backend Socket.io infrastructure is effectively unused on the client side — there is no live real-time functionality delivered to users.
Code Quality & Project Structure5 / 10
Monorepo with clear frontend/backend separation. Frontend uses TypeScript with TanStack Router file-based routing, React Query for data fetching, ESLint+Prettier configured. Backend is well-organized into routes/controllers/models/middleware/lib layers. Clear naming conventions and Zod type schemas. Issues: backend is plain JavaScript (no TypeScript), leftover "PortfolioAudit" template code (4 dead route files + audits.ts data module totaling ~350 lines of irrelevant code), login/signup page metadata titles reference wrong product, duplicate API fetch helpers (auth-route.ts:apiFetch vs poll-api.ts:pollFetch with similar logic), localStorage theme key uses "portfolioaudit_theme", .DS_Store files committed, gitignore file misspelled (missing dot), no automated tests anywhere, console.log statements in production backend (index.js:30,35,43), signup.md and backend.md working notes committed.
102
Pulse Board
Ayush Jain · @ayushjjainn
41
PulseBoard is a functional MERN polling platform with strong frontend presentation (polished CSS design system, comprehensive state handling, dark mode, nice animations) but weak backend engineering. The auth system is feature-rich (OTP verification, Google OAuth, forgot/reset password) but undermined by a hardcoded JWT secret fallback and missing security middleware. Poll creation works but lacks editing capability. Response collection has good validation but missing key checks. Analytics are visually appealing but shallow (no trends, no exports). Real-time is a minimal invalidation signal rather than true live data. The backend has zero database indexes, no input validation library, and no tests. Total: 41/100.
Authentication & Access Control5 / 10
Auth system has registration with OTP email verification (bcrypt 10 rounds, SendGrid API+SMTP dual delivery), login, Google OAuth (both idToken and accessToken flows), forgot/reset password, and JWT middleware. Anonymous vs authenticated poll modes enforced server-side (pollRoutes.js:104-125). AuthContext with localStorage persistence, 401 interceptor, and authReady pattern. Critical gaps: JWT secret hardcoded as "secret" fallback in 4 call sites across 3 files (auth.js:7, authRoutes.js:13, pollRoutes.js:69,112), no rate limiting on OTP endpoints, tokens in localStorage, no Helmet/CSRF, no refresh token rotation.
Poll Creation & Question Management5 / 15
CreatePoll.jsx (536 lines) has dynamic questions/options with add/remove, mandatory/optional toggle, anonymous toggle, expiry datetime picker, and sectioned form layout. Server validates title + expiry + min 1 question (pollRoutes.js:27-30). Significant gaps: no poll editing endpoint exists (PATCH only for publishing, not modifying questions/options), no server-side validation of individual question/option fields (text, min 2 options), no slug/QR sharing, no form library, no draft status. Covers the basic creation flow but lacks depth.
Response Collection Flow6 / 15
Response flow has multiple validation layers: expiry check (pollRoutes.js:98-99), mandatory question validation with specific error messages (pollRoutes.js:137-148), auth-gating for non-anonymous polls (pollRoutes.js:104-125), duplicate prevention by userId+pollId (pollRoutes.js:127-133), and proper HTTP status codes (400/401/403/404). Frontend PollView.jsx (412 lines) has radio selection, progress bar, required-remaining counter, disabled/expired states, post-submission success screen. Gaps: no option existence validation server-side, no anonymous duplicate prevention (no IP/device tracking), no question existence validation in answers payload, hasSubmitted is client-only state.
Analytics & Feedback Dashboard6 / 15
PollResults.jsx (584 lines) displays total responses, per-question option percentages/vote counts, winning option detection, animated progress bars (IntersectionObserver, staggered 80ms delays), voter lists with avatars (inline chips for ≤2, expandable dropdown for 3+). Creator-only publish button with expiry gate, published status indicator, zero-response states. Dashboard (Dashboard.jsx, 265 lines) lists polls with status dots, question counts, copy-link, and delete. Gaps: no time-series/trend analytics, no daily velocity, no CSV export, no individual response viewing, analytics computed in-memory per request (no MongoDB aggregation pipeline), no preloaded response counts on dashboard cards.
Frontend Experience7 / 10
Consistent design language with CSS token system (design tokens in index.css:6-58), dark/light mode (ThemeContext.jsx), custom animations (fade-in, slide-up with staggered nth-child delays, scale-in, shimmer skeletons, animated progress bars). All 8 routes handle loading (skeleton cards on Dashboard/PollView/PollResults), error (toast notifications + "not found" pages), empty (dashed-border CTA on Dashboard, "No responses yet" in analytics), and auth states (ProtectedRoute spinner, auth-required banner on PollView). Navbar has scroll-aware frosted glass, auth-conditional links, theme toggle. Gaps: no form library (raw useState throughout), extensive inline styles bypassing CSS classes, GoogleIcon duplicated in Login+Register, no data caching layer, no PWA support.
Backend Architecture & API Design4 / 15
Functional Express 5 API with RESTful routes, JWT middleware, Socket.io integration, and Mongoose schemas with proper references. Dependencies are appropriate (mongoose 9, socket.io 4, bcryptjs, jsonwebtoken). Critical gaps: zero explicit database indexes (only implicit email unique in User.js — no indexes on pollId, creator, or {pollId,userId} compound), no input validation library (no Zod/Joi/express-validator), hardcoded JWT secret fallback "secret" in 4 locations, no rate limiting (especially dangerous on OTP endpoints), no Helmet security headers, no request body size limits, error.message leaked to clients in all catch blocks, empty catch swallows errors silently (pollRoutes.js:73), no service layer (all logic in route handlers), no pagination.
Real-Time Updates Using WebSockets4 / 10
Socket.IO server attached to HTTP server with CORS (index.js:16-24), singleton IO pattern via socket.js setIO/getIO. Server handles join_poll room subscription (index.js:48-50) and emits poll_updated on response submission with totalResponses+analytics (pollRoutes.js:158-168). Client connects in PollResults.jsx:100-103, joins room, listens for poll_updated to trigger HTTP refetch, disconnects on unmount. Gaps: pure invalidation signal (no data pushed, client refetches HTTP), only covers one page (no live updates on poll answering page), no reconnection handling/room re-join, no room leave events, no debouncing, no socket authentication, only one event type, no broadcast on publish/delete.
Code Quality & Project Structure4 / 10
Clean monorepo with backend/frontend separation, CSS design system with tokens, ESLint configured, comprehensive README with setup instructions. Proper React patterns: useEffect cleanup on all effects, useRef for mutable refs, authReady flag, controlled OTPInput component with keyboard navigation. Gaps: no TypeScript, zero automated tests (backend test script explicitly returns "Error: no test specified"), no service layer separation, JWT secret hardcoded in 4 locations across 3 files, GoogleIcon SVG duplicated in Login.jsx:144-151 and Register.jsx:204-211, Bearer token extraction duplicated in 3 locations, no .env.example file, .env.production committed to repo, backend/test-register.js broken (CommonJS require in ESM project), extensive inline styles in every page component bypassing CSS class system.
103
PulseBoard
Sakshi Jha · @jhasakshi422_60672667
41
PulseBoard is a full-stack polling app (React/TypeScript/Vite frontend, Node.js/Express/PostgreSQL/Drizzle backend) with JWT auth, dynamic poll creation, response collection with expiry handling, Highcharts analytics, Socket.IO real-time updates, and result publishing. The project demonstrates competence across all required features but suffers from several quality and security gaps: HMAC-SHA256 instead of bcrypt for passwords, no database transactions for multi-table operations, N+1 query patterns, no duplicate vote prevention, no socket authentication, hardcoded localhost URLs preventing deployment, and code quality issues (any typing, @ts-ignore, dead code, no tests). Total score: 41/100 — a functional but shallow implementation that works locally but needs hardening for production use.
Authentication & Access Control5 / 10
Working JWT auth with registration/login, dual tokens (15-min access, 7-day refresh), refresh token hashing, auth middleware with restrictToAuthenticatedUser(), and frontend with react-hook-form + Axios interceptor with token refresh queueing (api.ts:14-30,64-119). BUT uses HMAC-SHA256 instead of bcrypt for password hashing (auth/controller.ts:24,48) — a critical security gap. No rate limiting, Helmet, CSRF protection, email verification, or password reset. typeOfRespondents='authenticated' is never enforced at submission (submitResponse has no auth middleware — poll/routes.ts:55-58). No frontend route guards — /dashboard is accessible without tokens. Tokens in localStorage. 8 @ts-ignore instances from missing Express Request type augmentation.
Poll Creation & Question Management6 / 15
CreatePoll.tsx (290 lines) with react-hook-form useFieldArray for dynamic questions/options, mandatory/optional toggle per question, anonymous/authenticated respondent toggle, and datetime-local expiry picker. Server Zod validation enforces min title(3), min questions(1), min options per question(2), min questionText(4), min option text(2) in models.ts. Update endpoint supports both creating new and editing existing questions/options (service.ts:104-339). BUT no poll slug/permalink or QR sharing, no draft status concept, QuestionBlock uses `any` typing (CreatePoll.tsx:185), no client-side future-date validation on expiry, drag icon present but non-functional, no confirmation dialogs for deletions, and update/delete use POST instead of PATCH/DELETE.
Response Collection Flow5 / 15
PollPage.tsx (458 lines) handles all states: active voting, submitted, expired, published. Server-side expiry check (service.ts:685-701) and required-question validation (service.ts:721-744). Client-side required-answered check disables submit (pollPage.tsx:250-253). Countdown timer with real-time expiry detection (pollPage.tsx:66-99,230-242). Unique constraint on (responseId, questionId) in answers schema. BUT NO duplicate vote prevention — no unique index on (pollId, userId), no localStorage/session tracking, no IP-based limiting. submitResponse accepts `body: any` with zero Zod validation (service.ts:641-642). No option-to-question validation (submitted optionIds not verified against poll's actual options). userId column exists in responses table but is never populated. No anonymous voter identification.
Analytics & Feedback Dashboard6 / 15
PollAnalytics.tsx (210 lines) with Highcharts bar charts per question, winner highlighting (#0f766e), top-choice text insights, and total response count. Publish results button in dashboard with "Published" badge state. Published polls make results publicly viewable via the poll page. Real-time analytics updates via Socket.IO push full analytics data. Proper chart cleanup in useEffect (line 115). BUT analytics use N+1 queries (service.ts:1062-1129 loops questions→options→answers individually). No CSV/export, no trend/time-series analysis, no individual response viewing, no anonymous vs authenticated breakdown, no summary stat cards (peak activity, response velocity). Total responses computed as max per-question answers, not distinct respondents. 4-color hardcoded palette.
Frontend Experience5 / 10
Clean Tailwind CSS v4 UI with consistent teal/gray design, comprehensive state handling (loading/error/empty/success across all pages), and responsive layout with sm:/xl: breakpoints. Auth page has split-panel layout with field-driven form config. Poll page properly handles active/voting/submitted/expired/published states. Submit button disabled until required questions answered. "Copied!" feedback on share. BUT loading states are plain text (no spinners/skeletons), xl: breakpoint at 1280px leaves tablet users with mobile layout, hardcoded profile data (name "Sakshi Jha", email "sakshi@gmail.com" at dashboard.tsx:46-62), 185-line dead App.css from Vite scaffolding, no 404 page, no toasts, no confirmation dialogs, logout uses window.location.href instead of useNavigate, hardcoded localhost:8080 URLs prevent deployment.
Backend Architecture & API Design4 / 15
Feature-based module organization with controller-service separation in poll module, Zod validation on createPoll and auth payloads, Drizzle ORM with 6 properly-related PostgreSQL tables, UUID PKs with DB-generated defaults, and pgEnum for respondent_type. BUT NO database transactions — createPoll writes to 3 tables sequentially, deletePoll cascades through 5 tables, any mid-operation failure leaves inconsistent state. N+1 queries in getPoll, getAnalytics, and submitResponse analytics. No helmet, no rate limiting, manual CORS headers instead of cors package. HMAC-SHA256 for passwords. HTTP verb misuse (POST for update/delete). submitResponse accepts `any` with zero validation. Raw error objects exposed in all 8 controller catch blocks. No ON DELETE CASCADE — manual cascading deletes risk orphans. socket.io-client misplaced in production deps. No graceful shutdown or env validation.
Real-Time Updates Using WebSockets5 / 10
Socket.IO server integrated with HTTP server (socket/index.ts, 71 lines), room-based pattern with join-poll/leave-poll on `pollId` rooms. Two server-emitted events: analytics-update on response submission (service.ts:854-877) and poll-published on publish (service.ts:967-972). Frontend client with lazy connection (autoConnect:false in socket.ts:4), proper lifecycle in useEffect cleanup on both dashboard (lines 260-278) and pollPage (lines 191-228). Analytics pushed as full data (not just signal), enabling instant UI update. BUT NO socket authentication — anyone can join any room and receive analytics. CORS set to `*`. No disconnect/reconnection room re-join logic. No debouncing. No connection status UI. No typed events or namespacing. Event names duplicated as magic strings. Analytics payloads reconstructed with N+1 queries inside submitResponse.
Code Quality & Project Structure5 / 10
Monorepo with separate backend/client packages and TypeScript throughout. Feature-based module organization (auth/, poll/, middleware/, db/schema/). Comprehensive README with setup instructions, API table, and architecture overview. Consistent naming conventions and ESLint on frontend. BUT no automated tests. Backend tsconfig lacks strict mode. 8 @ts-ignore instances in backend due to missing Express Request augmentation. QuestionBlock uses `any` props (CreatePoll.tsx:185). Dead 185-line App.css from Vite scaffolding. Duplicated analytics-building code in submitResponse and getAnalytics (service.ts:793-851 ≈ 1056-1129). Duplicated ownership check pattern in 4 service methods. Magic socket event strings duplicated across dashboard and pollPage. No shared types between frontend/backend. No .env.example. console.log in production (socket/index.ts:23,59). Hardcoded localhost URLs in api.ts:5,86 and socket.ts:3.
104
Qwizrs
Ayandip Pal · @ayandip
41
Qwizrs is a well-implemented LIVE QUIZ PLATFORM (Kahoot-style) with solid engineering, but fundamentally misaligned with the poll platform requirements. The submission builds a real-time quiz game where a host creates quizzes with correct answers, starts live sessions, players join via code and answer against a timer, scores are tracked on a leaderboard, and results are shown during gameplay. This architecture diverges from every poll requirement: no shareable poll links, no async response collection, no question-wise analytics (only score rankings), no results publishing, and no poll expiry. The auth system (JWT, email verification, password reset) and real-time infrastructure (Socket.IO with Redis) are well-built, and the UI is polished with Framer Motion. However, the core product delivers a quiz game engine, not a polling platform. Deployment is verified working at qwizrs.site and api.qwizrs.site.
Authentication & Access Control6 / 10
Solid auth: registration with argon2 hashing + email verification via Resend (auth.service.ts:22-48, generateValidationEmail:52-94), login with JWT access+refresh tokens in httpOnly/secure cookies (auth.controller.ts:28-46), forgot/reset password (auth.service.ts:258-356), Zod validation middleware on all inputs (validateSchema.ts:6-38), JWT middleware merges decoded userId into req.body (host.middleware.ts:4-21), rate limiting on auth endpoints. Frontend AuthContext (AuthContext.jsx:77 lines) with localStorage persistence, Axios interceptor. Gaps: no anonymous poll response mode (requiresAuth flag only controls quiz participation), no Helmet/CSRF, no token rotation on refresh, logout sends userId from client body (not extracted from token).
Poll Creation & Question Management5 / 15
QuizCreator.jsx (472 lines) allows dynamic questions with add/remove, dynamic options (2-6), correct answer selection, time limits and points per question. Public/Private toggle for auth requirement. Edit mode via /edit/:id. Zod validation on backend (QuizSchema + QuestionSchema in questionvalidationSchema.ts:1-26) enforces min 1 question, min 2 options, min 5s time. Delete confirmation modal. BUT this is a quiz creator, not a poll creator: no mandatory/optional question toggle, no poll expiry date, no shareable poll link/slug, no "anonymous responses" mode (auth toggle is about who can join the live game), questions require a "correct" answer (quiz feature). Questions stored as JSONB in PostgreSQL (schema.ts:58-66).
Response Collection Flow3 / 15
Response collection is entirely Socket.IO-based for live quiz games: players join via client:playerJoin (socket.service.ts:166-292), submit answers via client:playerAnswer with Redis-based duplicate prevention (sismember check at line 374, sadd at line 398). Server validates: quiz exists, quiz is active (status check), participant hasn't already answered. No HTTP poll response endpoint exists. No asynchronous response collection — participants must be in a live game session. No poll expiry mechanism (quiz sessions use 2-hour cleanup timer). No shareable poll link for response collection (quiz uses a 6-char code requiring a live host). The real-time answer system works for its intended purpose but completely misses the poll response collection requirements.
Analytics & Feedback Dashboard3 / 15
Dashboard (Dashboard.jsx:439 lines) shows quiz library with total participants/average score stat cards and 2-tab layout (Library/History). HistoryResults.jsx (230 lines) displays a podium visualization (top 3), full leaderboard table with ranks/scores, and CSV export via csvExport.js. getQuizLeaderBoardService (host.service.ts:418-431) fetches session results from quizHistoryTable. BUT this is a game leaderboard, not poll analytics: no question-wise response summaries, no per-question option counts/votes, no participation trend insights, no "publish results to public link" feature, no response velocity charts, no anonymous vs authenticated breakdown. The results are score-based rankings (leaderboard), not poll response analytics.
Frontend Experience6 / 10
Polished UI: Framer Motion animations throughout (page transitions, modal enter/exit, bar chart demo, hover effects), dark/light theme toggle via CSS variables (Navbar.jsx:18-24), responsive mobile menu, glass-morphism design language, landing page with animated live bar chart demo (Landing.jsx:292 lines), confirmation modals for destructive actions (delete quiz, delete question), react-hot-toast notifications, loading spinners on Dashboard and HistoryResults, and proper error state handling. 14 routes in App.jsx. Gaps: entirely JavaScript (no TypeScript on frontend), no form library (individual useState hooks in QuizCreator), no protected route components (no redirect to login for unauthenticated dashboard access), the UX is built around a live quiz hosting model rather than poll creation/sharing.
Backend Architecture & API Design6 / 15
Clean layered architecture: routes→controllers→services→middleware separation. TypeScript throughout backend. Zod validation on auth (verification.model.ts:3-37) and quiz inputs (questionvalidationSchema.ts:1-26). Drizzle ORM with clean PostgreSQL schema (schema.ts:79 lines, 4 tables with foreign keys, enum types). Centralized error handling (error.middleware.ts:43 lines, handles ApiError + ZodError). Rate limiting via express-rate-limit (rateLimit.middleware.ts:41 lines). CORS configured for production domains. JWT middleware for protected routes. AsyncHandler wrapper. Redis integration for real-time state. Gaps: PostgreSQL not MongoDB (MERN stack deviation), extensive `any` types in host.service.ts (userData: any at line 8, questions as any[] throughout), rate limits set to 100000 ("ABSURD AMOUNT FOR TESTING"), no Helmet, no request body size limits, no .env.example file, no tests, no graceful shutdown.
Real-Time Updates Using WebSockets7 / 10
Robust Socket.IO implementation: 8 event handlers with room-based pattern (index.socket.ts:102 lines), Redis-based per-socket rate limiting at 1 req/sec with atomic SET NX (index.socket.ts:31-45), centralized withErrorHandling wrapper for all socket handlers, host/player role separation, real-time live stats pushed to host (server:liveStatsUpdate in socket.service.ts:403-413), player join/leave notifications to room, host disconnect with 2-hour cleanup timer (lines 634-683), reconnection with score persistence (Redis ZSET preserves scores), atomic Redis pipeline operations for quiz initialization (host.service.ts:320-341), leaderboard computed via Redis ZREVRANGE. However, this is built for live quiz game orchestration (host/player rooms, question lifecycle, scoring), not for poll real-time updates (live response counts on a public page, analytics dashboard streaming).
Code Quality & Project Structure5 / 10
Clean monorepo with backend/frontend separation. TypeScript on backend with tsconfig.json, ESLint on frontend (eslint.config.js), Prettier on backend (.prettierrc). Docker support (docker-compose.yml for PostgreSQL + Valkey). Drizzle migrations with snapshot tracking (3 migration files). Single-purpose utility files (ApiError, ApiResponse, AsyncHandler, Cloudinary, MailHandler, UserCleanup). Comprehensive README with setup instructions, tech stack badges, architecture diagram. Gaps: no tests anywhere (no test scripts, no test files), frontend is entirely JavaScript (no TypeScript), extensive `any` types in backend services (host.service.ts has `any` in 8+ function signatures), mock rate limits with "ABSURD AMOUNT FOR TESTING" comments, no .env.example file, console.log in production code (socket.service.ts, host.service.ts), dead imports (Settings, ImageIcon, ShieldCheck, ShieldAlert in QuizCreator.jsx), `.js` imports in TypeScript files (validateSchema.ts:2 imports from .js file), nodemailer dependency unused (using Resend instead).
105
Votely A Polling App
Aadarsh Pandey · @adarsh_dev
41
Votely is a functional MERN polling app deployed on Railway that covers all core requirements: auth (register/login/JWT), poll creation with multi-question support, response collection with layered validation, analytics with Recharts, real-time updates via Socket.IO, and published results. The 501-line server (9 files) provides RESTful APIs with proper HTTP status codes and Mongoose models. The 1,559-line client (14 source files) handles loading/error/empty states across 8 routes with 7 distinct view states in PublicPoll. However, quality is average: no rate limiting or security middleware, raw error messages leaked, socket connections unauthenticated, code duplication between CreatePoll/EditPoll (~140 lines), no TypeScript, no tests, dead code throughout, and inadequate error handling on the socket client. A solid partial implementation that works but lacks the depth and polish expected of higher-scoring submissions.
Authentication & Access Control5 / 10
Auth system has registration with bcrypt (10 rounds), login with generic 401 (no user enumeration), JWT (7-day token), and Bearer-token middleware with auth/optionalAuth variants (middleware/auth.js:29 lines). AuthContext with login/register/logout + localStorage (AuthContext.jsx:49 lines). ProtectedRoute/GuestRoute wrappers in App.jsx. Anonymous vs authenticated poll modes enforced server-side at response submission (responses.js:21-22) and client-side with auth-gate UI (PublicPoll.jsx:174-186). Gaps: no rate limiting on auth endpoints, tokens in localStorage (XSS risk), raw err.message on 500 errors, empty catch{} in optionalAuth silences infrastructure failures, no email format validation, no password reset, no CSRF/Helmet.
Poll Creation & Question Management6 / 15
CreatePoll.jsx (211 lines) supports dynamic questions/options with immutable state updates via functional setState, mandatory/optional per-question toggle, anonymous poll toggle, expiry DatePicker with 5-min minDate, and 6-step client-side validation (title, expiry future, question text, min 2 options, option text). Server validates title, min 1 question, and expiry (polls.js:11-13). Mongoose questionSchema enforces min 2 options per question (Poll.js:10). EditPoll.jsx (165 lines) enables poll editing with data fetch and form pre-population. nanoid(10) for share tokens. Gaps: ~80% code duplication between CreatePoll/EditPoll (~140 lines), EditPoll missing future-expiry validation, sanitizePoll() is a no-op stub (polls.js:200-203), isActive field defined but never used (Poll.js:23), no draft status, no question reordering.
Response Collection Flow6 / 15
Response submission (responses.js:69 lines) has layered server-side validation: poll lookup (404), expiry check (410), published check (403), auth gate for non-anonymous polls (401), duplicate check via findOne (409), mandatory question validation (400), and answer reference validation (questionId+optionId against poll subdocs). Proper HTTP status codes throughout. PublicPoll.jsx (246 lines) handles 7 distinct UI states: loading, not-found, submitted success, already-responded, published results view, expired, unauthenticated gate, and active response form with radio buttons. Socket-powered live count. Gaps: race condition on duplicate check (no compound unique index on {poll,respondent}), no duplicate prevention for anonymous polls, no rate limiting, string comparison bug potential at responses.js:36 (=== vs .toString()), no transaction around response creation + socket emission.
Analytics & Feedback Dashboard6 / 15
buildAnalytics() (polls.js:157-198) computes total responses, per-question option counts/percentages, daily submission timeline, and completion rate. PollAnalytics.jsx (196 lines) renders BarChart + LineChart via Recharts with live socket updates, publish button with window.confirm, share-link copy, and empty-state handling. PublicPoll.jsx published results view (lines 119-159) shows BarChart with per-option percentage bars. Dashboard.jsx (125 lines) has summary stat cards (total polls, responses, active, published) with skeleton loading and empty state. Publish flow via POST /:id/publish broadcasts poll:published over socket. Gaps: no CSV/export, no individual response viewing, misleading completionRate (per-question full-coverage vs per-response completeness), N+1 query in GET /my (individual countDocuments per poll at polls.js:32-35), no peak-activity or time-of-day metrics, no pagination.
Frontend Experience5 / 10
CSS Modules with custom design system (CSS variables: --bg, --surface, --text, --accent, etc.), consistent card-based layout across 8 routes. React Router v6 with ProtectedRoute/GuestRoute patterns. Skeleton loading on Dashboard (Dashboard.jsx:98-101), spinner loading on all data-fetching pages. Error handling via react-hot-toast on all API failures. Empty states: Dashboard "No polls yet" with CTA, Analytics "No responses yet". PublicPoll handles 7 distinct view states. Responsive layout via flexbox/grid with hidden navbar on public pages. Gaps: no dark mode, no animations/transitions (no framer-motion), native window.confirm() for destructive actions, hard window.location.href full reload on 401 (api.js:16), dead Navbar link to /polls/my (undeclared route), no form library (raw useState hooks), no CSS framework for polished components.
Backend Architecture & API Design5 / 15
Express with separate route modules (auth/polls/responses), Mongoose models with embedded subdocs (questionSchema/optionSchema), JWT middleware (auth+optionalAuth), Socket.IO integration via req.io middleware (index.js:37-39). RESTful API with consistent JSON responses and proper HTTP status codes (400/401/403/404/409/410/500). Proper route ordering to avoid wildcard conflicts. Gaps: no service layer (business logic in route handlers), no input validation library (relies on Mongoose validators only), no security middleware (no Helmet, rate limiting, request size limits), raw err.message on all 500 errors, buildAnalytics cross-required between route modules (circular dependency risk at responses.js:59), no transactions, no DB indexes beyond implicit unique, no centralized error handler, server starts before MongoDB connection, no graceful shutdown, sanitizePoll no-op stub, isActive dead field on Poll model.
Real-Time Updates Using WebSockets4 / 10
Room-based Socket.IO pattern: clients join/leave poll rooms via join:poll/leave:poll (socket/index.js:21 lines). Server emits response:new on submission with {pollId, totalResponses, analytics} (responses.js:61) and poll:published on publish (polls.js:137). Client socket.js (15 lines) uses lazy connection with autoConnect:false and WebSocket-only transport. PublicPoll.jsx listens for response:new (updates liveCount) and poll:published (transitions to results mode). PollAnalytics.jsx similarly listens and properly calls leavePoll on cleanup. Gaps: no socket authentication (any client joins any room), no connect_error/disconnect/reconnection handlers on client, race condition (joinPoll called before .on listeners registered in PublicPoll.jsx:28-29), PublicPoll cleanup doesn't call leavePoll, no authorization on room join, no error events handled, full analytics recomputed per submission (O(N)), no debouncing on client events.
Code Quality & Project Structure4 / 10
Clean monorepo structure (client/server separation) with Docker/Docker Compose. Consistent naming conventions (camelCase variables, PascalCase components). CSS Modules for scoped styling. Immutable state updates with functional setState throughout. README (128 lines) has setup instructions, API table, and WebSocket event docs. Gaps: no TypeScript anywhere (all .js/.jsx), ~80% code duplication between CreatePoll/EditPoll (~140 lines), dead code (sanitizePoll no-op, isActive field, dead /polls/my nav link), no tests, no ESLint/Prettier config, console.log in production (socket/index.js:3,8,16), buildAnalytics cross-required between route modules, empty catch{} in optionalAuth, hardcoded fake fallback stats in Home.jsx ({polls:120, responses:4800, users:340}), inline require('../../models/User') instead of top-level import (polls.js:48).
106
chai-poll
Aman · @amansaluja017
41
Chai Poll is a functional polling platform with OAuth authentication, dynamic poll creation (multiple choice + text), response collection with duplicate prevention, analytics dashboard, result publishing, and basic Socket.IO real-time updates. The frontend has polished glassmorphism UI with dark/light themes. However, it has significant gaps: OAuth-only auth (no password registration), no poll editing, Recharts installed but unused (no chart visualizations), shallow WebSocket implementation (2 events only, results page only), missing security middleware (Helmet, rate limiting), no transactional writes for response collection, console.log leaks and dead code in production, and no automated tests. Total line count is approximately 2,500 lines of application code across ~40 source files. The deployment at chai-poll-five.vercel.app is live and serving the full application.
Authentication & Access Control6 / 10
OAuth-only authentication via external provider with authorization code flow (auth.services.ts:19-92). Dual JWT tokens with separate secrets, httpOnly/secure/sameSite refresh token cookie, Redis session management with UUID-based session IDs. Two-tier middleware (verifyJWT optional + requireAuth mandatory, auth.middleware.ts:7-49). Nonce-based CSRF protection in OAuth flow. Auto token refresh via axios interceptor (api.service.ts:15-33). Anonymous vs authenticated poll mode enforced server-side (poll.services.ts:114-116). Guest ID generation with localStorage persistence. Gaps: no password registration/login, no Helmet security headers, no rate limiting, no email verification, no CSRF beyond OAuth nonce, global verifyJWT middleware runs on auth routes unnecessarily.
Poll Creation & Question Management6 / 15
Full poll creation form at routes/_protected/poll/index.tsx (380 lines) with dynamic questions/options (add/remove), multiple choice + text types, per-question mandatory/optional toggle, authentication requirement toggle, expiry presets (30/40/60 min + custom minutes/hours/days). Server-side Zod validation (poll.dto.ts:5-20). Poll schema with embedded question subdocuments tracking option votes and text responses (poll.schema.ts, 81 lines). Redis caching after creation. Gaps: no poll editing endpoint or UI after creation, no slug/permalink generation, react-hook-form installed but unused (individual useState hooks instead), no QR code for sharing, and the poll detail page has dead code typo at line 120.
Response Collection Flow6 / 15
Response form at response/$pollId/index.tsx (291 lines) handles radio selection for choices, textarea for text, timer countdown, expiry enforcement (submit disabled when expired), auth gate with login redirect, guest ID generation, and comprehensive loading/error/success state UI. Server validates required questions (poll.services.ts:134-141), validates option IDs belong to poll (lines 143-162), checks expiry/status (lines 110-112), enforces auth mode (lines 114-116), and prevents duplicates by user or guestId (lines 118-131). Gaps: no transactional write for duplicate prevention (race condition possible), no compound unique index on Response model, no Mongoose runValidators on save (line 182), and the socket emit pattern after submission is fragile.
Analytics & Feedback Dashboard5 / 15
Dashboard (dashboard.tsx, 192 lines) with stat cards showing total polls/responses/active/expired, search and filter by status/sort. Results component (Results.tsx, 181 lines) renders per-question option breakdowns with CSS progress bars and percentages, text response display with expand/collapse (10 visible by default), and total vote counts. Responses component (Responses.tsx, 172 lines) lists responders with names, emails, timestamps, and anonymous name generation. Publish results feature changes status so anyone can view outcomes. Gaps: Recharts installed but unused (no charts anywhere), no CSV/export despite FAQ claiming it, no trend/time-series data, no per-question drop-off analysis, no individual response detail viewing, no peak activity metrics.
Frontend Experience5 / 10
Polished glassmorphism UI with Tailwind CSS v4, custom design tokens, dark/light theme toggle with system detection, collapsible animated header, responsive layout. Loading skeletons, error banners with icons, empty states, and success states across all pages. Landing page with hero section, feature cards, and FAQ accordion. Timer countdown component, share link copy-to-clipboard with feedback, status badges. Gaps: page title is "TanStack Start Starter" (leftover template), console.log statements in production (3+ locations), dead code at poll/$pollId/index.tsx:120, no confirmation dialogs for destructive actions, react-hook-form installed but unused, TanStack devtools in production bundle, no mobile hamburger menu, GET used for logout action.
Backend Architecture & API Design5 / 15
Clean module structure with routes→controllers→services→schemas→DTOs separation. Zod validation for all inputs (3 DTO classes with BaseDto pattern, poll.dto.ts). Centralized ApiError class with factory methods, error handling middleware. JWT with separate access/refresh secrets. Redis for session management and poll caching. Mongoose with proper schema definitions and embedded subdocuments. Cookie-parser for refresh tokens. CORS configured with credentials. Gaps: no Helmet, no rate limiting, global verifyJWT middleware applied before all routes including public ones, console.log in validate middleware and error handler, no Mongoose compound unique index on Response for race prevention, mutate-then-save pattern without transactions, dead code (generateResetToken), hardcoded Redis port 11430 instead of env var.
Real-Time Updates Using WebSockets4 / 10
Socket.IO integrated with HTTP server (socket.ts, 60 lines). Room-based pattern where users join by userId (line 44). Server emits 2 events after response: server:poll:response:result (full poll data) and server:poll:response:responders (responder list). Auth middleware supports JWT tokens or guestIds. Client SocketProvider with autoConnect:false, connect called manually after response submission. Results page listens for real-time updates (results.tsx:24-36). Gaps: only 2 server events with narrow scope, only results page listens (no live updates on response page or poll detail), no typed Socket.IO events (uses `any`), no reconnection handling or room re-join logic, no debouncing, force-auth middleware blocks unauthenticated sockets, socket.off() call on response page is meaningless (emits not listens).
Code Quality & Project Structure4 / 10
Clean monorepo with client/server separation, TypeScript throughout, consistent module pattern (routes/controllers/services/schema/DTO), CSS design tokens for consistency. Proper React patterns: useEffect cleanup, useMemo optimization, controlled components. ApiError class hierarchy and ApiResponse utility. Gaps: console.log debug statements in 3+ production files (validate.middleware.ts:7, errorHandle.middleware.ts:11, frontend pages), dead/broken code at poll/$pollId/index.tsx:120, unused dependencies (react-hook-form, recharts for analytics, redux-persist), leftover template title "TanStack Start Starter", no automated tests, generateResetToken dead code, TanStack devtools in production, GET method for logout, Redux usage is redundant with auth context.
107
P
MiraiVote
Prakash Jangid · @prakashjangid7357
40
MiraiVote is a full-stack polling platform (React/Vite + Express/MongoDB + Socket.IO) with comprehensive auth flows, dynamic poll creation, transaction-based response submission, Recharts analytics, and real-time updates. The architecture follows clean MVC patterns with Zod validation and service-layer separation. However, the submission is significantly undermined by a critical password authentication bypass bug (missing await on bcrypt.compare), missing required features (mandatory/optional questions, publish results), zero tests, no database indexes, inconsistent error handling, and dead code. The frontend UI looks polished (dark theme, Tailwind, responsive) but has dead interactive elements and inconsistent UX patterns. Deployment is verified working for the Vercel frontend and partially for the Render backend (health endpoint works, root route hangs).
Authentication & Access Control4 / 10
Extensive auth features (register/login/logout, email verification, forgot/reset password, JWT access+refresh tokens with httpOnly cookies, SHA-256 token hashing, bcryptjs 12 rounds, auth-optional middleware, anonymous/authenticated poll enforcement). BUT user.service.js:59 has a critical bug — `bcrypt.compare(password, user.password)` is missing `await`, making password comparison always truthy (any password passes authentication). Additionally, auth.middleware.js:9 crashes with TypeError on missing Authorization header (no optional chaining guard), no rate limiting on auth endpoints, and no Helmet middleware.
Poll Creation & Question Management5 / 15
CreatePoll.jsx (306 lines) supports dynamic question/option add/remove with proper Zod validation on the backend (poll.schema.js validates title, mode enum, expireAt future, min 1 question, min 2 options per question). Server-side Poll.create + Question.insertMany + Option.insertMany sequence works. However, the REQUIRED feature "mark questions as mandatory or optional" is completely absent — Question model has no isRequired/mandatory field. No poll editing after creation, no description field in creation form, uses alert() instead of toast for errors.
Response Collection Flow6 / 15
SubmitPoll service (poll.service.js:276-462) uses MongoDB transactions with solid validation: expiry check, auth/anonymous mode enforcement, duplicate submission prevention (both modes via Participant.findOne), duplicate question detection (Set size check), question ownership validation, and option-to-question ownership validation. Frontend PublicPollPage.jsx has progress bar, disabled states for submitted/expired, anonymous ID via crypto.randomUUID()+localStorage. Gaps: no server-side enforcement that ALL poll questions are answered (only validates submitted ones), no mandatory question enforcement exists at all, no CSRF protection on submission.
Analytics & Feedback Dashboard5 / 15
Analytics.jsx (318 lines) renders Recharts AreaChart, BarChart, PieChart, 4 stat cards (totalPolls/totalVotes/totalParticipants/totalQuestions), top poll highlight card, and poll filter dropdown. Backend fetchAnalytics computes aggregate stats across polls. PollDetail.jsx shows per-question option vote bars with real-time Socket.IO updates. However, the core requirement "publish final results — anyone visiting the poll link should see final outcome" is MISSING: no publish/isPublished mechanism exists, results only shown on PublicPollPage when expired (not via explicit publish action). No CSV/export, no per-question analytics breakdown on the Analytics page, no individual response viewing.
Frontend Experience5 / 10
Clean dark theme with consistent Tailwind CSS v4 styling across 13 routes. Responsive layout, skeleton loading states (Dashboard with PollCardSkeleton), progress bar on public poll page, copy-to-clipboard toggle. But error handling is inconsistent: Login/Register silently swallow errors (console.log only), CreatePoll uses alert(), PublicPollPage uses toast.error. Home.jsx is a 449-line monolith with inline styles and raw CSS instead of extracted components. PollDetail has ornamental dead buttons (Close, back). No Framer Motion usage despite being listed as a dependency in package.json and claimed in README.
Backend Architecture & API Design5 / 15
Clean layered MVC architecture (routes→controllers→services→models) with Zod validation middleware, ApiError/ApiResponse utility classes, MongoDB transactions, well-separated JWT utilities, and Nodemailer email service with rollback on send failure. Critical bugs: password auth bypass (missing await, user.service.js:59), auth middleware crashes on missing header (auth.middleware.js:9), root endpoint hangs (app.js:19), pollResult references wrong field `question.title` vs `question.text` (poll.service.js:526), ApiError.forbidden uses 412 instead of 403, ApiResponse.noContent sends body with 204. No database indexes, no Helmet, no rate limiting, N+1 query in fetchPoll, route collision between /:id and /:token/result.
Real-Time Updates Using WebSockets5 / 10
Socket.IO server integrated with Express HTTP server (server.js:14-36) with room-based pattern (poll:${pollId}). Client emits join_poll on connect+mount (PollDetail.jsx:72-89), server broadcasts poll_updated with {people, votes, questionVotes} after submission (poll.service.js:445-449), and client merges data immutably into state (PollDetail.jsx:91-134) with proper cleanup on unmount. Gaps: only PollDetail page uses sockets (Analytics and PublicPollPage do not), socket URL hardcoded as "https://pulse-board-7htj.onrender.com" (not env-configured), no authentication on socket connections, no reconnection room re-join logic, single event type, no data pushed (pure invalidation signal), no debouncing.
Code Quality & Project Structure5 / 10
Clean frontend/backend separation with consistent service layer, Zod validation, ApiError class hierarchy, and ESLint configured. Both packages have detailed READMEs. Issues: no TypeScript, zero tests, no root README, dead code (pollResult() in poll.service.js references undeclared `token` variable), debug console.log in 3 production locations, 9+ unused lucide-react imports across 5+ files, hardcoded socket URL, Home.jsx monolithic at 449 lines, inconsistent error handling patterns, no .env.example files, and no database indexes. The architecture scaffold is sound but quality execution is undermined by accumulated code health issues.
108
Opinio
Shubh Ujala · @shubh_ujala
40
Opinio is a functional full-stack polling app built with React/Vite + Express/MongoDB + Socket.IO. The implementation covers all core requirements: user authentication, multi-question poll creation with mandatory/optional toggles, anonymous/authenticated response modes, expiry handling, analytics with Recharts charts, result publishing, and real-time updates via Socket.IO. The frontend has 8 routes with a custom CSS design system supporting dark/light themes. However, the project has significant gaps across all dimensions: no duplicate vote prevention, no poll editing/deletion, no validation library, no security middleware (Helmet/rate limiting), JWT tokens in localStorage, no TypeScript, duplicated code, dead CSS/assets, and an inconsistent error response format. The backend API is deployed but unreachable (Render free tier cold-start failure). Total score: 40/80 — a middle-of-the-pack submission with working features but limited depth and production-readiness.
Authentication & Access Control5 / 10
Working registration (name/email/password, bcrypt 10 rounds, pre-save hook at user.js:24-29), login with JWT token (7-day expiry, token.js:5), auth middleware (auth.js:3-21) that verifies Bearer token and attaches req.user, and GET /me endpoint. Frontend has Zustand auth store (authStore.js) with initAuth/login/logout, Axios interceptor (axios.js:7-10) for token attachment, and authGuard (authGuard.js) used on 3 protected routes. Anonymous vs authenticated poll mode enforced server-side (responses.route.js:37-48). Critical gaps: no Helmet, no rate limiting, token in localStorage (XSS risk), no refresh token or rotation, no email verification/password reset, no CSRF protection, CORS origin:true with credentials (index.js:10-13).
Poll Creation & Question Management5 / 15
Poll creation form (create.jsx, 296 lines) supports dynamic questions with add/remove (lines 33-42), per-question isRequired toggle (line 228), dynamic options with add/remove (lines 53-67, min 2 enforced), and poll metadata (title, description, anonymous toggle, datetime-local expiry). Client-side validation (validate(), lines 78-93) checks all fields before submission. Server-side validation (polls.route.js:13-45) enforces title required, expiry future, question text non-empty, min 2 options per question. Questions stored as embedded sub-documents in Poll schema (poll.js:21-51). Gaps: no poll editing endpoint (no PATCH/PUT), no poll deletion, no slug/permalink generation, no form library (individual useState hooks), no draft status, no server-side option text emptiness validation.
Response Collection Flow5 / 15
Poll response page (poll/$id.jsx) handles 3 states: PollForm (active), ClosedMessage (expired), ResultsView (published). Radio buttons for single-option selection (lines 139-163), client-side required-question check (lines 62-68). Server-side (responses.route.js:9-90): validates poll exists, not expired, not closed/not published, enforces anonymous vs authenticated mode via token check (lines 37-48), validates required questions (lines 53-65). Socket.io emit on submission with totalResponses (lines 76-80). Thank-you screen after submission (lines 92-101). Critical gaps: no duplicate vote prevention (no unique compound index on {pollId,userId}, no localStorage tracking), no validation that submitted optionIds/questionIds belong to the poll, console.log of answers in production (line 68), error redirect for auth failures instead of inline prompt.
Analytics & Feedback Dashboard6 / 15
Analytics page (analytics/$id.jsx, 329 lines) with Recharts BarChart integration (lines 225-243), 3 stat cards (total responses with live indicator, total questions, expiry), per-question breakdown showing option counts and percentages (lines 246-261), empty state for 0 responses (line 221). MongoDB aggregation pipeline for vote counting (polls.route.js:178-199). Publish functionality via POST /:id/publish (lines 118-156) with confirmation dialog. Public results page with per-option progress bars and vote counts (poll/$id.jsx:204-281). Dashboard lists polls with response counts, status badges, and copy-link (dashboard.jsx). Gaps: no CSV/data export, no individual response viewing, no trend/time-series data, no authenticated vs anonymous breakdown, no peak activity metrics, N+1 issue in /mine (sequential countDocuments per poll, lines 74-78), duplicated aggregation pipeline between analytics and results endpoints.
Frontend Experience5 / 10
8 routes via TanStack Router (routeTree.gen.ts), custom CSS design system with CSS custom properties for light/dark theming (index.css, ~160 lines), dark/light toggle persisted in localStorage (__root.jsx:9-20). Landing page with simulated live demo, feature cards, step cards, and hover animations (index.jsx, 428 lines). Loading states on all pages, dedicated error page with status-specific styling for 401/403/404/410/500 (error.jsx), empty state on dashboard. Form UX includes sectioned layout with add/remove buttons. Gaps: no CSS framework or component library (all inline styles), no responsive media queries on most pages, no form library (manual useState for deeply nested arrays), index.html title is "frontend" instead of "Opinio", App.css contains ~185 lines of unused Vite boilerplate, no loading skeletons, error display can render [object Object] due to inconsistent response parsing.
Backend Architecture & API Design5 / 15
Express backend with 11 REST endpoints across 3 route files, 3 Mongoose models (User/Poll/Response) with embedded sub-schemas for questions/options/answers. Auth middleware for JWT verification, bcrypt password hashing, aggregation pipeline for vote counting. Functional but has significant gaps: no validation library (all manual ad-hoc checks), no service layer (all business logic in route handlers), inconsistent error envelope (error vs err keys across files), HTTP status code misuse (201 for GET /mine and GET /:id at polls.route.js:81,108, 400 for not-found at line 96), no Helmet/Rate Limiting, CORS origin:true with credentials, raw err.message leaked to clients in all catch blocks, no index on Poll.creatorId, no centralized error handler, no pagination, no edit/delete endpoints, polling for response counts (/mine does sequential N queries at lines 74-78).
Real-Time Updates Using WebSockets5 / 10
Socket.IO server attached to HTTP server (server.js:14-20) with room-based pattern (socket.join poll-${pollId} on join-poll, line 26). Server emits poll:update with totalResponses on response submission (responses.route.js:76-80) and poll:published on publish (polls.route.js:142-143). Client useSocket hook (useSocket.js, 35 lines) joins room, listens for both events, cleans up listeners in useEffect return (lines 29-32). Analytics page triggers full HTTP refetch on both events (analytics/$id.jsx:50-59). Gaps: only covers analytics page (not poll response page), pure invalidation signal — no actual analytics data pushed over socket, no reconnect room re-join logic, no debouncing, no socket authentication, callbacks not in useEffect dependency array (latent stale-closure risk), no leave-poll event on cleanup, no events for poll deletion (no delete endpoint exists).
Code Quality & Project Structure4 / 10
Clean frontend/backend separation, file-based routing via TanStack Router, Zustand for auth state, sensible module organization (models/routes/middleware/utils/hooks/store/api). ESLint configured for frontend. README has setup instructions but references wrong directory name ("poll-Application" vs "Opinio"). Issues: no TypeScript in any hand-written code (only auto-generated routeTree.gen.ts), no automated tests, commented-out code in response.js model (~30 lines), dead CSS in App.css (~185 lines of Vite boilerplate), unused asset files (react.svg, vite.svg, hero.png), duplicated getPollStatus function (dashboard.jsx:35-38 and analytics/$id.jsx:83-87), duplicated aggregation pipeline in two route handlers, inconsistent error envelope keys, spelling error in error message ("Tile" for "Title" at polls.route.js:17), no .env.example files, inline styles everywhere with no reusable component extraction.
109
A
Poll Adda
Atul Maurya · @atulmaurya773
40
Poll Adda is a functional polling platform with a polished glass-morphism UI and solid core features including JWT authentication, dynamic poll creation, response collection, analytics with Recharts charts, and basic Socket.IO real-time updates. The project demonstrates good MVC architecture with Zod validation and TypeScript throughout. However, it has critical issues: committed .env with real secrets, fake email verification (hardcoded "1111" code), no root README, no tests, several backend bugs (missing return in validate middleware, bitwise OR instead of logical OR), no security middleware (helmet/rate-limiting), no duplicate response prevention, disabled client-side form validation, and minimal Socket.IO implementation (single event, no room scoping). Total score: 40/100.
Authentication & Access Control5 / 10
Functional JWT auth with registration, login, logout, refresh tokens, and auth/optional-auth middleware. Password hashing via bcrypt (10 rounds). Client-side AuthContext with session recovery and axios 401 interceptor. Protected dashboard routes redirect unauthenticated users. Anonymous vs authenticated poll modes enforced server-side (poll-response.services.ts:19-21). Critical gaps: email verification is fake (hardcoded "1111" code at auth.service.ts:64, no emails sent), inconsistent refresh token hashing (plaintext on login at line 136 vs hashed on verify at line 86), tokens stored in localStorage, no rate limiting/helmet/CSRF, no password reset flow, PII logged via console.log(email) at auth.service.ts:27.
Poll Creation & Question Management6 / 15
CreatePoll.tsx (218 lines) uses react-hook-form with useFieldArray for dynamic questions/options, required question checkbox, response mode selector (anonymous/authenticated), and expiry datetime picker. Server-side CreatePollDto validates title, questions, and options via Zod. Publish and toggle-results endpoints exist. Key gap: zodResolver is COMMENTED OUT at CreatePoll.tsx:33, disabling all client-side Zod validation. No poll editing endpoint exists (no PATCH for updating questions/options/title after creation). No slug/permalink or QR code for sharing. Draft/active/expired status inferred from isPublished flag only.
Response Collection Flow5 / 15
PublicPoll.tsx (227 lines) handles loading/error/success states, client-side required question validation with modal alert, auth-gated poll detection, expiry display with disabled form, and radio button selection. Server validates: poll existence, isPublished status, resultsPublished status, auth mode enforcement (poll-response.services.ts:12-21). SubmitResponseDto validates answers array. Critical gaps: NO duplicate response prevention (no unique index on {userId,pollId}, no client-side check), NO server-side required-question validation, NO validation that submitted option IDs belong to the poll, PollResponse model uses Mixed for answers with no schema validation (poll-response.model.ts), no server-side expiry checking.
Analytics & Feedback Dashboard6 / 15
Analytics.tsx (196 lines) renders per-question Recharts BarChart with colored bars, percentage progress bars, and total responses summary card. Toggle results visibility button with publish/unpublish flow. Public Results.tsx shows horizontal bar visualization. DashboardHome displays stats cards (total/published polls, total responses). Backend analytics service (analytics.services.ts, 81 lines) computes per-question option counts and percentages, enforces owner-or-published access control. Gaps: no CSV/export, no trend/time-series data, no peak activity metrics, no individual response viewing, analytics computed via brute-force O(n*m) iteration rather than aggregation pipeline, search inputs on AnalyticsList and PublishedPolls are non-functional (no onChange handler).
Frontend Experience6 / 10
Polished glass-morphism design with ShaderBackground animated background, scroll-reveal animations (App.tsx:38-60), and consistent dark theme. 5-section landing page (Hero, Features, Pricing, Footer, Navbar). Dashboard layout with collapsible sidebar (6 navigation items), mobile overlay, user info header, and auth-gated redirect (DashboardLayout.tsx:26). All pages handle loading (spinners) and error states. Toast notifications via sonner. Shared ConfirmModal component. Gaps: Settings page (Settings.tsx) exists but has NO route defined, search inputs are visual-only (no onChange), no 404 route, Zod resolver disabled on create form, no code-splitting, hardcoded production URLs in 6 locations.
Backend Architecture & API Design5 / 15
Clean MVC separation with controller→service→model→routes per module. 16 REST endpoints, 7 Zod DTOs, 3 Mongoose models, consistent ApiResponse/ApiError helpers. Dashboard stats use MongoDB aggregation pipeline (poll.services.ts:119-140). However: BUG in validate.middleware.ts — error created at line 6-7 is not returned (missing return), causing downstream crashes. BUG: bitwise OR `|` used instead of logical OR `||` for port default (index.ts:7). Inconsistent error response format (double-wrapped for ValidationError at error.middleware.ts:6-9). No helmet, no rate limiting, CORS set to all origins. GET /auth/refresh-token reads req.body (violates HTTP semantics). No indexes on createdBy or pollId. Duplicate hash-utils files. Multiple console.log in production. Nested try/catch re-wrap antipattern across all services.
Real-Time Updates Using WebSockets4 / 10
Socket.IO server initialized on HTTP server (socket.ts, 31 lines) with CORS *. Single `response-update` event emitted via io.emit() on response submission (poll-response.controller.ts:18). 5 client pages listen for the event with pollId filtering and trigger HTTP refetch. Proper socket disconnect on unmount in all 5 pages. Significant gaps: NO room-based scoping (all events broadcast to ALL connected clients regardless of which poll changed), only 1 event type (no events for poll creation/publish/delete/expiry), no data pushed over socket (pure invalidation signal), no authentication on socket connections, no reconnection room re-join logic, creating 5 separate socket connections per user session, no debouncing.
Code Quality & Project Structure3 / 10
NO root README.md — client/README.md is unmodified Vite boilerplate describing the template, not the application. server/.env COMMITTED with real MongoDB credentials and JWT secrets (severe security violation). No tests anywhere. Server TypeScript source files NOT committed — only compiled .js in dist/ exists, meaning the source of truth is missing. Multiple typos: "verfiyEmail" (auth.controller.ts:12), "Toke refresh" (auth.controller.ts:47), "plainPassowrd" (hash-password-utils.ts:6), "starteing" (index.ts:16). Duplicate files: hash-password-utils.ts and hash-utils.ts (identical content), two submit-response.dto.ts files. Dead files: poll.middleware.ts, 3 empty .types.ts files. No .env.example. ESLint configured on client only. Positives: consistent module structure, TypeScript throughout, clean folder organization with clear separation of concerns.
110
PollSphere
Armaan Garg · @armaangarg755_65b4184f
40
PollSphere is a functional full-stack polling app with JWT auth, poll creation with multi-question support, response collection, analytics with Recharts visualizations, and Socket.IO real-time updates. Deployed across Netlify (frontend), Vercel (frontend mirror), and Render (backend). The dark glassmorphism UI is visually polished but leans heavily on decorative filler. Key strengths: working end-to-end flow, Socket.IO room-based participant tracking, Recharts analytics with per-question pie/bar charts and Top Voted badges, optional auth middleware for anonymous/authenticated hybrid responses. Critical weaknesses: zero server-side input validation (mass-assignment vulnerability via `...req.body`), no duplicate response prevention, no Helmet/rate limiting, tokens in localStorage, no indexes on queried fields, hardcoded engagement metrics, and significant code duplication throughout. Total score: 40/100.
Authentication & Access Control5 / 10
Registration with bcrypt (10 rounds) and login with JWT (7d expiry) via authController.js:5-95. Mandatory authMiddleware (middleware/authMiddleware.js:3-27) and optionalAuthMiddleware (optionalAuthMiddleware.js:3-25) enable authenticated vs anonymous hybrid responses. Client-side ProtectedRoute (routes/ProtectedRoute.jsx:3-11) checks localStorage for token. Significant gaps: tokens stored in localStorage (XSS risk), auth middleware never verifies user still exists in DB (line 19 sets req.user=decoded directly), no email verification, no password reset, no refresh token mechanism, no rate limiting on auth endpoints, no CSRF protection, no Helmet.
Poll Creation & Question Management5 / 15
CreatePoll.jsx (402 lines) supports title, description, expiresAt datetime picker, allowAnonymous checkbox, dynamic questions with text/options/correctAnswer. Option add button exists (line 63-70); question add button exists (line 47-62). But: no mandatory/optional (required) toggle in the UI form despite schema field existing (Poll.js:15-18, default=false), no option or question remove buttons, no client-side form validation (empty title/questions accepted), and critically no server-side input validation — createPoll (pollController.js:6-9) uses `...req.body` spread creating a mass-assignment vulnerability. No poll editing endpoint exists.
Response Collection Flow5 / 15
PollDetails.jsx (421 lines) renders radio-option UI with client-side required-question validation (lines 89-101), score calculation (lines 108-114), and submits via submitResponse(). Server-side: poll existence check (responseController.js:11-14) and expiry check (lines 17-21), optional auth for user attribution (line 25). Socket.IO emit on submission (line 28). Major gaps: no duplicate response prevention (no unique index on Response, no localStorage tracking), no server-side validation that submitted answers reference valid questions/options, no server-side mandatory question enforcement, no per-question single-option enforcement server-side, allowAnonymous flag defined but never checked server-side or client-side during submission.
Analytics & Feedback Dashboard6 / 15
PollResults.jsx (512 lines) renders Recharts PieChart per question (lines 274-292), BarChart for first question distribution (lines 354-377), per-option vote counts with animated gradient percentage bars and "Top Voted" badges (lines 404-500), stat cards for total responses/questions/status, and Socket.IO real-time refresh via pollUpdated event (line 47). Server-side getPollResults (pollController.js:51-99) computes per-question option counts in-memory and gates behind isPublished flag (line 61). Dashboard (Dashboard.jsx:280-345) shows total polls and published counts. Gaps: engagement rate is hardcoded "99%" (PollResults.jsx:228), no anonymous/authenticated breakdown, brute-force O(n*m) computation (not MongoDB aggregation), no CSV/export, no individual response viewing, bar chart only covers first question, no response counts on dashboard poll cards.
Frontend Experience5 / 10
7 React routes (App.jsx:14-36) with protected routes for /dashboard and /create. Consistent dark glassmorphism theme with gradient aesthetics, decorative blur orbs, and animated status indicators across pages. Loading states (PollDetails line 62-77, PollResults lines 56-71), error/status messages displayed as inline banners. QR code with show/hide toggle (PollDetails.jsx:237-297), copy-to-clipboard with transient message (lines 56-60), Socket.IO live participant counter (lines 40-54). Gaps: no form library (individual useState handlers everywhere), no confirmation dialogs for delete, no toast notification system (only inline messages), no skeleton loaders, Home.jsx is 500+ lines of hardcoded marketing filler with fake stats, Dashboard sidebar hidden entirely on mobile with no toggle, no empty state UI when dashboard has no polls.
Backend Architecture & API Design4 / 15
MVC pattern with 3 models, 3 controllers, 3 route files across ~625 server lines. RESTful API with 10 endpoints using appropriate HTTP status codes. Route ordering correct (/:id/results before /:id in pollRoutes.js:20-21). JWT auth middleware (mandatory + optional patterns). Cascade delete of responses on poll deletion (pollController.js:114-116). Critical issues: zero input validation anywhere (no Joi/Zod/express-validator), `...req.body` spread in createPoll (mass-assignment at pollController.js:6-9), no Helmet security headers, no rate limiting on any endpoint, no indexes on Poll.createdBy or Response.poll (full collection scans), no service layer (all logic in controllers), error.message leaked to clients in 9 identical catch blocks, user not re-verified in DB during auth middleware, no request body size limits.
Real-Time Updates Using WebSockets6 / 10
Socket.IO server integrated with Express HTTP server (server.js:31-75), room-based pattern with joinPoll/leavePoll events and disconnecting handler for automatic cleanup. In-memory participant counting with participantCount emission to room members (lines 44-74). pollUpdated-{pollId} global emit on response submission (responseController.js:28). Client: PollDetails joins poll room and displays live participant count (PollDetails.jsx:40-54), PollResults listens for pollUpdated-{id} and refetches via HTTP (PollResults.jsx:40-54). Cleanup on unmount with leavePoll, socket.off, and socket.disconnect. Gaps: pollUpdated uses io.emit() globally instead of io.to(pollId).emit() (broadcasts to ALL clients), no authentication on socket connections, pollUpdated is signal-only (no data pushed, client refetches HTTP), no reconnection/room-rejoin handling, no debouncing, only 2 event types, participant counting purely in-memory.
Code Quality & Project Structure4 / 10
Clean MVC separation with consistent naming, .gitignore correctly excludes .env, ESLint configured, ES modules used throughout. But: no TypeScript, no automated tests, no .env.example file. Significant code duplication: token generation duplicated in authController.js:25-33 and 71-79, user serialization duplicated at lines 38-42 and 84-88, "Poll not found" check duplicated 5 times across controllers, 9 identical try/catch/500 blocks. Controller imports io from server.js (responseController.js:3) — tight coupling. No service layer. Home.jsx contains 500+ lines of hardcoded static marketing content. Dashboard "Platform Status" section and "Engagement Rate 99%" are purely decorative. Flat pages/ directory with no sub-folder structure or component extraction. README lacks installation commands.
111
A
Pulse Board
Alok Yadav · @alokcodes
40
Pulse Board is a functional MERN polling platform with a polished frontend design system. The application covers the core polling lifecycle: registration with OTP verification (SendGrid+SMTP dual delivery), poll creation with dynamic questions/options, response submission with server-side validation, analytics with per-question breakdowns and voter lists, result publishing, and basic Socket.IO real-time updates. The frontend shows significant UI craftsmanship with a custom CSS token system, dark mode, skeleton loading, animated components, and comprehensive state coverage across all pages. However, the submission is held back by security concerns (JWT secret hardcoded as "secret" fallback in 4 locations, tokens in localStorage, no rate limiting, no helmet), the absence of TypeScript, zero automated tests, no input validation library, excessive inline styles, code duplication (GoogleIcon SVG, OAuth handler), and a Socket.IO implementation limited to a single event type on one page. The backend Render deployment is down, but the Vercel frontend is live.
Authentication & Access Control5 / 10
Auth system has registration with OTP email verification (bcrypt 10 rounds, SendGrid API+SMTP dual delivery), login, Google OAuth (both idToken and accessToken flows), forgot/reset password, and JWT middleware (auth.js:3-14). AuthContext with localStorage persistence, 401 interceptor, and authReady pattern (AuthContext.jsx:10-62). Anonymous vs authenticated poll modes enforced server-side (pollRoutes.js:104-125). Critical gaps: JWT secret hardcoded as "secret" fallback in 4 call sites across 3 files (auth.js:7, authRoutes.js:13, pollRoutes.js:69,112), no rate limiting on OTP endpoints, tokens in localStorage, no Helmet/CSRF, no email format validation at schema level.
Poll Creation & Question Management5 / 15
CreatePoll.jsx (536 lines) has dynamic questions/options with add/remove, mandatory/optional toggle per question, anonymous mode toggle with contextual helper text, and expiry datetime picker. Sectioned form layout (Details/Configuration/Questions) with helper components. Server validates title + expiry + min 1 question (pollRoutes.js:27-30). Significant gaps: no poll editing endpoint exists (PATCH only for publishing, not modifying questions/options), no server-side validation of individual question/option fields (text content, min 2 options), no slug/QR sharing, no form library, no draft status.
Response Collection Flow6 / 15
PollView.jsx (412 lines) renders response form with radio buttons, progress bar for multi-question polls, and status banners (expired/published/auth-required). Server validates: poll existence (pollRoutes.js:94-96), expiry (98-99), auth enforcement for non-anonymous polls (104-125), duplicate prevention via findOne check (127-133), and mandatory question enforcement (137-148). Socket.IO emits poll_updated on submission (158-169). Post-submission success screen with redirect-to-results if published. Gaps: no compound unique index on {pollId, userId} for DB-level dedup, no validation that submitted optionIds actually belong to the poll, no anonymous duplicate prevention, empty catch block at line 73 swallows errors.
Analytics & Feedback Dashboard5 / 15
PollResults.jsx (584 lines) has per-question vote counts with percentage display, winning option detection with accent-colored bar and checkmark, IntersectionObserver-driven animated bars with staggered delays, voter avatar stacks (3 avatars) with "+N more" expandable popover, full voter list dropdown, "Publish Results" button (creator-only, disabled until expired), "Live Insights" badge with pulsing dot, and creator fallback (if public results return 403, tries /analytics endpoint). Backend calculateAnalytics (pollRoutes.js:241-276) computes per-question option counts with voter details. Gaps: no CSV/export, no stat summary cards, no trend data or time-series, brute-force O(n*m) computation in application code rather than MongoDB aggregation pipeline, no per-poll response count on dashboard cards.
Frontend Experience6 / 10
8 routes with comprehensive states: loading (skeleton cards on Dashboard, PollView, PollResults), error (toast notifications, errorMsg state, 401 redirects), empty (dashed-border CTA on Dashboard, "Not found" on PollView/PollResults), and auth gates (authReady spinner in ProtectedRoute). Dark/light theme with system-preference detection and localStorage persistence (ThemeContext.jsx:34 lines). CSS design token system with 24 properties per theme (index.css:6-58). Scroll-aware glass-morphism navbar, OTPInput with keyboard nav and paste support, password strength indicator, animated counters on homepage, logout confirmation modal with focus trap and Escape handling, 5 button variants, responsive grid layouts, keyboard focus-visible accessibility. Drawbacks: heavy inline style overuse, no form library on poll creation form.
Backend Architecture & API Design5 / 15
Clean but minimal MVC architecture (index.js, routes/, models/, middleware/, socket.js). 14 RESTful endpoints. Mongoose schemas for User (with conditional password required), Poll (embedded questions/options), Response (with questionId+optionId tracking). CORS configured with allowlist origins. Dual-path email delivery (SendGrid API + SMTP fallback with Promise.race timeouts). Socket.IO singleton pattern (socket.js:6 lines). Core gaps: no input validation library (manual ad-hoc checks only), no Helmet middleware, no rate limiting on any endpoint, no request body size limits, no centralized error handler, no service layer, raw err.message leaked to clients in 6 catch blocks, JWT_SECRET hardcoded in 4 locations, no indexes on Poll.creator or Response.pollId, no DB-level duplicate prevention index, no email format validation in User schema.
Real-Time Updates Using WebSockets4 / 10
Socket.IO server attached to HTTP server with CORS (index.js:16-24), singleton IO pattern via socket.js setIO/getIO. Server handles join_poll room subscription (index.js:48-50) and emits poll_updated on response submission with totalResponses+analytics payload (pollRoutes.js:158-168). Client connects in PollResults.jsx:100-103, joins room, listens for poll_updated to trigger HTTP refetch, disconnects on unmount. Gaps: pure invalidation signal (no data pushed, client refetches HTTP), only covers PollResults page (not public voting page), no reconnection handling or room re-join logic, no room leave events, no debouncing, no socket authentication, only one event type, no broadcast on publish/delete.
Code Quality & Project Structure4 / 10
Clean monorepo with backend/frontend separation, CSS design system with tokens, ESLint configured, comprehensive README with setup instructions. Proper React patterns: useEffect cleanup on all effects, useRef for mutable refs, authReady flag. Controlled OTPInput component with keyboard navigation. Gaps: no TypeScript throughout, zero automated tests (backend test script explicitly returns "Error: no test specified"), no service layer separation, JWT secret hardcoded in 4 locations across 3 files, GoogleIcon SVG duplicated identically in Login.jsx:144-151 and Register.jsx:204-211, Bearer token extraction duplicated in 3 locations (auth.js:4-5, pollRoutes.js:63-67, 106-110), no .env.example file, .env.production committed to repo, test-register.js broken (CommonJS require in ESM project), extensive inline styles bypassing CSS class system.
112
PulseBoard
Subhajit Bag · @subhajitbag
40
PulseBoard is a functional full-stack polling platform with 7 frontend routes, 7 REST API endpoints, JWT auth with httpOnly cookies, dynamic poll creation, response collection with validation, per-question analytics with bar charts, Socket.io real-time updates, and results publishing. The frontend has a polished dark theme with good state coverage. However, the project is held back by missing security hardening (no Helmet, rate limiting, CSRF), no TypeScript anywhere, no automated tests, no form library, brute-force analytics (no MongoDB aggregation), basic WebSocket implementation (only 1 event), and template leftovers. Overall: a competent hackathon submission that works end-to-end but lacks production-level polish and depth. Total: 40/100.
Authentication & Access Control5 / 10
Registration/login/logout with bcryptjs (10 rounds) and JWT in httpOnly cookies (authController.js:1-108). Auth middleware protect/optionalAuth (authMiddleware.js:1-36). ProtectedRoute wrapper (App.jsx:14-19). Anonymous vs authenticated poll modes enforced server-side (getPoll:61-66, respondToPoll:88-90). Frontend AuthContext with login/register/logout/checkUser (AuthContext.jsx:1-47). Gaps: no Helmet, no CSRF protection, no rate limiting on auth endpoints, no email verification or password reset, single 30-day JWT without refresh token rotation.
Poll Creation & Question Management5 / 15
CreatePoll.jsx (234 lines) supports dynamic questions with add/remove, dynamic options per question (min 2), mandatory/optional toggle per question, anonymous/authenticated mode radio, expiry datetime-local picker. Client-side validation strips empty options and ensures 1+ question with 2+ options. Server-side validation checks title, questions array, and per-question text+options (pollController.js:4-25). Gaps: no poll editing endpoint exists (PATCH only toggles isPublished), no slug/permalink generation, no form library (individual useState hooks), no draft/active status concept.
Response Collection Flow6 / 15
Thorough response collection with multiple validation layers: poll existence check (pollController.js:79-82), expiry enforcement (84-86), auth mode gating (88-90), duplicate prevention for authenticated users via findOne (93-98), answers array presence (102-104), mandatory question validation iterating poll.questions (107-114), and selected option validation against actual poll options (117-122). Client-side mandatory validation with inline errors (PollView.jsx:48-62). Frontend handles loading/error/expired/submitted/success states. Gaps: no duplicate prevention for anonymous polls, no DB-level unique compound index, no rate limiting, no voter-key system for anonymous tracking.
Analytics & Feedback Dashboard5 / 15
PollAnalytics.jsx (155 lines) shows stat cards (responses, questions, status), per-question option breakdowns with count + percentage + leading indicator, CSS animated progress bars. Dashboard.jsx shows total/active/responses/avg stats with filter tabs (all/active/expired). PollResults.jsx (136 lines) shows published results with "Winner" badge. calculateAnalytics function (pollController.js:143-161) computes optionCounts object. Publish endpoint with creator-only check (pollController.js:186-204). Gaps: brute-force O(n*m) iteration instead of MongoDB aggregation pipeline, no CSV/export, no time-series data, no individual response viewing, no authenticated vs anonymous response breakdown, no peak activity metrics.
Frontend Experience7 / 10
7 routes across 6 page components with consistent dark "sunset" theme. Tailwind CSS v4 design system with CSS variables, glass-panel, btn-primary gradient, status pills (index.css:238 lines). Good state coverage: loading spinners on all pages, error banners with alert icons, empty state with CTA (Dashboard.jsx:90-106), success confirmation (PollView.jsx:96-112), auth redirect with ProtectedRoute. Responsive navbar with mobile hamburger menu (Navbar.jsx:67-74). Animations: fadeIn, stagger-children, growWidth bars, shimmer gradient. Copy-to-clipboard with visual feedback. Gaps: no TypeScript, frontend package name is "temp_app", no form library, no skeleton loading states, frontend README is unmodified Vite starter boilerplate.
Backend Architecture & API Design4 / 15
Clean MVC separation (models/controllers/routes/middlewares/config/) with 7 REST endpoints and proper route files (authRoutes.js, pollRoutes.js). Mongoose schemas for User/Poll/Response with embedded questions and answers. CORS configured with allowlist, cookie-parser for JWT cookies. Critical gaps: no Helmet security headers, no rate limiting on any endpoint, no request body size limits, no express-mongo-sanitize, no centralized error handler, raw error.message leaked to clients in 5 catch blocks (pollController.js:29,47,73,139,182), no Zod/Joi validation library (all manual ad-hoc checks), no Database indexes defined on Response model, no .env.example file, no service layer separation.
Real-Time Updates Using WebSockets4 / 10
Socket.io server attached to HTTP server (server.js:15,24-30). Room-based pattern: client emits 'join-poll' to join `poll_${pollId}` room (server.js:52-54). Server emits 'new-response' with full analytics object on response submission (pollController.js:132-135). Client PollAnalytics.jsx connects via SocketContext, joins room on mount, updates analytics state on 'new-response', cleans up listener on unmount (PollAnalytics.jsx:19-25). Gaps: only 1 event type, no poll-published/deleted/expired events, no socket authentication, no room leave (socket.leave), no reconnection room re-join logic, no debouncing/batching, socket data is invalidation signal (client refetches from HTTP for initial data).
Code Quality & Project Structure4 / 10
Clean monorepo structure with backend/frontend separation. Well-written README with project structure diagram, API endpoint table, WebSocket events table, setup guide. Consistent naming conventions (camelCase files, PascalCase React components). ESLint configured on frontend, Prettier via lint script. Code is readable with moderate component decomposition. Gaps: no TypeScript in entire project, no automated tests (backend test script: "echo Error: no test specified"), no ESLint on backend, frontend package name is "temp_app" (template leftover), frontend README is unmodified Vite starter, SKILL.md + skills-lock.json from scaffolding agent, minor dead code (unused App.css), "// Trigger restart" comment in server.js.
113
S
PulseBoard: Live Polls For Feedback
Sharath P D · @sharathpd14
40
PulseBoard: Live Polls For Feedback is a functional MERN stack polling platform with solid auth (registration, email verification, JWT with refresh rotation, forgot/reset password), working vote collection with dual anonymous/authenticated modes and duplicate prevention via MongoDB sparse indexes, a dashboard with per-question analytics and Socket.IO live updates, and a polished dark-themed UI. However, the critical missing feature is mandatory/optional question handling — the Question model has no isRequired field at all. The platform also lacks a README, uses only JavaScript (no TypeScript), has console.log debug statements throughout production code, no security hardening (Helmet, rate limiting), and the question creation flow is a step-by-step wizard rather than an inline builder. Socket.IO coverage is minimal (single event type, no data pushed). Overall: working core functionality with notable gaps in requirements coverage and production readiness.
Authentication & Access Control6 / 10
Complete auth flow: registration with email verification via nodemailer/SMTP (auth.service.js:10-43), login with JWT access+refresh tokens with SHA256 rotation (auth.service.js:45-110), forgot/reset password with timed expiry (auth.service.js:123-183), authenticate+optionalAuthenticate middleware (auth.middleware.js:7-50), bcrypt 10 rounds. Frontend has Login/Register/VerifyEmail pages with react-hook-form, tokenStore in localStorage, Axios interceptor for auto-refresh with _retry flag (api.js:21-45). Gaps: tokens in localStorage (XSS-vulnerable), no Helmet/CSRF, no rate limiting, server.js:14 logs MONGODB_URI, JWT refresh defaults to 15min (same as access), ApiError.forbidden returns 412 instead of 403.
Poll Creation & Question Management4 / 15
Poll creation with react-hook-form (CreatePoll.jsx:7-119) supports title, duration (value+unit), anonymous toggle. Backend Joi DTO validation, owner-only permissions on question/option creation (question.service.js:17, option.service.js:15-17). CRITICAL GAP: mandatory/optional question toggle is entirely missing — the Question model has no isRequired field, yet PollVote.jsx:207 displays a "Required" badge referencing question?.isRequired that never exists. Questions added one-at-a-time via separate pages rather than an inline builder. No poll editing, no slug generation, no draft status. Duration calculation with multipliers (poll.service.js:9-18) works correctly.
Response Collection Flow6 / 15
PollVote.jsx (241 lines) handles loading/expired/expired+published/active+published/submitted states. Vote casting uses Promise.all for multi-question submissions. Server validates poll exists, not expired, question/option exist, option belongs to question (vote.service.js:13-29). Duplicate prevention via sparse unique compound indexes on {user,question} and {anonymousId,question} (vote.model.js:26-30). Transaction-based vote+optionCount increment (vote.service.js:41-54). Anonymous voter tracking via localStorage crypto.randomUUID(). Gaps: mandatory/optional enforcement is cosmetic-only (isRequired field doesn't exist in model), no server-side per-question option validation beyond uniqueness, anonymousId from client not verified.
Analytics & Feedback Dashboard6 / 15
Analytics endpoint (poll.service.js:78-135) computes per-question breakdowns with authenticated/anonymous splits and percentage calculations (precision to 1 decimal). Publishing flow with owner-only gate (poll.service.js:138-145). Dashboard component (Dashboard.jsx:37-295, 295 lines) with sidebar poll list, stat cards (total/auth/anon/question count), OptionBar components with animated CSS progress bars, copy-link, and "Go Live" confirmation modal. Published results viewable on PollVote page via ResultsView component. Socket.IO live updates on dashboard. Gaps: no CSV export, no individual response viewing, no trend/time-series data, no peak activity metrics, analytics uses brute-force O(n*m) filtering instead of MongoDB aggregation, no caching.
Frontend Experience5 / 10
Consistent dark theme with CSS custom properties (index.css:6-41), TanStack Router with file-based routing, react-hook-form on all forms, react-toastify for notifications. Professional landing page (Hero with stats, PollList, Footer). Navbar with auth-aware state (login/register vs user avatar dropdown with logout confirmation modal). Loading spinners, error toasts, empty states, 404 page. Live countdown timer. CSS transitions on option bars and pulse animation. Gaps: no root README (only Vite boilerplate in frontend/README.md), console.log statements in production code (10+ locations: App.jsx:23, CreatePoll.jsx:27-31, AddQuestion.jsx:22-29, route files), step-by-step creation wizard is less ergonomic than inline builder, no search, no PWA/offline support.
Backend Architecture & API Design5 / 15
Clean layered module architecture: routes→controllers→services→models across auth/poll/question/option/vote modules. Joi DTO validation with BaseDto pattern (stripUnknown, abortEarly). ApiError class with static factories (badRequest, unAuthorized, conflict, forbidden, notfound). ApiResponse class with ok/created/noContent. Global error handler handles Joi, ApiError, CastError, TokenExpiredError, MongoServerError (error.middleware.js). MongoDB transactions for voting. Sparse unique compound indexes. Gaps: no TypeScript (JS only), no Helmet/rate limiting/body size limits, server.js:14 logs MONGODB_URI, ApiError.forbidden uses 412 not 403, JWT refresh default 15min same as access, 3 empty middleware files (poll/option/vote .middleware.js), no graceful shutdown, Question model missing isRequired field, no input sanitization beyond Joi stripUnknown.
Real-Time Updates Using WebSockets4 / 10
Socket.IO server with room-based pattern: `join:poll` on connect, `vote:new` emitted after vote (vote.controller.js:12-16, socket.js:16-18). Dashboard connects, joins room by poll ID, listens for `vote:new` and triggers analytics HTTP refetch (Dashboard.jsx:74-97). Proper cleanup: socket.off/disconnect on unmount (Dashboard.jsx:92-96). Gaps: only one event type (no publish/delete/expiry events), pure invalidation signal (no data pushed, client refetches full analytics on each event — heavy), no reconnect room re-join, no socket authentication, no typed events, no debouncing, live updates only on dashboard (not vote page), no `leave:poll` handling.
Code Quality & Project Structure4 / 10
Clean monorepo with backend/frontend separation, consistent module structure, common utilities (config/dto/utils/middleware), service layer pattern, CSS custom properties. TanStack Router with file-based routing, proper React patterns (useEffect cleanup, useRef). Gaps: no root README (only Vite template in frontend/), no TypeScript despite @types packages in devDeps, console.log statements throughout production code (10+ locations), 3 empty middleware files, duplicate Verify-email route (broken Verify-email.jsx without token + working verify-email/$token.jsx), getTimeRemaining duplicated in utils/Time.js and Countdown.jsx, unused cookieParser import in auth.service.js:6, no .env.example, no tests, no linting for backend, no Docker/compose setup.
114
pollzen
Vishal Ray · @rayvishal
40
PollZen is a functional full-stack polling platform with working JWT authentication, dynamic poll creation with QR code sharing, thorough response validation with duplicate prevention via sparse compound indexes, aggregation-based analytics with pie/bar charts, Socket.IO realtime notification updates, and results publishing. Three verified deploy URLs (pollzen.rayvishal.dev, pollzen.vercel.app, pollzen.onrender.com) are all accessible. The project demonstrates solid backend engineering (MVC separation, Zod validation, proper indexing) and decent frontend UI (shadcn/ui, React Hook Form, Framer Motion animations). However, significant gaps hold it back: critical pre-save hook bug in auth, PATCH route missing validation, hardcoded mock dashboard (zero API calls), broken alreadyVoted detection on frontend, singleton socket disconnect bug, no TypeScript, zero tests, 5 pages with silent error handling, and no mobile navigation. Total score: 40/100.
Authentication & Access Control5 / 10
Working JWT auth with register/login/me endpoints (auth.service.js:5-47), bcrypt 10-round hashing via pre-save hook (auth.model.js:10-14), isloggedin middleware with token verification and user hydration (auth.middleware.js:5-39), optionalAuthenticate for mixed auth routes. Zustand auth store with localStorage persistence (auth.store.js). Anonymous/authenticated poll modes enforced via responseMode field and optionalAuthenticate middleware. Critical bug: pre-save hook missing `next` parameter (auth.model.js:10-11) causing ReferenceError when saving user without password modification. Security gaps: no Helmet, tokens in localStorage, hardcoded JWT secret in .env.example (ec1818enh5FCVH18N), frontend-backend validation mismatch (login password min: 1 backend vs 6 frontend), no refresh tokens, no email verification, no server-side logout, no CSRF.
Poll Creation & Question Management5 / 15
Poll creation uses React Hook Form + useFieldArray for dynamic questions/options (CreatePollPage.jsx:347 lines, QuestionCard.jsx with optional add/remove). Zod validation on both frontend (poll.schema.js) and backend (polls.validation.js: min 1 question, min 2 options, future-date expiresAt refinement). QR code + copy-to-clipboard share modal after creation (CreatePollPage.jsx:271-344). PATCH endpoint allows editing questions/options (polls.service.js:31-51) with ownership and status gates. Gaps: no draft status (polls always live on creation), PATCH route has NO Zod validation middleware (polls.routes.js:19 — only isloggedin, no validate()), frontend CreatePollPage destructures formState.errors but never renders them, no slug/human-readable URLs, editing questions after responses collected causes orphaned answer references in analytics.
Response Collection Flow6 / 15
Thorough backend validation (response.service.js:79-142): poll existence check (404), status check (not "active" → 400), expiry check (expiresAt < now → 400), validateAnswers (lines 24-76) checks question existence, option validity, duplicate answers, required questions. Duplicate prevention via sparse compound unique indexes on {pollId, userId} and {pollId, anonymousId} (response.model.js:33-41), plus MongoDB 11000 catch (line 137). Anonymous ID generation via crypto.randomBytes(8) stored in localStorage (PublicPollPage.jsx:30,73,78). Atomic $inc for totalResponses (line 120). Bug: frontend reads `statusResponse.data?.alreadyVoted` (PublicPollPage.jsx:36) but backend returns `{ hasVoted: true/false }` (response.controller.js:42), silently breaking the "Already Submitted" screen for returning anonymous users. No response editing/deletion endpoints.
Analytics & Feedback Dashboard5 / 15
MongoDB aggregation pipeline ($match → $unwind → $group) computes per-question/option vote counts and percentages (analytic.service.js:9-20). Results merged via JavaScript Array.find() into poll question structure (lines 27-54). Per-question breakdowns with PollPieChart, PollBarChart, and option-level progress bars (QuestionAnalyticsCard.jsx). Results publishing via PATCH /publish (polls.service.js:70-89), public results page at /results/:pollId with 403 gate if not published. Socket-triggered live refetch on analytics page via usePollRealtime (AnalyticsPage.jsx). Major issues: DashboardPage (DashboardPage.jsx:8-45) is entirely hardcoded mock data — zero API calls, fake stats of "12 polls / 1,284 responses / 5 active". Sidebar Analytics link (/app/analytics without pollId) has no matching route. No CSV export, no individual response viewing, no trend/time-series charts. Unused dead Analytic model (analytic.model.js) with bare ObjectId bug.
Frontend Experience5 / 10
10 routes with proper layout hierarchy (router.jsx): AuthLayout for login/register, DashboardLayout (sidebar+navbar+Outlet) for authenticated pages, standalone pages for public routes. Consistent dark theme using zinc palette, Framer Motion animations on landing page and stat cards. QR code + copy-to-clipboard share modal after poll creation. Skeleton loading states (PollCardSkeleton, 6-placeholder grid). shadcn/ui + @base-ui component library properly configured. Issues: No mobile navigation — sidebar hidden below lg with no hamburger menu alternative. Five pages use `console.log(error)` with zero user-facing error feedback (MyPollsPage, PollDetailsPage, AnalyticsPage, PublicPollPage, PublicResultsPage). DashboardPage entirely hardcoded mock data. Broken sidebar Analytics nav link (/app/analytics with no route). Dark mode only despite shadcn theming infrastructure being configured. No response interceptor on Axios for centralized 401 handling.
Backend Architecture & API Design6 / 15
Clean layered MVC architecture: Routes → Controllers → Services → Models across 5 module groups (auth, polls, responses, analytics, health). 15 API routes, 13 service methods. Generic Zod validation middleware (validate.middleware.js) with human-readable error formatting. Custom ApiError and ApiResponse classes for standardized responses. Rate limiting — 100 req/15min globally (rateLimiter.middleware.js). Proper sparse compound unique indexes for duplicate vote prevention (response.model.js:33-41). MongoDB indexes on createdBy, expiresAt, status, pollId. Issues: PATCH /:pollId route lacks validate() middleware (polls.routes.js:19) — any field shape accepted. pre-save hook bug in auth.model.js (missing `next` parameter, line 10-11). Unused/dead Analytic model with bare ObjectId bug (analytic.model.js). No pagination on getUserPollsService. No graceful shutdown. Hardcoded JWT secret in committed .env.example. No helmet/security headers. No automated expiry mechanism — status stays "active" past expiresAt.
Real-Time Updates Using WebSockets4 / 10
Socket.IO server with room-based pattern: clients join/leave `poll:<pollId>` rooms (poll.socket.js:1-22). Single `poll-updated` event emitted on new response submission with payload `{ pollId, type: "NEW_RESPONSE" }` (response.service.js:124). Client usePollRealtime hook (usePollRealtime.js:1-27) connects socket, joins room, listens for poll-updated, triggers HTTP refetch callback, shows toast. Critical bug: cleanup calls `socket.disconnect()` on singleton socket (line 24), terminating WebSocket for ALL components when any one unmounts. No reconnect re-join — reconnected sockets silently lose room subscriptions. Only 1 event type (no events on publish/close/delete). Pure notification pattern (no analytics data pushed, client refetches via HTTP). No debouncing. No socket authentication. Socket.IO Server created with no CORS config (config/socket.js:6). Public results page doesn't subscribe to sockets at all.
Code Quality & Project Structure4 / 10
Clean monorepo structure with backend/ (Express, 33 source files) and pollzen-frontend/ (React+Vite, 55+ source files). Well-organized frontend directories: components/{layout,common,forms,charts,ui}, pages/{auth,dashboard,polls,analytics,public}, hooks/, services/, store/, sockets/, validations/. Consistent naming (PascalCase components, *.Page.jsx pages). ESLint configured for frontend with react-hooks plugin. Issues: Zero TypeScript — entire codebase is plain JS with no type safety. Zero test files anywhere — no vitest, jest, or testing-library. console.log(error) in 5 pages instead of proper error handling. Dead code: analytic.model.js (unused, has bug), response.schema.js (defined but never imported). DashboardPage entirely hardcoded mock data (212 lines of static content). Duplicate localStorage writes in LoginPage. No ESLint on backend. Five pages with `catch { console.log(error) }` — errors swallowed silently. No README-based .env.example reference for backend environment vars.
115
R
Poll Seeker
Renuka khirwadkar · @renuka_khirwadkarr
39
Poll Seeker is a well-executed live Q&A/polling session platform (Slido/Mentimeter-style) rather than a traditional poll creation platform as specified in the requirements. The project shines in its Socket.IO real-time implementation (param-7: 7/10) with room-based architecture, late-joiner support, and participant tracking. Authentication is solid with JWT, Google OAuth, and custom OIDC (param-1: 6/10). However, the fundamental model misalignment hurts several parameters: no mandatory/optional questions, no expiry time, no result publishing, no comprehensive analytics dashboard. The frontend is visually polished with a custom CSS design system but lacks proper routing and form libraries. Code quality is mixed — clean architecture but leftover starter boilerplate and Zod installed but unused. Total: 39/100.
Authentication & Access Control6 / 10
Solid auth implementation: email/password registration with bcryptjs (12 rounds), JWT access (15min) + refresh (7d) token strategy with httpOnly cookie for refresh token, Google OAuth via google-auth-library, custom OIDC (SeekID) provider, and authenticate middleware (auth.js:44 lines). Rate limiting (10 attempts/15min) on auth endpoints. Three auth methods (email, Google, OIDC). Gaps: access tokens stored in localStorage (XSS risk), JWT_SECRET hardcoded fallback "your-secret-key" in both auth.js:3 and authController.js:8, no CSRF protection, no email verification or password reset flow. The project uses a presenter/audience model rather than classic anonymous/authenticated poll modes — audience joins without auth.
Poll Creation & Question Management4 / 15
The project uses a session-based live Q&A model, not traditional poll creation. Sessions have a title only (sessionController.js:22-33), then questions are added one-at-a-time via PresenterView.jsx (lines 96-113) with types mc/open/rating and dynamic options for MC. The "Quick Poll" form in Dashboard.jsx (lines 150-195) chains session+question+start in one flow. Missing critical requirements: no mandatory/optional toggle per question, no anonymous vs authenticated poll mode enforcement server-side, no expiry time for polls, no multi-question batch creation, no slug/permalink. The anonymous checkbox in QuickPoll form (Dashboard.jsx:381-391) is UI-only — has no server-side effect.
Response Collection Flow5 / 15
Response submission works end-to-end: audience joins via 6-char code, enters name, waits for active question, votes, sees live results. Server-side (responseController.js:5-36): voterId tracking via localStorage UUID, duplicate vote prevention via Supabase unique partial index (schema.sql:62-64) plus explicit findOne check, rate limiting (5/min), and response summarization. Client handles MC/rating/open question types with proper UI states (submitted, disabled buttons). Gaps: no validation that submitted option values actually belong to the question's options, no session expiry enforcement at submission time (ended sessions may still accept responses), no mandatory/optional question enforcement (concept doesn't exist), and no server-side question existence validation before creating response.
Analytics & Feedback Dashboard3 / 15
Very limited analytics. PresenterView.jsx (lines 146-176) shows live results for the currently active question only: bar chart with per-option counts and percentages, total votes, and colored progress bars. getSummary endpoint (responseController.js:48-63) computes per-question aggregation server-side with frequency counting. Reaction stats and participant names are tracked. Missing: no comprehensive analytics dashboard, no cross-question summaries, no CSV/export, no individual response viewing, no publication of results for public viewing after session ends (the README's "publish" concept not implemented), no trend data, no time-series charts, and no poll health metrics. The analytics are purely live per-question results, not a dashboard.
Frontend Experience5 / 10
Visually polished with consistent glass-morphism dark theme (App.css:319 lines with CSS variables design system), dark/light theme toggle with Unsplash background images (useTheme.js hook), and 15+ custom CSS animations (fadeUp, scaleIn, shimmer, float, emojiPop). Handles loading (spinners), error (banners), and empty states across all pages. Copy-to-clipboard with 2s "Copied!" feedback, WhatsApp share button, responsive layout. Major gaps: no proper routing library (uses URL query params `?join=1` and `?code=`), no form library (all forms use individual useState hooks), no confirmation dialogs for destructive actions (delete is immediate), no toast notifications, and the overall UX is session/presenter-focused rather than poll-focused.
Backend Architecture & API Design5 / 15
Clean layered architecture: routes→controllers with Supabase direct calls, proper middleware stack (Helmet, CORS, express-rate-limit with 3 tiers, cookie-parser), JWT auth middleware, centralized error handler with differentiated status codes (errorHandler.js:57 lines). Supabase PostgreSQL schema (schema.sql:83 lines) with 4 tables, proper foreign keys with CASCADE deletes, 5 indexes including unique partial index for vote deduplication, and snake_case→camelCase mappers (mappers.js:18 lines). ESM modules throughout. Critical gaps: Zod listed as dependency but never actually used for validation (all validation is manual ad-hoc property checks), no service layer between controllers and DB, no request body size limits, JWT_SECRET hardcoded fallback in 2 files, no pagination on list endpoints, and no query sanitization.
Real-Time Updates Using WebSockets7 / 10
Well-implemented Socket.IO integration. Server (socket.js:218 lines): room-based pattern with socketAuthMiddleware verifying JWT for presenters, activeSessions Map tracking participants/active questions/presenter socket, 8 event handlers (session:join, question:activate/deactivate, response:submit, reaction:send, session:start/end, disconnect). Late-joiner support sends active question to newly connected audience members (socket.js:88-93). Participant tracking with join/leave notifications. Client (useSocket.js:94 lines): singleton pattern with ref-based socket access, auto-reconnect (5 attempts, 1-5s backoff), useCallback-based on/emit helpers, separation of socket events controller (socketEvents.js:54 lines) for REST→WebSocket bridge. Gaps: no typed events, some console.log in production handlers (socket.js:47,76,101,114,123,142,157,166,176), and only covers the session room — no analytics-specific rooms or event deduplication.
Code Quality & Project Structure4 / 10
Clean monorepo structure (src/ backend + frontend/ + mobile/ + tests/) with consistent ESM modules, snake_case→camelCase mapper pattern, and environment-based configuration. The CSS design system (index.css:155 lines with 15 animations, utility classes for glass/glow/shimmer) and useSocket/useTheme hook patterns are well-separated. Issues: App.css contains ~60% leftover Vite starter boilerplate (.counter, .hero, #next-steps, #docs, #spacer, .ticks — none used in the app), joinInviteUrl/whatsappInviteUrl duplicated verbatim in Dashboard.jsx and PresenterView.jsx, Zod installed but never imported/used, JWT_SECRET fallback "your-secret-key" hardcoded in 2 files, Hindi/English mixed comments throughout (auth.js:7, socket.js:12,23), console.log active in production across 15+ call sites, no TypeScript, and the README references tests/e2e/ directory that doesn't exist (actual tests are at tests/demo/). No automated tests found.
116
Pollify-plateform
Ritu Sood · @soodritu99_46366405
39
Pollify-platform is a full-stack polling app with working auth (JWT + Google OAuth), dynamic poll creation, public sharing via tokens, response collection, analytics with Chart.js, and Socket.io real-time updates. The application delivers core functionality across all 8 evaluation criteria but does so at a moderate depth level — things work on the happy path but critical gaps exist: no duplicate vote prevention, no poll editing, tokens in localStorage, no service layer, no tests, and significant code duplication. The consistent warm design language and comprehensive README are positives. Total: 39/100, placing it near the cohort mean of 40.85.
Authentication & Access Control5 / 10
Auth system has register/login with bcryptjs (12 rounds), Google OAuth, JWT (7-day), authenticate/optionalAuth middleware, ProtectedRoute component, AuthContext with localStorage persistence, and Axios 401 interceptor. However: tokens in localStorage (XSS risk), no Helmet/CSRF/rate limiting, no email verification or password reset, optionalAuth middleware defined but never applied to any route, the anonymous poll flag is stored but never enforced during response, and no server-side logout/token invalidation. Backend tsconfig lacks strict mode. (backend/src/middleware/auth.ts, backend/src/routes/auth.ts, frontend/src/contexts/AuthContext.tsx)
Poll Creation & Question Management6 / 15
CreatePoll.tsx (751 lines) delivers dynamic question/option add/remove with mandatory/optional toggle, anonymous toggle, expiry datetime picker. Server-side Zod validation enforces min 1 question, min 2 options per question. Transaction-based poll creation (BEGIN→poll→questions→options→COMMIT). Client-side validation checks title, question text, and option text are all non-empty. Gaps: no poll editing endpoint exists (only create), no form library (751 lines of useState hooks), share token is server-generated UUID, no QR/copy-link convenience, no slug system. (backend/src/routes/polls.ts:11-53, frontend/src/pages/CreatePoll.tsx, backend/src/schemas/index.ts:14-30)
Response Collection Flow5 / 15
PublicPoll.tsx handles respond/results/closed modes with radio-button option selection. Server-side validates poll existence, expiry (410), closed/published state (410), mandatory question completion, and uses transactional writes. Gaps: NO duplicate vote prevention whatsoever (no unique index, no cookie/localStorage tracking, no respondent_id population), respondent_id field is never set despite existing in schema, no validation that submitted option_ids belong to referenced question_ids, no per-question single-answer enforcement, Zod SubmitResponseSchema defined but unused at response endpoint, no rate limiting on submissions. (backend/src/routes/polls.ts:110-184, frontend/src/pages/PublicPoll.tsx)
Analytics & Feedback Dashboard5 / 15
Analytics.tsx (420 lines) with Chart.js (CDN-loaded) shows doughnut chart, line chart, and metric cards (Total Responses, Questions, Anonymous, Status). PublishResults.tsx has bar chart. getAnalytics() computes per-question option counts with SQL aggregation and json_agg. Public poll shows published results. Dashboard shows per-poll response counts. Gaps: percentage calculation is mathematically wrong for multi-question polls (divides option count by TOTAL responses instead of per-question total), no CSV/export, no individual response viewing, no time-series trend data, no peak activity metrics, Chart.js loaded via CDN script injection rather than npm. (backend/src/routes/polls.ts:240-267, frontend/src/pages/Analytics.tsx)
Frontend Experience5 / 10
Consistent warm amber/brown glassmorphism design across 8 pages with 9 routes. Loading spinners, error toasts, empty states ("No polls yet"), and auth-conditional navbar all present. Password show/hide on login. Responsive grid layouts. Gaps: excessive inline styles everywhere (style props instead of CSS classes), THEME color constants duplicated across 5 files with inconsistent values, no form library, no dark mode, no confirmation dialogs beyond browser confirm(), Chart.js loaded via CDN script, UI components (Card, Badge, Input, HBar) defined but rarely used, and home page analytics panel is limited to one poll's first question. (frontend/src/pages/*.tsx, frontend/src/components/ui.tsx)
Backend Architecture & API Design4 / 15
Backend has clean Express+TypeScript+Socket.io structure with 6 source files (578 lines total). Parameterized SQL queries, Zod validation (6 schemas), transactions for writes, proper foreign keys with CASCADE deletes, and 5 indexes. Gaps: no service layer (all logic in route handlers), backend tsconfig lacks strict mode (only 4 compilerOptions), no Helmet/rate limiting/body size limits, JWT_SECRET uses non-null assertion with no env validation, unused imports (OAuth2Client in index.ts:9, googleapis in deps), both bcrypt and bcryptjs in dependencies, optionalAuth defined but never used, no pagination, no centralized error handler, and raw err.message leaks possible via generic 500 catch. (backend/src/routes/polls.ts, backend/src/index.ts, backend/tsconfig.json)
Real-Time Updates Using WebSockets5 / 10
Socket.io integrated with room-based pub/sub: server creates `poll:{uuid}` rooms, clients join/leave, 3 event types (response_count, analytics_update, poll_published). Analytics.tsx and PublishResults.tsx listen for live updates and update state. Gaps: no socket authentication (anyone joins any room), no reconnection room re-join logic, no debouncing, full analytics payload pushed (not incremental), PublicPoll (voting page) does NOT use sockets, response_count event emitted but never consumed by any client component, getAnalytics() called redundantly on each submission, and no TypeScript event type definitions. (backend/src/index.ts:33-39, backend/src/routes/polls.ts:167-171, frontend/src/api/socket.ts, frontend/src/pages/Analytics.tsx:164-186)
Code Quality & Project Structure4 / 10
Clean monorepo structure (backend/ + frontend/), TypeScript across both packages, comprehensive README (211 lines), Docker Compose, ESLint on frontend. But significant quality issues: 0 tests, THEME color constants duplicated across 5 files, same background blur divs copied across 6 pages, NavLink className duplicated 3x in Navbar, Chart.js CDN loading logic duplicated in Analytics+PublishResults, Window.Chart declaration duplicated, unused components (Card/Badge/Input/HBar defined but rarely used), unused COLORS/BAR_COLORS exports, both bcrypt+bcryptjs as dependencies, no backend ESLint, backend tsconfig lacks strict mode, no .env.example file, and orphaned `{}` expression in Dashboard.tsx:300. (frontend/src/pages/*.tsx, frontend/src/components/ui.tsx, backend/tsconfig.json)
117
P
PulseBoard
PRINCE KUMAR · @princekumar
39
PulseBoard is a full-stack polling platform with React/Vite frontend and Node/Express/MongoDB backend. The submission has solid foundational work: working JWT auth with cookie-based sessions, a thorough poll creation flow with dynamic questions/options and dual validation, a well-structured response collection backend with comprehensive validation (expiry, duplicates, mandatory questions, existence checks), functional Socket.IO real-time analytics updates, and a clean monorepo structure with Docker support. However, two CRITICAL bugs severely impact the submission: (1) both public-facing pages (PublicPollPage and ResultsPage) have a useParams() destructuring mismatch where the code expects {id} but the route defines :pollId, making the main user flow non-functional, and (2) the analytics Dashboard uses entirely synthetic/fake data for its charts. Additional issues include empty service stubs, dead code, no logout endpoint, hardcoded CORS/localhost config, no rate limiting despite the package being installed, zero tests, no TypeScript, and significant code duplication. Total score: 38/100, placing it below the cohort median of 43.
Authentication & Access Control5 / 10
Registration and login with bcryptjs (10 salt rounds), JWT via httpOnly cookies, authMiddleware for required auth + optionalAuthMiddleware for anonymous support, ProtectedRoute wrapper with loading state, AuthContext with useAuth hook. But notable issues: no logout endpoint on server (authRoutes.js has no /auth/logout, but frontend calls it), secure:false hardcoded in cookies (authController.js:31,83), localStorage token stores "undefined" string (loginUser/registerUser response doesn't include token field), express-rate-limit installed but never used, no CSRF protection, no token revocation. Core auth flow works but has several rough edges.
Poll Creation & Question Management7 / 15
Dynamic question add/remove with local IDs, dynamic option add/remove per question (min 2 enforced), is_required toggle per question, public/private poll mode with allow_anonymous+requires_auth toggle, expiry date picker, title+description inputs. Client-side validation (CreatePollPage.jsx:207-251: strips empty options, checks questions have text, min 2 options), server-side validation via pollValidation middleware + Mongoose schema validators with min/max constraints. EditPollPage for updates, deletePoll with cascading response cleanup (pollController.js:262-268), ownership checks on update/delete/publish. Missing: no form library (raw useState), ~70% Create/Edit duplication, no slug/permalink sharing, no QR code.
Response Collection Flow3 / 15
Backend response submission pipeline is thorough: optionalAuthMiddleware, poll existence check, requires_auth enforcement, allow_anonymous enforcement, expiry checking via checkPollExpiry utility, duplicate prevention for both authenticated (respondent_id) and anonymous (anonymous_identifier) users, mandatory question validation, and question/option existence verification (responseController.js:9-222). CRITICAL BUG: PublicPollPage.jsx:23 destructures {id:pollId} from useParams() but route defines :pollId in path="/p/:pollId", making pollId always undefined and the page non-functional (stuck in loading). Same bug in ResultsPage.jsx:40. Also: no NPE guard before poll.requires_auth access (responseController.js:33), /p/:pollId/success route not registered, anonymous identifier stores raw IP+UserAgent (privacy concern).
Analytics & Feedback Dashboard5 / 15
Backend aggregateAnalytics utility (aggregateAnalytics.js:1-45) correctly computes per-question option vote counts and 2-decimal percentages. AnalyticsPage shows real per-question breakdown with colored progress bars, option names/counts/percentages, and Recharts pie chart of first question. Also shows total responses, anonymous/authenticated indicators, questions count. Published results view (ResultsPage) exists with per-question donut charts. However, Dashboard chart data is entirely synthetic (Dashboard.jsx:95-100 uses poll.questions.length*10 as fake response count), AnalyticsPage timeline is hardcoded (lines 160-194), "Export Analytics" button is cosmetic only, no individual response viewing, no actual time-series analytics, pie chart only covers first question.
Frontend Experience4 / 10
16 page components with React Router routing, TailwindCSS with consistent violet/indigo theme, loading/error/empty states on most pages, Sidebar navigation with active-route highlighting, UI component library (Button/Input/Card/StatsCard/Badge with variants), Lucide React icons, Vite proxy for Docker networking. CRITICAL: PublicPollPage and ResultsPage have useParams() destructuring bug (expects {id} but route defines :pollId) making public-facing features non-functional. Other issues: alert() used for validation errors (CreatePollPage.jsx:209,237,249) instead of inline feedback, no form library (raw useState across all forms), submission success page route not registered (navigates to non-existent /p/:pollId/success), CreatePollPage and EditPollPage share ~70% duplicate JSX, no confirmation dialogs, no dark mode.
Backend Architecture & API Design5 / 15
Clean MVC-inspired structure (routes→controllers→models), manual validation middleware for auth/poll/response, helmet+CORS+morgan+cookieParser middleware, body size limits (16kb), 15 API endpoints covering full CRUD+analytics+public routes, MongoDB schemas with validation/unique indexes/timestamps, ownership checks on update/delete/publish, cascading response deletion. But: services layer is empty stubs (pollService.js and responseService.js export only {message:"...Service Ready"}), validateMiddleware.js is dead code (no-op, never imported), express-rate-limit installed but never used, CORS hardcoded to localhost:5173 (app.js:19 + socket.js:12), error middleware bug (reads res.statusCode not err.statusCode at errorMiddleware.js:7-9), raw error.message leaked to clients, no logout endpoint, missing compound indexes on Response for efficient duplicate checks, no pagination, no graceful shutdown.
Real-Time Updates Using WebSockets6 / 10
Socket.IO server properly initialized with HTTP server (config/socket.js:9-39), room-based pattern with join_poll_room/leave_poll_room events (pollSocket.js:4-13), server emits live_analytics_update with full aggregated analytics payload after each response submission (responseController.js:192-198), client joins room on AnalyticsPage mount and listens for updates (AnalyticsPage.jsx:63-88), cleanup on unmount with leave_poll_room+off. Client configured with reconnectionAttempts:5, both websocket+polling transports. Docker timeout settings (pingTimeout:60s, pingInterval:25s). Gaps: only covers analytics pages (no live response count on public voting page), no auth on socket connections, no reconnect room re-join logic, analyticsSocket event handler (analyticsSocket.js:2-4) appears unused as data flows via direct io.to().emit(), hardcoded CORS origin.
Code Quality & Project Structure4 / 10
Monorepo with clean backend/frontend separation, Docker Compose with 3 services (MongoDB/backend/frontend), consistent file naming, good directory structure (controllers/routes/models/middleware/validations/utils/services/config). However: pure JavaScript with zero TypeScript or JSDoc, zero test files or test scripts, services layer is dead code (2 stub files), validateMiddleware.js is dead code, COLORS array duplicated across 4 files (Dashboard/AnalyticsPage/PieChart/ResultsPage), cookie config duplicated in authController.js register+login, CreatePollPage and EditPollPage share ~70% duplicate structure, console.log active throughout production code, backend Dockerfile runs npm run dev (nodemon, not production), no .env.example files, express-rate-limit installed but unused, no linting on backend, inconsistent indentation across files. README is setup-only with no feature documentation.
118
PulseBoard: Live Polls System
Shailesh Sharad Padwal · @sspadwal1002_8b017ec3
39
PulseBoard: Live Polls System — a MERN-stack polling platform with 1 verified deployment (https://pulse-board-iota-one.vercel.app). The project implements solid core functionality: full JWT authentication with refresh rotation, dynamic poll creation with multi-question support, well-validated response collection with duplicate prevention via DB indexes, functional analytics with per-question bar charts, and a consistent neumorphism UI design. However, it has several critical gaps: NO WebSocket/Socket.IO implementation at all (despite claiming socket.io in the tech stack - score 0 on param-7), no root README.md, several security issues (missing cookie-parser, inverted CSP config, localStorage token storage, password logging, overly permissive CORS), and analytics are shallow without exports or trends. Total: 39/100.
Authentication & Access Control5 / 10
Functional auth system with registration (name/email/password/role), login (access+refresh JWT pair, 15m/7d), token rotation with crypto hash, forgot/reset password with email, logout, and optionalAuthenticate middleware for anonymous/authenticated hybrid access. Anonymous vs authenticated poll modes enforced server-side at responses.service.js:23-25. However: no email verification (all users created with isVerified=true, auth.service.js:24), no OAuth, no rate limiting, missing cookie-parser dependency (req.cookies is undefined at runtime, auth.controller.js:38), access tokens in localStorage (XSS vulnerable, client.js:9), Helmet CSP disabled in production (app.js:27, appears inverted), CORS origin:true with credentials:true (app.js:21-22), console.log(req.body) leaks plaintext passwords (auth.controller.js:49), role-based authorize middleware defined but NEVER used and role field commented out on req.user (auth.middleware.js:22,30-38).
Poll Creation & Question Management7 / 15
Comprehensive poll creation form (CreatePollPage.jsx, 222 lines) with dynamic questions/options (add/remove, min 2 options enforced client and server), mandatory/optional toggle per question, anonymous toggle, expiry datetime picker with 14-day default, status selector (draft/active), title and description fields. Server-side Joi validation (create-poll.dto.js) enforces min 1 question, min 2 options per question, text length limits, date validation. Poll update via PATCH (poll.services.js:43-66) supports editing title/description/isAnonymous/expiresAt/status/questions (questions only if no responses). Close and publish actions with state validation. nanoid-based shareToken. Weaknesses: no form library (individual useState hooks), no client-side validation beyond HTML5 attributes, no slug/QR code generation, questions cannot be edited after responses exist. Covers all required flow solidly.
Response Collection Flow7 / 15
Strong defense-in-depth response validation (responses.service.js, 89 lines): mandatory question enforcement (lines 37-41), duplicate question ID detection with Set (lines 28-30), option-to-question ownership verification (lines 44-51), question existence check (lines 46-48), poll status gates (draft/expired/closed/published rejection, lines 17-22), auth requirement for non-anonymous polls (lines 23-25). Duplicate prevention with DB-level partial unique indexes on {pollId, respondentId} and {pollId, ipHash} (responses.model.js:46-64), plus MongoDB error code 11000 catch (lines 83-88). SHA256 IP hashing for anonymous fingerprinting (ip-hash.utils.js). Client-side: radio button selection, mandatory question check, comprehensive UI states (loading/error/success/login-required/expired/not-accepting/published results). Joi validation on submission. Gaps: no rate limiting, no real-time feedback, basic client-side validation.
Analytics & Feedback Dashboard5 / 15
Functional analytics with total response count, per-question option breakdowns with counts, answered counts per question, and participation mode breakdown (anonymous vs authenticated). MongoDB aggregation pipeline ($unwind/$group) for efficient counting (poll.services.js:144-159). Published results view on public poll page (PublicPollPage.jsx:118-154) with bar charts. Creator-only access enforced. However, the analytics are shallow: no CSV/export, no individual response viewing, no trend data or time-series, no peak activity metrics, no leader/winner identification, no percentages (only raw counts), no charting library (CSS bars only), bar chart code duplicated between AnalyticsPage.jsx:42-83 and PublicPollPage.jsx:118-154, no animated fills, no tabular data view. Just 87 lines in AnalyticsPage.jsx. The core analytics work but lack depth.
Frontend Experience6 / 10
Consistent neumorphism (soft UI) design language with CSS tokens (index.css, 516 lines) — cohesive shadows, colors, radii, typography. 8 routes with clean URL structure (App.jsx). All pages handle loading, error, empty, auth-required, expired, published states explicitly. Protected routes with redirect to /login preserving return state. Share URL with copy-to-clipboard. Color-coded status badges (draft/active/closed/published). Fade-in page transitions. Radio option hover/checked CSS effects. Responsive grid layout (mobile-friendly). Reduced-motion support. Notable gaps: no form library (individual useState hooks), no confirmation dialogs for destructive actions (close/publish), no skeleton loaders (text-based only), no dark mode, bar chart rendering duplicated, no React Query (useEffect+useState pattern, refetches on every navigation), no root README.md at all.
Backend Architecture & API Design5 / 15
Clean MVC architecture: routes→controllers→services→models with feature-based modules (auth/, poll/, responses/). Joi validation on all body-accepting routes via validate middleware. Mongoose schemas with embedded sub-documents, validators, proper indexes (unique shareToken, compound {createdBy,status}, partial unique response indexes). API error/response factories for consistent formatting. Dual-token JWT with hashed refresh token rotation. MongoDB aggregation for analytics. BUT significant issues: missing cookie-parser dependency (req.cookies broken), CORS origin:true with credentials:true (app.js:21-22), Helmet CSP disabled in production but enabled in dev (app.js:27-39, inverted), hardcoded personal email (email.js:15), console.log(req.body) leaks passwords (auth.controller.js:49), no rate limiting, no body size limits, nodemon as production dependency, email failure silently swallowed (auth.service.js:101-103), unused authorize middleware with commented-out role, inconsistent file naming (poll.services.js vs responses.service.js), no TypeScript, no automated tests, no server .env.example.
Real-Time Updates Using WebSockets0 / 10
Zero implementation. grep confirmed no matches for Socket.IO, WebSocket, socket.io, or any real-time technology in the entire codebase. No server-side Socket.IO setup, no client-side socket connections, no events emitted or listened for, no rooms, no live response counts, no real-time analytics updates. The submission claims "socket.io" in its tech stack but nothing exists. The analytics page fetches data once on mount with no refresh mechanism. Score 0 for complete absence of this required feature.
Code Quality & Project Structure4 / 10
Clean monorepo structure (client/ and server/) with feature-based module organization and consistent layered architecture. Cancelled flag pattern in all useEffect hooks for stale closure prevention. Consistent ApiError/ApiResponse patterns. CSS design system with tokens. BUT: No root README.md (only Vite boilerplate in client/), zero automated tests, no TypeScript, ~40 lines commented-out dead code (email.js:39-78), placeholder comments ("// something here preset." at auth.controller.js:5), commented-out imports (auth.routes.js:5,7), inconsistent file naming (poll.services.js vs responses.service.js, ip-hash.utils.js vs jwt.utils.js), Joi schema duplication between create-poll.dto.js and update-poll.dto.js (lines 4-14 identically), bar chart rendering duplicated between two pages, unused noContent() method with ReferenceError bug (api-responses.js:19-21), default import name mismatches across files, nodemon as production dependency, no ESLint on server, debug console.log in production code.
119
poll wave Re-scored
Nikhil Jha · @nikhil324008jha_1d7836c9
39
pollWave is a MERN polling app with notable polish on the frontend (animated landing, count-ups, password strength meter, dark mode, Recharts result panel) and a DTO-based backend pattern (Joi DTOs, BaseDTO.Validate factory, custom APiError class). However the implementation has multiple correctness bugs that materially hurt the rubric: pollCreate is not behind auth middleware (anyone can create polls), allowAnonymous is sent as the entire anonymousMap object instead of a boolean (Dashboard.jsx:97), expiresAt always uses expiryMap[0] regardless of which card was published, poll.service.js force-sets allowAnonymous=true on every read (nullifying anonymous mode), the Analytics page is an empty placeholder, the vote tally is split between unsynced in-memory socket counter and non-atomic DB save (no unique index on {pollId, voterId}, no $inc), routes are double-mounted at both /auth and /poll, the global error handler relies on a misspelled flag (isOPerational), and forBidden returns HTTP 412 instead of 403. A trailing '//socket not working' comment in ShowPoll.jsx:361 indicates known-broken behavior shipped.
Authentication & Access Control6 / 10
bcryptjs+salt-12 in Model/client.js, JWT access+refresh in jwt/jwt.js, httpOnly cookies with secure/SameSite=None, register/login/logout/me endpoints, refresh-token sha256-hashed before storage, protectedRoute HOC. Negatives: pollCreate route at routes/routes.js:27-31 is NOT behind authenticate middleware (anyone can create polls), pollAuthenticate at Dtos/pollAuthenticate.js:13-22 will TypeError if checkAuth is null, register signs JWT with {name,email} while login signs with {id,email} so access token from register lacks user id, password DTO caps at 12 chars but error message claims it requires special chars.
Poll Creation & Question Management5 / 15
Joi DTO validates question/options/expiresAt/allowAnonymous (Dtos/pollSchemaDto.js), Mongoose pollSchema (Model/pollSchema.js) has question/options[{text,vote}]/allowAnonymous/expiresAt/isActive, dashboard UI posts to /poll/pollCreate. Negatives: only single-question single-choice text polls, no mandatory flag, no edit/delete endpoints, allowAnonymous sent as the entire anonymousMap object instead of a boolean (dashboard.jsx:97) so always truthy, expiresAt: expiryMap[0] used regardless of which card was published, poll.service.js:32 force-sets resObj.allowAnonymous=true on every read.
Response Collection Flow7 / 15
voter-id cookie + pollResponse collection dedup middleware (Dtos/reg.response.js, Dtos/pollUserMiddleWare.js), expiry check in poll.service.js, option-not-found rejected (poll.service.js:46-50). Negatives: no unique compound index on {pollId, voterId} so the dedupe is TOCTOU race under concurrency, cookie sameSite=strict (pollUserMiddleWare.js:11) breaks across third-party prod domains so the same browser gets new uuids per tab, vote count uses option.vote += 1; poll.save() with no atomic $inc (concurrent votes lose), socket vote handler maintains separate in-memory tally that is NOT synced with Mongo and is wiped on restart.
Analytics & Feedback Dashboard3 / 15
Frontend Analytics.jsx is essentially a placeholder: header + literal <h1>Analytics</h1> with zero data fetching, no per-poll list, no aggregated metrics, no charts. The only charting in the project is the per-poll Recharts bar inside ShowPoll.jsx:309-350 and firstScreen/expired.jsx:303-353. No 'published results' toggle, no creator-side dashboard of past polls, no response counts surface.
Frontend Experience6 / 10
Polished landing page (animated hero, count-ups, testimonials, dark-mode toggle), login screen with password strength meter + tab switcher + mobile breakpoints, dashboard editor with dynamic option add/remove + anonymous/expiry toggles, ShowPoll with clean selection UX + Recharts result panel, tasteful AccessDenied and PollExpired empty-states. Negatives: confusing two-step submit-then-publish UX, submittedPoll resets on refresh, no list of 'my polls', vote prompts for name via window.prompt(), Analytics page empty, backend URL hardcoded in 5+ places.
Backend Architecture & API Design6 / 15
Clear folder split (routes/services/Dtos/Model/utils/jwt/Database), class-based DTO pattern with BaseDTO.Validate, validate() middleware factory, custom APiError with static factories and structured toJSON, ApiResponse envelope, cookie-parser+cors configured. Negatives: global error handler at Database/app.js:20-27 checks misspelled isOPerational flag (works by coincidence because APiError uses the same typo), so any non-APiError returns generic 500; routes mounted twice at /auth AND /poll on the same router so every auth route is also reachable at /poll/signup etc; verifyRefreshToken references undeclared err; forBidden returns HTTP 412 instead of 403; CORS hardcoded to single prod origin.
Real-Time Updates Using WebSockets4 / 10
Socket.io server attached to same HTTP server (Backend/server.js:11-18), room-per-poll via socket.join(pollId), broadcast on vote via io.to(pollId).emit('pollUpdate', ...), client connects with autoConnect:false and withCredentials:true (Frontend/src/Socket.js), ShowPoll subscribes on mount and emits join + vote, result chart re-renders via merged options memo. Negatives: tally is double-counted because the client emits socket.emit('vote',...) AFTER a successful POST and the server's socket handler maintains a separate in-memory counter NOT synced with Mongo, no auth on socket connections, trailing '//socket not working' comment at ShowPoll.jsx:361 suggests known-broken behavior shipped.
Code Quality & Project Structure2 / 10
No test directory, no helmet, no express-rate-limit, no env-validation library, no .env.example, no Husky/lint-staged. ESLint only configured on frontend, backend test script aliased to 'node server.js'. Multiple files hardcode the production API URL (AuthContext.jsx, LoginPage.jsx, dashboard.jsx, ShowPoll.jsx) instead of using import.meta.env. Accidental 'npm' and 'install' in Frontend/package.json dependencies. Several identifier typos (isOPerational, auhtMiddleware, forBidden=412, 'expiresIn: 15min' should be '15m'), commented-out dead code, console.logs in production paths, trailing '//socket not working' comment suggests known-broken behavior shipped.
120
PollSync
Tejas · @codingwithtejas7_020443bc
38
PollSync is a functional full-stack polling platform with working deployments, JWT auth, dynamic poll creation via React Hook Form, Socket.IO real-time updates, and a consistent TailwindCSS design. However, the implementation has significant bugs and gaps that hold it back: the analytics HTTP route is completely broken (never wired into the app), the analytics page isn't in the React Router config, there are 10+ dead imports, critical data mapping bugs in poll.controller.js, no duplicate prevention for anonymous poll responses, access tokens stored in localStorage, no security middleware (Helmet/CSRF/rate limiting), and no TypeScript or tests. The core poll creation, response submission (for authenticated users), and result publishing flows work end-to-end with Socket.IO, but the overall quality is at the "partial/average" level.
Authentication & Access Control5 / 10
Registration/login with bcryptjs (10 rounds) and JWT access+refresh tokens works. Auth middleware verifies Bearer tokens from ACCESS_TOKEN_SECRET (auth.middleware.js:51 lines). Optional auth middleware for response routes. Refresh endpoint and logout clearCookie exist. Anonymous vs authenticated poll modes enforced server-side (response.service.js:42-57). Critical gaps: access tokens stored in localStorage (XSS risk, LoginPage.jsx:16), refreshToken cookie set with `secure: false`, no Helmet/CSRF protection, no rate limiting, `console.log` in refresh handler (auth.controller.js:74,88), dead imports (`from "zod/v4/locales"` in auth.controller.js:4), typo "creditnals" (auth.service.js:53), no email verification or password reset. auth.routes.js imports router from poll.routes.js (line 1) instead of creating a new Router.
Poll Creation & Question Management5 / 15
React Hook Form with useFieldArray provides dynamic questions and options (CreatePollPage.jsx:171 lines, QuestionBlock.jsx:121 lines). Per-question mandatory/optional toggle, anonymous/authenticated toggle, expiry datetime picker. Server-side Zod validation enforces min title (3 chars), min options per question (2), coerce date/boolean (poll.validator.js:34 lines). Prisma nested create for questions+options in a single transaction (poll.service.js:7-53). Significant gaps: no poll editing capability (no PATCH endpoint for questions/options), no slug/permalink feedback or copy-to-clipboard on creation success (only toast), no client-side validation errors shown to users, slug generated via Math.random() without collision avoidance (generateSlug.js:8 lines), no draft status concept.
Response Collection Flow5 / 15
Server validates poll existence, published state, expiry date, required questions with meaningful error messages, option validity per question (response.service.js:152 lines). For authenticated polls: duplicate check by userId (line 47-57). Zod validates answers array with min 1 (response.validator.js:12 lines). Client tracks selected options per question and prevents submission when closed. Critical gaps: NO duplicate prevention for anonymous polls — anonymous voters can submit unlimited responses (response.service.js:42-57 only checks duplicates when !poll.isAnonymous). Bug in poll.controller.js: getPublicPoll maps `slug: result.poll.description` (line 42) and `isExpired: result.poll.published` (line 46) — these are incorrect field mappings. No client-side pre-submit validation of required questions.
Analytics & Feedback Dashboard4 / 15
AnalyticsPage.jsx (147 lines) renders per-question option breakdowns with counts, percentages, and CSS animated progress bars. Empty state when zero responses. PublicResultsPage.jsx (143 lines) shows published results with leader detection (green bar for winning option). Dashboard displays response counts per poll. However: analytics HTTP route is broken — analytics.routes.js (8 lines) has no Router import/create/export and is never mounted in app.js, making GET /api/analytics/:pollId unreachable. The AnalyticsPage route `/dashboard/poll/:pollId/analytics` is missing from React Router config (routes/index.jsx:73 lines). No charts/Recharts, no CSV export, no trend/time-series data, no individual response viewing, no authenticated vs anonymous breakdown.
Frontend Experience5 / 10
Consistent TailwindCSS design with blue (#097FE8) accent on slate/gray backgrounds across all pages. Loading spinners, error banners, and empty states on Dashboard, PublicPoll, Analytics, and PublicResults pages. React Router with ProtectedRoute redirect, React Hook Form on create poll and auth pages. Toast notifications via react-hot-toast. Responsive layouts with max-w containers. Issues: analytics page route `/dashboard/poll/:pollId/analytics` missing from router config, no confirmation dialogs for destructive actions (delete is immediate), unusual marketing-heavy terminology ("Component Matrix", "Deploy Live Pipeline"), signup says "Minimum 8 characters" but server validates min 6, App.jsx renders its own RouterProvider but is unused (main.jsx renders the router directly), no form-level validation errors shown to user.
Backend Architecture & API Design5 / 15
Clean modular architecture with modules/auth, poll, response, analytics, socket — each with controller/service/routes/validator separation. Prisma schema with 6 models, proper relations, and cascade deletes (schema.prisma:86 lines). Zod on all 4 input schemas. CORS with credentials and cookie-parser configured. Socket.IO attached to HTTP server. But: analytics.routes.js is broken (no Router import/create/export), auth.routes.js imports router from poll.routes.js (line 1) instead of creating new Router, 10+ dead imports across 4 files (e.g., `import { id } from "zod/v4/locales"` in auth.controller.js:4 and poll.service.js:5, `import { text } from "express"` in poll.controller.js:4 and poll.service.js:1), no Helmet/rate limiting/body size limits, no centralized error handler, `dotenv.config()` called after app import in server.js, `console.log` in auth and response controllers, no env var validation.
Real-Time Updates Using WebSockets5 / 10
Socket.IO integrated with HTTP server via room-based pattern (socket.handler.js:29 lines). Server emits `analytics-updated` (full analytics data) and `new-response` ({pollId, totalResponses}) to poll rooms on submission (response.service.js:138-150). Client connects with reconnection enabled (socket.js:20 lines), joins rooms on DashboardPage (line 21) and AnalyticsPage (line 26). Client listens and updates state on both pages. Gaps: websocket-only transport (no HTTP polling fallback), no room re-join on reconnection, no socket authentication, no room leave events, no live updates on the public voting page (PublicPollPage.jsx), no debouncing on client, no `poll-published` event as claimed in README, server doesn't emit on publish/delete. Two separate `if (io)` blocks emit sequentially (lines 138-150) when one check would suffice.
Code Quality & Project Structure4 / 10
Clean monorepo structure (client/ + server/ with docker-compose). Modular backend with consistent controller/service/routes/validator naming. Comprehensive README with setup instructions, API reference, and architecture diagram. However: no TypeScript anywhere, zero automated tests, no ESLint on server side, 10+ dead/unused imports across 5 files, bugs in production code (poll.controller.js:42 sets slug to description, analytics.routes.js incomplete and unwired), `console.log` in auth.controller.js and response.controller.js, AccessToken stored in localStorage without refresh token interceptor pattern, App.jsx renders RouterProvider redundantly (main.jsx already renders it), loginUser destructures unused `name` param (auth.service.js:34), typo "creditnals" (auth.service.js:53), no server .env.example, no confirmation dialogs, inconsistent formatting with excessive line breaks in some files.
121
S
PulseBoard
Swagatika Sahoo · @swagatika07
38
PulseBoard is a functional MERN-stack polling platform with JWT auth, dynamic poll creation, response collection with server-side validation, analytics dashboard, result publishing, and Socket.IO real-time updates. Deployed and working at http://74.208.206.245/login. Strengths: comprehensive response validation, proper auth flow with anonymous/authenticated modes, consistent UI with toast notifications, end-to-end working deployment. Weaknesses: no TypeScript, no security middleware (Helmet/CSRF/rate limiting), no validation library, no tests, dead code and unused dependencies, minimal WebSocket implementation (single global event, no rooms), basic analytics (no trends/export), no poll editing, no .env.example. The project demonstrates functional completion across all requirements but lacks depth and polish in most areas.
Authentication & Access Control5 / 10
Functional JWT auth with registration (bcryptjs, 10 rounds) and login at authRoutes.js:16-82, token generation with 7d expiry. Auth middleware (authMiddleware.js:4-26) properly verifies JWT and attaches req.user. Optional auth in responseRoutes.js:9-23 handles anonymous vs authenticated mode correctly. However: ProtectedRoute.jsx:4 checks only localStorage flag (not actual token validity), tokens stored in localStorage (XSS-vulnerable), no Helmet/CSRF/rate limiting, no email verification or password reset, no refresh token rotation, and CORS hardcoded to localhost:5173 in server.js:14-17.
Poll Creation & Question Management6 / 15
CreatePoll.jsx (318 lines) supports dynamic add/remove questions and options with mandatory/optional toggle per question, anonymous/authenticated mode select, and datetime-local expiry picker. Server-side validation at pollRoutes.js:13-23 checks title, expiry, questions array non-empty, each question has text and min 2 options. Client-side validation at CreatePoll.jsx:92-119 validates title, expiry, question text, and min 2 valid options. Missing: no poll editing endpoint (PATCH /:pollId/publish only toggles isPublished, no question/option editing), no slug/permalink generation, no draft status, no form library (manual useState hooks).
Response Collection Flow7 / 15
Solid submission flow at responseRoutes.js:25-108: validates poll existence (line 31-33), expiry (lines 35-37), authenticated mode requirement (lines 39-43), answers array format (lines 45-47), duplicate responses for authenticated users (lines 49-59), mandatory question enforcement (lines 62-71), and option validity checking per question (lines 73-83). PublicPoll.jsx (147 lines) handles loading, expired, submitted-thank-you (lines 72-91), and active form states with client-side mandatory validation (lines 37-42). Missing: no duplicate prevention for anonymous users (no localStorage/IP tracking), no DB-level sparse unique compound index on {pollId, respondent}.
Analytics & Feedback Dashboard5 / 15
Analytics.jsx (144 lines) shows total responses, question count, published/draft status with per-question option breakdowns including vote counts, percentages, and CSS progress bars (lines 88-130). Backend analytics at responseRoutes.js:110-168 enforces creator-only access (lines 118-122) and computes per-question option counts via O(n*m) brute-force filter operations (lines 126-153). Results.jsx (104 lines) renders public published results identically. Dashboard.jsx (181 lines) shows total polls/responses/active polls stat cards. Missing: no time-series/trend data, no CSV export, no individual response viewing, no peak activity metrics, no MongoDB aggregation pipeline, no animated progress bars (CSS-only width), no leading option identification.
Frontend Experience5 / 10
Consistent indigo-themed UI across 7 routes with React Router DOM, Tailwind CSS, and react-hot-toast notifications. All pages handle loading, error, empty, and edge-case states (expired polls, published results). Dashboard has stat summary cards and poll listing with copy-link-to-clipboard. PublicPoll has proper state flow (loading→active→submitted). Issues: index.css contains leftover Vite starter boilerplate with hardcoded layout rules, no dark mode, no animations/transitions beyond hover effects, browser confirm() for delete instead of modal dialog, typo "rounded-4g" in Dashboard.jsx:146, window.location.href used instead of React Router navigate in Login.jsx:36 and Register.jsx:35.
Backend Architecture & API Design4 / 15
Clean Express route structure across 3 route files (445 lines total) with proper MongoDB/Mongoose schemas using embedded sub-documents (optionSchema, questionSchema) and timestamps. Poll schema supports responseMode enum and isPublished defaults. However: no input validation library at all (all manual checks), no Helmet or security middleware, CORS hardcoded to localhost:5173 (server.js:15-17), no .env.example file, raw error.message leaked to clients in every catch block (8 instances across routes), no request body size limits, no rate limiting, no service layer (all logic inline in route handlers), no centralized error handler, no pagination, socket.io-client listed as unnecessary backend dependency (backend/package.json:22), config/db.js mentioned in README but doesn't exist.
Real-Time Updates Using WebSockets3 / 10
Socket.IO integrated with Express HTTP server (server.js:34-54), deployment verified working via curl. Server emits "responseSubmitted" event on response submission at responseRoutes.js:92-96 with {pollId} payload. Analytics.jsx:23-33 listens for "responseSubmitted", filters by pollId, and triggers HTTP refetch via fetchAnalytics(). Socket cleanup in useEffect return (socket.off). Major limitations: global broadcast only (no room-based pattern), single event type (no events for publish/delete/update), pure invalidation signal (no analytics data pushed over socket), no debouncing, no socket authentication, no reconnection room re-join logic, socket only used on Analytics page (not PublicPoll page for live response counts).
Code Quality & Project Structure3 / 10
Clean frontend/backend separation with ESLint configured on frontend. Total 654 lines backend + 1,256 lines frontend. Significant issues: no TypeScript (all plain JS/JSX), no automated tests (backend test script explicitly errors out), dead code (mockPolls.js: 25 lines never imported), unused dependencies (recharts in root package.json and frontend package.json never imported in any component, socket.io-client unnecessarily in backend), no .env.example, no service layer separation, no config directory despite README claiming one, inconsistent React patterns (window.location.href instead of React Router navigate, window.location.reload() for logout), code typo "rounded-4g" at Dashboard.jsx:146, random <br/> tags scattered in JSX, no Prettier config.
122
Pulseboard
Pankaj Thapaliya · @pankaj485
38
Pulseboard is a working full-stack polling platform with JWT auth, multi-step poll creation, WebSocket-based voting, and basic analytics. The backend has solid architecture patterns (controller/service/DTO, Zod validation, Drizzle ORM) but lacks critical security middleware (Helmet, rate limiting) and has several bugs (string "false", logic bug in question controller, creator_id from request body). The WebSocket voting flow works but ignores the anonymous response_mode entirely and has no reconnection logic. Analytics are minimal (just 3 aggregate numbers). The frontend source code is absent from the repo (only minified build), which is a significant issue for code quality evaluation. Overall: a functional but incomplete implementation with good architectural foundations undermined by missing features and quality issues. Deployment verified working at http://13.206.107.210:8000/.
Authentication & Access Control6 / 10
Solid JWT auth with scrypt password hashing, dual token strategy (15min access + 7d refresh with SHA-256 hashing and DB-stored rotation), Zod-validated signup/signin/refresh endpoints, authenticate middleware with differentiated error codes (TOKEN_MISSING, TOKEN_EXPIRED, TOKEN_INVALID), frontend AuthContext with silent refresh via useApiFetch, and GuestRoute/AuthRoute wrappers. Critical gaps: anonymous/authenticated response_mode schema field defined but NEVER enforced (WebSocket always requires auth - websocket.ts:69-74), creator_id accepted from request body instead of extracted from token allowing impersonation (poll.controller.ts:21-23), tokens stored in localStorage (XSS risk), no Helmet, no rate limiting, no email verification, no password reset flow, no CSRF protection.
Poll Creation & Question Management5 / 15
Working multi-step poll creation wizard (React Hook Form + Zod) with dynamic questions/options, mandatory/optional toggle (is_required), correct answer marking (is_correct), and future-date expiry validation (poll.dto.ts:17-19). Server-side Zod validation on poll (poll.dto.ts:4-20), question (question.dto.ts:4-10), and option (option.dto.ts:4-17) schemas. Bulk option creation with duplicate prevention via 409 (option.service.ts:9-17). Major gaps: NO poll editing endpoint (only PATCH for status, poll.route.ts:12-16), NO question/option edit after creation (question.route.ts only has POST), NO poll deletion, creator_id must be in request body instead of token, N+1 API calls for creating questions sequentially in frontend, no slug generation.
Response Collection Flow4 / 15
WebSocket-only voting flow (no REST endpoint). join_poll validates token, poll existence, joining_code matching, and status="active" (websocket.ts:63-133). cast_vote validates question belongs to poll and option belongs to question, prevents duplicate votes per-question and per-poll (websocket.ts:135-208). Vote stored in responses table. Critical gaps: anonymous response_mode completely ignored — ALL voting requires authenticated token (websocket.ts:69-71), no expires_at timestamp comparison at vote time (only status check), server does NOT validate that all required questions are answered, no DB-level unique constraint on {question_id, user_id} for duplicate prevention, question controller has logic bug checking `if (!questionService)` instead of `if (!data)` (question.controller.ts:22-24).
Analytics & Feedback Dashboard4 / 15
GET /poll/:id/insights returns only total_votes, correct_votes, wrong_votes via SQL aggregation (poll.service.ts:136-154). Frontend AnalyticsModal shows these 3 stats in color-coded cards. GET /poll/questions/:id returns questions with nested options via json_agg (poll.service.ts:156-178). Large gaps: NO per-question vote breakdown, NO per-option counts in analytics, NO trend/chart data, no CSV export, no individual response viewing, no participation rate metrics, no peak activity tracking, analytics endpoint has NO ownership check (any authenticated user can access any poll's insights), no published results page for public viewing (FAILs requirement that anyone should see results after publishing), no dashboard page (only modal overlay on homepage).
Frontend Experience5 / 10
React 19 SPA with Router 7, Tailwind CSS 4, custom design tokens (mint/sage/cream/rose). Features: multi-step poll creation wizard, WebSocket voting UI with progress bar and question-by-question flow, analytics/configure modals, AuthContext with silent refresh, copy-to-clipboard, loading/error/empty states, responsive layout, route guards (GuestRoute/AuthRoute). Weaknesses: frontend SOURCE CODE is NOT in the repo (only minified build in public/frontend/), HTML title is "frontend", API URLs hardcoded to deployment IP, no toast notifications, no skeleton loaders, no confirmation dialogs, no dark mode toggle, no 404 page (redirects to /), no animations beyond CSS transitions, no offline/PWA support. UI is functional but unpolished.
Backend Architecture & API Design5 / 15
Clean modular architecture: controller→service→DTO→route separation across auth/poll/question/option domains with consistent patterns. Zod validation on all endpoints via BaseDto abstract class + validatePayload middleware (shared/validate.ts). Drizzle ORM with PostgreSQL migrations (4 migration files). JWT middleware with discriminated VerifyResult types. Key issues: no Helmet security headers, no rate limiting, no request body size limits, no input sanitization, `success: "false"` as string (not boolean) in 2 poll controller error responses (poll.controller.ts:13,43), env.ts and drizzle.config.ts both hardcode `.env.example` path instead of `.env`, refresh_token_hash initialized as empty string on signup (auth.service.ts:69), poll controller has unused import `z, { number }`, question controller has logic bug checking service instance instead of query result (question.controller.ts:22), no centralized error handler, no tests.
Real-Time Updates Using WebSockets5 / 10
Working WebSocket server using `ws` library (not Socket.IO as claimed). Room-based pattern with Map<pollId, Set<WebSocket>> for broadcasting (websocket.ts:36-38,44-53). Full Zod validation chain on messages (join_poll and cast_vote schemas, websocket.ts:9-28). Per-message handling: token verification, poll/code validation, duplicate vote prevention at both question and poll levels, vote_cast confirmation, vote_update broadcast with per-question counts. Client state tracking with disconnect cleanup (websocket.ts:210-216). Gaps: no reconnection/resume logic, no ping/pong heartbeat, no debouncing on broadcasts, anonymous mode completely ignored (always requires auth), vote_update only broadcasts single-question counts, no room leave event from client, no authentication on raw WS connection, no fallback to REST for responses.
Code Quality & Project Structure4 / 10
TypeScript strict mode with noUncheckedIndexedAccess and exactOptionalPropertyTypes (tsconfig.json:24-25,36). Clean module-based folder structure with consistent naming. Comprehensive README (601 lines with architecture, API reference, WebSocket protocol docs). Issues: ZERO automated tests (package.json:8), frontend source code NOT in repo (only minified build), 32 console.log statements across production code, `as any` type assertion in seed file (db.seed.ts:55), non-null assertions in seed file (db.seed.ts:27,60,97), typo in filename `optoin.route.ts`, `success: "false"` string literal bug in poll controller, logic bug in question controller (checks service class instead of data), .env.example hardcoded as config path in 2 files, no ESLint/Prettier config, no git hooks, refresh_token_hash is hardcoded dummy string in seed file (db.seed.ts:14), unused import in poll controller.
123
Poll.io
ARKAPRAVA CHAKRABORTY · @arkapravachakraborty
37
Poll.io is a full-stack polling app (Next.js frontend + Express/PostgreSQL backend) with ~2,500 lines of hand-written TypeScript/TSX. It delivers a working end-to-end flow: email OTP registration, Google OAuth, JWT auth, dynamic poll creation with React Hook Form, response collection with proper state handling (expired/closed/published/auth-required), basic analytics with progress bars, publish/close lifecycle, and minimal Socket.IO real-time signaling. However, the implementation is shallow across all dimensions — no input validation library, no duplicate vote prevention, no poll editing, no data export, N+1 queries, localStorage token storage, `req: any` everywhere, hardcoded localhost URLs, and unchanged "Create Next App" metadata. The project works but lacks the depth, security, and polish expected for a production-style full-stack application. Total score: 37/100.
Authentication & Access Control5 / 10
Registration with email OTP verification (10-min expiry, Nodemailer+Gmail) at backend/src/routes/auth.ts:141-213, login with bcrypt at auth.ts:50-72, Google OAuth via Google OAuth2 server-side verification at auth.ts:77-127, JWT middleware at middleware/auth.ts:6-31, account soft-delete at auth.ts:216-245. Frontend auth guards on create and dashboard pages via localStorage token check. Major gaps: JWT stored in localStorage (XSS-vulnerable), no CSRF/Helmet/rate limiting, weak ownership verification (isOwner just checks token existence, not actual creator matching — poll/[pollId]/page.tsx:45-48), userId passed in request body by clients rather than extracted from token (security anti-pattern), no refresh token rotation.
Poll Creation & Question Management5 / 15
React Hook Form with useFieldArray for dynamic questions and nested options (create/page.tsx:59-62, OptionsList at lines 256-294), three question types (single/multiple/text), mandatory/optional toggle per question, anonymous/authenticated poll toggle, expiry datetime-local picker. Server-side transaction-based creation with rollback (polls.ts:15-54). Client-side validation via react-hook-form required rules. Critical gaps: no poll editing endpoint exists (polls can't be modified after creation), no slug/permalink generation for sharing, no server-side validation of question/option structure (accepts whatever frontend sends), no draft status, no max question limit enforced. The hackathon specifies single-option only but the implementation supports multiple select too.
Response Collection Flow5 / 15
Client-side mandatory question validation at vote/[pollId]/page.tsx:31-39 and 55-61. Backend rejects expired, closed, and published polls (polls.ts:96-99) and enforces anonymous/authenticated mode (polls.ts:101-103). Transaction-based submission at polls.ts:106-119 with Socket.IO new_vote emit at line 122-123. Comprehensive UI state coverage: success page (vote/page.tsx:96-109), voting closed (lines 122-134), login required (lines 136-145), results published (lines 111-120), not found. Critical gaps: no duplicate vote prevention (no unique index on {pollId, userId}, no localStorage tracking), no server-side mandatory question validation, no validation that submitted optionIds actually belong to the poll, userId passed from client request body instead of token extraction, no per-question single-option enforcement server-side.
Analytics & Feedback Dashboard5 / 15
Stats endpoint computes totalVotes and per-question option counts with percentages (polls.ts:161-198). Text responses collected and displayed (poll/[pollId]/page.tsx:252-265). Frontend shows stat cards (total responses, poll state, mode) at lines 208-234, progress bars with percentage labels at lines 267-283, and status badges (Active/Ended/Published). Publish lifecycle with confirmation dialog (poll/page.tsx:56-68), close/reopen toggle (lines 70-82). Share button copies voting link. Critical gaps: brute-force N+1 queries in stats (nested Promise.all over questions × options), no CSV/JSON export, no individual response viewing, no trend/time-series data, no anonymous vs authenticated breakdown, weak isOwner check (just localStorage token, lines 45-48), poll state stat card always shows "Real-time" (decorative, not computed), no chart library used.
Frontend Experience6 / 10
Clean, consistent dark/light aesthetic with Tailwind CSS across 8 pages (landing, login, register, verify, create, dashboard, poll analytics, vote). Comprehensive state coverage: loading spinners at dashboard/page.tsx:83 and poll/page.tsx:93-100, error banners at login/page.tsx:79-83, empty states at dashboard/page.tsx:112-115, success state at vote/page.tsx:96-109. Responsive layout with mobile-aware navigation (page.tsx:58-63). OTP input with monospace font and letter-spacing (verify/page.tsx:85-94). Confirmation dialogs for destructive actions (dashboard/page.tsx:50,68). Drawbacks: layout.tsx:16-17 still has "Create Next App" metadata title (not updated to "Poll.io"), all API URLs hardcoded to localhost:4000 in 6+ locations across 4 files, browser alert() used for copy/error feedback, no skeleton loading states, dark mode CSS media query in globals.css:15-19 is completely unused by the UI, no persistent navigation across pages.
Backend Architecture & API Design4 / 15
Clean layered architecture: index.ts (Express+Socket.IO setup) → routes/ (auth.ts 247 lines, polls.ts 255 lines) → db/schema.ts (76 lines, 6 Drizzle pgTable definitions). PostgreSQL+Drizzle ORM with 6 evolution migrations tracking schema history (drizzle/ dir). Transaction-based operations for poll creation and vote submission. Proper cascading deletes with referential integrity. Appropriate HTTP status codes (400/401/403/404/500). Critical gaps: no input validation library (no Zod, Joi, or express-validator — all validation is ad-hoc property checks), CORS set to `*` on both Express (index.ts:12) and Socket.IO (index.ts:25), no Helmet security headers, no rate limiting or body size limits, N+1 queries in stats endpoint (SELECT * FROM option_table without pollId filter at polls.ts:143, nested Promise.all at lines 169-191), `req: any` type used throughout all route handlers defeating TypeScript strict mode, hardcoded non-null env var assertions (JWT_SECRET!, DATABASE_URL!), no .env.example file, console.log in production code (index.ts:30, auth.ts:28), no centralized error handler or graceful shutdown.
Real-Time Updates Using WebSockets3 / 10
Socket.IO server integrated with Express HTTP server at index.ts:23-36. Room-based pattern: client emits join_poll to subscribe to poll-specific updates (index.ts:32-34). Server emits new_vote event on vote submission at polls.ts:122-123. Client connects via singleton io("http://localhost:4000") at poll/[pollId]/page.tsx:20, joins room and listens for new_vote to trigger HTTP refetch at lines 84-91. Visual live sync indicator (green pulsing dot) at poll/page.tsx:121-124. However, this is the bare minimum: only one event type (new_vote), no data pushed over socket (pure invalidation signal requiring full HTTP refetch), no reconnection handling or room re-join logic on disconnect, no leave_poll emitted on component unmount, no Socket.IO integration on the public voting page, no events for poll close/publish/delete state changes, no socket authentication, no debouncing on client-side (every vote triggers immediate refetch), server-side socket handling is minimal (just console.log on connection/join).
Code Quality & Project Structure4 / 10
Clean monorepo structure with separate backend/ and frontend/ directories. TypeScript used throughout, with strict mode, noUncheckedIndexedAccess, and exactOptionalPropertyTypes in backend tsconfig.json. Drizzle ORM with proper migration-based schema management. React Hook Form with useFieldArray for dynamic forms. Next.js App Router conventions. README with setup instructions and testing flow. However, code quality is undermined by: pervasive `req: any` type usage in all route handlers (defeating strict TypeScript), no automated tests in either project, "Create Next App" metadata title/description left unchanged in layout.tsx:16-17, all API URLs hardcoded to localhost:4000 in 6+ client locations, no .env.example files in either backend or frontend, 30 lines of commented-out code in auth.ts:17-47, dark mode CSS in globals.css:15-19 that's never used by any component, no ESLint config in backend, console.log in production code, inconsistent version targets (backend TS 6, frontend TS 5), next.config.ts is bare/default with no configuration.
124
PollSphere
Md Mudassir Akhter · @mudassir-jmi_0f020505
37
PollSphere is a functional full-stack polling platform with registration/login JWT auth, dynamic poll creation (React Hook Form + useFieldArray), response submission with expiry handling and required question validation, analytics with Recharts bar/pie charts, Socket.IO live updates, and a visually polished glass-morphism UI. Frontend deployed on Vercel and accessible. However, the submission has significant gaps: no poll editing, missing option-text validation on response submission (any string accepted), no unique database index for duplicate prevention, hardcoded MongoDB credentials in committed .env, no TypeScript, zero tests, heavy UI code duplication across pages, inconsistent error/loading patterns, and a minimal WebSocket implementation covering only one page. Overall a decent working prototype with real security and engineering quality issues.
Authentication & Access Control5 / 10
Working auth system: bcrypt 10-round hashing, JWT token generation (7d expiry), protect middleware (server/src/middleware/auth.middleware.js:1-39), optionalAuth middleware for mixed auth/anonymous endpoints, AuthContext with login/logout/loading (client/src/context/AuthContext.jsx:1-61), ProtectedRoute component, Axios interceptor auto-attaching JWT, post-login redirect pattern, and anonymous/authenticated poll modes enforced at both client (PollPage.jsx:52-55) and server (poll.controller.js:111-117). Gaps: no refresh tokens, token in localStorage (XSS-vulnerable), no Helmet/rate-limiting/CSRF, no email verification, password minlength 6 only, forgot-password link is dead end (/forgot-password route doesn't exist), no httpOnly cookies, protect middleware doesn't handle deleted-user-with-valid-JWT edge case.
Poll Creation & Question Management5 / 15
Functional poll creation: React Hook Form with useFieldArray for dynamic questions (CreatePoll.jsx:270-276, QuestionBlock.jsx:9-16), mandatory/optional toggle per question, anonymous/authenticated toggle (lines 150-184), expiry datetime picker, add/remove questions and options with minimum 2-option guard (QuestionBlock.jsx:117). Server validates title/questions/expiry are present (poll.controller.js:18-26) with Mongoose schema enforcing >=1 question and >=2 options/question (poll.model.js:24-29,61-64). Gaps: NO poll editing endpoint (only PATCH is /:id/publish which just flips isPublished=true — poll.routes.js:31), no slug/permalink (uses raw ObjectId), alert() instead of toast for errors (CreatePoll.jsx:54), no client-side expiry validation, no submit loading state on create button, no maximum limits on questions/options.
Response Collection Flow5 / 15
Working response submission: server validates poll existence (l.96), expiry (l.104), anonymous/authenticated gate (l.112), duplicate prevention for authenticated users via findOne (l.120-132), required questions (l.134-148). Client validates required questions with toast (PollPage.jsx:97-106). Multiple UI states handled: loading, not found, expired, submitted success, auth-required redirect. Socket emit on submission (l.163-170). Critical gaps: NO option-text validation — submitted selectedOption is never checked against question.options[] (any string accepted, causing phantom analytics entries), NO question ID validation against poll.questions[], NO unique index on {poll, respondent} (race condition), NO anonymous duplicate prevention (no localStorage/cookie/IP tracking), NO isPublished check (responses accepted after publishing), no per-question single-answer enforcement.
Analytics & Feedback Dashboard5 / 15
Analytics with Recharts bar charts showing per-question option counts with percentages and progress bars (AnalyticsPage.jsx:213-316, 331 lines). Published results with donut/pie charts and "Leading" badge (ResultsPage.jsx:169-271, 288 lines). Dashboard with 4 stat cards (derived client-side), poll list grid, search bar, and 4 filter tabs (All/Active/Expired/Published) (Dashboard.jsx:154-341, 347 lines). Empty states on all three pages. Live socket updates on analytics page. Publish flow: PATCH /:id/publish flips isPublished=true (poll.controller.js:269-313). Gaps: NO authenticated vs anonymous breakdown (model has fields but analytics controller ignores them), NO time-series/trend data, NO CSV/PDF export, NO response counts on dashboard poll cards (getMyPolls returns no counts), O(n*m) manual aggregation loops (no MongoDB aggregation pipeline), analytics/results aggregation logic duplicated verbatim (poll.controller.js:219-255 vs 349-381), no confirmation dialog before publishing, ResultsPage claims "real-time" but has zero socket code.
Frontend Experience5 / 10
Visually polished with consistent dark/glass-morphism theme across all pages (backdrop-blur, translucent cards). Custom landing page with 5 sections (HomePage.jsx:325 lines). Recharts bar and donut charts. Password strength meter with color-coded segments (Register.jsx:47-60). Password visibility toggle. 9 React Router routes with appropriate public/protected gating. Responsive design with sm/md/lg breakpoints (~107 responsive class usages). Toast notifications via react-toastify. Gaps: ONLY 2 shared components (Loader, QuestionBlock) — background orbs/grid-texture duplicated across 7+ pages, 6 different loading patterns (plain text, custom dots, Loader component, CSS spinner), alert() vs toast inconsistency (CreatePoll.jsx:54), no mobile hamburger menu on HomePage, background colors vary across 5 hex values, dead forgot-password link, no inline form validation errors (browser defaults only), no Framer Motion transitions, no error boundaries.
Backend Architecture & API Design4 / 15
Working MVC architecture with route→controller→model separation (server/src/routes/, controllers/, models/). JWT auth middleware (protect + optionalAuth patterns). Consistent JSON response format {success, message, data}. Mongoose schemas with embedded subdocuments. Socket.IO singleton pattern (getIO()). RESTful API with 8 endpoints. Critical gaps: NO input validation library (no Joi/Zod/express-validator — all manual ad-hoc checks), NO rate limiting, NO Helmet security headers, raw error.message exposed to clients in 8 catch blocks, MongoDB Atlas credentials + JWT secret hardcoded in committed server/.env, NO service layer (controllers handle everything), NO cascade delete of responses when poll deleted, NO pagination on getMyPolls, NO request body size limits, NO input sanitization middleware, deleted commented-out code in deletePoll (poll.controller.js:433-441), Socket.IO CORS hardcoded separately from Express CORS, server/package.json description says "pulseboard" (stale project name).
Real-Time Updates Using WebSockets4 / 10
Socket.IO implemented with room-based pub/sub: server attaches to HTTP server (server.js:50-52), clients join rooms via "join_poll" event (socket.js:21-24), server emits "response_submitted" on new response with {pollId, responseCount} payload (poll.controller.js:163-170). Only AnalyticsPage consumes sockets — joins room on mount, listens for "response_submitted", triggers full HTTP refetch (AnalyticsPage.jsx:47-62). Works end-to-end. Gaps: signal+refetch pattern only (no real data pushed, client ignores payload and refetches everything), singleton socket connects at module level even on pages that don't use it (socket.js:10), NO socket authentication, NO room leave mechanism (sockets accumulate server-side), NO reconnection handler (no re-join rooms after disconnect), only 1 event type emitted, no real-time on PollPage/Dashboard/ResultsPage, ResultsPage footer falsely claims "Results update in real-time" with zero socket code (ResultsPage.jsx:281).
Code Quality & Project Structure4 / 10
Clean client/server separation with 8 meaningful git commits. Consistent file naming (kebab-case with type suffixes on server, PascalCase pages on client). React Hook Form used correctly with useFieldArray. Axios interceptor for token attachment. Informative README (294 lines) with API table, tech stack, setup, screenshots. Gaps: NO TypeScript, ZERO tests, NO .env.example files, hardcoded MongoDB Atlas credentials committed to git (server/.env), NO Prettier, NO ESLint on server, indentation inconsistent across files (2-space, 4-space mixed), PALETTE array duplicated in AnalyticsPage/ResultsPage, background ornaments duplicated across 7+ pages (~68 usages), auth form logic duplicated between Login/Register, commented-out dead code in 5 files (poll.controller.js:433-441, PollPage.jsx:31-41, App.jsx:18, socket.js:1-5, CreatePoll.jsx:50), 6 different loading state patterns, console.log in production (AnalyticsPage.jsx:55), unused imports (Navigate in App.jsx, cat404 in NotFound.jsx), README references utils/ dirs that don't exist, stale package.json description.
125
PulseBoard
Mukul Sharma · @mukulsharma1594_9cbc7f78
37
PulseBoard is a functional but incomplete polling platform with 5,874 lines of JS/JSX. Both deployments work (frontend on Vercel, backend on Render). The project demonstrates real effort with Google OAuth, socket.io real-time updates, a clean UI with Tailwind CSS, and proper Mongoose schema design. However, it has significant gaps: email/password auth is dead code (not connected to routes), the response validator file is empty, the published results page is entirely hardcoded mock data, the analytics page doesn't use the backend analytics service, and the register page is a decorative form with no API calls. The codebase has good MVC architecture on the backend but suffers from dead files, excessive whitespace, and no TypeScript or tests. Overall score: 37/80, placing it in the average range.
Authentication & Access Control5 / 10
Google OAuth via Passport.js works (auth.routes.js:11-91), JWT auth middleware verifies Bearer tokens from env.JWT_SECRET (authMiddleware.js:7-69), ProtectedRoutes component checks localStorage token (ProtectedRoutes.jsx:7-14). However, register/login controllers/services exist (auth.controller.js, auth.service.js) but are NOT connected to any routes — auth.routes.js only has Google endpoints. Register page (Register.jsx) is a decorative form with no API calls. Login page (Login.jsx) only offers Google OAuth button. Dead code: registerValidator, loginValidator, registerController, loginController, registerUserService, loginUserService are all orphaned. No Helmet, no CSRF, no rate limiting. JWT in localStorage (XSS risk). CORS set to wildcard origin.
Poll Creation & Question Management5 / 15
CreatePoll.jsx (865 lines) has dynamic questions/options with add/remove, title/description fields, expiry with DatePicker + 5 quick-preset buttons (5min/15min/30min/1Day/7Days), and response mode selector (anonymous/authenticated/both). Client-side validation checks title, question text, min 2 options. Server-side createPollValidator checks title + min 1 question (poll.validator.js:5-44), createPollService validates min 2 options per question (poll.service.js:28-68). Gaps: no isRequired/mandatory toggle in UI (all questions hardcoded to required:true), no poll editing endpoint exists (only create/read/publish), no slug/QR generation in creation flow, responseMode mapping is client-side only (server just stores allowAnonymous/allowAuthenticated booleans), no delete endpoint.
Response Collection Flow4 / 15
PollForm.jsx (356 lines) renders questions with radio buttons, validates required questions client-side before submission. Server-side checks poll existence, expiry date, and prevents duplicate responses for authenticated users (response.service.js:19-138). Vote counting works by incrementing embedded option.votes in the poll document. However: response.validator.js is an EMPTY file (0 lines) — no server-side validation of submitted answers exists. The response route has NO auth middleware and does NOT enforce poll.allowAnonymous/allowAuthenticated modes at all. No per-question single-option enforcement server-side. Missing validation that submitted option texts actually belong to the poll's questions. The route is completely open — any request body is accepted.
Analytics & Feedback Dashboard4 / 15
Analytics page (Analytics.jsx, 501 lines) uses GET /polls/:id to get poll with embedded vote counts — it does NOT call the backend analytics service (analytics.service.js:153 lines, computes totalResponses, anonymous/authenticated counts, per-question percentages). The backend analytics endpoint GET /api/polls/analytics/:pollId exists and is auth-protected but is unused by the frontend. Per-question pie charts via Recharts with winner detection, progress bars, and color coding are present. Dashboard (Dashboard.jsx) shows card grid with total/active/expired poll counts but hardcodes Responses to "0". PublishedResults (PublishedResults.jsx, 305 lines) is ENTIRELY hardcoded dummy data — no API calls, no dynamic data. No CSV/export, no individual response viewing, no trend/time-series data, no peak activity metrics.
Frontend Experience5 / 10
Modern UI with Tailwind CSS v4, Framer Motion animations (Home.jsx hero section), Poppins font, consistent orange-themed neobrutalist design (thick borders, hard shadows throughout). Responsive grid layouts, mobile-friendly. Loading spinners appear on Analytics and PollForm, empty state on Dashboard ("No Polls Yet"), not-found state on Analytics. Navbar adapts to auth state. Copy-to-clipboard (PollCard.jsx:20-29), share via Web Share API (PollCard.jsx:82-103), QR code display (PublishedResults.jsx). However: Register page is a decorative form with no API integration (Register.jsx:60-115 has form elements but no onSubmit handler calling the API), Login page only has Google OAuth (no email/password), PublishedResults is entirely hardcoded mock data, the analytics page doesn't call analytics endpoints. No toast notifications (uses basic alert()), no form library (manual useState).
Backend Architecture & API Design5 / 15
Clean module structure with auth/poll/response/analytics modules, each with controller/service/model/validator/routes separation. ApiError class with static factory methods (badRequest/unauthorized/forbidden/notFound at ApiError.js:14-38), ApiResponse wrapper (ApiResponse.js), async error middleware (errorMiddleware.js:1-9). Mongoose schemas with proper embedded sub-documents (poll.model.js:61-156 embeds questionSchema + optionSchema with validators), User model with unique email index (user.model.js:11-16). Socket.IO integrated with HTTP server (server.js:22-41). Gaps: auth register/login routes not connected (dead code), response.validator.js empty (0 bytes), socket.js config file empty (0 bytes), CORS set to origin:"*", no rate limiting, no helmet, no input sanitization, no request body size limits, JWT secret directly from env without fallback validation, no centralized config, no .env.example file, auth.model.js is a duplicate of user.model.js (same schema but stale), no TypeScript.
Real-Time Updates Using WebSockets5 / 10
Socket.IO server with room-based pattern: clients join poll-specific rooms via join_poll event (poll.socket.js:16-29). Server emits vote_updated with full poll data on response submission (response.service.js:115-123). Client socket connects to backend URL (socket.js:3-7), joins room on Analytics page mount (Analytics.jsx:75-78), listens for vote_updated to update UI (Analytics.jsx:80-89), cleans up listener on unmount (Analytics.jsx:91-97). Implementation is functional but limited: only covers analytics page (not poll participation page for live response counts), no debouncing on client, no reconnection/room-rejoin logic, room leave events absent, no authentication on socket connections, the empty socket.js config file (common/config/socket.js:0 bytes) suggests abandoned socket configuration approach, no typed events. Effectively a basic invalidation signal — server pushes full poll object, client re-renders.
Code Quality & Project Structure4 / 10
Monorepo structure with frontend/backend separation and clear module organization. Consistent naming conventions (*.controller.js, *.service.js, *.routes.js). ESLint configured for frontend. However: No TypeScript across the project (all plain JS). Excessive vertical whitespace throughout — nearly every line is double-spaced, inflating line counts artificially. Multiple empty/dead files: response.validator.js (0 bytes), common/config/socket.js (0 bytes). auth.model.js is a stale duplicate of user.model.js — auth.service.js imports from auth.model.js while passport.js imports from user.model.js (same schema, different files). Register/login services are dead code (not connected to routes). PublishedResults.jsx is entirely hardcoded mock data (305 lines with no API calls). No tests anywhere. No .env.example. Backend has no ESLint. console.log left in production (poll.controller.js:24,50, etc.). The README claims MongoDB but the submission meta claims Drizzle/Postgres — inconsistent. No .gitignore at project root.
126
B
Pulseboard
bipin bhatt · @bipinbhatt280
37
Pulseboard is a functional full-stack polling platform with a deployed Vercel frontend, 25 backend API endpoints, JWT authentication with email verification, Socket.IO real-time updates, and an analytics dashboard. The project demonstrates good architectural instincts (layered backend, DTO validation pattern, transactions) and covers the core polling workflow end-to-end. However, it is held back by several critical bugs (broken refresh token flow, ApiResponse.success() undefined, route parameter mismatch), missing security infrastructure (no Helmet, no rate limiting), and engineering gaps (no README, no tests, no TypeScript). The mandatory/optional question feature — a core requirement — is not implemented despite a UI badge. Total: 37/100.
Authentication & Access Control5 / 10
Registration (bcrypt 10 rounds), login with JWT dual-token, email verification, forgot/reset password, authenticate + optionalAuthenticate middleware (auth.middleware.js), frontend Axios interceptor with 401 retry (api.js:21-44), anonymous vs authenticated poll modes enforced (allowAnonymousResponse flag). Critical gap: refresh token flow is broken — frontend sends token in request body (authService.js:26) but backend reads from req.cookies.refreshToken (auth.controller.js:17) which is never set by login endpoint. No Helmet, no rate limiting, tokens in localStorage, console.log of MongoDB URI (server.js:14).
Poll Creation & Question Management4 / 15
Poll creation with title, duration (value+unit via React Hook Form), anonymous toggle. Sequential flow: CreatePoll.jsx → AddQuestion.jsx → AddOption.jsx. Joi validation for all three creation DTOs. Major gap: question model has no isRequired field (question.model.js — only text and poll), so mandatory/optional question marking is non-functional despite UI badge in PollVote.jsx:207-209. No poll editing/deletion endpoints, no question editing/deletion, no draft status, no slug/permalinks. Questions/options must be created one at a time through separate pages rather than inline in a unified builder.
Response Collection Flow6 / 15
Comprehensive server-side validation in vote.service.js: poll existence (line 13-14), expiry check (line 16-18), question existence (line 20-21), option existence (line 23-24), option-to-question relationship (line 27-29), duplicate prevention via compound unique sparse indexes + explicit findOne (lines 34-37). MongoDB transaction for atomic vote+count (lines 41-54). Anonymous voting via crypto.randomUUID() in localStorage (PollVote.jsx:108-113). Frontend handles 6 distinct UI states: loading, expired+unpublished, expired+published (final results), active+published (live results+voting), active+unpublished (voting only), submitted thank-you. Gap: no mandatory question enforcement server-side since questions lack isRequired field.
Analytics & Feedback Dashboard5 / 15
Analytics endpoint returns totalVotes, authenticatedVotes, anonymousVotes, per-question and per-option breakdowns with percentages and auth/anon splits (poll.service.js:78-135). Dashboard.jsx (294 lines) shows stat cards (Total/Authenticated/Anonymous/Questions), per-question option bars with visual fills and winner highlighting, poll management sidebar with pagination. Publish flow with "Go Live" confirmation modal (keep votes open vs close). Published results visible on public poll page (PollVote.jsx:175-196). Gaps: N+1 query pattern in analytics computation, no time-series/trend data, no CSV export, no individual response viewing, no peak activity metrics, no drop-off tracking.
Frontend Experience5 / 10
11 TanStack Router routes covering home, login, register, verify-email, dashboard, polls browse, poll vote, create poll, add question, add option, and 404. React Hook Form on all forms. Multiple UI states handled: loading (Loader component), errors (react-toastify), empty states, auth-required redirect (Dashboard.jsx:54-57), expired/active/published/submitted poll states. Countdown timer component (Countdown.jsx, 53 lines). Custom CSS design system across 10 stylesheets. Gaps: no README.md at project root, CSS-only (no UI library), no loading skeletons, console.log debug output in production (App.jsx:23, CreatePoll.jsx:28, AddQuestion.jsx:22), no dark/light toggle, Vite starter README in frontend/ is default template content.
Backend Architecture & API Design5 / 15
Clean layered architecture (routes→controllers→services→models) with DTO+Joi validation pattern via BaseDto (base.dto.js) and validate middleware. ApiError class hierarchy with factory methods. Centralized global error handler (error.middleware.js). MongoDB transactions in vote.service.js. Composite unique sparse indexes on Vote model. Critical bugs: ApiResponse.success() called but doesn't exist (option.controller.js:12,18 — endpoints crash at runtime), question route param mismatch (route uses :pollid, controller reads req.params.id — getQuestionsByPoll receives undefined), ApiError.forbidden() returns HTTP 412 instead of 403 (api-error.js:21). No Helmet, no rate limiting, no .env.example, 3 empty middleware files, missing indexes on Poll.user/Question.poll/Option.question.
Real-Time Updates Using WebSockets4 / 10
Socket.IO server initialized with HTTP server (socket.js), room-based pattern with client joining pollId rooms. Server emits vote:new event on each vote (vote.controller.js:12-16). Client connects on dashboard analytics view, emits join:poll on connect, listens for vote:new to trigger HTTP refetch (Dashboard.jsx:74-97). Proper cleanup on unmount (disconnect + remove listeners). Gaps: only one event type, no data pushed (pure signal + HTTP refetch), no reconnection handling (doesn't re-join room), no debounce, no socket auth, no events for publish/expiry/delete, no integration on public voting page for live counts.
Code Quality & Project Structure3 / 10
Clean frontend/backend separation with module-based organization. Backend follows consistent layering across all 5 modules. Frontend uses TanStack Router file-based routing. However: no README.md at project root (frontend/README.md is unmodified Vite starter), zero TypeScript, zero tests, 3 empty middleware files (dead code), filename typo (vote.middlware.js), function name typo (getPollAnylytics throughout), response message typos ("fetehed", "Vout"), console.log debug output in production across 5+ files, unused import (cookieParser in auth.service.js), auth middleware ~80% duplicated between authenticate and optionalAuthenticate, no .env.example, no linting config.
127
K
Poll Baba - Realtime Polling Platform
KRISHNA KUMAR SINGH SOLANKI · @krishnasinghsolanki05
36
Poll Baba is a working MERN-stack polling platform deployed at poll-platform-alpha.vercel.app with 9 pages, 11 API endpoints, Socket.io real-time updates, and Recharts visualizations. The app covers the core polling flow end-to-end (create → share → respond → analyze → publish). However, every dimension has significant gaps: the anonymous/authenticated response mode exists in the UI and schema but is never enforced server-side; there's no poll editing or deletion; no duplicate vote prevention; analytics stat cards show misleading hardcoded values; no security middleware (Helmet, rate limiting); error messages leak to clients; massive code duplication (Login/Register ~85% copy, analytics logic duplicated); 281 lines of inline CSS in Home.jsx; and zero automated tests. The deployment works but the engineering quality is shallow — "happy path only" across most dimensions.
Authentication & Access Control4 / 10
Register/login with bcrypt (10 rounds) and JWT (7d) exist (authController.js:1-119). Auth middleware extracts Bearer token, verifies JWT, attaches req.user (authMiddleware.js:5-43). ProtectedRoute checks localStorage for token (ProtectedRoute.jsx:1-14). GET /api/auth/me returns user profile. CRITICAL GAPS: responseMode (anonymous/authenticated) is NEVER enforced server-side — the submitResponse controller unconditionally sets userId: req.user?.id??null regardless of the poll's responseMode setting (responseController.js:48-53). Token stored in localStorage (XSS-vulnerable, Login.jsx:24, Register.jsx:24). ProtectedRoute never validates token with server — just checks truthiness. cookie-parser middleware imported but entirely unused (app.js:3,24). No rate limiting, Helmet, CSRF, email verification, or password reset. No server-side logout endpoint — only client-side localStorage.clear().
Poll Creation & Question Management4 / 15
CreatePoll.jsx (550 lines) supports dynamic questions with add/remove (min 1 enforced), dynamic options per question with add/remove (min 2 enforced via toast), responseMode dropdown (anonymous/authenticated), and expiry datetime-local picker. Server validates title+questions not empty (pollController.js:17-22). CRITICAL GAPS: No poll editing endpoint whatsoever — PATCH/PUT only exists for publishing (pollRoutes.js:23), no DELETE endpoint. Individual questionText and option.text not validated server-side (empty text passes). responseMode field defined in schema (Poll.js:51-55) but NEVER enforced anywhere. No slug/permalink, no QR code, no draft status, no form library (manual useState management across 550 lines). The PollCard copy link uses hardcoded localhost URL (PollCard.jsx:16).
Response Collection Flow4 / 15
PublicPoll.jsx (356 lines) renders questions with numbered labels, required (*) indicators, clickable option buttons (purple highlight on selected), client-side required-field validation with toast, submitting state, and submitted thank-you screen. Server-side: poll existence check, expiry validation, required-question loop (responseController.js:12-44). Socket.io event emitted on submission (line 58-61). CRITICAL GAPS: No duplicate vote prevention — no unique compound index on {pollId,userId}, no localStorage tracking, no findOne check before create. No validation that submitted questionIds exist in the poll or that selectedOption values are real poll options — garbage data silently saved. responseRoute has NO auth middleware (responseRoutes.js:9), and responseMode enforcement is entirely absent. No per-question single-option enforcement at any level.
Analytics & Feedback Dashboard5 / 15
Analytics.jsx (671 lines): 4 stat cards (Total Responses, Questions, Status, Published), per-question Recharts BarChart with gradient fill, custom tooltip, animated bars (1500ms), and per-option breakdown cards with count + percentage + CSS progress bars. "Publish Results" button toggles isPublished (pollController.js:225-260). PublicResults.jsx (263 lines): total responses card, per-question Recharts PieChart (donut), per-option stat cards. ResultsHub.jsx (154 lines): gallery of published polls with vote counts. CRITICAL GAPS: analytics.isPublished is NEVER returned by the server (getPollAnalytics returns only {pollId,title,totalResponses,analytics}), so the Published stat card ALWAYS shows "No" regardless of actual state. "Poll Status" hardcoded to "Active" (never checks expiry). No trend data/time-series, no peak activity metrics, no individual response viewing, no CSV/export. Analytics computation is brute-force O(n*m) and duplicated between getPollAnalytics and getPublicResults (pollController.js:149-203 vs 303-377).
Frontend Experience6 / 10
9 client pages with react-router-dom routing (App.jsx:1-58), Framer Motion page-entry animations on Login/Register, Recharts for analytics (BarChart + PieChart), react-hot-toast notifications across all interactive pages, consistent dark theme with glassmorphism styling across dashboard and public pages, responsive layout (Sidebar collapses on mobile, DashboardLayout.jsx). Loading, error, empty, not-found, and submitted states handled on most pages. INTERACTIVE HOMEPAGE with scroll-reveal, count-up animations, cursor glow, mobile hamburger menu, feature cards, testimonials (Home.jsx, 798 lines). GAPS: 281 lines of inline CSS in Home.jsx (global-scoped, injected via `<style>` tag), Login/Register are ~85% duplicated (~200 lines each), no form library (imperative useState in CreatePoll), no data caching layer (no TanStack Query/React Query), PollCard hardcodes localhost URL, DashboardLayout's "My Polls" isn't scrolled into view when empty.
Backend Architecture & API Design4 / 15
Clean MVC separation: routes → controllers → models. Mongoose schemas with embedded sub-documents (Poll embeds Question→Option, Response embeds Answer). JWT auth middleware verifies and attaches user. CORS configured for localhost:5173 + CLIENT_URL env. Socket.io server with CORS. CRITICAL GAPS: No input validation library (no Joi/Zod/express-validator) — all validation is manual ad-hoc checks. No Helmet middleware. No rate limiting on any endpoint (including login). No request body size limits. Route ordering bug: GET /:id registered BEFORE GET /published/all (pollRoutes.js:25-31), so /api/polls/published/all matches /:id first (treats "published" as ObjectId). responseMode never enforced. Every catch block leaks error.message to clients (11 instances across controllers). cookie-parser imported but unused — dead dependency. No centralized error handler. No service layer — all logic in controllers. No pagination. No DELETE or PATCH (edit) routes. Response model imported inside getPublishedPolls function body redundantly (pollController.js:426-427, already imported at line 2).
Real-Time Updates Using WebSockets5 / 10
Socket.io server attached to HTTP server (server.js:9,16). Room-based pattern: clients emit "join_poll" to subscribe to poll-specific rooms (socket.js:20-22). Server emits "new_response" on response submission via getIO() singleton (responseController.js:56-61). Client connects (socket.js:1-10), joins room on Analytics mount, listens for "new_response" to refetch analytics + show toast, cleans up listener on unmount (Analytics.jsx:156-179). GAPS: Only one event type (new_response) — no events for poll publishing, expiry, or deletion. No data pushed over socket — pure invalidation signal (client re-fetches HTTP). No debouncing on client-side refresh. No socket authentication on connection. No reconnect room re-join logic (socket silently loses subscription on reconnect). No socket integration on PublicPoll page (only Analytics). Socket URL construction strips /api from VITE_API_URL (socket.js:3-8) — brittle pattern.
Code Quality & Project Structure4 / 10
Clean root-level client/server separation with reasonable folder structure (server: config/controllers/middleware/models/routes/; client: components/layout/pages/routes/services/). Consistent component naming, lucide-react icons throughout. GAPS: Massive code duplication — Login.jsx and Register.jsx share ~85% identical structure (~200 lines duplicated each including identical inputStyle function), analytics computation duplicated between getPollAnalytics and getPublicResults (pollController.js:149-203 vs 303-377), background orb patterns copy-pasted across 4 files. Home.jsx has 281 lines of inline CSS as a template literal string (lines 516-796) — unscoped, injected globally, persists on navigation. No TypeScript anywhere. Zero automated tests. No .env.example file. Minimal README (6 bullet points, no setup instructions, no env vars documented, placeholder "frontend url" and "backend url" links). Dead dependency (cookie-parser). Hardcoded localhost URL in PollCard.jsx:16. No ESLint on server side. Route ordering bug. No consistent error handler.
128
O
PollForge
Om Chandrakant Joshi · @om_joshi
36
PollForge is a functional but basic full-stack polling app (~1600 lines of JS). It implements JWT auth, multi-question poll creation, response collection, analytics with Recharts, result publishing, and Socket.io real-time updates. The frontend has a cohesive dark theme and handles multiple UI states. However, critical weaknesses significantly limit it: the backend uses in-memory arrays with no persistent database (despite claiming MongoDB), the JWT secret is hardcoded, tokens are in localStorage, there's no backend deployment (only the Vercel frontend works), polls cannot be edited after creation, anonymous duplicate vote prevention is missing, and there are no tests or TypeScript. The code is cleanly organized for its size but lacks production-readiness safeguards.
Authentication & Access Control4 / 10
JWT-based register/login with bcrypt (10 rounds) in server/routes/auth.js (64 lines). requireAuth/optionalAuth middleware (server/middleware/auth.js, 25 lines) properly verifies tokens and attaches req.user. Anonymous vs authenticated poll modes enforced server-side in polls.js lines 121-122 and 135-136. ProtectedRoute component (9 lines) guards client-side. Critical gaps: JWT_SECRET hardcoded as 'pollforge-secret-2024' fallback in auth.js:8 (also exported); tokens stored in localStorage (AuthContext.jsx:31,45); no refresh tokens, no httpOnly cookies, no Helmet, no CSRF, no rate limiting, no email verification or password reset. Score 4 for functional but insecure auth.
Poll Creation & Question Management5 / 15
CreatePoll.jsx (199 lines) supports dynamic questions with add/remove, per-question mandatory/optional toggle, per-option add/remove (min 2 enforced), poll-level title, description, anonymous toggle, and datetime expiry picker. Client-side validation checks title, question text, and min 2 options per question (lines 40-46). Server validates title+questions required and per-question text+options (polls.js:52-58). Gaps: questions cannot be edited after creation (no PATCH/update endpoint exists), no form library (individual useState hooks), no slug/permalink generation (raw UUID), no server-side expiry datetime validation. Score 5 for solid creation flow but missing edit capability.
Response Collection Flow5 / 15
PollRespond.jsx (280 lines) handles response form with radio button selection, client-side mandatory validation with inline error display (lines 113-118). Server validates poll existence, expiry (isPollActive), publishing state, and auth mode (polls.js:128-142). Duplicate prevention for authenticated polls via respondentId check (line 140). Covers 6 UI states: loading, login-required, expired, published/results, creator redirect, submitted success. Socket.io live count shown. Gaps: no duplicate prevention for anonymous polls (no localStorage/IP tracking), no validation that submitted optionIds belong to the question, no server-side per-question single-option enforcement, no cookie/session for anonymous dedup. Score 5 for decent flow but missing critical anonymous dedup.
Analytics & Feedback Dashboard5 / 15
PollAnalytics.jsx (239 lines) shows total responses, per-question option breakdowns with Recharts BarChart, mandatory/optional badges, answered/skipped counts, leader detection with percentage. Stat cards display total responses, questions count, avg completion %. Publishing via PATCH /polls/:id/publish with confirmation dialog. Published results publicly viewable via ResultsView component in PollRespond.jsx (lines 10-61). Dashboard shows per-poll response counts. Gaps: no CSV/export, no individual response viewing, no time-series or trend data, no peak activity metrics, analytics are computed brute-force via computeAnalytics helper in polls.js. Score 5 for decent visualization but no export or temporal analysis.
Frontend Experience5 / 10
7 React routes with consistent dark theme (custom CSS variables in index.css, 180 lines) across Home, Login, Register, Dashboard, CreatePoll, PollRespond, PollAnalytics. Responsive layout at 768px breakpoint. Loading spinners, error-box, success-box, empty states (Dashboard no-polls CTA, Analytics no-responses) all handled. Fade-up animations, pulsing live-dot, custom toggle switches, styled option radio buttons. Sticky navbar with gradient logo. Gaps: no form library, HTML title is 'client' (index.html:7), mixed inline styles with CSS classes, no PWA/offline support, no confirmation dialogs except delete, no toast notification system. Score 5 for decent visual polish but uncustomized meta and basic form UX.
Backend Architecture & API Design3 / 15
Backend at 403 lines across 5 files with flat route-centric architecture (no service layer, no controller separation). In-memory arrays (db.js: users, polls, responses) instead of MongoDB — the README claims 'MongoDB' in tech stack but actual implementation uses plain JS arrays. All validation is manual ad-hoc checks (no Joi/Zod). CORS configured with ALLOWED_ORIGINS env var. No Helmet, no rate limiting, no request body size limits, no centralized error handler (catch blocks return generic 'Server error'). JWT_SECRET hardcoded fallback and exported. No pagination, no input sanitization. Score 3 for basic Express scaffolding lacking persistent storage and security.
Real-Time Updates Using WebSockets5 / 10
Socket.io server with room-based pub/sub (server/index.js:58-66): clients join/leave `poll-{id}` rooms. Server emits analytics-update on response submission (polls.js:175) and poll-published on publish (polls.js:211). Both PollAnalytics.jsx and PollRespond.jsx connect, join rooms, listen for events, and clean up properly on unmount (leave room + disconnect). Uses event-driven invalidation (client refetches or updates state) rather than server-pushed full state. Gaps: no debouncing, no reconnection/room re-join logic after disconnect, no socket authentication, only 2 event types, duplicated socket setup code across 2 pages. Score 5 for functional room-based real-time with proper lifecycle but limited implementation depth.
Code Quality & Project Structure4 / 10
Clean monorepo: server/ and client/ with root orchestration package.json. ~1610 lines of source code (excluding lockfiles). ESLint configured for client only. All JavaScript (no TypeScript). No tests anywhere. Consistent naming conventions and readable code. Duplication: API_URL computed identically in PollRespond.jsx:7 and PollAnalytics.jsx:8; socket connection + room lifecycle pattern duplicated across both files. HTML title is default 'client'. No .env.example files. JWT secret hardcoded. Unused assets (react.svg, vite.svg boilerplate). Good README (146 lines) with API reference table, WebSocket events, and setup instructions. Score 4 for clean organization but no TypeScript, no tests, duplicated patterns.
129
PulseBoard
aditya om · @adityaom
36
PulseBoard is a MERN-stack polling platform with a single-page React frontend and Express/MongoDB backend. Auth works with JWT (stored in localStorage), poll creation uses React Hook Form for dynamic multi-question forms, responses are collected with basic validation, analytics show option counts with Recharts pie charts, and Socket.IO provides real-time update signals. However, quality is undermined by: (1) a critical runtime bug in responseController.js using undefined variables, (2) no input validation framework or security middleware, (3) substantial code duplication (analytics logic copied 2x, progress bars 4x), (4) no TypeScript or tests, (5) dead dependencies and unused components, and (6) typos in imported module names. The frontend has a consistent dark theme with loading/error/empty states across pages. Overall: functional happy path but with genuine bugs and significant code quality/security gaps. Total score: 36/80.
Authentication & Access Control5 / 10
Registration (bcrypt 10 rounds) and login endpoints in authController.js (89 lines), JWT Bearer token verification in authMiddleware.js (33 lines), optionalAuth for public routes (26 lines). ProtectedRoute.jsx gates client routes. Anonymous vs authenticated poll modes enforced server-side (responseController.js:16-19). However: JWT stored in localStorage (XSS risk), no httpOnly cookies, no refresh token mechanism, no logout endpoint on server, no email verification or password reset, CORS wide open `cors()` with no origin restrictions, no Helmet/CSRF/rate limiting.
Poll Creation & Question Management6 / 15
CreatePoll.jsx (248 lines) uses React Hook Form with useFieldArray for dynamic questions/options. QuestionField.jsx (106 lines) handles individual question with required/optional toggle, option add/remove (min 2 enforced). Anonymous toggle and expiry datetime picker present. Client-side validation via react-hook-form (title required, question text required, option text required, expiresAt required). Server only validates title + questions array non-empty (pollController.js:12). No poll editing endpoint exists, no draft status, no slug/permalink generation, no QR code sharing.
Response Collection Flow4 / 15
PublicPoll.jsx (414 lines) renders questions with radio buttons, required question indicators, live countdown timer. Client-side duplicate prevention via localStorage `answeredPolls`. Server-side required question validation (responseController.js:56-68), expiry check with auto-publish (lines 37-53), authenticated duplicate prevention via findOne. BUT: Critical bug on lines 42-44 — uses undefined `pollId` variable and non-existent `req.io` (should be `getIO()` and `req.params.pollId`), causing ReferenceError on expired poll submission. No validation that submitted options belong to poll. No MongoDB compound unique index for deduplication. No per-question single-selection enforcement server-side.
Analytics & Feedback Dashboard5 / 15
Two analytics views: PollDashboard.jsx (299 lines, freely accessible) and Analytics.jsx (198 lines, protected). Shows total responses, question count, status, per-question option breakdowns with counts/percentages/progress bars, and Recharts PieChart per question (AnalyticsChart.jsx, 86 lines). Publish/delete/ copy-link actions. Socket.IO live updates on new responses. However: analytics computation duplicated identically in getPollAnalytics (lines 86-136) and getPublicResults (lines 202-242). No anonymous vs authenticated breakdown, no time-series/velocity, no peak activity, no leader highlighting, no CSV/export, N+1 query pattern (nested forEach), no zero-vote chart handling, PublicResults.jsx exists but is never routed.
Frontend Experience5 / 10
Consistent dark purple theme with glassmorphism (backdrop-blur, bg-white/5, border-white/10), responsive grid layouts, sticky navbar with active link highlighting, loading spinners on all pages, error banners, empty states (Dashboard CTA), toast notifications with custom styling, smooth hover transitions, copy-to-clipboard with feedback, live countdown timer. Gaps: background glow divs duplicated verbatim across 7+ pages, progress bars are static (no animation), no skeleton states, no mobile hamburger menu, HTML title is "client", no landing page (unauthenticated users get redirected to login immediately), dead dependencies (zod, @hookform/resolvers), user data fetched from localStorage individually in each page rather than via AuthContext.
Backend Architecture & API Design4 / 15
MVC-like pattern: routes→controllers→models. 3 Mongoose models (User, Poll with embedded questionSchema/optionSchema, Response). Two-tier auth middleware (mandatory + optional). 11 REST endpoints. Poll expiry scheduler with in-memory timer map. Socket.IO integration. Critical gaps: no input validation library (no Joi/Zod/express-validator), no security middleware (no Helmet, no rate limiting, CORS wide open), no database indexes on Response.pollId/Response.userId/Poll.creator, typo `mongooose` in Poll.js:1, typo `pollRutes` in index.js:9, bug with undefined `pollId` in responseController.js:42-44, error.message leaked to clients in all catch blocks, no graceful shutdown, 500 errors for validation failures, no request body limits, no pagination, no .env.example.
Real-Time Updates Using WebSockets4 / 10
Socket.IO server on HTTP server with room-based pub/sub (socket.js, 37 lines). Server emits `newResponse` on submission (getIO used correctly at line 76-80), `pollPublished` on publish (pollController.js:169), `pollClosed` from expiry scheduler. Client joins rooms via `joinPollRoom`, listens on Analytics/PollDashboard/PublicPoll pages with proper useEffect cleanup. However: pure invalidation signal — no data pushed over socket, just triggers HTTP refetch. Critical socket.js bug: `socket.on('disconnect')` nested inside `socket.on('joinPollRoom')` (lines 21-23), causing duplicate disconnect listeners. `autoConnect: false` but `socket.connect()` never called — socket may never connect. No reconnection room re-join logic. No debouncing. No leave-room emit. No socket authentication. Socket URL uses fragile `.replace('/api', '')` string manipulation.
Code Quality & Project Structure3 / 10
Clean high-level structure: client/ and server/ separated, routes→controllers→models pattern, pages→components→services→context. ESLint configured for client. README with feature list and tech stack. However: no TypeScript (entirely plain JS despite @types/* in devDeps), dead dependencies (zod, @hookform/resolvers never imported), unused PublicResults.jsx (165 lines), analytics computation duplicated 2x server-side + progress bars duplicated 4x across client pages, auth header pattern repeated 6+ times, background glow markup duplicated 7+ times, typos (mongooose, pollRutes), console.log 20+ times, no automated tests, no .env.example, no server .gitignore, actual bugs in responseController.js, no JSDoc, server has no start/build script (only dev), no Prettier, no PropTypes.
130
PulseBoard
Pramith Roy · @roy.pramith08_d61431c8
36
PulseBoard is a MERN-stack polling platform with React/Vite frontend (Vercel) and Express/MongoDB backend (Render). It demonstrates solid JWT authentication with HttpOnly refresh cookies and RBAC, but has significant gaps across all other parameters: no poll edit/delete, no duplicate vote prevention, no public results view (major requirement failure), no form library, no validation library, no security middleware (Helmet, rate limiting), no TypeScript, no tests, and Socket.io broadcasts globally without rooms or data push. The frontend has dark mode and decent state coverage but uses alert() for errors. Overall: functional but shallow implementation across most dimensions.
Authentication & Access Control6 / 10
Solid JWT auth with bcrypt (10 rounds), dual-token strategy (1h access + 7d refresh), HttpOnly/secure/sameSite refresh cookie, silent login via refresh endpoint with DB lookup (makerRoutes.js:95-119), Bearer token authGuard middleware (authGuard.js:3-21), AuthContext with Axios interceptor + login/logout (AuthContext.jsx:33-58), register/login/refresh/logout endpoints. Anonymous vs authenticated poll modes enforced via isIncognitoAllowed gate (pulseRoutes.js:114-120). Creator-only RBAC on analytics (pulseRoutes.js:147) and publish (pulseRoutes.js:206). Gaps: no Helmet, no rate limiting, no CSRF, no email verification, no password reset, no input validation library.
Poll Creation & Question Management4 / 15
Dynamic question/option add/remove in PulseCreator.jsx (212 lines), datetime-local expiry picker (line 109), incognito toggle checkbox (line 117). Server validates title+questions+expiry presence (pulseRoutes.js:18-30). Schema supports isMandatory field (PulseDocket.js:23). Critical gaps: no isMandatory toggle UI (hardcoded to true), no poll edit/update endpoint, no poll delete endpoint, no server-side validation of individual question/option content (text non-empty, min 2 options), no slug/copy-link/QR, no form library (bare useState), error handling uses alert() (line 69), no draft status or question reordering.
Response Collection Flow4 / 15
Public poll fetch with expiry+CONCLUDED gates (pulseRoutes.js:69-97), incognito mode enforcement (pulseRoutes.js:114-120). Client validates mandatory questions before submit (LivePulse.jsx:39-45). Radio button UI with selection highlighting (LivePulse.jsx:127-149). Success/error/loading states present. Critical gaps: no duplicate vote prevention (no unique index on {linkedPulse, voterFingerprint} in PulseEcho model; fingerprint is ANON_+Math.random() generated fresh on every submission at LivePulse.jsx:53 — same user can vote unlimited times), no server-side mandatory question validation, no validation of submitted options against poll's actual questions/options, no submit button loading state.
Analytics & Feedback Dashboard5 / 15
Analytics endpoint computes total responses + per-question option counts via manual O(n*m) loops (pulseRoutes.js:160-182). Publish endpoint marks CONCLUDED+publiclyViewable with creator-only authorization (pulseRoutes.js:201-226). Frontend shows 3 stat cards (total responses, completion rate, expires), per-question horizontal bar charts with percentages (Analytics.jsx:161-203), ACTIVE/CONCLUDED status badge, publish button with confirm dialog. Major gap: NO public results view — GET /api/pulses/:id returns 403 for CONCLUDED polls, and there's no separate public results endpoint, contradicting the "anyone can view final results" requirement. Completion rate is misleading (100% if any responses exist, not actual per-question rate at pulseRoutes.js:191). No CSV export, no time-series, no individual response viewing, no aggregation pipeline.
Frontend Experience5 / 10
6 routes via React Router (/, /auth, /dashboard, /create, /vote/:id, /analytics/:id). Dark/light mode with localStorage persistence + Sun/Moon toggle (App.jsx:76-95). Custom Japanese-inspired Tailwind palette (tailwind.config.js:11-16). Responsive layout with sm/md/lg breakpoints. Loading states (animate-pulse text), error states (red banners), empty state (creative card on Dashboard.jsx:61-78), success state after voting. Sticky nav with conditional auth links. Vercel SPA rewrites (vercel.json). Gaps: landing page is bare text-only, errors use alert() (PulseCreator.jsx:69), no mobile hamburger menu, no toast notifications, no skeleton loaders, no form library, isMandatory toggle has no UI, no confirmation dialogs except browser confirm().
Backend Architecture & API Design4 / 15
11 REST endpoints with clean models/routes/middlewares/config separation. Mongoose schemas with timestamps, refs, enums (PulseDocket, PulseEcho, Maker). Centralized authGuard middleware. CORS with credentials, cookie-parser, JSON body parser. Environment variables for secrets. However: all business logic in route handlers (no service/controller layers), no input validation library (all manual ad-hoc checks), no Helmet, no rate limiting, no CSRF, no request body size limits, no .env.example file, no centralized error handler, no MongoDB indexing strategy, no graceful shutdown, Socket.io CORS hardcoded to localhost:5173 (not env var), console.log left in 6 places, no poll update/delete endpoints, no pagination.
Real-Time Updates Using WebSockets4 / 10
Socket.io server on HTTP server (core.js:30-38). Two server-emitted events: pulse_updated on vote submit (pulseRoutes.js:130-131) and pulse_concluded on publish (pulseRoutes.js:214-217). Client connects, listens, and refetches analytics via HTTP on events (Analytics.jsx:32-52). Cleanup on unmount (Analytics.jsx:51). Critical gaps: uses io.emit() globally (no rooms), meaning ALL connected clients receive ALL poll events, not just relevant ones. Socket.io CORS hardcoded to "http://localhost:5173" (core.js:32) — doesn't accept production Vercel URL. No data pushed over socket (pure invalidation signal + HTTP refetch). No socket integration on voting page. No authentication on socket connections. No reconnect/room re-join logic. No debouncing.
Code Quality & Project Structure4 / 10
Clean client/server monorepo with ~15 source files. Consistent naming: PascalCase components, camelCase utilities, ES modules throughout. Tailwind CSS with theme config. ESLint on client. README with demo credentials and architecture overview. However: no TypeScript (all vanilla JS), zero automated tests, no .env.example file, no service layer (all logic in route handlers), bare useState hooks with imperative array manipulation (no form library), isMandatory field defined but has no UI (dead code path), alert() for errors in PulseCreator, 6 console.log statements in production code, server/package.json "main" points to nonexistent index.js (actual entry is core.js), no .editorconfig/.prettierrc, no lint-staged/husky, no code comments, no component extraction (all in page files). The code is functional but lacks engineering discipline.
131
PulseBoard — Real-Time Polling & Analytics Platform
Darshan Pawar · @darshan12674_93e37cbf
36
PulseBoard is a functional polling platform (2,397 total lines of source) with working registration/login (bcrypt + JWT), poll creation with dynamic questions/options, response collection with expiry/required validation, per-question analytics with Recharts pie charts, and Socket.IO for live updates. However, the platform has significant gaps across all dimensions: no root README, no deployment, no input validation, no duplicate vote prevention, no poll editing/deletion, an empty dashboard, globally-broadcasting WebSockets (no rooms), no security middleware, and ~300 lines of Vite boilerplate. The clean module structure and working end-to-end flow show promise but the implementation is partial across the board, landing around the average range (score: 36/100).
Authentication & Access Control5 / 10
Registration with bcrypt (10 rounds, auth.service.js:16-27), login with JWT (7d expiry, auth.service.js:47-56), protect middleware (auth.middleware.js:3-34) verifying Bearer token from Authorization header, ProtectedRoute component checking localStorage token (ProtectedRoute.jsx:3-11), anonymous vs authenticated poll modes via isAnonymous field and dual response routes. Critical gaps: JWT stored in localStorage (XSS-vulnerable, no httpOnly cookies), no Helmet/CSRF/rate limiting, no email verification, no password reset flow, typos ("Longin successful" line 26, "Invalid creadentials" lines 37/44).
Poll Creation & Question Management5 / 15
CreatePoll.jsx (283 lines) has dynamic questions/options with add/remove, mandatory/optional toggle per question, anonymous toggle, expiry datetime-local picker, title/description fields. Poll model (poll.model.js) embeds questions with required/options subdocuments. Gaps: no poll editing endpoint at all (PATCH /:id/publish only toggles isPublished), no server-side input validation beyond Mongoose defaults (createPollService at poll.service.js:3-9 spreads req.body directly—mass assignment), no client-side validation beyond HTML5 required, no slug/permalink system, no draft/active status concept.
Response Collection Flow5 / 15
Response service (response.service.js:5-67) checks poll existence, expiry, required questions, and handles anonymous userId. Response model (response.model.js) properly stores questionId + selectedOption per answer. PublicPoll.jsx (141 lines) has client-side required validation, loading/error/success/expired states. Critical gaps: NO duplicate vote prevention—no unique index on {pollId, userId}, no localStorage tracking, same user can submit unlimited responses; no server-side validation that submitted optionIds belong to the poll's questions; no validation that submitted questionIds are in the poll; no per-user response count limit.
Analytics & Feedback Dashboard5 / 15
Analytics service (analytics.service.js:58-107) computes totalResponses and per-question option counts with ownership check. Creator-only PollAnalytics.jsx (245 lines) shows total responses, per-question breakdowns with Recharts pie charts, publish button with state management, copy-to-clipboard for poll/results share links. PublishedResults.jsx (154 lines) shows public results with pie charts. Gaps: Dashboard.jsx is practically empty—no poll listing (no GET /polls/mine endpoint exists), no authenticated vs anonymous breakdown, no time-series/trend data, no CSV/PDF export, no peak activity metrics, no per-response viewing, no aggregation pipeline (O(n*m) nested loops).
Frontend Experience5 / 10
Seven routes with React Router v7 (App.jsx:19-58), dark gradient theme (Tailwind CSS v4) on authenticated pages, loading/error states on all pages, success state on PublicPoll, copy-to-clipboard with timed feedback on PollAnalytics. Login (112 lines), Register (134 lines), CreatePoll (283 lines), and PollAnalytics (245 lines) are reasonably well-styled. Gaps: PublishedResults page nearly unstyled (bare div elements, no Tailwind classes), Dashboard empty (just a welcome card), App.css + index.css contain ~300 lines of Vite template boilerplate unused by the app, HTML title is "frontend", public pages use inconsistent light styling vs dark authenticated pages, no confirmation dialogs, no toast/notification system (uses alert()).
Backend Architecture & API Design5 / 15
Clean modular architecture (routes→controllers→services→models) with separate modules for auth, polls, responses, analytics (backend/src/modules/). Proper Mongoose schemas with embedded questions, ref-based responses. Auth middleware as shared concern (auth.middleware.js). Socket.IO singleton pattern (analytics.socket.js:3-28) with getIo(). Gaps: zero input validation library (no Joi/Zod)—createPollService spreads req.body directly (poll.service.js:4-7); no security middleware (no Helmet, rate limiting, body size limits); no pagination on any endpoint; missing CRUD (no delete, no edit, no GET /polls/mine); hardcoded localhost CORS origins only; no centralized error handler; no .env validation; no graceful shutdown.
Real-Time Updates Using WebSockets3 / 10
Socket.IO server initialized in analytics.socket.js (28 lines) with Express HTTP server, CORS configured for localhost:5173. Server emits io.emit(`poll-${pollId}`, {message: "New response submitted"}) on response submission (response.service.js:59-63). Client (PollAnalytics.jsx:15, 58-60, 63-64) creates socket on import, listens for poll-${pollId} events, triggers fetchAnalytics() refetch, cleans up on unmount. Major gaps: uses io.emit() (broadcasts to ALL clients) instead of room-based socket.to()—every connected client receives every response notification regardless of which poll they're viewing; only one event type (responses only, nothing on publish/update/delete); hardcoded localhost:4000 socket URL; no socket authentication; no reconnection room re-join; no debouncing; only analytics page uses sockets (public poll page does not).
Code Quality & Project Structure3 / 10
Clean module structure with consistent naming (model/controller/service/routes per module), separate backend/frontend directories, ESLint on frontend. Critical gaps: NO root README (backend/README.md is empty, frontend/README.md is unmodified Vite template) so documentation scores at most 3 per rules; multiple typos: "Longin successful" (auth.controller.js:26), "Invalid creadentials" twice (auth.service.js:37,44), "Poll not foun" (poll.service.js:16), "Anonmous loic" comment (response.service.js:44); ~300 lines of Vite boilerplate in index.css/App.css unused by the app; no .env.example; no tests; no TypeScript; no linting on backend; inconsistent indentation in analytics.service.js; duplicate analytics computation in getPublishedResultsService and getPollAnalyticsService; HTML title is "frontend".
132
S
PulseVote
SHAHBAZ AHMED · @shahbazahmed96
36
PulseVote is a functional full-stack polling app with RSA JWT auth, dynamic poll creation with multiple questions, response collection, analytics with per-question breakdowns, and results publishing. The frontend (React+Vite, Vercel-deployed) has a polished dark blue theme with responsive layout. The backend (Express+Mongoose) has clean architecture. However, the app has significant gaps across all dimensions: no input validation library, no Helmet/rate limiting, responseMode never enforced server-side, no duplicate vote prevention, Socket.IO is server-only (client has zero integration), analytics unprotected, no poll edit/delete, no indexes, schema typos, and no tests. The deployment is frontend-only (Vercel static), API routes return HTML — the backend is not deployed. Total: 36/100.
Authentication & Access Control4 / 10
RSA-256 JWT auth with bcrypt 12 rounds (user.model.ts:36-39) and proper Bearer token middleware (auth.middleware.ts:10-41). Registration (auth.controller.ts:68-95) hashes passwords and creates users. However, critical gaps: responseMode field is stored but NEVER enforced server-side — the submitPollResponse controller ignores it entirely. Tokens stored in localStorage (api.ts:18, App.tsx:107), no Helmet, no rate limiting, no CSRF protection, ISSUER hardcoded to `http://localhost:${PORT}` (auth.controller.ts:51), no email verification or password reset, `@ts-ignore` on jwt.verify (middleware line 28), `user?: any` type, and no input validation library. Token has 1-hour expiry with no refresh mechanism.
Poll Creation & Question Management5 / 15
PollWorkspace.tsx (442 lines) implements dynamic questions with add/remove, dynamic options per question (min 2 enforced client-side at line 73-75), mandatory/optional toggle per question (checkbox, line 274-283), responseMode dropdown (anonymous/authenticated), expiresAt datetime-local picker. Schema typo: `defalut` instead of `default` at poll.model.ts:22. Server-side createPoll (poll.controller.ts:9-25) passes req.body directly to Poll.create() with zero validation — relies solely on Mongoose schema validators. No poll editing endpoint exists (only PATCH for publish), no deletion endpoint, no slug/permalink generation, no QR code, no draft status, questions keyed by array index.
Response Collection Flow5 / 15
Poll response model (response.model.ts) correctly stores answers as {questionId, selectedOption} array. submitPollResponse controller (pollResponse.controller.ts:10-56) validates poll existence, checks expiry, enforces required question answering (lines 25-38), increments totalResponses, and emits Socket.IO event. ResponseView.tsx renders radio buttons with required attribute for mandatory questions. Critical omissions: NO duplicate vote prevention (no unique index on {pollId, userId}, no existing-response check), NO responseMode enforcement (authenticated mode still allows anonymous submissions), NO validation that selected options belong to the poll's questions, NO per-question single-option enforcement server-side.
Analytics & Feedback Dashboard5 / 15
generateAnalytics.ts (49 lines) computes per-question option counts and percentages by iterating all responses with nested loops. AnalyticsView.tsx (129 lines) renders total response stat card, per-question analytics cards with CSS bar charts showing option votes and percentages. Results publishing via PATCH /api/poll/:id/publish with creator-only gate. Gaps: analytics endpoint has NO auth middleware (anyone can view), but results endpoint requires auth (contradictory), no peak activity metrics, no time-series data, no anonymous vs authenticated breakdown, no CSV export, no individual response viewing, brute-force O(n*m) computation (N+1 pattern), no leading option identification, static CSS-only bars without animation.
Frontend Experience6 / 10
Clean dark blue theme with consistent CSS design system (1097-line index.css with CSS custom properties). 3 routes (/, /auth, /dashboard) + 4 sub-views within dashboard (polls, create, respond, analytics). LandingPage.tsx (272 lines) features hero with dashboard preview, workflow steps, feature grid, and demo credentials section. All major states covered: loading ("Loading polls/poll/analytics"), error (message.error), empty ("No polls yet" with CTA), and success notices. Responsive design with 900px and 560px breakpoints collapsing grid layouts. However: custom routing via history.pushState (no React Router), no animations or transitions, no dark/light theme toggle, form UX uses individual useState hooks (no form library), no confirmation dialogs, no QR code or copy-link, no skeleton loading states.
Backend Architecture & API Design5 / 15
Clean routes→controllers→models architecture with TypeScript throughout. 10 API endpoints (5 poll, 2 auth+userinfo, 1 response, 1 analytics, 1 health). Differentiated HTTP status codes (200/201/400/401/403/404/500). RSA-256 JWT + bcrypt 12 rounds for auth. CORS configured with credentials (index.ts:28-33). Critical gaps: NO input validation library (Zod/Joi/express-validator) — validation relies on Mongoose schema + manual ad-hoc checks, createPoll passes req.body directly. No Helmet, no rate limiting, no request size limits. No service layer. No indexes on any model (no unique compound for duplicate prevention). No pagination on GET /api/poll. Schema typos (`defalut`, `maxLenght`). Unused drizzle.config.js (project uses Mongoose). Inconsistent auth: analytics and response endpoints are unprotected while all poll endpoints require auth.
Real-Time Updates Using WebSockets2 / 10
Server has Socket.IO attached to HTTP server with CORS (index.ts:66-71), room-based join via 'join_poll' event (index.ts:76-78). Server emits 2 events: 'poll_updated' with full analytics payload after response submission (pollResponse.controller.ts:50), and 'results_published' on result fetch (poll.controller.ts:97). However, the CLIENT has ZERO Socket.IO implementation: grep confirms no socket.io-client dependency in client/package.json, no socket-related imports or code anywhere in client/src/. The real-time feature is non-functional — the server broadcasts events that nothing listens to.
Code Quality & Project Structure4 / 10
Clean TypeScript monorepo with client/server separation and consistent naming conventions (*.controller.ts, *.model.ts, *.routes.ts). Types defined in shared types.ts. ESLint configured for client. Issues: schema typos (`defalut` in poll.model.ts:22, `maxLenght` in user.model.ts:13,20,26), `RequestWithUser` type duplicated across 3 controller files, `console.log` in production (analytics.controller.ts:32, index.ts:74,80), hardcoded ISSUER URL, dead drizzle.config.js (unused — project uses Mongoose), .env.example missing PRIVATE_KEY/PUBLIC_KEY vars, `@ts-ignore` in middleware, zero automated tests anywhere, no ESLint on server, old commented-out cert loading code left in cert.ts:1-5.
133
P
Poll-App
Pratham Kumar · @prathamkumarsahu420_e3bb0225
35
Poll-App is a full-stack polling platform (Next.js + Express + PostgreSQL/Drizzle + Socket.IO) with working authentication (JWT, Google OAuth, OTP email verification), dynamic poll creation with 3 question types, a public voting flow with multiple state handling, basic analytics with option breakdowns, and Socket.io real-time signals. The project achieves core functionality but has significant gaps in production readiness: no backend input validation, no duplicate vote prevention, mixed ESM/CommonJS module system, no TypeScript despite dev tooling, no deployment, hardcoded localhost URLs with port mismatch (backend:4000, frontend calls:5000), no shared components, no tests, and minimal WebSocket implementation (1 event, no data push). Total backend source is ~927 lines, frontend pages are inline with no component extraction. Score: 35/80.
Authentication & Access Control6 / 10
Solid auth: registration with bcrypt(10)+OTP via nodemailer HTML email, login with JWT (7d), Google OAuth auto-creation, verifyToken middleware on 6 routes (auth.js:9-43), anonymous/authenticated poll enforcement server-side (polls.js:137-139), email verification gating at login (auth.js:72-76). Gaps: tokens in localStorage (XSS risk), no refresh token rotation, no logout endpoint (client-side removal only), no Helmet/CSRF/rate limiting, no password reset, no AuthContext/AuthProvider on frontend.
Poll Creation & Question Management5 / 15
React Hook Form with useFieldArray for dynamic questions/options (create/page.jsx:49-93, 488-577). Per-question type selection (single/multiple/text), isMandatory toggle, option add/remove (min 2 enforced). Poll-level: title, isAnonymous, expireTime datetime. Transaction-based DB creation (polls.js:20-57). Gaps: zero server-side validation of questions/options (no Zod/Joi), no poll EDIT endpoint (only create/close/publish/delete), no slug/QR, no draft status, no backend enforcement of min options per question.
Response Collection Flow4 / 15
Client-side mandatory question validation (vote/page.jsx:41-51). Server checks: poll existence, expiry, closed/published blocking, anonymous mode enforcement (polls.js:119-139). Transaction-based submission (polls.js:141-160). Socket.io emit on vote. Comprehensive UI states (success, closed, published, login-required, loading, not found). Critical gaps: NO duplicate vote prevention (no unique index, no re-vote check), no server-side mandatory question validation, no backend validation of optionId↔questionId relationship, userId passed from client request body instead of JWT extraction (polls.js:109).
Analytics & Feedback Dashboard4 / 15
Stats endpoint computes total votes, per-question option counts+percentages, text responses (polls.js:206-283). Analytics page shows 3 stat cards (responses, poll state, sync mode), per-question option bars with percentages+counts, text response cards, empty state (poll/page.jsx:466-674). Publish/close controls for owners. Gaps: N+1 query problem in stats computation, no trend/time-series data, no anonymous vs authenticated breakdown, no export (CSV/PDF), no individual response viewing, isOwner check is superficial (localStorage token existence, not userId comparison), percentages use totalVotes denominator inflating multi-choice stats.
Frontend Experience5 / 10
Next.js App Router with 8 routes, Tailwind CSS with consistent gradient-heavy design, React Hook Form on auth/create pages. Loading states (spinners, pulsing text) on all data pages. Error states (banners, specific error pages). Empty states (no polls CTA, no responses message). Responsive grid layout. Vote page handles 6+ distinct states. Gaps: no shared components (all UI inline), alert()/confirm() for user feedback (7 instances), no toast system, no dark mode, all 16 API calls hardcoded to localhost:5000 (non-functional outside local dev), no form library helpers beyond basic react-hook-form setup.
Backend Architecture & API Design4 / 15
14 RESTful API endpoints, PostgreSQL+Drizzle ORM with 6 well-designed tables and 6 iterative migrations, DB transactions for create+vote, JWT middleware extracted. However: no input validation library at all (no Zod/Joi/express-validator), no service layer (all logic in route handlers), mixed ESM/CommonJS (polls.js uses require(), everything else uses import), no Helmet/rate limiting/CSRF, CORS wildcard, N+1 queries in stats endpoint, dead code at polls.js:85-87, no pagination, no centralized error handler, userId extracted from client body for votes instead of JWT.
Real-Time Updates Using WebSockets3 / 10
Socket.IO server integrated with HTTP server (index.js:23-26), room-based pattern with join_poll subscription (index.js:32-35). Server emits "new_vote" on submission (polls.js:162-163). Client connects to room on mount, listens for new_vote, triggers HTTP refetch, cleanup on unmount (poll/page.jsx:189-211). Gaps: only 1 custom event (new_vote), no data pushed over socket (pure invalidation signal), no reconnection room re-join logic, no socket authentication, no events for close/publish/delete, no debouncing on client refetch, only covers analytics page (not voting page), hardcoded to localhost:5000.
Code Quality & Project Structure4 / 10
Clean frontend/backend separation, Next.js App Router conventions, Tailwind CSS v4, React Hook Form patterns. However: no TypeScript despite claiming it (zero .ts/.tsx files), mixed ESM/CommonJS in backend, no test files anywhere, no shared components, 16 hardcoded localhost:5000 URLs with port mismatch (backend runs on 4000), localStorage magic strings duplicated 20+ times, no .env.example files, Google OAuth handler duplicated in login+register, isOwner check superficial (token existence vs userId comparison), developer name "Pratham"/"Pratham User" hardcoded in multiple locations, dead code at polls.js:85-87.
134
PulseBoard
Kunal Bhanuse · @kunalbhanuse07_20f4e920
34
PulseBoard is a functional full-stack polling platform with working frontend (Vercel) and backend (Render) deployments. It implements all core features: registration with email verification, JWT auth with dual tokens, dynamic poll creation with multiple questions/options, public vote submission with radio buttons, per-question results visualization with bar charts, and minimal Socket.io real-time updates. However, the implementation quality is below average across all dimensions: critical security gaps (no Helmet, no rate limiting, wildcard CORS, localStorage tokens), no duplicate vote prevention (missing userId on Vote model), minimal Socket.io (global broadcast only, no rooms), no poll editing or deletion, no README, zero tests, multiple typos, hardcoded URLs, and missing indexes on foreign keys. Total: 34/100.
Authentication & Access Control5 / 10
Registration with email verification (SHA-256 token + nodemailer SMTP), login with JWT dual-token (access token in body, refresh in httpOnly cookie), bcrypt 10-round pre-save hook, authMiddleware for protected routes, optionalAuth for vote endpoint, poll.requiresAuth enforcement server-side. Significant gaps: no Helmet/CORS is wildcard/CSRF protection, no rate limiting on auth endpoints, refresh cookie missing `secure` flag, access token stored in localStorage (XSS-vulnerable), no token refresh mechanism, broken email verification link hardcoded to http://localhost:3000, Navbar not auth-aware, no frontend route guards (each page manually checks localStorage).
Poll Creation & Question Management5 / 15
CreatePoll.jsx (349 lines) implements dynamic questions/options with add/remove, isRequired toggle per question, requiresAuth toggle, and expiry via days input (1-30). Server-side Zod validation via pollSchema.parseAsync enforces title (5-50 chars), description (15-200), min 1 question, min 2 options per question, optional date for expiry. nanoid shareId generation. Critical gaps: no poll editing endpoint exists (PATCH not implemented), no draft/publish workflow in creation form, no question reordering, no client-side validation library (HTML5 only), no slug customization, no QR code for sharing.
Response Collection Flow4 / 15
PollView.jsx renders radio-button form with per-question selection, client-side required question validation (lines 47-56), loading/error/submitted/success states. Server-side validates question existence, option membership, and expiry ($or null/future date). Socket.io emit on vote submission. Critical flaw: vote.model.js has NO userId field and NO compound unique index — unlimited duplicate voting possible. The `vote` variable used in response at poll.controller.js:157 is scoped inside the for-loop, causing a reference bug when answers.length > 1 (papered over with error-message string hack at PollView.jsx:85-88). No Zod validation on vote submission body. result() function has no try/catch.
Analytics & Feedback Dashboard5 / 15
Dashboard.jsx shows owner polls list with total/active/link counts and copy-to-clipboard. PollResult.jsx (295 lines) has summary stat cards (questions, total votes, top option, average votes), per-question bar charts with counts and percentages (formatPercent helper), leading option sidebar, and live refresh via Socket.io. Gaps: dashboard lacks per-poll response counts, no publishing workflow (isPublished field in schema but no endpoint to toggle it), results page has no access control (anyone can view), result() uses O(n*m) filter loops instead of MongoDB aggregation, no authenticated vs anonymous breakdown, no CSV export, no individual response viewing, no response timeline/peak metrics.
Frontend Experience5 / 10
Clean CSS design system with custom properties (index.css), 7 React Router routes, responsive layouts with breakpoints. Every page handles loading, error, empty, and success states. Landing page with feature cards and preview mockup. Copy-to-clipboard with visual "Copied!" feedback. Radio-button voting UI and bar chart results visualization. Gaps: no form library (plain useState hooks in CreatePoll), Navbar not auth-aware (always shows Login/Sign Up), API_URL hardcoded in 7 files, no dark mode, Signup imports Login.css, no skeleton loaders, no confirmation dialogs, no form-level inline validation errors.
Backend Architecture & API Design4 / 15
Clean module structure (auth/, poll/ each with controller/model/dto/route), Zod validation on creation inputs (4 schemas), ApiError/ApiResponse utility classes for consistent JSON responses, dual JWT strategy, bcrypt pre-save hook, email verification with SHA-256 tokens. Critical gaps: no service layer (all logic in controllers), no centralized error handler middleware, zero indexes on foreign keys (Question.pollId, Vote.pollId, Poll.createdBy — full collection scans), no rate limiting, CORS wildcard, no Helmet/security headers, no request body size limits, no poll edit/delete endpoints, result() has no try/catch, multiple typos in user-facing API messages.
Real-Time Updates Using WebSockets3 / 10
Socket.io server integrated with HTTP server (server.js:8-14), global `io.emit("poll-updated")` broadcast on vote submission (poll.controller.js:153), client connects via socket.io-client (socket.js:2), PollResult.jsx listens for "poll-updated" and refetches HTTP results (lines 73-83), proper cleanup with socket.off on unmount. Major gaps: no room-based broadcasting (all clients receive every update regardless of poll), only 1 event type, no data pushed with event (pure invalidation signal, client refetches via HTTP), CORS wildcard on Socket.io, no socket authentication, no reconnection handling or room re-join, only used on PollResult page (not PollView voting page), stale closure risk from empty [] useEffect dependency on fetchResults.
Code Quality & Project Structure3 / 10
Project has clean monorepo structure (backend/frontend), consistent module organization, and CSS design tokens. However: the root Readme file is completely empty (1 line, no content) — documentation scores cap at 3. Multiple typos in production-facing API messages ("eror", "feached", "Succefull", "aswer"), filename typo "ApiResponce.js", API_URL hardcoded in 7 frontend files (DRY violation), no TypeScript on either side, zero automated tests, forgetPasswordToken/Expires dead code in User model, console.log left in optionalAuth middleware, no .env.example file, no globabl state management for auth, no centralized API client.
135
A
PulseBoard
Aditi Ninad Divekar · @divaaditi101
34
PulseBoard is a MERN polling platform with a solid conceptual architecture and clean visual design, but suffers from critical client-server API route mismatches that break core functionality in production. The frontend has complete UI for all pages (landing, auth, dashboard, create, respond, analytics), and the backend has proper Mongoose schemas with embedded question/option models. However, 4 of 7 API endpoints have mismatches between client and server (wrong URL paths, wrong HTTP methods, wrong payload structure), rendering response submission, analytics, poll sharing via token, and publishing all non-functional. The auth system works (register/login/JWT) and poll creation/listing/deletion function correctly, but the core polling workflow is broken. Additional quality issues include unused dependencies, duplicate function calls, dead boilerplate code, hardcoded localhost URLs, and lack of security middleware. Total score: 34/100.
Authentication & Access Control5 / 10
Registration, login, and JWT auth middleware are implemented (authController.js: 60 lines, auth.js middleware: 30 lines, User model with bcrypt hashing at 10 rounds). AuthContext provides login/logout with localStorage persistence and ProtectedRoute wrapper on dashboard, create-poll, and analytics routes (App.jsx:15-18). Anonymous/authenticated response modes enforced via isAnonymous toggle on poll creation (CreatePollPage.jsx:168-188) and server-side (responseController.js:57). Gaps: JWT tokens stored in localStorage (XSS-vulnerable), no Helmet/CSRF protection, no rate limiting, no refresh token rotation, no email verification or password reset, express-validator dependency installed but never used for input validation.
Poll Creation & Question Management5 / 15
CreatePollPage.jsx (271 lines) implements dynamic questions/options with add/remove, mandatory/optional toggle per question, anonymous/authenticated mode selector, and expiry datetime picker. Client-side validation checks title, expiry, all question text, all option text, min 2 options, min 1 question (lines 64-72). Server-side validates at least one question exists (pollController.js:8-10). Share token auto-generated via uuid v4 in Poll schema (poll.js:68-71). Gaps: no poll editing endpoint exists (only create/read/delete), no server-side validation of individual question/option fields, no slug/permalink, no draft status support at creation.
Response Collection Flow3 / 15
PollPage.jsx (225 lines) renders response form with good UI state coverage: loading, not-found, expired, published-results, and submitted-confirmation views. Client-side mandatory question validation (lines 37-43) with inline error toasts. Server-side checks expiry (responseController.js:19-24), mandatory questions (lines 27-37), updates embedded option counts using Mongoose subdoc methods (lines 40-48). CRITICAL BUG: client calls POST /api/polls/${pollId}/respond (pollService.js:23) but server has no such route — the actual endpoint is POST /api/responses/:token (responseRoutes.js:8). Also, client sends raw array as body but server destructures `{ answers }` from req.body. No duplicate vote prevention (no unique index, no localStorage tracking). These route and payload mismatches render response submission non-functional in production.
Analytics & Feedback Dashboard4 / 15
AnalyticsPage.jsx (179 lines) renders stat cards (total responses, questions, status), per-question bar charts via Recharts (BarChart/Bar/XAxis/YAxis/Tooltip/ResponsiveContainer/Cell), and option breakdown with percentage progress bars. Server-side getAnalytics (responseController.js:83-121) computes per-question option counts/percentages and total responses. Published results view on PollPage.jsx (lines 72-114) shows option results with progress bars publicly. DashboardPage shows polls list with response counts. Gaps: analytics endpoint unreachable due to route mismatch (client GET /api/polls/:id/analytics vs server GET /api/responses/:pollId/analytics), no CSV/export, no time-series data, no individual response viewing, no peak activity metrics, publish uses PATCH but server expects PUT.
Frontend Experience5 / 10
Complete multi-page UI: LandingPage (252 lines) with hero, features grid, use cases, how-it-works, CTA, footer; split-panel Login (151 lines) and Register (166 lines) pages; Dashboard (228 lines) with sidebar, stat cards, polls list with status badges; CreatePollPage (271 lines) with dynamic form; PollPage (225 lines) with 5 UI states; AnalyticsPage (179 lines) with Recharts bar charts. Custom design system in index.css (125 lines) with CSS custom properties, card/button/input classes, sidebar styling, animations. 7 routes with ProtectedRoute wrapper. React Hot Toast for notifications. Gaps: all styling via inline `style` objects (no CSS modules or styled-components), App.css (185 lines) is completely unused Vite boilerplate, no form library, route mismatches break core flows (poll sharing, response submission, analytics, publishing) leaving only auth and poll listing functional.
Backend Architecture & API Design4 / 15
MVC-like structure with Express routes→controllers→Mongoose models. Mongoose schemas are clean: User (35 lines, bcrypt pre-save hook, matchPassword method), Poll (78 lines, embedded questions/options with nested schemas, UUID shareToken), Response (35 lines, embedded answer schema). JWT protect middleware (30 lines). CORS configured with allowedOrigins. Socket.io mounted on HTTP server. Issues: express-validator dependency listed but never used for validation (zero imports), no Helmet, no rate limiting, no request body size limits, raw error.message leaked to clients, dotenv.config() called twice (index.js:12-13), connectDB() called twice (index.js:15,17), unused `import e from "express"` in poll.js:1, backend uses MONGODB_URI env var but README documents MONGO_URI, debug console.log in production (index.js:14), 4 of 7 client-server route mismatches.
Real-Time Updates Using WebSockets4 / 10
Socket.io server integrated with HTTP server (index.js:29-34). Room-based pattern: clients emit joinPoll/leavePoll events (index.js:62-70), server emits `newResponse` event on response submission with totalResponses and per-question option data (responseController.js:63-74). AnalyticsPage connects via io('http://localhost:5001'), joins room, listens for newResponse, triggers refetch (AnalyticsPage.jsx:14-23). Issues: hardcoded localhost:5001 URL makes WebSocket non-functional against deployed Render backend (needs the actual backend URL), PollPage imports io from socket.io-client but never uses it (dead import at line 6), only covers analytics page (no live updates on poll answering page), no reconnection handling, no room re-join on reconnect, and since response submission is broken by route mismatch, the real-time chain never fires in production.
Code Quality & Project Structure4 / 10
Clean client/server separation with backend src/ structure (config/controllers/middleware/models/routes/utils) and frontend src/ structure (context/pages/services). Consistent naming conventions (camelCase filenames, PascalCase components). README (170 lines) documents features, tech stack, setup instructions, API overview, and environment variables. Issues: unused `import e from "express"` in poll.js:1; dotenv.config() and connectDB() each called twice in index.js; debug console.log in production; duplicated comment in index.js:39-40; express-validator dep never used; .env files committed to repo; App.css is dead Vite boilerplate (185 lines of unused CSS); extensive inline styles in every page (no CSS module/component extraction); route mismatches between client and server for 4 of 7 endpoints; no TypeScript; no automated tests; no .env.example file; no ESLint configured for backend; sidebar component duplicated inline across 3 pages (DashboardPage, CreatePollPage, AnalyticsPage).
136
PollFlow
Vinny Madaan · @vinnymadaan8448_ed3d51ea
33
PollFlow is a MERN polling platform with a polished dark UI and basic feature coverage. Authentication (JWT), poll creation with dynamic Q&A, public response collection, Recharts analytics dashboard, and Socket.io real-time updates are all present. However, nearly every feature has significant gaps: no server-side validation on poll creation or vote submission, no security middleware, no duplicate vote prevention in DB, anonymous/authenticated modes captured but never enforced, Socket.io uses global broadcast without rooms, protected routes are inconsistently applied, and code quality suffers from empty stub files (4), duplicated logic, console.log in production, and zero project documentation. The UI looks good but the engineering underneath is shallow. Total: 33/80.
Authentication & Access Control4 / 10
Registration (name/email/phone/password, bcrypt 10), login (JWT 7d), protected middleware, password reset via email+Firebase phone OTP. But significant gaps: `allowAnonymous` field is never enforced server-side in submitVote (auth.controllers.js only uses it during poll creation), JWT stored in localStorage (not httpOnly cookies), no refresh token rotation, no email verification, and CreatePoll/Analytics/MyPolls pages are NOT wrapped in ProtectedRoute component (though API calls fail with 401).
Poll Creation & Question Management5 / 15
CreatePoll.jsx (634 lines) has dynamic questions with add/remove, dynamic options per question, mandatory/optional checkbox, anonymous toggle, expiry datetime picker, and live preview panel. However, the server-side createPoll controller has zero input validation (no Zod/Joi, no checks on title/questions/options/expiry), there is no poll editing endpoint (only create/read/delete), no slugs/permalinks, and `scheduledAt` state exists but is never used.
Response Collection Flow4 / 15
PublicPoll.jsx (548 lines) has good client-side UX: expiry check (isExpired), localStorage duplicate prevention (votedPolls), submitted/already-voted/expired states. But the server-side submitVote controller (poll.controllers.js:117-203) has almost no validation: no expiry enforcement, no required-question enforcement, no duplicate vote DB constraint (Response model has no unique index), no option-existence validation, and no anonymous/authenticated mode enforcement. Vote counts are stored inline (option.votes++) rather than computed from Response collection.
Analytics & Feedback Dashboard5 / 15
Analytics.jsx (485 lines) renders per-question BarChart and PieChart via Recharts, animated progress bars, vote counts, and percentage breakdowns. Dashboard.jsx (308 lines) shows stat cards (total polls, responses, active, published) and recent polls with share/analytics links. Results.jsx (304 lines) displays published results with progress bars. MyPolls.jsx (467 lines) has publish/delete/share/analytics actions. Gaps: no CSV export, no individual response viewing, no trend/time-series data, analytics rely on inline option.votes counters (not computed from Response collection), and no leading-option identification or drop-off metrics.
Frontend Experience5 / 10
Consistent dark glassmorphism theme across all pages, Framer Motion animations (page transitions, question add/remove), react-hot-toast notifications, Recharts integration. Home.jsx (538 lines) has a premium landing page with features/how-it-works/CTA/footer sections. Loading states present on most pages, empty state on MyPolls, mobile responsive with hamburger menu. Gaps: inconsistent protected route wrapping (CreatePoll, Analytics, MyPolls not wrapped in ProtectedRoute), README is Vite starter boilerplate (no project docs), no form library, and error states are silently caught in some places.
Backend Architecture & API Design4 / 15
Express/Mongoose backend with MVC separation (controllers, routes, middleware, models, config) and 15+ API endpoints. But: poll.routes.js imports router from auth.routes.js instead of creating its own express.Router(); no input validation library (no Joi/Zod/express-validator); no security middleware (no helmet, rate limiting, CSRF, or mongo-sanitize); raw error.message leaked in 500 responses; no pagination; no centralized error handler; MongoDB connection has no retry logic; no .env.example file.
Real-Time Updates Using WebSockets3 / 10
Socket.io server configured in server.js, shared `io` export used in poll.controllers.js. On vote submission, `io.emit("pollUpdated")` broadcasts globally. Client Analytics.jsx listens for `pollUpdated` event and refetches analytics data, with proper cleanup via `socket.off()`. Major gaps: uses global broadcast instead of room-based targeting, only covers the analytics page (no socket on PublicPoll or Dashboard), no data pushed over socket (just a refresh trigger signal), no authentication on socket connections, and no debouncing for rapid updates.
Code Quality & Project Structure3 / 10
Client/server separation with service files and context-based auth. But quality issues are pervasive: PollDetails.jsx, PollResults.jsx, response.service.js, and PollContext.jsx are all empty/stub files; poll.routes.js incorrectly imports router from auth.routes.js; no TypeScript; no tests; console.log debugging left in production (MyPolls.jsx:173,185,192,200,222; ForgotPassword.jsx:150,170,222); handleCopyLink logic duplicated in Dashboard and MyPolls; hardcoded Firebase API key in frontend; README is stock Vite template; no .env.example; index.css imported with wrong relative path from main.jsx.
137
PulseBoard
Ankit kumar singh · @ankit2003
33
PulseBoard is a full-stack polling platform with a visually appealing orange-themed UI. The core flow works: Google OAuth login → create polls with multi-question support → share via link → collect responses → view analytics with real-time Socket.IO updates. However, significant gaps exist across all dimensions: local email/password registration is implemented but unreachable (routes missing, frontend form non-functional), the published results page uses entirely hardcoded fake data, the dedicated analytics API endpoint goes unused, anonymous users have no duplicate vote prevention, there are no ownership checks on publish/analytics endpoints, security is minimal (no Helmet, CORS wide open, tokens in localStorage), and code quality suffers from duplicate models and empty files. The project shows good UI polish and architectural intent but falls short on backend robustness, auth completeness, and feature depth.
Authentication & Access Control3 / 10
Google OAuth via Passport.js works (passport.js:14-82, auth.routes.js:11-91, AuthSuccess.jsx:9-33). JWT authMiddleware extracts Bearer tokens and attaches req.user (authMiddleware.js:7-69). ProtectedRoutes checks localStorage (ProtectedRoutes.jsx:7-14). BUT: registerController/loginController exist (auth.controller.js:7-73) yet are NEVER mounted in routes — auth.routes.js only has Google OAuth routes. Register.jsx form has NO onSubmit API call (Register.jsx:60-115). Login.jsx only has Google button (Login.jsx:3-8). Tokens stored in localStorage, no httpOnly/secure/sameSite. No Helmet, CSRF, rate limiting. allowAnonymous/allowAuthenticated defined in poll schema (poll.model.js:104-117) but never enforced during response submission. Two conflicting User models (auth.model.js vs user.model.js). No /me endpoint or refresh tokens.
Poll Creation & Question Management5 / 15
CreatePoll.jsx (865 lines) supports dynamic multi-question creation with add/remove, per-question mandatory flag, per-question dynamic options with add/remove and min-2 enforcement. Poll schema (poll.model.js:61-150) has embedded questionSchema with optionSchema, createdBy ref, allowAnonymous/allowAuthenticated flags, expiresAt Date, isPublished Boolean, status enum. Server validates min 2 options and non-empty questions (poll.service.js:28-68). Client validates title, question text, and min 2 options before submission (CreatePoll.jsx:167-214). Expiry with DatePicker + quick presets (5min-7days) (CreatePoll.jsx:511-660). BUT: no poll edit endpoint (PATCH/publish only flips isPublished), no slug/permalink generation, no form library (manual useState), no draft status handling, publish doesn't check creator ownership (publishPollService.js:154-182), no delete endpoint, poll.validator.js only checks title and questions array existence (lines 5-44).
Response Collection Flow4 / 15
Response submission works: submitResponseService (response.service.js:9-139) checks poll existence, polls expiry (poll.expiresAt > new Date()), prevents duplicate auth responses via findOne, increments option.votes on poll document, emits Socket.IO vote_updated, and saves response. Client PollForm.jsx (355 lines) renders radio buttons per question, validates required questions client-side (PollForm.jsx:71-93), formats answers as {questionIndex, selectedOption} array. BUT: no duplicate prevention for anonymous users (only checks userId), no server-side mandatory question validation, response.validator.js is empty (0 lines), no check if poll requires authenticated responses, no DB-level unique compound index on {pollId,userId}, no transaction for vote+response save, selectedOption stored/comparing as text string (fragile), no unique compound index on response schema.
Analytics & Feedback Dashboard4 / 15
Analytics.jsx (501 lines) displays per-question Recharts pie charts, animated progress bars with percentages, leading option with 👑 badge, total votes per question, live indicator with pulsing dot, real-time updates via Socket.IO vote_updated event. Analytics service (analytics.service.js:7-153) computes total/anonymous/authenticated response counts with per-question optionCounts and percentages. Dashboard.jsx (409 lines) shows stat cards (total/active/expired polls) and PollCard list. BUT: PublishedResults.jsx (305 lines) is entirely hardcoded dummy data — no API calls, static example resultLink and poll object (lines 22-55). Analytics page fetches from /polls/${id} instead of using the dedicated /polls/analytics/${pollId} or /api/analytics/${pollId} endpoints. Dashboard "Responses" stat hardcoded to "0" (Dashboard.jsx:201-205). No CSV/export, no individual response viewing, no time-series or trend analysis, no peak activity metrics.
Frontend Experience5 / 10
Clean orange/amber color theme with consistent neo-brutalist design (heavy borders, drop shadows) across all pages using Tailwind CSS v4. 9 routes with proper React Router setup (AppRoutes.jsx:14-57). Responsive layout with mobile-friendly breakpoints. Framer Motion animations on Home page hero. Component separation: Navbar (auth-aware, sticky), PollCard (with copy link, publish, share, analytics actions), StatsCard. States handled: loading (Analytics spinner line 103-123, PollForm text line 159-174), empty (Dashboard "No Polls Yet" line 368-392), error (alerts), not found. BUT: uses window.alert() everywhere instead of react-hot-toast (installed but unused). Register form has NO functional submit logic (Register.jsx:60-115). Login page only has Google OAuth (no email/password). PublishedResults is fake static data. No confirmation dialogs, no field-level validation messages, no skeleton states.
Backend Architecture & API Design4 / 15
Clean module structure: auth/poll/response/analytics each with controller/service/model/routes/validator. ApiError class with static factories (badRequest/unauthorized/forbidden/notFound). ApiResponse.send() utility. Centralized errorMiddleware. RESTful API under /api/auth, /api/polls, /api/responses, /api/analytics. BUT: no input validation library (Joi/Zod/express-validator) — all manual ad-hoc checks. response.validator.js and socket.js config are empty files (0 lines). Two User models registered as "User" (auth.model.js, user.model.js). CORS wide open: cors() with no options, Socket.IO origin:"*". No Helmet, rate limiting, or body size limits. No ownership checks on publish (publishPollService) or analytics (getPollAnalyticsService). Local register/login controllers unreachable. Only 6 poll endpoints, 1 response, 1 analytics, 2 auth. No poll delete endpoint. No pagination. console.log in all production controllers.
Real-Time Updates Using WebSockets4 / 10
Socket.IO server integrated with HTTP server (server.js:27-41), pollSocket handles room-based pub/sub: clients join via "join_poll" event (poll.socket.js:16-29), server emits "vote_updated" with full poll data on response submission (response.service.js:115-123). Analytics.jsx joins room on mount, listens for vote_updated to update poll state in real-time, cleans up listener on unmount (Analytics.jsx:71-98). BUT: only one event type (vote_updated) — no events for poll publishing, expiry, or deletion. No authentication on socket connections. No reconnection room re-join logic. No debouncing/batching. Client never sends "leave_poll" — rooms accumulate. socket.js config file is empty (0 lines). "vote_updated" sends entire poll document (bloated payload). Only used on Analytics page, not on PollForm voting page. No Socket.IO rooms for analytics-specific updates beyond poll ID.
Code Quality & Project Structure4 / 10
Clean monorepo: frontend/backend separation with consistent module pattern (controller/service/model/routes/validator). ESM throughout. Frontend ESLint configured, Tailwind v4 via Vite plugin. Good library choices: Framer Motion, Recharts, react-datepicker, react-qr-code, react-hot-toast. Backend uses Mongoose embedded schemas properly. BUT: no TypeScript. Zero automated tests (no test scripts, no test files). Duplicate User models (auth.model.js, user.model.js) both registered with Mongoose. Two empty files (response.validator.js 0 lines, socket.js config 0 lines). Inconsistent code formatting — wildly varying indentation and blank line spacing. No .env.example. console.log debug statements in all production controllers. Hardcoded backend URL in 5 frontend locations (api.js, socket.js, Login.jsx, Navbar.jsx, PublishedResults.jsx). No centralized config or env variables on frontend. JWT payload inconsistencies (generateToken uses `id`, Google callback uses `_id`).
138
S
proper-poll
Shweta Nigam · @shwetanigam2106
32
proper-poll is a partially implemented polling platform with a working skeleton but critical defects throughout. The frontend has polished visuals (Framer Motion, Tailwind CSS, Recharts) with 5 functional pages, but 2 pages are static mockups and there's no Socket.IO client despite claiming real-time features. Backend has a clean layered architecture (routes/controllers/services/models) with TypeScript and Zod validation, but the hardcoded userId in poll creation, broken refresh token flow, flat response schema (no questionId), missing CRUD operations, and no error handler middleware prevent it from being production-viable. Total score: 32/100.
Authentication & Access Control5 / 10
Auth module has registration with email verification (Nodemailer), login with bcrypt, Google OAuth, JWT tokens, and a protect middleware. Frontend has working Login/Register pages with Google OAuth button. But: refresh token flow is broken (tokens never persisted in DB, making /refresh-token non-functional); most controllers have no try/catch → unhandled promise rejections; no global Express error handler; SMTP credentials logged to console at server.ts:15-16; access token expiry defaults to 7d (too long); no Helmet, no rate limiting on auth endpoints; frontend has no auth context — localStorage reads duplicated 7x across files with hardcoded "accessToken" string. Anonymous vs authenticated poll modes: allowAnonymous field exists in Poll model but is never checked in response flow; response route lacks protect middleware entirely.
Poll Creation & Question Management4 / 15
Poll creation form (CreatePollPage.tsx, 658 lines) supports dynamic questions with add/remove and dynamic options per question. Backend validates min 1 question, min 2 options per question via Zod (poll.validation.ts:42-47). However: poll.controller.ts:22 hardcodes userId = "68218d447c3f2e9f45e8a333" instead of req.user.id, defeating the auth system — every poll is created by a fake user. isRequired flag per question (poll.model.ts:24-27) is defined but never enforced server-side. allowAnonymous flag (poll.model.ts:58-61) defined but never checked. No poll edit or delete endpoints exist (only Create + Read). No datetime validation on expiryDate field (accepts any string). Frontend has no client-side validation beyond HTML5 required attributes.
Response Collection Flow3 / 15
Response submission route exists at POST /api/v1/responses (no protect middleware). Frontend ResponsePage.tsx (517 lines) fetches poll and renders option buttons. But severe issues: (1) Response model stores flat selectedOption with no questionId — cannot distinguish which question a vote belongs to, making multi-question polls unanswerable; orphaned answerSchema (response.model.ts:5-21) suggests the correct per-question schema was planned but abandoned. (2) No duplicate prevention — same user can submit unlimited responses. (3) No expiry checking — expired polls still accept responses. (4) No poll existence validation before creating response. (5) Zod validation only checks pollId and selectedOption are non-empty strings. (6) totalResponses counter on Poll model never incremented.
Analytics & Feedback Dashboard4 / 15
Analytics endpoint GET /api/v1/responses/analytics/:pollId returns { totalResponses, options[{option, votes, percentage}] }. Frontend PollAnalytics.tsx (696 lines) renders BarChart and PieChart via Recharts with animated progress bars and stat cards (total responses, option count, leading option). However: analytics cannot provide per-question breakdowns because the response model lacks questionId — all votes across all questions pool into one flat bucket. The isPublished field exists on Poll model but no endpoint toggles it and no publish-results feature exists. Frontend "Live" indicator is cosmetic only — updates via setInterval polling every 5 seconds, not WebSocket. totalResponses counter on Poll model stays at 0 permanently (never updated by response service). No CSV/export.
Frontend Experience5 / 10
7 routes defined in AppRoutes.tsx. 5 are functional: LoginPage, RegisterPage, CreatePollPage, ResponsePage, PollAnalytics. UI has consistent dark gradient theme with Framer Motion animations throughout (motion.div entrance effects, whileHover/whileTap). Tailwind CSS v4 for responsive styling. Recharts used for analytics charts. However: PollsPage.tsx (387 lines) is a completely static mockup with 4 hardcoded poll entries — no API calls, non-functional search/filter/buttons. HomePage is purely decorative marketing content. No form library (all forms use raw useState). Error handling is inconsistent: login uses alert(), PollAnalytics uses console.log() silently, ResponsePage sets error state but never renders it. No auth context or centralized state management. Socket.IO client not installed (no real-time UI). ~78% of frontend code is layout/styling vs ~22% application logic.
Backend Architecture & API Design5 / 15
Clean layered architecture: routes→controllers→services→models with TypeScript throughout. Zod validation schemas for auth, poll, and response inputs. Common utility pattern: ApiError class hierarchy with static factories (api-error.ts:45 lines), ApiResponse formatter (api-response.ts:52 lines), centralized validate middleware. Mongoose with embedded subdocument schemas (Poll → questions → options). CORS configured. However: no Express error-handling middleware — thrown ApiErrors in async controllers become unhandled rejections. No poll update/delete endpoints. No pagination on GET /polls. Response collection has no index on {pollId} despite every query filtering by it. No rate limiting, no Helmet security headers. extraneous .js imports in TypeScript source files. Console.log debug statements in auth.service.ts:275-277 and response.service.ts:18-22. MongoDB connection uses non-null assertion on MONGODB_URI (db.ts:5).
Real-Time Updates Using WebSockets2 / 10
Server-side Socket.IO exists: server.ts:26-32 initializes Socket.IO server with CORS config. socket.ts:22 lines handles connection, joinPoll room subscription (socket.join), and disconnect logging. One event emitted: response.service.ts:38-45 emits "pollUpdated" to pollId room with {pollId, totalResponses, selectedOption} after each response. However, the frontend has ZERO WebSocket implementation: no socket.io-client in package.json, no Socket.IO imports anywhere in frontend code, no connection/join/event-listening logic. PollAnalytics page uses setInterval polling every 5 seconds (lines 71-74) instead. The architecture is also fragile: io is exported as mutable module-level let from server.ts, creating implicit initialization ordering dependency. No events emitted for poll creation, updates, deletions, or analytics. The README's "real-time" claims are misleading.
Code Quality & Project Structure4 / 10
Clean monorepo with backend/frontend separation, TypeScript in both, ESLint on frontend. Module-based architecture (auth/poll/response modules with domain files). Common utilities for errors, responses, validation, and JWT. However: duplicated validate middleware (auth.validator.ts duplicates common/middleware/validate.ts with slightly different behavior). 7x duplicated localStorage.getItem("accessToken") pattern across frontend files with no shared auth utility. Dead code: answerSchema in response.model.ts (lines 5-21, defined but never used), updateProfileSchema and forgotPasswordSchema (schemas exist but no routes/controllers), commented-out route sections in poll.routes.ts and response.routes.ts. No tests anywhere. No .env.example files. Console.log debug statements in production code (server.ts, auth.service.ts, response.service.ts). README describes non-existent features (payment module, subscription module, "advanced analytics") and empty directories (feedback/, payment/, subscription/ folders don't exist in modules). CSS duplication: Navbar/Footer share identical logo markup pattern.
139
J
pulseboard-mern
Joy Swarnakar · @joyswarnakar3
32
Pulseboard-mern is a functional but shallow MERN polling platform with ~1,230 lines of source code across 26 files. It implements the core happy path: JWT auth, poll creation with dynamic questions/options, response submission, basic analytics with Socket.io live refresh, and result publishing. However, nearly every feature has significant gaps. Auth returns password hashes on register and blocks anonymous responses (all response routes require authentication). Poll creation hardcodes expiry and anonymous flags with no server-side validation (req.body spread is an injection vector). The analytics dashboard is a static placeholder. Socket.io has room infrastructure that the client never uses, broadcasting globally instead. Code quality suffers from duplication, dead code across 6 files, zero tests, and no input validation anywhere. No deployment URLs exist. Overall: a working demo that covers the requirements superficially but lacks the depth, security, and polish expected of a production-quality submission.
Authentication & Access Control4 / 10
Register/login endpoints with bcrypt (10 rounds) and JWT (7d expiry) in authController.js (71 lines). authMiddleware.js (30 lines) verifies Bearer tokens and attaches req.user. Frontend has AuthContext (34 lines) with localStorage hydration, ProtectedRoute component, Login.jsx (99 lines), Register.jsx (98 lines), and axios interceptor for Bearer tokens. Critical gaps: password hash is returned to client on register (authController.js:26 sends full user object), response routes always require auth middleware (responseRoutes.js:8) breaking anonymous poll mode, no input validation on auth fields, JWT in localStorage (XSS vulnerable), no rate limiting, no email verification, no password reset, and no Helmet/CSRF.
Poll Creation & Question Management4 / 15
PollForm.jsx (121 lines) provides dynamic add question, add option per question, and required/optional toggle via checkbox. Poll model embeds questionSchema with question text, required boolean, and options array. CreatePoll.jsx (82 lines) wraps PollForm with a title input. However: `anonymous: true` and `expiresAt: "2026-12-31"` are hardcoded (CreatePoll.jsx:27-28) — users cannot configure anonymous mode or set expiry. No server-side validation exists — `{...req.body}` spread in pollController.js:5-6 is an injection vector. No poll editing endpoint, no option to remove questions, no form library (raw useState), no "my polls" listing (Dashboard is a static placeholder), and error handling just `console.log(error)`.
Response Collection Flow4 / 15
ResponseController.js (32 lines) checks poll existence (404), enforces expiry (400), and creates Response documents with per-question answerSchema (questionId + selectedOption). PublicPoll.jsx (107 lines) renders radio buttons per question with proper answer state management. However: all response submissions require authMiddleware (responseRoutes.js:8), contradicting the anonymous poll feature entirely. No duplicate vote prevention — same user can submit unlimited responses. No server-side validation that submitted options actually belong to the poll, no enforcement of required questions server-side or client-side. No try-catch on fetch/submit calls. Only `alert("Submitted")` for feedback with no success state, no expired/closed poll UI states.
Analytics & Feedback Dashboard4 / 15
AnalyticsController.js (33 lines) computes totalResponses count and per-question per-option vote summaries via brute-force nested forEach. Analytics.jsx (79 lines) renders total response stat card, per-question progress bars with percentages, and listens for Socket.io new-response events for live refresh. PublishedResults.jsx (90 lines) provides a public-facing results view with the same visualization pattern. PublishResults endpoint sets published=true (pollController.js:31-39). However: analytics endpoint has no authentication, displays MongoDB _id as question labels instead of question text, no ownership check on analytics, Dashboard is entirely hardcoded (static "Quarterly Team Feedback" card at Dashboard.jsx:35-46), no try-catch in getAnalytics or publishResults, no export/CSV functionality, no individual response viewing, no trend/time-series data.
Frontend Experience5 / 10
Clean modern UI with Tailwind CSS v4, custom theme tokens in index.css (14 lines), gradient accents, card layouts with hover effects, animated progress bars (transition-all duration-1000), and custom radio buttons in PublicPoll. React Router DOM v7 with 8 route definitions. Responsive design through Tailwind utilities. However: Dashboard is a static placeholder with hardcoded content, all errors use `alert()` (Login.jsx:41, Register.jsx:30, CreatePoll.jsx:32, PublicPoll.jsx:26), no loading states beyond plain "Loading..." text, no mobile hamburger menu, no dark mode, no landing page (root redirects to login), `error.response.data.message` accessed without null-checking error.response, and no form validation beyond HTML5 required attributes.
Backend Architecture & API Design4 / 15
Clean MVC structure (routes→controllers→models) with ESM modules throughout. 3 Mongoose models with embedded subdocuments, 7 REST API endpoints with proper HTTP methods, thin route files that delegate to controllers. However: zero input validation anywhere — no Joi/Zod/manual checks — and `{...req.body}` spread in pollController.js:6 is a direct database injection vector. Password hash returned to client on register. `publishResults` (pollController.js:31-39) and `getAnalytics` (analyticsController.js:7-34) have no try-catch blocks. No rate limiting, no Helmet, no centralized error handler, no request body size limits, `error.message` leaked to all clients. CORS `*` on Socket.io (server.js:20). No ownership check on publish/analytics. Inconsistent route file naming (PollRoutes.js PascalCase vs camelCase for others).
Real-Time Updates Using WebSockets4 / 10
Socket.io server is configured with room-based infrastructure (join-poll, leave-poll events, connectedUsers Map with active-users count broadcast) in socket.js (52 lines). Response submission emits `new-response` via `req.io.emit` (responseController.js:25-27). Frontend Analytics.jsx listens for `new-response` and triggers HTTP refetch (Analytics.jsx:15-22). However: the client never emits `join-poll` or `leave-poll` — the room infrastructure exists server-side but is unused. `new-response` is broadcast globally (req.io.emit is broadcast, not room-targeted), so all connected clients refresh regardless of which poll they're viewing. No data is pushed via socket — it's a blind refetch trigger. No debouncing. No events for publish/delete/expiry. No socket integration on the voting page. The `initSocket` function in socket.js is dead code (never called).
Code Quality & Project Structure3 / 10
Good monorepo separation (frontend/ + backend/) with clean folder structure and ESM modules throughout. docker-compose.yml for MongoDB, .env.example present, frontend ESLint configured. However: zero tests (no test runner or test files), no TypeScript, no backend linting at all. Substantial code duplication: Login.jsx and Register.jsx share identical handleChange logic and near-identical form markup; Analytics.jsx and PublishedResults.jsx share identical bar chart rendering (~25 duplicated lines). 10 instances of dead/commented-out code across 6 files from incomplete CJS→ESM migration. 3 unused imports (User in app.js:9, express in analyticsController.js:3, express in authMiddleware.js:7). Inconsistent file naming (PollRoutes.js PascalCase vs all other camelCase routes). Frontend hardcodes localhost URLs despite README documenting VITE_API_URL/VITE_SOCKET_URL env vars. Inconsistent formatting (mixed quotes, semicolons, indentation).
140
A
PollBoard
Akhtar Raza · @akhtarrazamirza111
31
PollBoard is a functional but roughly-built polling platform (1 working deployment at pollboard-opal.vercel.app). It has working auth (registration, login, JWT, password reset), a dynamic poll creation form with live preview, public voting pages, Recharts-based analytics with bar/pie charts, and Socket.IO real-time updates. However, nearly every feature has significant gaps: no server-side validation anywhere, the required-question toggle is broken (controlled checkbox without onChange), expiry enforcement is client-only, vote counting has race conditions, no Helmet/rate-limiting/CSRF, 19 empty component files, console.log pollution (19 instances), a default Vite template README, and hardcoded Firebase credentials. The project demonstrates effort and functional happy paths but lacks the robustness expected of a production-style application. Total score: 32/100.
Authentication & Access Control4 / 10
Working registration (bcrypt 10 rounds), login, JWT (7d expiry), forgot/reset password with email token + phone-based flows (auth.controllers.js:342 lines), and JWT auth middleware (auth.middleware.js:40 lines). AuthProvider context with localStorage persistence (Auth.context.jsx:55 lines). However: CreatePoll and MyPolls pages lack ProtectedRoute wrappers in App.jsx (lines 46-52), meaning unauthenticated users can access them. The allowAnonymous toggle is defined in schema (Poll.models.js:60-63) but never enforced server-side during voting (poll.controllers.js:117-203 ignores it). No Helmet, no CSRF, no rate limiting. JWT stored in localStorage. resetPasswordWithPhone lacks OTP verification (auth.controllers.js:295-342).
Poll Creation & Question Management3 / 15
CreatePoll.jsx (634 lines) has dynamic questions with add/remove, dynamic options (add only — no remove for individual options), live preview panel, anonymous toggle, and expiry datetime picker. CRITICAL BUG: the "Required Question" checkbox (CreatePoll.jsx:495-503) uses `checked={question.required}` without an `onChange` handler — it's a controlled input that cannot be toggled, making mandatory/optional non-functional. No client-side validation (empty title/questions/options allowed). No server-side validation at all — no Joi/Zod/express-validator, controller blindly accepts req.body (poll.controllers.js:5-49). No poll edit endpoint exists. No slug/unique URL generation. No draft status.
Response Collection Flow4 / 15
PublicPoll.jsx (548 lines) renders poll questions with radio-style option selection, client-side expiry check (lines 35-41, 513-522) with expired banner and disabled buttons, localStorage-based duplicate voting prevention (votedPolls array, lines 70-88), and format answers for submission (lines 117-132). Server stores Response documents and increments embedded option.votes counters (poll.controllers.js:117-203). Gaps: expiry enforcement is client-side ONLY — API has no check, so expired polls accept submissions via direct API call. No server-side duplicate prevention (client localStorage only). Vote counting is read-modify-write (race condition — should use $inc). No required question enforcement on submission. No questionIndex bounds checking — malformed indices crash. Poll mutation fetches then saves (no atomic update).
Analytics & Feedback Dashboard5 / 15
Analytics.jsx (486 lines) renders Recharts BarChart + PieChart per question with custom Cell colors, Framer Motion animated percentage progress bars, option counts with percentages, and a total responses stat card. Dashboard.jsx (309 lines) shows 4 stat cards (totalPolls, totalResponses, activePolls, publishedResults) and recent polls with embedded vote counts. Results.jsx (304 lines) shows public published results with per-option percentage bars and isPublished gate (lines 58-70). Server analytics endpoint (poll.controllers.js:205-253) returns pollTitle, Response.countDocuments, and questions with vote data. Publish endpoint with owner check (poll.controllers.js:256-317). Gaps: no CSV/export, no individual response viewing, no trend/time-series data, dashboard silenty swallows errors (empty catch block at line 58-62), no leader/top-option summary, totalResponses computation via nested forEach loops on dashboard (poll.controllers.js:365-382).
Frontend Experience5 / 10
Consistent dark gradient theme across all poll pages with Tailwind CSS and Framer Motion animations (page transitions, staggered question reveals, progress bar animation). Loading states (animated pulse text), error states (Poll not found, Results not published, Analytics not found), success/already-voted states all handled. Home page (538 lines) has a well-designed landing page with mobile hamburger menu, Lucide icons, and sticky navbar. Auth layout (Auth.layout.jsx:39 lines) with branded background glows. React Router with 10 routes. Toast notifications via react-hot-toast. Major gaps: no persistent navbar/sidebar on poll pages (Navbar.jsx, Sidebar.jsx, PageWrapper.jsx are all empty 0-byte files — 19 total empty component stubs), navigation relies on links embedded in individual page content. Copy-to-clipboard uses deprecated document.execCommand("copy") (Dashboard.jsx:84-116, MyPolls.jsx:89-119). No form library on poll creation. No confirmation dialogs for destructive actions.
Backend Architecture & API Design4 / 15
Working Express + Mongoose backend with 15 endpoints across auth and polls. JWT middleware is clean (auth.middleware.js:40 lines). Three Mongoose models with embedded sub-schemas (Poll has embedded questionSchema+optionSchema). CRITICAL GAPS: zero input validation — no Joi/Zod/express-validator installed, controllers destructure req.body blindly. No centralized error handler — every catch block returns 500 with raw error.message (19 identical patterns). No Helmet, no rate limiting, no express-mongo-sanitize. No service layer — controllers contain all business logic (poll.controllers.js at 483 lines). No proper indexing — missing index on Response.pollId (analytics does collection scan), missing on Poll.createdBy. Router bug: poll.routes.js:3 imports router from auth.routes.js instead of creating express.Router(). Deprecated "crypto" npm package installed (v1.0.1) instead of Node built-in. No pagination. CORS whitelists specific origins but Socket.IO uses origin:"*".
Real-Time Updates Using WebSockets3 / 10
Socket.IO server configured with CORS origin:"*" (server.js:20-25). Server emits single "pollUpdated" event after each vote submission via io.emit() (poll.controllers.js:181). Client connects via singleton (socket.js:5) and Analytics.jsx listens for "pollUpdated" with socket.on, triggers silent refetch on receipt, cleans up listener on unmount (lines 74-84). GAPS: no room-based approach — io.emit broadcasts to ALL connected clients regardless of which poll they're viewing (analytics for any poll receives events from any poll). No data pushed over socket — it's a pure invalidation signal, client does full HTTP refetch. Only 1 event type. No per-poll subscription/join. No reconnection/room re-join logic. No debouncing on client. No Socket.IO integration on voting pages or dashboard. Socket connections have no authentication.
Code Quality & Project Structure3 / 10
Client/server monorepo without workspace tooling (no root package.json, separate package-locks). Plain JavaScript throughout (no TypeScript). 19 empty component stub files (Navbar.jsx, Sidebar.jsx, LoadingSpinner.jsx, PollCard.jsx, etc. — all 0-byte bodies). README is default Vite template (no setup instructions, no architecture docs, no API docs). Code duplication: handleCopyLink verbatim in Dashboard.jsx and MyPolls.jsx, JWT signing duplicated in signup+login. 19 console.log statements left in production (including "DELETE START", "TRY START" debug logs in MyPolls.jsx:173-226). Firebase credentials hardcoded in firebase.js (apiKey, projectId, appId). Deprecated "crypto" npm package. Unconventional formatting (excessive line breaks). No tests, no ESLint on server, no .env.example. Only positive: consistent patterns across pages and controllers.
141
S
pollApp
Sayed Meer Hamza · @sayedmeerhamza
31
Working full-stack polling app deployed at Vercel (frontend) and Render (backend) with ~5000 lines of code across 30 files. Has authentication (email+Google OAuth), poll creation with dynamic options, response collection with expiry enforcement, analytics with Recharts, and Socket.IO infrastructure. However, the project has several critical bugs: (1) Socket.IO event name mismatch ("pollUpdated" vs "voteUpdate") breaks real-time updates entirely, (2) Poll schema design conflict where options are stored as strings but treated as objects in vote counting, (3) only single-question support despite array structure, (4) no publish/results feature despite isPublished field existing, (5) shadowed admin route makes auth-gated stats unreachable, (6) no input validation library, and (7) App.css is entirely Vite starter template. Overall score: 31/100.
Authentication & Access Control5 / 10
Has working email/password registration with bcrypt (10 rounds), login with JWT (7-day expiry), Google OAuth integration, authMiddleware that verifies JWT and attaches req.user, and ProtectedRoute component. However: no anonymous/authenticated poll mode toggle exists (voting is always public, no configurable modes); tokens stored in localStorage (XSS-vulnerable); no Helmet/CSRF protection; no rate limiting on auth endpoints; no email verification or password reset; no refresh token rotation. Poll model has isPublished field but no endpoint toggles it (authRoutes.js:123-153, server/middleware/authMiddleware.js:1-37, client/src/components/ProtectedRoute.jsx:1-14).
Poll Creation & Question Management5 / 15
CreatePoll.jsx (228 lines) has title, question, dynamic options (add/remove with min 2 enforced), expiry datetime-local picker, client-side validation (title, question, options non-empty, min 2 options), and dual-mode create/edit. However: ONLY supports a single question — the entire form hardcodes questions[0] in all handlers (lines 58, 62, 67, 124, 130, 136, 142, 147, 175, 191, 203) despite using an array structure. The required field is always hardcoded to true (line 82) — no mandatory/optional toggle exists. No server-side validation of questions/options structure (no Zod/Joi). No slug/permalink generation. No multiple question support despite the schema allowing it.
Response Collection Flow4 / 15
PollPage.jsx (219 lines) renders radio buttons per question, validates at least one selection, checks expiry client-side with live countdown timer, prevents duplicate votes via localStorage votedPolls array, and shows "Already voted" screen. Server checks expiry before accepting (pollRoutes.js:56-61). However: Poll schema stores options as [String] but response handler treats them as objects with .text property (pollRoutes.js:68-69) — vote count increments potentially broken since opt.text is undefined on string elements. No server-side duplicate prevention (no unique compound index on {pollId, voterId}). No server-side validation that submitted options belong to the poll. No required/mandatory question enforcement server-side. Response schema has typo: require: true instead of required: true (Response.js:7).
Analytics & Feedback Dashboard4 / 15
Analytics.jsx (211 lines) shows per-question option breakdowns with Recharts BarChart (custom tooltip, colored bars) and toggle to progress bar view, plus total responses badge. Admin dashboard has stat cards (total polls/users/votes), polls-per-day bar chart, recent polls list (AdminDashboard.jsx:178 lines). However: isPublished field on Poll model is NEVER toggled — no publish endpoint exists, so public results viewing is impossible. Analytics computed via brute-force O(n*m) nested loops (pollRoutes.js:99-113). No CSV/export, no trend data or time-series analysis, no individual response viewing. Analytics page has NO frontend auth gate (route not wrapped in ProtectedRoute in App.jsx). Admin stats route is shadowed — two GET /stats handlers, only the first (no-auth) runs (admin.js:11,89).
Frontend Experience5 / 10
Clean landing page (Home.jsx:201 lines) with hero section, how-it-works steps, feature grid, auth-aware CTA. Nine routes with React Router including protected routes. States covered: loading spinners (Analytics, AdminDashboard), error handling with redirect on 401, empty states for no polls/search. Countdown timer on voting page, share link with clipboard copy+confirmation, responsive CSS (Navbar.css, Home.css). However: uses alert() for ALL notifications (login, register, vote, create, delete — no toast system); no confirmation dialog before delete; Dashboard uses 190-line inline style object while other pages use CSS files (inconsistent); App.css is entirely Vite starter template (185 lines of dead code: .hero, .counter, #next-steps, #docs, #spacer, .ticks); no dark/light mode toggle; no success modals.
Backend Architecture & API Design3 / 15
Express server with separated route files (authRoutes, pollRoutes, admin), Mongoose models (Poll, Response, User), and auth middleware. However: NO input validation library (no Zod/Joi/express-validator) — req.body passed directly to Mongoose. Critical schema design conflict: optionSchema (with text+votes) defined but unused; questionSchema uses options: [String] not [optionSchema] (Poll.js:3-15), yet response handler treats options as objects (pollRoutes.js:68-69). Admin route shadowing: two GET /stats handlers on same router, second (auth-gated) unreachable (admin.js:11,89). CORS registered twice (server.js:16 and 29-34), second after routes mounted. No rate limiting, Helmet, request body size limits. Raw error.message leaked to clients in 6+ catch blocks. No pagination on GET /api/polls. No .env.example. package.json main points to non-existent index.js. Config.js never imported.
Real-Time Updates Using WebSockets2 / 10
Socket.IO server initializes on HTTP server with singleton getIO() pattern (socket.js:28 lines). Server emits on vote submission (pollRoutes.js:76). Client connects and listens (Analytics.jsx:20,56). BUT: CRITICAL BUG — server emits "pollUpdated" (pollRoutes.js:76) while client listens for "voteUpdate" (Analytics.jsx:56) — completely mismatched event names, so real-time updates NEVER trigger. Additional issues: Socket.IO CORS only configured for localhost:5173, not the production Vercel URL; no room-based subscriptions (broadcasts to ALL connected clients); only one event type emitted (no events for poll deletion, updates, or publishing); no data pushed over socket (client refetches HTTP); no debounce; no reconnection/room re-join logic; no socket authentication; no socket integration on voting page.
Code Quality & Project Structure3 / 10
Clean client/server directory separation with consistent file naming (PascalCase components, camelCase utilities). ESLint configured for client. README with setup instructions and API docs. However: no TypeScript (all plain JS). Zero automated tests (server test script: echo "Error: no test specified" && exit 1). Significant dead code: App.css is entirely Vite starter template (185 lines), optionSchema defined but never used (Poll.js:3-9), unused Poll import in authRoutes (authRoutes.js:5), unused useState(count) in App.jsx:16. Console.log statements in production (authRoutes.js:44,49,72,78,150; pollRoutes.js:80). Inconsistent styling approaches (Dashboard inline styles vs CSS files). Server config.js redundant (never imported). No .env.example in server/. Schema modeling bugs (option schema conflict). package.json main points to non-existent index.js.
142
pollX
Amit Dewangan · @dewanganamit40_ceedf3b0
31
pollX is a partially functional polling application with live deployments (frontend on Vercel, backend on Render). The core CRUD flow works (register, login, create poll, vote, view results) but with significant gaps across every dimension. The Socket.IO implementation - one of the key requirements - is entirely commented out and non-functional, with a runtime crash bug in PollDetails.jsx. Response validation is minimal (no expiry check, no duplicate prevention, no required question enforcement). UI has nice visual polish (Tailwind, Framer Motion, dark/light theme) but lacks loading/error/empty states. Backend architecture is clean with TypeScript + Drizzle ORM + PostgreSQL but missing validation library, security middleware, and service layer. Console.log debugging remains in production code. Score range: 31/100 - a below-average submission that shows effort but has critical gaps in real-time functionality and validation.
Authentication & Access Control5 / 10
Server has register (auth.controller.ts:9-73) with bcrypt 10 rounds and JWT 7d expiry, login (lines 75-126), protect middleware (auth.middleware.ts:4-34) that verifies Bearer token and attaches req.user. Client has Login (Login.jsx, 130 lines), Register (Register.jsx, 138 lines with client-side name/password length check), ProtectedRoute (simple localStorage token check), and Navbar that shows/hides based on token. However: JWT stored in localStorage (no httpOnly cookies), no refresh token, no password reset, no email verification, no rate limiting, no helmet/CSRF, allowAnonymous field exists in DB schema but anonymous vs authenticated poll modes are never enforced server-side in votePoll (poll.controller.ts:134-194). JWT_SECRET uses non-null assertion (process.env.JWT_SECRET!) in 3 locations risking crash. No logout endpoint on server.
Poll Creation & Question Management5 / 15
CreatePoll.jsx (541 lines) supports dynamic questions with add/remove (min 1 enforced by Trash2 hide), dynamic options per question with add/remove (min 2 enforced), title/description/expiry date inputs, and slug-based link generation on success with copy-to-clipboard. Server createPoll (poll.controller.ts:11-101) validates title and expiresAt, generates slug with timestamp suffix, and inserts poll+questions+options via for-loops. DB schema has required boolean on questions. However: mandatory/optional question toggle is completely missing from UI (always hardcoded required:true in useState), allowAnonymous hardcoded to true on frontend (CreatePoll.jsx:39), no poll edit/delete endpoints exist, no client-side validation beyond HTML5 required on date, no server-side validation of question text or option count, no poll listing/dashboard for creators, no slug collision handling beyond timestamp suffix.
Response Collection Flow3 / 15
PollDetails.jsx (193 lines) fetches poll by slug, renders questions with radio button-like option selection, and submits formatted answers to POST /api/polls/:slug/vote. Server votePoll (poll.controller.ts:134-194) creates response+answers records and returns success. However, critical gaps: (1) No expiry check - expired polls accept votes, (2) No required question validation - server doesn't verify required questions were answered, (3) No option validity check - submitted optionIds aren't verified to belong to this poll's questions, (4) No duplicate vote prevention - no unique index, no localStorage tracking, no userId on responses, anonymousToken field defined but never populated, (5) No anonymous vs authenticated enforcement despite allowAnonymous field, (6) CRITICAL BUG: PollDetails.jsx line 4 imports socket from commented-out file, but lines 42-54 call socket.on("pollUpdated", ...) causing a ReferenceError crash on mount (socket is undefined).
Analytics & Feedback Dashboard4 / 15
PollResults.jsx (320 lines) shows per-question option vote counts with calculated percentages, animated progress bars (CSS transition width), and Recharts PieChart visualization. Server getPollResults (poll.controller.ts:196-272) queries per-option vote counts via N+1 count() queries in a nested for-loop. Summary cards show total questions count, "Active" status (hardcoded text, not actual status from DB), and "Live" analytics label. However: no total response count is computed or displayed, no anonymous/authenticated respondent breakdown, no time-series or trend data, no peak activity metrics, no CSV/PDF export, no publish results feature (isPublished field in schema:51 but never toggled by any endpoint), no individual response viewing, pie charts only support 2 alternating orange colors (hardcoded #ff7b00/#cc5500), no creator-only analytics gating (results are fully public, no distinction between draft/active/expired), no loading or error states.
Frontend Experience5 / 10
6 React routes (Home, Login, Register, CreatePoll, PollDetails, PollResults) with React Router DOM v7. Tailwind CSS v4 for styling, Framer Motion for page/section animations (hero entrance, navbar slide, hover effects), dark/light theme via ThemeContext with localStorage persistence, react-hot-toast for notifications. Landing page (Home.jsx, 270 lines) has animated hero, stat cards, feature preview cards with animated progress bars. CreatePoll has dynamic form with question add/remove animations. Copy-to-clipboard on poll creation success. Responsive layout using Tailwind responsive classes. However: no loading states anywhere (no spinners/skeletons), no empty states, no error boundaries, errors silently console.log'd, no poll listing/dashboard page for users, no confirmation dialogs for destructive actions, no form library (manual useState throughout), PollDetails ignores theme system (always dark bg-black), no offline/PWA support.
Backend Architecture & API Design5 / 15
Clean TypeScript Express backend (699 lines source) with controller/route/middleware/DB separation. 8 API endpoints: GET /api/auth/test, GET /api/auth/me, POST /api/auth/register, POST /api/auth/login, GET /api/polls/test, POST /api/polls (protected), GET /api/polls/:slug, POST /api/polls/:slug/vote, GET /api/polls/:slug/results. Drizzle ORM with 5 related tables (users, polls, questions, options, responses, answers) and proper relations in schema.ts (157 lines) with 2 SQL migration files. CORS configured for FRONTEND_URL with fallback. But: no input validation library (no Zod/Joi/express-validator) - all validation is manual if-checks, no centralized error handler, no service layer (controllers contain all logic), no rate limiting, no helmet, no request body size limits, mongoose listed as dependency but unused (using Drizzle+postgres), JWT_SECRET! non-null assertions in 3 locations, N+1 query in getPollResults (count per option in nested loop), no pagination, no poll listing endpoint, unused Socket.IO Server import, double error logging (console.error + res.status in catch blocks), no graceful shutdown.
Real-Time Updates Using WebSockets0 / 10
Socket.IO is 100% non-functional. server.ts lines 9-26 comment out the entire http.createServer + Socket.IO setup including the setIo/io.on connection handlers. socket.ts (6 lines) is entirely commented out - just stubs for setIo/getIo. Client socket.js (4 lines) is entirely commented out. The getPollResults controller has commented-out io.emit (lines 217-219). PollDetails.jsx imports socket from "../socket" (line 4 commented out) but references undefined socket at lines 42-54 (socket.on/socket.off), causing a runtime crash. Server starts with plain app.listen (line 45), not http server. Zero working WebSocket/Socket.IO functionality anywhere in the deployed application.
Code Quality & Project Structure4 / 10
Monorepo structure with client/Frontend/ and server/ separation, each with own package.json. Server in TypeScript (strict mode in tsconfig), frontend in JSX. ESLint configured on frontend with flat config format. Proper .gitignore. README.md at root with project structure, features, tech stack, deployment URLs, and sample screenshots. TypeScript Express.Request augmentation in types/express.d.ts. Drizzle migrations properly versioned. However: no .env.example file; ~25 console.log statements in server source code (~6 in client); process.env.JWT_SECRET! non-null assertions in 3 locations across auth.controller and middleware; unused mongoose dependency in server package.json; PollDetails.jsx will crash at runtime (undefined socket reference); no automated tests (test script returns "Error: no test specified"); no TypeScript on frontend; inconsistent ESM import style (.js extensions); schema relations defined for polls/questions/options but missing for responses/answers; root package.json only has Prisma dependencies (unused); minimal code comments; no graceful shutdown.
143
A
surveyLemons
ansh · @ansh_dietcoke
31
surveyLemons is a functional but minimal polling application built with vanilla JS frontend and Node/Express/MongoDB backend. It successfully implements JWT authentication, multi-question poll creation with mandatory/optional toggles, real-time Socket.io voting, basic analytics with result publishing, and is deployed on Render. However, it falls short on several core requirements: no poll expiry time (a key spec requirement), no anonymous vs authenticated response modes, no duplicate vote prevention, analytics page lacks real-time updates, and the codebase has real bugs (typo in Mongoose schema, broken npm start script). The vanilla JS frontend lacks responsiveness and SPA routing. Overall, the project demonstrates the core concepts but with significant gaps across all evaluation dimensions.
Authentication & Access Control5 / 10
Working JWT cookie-based auth with registration, login, protected routes, and logout. User model (user.js:30 lines) has bcrypt 12-round hashing with pre-save hook and comparePassword method. Auth middleware (auth.js:17 lines) verifies JWT from httpOnly cookie and attaches req.user. Routes /create-poll and POST /poll/create are protected. However: cookies lack secure/sameSite flags (user.js:36-39), no anonymous/authenticated poll mode exists anywhere (neither schema field nor UI toggle), no CSRF protection, no email verification, no password reset, and expired tokens silently redirect with no error feedback.
Poll Creation & Question Management4 / 15
Card-based poll creation UI (createPoll.js:78 lines) supports dynamic question adding with mandatory/optional checkbox, client-side validation for question text and min 2 options. Backend formats questions with vote arrays initialized to zero (poll.js controller:3-19). Critically missing: NO expiry time support whatsoever (no field in pollSchema, no UI, no auto-expiry logic) — this is a core requirement. Also missing: poll editing, poll deletion, option add/remove beyond fixed 4, anonymous/authenticated poll toggle, slug/permalink, server-side validation, and draft status.
Response Collection Flow3 / 15
Response collection is entirely Socket.io-based with no REST API endpoint. Users vote by clicking options which emit socket "vote" events; the server handler (server.js:73-86) directly increments votes[questionIndex].votes[optionIndex] in the Poll document and saves. No separate Response collection exists. Key gaps: no duplicate vote prevention (same user can vote unlimited times), no expiry checking (expiry doesn't exist), no server-side validation on option indices, no user identity capture on responses, no anonymous vs authenticated response handling, and the submit button merely navigates to analytics without performing a real submission — all votes were already persisted live.
Analytics & Feedback Dashboard4 / 15
Analytics page (responsePage.js:107 lines) renders summary cards (Total Responses, Questions, Votes Cast, Avg Votes/Q), per-question breakdowns with option vote counts, percentages, CSS progress bars, and "Leading" tag on top options. Publish button triggers POST /poll/:id/publish (auth-gated, creator-only check at poll.js controller:27-36) and UI shows published badge. Gaps: no creator dashboard listing all polls, no real-time updates on analytics page (only one-time fetch, no Socket.io connection), no individual response viewing, no CSV/export, no trend data or time-series, no anonymous vs authenticated breakdown, and no auth gate prevents non-creators from seeing unpublished results.
Frontend Experience4 / 10
Vanilla JS static HTML pages (6 pages) with consistent blue/gold color scheme (#003f88 / #fdc500). Nice touches: Remixicon icons, 3D card flip animation on poll creation, mandatory-popup toast, HTML5 progress bars for voting. But: no responsive design (fixed 400px widths, no media queries), HTML titles are all "Document", no SPA routing (full page reloads), error handling limited to alert() calls only, no loading/skeleton states (except one text string in responsePage), no form library, no confirmation dialogs, and no mobile-friendly layout.
Backend Architecture & API Design4 / 15
Basic MVC separation with models/, controllers/, routes/, middleware/ directories. Mongoose schemas (poll.js:34 lines, user.js:34 lines) are clean though minimal. REST endpoints: POST /signup, POST /login, POST /poll/create, GET /poll/api/:id, POST /poll/:id/publish. Gaps: no input validation library (no Joi/Zod/express-validator), no server-side validation on poll creation beyond Mongoose, no rate limiting, no Helmet/security headers, no CORS config, no centralized error handling, no service layer, no pagination, no separate Response schema (votes stored inline in Poll document), and Socket.io handler in server.js directly manipulates database bypassing controller/service layers.
Real-Time Updates Using WebSockets4 / 10
Socket.io integrated with HTTP server (server.js:19), room-based pattern: clients join `pollId` room on "join-poll" event (index.js:14). Server handles "vote" event by incrementing votes in DB and broadcasting "vote-updated" with updated vote counts to all room members (server.js:73-86). Client-side displayPoll.html connects Socket.io on load, renders progress bars with live percentages (index.js:66-75). However: analytics page (responsePage.js) has ZERO Socket.io code — no real-time analytics updates at all, only one-time fetch. Also missing: no reconnection/room re-join logic, no socket authentication, no debouncing, no leave-room handling, and socket handler directly modifies DB document.
Code Quality & Project Structure3 / 10
Flat project structure with no src/ or app/ directory. Real bugs: Mongoose schema typo — "require: true" instead of "required: true" on password field (user.js:16), meaning password not enforced at DB level. npm start script typo: "mode server.js" instead of "node server.js" (package.json:8), breaking the start command. No TypeScript, no ESLint/Prettier, no automated tests, no .env.example. Inconsistent code formatting and mixed-language comments (Hindi/English). Server.js mixes route definitions, DB connection, and Socket.io logic. Duplicated background-image URLs across 3 CSS files. MVC separation exists but is shallow.
144
Poll Cast
Pritam Mandal · @pritamdevbit
30
This submission ("Poll Cast") is a partially-built polling app with a polished visual design but significant functional gaps across all criteria. The Clerk-based auth and Socket.IO real-time voting form the strongest aspects, but the application fundamentally fails to implement multiple core requirements: no multiple questions per poll, no mandatory/optional question handling, no anonymous polling mode, no analytics dashboard, no results publishing, and no poll editing. The backend is a monolithic single file with critical bugs (double-response pattern in every route) and no input validation. The frontend has many non-functional UI placeholders, hardcoded data in the polls list, and an essentially empty dashboard. Total score: 30/100.
Authentication & Access Control5 / 10
Clerk-based auth is solid: sign-in (sign-in.tsx, 167 lines) with email/password, sign-up (sign-up.tsx, 231 lines) with email verification + firstName, Google and GitHub OAuth, SSO callback (SSOCallback.tsx), and ClerkProvider wrapping all routes (App.tsx:19-26). Protected pages (Dashboard, Polls, CreatePoll, VotePoll) check isSignedIn and redirect to /sign-in. Backend uses clerkMiddleware (index.js:25) and getAuth(req) to extract userId for authorization on all endpoints. Vote duplicate prevention via findOne on {poll_id, voter_id} (index.js:83-86). However, anonymous polling is completely absent — all routes require auth, the "Who can vote" dropdown on CreatePollPage is disabled with only "Signed in users" as option (CreatePollPage.tsx:256-274), and there is no anonymous/authenticated toggle in the poll model or backend logic. No custom registration/login exists (fully Clerk-dependent).
Poll Creation & Question Management4 / 15
CreatePollPage.tsx (407 lines) has a polished form with question text input, dynamic option add/remove (2-10 options with Trash2 delete, CreatePollPage.tsx:37-47), duration selector dropdown (5/10/15/20/25/30 min), and a live preview sidebar. However, the submission completely fails the core requirement of "add multiple questions, mark questions as mandatory or optional" — the poll model (pollModel.js) has a single `title` field and flat `options` array with no questions sub-document. The "SAVE AS DRAFT", "PREVIEW", "BROWSE TEMPLATES" buttons are all non-functional placeholders. Poll type selector only has "Multiple choice" as functional; Who Can Vote, Allow Multiple Votes, and Result Visibility dropdowns are all disabled with single hardcoded options. No poll editing (PATCH) endpoint exists despite README claiming it. No server-side input validation (no Joi/Zod). Polls always go "Live" on creation — no draft saving.
Response Collection Flow4 / 15
VotePollPage.tsx (232 lines) provides the voting UI: fetches poll by ID, renders options as styled progress bars with percentage, shows live vote counts, elapsed time, and time-left countdown (useEffect timer at lines 71-93). Backend vote route (index.js:76-121) checks for existing vote, creates vote record, then checks if poll is closed — but the ordering is buggy (vote is created at line 92 BEFORE the closed check at line 104, meaning a vote is persisted even for a closed poll before the error response). Option increment uses `vote-1` as array index (line 110) which would fail if client sends a non-numeric or out-of-range value — no server-side validation exists. Only single-question polls supported (no per-question answer selection). No anonymous response path (all submissions require Clerk auth). No expiry enforcement on the client before submission — timer display is cosmetic. No unique index on vote model for DB-level duplicate prevention.
Analytics & Feedback Dashboard2 / 15
No dedicated analytics dashboard exists. The navbar links to /analytics (NavbarDashboard.tsx:18) but no route is defined for it in App.tsx. There are no analytics API endpoints on the backend — no GET /analytics/:pollId, no aggregation pipelines, no per-question breakdowns. The poll model has engagement and avgTime fields (pollModel.js:54-62) but these are never updated by any code — they remain at their default 0 values forever. The PollList component (PollList.tsx:64-111) uses 4 hardcoded poll objects rather than fetching from the API. Vote counts and percentages are displayed on the voting page (VotePollPage.tsx:154, 160) but this is the public voting view, not a creator analytics dashboard. The "publish final results" feature (POST /publish) does not exist — there is no endpoint to toggle results visibility. Score 2 for basic vote count display only.
Frontend Experience4 / 10
The UI has a distinctive neubrutalist/playful visual design with bold lime-green and pink accents, custom hover shadows, and a consistent brand identity throughout. Homepage (Homepage.tsx, 147 lines) features a hero with animated poll demo card, feature grid, and footer CTA. Sign-in/sign-up pages have attractive split layouts with OAuth buttons and decorative background elements. CreatePollPage has a polished two-column layout with live preview and tips sidebar. However, functional depth is very limited: Dashboard.tsx (36 lines) is essentially empty (just "Welcome, [name]!" and sign out). PollList renders 4 hardcoded poll objects with fake pagination. Many UI controls are non-functional: SAVE AS DRAFT, PREVIEW, BROWSE TEMPLATES, VIEW ARCHIVE, and sidebar filter buttons don't actually filter. No empty states, no toast notifications, no confirmation dialogs, no form library (individual useState hooks in CreatePollPage). Responsive design works via Tailwind but mobile optimization is basic.
Backend Architecture & API Design3 / 15
The backend is a single monolithic index.js (166 lines) with all routes, Socket.IO, and middleware in one file — no controllers, services, routes, or middleware modules. No input validation library whatsoever (no Joi, Zod, or express-validator) — the vote route uses `vote-1` as an array index without checking bounds. Critical bugs: every route has a double-response pattern where success (200/201) is sent followed by a fallthrough 500 error (e.g., index.js:67-72 for create-poll, lines 139-142 for get poll, lines 153-156 for get polls). No security middleware: no Helmet, no rate limiting, no request body size limits. Several endpoints promised in the README don't exist: PATCH /poll/:id, DELETE /poll/:id, GET /votes/:pollId. The poll schema embeds CSS class names (statusColor, iconBg, iconColor) as database fields — presentation logic leaking into the data layer. voteModel has no unique compound index for duplicate prevention, relies entirely on application-level findOne check. No .env.example for backend. The Clerk publishable key is committed in frontend/.env.
Real-Time Updates Using WebSockets5 / 10
Socket.IO is correctly integrated: server creates an io instance on the HTTP server with CORS config (index.js:17-22), clients join/leave poll-specific rooms (index.js:32-33), and the server emits `vote:updated` with options+votes after each vote (index.js:119) and `poll:closed` from the cron job (cronjob.js:33-36). The frontend has a custom usePollSocket hook (usePollSocket.ts, 40 lines) that auto-connects, joins the poll room, listens for vote:updated and poll:closed events, and cleans up on unmount. Auth token is attached before connecting (usePollSocket.ts:13-14). Gaps: only 2 event types, no debouncing on client for rapid updates, no reconnection room re-join logic (socket.on('connect') handler missing), no server-side verification of socket.auth token, no typed Socket.IO events (plain strings), no events for poll creation/deletion/publishing, and the hook doesn't handle the case where a component remounts with the same pollId.
Code Quality & Project Structure3 / 10
Frontend has clean component/page/lib/asset separation with TypeScript throughout, ESLint configured, and shadcn/ui component patterns (button.tsx with cva variants, card.tsx with compound components). However, multiple issues: the backend is a single monolithic file with no modular structure, is written in plain JS (no TypeScript), and has 3 copy-paste bugs where routes send both success and error responses. Several files are dead code: SignInComponent.tsx (10 lines) is defined but never imported anywhere; PollList.tsx contains 63 lines of hardcoded poll data instead of fetching from API. Google and GitHub SVG icons are duplicated verbatim in both sign-in.tsx and sign-up.tsx (50+ lines each). The navbar logo is duplicated in NavbarDashboard.tsx and NavbarHomepage.tsx. frontend/.env is committed to the repo exposing the Clerk publishable key. No automated tests exist. No .env.example files for either frontend or backend. Several unused imports (e.g., `Upload` in VotePollPage.tsx at line 2). The `usePollSocket` hook uses `any` types (usePollSocket.ts:6).
145
Poll Pulse
Aarav Ahuja · @aarav_ahuja
30
Poll Pulse is a frontend-only submission (no backend code in repo) with a dark-themed polling platform. It has working auth (login/signup/protected routes with JWT in localStorage), a feature-rich poll creation modal supporting 3 question types with dynamic options and various settings, a sequential question-by-question voter screen with inline results, per-question analytics breakdowns with live activity feed, and Socket.io-based real-time updates. However, significant quality issues reduce the score: the frontend deployment is broken (Netlify 404), the README is unmodified Vite template, 11 of 12 API calls hardcode localhost, Tailwind is completely broken (empty content config), TypeScript isn't used, there's no separation of concerns or shared infrastructure, branding is inconsistent, and all user feedback uses alert()/confirm(). Total: 30/100.
Authentication & Access Control4 / 10
Login (Login.jsx:14,72 lines) and Signup (Signup.jsx:14,104 lines) with email+password work. JWT stored in localStorage as JSON (Login.jsx:19-25). Protected routes check token manually in Dashboard (line 106-115) and CampaignDashboard (line 23-24) by parsing localStorage and redirecting. Logout via localStorage.clear() (Dashboard.jsx:119). No auth context/provider — token extraction pattern duplicated 6+ times across 4 files. Google OAuth button in Signup (lines 88-94) has no onClick handler and is purely decorative. No email verification, password reset, rate limiting, or CSRF protection. collectVoterDetails UI toggle exists but isn't enforced client-side.
Poll Creation & Question Management5 / 15
CreatePollModal.jsx (896 lines) supports 3 question types (CHOICE/TEXT/RATING), dynamic add/remove questions (line 36,52), dynamic add/remove options per question with min-2 guard (lines 83-102), mandatory/optional checkbox per question (lines 352-383), session expiry select (1h/24h/7d/Never at line 730), voter identity toggle (anonymous/named at line 762), poll access toggle (open/password at line 793), image upload with ObjectURL preview (lines 499-515), correct answer marking (lines 104-111), and time limit per question (line 529). Client-side validation via imperative if/return alert() (lines 114-121). No form library (pure useState), no slug auto-generation (slug field exists empty), no server-side validation visible in this repo.
Response Collection Flow5 / 15
VoterScreen.jsx (265 lines) implements sequential question-by-question display with progress bar (lines 135-140), radio-style option selection with visual border highlighting (lines 58-61, 165-181), Next/Back navigation with pre-selection validation (lines 91-101), inline live per-option results during answering (lines 154-181), submit validation requiring all questions answered (lines 63-67), and post-vote results view with percentage bars and "(Your Choice) ✨" labels (lines 215-252). Critical gaps: no duplicate vote prevention, no expiry checking on client, mandatory/optional is ignored (all questions required), userId hardcoded as "Anonymous Voter" (line 78), hasVoted state is local only, and poll password protection is not enforced.
Analytics & Feedback Dashboard4 / 15
CampaignDashboard.jsx (222 lines) provides 3 stat cards (Total Responses/Status/Voter Privacy at lines 130-133), per-question option breakdowns with counts, percentages, and animated CSS progress bars (lines 138-177), and a Live Activity Feed from socket.io new-voter-activity events (lines 180-202). Clear All Votes (line 78) and Reset per-Question (line 95) controls. Major gaps: Status stat is hardcoded "ACTIVE" (line 131), no publish results feature exists anywhere, no CSV/export, no trend/time-series data, no individual response viewing, analytics computation is pure client-side array reduction (line 65), and loading state renders null (line 104 — blank page). Dashboard.jsx shows basic poll list with question counts (line 355) but no response counts.
Frontend Experience4 / 10
Six routes (App.jsx:43-52) with React Router and consistent dark theme (928-line index.css with custom CSS). Landing page has Hero/Features/Steps/Footer sections. All core UI states handled: 3 loading states (Dashboard:123-138, VoterScreen:103-108, CampaignDashboard:104), 3+ error states (VoterScreen:111-117 "not found", Login/Signup alerts), 2 empty states (Dashboard:282-302, CampaignDashboard:185-188), and 1 success state (VoterScreen:249-251). But: Tailwind is broken (tailwind.config.js content:[] generates zero utility classes), CSS organization is a 928-line monolith with dead Vite template App.css (185 lines unused), all feedback uses alert()/confirm() instead of toast/banner components, index.html title is "client", and Google OAuth button is decorative dead UI.
Backend Architecture & API Design2 / 15
This repository contains only the React frontend (named "poll-pulse-frontend"). No backend code exists — no Express server, no Node.js routes, no MongoDB/Mongoose schemas, no middleware, no input validation, no database configuration. The .env file references an external backend at poll-pulse-backend.onrender.com, but that code is not included in this submission. The frontend calls 13 API endpoints on this external server, but no backend architecture or API design can be evaluated since the backend implementation is absent. The tech stack claims Node.js, Express, MongoDB, Mongoose, and JWT are used, but none of these technologies are present in this repository.
Real-Time Updates Using WebSockets4 / 10
Socket.io client (v4.8.3) used with room-based pub/sub: VoterScreen (line 6) and CampaignDashboard (line 6) each create independent io() connections (no singleton, causing duplicate connections). Both emit join-poll (VoterScreen:26, CampaignDashboard:41). Server-emitted update-analytics triggers HTTP refetch in both components (VoterScreen:41-44, CampaignDashboard:45-47) with proper socket.off cleanup on unmount. CampaignDashboard additionally listens for new-voter-activity for live feed (lines 48-49). Gaps: hardcoded localhost for socket URL, no socket authentication, no reconnection/room re-join logic, no room leave events, data is HTTP-refetched rather than pushed through socket (signal-only pattern), and only 2 event types implemented.
Code Quality & Project Structure2 / 10
README.md is unmodified Vite + React scaffold template (17 lines about Vite plugins/ESLint — zero project-specific content). index.html title is "client". Massive code quality issues: no TypeScript, no service/API/hooks/utils layers — all logic embedded in page components, token extraction from localStorage duplicated 6+ times across 4 files, 11 of 12 API calls hardcode http://localhost:5001 (only 1 uses VITE_BACKEND_URL env var), tailwind.config.js has content:[] (generates zero CSS — entirely broken), 185-line App.css is dead Vite template code, react.svg/vite.svg assets unused, @types/react and @types/react-dom in dependencies but no TS files exist, socket connection instantiated twice instead of singleton, branding is inconsistent across files (PulseBoard/Poll Pulse/P B/"client"), and all user feedback uses alert()/confirm() with Hindi/English mixed strings.
146
PollWave
Rohit Chornele · @rohitchornele_85624453
30
PollWave is a MERN-stack polling platform with a dark-themed UI deployed on Vercel. The project has a solid auth system (register/login/logout/refresh/reset/verify) and a well-implemented vote service with transactions, duplicate prevention, and state validations. However, it has several critical shortcomings: (1) the poll model only supports single-question polls with flat options — no multi-question support, violating a core requirement; (2) the `hashToken` function has a missing `return` statement that silently breaks email verification, password reset, and renders refresh token hashing meaningless; (3) the Analytics.jsx page (535 lines) uses entirely hardcoded mock data with no real API integration; (4) zero Socket.IO/WebSocket implementation; (5) no root README or project documentation; (6) the dashboard module is fully commented out; (7) extensive inline styles, console.log statements, and dead code throughout. The visual design is attractive and the frontend handles multiple states well, but the backend has fundamental bugs and the project is missing key features. Total: 30/100.
Authentication & Access Control6 / 10
Comprehensive auth system: register, login, logout, refresh token, forgot/reset password, email verification all implemented. JWT access+refresh tokens stored in httpOnly/secure cookies with 15min/7day expiry (auth.controller.js:19-29). Joi DTO validation on all auth endpoints (4 DTO files). authenticate middleware checks cookies then Authorization header (auth.middleware.js:8-52), optionalAuth middleware for public vote routes (optionalAuth.middleware.js:4-48), authorize middleware for role-based access. Frontend AuthContext with login/logout/refresh user (AuthContext.jsx:230 lines), ProtectedRoute with loading/redirect states. User model with bcrypt(12) pre-save hook, comparePassword method, refreshToken/verificationToken/resetPasswordToken with select:false (auth.model.js:5-114). Critical bug: hashToken() at auth.service.js:8-10 is missing `return` statement, returning undefined for all hashed tokens — breaks password reset and email verification flows, and makes refresh token comparison degenerate. Rate limiting and helmet are installed dependencies but never wired in app.js. Tokens also stored in localStorage on frontend (authService.js:34-38). No Helmet/CSRF protection.
Poll Creation & Question Management4 / 15
CreatePoll.jsx (780 lines) has a polished 3-step wizard (Question/Settings/Preview) with dynamic option add/remove (max 10, min 2 enforced), character counters, visibility toggle (public/private), multiple toggle settings (allowMultiple, allowAnonymous, showResults, allowComments), expiry date picker, and a live preview panel. However, fundamentally limited to SINGLE-question polls — the Poll model (poll.model.js:20-96) only has a flat `options` array with no `questions` field, violating the core requirement of "add multiple questions, mark questions as mandatory or optional." Poll creation has zero server-side validation (poll.service.js:3-8 just calls Poll.create). The payload mapping has field name mismatches: frontend sends `isMultiChoice`/`closesAt` but backend expects `allowMultipleVotes`/`expiresAt`. "Save Draft" button just navigates away without saving. No poll editing endpoint exists. No question-level mandatory/optional toggles in the UI.
Response Collection Flow5 / 15
The vote service (vote.service.js:384 lines) is well-implemented: MongoDB transactions, validates poll existence/status (active/closed/upcoming), checks expiry with auto-close (lines 63-75), prevents self-voting (lines 78-86), validates selected options belong to the poll (lines 109-123), enforces anonymous-only rules (lines 53-60), checks single/multi-vote mode (lines 98-106), duplicate vote prevention via both unique compound indexes (vote.model.js:52-71) and explicit findOne check (lines 141-150), and updates option vote counters atomically (lines 170-179). Frontend PollVote.jsx (690 lines) handles all states: loading, error, upcoming, closed, already-voted, auth-required, anonymous tracking via localStorage UUID (anonymousId.js:8-20), and post-vote results with animated progress bars. Gap: single-question limitation prevents per-question validation. No rate limiting on vote endpoints. checkUserVote at vote.controller.js:157-187 properly returns prior selection for display.
Analytics & Feedback Dashboard3 / 15
Two distinct components: PollDetail.jsx (192 lines) shows real per-option vote percentages using actual poll data via pollService.getPollById and calculatePct, functioning for basic results display. However, Analytics.jsx (535 lines) is entirely hardcoded mock data — RANGE_DATA, TOP_POLLS, DEVICE_DATA, HOURLY_DATA, and OPTION_BREAKDOWN are all static fake arrays (lines 44-94) with zero API calls to fetch actual analytics. The dashboard service (dashboard.service.js:132 lines) has real implementations for getDashboardStats/getRecentPolls/getTrendingPolls/getUserDashboard but the entire dashboard.controller.js and dashboard.routes.js are fully commented out, so none of these are wired to endpoints. No publish-results feature exists — no endpoint to mark a poll as published, no distinction between post-vote results and publicly published outcomes. No CSV/export functionality. The Recharts components are beautifully styled but display fake data.
Frontend Experience5 / 10
Attractive dark theme with consistent zinc-950 (#0c0a09) background and orange (#ea580c) accent throughout. React Router v7 with well-organized routes: public (/, /login, /register, /forgot-password, /reset-password/:token, /verify-email/:token), protected dashboard nested under DashboardLayout (/, polls, polls/create, polls/:pollId), and public vote (/poll/:pollId). Handles loading (spinners), error (error cards with messages), empty (contextual empty states with CTAs), and auth states across all key pages. Three-step poll creation wizard with live preview, MyPolls with grid/list views, search, filter, and sort (MyPolls.jsx:580 lines). Delete confirmation modal with animation. Recharts for charts. Toast notifications via react-hot-toast. Issues: excessive inline styles without CSS modules/classes, no form library (useState for all form state), analytics page is entirely fake data, some dead code/commented routes, no PWA/offline support, no skeleton loading states.
Backend Architecture & API Design4 / 15
Clean layered architecture: routes → controllers → services → models with DTO validation for auth endpoints. Joi validation with BaseDto pattern (4 auth DTOs) and generic validate middleware (validate.middleware.js:19 lines). MongoDB schemas with proper indexes: Vote model has unique compound indexes for duplicate prevention with partialFilterExpression (vote.model.js:52-71), User model with email unique index. Transaction support in vote service using mongoose sessions (vote.service.js:17-200). ApiError class with static factory methods for all HTTP codes (api-error.js:49 lines). CORS configured with credential support and allowlist (app.js:14-36). Critical bugs: hashToken() returns undefined (missing `return` in auth.service.js:8-10), breaking all hashed token flows. DTO typos: `static schmea` instead of `schema` (register.dto.js:6, login.dto.js:6). helmet and rateLimit installed but never wired in app.js. Dashboard controller/routes fully commented out. No centralized error handling middleware. Poll service returns plain objects with status:400 rather than throwing ApiError. No pagination on any list endpoints. express-validator installed but unused.
Real-Time Updates Using WebSockets0 / 10
Zero implementation. Socket.IO is not listed in either frontend/package.json or server/package.json dependencies. No WebSocket code, no socket event handlers, no real-time update mechanism exists anywhere in the codebase. The server creates a plain HTTP server in server.js:10 but never attaches Socket.IO. The analytics page uses entirely static mock data with no live updates. The voting page fetches results after submission via standard HTTP GET but has no WebSocket subscription for live counts. A grep for "socket", "websocket", "io.emit", "io.on" across all source files returned zero relevant matches.
Code Quality & Project Structure3 / 10
Reasonable module structure on backend (modules/auth, modules/poll, modules/votes, modules/dashboard) with services/utils/common separation. Frontend organized into components/dashboard, components/homepage, components/polling, components/routing, components/utils, pages, services, context. However: no root README (only unmodified Vite starter README in frontend/ with no project documentation), no .env.example files for either package, no TypeScript anywhere, `hashToken` function returns undefined due to missing `return` (production-breaking bug), DTO typo `schmea` instead of `schema` in 2 files, extensive console.log statements left in production code (auth.service.js, vote.controller.js, auth.middleware.js, poll.service.js, MyPolls.jsx, etc.), commented-out code blocks in multiple files (auth.model.js:92-99, vote.controller.js:70-124, dashboard.controller.js entirely commented out), excessive inline styles (800+ lines of raw style objects), redundant imports in main.jsx (createBrowserRouter, createRoutesFromElements imported but unused), dead code/imports, no automated tests ("echo Error: no test specified"), multiple unused dependencies (multer, resend).
147
Polling application
Janmejai Singh · @cyd3er
30
A full-stack polling platform with Next.js frontend and Express backend that has solid architectural foundations (Prisma schema, JWT auth, Socket.io server setup) but suffers from several critical frontend gaps that prevent core functionality from working end-to-end. The poll creation form cannot create valid polls (options UI missing), the creator analytics dashboard shows hardcoded zeros, publish/delete buttons are dead, the API client doesn't check HTTP status codes, and Socket.io has zero client-side integration. The public poll results page (when published) is the one feature that actually works correctly. The project feels rushed — multiple dependencies installed but unused, dead code from templates, and incomplete UI components throughout.
Authentication & Access Control5 / 10
Working JWT-based auth: registration with bcrypt (10 rounds), login with express-validator, auth middleware verifying Bearer tokens (middleware/auth.ts:13-39), AuthContext with localStorage persistence (auth-context.tsx:22-90), protected dashboard layout redirect (layout.tsx:21-25). Anonymous vs authenticated poll modes enforced server-side — requiresAuth check at response.ts:97-99, isAnonymous controls userId storage at response.ts:128. Gaps: JWT secret hardcoded fallback 'your-secret-key' (utils/jwt.ts:3), tokens in localStorage (XSS-vulnerable), no httpOnly cookies, no Helmet/CSRF, no email verification, no refresh token rotation, no token revocation on logout, req.user typed as any.
Poll Creation & Question Management3 / 15
Backend schema and API are well-prepared: Prisma schema with Poll→Question→Option relations, slug auto-generation (poll.ts:8-10), Zod validation requiring min 1 question and min 2 options per question (schemas.ts:14-27), update and delete with ownership checks. CRITICAL GAP: The create poll form (create/page.tsx) has incomplete option management — the code at line 88 literally says "Options would be mapped here" with comments indicating the option input fields were never implemented. No useFieldArray for options within questions. Default options are empty strings ['',''] which fail Zod's .min(1) validation, making the entire create form unusable. Also missing: expiry date picker in the form UI, no per-question option add/remove, questions/options cannot be edited after creation.
Response Collection Flow4 / 15
Server-side validation is decent: poll existence check (response.ts:89-91), expiry check returning 410 (lines 93-95), requiresAuth enforcement (lines 97-99), duplicate response check for authenticated users (lines 102-108), per-question existence and required validation (lines 112-123), option ID validation against poll's questions. Transactional response creation with answers (lines 125-136). However: api.ts submit function (line 64-71) doesn't check res.ok — HTTP errors return as resolved promises, so toast.success fires even on 400/401/410 errors. No duplicate prevention for anonymous users (no localStorage tracking, no IP tracking). No unique compound index on (pollId, userId) in schema. No post-submission disable/redirect. Client doesn't handle different error states (expired, already-voted, auth-required) with distinct UI.
Analytics & Feedback Dashboard4 / 15
Two analytics views: (1) Public results page (poll/[slug]/page.tsx:56-94) when isPublished=true computes per-question option counts with percentages and animated progress bars from server-computed analytics — this actually works. (2) Creator dashboard poll detail (poll/[id]/page.tsx:59-89) shows HARCODED "0 votes" and width:'0%' for every option — the responses data returned by the server is never processed, making creator analytics non-functional. Dashboard list page (dashboard/page.tsx) correctly shows response counts via _count. Gaps: publish button has no onClick handler (dead button at poll/[id]/page.tsx:94-96), Recharts imported but never rendered (no actual charts), no CSV export despite papaparse dependency, no trend/time-series data, no anonymous vs authenticated breakdown, no individual response viewing, no peak activity metrics.
Frontend Experience4 / 10
Clean Tailwind CSS with dark mode toggle (next-themes), responsive layout, gradient hero landing page with feature cards. All pages have loading states (text "Loading..."), form validation via React Hook Form + Zod, toast notifications (react-hot-toast), status badges (Draft/Published). TanStack Query for data fetching. Gaps: poll creation form missing option input fields entirely (comment "Options would be mapped here"), publish and delete buttons are dead (no onClick), no Socket.io client integration despite dependency installed, no post-submission redirect/disable on public poll page, no skeleton loaders (just "Loading..." text), no confirmation dialogs, no expiry date input on create form, QR code and CSV dependencies installed but unused, commented-out Next.js template code left in page.tsx:74-88, template SVGs still in public/.
Backend Architecture & API Design5 / 15
Clean Express + TypeScript architecture: routes→controllers separation, Prisma ORM with well-designed relational schema (cascade deletes, proper foreign keys, unique constraints including @@unique on [responseId, questionId]), CORS configured, express-rate-limit (100/15min global), express-validator on auth and poll endpoints, error handler with Prisma error code mapping (errorHandler.ts:6-15), Socket.io server attached to HTTP server. Gaps: no Helmet, no CSRF, no request body size limits, JWT secret hardcoded fallback (jwt.ts:3), PrismaClient exported from index.ts (bad for testing), response submission validation only checks `answers.isArray` without validating individual answer fields, no pagination on any GET endpoint, no service layer, req.user is any, response route has commented-out dead route overridden by the next one (response.ts:8-11), no graceful shutdown, slugify import has comment "need to install slugify" suggesting incomplete setup.
Real-Time Updates Using WebSockets2 / 10
Server-side Socket.io infrastructure exists: Server initialized with CORS (index.ts:15-21), room-based join/leave handlers (socket.ts:8-14), emitPollUpdate function counts responses and emits to room (socket.ts:22-28), global declaration for cross-module access (types/index.ts:1-3), called from submitResponse controller (response.ts:139). CRITICAL GAP: Zero Socket.io client implementation anywhere in the frontend. Despite socket.io-client being in client/package.json dependencies, no file imports or uses it — no connection, no event listeners, no room joining, no real-time UI updates. The server emits events into the void. Only one event type (poll-update with just responseCount, no per-question analytics). No auth on socket connections, no reconnection handling, no debouncing.
Code Quality & Project Structure3 / 10
Monorepo with client/server separation, TypeScript on both sides, Prisma for DB, .env.example and docker-compose.yml provided, start.sh script. However: server/package.json includes frontend dependencies (react-hook-form, recharts, qrcode.react, papaparse, react-hot-toast, next-themes) — clear copy-paste dependency management. Duplicate prisma.config.ts files in both server/ and server/prisma/. Multiple unused dependencies across both packages (papaparse, qrcode.react, framer-motion, lucide-react). Client still has Next.js starter template SVGs (file.svg, globe.svg, next.svg, vercel.svg, window.svg) and commented-out template code (page.tsx:74-88). api.ts reads localStorage directly instead of using auth context. Extensive any types in controllers. Dead UI buttons with no handlers. No tests. No pagination. Duplicate slugify config reference.
148
PulseBoard
Sarika Malhotra · @malhotrasarika789_291426e3
29
PulseBoard is a partial full-stack polling platform with a dark-themed React frontend and Express/MongoDB backend. The core happy path works: users can register/login, create multi-question polls with options, vote via radio buttons, and view basic progress-bar analytics. However, the implementation is undermined by critical gaps: (1) Socket.io "real-time" is completely non-functional on the frontend (dead code never wired), (2) JWT secret has hardcoded fallback creating a security vulnerability, (3) Frontend routes are unprotected despite ProtectedRoute existing, (4) Anonymous voting is impossible due to authMiddleware on the vote endpoint, (5) Polls cannot be edited after creation, (6) No published results view exists despite isPublished field, (7) Triple code duplication across root/backend/frontend with git merge conflicts, (8) No input validation library or security middleware, (9) ~20% dead code, (10) All URLs hardcoded to localhost:5000. The backend deployment is down (Render status: no-server). Overall score: 29/100.
Authentication & Access Control3 / 10
Registration (bcrypt 10 rounds) and login with JWT (7d expiry) work in controllers/authController.js, and authMiddleware.js verifies Bearer tokens. However: (1) JWT_SECRET has hardcoded fallback "YOUR_JWT_SECRET_KEY" across all 3 copies of authMiddleware.js (line 20-21) — a critical vulnerability. (2) ProtectedRoute.jsx exists but is NEVER imported in App.jsx — all frontend routes are publicly accessible. (3) Token stored in localStorage (XSS-vulnerable). (4) Register endpoint returns full user object including hashed password (line 28-29). (5) Hardcoded resetPassword for "sarika@gmail.com"/"123456" is a debug backdoor, not a real feature. (6) No logout anywhere, no CSRF protection, no Helmet, no email verification. CORS allows all origins.
Poll Creation & Question Management5 / 15
CreatePoll.jsx (192 lines) supports dynamic questions with add/remove, dynamic options per question with add, mandatory/optional toggle (isMandatory checkbox), anonymous toggle, and datetime expiry picker. Client-side validation (lines 40-60) checks title, expiresAt, question text, and option emptiness. Server-side in backend/controllers/pollController.js validates req.user and non-empty questions array. But significant gaps: no poll edit/update endpoint exists (PATCH/ PUT absent), questions cannot be modified after creation, no server-side option count validation per question, no slug/permalink generation, no form library (vanilla useState), no draft status, and no min/max constraints on questions or options.
Response Collection Flow4 / 15
PollView.jsx renders radio buttons per question, checks all questions answered (line 41), and submits via POST /api/polls/:id/vote. Backend votePoll (controllers/pollController.js:65-113) finds matching options, increments votes, pushes response metadata, and uses markModified("questions") for nested subdoc persistence. However: (1) authMiddleware blocks anonymous voting despite allowAnonymous flag (route line 22), (2) no expiry checking server-side — expired polls still accept votes, (3) no duplicate vote prevention (no unique index, no check), (4) isPublished field exists in schema but is never gated on vote submission, (5) no server-side validation that submitted option strings belong to the poll's actual options, (6) no server-side mandatory question enforcement matching the isMandatory schema flag.
Analytics & Feedback Dashboard4 / 15
PollAnalytics.jsx (124 lines) renders per-question progress bars with vote counts and percentages computed from opt.votes, plus 10-second auto-refresh via setInterval (line 30). Dashboard.jsx (153 lines) shows 3 stat cards (Total Configurations, Active Streams, Aggregated Responses) with Active/Closed expiry badges and poll card grid. Loading spinners and empty states handled. However: all analytics are client-side computed from the raw poll JSON — no server-side aggregation or dedicated analytics API. isPublished field exists in schema but no publish/unpublish endpoint or published results view exists. No CSV/export, no trend data or time-series charts, no individual response viewing, no per-question leading option identification, no peak activity metrics. Dashboard computes totalVotes from q.responses.length which counts metadata entries rather than actual option votes.
Frontend Experience5 / 10
Consistent dark glassmorphism theme with Tailwind CSS throughout all 6 pages. Sticky navbar with branding, responsive layout (max-w-6xl, grid breakpoints). All pages handle loading (spinner with animate-spin) and empty/null states explicitly. 7 routes defined in App.jsx with react-router-dom. However: all 6 pages hardcode backend URL as "http://localhost:5000" individually (no centralized API config), error handling is alert() calls with no toast/notification system, no form library (vanilla useState), no confirmation dialogs for destructive actions, no logout button anywhere in the UI, ProtectedRoute exists but unused so all routes are public, and the UI text is corny/non-standard ("Deploy Live Poll Architecture", "Core Node Connected"). The frontend landing page is just the login form — no marketing/hero page.
Backend Architecture & API Design3 / 15
Backend follows flat MVC pattern (routes→controllers→models) with no service layer. 9 API endpoints total (3 auth, 5 poll, 1 health). Mongoose schemas for User and Poll with embedded questionSchema. Auth middleware verifies JWT on protected routes. However: (1) no input validation library (no Joi/Zod/express-validator) — controllers accept raw req.body with zero sanitization, (2) no security middleware: no helmet, no rate-limiting, no express-mongo-sanitize, no request body size limits, (3) CORS configured with no options (all origins allowed), Socket.IO CORS set to origin: "*", (4) three overlapping code locations (root/, backend/, frontend/) with inconsistent quality — backend/server.js has global error handler and health endpoint that root/server.js lacks, (5) no pagination on poll listing, (6) schemas lack validators (no email regex, no minlength/maxlength), (7) votePoll mutates schema at runtime adding votes/responses properties not in the Mongoose schema definition, (8) raw error.message leaked to clients in all catch blocks, (9) no graceful shutdown or connection handling.
Real-Time Updates Using WebSockets2 / 10
Server-side Socket.io (server.js:33-47) is correctly configured with room-based pattern: clients join rooms via "joinPoll" event, server receives "newVote" and emits "voteUpdate" to the room. However, the frontend integration is completely dead: socket.js creates a socket.io-client connection but is never imported by any page component. SocketContext.jsx/SocketProvider is defined but never mounted in main.jsx (which only renders <App /> with no providers). No frontend component listens for socket events. The analytics page uses 10-second HTTP polling (setInterval) instead of WebSocket updates. The Socket.io infrastructure exists on the server but the full real-time pipeline is broken — "real-time" is fake HTTP polling. Score 2 because the server code works architecturally but the client integration is entirely non-functional.
Code Quality & Project Structure3 / 10
JavaScript-only (no TypeScript despite @types/react in dependencies), no tests whatsoever. Three overlapping code locations (root/, backend/, frontend/) with near-identical copies of controllers, models, middleware, and routes — unclear which is canonical. Unresolved git merge conflicts in frontend/.gitignore (lines 1-35) and frontend/README.md (lines 1-63). ~10 dead/unused files: ProtectedRoute.jsx, SocketContext.jsx, socket.js, api.js, 3 backend files in frontend/ (controllers/authController, middleware/authMiddleware, routes/pollRoutes), App.css (184 lines of Vite boilerplate), plus Vite starter assets. Hardcoded localhost:5000 URL duplicated in 9 locations across all 6 pages + services + socket files. Comments in Hinglish throughout. Inconsistent naming: Dashboard calls polls "Configurations" and "Structures". No .env.example for frontend (existing one has backend vars). No service layer separation. Console.log debugging statements in production paths. No ESLint on backend.
149
Poll Stack
Nikhil Prashar · @nikhilprashar561
27
PollStack is a submission with a solid backend foundation but a frontend that is entirely disconnected static mockups. The backend has well-structured authentication (JWT access+refresh, bcrypt), comprehensive CRUD for polls/questions, robust response validation with transaction support, and decent analytics computation. However, the frontend—while visually polished with Tailwind CSS, react-hook-form, responsive Navbar, and animations—is completely non-functional: no page makes API calls, all data is hardcoded, login/register just console.log, the poll creation form has hardcoded questions, dashboard/results pages display fake data, and the submit button has no handler. Socket.IO libraries are installed but have zero implementation (no event handlers server-side, no imports client-side). Additional issues include misspelled filenames throughout (contoller, midlleware, routee), broken context exports, migration/schema mismatch, unused dependencies, and a README that's a personal task list rather than documentation. The backend is at ~5-6 quality level in isolation, but the complete disconnect between frontend and backend pulls the overall score down significantly.
Authentication & Access Control5 / 10
Backend auth is solid: JWT access+refresh tokens, bcryptjs 10 rounds, httpOnly refreshToken cookie, auth middleware sets req.user (auth.midlleware.ts:11-53), register/login/logout/refreshToken endpoints in auth.contoller.ts (314 lines). Frontend has AuthProvider context, authService with all methods, Axios 401 interceptor for auto-refresh (axios.js:34-55), tokenStore for localStorage persistence. However: LoginPage and RegisterPage submit handlers only console.log—they NEVER call authService (LoginPage.jsx:17-18, RegisterPage.jsx:22-23). No protected route enforcement on frontend (no RequireAuth wrapper). Token stored in localStorage (XSS-vulnerable). useAuth exports useContext(AuthProvider) instead of useContext(userContext) (userContext.jsx:28)—context is broken. verifyPassword throws on mismatch leaking email existence (passwordCheck.ts:28-29). No Helmet, CSRF, or rate limiting. Backend works in isolation; frontend is disconnected.
Poll Creation & Question Management4 / 15
Backend has full CRUD: createPoll (polling.controllers.ts:25-83) with UUID link generation, expiry validation, isAnonymous/requiresAuth flags; createQuestion (lines 85-140) validates poll existence and all 4 options required; finalSubmission (lines 142-222) state-changes draft→active after validating questions exist; updatePollDetails (lines 292-404) handles partial updates with enum validation; deletePoll (lines 465-548) uses DB transaction for cascading question deletion. Frontend FormBuilder (Questions.jsx, 290 lines) has dynamic question/option add/remove with react-hook-form, AnimateIn wrapper, per-question required toggle, max 4 options enforced. BUT: CreatePollPage.jsx submit handler calls data.preventDefault() (wrong API—line 21) and just console.logs; questions array (lines 26-42) is hardcoded; NO backend API calls made anywhere in creation flow. Entire frontend is disconnected.
Response Collection Flow3 / 15
Backend response submission is thorough: submitFinalPoll (response.controllers.ts:17-183) validates poll existence, active status, expiry, requiresAuth, required questions completeness, option validity against actual question options, duplicate submission prevention for authenticated users, and uses DB transaction for atomic submission+response insertion. However, frontend PollResponsePage.jsx (184 lines) is a completely static mockup: hardcoded questions array (lines 3-34), no form state, no selected option tracking, submit button has no onClick handler (line 170). responseService.js has submitPoll method but it's never called from any page. The response flow exists only on the backend—a real user cannot actually submit a poll through the UI.
Analytics & Feedback Dashboard3 / 15
Backend analytics in getPollAnalytics (response.controllers.ts:456-605) computes totalQuestions, totalResponses, totalSubmissions, participationRate, and per-question option vote counts with percentages via in-memory aggregation. finalPollResult (lines 251-376) provides published results with formatted per-question breakdowns for closed/expired polls. BUT frontend is entirely mock data: DashboardPage.jsx (369 lines) has 5 hardcoded polls with fake response numbers, hardcoded stat cards, CSS bar chart from hardcoded [40,65,52,80,92,70,88] array, SVG donut with hardcoded dasharrays. PollResultsPage.jsx (202 lines) has 2 hardcoded questions with fake vote counts and percentages. No page ever calls getPollAnalytics or finalPollResult APIs. No Recharts or any charting library used—just raw CSS/SVG.
Frontend Experience4 / 10
Visually polished with consistent black/white design language using Tailwind CSS v4. 8 routes defined (routes/index.jsx), responsive Navbar with mobile hamburger menu and dashboard/non-dashboard mode detection. All forms use react-hook-form with proper validation messages. HomePage has well-crafted hero, feature grid, marquee, and CTA sections. Animate-fade-up CSS animations throughout. Sonner toast for notifications. However, every page is a static mockup with zero API integration—LoginPage/RegisterPage just console.log, CreatePollPage has hardcoded questions, DashboardPage shows fake data, PollResponsePage/PollResultsPage are pure static displays. No loading spinners, no error state handling, no empty state handling beyond Questions component. The app looks good in a screenshot but has no real functionality.
Backend Architecture & API Design5 / 15
Clean TypeScript backend with strict mode, noUncheckedIndexedAccess (tsconfig.json:24). Well-structured ApiError class hierarchy with static factory methods (ApiError.ts, 49 lines) and ApiResponse class (ApiResponse.ts, 47 lines). 18 REST endpoints across 3 route modules. PostgreSQL + Drizzle ORM with 5 tables. JWT with separate access/refresh secret+expiry. Critical gaps: Zod is a dependency but NEVER imported (no input validation library used anywhere—all validation is manual if-checks). cors package declared but never used. No centralized error handling middleware. completedPolls has `or(eq("closed"), eq("closed"))` bug (response.controllers.ts:420)—checks same status twice. Migration SQL (0000_elite_morbius.sql) defines options table and question_type enum, but Drizzle schema (schema.ts) uses hardcoded option1-option4 columns—schema/migration mismatch. Filenames are misspelled: contoller, midlleware, routee (6 files). @ts-ignore used in auth controller (lines 144, 169). No service layer—controllers handle business logic directly.
Real-Time Updates Using WebSockets0 / 10
Socket.IO is listed as a dependency in both Backend/package.json ("socket.io": "^4.8.3") and Frontend/package.json ("socket.io-client": "^4.8.3"). Backend socket.server.ts (15 lines) creates a Socket.IO Server with CORS config. However: NO event handlers are ever registered—no io.on("connection", ...), no socket.on(...), no room management, no emit calls. The return value of socketServer() is discarded in index.ts:13. Frontend socket.io-client is NEVER imported in any file. Zero real-time functionality exists. This feature was not implemented at all—only the npm packages were installed.
Code Quality & Project Structure3 / 10
Monorepo with Backend/ and Frontend/ separation is clean. TypeScript strict mode with noUncheckedIndexedAccess on backend. Consistent folder structure: modules/auth|polling|response pattern. Frontend has organized Context/Components/pages/services/utils/common/routes layout. However, major issues: README is a personal task list, not documentation. Filename misspellings across 6 files (contoller, midlleware, routee). useAuth broken—exports useContext(AuthProvider) instead of useContext(userContext) (userContext.jsx:28). pollContext deletePoll never returns filtered array (pollContext.jsx:14). responseContext pollResponse is empty stub (responseContext.jsx:9). Frontend is all .jsx (no TypeScript). Drizzle migration and schema totally mismatched. Unused deps: zod, cors, socket.io-client. @ts-ignore hacks. Hardcoded mock data in all 5 main pages. No automated tests, no ESLint on backend. No .env.example for frontend's env vars. Deployed frontend works but backend not deployed.
150
R
polling-app
Radhika · @radhika
27
This is a partially implemented MERN polling app with a neo-brutalist frontend. The project shows ambition (Socket.IO, Recharts, quiz-style poll flow) but suffers from critical issues that prevent it from working end-to-end: mixed CommonJS/ESM module syntax makes the backend unrunnable, field name mismatches silently break expiry and anonymous features, a double-count bug corrupts response data, two controllers have no routes, and Socket.IO is server-only with zero client consumption. The deployed Vercel frontend loads but the Render backend is unreachable. The core auth flow and poll creation form work in concept but lack polish and security hardening. Total: 27/90.
Authentication & Access Control4 / 10
Registration (signupUser, authController.js:8-41) uses bcrypt 10-round hashing, login (loginUser, lines 43-87) signs JWT with 7-day expiry. AuthMiddleware (authMiddleware.js:3-27) verifies Bearer tokens and attaches req.user. ProtectedRoute (ProtectedRoute.jsx:7-15) redirects to /login. AuthContext (AuthContext.jsx:5-43) manages login/logout with localStorage persistence and Axios interceptor (axios.js:7-13) auto-attaches tokens. Critical gaps: mixed CommonJS/ESM modules (User.js uses require, Poll.js uses import) means the backend cannot start; no Helmet, rate limiting, or httpOnly cookies (token in localStorage); JWT secret hardcoded as "mysecretkey" in committed .env; logout removes user but not token from localStorage; Navbar never reflects auth state.
Poll Creation & Question Management4 / 15
CreatePoll.jsx (159 lines) supports dynamic questions with add/remove and dynamic options per question, plus anonymous toggle checkbox and expiry date picker. Backend createPoll (pollController.js:5-36) validates title and questions array existence and creates poll with embedded questions/options using createdBy from JWT. Poll schema (Poll.js:29-66) has embedded questionSchema with isMandatory field and optionSchema with votes. Critical bugs: frontend sends 'expiryDate' (CreatePoll.jsx:60) but backend destructures 'expiresAt' (pollController.js:7) — expiry dates are silently dropped. No mandatory/optional toggle in the UI (required:true is hardcoded). No poll editing or deletion endpoints. No per-field server-side validation (option text, min 2 options). CreatePoll page is not wrapped in ProtectedRoute in App.jsx.
Response Collection Flow3 / 15
votePoll controller (pollController.js:73-123) checks poll existence, expiry, mandatory questions, and increments option votes + totalResponses. PollPage.jsx (179 lines) provides quiz-style sequential question flow with radio buttons, 30-second per-question timer, progress indicator, and loading state. Critical bugs: totalResponses incremented TWICE on lines 107 and 109 (doubled counts); no duplicate vote prevention (no compound index, no localStorage tracking, no previous-submission check); no Response document is saved to database; anonymous/authenticated enforcement is absent in the active vote path; expiry check fails silently because expiresAt is always undefined (see param-2 bug). The alternate submitResponse controller in responseController.js has no route and references non-existent field 'poll.allowAnonymous'. No server-side validation that submitted options exist in the poll.
Analytics & Feedback Dashboard3 / 15
PollPage.jsx (lines 138-175) renders Recharts BarChart per question with ResponsiveContainer and a sorted leaderboard when poll.isPublished. Dashboard (Dashboard.jsx:47-113) shows poll list with response counts and publish button calling PUT /api/polls/publish/:id. Analytics.jsx (27 lines) shows total responses but is NOT routed in App.jsx — completely inaccessible. Backend getAnalytics (analyticsController.js:4-19) returns only poll + totalResponses count with no per-question breakdown, but has no route connected (pollRoutes.js doesn't import it). No authenticated vs anonymous breakdown, no participation metrics or time-series data, no CSV export, no individual response viewing. Vote tallies are corrupted by the double-count bug in votePoll.
Frontend Experience4 / 10
Consistent neo-brutalist design language (Tailwind CSS with heavy borders, hard shadows, yellow accent, bold typography across AuthCard, Button, InputField, Navbar). react-hot-toast for notifications across all pages. Recharts integration for analytics visualization. Loading states in PollPage and Analytics, empty state in Dashboard. Sequential quiz-style poll UI with countdown timer. Critical gaps: Navbar never adapts to auth state (always shows LOG IN/SIGN UP); Home page is 3 lines of JSX with no CTAs or features; no logout button anywhere; CreatePoll has no loading state during submission; Analytics page is unreachable; no 404 page; no confirmation dialogs; no delete poll functionality; form UX is basic HTML inputs with no inline validation feedback or form library.
Backend Architecture & API Design4 / 15
MVC structure with models/controllers/routes/middleware/config separation. Mongoose schemas with proper data types, embedded sub-schemas (optionSchema, questionSchema, answerSchema), foreign key refs (createdBy, pollId, userId). JWT middleware applied to protected routes. CORS configured for Vercel + localhost. Socket.IO integrated with Express HTTP server. Critical gaps: backend cannot start due to mixed CommonJS/ESM modules (7 files use import, 3 use require) plus "types":"module" typo in package.json; two controllers (responseController, analyticsController) have no routes; no input validation library; no Helmet/rate limiting/body size limiters; raw err.message leaked to clients; MongoDB credentials committed in .env with weak JWT secret; duplicate route GET /:id at pollRoutes.js:18; Express 5 verbose errors in non-production.
Real-Time Updates Using WebSockets2 / 10
Server-side Socket.IO is configured (server.js:33-38): io.to(req.params.id).emit("voteUpdated", poll) fires on vote submission (pollController.js:112), and joinPoll room subscription handler exists (server.js:52-54). However, frontend/src/socket.js is NEVER imported or used by any component — zero real-time functionality in the client. Additional issues: Socket.IO CORS only allows localhost:5173 (not production Vercel URL at server.js:35); socket client hardcodes localhost:5000; no frontend emits joinPoll or listens for voteUpdated; no room leave events; the response controller (unwired) uses global req.io.emit("pollUpdated") instead of room-scoped emission.
Code Quality & Project Structure3 / 10
Clean folder separation (backend: models/controllers/routes/middleware/config; frontend: pages/components/context/api/routes) with consistent naming conventions. ESLint and Prettier configured in both packages. Critical gaps: no root-level README.md (only an empty Vite starter frontend/README.md); mixed CommonJS/ESM modules make the codebase unrunnable; .env with real MongoDB credentials and weak JWT secret committed to repo; console.log statements in 8 locations across production code; significant dead code (Analytics.jsx unrouted, socket.js unused, responseController and analyticsController unrouted, duplicate route, named export createPoll unused); no tests; "types":"module" typo in package.json; no .env.example; commented-out code in vite.config.js.
151
Pollaris
Krrishvardhan vyas · @vyaskrrishvardhan_1ce96e47
26
Pollaris is a basic polling platform with functional auth (register/login/JWT), poll creation with multiple questions/options, and response submission. Both frontend (Vercel) and backend (Render) are deployed and accessible. The database schema is well-designed with 6 properly related tables using Drizzle ORM. However, the submission is shallow across most dimensions: analytics is nearly non-existent (3 stat cards, no charts despite 2000+ lines of dead evilcharts code), real-time updates are completely absent (score 0), critical validations are missing (no expiry enforcement, no server-side option/question validation), security is minimal (no Helmet, rate limiting, or bcrypt), and there are logic errors (inverted activePolls, wrong HTTP status codes in ApiError). The implementation covers basic happy-path flows but lacks the depth and production readiness expected at higher score levels.
Authentication & Access Control4 / 10
Working register/login with Zod validation and JWT (auth/controller.ts:11-85, jwt.ts:7-18), plus GET /auth/me. However: only optional auth middleware exists (auth-middleware.ts:4-25) with no strict required-auth guard; passwords use HMAC-SHA256 not bcrypt; JWT has no expiry configuration; tokens stored in localStorage (XSS-vulnerable); no refresh tokens, email verification, OAuth, Helmet, CSRF, or rate limiting. DashboardRoute has client-side auth redirect (App.tsx:63) but polls/create uses optional auth only.
Poll Creation & Question Management4 / 15
CreatePoll.tsx (183 lines) supports dynamic question add/remove/reorder, dynamic option add/remove (min 2), and per-question mandatory/optional toggle via Switch. Backend creates polls+questions+options in a transaction (poll/controller.ts:27-72). However: NO expiry date picker in the form UI despite expiresAt being in the schema — a key requirement is missing. Zod validation schema (poll/models.ts) is imported but never used in the controller (manual null-check at line 20 instead). No poll edit, delete, or slug generation endpoints exist.
Response Collection Flow4 / 15
PollPage.tsx (294 lines) renders radio groups per question, validates required fields client-side (allRequiredAnswered, line 185-187), sends structured answers payload, and shows loading/success/error states. Backend (poll/controller.ts:108-181) creates responses in a transaction with duplicate prevention for authenticated users (lines 114-129). Critical gaps: NO server-side expiry checking (expiresAt never verified before accepting responses), NO validation that submitted optionIds belong to the referenced questions, NO server-side required-question enforcement, NO isPublished gate, and no anonymous duplicate prevention.
Analytics & Feedback Dashboard2 / 15
Dashboard backend (dashboard/controller.ts, 39 lines) returns only totalPolls, totalResponses, and a poll list — with an inverted activePolls calculation (line 31: `!p.expiresAt` counts polls without expiry as active). No per-question analytics, no option counts/percentages, no participation insights, and no results publishing endpoint (isPublished in schema but never used by any controller). evilcharts (2000+ lines across 6 files) is completely dead code — never imported by App.tsx, Dashboard.tsx, or any other component. Dashboard.tsx shows only 3 stat cards and a clickable poll list.
Frontend Experience5 / 10
Clean UI built with shadcn/ui (28+ UI components), Tailwind CSS v4 with full dark/light/system theme system (theme-provider.tsx, 230 lines with keyboard shortcut and storage sync). React Router with 4 routes, skeleton loading states (DashboardSkeleton, PollSkeleton), toast notifications (sonner), copy-to-clipboard with fallback (PollPage.tsx:93-143). However: no analytics visualization page, no landing/home page (root is auth form), no poll editing UI, no expiry date picker in creation form, no confirmation dialogs, no error boundaries, and form IDs use non-unique `required-{qi}` pattern.
Backend Architecture & API Design4 / 15
Clean module-based structure (auth/poll/dashboard with controller/routes/models separation) using Drizzle ORM with PostgreSQL and 6 properly related tables (schema.ts:118 lines). Zod validation schemas present, transactions for multi-table writes (poll/controller.ts:27-72, 148-173). However: no Helmet, no rate limiting, no CORS allowlist (app/index.ts:12), ApiError.notfound() and .forbidden() both return 412 (api.error.ts:26-28), no centralized error handler middleware, passwords use HMAC-SHA256 not bcrypt, JWT lacks expiry, no poll edit/delete/publish endpoints, and Zod schema imported but unused in poll controller.
Real-Time Updates Using WebSockets0 / 10
Zero implementation. No Socket.IO or WebSocket dependency in either server-pollaris/package.json or client-pollaris/package.json. grep for 'socket.io', 'socketio', 'websocket' across the entire repo returned no results. No real-time features exist whatsoever.
Code Quality & Project Structure3 / 10
TypeScript throughout with consistent module structure and ESLint/Prettier configured on the client. However: no root README (client-pollaris/README.md is the Vite starter template), no .env.example, no tests (test script echoes "Error: no test specified"), ~2000+ lines of dead code in evilcharts/ (never imported), ApiError class has wrong status codes (notfound/forbidden both return 412), console.log debug lines left in auth controller (lines 73-76), HMAC-SHA256 instead of bcrypt, API_BASE duplicated in 4 files, activePolls logic is inverted (dashboard/controller.ts:31), @ts-ignore in auth routes and poll controller, and Docker compose exposes plaintext credentials.
152
H
Pollify
Happy singh negi · @happysinghnegi975
26
Score: 26/85. Pollify is a partially working MVP with significant gaps across all dimensions. The app uses Google OAuth only (no email/password auth), supports only single-question polls (violates the multi-question requirement), has no analytics dashboard (just basic vote counts with bars), no publish-results flow, and no deployment. The backend has critical security issues: hardcoded JWT_SECRET fallback ("polling_app"), broken private poll auth enforcement, client-side-only vote deduplication, and in-memory vote storage that's lost on server restart. Socket.io provides real-time vote updates but without rooms, authentication, or state persistence. Code quality suffers from major duplication (SignIn/SignUp near-identical, 5 copies of BASE_URL, 2 copies of JWT_SECRET), all errors swallowed, no tests, and inconsistent naming conventions. The project reads like an early-stage prototype rather than a production-style submission.
Authentication & Access Control3 / 10
Google OAuth only — no email/password registration or login. SignIn and Signup are near-identical components calling the same `/api/auth/google` endpoint (SignIn.tsx:14-47, Signup.tsx:14-48). JWT_SECRET hardcoded as "polling_app" fallback in both controller.ts:7 and auth.middleware.ts:4 — anyone can forge tokens if env var is missing. Token stored in localStorage (SignIn.tsx:28), no httpOnly cookies. No refresh token rotation (model has refreshToken field but Google users get empty string, controller.ts:50). No anonymous/authenticated poll response mode — only "public/private" visibility toggle. Private poll auth check is broken: authMiddleware is called at poll.route.ts:170 but execution falls through to return poll data regardless (line 173). No auth context — each component independently reads localStorage. Logout is just localStorage.removeItem + window.location.reload (Navbar.tsx:27-33).
Poll Creation & Question Management4 / 15
Only single question per poll — the Poll model has a single `question` field (poll.model.ts:49-55), violating the requirement for "multiple questions." No mandatory/optional question handling at all. No anonymous vs authenticated response toggle. React Hook Form with useFieldArray provides dynamic answer add/remove (createpoll.tsx:61-64, 292-311). Server validates min 2 answers (poll.route.ts:47-51) and duration format (poll.route.ts:54-59). Duration supports 4 presets + custom datetime-local (createpoll.tsx:70-100, 233-257). Public/private visibility toggle exists (createpoll.tsx:125-143). No poll editing after creation, no draft state, no slug/permalink. Title and question have min/max length validation both client-side via register options and server-side via Mongoose schema.
Response Collection Flow3 / 15
Voting is Socket.io-only — no REST endpoint for vote submission. Vote counts are stored in-memory (livePolls Map at poll.route.ts:28-29), lost on any server restart. Vote deduplication is purely client-side via localStorage (createdpoll.$id.tsx:68-72) and a `voted` bool state (line 27) — trivial to bypass by clearing localStorage or using incognito mode. No server-side deduplication (no unique index on voter+ poll). No authentication on vote socket handler (poll.route.ts:186-241) — anyone can vote for any poll. Expiry enforced via fragile setTimeout (poll.route.ts:98-113), lost on restart. Vote confirmation modal displays "Vote Submitted!" (createdpoll.$id.tsx:131-149). No server-side validation that answerIndex is within bounds or that options belong to the poll. Private poll auth check is broken (poll.route.ts:169-171 calls authMiddleware but doesn't block execution at line 173).
Analytics & Feedback Dashboard2 / 15
No analytics dashboard exists — only a basic results view on `/pollDetails/$id` (pollDetails.$id.tsx). Shows per-option vote counts with percentages (lines 123-149) and CSS progress bars (line 138-144). Computes total votes via reduce (line 99). Displays visibility/duration badges (lines 111-118). Real-time updates via Socket.io voteUpdate listener (line 50-54). No dedicated creator analytics dashboard, no per-question breakdowns (only 1 question supported), no participation insights, no time-series/trend data, no export functionality, no publish-results toggle (requirement says "once a poll is completed, the creator should also be able to publish the final results"), no list of user's polls, no response history. This is a vote-results display, not an analytics dashboard.
Frontend Experience4 / 10
4 routes: Home (static marketing), Create Poll, Poll Details (results), Voting page. Clean minimalist Tailwind CSS design with consistent border/shadow styling. Loading states present on Create Poll (createpoll.tsx:150-152), Poll Details (pollDetails.$id.tsx:79-87), Voting page (createdpoll.$id.tsx:76). Error/not-found states exist (e.g., "Poll not found", "Please sign in"). But: Home page is entirely static content with no dynamic data (index.tsx). Root layout is bare-bones with empty Link components (__root.tsx:6-12). No shared API client — raw fetch() with hardcoded BASE_URL in 5 files. No auth context — each component reads localStorage independently. ToggleSwitch defined inline in createpoll.tsx (lines 125-143). Auth modals are minimal modals with no transitions. Navbar dropdown uses awkward hover-based interaction. No responsive optimization beyond basic Tailwind. No form error display (RHF validation runs but errors aren't shown).
Backend Architecture & API Design3 / 15
5 REST endpoints total. No input validation library — inline `if` checks only (poll.route.ts:47-59). No Zod/Joi/express-validator. No error handling middleware — all catch blocks swallow error details and return generic 500s. No Helmet, rate limiting, or request body size limits. CORS hardcoded to `http://localhost:5173` (app.ts:8) — won't work in production. JWT_SECRET falls back to `"polling_app"` in two files (controller.ts:7, auth.middleware.ts:4). In-memory livePolls Map is a single point of failure (poll.route.ts:28-29). setTimeout-based poll expiry won't survive restarts (poll.route.ts:98-113). Controller logic inlined in route file — poll.route.ts mixes routes, controllers, and socket handlers all in one 246-line function. Private poll auth check is broken (returns data regardless). No service layer, no pagination, no list endpoints. Mongoose schemas have reasonable field validation (User model: min/max lengths, unique email; Poll model: enum on visibility).
Real-Time Updates Using WebSockets4 / 10
Socket.io integrated with Express HTTP server (server.ts:18-22). 6 events: connection, vote, voteUpdate, newPoll, pollClosed, error (poll.route.ts:27-29, 92-95, 111, 186-241). Vote counting is in-memory via livePolls Map. Server broadcasts voteUpdate with per-answer vote array + total to all clients (poll.route.ts:229-233). PollDetails page receives real-time voteUpdate and updates bars/counts (pollDetails.$id.tsx:50-54). Voting page submits votes via socket.emit("vote") (createdpoll.$id.tsx:64). Gaps: no room-based broadcasting (all clients get all poll updates), no socket authentication, each page creates its own module-level socket connection (pollDetails.$id.tsx:6, createdpoll.$id.tsx:10), no reconnection state recovery, no debouncing on updates, in-memory state lost on server restart, no data pushed over socket — just invalidation signals and counts.
Code Quality & Project Structure3 / 10
TypeScript used throughout but without strict mode — no `strict`, `noImplicitAny`, or `noUncheckedIndexedAccess` in tsconfig.app.json. `any` type on AuthRequest.user (auth.middleware.ts:8). Significant code duplication: SignIn.tsx and Signup.tsx are near-identical (76 vs 79 lines, differ only in headline text and success message), Poll interface duplicated across 3 route files (createpoll.tsx:17-28, pollDetails.$id.tsx:8-19, createdpoll.$id.tsx:12-21), BASE_URL constant duplicated in 5 files, JWT_SECRET fallback duplicated in 2 files, socket.io initialized per-file instead of shared. All catch blocks swallow errors with no logging — including an empty catch block in pollDetails.$id.tsx:42. No tests. No .env.example file. docker-compose.yml has literal password string "MONGO_INITDB_ROOT_PASSWORD" (line 14). Mixed naming: `.route` vs `.routes`, `connectDB` vs `createApp` vs `createRoutes`, `IAnswer` (Hungarian) vs `PollRequest` (no prefix). Module-level folder organization (admin/, poll/, middleware/, common/) is the one good structural choice.
153
polls pluses
Vishal · @bvishal
26
"polls pluses" (PollPulse) is a MERN polling app with functional auth (register/login/JWT), poll creation with dynamic questions/options, voting, and basic results visualization. The UI features a polished dark/light glassmorphism design. However, the implementation has critical flaws: (1) Socket.io is claimed but NOT implemented — zero code, (2) the voting mechanism is fundamentally broken for multi-question polls (each answer redirects immediately), (3) no input validation anywhere in the backend, (4) key features from the requirements (mandatory/optional toggle, expiry enforcement, anonymous/authenticated mode, publish results) are schema-defined but never enforced, (5) tokens in localStorage with no httpOnly cookies, (6) dead code (Results.jsx, App.css boilerplate), and (7) README makes false claims about Socket.io and PostgreSQL support. Total: 26/100 — a partially working prototype with significant missing and broken features.
Authentication & Access Control4 / 10
Basic auth exists: registration with bcryptjs (10 rounds) at authController.js:5-33, login with JWT at authController.js:35-55, authMiddleware at authMiddleware.js:1-15 extracts JWT from Bearer header and attaches userId. Frontend ProtectedRoute in App.jsx:25-28 checks localStorage token. Critical gaps: JWT stored in localStorage (XSS risk, not httpOnly cookie), no email verification, no password reset, no refresh tokens, no rate limiting, no CSRF/Helmet. The poll schema's isAnonymous field (Poll.js:16) is never enforced in any route — no anonymous vs authenticated poll mode logic exists. Score 4 for functional but shallow auth with significant security gaps.
Poll Creation & Question Management4 / 15
CreatePoll.jsx (138 lines) supports dynamic add/remove of questions and options with individual useState hooks. Backend accepts questions/options array via POST /api/polls/create (pollRoutes.js:9-24). However: isMandatory is hardcoded to true (CreatePoll.jsx:33) with no UI toggle, expiresAt field exists in schema but has no UI for setting it (never populated), no anonymous/authenticated toggle in creation form, no client-side validation beyond empty title check (line 23), no server-side validation (no Zod/Joi at all — req.body is trusted directly), no poll editing endpoint, no slug/permalink generation. Score 4 for working dynamic form with significant missing features.
Response Collection Flow2 / 15
VoteView.jsx (75 lines) renders poll options and calls PATCH /api/polls/vote/:id with $inc for atomic increments (pollRoutes.js:52-72). CRITICAL FLAW: each option click is a separate PATCH request that immediately redirects to /results/:id — multi-question polls are broken because users can only answer one question before being redirected. Additionally: no expiry checking (expiresAt never validated), no duplicate vote prevention (unlimited votes), no mandatory question enforcement, no validation that questionIndex/optionIndex are in range, no isAnonymous enforcement, and no check for isPublished status. Score 2 because the core voting mechanism is fundamentally broken for multi-question polls and has zero protection layers.
Analytics & Feedback Dashboard4 / 15
ResultsView.jsx (88 lines) shows per-question option breakdowns with animated percentage bars, vote counts, and per-question total votes using GET /:id/results (pollRoutes.js:74-95). Dashboard.jsx calculates totalVotesCount client-side per poll card (Dashboard.jsx:16-21). Results.jsx (102 lines) is dead code — routes use /results/:id which maps to ResultsView, not Results. Gaps: no publish results endpoint (isPublished in schema is never toggled), no creator-only analytics dashboard, no total response counts, no anonymous/authenticated breakdowns, no peak activity metrics, no trend data, no CSV export, no individual response viewing. Score 4 for working per-question option breakdowns but analytics are shallow.
Frontend Experience5 / 10
6 routes with auth-aware ProtectedRoute pattern (App.jsx:208-230). Dark/light mode via CSS variables and toggle (App.jsx:98-117), glassmorphism UI with Tailwind. Loading spinners/pulse text on dashboard, vote, and results pages. Error and empty states handled. Copy-to-clipboard share links. However: App.css (184 lines) is complete Vite starter boilerplate, HTML title is "client", server dev script points to nonexistent "server.js", Results.jsx is dead code (not in routing), poll creation form lacks expiry picker and mandatory/optional toggle, no form library (raw useState hooks), no confirmation dialogs beyond window.confirm. Score 5 for polished visual design but missing essential form controls and containing dead code.
Backend Architecture & API Design4 / 15
Clean Express + Mongoose architecture with routes/controllers/models separation (server/: 7 source files, 282 lines total). CORS configured with specific origins (index.js:13-21). Owner-based deletion check at pollRoutes.js:106-108. However: ZERO input validation — no Zod, Joi, or express-validator anywhere, req.body is trusted directly in all endpoints. No Helmet, no rate limiting, no request body limits. No service layer (logic in routes). No centralized error handler. Schema fields isAnonymous, isPublished, expiresAt are defined but never enforced in any route. No response model (votes stored inline via $inc). No pagination. JWT in localStorage with no httpOnly cookie. Score 4 for decent separation but missing essential middleware and validation layers.
Real-Time Updates Using WebSockets0 / 10
Socket.io is completely absent from the codebase. No socket.io dependency in server/package.json, no imports or setup code in any .js/.jsx source file. The README claims "Real-time Analytics: Powered by Socket.io" but this is false. ResultsView.jsx uses a 10-second setInterval polling fallback (ResultsView.jsx:25-26) rather than WebSockets. Score 0 for zero implementation.
Code Quality & Project Structure3 / 10
Clean client/server folder separation with routes/controllers/models pattern on backend. Docker compose for local dev. CSS variables design system. However: no TypeScript, zero tests, no ESLint on server. Results.jsx (102 lines) is dead code not connected to any route. App.css (184 lines) is complete Vite boilerplate. Server dev script broken ("nodemon server.js" — file doesn't exist). README makes false claims about Socket.io and PostgreSQL support. No .env.example files. HTML title is "client". No documentation on setup/env vars. Score 3 for acceptable basic structure undermined by dead code, broken scripts, false claims, and complete lack of testing/TypeScript.
154
S
PulseBoard
SNEHA · @sneha12c
24
PulseBoard by Sneha12c is a partial MERN polling platform. The backend has a reasonable auth scaffold (JWT, refresh tokens, cookies) and poll CRUD with Zod validation, but many features are incomplete or buggy. No deployment exists. No root README. Critical bugs include: response submit route lacks auth middleware, token field mismatch between frontend/backend, optionlist/optionList casing bug, wrong API paths, and an empty response validation file. Socket.io client exists on the frontend but there is zero server-side implementation. The frontend has 6 routes with basic Tailwind styling but no landing page, no protected routes, no auth context, and uses alert() for notifications. Analytics show raw ObjectIds rather than question text. No poll editing, no publish-results UI, no .env templates. Total: 24/80.
Authentication & Access Control4 / 10
Backend: register/login with Zod validation, bcryptjs (12 rounds), JWT access (15m) + refresh (7d) tokens, httpOnly cookies, refresh token rotation in DB, logout clears cookies + DB. Auth middleware verifies from cookies or Authorization header. Frontend: Login.tsx (102 lines) and Register.tsx (115 lines) with form handling. Critical gaps: no frontend protected route logic — anyone can navigate to /dashboard without auth; response submit route (router.ts:8) has NO auth middleware, so req.user is always undefined, breaking the authenticated/anonymous gate in controller.ts:34; frontend stores res.data.token (Login.tsx:41) but backend returns res.data.accessToken — token key mismatch; no auth context/provider; no Helmet, CSRF, or rate limiting; secure:false on cookies; no email verification.
Poll Creation & Question Management4 / 15
CreatePoll.tsx (175 lines) supports dynamic questions with add, option editing, required toggle, and question type (single/multiple choice). Backend poll schema embeds questions with Zod validation (pollSchema + questionSchema in validate.ts:15 lines). Significant gaps: no poll title/name field anywhere (polls have no title); expiryTime hardcoded to Date.now()+86400000 (CreatePoll.tsx:40) — no date picker UI; authenticated hardcoded to true (CreatePoll.tsx:39) — no anonymous/authenticated toggle; no edit or delete poll endpoints; no server-side validation for min 2 options per question; QuestionCard component (75 lines) defined but never used; poll link stored as full URL but frontend uses just the ID as route param.
Response Collection Flow2 / 15
Backend ResponseController (107 lines) has expiry check (line 26), auth gate (line 34), and duplicate prevention (line 41-51). Response model uses answerList array with questionId+selectedOption. However, multiple bugs make it non-functional: (1) response submit route has NO auth middleware, so req.user is always undefined; (2) PollForm.tsx uses `/response/${pollLink}` (line 36) but backend route is `/response/submit/:polllink` — path mismatch; (3) `optionlist` vs `optionList` casing bug — PollForm reads `question.optionlist.map` (line 71) but schema uses `optionList`; (4) response validate.ts is completely empty (0 lines) — no server-side answer validation; (5) no client-side required question validation; (6) answers array built with sparse indices when questions are skipped; (7) no error/expiry/already-submitted UI states.
Analytics & Feedback Dashboard3 / 15
Backend handleAnalytics (controller.ts:85-107) uses MongoDB aggregation pipeline ($match→$unwind→$group) to count votes per questionId+option. Returns totalResponses and breakdown. Frontend Analytics.tsx (93 lines) shows total responses and per-item question/option/votes with basic bars. AnalyticsChart.tsx (59 lines) renders progress bars. Critical gaps: aggregation returns raw MongoDB ObjectIds as questionId — no question description lookup, so creator sees hex strings not question text; no publish results button on frontend (backend has endpoint but never called); progress bars use Math.min(count*10, 100)% (hardcoded multiplier, not real percentage); no CSV export; no per-question response counts or drop-off; no participation insights beyond total count; dashboard doesn't show response counts on poll cards.
Frontend Experience3 / 10
Six routes defined (login, register, dashboard, create-poll, poll/:pollLink, analytics/:pollId). Tailwind CSS v4 with basic responsive styling. Functional pages for core flows. Issues: no landing/home page (root "/" renders nothing); no protected route logic — any user can navigate to /dashboard without auth; no auth context/provider; Navbar component is completely empty (0 bytes); alert() used for all notifications (Login.tsx:46, Register.tsx:49, CreatePoll.tsx:47); React Hook Form installed but unused; no loading states except simple "Loading..." text in PollForm; no error boundaries; no confirmation dialogs; index.css and App.css retain Vite starter boilerplate (hero/.framework/.vite classes, #next-steps, #docs — all from template); HTML title is "frontend"; no landing/hero page for the actual polling app.
Backend Architecture & API Design4 / 15
Express 5 + TypeScript + Mongoose with clean module structure: auth/, poll/, response/, middleware/, utils/. Zod validation schemas for auth and poll. Controller classes with method binding. MongoDB aggregation pipeline for analytics. Issues: no Helmet, no rate limiting, no request body size limits, no centralized error handler (every controller has its own try/catch); response validate.ts is 0 bytes; fetchResponse (controller.ts:79) uses responseModel.findById(pollId) instead of find({pollId}) — fetches a single doc by ObjectId, not all responses for a poll; fetchPoll (controller.ts:53-63) has no auth check — anyone can fetch any poll; route parameter naming inconsistent: :polllink vs :id vs :pollId across routes; CORS allowlist via env var with no default — could be misconfigured; console.log("bnjd", mongourl) debug statement in db/index.ts:6.
Real-Time Updates Using WebSockets1 / 10
Frontend: socket.io-client installed (package.json:18) and Socket singleton created in socket.ts (4 lines) connecting to VITE_API_URL. Analytics.tsx emits "join_poll" on mount, listens for "analytics_updated" to trigger HTTP refetch, and cleans up with socket.off on unmount. Backend: ZERO Socket.io server implementation. No socket.io dependency in backend package.json. The only reference is a comment placeholder in response controller.ts:61-62 ("// SOCKET EVENT HERE // io.to(poll._id.toString()).emit(...)"). Without a server, the client socket connection will fail or hang indefinitely — this feature is effectively unimplemented.
Code Quality & Project Structure3 / 10
TypeScript used in both backend and frontend. Backend has strict tsconfig (noUncheckedIndexedAccess, exactOptionalPropertyTypes, strict:true). Modular folder structure with controller/validate/model separation per feature. Zod for validation. Issues: no root README (Backend README is 2 lines, Frontend README is Vite starter boilerplate); no .env.example files; Navbar.tsx is completely empty file; QuestionCard.tsx defined but never imported/used; App.css has stale Vite starter boilerplate (hero, framework, vite, counter classes for a different app); response/validate.ts is 0 bytes; debug log "bnjd" in db/index.ts; `any` types throughout all frontend pages; no tests; no linting configured in backend; naming inconsistencies: polllink vs pollLink vs pollId vs id across files; optionlist vs optionList casing bug; token field mismatch (token vs accessToken).
155
Quizzler
Tanish Rastogi · @codetanish
22
Quizzler is a polling platform with a deployed React frontend (Vercel) and Express/PostgreSQL backend. The frontend shows strong visual design with GSAP animations and consistent "neo-brutalist" styling across multiple routes including poll creation, response collection, and community browsing. However, the project is fundamentally incomplete: the server has zero authentication (no JWT/bcrypt/auth middleware), no analytics dashboard (all dashboard pages are decorative placeholders), no real-time WebSocket support (not attempted), individual poll responses are never stored (only aggregate counts), no expiry/mandatory/optional question features, and the user/quiz modules are empty stubs. The frontend auth relies on an external OIDC service not part of this project. Total score: 22/100.
Authentication & Access Control2 / 10
Server has ZERO auth implementation: no JWT/bcrypt/passport packages, no auth middleware, no protected routes. User controller (user.controller.ts) is entirely commented-out stubs, user routes are all commented out (user.routes.ts line 6). The frontend login/register forms (login.jsx, register.jsx) call an external OIDC service via authService.js, but the server never validates tokens or enforces authentication. JWT token is stored in localStorage and used as Bearer token for getProfile(), but incorrectly passes storage.getUserId() which returns raw localStorage value, not a JWT. CORS is wide open (origin: '*'). No anonymous vs authenticated poll mode enforcement exists at any level.
Poll Creation & Question Management4 / 15
Frontend createPool.jsx (581 lines) implements a dynamic question builder with add/remove/duplicate questions, add/remove options (max 4), and client-side validation (empty checks, duplicate detection). Server POST /feedback creates poll+questions+options in sequence (feedback.service.ts:116-152). However, MAJOR gaps: no mandatory/optional toggle on questions (no isRequired field in schema or UI), no expiry time support (no expiresAt field, no datetime picker), no anonymous/authenticated poll mode toggle, no poll editing endpoint exists (only create), no server-side validation of min 2 options/question or min 1 question requirement, no slug/permalink, no form library.
Response Collection Flow3 / 15
Frontend feedbackPool.$id.jsx (432 lines) renders poll questions with click-to-select option UI, tracks selected answers, and handles submission with proper state coverage (loading/error/success/already-submitted). However, the implementation is critically shallow: the submission endpoint POST /feedback/:id/submit stores ONLY aggregate counts (totalQuestions, answeredQuestions, ipAddress) — individual question answers are NEVER stored (see SubmitFeedbackBody type: {pollId, totalQuestions, answeredQuestions}). The IP-based duplicate check in feedback.repository.ts:91-95 is crude and prevents any two users behind same NAT from submitting. No expiry checking (no expiresAt field), no server-side mandatory question validation, no validation that submitted optionIds actually belong to the poll. No authentication gating exists.
Analytics & Feedback Dashboard1 / 15
No analytics dashboard implementation exists. The user/dashboard.jsx page is purely decorative with hardcoded empty stats and a TODO comment: "TODO: replace with real API calls" (line 39-41). The fetchDashboardData function body is empty. The user/profile.jsx similarly has an empty data fetch. No response summaries, no per-question option counts, no participation insights, no results publishing page. The publishPollById endpoint exists (flips status to 'publish') but has no corresponding results view. The anonumous/dashboard.jsx shows drafted/published polls as a list but provides zero analytics. This is a placeholder UI with no functional analytics.
Frontend Experience5 / 10
Consistent neo-brutalist design language (heavy borders, bold typography, shadows) across all 14+ routes using Tailwind CSS v4. GSAP animations throughout (page transitions, question animations, error/success feedback, scroll triggers). Good state coverage: loading spinners, animated error banners, empty states, success messages. TanStack Router with nested layouts (auth, user, anonymous, root). Search filtering on feedback card page. However: NO TypeScript on frontend (all .jsx/.js), form UX uses individual useState hooks rather than a form library, "anonumous" typo used consistently across 10+ files, password strength indicator is commented out, no confirmation dialogs for destructive actions, README is untouched Vite starter boilerplate with no project-specific documentation.
Backend Architecture & API Design4 / 15
Server uses Express 5.x + TypeScript + Drizzle ORM + PostgreSQL with clean module separation (modules/feedback, user, quiz), repository-service-controller layering, custom ApiError/ApiResponse classes, and typed TypeScript interfaces (feedback.types.ts:154 lines). Drizzle schema has proper foreign keys with cascading deletes and an enum type for poll status. However the server is fundamentally incomplete: NO authentication middleware anywhere, NO input validation library (no Zod/Joi/express-validator), NO rate limiting, NO Helmet, CORS is unrestricted (origin: '*'), user/quiz modules are entirely stubs (user controller: 5 lines of commented code), no centralized error handler, missing critical DB fields (expiresAt, isRequired, isAnonymous). The architecture patterns are good scaffolding but the implementation stops short of being functional.
Real-Time Updates Using WebSockets0 / 10
Zero implementation. No Socket.IO or WebSocket dependency exists in either package.json. A grep for `socket.io`, `socketio`, `new WebSocket`, `ws://`, or `wss://` returned zero matches across the entire codebase. The http.createServer(app) in index.ts does not integrate with any WebSocket library. Kafka is listed in docker-compose.yml but is never imported or used in any code file. This feature was not attempted.
Code Quality & Project Structure3 / 10
Good module structure on backend with layered architecture. TypeScript used on backend with defined types. However: NO TypeScript on frontend (all .jsx/.js files), zero automated tests, READMEs are boilerplate (server: bun init text, frontend: Vite starter template), "anonumous" typo used consistently across 10+ files (directory name and all route references), duplicate eslint configs (eslint.config.js AND eslint.config.ts), no .env.example file, console.log left in production code (feedback.controller.ts:92), jwtDecode/decode.js reads from storage at module scope (side effect at import time), quiz model stores correctAnswer in plaintext in DB, authService passes localStorage userId as Bearer token instead of actual JWT. Docker-compose includes Kafka and Redis services that are never referenced in application code.
156
Pulls Polls
Ranjan Gupta · @jay
21
Pulse-Polls ("Pulls Polls") is a well-structured but entirely non-functional scaffold. The README explicitly states on line 375: "Note: This is Step 1 - Foundation setup. Business logic, authentication, poll management, analytics, and real-time features will be implemented in subsequent steps." Every backend route (17 across auth, polls, responses, analytics) returns placeholder "not implemented yet" messages. Auth middleware is a stub that always calls next(). No Mongoose models exist. Socket.io only logs connections. The frontend has polished UI components (shadcn/ui, Framer Motion, react-hook-form forms, loading/error/empty states) but all data is hardcoded and forms only console.log. The deployment URL (pulsepolls.ranjangupta.online) returns HTTP 404 from Vercel with DEPLOYMENT_NOT_FOUND. Good infrastructure and architecture decisions (monorepo, shared types, TypeScript strictness, Prettier/ESLint/Husky), but zero working features. Total: 21/80.
Authentication & Access Control2 / 10
Auth routes are all stubs (auth.routes.ts:10-50 — all 5 endpoints return "not implemented yet"). Auth middleware (auth.ts:4-8, 10-14) always calls next() without any JWT verification. JWT utility exists and is well-implemented (jwt.ts — generate/verify access + refresh tokens) but is never consumed. Frontend LoginPage (line 25-27) and RegisterPage forms validate via react-hook-form+zod but onSubmit only console.logs. AuthStore with Zustand persist exists but never populated by any login flow. No anonymous vs authenticated poll mode logic anywhere. Score 2 for a decent scaffold that never reached implementation.
Poll Creation & Question Management2 / 15
CreatePollPage.tsx (71 lines) is a purely static form with title input, description textarea, and two PollTypeCard static selection cards — no react-hook-form integration, no submit handler, no state management. No question management at all (no add/remove questions, no option management, no mandatory/optional toggles). No expiry time support. POST /api/v1/polls route (polls.routes.ts:17-23) returns "Create poll - not implemented yet". No Mongoose poll schema exists anywhere. Score 2 for static form UI and route stubs only.
Response Collection Flow1 / 15
Both response routes are stubs (responses.routes.ts:8-23): POST / returns "Submit response - not implemented yet", GET / returns empty array. PublicPollPage.tsx renders static, hardcoded sample options with a non-functional "Submit Vote" button. No validation, no duplicate prevention, no expiry enforcement, no anonymous/authenticated checking, no answer storage logic exists anywhere. The IResponse interface exists in shared-types (line 54-56) but is never used. Score 1 for nothing beyond route stubs and a static UI.
Analytics & Feedback Dashboard1 / 15
Analytics route is a stub (analytics.routes.ts:8-13) returning "{ analytics: {}, message: 'Get analytics - not implemented yet' }". AnalyticsPage.tsx always renders EmptyState with BarChart icon — no data fetching, no charts. DashboardPage.tsx shows three StatCards with hardcoded "0" values. No per-question breakdowns, no option counts, no participation insights, no result publishing endpoint, no Recharts integration despite being claimed in submission. Score 1 for static placeholder UI only.
Frontend Experience4 / 10
Good component architecture: 9 routes with MainLayout/DashboardLayout separation, ProtectedRoute wrapper, shadcn/ui components (Button with 6 cva variants, Card with 5 sub-components, Input with forwardRef). LandingPage has framer-motion hero animation and feature cards. LoginPage/RegisterPage have working form validation via react-hook-form+zod with field-level errors. Common components exist for EmptyState, ErrorFallback, Skeleton/CardSkeleton/TableSkeleton. Sonner toast and TanStack Query configured. However, everything is non-functional: forms submit to console.log, all data is hardcoded/static, CreatePollPage has no form library integration, multiple components (MotionWrapper, ErrorFallback, Skeleton, useDebounce) are never used by any page. Score 4 for polished-but-non-functional UI scaffold.
Backend Architecture & API Design4 / 15
Server infrastructure is well-constructed: Zod env validation with minimum length requirements (env.ts), Winston logger with multi-transport, Helmet, CORS with specific origin, rate limiting, cookie-parser with secret, compression, request logger middleware, AppError class + global error handler + notFound handler, Zod validation middleware, asyncHandler wrapper, standardized ApiResponseUtil (success/error/created/noContent), JWT util with access/refresh tokens, MongoDB connection with event listeners, graceful shutdown. However, ZERO Mongoose schemas/models exist, all business logic routes are stubs, auth middleware is empty, validate middleware is never used, and no controllers/services/repositories implement any feature. Score 4 for excellent infrastructure that has nothing built on top of it.
Real-Time Updates Using WebSockets2 / 10
Socket.io server initialized (socket/index.ts:6-27) with HTTPServer attachment and CORS config. Connection/disconnect events are logged. But room join/leave functionality is commented-out TODOs (lines 22-23), no server-emitted events exist, and no client-side socket.io code exists anywhere — the client doesn't import or use socket.io-client. Score 2 for basic Socket.io attachment that only logs connections with zero real-time features.
Code Quality & Project Structure5 / 10
Clean monorepo with pnpm workspaces, shared-types package with proper domain modeling (enums, interfaces, API routes, HTTP status constants). Strong TypeScript strictness (strict, noUnusedLocals, noUnusedParameters across all tsconfigs). Consistent naming conventions (PascalCase components, camelCase utilities, UPPER_SNAKE_CASE constants). Prettier + ESLint + Husky + lint-staged configured. shadcn/ui pattern followed correctly. However: 12+ unused exports (MotionWrapper, ErrorFallback, Skeleton, useDebounce, STORAGE_KEYS, ROUTES, API_ROUTES, formatDate, etc.), 3 `any` usages in api.ts, 19 TODO markers, zero tests, route stubs duplicated 17 times, LoginPage/RegisterPage near-identical structure. README admits "Step 1 - Foundation setup" on line 375. Score 5 for good engineering foundations with significant unused code and no tests.
157
Pulse board
sushil sunar · @sunarsushil100_14a0bc99
19
"Pulse Board" is a heavily backend-skewed submission (~982 lines backend, ~22 lines frontend). The backend demonstrates competent Express.js engineering with TypeScript, Zod validation, JWT auth with argon2, and proper Mongoose schemas — but the frontend is essentially an untouched Vite starter template ("Hello world" in App.tsx, empty login page). The submission has no functional user interface whatsoever: no poll creation form, no response collection flow, no analytics dashboard. Several backend features are incomplete or broken: analytics is a completely empty stub, the answer submission logic stores booleans instead of option indices (a conceptual bug), Socket.IO room joining exists but no events are ever emitted, and getPollInfo is an empty function. The backend auth and poll creation validation show promise but the project as a whole is far from a working full-stack application.
Authentication & Access Control4 / 10
Backend auth is solid: registration with argon2 hashing + duplicate email check (controller.ts:32-74), login with password verification (controller.ts:76-109), JWT access+refresh token strategy with cryptographic separation (jwt.ts:22-35), httpOnly cookies (cookie.ts:1-9), refresh token rotation (controller.ts:111-158), logout with cookie clearing (controller.ts:179-201), Zod DTO validation on signup/signin (dto.ts:1-21), and a middleware chain (validateBearerToken → authenticate at middleware.ts:5-39). Optional auth via setUserdata (set_user_id.ts:1-21) enables anonymous+authenticated poll modes. However, the frontend has ZERO auth implementation — App.tsx renders "Hello world", login.tsx is an empty div, no auth context, no routing, no protected routes. Minor bug: cookie type labels are swapped (setCookieForAccessAndRefreshToken passes "refresh" for accessToken cookie, "access" for refreshToken cookie at controller.ts:20-29). No Helmet, CSRF protection, or rate limiting. Empty .env.example (0 bytes).
Poll Creation & Question Management4 / 15
Backend poll creation has thorough validation: pollDTO in dto.ts:34-54 validates title (3-100 chars), optional description (max 255), questions array (min 1, max 10), isAuthenticated boolean, and expiryTime must be future. Each questionDTO (dto.ts:3-32) validates name, options array (min 2, max 10, unique), correctOption index within range, and isOptional flag. Mongoose schema duplicates validation at models.ts:56-110 (options uniqueness case-insensitive, correctOption range check). createPoll controller (controller.ts:7-35) creates Poll document, batch-inserts Questions via insertMany, and links them. However: zero frontend implementation (no poll creation form, no dynamic question/option UI), no poll edit endpoint, no poll delete endpoint, no slug/permalink generation for sharing.
Response Collection Flow2 / 15
takePoll endpoint exists at controller.ts:37-79 and checks poll existence, auth requirement (isAuthenticated flag), and answer length matches question count. Unique compound index on {user, poll} in PollAnswer schema (models.ts:134) provides basic duplicate prevention for authenticated users. However, there is a critical conceptual bug: the DTO (dto.ts:56-62) validates answers as z.array(z.number()) — option indices — but the controller compares each answer to correctOption (controller.ts:61-68) producing a boolean[], then stores that boolean array as the answer (controller.ts:70-74). This means submitted option indices are treated as correctness checks rather than actual selections. No expiry time enforcement on submission (controller never checks if poll.expiryTime has passed). getPollInfo is an empty stub (controller.ts:81). No frontend poll-taking UI exists.
Analytics & Feedback Dashboard0 / 15
Zero analytics implementation. getPollInfo controller (controller.ts:81) is an empty stub function with no body. There are no analytics endpoints, no response counting, no per-question breakdowns, no participation insights, no dashboard, and no publish results functionality. The Poll model includes isPublished and isCompleted fields (models.ts:34-43) but nothing in the codebase writes to or reads from them. This feature was not attempted.
Frontend Experience0 / 10
Frontend is essentially untouched Vite starter boilerplate. App.tsx renders a single line: '<div className="text-amber-500">Hello world</div>' (5 lines). login.tsx is an empty div component (5 lines). main.tsx is standard React entry point. index.html title is "frontend" — never customized. No routing (no react-router), no forms, no poll creation UI, no poll response UI, no dashboard, no auth pages, no components. Only dependency added beyond Vite defaults is Tailwind CSS v4 and its Vite plugin. The 22 lines of frontend code across 3 files are effectively unchanged from the Vite+React+TS template.
Backend Architecture & API Design5 / 15
Clean architecture with routes→controllers→models separation across users and polls modules. Strong TypeScript usage (strict mode, noUncheckedIndexedAccess in tsconfig.json). Zod DTO validation on all input endpoints (validate_dto.ts:5-20 middleware with safeParse). Mongoose schemas with proper indexes (pollAnswerSchema index on {user, poll} unique), field-level validators (options uniqueness, correctOption range), and refs between Poll/Question/User. JWT access+refresh dual-token strategy (jwt.ts:22-48). Argon2 password hashing (password.ts). Cookie-based auth with httpOnly (cookie.ts). Global error handler distinguishes TokenExpiredError, E11000 duplicate key, and generic 500s with env-aware stack traces (error_handler.ts:1-36). However: no service layer (controllers talk directly to models), empty .env.example (0 bytes), takePoll answer logic stores booleans when DTO expects number indices, unused import `import { string } from "zod/v3"` in users/controller.ts:13, no Helmet/CORS/rate-limiting security middleware, no pagination on any endpoint, no graceful shutdown, console.log in production (server.ts:17, db.ts:5), @ts-ignore in polls/controller.ts:62, getPollInfo is empty stub.
Real-Time Updates Using WebSockets1 / 10
Socket.IO server is initialized with CORS:* in sockets/index.ts:1-14, and a room-based pattern exists: clients can emit "join-poll" to join a poll-specific room (poll.socket.ts:4-8). Event constants are defined: JOIN_POLL, POLL_UPDATE, NEW_VOTE (socket_events.ts:1-5). However, zero server-emitted events exist anywhere — the takePoll controller never calls io.to(pollId).emit(), createPoll never broadcasts, and there are no polling status change events. POLL_UPDATE and NEW_VOTE constants are defined but never referenced in any emit call. The frontend has no socket.io-client dependency in package.json and no socket connection code. This is a non-functional scaffold.
Code Quality & Project Structure3 / 10
Clean monorepo structure with backend/ and frontend/ separation. Consistent naming conventions throughout backend (camelCase variables, PascalCase types, kebab-case files). TypeScript used throughout with proper type augmentation for Express Request (types/express.d.ts). However, the README is only 23 lines and minimally descriptive. .env.example is completely empty (0 bytes). Frontend is effectively dead code (Vite boilerplate). Unused import in users/controller.ts (line 13: `import { string } from "zod/v3"`). @ts-ignore in polls/controller.ts:62. Cookie type labels are swapped (access/refresh). getPollInfo is an empty function body. takePoll stores booleans instead of option indices — a fundamental logic bug. No tests anywhere. console.log used for operational logging instead of a proper logger. ESLint configured for frontend only, no backend linting.
158
Mythos Polls
Shalini Nigam · @shalininigam765_f119850c
13
Mythos Polls is a visually-themed polling app with 4308 lines of code and extensive CSS (2571 lines) that unfortunately suffers from fundamental integration failures making it non-functional. The backend has well-designed models and utility classes but critical bugs prevent any feature from working end-to-end: the poll routes mount is missing a leading slash (app.js:39), the verifyJWT middleware uses a named-vs-default export mismatch (rendering auth checks non-functional), the poll controller uses a schema completely different from the Poll model (guaranteeing creation failures), and the voteOnPoll handler is imported but never wired to any route. On the frontend, there's no React Router (useState-based navigation), making the VotingPage component dead code. Has Socket.io with basic emit/listen but no rooms or auth. Zero analytics implementation. The project appears to be a partial assembly from different sources that was never integrated. Deployed URL returns 404. Total score: 13/100.
Authentication & Access Control2 / 10
Registration and login endpoints exist with bcrypt hashing (10 rounds), JWT access+refresh tokens, and httpOnly cookies (user.controller.js:94-117). However, the verifyJWT middleware is exported as a NAMED export (`export const verifyJWT`) but imported as DEFAULT (`import verifyJWT from ...`) in polls.routes.js:3 — making the middleware `undefined` and all auth checks non-functional. Additionally, `req.user.userId` (poll.controller.js:79) doesn't match the JWT payload which uses `_id` (user.models.js:64). No logout, no password reset, no rate limiting, no CSRF protection. Frontend has no auth context/provider (just prop drilling), no protected route guards (useState-based navigation), and Login.jsx has a ReferenceError bug: `err` is used in catch but variable is `error`.
Poll Creation & Question Management1 / 15
The Poll model (polls.models.js) defines a multi-question schema with title, questions[] (each with text, options[], isMandatory), createrId, expiresAt, and isPublished — a well-structured design. However, the createPoll controller (poll.controller.js:4-35) uses entirely different fields: `question` (string, not `title`), `options` (flat array, not `questions`), `creator` (not `createrId`), and `shareId` (uuid, not in schema). This schema-controller mismatch would cause poll creation to fail at the database level. Frontend (createPoll.jsx, 118 lines) only supports single-question input, has no mandatory/optional toggle, no expiry date picker, and no multi-question support. No edit or delete endpoints exist.
Response Collection Flow1 / 15
The voteOnPoll controller (poll.controller.js:64-112) has basic logic for finding polls by shareId, validating optionId, checking requiresAuth, preventing double-voting via voters array, and incrementing vote counts. However, it is imported in polls.routes.js:2 but NEVER wired to any route — there is no POST route for vote submission (only GET /, POST /create, GET /vote/:shareId exist). This means votes cannot be submitted through the API. Additionally: the Response model (response.models.js, 27 lines) is never used (votes go on the Poll document directly), expiry checking (expiresAt field) is never performed, and the VotingPage frontend component uses `useParams` from react-router-dom but is never rendered by App.jsx (which uses useState-based navigation instead of React Router). The entire response collection flow is effectively broken at both API and frontend levels.
Analytics & Feedback Dashboard1 / 15
Zero analytics implementation. The dashboard (dashboard.jsx, 100 lines) is a basic poll card list showing only question text and option count — no per-option vote breakdowns, no total response counts, no participant insights, no charts or visualizations. The `isPublished` field on the Poll model and the unused Response model suggest analytics were planned but never built. No analytics endpoints exist (no aggregations, no summary computation). No results publishing feature (no toggle for isPublished, no public results view). The only "analytics-like" UI is the hardcoded static PollCarousel (PollCarousel.jsx, data defined at lines 4-60) which shows fake data with bar charts — purely decorative, not connected to any real poll data.
Frontend Experience2 / 10
Significant CSS effort (2571 lines across 11 files) with a cohesive dark fantasy "Mythos" theme, CSS custom properties (index.css:3-17), animated orbs/particles, scroll-triggered navbar, and a 3D flip card carousel (PollCarousel.jsx:85-125). However, the functional app is severely broken: no React Router (uses useState for page switching — App.jsx:14-21), meaning no URL-based navigation and VotingPage (which uses `useParams`) is dead code. Login/Register forms hardcode `axios.post("http://localhost:8080/...")` instead of relative URLs. No responsive hamburger menu on mobile navbar. Poll creation only has single-question input. The landing page components (Hero, Features, Security, PollCarousel) are purely static marketing content with no functional integration.
Backend Architecture & API Design2 / 15
Clean layered architecture (routes→controllers→middleware→models→utility) with well-designed utility classes: ApiError with status codes/stack traces (apiError.js:24 lines), ApiResponse with success boolean (apiResponse.js), and asyncHandler for promise wrapping (asyncHandler.js). Models are well-structured with proper sub-documents and refs. However, fundamental integration failures: (1) polls route missing leading slash — `app.use("api/v1/polls", ...)` at app.js:39 renders all poll routes unreachable, (2) schema-controller mismatch (Poll model vs controller fields are entirely different), (3) verifyJWT named-vs-default export mismatch renders auth middleware non-functional, (4) voteOnPoll imported but never used in any route, (5) Response model defined but never instantiated. No input validation library, no centralized error handler, DB_NAME hardcoded as "PulseBoard" (copy from another project), no rate limiting, no Helmet, and inconsistent error handling between controllers (asyncHandler+ApiError vs raw try/catch).
Real-Time Updates Using WebSockets2 / 10
Socket.io server is set up with Express HTTP server (app.js:11-23) and stored via `app.set('socketio', io)`. Two event types: `newPoll` emitted on poll creation (poll.controller.js:25) and `voteUpdate` emitted with full poll data on vote (poll.controller.js:104). Dashboard.jsx listens for `newPoll` with proper cleanup (dashboard.jsx:34-49), and VotingPage.jsx listens for `voteUpdate` with shareId filtering and cleanup (votingPage.jsx:37-50). However, events are global broadcast (no room-based pattern), no socket authentication, VotingPage component is dead code (never rendered), hardcoded localhost:8080 URLs, no reconnection configuration, no debouncing, and the `io.on('connection')` handler only logs (app.js:34-36) — no room join/leave logic at all. Since the vote endpoint doesn't exist in routes, the voteUpdate event is effectively never triggered.
Code Quality & Project Structure2 / 10
Clean folder separation (server/src/{controllers,models,routes,middleware,utility,db}, client/src/{components,dashboard,sign-up}) and consistent ES module usage. CSS uses design tokens via custom properties (index.css:3-17). However, the codebase shows strong evidence of being assembled from incomplete/borrowed parts: DB_NAME is "PulseBoard" (db/index.js:4, from a different submission), `createrId` typo in Poll schema vs `creator` in controller, the Poll schema and controller handle entirely different data shapes. Critical bugs: `err` vs `error` variable reference in Login.jsx:30-32, missing leading `/` on route mount (app.js:39), named-vs-default export mismatch for verifyJWT. No TypeScript, no tests, no .env.example file. ESLint configured on client but not server. Login controller has leftover `console.log("response:", response)` debug line (user.controller.js:115). README has placeholder clone URL (`github.com/your-username/your-repo-name`).
159
S
Pollwave
sabin shrestha · @sabinshrestha292
12
PollWave is an early-stage authentication scaffold with a well-structured server architecture but zero poll-specific functionality. The server implements working registration and login (password hashing, JWT tokens, PostgreSQL via Drizzle ORM) with clean controller/service/repository layering. However, 3 of 5 auth endpoints are empty stubs, no authentication middleware exists, and the entire poll domain (creation, response collection, analytics, publishing, WebSockets) is completely absent. The client is an unmodified Vite starter template with a counter button. No deployment exists. The project is fundamentally incomplete for the Full Stack Engineering track requirements — it's essentially just an auth boilerplate.
Authentication & Access Control4 / 10
Registration and login work with proper password hashing (bcryptjs, 10 rounds), JWT generation, httpOnly cookies, and email uniqueness checks (auth.service.ts:22-58). However, three endpoints (GET /self, GET /refresh, POST /logout) are empty stubs returning undefined (auth.service.ts:89-93). No JWT authentication middleware exists to protect routes. No anonymous/authenticated poll modes are implemented. Passwords (hashes) are returned in response bodies (auth.service.ts:55,85). No rate limiting present. Score 4: partial - only 2 of 5 auth routes have real logic.
Poll Creation & Question Management0 / 15
Zero poll-related features implemented. No poll schema/table in database (db/schema.ts only exports users and refresh_tokens). No poll creation API routes. No question management. No mandatory/optional handling. No form management on client side. The project has no poll module at all despite being named "PollWave".
Response Collection Flow0 / 15
Zero response collection features implemented. No poll response submission endpoints, no response validation, no expiry handling for polls, no response restrictions. No poll_answer or response tables exist in the database schema. Not a single line of response collection code exists.
Analytics & Feedback Dashboard0 / 15
Zero analytics or feedback dashboard implemented. No API routes for analytics, no option count summaries, no participation insights. No dashboard UI on client side. No result publishing functionality exists.
Frontend Experience1 / 10
The client is the completely unmodified Vite + React + TypeScript starter template. App.tsx (122 lines) is the default counter demo with useState, Vite/React logos, and documentation links. Zero PollWave-specific code: no routing, no forms, no poll UI, no auth forms, no API integration. The template has a working counter button, hence score 1 instead of 0.
Backend Architecture & API Design4 / 15
Server has a clean module-based architecture (controller/service/repository/DTO), proper TypeScript strict mode, Joi body validation middleware, Zod env validation, Drizzle ORM with PostgreSQL, and centralized error handling. However: (1) uses PostgreSQL, not MongoDB as required; (2) three auth service methods are empty stubs; (3) TokenService constructor ignores injected dependency (token.service.ts:9); (4) refresh tokens not persisted on login (auth.service.ts:81); (5) passwords included in API responses; (6) no auth middleware exists; (7) no CORS, rate limiting, or security headers; (8) stale dist/ artifacts committed. Score 4: decent scaffolding but major unfinished gaps.
Real-Time Updates Using WebSockets0 / 10
Zero WebSocket or Socket.io implementation. No socket.io package in either package.json. No WebSocket-related imports, setup, event handlers, or real-time data emission anywhere in the source code. The grep for 'socket' across all non-node_modules files returned zero hits in project source.
Code Quality & Project Structure3 / 10
Folder structure follows a clean module-based convention (controller/service/repository/schema/dto/hooks). TypeScript strict mode enabled with consistent kebab-case filenames and PascalCase classes. However: BaseRepository uses `as any` on every Drizzle query (6 occurrences, defeating type safety); no README.md at project root (only Vite boilerplate in client/); `.env` with secrets committed; `dist/` not gitignored and full of stale artifacts; console.log left in auth.service.ts:26; inconsistent error handling (asyncHandler on register but not login/others); dual validation libraries (Joi + Zod); zero tests; empty stub classes (UserService, UserRepository). Naming conventions are mostly followed. Score 3: structure is reasonable but quality issues are pervasive.
160
PulseBoard: Live Polls For Feedback
Hitakshi · @hitakshi1270_d9ac6685
7
PulseBoard: Live Polls For Feedback is a static HTML/CSS/JS landing page, not a full-stack application. It contains zero backend code (no Express, MongoDB, authentication, APIs, or WebSockets). The app consists of a single hardcoded poll demo with locally simulated voting, a dark mode toggle, and basic CSS progress bars. None of the required features (auth, poll creation, response collection, analytics, real-time updates) are actually implemented beyond cosmetic UI. Total: 7/80.
Authentication & Access Control0 / 10
Zero authentication implementation. No registration, login, logout, protected routes, JWT, sessions, or auth middleware anywhere in the codebase. The entire project is 3 static files (index.html, style.css, script.js) with no server-side code at all. No anonymous vs authenticated poll mode logic exists.
Poll Creation & Question Management0 / 15
Zero poll creation or question management. There is a single hardcoded poll in index.html:55-57 with 3 static buttons (React/Vue/Angular). No dynamic form for creating polls, no multiple questions, no mandatory/optional toggles, no expiry date setting, no poll editor UI. Purely static HTML content.
Response Collection Flow1 / 15
No real response collection flow. The script.js vote() function (lines 21-24) simply increments a local JavaScript object that starts with hardcoded values (React:50, Vue:30, Angular:20). No server endpoint, no database persistence, no validation, no expiry enforcement, no duplicate vote prevention. Score 1 for the bare-bones local vote simulation rather than 0 since the voting interaction technically works in a browser session.
Analytics & Feedback Dashboard1 / 15
No analytics dashboard or feedback analysis. The only "analytics" are CSS-width-based progress bars (script.js:8-19, style.css:133-138) driven by hardcoded starting vote counts (React:50, Vue:30, Angular:20). No total response counts, no question-wise summaries, no participation insights, no result publishing, no creator dashboard. Score 1 for the basic progress bar visualization rather than 0.
Frontend Experience3 / 10
Single static landing page with decent CSS: dark mode toggle (script.js:28-30), responsive grid layout (style.css:82-86, 140-145), smooth scrolling, hover animations on cards (style.css:96-98), hero gradient, and fade-in animation (style.css:161-170). However, there is no routing, no forms, no poll creation UX, no login/auth UI, no error/loading/empty states, and no SPA framework. It is a marketing-style landing page, not a functional polling app frontend.
Backend Architecture & API Design0 / 15
Zero backend implementation. No Express server, no Node.js files, no API routes, no controllers, no services, no middleware, no MongoDB/Mongoose schemas, no database connection, no input validation, no request handling of any kind. The entire project is 3 static frontend files with no server-side code whatsoever.
Real-Time Updates Using WebSockets0 / 10
Zero WebSocket/Socket.io implementation. No socket.io dependency, no WebSocket server, no real-time event handling, no room management, no live updates. The only "live" behavior is DOM updates from local JavaScript state on the same page after a button click.
Code Quality & Project Structure2 / 10
Three clean, readable files (106-line HTML, 170-line CSS, 30-line JS) with no errors or dead code. But there is virtually no project structure — no src/ directory, no separation of concerns, no package.json, no dependencies, no build tooling, no tests, no TypeScript, no linting config. The README is 6 lines and documents nothing about setup or architecture. Score 2 for clean but trivially minimal code.
161
poll-app
Ankur Kumawat · @hustler_ankur
1
The submission repo contains ZERO source code files. The only non-git files are .deadline-info.json and .github-meta.json. Both the backend/ and frontend/ directories are completely empty. There is no README.md, no package.json, and no application code of any kind. While the deployed URL (https://poll-app-frontend-chi.vercel.app/) serves a functional-looking React application with routes for authentication, poll creation, analytics, and sharing, and the JS bundle (~305KB) references poll, auth, analytics, and socket features, none of this code exists in the submitted repository. Without source code, it's impossible to evaluate implementation quality, architecture, security practices, or any other aspect of the submission beyond the simple existence of a deployed frontend page. All parameters score 0, with param-5 receiving 1 solely because the deployment URL resolves to a working page.
Authentication & Access Control0 / 10
No authentication code exists in the repository. Both the backend/ and frontend/ directories are completely empty. There are zero source files — no registration, login, logout, JWT, OAuth, protected routes, or anonymous/authenticated poll mode logic to evaluate.
Poll Creation & Question Management0 / 15
No poll creation code exists in the repository. The backend/ and frontend/ directories contain zero files. No form components, question management logic, validation schemas, or poll creation endpoints exist to evaluate.
Response Collection Flow0 / 15
No response collection code exists in the repository. Zero source files means no submission endpoints, no validation logic, no expiry handling, no response restrictions, and no duplicate vote prevention to evaluate.
Analytics & Feedback Dashboard0 / 15
No analytics or dashboard code exists in the repository. Both backend/ and frontend/ are empty directories. Zero source files means no response summaries, option counts, participation insights, or result publishing logic to evaluate.
Frontend Experience1 / 10
The deployed URL (https://poll-app-frontend-chi.vercel.app/) returns HTTP 200 and serves a React application with routes for /login, /register, /dashboard, /poll/create, /poll/:pollId/analytics, /poll/share/:shareId, etc. The JS bundle (~305KB) references poll, auth, analytics, and socket features. However, ZERO frontend source code exists in the repo — the frontend/ directory is completely empty. Score 1 for the fact that a deployed app exists and loads, but no source code is available in the submission for evaluation.
Backend Architecture & API Design0 / 15
No backend code exists in the repository. The backend/ directory is empty — zero files. No Express server, routes, controllers, services, middleware, Drizzle ORM schemas, database connection, environment configuration, or input validation code to evaluate.
Real-Time Updates Using WebSockets0 / 10
No WebSocket/Socket.io code exists in the repository. Zero source files in either backend/ or frontend/ directories. While the deployed JS bundle contains socket-related references, there is no source code in the repo to evaluate implementation quality.
Code Quality & Project Structure0 / 10
No code exists in the repository to evaluate quality. Zero source files, no README.md, no package.json, no configuration files, no tests. The project structure consists of two empty directories (backend/, frontend/) and git metadata. Without any source code, naming conventions, architecture, maintainability, and engineering practices cannot be assessed.
162
Y
Pulse Board
Yogesh jamatia · @jmtygsh
0
Submission "Pulse Board" is an empty repository containing only git submodule references. The `PulseBoard` and `PulseBoard-Client` directories are gitlink entries (mode 160000) pointing to external commits, but there is no .gitmodules file mapping them to remote URLs, making submodule initialization impossible. The entire repository contains exactly 2 files: .deadline-info.json and .github-meta.json — both are evaluation platform metadata, not application code. No README.md, package.json, source files, or any application code exists. The deployment URL (https://pulse-board.ygshjm.dev/) does serve a React app (HTTP 200), but the source code is not accessible in this submission repository. Total score: 0/100.
Authentication & Access Control0 / 10
No source code present in the submission repository. The PulseBoard and PulseBoard-Client directories are empty git submodule references (mode 160000 in git ls-tree) with no .gitmodules file, making submodule initialization impossible. No authentication, registration, login, protected routes, or anonymous/authenticated poll mode code exists to evaluate.
Poll Creation & Question Management0 / 15
No source code present. The repo contains no poll creation forms, question management logic, mandatory/optional handling, or any server-side poll schema. Both PulseBoard and PulseBoard-Client subdirectories are empty.
Response Collection Flow0 / 15
No source code present. No response submission endpoints, validation logic, expiry handling, or duplicate vote prevention exists in the submission repository. Zero implementation.
Analytics & Feedback Dashboard0 / 15
No source code present. No analytics dashboard, response summaries, option counts, participation insights, or results publishing exists in the codebase. The repo contains zero application code.
Frontend Experience0 / 10
No source code present. No routing, forms, UI components, or frontend implementation exists in the submission. The PulseBoard-Client directory is an unresolvable git submodule reference with no actual files.
Backend Architecture & API Design0 / 15
No source code present. No API design, middleware, validations, database schemas, controllers, services, or any backend code exists. The PulseBoard directory is an unresolvable git submodule reference.
Real-Time Updates Using WebSockets0 / 10
No source code present. No Socket.io/WebSocket implementation, real-time event handling, or live update logic exists in the repository. Zero real-time feature code available for evaluation.
Code Quality & Project Structure0 / 10
No source code present. No folder structure, naming conventions, readability analysis, or engineering practices can be evaluated. The repo contains only .deadline-info.json and .github-meta.json (evaluation platform metadata). No README.md exists at the project root.
163
A
logokepoll
Anubhav Gupta · @anubhavgupta0910
0
This submission is an empty repository. The single commit ("Initial commit") contains only a 2-line README.md with no other files. Despite claiming a tech stack of Node.js, Express, MongoDB, Mongoose, JWT, Socket.io, bcryptjs, React, Vite, React Router DOM, Axios, Tailwind CSS, and Socket.io Client, none of these technologies appear anywhere in the codebase. There is no server, no client, no database schema, no API routes, no authentication, no WebSocket implementation — literally zero application code. This is an abandoned/unfinished submission with no identifiable engineering work to evaluate.
Authentication & Access Control0 / 10
No code whatsoever exists in the repository — no server, no auth endpoints, no authentication middleware, no protected routes, no user model. The repo contains only a README.md (2 lines).
Poll Creation & Question Management0 / 15
Zero implementation. No poll creation form, no question management, no database schema for polls or questions, no API endpoints. The repo is empty of all application code.
Response Collection Flow0 / 15
No response collection flow exists. No submission endpoints, no validation logic, no expiry handling, no response model. Only a 2-line README in the entire repository.
Analytics & Feedback Dashboard0 / 15
No analytics, summaries, option counts, or participation insights exist. No dashboard page, no aggregation queries, no results publishing. The repo has no source code.
Frontend Experience0 / 10
No frontend application exists. No React components, no routing, no forms, no UI of any kind. The repo contains nothing beyond a README.md.
Backend Architecture & API Design0 / 15
No backend code exists. No Express server, no API routes, no middleware, no MongoDB schemas, no validations. The claimed tech stack is entirely absent from the codebase.
Real-Time Updates Using WebSockets0 / 10
No WebSocket/Socket.io implementation exists. No real-time events, no server-side socket setup, no client-side socket integration. No code of any kind was committed.
Code Quality & Project Structure0 / 10
No project structure, no source files, no naming conventions to evaluate. The single commit contains only README.md (2 lines in Hindi). Total absence of any engineering work.

Eliminated · Misbehaviourscores withheld · not part of ranking

NOTE: The following participants were removed from the ranking due to misbehaviour in the hackathon townhall. Their scores are withheld. A third entrant (aalampatil001) was pre-banned from the hackathon and is not shown at all.
VoteZap Eliminated · Misbehaviour
Rachit Taneja · @rachittaneja
Pulse Board Eliminated · Misbehaviour
Barun Tiwary · @baruntiwary

Invalid Submissionno valid GitHub repository · not evaluated

NOTE: The following submissions could not be evaluated because the provided repository URL was missing, broken, or pointed somewhere that wasn't a public GitHub repo. They are named here so the listing is complete.
R
VikalpChayan Invalid Submission
rokshhh · @rokshh28 · no GitHub URL
PollSync Invalid Submission
Ashutosh Panda · @asu10118 · repo not found or clone failed
K
Polly Invalid Submission
Kushagra Trivedi · @kushagra_trivedi · no GitHub URL
V
Vimarsh Invalid Submission
Vimarsh · @janardhanv9354_3a48b41e · repo not found or clone failed