Date: 2026-06-18 Project: WebDevBlogsite (Next.js 16 App Router) Status: Production-ready with improvement opportunities
- File:
src/middleware.ts(renamed fromsrc/proxy.ts) - Problem: Exports
proxy()function but Next.js requiresmiddleware()inmiddleware.ts. Nomiddleware.tsexists anywhere in the project. The rate limiting, CSP nonce generation, and content security policy logic in this file are never executed. - Impact: No rate limiting on API routes or server actions. No CSP headers beyond what
next.config.tsprovides (basic X-Frame-Options, etc.). - Fix: Rename file to
middleware.ts, rename export tomiddleware, verify matcher config works. - Priority: CRITICAL
- File:
docs/DELETION_LOG.md - Problem: Claims
engineering-backend-architect.mdandhandoff.mdwere deleted in the 2026-04-11 cleanup, but both files still exist at the project root. The deletion log is stale/inaccurate. - Impact: Documentation distrust. Future developers may not know what's actually in the repo.
- Fix: Either delete the files for real, or update the deletion log to reflect they were restored.
- Priority: HIGH
- Files:
engineering-backend-architect.md,docs/engineering-security-engineer.md(both deleted) - Problem: AI agent personality/definition files that don't belong in a production codebase.
engineering-backend-architect.mdis at the project root (235 lines of agent persona).docs/engineering-security-engineer.mdis in docs (304 lines). - Impact: Repo clutter, confusion for contributors, bloat.
- Fix: Delete both files. They belong in an AI config directory, not the project.
- Priority: HIGH
- File:
src/app/blog/[slug]/page.tsx:55(fixed) - Code:
const [adjacentPosts] = await Promise.all([getAdjacentPublishedPosts(slug)]); - Problem: Wraps a single async call in
Promise.alland destructures the first element.Promise.allwith one argument is pointless overhead. - Fix:
const adjacentPosts = await getAdjacentPublishedPosts(slug); - Priority: MEDIUM
- File:
src/lib/site.ts:6(fixed) - Code:
url: process.env.NEXT_PUBLIC_APP_URL ?? "http://localhost:3000" - Problem: Uses raw
process.envinstead of the validatedpublicEnvfromenv.ts. This bypasses Zod validation and could produce stale or incorrect values if the env var changes after module initialization. - Fix: Import
publicEnvand usepublicEnv.NEXT_PUBLIC_APP_URL. - Priority: MEDIUM
- File:
src/server/actions/blog.ts:188-193(fixed) - Code:
catch { return; }afterrequireAdmin() - Problem: When an unauthorized user tries to delete, the action silently returns void. No feedback, no error state. The caller has no way to know the operation failed.
- Fix: Return an error state or throw, similar to create/update actions.
- Priority: MEDIUM
- File:
src/app/blog/[slug]/page.tsx:45(fixed - ISR enabled) - Code:
export const dynamic = "force-dynamic"; - Problem: Every blog post visit triggers a fresh database query. Blog posts change infrequently. This prevents ISR (Incremental Static Regeneration) which would serve cached pages and revalidate in the background.
- Fix: Remove
force-dynamic, addrevalidate = 3600(or appropriate interval) for ISR. UserevalidateTagin admin actions to bust cache on edits. - Priority: HIGH
- File:
src/server/queries/posts.ts:97-135(fixed) - Problem:
getAdjacentPublishedPostscallsgetPublishedPostBySlug(1 query), then queries for previous post (2), then next post (3). Three sequential round-trips. - Fix: Combine into a single query using subqueries or CTEs, or at minimum run the previous/next queries in parallel with
Promise.all. - Priority: MEDIUM
- File:
src/server/queries/posts.ts:137-152(fixed) - Problem:
slugExistscallsgetPostBySlugwhich selects all columns (postColumns) just to check if a slug exists. Wasteful I/O. - Fix: Use a lightweight
SELECT 1 FROM posts WHERE slug = ? LIMIT 1query. - Priority: LOW
- File:
src/lib/markdown.ts:104-108(fixed) - Problem:
getCodeHighlighteruses Reactcache()which is per-request. Each request creates a new Shiki highlighter instance, loading all 18 languages into memory. - Fix: Use a module-level singleton (global variable) so the highlighter is created once and shared across requests.
- Priority: MEDIUM
- File:
src/app/feed.xml/route.ts(fixed) - Problem: Returns XML with only
Content-Typeheader. NoCache-Control. Every request hits the database. - Fix: Add
Cache-Control: public, max-age=3600, stale-while-revalidate=86400. - Priority: MEDIUM
- File:
src/app/sitemap.ts(fixed) - Problem: Queries database on every request. Sitemap data rarely changes.
- Fix: Add
revalidate = 3600or useunstable_cache/revalidateTag. - Priority: LOW
- File:
handoff.md(deleted) - Problem: AI session handoff notes from a previous session. Not useful to contributors. The DELETION_LOG says it was deleted but it's still here.
- Fix: Delete it.
- Priority: MEDIUM
- File:
engineering-backend-architect.md(deleted) - Problem: 235-line AI agent personality definition. Not project code.
- Fix: Delete it.
- Priority: MEDIUM
- File:
docs/engineering-security-engineer.md(deleted) - Problem: 304-line AI agent personality definition in the docs directory.
- Fix: Delete it.
- Priority: MEDIUM
- Current: Only 2 test files:
src/lib/utils.test.ts(4 tests) andsrc/lib/security.test.ts(4 tests). Total: 8 tests. - Missing:
- Server action tests (create/update/delete post)
- Query layer tests (posts, projects)
- Auth flow tests (isAdminSession, signIn callback)
- Schema validation tests (blog schemas)
- Markdown rendering tests (TOC extraction edge cases, code highlighting)
- API route tests (feed.xml, sitemap)
- Component tests (PostForm, CodeBlock, MobileMenu)
- Fix: Add tests per the TDD workflow. Target 80%+ coverage.
- Priority: HIGH
- Problem: No Playwright tests for critical user flows (admin CRUD, blog reading, navigation).
- Fix: Add E2E tests for: login → create post → verify public post → edit → delete → verify removal.
- Priority: HIGH
- Impact: Server actions and API routes are unprotected. An attacker could spam create/update/delete actions.
- Fix: Fix middleware first (#1), then verify rate limits work. (Completed in item #1)
- File:
next.config.ts:26-51 - Problem:
next.config.tssets basic security headers but no Content-Security-Policy. The CSP inproxy.tsis dead code. - Fix: Either activate the middleware CSP or add CSP to
next.config.tsheaders. (Completed in item #1 - middleware now active)
- File:
next.config.ts:57(reduced to 1mb)
- Problem: README and ARCHITECTURE.md don't document the middleware layer (because it's dead code). If it's activated, docs need updating. (Updated)
- Problem: The architecture doc doesn't mention rate limiting, CSP, or the proxy layer. (Updated)
- Problem:
handoff.mddescribes "recent backend hardening" and "recent frontend changes" as if they just happened. This is stale context from a previous session. (File deleted in item #13)
- Problem: Blog index loads all published posts. If the blog grows, this becomes slow.
- Fix: Add cursor-based or offset-based pagination.
- Problem: Blog posts have categories but no filtering UI on the blog index.
- Fix: Add category filter chips/tabs to
/blog.
- Problem: No way to search blog posts by title or content.
- Fix: Add a search input to the blog index with client-side or server-side filtering.
- Problem: Pages show nothing while data loads. No skeleton UI or loading indicators.
- Fix: Add
loading.tsxfiles for routes and Suspense boundaries for async components.
- Problem: No error boundary specifically for admin pages. Errors fall through to the root error boundary.
- Fix: Add
src/app/admin/error.tsx.
- Problem: Some pages import Lucide icons individually (good) but some import multiple icons from the same page component. Consider if tree-shaking is optimal.
- Priority: LOW
- Problem: All pages use a static
/images/mySite.webpfor OG images. Dynamic OG images per blog post would improve social sharing. - Fix: Use Next.js
ImageResponseor a static generation approach for per-post OG images. - Priority: LOW
| Priority | Count | Items |
|---|---|---|
| CRITICAL | 1 | #1 (Dead middleware) |
| HIGH | 5 | #2, #3, #7, #16, #17 |
| MEDIUM | 9 | #4, #5, #6, #8, #10, #11, #13, #14, #15 |
| LOW | 5 | #9, #12, #29, #30, + others |
| INFO | 10 | #18-#28 (improvements) |