This commit is contained in:
hrz
2025-02-15 23:22:46 +08:00
13 changed files with 93 additions and 33 deletions
+36 -10
View File
@@ -1,12 +1,31 @@
# 第一阶段:构建依赖 # 第一阶段:前端构建
FROM node:18 as frontend-builder
WORKDIR /app/ZhiKongTaiWeb
COPY ZhiKongTaiWeb/package*.json ./
RUN npm install
COPY ZhiKongTaiWeb .
RUN npm run build
# 第二阶段:构建Python依赖
FROM python:3.10-slim as builder FROM python:3.10-slim as builder
WORKDIR /app WORKDIR /app
COPY requirements.txt . COPY requirements.txt .
# 安装构建依赖 # 使用清华源加速apt安装
RUN apt-get update && \ RUN rm -rf /etc/apt/sources.list.d/* && \
echo "deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm main contrib non-free non-free-firmware" > /etc/apt/sources.list && \
echo "deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm-updates main contrib non-free non-free-firmware" >> /etc/apt/sources.list && \
echo "deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm-backports main contrib non-free non-free-firmware" >> /etc/apt/sources.list && \
echo "deb https://mirrors.tuna.tsinghua.edu.cn/debian-security bookworm-security main contrib non-free non-free-firmware" >> /etc/apt/sources.list && \
apt-get clean && \
apt-get update && \
apt-get install -y --no-install-recommends \ apt-get install -y --no-install-recommends \
gcc \ gcc \
libopus-dev \ libopus-dev \
@@ -16,24 +35,31 @@ RUN apt-get update && \
# 安装Python依赖到虚拟环境 # 安装Python依赖到虚拟环境
RUN python -m venv /opt/venv RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH" ENV PATH="/opt/venv/bin:$PATH"
RUN pip install --no-cache-dir -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/ RUN pip install --no-cache-dir -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
# 第阶段:生产镜像 # 第阶段:生产镜像
FROM python:3.10-slim FROM python:3.10-slim
WORKDIR /opt/xiaozhi-esp32-server WORKDIR /opt/xiaozhi-esp32-server
# 从构建阶段复制虚拟环境 # 使用清华源加速apt安装
COPY --from=builder /opt/venv /opt/venv RUN rm -rf /etc/apt/sources.list.d/* && \
echo "deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm main contrib non-free non-free-firmware" > /etc/apt/sources.list && \
# 安装运行时依赖 echo "deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm-updates main contrib non-free non-free-firmware" >> /etc/apt/sources.list && \
RUN apt-get update && \ echo "deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm-backports main contrib non-free non-free-firmware" >> /etc/apt/sources.list && \
echo "deb https://mirrors.tuna.tsinghua.edu.cn/debian-security bookworm-security main contrib non-free non-free-firmware" >> /etc/apt/sources.list && \
apt-get clean && \
apt-get update && \
apt-get install -y --no-install-recommends \ apt-get install -y --no-install-recommends \
libopus0 \ libopus0 \
ffmpeg && \ ffmpeg && \
apt-get clean && \ apt-get clean && \
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
# 从构建阶段复制虚拟环境和前端构建产物
COPY --from=builder /opt/venv /opt/venv
COPY --from=frontend-builder /app/ZhiKongTaiWeb/dist /opt/xiaozhi-esp32-server/manager/static
# 设置虚拟环境路径 # 设置虚拟环境路径
ENV PATH="/opt/venv/bin:$PATH" ENV PATH="/opt/venv/bin:$PATH"
+1 -1
View File
@@ -1 +1 @@
VITE_API_BASE_URL=http://localhost:8002 VITE_API_BASE_URL=''
+1 -1
View File
@@ -1 +1 @@
VITE_API_BASE_URL=http://localhost:8002 VITE_API_BASE_URL='' # 空字符串表示使用当前域名和端口
+1 -1
View File
@@ -24,7 +24,7 @@
import Login from './components/Login.vue'; import Login from './components/Login.vue';
import Registration from './components/Registration.vue'; import Registration from './components/Registration.vue';
import panel from './components/panel.vue'; import panel from './components/panel.vue';
import MainPage from './components/main.vue'; import MainPage from './components/Main.vue';
import RoleSetting from './components/RoleSetting.vue'; import RoleSetting from './components/RoleSetting.vue';
import Footer from './components/Footer.vue'; import Footer from './components/Footer.vue';
+3 -1
View File
@@ -17,6 +17,8 @@
</template> </template>
<script> <script>
import { API_BASE_URL } from '../config/api';
export default { export default {
data() { data() {
return { return {
@@ -34,7 +36,7 @@
this.isLoading = true; this.isLoading = true;
try { try {
const response = await fetch(`${import.meta.env.VITE_API_BASE_URL}/api/login`, { const response = await fetch(`${API_BASE_URL}/api/login`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
@@ -24,6 +24,8 @@
</template> </template>
<script> <script>
import { API_BASE_URL } from '../config/api';
export default { export default {
data() { data() {
return { return {
@@ -47,7 +49,7 @@ export default {
this.isLoading = true; this.isLoading = true;
try { try {
const response = await fetch(`${import.meta.env.VITE_API_BASE_URL}/api/register`, { const response = await fetch(`${API_BASE_URL}/api/register`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
+3 -2
View File
@@ -51,7 +51,7 @@
</option> </option>
</select> </select>
<div class="model-description"> <div class="model-description">
除了qwen-turbo其他模型通常会增加约 1 秒的延迟改变模型后建议清空记忆体以免影响体验 除了"qwen-turbo"其他模型通常会增加约 1 秒的延迟改变模型后建议清空记忆体以免影响体验
</div> </div>
</div> </div>
</div> </div>
@@ -109,6 +109,7 @@
import { ref, onMounted } from 'vue'; import { ref, onMounted } from 'vue';
import NavBar from './NavBar.vue'; import NavBar from './NavBar.vue';
import RoleTemplates from '../utils/RoleTemplates'; import RoleTemplates from '../utils/RoleTemplates';
import { API_BASE_URL } from '../config/api';
const roleTemplates = RoleTemplates.getTemplates(); const roleTemplates = RoleTemplates.getTemplates();
@@ -120,7 +121,7 @@ const activeMemoryTab = ref('recent');
const memoryContent = ref(''); const memoryContent = ref('');
const selectedModel = ref('qianwen'); const selectedModel = ref('qianwen');
const baseUrl = import.meta.env.VITE_API_BASE_URL; const baseUrl = API_BASE_URL;
const moduleOptions = ref({ const moduleOptions = ref({
LLM: [], LLM: [],
TTS: [], TTS: [],
+2 -1
View File
@@ -41,10 +41,11 @@
import { ref, onMounted } from 'vue'; import { ref, onMounted } from 'vue';
import NavBar from './NavBar.vue'; import NavBar from './NavBar.vue';
import DeviceCard from './DeviceCard.vue'; import DeviceCard from './DeviceCard.vue';
import { API_BASE_URL } from '../config/api';
const emit = defineEmits(['show-role', 'go-home']); const emit = defineEmits(['show-role', 'go-home']);
const baseUrl = import.meta.env.VITE_API_BASE_URL; const baseUrl = API_BASE_URL;
const devices = ref([]); const devices = ref([]);
const formatLastActivity = (timestamp) => { const formatLastActivity = (timestamp) => {
+16
View File
@@ -0,0 +1,16 @@
// 获取当前运行环境的基础 URL
const getBaseUrl = () => {
// 如果是开发环境,使用环境变量中的地址
if (import.meta.env.DEV) {
return import.meta.env.VITE_API_BASE_URL;
}
// 生产环境使用当前域名和端口
const protocol = window.location.protocol;
const hostname = window.location.hostname;
const port = window.location.port;
return `${protocol}//${hostname}${port ? `:${port}` : ''}`;
};
export const API_BASE_URL = getBaseUrl();
+20
View File
@@ -3,6 +3,9 @@ from config.logger import setup_logging
from config.settings import load_config from config.settings import load_config
from core.websocket_server import WebSocketServer from core.websocket_server import WebSocketServer
from manager.http_server import ConfigServer from manager.http_server import ConfigServer
from core.webui import WebUI
from aiohttp import web
from core.utils.util import get_local_ip
async def main(): async def main():
setup_logging() # 最先初始化日志 setup_logging() # 最先初始化日志
@@ -21,6 +24,20 @@ async def main():
except Exception as e: except Exception as e:
print(f"Failed to start HTTP server: {e}") print(f"Failed to start HTTP server: {e}")
# 启动 WebUI 服务器
webui_runner = None
try:
webui = WebUI()
runner = web.AppRunner(webui.app)
await runner.setup()
site = web.TCPSite(runner, '0.0.0.0', 8002)
await site.start()
webui_runner = runner
local_ip = get_local_ip()
print(f"WebUI server is running at http://{local_ip}:8002")
except Exception as e:
print(f"Failed to start WebUI server: {e}")
try: try:
# 等待 WebSocket 服务器运行 # 等待 WebSocket 服务器运行
await ws_task await ws_task
@@ -28,6 +45,9 @@ async def main():
# 清理 HTTP 服务器 # 清理 HTTP 服务器
if http_runner: if http_runner:
await http_runner.cleanup() await http_runner.cleanup()
# 清理 WebUI 服务器
if webui_runner:
await webui_runner.cleanup()
if __name__ == "__main__": if __name__ == "__main__":
asyncio.run(main()) asyncio.run(main())
+4
View File
@@ -3,6 +3,7 @@ import sys
import logging import logging
from aiohttp import web from aiohttp import web
from aiohttp_cors import setup as cors_setup, ResourceOptions from aiohttp_cors import setup as cors_setup, ResourceOptions
from core.utils.util import get_local_ip
# 添加项目根目录到Python路径 # 添加项目根目录到Python路径
current_dir = os.path.dirname(os.path.abspath(__file__)) current_dir = os.path.dirname(os.path.abspath(__file__))
@@ -68,6 +69,7 @@ class WebUI:
# 添加静态文件服务 # 添加静态文件服务
self.app.router.add_static('/assets/', path=os.path.join(self.static_path, 'assets')) self.app.router.add_static('/assets/', path=os.path.join(self.static_path, 'assets'))
# 所有未匹配的路由都返回 index.html
self.app.router.add_get('/{tail:.*}', self.handle_static_files) self.app.router.add_get('/{tail:.*}', self.handle_static_files)
async def handle_static_files(self, request): async def handle_static_files(self, request):
@@ -79,6 +81,8 @@ class WebUI:
def run(self, host='0.0.0.0', port=8002): def run(self, host='0.0.0.0', port=8002):
"""运行服务器""" """运行服务器"""
local_ip = get_local_ip()
logger.info(f"WebUI server is running at http://{local_ip}:{port}")
web.run_app(self.app, host=host, port=port) web.run_app(self.app, host=host, port=port)
if __name__ == '__main__': if __name__ == '__main__':
File diff suppressed because one or more lines are too long
-12
View File
@@ -1,12 +0,0 @@
import os
import sys
# 添加项目根目录到Python路径
project_root = os.path.dirname(os.path.abspath(__file__))
sys.path.append(project_root)
from core.webui import WebUI
if __name__ == '__main__':
webui = WebUI()
webui.run()