Compare commits
3 Commits
d8eabcb606
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 123cbf1dbb | |||
| 3a38dcab00 | |||
| 39158880ba |
+10
-2
@@ -7,9 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
6 种更新类别:Added、Changed、Deprecated、Removed、Fixed、Security。
|
6 种更新类别:Added、Changed、Deprecated、Removed、Fixed、Security。
|
||||||
|
|
||||||
## [Unreleased]
|
## [0.1.5] - 2026-04-28
|
||||||
|
|
||||||
等待中...
|
### Added
|
||||||
|
|
||||||
|
- 增加开关 FastAPI 内置文档功能的选项。在此前,文档功能一直默认开启。
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- WebUI 版本升级至 1.1.0
|
||||||
|
- 增加开关 FastAPI `/docs` 文档端点的设置项。
|
||||||
|
- 删除「重启」和「关闭」Suan API 的预留按钮,因为技术上这难以实现。
|
||||||
|
|
||||||
## [0.1.4] - 2026-04-26
|
## [0.1.4] - 2026-04-26
|
||||||
|
|
||||||
|
|||||||
@@ -13,11 +13,17 @@ NJUPT Suan API 是一个 FastAPI 项目,目标在于实现对 NJUPT(南京
|
|||||||
## 功能
|
## 功能
|
||||||
|
|
||||||
| 计划功能(芒果画饼中) | 支持进度 |
|
| 计划功能(芒果画饼中) | 支持进度 |
|
||||||
|---------------|------------------|
|
|-------------------|------|
|
||||||
| 教务系统 - 课程表获取 | ✅ |
|
| 教务系统 - 课程表获取 | ✅ |
|
||||||
| 教务系统 - 课程获取 | ⌛️ |
|
| 教务系统 - 课程获取 | ⌛️ |
|
||||||
| 教务系统 - 成绩获取 | ⌛️ |
|
| 教务系统 - 成绩获取 | ⌛️ |
|
||||||
| 体育部系统 - 早锻炼获取 | ❌(无从破解微信小程序 QAQ) |
|
| 教务系统 - 课程表 - 日历订阅 | ⌛️ |
|
||||||
|
| 体育部系统 - 早锻炼获取 | ❌ |
|
||||||
|
|
||||||
|
| 优化待办 | 支持进度 |
|
||||||
|
|---------------|------|
|
||||||
|
| 自定义课程表 CSS 样式 | ⌛️ |
|
||||||
|
| 生成的静态资源有效期管理 | ⌛️ |
|
||||||
|
|
||||||
## 运行
|
## 运行
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ class Config:
|
|||||||
doc_system["port"] = 8000
|
doc_system["port"] = 8000
|
||||||
doc_system["reload"] = False
|
doc_system["reload"] = False
|
||||||
doc_system["public_host"] = "http://127.0.0.1:8000"
|
doc_system["public_host"] = "http://127.0.0.1:8000"
|
||||||
|
doc_system["docs"] = True
|
||||||
|
|
||||||
doc_schedule["playwright_headless"] = True
|
doc_schedule["playwright_headless"] = True
|
||||||
doc_schedule["jwxt_login_method"] = "sso"
|
doc_schedule["jwxt_login_method"] = "sso"
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ WEBUI_INDEX = STATIC_DIR / "index.html"
|
|||||||
SCHEDULE_INDEX = STATIC_DIR / "index-schedule.html"
|
SCHEDULE_INDEX = STATIC_DIR / "index-schedule.html"
|
||||||
ASSETS_DIR = STATIC_DIR / "assets"
|
ASSETS_DIR = STATIC_DIR / "assets"
|
||||||
|
|
||||||
webui_router = APIRouter(prefix="/webui")
|
webui_router = APIRouter(prefix="/webui", tags=["webui"])
|
||||||
|
|
||||||
|
|
||||||
@webui_router.get("/", response_class=HTMLResponse)
|
@webui_router.get("/", response_class=HTMLResponse)
|
||||||
|
|||||||
@@ -24,10 +24,36 @@ from njupt_suan_api.router.enhance import ReturnDto, create_db_and_tables
|
|||||||
|
|
||||||
DATA_DIR = Path.cwd() / "data"
|
DATA_DIR = Path.cwd() / "data"
|
||||||
|
|
||||||
|
description = """
|
||||||
|
🚀 NJUPT Suan API 的 API 文档在此。你也可以直接在此处测试 API。
|
||||||
|
|
||||||
|
**NJUPT Suan API** 是一个为南京邮电大学(NJUPT)开发的项目。
|
||||||
|
|
||||||
|
**项目** -
|
||||||
|
[GitHub](https://github.com/mangofanfan/njupt-suan-api) |
|
||||||
|
[Mango Gitea](https://gitea.mangofanfan.cn/SuanDev/njupt-suan-api) |
|
||||||
|
[文档](https://suan.mangofanfan.cn)
|
||||||
|
|
||||||
|
**文档** - [SwaggerUI](/docs) | [Redoc](/redoc) | [openapi.json](/openapi.json)
|
||||||
|
|
||||||
|
### 如果您是访客
|
||||||
|
|
||||||
|
部分 **admin** 分组的端点需要身份验证才能调用,它们一般会被标记。
|
||||||
|
|
||||||
|
你可以在这里查看所有端点的详细信息,如果你需要进行针对 Suan API 的开发的话,这一定会对你有所帮助。
|
||||||
|
|
||||||
|
### 如果您是管理员
|
||||||
|
|
||||||
|
文档功能作为 FastAPI 的特色功能默认开启。
|
||||||
|
|
||||||
|
如果你需要,在 Suan API 的设置中可以选择关闭文档功能。
|
||||||
|
"""
|
||||||
|
|
||||||
|
config.sync_load_json()
|
||||||
|
|
||||||
|
|
||||||
async def toml_watcher() -> None:
|
async def toml_watcher() -> None:
|
||||||
"""配置文件监听器"""
|
"""配置文件监听器"""
|
||||||
await config.load_json()
|
|
||||||
async for change in awatch(DATA_DIR / "config.json"):
|
async for change in awatch(DATA_DIR / "config.json"):
|
||||||
logger.info(f"配置文件更新,重新加载 | {change=}")
|
logger.info(f"配置文件更新,重新加载 | {change=}")
|
||||||
await config.load_json()
|
await config.load_json()
|
||||||
@@ -48,7 +74,20 @@ async def life_span(_: FastAPI) -> AsyncGenerator[None, None]:
|
|||||||
logger.info("配置文件监听任务已结束。")
|
logger.info("配置文件监听任务已结束。")
|
||||||
|
|
||||||
|
|
||||||
app = FastAPI(lifespan=combine_lifespans(life_span, mcp_app.lifespan))
|
# 文档功能是对 FastAPI app 实例进行配置的
|
||||||
|
enable_docs = config.get("system", "docs", True)
|
||||||
|
logger.debug(f"FastAPI 文档功能状态 - {enable_docs=}")
|
||||||
|
|
||||||
|
app = FastAPI(
|
||||||
|
title="njupt-suan-api",
|
||||||
|
lifespan=combine_lifespans(life_span, mcp_app.lifespan),
|
||||||
|
description=description,
|
||||||
|
docs_url="/docs" if enable_docs else None,
|
||||||
|
redoc_url="/redoc" if enable_docs else None,
|
||||||
|
openapi_url="/openapi.json" if enable_docs else None,
|
||||||
|
version=__version__,
|
||||||
|
license_info={"name": "MIT", "identifier": "MIT"},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.middleware("http")
|
@app.middleware("http")
|
||||||
|
|||||||
@@ -701,7 +701,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "njupt-suan-api"
|
name = "njupt-suan-api"
|
||||||
version = "0.1.3"
|
version = "0.1.5"
|
||||||
source = { editable = "." }
|
source = { editable = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "aiofiles" },
|
{ name = "aiofiles" },
|
||||||
|
|||||||
Vendored
+2
@@ -38,6 +38,7 @@ declare module 'vue' {
|
|||||||
NSwitch: typeof import('naive-ui')['NSwitch']
|
NSwitch: typeof import('naive-ui')['NSwitch']
|
||||||
NTag: typeof import('naive-ui')['NTag']
|
NTag: typeof import('naive-ui')['NTag']
|
||||||
NText: typeof import('naive-ui')['NText']
|
NText: typeof import('naive-ui')['NText']
|
||||||
|
NTooltip: typeof import('naive-ui')['NTooltip']
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
}
|
}
|
||||||
@@ -71,6 +72,7 @@ declare global {
|
|||||||
const NSwitch: typeof import('naive-ui')['NSwitch']
|
const NSwitch: typeof import('naive-ui')['NSwitch']
|
||||||
const NTag: typeof import('naive-ui')['NTag']
|
const NTag: typeof import('naive-ui')['NTag']
|
||||||
const NText: typeof import('naive-ui')['NText']
|
const NText: typeof import('naive-ui')['NText']
|
||||||
|
const NTooltip: typeof import('naive-ui')['NTooltip']
|
||||||
const RouterLink: typeof import('vue-router')['RouterLink']
|
const RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
const RouterView: typeof import('vue-router')['RouterView']
|
const RouterView: typeof import('vue-router')['RouterView']
|
||||||
}
|
}
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "webui",
|
"name": "webui",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
+9
-1
@@ -54,6 +54,7 @@ onMounted(async () => {
|
|||||||
<sidebar-nav />
|
<sidebar-nav />
|
||||||
</div>
|
</div>
|
||||||
<div id="center-content">
|
<div id="center-content">
|
||||||
|
<div id="extra-control" />
|
||||||
<div id="content-container">
|
<div id="content-container">
|
||||||
<router-view v-slot="{ Component }">
|
<router-view v-slot="{ Component }">
|
||||||
<keep-alive>
|
<keep-alive>
|
||||||
@@ -107,13 +108,20 @@ onMounted(async () => {
|
|||||||
#center-content {
|
#center-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
#extra-control {
|
||||||
|
flex: 0;
|
||||||
|
}
|
||||||
|
|
||||||
#content-container {
|
#content-container {
|
||||||
border: 1px solid #519f72;
|
border: 1px solid #519f72;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
height: 100%;
|
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
height: 100%;
|
||||||
|
flex: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+46
-14
@@ -38,13 +38,6 @@ function varTag(code: string, description: string) {
|
|||||||
<template>
|
<template>
|
||||||
<div id="config-container">
|
<div id="config-container">
|
||||||
<n-h2 prefix="bar">酸酸设置</n-h2>
|
<n-h2 prefix="bar">酸酸设置</n-h2>
|
||||||
<n-alert title="设置注意" type="info">
|
|
||||||
<n-p class="no-margin">
|
|
||||||
<n-text strong type="warning">系统设置</n-text>
|
|
||||||
- 需要完全重新启动 Suan API 才能应用;
|
|
||||||
</n-p>
|
|
||||||
<n-p class="no-margin">其他的设置可以即时生效。</n-p>
|
|
||||||
</n-alert>
|
|
||||||
|
|
||||||
<n-collapse v-if="CONFIG.dataStatus" style="margin-top: 1rem">
|
<n-collapse v-if="CONFIG.dataStatus" style="margin-top: 1rem">
|
||||||
<n-collapse-item name="system" title="系统设置">
|
<n-collapse-item name="system" title="系统设置">
|
||||||
@@ -73,6 +66,25 @@ function varTag(code: string, description: string) {
|
|||||||
show-input
|
show-input
|
||||||
title="外部域名 / 主机名"
|
title="外部域名 / 主机名"
|
||||||
/>
|
/>
|
||||||
|
<setting-card
|
||||||
|
v-model:boolean-value="CONFIG.data.system.docs"
|
||||||
|
show-switch
|
||||||
|
title="Fast API 文档功能"
|
||||||
|
>
|
||||||
|
<n-p>
|
||||||
|
FastAPI 提供
|
||||||
|
<n-code inline>/docs</n-code>
|
||||||
|
和
|
||||||
|
<n-code inline>/redoc</n-code>
|
||||||
|
两个文档端口,以及一个
|
||||||
|
<n-code inline>/openapi.json</n-code>
|
||||||
|
结构化 API 数据文件。
|
||||||
|
</n-p>
|
||||||
|
<n-p
|
||||||
|
>如果你需要深入研究 Suan API,这是来自 FastAPI
|
||||||
|
的赠礼;如有安全方面顾虑也可以关闭。</n-p
|
||||||
|
>
|
||||||
|
</setting-card>
|
||||||
</n-flex>
|
</n-flex>
|
||||||
</n-collapse-item>
|
</n-collapse-item>
|
||||||
<n-collapse-item name="schedule" title="课表设置">
|
<n-collapse-item name="schedule" title="课表设置">
|
||||||
@@ -146,12 +158,20 @@ function varTag(code: string, description: string) {
|
|||||||
</n-collapse-item>
|
</n-collapse-item>
|
||||||
</n-collapse>
|
</n-collapse>
|
||||||
|
|
||||||
<teleport v-if="extraVisible" defer to="#center-container">
|
<teleport v-if="extraVisible" defer to="#extra-control">
|
||||||
<div class="header-card">
|
<div class="header-card">
|
||||||
<n-flex vertical>
|
<n-flex align="center">
|
||||||
|
<n-alert type="info">
|
||||||
|
<n-p class="no-margin">
|
||||||
|
<n-text strong type="warning">系统设置</n-text>
|
||||||
|
需要完全重新启动 Suan API 才能应用;其他的设置可以即时生效。
|
||||||
|
</n-p>
|
||||||
|
</n-alert>
|
||||||
|
|
||||||
<n-button
|
<n-button
|
||||||
circle
|
round
|
||||||
size="large"
|
size="large"
|
||||||
|
style="margin-left: auto"
|
||||||
type="success"
|
type="success"
|
||||||
@click="
|
@click="
|
||||||
() => {
|
() => {
|
||||||
@@ -159,14 +179,26 @@ function varTag(code: string, description: string) {
|
|||||||
MESSAGE.success('保存设置成功,后端会自动应用新的设置 ~')
|
MESSAGE.success('保存设置成功,后端会自动应用新的设置 ~')
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
>保存</n-button
|
>保存设置</n-button
|
||||||
>
|
>
|
||||||
<n-button circle size="large" type="warning">重启</n-button>
|
|
||||||
<n-button circle size="large" type="error">关闭</n-button>
|
<n-popover placement="bottom" trigger="hover">
|
||||||
|
<template #trigger>
|
||||||
|
<n-button round size="large" type="tertiary">重启 Suan API</n-button>
|
||||||
|
</template>
|
||||||
|
<n-p>由于技术限制,无法从 WebUI 重启 Suan API 应用。</n-p>
|
||||||
|
</n-popover>
|
||||||
</n-flex>
|
</n-flex>
|
||||||
</div>
|
</div>
|
||||||
</teleport>
|
</teleport>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped>
|
||||||
|
div.header-card {
|
||||||
|
border: 1px solid #519f72;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ export interface ConfigSystemDto {
|
|||||||
port?: number
|
port?: number
|
||||||
reload?: boolean
|
reload?: boolean
|
||||||
public_host?: string
|
public_host?: string
|
||||||
|
docs?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ConfigScheduleDto {
|
export interface ConfigScheduleDto {
|
||||||
|
|||||||
Reference in New Issue
Block a user