Skip to main content

Implementation Roadmap โ€” Frontpics Image Marketplace


Phase 1 โ€” Core Platform (MVP v1)โ€‹

Goal: A working marketplace that photographers can upload to, buyers can purchase from, and admins can manage.
Target duration: 8โ€“10 weeks with 1โ€“2 developers.

Week 1โ€“2: Foundationโ€‹

Monorepo & tooling setup

  • Initialize pnpm workspace with Turborepo
  • Create apps/api, apps/web, apps/worker, packages/database, packages/shared, packages/config
  • Configure shared TypeScript (tsconfig.base.json) and ESLint across all packages
  • Set up Prettier, .editorconfig

Infrastructure bootstrap

  • docker-compose.yml with PostgreSQL (PostGIS), Redis
  • NestJS app bootstrapped with Fastify adapter, Helmet, global pipes
  • Next.js app bootstrapped with App Router, Tailwind, shadcn/ui
  • BullMQ worker app bootstrapped

Database

  • Write Prisma schema (all models from 05-database-schema.md)
  • Run initial prisma migrate dev
  • Apply raw SQL migrations (PostGIS column + trigger, FTS trigger + GIN index, check constraints)
  • Write seed script: default license types (3โ€“4), admin user, sample tags

Shared package

  • Zod schemas for auth, image create/update
  • TypeScript enums (Role, ImageStatus, etc.)
  • Generic ApiResponse<T>, PaginatedResponse<T> types

Week 3โ€“4: Authentication & User Profilesโ€‹

Auth (NestJS)

  • POST /auth/register โ€” create user + wallet + profile in transaction
  • POST /auth/login โ€” JWT access token + httpOnly refresh cookie
  • POST /auth/refresh โ€” rotate refresh token (Redis)
  • POST /auth/logout โ€” invalidate refresh token
  • POST /auth/verify-email โ€” verify with one-time token from Redis
  • POST /auth/forgot-password + POST /auth/reset-password
  • JwtAuthGuard, RolesGuard, @Public(), @Roles() decorators
  • Rate limiting with @nestjs/throttler + Redis store

User & profile endpoints

  • GET/PATCH /users/me
  • GET/PATCH /photographers/me/profile
  • GET/PATCH /agents/me/profile

Next.js auth UI

  • /auth/login, /auth/register pages with React Hook Form + Zod
  • middleware.ts route protection for /dashboard, /photographer, /admin
  • useAuth hook with token refresh interceptor
  • Auth context (React Context or Zustand)

Week 5โ€“6: Images & Upload Pipelineโ€‹

Upload pipeline (NestJS + Worker)

  • POST /upload/presigned โ€” generate R2 presigned PUT URLs
  • POST /upload/confirm โ€” create Image + Location + Tags + enqueue jobs
  • GET /upload/status/:jobId โ€” poll job status
  • Worker: download from R2, validate MIME/size, extract EXIF
  • Worker: Sharp variant generation (large, medium, thumbnail, watermarked with SVG overlay)
  • Worker: upload variants to R2, set Cache-Control headers
  • Worker: update image_files + images status โ†’ pending_review
  • BullMQ retry strategy (3 attempts, exponential backoff)

Images API (NestJS)

  • GET /images โ€” browse with filters, FTS, keyset pagination, Redis caching
  • GET /images/:id โ€” detail, increment view count
  • GET /images/mine โ€” photographer's own images
  • POST /images โ€” create after upload confirm
  • PATCH /images/:id โ€” update metadata (own, draft/active only)
  • DELETE /images/:id โ€” soft delete (own, no purchases)
  • POST /images/:id/submit โ€” submit draft for review
  • GET /images/:id/download/:orderItemId โ€” generate signed download URL

Photographer upload UI (Next.js)

  • Drag-and-drop DropZone with file list and per-file progress bars
  • Metadata form per image (title, description, tags, location, price, license, is_drone toggle)
  • Batch submit with progress tracking via job polling
  • /photographer/images โ€” gallery view of own images with status badges
  • /photographer/images/[id]/edit โ€” metadata edit form

