Skip to content

Commit 911aa0a

Browse files
m4dm4rtig4nClément VALENTINclaude
authored
Develop (#48)
* feat(web): make submenu tabs responsive and full-width - Add horizontal scroll navigation with chevron buttons for AdminTabs - Implement flex-1 layout for tabs to fill available width on large screens - Add smooth scrolling and scroll state detection for small screens - Ensure tab borders align with main content container - Update all tab components (AdminTabs, ApiDocsTabs, ConsumptionTabs) for consistency 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> * feat(web): make submenu tabs responsive and full-width (#41) - Add horizontal scroll navigation with chevron buttons for AdminTabs - Implement flex-1 layout for tabs to fill available width on large screens - Add smooth scrolling and scroll state detection for small screens - Ensure tab borders align with main content container - Update all tab components (AdminTabs, ApiDocsTabs, ConsumptionTabs) 🤖 Generated with Claude Code Co-authored-by: Clément VALENTIN <clement.valentin@blacktiger.tech> Co-authored-by: Claude <noreply@anthropic.com> * fix(admin): add missing cache_service import and improve stats loading UI (#43) Fixed NameError in /admin/stats endpoint caused by missing cache_service import. Added proper loading state using LoadingOverlay component with blur effect. Extended LoadingOverlay to support 'admin' data type with appropriate messages. 🤖 Generated with Claude Code Co-authored-by: Clément VALENTIN <clement.valentin@blacktiger.tech> Co-authored-by: Claude <noreply@anthropic.com> * fix(web): regenerate package-lock.json for npm ci compatibility (#45) The previous package-lock.json was missing tree-sitter dependencies (tree-sitter@0.21.1 and tree-sitter@0.22.4), causing `npm ci` to fail during Docker builds with the error "package.json and package-lock.json are not in sync". This regenerates the lock file to include all required dependencies. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Clément VALENTIN <clement.valentin@blacktiger.tech> Co-authored-by: Claude <noreply@anthropic.com> * feat(web): add password visibility toggle on login page Add eye icon button to show/hide password in the login form for better UX. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat(k8s): add automatic Kubernetes deployment script with dev mode (#47) Add complete Kubernetes deployment automation via rancher-desktop with dev mode support (local volume mounts for hot-reload). Includes Helm chart modifications for dev volumes, readiness probes with Host header, Vite allowedHosts configuration, and environment variable loading from .env files. Integrates with Conductor for automatic project startup/shutdown. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Clément VALENTIN <clement.valentin@blacktiger.tech> Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Clément VALENTIN <clement.valentin@blacktiger.tech> Co-authored-by: Claude <noreply@anthropic.com>
1 parent 3641ab3 commit 911aa0a

File tree

9 files changed

+810
-1
lines changed

9 files changed

+810
-1
lines changed

Makefile

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,14 @@ help:
8989
@echo " REGISTRY=$(REGISTRY)"
9090
@echo " GITHUB_ORG=$(GITHUB_ORG)"
9191
@echo ""
92+
@echo "$(YELLOW)Kubernetes (rancher-desktop):$(NC)"
93+
@echo " make k8s-deploy - Deploy to K8s in dev mode (volume mounts)"
94+
@echo " make k8s-deploy-prod - Deploy to K8s in production mode"
95+
@echo " make k8s-delete - Delete deployment from K8s"
96+
@echo " make k8s-status - Show deployment status"
97+
@echo " make k8s-logs-backend - Show backend logs"
98+
@echo " make k8s-logs-frontend - Show frontend logs"
99+
@echo ""
92100

93101
## Start development environment with hot reload
94102
dev: check-deps
@@ -474,4 +482,37 @@ docker-info:
474482
@echo " Backend: $(IMAGE_BACKEND):$(VERSION)"
475483
@echo " Frontend: $(IMAGE_FRONTEND):$(VERSION)"
476484

477-
.PHONY: help dev up up-fg down restart watch stop-watch stop-docs backend-logs backend-restart db-shell db-backup migrate migrate-downgrade migrate-history migrate-current migrate-revision migrate-stamp logs ps clean rebuild check-deps install-fswatch docs docs-build docs-dev docs-down docker-login docker-build docker-build-backend docker-build-frontend docker-push docker-push-backend docker-push-frontend docker-release docker-release-native docker-release-multiarch docker-release-amd64 docker-release-arm64 docker-release-ci docker-buildx-setup docker-info
485+
# =============================================================================
486+
# Kubernetes Commands (rancher-desktop)
487+
# =============================================================================
488+
489+
K8S_DEPLOY_SCRIPT = ./scripts/k8s-deploy.sh
490+
491+
## Deploy to Kubernetes (dev mode with volume mounts)
492+
k8s-deploy:
493+
@echo "$(GREEN)Deploying to Kubernetes (dev mode)...$(NC)"
494+
@$(K8S_DEPLOY_SCRIPT) deploy
495+
496+
## Deploy to Kubernetes (production mode with built images)
497+
k8s-deploy-prod:
498+
@echo "$(GREEN)Deploying to Kubernetes (production mode)...$(NC)"
499+
@$(K8S_DEPLOY_SCRIPT) --prod deploy
500+
501+
## Delete Kubernetes deployment
502+
k8s-delete:
503+
@echo "$(YELLOW)Deleting Kubernetes deployment...$(NC)"
504+
@$(K8S_DEPLOY_SCRIPT) delete
505+
506+
## Show Kubernetes deployment status
507+
k8s-status:
508+
@$(K8S_DEPLOY_SCRIPT) status
509+
510+
## Show backend logs in Kubernetes
511+
k8s-logs-backend:
512+
@$(K8S_DEPLOY_SCRIPT) logs backend
513+
514+
## Show frontend logs in Kubernetes
515+
k8s-logs-frontend:
516+
@$(K8S_DEPLOY_SCRIPT) logs frontend
517+
518+
.PHONY: help dev up up-fg down restart watch stop-watch stop-docs backend-logs backend-restart db-shell db-backup migrate migrate-downgrade migrate-history migrate-current migrate-revision migrate-stamp logs ps clean rebuild check-deps install-fswatch docs docs-build docs-dev docs-down docker-login docker-build docker-build-backend docker-build-frontend docker-push docker-push-backend docker-push-frontend docker-release docker-release-native docker-release-multiarch docker-release-amd64 docker-release-arm64 docker-release-ci docker-buildx-setup docker-info k8s-deploy k8s-deploy-prod k8s-delete k8s-status k8s-logs-backend k8s-logs-frontend

conductor.json

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
{
2+
"name": "k8s-auto-deploy-script",
3+
"description": "Script de déploiement automatique K8s avec mode dev (volumes locaux)",
4+
"onStart": {
5+
"command": "./scripts/k8s-deploy.sh deploy",
6+
"description": "Déploie automatiquement sur K8s au démarrage du projet",
7+
"waitForReady": true,
8+
"timeout": 300
9+
},
10+
"onStop": {
11+
"command": "./scripts/k8s-deploy.sh delete",
12+
"description": "Supprime le déploiement K8s à l'arrêt du projet",
13+
"confirm": true
14+
},
15+
"scripts": {
16+
"k8s:deploy": {
17+
"command": "./scripts/k8s-deploy.sh deploy",
18+
"description": "Déploie l'application en mode dev sur K8s (rancher-desktop)"
19+
},
20+
"k8s:deploy:prod": {
21+
"command": "./scripts/k8s-deploy.sh --prod deploy",
22+
"description": "Déploie l'application en mode production sur K8s"
23+
},
24+
"k8s:delete": {
25+
"command": "./scripts/k8s-deploy.sh delete",
26+
"description": "Supprime le déploiement K8s"
27+
},
28+
"k8s:status": {
29+
"command": "./scripts/k8s-deploy.sh status",
30+
"description": "Affiche le statut du déploiement K8s"
31+
},
32+
"k8s:logs:backend": {
33+
"command": "./scripts/k8s-deploy.sh logs backend",
34+
"description": "Affiche les logs du backend"
35+
},
36+
"k8s:logs:frontend": {
37+
"command": "./scripts/k8s-deploy.sh logs frontend",
38+
"description": "Affiche les logs du frontend"
39+
}
40+
},
41+
"kubernetes": {
42+
"context": "rancher-desktop",
43+
"namespace": "auto",
44+
"devMode": {
45+
"enabled": true,
46+
"hostPath": "/Users/cvalentin/Git/myelectricaldata_new",
47+
"mounts": {
48+
"backend": {
49+
"src": "apps/api/src",
50+
"static": "apps/api/static"
51+
},
52+
"frontend": {
53+
"src": "apps/web/src",
54+
"public": "apps/web/public"
55+
}
56+
}
57+
},
58+
"helm": {
59+
"chart": "helm/myelectricaldata",
60+
"valuesFile": "helm/values-dev.yaml",
61+
"releasePrefix": "med"
62+
}
63+
}
64+
}
1 Byte
Binary file not shown.
1 Byte
Binary file not shown.

helm/myelectricaldata/templates/backend/backend-deployment.yaml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,25 @@ spec:
4242
image: busybox:1.36
4343
command: ['sh', '-c', 'until nc -z {{ .Release.Name }}-redis {{ .Values.redis.service.port | default 6379 }}; do echo "Waiting for Redis..."; sleep 2; done; echo "Redis is ready!"']
4444
{{- end }}
45+
{{- if .Values.devMode.enabled }}
46+
# Install Python dependencies in dev mode using uv
47+
- name: uv-sync
48+
image: "{{ .Values.global.imageRegistry }}{{ .Values.backend.image.repository }}:{{ .Values.backend.image.tag }}"
49+
workingDir: /workspace
50+
env:
51+
- name: UV_CACHE_DIR
52+
value: "/workspace/.cache"
53+
command: ['sh', '-c', 'cp /host-app/pyproject.toml /host-app/uv.lock /host-app/README.md /workspace/ && uv sync --frozen']
54+
volumeMounts:
55+
- name: host-backend-files
56+
mountPath: /host-app
57+
readOnly: true
58+
- name: workspace
59+
mountPath: /workspace
60+
- name: dev-src
61+
mountPath: /workspace/src
62+
readOnly: true
63+
{{- end }}
4564
containers:
4665
- name: backend
4766
{{- with .Values.backend.securityContext }}
@@ -50,15 +69,34 @@ spec:
5069
{{- end }}
5170
image: "{{ .Values.global.imageRegistry }}{{ .Values.backend.image.repository }}:{{ .Values.backend.image.tag }}"
5271
imagePullPolicy: {{ .Values.backend.image.pullPolicy }}
72+
{{- if .Values.devMode.enabled }}
73+
workingDir: /workspace
74+
command: ["uv"]
75+
args: ["run", "uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload", "--reload-dir", "/workspace/src"]
76+
{{- end }}
5377
ports:
5478
- name: http
5579
containerPort: 8000
5680
protocol: TCP
81+
{{- if .Values.devMode.enabled }}
82+
envFrom:
83+
- secretRef:
84+
name: {{ include "myelectricaldata.backend.fullname" . }}-env
85+
optional: true
86+
{{- end }}
5787
env:
5888
- name: TZ
5989
value: {{ .Values.backend.env.TZ | quote }}
6090
- name: PYTHONUNBUFFERED
6191
value: {{ .Values.backend.env.PYTHONUNBUFFERED | quote }}
92+
{{- if .Values.devMode.enabled }}
93+
- name: WATCHFILES_FORCE_POLLING
94+
value: "true"
95+
- name: WATCHFILES_POLL_INTERVAL
96+
value: "1000"
97+
- name: UV_CACHE_DIR
98+
value: "/tmp/uv-cache"
99+
{{- end }}
62100
# PostgreSQL password MUST be defined before DATABASE_URL for $(POSTGRES_PASSWORD) substitution
63101
- name: POSTGRES_PASSWORD
64102
valueFrom:
@@ -209,6 +247,14 @@ spec:
209247
mountPath: /app/data
210248
- name: logs
211249
mountPath: /logs
250+
{{- if .Values.devMode.enabled }}
251+
- name: workspace
252+
mountPath: /workspace
253+
- name: dev-src
254+
mountPath: /workspace/src
255+
- name: dev-static
256+
mountPath: /app/static
257+
{{- end }}
212258
volumes:
213259
- name: data
214260
{{- /* Use PVC only for SQLite mode (postgres.enabled is false) */ -}}
@@ -220,6 +266,22 @@ spec:
220266
{{- end }}
221267
- name: logs
222268
emptyDir: {}
269+
{{- if .Values.devMode.enabled }}
270+
- name: dev-src
271+
hostPath:
272+
path: {{ .Values.devMode.hostPath }}/{{ .Values.devMode.backend.srcPath }}
273+
type: Directory
274+
- name: dev-static
275+
hostPath:
276+
path: {{ .Values.devMode.hostPath }}/{{ .Values.devMode.backend.staticPath }}
277+
type: DirectoryOrCreate
278+
- name: host-backend-files
279+
hostPath:
280+
path: {{ .Values.devMode.hostPath }}/apps/api
281+
type: Directory
282+
- name: workspace
283+
emptyDir: {}
284+
{{- end }}
223285
{{- with .Values.backend.nodeSelector }}
224286
nodeSelector:
225287
{{- toYaml . | nindent 8 }}

helm/myelectricaldata/templates/frontend/frontend-deployment.yaml

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,25 +32,79 @@ spec:
3232
image: busybox:1.36
3333
command: ['sh', '-c', 'until nc -z {{ include "myelectricaldata.backend.fullname" . }} {{ .Values.backend.service.port | default 8000 }}; do echo "Waiting for Backend..."; sleep 2; done; echo "Backend is ready!"']
3434
{{- end }}
35+
{{- if .Values.devMode.enabled }}
36+
# Install npm dependencies in dev mode
37+
- name: npm-install
38+
image: node:20-alpine
39+
workingDir: /app
40+
command: ['sh', '-c', 'cp /host-app/package*.json /app/ && npm ci']
41+
volumeMounts:
42+
- name: host-package-files
43+
mountPath: /host-app
44+
readOnly: true
45+
- name: node-modules
46+
mountPath: /app/node_modules
47+
{{- end }}
3548
containers:
3649
- name: frontend
3750
{{- with .Values.frontend.securityContext }}
3851
securityContext:
3952
{{- toYaml . | nindent 12 }}
4053
{{- end }}
54+
{{- if .Values.devMode.enabled }}
55+
image: "node:20-alpine"
56+
imagePullPolicy: IfNotPresent
57+
workingDir: /app
58+
command: ["sh", "-c"]
59+
args:
60+
- |
61+
# Create a vite config that allows all hosts in K8s
62+
cat > /app/vite.config.k8s.mjs << 'VITECONFIG'
63+
import { defineConfig, loadEnv } from 'vite'
64+
import react from '@vitejs/plugin-react'
65+
import { resolve } from 'path'
66+
67+
export default defineConfig(({ mode }) => {
68+
const env = loadEnv(mode, process.cwd(), '')
69+
return {
70+
plugins: [react()],
71+
resolve: { alias: { '@': resolve(process.cwd(), './src') } },
72+
server: {
73+
port: 5173,
74+
host: '0.0.0.0',
75+
allowedHosts: true,
76+
watch: { usePolling: true, interval: 1000 },
77+
},
78+
}
79+
})
80+
VITECONFIG
81+
exec npx vite --config vite.config.k8s.mjs
82+
{{- else }}
4183
image: "{{ .Values.global.imageRegistry }}{{ .Values.frontend.image.repository }}:{{ .Values.frontend.image.tag }}"
4284
imagePullPolicy: {{ .Values.frontend.image.pullPolicy }}
85+
{{- end }}
4386
ports:
4487
- name: http
88+
{{- if .Values.devMode.enabled }}
89+
containerPort: 5173
90+
{{- else }}
4591
containerPort: 80
92+
{{- end }}
4693
protocol: TCP
94+
{{- if .Values.devMode.enabled }}
95+
envFrom:
96+
- secretRef:
97+
name: {{ include "myelectricaldata.frontend.fullname" . }}-env
98+
optional: true
99+
{{- end }}
47100
{{- if .Values.frontend.env }}
48101
env:
49102
{{- range $key, $value := .Values.frontend.env }}
50103
- name: {{ $key }}
51104
value: {{ $value | quote }}
52105
{{- end }}
53106
{{- end }}
107+
{{- if not .Values.devMode.enabled }}
54108
{{- with .Values.frontend.livenessProbe }}
55109
livenessProbe:
56110
{{- toYaml . | nindent 12 }}
@@ -59,10 +113,61 @@ spec:
59113
readinessProbe:
60114
{{- toYaml . | nindent 12 }}
61115
{{- end }}
116+
{{- end }}
62117
{{- with .Values.frontend.resources }}
63118
resources:
64119
{{- toYaml . | nindent 12 }}
65120
{{- end }}
121+
{{- if .Values.devMode.enabled }}
122+
volumeMounts:
123+
- name: dev-src
124+
mountPath: /app/src
125+
- name: dev-public
126+
mountPath: /app/public
127+
- name: node-modules
128+
mountPath: /app/node_modules
129+
- name: host-package-files
130+
mountPath: /app/package.json
131+
subPath: package.json
132+
- name: host-package-files
133+
mountPath: /app/package-lock.json
134+
subPath: package-lock.json
135+
- name: host-package-files
136+
mountPath: /app/vite.config.ts
137+
subPath: vite.config.ts
138+
- name: host-package-files
139+
mountPath: /app/tsconfig.json
140+
subPath: tsconfig.json
141+
- name: host-package-files
142+
mountPath: /app/tsconfig.node.json
143+
subPath: tsconfig.node.json
144+
- name: host-package-files
145+
mountPath: /app/tailwind.config.js
146+
subPath: tailwind.config.js
147+
- name: host-package-files
148+
mountPath: /app/postcss.config.js
149+
subPath: postcss.config.js
150+
- name: host-package-files
151+
mountPath: /app/index.html
152+
subPath: index.html
153+
{{- end }}
154+
{{- if .Values.devMode.enabled }}
155+
volumes:
156+
- name: dev-src
157+
hostPath:
158+
path: {{ .Values.devMode.hostPath }}/{{ .Values.devMode.frontend.srcPath }}
159+
type: Directory
160+
- name: dev-public
161+
hostPath:
162+
path: {{ .Values.devMode.hostPath }}/{{ .Values.devMode.frontend.publicPath }}
163+
type: Directory
164+
- name: host-package-files
165+
hostPath:
166+
path: {{ .Values.devMode.hostPath }}/apps/web
167+
type: Directory
168+
- name: node-modules
169+
emptyDir: {}
170+
{{- end }}
66171
{{- with .Values.frontend.nodeSelector }}
67172
nodeSelector:
68173
{{- toYaml . | nindent 8 }}

helm/myelectricaldata/values.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,24 @@ global:
77
imagePullSecrets: []
88
storageClass: ""
99

10+
# Development mode configuration
11+
# Mounts local source code into pods for hot-reload development
12+
devMode:
13+
enabled: false
14+
# Base path on the host machine where the project is located
15+
# This path will be used to mount source code into containers
16+
hostPath: "/path/to/myelectricaldata"
17+
# Backend source code mount (relative to hostPath)
18+
backend:
19+
# Mount path for source code inside container
20+
srcPath: "apps/api/src"
21+
dataPath: "apps/api/data"
22+
staticPath: "apps/api/static"
23+
# Frontend source code mount (relative to hostPath)
24+
frontend:
25+
srcPath: "apps/web/src"
26+
publicPath: "apps/web/public"
27+
1028
# Backend (FastAPI) configuration
1129
backend:
1230
enabled: true

0 commit comments

Comments
 (0)