{"openapi":"3.0.3","info":{"title":"Blog CMS API (blog.huanchain.online)","version":"1.0.0","description":"RESTful API for the Next.js 16 + Upstash Redis Blog CMS with Start Bootstrap (SB Admin) Dashboard and Feature-Driven Architecture (FDA).","contact":{"name":"Huanchain Online CMS","url":"https://blog.huanchain.online"}},"servers":[{"url":"/","description":"Current Environment Server (Local / Vercel)"}],"tags":[{"name":"Dashboard","description":"SB Admin Dashboard KPI cards, Area Chart, Bar Chart, and Redis status"},{"name":"Posts","description":"Blog post CRUD, search, filtering, pagination, and view tracking"},{"name":"Categories","description":"Category management and post counts"},{"name":"Tags","description":"Article tag taxonomy management"},{"name":"Auth","description":"Admin authentication and session tokens"},{"name":"System","description":"Database seed and Redis maintenance utilities"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Use /api/v1/auth/login (default: admin / admin123) to obtain a JWT token."}},"schemas":{"Post":{"type":"object","properties":{"id":{"type":"string","example":"post-fda-architecture"},"title":{"type":"string","example":"Scaling Next.js Applications with Feature-Driven Architecture (FDA)"},"slug":{"type":"string","example":"scaling-nextjs-feature-driven-architecture"},"excerpt":{"type":"string","example":"Why organizing your codebase by self-contained feature modules accelerates team velocity."},"content":{"type":"string","example":"## Why Feature-Driven Architecture?\n\nFull markdown body..."},"coverImage":{"type":"string","example":"https://images.unsplash.com/photo-1555066931-4365d14bab8c?auto=format&fit=crop&w=1200&q=80"},"status":{"type":"string","enum":["PUBLISHED","DRAFT","ARCHIVED"],"example":"PUBLISHED"},"categoryId":{"type":"string","example":"cat-architecture"},"categoryName":{"type":"string","example":"Architecture & FDA"},"tags":{"type":"array","items":{"type":"string"},"example":["Feature-Driven","Next.js 16","TypeScript"]},"authorName":{"type":"string","example":"Airi Satou"},"authorRole":{"type":"string","example":"Accountant & Systems Architect"},"readingTimeMinutes":{"type":"integer","example":5},"views":{"type":"integer","example":162700},"featured":{"type":"boolean","example":true},"createdAt":{"type":"string","format":"date-time","example":"2026-03-01T09:00:00.000Z"},"updatedAt":{"type":"string","format":"date-time","example":"2026-03-01T09:00:00.000Z"},"publishedAt":{"type":"string","format":"date-time","nullable":true,"example":"2026-03-01T09:00:00.000Z"}}},"CreatePostRequest":{"type":"object","required":["title","content","categoryId"],"properties":{"title":{"type":"string","example":"Scaling Serverless Redis on Vercel Fluid Compute"},"slug":{"type":"string","example":"scaling-serverless-redis-vercel-fluid-compute"},"excerpt":{"type":"string","example":"A practical deep dive into Redis caching and indexing."},"content":{"type":"string","example":"## Introduction\n\nWrite your markdown article content here..."},"coverImage":{"type":"string","example":"https://images.unsplash.com/photo-1558494949-ef010cbdcc31?auto=format&fit=crop&w=1200&q=80"},"status":{"type":"string","enum":["PUBLISHED","DRAFT","ARCHIVED"],"default":"PUBLISHED"},"categoryId":{"type":"string","example":"cat-redis"},"tags":{"type":"array","items":{"type":"string"},"example":["Redis","Vercel"]},"authorName":{"type":"string","example":"Start Bootstrap"},"authorRole":{"type":"string","example":"Senior Architect"},"featured":{"type":"boolean","default":false}}},"UpdatePostRequest":{"type":"object","properties":{"title":{"type":"string"},"slug":{"type":"string"},"excerpt":{"type":"string"},"content":{"type":"string"},"coverImage":{"type":"string"},"status":{"type":"string","enum":["PUBLISHED","DRAFT","ARCHIVED"]},"categoryId":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"authorName":{"type":"string"},"authorRole":{"type":"string"},"featured":{"type":"boolean"}}},"Category":{"type":"object","properties":{"id":{"type":"string","example":"cat-architecture"},"name":{"type":"string","example":"Architecture & FDA"},"slug":{"type":"string","example":"architecture-fda"},"description":{"type":"string","example":"Feature-Driven Architecture and modular design."},"color":{"type":"string","example":"#0d6efd"},"postCount":{"type":"integer","example":2},"createdAt":{"type":"string","format":"date-time"}}},"CreateCategoryRequest":{"type":"object","required":["name"],"properties":{"name":{"type":"string","example":"AI & Agents"},"slug":{"type":"string","example":"ai-agents"},"description":{"type":"string","example":"Autonomous agents, AI SDK, and LLM workflows."},"color":{"type":"string","example":"#0d6efd"}}},"Tag":{"type":"object","properties":{"id":{"type":"string","example":"tag-1"},"name":{"type":"string","example":"Next.js 16"},"slug":{"type":"string","example":"nextjs-16"},"postCount":{"type":"integer","example":3},"createdAt":{"type":"string","format":"date-time"}}},"CreateTagRequest":{"type":"object","required":["name"],"properties":{"name":{"type":"string","example":"GraphQL"},"slug":{"type":"string","example":"graphql"}}},"LoginRequest":{"type":"object","required":["username","password"],"properties":{"username":{"type":"string","example":"admin"},"password":{"type":"string","example":"admin123"}}}}},"paths":{"/api/v1/dashboard/stats":{"get":{"tags":["Dashboard"],"summary":"Get SB Admin dashboard KPI cards, Area Chart, Bar Chart, and recent posts","responses":{"200":{"description":"Dashboard statistics retrieved from Redis"}}}},"/api/v1/posts":{"get":{"tags":["Posts"],"summary":"List blog posts with filtering, search, sorting, and pagination","parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["PUBLISHED","DRAFT","ARCHIVED"]}},{"name":"categoryId","in":"query","schema":{"type":"string"}},{"name":"tag","in":"query","schema":{"type":"string"}},{"name":"search","in":"query","schema":{"type":"string"}},{"name":"sortBy","in":"query","schema":{"type":"string","enum":["createdAt","publishedAt","views","title"]}},{"name":"sortOrder","in":"query","schema":{"type":"string","enum":["asc","desc"]}},{"name":"page","in":"query","schema":{"type":"integer","default":1}},{"name":"limit","in":"query","schema":{"type":"integer","default":20}}],"responses":{"200":{"description":"Paginated list of blog posts"}}},"post":{"tags":["Posts"],"summary":"Create a new blog post in Redis","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePostRequest"}}}},"responses":{"201":{"description":"Post created successfully"},"400":{"description":"Validation error"}}}},"/api/v1/posts/{id}":{"get":{"tags":["Posts"],"summary":"Get a single post by ID or slug","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Post ID or URL slug"},{"name":"trackView","in":"query","schema":{"type":"boolean","default":false},"description":"Increment view counter in Redis when true"}],"responses":{"200":{"description":"Post details"},"404":{"description":"Post not found"}}},"put":{"tags":["Posts"],"summary":"Update an existing blog post","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePostRequest"}}}},"responses":{"200":{"description":"Post updated successfully"},"404":{"description":"Post not found"}}},"delete":{"tags":["Posts"],"summary":"Delete a blog post by ID","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Post deleted successfully"},"404":{"description":"Post not found"}}}},"/api/v1/categories":{"get":{"tags":["Categories"],"summary":"List all categories with post counts","responses":{"200":{"description":"Categories list"}}},"post":{"tags":["Categories"],"summary":"Create a new category","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCategoryRequest"}}}},"responses":{"201":{"description":"Category created"}}}},"/api/v1/categories/{id}":{"put":{"tags":["Categories"],"summary":"Update a category by ID","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCategoryRequest"}}}},"responses":{"200":{"description":"Category updated"}}},"delete":{"tags":["Categories"],"summary":"Delete a category by ID","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Category deleted"}}}},"/api/v1/tags":{"get":{"tags":["Tags"],"summary":"List all tags with post usage counts","responses":{"200":{"description":"Tags list"}}},"post":{"tags":["Tags"],"summary":"Create a new tag","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTagRequest"}}}},"responses":{"201":{"description":"Tag created"}}}},"/api/v1/tags/{id}":{"delete":{"tags":["Tags"],"summary":"Delete a tag by ID","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Tag deleted"}}}},"/api/v1/auth/login":{"post":{"tags":["Auth"],"summary":"Authenticate admin user and receive JWT session cookie/token","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}}},"responses":{"200":{"description":"Authenticated successfully"},"401":{"description":"Invalid credentials"}}}},"/api/v1/auth/logout":{"post":{"tags":["Auth"],"summary":"Clear admin authentication cookie","responses":{"200":{"description":"Logged out successfully"}}}},"/api/v1/seed":{"post":{"tags":["System"],"summary":"Reset or re-seed Redis database with default SB Admin sample posts, categories, tags, and charts","responses":{"200":{"description":"Database seeded successfully"}}}}}}