feat: 添加系统信息配置功能

- 在 API 路由中新增获取和重新加载系统信息配置的接口。
- 实现信息配置的加载逻辑,并提供默认配置。
- 更新前端以支持动态加载和展示信息配置。
- 修改 Dockerfile,移除代理设置。
This commit is contained in:
Wenjie Zhang 2025-06-22 23:07:57 +08:00
parent 2760efcbb7
commit 4ecf9a8cd6
10 changed files with 262 additions and 18 deletions

View File

@ -35,8 +35,7 @@ COPY ../src /app/src
COPY ../server /app/server
# 关闭代理设置
ENV http_proxy="" \
https_proxy=""
RUN unset http_proxy https_proxy
# 同步项目
RUN --mount=type=cache,target=/root/.cache/uv \

View File

@ -1,13 +1,63 @@
from fastapi import Request, Body, Depends
import os
import yaml
from pathlib import Path
from fastapi import Request, Body, Depends, HTTPException
from fastapi import APIRouter
from src import config, retriever, knowledge_base, graph_base
from server.utils.auth_middleware import get_admin_user, get_superadmin_user
from server.models.user_model import User
from src.utils.logging_config import logger
base = APIRouter()
def load_info_config():
"""加载信息配置文件"""
try:
# 配置文件路径
config_path = Path("src/static/info.local.yaml")
# 检查文件是否存在
if not config_path.exists():
logger.warning(f"配置文件 {config_path} 不存在,使用默认配置")
return get_default_info_config()
# 读取配置文件
with open(config_path, 'r', encoding='utf-8') as file:
config = yaml.safe_load(file)
# logger.info(f"成功加载信息配置文件: {config_path}")
return config
except Exception as e:
logger.error(f"加载信息配置文件失败: {e}")
return get_default_info_config()
def get_default_info_config():
"""获取默认信息配置"""
return {
"organization": {
"name": "江南大学",
"short_name": "语析",
"logo": "/favicon.svg",
"avatar": "/avatar.jpg"
},
"branding": {
"title": "Yuxi-Know",
"subtitle": "大模型驱动的知识库管理工具",
"description": "结合知识库与知识图谱,提供更准确、更全面的回答"
},
"features": [
"📚 灵活知识库",
"🕸️ 知识图谱集成",
"🤖 多模型支持"
],
"footer": {
"copyright": "© 江南大学 2025 [WIP] v0.12.138"
}
}
@base.get("/")
async def route_index():
return {"message": "You Got It!"}
@ -58,4 +108,32 @@ def get_log(current_user: User = Depends(get_admin_user)):
log = ''.join(last_lines)
return {"log": log, "message": "success", "log_file": LOG_FILE}
@base.get("/info")
async def get_info_config():
"""获取系统信息配置(公开接口,无需认证)"""
try:
config = load_info_config()
return {
"success": True,
"data": config
}
except Exception as e:
logger.error(f"获取信息配置失败: {e}")
raise HTTPException(status_code=500, detail="获取信息配置失败")
@base.get("/info/reload")
async def reload_info_config():
"""重新加载信息配置(管理员接口)"""
# 注:这里暂时不添加权限验证,后续可以根据需要添加
try:
config = load_info_config()
return {
"success": True,
"message": "配置重新加载成功",
"data": config
}
except Exception as e:
logger.error(f"重新加载信息配置失败: {e}")
raise HTTPException(status_code=500, detail="重新加载信息配置失败")

View File

@ -18,6 +18,8 @@ PUBLIC_PATHS = [
r"^/api/auth/initialize$", # 初始化系统
r"^/api$", # Health Check
r"^/api/login$", # 登录页面
r"^/api/info$", # 获取系统信息配置
r"^/api/info/.*$", # 系统信息配置相关接口
]
# 获取数据库会话

View File

