fix: hide raw ops terms on governance pages
This commit is contained in:
@@ -402,7 +402,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
|
||||
# ==========================================
|
||||
# 系統版本與路徑
|
||||
# ==========================================
|
||||
SYSTEM_VERSION = "V10.701"
|
||||
SYSTEM_VERSION = "V10.702"
|
||||
LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log')
|
||||
public_url = PUBLIC_URL # 用於模板顯示
|
||||
|
||||
|
||||
@@ -778,3 +778,4 @@ POSTGRES_HOST=momo-db
|
||||
| 2026-06-25 | 工具頁與簡報頁也必須使用作戰語言;Google Drive token 必須固定到持久化掛載 | V10.699 起簡報預覽、品牌素材、比價、匯入、缺貨與觀測台操作提示移除英文/內部流程字,改成可直接理解的狀態與下一步;正式容器明確指定 `/app/config/google_token.json` 與 `/app/config/google_credentials.json`,背景匯入不得因主機重啟或工作目錄變動而改找瀏覽器授權。 |
|
||||
| 2026-06-26 | 邊角治理頁不得把 raw caller、模型資料或推版代碼當主訊息 | V10.700 起 AI 流量、主機健康、通知模板與缺貨匯入提示再收斂為「使用情境、建議路徑、服務資料、更新摘要、先選擇供應商缺貨檔」等營運語言,避免低頻治理頁回流 `原始`、模型品牌、commit/pipeline 欄位或泛用檔案提示。 |
|
||||
| 2026-06-26 | 靜態資源部署必須保持容器可讀 | V10.701 起部署 SOP 改用 Git 物件打包差異檔,並以測試檢查 `web/static` 檔案可被容器讀取,避免 macOS / iCloud 工作目錄權限造成正式 `/static/*` 500,讓全站 CSS/JS 不再因檔案模式回歸而失效。 |
|
||||
| 2026-06-26 | 治理頁也要用營運情境語,不得渲染 raw caller、服務代碼或錯誤日誌 | V10.702 起 AI 流量頁把 caller/provider/上下文改成「使用情境、建議路徑、作戰素材」;服務更新監控頁隱藏 branch/sha/error log/pod 名稱等工程細節,改用「更新流程、服務元件、診斷線索」語言;缺貨頁移除英文 Vendor Stockout。 |
|
||||
|
||||
@@ -119,16 +119,36 @@
|
||||
'gcp_ollama': '主力建議路徑',
|
||||
'ollama_secondary': '備援建議路徑',
|
||||
'ollama_111': '第三建議路徑',
|
||||
'nim_via_elephant': 'NIM Elephant',
|
||||
'nim_via_elephant': '雲端加速備援',
|
||||
'gemini': '雲端備援',
|
||||
'claude': '雲端審查備援',
|
||||
'nim': 'NIM',
|
||||
'openrouter': 'OpenRouter',
|
||||
'nim': '快速雲端建議',
|
||||
'openrouter': '外部備援路徑',
|
||||
'unknown': '未分類供應商'
|
||||
} -%}
|
||||
{%- if value -%}{{ labels.get(value, value|replace('_', ' ')) }}{%- else -%}{{ fallback }}{%- endif -%}
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro caller(value, fallback='營運建議流程') -%}
|
||||
{%- set labels = {
|
||||
'hermes_analyst': '業績分析建議',
|
||||
'nemotron_dispatch': '深度策略判斷',
|
||||
'openclaw_strategist': '作戰策略建議',
|
||||
'openclaw': '作戰策略建議',
|
||||
'code_review_openclaw': '程式品質審查',
|
||||
'pchome_growth': 'PChome 成長判斷',
|
||||
'pchome_growth_dashboard': 'PChome 成長儀表板',
|
||||
'pchome_price_intelligence': 'PChome 價格情報',
|
||||
'price_recommendation': '價格建議',
|
||||
'sales_analysis': '業績分析',
|
||||
'monthly_summary': '月度結構分析',
|
||||
'ai_recommend': '銷售建議',
|
||||
'telegram_bot': '通知助理',
|
||||
'scheduler': '排程任務'
|
||||
} -%}
|
||||
{%- if value -%}{{ labels.get(value, fallback) }}{%- else -%}{{ fallback }}{%- endif -%}
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro source(value, fallback='未分類來源') -%}
|
||||
{%- set labels = {
|
||||
'ai_insights': 'AI 知識庫',
|
||||
|
||||
@@ -55,10 +55,10 @@
|
||||
<h1 class="calls-title">AI 流量控制塔</h1>
|
||||
<p class="calls-subtitle">看成本、錯誤率與知識命中,確保 AI 建議穩定支援業績判斷。</p>
|
||||
<div class="calls-actions">
|
||||
<button class="btn btn-warning btn-sm" onclick="triggerCodeReview()"><i class="fas fa-microscope me-1"></i>觸發程式碼審查管線</button>
|
||||
<button class="btn btn-warning btn-sm" onclick="triggerCodeReview()"><i class="fas fa-microscope me-1"></i>啟動品質審查</button>
|
||||
<form method="get" class="calls-filter">
|
||||
<select name="hours" class="form-select form-select-sm">{% for h in [1, 6, 24, 72, 168] %}<option value="{{ h }}" {% if hours == h %}selected{% endif %}>{% if h < 24 %}{{ h }} 小時{% else %}{{ (h//24) }} 天{% endif %}</option>{% endfor %}</select>
|
||||
<select name="caller" class="form-select form-select-sm"><option value="">全部呼叫端</option>{% for c in callers %}<option value="{{ c }}" {% if caller_filter == c %}selected{% endif %}>{{ c }}</option>{% endfor %}</select>
|
||||
<select name="caller" class="form-select form-select-sm"><option value="">全部使用情境</option>{% for c in callers %}<option value="{{ c }}" {% if caller_filter == c %}selected{% endif %}>{{ obs_label.caller(c) }}</option>{% endfor %}</select>
|
||||
<select name="provider" class="form-select form-select-sm"><option value="">全部供應商</option>{% for p in ['gcp_ollama','ollama_secondary','ollama_111','gemini','claude','nim','openrouter','nim_via_elephant'] %}<option value="{{ p }}" {% if provider_filter == p %}selected{% endif %}>{{ obs_label.provider(p) }}</option>{% endfor %}</select>
|
||||
<button class="btn btn-primary btn-sm">套用</button>
|
||||
</form>
|
||||
@@ -87,8 +87,8 @@
|
||||
|
||||
{% if caller_richness %}
|
||||
<article class="calls-table-shell">
|
||||
<div class="calls-table-title"><div><div class="calls-label">呼叫端編排</div><h3>呼叫端 × 知識與工具編排矩陣</h3></div></div>
|
||||
<div class="table-responsive"><table class="table table-sm mb-0"><thead class="table-light"><tr><th>呼叫端</th><th class="text-end">總呼叫</th><th class="text-end">知識命中</th><th class="text-end">工具編排</th><th class="text-end">知識反饋</th><th class="text-end">筆數</th></tr></thead><tbody>{% for c in caller_richness %}<tr><td><code>{{ c.caller }}</code></td><td class="text-end">{{ "{:,}".format(c.total_calls) }}</td><td class="text-end"><strong class="{% if c.rag_hit_rate >= 50 %}status-good{% elif c.rag_hit_rate >= 20 %}status-warn{% else %}text-muted{% endif %}">{{ "%.1f"|format(c.rag_hit_rate) }}%</strong> <small class="text-muted">({{ c.rag_hits }})</small></td><td class="text-end"><strong class="{% if c.mcp_rate >= 30 %}status-blue{% elif c.mcp_rate >= 10 %}status-warn{% endif %}">{{ "%.1f"|format(c.mcp_rate) }}%</strong></td><td class="text-end">{% if c.feedback_count > 0 %}<strong class="{% if c.avg_rag_feedback >= 4 %}status-good{% elif c.avg_rag_feedback >= 3 %}status-warn{% else %}status-bad{% endif %}">{{ "%.2f"|format(c.avg_rag_feedback) }}/5</strong>{% else %}<small class="text-muted">—</small>{% endif %}</td><td class="text-end">{{ c.feedback_count }}</td></tr>{% endfor %}</tbody></table></div>
|
||||
<div class="calls-table-title"><div><div class="calls-label">使用情境</div><h3>情境 × 知識命中矩陣</h3></div></div>
|
||||
<div class="table-responsive"><table class="table table-sm mb-0"><thead class="table-light"><tr><th>使用情境</th><th class="text-end">總次數</th><th class="text-end">知識命中</th><th class="text-end">工具協作</th><th class="text-end">品質回饋</th><th class="text-end">筆數</th></tr></thead><tbody>{% for c in caller_richness %}<tr><td><span>{{ obs_label.caller(c.caller) }}</span></td><td class="text-end">{{ "{:,}".format(c.total_calls) }}</td><td class="text-end"><strong class="{% if c.rag_hit_rate >= 50 %}status-good{% elif c.rag_hit_rate >= 20 %}status-warn{% else %}text-muted{% endif %}">{{ "%.1f"|format(c.rag_hit_rate) }}%</strong> <small class="text-muted">({{ c.rag_hits }})</small></td><td class="text-end"><strong class="{% if c.mcp_rate >= 30 %}status-blue{% elif c.mcp_rate >= 10 %}status-warn{% endif %}">{{ "%.1f"|format(c.mcp_rate) }}%</strong></td><td class="text-end">{% if c.feedback_count > 0 %}<strong class="{% if c.avg_rag_feedback >= 4 %}status-good{% elif c.avg_rag_feedback >= 3 %}status-warn{% else %}status-bad{% endif %}">{{ "%.2f"|format(c.avg_rag_feedback) }}/5</strong>{% else %}<small class="text-muted">—</small>{% endif %}</td><td class="text-end">{{ c.feedback_count }}</td></tr>{% endfor %}</tbody></table></div>
|
||||
</article>
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -107,9 +107,9 @@
|
||||
|
||||
{% if recent_contexts %}
|
||||
<article class="calls-panel">
|
||||
<div class="calls-panel-head"><div><div class="calls-label">AI 上下文</div><h2 class="calls-panel-title">最近上下文</h2></div></div>
|
||||
<div class="calls-panel-head"><div><div class="calls-label">建議素材</div><h2 class="calls-panel-title">最近作戰素材</h2></div></div>
|
||||
<div class="calls-panel-body">
|
||||
{% for c in recent_contexts[:5] %}<div class="mb-2 pb-2 border-bottom"><span class="badge bg-info">{{ c.agent_name }}</span> <code>{{ c.context_key }}</code><div class="text-muted small mt-1">{{ c.preview }}{% if c.preview|length >= 120 %}…{% endif %}</div></div>{% endfor %}
|
||||
{% for c in recent_contexts[:5] %}<div class="mb-2 pb-2 border-bottom"><span class="badge bg-info">{{ obs_label.caller(c.agent_name) }}</span><div class="text-muted small mt-1">已納入近期業績判斷素材</div></div>{% endfor %}
|
||||
</div>
|
||||
</article>
|
||||
{% endif %}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% extends "ewoooc_base.html" %}
|
||||
|
||||
{% block title %}部署監控 - EwoooC{% endblock %}
|
||||
{% block title %}服務更新監控 - EwoooC{% endblock %}
|
||||
|
||||
{% block extra_head %}
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css" rel="stylesheet">
|
||||
@@ -80,7 +80,7 @@
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
/* 部署流程 */
|
||||
/* 服務更新流程 */
|
||||
.pipeline-flow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -200,7 +200,7 @@
|
||||
.pod-status .pod-ready.healthy { background: rgba(40, 167, 69, 0.3); }
|
||||
.pod-status .pod-ready.unhealthy { background: rgba(220, 53, 69, 0.3); }
|
||||
|
||||
/* 部署歷史 */
|
||||
/* 更新歷史 */
|
||||
.pipeline-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -473,8 +473,8 @@
|
||||
<div class="container">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h1><i class="bi bi-rocket-takeoff me-2"></i>部署監控</h1>
|
||||
<p class="mb-0 opacity-75">PChome 業績成長自動化作戰系統 · 持續整合與部署監控</p>
|
||||
<h1><i class="bi bi-rocket-takeoff me-2"></i>服務更新監控</h1>
|
||||
<p class="mb-0 opacity-75">PChome 業績成長自動化作戰系統 · 正式服務更新與可用性監控</p>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="realtime-badge">
|
||||
@@ -524,11 +524,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 部署流程 -->
|
||||
<!-- 服務更新流程 -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<span><i class="bi bi-diagram-3 me-2"></i>最新部署流程</span>
|
||||
<span id="pipelineId" class="badge bg-secondary">#--</span>
|
||||
<span><i class="bi bi-diagram-3 me-2"></i>最新更新流程</span>
|
||||
<span id="pipelineId" class="badge bg-secondary">待確認</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="pipeline-flow" id="pipelineFlow">
|
||||
@@ -564,12 +564,12 @@
|
||||
<div class="row g-3">
|
||||
<div class="col-6">
|
||||
<button class="action-btn primary w-100" onclick="triggerDeploy('uat')">
|
||||
<i class="bi bi-cloud-upload"></i>部署 UAT
|
||||
<i class="bi bi-cloud-upload"></i>更新測試站
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<button class="action-btn primary w-100" onclick="triggerDeploy('prod')">
|
||||
<i class="bi bi-cloud-upload"></i>部署 PROD
|
||||
<i class="bi bi-cloud-upload"></i>更新正式站
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
@@ -589,7 +589,7 @@
|
||||
<div class="row g-3">
|
||||
<div class="col-12">
|
||||
<a href="http://192.168.0.110:8929/root/momo-pro-system/-/pipelines" target="_blank" class="btn btn-outline-light w-100">
|
||||
<i class="bi bi-box-arrow-up-right me-2"></i>開啟 GitLab 部署紀錄
|
||||
<i class="bi bi-box-arrow-up-right me-2"></i>開啟更新紀錄
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
@@ -617,7 +617,7 @@
|
||||
<i class="bi bi-search me-1"></i>診斷
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-info" onclick="triggerFullRepair()">
|
||||
<i class="bi bi-wrench me-1"></i>修復部署
|
||||
<i class="bi bi-wrench me-1"></i>修復服務
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -625,12 +625,12 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 部署歷史 -->
|
||||
<!-- 更新歷史 -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<span><i class="bi bi-clock-history me-2"></i>部署歷史</span>
|
||||
<span><i class="bi bi-clock-history me-2"></i>更新歷史</span>
|
||||
<a href="http://192.168.0.110:8929/root/momo-pro-system/-/pipelines" target="_blank" class="btn btn-sm btn-outline-light">
|
||||
查看部署紀錄
|
||||
查看更新紀錄
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-body" id="pipelineHistory">
|
||||
@@ -687,7 +687,7 @@
|
||||
updateLastUpdate();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load dashboard:', error);
|
||||
console.error('服務更新監控讀取失敗:', error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -718,10 +718,10 @@
|
||||
<div class="issue-detail">
|
||||
${issue.type === 'job' ? `<span class="badge bg-secondary me-1">${displayStageName(issue.stage)}</span>` : ''}
|
||||
${issue.type === 'runtime' ? `<span class="badge bg-info me-1">${issue.environment?.toUpperCase()}</span>` : ''}
|
||||
${issue.error ? `<br><span class="text-muted">${escapeHtml(issue.error.substring(0, 120))}</span>` : ''}
|
||||
${issue.error ? `<br><span class="text-muted">已記錄診斷線索</span>` : ''}
|
||||
</div>
|
||||
${issue.fix_suggestion ? `<div class="issue-suggestion">💡 ${escapeHtml(issue.fix_suggestion)}</div>` : ''}
|
||||
${issue.error_log ? `<div class="error-log-preview">${escapeHtml(issue.error_log.substring(0, 300))}</div>` : ''}
|
||||
${issue.error_log ? `<div class="error-log-preview">已保留診斷資料,請由維護者查看。</div>` : ''}
|
||||
</div>
|
||||
<div class="issue-actions">
|
||||
${issue.auto_fixable ? `
|
||||
@@ -768,7 +768,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
pipelineIdEl.textContent = `#${latestPipeline.id}`;
|
||||
pipelineIdEl.textContent = '最近一次';
|
||||
|
||||
// 如果已有 jobs 數據,直接使用
|
||||
if (latestJobs && latestJobs.length > 0) {
|
||||
@@ -940,8 +940,8 @@
|
||||
|
||||
${!env.healthy && env.error ? `
|
||||
<div class="env-error">
|
||||
<div class="env-error-title">❌ 連線錯誤</div>
|
||||
<div class="env-error-detail">${escapeHtml(env.error)}</div>
|
||||
<div class="env-error-title">❌ 服務連線需確認</div>
|
||||
<div class="env-error-detail">已記錄診斷線索,請先確認健康檢查與服務狀態。</div>
|
||||
<button class="btn-fix mt-2" onclick="triggerAutoFix('diagnose', '${envId}')">
|
||||
<i class="bi bi-search me-1"></i>診斷服務
|
||||
</button>
|
||||
@@ -949,7 +949,7 @@
|
||||
` : ''}
|
||||
|
||||
<div class="env-details mt-2">
|
||||
<strong class="d-block mb-2">執行環境狀態:</strong>
|
||||
<strong class="d-block mb-2">服務元件狀態:</strong>
|
||||
${renderPods(env.pods, envId)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -963,14 +963,14 @@
|
||||
// 渲染 runtime 狀態
|
||||
function renderPods(pods, envId) {
|
||||
if (!pods || pods.length === 0) {
|
||||
return '<p class="text-muted small mb-0">Docker Compose 執行環境;外部叢集資訊不適用</p>';
|
||||
return '<p class="text-muted small mb-0">正式服務由主機容器管理,暫無元件明細。</p>';
|
||||
}
|
||||
|
||||
return pods.map(pod => `
|
||||
<div class="pod-status">
|
||||
<span class="status-indicator ${pod.healthy ? 'healthy' : 'unhealthy'}"></span>
|
||||
<span class="pod-name">${pod.name}</span>
|
||||
<span class="pod-ready ${pod.healthy ? 'healthy' : 'unhealthy'}">${pod.ready}</span>
|
||||
<span class="pod-name">${displayServiceName(pod.name)}</span>
|
||||
<span class="pod-ready ${pod.healthy ? 'healthy' : 'unhealthy'}">${pod.healthy ? '正常' : '需確認'}</span>
|
||||
<span class="text-muted small ms-2">${pod.age}</span>
|
||||
${pod.restarts > 0 ? `<span class="badge bg-warning ms-2">${pod.restarts} 重啟</span>` : ''}
|
||||
${!pod.healthy ? `<span class="badge bg-danger ms-2">${pod.status}</span>` : ''}
|
||||
@@ -978,12 +978,12 @@
|
||||
`).join('');
|
||||
}
|
||||
|
||||
// 更新部署歷史
|
||||
// 更新歷史
|
||||
function updatePipelineHistory(pipelines) {
|
||||
const container = document.getElementById('pipelineHistory');
|
||||
|
||||
if (!pipelines || pipelines.length === 0) {
|
||||
container.innerHTML = '<p class="text-center text-muted">暫無部署流程紀錄</p>';
|
||||
container.innerHTML = '<p class="text-center text-muted">暫無更新紀錄</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -992,9 +992,9 @@
|
||||
html += `
|
||||
<div class="pipeline-item">
|
||||
<span class="pipeline-status">${p.status_icon}</span>
|
||||
<span class="pipeline-id">#${p.id}</span>
|
||||
<span class="pipeline-id">更新</span>
|
||||
<div class="pipeline-info">
|
||||
<div class="pipeline-commit">${p.ref} @ ${p.sha}</div>
|
||||
<div class="pipeline-commit">${displayRefName(p.ref)}</div>
|
||||
<div class="pipeline-time">${formatTime(p.created_at)} ${p.duration ? `• ${p.duration}` : ''}</div>
|
||||
</div>
|
||||
<a href="${p.web_url}" target="_blank" class="btn btn-sm btn-outline-secondary">
|
||||
@@ -1014,9 +1014,9 @@
|
||||
el.textContent = `最後更新: ${now.toLocaleTimeString('zh-TW')}`;
|
||||
}
|
||||
|
||||
// 觸發部署
|
||||
// 觸發服務更新
|
||||
async function triggerDeploy(env) {
|
||||
if (!confirm(`確定要觸發 ${env.toUpperCase()} 部署嗎?`)) return;
|
||||
if (!confirm(`確定要更新${displayEnvName(env)}嗎?`)) return;
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/cicd/deploy', {
|
||||
@@ -1032,13 +1032,13 @@
|
||||
setTimeout(loadDashboard, 2000);
|
||||
}
|
||||
} catch (error) {
|
||||
showNotification('錯誤', '無法觸發部署');
|
||||
showNotification('錯誤', '無法啟動服務更新');
|
||||
}
|
||||
}
|
||||
|
||||
// 觸發回滾
|
||||
async function triggerRollback(env) {
|
||||
if (!confirm(`確定要回滾 ${env.toUpperCase()} 嗎?這將恢復到上一個版本。`)) return;
|
||||
if (!confirm(`確定要回復${displayEnvName(env)}到上一個版本嗎?`)) return;
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/cicd/rollback', {
|
||||
@@ -1074,9 +1074,9 @@
|
||||
|
||||
// 自動修復
|
||||
async function triggerAutoFix(action, env) {
|
||||
if (!confirm(`確定要執行自動修復 (${action}) 嗎?`)) return;
|
||||
if (!confirm(`確定要執行服務修復嗎?`)) return;
|
||||
|
||||
showNotification('執行中', `正在執行 ${action}...`);
|
||||
showNotification('執行中', '正在執行服務修復...');
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/cicd/auto-fix', {
|
||||
@@ -1094,11 +1094,11 @@
|
||||
showNotification('❌ 修復失敗', data.error, true);
|
||||
}
|
||||
} catch (error) {
|
||||
showNotification('❌ 錯誤', '無法執行自動修復: ' + error, true);
|
||||
showNotification('❌ 錯誤', '無法執行自動修復', true);
|
||||
}
|
||||
}
|
||||
|
||||
// 部署修復
|
||||
// 服務修復
|
||||
async function triggerFullRepair() {
|
||||
const env = prompt('請輸入要修復的環境 (uat 或 prod):', 'uat');
|
||||
if (!env || !['uat', 'prod'].includes(env)) {
|
||||
@@ -1106,9 +1106,9 @@
|
||||
return;
|
||||
}
|
||||
|
||||
if (!confirm(`確定要對 ${env.toUpperCase()} 執行部署修復嗎?\n這會執行必要服務修復與環境診斷。`)) return;
|
||||
if (!confirm(`確定要對${displayEnvName(env)}執行服務修復嗎?\n這會執行必要服務修復與環境診斷。`)) return;
|
||||
|
||||
showNotification('執行中', '正在修復部署狀態...');
|
||||
showNotification('執行中', '正在修復服務狀態...');
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/cicd/auto-fix', {
|
||||
@@ -1120,13 +1120,13 @@
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
showNotification('✅ 部署修復完成', `已執行 ${data.results?.length || 0} 個修復動作`);
|
||||
showNotification('✅ 服務修復完成', `已執行 ${data.results?.length || 0} 個修復動作`);
|
||||
setTimeout(loadDashboard, 5000);
|
||||
} else {
|
||||
showNotification('❌ 修復失敗', data.error, true);
|
||||
}
|
||||
} catch (error) {
|
||||
showNotification('❌ 錯誤', '無法執行部署修復: ' + error, true);
|
||||
showNotification('❌ 錯誤', '無法執行服務修復', true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1138,7 +1138,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
showNotification('執行中', `正在診斷 ${env.toUpperCase()} 環境...`);
|
||||
showNotification('執行中', `正在診斷${displayEnvName(env)}...`);
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/cicd/diagnose', {
|
||||
@@ -1171,7 +1171,7 @@
|
||||
showNotification('❌ 診斷失敗', data.error, true);
|
||||
}
|
||||
} catch (error) {
|
||||
showNotification('❌ 錯誤', '無法執行診斷: ' + error, true);
|
||||
showNotification('❌ 錯誤', '無法執行診斷', true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1194,6 +1194,26 @@
|
||||
return labels[stage] || stage || '未分類';
|
||||
}
|
||||
|
||||
function displayEnvName(env) {
|
||||
const labels = { uat: '測試站', prod: '正式站' };
|
||||
return labels[env] || '指定環境';
|
||||
}
|
||||
|
||||
function displayRefName(ref) {
|
||||
const labels = { main: '正式版更新', master: '正式版更新', dev: '測試版更新', uat: '測試版更新', prod: '正式版更新' };
|
||||
return labels[ref] || '服務更新';
|
||||
}
|
||||
|
||||
function displayServiceName(name) {
|
||||
const labels = {
|
||||
'momo-pro-system': '主系統',
|
||||
'momo-scheduler': '排程與匯入',
|
||||
'momo-telegram-bot': '通知服務',
|
||||
'momo-db': '資料服務'
|
||||
};
|
||||
return labels[name] || '服務元件';
|
||||
}
|
||||
|
||||
function displayStatusText(status) {
|
||||
const labels = {
|
||||
ok: '正常',
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<section class="vendor-tools-page" data-vendor-tool="history">
|
||||
<header class="vendor-tools-header">
|
||||
<div>
|
||||
<span class="vendor-tools-kicker"><i class="fas fa-boxes-stacked"></i> Vendor Stockout</span>
|
||||
<span class="vendor-tools-kicker"><i class="fas fa-boxes-stacked"></i>供貨風險</span>
|
||||
<h1>發送歷史</h1>
|
||||
<p>回看缺貨處理紀錄,找出需要補救的供貨風險。</p>
|
||||
</div>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<div>
|
||||
<div class="vendor-eyebrow momo-mono">
|
||||
<i class="fas fa-box-open"></i>
|
||||
VENDOR STOCKOUT
|
||||
供貨風險
|
||||
</div>
|
||||
<h1 class="vendor-title">廠商缺貨</h1>
|
||||
<p class="vendor-subtitle">
|
||||
|
||||
@@ -80,8 +80,8 @@ def test_high_visibility_pages_use_traditional_chinese_labels():
|
||||
assert "工作隊列" in combined
|
||||
assert "覆蓋率流程" in combined
|
||||
assert "同步部署" in combined
|
||||
assert "部署監控" in combined
|
||||
assert "最新部署流程" in combined
|
||||
assert "服務更新監控" in combined
|
||||
assert "最新更新流程" in combined
|
||||
assert "執行環境正常" in combined
|
||||
assert "視覺檢查" in combined
|
||||
|
||||
@@ -617,10 +617,27 @@ def test_utility_pages_keep_operator_copy_professional():
|
||||
auto_import = (ROOT / "templates/auto_import_index.html").read_text(encoding="utf-8")
|
||||
stockout_import = (ROOT / "templates/vendor_stockout_import_v2.html").read_text(encoding="utf-8")
|
||||
vendor_import_js = (ROOT / "web/static/js/page-vendor-import.js").read_text(encoding="utf-8")
|
||||
stockout_index = (ROOT / "templates/vendor_stockout_index_v2.html").read_text(encoding="utf-8")
|
||||
stockout_history = (ROOT / "templates/vendor_stockout_history_v2.html").read_text(encoding="utf-8")
|
||||
ai_calls = (ROOT / "templates/admin/ai_calls_dashboard.html").read_text(encoding="utf-8")
|
||||
observability_labels = (ROOT / "templates/admin/_observability_labels.html").read_text(encoding="utf-8")
|
||||
host_health = (ROOT / "templates/admin/host_health.html").read_text(encoding="utf-8")
|
||||
cicd_dashboard = (ROOT / "templates/cicd_dashboard.html").read_text(encoding="utf-8")
|
||||
observability_js = (ROOT / "web/static/js/observability-charts.js").read_text(encoding="utf-8")
|
||||
combined = "\n".join([ppt_history, ppt_preview, auto_import, stockout_import, vendor_import_js, ai_calls, host_health, observability_js])
|
||||
combined = "\n".join([
|
||||
ppt_history,
|
||||
ppt_preview,
|
||||
auto_import,
|
||||
stockout_import,
|
||||
vendor_import_js,
|
||||
stockout_index,
|
||||
stockout_history,
|
||||
ai_calls,
|
||||
observability_labels,
|
||||
host_health,
|
||||
cicd_dashboard,
|
||||
observability_js,
|
||||
])
|
||||
|
||||
assert "簡報線上預覽" in ppt_preview
|
||||
assert "下載簡報檔" in ppt_history
|
||||
@@ -628,6 +645,11 @@ def test_utility_pages_keep_operator_copy_professional():
|
||||
assert "缺少必要資料時,會先停止匯入" in stockout_import
|
||||
assert "先選擇供應商缺貨 Excel 檔。" in vendor_import_js
|
||||
assert "使用情境" in ai_calls
|
||||
assert "全部使用情境" in ai_calls
|
||||
assert "情境 × 知識命中矩陣" in ai_calls
|
||||
assert "服務更新監控" in cicd_dashboard
|
||||
assert "正式服務更新與可用性監控" in cicd_dashboard
|
||||
assert "供貨風險" in stockout_index
|
||||
assert "無服務資料 / 未連線" in host_health
|
||||
assert "部署檢查已排入背景處理" in observability_js
|
||||
|
||||
@@ -638,6 +660,17 @@ def test_utility_pages_keep_operator_copy_professional():
|
||||
"原始:",
|
||||
"無模型資料",
|
||||
"請選擇 Excel 檔案",
|
||||
"Vendor Stockout",
|
||||
"VENDOR STOCKOUT",
|
||||
"全部呼叫端",
|
||||
"呼叫端 ×",
|
||||
"<code>{{ c.caller }}</code>",
|
||||
"NIM Elephant",
|
||||
"OpenRouter",
|
||||
"GitLab 部署紀錄",
|
||||
"${p.ref} @ ${p.sha}",
|
||||
"error_log.substring",
|
||||
"${escapeHtml(env.error)}",
|
||||
"系統會去重",
|
||||
"系統會拒絕",
|
||||
"管線 ID",
|
||||
|
||||
@@ -905,9 +905,9 @@ def test_visible_operations_pages_hide_internal_runtime_terms():
|
||||
"templates/vendor_stockout_import_v2.html": ["會先停止匯入", "處理缺貨清單"],
|
||||
"templates/admin/ppt_audit_history.html": ["產出紀錄", "最近產出", "保存紀錄"],
|
||||
"templates/admin/agent_orchestration.html": ["AI 分工指揮台", "建議路徑、工具與知識命中矩陣", "工具服務明細"],
|
||||
"templates/admin/ai_calls_dashboard.html": ["用量", "AI 上下文", "知識與工具編排矩陣"],
|
||||
"templates/admin/ai_calls_dashboard.html": ["用量", "最近作戰素材", "情境 × 知識命中矩陣"],
|
||||
"templates/admin/observability_overview.html": ["用量", "知識與工具矩陣"],
|
||||
"templates/cicd_dashboard.html": ["部署流程", "部署歷史", "修復部署", "查看部署紀錄"],
|
||||
"templates/cicd_dashboard.html": ["最新更新流程", "更新歷史", "修復服務", "查看更新紀錄"],
|
||||
}
|
||||
forbidden_by_path = {
|
||||
"templates/ai_recommend.html": ["權杖:", "AI 模型", "分析模型", "AI 路徑", "Gemini 備援", "Ollama 主路徑"],
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
ollama_111: '第三建議路徑',
|
||||
gemini: '雲端備援',
|
||||
claude: 'Claude',
|
||||
nim: 'NIM',
|
||||
openrouter: 'OpenRouter',
|
||||
nim_via_elephant: 'NIM Elephant'
|
||||
nim: '快速雲端建議',
|
||||
openrouter: '外部備援路徑',
|
||||
nim_via_elephant: '雲端加速備援'
|
||||
};
|
||||
|
||||
function readJson(id, fallback) {
|
||||
|
||||
Reference in New Issue
Block a user