feat: 整理项目结构
This commit is contained in:
parent
aa62486f21
commit
8cd2cca57a
2
.env
2
.env
|
|
@ -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
123
README.md
|
|
@ -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>
|
||||
<!--[](https://opencollective.com/nest#backer)
|
||||
[](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
|
||||
```
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
@ -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('需要管理员权限');
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue