内容
# NustDevKit
**开发者门户和 NUST LMS 的 REST API 网关。**
NustDevKit 在干净的身份验证 REST API 后封装了 NUST 学习管理系统(Moodle)——学生和开发者可以在不接触 Moodle 会话 cookie 或内部 AJAX 约定时查询他们的学术数据(课程、截止日期、通知、成绩)。
实时访问 **[nustdevkit.com](https://www.nustdevkit.com)**
---
## 内容列表
| 组件 | 描述 |
|-----------|-------------|
| **网关** (`ndk_backend/`) | FastAPI 应用。针对 LMS 进行身份验证,在服务器端管理会话,并通过持有者令牌 API 代理数据。 |
| **门户** (`ndk_frontend/`) | Scalar 驱动的 API 参考。托管在 S3 + CloudFront 上,网址为 `nustdevkit.com`。 |
| **MCP 服务器** | 自托管的 FastMCP 服务器,位于 `/mcp/`(可流式传输的 HTTP,OAuth 2.0 + PKCE)。允许 AI 助手(如 Claude、Cursor 等)调用 LMS 数据作为工具。 |
| **文档助手** | 门户中嵌入的 Groq 支持的流式聊天小部件。回答有关 API 和 SDK 的问题。 |
| **SDK** | APIMatic 生成的 TypeScript、Python、Java、.NET、Go、PHP 和 Ruby 客户端 - 可从门户下载。 |
---
## 架构
```
浏览器 / AI 助手
│
▼
CloudFront CDN
(nustdevkit.com)
│
▼
S3 存储桶 ──── 静态门户(HTML、OpenAPI 规范、SDK)
api.nustdevkit.com
│
▼
Caddy(TLS) ──── 反向代理,Let's Encrypt 证书
│
▼
FastAPI 网关 ──── 持有者令牌身份验证,速率限制,内存会话
(EC2 上的 Docker)
│
▼
NUST LMS(Moodle) ──── 上游:AJAX + 令牌 REST 端点
```
---
## 项目结构
```
NustDevKit/
├── ndk_backend/ # FastAPI 网关
│ ├── main.py # 应用入口点,中间件,MCP 挂载
│ ├── app/
│ │ ├── core/ # 配置,JWT,速率限制器
│ │ ├── routes/ # 身份验证,oauth,服务端点,助手
│ │ ├── services/ # LMSSession(httpx 客户端),会话存储
│ │ ├── schemas/ # Pydantic 请求/响应模型
│ │ ├── dependencies/ # FastAPI 依赖(身份验证解析)
│ │ ├── oauth/ # OAuth 2.0 流:存储,令牌验证器,登录页面
│ │ ├── mcp/ # FastMCP 服务器 + 工具(课程,日历,通知)
│ │ └── assistant/ # Groq 流式助手 + 知识库
│ ├── docker/
│ │ └── Dockerfile
│ └── requirements.txt
│
├── ndk_frontend/ # 静态门户(Scalar)
│ ├── index.html # 自定义标题,聊天小部件,Scalar 配置
│ ├── openapi.yaml # 处理后的规范(生成 - 请勿直接编辑)
│ └── sdks/ # 可下载的 SDK zip 文件
│
├── src/ # 源规范和门户内容
│ └── spec/openapi.yaml # 编辑此文件 - 运行 `npm run sync` 以传播
│
├── portal/ # APIMatic 生成的参考(SDK 和文档的源)
│ ├── static/sdks/ # SDK zip 文件 - 构建会将这些复制到 ndk_frontend/sdks/
│ └── llms-pages/ # 每种语言的 Markdown 文档,一个文件夹对应一种语言:
│ └── <language>/ # (python,typescript,java,net-standard-library,go,php,ruby)
│ ├── getting-started/ # 安装 + 客户端初始化
│ ├── api-endpoints/ # 每个端点一个文档(参数,示例用法)
│ ├── models/ # 每个模型/枚举一个文档(字段 + 导入)
│ └── sdk-infrastructure/ # 客户端配置,身份验证,错误,日志记录
│
├── scripts/ # 构建脚本
│ ├── sync-spec.mjs # 将规范 + 资产复制到 ndk_frontend/
│ └── inject-code-samples.mjs # 将 SDK/MCP 代码示例注入规范
│
└── .github/workflows/
├── deploy-backend.yml # 构建 → 推送到 ECR → 在 EC2 上重新部署
└── deploy-frontend.yml # 将 ndk_frontend/ 同步到 S3 + 使 CloudFront 缓存失效
```
> **阅读 SDK 文档:** 对于特定 SDK 的实际使用,打开 `portal/llms-pages/<language>/`。从 `getting-started/` 开始(安装 + 客户端设置),然后是 `api-endpoints/` 用于您需要的调用,以及 `models/` 用于它引用的任何请求/响应类型。这些是 APIMatic 生成的每个 SDK 的 README;实时门户的代码示例从同一源中提取。
---
## 本地开发
### 先决条件
- Python 3.12+
- Node.js 18+
- Docker Desktop(可选,用于容器测试)
### 后端
```bash
cd ndk_backend
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env # 填写 JWT_SECRET 和 GROQ_API_KEY
uvicorn main:app --reload
```
网关运行在 `http://127.0.0.1:8000`。交互式文档位于 `/docs`。
### 前端
```bash
npm install
npm run sync # 处理 src/spec/openapi.yaml → ndk_frontend/openapi.yaml
npm run dev # 同步并启动本地文件服务器
```
门户打开在 `http://localhost:3000`。
---
## 环境变量
所有设置都从 `.env`(或环境)加载。生产环境中必填:
| 变量 | 描述 |
|----------|-------------|
| `JWT_SECRET` | 用于签署网关 JWT 的密钥。使用 `python -c "import secrets; print(secrets.token_hex(32))"` 生成。 |
| `GROQ_API_KEY` | 文档助手的 Groq API 密钥。留空以禁用助手(返回 503)。 |
可选覆盖:
| 变量 | 默认值 | 描述 |
|----------|---------|-------------|
| `ACCESS_TOKEN_EXPIRE_MINUTES` | `720` | JWT 生命周期(12 小时)。 |
| `SESSION_IDLE_MINUTES` | `60` | 内存 LMS 会话的滑动空闲 TTL。 |
| `AUTH_LOGIN_RATE_LIMIT` | `5/minute` | `POST /auth/login` 的每 IP 速率限制。 |
| `ASSISTANT_RATE_LIMIT` | `15/minute` | 助手的每 IP 速率限制。 |
| `CORS_ORIGINS` | `["*"]` | 允许的 CORS 源。 |
| `LMS_BASE_URL` | `https://lms.nust.edu.pk/portal` | 上游 LMS 基本 URL。 |
---
## Docker
本地构建和运行:
```bash
cd ndk_backend
docker build -f docker/Dockerfile -t nustdevkit .
docker run -p 8000:8000 \
-e JWT_SECRET="your-secret" \
-e GROQ_API_KEY="your-key" \
nustdevkit
```
> **仅限单个工作线程。** 会话存储在进程中。不要在未将会话存储迁移到 Redis 之前运行多个副本。
---
## 部署
该项目部署在 AWS 上:
| 服务 | 角色 |
|---------|------|
| EC2 t3.micro (孟买) | 运行 Docker 容器 |
| Amazon ECR | 私有 Docker 镜像注册表 |
| Caddy | EC2 上的 TLS 终止 + 反向代理 |
| S3 | 托管静态前端 |
| CloudFront | 前端的 CDN |
| Elastic IP | EC2 实例的静态 IP |
CI/CD 由 GitHub Actions 处理:
- 推送到 `main` 并在 `ndk_backend/` 下进行更改 → 构建镜像,推送到 ECR,在 EC2 上重新部署。
- 推送到 `main` 并在 `ndk_frontend/` 下进行更改 → 同步到 S3,使 CloudFront 缓存失效。
**更新 API 规范:**
```bash
# 编辑 src/spec/openapi.yaml,然后:
npm run sync
git add ndk_frontend/openapi.yaml
git commit -m "更新规范"
git push
```
---
## API 概述
所有 `/service/*` 端点都需要持有者令牌。`POST /auth/login`、`POST /assistant/ask`、`GET /health` 和 OAuth/发现路由是开放的(助手受到速率限制)。服务路由的名称基于它们代理的底层 Moodle 功能:
```
POST /auth/login # 身份验证 → JWT
POST /auth/logout # 使会话失效
GET /service/core_webservice_get_site_info # 用户 ID、姓名、站点信息
GET /service/core_course_get_recent_courses # 最近访问的课程
GET /service/core_course_get_enrolled_courses_by_timeline_classification # 按时间线筛选的课程
GET /service/core_course_get_contents # 课程章节和文件
GET /service/core_calendar_get_action_events_by_timesort # 即将到来的截止日期
GET /service/core_calendar_get_action_events_by_course # 特定课程的截止日期
GET /service/message_popup_get_popup_notifications # 弹出通知
POST /assistant/ask # 流式文档助手(Groq,开放 + 速率限制)
GET /health # 活动检查
/mcp/ # FastMCP 端点(可流式传输的 HTTP,OAuth 保护)
```
完整参考:[nustdevkit.com](https://www.nustdevkit.com)
---
## MCP 服务器
网关也是一个模型上下文协议服务器,因此支持 MCP 的 AI 助手(如 Claude Code、Claude Desktop、Cursor 等)可以将 LMS 作为一组工具调用。
- **端点:** `https://api.nustdevkit.com/mcp/`
- **传输:** 可流式传输的 HTTP
- **身份验证:** OAuth 2.0 with PKCE - 您通过托管页面登录一次;客户端从不看到您的凭证并自动获取令牌。
### 连接(Claude Code 示例)
将服务器添加到项目的 `.mcp.json` - 不需要令牌,客户端为您运行 OAuth 流:
```json
{
"mcpServers": {
"nust-lms": {
"type": "http",
"url": "https://api.nustdevkit.com/mcp/"
}
}
}
```
首次连接时,客户端会打开一个浏览器,指向网关的登录页面。输入您的 NUST LMS 凭证后,它会显示“身份验证成功”,然后返回到助手,工具即可启用。
### 可用工具
| 工具 | 返回 |
|------|---------|
| `get_my_account` | 用户 ID、姓名和站点信息 |
| `list_recent_courses` | 最近访问的课程 |
| `list_courses_by_timeline` | 按时间线筛选的课程(过去 / 进行中 / 未来) |
| `list_course_contents` | 课程的章节和文件 |
| `list_upcoming_deadlines` | 所有课程的即将到来的截止日期 |
| `list_course_deadlines` | 特定课程的截止日期 |
| `list_popup_notifications` | 弹出通知 |
### 身份验证工作原理
OAuth 流是 **无状态** 的 - 对于公共 PKCE 客户端,安全性依赖于 PKCE 加上环回重定向,因此网关存储每个客户端的记录。端点是:`/.well-known/oauth-authorization-server`(发现),`/oauth/register`(动态客户端注册),`/oauth/authorize`(登录页面)和 `/oauth/token`(代码 → 令牌交换)。令牌在 MCP 门户由自定义 `GatewayTokenVerifier` 验证。
> **注意:** LMS 会话和 OAuth 令牌存储在服务器内存中(单个工作线程)。重新部署或会话超时意味着您需要重新身份验证 - 如果工具返回 `401: Session expired`,只需重新连接并再次登录。
---
## 贡献
1. 复刻仓库并从 `main` 创建一个分支。
2. 后端更改:在 `ndk_backend/` 下编辑,本地测试使用 `uvicorn main:app --reload`。
3. 规范更改:编辑 `src/spec/openapi.yaml`,运行 `npm run sync`,提交两个文件。
4. 打开一个拉取请求 - 合并时 CI 将运行。
---
## 许可证
版权 (c) 2026 Hasaan Hameed。保留所有权利。详情请参见 [LICENSE](LICENSE)。