Modern portfolio platform built with React + TypeScript on the client and Express on the server.
This project powers my multi-circle personal brand site:
EngCircle(engineering)CodeCircle(software projects)DesignCircle(creative work, gallery, writings)BizCircle/ technopreneur track
It also includes dynamic content APIs so media and writing files are automatically discovered from folders.
- Frontend:
React,TypeScript,Wouter,Tailwind CSS,shadcn/ui,Framer Motion - Backend:
Express,TypeScript - Build:
Vite(client) +esbuild(server bundle) - Data layer: currently file/folder-driven for portfolio content, with schema support in
shared/
client/
public/
gallery/ # photo gallery source files
creatives/ # design/creative source files
writings/ # markdown/text writing files
src/
components/
data/
hooks/
pages/
server/
routes.ts # content discovery APIs
shared/
schema.tsFiles dropped into:
client/public/gallery
Behavior:
- files at root ->
Generalconcept - files inside subfolders -> folder name becomes concept
- gallery page auto-loads from
/api/content/gallery
Files dropped into:
client/public/creatives
Behavior:
- same concept rules as gallery
- supports current image workflows and future media growth (video / 3D / docs metadata in API)
- design page auto-loads from
/api/content/designs
Files dropped into:
client/public/writings
Supported extensions:
.md.markdown.txt
Frontmatter example:
---
title: Building a Living Portfolio System
date: 2026-02-08
kind: blog
status: published
tags: portfolio, systems
collection: General
link: https://elvinmazwi.me
coverImage: /gallery/Zz4/20250623_120419.jpg
---Notes:
- root files ->
General - folder files -> concept by folder
linksupports embedded external referencescoverImagesupports image preview (future-ready)- page auto-loads from
/api/content/writings
Theme support is class-based with persistence:
- provider:
client/src/components/theme-provider.tsx - hook used by headers/buttons:
client/src/hooks/use-theme.ts - theme class applied on
htmlelement (light/dark) - preference stored in
localStorage(theme)
The app now uses shared CSS tokens so both themes render consistently.
Core routes include:
//engineer/developer/creative/portfolio/creative/gallery/creative/visual-designs/blog/technopreneur/privacy-policy/terms-of-service
The app now includes:
- AI-powered search endpoint:
POST /api/ai/search - AI chat endpoint:
POST /api/ai/chat - AI reindex endpoint:
POST /api/ai/reindex - Referenced responses (site links + citations when available)
To route AI responses to my Eddy backend, we have the following template:
EDDY_AI_BACKEND_URL="https://eddy.iqal.me/v1/chat/completions"
EDDY_AI_API_KEY="api-key"
EDDY_AI_AUTH_HEADER="Authorization"
EDDY_AI_MODEL="eddy-model-id"
EDDY_AI_MODE="openai" # or "generic"
EDDY_AI_TIMEOUT_MS="20000"
EDDY_AI_SYSTEM_PROMPT="Assistant behavior instructions"If EDDY_AI_BACKEND_URL is not set, APIs fall back to local site-index retrieval and still return referenced results.
Added:
Privacy PolicypageTerms of ServicepageData Handlingpage
Footer links now route to those pages.
The site includes:
client/public/robots.txtclient/public/sitemap.xml- route-level SEO tags through
client/src/components/seo/RouteSeo.tsx - env-driven verification meta tags for Google and Bing via
client/src/components/seo/RouteSeo.tsx - IndexNow API endpoints:
GET /api/indexnow/keyPOST /api/indexnow/submit
Set these environment variables in local .env and in Vercel:
VITE_GOOGLE_SITE_VERIFICATION="google-verification-token"
VITE_BING_SITE_VERIFICATION="bing-verification-token"
PUBLIC_SITE_URL="https://www.elvinmazwi.me"
INDEXNOW_KEY="your-indexnow-key"
INDEXNOW_KEY_PATH="/api/indexnow/key" # optional
INDEXNOW_ENDPOINTS="https://api.indexnow.org/indexnow,https://www.bing.com/indexnow" # optionalExample IndexNow submission:
curl -X POST https://www.elvinmazwi.me/api/indexnow/submit \
-H "Content-Type: application/json" \
-d "{\"urls\":[\"https://www.elvinmazwi.me/\",\"https://www.elvinmazwi.me/projects\"]}"After deploy, complete account-side steps:
- verify site in Google Search Console
- verify site in Bing Webmaster Tools
- submit
https://www.elvinmazwi.me/sitemap.xmlin both consoles
npm run dev
npm run dev:api
npm run build
npm run start- Install dependencies:
npm install- Start dev server:
npm run dev- Optional API-only mode (when running client Vite separately):
npm run dev:api- Open local URL shown by Express/Vite.
- Deploy from repository root (not
client/). - Static frontend is built to
client/dist. - Express API is served as a Vercel function via
api/[...route].ts. - Content manifests are generated at build time into
server/generated/contentso API content endpoints work in serverless mode.
If this project was previously configured as a client-only app on Vercel, update Project Settings:
Root Directory: set to repository root (.), notclientFramework Preset:Otheror clear any oldVitepreset overrideBuild Command: clear any override (or set tonpm run vercel:build)Output Directory: clear any override (or set toclient/dist)Install Command: clear any override (or set tonpm ci)Node.js Version: use20.x+- Save settings, then trigger a new deployment
Important:
- This repository now uses only the root
vercel.json. - Do not keep a second
vercel.jsoninclient/. - Static assets are built by Vite to
client/dist, while API traffic is served separately byapi/[...route].ts.
Build flow used on Vercel:
npm run vercel:buildSet required environment variables in Vercel for backend features (for example DATABASE_URL, SMTP settings, and Eddy AI variables if used).
Production build bundles:
- client assets to
dist/public - server entry to
dist/index.js
Use:
npm run build
npm run start- Some large chunk warnings may appear in production builds due to rich UI dependencies.
- If folder content is updated while app is open, need to use in-page refresh buttons on gallery/design/writings pages.