# aforardi/mohammadardi-portfolio architecture

> Architecture diagram of the GitHub repository aforardi/mohammadardi-portfolio, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-11-12). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/aforardi/mohammadardi-portfolio
- Repository: https://github.com/aforardi/mohammadardi-portfolio

## Overview

This is a single Next.js portfolio app with an owner dashboard. Visitors load published portfolio content and can preview or download the resume; the owner signs in with password and authenticator MFA, then edits content, uploads media, and manages video cards. The dashboard’s API routes enforce owner access and persist changes, while public pages render published data. The README documents additional editable collections and contact-provider configuration. Several UI-to-API and unsampled subsystem connections are not visible in the excerpts, so those links are omitted rather than inferred.

Main entry points: [`src/db/index.ts`](https://github.com/aforardi/mohammadardi-portfolio/blob/main/src/db/index.ts) (PostgreSQL data), [`app/api/admin/content/[collection]/route.ts`](https://github.com/aforardi/mohammadardi-portfolio/blob/main/app/api/admin/content/%5Bcollection%5D/route.ts) (Content API), [`app/api/admin/upload/route.ts`](https://github.com/aforardi/mohammadardi-portfolio/blob/main/app/api/admin/upload/route.ts) (Upload API), [`src/lib/content/mutations.ts`](https://github.com/aforardi/mohammadardi-portfolio/blob/main/src/lib/content/mutations.ts) (Content mutations)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_public["Public portfolio"]
  node_homepage["Portfolio page<br/>[page.tsx]"]
  node_sections["Portfolio sections"]
  node_publicdata["Published content<br/>[public.ts]"]
  node_contact["Contact form<br/>[ContactSection.tsx]"]
end

subgraph group_admin["Owner content"]
  node_dashboard["Content studio<br/>[ContentStudio.tsx]"]
  node_contentapi["Content API<br/>[route.ts]"]
  node_mutations["Content mutations<br/>[mutations.ts]"]
  node_validation["Content validation<br/>[validation.ts]"]
end

subgraph group_security["Owner security"]
  node_login["Owner login<br/>[page.tsx]"]
  node_loginform["Login form<br/>[LoginForm.tsx]"]
  node_guard["Owner access guard<br/>[guard.ts]"]
  node_supabaseauth{{"Supabase Auth"}}
end

subgraph group_media["Media and integrations"]
  node_database[("PostgreSQL data<br/>[index.ts]")]
  node_schema[("Portfolio schema<br/>[schema.ts]")]
  node_uploadapi["Upload API<br/>[route.ts]"]
  node_imageprocess["Image processing<br/>[images.ts]"]
  node_storage[("Supabase Storage<br/>[client.ts]")]
  node_mediapi["Published media endpoint<br/>[route.ts]"]
  node_resumeapi["Resume endpoint<br/>[route.ts]"]
  node_resumelogic["Resume delivery<br/>[resume.ts]"]
  node_cleanup["Storage cleanup<br/>[cleanup.ts]"]
  node_youtubeapi["YouTube preview API<br/>[route.ts]"]
  node_youtube["YouTube metadata<br/>[service.ts]"]
  node_contactservices{{"Email and CAPTCHA"}}
end

node_visitor(("Visitor"))
node_owner(("Portfolio owner"))

node_visitor -->|"opens"| node_homepage
node_homepage -->|"loads content"| node_publicdata
node_homepage -->|"renders sections"| node_sections
node_publicdata -.->|"reads published data"| node_database
node_database -->|"uses schema"| node_schema
node_owner -->|"signs in"| node_login
node_login -->|"renders form"| node_loginform
node_loginform -.->|"authenticates"| node_supabaseauth
node_contentapi -->|"checks owner"| node_guard
node_guard -->|"verifies identity"| node_supabaseauth
node_contentapi -->|"reads and writes"| node_database
node_contentapi -->|"saves content"| node_mutations
node_contentapi -->|"validates deletion"| node_validation
node_mutations -->|"updates records"| node_database
node_mutations -->|"moves media"| node_storage
node_mutations -->|"fetches metadata"| node_youtube
node_uploadapi -->|"checks owner"| node_guard
node_uploadapi -->|"prepares images"| node_imageprocess
node_uploadapi -->|"uploads files"| node_storage
node_uploadapi -->|"records assets"| node_database
node_mediapi -->|"reads asset records"| node_database
node_mediapi -->|"serves files"| node_storage
node_visitor -->|"requests resume"| node_resumeapi
node_resumeapi -->|"reads resume asset"| node_database
node_resumeapi -->|"delegates delivery"| node_resumelogic
node_resumeapi -->|"downloads PDF"| node_storage
node_contentapi -->|"drains queue"| node_cleanup
node_youtubeapi -->|"checks owner"| node_guard
node_youtubeapi -->|"fetches preview"| node_youtube
node_contact -.->|"submits inquiry"| node_contactservices

click node_homepage "https://github.com/aforardi/mohammadardi-portfolio/blob/main/app/(public)/page.tsx"
click node_sections "https://github.com/aforardi/mohammadardi-portfolio/blob/main/src/components/ProjectsSection.tsx"
click node_publicdata "https://github.com/aforardi/mohammadardi-portfolio/blob/main/src/lib/content/public.ts"
click node_database "https://github.com/aforardi/mohammadardi-portfolio/blob/main/src/db/index.ts"
click node_schema "https://github.com/aforardi/mohammadardi-portfolio/blob/main/src/db/schema.ts"
click node_dashboard "https://github.com/aforardi/mohammadardi-portfolio/blob/main/src/components/admin/ContentStudio.tsx"
click node_contentapi "https://github.com/aforardi/mohammadardi-portfolio/blob/main/app/api/admin/content/%5Bcollection%5D/route.ts"
click node_mutations "https://github.com/aforardi/mohammadardi-portfolio/blob/main/src/lib/content/mutations.ts"
click node_validation "https://github.com/aforardi/mohammadardi-portfolio/blob/main/src/lib/content/validation.ts"
click node_login "https://github.com/aforardi/mohammadardi-portfolio/blob/main/app/(auth)/admin/login/page.tsx"
click node_loginform "https://github.com/aforardi/mohammadardi-portfolio/blob/main/src/components/admin/LoginForm.tsx"
click node_guard "https://github.com/aforardi/mohammadardi-portfolio/blob/main/src/lib/auth/guard.ts"
click node_uploadapi "https://github.com/aforardi/mohammadardi-portfolio/blob/main/app/api/admin/upload/route.ts"
click node_imageprocess "https://github.com/aforardi/mohammadardi-portfolio/blob/main/src/lib/storage/images.ts"
click node_storage "https://github.com/aforardi/mohammadardi-portfolio/blob/main/src/lib/storage/client.ts"
click node_mediapi "https://github.com/aforardi/mohammadardi-portfolio/blob/main/app/api/media/%5Btype%5D/%5Bid%5D/route.ts"
click node_resumeapi "https://github.com/aforardi/mohammadardi-portfolio/blob/main/app/api/resume/route.ts"
click node_resumelogic "https://github.com/aforardi/mohammadardi-portfolio/blob/main/src/lib/storage/resume.ts"
click node_cleanup "https://github.com/aforardi/mohammadardi-portfolio/blob/main/src/lib/storage/cleanup.ts"
click node_youtubeapi "https://github.com/aforardi/mohammadardi-portfolio/blob/main/app/api/admin/youtube/preview/route.ts"
click node_youtube "https://github.com/aforardi/mohammadardi-portfolio/blob/main/src/lib/youtube/service.ts"
click node_contact "https://github.com/aforardi/mohammadardi-portfolio/blob/main/src/components/ContactSection.tsx"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_homepage,node_sections,node_publicdata,node_contact toneBlue
class node_dashboard,node_contentapi,node_mutations,node_validation toneAmber
class node_login,node_loginform,node_guard,node_supabaseauth toneMint
class node_database,node_schema,node_uploadapi,node_imageprocess,node_storage,node_mediapi,node_resumeapi,node_resumelogic,node_cleanup,node_youtubeapi,node_youtube,node_contactservices toneRose
class node_visitor,node_owner toneIndigo
```

## Components

### Public portfolio

- [Portfolio page](https://github.com/aforardi/mohammadardi-portfolio/blob/main/app/(public)/page.tsx): `app/(public)/page.tsx`
- [Portfolio sections](https://github.com/aforardi/mohammadardi-portfolio/blob/main/src/components/ProjectsSection.tsx): `src/components/ProjectsSection.tsx`
- [Published content](https://github.com/aforardi/mohammadardi-portfolio/blob/main/src/lib/content/public.ts): `src/lib/content/public.ts`
- [Contact form](https://github.com/aforardi/mohammadardi-portfolio/blob/main/src/components/ContactSection.tsx): `src/components/ContactSection.tsx`

### Owner content

- [Content studio](https://github.com/aforardi/mohammadardi-portfolio/blob/main/src/components/admin/ContentStudio.tsx): `src/components/admin/ContentStudio.tsx`
- [Content API](https://github.com/aforardi/mohammadardi-portfolio/blob/main/app/api/admin/content/%5Bcollection%5D/route.ts): `app/api/admin/content/[collection]/route.ts`
- [Content mutations](https://github.com/aforardi/mohammadardi-portfolio/blob/main/src/lib/content/mutations.ts): `src/lib/content/mutations.ts`
- [Content validation](https://github.com/aforardi/mohammadardi-portfolio/blob/main/src/lib/content/validation.ts): `src/lib/content/validation.ts`

### Owner security

- [Owner login](https://github.com/aforardi/mohammadardi-portfolio/blob/main/app/(auth)/admin/login/page.tsx): `app/(auth)/admin/login/page.tsx`
- [Login form](https://github.com/aforardi/mohammadardi-portfolio/blob/main/src/components/admin/LoginForm.tsx): `src/components/admin/LoginForm.tsx`
- [Owner access guard](https://github.com/aforardi/mohammadardi-portfolio/blob/main/src/lib/auth/guard.ts): `src/lib/auth/guard.ts`
- **Supabase Auth**

### Media and integrations

- [PostgreSQL data](https://github.com/aforardi/mohammadardi-portfolio/blob/main/src/db/index.ts): `src/db/index.ts`
- [Portfolio schema](https://github.com/aforardi/mohammadardi-portfolio/blob/main/src/db/schema.ts): `src/db/schema.ts`
- [Upload API](https://github.com/aforardi/mohammadardi-portfolio/blob/main/app/api/admin/upload/route.ts): `app/api/admin/upload/route.ts`
- [Image processing](https://github.com/aforardi/mohammadardi-portfolio/blob/main/src/lib/storage/images.ts): `src/lib/storage/images.ts`
- [Supabase Storage](https://github.com/aforardi/mohammadardi-portfolio/blob/main/src/lib/storage/client.ts): `src/lib/storage/client.ts`
- [Published media endpoint](https://github.com/aforardi/mohammadardi-portfolio/blob/main/app/api/media/%5Btype%5D/%5Bid%5D/route.ts): `app/api/media/[type]/[id]/route.ts`
- [Resume endpoint](https://github.com/aforardi/mohammadardi-portfolio/blob/main/app/api/resume/route.ts): `app/api/resume/route.ts`
- [Resume delivery](https://github.com/aforardi/mohammadardi-portfolio/blob/main/src/lib/storage/resume.ts): `src/lib/storage/resume.ts`
- [Storage cleanup](https://github.com/aforardi/mohammadardi-portfolio/blob/main/src/lib/storage/cleanup.ts): `src/lib/storage/cleanup.ts`
- [YouTube preview API](https://github.com/aforardi/mohammadardi-portfolio/blob/main/app/api/admin/youtube/preview/route.ts): `app/api/admin/youtube/preview/route.ts`
- [YouTube metadata](https://github.com/aforardi/mohammadardi-portfolio/blob/main/src/lib/youtube/service.ts): `src/lib/youtube/service.ts`
- **Email and CAPTCHA**

### Other components

- **Visitor**
- **Portfolio owner**

## Connections

- Visitor → Portfolio page: opens
- Portfolio page → Published content: loads content (evidence: `app/(public)/page.tsx`)
- Portfolio page → Portfolio sections: renders sections (evidence: `app/(public)/page.tsx`)
- Published content → PostgreSQL data: reads published data
- PostgreSQL data → Portfolio schema: uses schema (evidence: `src/db/index.ts`)
- Portfolio owner → Owner login: signs in
- Owner login → Login form: renders form (evidence: `app/(auth)/admin/login/page.tsx`)
- Login form → Supabase Auth: authenticates
- Content API → Owner access guard: checks owner (evidence: `app/api/admin/content/[collection]/route.ts`)
- Owner access guard → Supabase Auth: verifies identity (evidence: `src/lib/auth/guard.ts`)
- Content API → PostgreSQL data: reads and writes (evidence: `app/api/admin/content/[collection]/route.ts`)
- Content API → Content mutations: saves content (evidence: `app/api/admin/content/[collection]/route.ts`)
- Content API → Content validation: validates deletion (evidence: `app/api/admin/content/[collection]/route.ts`)
- Content mutations → PostgreSQL data: updates records (evidence: `src/lib/content/mutations.ts`)
- Content mutations → Supabase Storage: moves media (evidence: `src/lib/content/mutations.ts`)
- Content mutations → YouTube metadata: fetches metadata (evidence: `src/lib/content/mutations.ts`)
- Upload API → Owner access guard: checks owner (evidence: `app/api/admin/upload/route.ts`)
- Upload API → Image processing: prepares images (evidence: `app/api/admin/upload/route.ts`)
- Upload API → Supabase Storage: uploads files (evidence: `app/api/admin/upload/route.ts`)
- Upload API → PostgreSQL data: records assets (evidence: `app/api/admin/upload/route.ts`)
- Published media endpoint → PostgreSQL data: reads asset records (evidence: `app/api/media/[type]/[id]/route.ts`)
- Published media endpoint → Supabase Storage: serves files (evidence: `app/api/media/[type]/[id]/route.ts`)
- Visitor → Resume endpoint: requests resume
- Resume endpoint → PostgreSQL data: reads resume asset (evidence: `app/api/resume/route.ts`)
- Resume endpoint → Resume delivery: delegates delivery (evidence: `app/api/resume/route.ts`)
- Resume endpoint → Supabase Storage: downloads PDF (evidence: `app/api/resume/route.ts`)
- Content API → Storage cleanup: drains queue (evidence: `app/api/admin/content/[collection]/route.ts`)
- YouTube preview API → Owner access guard: checks owner (evidence: `app/api/admin/youtube/preview/route.ts`)
- YouTube preview API → YouTube metadata: fetches preview (evidence: `app/api/admin/youtube/preview/route.ts`)
- Contact form → Email and CAPTCHA: submits inquiry
