A modern, static single‑page application (SPA) for a personal portfolio. The site runs entirely on the client (React + Vite + Tailwind). When an API is not available, pages use built‑in static fallbacks so everything remains functional out of the box.
apps/
client/
public/ # Static assets (images, pdf, CNAME, robots)
src/
index.css # Global styles + utility classes
main.jsx # App entry (React + Router)
modules/
App.jsx # Layout/shell
components/ # Reusable UI
data/fallback.js # Static projects + case studies
pages/ # Route pages (Home, Projects, Detail, etc.)
pnpm install
pnpm --filter @chanin/client dev
Open http://localhost:5173
apps/client/.env and set:VITE_API_BASE=https://your-api.example.com/api
If omitted, the UI uses static fallback data and still works normally.
Build production assets:
pnpm --filter @chanin/client build
Artifacts are written to apps/client/dist.
Preview the built site:
pnpm --filter @chanin/client preview
apps/client/src/modules/pages/*apps/client/src/modules/data/fallback.jsapps/client/index.htmlapps/client/public/**pnpm --filter @chanin/client dev — run the client dev serverpnpm --filter @chanin/client build — build for productionpnpm --filter @chanin/client preview — preview the production buildVITE_API_BASE unset.This repo contains an Express + Mongoose API in apps/server that powers dynamic features. It is optional in local dev; the UI has static fallbacks.
pnpm --filter @chanin/server dev (default port 8080)GET http://localhost:8080/healthapps/server/.env.example to apps/server/.envMONGODB_URI (or MONGO_URL/DATABASE_URL), JWT_SECRETThis repository includes railway.json which defines two services (server, client) and uses Nixpacks.
railway.json.server service → Variables → set:
MONGODB_URI = the MongoDB connection URL (compatible keys MONGO_URL/DATABASE_URL also work)JWT_SECRET = long random string (≥16 chars)ADMIN_USER, ADMIN_PASS, PUBLIC_EMAIL, SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS, PLAUSIBLE_DOMAINclient service → Variables:
VITE_API_BASE = https://<your-server-domain>.railway.app/apiVITE_PLAUSIBLE_DOMAINpnpm install && pnpm -r buildpnpm --filter @chanin/server startpnpm install && pnpm --filter @chanin/client buildpnpm --filter @chanin/client preview -- --host 0.0.0.0 --port $PORTpnpm --filter @chanin/server build && pnpm --filter @chanin/server seedserver service and run pnpm --filter @chanin/server seedNotes
PORT from Railway automatically.