mirror of
https://github.com/Dvorinka/Containr.git
synced 2026-10-28 15:51:26 +00:00
feat(services): env doctor — surface empty vars, bad refs, unreadable secrets
GET /services/:id/env-check (read scope) reports, without exposing values:
- unresolved: ${{service.KEY}} refs that don't match a sibling service/env
- empty: variables with blank values
- unreadable: secrets whose ciphertext fails decryption (key rotation)
install.sh now generates SECRETS_KEY explicitly (was falling back to
JWT_SECRET — a rotation would orphan ciphertext). .env.example documents it.
Surfaces: CLI `services env-check`, MCP containr_services_env_check
(90 tools), OpenAPI path, and a warning banner in the service Variables
panel listing each issue.
Verified live: injected a bogus ${{nosuchservice.PORT}} var plus an
unreadable ciphertext row; the endpoint flagged both, ok=false.
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
dbc87bf34c
commit
d135e5fd4b
13 files changed
+269
-3
No files matched your search
@@ -36,6 +36,7 @@ import {
|
||||
sleepService,
|
||||
wakeService,
|
||||
getServiceRuntime,
|
||||
getServiceEnvCheck,
|
||||
type CronJobEntity,
|
||||
type ServiceVolume,
|
||||
type ServiceDomain,
|
||||
@@ -189,6 +190,12 @@ export function ServiceDetailPage() {
|
||||
enabled: Boolean(serviceId) && !isDemoMode && activeSection === 'variables',
|
||||
});
|
||||
|
||||
const envCheckQuery = useQuery({
|
||||
queryKey: ['service-env-check', serviceId],
|
||||
queryFn: () => getServiceEnvCheck(serviceId),
|
||||
enabled: Boolean(serviceId) && !isDemoMode && activeSection === 'variables',
|
||||
});
|
||||
|
||||
const saveVariablesMutation = useMutation({
|
||||
mutationFn: (rows: VariableDraft[]) =>
|
||||
updateServiceVariables(
|
||||
@@ -1315,6 +1322,23 @@ export function ServiceDetailPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{envCheckQuery.data && !envCheckQuery.data.ok && (
|
||||
<div className="mb-4 rounded-[var(--radius-md)] border border-[var(--warning)]/40 bg-[var(--warning-soft)] p-4">
|
||||
<p className="text-xs font-semibold text-[var(--warning)] mb-2">Environment issues detected</p>
|
||||
<ul className="space-y-1 text-xs text-[var(--text-secondary)]">
|
||||
{(envCheckQuery.data.unresolved ?? []).map((u) => (
|
||||
<li key={u} className="mono">Unresolved reference: {u}</li>
|
||||
))}
|
||||
{(envCheckQuery.data.empty ?? []).map((k) => (
|
||||
<li key={k} className="mono">Empty value: {k}</li>
|
||||
))}
|
||||
{(envCheckQuery.data.unreadable ?? []).map((k) => (
|
||||
<li key={k} className="mono">Cannot decrypt secret (key rotated?): {k}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{bulkOpen && (
|
||||
<div className="mb-4 panel-soft p-4">
|
||||
<label className="block text-xs font-medium uppercase tracking-wider text-[var(--text-muted)] mb-2">
|
||||
|
||||
Reference in new issue
Block a user