Buyer browse UI (Next.js)

  • /browse โ€” image grid with filter bar (search, city, tags, drone, price)
  • Infinite scroll via useInfiniteQuery
  • URL-synced filters (shareable links)
  • /images/[id] โ€” image detail page (watermarked preview, EXIF, metadata, purchase button)

Week 7โ€“8: Credits, Orders & Adminโ€‹

Orders & wallet (NestJS)

  • POST /orders โ€” atomic purchase (pessimistic lock on wallet)
  • GET /orders + GET /orders/:id โ€” buyer order history + download links
  • GET /wallets/me + GET /wallets/me/transactions
  • POST /payouts/request โ€” photographer payout request

Admin module (NestJS)

  • GET /admin/stats
  • User management CRUD (list, view, suspend, role change, manual credit)
  • Image moderation (pending queue, approve, reject with note)
  • Order list + refund action
  • Payout management (process, complete, fail)
  • Tag CRUD
  • License type CRUD
  • Audit log viewer

Admin UI (Next.js)

  • Admin sidebar layout
  • Stats dashboard with KPI cards
  • User list with DataTable (TanStack Table)
  • Moderation queue with watermarked preview + approve/reject actions
  • Orders table with refund action
  • Payouts table with process/complete/fail actions
  • Tags and License types tables with inline edit

Buyer dashboard UI

  • /dashboard โ€” wallet balance, recent orders
  • /dashboard/orders โ€” paginated order history
  • Download button generating fresh signed URL

Photographer earnings UI

  • /photographer/earnings โ€” credits earned, payout history, request payout form

Week 9โ€“10: CI/CD, Deployment, Polishโ€‹

CI/CD

  • GitHub Actions: ci.yml (type-check, lint, test on PR)
  • GitHub Actions: deploy.yml (build + push Docker images to GHCR)
  • Per-app Dockerfiles (api, web, worker) with multi-stage builds
  • docker-compose.blue.yml + docker-compose.green.yml
  • docker-compose.infra.yml (Postgres + Redis โ€” shared, persistent)

