Project story

Umfrage

A survey application that started life as a Nuxt starter and grew into a typed full-stack app with authentication, survey workflows, results reporting, and a repeatable CI/CD pipeline.
How it's put together

A clear split between UI, server, and data

The frontend uses Nuxt UI for the interactive shell. Nitro powers API routes under server/api/, while Prisma owns the schema, migrations, seed data, and the generated client. Shared helpers and table logic live in focused folders so UI and server concerns stay separated.
  • Frontend
    Nuxt UI pages, components, composables, and the shared table logic reused across home, admin, and search.
  • Server
    Nitro API routes, middleware, plugins, and request helpers under server/utils/ — input is validated server-side.
  • Data
    Prisma schema, versioned migrations, seed scripts, and a generated client consumed by server routes.
  • Docs & DX
    Public docs under pages/Documentation/, Scalar-rendered OpenAPI, i18n keys, and a preview pipeline for emails.
Feature highlights

What the recent work focused on

A few areas where the codebase moved in meaningful chunks rather than incremental fixes — the cards below summarize the why and the shape of each piece.
Features

Aggregated results + reusable search

An aggregated survey-results API and matching UI panels turned raw answers into something readable. home.vue was split into smaller components so the same survey table can be reused inside the header search box, and table state is persisted and localized.
CI/CD

Modular GitLab CI + Twemoji tree-shaking

The pipeline was split into include files for build, test, package, and deploy stages, with a benchmarking helper to track regressions. A postinstall step now collects only the Twemoji SVGs the app actually references — generated assets stay out of git, and Docker/CI builds get the inputs they need on a fresh clone.
Security

Sign-up flow + brute-force protection

Registration creates a pending account, holds it inactive until email confirmation, and dedupes repeat sign-ups behind a generic 409 so attackers can't probe addresses. IP rate limiting via nuxt-api-shield gates /api/auth/login, /api/auth/register, and /api/survey/accessCode, with a lightweight in-memory helper writing suspicious-activity rows for audit without slowing the hot path.
Email

Templated email + offline preview

Confirmation mail is composed from vue-email components behind a shared EmailLayout that owns <Html>, <Tailwind>, the logo header, and footer links. Theming is implicit — the layout imports app.config.ts so the brand primaryColor flows in without prop drilling. yarn preview-emails renders every template with a previewProps export to email/preview/*.html via Vite SSR, so design iteration doesn't depend on SMTP. DKIM/SPF/DMARC alignment landed alongside, reaching compauth=pass in Outlook.
Finalization
Milestones

From scaffold to today

Picked from the git history. No semantic versions yet, so each entry is dated and grouped under the phase it belongs to.