API and store UML
HTTP surface and durable-store component model for @tgmc/web.
Endpoint inventory
| Method | Path | Auth | Purpose |
|---|---|---|---|
| GET | /api/greet |
none | Demo { message } |
| GET | /api/content/:collection |
none | Content collections (home, resume, product, gallery, code, writing, docs, caseStudies, decisionCards); mode=first\|all |
| POST | /api/messages |
none + IP rate limit | Create contact message |
| GET | /api/posts |
none | List published posts |
| GET | /api/posts/:slug |
none | Published post + rendered HTML |
| POST | /api/ai-lab/plan |
anonymous quota | Idea → plan (live or replay) |
| POST | /api/ai-lab/complete |
continuation token | Approved plan → brief |
| GET | /api/admin/posts |
Admin Bearer | List all posts |
| POST | /api/admin/posts |
Admin Bearer | Create post |
| GET | /api/admin/posts/:id |
Admin Bearer | Get by id |
| PUT | /api/admin/posts/:id |
Admin Bearer | Update |
| DELETE | /api/admin/posts/:id |
Admin Bearer | Delete |
| GET | /api/admin/messages |
Admin Bearer | List contact messages (@tgmc/admin, Docker only) |
| DELETE | /api/admin/messages/:id |
Admin Bearer | Delete contact message (@tgmc/admin) |
| GET | /api/admin/cdn/objects |
Admin Bearer | List MinIO objects (@tgmc/admin) |
| PUT | /api/admin/cdn/objects |
Admin Bearer | Upload object (@tgmc/admin) |
| DELETE | /api/admin/cdn/objects |
Admin Bearer | Delete object (@tgmc/admin) |
| POST | /api/admin/cdn/sync |
Admin Bearer | Sync .output/<SYS_ENV>/public → MinIO (@tgmc/admin) |
| GET | /sitemap.xml |
none | Sitemap |
| GET | /robots.txt |
none | Robots |
Handlers live under core/web/server/api/**. Admin routes (/api/admin/**, /admin/**) are served by @tgmc/admin (standalone npm run docker:admin stack) and reuse packages/web-layer-admin for blog CRUD. Auth: Authorization: Bearer <ADMIN_TOKEN> via timing-safe compare; database: X-Admin-Database (sqlite |
postgres |
dynamodb). Fail-closed if token empty. There are no end-user sessions or OAuth. |
Content API component view
flowchart LR
Page[Portfolio page] --> FCC[fetchContentCollection]
FCC --> API["GET /api/content/:collection"]
API --> QC[queryCollection]
QC --> Dump[(Content SQLite dump)]
Repo["docs/** + content/*"] -.-> Dump
Pages must not call client queryCollection() for portfolio data — see page data loading.
MessageStore / BlogPostStore UML
classDiagram
class MessageStore {
<<interface>>
+list() ContactMessage[]
+create(input) ContactMessage
+delete(id) boolean
}
class BlogPostStore {
<<interface>>
+listPublished()
+listAll()
+getBySlug(slug)
+getById(id)
+create(input)
+update(id, input)
+delete(id)
}
class SqliteMessageStore
class PostgresMessageStore
class DynamoMessageStore
class SqliteBlogPostStore
class PostgresBlogPostStore
class DynamoBlogPostStore
class Factory {
+createMessageStore(env)
+getMessageStore()
+createBlogPostStore(env)
+getBlogPostStore()
}
MessageStore <|.. SqliteMessageStore
MessageStore <|.. PostgresMessageStore
MessageStore <|.. DynamoMessageStore
BlogPostStore <|.. SqliteBlogPostStore
BlogPostStore <|.. PostgresBlogPostStore
BlogPostStore <|.. DynamoBlogPostStore
Factory --> MessageStore
Factory --> BlogPostStore
Adapter selection
SYS_ENV / store env |
MessageStore | BlogPostStore |
|---|---|---|
local |
SQLite data/local.sqlite |
SQLite |
development |
Postgres | Postgres |
test / production |
DynamoDB Messages |
DynamoDB Posts |
E2E_STORE_SYS_ENV can override store selection (e.g. CI keeps public SYS_ENV=test while stores use SQLite). Factories: core/web/server/db/index.ts, blog-store.ts. Migrations: server/db/migrations/001_messages.sql, 002_posts.sql via npm run db:migrate:local.
API package diagram
flowchart TB
subgraph Public
greet["/api/greet"]
content["/api/content/:collection"]
postsGet["/api/posts"]
postsSlug["/api/posts/:slug"]
msgPost["POST /api/messages"]
aiPlan["POST /api/ai-lab/plan"]
aiComplete["POST /api/ai-lab/complete"]
end
subgraph Admin
msgGet["GET /api/messages"]
adminPosts["/api/admin/posts*"]
end
subgraph CrossCutting
RL[message rate-limit]
Auth[requireAdminToken]
Quota[AI Lab quota + HMAC]
CDN[CDN middleware 302]
end
msgPost --> RL
msgGet --> Auth
adminPosts --> Auth
aiPlan --> Quota
aiComplete --> Quota