VPS setup

  • Install Docker, Nginx, Certbot on VPS
  • Set up shared Docker network (frontpics-net)
  • Configure Nginx with TLS (Let's Encrypt)
  • Deploy script (deploy.sh) + rollback script (rollback.sh)
  • First manual deploy, verify blue/green switch works
  • Wire GitHub Actions to VPS SSH (secrets configured)
  • Automated pg_dump backup cron โ†’ R2 backup bucket

Testing & polish

  • Vitest unit tests for: credit purchase transaction, upload confirmation logic, search query builder
  • Supertest integration tests for: auth flow, purchase flow, presigned URL generation
  • Playwright E2E: register โ†’ upload โ†’ admin approve โ†’ buyer purchase โ†’ download
  • Error handling review: global exception filter, meaningful error codes
  • API response time check: all GET routes < 200ms for p95
  • Lighthouse score: buyer browse page โ‰ฅ 80

Documentation

  • README.md โ€” project overview, local dev setup instructions
  • .env.example โ€” all required variables with descriptions
  • CONTRIBUTING.md โ€” PR process, coding standards

Phase 2 โ€” Marketplace Growthโ€‹

Goal: Better discovery, payment infrastructure, and photographer tools.
Target duration: 6โ€“8 weeks.

Discovery & UXโ€‹

  • Map view โ€” PostGIS bounding box API + MapLibre GL + Supercluster client clustering
  • Advanced filters โ€” filter by neighborhood, altitude (drone altitude from EXIF), shot date range
  • Related images โ€” "More from this photographer", "More in this area"
  • Image collections โ€” buyers can save images to private wishlists
  • URL slugs for images (/images/aerial-view-austin-tx-drone via slug field)

Paymentsโ€‹

  • Stripe integration โ€” fiat-to-credits purchase (Stripe Checkout or Elements)
  • Stripe webhooks for payment confirmation โ†’ credit top-up
  • Credit price configurable via admin settings table
  • Invoice generation per order (PDF via pdfmake or Puppeteer)

Communicationโ€‹

  • Transactional email โ€” welcome, email verification, password reset, moderation result, payout processed
  • Nodemailer templates with React Email or MJML

Authโ€‹

  • Google OAuth โ€” passport-google-oauth20, link to existing account by email
  • Account linking page (for users who registered with email via Google)

Photographer Toolsโ€‹

  • Analytics dashboard: views/downloads per image over time (Chart.js)
  • Image performance table (sorted by downloads, revenue)
  • Batch metadata edit (edit tags/price across selected images)
  • Custom watermark upload (replace platform watermark with own logo)

Adminโ€‹

  • Platform settings screen (credit price, max upload batch size, watermark config)
  • Photographer verification workflow (submit ID โ†’ admin mark verified)
  • CSV export of orders, users, transactions

Phase 3 โ€” Scale & Ecosystemโ€‹

Goal: Handle significant traffic, add ecosystem features, prepare for open-source community.
Target duration: Ongoing.

Search & Discoveryโ€‹

  • OpenSearch cluster for full-text search (replace PostgreSQL FTS at scale)
  • Synonym support, spell correction, fuzzy matching
  • Faceted search with aggregation counts (sidebar filters with numbers)
  • Elasticsearch/OpenSearch index kept in sync via Prisma event hooks or CDC

Performance & Scaleโ€‹

  • PgBouncer connection pooling (or switch to managed RDS)
  • Redis Cluster or Upstash for high-availability cache/queue
  • CDN purge API integration (Cloudflare Cache API) on image approve/delete
  • API horizontal scaling (multiple replicas behind Nginx upstream)
  • Nginx upstream health checks and automatic removal of unhealthy replicas

Marketplace Featuresโ€‹

  • Bulk ZIP download (order items โ†’ async job โ†’ send email download link)
  • Photographer subscription tiers (higher commission rate, featured placement)
  • Exclusive licenses (image removed from marketplace after exclusive purchase)
  • Image licensing history page (public โ€” shows license grants without buyer names)
  • Referral program (photographer invites โ†’ bonus credits)

API & Ecosystemโ€‹

  • Public REST API with API key authentication (for partner integrations)
  • Rate limiting per API key (Redis-based sliding window)
  • Webhook delivery to photographer endpoints on key events (image sold)
  • OpenAPI / Swagger documentation (@nestjs/swagger)
  • Mobile app readiness (review API design for React Native client)

Operationsโ€‹

  • Structured logging pipeline (Pino โ†’ Loki โ†’ Grafana)
  • Metrics (Prometheus + Grafana) โ€” API response times, queue depth, error rates
  • Alerting (PagerDuty or simple email on queue backup > 100 jobs)
  • Ansible playbook for VPS provisioning (reproducible setup)
  • Multi-language support (i18n via next-intl)

Decision Logโ€‹

Track decisions that are easy to reverse vs. hard to reverse:

DecisionReversibilityNotes
PostgreSQL FTS over OpenSearchEasy โ€” same API contractMigrate anytime by swapping service implementation
R2 over S3Medium โ€” S3-compatible API; key names may need adjustmentObject keys and SDK calls are compatible
NestJS over ExpressHardCore framework choice. Not worth changing after phase 1.
Monorepo vs. polyrepoHardMonorepo is the right choice; don't second-guess it.
Credits over direct paymentsMedium โ€” Stripe can be added alongsideCredits simplify MVP; Stripe added in Phase 2
Single-region VPSEasy โ€” add more VPS nodes behind NginxScale by adding replicas to the Nginx upstream
Blue/green on same VPSEasy โ€” move to Kubernetes if neededK8s is overkill for MVP; add when team size justifies it

Definition of Done โ€” Phase 1โ€‹

Phase 1 is done when:

  1. A photographer can register, verify email, upload 10 images in a batch, and see them appear in the moderation queue.
  2. An admin can log into /admin, review and approve images, see them appear in /browse.
  3. A buyer can search for "aerial Austin", find a drone image, purchase it with credits, and download the original.
  4. All three flows work end-to-end in the production environment (not just local).
  5. CI pipeline passes on every commit to main.
  6. A push to main automatically deploys to production green/blue stack with zero downtime.
  7. The rollback.sh script restores the previous version in under 30 seconds.
  8. The system handles 100 concurrent users with all API P95 response times under 500ms.
  9. No OWASP Top 10 vulnerabilities in the auth and purchase flows (manual review + basic automated scan).