2026 04 05 Learn To Academy
Learn to Academy Migration
Date: 2026-04-05 Status: Completed Branch: feat/refactor-learn-to-academy
Summary#
Renamed the training platform from "learn" to "academy" for clearer branding and consistent product positioning.
Changes#
Frontend App#
- Directory:
apps/learn→apps/academy - Package name:
learn→academy - Dev port: 3044 (unchanged)
- Prod port: 3054 → 13007 (new allocation from reserved range)
- Wrangler project:
assistance-learn→assistance-academy
Backend Service#
- Service name:
learn-api→academy-api - Directory:
backend/cmd/learn-api→backend/cmd/academy-api - Internal package:
backend/internal/learn→backend/internal/academy - Seed service:
learn-seed→academy-seed - Dev port: 3065 (unchanged)
- Prod port: 3066 (unchanged)
- Health endpoint: Returns
{"service":"academy-api","status":"ok"}
Manager Admin#
- Routes:
/admin/learn→/admin/academy - Route files:
apps/manager/src/routes/_admin/admin/learn/→academy/ - Query keys:
["admin", "learn", ...]→["admin", "academy", ...] - API endpoints:
/admin/learn/*→/admin/academy/*
Configuration Files#
- package.json: All
--filter=learn→--filter=academy(9 occurrences) - turbo.json:
learn#build,learn#test→academy#build,academy#test - .mise.toml: 50+ task updates (dev:learn → dev:academy, etc.)
- podman-compose.yml:
learn-apiservice →academy-api
DNS & Infrastructure#
- Dev domain:
academy.assistance.dev.assistance.bg→ 139.162.191.120 - Prod domain:
academy.assistance.prod.assistance.bg→ 139.162.191.120 - Caddy config: Site blocks added on both public and LAN gateways
- TLS certificates: Valid Let's Encrypt certificates obtained
- Split-horizon DNS: LAN resolution to 192.168.5.10 (local Caddy)
URL Changes#
- robots.ts:
learn.assistance.prod.assistance.bg→academy.assistance.prod.assistance.bg - sitemap.ts: Same URL update
- course-json-ld.tsx: Same URL update
Breaking Changes#
DNS Records#
Old DNS records were replaced (direct cutover):
- ❌
learn.assistance.dev.assistance.bg(removed) - ❌
learn.assistance.prod.assistance.bg(removed) - ✅
academy.assistance.dev.assistance.bg(added) - ✅
academy.assistance.prod.assistance.bg(added)
API Endpoints#
Backend API routes changed:
- Old:
/admin/learn/courses,/admin/learn/enrollments, etc. - New:
/admin/academy/courses,/admin/academy/enrollments, etc.
Manager Admin#
Bookmarks and direct links to /admin/learn need updating to /admin/academy.
Environment Variables#
No breaking changes - NEXT_PUBLIC_LEARN_API_URL still supported for backward compatibility.
Rollback Procedure#
If critical issues are discovered:
Quick Rollback (< 10 minutes)#
- Revert DNS:
1# Via registry API2curl -X POST "http://192.168.3.20:9070/domains/3451891/records" \3 -H "Content-Type: application/json" \4 -d '{"type":"A","name":"learn.assistance","target":"139.162.191.120","ttl_sec":300}'56curl -X POST "http://192.168.3.20:9070/domains/3451909/records" \7 -H "Content-Type: application/json" \8 -d '{"type":"A","name":"learn.assistance","target":"139.162.191.120","ttl_sec":300}'- Revert Caddy:
1ssh 192.168.3.20 'cd /home/vchavkov/src/BA/registry/caddy && \2 cp Caddyfile.backup-learn Caddyfile && \3 bash scripts/sync.sh'- Restart services with old binaries:
1cd /home/vchavkov/src/assistance2git checkout main3systemctl restart learn-api # If running as systemd serviceFull Rollback (< 30 minutes)#
1# Checkout main branch2git checkout main34# Restore directory structure5cd apps/6mv academy learn78# Reset all configs9git checkout main -- package.json .mise.toml turbo.json podman-compose.yml1011# Reinstall dependencies12pnpm install1314# Rebuild15cd backend && make build SERVICE=learn-api16cd ../apps/learn && pnpm build1718# Redeploy19mise run cf:deploy:learnVerification#
Technical Checks#
- All builds pass without errors
- All tests pass (
pnpm test) - Dev environment starts cleanly (
mise run dev:academy) - Prod deployment successful
- No 500 errors in logs
- DNS resolves correctly
- Caddy routing works
- Manager admin panel functional
Functional Checks#
- Course catalog loads
- Search functionality works
- User enrollment flow works
- Progress tracking works
- Certificate generation works
- Manager admin can list/edit courses
- Analytics/telemetry flowing
Infrastructure Checks#
- Dev DNS:
academy.assistance.dev.assistance.bg→ 139.162.191.120 ✓ - Prod DNS:
academy.assistance.prod.assistance.bg→ 139.162.191.120 ✓ - Caddy dev proxy: port 3044 ✓
- Caddy prod proxy: port 13007 ✓
- Backend API health check:
{"service":"academy-api","status":"ok"}✓ - TLS certificates: Valid Let's Encrypt certificates ✓
- HTTPS endpoints: Both dev and prod responding ✓
Testing Results#
Dev Environment#
1# Frontend2curl -I https://academy.assistance.dev.assistance.bg/trainings3# HTTP/2 200 ✓45# Backend6curl http://localhost:3065/health7# {"service":"academy-api","status":"ok"} ✓89# Manager10curl http://localhost:3045/admin/academy11# HTTP/1.1 200 ✓Build Verification#
1# Frontend build2pnpm --filter academy build3# ✓ Compiled successfully45# Backend build6make build SERVICE=academy-api7# ✓ Binary: backend/bin/academy-api (39MB)89# Cloudflare build10mise run cf:build:academy11# ✓ OpenNext artifacts generatedFiles Changed#
Core Implementation (100+ files)#
apps/academy/(renamed from apps/learn/)backend/cmd/academy-api/(renamed from learn-api/)backend/internal/academy/(renamed from learn/)apps/manager/src/routes/_admin/admin/academy/(renamed from learn/)
Configuration (5 files)#
package.json- npm scripts and filtersturbo.json- build task definitions.mise.toml- 50+ task updatespodman-compose.yml- service name and imageapps/academy/wrangler.jsonc- Cloudflare project name
URLs (3 files)#
apps/academy/app/robots.tsapps/academy/app/sitemap.tsapps/academy/components/course-json-ld.tsx
Infrastructure (external)#
- DNS: 2 A records added via registry API
- Caddy: 2 site blocks added (public + LAN gateways)
Cloudflare Workers Deployment#
Status: BLOCKED#
Deployment to Cloudflare Workers is blocked pending KV namespace configuration:
Issue: wrangler.jsonc requires a KV namespace ID for Next.js incremental cache
Error: No KV binding "NEXT_INC_CACHE_KV" found
Workaround: Removed KV namespace from config, but OpenNext requires it
Resolution needed:
- Set
CLOUDFLARE_API_TOKENenvironment variable - Run
wrangler kv namespace create NEXT_INC_CACHE_KV - Update
apps/academy/wrangler.jsoncwith namespace ID - Retry deployment:
npx wrangler deploy
Current workaround: Academy is deployed locally and accessible via Caddy reverse proxy at dev and prod domains. Full Cloudflare Workers deployment pending KV setup.
Post-Migration Monitoring#
First 48 Hours#
- Monitor error rates (target: < 1%)
- Monitor response times (p95 target: < 500ms)
- Check logs for unexpected errors
- Verify analytics tracking
- Test all critical user paths (dev environment verified)
Cleanup (After 1 Week)#
- Complete Cloudflare Workers deployment (pending KV setup)
- Remove old Cloudflare Workers project (
assistance-learn) - Clean up old Docker images (
learn-api:latest) - Update team documentation/runbooks
- Archive backup branches
References#
- Planning doc:
/home/vchavkov/.claude/plans/recursive-shimmying-turing.md - Feature branch:
feat/refactor-learn-to-academy - Backup branch:
backup/learn-to-academy-20260405-1816 - Port allocation:
~/.config/brain/claude/rules/reserved-ports.md
Lessons Learned#
What Went Well#
- Auto-checkpoint commits preserved all intermediate states
- Comprehensive planning prevented missed references
- Direct cutover DNS strategy worked without issues
- TLS certificate automation (Let's Encrypt) worked seamlessly
Challenges#
- Next.js 15 params type change required async/await fix
- Local LAN Caddy needed separate configuration
- Backend hostname resolution required IP address in Caddy config
Recommendations#
- Always verify both public and LAN Caddy configurations
- Test TLS certificate issuance before announcing availability
- Use registry API for DNS changes (faster than manual Linode UI)