mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 17:30:23 +08:00
Merge pull request #128 from AAEE86/master
fix(frontend): 修复端点配置对话框中多个 Select 下拉框同时打开的冲突问题
This commit is contained in:
@@ -24,10 +24,12 @@ COPY --from=builder /usr/local/bin/uvicorn /usr/local/bin/
|
|||||||
COPY --from=builder /usr/local/bin/alembic /usr/local/bin/
|
COPY --from=builder /usr/local/bin/alembic /usr/local/bin/
|
||||||
# 从 builder 阶段复制前端构建产物
|
# 从 builder 阶段复制前端构建产物
|
||||||
COPY --from=builder /app/frontend/dist /usr/share/nginx/html
|
COPY --from=builder /app/frontend/dist /usr/share/nginx/html
|
||||||
|
RUN chmod -R 755 /usr/share/nginx/html
|
||||||
# 复制后端代码
|
# 复制后端代码
|
||||||
COPY src/ ./src/
|
COPY src/ ./src/
|
||||||
COPY alembic.ini ./
|
COPY alembic.ini ./
|
||||||
COPY alembic/ ./alembic/
|
COPY alembic/ ./alembic/
|
||||||
|
COPY gunicorn_conf.py ./
|
||||||
# Nginx 配置模板
|
# Nginx 配置模板
|
||||||
# 智能处理 IP:有外层代理头就透传,没有就用直连 IP
|
# 智能处理 IP:有外层代理头就透传,没有就用直连 IP
|
||||||
RUN printf '%s\n' \
|
RUN printf '%s\n' \
|
||||||
@@ -123,7 +125,7 @@ RUN printf '%s\n' \
|
|||||||
'stderr_logfile=/var/log/nginx/error.log' \
|
'stderr_logfile=/var/log/nginx/error.log' \
|
||||||
'' \
|
'' \
|
||||||
'[program:app]' \
|
'[program:app]' \
|
||||||
'command=gunicorn src.main:app --preload -w %(ENV_GUNICORN_WORKERS)s -k uvicorn.workers.UvicornWorker --bind 127.0.0.1:8084 --timeout 120 --access-logfile - --error-logfile - --log-level info' \
|
'command=gunicorn src.main:app -c gunicorn_conf.py --preload -w %(ENV_GUNICORN_WORKERS)s -k uvicorn.workers.UvicornWorker --bind 127.0.0.1:8084 --timeout 120 --max-requests 2000 --max-requests-jitter 100 --access-logfile - --error-logfile - --log-level info' \
|
||||||
'directory=/app' \
|
'directory=/app' \
|
||||||
'autostart=true' \
|
'autostart=true' \
|
||||||
'autorestart=true' \
|
'autorestart=true' \
|
||||||
|
|||||||
@@ -33,11 +33,13 @@ COPY --from=builder /usr/local/bin/alembic /usr/local/bin/
|
|||||||
|
|
||||||
# 从 builder 阶段复制前端构建产物
|
# 从 builder 阶段复制前端构建产物
|
||||||
COPY --from=builder /app/frontend/dist /usr/share/nginx/html
|
COPY --from=builder /app/frontend/dist /usr/share/nginx/html
|
||||||
|
RUN chmod -R 755 /usr/share/nginx/html
|
||||||
|
|
||||||
# 复制后端代码
|
# 复制后端代码
|
||||||
COPY src/ ./src/
|
COPY src/ ./src/
|
||||||
COPY alembic.ini ./
|
COPY alembic.ini ./
|
||||||
COPY alembic/ ./alembic/
|
COPY alembic/ ./alembic/
|
||||||
|
COPY gunicorn_conf.py ./
|
||||||
|
|
||||||
# Nginx 配置模板
|
# Nginx 配置模板
|
||||||
# 智能处理 IP:有外层代理头就透传,没有就用直连 IP
|
# 智能处理 IP:有外层代理头就透传,没有就用直连 IP
|
||||||
@@ -126,7 +128,7 @@ RUN printf '%s\n' \
|
|||||||
'stderr_logfile=/var/log/nginx/error.log' \
|
'stderr_logfile=/var/log/nginx/error.log' \
|
||||||
'' \
|
'' \
|
||||||
'[program:app]' \
|
'[program:app]' \
|
||||||
'command=gunicorn src.main:app --preload -w %(ENV_GUNICORN_WORKERS)s -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:%(ENV_PORT)s --timeout 120 --access-logfile - --error-logfile - --log-level info' \
|
'command=gunicorn src.main:app -c gunicorn_conf.py --preload -w %(ENV_GUNICORN_WORKERS)s -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:%(ENV_PORT)s --timeout 120 --max-requests 2000 --max-requests-jitter 100 --access-logfile - --error-logfile - --log-level info' \
|
||||||
'directory=/app' \
|
'directory=/app' \
|
||||||
'autostart=true' \
|
'autostart=true' \
|
||||||
'autorestart=true' \
|
'autorestart=true' \
|
||||||
|
|||||||
@@ -54,9 +54,6 @@ services:
|
|||||||
GUNICORN_WORKERS: ${GUNICORN_WORKERS:-4}
|
GUNICORN_WORKERS: ${GUNICORN_WORKERS:-4}
|
||||||
# 容器级别设置
|
# 容器级别设置
|
||||||
TZ: Asia/Shanghai
|
TZ: Asia/Shanghai
|
||||||
PYTHONIOENCODING: utf-8
|
|
||||||
LANG: C.UTF-8
|
|
||||||
LC_ALL: C.UTF-8
|
|
||||||
depends_on:
|
depends_on:
|
||||||
postgres:
|
postgres:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|||||||
@@ -45,9 +45,6 @@ services:
|
|||||||
GUNICORN_WORKERS: ${GUNICORN_WORKERS:-4}
|
GUNICORN_WORKERS: ${GUNICORN_WORKERS:-4}
|
||||||
# 容器级别设置
|
# 容器级别设置
|
||||||
TZ: Asia/Shanghai
|
TZ: Asia/Shanghai
|
||||||
PYTHONIOENCODING: utf-8
|
|
||||||
LANG: C.UTF-8
|
|
||||||
LC_ALL: C.UTF-8
|
|
||||||
depends_on:
|
depends_on:
|
||||||
postgres:
|
postgres:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|||||||
@@ -4,12 +4,12 @@
|
|||||||
:title="isEditMode ? '编辑模型' : '创建统一模型'"
|
:title="isEditMode ? '编辑模型' : '创建统一模型'"
|
||||||
:description="isEditMode ? '修改模型配置和价格信息' : ''"
|
:description="isEditMode ? '修改模型配置和价格信息' : ''"
|
||||||
:icon="isEditMode ? SquarePen : Layers"
|
:icon="isEditMode ? SquarePen : Layers"
|
||||||
size="3xl"
|
size="4xl"
|
||||||
@update:model-value="handleDialogUpdate"
|
@update:model-value="handleDialogUpdate"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="flex gap-4"
|
class="flex gap-4"
|
||||||
:class="isEditMode ? '' : 'h-[500px]'"
|
:class="isEditMode ? '' : 'h-[600px]'"
|
||||||
>
|
>
|
||||||
<!-- 左侧:模型选择(仅创建模式) -->
|
<!-- 左侧:模型选择(仅创建模式) -->
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -180,7 +180,9 @@
|
|||||||
>
|
>
|
||||||
<Select
|
<Select
|
||||||
:model-value="rule.action"
|
:model-value="rule.action"
|
||||||
|
:open="ruleSelectOpen[`${endpoint.id}-${index}`]"
|
||||||
@update:model-value="(v) => updateEndpointRuleAction(endpoint.id, index, v as 'set' | 'drop' | 'rename')"
|
@update:model-value="(v) => updateEndpointRuleAction(endpoint.id, index, v as 'set' | 'drop' | 'rename')"
|
||||||
|
@update:open="(v) => handleRuleSelectOpen(endpoint.id, index, v)"
|
||||||
>
|
>
|
||||||
<SelectTrigger class="w-24 h-8 text-xs shrink-0">
|
<SelectTrigger class="w-24 h-8 text-xs shrink-0">
|
||||||
<SelectValue />
|
<SelectValue />
|
||||||
@@ -301,7 +303,8 @@
|
|||||||
<Label class="text-xs text-muted-foreground">API 格式</Label>
|
<Label class="text-xs text-muted-foreground">API 格式</Label>
|
||||||
<Select
|
<Select
|
||||||
v-model="newEndpoint.api_format"
|
v-model="newEndpoint.api_format"
|
||||||
v-model:open="formatSelectOpen"
|
:open="formatSelectOpen"
|
||||||
|
@update:open="handleFormatSelectOpen"
|
||||||
>
|
>
|
||||||
<SelectTrigger class="h-8">
|
<SelectTrigger class="h-8">
|
||||||
<SelectValue placeholder="选择格式" />
|
<SelectValue placeholder="选择格式" />
|
||||||
@@ -439,6 +442,32 @@ const emit = defineEmits<{
|
|||||||
|
|
||||||
const { success, error: showError } = useToast()
|
const { success, error: showError } = useToast()
|
||||||
|
|
||||||
|
// 规则 Select 的展开状态(与 Collapsible 分开管理)
|
||||||
|
const ruleSelectOpen = ref<Record<string, boolean>>({})
|
||||||
|
|
||||||
|
// 打开规则选择器时关闭其他所有下拉
|
||||||
|
function handleRuleSelectOpen(endpointId: string, index: number, open: boolean) {
|
||||||
|
if (open) {
|
||||||
|
formatSelectOpen.value = false
|
||||||
|
// 关闭其他 Select
|
||||||
|
Object.keys(ruleSelectOpen.value).forEach(key => {
|
||||||
|
ruleSelectOpen.value[key] = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ruleSelectOpen.value[`${endpointId}-${index}`] = open
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开格式选择器时关闭其他所有下拉
|
||||||
|
function handleFormatSelectOpen(open: boolean) {
|
||||||
|
if (open) {
|
||||||
|
// 关闭所有规则 Select
|
||||||
|
Object.keys(ruleSelectOpen.value).forEach(key => {
|
||||||
|
ruleSelectOpen.value[key] = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
formatSelectOpen.value = open
|
||||||
|
}
|
||||||
|
|
||||||
// 状态
|
// 状态
|
||||||
const addingEndpoint = ref(false)
|
const addingEndpoint = ref(false)
|
||||||
const savingEndpointId = ref<string | null>(null)
|
const savingEndpointId = ref<string | null>(null)
|
||||||
|
|||||||
20
gunicorn_conf.py
Normal file
20
gunicorn_conf.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# Gunicorn configuration file
|
||||||
|
|
||||||
|
import gc
|
||||||
|
|
||||||
|
def when_ready(server):
|
||||||
|
"""
|
||||||
|
Called just after the server is started.
|
||||||
|
Freeze GC before forking workers to optimize Copy-on-Write memory sharing.
|
||||||
|
"""
|
||||||
|
gc.freeze()
|
||||||
|
server.log.info("GC frozen for Copy-on-Write optimization")
|
||||||
|
server.log.info(f"Objects in permanent generation: {gc.get_freeze_count()}")
|
||||||
|
|
||||||
|
def post_fork(server, worker):
|
||||||
|
try:
|
||||||
|
import resource
|
||||||
|
rss = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
|
||||||
|
server.log.info(f"Worker {worker.pid} RSS after fork: {rss} KB")
|
||||||
|
except ImportError:
|
||||||
|
pass # Windows 不支持 resource 模块
|
||||||
Reference in New Issue
Block a user