# suv4o/personal-blog-2023 architecture

> Architecture diagram of the GitHub repository suv4o/personal-blog-2023, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-09-29). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/suv4o/personal-blog-2023
- Repository: https://github.com/suv4o/personal-blog-2023

## Overview

Aleks Blog is a Nuxt-based personal publishing site with article and category browsing, photography and keyboard-lab sections, contact/newsletter features, and WebMCP tools. The reader workflow is to browse a listing or navigate to a page, read an article or view a photo, and optionally explore related content. Article similarity data is prepared during Nuxt configuration and served by a server API; photo similarity uses a separate API and embedding file. The excerpts do not show page-to-content wiring or client calls to APIs/functions, so those links are omitted or marked unverified.

Main entry points: [`functions/src/index.ts`](https://github.com/suv4o/personal-blog-2023/blob/main/functions/src/index.ts) (Newsletter and contact handlers), [`server/api/similar-articles/[...slug].get.ts`](https://github.com/suv4o/personal-blog-2023/blob/main/server/api/similar-articles/%5B...slug%5D.get.ts) (Article similarity API), [`server/api/similar-photos/[...slug].get.ts`](https://github.com/suv4o/personal-blog-2023/blob/main/server/api/similar-photos/%5B...slug%5D.get.ts) (Photo similarity API), [`app/pages/[...slug].vue`](https://github.com/suv4o/personal-blog-2023/blob/main/app/pages/%5B...slug%5D.vue) (Page routes)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_publishing["Publishing and navigation"]
  node_pages["Page routes<br/>[&#91;...slug&#93;.vue]"]
  node_category_pages["Category listings"]
  node_article_view["Article view<br/>[SinglePost.vue]"]
  node_site_shell["Site navigation<br/>[default.vue]"]
end

subgraph group_experiences["Interactive sections"]
  node_photo_gallery["Photo galleries"]
  node_keyboard_lab["Keyboard lab"]
  node_webmcp["WebMCP tools<br/>[useMcpTool.ts]"]
end

subgraph group_discovery["Related-content services"]
  node_article_similarity["Article similarity API<br/>[&#91;...slug&#93;.get.ts]"]
  node_photo_similarity["Photo similarity API<br/>[&#91;...slug&#93;.get.ts]"]
  node_similarity_math["Cosine scoring"]
  node_article_embeddings["Article embeddings<br/>[file-paths.ts]"]
  node_photo_embeddings["Photo embeddings<br/>[&#91;...slug&#93;.get.ts]"]
  node_embedding_build["Embedding generation<br/>[nuxt.config.ts]"]
  node_embedding_helpers["Embedding helpers"]
end

subgraph group_engagement["Reader engagement"]
  node_contact["Contact form<br/>[GetInTouch.vue]"]
  node_subscriptions["Newsletter flows<br/>[Subscribe.vue]"]
  node_newsletter_functions["Newsletter and contact handlers<br/>[index.ts]"]
end

node_reader(("Reader"))
node_mcp_runtime["WebMCP runtime"]
node_email_service["Mailgun"]
node_firestore[("Subscriber records")]
node_recaptcha["Google reCAPTCHA"]

node_reader -.->|"opens pages"| node_pages
node_reader -.->|"browses topics"| node_category_pages
node_reader -.->|"views photos"| node_photo_gallery
node_reader -.->|"explores lab"| node_keyboard_lab
node_reader -.->|"sends inquiry"| node_contact
node_reader -.->|"manages subscription"| node_subscriptions
node_reader -.->|"invokes tools"| node_webmcp
node_pages -.->|"renders"| node_article_view
node_site_shell -.->|"frames pages"| node_pages
node_article_view -.->|"requests related articles"| node_article_similarity
node_photo_gallery -.->|"requests similar photos"| node_photo_similarity
node_article_similarity -->|"reads embeddings"| node_article_embeddings
node_article_similarity -->|"scores articles"| node_similarity_math
node_photo_similarity -->|"reads embeddings"| node_photo_embeddings
node_photo_similarity -->|"scores photos"| node_similarity_math
node_embedding_build -->|"generates vectors"| node_embedding_helpers
node_embedding_build -->|"writes vectors"| node_article_embeddings
node_webmcp -->|"registers tools"| node_mcp_runtime
node_contact -.->|"submits inquiry"| node_newsletter_functions
node_subscriptions -.->|"submits subscription"| node_newsletter_functions
node_newsletter_functions -->|"reads and writes"| node_firestore
node_newsletter_functions -->|"sends email"| node_email_service
node_newsletter_functions -->|"verifies token"| node_recaptcha

click node_pages "https://github.com/suv4o/personal-blog-2023/blob/main/app/pages/%5B...slug%5D.vue"
click node_category_pages "https://github.com/suv4o/personal-blog-2023/tree/main/app/pages"
click node_article_view "https://github.com/suv4o/personal-blog-2023/blob/main/app/components/SinglePost.vue"
click node_site_shell "https://github.com/suv4o/personal-blog-2023/blob/main/app/layouts/default.vue"
click node_photo_gallery "https://github.com/suv4o/personal-blog-2023/tree/main/app/components/ThroughTheLens"
click node_keyboard_lab "https://github.com/suv4o/personal-blog-2023/tree/main/app/components/TheKeyboardLab"
click node_webmcp "https://github.com/suv4o/personal-blog-2023/blob/main/app/composables/useMcpTool.ts"
click node_article_similarity "https://github.com/suv4o/personal-blog-2023/blob/main/server/api/similar-articles/%5B...slug%5D.get.ts"
click node_photo_similarity "https://github.com/suv4o/personal-blog-2023/blob/main/server/api/similar-photos/%5B...slug%5D.get.ts"
click node_similarity_math "https://github.com/suv4o/personal-blog-2023/blob/main/server/utils/cosine-similarity.ts"
click node_article_embeddings "https://github.com/suv4o/personal-blog-2023/blob/main/server/utils/file-paths.ts"
click node_photo_embeddings "https://github.com/suv4o/personal-blog-2023/blob/main/server/api/similar-photos/%5B...slug%5D.get.ts"
click node_embedding_build "https://github.com/suv4o/personal-blog-2023/blob/main/nuxt.config.ts"
click node_embedding_helpers "https://github.com/suv4o/personal-blog-2023/blob/main/app/utils/process-embeddings.ts"
click node_contact "https://github.com/suv4o/personal-blog-2023/blob/main/app/components/GetInTouch.vue"
click node_subscriptions "https://github.com/suv4o/personal-blog-2023/blob/main/app/components/Subscribe.vue"
click node_newsletter_functions "https://github.com/suv4o/personal-blog-2023/blob/main/functions/src/index.ts"

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_pages,node_category_pages,node_article_view,node_site_shell toneBlue
class node_photo_gallery,node_keyboard_lab,node_webmcp,node_firestore toneAmber
class node_article_similarity,node_photo_similarity,node_similarity_math,node_article_embeddings,node_photo_embeddings,node_embedding_build,node_embedding_helpers toneMint
class node_contact,node_subscriptions,node_newsletter_functions toneRose
class node_reader,node_mcp_runtime,node_email_service,node_recaptcha toneIndigo
```

## Components

### Publishing and navigation

- [Page routes](https://github.com/suv4o/personal-blog-2023/blob/main/app/pages/%5B...slug%5D.vue): `app/pages/[...slug].vue`
- [Category listings](https://github.com/suv4o/personal-blog-2023/tree/main/app/pages): `app/pages`
- [Article view](https://github.com/suv4o/personal-blog-2023/blob/main/app/components/SinglePost.vue): `app/components/SinglePost.vue`
- [Site navigation](https://github.com/suv4o/personal-blog-2023/blob/main/app/layouts/default.vue): `app/layouts/default.vue`

### Interactive sections

- [Photo galleries](https://github.com/suv4o/personal-blog-2023/tree/main/app/components/ThroughTheLens): `app/components/ThroughTheLens`
- [Keyboard lab](https://github.com/suv4o/personal-blog-2023/tree/main/app/components/TheKeyboardLab): `app/components/TheKeyboardLab`
- [WebMCP tools](https://github.com/suv4o/personal-blog-2023/blob/main/app/composables/useMcpTool.ts): `app/composables/useMcpTool.ts`

### Related-content services

- [Article similarity API](https://github.com/suv4o/personal-blog-2023/blob/main/server/api/similar-articles/%5B...slug%5D.get.ts): `server/api/similar-articles/[...slug].get.ts`
- [Photo similarity API](https://github.com/suv4o/personal-blog-2023/blob/main/server/api/similar-photos/%5B...slug%5D.get.ts): `server/api/similar-photos/[...slug].get.ts`
- [Cosine scoring](https://github.com/suv4o/personal-blog-2023/blob/main/server/utils/cosine-similarity.ts): `server/utils/cosine-similarity.ts`
- [Article embeddings](https://github.com/suv4o/personal-blog-2023/blob/main/server/utils/file-paths.ts): `server/utils/file-paths.ts`
- [Photo embeddings](https://github.com/suv4o/personal-blog-2023/blob/main/server/api/similar-photos/%5B...slug%5D.get.ts): `server/api/similar-photos/[...slug].get.ts`
- [Embedding generation](https://github.com/suv4o/personal-blog-2023/blob/main/nuxt.config.ts): `nuxt.config.ts`
- [Embedding helpers](https://github.com/suv4o/personal-blog-2023/blob/main/app/utils/process-embeddings.ts): `app/utils/process-embeddings.ts`

### Reader engagement

- [Contact form](https://github.com/suv4o/personal-blog-2023/blob/main/app/components/GetInTouch.vue): `app/components/GetInTouch.vue`
- [Newsletter flows](https://github.com/suv4o/personal-blog-2023/blob/main/app/components/Subscribe.vue): `app/components/Subscribe.vue`
- [Newsletter and contact handlers](https://github.com/suv4o/personal-blog-2023/blob/main/functions/src/index.ts): `functions/src/index.ts`

### Other components

- **Reader**
- **WebMCP runtime**
- **Mailgun**
- **Subscriber records**
- **Google reCAPTCHA**

## Connections

- Reader → Page routes: opens pages
- Reader → Category listings: browses topics
- Reader → Photo galleries: views photos
- Reader → Keyboard lab: explores lab
- Reader → Contact form: sends inquiry
- Reader → Newsletter flows: manages subscription
- Reader → WebMCP tools: invokes tools
- Page routes → Article view: renders
- Site navigation → Page routes: frames pages
- Article view → Article similarity API: requests related articles
- Photo galleries → Photo similarity API: requests similar photos
- Article similarity API → Article embeddings: reads embeddings (evidence: `server/api/similar-articles/[...slug].get.ts`)
- Article similarity API → Cosine scoring: scores articles (evidence: `server/api/similar-articles/[...slug].get.ts`)
- Photo similarity API → Photo embeddings: reads embeddings (evidence: `server/api/similar-photos/[...slug].get.ts`)
- Photo similarity API → Cosine scoring: scores photos (evidence: `server/api/similar-photos/[...slug].get.ts`)
- Embedding generation → Embedding helpers: generates vectors (evidence: `nuxt.config.ts`)
- Embedding generation → Article embeddings: writes vectors (evidence: `nuxt.config.ts`)
- WebMCP tools → WebMCP runtime: registers tools (evidence: `app/composables/useMcpTool.ts`)
- Contact form → Newsletter and contact handlers: submits inquiry
- Newsletter flows → Newsletter and contact handlers: submits subscription
- Newsletter and contact handlers → Subscriber records: reads and writes (evidence: `functions/src/index.ts`)
- Newsletter and contact handlers → Mailgun: sends email (evidence: `functions/src/index.ts`)
- Newsletter and contact handlers → Google reCAPTCHA: verifies token (evidence: `functions/src/index.ts`)
