add config-driven channel label and dev-workstation bootstrap
A bleeding-edge container running next to a stable one on the same dev box is easy to confuse, and a branch-only badge would force perpetual rebase/cherry-pick drift between main and dev-be. Instead the badge is config-driven: the settings endpoint injects a synthetic env_label from SHOPDB_ENV_LABEL, the sidebar renders a red DEV tag only when it is non-empty, and stable simply leaves it unset. Same code on both channels, no divergence. dev-bootstrap.ps1 makes a fresh Windows box turnkey: it checks git/Docker (winget-installs with a prompt), verifies the engine is actually running, then builds and starts an isolated per-channel stack so stable and edge coexist on distinct ports, projects, and volumes. docker-compose.channel.yml is a thin overlay so the base compose file stays channel-agnostic.
This commit is contained in:
@@ -64,6 +64,40 @@ PUBLIC_SETTING_KEYS = {
|
||||
'site_timezone',
|
||||
}
|
||||
|
||||
# Environment channel label. Sourced from the SHOPDB_ENV_LABEL environment
|
||||
# variable (set by the edge docker-compose override and the bleeding-edge
|
||||
# installer, unset on stable/main), NOT from a settings row. It is injected as a
|
||||
# synthetic public item into the settings list so the frontend picks it up
|
||||
# through the same read-through it already uses for branding, with no extra
|
||||
# request. Empty when unset, which is how a stable deployment renders no DEV
|
||||
# badge. Public and read-only: it carries no secret and cannot be edited via the
|
||||
# settings API, only by redeploying with a different environment value.
|
||||
ENV_LABEL_KEY = 'env_label'
|
||||
|
||||
|
||||
def _env_label() -> str:
|
||||
"""The configured environment channel label (e.g. 'DEV'), or '' when unset."""
|
||||
return (os.environ.get('SHOPDB_ENV_LABEL') or '').strip()
|
||||
|
||||
|
||||
def _env_label_item() -> dict:
|
||||
"""Synthetic, serialized settings item carrying the environment label.
|
||||
|
||||
Shaped like a real serialized Setting so the frontend read-through treats it
|
||||
as just another public key. settingid is None because it is not a DB row.
|
||||
"""
|
||||
return {
|
||||
'settingid': None,
|
||||
'key': ENV_LABEL_KEY,
|
||||
'value': _env_label(),
|
||||
'valuetype': 'string',
|
||||
'category': 'branding',
|
||||
'description': 'Environment channel label (from SHOPDB_ENV_LABEL); '
|
||||
'drives the DEV badge in the sidebar. Read-only.',
|
||||
'createddate': None,
|
||||
'modifieddate': None,
|
||||
}
|
||||
|
||||
|
||||
def _plugin_declared_settings() -> dict:
|
||||
"""Declared settings ({key: entry}) of every loaded plugin, or {}."""
|
||||
@@ -576,7 +610,19 @@ def list_settings():
|
||||
# bootstrap keys); an authed principal sees everything (secrets masked).
|
||||
if get_jwt_identity() is None:
|
||||
settings = [s for s in settings if _is_public_setting(s)]
|
||||
return success_response([_serialize_setting(s) for s in settings])
|
||||
items = [_serialize_setting(s) for s in settings]
|
||||
|
||||
# Inject the environment channel label as a synthetic public item so the
|
||||
# frontend read-through surfaces it alongside branding. It is env-sourced,
|
||||
# not a DB row, and is always present (empty string when unset) so the
|
||||
# public-settings contract can rely on the key existing. Drop any DB row that
|
||||
# somehow shares the key so the env value is authoritative and appears once.
|
||||
# Skip it when a category filter is active and does not select branding, so a
|
||||
# scoped query is not polluted.
|
||||
if not category or category == 'branding':
|
||||
items = [i for i in items if i.get('key') != ENV_LABEL_KEY]
|
||||
items.append(_env_label_item())
|
||||
return success_response(items)
|
||||
|
||||
|
||||
@settings_bp.route('/<key>', methods=['GET'])
|
||||
|
||||
Reference in New Issue
Block a user