A simple poll application that displays results in tierlist format (S, A, B, C tiers).
pnpm install
Using Docker Compose:
docker compose up -d postgres
This starts PostgreSQL on localhost:5432 with:
pollspostgrespostgresGenerate migration files from schema:
pnpm db:generate
Apply migrations to create tables:
pnpm db:migrate
This creates tables: polls, options, votes, and migration tracking.
Start the development server:
pnpm dev
Visit http://localhost:5173
The app will be available with:
//polls/createBuild and start all services:
docker compose up -d
This will:
localhost:3000Stop services:
docker compose down
Stop and remove volumes:
docker compose down -v
To expose your app publicly using Cloudflare Tunnel:
.env file in the project root:CLOUDFLARE_TUNNEL_TOKEN=your_tunnel_token_here
docker compose --env-file .env up -d
The Cloudflare tunnel will:
https://polls.yourdomain.com)To stop all services including the tunnel:
docker compose down
Build for production:
pnpm build
Preview production build:
pnpm preview
The application uses the following environment variable:
| Variable | Default | Description |
|---|---|---|
DATABASE_URL |
postgresql://postgres:postgres@localhost:5432/polls |
PostgreSQL connection string |
NODE_ENV |
development |
Environment mode (production/development) |
PORT |
3000 |
Port for production server |
For development, the defaults work with the Docker Compose setup. For production, set DATABASE_URL to your PostgreSQL instance.
| Command | Description |
|---|---|
pnpm db:migrate |
Apply pending migrations to the database |
pnpm db:generate |
Generate new migration files after schema changes |
Apply all pending migrations:
pnpm db:migrate
After modifying the schema in src/lib/db/schema.ts:
pnpm db:generate
This creates SQL migration files in the drizzle/ directory.
Using Docker Compose:
docker compose exec postgres psql -U postgres -d polls
Or connect from host (requires PostgreSQL client):
psql postgresql://postgres:postgres@localhost:5432/polls
Common PostgreSQL commands:
\dt -- List all tables
\d polls -- View table structure
SELECT * FROM polls; -- Query all polls
SELECT COUNT(*) FROM votes;-- Count total votes
\q -- Exit psql
src/
├── lib/
│ ├── components/ui/ # shadcn-svelte components (Button, Card, Badge, etc.)
│ ├── db/
│ │ ├── schema.ts # Database schema definitions
│ │ ├── index.ts # Database connection
│ │ └── migrate.ts # Migration runner script
│ ├── tier.ts # Tier calculation algorithm
│ ├── hash.ts # Voter hash generation for duplicate prevention
│ └── utils.ts # Utility functions (cn, transitions)
├── routes/
│ ├── +layout.svelte # Root layout with header
│ ├── +page.svelte # Home/landing page
│ ├── Header.svelte # Navigation header component
│ ├── polls/
│ │ └── +page.svelte # Browse all polls
│ ├── create/
│ │ ├── +page.svelte # Poll creation form
│ │ └── +page.server.ts # Create poll server action
│ └── poll/[id]/
│ ├── +page.svelte # Vote page
│ ├── +page.server.ts # Vote handling
│ └── dashboard/
│ ├── +page.svelte # Tierlist results dashboard
│ └── +page.server.ts # Results data loading
└── app.css # Global styles and Tailwind config
/create/polls with vote counts and creation datespolls
id (TEXT, PRIMARY KEY) - Unique poll identifiertitle (TEXT, NOT NULL) - Poll question/titledescription (TEXT) - Optional poll descriptionresult_type (TEXT) - Display type (default: "TIERLIST")tier_labels (TEXT) - JSON array of tier labels (default: ["S","A","B","C"])created_at (TIMESTAMP) - Timestamp of creation (default: now())options
id (TEXT, PRIMARY KEY) - Unique option identifierpoll_id (TEXT, FOREIGN KEY) - References polls(id)text (TEXT, NOT NULL) - Option textsort_order (INTEGER) - Display ordervotes
id (TEXT, PRIMARY KEY) - Unique vote identifierpoll_id (TEXT, FOREIGN KEY) - References polls(id)option_id (TEXT, FOREIGN KEY) - References options(id)voter_hash (TEXT, NOT NULL) - SHA-256 hash for duplicate preventioncreated_at (TIMESTAMP) - Timestamp of vote (default: now())If the app can't connect to PostgreSQL:
# Check if PostgreSQL is running
docker compose ps
# View PostgreSQL logs
docker compose logs postgres
# Restart PostgreSQL
docker compose restart postgres
If migrations fail:
Reset the database:
docker compose down -v
docker compose up -d postgres
Delete migration folder: rm -rf drizzle/
Regenerate migrations: pnpm db:generate
Run migrations: pnpm db:migrate
If port 5432 or 3000 is already in use:
# Find process using the port
lsof -i :5432
lsof -i :3000
# Change ports in compose.yml
# For example: "5433:5432" for PostgreSQL