Construire un dashboard Nuxt 4 pour piloter Caddy via son Admin API — authentification JWT, reverse proxy CRUD, import/export, et déploiement Docker Compose.
On part du repo GitHub et on arrive à un dashboard fonctionnel connecté à une instance Caddy locale.
git clone https://github.com/williamloree/OpenCaddyHost.git
cd OpenCaddyHost
npm installcp .env.example .env# Clé JWT — générer avec : openssl rand -base64 48
JWT_SECRET=your-super-secret-jwt-key-change-this-in-production
# Compte administrateur du dashboard
ADMIN_USERNAME=admin
ADMIN_PASSWORD=changeme123
# URL de l'Admin API Caddy
CADDY_ADMIN_URL=http://127.0.0.1:2019
NODE_ENV=developmentEn production : remplacer changeme123 par un mot de passe fort et générer un vrai JWT_SECRET :
openssl rand -base64 48Par défaut, Caddy expose son Admin API sur 127.0.0.1:2019. Pour démarrer Caddy manuellement :
caddy run --config /etc/caddy/CaddyfileVérifier que l'API répond :
curl http://127.0.0.1:2019/config/La réponse doit être un JSON avec la configuration Caddy courante. Si la commande échoue, l'Admin API n'est pas activée ou Caddy ne tourne pas.
npm run devOuvrir http://localhost:3000. Identifiants par défaut : admin / changeme123.
OpenCaddyHost/
├── app/
│ ├── components/ ← composants Vue réutilisables
│ ├── composables/ ← useAuth, useNotifications
│ ├── layouts/ ← default.vue (sidebar + header)
│ ├── pages/
│ │ ├── index.vue ← dashboard principal
│ │ ├── login.vue ← formulaire de connexion
│ │ ├── import-export.vue ← backup/restore config
│ │ ├── proxies/ ← gestion des proxies
│ │ └── settings/ ← paramètres
│ ├── stores/
│ │ └── auth.ts ← Pinia auth store
│ └── types/ ← interfaces TypeScript
├── server/
│ ├── api/
│ │ ├── auth.post.ts ← POST /api/auth (login)
│ │ ├── auth/
│ │ │ └── verify.get.ts ← GET /api/auth/verify
│ │ ├── proxies.get.ts ← GET /api/proxies
│ │ ├── proxies.post.ts ← POST /api/proxies
│ │ ├── caddy/
│ │ │ └── health.get.ts ← GET /api/caddy/health
│ │ └── health.get.ts ← GET /api/health
│ └── utils/
│ └── caddy.ts ← client Caddy Admin API
├── .env.example
├── docker-compose.yml
├── Dockerfile
└── nuxt.config.tsexport default defineNuxtConfig({
compatibilityDate: '2025-09-02',
modules: ['@pinia/nuxt', '@nuxtjs/tailwindcss', '@nuxt/icon'],
typescript: { strict: true },
devServer: { port: 3000 },
runtimeConfig: {
// Côté serveur uniquement
jwtSecret: process.env.JWT_SECRET || 'your-super-secret',
adminUsername: process.env.ADMIN_USERNAME || 'admin',
adminPassword: process.env.ADMIN_PASSWORD || 'changeme123',
caddyAdminUrl: process.env.CADDY_ADMIN_URL || 'http://127.0.0.1:2019',
public: {
// Côté client
apiBase: '/api',
},
},
})runtimeConfig sépare les secrets serveur des variables publiques. Tout ce qui est dans runtimeConfig (hors public) n'est jamais envoyé au navigateur.