mirror of
https://github.com/Alishahryar1/free-claude-code.git
synced 2026-07-09 16:00:45 +00:00
## Problem
Admin config was a single responsibility hub with manually duplicated
provider metadata. Provider labels, fields, template loading,
validation, persistence, and status lived in one place.
## Changes
| Before | After |
| --- | --- |
| Admin config lived in one large `api/admin_config.py` module. | Admin
config lives in package modules for manifest, sources, values,
validation, persistence, and status. |
| Provider admin fields and UI labels were manually duplicated. |
Provider admin fields and display names derive from `PROVIDER_CATALOG`
with admin-only help overrides. |
| `fcc-init` and Admin UI loaded `.env.example` separately. | `fcc-init`
and Admin UI use shared `config.env_template` loading. |
| Architecture docs pointed to the old admin config module. |
Architecture docs describe the package owners and catalog-driven
provider manifest. |
<!-- greptile_comment -->
<details open><summary><h3>Greptile Summary</h3></summary>
This PR refactors admin configuration into a catalog-driven package. The
main changes are:
- Split the former monolithic `api/admin_config.py` into manifest,
source loading, value presentation, validation, persistence, and
provider status modules.
- Generate provider admin fields and display names from
`PROVIDER_CATALOG` with admin-specific help overrides.
- Share `.env.example` loading between `fcc-init` and Admin UI defaults
through `config.env_template`.
- Update admin routes, Admin UI provider labels, architecture docs,
version metadata, and contract/API tests for the new module layout.
</details>
<h3>Confidence Score: 5/5</h3>
The refactor appears merge-safe with no code issues identified in the
reviewed changes.
The package split, catalog-driven provider metadata, shared environment
template loading, route updates, and tests/docs changes are cohesive and
covered by corresponding contract/API/CLI test updates.
<details><summary><h3><a href="https://www.greptile.com/trex"><img
alt="T-Rex"
src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg"
height="20" align="absmiddle"></a> T-Rex Logs</h3></summary>
**What T-Rex did**
- T-Rex ran manifest validation for catalog provider before and after
routes, capturing base and head responses and catalog-alignment checks,
and confirmed the validation completed successfully.
- T-Rex evaluated the shared-env-template scenarios, observing the
before run with no config.env\_template module and the after run with
the module present, with patched loader values and all consistency
checks passing, and the run exited with code 0.
- T-Rex executed the package-admin-workflow validation, verifying the
base and after import paths, the load/validate/write workflow produced
matching outputs, and the run completed with exit code 0.
<a
href="https://app.greptile.com/trex/runs/12529845/artifacts"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifactsDark.svg?v=1"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=1"><img
alt="View all artifacts"
src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=1"
height="32"></picture></a>
<sub><a href="https://www.greptile.com/trex"><img alt="T-Rex"
src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg"
height="14" align="absmiddle"></a> Ran code and verified through
T-Rex</sub>
</details>
<sub>Reviews (1): Last reviewed commit: ["Refactor admin config into
catalog-drive..."](d6239d7953)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40315222)</sub>
<!-- /greptile_comment -->
149 lines
5 KiB
Python
149 lines
5 KiB
Python
"""Managed env persistence, validation preview, and rendering."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from collections.abc import Mapping
|
|
from typing import Any
|
|
|
|
from config.paths import managed_env_path
|
|
|
|
from .manifest import FIELD_BY_KEY, FIELDS, SECTIONS, ConfigFieldSpec
|
|
from .sources import dotenv_values_from_file, is_locked_source, template_values
|
|
from .validation import validate_values
|
|
from .values import MASKED_SECRET, load_value_state, normalize_for_env
|
|
|
|
|
|
def target_values_with_updates(updates: Mapping[str, Any]) -> dict[str, str]:
|
|
"""Return managed env values after applying admin updates."""
|
|
|
|
state = load_value_state()
|
|
values = template_values()
|
|
|
|
# Preserve existing managed values when present. If no managed config exists,
|
|
# seed the first write from effective repo values to migrate legacy setups.
|
|
managed_values = dotenv_values_from_file(managed_env_path())
|
|
if managed_values:
|
|
values.update(
|
|
{key: val for key, val in managed_values.items() if key in values}
|
|
)
|
|
else:
|
|
for key, entry in state.items():
|
|
if entry["source"] in {"repo_env", "template", "default"}:
|
|
values[key] = str(entry["value"])
|
|
|
|
for key, value in updates.items():
|
|
field = FIELD_BY_KEY.get(key)
|
|
if field is None:
|
|
continue
|
|
if is_locked_source(state[key]["source"]):
|
|
continue
|
|
if field.secret and value == MASKED_SECRET:
|
|
continue
|
|
values[key] = normalize_for_env(value)
|
|
|
|
for field in FIELDS:
|
|
values.setdefault(field.key, field.default)
|
|
return values
|
|
|
|
|
|
def effective_values_for_validation(
|
|
target_values: Mapping[str, str],
|
|
) -> dict[str, str]:
|
|
"""Return values validated after preserving locked external sources."""
|
|
|
|
values = dict(target_values)
|
|
for key, entry in load_value_state().items():
|
|
if is_locked_source(entry["source"]):
|
|
values[key] = str(entry["value"])
|
|
return values
|
|
|
|
|
|
def validate_updates(updates: Mapping[str, Any]) -> dict[str, Any]:
|
|
"""Validate partial admin updates and return a masked generated env preview."""
|
|
|
|
target_values = target_values_with_updates(updates)
|
|
effective_values = effective_values_for_validation(target_values)
|
|
valid, errors = validate_values(effective_values)
|
|
return {
|
|
"valid": valid,
|
|
"errors": errors,
|
|
"env_preview": render_env_file(target_values, mask_secrets=True),
|
|
}
|
|
|
|
|
|
def changed_pending_fields(updates: Mapping[str, Any]) -> list[str]:
|
|
"""Return changed fields that require manual runtime action."""
|
|
|
|
state = load_value_state()
|
|
pending: list[str] = []
|
|
for key, value in updates.items():
|
|
field = FIELD_BY_KEY.get(key)
|
|
if field is None or not (field.restart_required or field.session_sensitive):
|
|
continue
|
|
if normalize_for_env(value) == str(state[key]["value"]):
|
|
continue
|
|
pending.append(key)
|
|
return pending
|
|
|
|
|
|
def write_managed_env(updates: Mapping[str, Any]) -> dict[str, Any]:
|
|
"""Validate and atomically write the admin-managed env file."""
|
|
|
|
validation = validate_updates(updates)
|
|
if not validation["valid"]:
|
|
return validation | {"applied": False, "pending_fields": []}
|
|
|
|
target_values = target_values_with_updates(updates)
|
|
pending_fields = changed_pending_fields(updates)
|
|
path = managed_env_path()
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
temp_path = path.with_suffix(path.suffix + ".tmp")
|
|
temp_path.write_text(render_env_file(target_values), encoding="utf-8")
|
|
os.replace(temp_path, path)
|
|
return {
|
|
"applied": True,
|
|
"valid": True,
|
|
"errors": [],
|
|
"env_preview": render_env_file(target_values, mask_secrets=True),
|
|
"path": str(path),
|
|
"pending_fields": pending_fields,
|
|
}
|
|
|
|
|
|
def quote_env_value(value: str) -> str:
|
|
"""Quote a value when dotenv syntax requires it."""
|
|
|
|
if value == "":
|
|
return ""
|
|
escaped = value.replace("\\", "\\\\").replace('"', '\\"')
|
|
if any(char.isspace() for char in value) or any(
|
|
char in value for char in ('"', "#", "=", "$")
|
|
):
|
|
return f'"{escaped}"'
|
|
return value
|
|
|
|
|
|
def render_env_file(values: Mapping[str, str], *, mask_secrets: bool = False) -> str:
|
|
"""Render a complete grouped env file."""
|
|
|
|
lines: list[str] = [
|
|
"# Managed by Free Claude Code /admin.",
|
|
"# Edit in the server UI when possible.",
|
|
"",
|
|
]
|
|
fields_by_section: dict[str, list[ConfigFieldSpec]] = {
|
|
section.section_id: [] for section in SECTIONS
|
|
}
|
|
for field in FIELDS:
|
|
fields_by_section.setdefault(field.section_id, []).append(field)
|
|
|
|
for section in SECTIONS:
|
|
lines.append(f"# {section.label}")
|
|
for field in fields_by_section.get(section.section_id, []):
|
|
value = values.get(field.key, field.default)
|
|
if mask_secrets and field.secret and value:
|
|
value = MASKED_SECRET
|
|
lines.append(f"{field.key}={quote_env_value(value)}")
|
|
lines.append("")
|
|
return "\n".join(lines).rstrip() + "\n"
|