Commit 18c4bf84 authored by mingyard's avatar mingyard

feat:auth:login register logout

parent f890cb0a
...@@ -6,12 +6,16 @@ ...@@ -6,12 +6,16 @@
"README.md" "README.md"
], ],
"words": [ "words": [
"activerecords",
"dotenv", "dotenv",
"healthz", "healthz",
"hset",
"Millis", "Millis",
"mingyard",
"nestjs", "nestjs",
"oidc", "oidc",
"pids", "pids",
"regtime",
"typeorm" "typeorm"
] ]
} }
...@@ -23,24 +23,25 @@ ...@@ -23,24 +23,25 @@
"test:e2e": "jest --config ./test/jest-e2e.json" "test:e2e": "jest --config ./test/jest-e2e.json"
}, },
"dependencies": { "dependencies": {
"@nestjs/cache-manager": "^2.3.0", "@nestjs-modules/ioredis": "^2.0.2",
"@nestjs/common": "^10.0.0", "@nestjs/common": "^10.0.0",
"@nestjs/core": "^10.0.0", "@nestjs/core": "^10.0.0",
"@nestjs/jwt": "^10.2.0",
"@nestjs/platform-express": "^10.4.15", "@nestjs/platform-express": "^10.4.15",
"@nestjs/swagger": "^8.1.0", "@nestjs/swagger": "^8.1.0",
"@nestjs/typeorm": "^10.0.2", "@nestjs/typeorm": "^10.0.2",
"axios": "^1.7.9", "axios": "^1.7.9",
"cache-manager": "^6.3.2",
"cache-manager-redis-yet": "^5.1.5",
"class-transformer": "^0.5.1", "class-transformer": "^0.5.1",
"class-validator": "^0.14.1", "class-validator": "^0.14.1",
"dotenv": "^16.4.7", "dotenv": "^16.4.7",
"ioredis": "^5.4.2",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mysql2": "^3.12.0", "mysql2": "^3.12.0",
"on-headers": "^1.0.2", "on-headers": "^1.0.2",
"redis": "^4.7.0", "redis": "^4.7.0",
"reflect-metadata": "^0.2.0", "reflect-metadata": "^0.2.0",
"rxjs": "^7.8.1", "rxjs": "^7.8.1",
"sha1": "^1.1.1",
"typeorm": "^0.3.20" "typeorm": "^0.3.20"
}, },
"devDependencies": { "devDependencies": {
......
...@@ -8,6 +8,8 @@ import { config } from './config'; ...@@ -8,6 +8,8 @@ import { config } from './config';
import { TypeOrmModule } from '@nestjs/typeorm'; import { TypeOrmModule } from '@nestjs/typeorm';
import { SystemController } from './controller/system.controller'; import { SystemController } from './controller/system.controller';
import { UserModule } from './controller/user/user.module'; import { UserModule } from './controller/user/user.module';
import { AuthModule } from './controller/auth/auth.module';
import { RedisModule } from '@nestjs-modules/ioredis';
@Module({ @Module({
imports: [ imports: [
...@@ -20,7 +22,12 @@ import { UserModule } from './controller/user/user.module'; ...@@ -20,7 +22,12 @@ import { UserModule } from './controller/user/user.module';
maxQueryExecutionTime: 200, maxQueryExecutionTime: 200,
logging: [config.database.logging], logging: [config.database.logging],
}), }),
RedisModule.forRoot({
type: 'single',
url: config.cache.redis.url,
}),
UserModule, UserModule,
AuthModule,
], ],
controllers: [SystemController], controllers: [SystemController],
}) })
......
import { CacheModule } from '@nestjs/cache-manager';
import { RedisClientOptions } from 'redis';
import { redisStore } from 'cache-manager-redis-yet';
import { Module } from '@nestjs/common';
import { config } from '../../config';
import { CacheService } from './cache.service';
@Module({
imports: [
CacheModule.register<RedisClientOptions>({
store: redisStore,
url: config.cache.redis.url,
isGlobal: true,
}),
],
providers: [CacheService],
exports: [CacheService],
})
export class AppCacheModule {}
import { Inject, Injectable } from '@nestjs/common';
import { Response } from 'express';
import { v4 } from 'uuid';
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import { RedisCache } from 'cache-manager-redis-yet';
import { IRequest } from '@/interface';
import { config } from '../../config';
// const EXPIRE_TIME_IN_SEC = 86400;
const EXPIRE_TIME_IN_SEC = 3600;
export function readKeyFromCookie(
type: string,
req: IRequest,
throwError = true,
) {
const key = req.cookies[type];
if (!key && throwError) {
throw new Error('Without ' + type);
}
return key;
}
export function saveKeyIntoCookie(
type: string,
key: string,
res: Response,
options: {
timeout?: number;
} = {},
) {
const { timeout = EXPIRE_TIME_IN_SEC } = options;
res.cookie(type, key, {
maxAge: timeout * 1000,
secure: true,
sameSite: 'none',
httpOnly: true,
signed: true,
});
}
export async function clearKeyFromCookie(type: string, res: Response) {
res.clearCookie(type, {
// domain: getSubDomainHost('').split(':')[0],
});
}
/**
* 负责在顶级域名下存储一次性的临时 session,用来在流程中的各端点之间传递状态信息
* 将redis的key保存在cookie中,将js对象转为string生成redis键值对
*/
@Injectable()
export class CacheService {
constructor(@Inject(CACHE_MANAGER) private cacheManager: RedisCache) {}
async setNx<T = Record<string, any>>(
type: string,
key: string,
value: T,
options: {
timeout?: number;
} = {},
) {
const { timeout = EXPIRE_TIME_IN_SEC } = options;
await this.cacheManager.store.client.set(
config.env + ':' + type + ':' + key,
JSON.stringify(value),
// 超时秒数
{ EX: timeout, NX: true },
);
}
/**
* 创建一个 Interaction,存储到 redis 中。
*
* @param type 类型
* @param value 值
* @param options 额外参数
* - timeout:过期时间,默认为 86400 秒
* - key:唯一标识,不传会自动生成
* @returns Interaction 的唯一标识
*/
async create<T = Record<string, any>>(
type: string,
key: string,
value: T,
options: {
timeout?: number;
} = {},
) {
const { timeout = EXPIRE_TIME_IN_SEC } = options;
await this.cacheManager.store.client.set(
config.env + ':' + type + ':' + key,
JSON.stringify(value),
// 超时秒数
{ EX: timeout },
);
return key;
}
/**
* 创建一个 Interaction,存储到 cookie 和 redis 中。
*
* @param type 类型
* @param value 值
* @param res 响应对象
* @param options 额外参数
* - timeout:过期时间,默认为 86400 秒
* - key:唯一标识,不传会自动生成`
* @returns Interaction 的唯一标识
*/
async createIntoCookie<T = Record<string, any>>(
type: string,
value: T,
res: Response,
options: {
timeout?: number;
key?: string;
} = {},
) {
const key = options.key ?? v4();
await this.create(type, key, value, options);
saveKeyIntoCookie(type, key, res, options);
return key;
}
/**
* 更新一个 Interaction 的值。
*
* @param type 类型
* @param value 值
* @param key 唯一标识
*/
async update<T = Record<string, any>>(
type: string,
key: string,
value: T,
options: {
timeout?: number;
} = {},
) {
await this.cacheManager.set(
config.env + ':' + type + ':' + key,
JSON.stringify(value),
options.timeout ?? EXPIRE_TIME_IN_SEC,
);
}
/**
* 读取缓存值。
*
* @param type 类型
* @param key 唯一标识
* @returns Interaction 的值
*/
async read<T = Record<string, any>>(type: string, key: string) {
const value = await this.cacheManager.get<T>(
config.env + ':' + type + ':' + key,
);
return value;
}
/**
* 清除一个 Interaction。
*
* @param type 类型
* @param key 唯一标识
*/
async clear(type: string, key: string) {
await this.cacheManager.set(config.env + ':' + type + ':' + key, 10);
}
}
import { UseGuards } from '@nestjs/common';
import { AuthGuard } from '../../controller/auth/auth.guard';
/**
* 认证 装饰器
*
*/
export const Auth = () => {
return (...args: any[]) => {
// 装饰器由上至下求值,由下至上执行
const decorators: any[] = [];
decorators.push(UseGuards(AuthGuard));
decorators.reverse().forEach((f) => f(...args));
};
};
import { ErrorCodeEnum } from '@/common/enum/ErrorCodeEnum';
import { UnauthorizedError } from './UnauthorizedError';
export class UserStatusError extends UnauthorizedError {
protected getErrorCode() {
return ErrorCodeEnum.EMAIL_NOT_VERIFIED;
}
static default(message?: string) {
return new UserStatusError(message ?? 'User is not active');
}
}
import { import {
ArgumentsHost, ArgumentsHost,
BadRequestException,
Catch, Catch,
ExceptionFilter, ExceptionFilter,
ForbiddenException,
Injectable, Injectable,
UnauthorizedException,
} from '@nestjs/common'; } from '@nestjs/common';
import { Response } from 'express'; import { Response } from 'express';
import { IRequest } from '@/interface'; import { IRequest } from '@/interface';
import { isArray, isString } from 'lodash'; import { BaseError } from '@/common/exception/BaseError';
@Injectable() @Injectable()
@Catch() @Catch()
...@@ -19,38 +16,14 @@ export class ApiExceptionsFilter implements ExceptionFilter { ...@@ -19,38 +16,14 @@ export class ApiExceptionsFilter implements ExceptionFilter {
const response = ctx.getResponse<Response>(); const response = ctx.getResponse<Response>();
const req = ctx.getRequest<IRequest>(); const req = ctx.getRequest<IRequest>();
if (exception instanceof ForbiddenException) { if (exception instanceof BaseError) {
return response.status(403).json({ const json = exception.toJson();
code: 403, return response.status(200).json({
message: 'Not Permission', code: json.code,
}); message: json.message,
} requestId: req.requestId,
if (exception instanceof UnauthorizedException) {
const statusCode = (exception as any)?.response?.statusCode || 400;
return response.status(401).json({
code: statusCode,
message: (exception as any)?.response?.message || 'Bad Request',
});
}
if (exception instanceof BadRequestException) {
const validationErrors = (exception as any)?.response?.message;
const statusCode = (exception as any)?.response?.statusCode || 400;
if (isArray(validationErrors) && validationErrors?.length) {
return response.status(statusCode).json({
code: statusCode,
message: validationErrors[0],
});
} else {
return response.status(statusCode).json({
code: statusCode,
message: (exception as any)?.response?.message || 'Bad Request',
}); });
} }
}
// 未知错误,触发错误报警 // 未知错误,触发错误报警
console.error('请求 HOST:' + req?.hostname); console.error('请求 HOST:' + req?.hostname);
...@@ -58,20 +31,21 @@ export class ApiExceptionsFilter implements ExceptionFilter { ...@@ -58,20 +31,21 @@ export class ApiExceptionsFilter implements ExceptionFilter {
console.error('请求参数 QUERY:' + JSON.stringify(req?.query)); console.error('请求参数 QUERY:' + JSON.stringify(req?.query));
console.error('请求参数 BODY:' + JSON.stringify(req?.body)); console.error('请求参数 BODY:' + JSON.stringify(req?.body));
console.error( console.error('\n***\n', '未知错误', exception);
'\n***\n', const status = exception?.status || exception?.statusCode || 500;
'未知错误',
JSON.stringify(exception?.message ?? exception),
);
const status = exception?.status || 500;
console.error('requestId:' + req.requestId); console.error('requestId:' + req.requestId);
const errorMessage = exception?.response?.message ?? exception.message;
return response.status(status || 500).json({ console.error(
code: status, 'errorMessage:' + errorMessage
message: isString(exception.message) ? errorMessage
? exception.message
: JSON.parse(exception.message), : JSON.parse(exception.message),
);
return response.status(200).json({
code: status,
message: errorMessage ? errorMessage : JSON.parse(exception.message),
requestId: req.requestId, requestId: req.requestId,
}); });
} }
......
...@@ -29,6 +29,11 @@ interface AppConfig { ...@@ -29,6 +29,11 @@ interface AppConfig {
secret: string; secret: string;
}; };
jwt: {
secret: string;
expiresIn: string;
};
rsaSecret: { rsaSecret: {
/** 应用公钥 */ /** 应用公钥 */
publicKey: string; publicKey: string;
...@@ -54,7 +59,12 @@ export const app: AppConfig = { ...@@ -54,7 +59,12 @@ export const app: AppConfig = {
}, },
session: { session: {
secret: env.APP_SESSION_SECRET ?? 'api-service', secret: env.APP_SESSION_SECRET ?? 'session',
},
jwt: {
secret: env.APP_JWT_SECRET ?? 'secret',
expiresIn: env.APP_JWT_EXPIRES_IN ?? '1d',
}, },
rsaSecret: { rsaSecret: {
......
import { Test, TestingModule } from '@nestjs/testing';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
describe('AuthController', () => {
let controller: AuthController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [AuthController],
providers: [AuthService],
}).compile();
controller = module.get<AuthController>(AuthController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
import {
Controller,
Post,
Body,
Req,
UseInterceptors,
UseFilters,
} from '@nestjs/common';
import { AuthService } from './auth.service';
import { LoginDto } from './dto/req/login.dto';
import { IRequest } from '@/interface';
import { LoginRecordDto } from './dto/loginRecord.dto';
import { ApiResponseInterceptor } from '@/common/interceptor/api.response.interceptor';
import { ApiExceptionsFilter } from '@/common/filter/api.exception.filter';
import { RegisterDto } from './dto/req/register.dto';
import { Auth } from '@/common/decorators/auth.decorator';
@Controller('auth')
@UseInterceptors(ApiResponseInterceptor)
@UseFilters(ApiExceptionsFilter)
export class AuthController {
constructor(private readonly authService: AuthService) {}
// 登录接口
@Post('login')
async login(@Body() loginDto: LoginDto, @Req() req: IRequest) {
const record: LoginRecordDto = {
userId: null,
ip: req.ip,
lastActiveTime: Math.floor(Date.now() / 1000),
level: 0,
platform: 9,
userAgent: req.userAgent,
};
// 调用服务层登录方法
return await this.authService.login(loginDto, record);
}
// 注册接口
@Post('register')
async register(@Body() registerDto: RegisterDto) {
return await this.authService.register(registerDto);
}
// 退出登录接口
@Auth()
@Post('logout')
async logout(@Req() req: IRequest) {
return await this.authService.logout(req.token, req.decodedToken.exp);
}
}
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { config } from '@/config';
import { Request } from 'express';
import { UnauthorizedError } from '@/common/exception/unauthorized/UnauthorizedError';
import { UserService } from '../user/user.service';
import { AuthService } from './auth.service';
@Injectable()
export class AuthGuard implements CanActivate {
constructor(
private jwtService: JwtService,
private authService: AuthService,
private readonly userService: UserService,
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
const token = this.extractTokenFromHeader(request);
if (!token) {
throw UnauthorizedError.default('Token not found');
}
try {
const payload = await this.jwtService.verifyAsync(token, {
secret: config.jwt.secret,
});
// 💡 We're assigning the payload to the request object here
// so that we can access it in our route handlers
request['decodedToken'] = payload;
request['token'] = token;
const isBlack = await this.authService.isTokenInBlackList(token);
if (isBlack) {
throw UnauthorizedError.default('Invalid token');
}
const user = await this.userService.getUserById(payload.sub);
if (!user) {
throw UnauthorizedError.default('User not found');
}
request['user'] = user;
} catch {
throw UnauthorizedError.default('Invalid token');
}
return true;
}
private extractTokenFromHeader(request: Request): string | undefined {
const [type, token] = request.headers.authorization?.split(' ') ?? [];
return type === 'Bearer' ? token : undefined;
}
}
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { JwtModule } from '@nestjs/jwt';
import { config } from '@/config';
import { ActiveRecordsEntity } from '@/entities/activeRecords.entity';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserService } from '../user/user.service';
import { AuthGuard } from './auth.guard';
import { UserModule } from '../user/user.module';
@Module({
imports: [
JwtModule.register({
global: true,
secret: config.jwt.secret,
signOptions: { expiresIn: config.jwt.expiresIn },
}),
TypeOrmModule.forFeature([ActiveRecordsEntity]),
UserModule,
],
controllers: [AuthController],
providers: [AuthService, UserService, AuthGuard],
exports: [AuthService, AuthGuard],
})
export class AuthModule {}
import { Test, TestingModule } from '@nestjs/testing';
import { AuthService } from './auth.service';
describe('AuthService', () => {
let service: AuthService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [AuthService],
}).compile();
service = module.get<AuthService>(AuthService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
import { Injectable } from '@nestjs/common';
import { LoginDto } from './dto/req/login.dto';
import { InjectRepository } from '@nestjs/typeorm';
import { UserEntity } from '../../entities/user.entity';
import { MoreThanOrEqual, Repository } from 'typeorm';
import * as sha1 from 'sha1';
import { UnauthorizedError } from '@/common/exception/unauthorized/UnauthorizedError';
import { ActiveRecordsEntity } from '@/entities/activeRecords.entity';
import { LoginRecordDto } from './dto/loginRecord.dto';
import { JwtService } from '@nestjs/jwt';
import { plainToClass } from 'class-transformer';
import { RegisterDto } from './dto/req/register.dto';
import { LoginResDto } from './dto/res/loginRes.dto';
import { UserService } from '../user/user.service';
import { InjectRedis } from '@nestjs-modules/ioredis';
import Redis from 'ioredis';
import { MD5 } from '@/common/utils/tool';
import { DecodedToken } from '@/interface';
@Injectable()
export class AuthService {
constructor(
@InjectRepository(ActiveRecordsEntity)
private readonly activeRecordsRepository: Repository<ActiveRecordsEntity>,
private readonly jwtService: JwtService,
private readonly userService: UserService,
@InjectRedis() private readonly redis: Redis,
) {}
// 登录
async login(
loginDto: LoginDto,
loginRecord: LoginRecordDto,
): Promise<LoginResDto> {
const { phone, password } = loginDto;
// 验证用户
const user = await this.validateUser(phone, password);
// 更新用户登录时间
await this.logSingIn(user);
// 记录登录信息
await this.loginRecords(user, loginRecord);
// jwt
const payload = { sub: user.id, username: user.phone };
const token = await this.jwtService.signAsync(payload);
// 转换并返回用户信息
return plainToClass(
LoginResDto,
{ ...user, token },
{ excludeExtraneousValues: true },
);
}
// 注册
async register(registerDto: RegisterDto): Promise<LoginResDto> {
// 校验 验证码
await this.verifyCode(registerDto.code);
// 校验手机号是否已注册
const exist = await this.userService.existByPhone(registerDto.phone);
if (exist) {
throw UnauthorizedError.default('手机号已注册');
}
// 创建用户
const user = await this.userService.createUser(registerDto);
// jwt
const payload = { sub: user.id, username: user.phone };
const token = await this.jwtService.signAsync(payload);
// 转换并返回用户信息
return plainToClass(
LoginResDto,
{ ...user, token },
{ excludeExtraneousValues: true },
);
}
// 登出
async logout(token: string, exp: number): Promise<void> {
// 将 token 添加到 Redis 黑名单中,并设置过期时间
const ttl = exp - Math.floor(Date.now() / 1000);
const key = this.getBlackListKey(token);
await this.redis.set(key, token, 'EX', ttl);
}
// 获取黑名单 Key
getBlackListKey(token: string): string {
return `translation:blacklist:${MD5(token)}`;
}
// 验证 token 是否在黑名单中
async isTokenInBlackList(token: string): Promise<boolean> {
const key = this.getBlackListKey(token);
const exists = await this.redis.exists(key);
return !!exists;
}
// 重置密码
async resetPwd(resetDto: RegisterDto): Promise<LoginResDto> {
// 校验 验证码
await this.verifyCode(resetDto.code);
// 校验手机号是否已注册
const exist = await this.userService.existByPhone(resetDto.phone);
if (!exist) {
throw UnauthorizedError.default('手机号未注册');
}
// 更新用户密码
const user = await this.userService.updatePwd(resetDto);
// jwt
const payload = { sub: user.id, username: user.phone };
const token = await this.jwtService.signAsync(payload);
// 转换并返回用户信息
return plainToClass(
LoginResDto,
{ ...user, token },
{ excludeExtraneousValues: true },
);
}
// 验证用户
async validateUser(phone: string, password: string): Promise<UserEntity> {
const user = await this.userService.getUserByPhone(phone);
if (user && user.pwd === sha1(password)) {
return user;
}
throw UnauthorizedError.default('用户名或密码错误');
}
// 更新用户登录时间
async logSingIn(user: UserEntity): Promise<void> {
user.lastLoginTime = Math.floor(Date.now() / 1000);
await this.userService.update(user);
}
// 添加登录日志
async loginRecords(
user: UserEntity,
loginRecord: LoginRecordDto,
): Promise<void> {
// 查询用户登录日志
const record = await this.activeRecordsRepository.findOne({
where: {
userId: user.id,
lastActiveTime: MoreThanOrEqual(new Date().setHours(0, 0, 0, 0) / 1000),
},
order: {
lastActiveTime: 'DESC',
},
});
loginRecord.userId = user.id;
loginRecord.level = user.level;
loginRecord.lastActiveTime = Math.floor(Date.now() / 1000);
if (record) {
// 更新记录
await this.activeRecordsRepository.update(
{
id: record.id,
},
{
...loginRecord,
times: (record.times || 0) + 1,
},
);
}
// 新增记录
await this.activeRecordsRepository.save({
...loginRecord,
});
}
// 校验验证码
async verifyCode(code: number): Promise<boolean> {
// TODO
return true;
}
}
export class LoginRecordDto {
userId: number | null;
ip: string;
lastActiveTime: number;
level: number;
platform: number;
userAgent: string;
}
import { IsString, IsNotEmpty, IsPhoneNumber } from 'class-validator';
export class LoginDto {
@IsNotEmpty()
@IsString()
@IsPhoneNumber('CN')
phone: string;
@IsNotEmpty()
@IsString()
password: string;
}
import {
IsString,
IsNotEmpty,
IsPhoneNumber,
IsNumber,
Matches,
} from 'class-validator';
export class RegisterDto {
@IsNotEmpty()
@IsString()
@IsPhoneNumber('CN')
phone: string;
@IsNotEmpty()
@IsNumber()
code: number;
@IsNotEmpty()
@IsString()
@Matches(/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/, {
message: '密码必须包含至少8个字符,包括字母和数字',
})
password: string;
}
import { Expose } from 'class-transformer';
import { UserBaseDto } from '../userBase.dto';
export class LoginResDto extends UserBaseDto {
@Expose()
token: string;
}
import { Expose } from 'class-transformer';
export class UserBaseDto {
@Expose()
id: number;
@Expose()
acc: string;
@Expose()
phone: string;
@Expose()
mailAddr: string;
@Expose()
lastLoginTime: number;
@Expose()
state: number;
@Expose()
nickname?: string;
}
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common'; import { Controller } from '@nestjs/common';
import { UserService } from './user.service'; import { UserService } from './user.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
@Controller('user') @Controller('user')
export class UserController { export class UserController {
constructor(private readonly userService: UserService) {} constructor(private readonly userService: UserService) {}
@Post()
create(@Body() createUserDto: CreateUserDto) {
return this.userService.create(createUserDto);
}
@Get()
findAll() {
return this.userService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.userService.findOne(+id);
}
@Patch(':id')
update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
return this.userService.update(+id, updateUserDto);
}
@Delete(':id')
remove(@Param('id') id: string) {
return this.userService.remove(+id);
}
} }
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { UserService } from './user.service'; import { UserService } from './user.service';
import { UserController } from './user.controller'; import { UserController } from './user.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserEntity } from '@/entities/user.entity';
import { UserInfoEntity } from '@/entities/userInfo.entity';
@Module({ @Module({
imports: [TypeOrmModule.forFeature([UserEntity, UserInfoEntity])],
controllers: [UserController], controllers: [UserController],
providers: [UserService], providers: [UserService],
exports: [UserService, TypeOrmModule],
}) })
export class UserModule {} export class UserModule {}
import { UserEntity } from '@/entities/user.entity';
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto'; import { InjectRepository } from '@nestjs/typeorm';
import { UpdateUserDto } from './dto/update-user.dto'; import { Repository } from 'typeorm';
import { RegisterDto } from '../auth/dto/req/register.dto';
import { v4 as uuid } from 'uuid';
import * as sha1 from 'sha1';
import { UserInfoEntity } from '@/entities/userInfo.entity';
import { UserBaseDto } from '../auth/dto/userBase.dto';
import { plainToClass } from 'class-transformer';
@Injectable() @Injectable()
export class UserService { export class UserService {
create(createUserDto: CreateUserDto) { constructor(
return 'This action adds a new user'; @InjectRepository(UserEntity)
private readonly userRepository: Repository<UserEntity>,
@InjectRepository(UserInfoEntity)
private readonly userInfoRepository: Repository<UserInfoEntity>,
) {}
// 用户是否存在
async existByPhone(phone: string): Promise<boolean> {
const user = await this.userRepository.findOne({ where: { phone } });
return !!user;
}
// 查找最大用户id
async findMaxUserId(): Promise<number | undefined> {
const result = await this.userRepository
.createQueryBuilder('user')
.select('MAX(user.id)', 'max')
.getRawOne();
return result ? result.max : undefined;
}
// 创建用户
async createUser({ phone, password }: RegisterDto): Promise<UserBaseDto> {
// 注册
const userEntity = new UserEntity();
userEntity.phone = phone;
userEntity.pwd = sha1(password);
// 注册类型 0: 手机号注册
userEntity.regType = 0;
userEntity.state = 0;
// 注册来源 18: 翻译机
userEntity.regSource = 18;
userEntity.regtime = Math.floor(Date.now() / 1000);
userEntity.uid = uuid();
const maxId = await this.findMaxUserId();
// userEntity.acc = (maxId + 100000000 + Math.random() * 100).toString();
userEntity.acc = `${maxId + 100000000}${Math.floor(Math.random() * 100)}`;
// 保存用户
const user = await this.userRepository.save(userEntity);
// 创建用户信息
const userInfo = await this.userInfoRepository.save({
userId: userEntity.id,
nickname: `InnAIO-${user.id * 11}`,
});
// 转换并返回用户信息
return plainToClass(
UserBaseDto,
{ ...userInfo, ...user },
{ excludeExtraneousValues: true },
);
}
// 重置密码
async updatePwd({ phone, password }: RegisterDto): Promise<UserEntity> {
const user = await this.userRepository.findOne({ where: { phone } });
if (user) {
user.pwd = sha1(password);
await this.userRepository.save(user);
} }
findAll() { return user;
return `This action returns all user`;
} }
findOne(id: number) { async getUserById(id: number): Promise<UserEntity> {
return `This action returns a #${id} user`; return await this.userRepository.findOneBy({ id });
} }
update(id: number, updateUserDto: UpdateUserDto) { async getUserByPhone(phone: string): Promise<UserEntity> {
return `This action updates a #${id} user`; return await this.userRepository.findOneBy({ phone });
} }
remove(id: number) { async update(user: UserEntity): Promise<void> {
return `This action removes a #${id} user`; await this.userRepository.update(user.id, user);
} }
} }
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity('activerecords')
export class ActiveRecordsEntity {
@PrimaryGeneratedColumn()
id: number;
@Column({
nullable: true,
name: 'userId',
comment: '用户id',
})
userId: number;
@Column({
nullable: true,
name: 'lastActiveTime',
comment: '上次活跃时间',
})
lastActiveTime: number;
@Column({
nullable: true,
name: 'state',
comment: '是否删除',
})
state: number;
@Column({
nullable: true,
name: 'ip',
comment: '当前ip',
})
ip: string;
@Column({
nullable: true,
name: 'platform',
comment: '平台',
})
platform: number;
@Column({
nullable: true,
name: 'userAgent',
comment: '客户端信息',
})
userAgent: string;
@Column({
nullable: true,
name: 'level',
comment: '会员级别',
})
level: number;
@Column({
nullable: true,
name: 'logoutTime',
comment: '离线时间',
})
logoutTime: number;
@Column({
nullable: true,
name: 'times',
comment: '登录次数',
})
times: number;
}
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity('user')
export class UserEntity {
@PrimaryGeneratedColumn()
id: number;
@Column({
nullable: false,
name: 'acc',
comment: '账号',
})
acc: string;
@Column({
nullable: true,
name: 'uid',
comment: '唯一id',
})
uid: string;
@Column({
nullable: false,
name: 'pwd',
comment: '密码',
})
pwd: string;
@Column({
nullable: true,
name: 'mailAddr',
comment: '邮箱',
})
mailAddr: string;
@Column({
nullable: true,
name: 'phone',
comment: '手机号',
})
phone: string;
@Column({
nullable: true,
name: 'state',
comment: '状态',
})
state: number;
@Column({
nullable: true,
name: 'regType',
comment: '注册类型',
})
regType: number;
@Column({
nullable: true,
name: 'mailValidate',
comment: '邮箱验证',
})
mailValidate: number;
@Column({
nullable: false,
name: 'type',
comment: '类型',
})
type: number;
@Column({
nullable: false,
name: 'regtime',
comment: '注册时间',
})
regtime: number;
@Column({
nullable: false,
name: 'regSource',
comment: '注册来源',
})
regSource: number;
@Column({
nullable: true,
name: 'lastLoginTime',
comment: '最后登录时间',
})
lastLoginTime: number;
@Column({
nullable: true,
name: 'level',
comment: '会员等级',
})
level: number;
}
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity('userinfo')
export class UserInfoEntity {
@PrimaryGeneratedColumn()
id: number;
@Column({
nullable: true,
name: 'nickname',
comment: '昵称',
})
nickname: string;
@Column({
nullable: true,
name: 'sex',
comment: '性别',
})
birth: string;
@Column({
nullable: true,
name: 'headImgUrl',
comment: '头像',
})
headImgUrl: string;
@Column({
nullable: true,
name: 'age',
comment: '年龄',
})
age: number;
@Column({
nullable: true,
name: 'username',
comment: '用户名',
})
username: string;
}
import { UserEntity } from '@/entities/user.entity';
import * as express from 'express'; import * as express from 'express';
// JSON 类型递归定义 // JSON 类型递归定义
...@@ -45,11 +46,14 @@ export interface IRequest extends express.Request { ...@@ -45,11 +46,14 @@ export interface IRequest extends express.Request {
userAgent: string; userAgent: string;
loggedIn: boolean; loggedIn: boolean;
user: UserEntity;
clientIp: string; clientIp: string;
device: string; device: string;
os: string; os: string;
octetString: string; octetString: string;
token: string; token: string;
decodedToken: DecodedToken;
lang: Lang; lang: Lang;
...@@ -81,6 +85,7 @@ export interface DecodedToken { ...@@ -81,6 +85,7 @@ export interface DecodedToken {
phone_number?: string; phone_number?: string;
phone_number_verified?: boolean; phone_number_verified?: boolean;
address?: JSONValue; address?: JSONValue;
exp: number;
} }
export type KeyValuePair = Record<string, any>; export type KeyValuePair = Record<string, any>;
...@@ -6,12 +6,21 @@ import * as onHeaders from 'on-headers'; ...@@ -6,12 +6,21 @@ import * as onHeaders from 'on-headers';
import { setupOpenApi } from './openapi'; import { setupOpenApi } from './openapi';
import { NestExpressApplication } from '@nestjs/platform-express'; import { NestExpressApplication } from '@nestjs/platform-express';
import { config } from './config'; import { config } from './config';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() { async function bootstrap() {
const app = await NestFactory.create<NestExpressApplication>(AppModule, { const app = await NestFactory.create<NestExpressApplication>(AppModule, {
rawBody: true, rawBody: true,
}); });
app.enableCors({
credentials: true,
origin: config.cors.origin,
maxAge: 604800,
});
app.useGlobalPipes(new ValidationPipe());
app.use((req: IRequest, res: Response, next) => { app.use((req: IRequest, res: Response, next) => {
onHeaders(res, () => { onHeaders(res, () => {
res.setHeader('Cache-Control', 'no-cache'); res.setHeader('Cache-Control', 'no-cache');
......
import { Injectable } from '@nestjs/common';
import { UserEntity } from '../entities/user.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
@Injectable()
export class UserProvider {
constructor(
@InjectRepository(UserEntity)
private userRepository: Repository<UserEntity>,
) {}
async getUserById(id: number): Promise<UserEntity> {
return await this.userRepository.findOneBy({ id });
}
}
...@@ -371,6 +371,11 @@ ...@@ -371,6 +371,11 @@
resolved "https://registry.npmmirror.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" resolved "https://registry.npmmirror.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3"
integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==
"@ioredis/commands@^1.1.1":
version "1.2.0"
resolved "https://registry.npmmirror.com/@ioredis/commands/-/commands-1.2.0.tgz#6d61b3097470af1fdbbe622795b8921d42018e11"
integrity sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==
"@isaacs/cliui@^8.0.2": "@isaacs/cliui@^8.0.2":
version "8.0.2" version "8.0.2"
resolved "https://registry.npmmirror.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" resolved "https://registry.npmmirror.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550"
...@@ -639,13 +644,6 @@ ...@@ -639,13 +644,6 @@
"@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/resolve-uri" "^3.1.0"
"@jridgewell/sourcemap-codec" "^1.4.14" "@jridgewell/sourcemap-codec" "^1.4.14"
"@keyv/serialize@^1.0.2":
version "1.0.2"
resolved "https://registry.npmmirror.com/@keyv/serialize/-/serialize-1.0.2.tgz#72507c4be94d8914434a4aa80661f8ac6131967f"
integrity sha512-+E/LyaAeuABniD/RvUezWVXKpeuvwLEA9//nE9952zBaOdBd2mQ3pPoM8cUe2X6IcMByfuSLzmYqnYshG60+HQ==
dependencies:
buffer "^6.0.3"
"@ljharb/through@^2.3.12": "@ljharb/through@^2.3.12":
version "2.3.13" version "2.3.13"
resolved "https://registry.npmmirror.com/@ljharb/through/-/through-2.3.13.tgz#b7e4766e0b65aa82e529be945ab078de79874edc" resolved "https://registry.npmmirror.com/@ljharb/through/-/through-2.3.13.tgz#b7e4766e0b65aa82e529be945ab078de79874edc"
...@@ -663,10 +661,12 @@ ...@@ -663,10 +661,12 @@
resolved "https://registry.npmmirror.com/@microsoft/tsdoc/-/tsdoc-0.15.1.tgz#d4f6937353bc4568292654efb0a0e0532adbcba2" resolved "https://registry.npmmirror.com/@microsoft/tsdoc/-/tsdoc-0.15.1.tgz#d4f6937353bc4568292654efb0a0e0532adbcba2"
integrity sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw== integrity sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==
"@nestjs/cache-manager@^2.3.0": "@nestjs-modules/ioredis@^2.0.2":
version "2.3.0" version "2.0.2"
resolved "https://registry.npmmirror.com/@nestjs/cache-manager/-/cache-manager-2.3.0.tgz#e318ba56856c89b34f158efe9ca749fe3886dfa9" resolved "https://registry.npmmirror.com/@nestjs-modules/ioredis/-/ioredis-2.0.2.tgz#b1799944c4932e26d24a930caad83cc7af34601d"
integrity sha512-pxeBp9w/s99HaW2+pezM1P3fLiWmUEnTUoUMLa9UYViCtjj0E0A19W/vaT5JFACCzFIeNrwH4/16jkpAhQ25Vw== integrity sha512-8pzSvT8R3XP6p8ZzQmEN8OnY0yWrJ/elFhwQK+PID2zf1SLBkAZ18bDcx3SKQ2atledt0gd9kBeP5xT4MlyS7Q==
optionalDependencies:
"@nestjs/terminus" "10.2.0"
"@nestjs/cli@^10.0.0": "@nestjs/cli@^10.0.0":
version "10.4.9" version "10.4.9"
...@@ -714,6 +714,14 @@ ...@@ -714,6 +714,14 @@
path-to-regexp "3.3.0" path-to-regexp "3.3.0"
tslib "2.8.1" tslib "2.8.1"
"@nestjs/jwt@^10.2.0":
version "10.2.0"
resolved "https://registry.npmmirror.com/@nestjs/jwt/-/jwt-10.2.0.tgz#6aa35a04922d19c6426efced4671620f92e6dbd0"
integrity sha512-x8cG90SURkEiLOehNaN2aRlotxT0KZESUliOPKKnjWiyJOcWurkF3w345WOX0P4MgFzUjGoZ1Sy0aZnxeihT0g==
dependencies:
"@types/jsonwebtoken" "9.0.5"
jsonwebtoken "9.0.2"
"@nestjs/mapped-types@2.0.6": "@nestjs/mapped-types@2.0.6":
version "2.0.6" version "2.0.6"
resolved "https://registry.npmmirror.com/@nestjs/mapped-types/-/mapped-types-2.0.6.tgz#d2d8523709fd5d872a9b9e0c38162746e2a7f44e" resolved "https://registry.npmmirror.com/@nestjs/mapped-types/-/mapped-types-2.0.6.tgz#d2d8523709fd5d872a9b9e0c38162746e2a7f44e"
...@@ -753,6 +761,14 @@ ...@@ -753,6 +761,14 @@
path-to-regexp "3.3.0" path-to-regexp "3.3.0"
swagger-ui-dist "5.18.2" swagger-ui-dist "5.18.2"
"@nestjs/terminus@10.2.0":
version "10.2.0"
resolved "https://registry.npmmirror.com/@nestjs/terminus/-/terminus-10.2.0.tgz#3776ac21d2b1a1b8ce0a60e7f51dd6c498ca1de6"
integrity sha512-zPs98xvJ4ogEimRQOz8eU90mb7z+W/kd/mL4peOgrJ/VqER+ibN2Cboj65uJZW3XuNhpOqaeYOJte86InJd44A==
dependencies:
boxen "5.1.2"
check-disk-space "3.4.0"
"@nestjs/testing@^10.0.0": "@nestjs/testing@^10.0.0":
version "10.4.15" version "10.4.15"
resolved "https://registry.npmmirror.com/@nestjs/testing/-/testing-10.4.15.tgz#4c9fe17bf026c0142040cbe3db464c526f89d36a" resolved "https://registry.npmmirror.com/@nestjs/testing/-/testing-10.4.15.tgz#4c9fe17bf026c0142040cbe3db464c526f89d36a"
...@@ -807,12 +823,12 @@ ...@@ -807,12 +823,12 @@
resolved "https://registry.npmmirror.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31" resolved "https://registry.npmmirror.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31"
integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA== integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==
"@redis/bloom@1.2.0", "@redis/bloom@^1.2.0": "@redis/bloom@1.2.0":
version "1.2.0" version "1.2.0"
resolved "https://registry.npmmirror.com/@redis/bloom/-/bloom-1.2.0.tgz#d3fd6d3c0af3ef92f26767b56414a370c7b63b71" resolved "https://registry.npmmirror.com/@redis/bloom/-/bloom-1.2.0.tgz#d3fd6d3c0af3ef92f26767b56414a370c7b63b71"
integrity sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg== integrity sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==
"@redis/client@1.6.0", "@redis/client@^1.6.0": "@redis/client@1.6.0":
version "1.6.0" version "1.6.0"
resolved "https://registry.npmmirror.com/@redis/client/-/client-1.6.0.tgz#dcf4ae1319763db6fdddd6de7f0af68a352c30ea" resolved "https://registry.npmmirror.com/@redis/client/-/client-1.6.0.tgz#dcf4ae1319763db6fdddd6de7f0af68a352c30ea"
integrity sha512-aR0uffYI700OEEH4gYnitAnv3vzVGXCFvYfdpu/CJKvk4pHfLPEy/JSZyrpQ+15WhXe1yJRXLtfQ84s4mEXnPg== integrity sha512-aR0uffYI700OEEH4gYnitAnv3vzVGXCFvYfdpu/CJKvk4pHfLPEy/JSZyrpQ+15WhXe1yJRXLtfQ84s4mEXnPg==
...@@ -821,22 +837,22 @@ ...@@ -821,22 +837,22 @@
generic-pool "3.9.0" generic-pool "3.9.0"
yallist "4.0.0" yallist "4.0.0"
"@redis/graph@1.1.1", "@redis/graph@^1.1.1": "@redis/graph@1.1.1":
version "1.1.1" version "1.1.1"
resolved "https://registry.npmmirror.com/@redis/graph/-/graph-1.1.1.tgz#8c10df2df7f7d02741866751764031a957a170ea" resolved "https://registry.npmmirror.com/@redis/graph/-/graph-1.1.1.tgz#8c10df2df7f7d02741866751764031a957a170ea"
integrity sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw== integrity sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==
"@redis/json@1.0.7", "@redis/json@^1.0.7": "@redis/json@1.0.7":
version "1.0.7" version "1.0.7"
resolved "https://registry.npmmirror.com/@redis/json/-/json-1.0.7.tgz#016257fcd933c4cbcb9c49cde8a0961375c6893b" resolved "https://registry.npmmirror.com/@redis/json/-/json-1.0.7.tgz#016257fcd933c4cbcb9c49cde8a0961375c6893b"
integrity sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ== integrity sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==
"@redis/search@1.2.0", "@redis/search@^1.2.0": "@redis/search@1.2.0":
version "1.2.0" version "1.2.0"
resolved "https://registry.npmmirror.com/@redis/search/-/search-1.2.0.tgz#50976fd3f31168f585666f7922dde111c74567b8" resolved "https://registry.npmmirror.com/@redis/search/-/search-1.2.0.tgz#50976fd3f31168f585666f7922dde111c74567b8"
integrity sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw== integrity sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==
"@redis/time-series@1.1.0", "@redis/time-series@^1.1.0": "@redis/time-series@1.1.0":
version "1.1.0" version "1.1.0"
resolved "https://registry.npmmirror.com/@redis/time-series/-/time-series-1.1.0.tgz#cba454c05ec201bd5547aaf55286d44682ac8eb5" resolved "https://registry.npmmirror.com/@redis/time-series/-/time-series-1.1.0.tgz#cba454c05ec201bd5547aaf55286d44682ac8eb5"
integrity sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g== integrity sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==
...@@ -1028,6 +1044,13 @@ ...@@ -1028,6 +1044,13 @@
resolved "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" resolved "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
"@types/jsonwebtoken@9.0.5":
version "9.0.5"
resolved "https://registry.npmmirror.com/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz#0bd9b841c9e6c5a937c17656e2368f65da025588"
integrity sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==
dependencies:
"@types/node" "*"
"@types/methods@^1.1.4": "@types/methods@^1.1.4":
version "1.1.4" version "1.1.4"
resolved "https://registry.npmmirror.com/@types/methods/-/methods-1.1.4.tgz#d3b7ac30ac47c91054ea951ce9eed07b1051e547" resolved "https://registry.npmmirror.com/@types/methods/-/methods-1.1.4.tgz#d3b7ac30ac47c91054ea951ce9eed07b1051e547"
...@@ -1410,6 +1433,13 @@ ajv@^8.0.0, ajv@^8.9.0: ...@@ -1410,6 +1433,13 @@ ajv@^8.0.0, ajv@^8.9.0:
json-schema-traverse "^1.0.0" json-schema-traverse "^1.0.0"
require-from-string "^2.0.2" require-from-string "^2.0.2"
ansi-align@^3.0.0:
version "3.0.1"
resolved "https://registry.npmmirror.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59"
integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==
dependencies:
string-width "^4.1.0"
ansi-colors@4.1.3: ansi-colors@4.1.3:
version "4.1.3" version "4.1.3"
resolved "https://registry.npmmirror.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" resolved "https://registry.npmmirror.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b"
...@@ -1633,6 +1663,20 @@ body-parser@1.20.3: ...@@ -1633,6 +1663,20 @@ body-parser@1.20.3:
type-is "~1.6.18" type-is "~1.6.18"
unpipe "1.0.0" unpipe "1.0.0"
boxen@5.1.2:
version "5.1.2"
resolved "https://registry.npmmirror.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50"
integrity sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==
dependencies:
ansi-align "^3.0.0"
camelcase "^6.2.0"
chalk "^4.1.0"
cli-boxes "^2.2.1"
string-width "^4.2.2"
type-fest "^0.20.2"
widest-line "^3.1.0"
wrap-ansi "^7.0.0"
brace-expansion@^1.1.7: brace-expansion@^1.1.7:
version "1.1.11" version "1.1.11"
resolved "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" resolved "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
...@@ -1679,6 +1723,11 @@ bser@2.1.1: ...@@ -1679,6 +1723,11 @@ bser@2.1.1:
dependencies: dependencies:
node-int64 "^0.4.0" node-int64 "^0.4.0"
buffer-equal-constant-time@1.0.1:
version "1.0.1"
resolved "https://registry.npmmirror.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==
buffer-from@^1.0.0: buffer-from@^1.0.0:
version "1.1.2" version "1.1.2"
resolved "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" resolved "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
...@@ -1712,37 +1761,6 @@ bytes@3.1.2: ...@@ -1712,37 +1761,6 @@ bytes@3.1.2:
resolved "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" resolved "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
cache-manager-redis-yet@^5.1.5:
version "5.1.5"
resolved "https://registry.npmmirror.com/cache-manager-redis-yet/-/cache-manager-redis-yet-5.1.5.tgz#9175287ca0f7efa9a2d3b76d51b7825a98629f80"
integrity sha512-NYDxrWBoLXxxVPw4JuBriJW0f45+BVOAsgLiozRo4GoJQyoKPbueQWYStWqmO73/AeHJeWrV7Hzvk6vhCGHlqA==
dependencies:
"@redis/bloom" "^1.2.0"
"@redis/client" "^1.6.0"
"@redis/graph" "^1.1.1"
"@redis/json" "^1.0.7"
"@redis/search" "^1.2.0"
"@redis/time-series" "^1.1.0"
cache-manager "^5.7.6"
redis "^4.7.0"
cache-manager@^5.7.6:
version "5.7.6"
resolved "https://registry.npmmirror.com/cache-manager/-/cache-manager-5.7.6.tgz#bdd8a154c73e5233824aa09ceb359ed225d37b7e"
integrity sha512-wBxnBHjDxF1RXpHCBD6HGvKER003Ts7IIm0CHpggliHzN1RZditb7rXoduE1rplc2DEFYKxhLKgFuchXMJje9w==
dependencies:
eventemitter3 "^5.0.1"
lodash.clonedeep "^4.5.0"
lru-cache "^10.2.2"
promise-coalesce "^1.1.2"
cache-manager@^6.3.2:
version "6.3.2"
resolved "https://registry.npmmirror.com/cache-manager/-/cache-manager-6.3.2.tgz#49275f3f52da07c5f1ff212f706fde0e26f2580b"
integrity sha512-VmLouPUrvpm9dfwYB6OE7YVXDZ7BCfbt7hq10EHiBYaW9K9ZthK1bbjDQAtXGDK7d9u8t4G/7dMWSJOwN33msg==
dependencies:
keyv "^5.2.3"
call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1: call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz#32e5892e6361b29b0b545ba6f7763378daca2840" resolved "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz#32e5892e6361b29b0b545ba6f7763378daca2840"
...@@ -1812,6 +1830,16 @@ chardet@^0.7.0: ...@@ -1812,6 +1830,16 @@ chardet@^0.7.0:
resolved "https://registry.npmmirror.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" resolved "https://registry.npmmirror.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
"charenc@>= 0.0.1":
version "0.0.2"
resolved "https://registry.npmmirror.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==
check-disk-space@3.4.0:
version "3.4.0"
resolved "https://registry.npmmirror.com/check-disk-space/-/check-disk-space-3.4.0.tgz#eb8e69eee7a378fd12e35281b8123a8b4c4a8ff7"
integrity sha512-drVkSqfwA+TvuEhFipiR1OC9boEGZL5RrWvVsOthdcvQNXyCCuKkEiTOTXZ7qxSf/GLwq4GvzfrQD/Wz325hgw==
chokidar@3.6.0, chokidar@^3.5.3: chokidar@3.6.0, chokidar@^3.5.3:
version "3.6.0" version "3.6.0"
resolved "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" resolved "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b"
...@@ -1856,6 +1884,11 @@ class-validator@^0.14.1: ...@@ -1856,6 +1884,11 @@ class-validator@^0.14.1:
libphonenumber-js "^1.10.53" libphonenumber-js "^1.10.53"
validator "^13.9.0" validator "^13.9.0"
cli-boxes@^2.2.1:
version "2.2.1"
resolved "https://registry.npmmirror.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f"
integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==
cli-cursor@^3.1.0: cli-cursor@^3.1.0:
version "3.1.0" version "3.1.0"
resolved "https://registry.npmmirror.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" resolved "https://registry.npmmirror.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307"
...@@ -1922,7 +1955,7 @@ clone@^1.0.2: ...@@ -1922,7 +1955,7 @@ clone@^1.0.2:
resolved "https://registry.npmmirror.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" resolved "https://registry.npmmirror.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==
cluster-key-slot@1.1.2: cluster-key-slot@1.1.2, cluster-key-slot@^1.1.0:
version "1.1.2" version "1.1.2"
resolved "https://registry.npmmirror.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz#88ddaa46906e303b5de30d3153b7d9fe0a0c19ac" resolved "https://registry.npmmirror.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz#88ddaa46906e303b5de30d3153b7d9fe0a0c19ac"
integrity sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA== integrity sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==
...@@ -2084,6 +2117,11 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: ...@@ -2084,6 +2117,11 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
shebang-command "^2.0.0" shebang-command "^2.0.0"
which "^2.0.1" which "^2.0.1"
"crypt@>= 0.0.1":
version "0.0.2"
resolved "https://registry.npmmirror.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==
dayjs@^1.11.9: dayjs@^1.11.9:
version "1.11.13" version "1.11.13"
resolved "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c" resolved "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c"
...@@ -2203,6 +2241,13 @@ eastasianwidth@^0.2.0: ...@@ -2203,6 +2241,13 @@ eastasianwidth@^0.2.0:
resolved "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" resolved "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb"
integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
ecdsa-sig-formatter@1.0.11:
version "1.0.11"
resolved "https://registry.npmmirror.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf"
integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==
dependencies:
safe-buffer "^5.0.1"
ee-first@1.1.1: ee-first@1.1.1:
version "1.1.1" version "1.1.1"
resolved "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" resolved "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
...@@ -2438,11 +2483,6 @@ etag@~1.8.1: ...@@ -2438,11 +2483,6 @@ etag@~1.8.1:
resolved "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" resolved "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
eventemitter3@^5.0.1:
version "5.0.1"
resolved "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4"
integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==
events@^3.2.0: events@^3.2.0:
version "3.3.0" version "3.3.0"
resolved "https://registry.npmmirror.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" resolved "https://registry.npmmirror.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
...@@ -3025,6 +3065,21 @@ inquirer@9.2.15: ...@@ -3025,6 +3065,21 @@ inquirer@9.2.15:
strip-ansi "^6.0.1" strip-ansi "^6.0.1"
wrap-ansi "^6.2.0" wrap-ansi "^6.2.0"
ioredis@^5.4.2:
version "5.4.2"
resolved "https://registry.npmmirror.com/ioredis/-/ioredis-5.4.2.tgz#ebb6f1a10b825b2c0fb114763d7e82114a0bee6c"
integrity sha512-0SZXGNGZ+WzISQ67QDyZ2x0+wVxjjUndtD8oSeik/4ajifeiRufed8fCb8QW8VMyi4MXcS+UO1k/0NGhvq1PAg==
dependencies:
"@ioredis/commands" "^1.1.1"
cluster-key-slot "^1.1.0"
debug "^4.3.4"
denque "^2.1.0"
lodash.defaults "^4.2.0"
lodash.isarguments "^3.1.0"
redis-errors "^1.2.0"
redis-parser "^3.0.0"
standard-as-callback "^2.1.0"
ipaddr.js@1.9.1: ipaddr.js@1.9.1:
version "1.9.1" version "1.9.1"
resolved "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" resolved "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
...@@ -3629,6 +3684,39 @@ jsonfile@^6.0.1: ...@@ -3629,6 +3684,39 @@ jsonfile@^6.0.1:
optionalDependencies: optionalDependencies:
graceful-fs "^4.1.6" graceful-fs "^4.1.6"
jsonwebtoken@9.0.2:
version "9.0.2"
resolved "https://registry.npmmirror.com/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz#65ff91f4abef1784697d40952bb1998c504caaf3"
integrity sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==
dependencies:
jws "^3.2.2"
lodash.includes "^4.3.0"
lodash.isboolean "^3.0.3"
lodash.isinteger "^4.0.4"
lodash.isnumber "^3.0.3"
lodash.isplainobject "^4.0.6"
lodash.isstring "^4.0.1"
lodash.once "^4.0.0"
ms "^2.1.1"
semver "^7.5.4"
jwa@^1.4.1:
version "1.4.1"
resolved "https://registry.npmmirror.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a"
integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==
dependencies:
buffer-equal-constant-time "1.0.1"
ecdsa-sig-formatter "1.0.11"
safe-buffer "^5.0.1"
jws@^3.2.2:
version "3.2.2"
resolved "https://registry.npmmirror.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304"
integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==
dependencies:
jwa "^1.4.1"
safe-buffer "^5.0.1"
keyv@^4.5.3: keyv@^4.5.3:
version "4.5.4" version "4.5.4"
resolved "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" resolved "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
...@@ -3636,13 +3724,6 @@ keyv@^4.5.3: ...@@ -3636,13 +3724,6 @@ keyv@^4.5.3:
dependencies: dependencies:
json-buffer "3.0.1" json-buffer "3.0.1"
keyv@^5.2.3:
version "5.2.3"
resolved "https://registry.npmmirror.com/keyv/-/keyv-5.2.3.tgz#32db1a4aa8d05e2b8ab82688a57ddc5d2184a25c"
integrity sha512-AGKecUfzrowabUv0bH1RIR5Vf7w+l4S3xtQAypKaUpTdIR1EbrAcTxHCrpo9Q+IWeUlFE2palRtgIQcgm+PQJw==
dependencies:
"@keyv/serialize" "^1.0.2"
kleur@^3.0.3: kleur@^3.0.3:
version "3.0.3" version "3.0.3"
resolved "https://registry.npmmirror.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" resolved "https://registry.npmmirror.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
...@@ -3690,10 +3771,45 @@ locate-path@^6.0.0: ...@@ -3690,10 +3771,45 @@ locate-path@^6.0.0:
dependencies: dependencies:
p-locate "^5.0.0" p-locate "^5.0.0"
lodash.clonedeep@^4.5.0: lodash.defaults@^4.2.0:
version "4.5.0" version "4.2.0"
resolved "https://registry.npmmirror.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" resolved "https://registry.npmmirror.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c"
integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ== integrity sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==
lodash.includes@^4.3.0:
version "4.3.0"
resolved "https://registry.npmmirror.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
integrity sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==
lodash.isarguments@^3.1.0:
version "3.1.0"
resolved "https://registry.npmmirror.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a"
integrity sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==
lodash.isboolean@^3.0.3:
version "3.0.3"
resolved "https://registry.npmmirror.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6"
integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==
lodash.isinteger@^4.0.4:
version "4.0.4"
resolved "https://registry.npmmirror.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343"
integrity sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==
lodash.isnumber@^3.0.3:
version "3.0.3"
resolved "https://registry.npmmirror.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc"
integrity sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==
lodash.isplainobject@^4.0.6:
version "4.0.6"
resolved "https://registry.npmmirror.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==
lodash.isstring@^4.0.1:
version "4.0.1"
resolved "https://registry.npmmirror.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==
lodash.memoize@^4.1.2: lodash.memoize@^4.1.2:
version "4.1.2" version "4.1.2"
...@@ -3705,6 +3821,11 @@ lodash.merge@^4.6.2: ...@@ -3705,6 +3821,11 @@ lodash.merge@^4.6.2:
resolved "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" resolved "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
lodash.once@^4.0.0:
version "4.1.1"
resolved "https://registry.npmmirror.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==
lodash@4.17.21, lodash@^4.17.21: lodash@4.17.21, lodash@^4.17.21:
version "4.17.21" version "4.17.21"
resolved "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" resolved "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
...@@ -3723,7 +3844,7 @@ long@^5.2.1: ...@@ -3723,7 +3844,7 @@ long@^5.2.1:
resolved "https://registry.npmmirror.com/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1" resolved "https://registry.npmmirror.com/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1"
integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q== integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==
lru-cache@^10.2.0, lru-cache@^10.2.2: lru-cache@^10.2.0:
version "10.4.3" version "10.4.3"
resolved "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" resolved "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119"
integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==
...@@ -3891,7 +4012,7 @@ ms@2.0.0: ...@@ -3891,7 +4012,7 @@ ms@2.0.0:
resolved "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" resolved "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
ms@2.1.3, ms@^2.1.3: ms@2.1.3, ms@^2.1.1, ms@^2.1.3:
version "2.1.3" version "2.1.3"
resolved "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" resolved "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
...@@ -4257,11 +4378,6 @@ process-nextick-args@~2.0.0: ...@@ -4257,11 +4378,6 @@ process-nextick-args@~2.0.0:
resolved "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" resolved "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
promise-coalesce@^1.1.2:
version "1.1.2"
resolved "https://registry.npmmirror.com/promise-coalesce/-/promise-coalesce-1.1.2.tgz#5d3bc4d0b2cf2e41e9df7cbeb6519b2a09459e3d"
integrity sha512-zLaJ9b8hnC564fnJH6NFSOGZYYdzrAJn2JUUIwzoQb32fG2QAakpDNM+CZo1km6keXkRXRM+hml1BFAPVnPkxg==
prompts@^2.0.1: prompts@^2.0.1:
version "2.4.2" version "2.4.2"
resolved "https://registry.npmmirror.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" resolved "https://registry.npmmirror.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069"
...@@ -4368,6 +4484,18 @@ readdirp@~3.6.0: ...@@ -4368,6 +4484,18 @@ readdirp@~3.6.0:
dependencies: dependencies:
picomatch "^2.2.1" picomatch "^2.2.1"
redis-errors@^1.0.0, redis-errors@^1.2.0:
version "1.2.0"
resolved "https://registry.npmmirror.com/redis-errors/-/redis-errors-1.2.0.tgz#eb62d2adb15e4eaf4610c04afe1529384250abad"
integrity sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==
redis-parser@^3.0.0:
version "3.0.0"
resolved "https://registry.npmmirror.com/redis-parser/-/redis-parser-3.0.0.tgz#b66d828cdcafe6b4b8a428a7def4c6bcac31c8b4"
integrity sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==
dependencies:
redis-errors "^1.0.0"
redis@^4.7.0: redis@^4.7.0:
version "4.7.0" version "4.7.0"
resolved "https://registry.npmmirror.com/redis/-/redis-4.7.0.tgz#b401787514d25dd0cfc22406d767937ba3be55d6" resolved "https://registry.npmmirror.com/redis/-/redis-4.7.0.tgz#b401787514d25dd0cfc22406d767937ba3be55d6"
...@@ -4585,6 +4713,14 @@ sha.js@^2.4.11: ...@@ -4585,6 +4713,14 @@ sha.js@^2.4.11:
inherits "^2.0.1" inherits "^2.0.1"
safe-buffer "^5.0.1" safe-buffer "^5.0.1"
sha1@^1.1.1:
version "1.1.1"
resolved "https://registry.npmmirror.com/sha1/-/sha1-1.1.1.tgz#addaa7a93168f393f19eb2b15091618e2700f848"
integrity sha512-dZBS6OrMjtgVkopB1Gmo4RQCDKiZsqcpAQpkV/aaj+FCrCg8r4I4qMkDPQjBgLIxlmu9k4nUbWq6ohXahOneYA==
dependencies:
charenc ">= 0.0.1"
crypt ">= 0.0.1"
shebang-command@^2.0.0: shebang-command@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" resolved "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
...@@ -4700,6 +4836,11 @@ stack-utils@^2.0.3: ...@@ -4700,6 +4836,11 @@ stack-utils@^2.0.3:
dependencies: dependencies:
escape-string-regexp "^2.0.0" escape-string-regexp "^2.0.0"
standard-as-callback@^2.1.0:
version "2.1.0"
resolved "https://registry.npmmirror.com/standard-as-callback/-/standard-as-callback-2.1.0.tgz#8953fc05359868a77b5b9739a665c5977bb7df45"
integrity sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==
statuses@2.0.1: statuses@2.0.1:
version "2.0.1" version "2.0.1"
resolved "https://registry.npmmirror.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" resolved "https://registry.npmmirror.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
...@@ -4727,7 +4868,7 @@ string-length@^4.0.1: ...@@ -4727,7 +4868,7 @@ string-length@^4.0.1:
is-fullwidth-code-point "^3.0.0" is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1" strip-ansi "^6.0.1"
string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
version "4.2.3" version "4.2.3"
resolved "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" resolved "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
...@@ -5252,6 +5393,13 @@ which@^2.0.1: ...@@ -5252,6 +5393,13 @@ which@^2.0.1:
dependencies: dependencies:
isexe "^2.0.0" isexe "^2.0.0"
widest-line@^3.1.0:
version "3.1.0"
resolved "https://registry.npmmirror.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca"
integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==
dependencies:
string-width "^4.0.0"
word-wrap@^1.2.5: word-wrap@^1.2.5:
version "1.2.5" version "1.2.5"
resolved "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" resolved "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment