A CDN and file management platform built on AWS S3 + CloudFront with a Convex real-time backend.
Browser ──(HTTPS)──► CloudFront ──► S3 (cdn.mark-life.com — Vite React SPA)
Browser ──(WSS)────► Convex Cloud (real-time DB, auth, file metadata)
Browser ──(HTTPS)──► CloudFront ──► S3 (static.mark-life.com — media files)
apps/web— Vite + React SPA (deployed to S3/CloudFront)apps/convex— Convex backend (schema, queries, mutations, actions)packages/ui— Shared shadcn/ui component librarypackages/env— Environment variable validation (Zod)packages/typescript-config— Shared TypeScript configsinfrastructure/terraform— AWS infrastructure (S3, CloudFront, Route 53, ACM, IAM)
- Runtime: Bun
- Build: Turborepo
- Frontend: Vite + React + React Router
- Backend: Convex (real-time database, auth, actions)
- Auth: WorkOS via Convex AuthKit
- Storage: AWS S3 (prefix-based account isolation)
- CDN: AWS CloudFront
- DNS/SSL: AWS Route 53 + ACM
- Infrastructure: Terraform
- UI: shadcn/ui + Tailwind CSS
- Linting/Formatting: Ultracite (Biome)
- Pre-commit: Husky + Ultracite
- CI/CD: GitHub Actions
- Bun >= 1.3.9
- Terraform >= 1.0
- AWS account with Route 53 hosted zone for
mark-life.com - Convex account
bun install# Start both the Vite dev server and Convex dev watcher
bun run devCopy .env.example to .env.local and fill in the values:
cp .env.example .env.localSet Convex environment variables for the backend:
cd apps/convex
bunx convex env set AWS_REGION eu-north-1
bunx convex env set AWS_ACCESS_KEY_ID <your-key>
bunx convex env set AWS_SECRET_ACCESS_KEY <your-secret>
bunx convex env set S3_BUCKET_NAME <your-bucket>
bunx convex env set CLOUDFRONT_MEDIA_DISTRIBUTION_ID <CLOUDFRONT_MEDIA_DISTRIBUTION_ID>cd infrastructure/terraform
terraform init
terraform plan
terraform apply| Command | Description |
|---|---|
bun run dev |
Start all apps in dev mode |
bun run build |
Build all apps and packages |
bun run fix |
Auto-fix formatting and lint issues |
bun run check |
Check for lint/format issues |
bunx shadcn@latest add button -c packages/uiThen import:
import { Button } from "@workspace/ui/components/button"Files are stored in S3 with prefix-based account isolation:
s3://bucket/
{account-slug}/
images/
logo.png
documents/
report.pdf
Convex tracks file metadata (name, path, content type, size, uploader) and provides real-time subscriptions for file tree updates. Uploads use S3 presigned URLs generated by Convex actions — files go directly from the browser to S3.
Pushing to main triggers the GitHub Actions pipeline:
- Builds the Vite app with
VITE_CONVEX_URLbaked in - Syncs build output to the app S3 bucket
- Invalidates the CloudFront cache
- Deploys Convex backend functions