mirror of
https://github.com/jhd3197/ServerKit.git
synced 2026-10-23 03:27:43 +09:00
Remove bare plan-section citations
Comments cited plan sections as "(§G3)" or "(§8 unification)", which point into the same git-ignored plans the plan-number citations did. References next to a real document (docs/REDESIGN_MAP.md §3) stay. Code and runtime strings are unchanged (AST and token comparison).
This commit is contained in:
1 parent
e94537d0a4
commit
ddaeb549ad
34 files changed
+53
-53
No files matched your search
@@ -281,7 +281,7 @@ def create_app(config_name=None):
|
||||
from app.services.dns_zone_service import DNSZoneService
|
||||
n_zones = DNSZoneService.link_legacy_zones()
|
||||
# Fold a legacy single-row email relay config into the unified
|
||||
# EmailProviderConnection table (§6); idempotent, best-effort.
|
||||
# EmailProviderConnection table; idempotent, best-effort.
|
||||
try:
|
||||
from app.services.email_relay_service import EmailRelayService
|
||||
EmailRelayService.migrate_legacy_config()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Bucket: PER-APP read. Per-app history gates via _access →
|
||||
# can_access_app. See docs/WORKSPACE_SCOPING.md.
|
||||
"""Unified deployment surface (§3 unification).
|
||||
"""Unified deployment surface.
|
||||
|
||||
One read namespace over the deploy records that today live in three places:
|
||||
versioned build ``Deployment`` rows, webhook-triggered ``GitDeployment`` rows,
|
||||
|
||||
@@ -33,7 +33,7 @@ def _get_snapshot_or_404(app_id, snap_id):
|
||||
|
||||
|
||||
# "Config Checkpoint" is the user-facing name for a deployment config snapshot
|
||||
# (§8 decision: rename in the UI; keep /snapshots working). Every route below is
|
||||
# (decision: rename in the UI; keep /snapshots working). Every route below is
|
||||
# also reachable under /config-checkpoints so the API matches the new wording.
|
||||
@snapshots_bp.route('/<int:app_id>/snapshots', methods=['GET'])
|
||||
@snapshots_bp.route('/<int:app_id>/config-checkpoints', methods=['GET'])
|
||||
|
||||
@@ -39,7 +39,7 @@ class AppSlot(db.Model):
|
||||
# What the app listens on inside the container; the same for both slots.
|
||||
container_port = db.Column(db.Integer, nullable=True)
|
||||
container_name = db.Column(db.String(128), nullable=True)
|
||||
# Compose slots (§C): the slot's own compose project.
|
||||
# Compose slots: the slot's own compose project.
|
||||
project_name = db.Column(db.String(128), nullable=True)
|
||||
# Immutable image this slot runs (serverkit-app-<id>:d<deployment> or a digest).
|
||||
image_ref = db.Column(db.String(255), nullable=True)
|
||||
|
||||
@@ -5,7 +5,7 @@ from app import db
|
||||
from app.models.mixins import TimestampMixin
|
||||
from app.models.json_column_mixin import JsonColumnMixin
|
||||
|
||||
# Every CORE data-protection target the unified policy system can back up (§8).
|
||||
# Every CORE data-protection target the unified policy system can back up.
|
||||
# 'application' is the original target; 'database', 'files', and 'server' were
|
||||
# folded in from the legacy BackupService so a single BackupPolicy/BackupRun
|
||||
# system covers all data-protection backups.
|
||||
|
||||
@@ -34,7 +34,7 @@ class DeploymentJob(RunLifecycleMixin, JsonColumnMixin, db.Model):
|
||||
requested_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True, index=True)
|
||||
trigger = db.Column(db.String(30), default='manual')
|
||||
|
||||
# Links to the release ledgers (§3 unification): the DeploymentJob is the
|
||||
# Links to the release ledgers: the DeploymentJob is the
|
||||
# canonical execution record; Deployment / GitDeployment remain the
|
||||
# version/release ledgers it produces. All nullable — a template-install job
|
||||
# has none of these set.
|
||||
|
||||
@@ -30,7 +30,7 @@ class EmailProviderConnection(TimestampMixin, JsonColumnMixin, db.Model):
|
||||
is_default = db.Column(db.Boolean, default=False, index=True)
|
||||
is_active = db.Column(db.Boolean, default=True)
|
||||
|
||||
# Usage flags (§6 unification): one connection can power the Notification Bus
|
||||
# Usage flags: one connection can power the Notification Bus
|
||||
# and/or the Postfix outbound relay. API providers (SendGrid/Postmark/SES/
|
||||
# Mailgun) can't be a Postfix smarthost, so only 'smtp' rows set uses_relay.
|
||||
uses_notifications = db.Column(db.Boolean, default=True, nullable=False)
|
||||
|
||||
@@ -77,7 +77,7 @@ class EmailProviderService:
|
||||
def default_provider(cls):
|
||||
"""The provider the Notification Bus should send through: the active
|
||||
default, else the first active one — both restricted to providers
|
||||
flagged ``uses_notifications`` so relay-only rows are never picked (§6)."""
|
||||
flagged ``uses_notifications`` so relay-only rows are never picked."""
|
||||
row = EmailProviderConnection.query.filter_by(
|
||||
is_default=True, is_active=True, uses_notifications=True).first()
|
||||
if row:
|
||||
@@ -120,7 +120,7 @@ class EmailProviderService:
|
||||
if make_default:
|
||||
EmailProviderConnection.query.update({EmailProviderConnection.is_default: False})
|
||||
|
||||
# Usage flags (§6). API providers cannot be a Postfix smarthost, so
|
||||
# Usage flags. API providers cannot be a Postfix smarthost, so
|
||||
# uses_relay is only honored for SMTP.
|
||||
uses_relay = bool(data.get('uses_relay')) and provider == 'smtp'
|
||||
row = EmailProviderConnection(
|
||||
@@ -142,7 +142,7 @@ class EmailProviderService:
|
||||
|
||||
@classmethod
|
||||
def update_usage(cls, provider_id, fields):
|
||||
"""Toggle a provider's usage flags (§6): uses_notifications, uses_relay,
|
||||
"""Toggle a provider's usage flags: uses_notifications, uses_relay,
|
||||
relay_priority. uses_relay is only honored for SMTP connections."""
|
||||
row = cls.get_provider(provider_id)
|
||||
if not row:
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"""Name an app's likely bottleneck from what the panel measures.
|
||||
|
||||
A rule-based doctor, no AI: it reads the app's request metrics (§A2), its
|
||||
A rule-based doctor, no AI: it reads the app's request metrics, its
|
||||
container's CPU, and, for a PostgreSQL / MySQL it reaches through
|
||||
``fromService``, that database's CPU and top queries (§A3). Each hint names
|
||||
``fromService``, that database's CPU and top queries. Each hint names
|
||||
the component that fits the signal, links to where it is set up, and states
|
||||
that component's failure mode. Nothing is ever added automatically.
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ class CronService:
|
||||
def _read_crontab(cls) -> Optional[str]:
|
||||
"""The current user's crontab, or ``None`` if it could not be read.
|
||||
|
||||
``None`` is "could not read", never "empty crontab" (§A). An empty
|
||||
``None`` is "could not read", never "empty crontab". An empty
|
||||
crontab is ``''`` and ``crontab -l`` exits non-zero for it on some
|
||||
implementations, so callers that install a replacement treat a failed
|
||||
read as "start from nothing" only where that was already the behaviour.
|
||||
|
||||
@@ -46,7 +46,7 @@ class DbAdminSsoService:
|
||||
# ── choke-point: ALL docker interaction goes through here (tests stub it) ──
|
||||
@classmethod
|
||||
def _docker(cls, args, timeout=60):
|
||||
"""Run ``docker <args>`` — via DockerService.run (§G3).
|
||||
"""Run ``docker <args>`` — via DockerService.run.
|
||||
|
||||
The named seam stays because this service's tests stub it; the wrapper
|
||||
body does not.
|
||||
|
||||
@@ -150,7 +150,7 @@ class DbConfigTunerService:
|
||||
|
||||
@classmethod
|
||||
def _docker(cls, args, timeout=60):
|
||||
"""Run a docker CLI command — via DockerService.run (§G3).
|
||||
"""Run a docker CLI command — via DockerService.run.
|
||||
|
||||
The named seam stays (tests stub it). The private copy collapsed a
|
||||
missing docker and a timed-out docker into the same ``str(e)``, so a
|
||||
@@ -161,7 +161,7 @@ class DbConfigTunerService:
|
||||
|
||||
@classmethod
|
||||
def _exec_sql(cls, target, sql):
|
||||
"""Run SQL against the target's engine — via ``db_exec`` (§G5).
|
||||
"""Run SQL against the target's engine — via ``db_exec``.
|
||||
|
||||
``target``: {'container', 'engine', 'user', 'password'}.
|
||||
Returns {'success', 'output', 'error'} with machine-readable output
|
||||
|
||||
@@ -29,7 +29,7 @@ from typing import Optional
|
||||
from app.services.database_service import DatabaseService
|
||||
|
||||
# The names the rest of the codebase spells these engines with. Kept here so an
|
||||
# unknown engine is *reported*, never silently routed to mysql (§A).
|
||||
# unknown engine is *reported*, never silently routed to mysql.
|
||||
MYSQL_ALIASES = ('mysql', 'mariadb')
|
||||
POSTGRES_ALIASES = ('postgresql', 'postgres', 'pgsql')
|
||||
|
||||
|
||||
@@ -7,28 +7,28 @@ uses, so a default changes in one place.
|
||||
import json
|
||||
|
||||
DEFAULTS = {
|
||||
# Health gate (§A3): how long a new release may take to answer, whether a
|
||||
# Health gate: how long a new release may take to answer, whether a
|
||||
# 4xx on the health path still counts as up, and how many answers in a row
|
||||
# it takes.
|
||||
'healthcheck_timeout': 120,
|
||||
'healthcheck_allow_4xx': False,
|
||||
'healthcheck_consecutive': 3,
|
||||
# How many deployments' images stay on disk for rollback (§A1).
|
||||
# How many deployments' images stay on disk for rollback.
|
||||
'keep_images': 3,
|
||||
# Slot deploys (§B): how long to watch a release after the switch, how
|
||||
# Slot deploys: how long to watch a release after the switch, how
|
||||
# long the old slot stays warm, and the operator's word that two copies
|
||||
# sharing the app's volumes for a moment is safe.
|
||||
'watch_seconds': 60,
|
||||
'standby_warm_minutes': 10,
|
||||
'slot_volumes_confirmed': False,
|
||||
# Release phase (§D): a command to run once before the switch (overrides
|
||||
# Release phase: a command to run once before the switch (overrides
|
||||
# the Procfile's release: line), whether to dump the app's databases
|
||||
# first, and the announced-downtime fallback for schema changes that
|
||||
# cannot stay backward-compatible for one deploy.
|
||||
'release_command': None,
|
||||
'snapshot_databases': True,
|
||||
'stop_old_before_release': False,
|
||||
# Compose slots (§C), written by the panel, never by the settings API:
|
||||
# Compose slots, written by the panel, never by the settings API:
|
||||
# the web service, the port it published before slots, and whether the
|
||||
# stateful services have moved to the shared data project.
|
||||
'compose_web': None,
|
||||
|
||||
@@ -142,7 +142,7 @@ class DeploymentService:
|
||||
if not pull_result.get('success'):
|
||||
return cls._fail(app, deployment, pull_result.get('error') or 'Git pull failed')
|
||||
# create_deployment read HEAD before this pull, so the row named
|
||||
# the commit that was live, not the one being deployed (§A2).
|
||||
# the commit that was live, not the one being deployed.
|
||||
cls._record_commit(app, deployment)
|
||||
|
||||
if cls._uses_compose(app):
|
||||
@@ -168,7 +168,7 @@ class DeploymentService:
|
||||
|
||||
deploy_stages.mark('Build')
|
||||
# An immutable tag per deployment: a rollback then redeploys
|
||||
# the image that actually ran, not whatever :latest became (§A1).
|
||||
# the image that actually ran, not whatever :latest became.
|
||||
from app.services.app_image_retention import deploy_image_tag
|
||||
build_result = BuildService.build(
|
||||
app_id,
|
||||
@@ -727,7 +727,7 @@ class DeploymentService:
|
||||
|
||||
# A compose app's code lives in its images: checking out the old
|
||||
# commit changed files and nothing else, so the "rollback"
|
||||
# reported success while the new release kept serving (§A6).
|
||||
# reported success while the new release kept serving.
|
||||
# Rebuild and recreate from the checked-out tree, validated first.
|
||||
if cls._uses_compose(app):
|
||||
from app.services import deploy_preflight_service as preflight
|
||||
|
||||
@@ -1040,7 +1040,7 @@ class DockerService:
|
||||
|
||||
@classmethod
|
||||
def run_compose(cls, cmd, *, cwd=None, timeout=None):
|
||||
"""Run an already-built compose argv; the shared result shape (§G1).
|
||||
"""Run an already-built compose argv; the shared result shape.
|
||||
|
||||
Separate from :meth:`run` because the argv head is whatever
|
||||
``_get_compose_cmd()`` resolved — ``docker compose`` (v2) or
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Outbound SMTP relay (smarthost) for the mail server.
|
||||
|
||||
The relay is now one row of the unified ``EmailProviderConnection`` table — an
|
||||
``smtp`` connection flagged ``uses_relay`` (§6 unification) — so a single store
|
||||
``smtp`` connection flagged ``uses_relay`` — so a single store
|
||||
drives both the Notification Bus and the Postfix relay. The legacy single-row
|
||||
``email_relay_config`` is migrated in on first boot and kept only as a read
|
||||
fallback during the deprecation window.
|
||||
|
||||
@@ -135,7 +135,7 @@ class FirewallService:
|
||||
# unprivileged LXC, where ufw exists and is on PATH but fails on
|
||||
# dropped CAP_NET_ADMIN) or an exception is "could not check": None,
|
||||
# never a fabricated inactive — the shape a past outage warned about,
|
||||
# verified live in the §D capability-restricted container.
|
||||
# verified live in a capability-restricted container.
|
||||
active = None if installed else False
|
||||
error = None
|
||||
if installed:
|
||||
|
||||
@@ -101,7 +101,7 @@ class GitService:
|
||||
def _git_out(app_path, *args, timeout=None):
|
||||
"""stdout of a read-only ``git -C <app_path> <args>``, or ``None``.
|
||||
|
||||
``None`` means the command did not succeed — not "empty output" (§A).
|
||||
``None`` means the command did not succeed — not "empty output".
|
||||
``get_commit_info`` alone had five copies of
|
||||
``subprocess.run(...); x = result.stdout.strip() if result.returncode
|
||||
== 0 else None``, which is this function written out by hand each time.
|
||||
|
||||
@@ -7,7 +7,7 @@ Phase-4 matrix, and it is deliberately narrow:
|
||||
- No free text ever reaches a shell. Every parameter is validated against a
|
||||
typed schema (enum / bounded int) before the executor runs.
|
||||
- Each template declares (id, label, param schema, executor, min_role). The
|
||||
min_role reuses the same capability tiers as the rest of the plan
|
||||
min_role reuses the panel's capability tiers
|
||||
(can_operate_app for 'member', can_admin_app for 'admin').
|
||||
- Every run is audit-logged.
|
||||
- The whole surface is feature-flagged (config MEMBER_ACTIONS_ENABLED).
|
||||
@@ -15,7 +15,7 @@ Phase-4 matrix, and it is deliberately narrow:
|
||||
The registry ships one concrete template today — `set-backup-frequency`, which
|
||||
adjusts an app's backup schedule through the existing BackupPolicyService (no new
|
||||
side-channel). The cron "ping this app's scheduler every N minutes" template
|
||||
named in the plan is deferred until its ScheduledJob wiring lands;
|
||||
is deferred until its ScheduledJob wiring lands;
|
||||
adding it is just another registry entry.
|
||||
"""
|
||||
from app.services.resource_grant_service import ResourceGrantService
|
||||
|
||||
@@ -990,7 +990,7 @@ location /p/ {{
|
||||
the cache file, so a fixed-string grep for ``KEY: http[s]://<host>/``
|
||||
finds exactly this site's files; the trailing ``/`` keeps
|
||||
``shop.example.com`` from matching ``shop.example.com.evil``. Entries
|
||||
cached under the pre-§B3 key (before the vhost was rewritten) aren't
|
||||
cached under an older key (before the vhost was rewritten) aren't
|
||||
found this way, and expire on their own TTL.
|
||||
|
||||
Without ``hosts`` the whole shared directory is wiped (every site).
|
||||
@@ -1118,7 +1118,7 @@ location /p/ {{
|
||||
def read_vhost(cls, name: str) -> Optional[str]:
|
||||
"""The vhost's current content, or ``None`` if it cannot be read.
|
||||
|
||||
``None`` means "could not determine", never "empty file" (§A). Callers
|
||||
``None`` means "could not determine", never "empty file". Callers
|
||||
that need to distinguish a missing vhost from an unreadable one check
|
||||
``os.path.exists`` themselves.
|
||||
"""
|
||||
@@ -1143,7 +1143,7 @@ location /p/ {{
|
||||
|
||||
@classmethod
|
||||
def write_vhost(cls, name: str, content: str, *, enable: bool = True) -> Dict:
|
||||
"""Write, enable, config-test, roll back on failure, reload. One door (§G4).
|
||||
"""Write, enable, config-test, roll back on failure, reload. One door.
|
||||
|
||||
``environment_domain_service`` re-implemented this whole sequence with
|
||||
raw ``sudo`` calls, and ``nginx_advanced_service`` wrote the file with a
|
||||
|
||||
@@ -272,7 +272,7 @@ class ProcessService:
|
||||
|
||||
@classmethod
|
||||
def get_systemd_unit_status(cls, unit_name: str) -> Dict:
|
||||
"""Check whether a systemd unit is active — via ``unit_is_active`` (§G6).
|
||||
"""Check whether a systemd unit is active — via ``unit_is_active``.
|
||||
|
||||
The probe itself was re-inlined in four other services; this keeps the
|
||||
dict shape its own callers read and lets one implementation answer.
|
||||
|
||||
@@ -83,7 +83,7 @@ def sanitize(text: str) -> str:
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Failure hints (D6 / §5.3): first match wins, evaluated against the failure tail
|
||||
# Failure hints: first match wins, evaluated against the failure tail
|
||||
# at close time. One sentence, action-first, never jokey.
|
||||
# ---------------------------------------------------------------------------
|
||||
HINTS: List = [
|
||||
|
||||
@@ -6,7 +6,7 @@ fails. This is the same idea for the apps ServerKit runs:
|
||||
|
||||
1. the new release boots in the IDLE slot, on its own loopback port, while
|
||||
the live slot keeps serving;
|
||||
2. it must pass the health gate there (§A3) — a release that never becomes
|
||||
2. it must pass the health gate there — a release that never becomes
|
||||
healthy is torn down and the site never noticed;
|
||||
3. the switch is ``app.port = <idle slot port>`` + ``write_app_vhost`` (which
|
||||
runs ``nginx -t`` and restores the old vhost on failure) — so every reader
|
||||
@@ -388,7 +388,7 @@ class SlotDeployService:
|
||||
reuse = (idle.image_ref == image and idle.state in ('standby', 'stopped')
|
||||
and idle.container_name and DockerService.get_container(idle.container_name))
|
||||
|
||||
# ---- snapshot + release (§D): once, before the switch, old slot live
|
||||
# ---- snapshot + release: once, before the switch, old slot live
|
||||
from app.services import release_phase_service as release
|
||||
stage('Snapshot')
|
||||
release.snapshot_databases(app, deployment, log)
|
||||
@@ -500,7 +500,7 @@ class SlotDeployService:
|
||||
return None
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# Deploy (compose, §C)
|
||||
# Deploy (compose)
|
||||
# ------------------------------------------------------------------ #
|
||||
@classmethod
|
||||
def deploy_compose(cls, app, deployment, log=None, stage=None, reuse_standby=False) -> Dict:
|
||||
@@ -643,7 +643,7 @@ class SlotDeployService:
|
||||
return cls.deploy_compose(app, rollback_deployment, log=log)
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# The one-time data split (§C)
|
||||
# The one-time data split
|
||||
# ------------------------------------------------------------------ #
|
||||
@classmethod
|
||||
def compose_split_preview(cls, app) -> Dict:
|
||||
|
||||
@@ -660,8 +660,8 @@ class TemplateService:
|
||||
# SERVICE_FQDN_<NAME> -> the app's auto-assigned hostname (best-effort)
|
||||
# SERVICE_URL_<NAME> -> full URL derived from the FQDN (+ scheme)
|
||||
# SERVICE_BASE64_<NAME> -> base64 of a freshly generated secret
|
||||
# SERVERKIT_METRICS_TOKEN -> the panel's Prometheus scrape token (plan
|
||||
# 86 §A4), so a monitoring template can scrape
|
||||
# SERVERKIT_METRICS_TOKEN -> the panel's Prometheus scrape token, so a
|
||||
# monitoring template can scrape
|
||||
# /api/v1/fleet-monitor/prometheus
|
||||
#
|
||||
# Resolution is PURE and unit-testable: no Docker, no network. The only
|
||||
@@ -2470,7 +2470,7 @@ class TemplateService:
|
||||
|
||||
if not compose_result.get('success'):
|
||||
# Rollback — and say whether it worked: an unchecked `up` here
|
||||
# reported a plain failure while the app sat stopped (§A6).
|
||||
# reported a plain failure while the app sat stopped.
|
||||
if os.path.exists(backup_path):
|
||||
shutil.copy(backup_path, compose_path)
|
||||
restored = DockerService.compose_up(app_path, detach=True)
|
||||
|
||||
@@ -386,7 +386,7 @@ def write_privileged_file(path: str, content: str, *, append: bool = False,
|
||||
|
||||
``success`` is never True on a write that did not demonstrably happen — a
|
||||
``FileNotFoundError`` (no ``tee``) or a timeout reports the failure instead
|
||||
of being swallowed into a cheerful return (§A, probe honesty).
|
||||
of being swallowed into a cheerful return (probe honesty).
|
||||
|
||||
*mode* and *owner* are applied after the write (``chmod`` / ``chown``); a
|
||||
failure there is reported, because a config written with the wrong
|
||||
@@ -437,7 +437,7 @@ _COMMAND_SEARCH_DIRS = ('/usr/local/sbin', '/usr/local/bin', '/usr/sbin',
|
||||
|
||||
|
||||
def unit_is_active(unit: str, *, timeout: int = 10) -> Optional[bool]:
|
||||
"""Tri-state systemd unit probe: ``True``, ``False``, or ``None`` (§G6).
|
||||
"""Tri-state systemd unit probe: ``True``, ``False``, or ``None``.
|
||||
|
||||
``None`` means *could not determine* — no systemctl, no systemd, a probe
|
||||
that timed out — and is deliberately not ``False``. The five hand-rolled
|
||||
|
||||
@@ -39,7 +39,7 @@ import {
|
||||
routesForPlacement,
|
||||
} from './routes/routeManifest';
|
||||
|
||||
// /apps/* is the legacy URL space for what is now "Services" (§1 unification).
|
||||
// /apps/* is the legacy URL space for what is now "Services".
|
||||
// The list route already redirects; this preserves deep links to a single app
|
||||
// (and its active tab) by forwarding to the matching /services/* path.
|
||||
function LegacyAppRedirect() {
|
||||
|
||||
@@ -18,7 +18,7 @@ import GunicornTab from '../components/service-detail/GunicornTab';
|
||||
import CommandsTab from '../components/service-detail/CommandsTab';
|
||||
import OverviewTab from '../components/service-detail/OverviewTab';
|
||||
// Container Ops / WAF / Build / Deploy (merged in from the retired
|
||||
// ApplicationDetail page in §1) now live under the Settings sub-nav, so they're
|
||||
// ApplicationDetail page) now live under the Settings sub-nav, so they're
|
||||
// imported by SettingsTab rather than rendered as top-level tabs here.
|
||||
import PreviewList from '../components/previews/PreviewList';
|
||||
import EmptyState from '../components/EmptyState';
|
||||
|
||||
@@ -87,7 +87,7 @@ const Terminal = () => {
|
||||
|
||||
// ─── Interactive shell tab (demo console: target rail + terminal pane) ───
|
||||
// Sessions run over the ServerKit agent (terminal:create); the panel host has
|
||||
// no PTY endpoint (§5 gap), so the rail lists paired agent servers only.
|
||||
// no PTY endpoint, so the rail lists paired agent servers only.
|
||||
const TerminalShellTab = () => {
|
||||
const { t } = useTranslation();
|
||||
const [servers, setServers] = useState([]);
|
||||
|
||||
@@ -11,7 +11,7 @@ export async function getApps(options = {}) {
|
||||
}
|
||||
|
||||
// "Services" is the user-facing term for what the backend calls Applications
|
||||
// (§1 unification). These aliases let new code speak "services" while the
|
||||
//. These aliases let new code speak "services" while the
|
||||
// API client and backend model keep the canonical `apps`/`Application` names.
|
||||
export async function getAppServices(options = {}) {
|
||||
return getApps.call(this, options);
|
||||
|
||||
@@ -58,7 +58,7 @@ export async function deleteBackupRun(targetType, targetId, runId) {
|
||||
return this.request(`${runsBase(targetType, targetId)}/${runId}`, { method: 'DELETE' });
|
||||
}
|
||||
|
||||
// ---- fleet-wide policy + run views (§8 unification) -----------------------
|
||||
// ---- fleet-wide policy + run views -----------------------
|
||||
// The per-target methods above answer "how is THIS site protected?"; these two
|
||||
// answer "how is everything protected?" and back the Backups overview — the
|
||||
// protected-resource count, the activity heatmap, and the success rate all come
|
||||
|
||||
@@ -133,7 +133,7 @@ export async function installCertbot() {
|
||||
return this.request('/ssl/install-certbot', { method: 'POST' });
|
||||
}
|
||||
|
||||
// Advanced SSL endpoints — unified under the one /ssl surface (§5). The old
|
||||
// Advanced SSL endpoints — unified under the one /ssl surface. The old
|
||||
// /ssl/advanced/* paths still resolve (deprecated alias).
|
||||
export async function getSSLProfiles() {
|
||||
return this.request('/ssl/profiles');
|
||||
|
||||
@@ -21,7 +21,7 @@ export async function restoreSnapshot(appId, snapId) {
|
||||
}
|
||||
|
||||
// "Config Checkpoint" is the user-facing name for a deployment config snapshot
|
||||
// (§8). These aliases let new UI code use the new term; the backend serves both
|
||||
//. These aliases let new UI code use the new term; the backend serves both
|
||||
// /snapshots and /config-checkpoints.
|
||||
export async function getConfigCheckpoints(appId, limit = 50) {
|
||||
return getAppSnapshots.call(this, appId, limit);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// cyan #49c7f0 / violet #b07bf5, neutral gray #646b7a.
|
||||
const SERVICE_TYPES = {
|
||||
// Tab order note: ops/waf/build/deploy were merged in from the retired
|
||||
// ApplicationDetail page (§1 feature unification), then relocated into the
|
||||
// ApplicationDetail page, then relocated into the
|
||||
// Settings sub-nav to declutter the top strip — Container Ops + Git & Deploy
|
||||
// + Build live under Settings, WAF under Settings → Security. Type-gating of
|
||||
// those sections is handled in SettingsTab (ops = Docker only; waf = Docker +
|
||||
|
||||
Reference in new issue
Block a user