diff --git a/deploy/toggle_bioflow_176.sh b/deploy/toggle_bioflow_176.sh new file mode 100755 index 0000000..6904981 --- /dev/null +++ b/deploy/toggle_bioflow_176.sh @@ -0,0 +1,51 @@ +#!/bin/bash +# deploy/toggle_bioflow_176.sh —— 176 生信模块快速开关(off/on) +# 用法(在 176 上执行,或从 175 ssh 过去执行): +# bash /srv/plantai/toggle_bioflow_176.sh off # 关生信:env=false + 重启 BFF → 生信 TAB/入口消失 +# bash /srv/plantai/toggle_bioflow_176.sh on # 开生信:env=true + 重启 BFF → 生信 TAB 恢复 +# 原理:BFF main.py 按 BIOFLOW_ENABLED 决定是否注册 bioflow 反代路由; +# 不注册时 /api/bioflow/health 返回 404,前端 probe 落 offline,生信 TAB 自动隐藏。 +set -uo pipefail + +ACTION="${1:-}" +ENV_FILE="/srv/plantai/install/sdk/.env" +PY="/srv/plantai/install/venv/bin/python" + +if [ "$ACTION" != "off" ] && [ "$ACTION" != "on" ]; then + echo "用法: $0 " >&2 + exit 2 +fi + +VAL="true" +[ "$ACTION" = "off" ] && VAL="false" + +# 1. 改 env +if grep -q "^BIOFLOW_ENABLED=" "$ENV_FILE"; then + sed -i "s/^BIOFLOW_ENABLED=.*/BIOFLOW_ENABLED=$VAL/" "$ENV_FILE" +else + echo "BIOFLOW_ENABLED=$VAL" >> "$ENV_FILE" +fi +echo "BIOFLOW_ENABLED -> $VAL" + +# 2. 重启 BFF 8090(仅取 LISTEN 者;venv 必须绝对路径,cd sdk 后才能 import app.main) +kill $(lsof -ti tcp:8090 -sTCP:LISTEN) 2>/dev/null +sleep 2 +cd /srv/plantai/install && set -a && . sdk/.env && set +a +( cd sdk && setsid "$PY" -m uvicorn app.main:app --host 127.0.0.1 --port 8090 \ + > /tmp/plantai-sdk.log 2>&1 < /dev/null & ) && disown -a + +# 3. 自检 +for i in $(seq 1 20); do + sleep 2 + curl -s -m 3 http://127.0.0.1:8090/api/health >/dev/null 2>&1 && break +done +curl -s -m 10 http://127.0.0.1:8090/api/health | head -c 150 +echo +if [ "$ACTION" = "off" ]; then + CODE=$(curl -s -o /dev/null -w "%{http_code}" -m 8 http://127.0.0.1:8090/api/bioflow/health) + echo "bioflow/health HTTP $CODE (off 期望 404)" + [ "$CODE" = "404" ] && echo "OFF_OK" || echo "OFF_CHECK_FAIL" +else + curl -s -m 8 http://127.0.0.1:8090/api/bioflow/health | head -c 150 + echo +fi diff --git a/ui/src/components/layout/TopBar.vue b/ui/src/components/layout/TopBar.vue index bbffc03..afdb3aa 100644 --- a/ui/src/components/layout/TopBar.vue +++ b/ui/src/components/layout/TopBar.vue @@ -13,6 +13,7 @@ import { computed, onMounted, ref } from 'vue'; import { APP_NAME, STATUS_LABEL, T } from '../../i18n/labels.js'; import { BF } from '../../i18n/bioflow.js'; import { useThemeStore } from '../../stores/theme.js'; +import { useBioflowStore } from '../../stores/bioflow.js'; const props = defineProps({ /** 当前会话标题(无会话为 null → 显示「就绪」徽章) */ @@ -34,6 +35,11 @@ const emit = defineEmits(['menu', 'bench', 'close-bench', 'goto-extensions', 'go /* ---------- 主题 ---------- */ const themeStore = useThemeStore(); +/* ---------- 生信可用性(右上角生信跳转按钮显隐;=== 'online' 避免 unknown 闪现) ---------- */ +const bioflowStore = useBioflowStore(); +const bioflowOnline = computed(() => bioflowStore.availability === 'online'); +const bioflowStatus = computed(() => bioflowStore.availability); + /* ---------- B2/B3 store 数据(动态导入 + 空值守卫) ---------- */ const chatStore = ref(null); const benchStore = ref(null); @@ -134,6 +140,12 @@ function onMenu() { function onBench() { emit('bench'); } +/** 生信跳转(2026-09-16):开新窗口访问 PlantAI-dev(9017);主机名跟随当前访问地址自适应 */ +function openBioflowDev() { + const devUrl = window.location.protocol + '//' + window.location.hostname + ':9017/'; + window.open(devUrl, '_blank', 'noopener'); +} + function onToggleTheme() { themeStore.toggle(); } @@ -147,6 +159,7 @@ const iconClose = ' + {{ BF.extRailTitle }} + + + diff --git a/ui/src/styles/base.css b/ui/src/styles/base.css index 5ae0937..2aee2db 100644 --- a/ui/src/styles/base.css +++ b/ui/src/styles/base.css @@ -119,8 +119,18 @@ html.drawer-open { overflow: hidden; } } .icon-btn svg { width: 16px; height: 16px; } .icon-btn:disabled { opacity: .45; cursor: not-allowed; } -.top-rail-btn { margin-left: 2px; } +.top-rail-btn { + margin-left: 2px; + width: auto; + padding: 0 10px; + gap: 6px; +} .top-rail-btn > span { display: flex; align-items: center; justify-content: center; } +.top-rail-btn .rail-label { + font-size: 12px; + font-weight: 500; + white-space: nowrap; +} /* 状态徽章(pill,三编码:颜色+图标+文字,UI_SPEC §4.1 / PRD §2.1) */ .badge {