@ -0,0 +1,26 @@
# 单位信息配置文件
# 用于配置网站的基本信息、品牌信息等
# 修改后需要重启服务生效
# 组织信息
organization:
name: "江南语析" # 完整组织名称
short_name: "语析" # 简短名称
logo: "/favicon.svg" # Logo文件路径
avatar: "/avatar.jpg" # 头像文件路径
# 品牌信息
branding:
title: "Yuxi-Know" # 系统标题
subtitle: "大模型驱动的知识库管理工具" # 副标题
description: "结合知识库与知识图谱,提供更准确、更全面的回答" # 描述信息
# 功能特性
features:
- "📚 灵活知识库"
- "🕸️ 知识图谱集成"
- "🤖 多模型支持"
# 页脚信息
footer:
copyright: "© 江南语析 2025 [WIP] v0.12.138"

1
web/public/jnu.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -46,6 +46,21 @@ export const configApi = {
getConfig: () => apiGet('/api/config'),
}
// 系统信息配置API
export const infoApi = {
/**
* 获取系统信息配置
* @returns {Promise} - 系统信息配置
*/
getInfoConfig: () => apiGet('/api/info'),
/**
* 重新加载信息配置
* @returns {Promise} - 重新加载结果
*/
reloadInfoConfig: () => apiGet('/api/info/reload')
}
// 健康检查API
export const healthApi = {
/**

View File

@ -10,11 +10,13 @@ import { Bot, Waypoints, LibraryBig, MessageSquareMore, Settings } from 'lucide-
import { useConfigStore } from '@/stores/config'
import { useDatabaseStore } from '@/stores/database'
import { useInfoStore } from '@/stores/info'
import DebugComponent from '@/components/DebugComponent.vue'
import UserInfoComponent from '@/components/UserInfoComponent.vue'
const configStore = useConfigStore()
const databaseStore = useDatabaseStore()
const infoStore = useInfoStore()
const layoutSettings = reactive({
showDebug: false,
@ -51,7 +53,10 @@ const fetchGithubStars = async () => {
}
}
onMounted(() => {
onMounted(async () => {
//
await infoStore.loadInfoConfig()
//
getRemoteConfig()
getRemoteDatabase()
fetchGithubStars() // Fetch GitHub stars on mount
@ -115,8 +120,8 @@ const mainList = [{
<div class="header" :class="{ 'top-bar': layoutSettings.useTopBar }">
<div class="logo circle">
<router-link to="/">
<img src="/avatar.jpg">
<span class="logo-text">语析</span>
<img :src="infoStore.organization.avatar">
<span class="logo-text">{{ infoStore.organization.short_name }}</span>
</router-link>
</div>
<div class="nav">

View File

@ -9,9 +9,17 @@ import 'ant-design-vue/dist/reset.css';
import './assets/main.css'
const app = createApp(App)
const pinia = createPinia()
app.use(createPinia())
app.use(pinia)
app.use(router)
app.use(Antd)
// 预加载信息配置
import { useInfoStore } from '@/stores/info'
const infoStore = useInfoStore()
infoStore.loadInfoConfig().then(() => {
console.log('应用信息配置预加载完成')
})
app.mount('#app')

108
web/src/stores/info.js Normal file
View File

@ -0,0 +1,108 @@
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
import { infoApi } from '@/apis/public_api'
export const useInfoStore = defineStore('info', () => {
// 状态
const infoConfig = ref({})
const isLoading = ref(false)
const isLoaded = ref(false)
// 计算属性 - 组织信息
const organization = computed(() => infoConfig.value.organization || {
name: "江南语析",
short_name: "语析",
logo: "/favicon.svg",
avatar: "/avatar.jpg"
})
// 计算属性 - 品牌信息
const branding = computed(() => infoConfig.value.branding || {
title: "Yuxi-Know",
subtitle: "大模型驱动的知识库管理工具",
description: "结合知识库与知识图谱,提供更准确、更全面的回答"
})
// 计算属性 - 功能特性
const features = computed(() => infoConfig.value.features || [
"📚 灵活知识库",
"🕸️ 知识图谱集成",
"🤖 多模型支持"
])
// 计算属性 - 页脚信息
const footer = computed(() => infoConfig.value.footer || {
copyright: "© 江南大学 2025 [WIP] v0.12.138"
})
// 动作方法
function setInfoConfig(newConfig) {
infoConfig.value = newConfig
isLoaded.value = true
}
async function loadInfoConfig(force = false) {
// 如果已经加载过且不强制刷新,则不重新加载
if (isLoaded.value && !force) {
return infoConfig.value
}
try {
isLoading.value = true
const response = await infoApi.getInfoConfig()
if (response.success && response.data) {
setInfoConfig(response.data)
console.log('信息配置加载成功:', response.data)
return response.data
} else {
console.warn('信息配置加载失败,使用默认配置')
return null
}
} catch (error) {
console.error('加载信息配置时发生错误:', error)
return null
} finally {
isLoading.value = false
}
}
async function reloadInfoConfig() {
try {
isLoading.value = true
const response = await infoApi.reloadInfoConfig()
if (response.success && response.data) {
setInfoConfig(response.data)
console.log('信息配置重新加载成功:', response.data)
return response.data
} else {
console.warn('信息配置重新加载失败')
return null
}
} catch (error) {
console.error('重新加载信息配置时发生错误:', error)
return null
} finally {
isLoading.value = false
}
}
return {
// 状态
infoConfig,
isLoading,
isLoaded,
// 计算属性
organization,
branding,
features,
footer,
// 方法
setInfoConfig,
loadInfoConfig,
reloadInfoConfig
}
})

View File

@ -3,8 +3,8 @@
<div class="hero-section">
<div class="glass-header">
<div class="logo">
<img src="/favicon.svg" alt="江南语析" class="logo-img" />
<span style="font-size: 1.3rem; font-weight: bold;">江南语析</span>
<img :src="infoStore.organization.logo" :alt="infoStore.organization.name" class="logo-img" />
<span style="font-size: 1.3rem; font-weight: bold;">{{ infoStore.organization.name }}</span>
</div>
<div class="github-link">
<a href="https://github.com/xerrors/Yuxi-Know" target="_blank">
@ -17,13 +17,11 @@
</div>
<div class="hero-content">
<h1 class="title">{{ title }}</h1>
<h1 class="title">{{ infoStore.branding.title }}</h1>
<div class="description">
<p class="subtitle">大模型驱动的知识库管理工具</p>
<p class="subtitle">{{ infoStore.branding.subtitle }}</p>
<p class="features">
<span>📚 灵活知识库</span>
<span>🕸 知识图谱集成</span>
<span>🤖 多模型支持</span>
<span v-for="feature in infoStore.features" :key="feature">{{ feature }}</span>
</p>
</div>
<button class="start-button" @click="goToChat">开始对话</button>
@ -36,14 +34,14 @@
<div class="preview-overlay">
<div class="overlay-content">
<h3>强大的问答能力</h3>
<p>结合知识库与知识图谱提供更准确更全面的回答</p>
<p>{{ infoStore.branding.description }}</p>
</div>
</div>
</div>
</div>
<footer>
<p>© 江南语析 2025 [WIP] v0.12.138</p>
<p>{{ infoStore.footer.copyright }}</p>
</footer>
</div>
</template>
@ -52,11 +50,12 @@
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { useUserStore } from '@/stores/user'
import { useInfoStore } from '@/stores/info'
import { chatApi } from '@/apis/auth_api'
const title = ref('Yuxi-Know')
const router = useRouter()
const userStore = useUserStore()
const infoStore = useInfoStore()
const githubStars = ref(0)
const isLoadingStars = ref(false)
@ -113,7 +112,10 @@ const fetchGithubStars = async () => {
}
}
onMounted(() => {
onMounted(async () => {
//
await infoStore.loadInfoConfig()
// GitHub stars
fetchGithubStars()
})
</script>