feat: 整理项目结构

This commit is contained in:
lichao 2026-02-21 22:50:02 +08:00
parent aa62486f21
commit 8cd2cca57a
15 changed files with 121 additions and 77 deletions

2
.env
View File

@ -8,7 +8,7 @@ DB_NAME='auth_db'
# github # github
GITHUB_CLIENT_ID=Ov23lihk723FlNAwlFg6 GITHUB_CLIENT_ID=Ov23lihk723FlNAwlFg6
GITHUB_CLIENT_SECRET=b839f50bba1f006ffdd43fb73c5ae221a54e1e2e GITHUB_CLIENT_SECRET=b839f50bba1f006ffdd43fb73c5ae221a54e1e2e
GITHUB_CALLBACK_URL=http://108.171.193.155:8000/callback GITHUB_CALLBACK_URL=http://45.130.23.71:8000/callback
# 飞书 # 飞书

123
README.md
View File

@ -1,73 +1,96 @@
<p align="center"> # OAuth NestJS Demo
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="200" alt="Nest Logo" /></a>
</p>
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456 A NestJS backend with multi-provider OAuth, JWT authentication, task management, and a license/activation key system.
[circleci-url]: https://circleci.com/gh/nestjs/nest
<p align="center">A progressive <a href="http://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p> ## Tech Stack
<p align="center">
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/common.svg" alt="NPM Downloads" /></a>
<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a>
<a href="https://coveralls.io/github/nestjs/nest?branch=master" target="_blank"><img src="https://coveralls.io/repos/github/nestjs/nest/badge.svg?branch=master#9" alt="Coverage" /></a>
<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a>
<a href="https://opencollective.com/nest#backer" target="_blank"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
<a href="https://paypal.me/kamilmysliwiec" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg"/></a>
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://img.shields.io/badge/Support%20us-Open%20Collective-41B883.svg" alt="Support us"></a>
<a href="https://twitter.com/nestframework" target="_blank"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow"></a>
</p>
<!--[![Backers on Open Collective](https://opencollective.com/nest/backers/badge.svg)](https://opencollective.com/nest#backer)
[![Sponsors on Open Collective](https://opencollective.com/nest/sponsors/badge.svg)](https://opencollective.com/nest#sponsor)-->
## Description - **Framework**: NestJS 10 + TypeScript
- **Database**: MySQL + TypeORM
- **Auth**: JWT, GitHub OAuth2, Lark OAuth2
- **API Docs**: Swagger (`/api`)
- **Package Manager**: pnpm
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository. ## Getting Started
## Installation
```bash ```bash
$ pnpm install # Install dependencies
pnpm install
# Configure environment variables
cp .env.example .env # then edit .env
# Start in development mode
pnpm run start:dev
``` ```
## Running the app See [docs/QUICKSTART.md](docs/QUICKSTART.md) for a detailed setup guide.
## Documentation
| Document | Description |
| --- | --- |
| [QUICKSTART.md](docs/QUICKSTART.md) | Quick start guide |
| [OAuth-JWT-Integration.md](docs/OAuth-JWT-Integration.md) | OAuth and JWT integration |
| [database-flow.md](docs/database-flow.md) | Database schema and data flow |
| [LICENSE_SYSTEM.md](docs/LICENSE_SYSTEM.md) | License system overview |
| [LICENSE_IMPLEMENTATION.md](docs/LICENSE_IMPLEMENTATION.md) | License implementation details |
| [GENERATE_LICENSE_GUIDE.md](docs/GENERATE_LICENSE_GUIDE.md) | License generation guide |
| [HOW_TO_GENERATE.md](docs/HOW_TO_GENERATE.md) | License generation instructions |
## Scripts
| Script | Description |
| --- | --- |
| `scripts/generate-licenses.sh` | Interactive license key generation |
| `scripts/test-license-system.sh` | License system integration tests |
| `scripts/check-db-schema.js` | Database schema validation |
```bash ```bash
# development # Generate license keys
$ pnpm run start bash scripts/generate-licenses.sh
# watch mode # Run license system tests
$ pnpm run start:dev bash scripts/test-license-system.sh
# production mode
$ pnpm run start:prod
``` ```
## Test ## Running the App
```bash ```bash
# unit tests # Development
$ pnpm run test pnpm run start:dev
# e2e tests # Production build
$ pnpm run test:e2e pnpm run build
pnpm run start:prod
# test coverage
$ pnpm run test:cov
``` ```
## Support ## Tests
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support). ```bash
# Unit tests
pnpm run test
## Stay in touch # E2E tests
pnpm run test:e2e
- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com) # Coverage
- Website - [https://nestjs.com](https://nestjs.com/) pnpm run test:cov
- Twitter - [@nestframework](https://twitter.com/nestframework) ```
## License ## Project Structure
Nest is [MIT licensed](LICENSE). ```text
src/
├── auth/ # JWT auth, strategies, guards
├── common/ # Shared filters, interceptors, guards
├── user/ # User management
├── task/ # Task management
├── license/ # License/activation key system
├── github/ # GitHub OAuth provider
├── lark/ # Lark OAuth provider
├── deepseek/ # DeepSeek AI provider
└── constants/ # Shared constants
docs/ # Project documentation
scripts/ # Utility and test scripts
test/ # E2E tests
```

View File

@ -38,7 +38,7 @@ if [ "$has_admin" != "y" ]; then
\"email\": \"$admin_email\" \"email\": \"$admin_email\"
}") }")
ADMIN_TOKEN=$(echo $REGISTER_RESPONSE | jq -r '.access_token') ADMIN_TOKEN=$(echo $REGISTER_RESPONSE | jq -r '.data.access_token')
if [ "$ADMIN_TOKEN" = "null" ] || [ -z "$ADMIN_TOKEN" ]; then if [ "$ADMIN_TOKEN" = "null" ] || [ -z "$ADMIN_TOKEN" ]; then
echo -e "${RED}注册失败!${NC}" echo -e "${RED}注册失败!${NC}"
@ -64,7 +64,7 @@ else
\"password\": \"$admin_password\" \"password\": \"$admin_password\"
}") }")
ADMIN_TOKEN=$(echo $LOGIN_RESPONSE | jq -r '.access_token') ADMIN_TOKEN=$(echo $LOGIN_RESPONSE | jq -r '.data.access_token')
if [ "$ADMIN_TOKEN" = "null" ] || [ -z "$ADMIN_TOKEN" ]; then if [ "$ADMIN_TOKEN" = "null" ] || [ -z "$ADMIN_TOKEN" ]; then
echo -e "${RED}登录失败!请检查用户名和密码${NC}" echo -e "${RED}登录失败!请检查用户名和密码${NC}"
@ -126,11 +126,17 @@ GENERATE_RESPONSE=$(curl -s -X POST "$BASE_URL/license/generate" \
}") }")
# 检查是否成功 # 检查是否成功
if echo "$GENERATE_RESPONSE" | jq -e '.[0].code' > /dev/null 2>&1; then if echo "$GENERATE_RESPONSE" | jq -e '.data[0].code' > /dev/null 2>&1; then
echo -e "${GREEN}卡密生成成功!${NC}" LICENSES=$(echo "$GENERATE_RESPONSE" | jq -r '.data')
ACTUAL_COUNT=$(echo "$LICENSES" | jq 'length')
echo -e "${GREEN}✓ 卡密生成成功!共 $ACTUAL_COUNT${NC}"
echo "" echo ""
echo "生成的卡密:" echo -e " ┌─────────────────────┬──────────┬────────┬──────────────┐"
echo "$GENERATE_RESPONSE" | jq -r '.[] | " \(.code) - \(.type) (\(.validDays)天) - \(.remarks // "无备注")"' echo -e " │ 卡密 │ 类型 │ 有效期 │ 备注 │"
echo -e " ├─────────────────────┼──────────┼────────┼──────────────┤"
echo "$LICENSES" | jq -r '.[] | " │ \(.code) │ \(.type | if . == "trial" then "试用卡 " elif . == "monthly" then "月卡 " elif . == "yearly" then "年卡 " else "终身卡 " end) │ \(.validDays)天 │ \(.remarks // "无备注 ") │"'
echo -e " └─────────────────────┴──────────┴────────┴──────────────┘"
echo "" echo ""
# 询问是否保存到文件 # 询问是否保存到文件
@ -138,16 +144,20 @@ if echo "$GENERATE_RESPONSE" | jq -e '.[0].code' > /dev/null 2>&1; then
if [ "$save_to_file" = "y" ]; then if [ "$save_to_file" = "y" ]; then
FILENAME="licenses_$(date +%Y%m%d_%H%M%S).txt" FILENAME="licenses_$(date +%Y%m%d_%H%M%S).txt"
echo "生成时间: $(date)" > "$FILENAME" {
echo "类型: $LICENSE_TYPE" >> "$FILENAME" echo "==============================="
echo "有效天数: $VALID_DAYS" >> "$FILENAME" echo " 卡密生成记录"
echo "数量: $count" >> "$FILENAME" echo "==============================="
echo "备注: $remarks" >> "$FILENAME" echo " 生成时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo "" >> "$FILENAME" echo " 类型: $LICENSE_TYPE"
echo "卡密列表:" >> "$FILENAME" echo " 有效天数: ${VALID_DAYS}"
echo "$GENERATE_RESPONSE" | jq -r '.[] | .code' >> "$FILENAME" echo " 数量: $ACTUAL_COUNT"
echo " 备注: ${remarks:-}"
echo -e "${GREEN}卡密已保存到: $FILENAME${NC}" echo "==============================="
echo ""
echo "$LICENSES" | jq -r '.[] | .code'
} > "$FILENAME"
echo -e "${GREEN}✓ 卡密已保存到: $FILENAME${NC}"
fi fi
# 显示统计信息 # 显示统计信息
@ -155,16 +165,27 @@ if echo "$GENERATE_RESPONSE" | jq -e '.[0].code' > /dev/null 2>&1; then
read -p "是否查看卡密统计信息?(y/n): " show_stats read -p "是否查看卡密统计信息?(y/n): " show_stats
if [ "$show_stats" = "y" ]; then if [ "$show_stats" = "y" ]; then
echo -e "${YELLOW}查询统计信息...${NC}"
STATS=$(curl -s -X GET "$BASE_URL/license/statistics" \ STATS=$(curl -s -X GET "$BASE_URL/license/statistics" \
-H "Authorization: Bearer $ADMIN_TOKEN") -H "Authorization: Bearer $ADMIN_TOKEN")
TOTAL=$(echo "$STATS" | jq -r '.data.total')
echo "$STATS" | jq '.' UNUSED=$(echo "$STATS" | jq -r '.data.unused')
ACTIVE=$(echo "$STATS" | jq -r '.data.active')
EXPIRED=$(echo "$STATS"| jq -r '.data.expired')
REVOKED=$(echo "$STATS"| jq -r '.data.revoked')
echo ""
echo -e " ${YELLOW}── 卡密统计 ──────────────────${NC}"
echo -e " 总数: $TOTAL"
echo -e " 未使用: ${GREEN}$UNUSED${NC}"
echo -e " 已激活: $ACTIVE"
echo -e " 已过期: $EXPIRED"
echo -e " 已撤销: ${RED}$REVOKED${NC}"
echo -e " ${YELLOW}──────────────────────────────${NC}"
fi fi
else else
echo -e "${RED}生成失败!${NC}" echo -e "${RED}✗ 生成失败!${NC}"
echo "$GENERATE_RESPONSE" | jq '.' ERR_MSG=$(echo "$GENERATE_RESPONSE" | jq -r '.message // .data // .')
echo -e " 原因: $ERR_MSG"
exit 1 exit 1
fi fi

