mirror of
https://github.com/Alishahryar1/free-claude-code.git
synced 2026-07-03 14:05:26 +02:00
51157f91bd
## 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..."](https://github.com/alishahryar1/free-claude-code/commit/d6239d7953fce75d435b8d6a20536c1aff53aa88) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=40315222)</sub> <!-- /greptile_comment -->
116 lines
3.3 KiB
Python
116 lines
3.3 KiB
Python
"""Admin config value state and API response assembly."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from typing import Any
|
|
|
|
from config.paths import managed_env_path
|
|
|
|
from .manifest import FIELD_BY_KEY, FIELDS, SECTIONS, ConfigFieldSpec
|
|
from .sources import (
|
|
configured_env_files,
|
|
dotenv_values_from_file,
|
|
explicit_env_path,
|
|
is_locked_source,
|
|
repo_env_path,
|
|
template_values,
|
|
)
|
|
from .status import provider_config_status
|
|
|
|
MASKED_SECRET = "********"
|
|
ValueState = dict[str, dict[str, Any]]
|
|
|
|
|
|
def normalize_for_env(value: Any) -> str:
|
|
"""Normalize a submitted admin value for dotenv persistence."""
|
|
|
|
if value is None:
|
|
return ""
|
|
if isinstance(value, bool):
|
|
return "true" if value else "false"
|
|
return str(value)
|
|
|
|
|
|
def display_value(field: ConfigFieldSpec, value: str) -> str:
|
|
"""Return the Admin UI display value for a raw config value."""
|
|
|
|
if field.secret and value:
|
|
return MASKED_SECRET
|
|
return value
|
|
|
|
|
|
def load_value_state() -> ValueState:
|
|
"""Load effective admin field values and their sources."""
|
|
|
|
values = template_values()
|
|
sources = {key: "template" if key in values else "default" for key in FIELD_BY_KEY}
|
|
|
|
for source, path in configured_env_files():
|
|
file_values = dotenv_values_from_file(path)
|
|
for key, value in file_values.items():
|
|
if key in FIELD_BY_KEY:
|
|
values[key] = value
|
|
sources[key] = source
|
|
|
|
for key in FIELD_BY_KEY:
|
|
if key in os.environ:
|
|
values[key] = os.environ[key]
|
|
sources[key] = "process"
|
|
|
|
return {
|
|
key: {
|
|
"value": values.get(key, ""),
|
|
"source": sources.get(key, "default"),
|
|
}
|
|
for key in FIELD_BY_KEY
|
|
}
|
|
|
|
|
|
def load_config_response() -> dict[str, Any]:
|
|
"""Return manifest and current config values for the admin UI."""
|
|
|
|
state = load_value_state()
|
|
fields: list[dict[str, Any]] = []
|
|
for field in FIELDS:
|
|
entry = state[field.key]
|
|
source = entry["source"]
|
|
raw_value = entry["value"]
|
|
fields.append(
|
|
{
|
|
"key": field.key,
|
|
"label": field.label,
|
|
"section": field.section_id,
|
|
"type": field.field_type,
|
|
"value": display_value(field, raw_value),
|
|
"configured": bool(str(raw_value).strip()),
|
|
"source": source,
|
|
"locked": is_locked_source(source),
|
|
"secret": field.secret,
|
|
"advanced": field.advanced,
|
|
"restart_required": field.restart_required,
|
|
"session_sensitive": field.session_sensitive,
|
|
"options": list(field.options),
|
|
"description": field.description,
|
|
}
|
|
)
|
|
|
|
return {
|
|
"sections": [
|
|
{
|
|
"id": section.section_id,
|
|
"label": section.label,
|
|
"description": section.description,
|
|
"advanced": section.advanced,
|
|
}
|
|
for section in SECTIONS
|
|
],
|
|
"fields": fields,
|
|
"paths": {
|
|
"managed": str(managed_env_path()),
|
|
"repo": str(repo_env_path()),
|
|
"explicit": str(explicit_env_path()) if explicit_env_path() else None,
|
|
},
|
|
"provider_status": provider_config_status(state),
|
|
}
|