Personal portfolio and blog platform. React + Express + SQLite, deployed via Docker on a Raspberry Pi behind Cloudflare Tunnel.
Live: nikcadez.com
| Layer | Technology |
|---|---|
| Frontend | React 19, Vite 6, Tailwind CSS v4, Framer Motion |
| Backend | Express 4, TypeScript, better-sqlite3 |
| Auth | JWT + bcrypt |
| Resend | |
| Infrastructure | Docker, Cloudflare Tunnel, GitHub Actions (self-hosted runner) |
# Install dependencies
npm install
# Create .env from example
cp .env.example .env
# Fill in JWT_SECRET and RESEND_KEY
# Start both frontend and backend
npm run dev:all
# Frontend: http://localhost:3000
# API: http://localhost:3001The Vite dev server proxies /api and /uploads to the Express server automatically.
| Command | Description |
|---|---|
npm run dev |
Start Vite dev server (port 3000) |
npm run dev:server |
Start Express API with hot reload (port 3001) |
npm run dev:all |
Start both in parallel |
npm run build |
Build frontend for production |
npm start |
Start production server |
npm run seed |
Seed database with sample data |
npm run user -- <action> <args> |
Manage users (see below) |
npm run lint |
TypeScript type-check |
# Create a regular user
npm run user -- create alice password123
# Create an admin
npm run user -- create admin securepass --admin
# Reset password
npm run user -- reset alice newpassword
# Promote to admin
npm run user -- promote alice
# Delete user
npm run user -- delete alice
# List all users
npm run user -- list# Build and start
docker compose up -d --build
# Create admin user
docker exec -it nikpro node --import tsx server/manage-user.ts create <user> <pass> --admin
# View logs
docker logs -f nikpro| Path | Content |
|---|---|
data/ |
SQLite database |
uploads/ |
User-uploaded images |
.env |
Environment variables (secrets) |
These are excluded from git and must be transferred manually to the server.
| Variable | Required | Default |
|---|---|---|
JWT_SECRET |
Yes | - |
RESEND_KEY |
Yes | - |
API_PORT |
No | 3001 |
APP_URL |
No | http://localhost:3000 |
MAIL_FROM |
No | nikcadez.com <blog@nikcadez.com> |
MAIL_REPLY_TO |
No | gmail@nikcadez.com |
MAIL_CONTACT_FROM |
No | nikcadez.com Contact <contact.me@nikcadez.com> |
MAIL_CONTACT_TO |
No | gmail@nikcadez.com |
Generate a JWT secret: openssl rand -hex 32
Detailed documentation is in the docs/ folder:
- Architecture - System design, request flow, and tech choices
- API Reference - All endpoints, request/response formats
- Database - Schema, tables, and backup procedures
- Frontend - Components, routing, and styling
- Deployment - Docker, Cloudflare Tunnel, CI/CD, and operations
- Security - Auth, rate limiting, input validation, and hardening
.
|-- src/ # React frontend
| |-- components/ # Navbar, Footer, LavaLampBackground
| |-- pages/ # Route pages (Experience, Work, Blog, Contact, Admin, Register)
| |-- lib/ # API client, utilities
| |-- types.ts # TypeScript interfaces
| |-- constants.ts # Static resume data
| |-- index.css # Global styles and theme tokens
|-- server/ # Express backend
| |-- index.ts # Main server, routes, middleware
| |-- auth.ts # JWT, bcrypt, middleware
| |-- db.ts # SQLite setup and schema
| |-- mail.ts # Resend email functions
| |-- manage-user.ts # CLI user management
| |-- seed*.ts # Database seed scripts
|-- docs/ # Project documentation
|-- data/ # SQLite database (gitignored)
|-- uploads/ # Uploaded images (gitignored)
|-- Dockerfile # Multi-stage production build
|-- docker-compose.yml # Container orchestration
|-- .github/workflows/ # CI/CD pipeline
All rights reserved.