- Created src/config/ (env.js, site-config.js) - Created src/utils/ (async-handler.js, responses.js, safe-error.js) - server.js not yet modified (backward-compatible extraction)
13 KiB
DashCaddy API Deslopification Roadmap
Audited: 2026-03-22
Version: 1.1.0
Total Lines: ~26,000 (API), ~10,000 (dashboard)
Priority: API-first (make backend powerful, clean dashboard follows naturally)
Executive Summary
The DashCaddy API is feature-complete and security-hardened, but the codebase shows signs of rapid evolution. While functionally robust, it would significantly benefit from architectural refactoring to improve maintainability, testability, and long-term scalability.
Key Strengths
✅ Comprehensive feature set (76+ app templates, Docker/Caddy/DNS management)
✅ Security-conscious (TOTP auth, AES-256-GCM credentials, CSRF protection, audit logging)
✅ Recent test coverage additions (auth, credentials, Docker security)
✅ Modular route organization (routes/ subdirectories)
✅ Shared context pattern for dependency injection
Core Issues
❌ Monolithic server.js (1960 lines) — initialization, middleware, utilities, business logic all in one file
❌ God object ctx — 50+ properties/methods across multiple domains with hidden dependencies
❌ Inconsistent patterns — routes use classes, factory functions, or flat modules with no standard
❌ No code standards — ESLint installed but no config, no formatting rules
❌ Mixed concerns — HTTP handlers, business logic, validation intertwined in route files
Current Architecture
dashcaddy-api/
├── server.js (1960 lines) ← MAIN PROBLEM
│ ├── 89 require() statements
│ ├── 131 top-level declarations
│ ├── Middleware setup
│ ├── Context (`ctx`) assembly (50+ properties)
│ ├── Route mounting
│ ├── Error handlers
│ └── Server startup
├── routes/
│ ├── auth/ (5 files, modular) ✅
│ ├── config/ (4 files, modular) ✅
│ ├── apps/ (6 files, helpers pattern) ⚠️
│ ├── arr/ (4 files, helpers pattern) ⚠️
│ ├── recipes/ (3 files) ⚠️
│ └── *.js (19 flat route files) ❌
├── Managers (clean, well-separated)
│ ├── auth-manager.js (307 lines) ✅
│ ├── credential-manager.js (395 lines) ✅
│ ├── state-manager.js (237 lines) ✅
│ ├── backup-manager.js (835 lines) ⚠️
│ ├── health-checker.js (591 lines) ⚠️
│ └── update-manager.js (911 lines) ⚠️
├── Utilities
│ ├── input-validator.js (606 lines) ⚠️
│ ├── crypto-utils.js (340 lines) ✅
│ ├── middleware.js (430 lines) ⚠️
│ └── constants.js ✅
└── Templates
├── app-templates.js (2496 lines) ⚠️
└── recipe-templates.js (339 lines) ✅
Legend:
✅ Good structure
⚠️ Works but could be cleaner
❌ Needs refactoring
Deslopification Phases
Phase 1: Foundation & Standards (IMMEDIATE)
Goal: Establish code quality baseline before refactoring
Effort: 2-4 hours
Risk: Low (tooling only, no code changes)
1.1 Code Standards Setup
- Create
.eslintrc.jswith recommended rules - Add Prettier config (
.prettierrc) - Add npm scripts:
lint,lint:fix,format - Run
npm run lint:fixand commit baseline cleanup - Add pre-commit hooks (optional)
Why first: Establish formatting/style consistency before making structural changes. Prevents "should I refactor this while I'm here?" scope creep.
1.2 Dependency Graph Documentation
- Map
ctxproperties → which routes actually use them - Identify circular dependencies (if any)
- Document shared utilities used across routes
Deliverable: DEPENDENCIES.md — reference for refactoring decisions
Phase 2: Extract & Organize (HIGH PRIORITY)
Goal: Break server.js into logical modules
Effort: 1-2 days
Risk: Medium (requires testing at each step)
2.1 Split server.js into Layers
Before: 1960-line monolith
After: Clean initialization flow
Create new structure:
src/
├── app.js ← Express app setup (middleware, routes)
├── server.js ← Entry point (load config, start server)
├── config/
│ ├── index.js ← Load all config (env, files, constants)
│ ├── env.js ← Environment variable validation
│ └── paths.js ← Platform-specific paths
├── context/
│ ├── index.js ← Assemble context (DI container)
│ ├── docker.js ← Docker-related context properties
│ ├── caddy.js ← Caddy-related context properties
│ ├── dns.js ← DNS context
│ ├── session.js ← Session context
│ └── notification.js ← Notification context
├── middleware/
│ ├── index.js ← Export all middleware
│ ├── auth.js ← Move from middleware.js
│ ├── error.js ← Error handlers
│ └── security.js ← Helmet, CORS, CSRF
└── routes/
└── (existing structure)
Migration Steps:
- Create
src/config/— extract all config loading fromserver.js - Create
src/context/— split god object into domain modules - Create
src/middleware/— break upmiddleware.js(430 lines) - Create
src/app.js— Express setup + route mounting - Slim
server.js→ minimal entry point (~50 lines)
Tests: Ensure existing test suite still passes after each step
Phase 3: Route Standardization (MEDIUM PRIORITY)
Goal: Consistent route module pattern across entire API
Effort: 2-3 days
Risk: Medium (touching business logic)
3.1 Establish Route Pattern
Chosen Pattern: Factory function with explicit dependencies
// routes/services.js (before)
module.exports = (ctx) => {
const router = express.Router();
// ... uses ctx.docker, ctx.servicesStateManager, ctx.log, etc.
return router;
};
// routes/services.js (after)
module.exports = ({ docker, servicesStateManager, log, asyncHandler }) => {
const router = express.Router();
// ... explicitly passed dependencies
return router;
};
Benefits:
- Self-documenting (you see what each route needs)
- Easier testing (mock only what's used)
- No hidden dependencies via god object
3.2 Refactor Routes by Priority
Order: Most-used routes first
-
High-traffic routes:
routes/services.js(467 lines) — core service managementroutes/containers.js(246 lines) — Docker operationsroutes/health.js(297 lines) — health checksroutes/dns.js(632 lines) — DNS management
-
Auth routes (already modular, just align pattern):
routes/auth/*
-
Feature routes:
routes/apps/*routes/arr/*routes/recipes/*
-
Utility routes:
routes/logs.jsroutes/backups.jsroutes/ca.js- etc.
Per-route checklist:
- Extract dependencies from
ctx→ explicit parameters - Move business logic to service layer (if complex)
- Validate inputs at route boundary
- Return consistent error format
- Add route-level tests
Phase 4: Service Layer Introduction (LOWER PRIORITY)
Goal: Separate business logic from HTTP handlers
Effort: 3-5 days
Risk: Medium-High (significant refactor)
Problem: Routes currently mix HTTP concerns with business logic:
// Current: Everything in route handler
router.post('/deploy', async (req, res) => {
// 1. Parse request
// 2. Validate inputs
// 3. Business logic (complex Docker operations)
// 4. Error handling
// 5. Format response
});
Solution: Service layer pattern
// routes/apps/deploy.js
router.post('/deploy', async (req, res) => {
const result = await appDeployService.deploy(req.body);
res.json({ success: true, data: result });
});
// services/app-deploy-service.js
class AppDeployService {
async deploy({ templateId, config }) {
// Pure business logic, no HTTP awareness
}
}
Candidates for service extraction:
services/docker-service.js— container lifecycle, networkingservices/caddy-service.js— Caddyfile manipulation, reloadservices/dns-service.js— record management, zone operationsservices/app-deploy-service.js— template-based deploymentservices/backup-service.js— backup/restore workflows
Benefits:
- Routes become thin HTTP adapters (easy to test)
- Business logic testable without HTTP mocking
- Reusable across routes (e.g., CLI tools, cron jobs)
Phase 5: Manager Cleanup (ONGOING)
Goal: Refine existing manager modules
Effort: 1-2 days (parallel to other phases)
Issues to Address
backup-manager.js(835 lines) — too large, split backup vs restore logicupdate-manager.js(911 lines) — complex state machine, extract version comparison utilitieshealth-checker.js(591 lines) — separate health check logic from notification daemoninput-validator.js(606 lines) — split by domain (docker, caddy, dns validators)
Approach: Incremental splitting, preserve existing API
Phase 6: Template Organization (LOW PRIORITY)
Goal: Make templates maintainable and extensible
Effort: 1 day
Problem: app-templates.js is 2496 lines (76 templates in one file)
Solution:
templates/
├── index.js ← Export TEMPLATE_CATEGORIES, DIFFICULTY_LEVELS
├── apps/
│ ├── media/
│ │ ├── plex.js
│ │ ├── jellyfin.js
│ │ └── ...
│ ├── automation/
│ └── ...
└── recipes/
├── arr-stack.js
└── ...
Benefits:
- Easier to find/edit specific templates
- Contributors can add templates without merge conflicts
- Templates can import shared snippets (e.g., common env vars)
Metrics & Success Criteria
Code Quality Metrics (Before → After)
| Metric | Before | Target | How to Measure |
|---|---|---|---|
server.js lines |
1960 | <200 | wc -l server.js |
| Avg route file size | ~300 | <150 | find routes -name '*.js' -exec wc -l {} + | awk '{sum+=$1; n++} END {print sum/n}' |
ctx properties |
50+ | 0 (removed) | Manual count |
| ESLint errors | Unknown | 0 | npm run lint |
| Test coverage | ~30% | >60% | npm run test:coverage |
| Files >500 lines | 8 | <3 | find . -name '*.js' -exec wc -l {} + | awk '$1 > 500' |
Developer Experience Improvements
- Onboarding: New contributor should understand route structure in <10 minutes
- Testing: Mock only what you use (no god object sprawl)
- Changes: Touching one domain shouldn't require understanding entire codebase
- Deployment: Confidence that refactor didn't break anything (test suite)
Risk Mitigation
How to Refactor Safely
-
Test suite first — before touching code:
- Run existing tests:
npm test - Identify untested critical paths → add tests
- Establish coverage baseline
- Run existing tests:
-
Incremental changes:
- Each phase = separate branch
- Each phase passes full test suite
- Deploy to test environment (Contabo) before merging
-
Preserve API contract:
- Frontend expects same endpoints/responses
- Dashboard shouldn't need changes during API refactor
- Version routes if breaking changes needed
-
Rollback plan:
- Git tags before each phase merge
- Keep old code in
legacy/until confidence is high - Document what changed in each PR
Recommended Order of Execution
Week 1: Foundation
- Day 1-2: Phase 1 (ESLint, Prettier, dependency mapping)
- Day 3-5: Phase 2.1 (split
server.js)
Week 2: Routes
- Day 1-3: Phase 3.1 (standardize top 5 routes)
- Day 4-5: Phase 3.2 (remaining routes)
Week 3: Refinement
- Day 1-3: Phase 4 (service layer for complex routes)
- Day 4-5: Phase 5 (manager cleanup)
Week 4: Polish
- Day 1-2: Phase 6 (template organization)
- Day 3-5: Documentation, final testing, deployment
Total: ~4 weeks part-time or ~2 weeks full-time
Questions for Sami
Before starting, clarify:
-
Testing strategy: Current test coverage is partial. Should we:
- Write tests BEFORE refactoring (safer, slower)?
- Refactor with existing tests, add coverage later (faster, riskier)?
-
Breaking changes: Can we introduce backwards-incompatible API changes if we version routes (
/api/v2/...)? -
Deployment cadence: Should each phase deploy to production, or batch into one big release?
-
Priority tweaks: Does this roadmap align with "deslopify → market → sell" timeline, or should we focus only on the most visible pain points first?
Next Steps
If approved:
- Create feature branch:
refactor/deslopification-phase-1 - Add ESLint + Prettier configs
- Run
npm run lint:fixand commit baseline - Create
DEPENDENCIES.md(ctx usage map) - Review with Sami before proceeding to Phase 2
Estimated time to first visible improvement: 1 week (server.js split + linting)