perf: frontend performance & edge infrastructure optimization (Phase 1)#85
Merged
joaquimscosta merged 9 commits intomainfrom Mar 2, 2026
Merged
perf: frontend performance & edge infrastructure optimization (Phase 1)#85joaquimscosta merged 9 commits intomainfrom
joaquimscosta merged 9 commits intomainfrom
Conversation
- Replace force-dynamic with revalidate=1800 on homepage (T-02) - Add immutable Cache-Control header for /_next/static/ assets (T-03) - Add lightningcss-darwin-x64 for local build compatibility
- Add sharp as production dependency for native image processing (T-04) - Switch Dockerfile from node:20-alpine to node:22-slim for glibc compat (T-05) - Configure AVIF format with WebP fallback for ~50% smaller images (T-06)
- Add babel-plugin-react-compiler as devDependency (T-07) - Set reactCompiler: true in next.config.ts - Healthcheck confirmed 439/439 components compatible
🚀 PR Validation Results📁 Components Changed:
🔍 Validation Results:⏭️ Backend CI: Skipped (no relevant changes) 🎉 Status: READY FOR REVIEWAll validation checks have passed! This PR is ready for code review. Updated: 2026-03-02T20:44:39.411Z | PR: #85 |
Add Cloudflare provider v5 to manage DNS zone, 17 DNS records, and R2 media bucket as code. All resources imported from existing Cloudflare dashboard config. CI/CD workflow updated with TF_VAR_cloudflare_* env vars from GitHub secrets.
…alidation
Replace legacy force-static/revalidate pattern with "use cache" + cacheLife()
for next-generation on-demand ISR with three cache profiles:
- max: infinite cache (static pages: about, contact, privacy, terms, contribute)
- content: 5min stale, 1hr revalidate, 24hr expire (directory, history, people slugs)
- entry: 1min stale, 30min revalidate, 24hr expire (homepage, gallery, directory detail)
- longLived: 10min stale, 2hr revalidate, 7d expire (history/people index)
Add tag-based cache invalidation for directory/gallery pages. Extend
/api/revalidate to support both path-based and tag-based revalidation:
- revalidateTag("gallery") for all gallery content
- revalidateTag("category:hotels") for category-specific content
- revalidateTag("entry:heritage:chiesa") for individual entries
Update .env.local.example to document REVALIDATE_SECRET configuration for
frontend cache invalidation authentication.
All pages verified with full Playwright sweep. ESLint and TypeScript checks pass.
References: #83
Terraform Plan 📖Show Plan (Click to expand)Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
~ update in-place
Terraform will perform the following actions:
# google_cloud_run_v2_service.nosilha_backend_api will be updated in-place
~ resource "google_cloud_run_v2_service" "nosilha_backend_api" {
- client = "gcloud" -> null
- client_version = "557.0.0" -> null
id = "projects/nosilha/locations/us-east1/services/nosilha-backend-api"
name = "nosilha-backend-api"
# (31 unchanged attributes hidden)
~ template {
# (10 unchanged attributes hidden)
~ containers {
~ image = "us-east1-docker.pkg.dev/nosilha/nosilha-backend/nosilha-core-api:4da0780486b228ccd84f48e64d7e1180c35c8bd6" -> "us-east1-docker.pkg.dev/nosilha/nosilha-backend/nosilha-core-api:latest"
name = null
# (6 unchanged attributes hidden)
~ resources {
~ limits = {
~ "cpu" = "1" -> "1000m"
# (1 unchanged element hidden)
}
# (2 unchanged attributes hidden)
}
# (24 unchanged blocks hidden)
}
# (1 unchanged block hidden)
}
# (1 unchanged block hidden)
}
# google_cloud_run_v2_service.nosilha_frontend will be updated in-place
~ resource "google_cloud_run_v2_service" "nosilha_frontend" {
- client = "gcloud" -> null
- client_version = "557.0.0" -> null
id = "projects/nosilha/locations/us-east1/services/nosilha-frontend"
name = "nosilha-frontend"
# (31 unchanged attributes hidden)
~ template {
# (10 unchanged attributes hidden)
~ containers {
~ image = "us-east1-docker.pkg.dev/nosilha/nosilha-frontend/nosilha-web-ui:4da0780486b228ccd84f48e64d7e1180c35c8bd6" -> "us-east1-docker.pkg.dev/nosilha/nosilha-frontend/nosilha-web-ui:latest"
name = null
# (6 unchanged attributes hidden)
~ resources {
~ limits = {
~ "cpu" = "1" -> "1000m"
# (1 unchanged element hidden)
}
# (2 unchanged attributes hidden)
}
# (3 unchanged blocks hidden)
}
# (1 unchanged block hidden)
}
# (1 unchanged block hidden)
}
Plan: 0 to add, 2 to change, 0 to destroy.
Pusher: @joaquimscosta, Action: |
…e invalidation - Create GCP Secret Manager secret (revalidate_secret) - Grant IAM access to both backend and frontend Cloud Run services - Inject REVALIDATE_SECRET env var into backend and frontend Cloud Run services - Backend uses secret to call frontend revalidation endpoint with authentication - Frontend validates incoming revalidation requests using shared secret This enables backend services (e.g., gallery media uploads, content updates) to invalidate the frontend's Next.js cache without exposing the cache invalidation endpoint publicly.
Terraform Plan 📖Show Plan (Click to expand)No changes. Your infrastructure matches the configuration.
Terraform has compared your real infrastructure against your configuration
and found no differences, so no changes are needed.
Pusher: @joaquimscosta, Action: |
- Add missing depends_on for revalidation and resend_api_key IAM bindings on frontend Cloud Run service (prevents race condition during Terraform apply) - Move COPYRIGHT_YEAR constant after imports in footer component (fixes import ordering) - Update misleading 'Build-time constant' comment to 'Module-level constant' (reflects actual evaluation timing in use client component) Addresses code review findings from pragmatic-code-review.
Terraform Plan 📖Show Plan (Click to expand)No changes. Your infrastructure matches the configuration.
Terraform has compared your real infrastructure against your configuration
and found no differences, so no changes are needed.
Pusher: @joaquimscosta, Action: |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Contributor
Author
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance. 🤖 Generated with Claude Code |
…erateStaticParams
- Add missing cacheTag(`photo:${id}`) to photo detail page for on-demand
cache invalidation, matching the pattern in directory entry detail pages
- Fix _domainconnect DNS CNAME record to use proxied=false since Cloudflare
does not support proxying underscore-prefixed service records
- Restore generateStaticParams to history/[slug] page for build-time
pre-rendering consistency with people/[slug] page
- Update .claude/rules/frontend/app-router.md to reflect ISR → cacheLife migration
Follow-up: #86 tracks backend tag-based revalidation support
Terraform Plan 📖Show Plan (Click to expand)Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
~ update in-place
Terraform will perform the following actions:
# cloudflare_dns_record.domainconnect_cname will be updated in-place
~ resource "cloudflare_dns_record" "domainconnect_cname" {
id = "76f9e464de0099400fcf8a42b6de04e0"
~ modified_on = "2025-09-14T00:37:35Z" -> (known after apply)
name = "_domainconnect.nosilha.com"
~ proxied = true -> false
tags = []
~ ttl = 1 -> 3600
# (7 unchanged attributes hidden)
}
Plan: 0 to add, 1 to change, 0 to destroy.
Pusher: @joaquimscosta, Action: |
…ges exist The history category has no sub-pages in the Velite content (only _meta.yaml), so generateStaticParams returns an empty array which Next.js 16 rejects with EmptyGenerateStaticParamsError when using "use cache". The people category correctly has generateStaticParams because it has actual sub-pages.
Terraform Plan 📖Show Plan (Click to expand)Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
~ update in-place
Terraform will perform the following actions:
# cloudflare_dns_record.domainconnect_cname will be updated in-place
~ resource "cloudflare_dns_record" "domainconnect_cname" {
id = "76f9e464de0099400fcf8a42b6de04e0"
~ modified_on = "2025-09-14T00:37:35Z" -> (known after apply)
name = "_domainconnect.nosilha.com"
~ proxied = true -> false
tags = []
~ ttl = 1 -> 3600
# (7 unchanged attributes hidden)
}
Plan: 0 to add, 1 to change, 0 to destroy.
Pusher: @joaquimscosta, Action: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR completes the first phase of the deployment & performance evaluation roadmap with 6 incremental commits focusing on frontend performance optimization and edge infrastructure setup.
Frontend Performance Improvements
Homepage ISR + Static Asset Cache Headers
force-dynamicwithrevalidate=1800(30-min ISR)/_next/static/*(1-year max-age)Image Optimization (Sharp + AVIF)
sharpas production dependency for native image processingReact Compiler
Next.js 16 cacheLife API
force-static/revalidateto"use cache"+cacheLife()max: infinite (static pages)content: 5min stale, 1hr revalidate, 24hr expire (directory/history/people)entry: 1min stale, 30min revalidate, 24hr expire (homepage/gallery)longLived: 10min stale, 2hr revalidate, 7d expire (index pages)Infrastructure Foundation
Cloudflare Terraform Provider
Revalidation Secret Infrastructure
revalidate_secret)Test Plan
Performance Impact
Next Steps
Files Changed