View File

@ -14,11 +14,11 @@ export class AdminGuard implements CanActivate {
const request = context.switchToHttp().getRequest(); const request = context.switchToHttp().getRequest();
const user = request.user; const user = request.user;
if (!user || !user.userId) { if (!user || !user.id) {
throw new ForbiddenException('请先登录'); throw new ForbiddenException('请先登录');
} }
const isAdmin = await this.adminService.isAdmin(user.userId); const isAdmin = await this.adminService.isAdmin(user.id);
if (!isAdmin) { if (!isAdmin) {
throw new ForbiddenException('需要管理员权限'); throw new ForbiddenException('需要管理员权限');

View File

@ -1,7 +1,7 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm'; import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm'; import { Repository } from 'typeorm';
import { User } from '../user/entities/user.entity'; import { User } from '../../user/entities/user.entity';
@Injectable() @Injectable()
export class AdminService { export class AdminService {

View File

@ -28,12 +28,12 @@ export class LicenseGuard implements CanActivate {
const request = context.switchToHttp().getRequest(); const request = context.switchToHttp().getRequest();
const user = request.user; const user = request.user;
if (!user || !user.userId) { if (!user || !user.id) {
throw new ForbiddenException('请先登录'); throw new ForbiddenException('请先登录');
} }
const hasValidLicense = await this.licenseService.verifyUserLicense( const hasValidLicense = await this.licenseService.verifyUserLicense(
user.userId, user.id,
); );
if (!hasValidLicense) { if (!hasValidLicense) {