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_CLIENT_ID=Ov23lihk723FlNAwlFg6
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">
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="200" alt="Nest Logo" /></a>
</p>
# OAuth NestJS Demo
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
[circleci-url]: https://circleci.com/gh/nestjs/nest
A NestJS backend with multi-provider OAuth, JWT authentication, task management, and a license/activation key system.
<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>
<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)-->
## Tech Stack
## 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.
## Installation
## Getting Started
```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
# development
$ pnpm run start
# Generate license keys
bash scripts/generate-licenses.sh
# watch mode
$ pnpm run start:dev
# production mode
$ pnpm run start:prod
# Run license system tests
bash scripts/test-license-system.sh
```
## Test
## Running the App
```bash
# unit tests
$ pnpm run test
# Development
pnpm run start:dev
# e2e tests
$ pnpm run test:e2e
# test coverage
$ pnpm run test:cov
# Production build
pnpm run build
pnpm run start:prod
```
## 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)
- Website - [https://nestjs.com](https://nestjs.com/)
- Twitter - [@nestframework](https://twitter.com/nestframework)
# Coverage
pnpm run test:cov
```
## 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\"
}")
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
echo -e "${RED}注册失败!${NC}"
@ -64,7 +64,7 @@ else
\"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
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
echo -e "${GREEN}卡密生成成功!${NC}"
if echo "$GENERATE_RESPONSE" | jq -e '.data[0].code' > /dev/null 2>&1; then
LICENSES=$(echo "$GENERATE_RESPONSE" | jq -r '.data')
ACTUAL_COUNT=$(echo "$LICENSES" | jq 'length')
echo -e "${GREEN}✓ 卡密生成成功!共 $ACTUAL_COUNT${NC}"
echo ""
echo "生成的卡密:"
echo "$GENERATE_RESPONSE" | jq -r '.[] | " \(.code) - \(.type) (\(.validDays)天) - \(.remarks // "无备注")"'
echo -e " ┌─────────────────────┬──────────┬────────┬──────────────┐"
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 ""
# 询问是否保存到文件
@ -138,16 +144,20 @@ if echo "$GENERATE_RESPONSE" | jq -e '.[0].code' > /dev/null 2>&1; then
if [ "$save_to_file" = "y" ]; then
FILENAME="licenses_$(date +%Y%m%d_%H%M%S).txt"
echo "生成时间: $(date)" > "$FILENAME"
echo "类型: $LICENSE_TYPE" >> "$FILENAME"
echo "有效天数: $VALID_DAYS" >> "$FILENAME"
echo "数量: $count" >> "$FILENAME"
echo "备注: $remarks" >> "$FILENAME"
echo "" >> "$FILENAME"
echo "卡密列表:" >> "$FILENAME"
echo "$GENERATE_RESPONSE" | jq -r '.[] | .code' >> "$FILENAME"
echo -e "${GREEN}卡密已保存到: $FILENAME${NC}"
{
echo "==============================="
echo " 卡密生成记录"
echo "==============================="
echo " 生成时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo " 类型: $LICENSE_TYPE"
echo " 有效天数: ${VALID_DAYS}"
echo " 数量: $ACTUAL_COUNT"
echo " 备注: ${remarks:-}"
echo "==============================="
echo ""
echo "$LICENSES" | jq -r '.[] | .code'
} > "$FILENAME"
echo -e "${GREEN}✓ 卡密已保存到: $FILENAME${NC}"
fi
# 显示统计信息
@ -155,16 +165,27 @@ if echo "$GENERATE_RESPONSE" | jq -e '.[0].code' > /dev/null 2>&1; then
read -p "是否查看卡密统计信息?(y/n): " show_stats
if [ "$show_stats" = "y" ]; then
echo -e "${YELLOW}查询统计信息...${NC}"
STATS=$(curl -s -X GET "$BASE_URL/license/statistics" \
-H "Authorization: Bearer $ADMIN_TOKEN")
echo "$STATS" | jq '.'
TOTAL=$(echo "$STATS" | jq -r '.data.total')
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
else
echo -e "${RED}生成失败!${NC}"
echo "$GENERATE_RESPONSE" | jq '.'
echo -e "${RED}✗ 生成失败!${NC}"
ERR_MSG=$(echo "$GENERATE_RESPONSE" | jq -r '.message // .data // .')
echo -e " 原因: $ERR_MSG"
exit 1
fi

View File

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

View File

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

View File

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