├── commons
├── public-module
│ ├── global
│ │ ├── index.ts
│ │ └── global.module.ts
│ ├── logger
│ │ ├── index.ts
│ │ ├── logger.module.ts
│ │ └── logger.service.ts
│ ├── roles
│ │ ├── role.enum.ts
│ │ ├── role.decorator.ts
│ │ └── role.guard.ts
│ ├── jwtAuth
│ │ ├── index.ts
│ │ ├── local-auth.guard.ts
│ │ ├── local.strategy.ts
│ │ ├── jwt.strategy.ts
│ │ ├── gql-auth.guard.ts
│ │ └── jwt-auth.guard.ts
│ ├── index.ts
│ ├── limiter
│ │ ├── limiter.middleware.ts
│ │ └── tokenlimit.lua
│ ├── middlewareGql
│ │ └── conditionFilterMiddleware.ts
│ └── errors
│ │ └── errorsGql.ts
├── public-decorator
│ ├── validator
│ │ ├── index.ts
│ │ ├── account.ts
│ │ └── common.ts
│ ├── index.ts
│ ├── transformer.ts
│ ├── swagger.ts
│ └── util.ts
└── public-tool
│ ├── index.ts
│ ├── redis.ts
│ ├── prisma.ts
│ ├── transform.interceptor.ts
│ ├── config.ts
│ ├── all.exception.filter.ts
│ ├── data.ts
│ └── bootstrap.ts
├── prisma
└── .env
├── public
├── playground-offline
│ └── @apollographql
│ │ └── graphql-playground-react@1.7.42
│ │ └── build
│ │ ├── static
│ │ ├── .gitkeep
│ │ ├── css
│ │ │ ├── .gitkeep
│ │ │ ├── index.css
│ │ │ ├── middleware.css
│ │ │ ├── index.css.map
│ │ │ └── middleware.css.map
│ │ └── js
│ │ │ └── .gitkeep
│ │ └── favicon.png
└── graphql-voyager
│ ├── dist
│ └── voyager.css.map
│ ├── LICENSE
│ ├── index.html
│ └── package.json
├── .dockerignore
├── tsconfig.build.json
├── src
├── main.ts
├── datos
│ ├── common.dto.ts
│ └── tasks.dto.ts
├── resolver
│ ├── index.ts
│ ├── page
│ │ ├── page.module.ts
│ │ └── page.controller.ts
│ └── auth
│ │ ├── auth.module.ts
│ │ ├── auth.entity.ts
│ │ └── auth.controller.ts
├── wsResolver
│ └── events
│ │ ├── events.module.ts
│ │ └── events.gateway.ts
├── graphqlResolver
│ ├── index.ts
│ ├── simple
│ │ ├── simple.model.ts
│ │ ├── simple.module.ts
│ │ └── simple.resolver.ts
│ └── multiTask
│ │ ├── multiTask.module.ts
│ │ └── multiTask.resolver.ts
├── graphqlDirective
│ ├── lower-case.directive.ts
│ ├── upper-case.directive.ts
│ └── index.directive.ts
├── upload
│ ├── upload.module.ts
│ └── upload.controller.ts
├── tasks
│ ├── tasks.module.ts
│ ├── tasks.controller.ts
│ └── tasks.service.ts
└── app.module.ts
├── config
├── config.jwt.yaml
└── config.yaml
├── test
├── jest-e2e.json
└── app.e2e-spec.ts
├── .prettierrc
├── @generated
├── prisma
│ ├── sort-order.enum.ts
│ ├── role.enum.ts
│ ├── string-field-update-operations.input.ts
│ ├── date-time-field-update-operations.input.ts
│ ├── affected-rows.output.ts
│ ├── nullable-string-field-update-operations.input.ts
│ ├── nested-int-filter.input.ts
│ ├── nested-int-nullable-filter.input.ts
│ ├── nested-date-time-filter.input.ts
│ ├── date-time-filter.input.ts
│ ├── nested-string-filter.input.ts
│ ├── nested-string-nullable-filter.input.ts
│ ├── string-filter.input.ts
│ ├── string-nullable-filter.input.ts
│ ├── nested-date-time-with-aggregates-filter.input.ts
│ ├── date-time-with-aggregates-filter.input.ts
│ ├── nested-string-with-aggregates-filter.input.ts
│ ├── string-with-aggregates-filter.input.ts
│ ├── nested-string-nullable-with-aggregates-filter.input.ts
│ └── string-nullable-with-aggregates-filter.input.ts
└── user
│ ├── delete-many-user.args.ts
│ ├── delete-one-user.args.ts
│ ├── find-unique-user.args.ts
│ ├── user-order-by-relevance-field.enum.ts
│ ├── user-where-unique.input.ts
│ ├── create-one-user.args.ts
│ ├── update-one-user.args.ts
│ ├── update-many-user.args.ts
│ ├── create-many-user.args.ts
│ ├── user-scalar-field.enum.ts
│ ├── upsert-one-user.args.ts
│ ├── user-order-by-relevance.input.ts
│ ├── aggregate-user.output.ts
│ ├── user-max-aggregate.input.ts
│ ├── user-min-aggregate.input.ts
│ ├── find-many-user.args.ts
│ ├── find-first-user.args.ts
│ ├── user-count-aggregate.input.ts
│ ├── user-count-aggregate.output.ts
│ ├── user-max-aggregate.output.ts
│ ├── user-min-aggregate.output.ts
│ ├── user-create-many.input.ts
│ ├── user-max-order-by-aggregate.input.ts
│ ├── user-min-order-by-aggregate.input.ts
│ ├── user-count-order-by-aggregate.input.ts
│ ├── user-aggregate.args.ts
│ ├── user-create.input.ts
│ ├── user-group-by.args.ts
│ ├── user-unchecked-create.input.ts
│ ├── user-order-by-with-relation-and-search-relevance.input.ts
│ ├── user-group-by.output.ts
│ ├── user-order-by-with-aggregation.input.ts
│ ├── user-where.input.ts
│ ├── user-unchecked-update-many.input.ts
│ ├── user-update-many-mutation.input.ts
│ ├── user-scalar-where-with-aggregates.input.ts
│ ├── user-update.input.ts
│ ├── user.model.ts
│ └── user-unchecked-update.input.ts
├── views
├── index.ejs
├── view.ejs
├── login.ejs
└── dev.ejs
├── nest-cli.json
├── .vscode
└── tasks.json
├── ws-client.html
├── .gitignore
├── tsconfig.json
├── .eslintrc.js
├── tools
└── config.ts
├── Dockerfile
├── services
├── user
│ └── user.module.ts
└── index.ts
├── docker-compose.yml
├── example
└── rabbit
│ ├── init.ts
│ ├── producer.ts
│ └── consumer.ts
└── README.md
/commons/public-module/global/index.ts:
--------------------------------------------------------------------------------
1 | export * from './global.module';
2 |
--------------------------------------------------------------------------------
/prisma/.env:
--------------------------------------------------------------------------------
1 | DATABASE_URL="mysql://root:prisma@localhost:3306/menuPermissions"
2 |
--------------------------------------------------------------------------------
/public/playground-offline/@apollographql/graphql-playground-react@1.7.42/build/static/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/playground-offline/@apollographql/graphql-playground-react@1.7.42/build/static/css/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/playground-offline/@apollographql/graphql-playground-react@1.7.42/build/static/js/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/commons/public-decorator/validator/index.ts:
--------------------------------------------------------------------------------
1 | export * from './common';
2 | export * from './account';
3 |
--------------------------------------------------------------------------------
/commons/public-module/logger/index.ts:
--------------------------------------------------------------------------------
1 | export * from './logger.module';
2 | export * from './logger.service';
3 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .dockerignore
3 | .git
4 | .gitignore
5 | README.md
6 | LICENSE
7 | .vscode
8 | logs
--------------------------------------------------------------------------------
/public/graphql-voyager/dist/voyager.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"voyager.css","sources":[],"mappings":";;;;;;;;A","sourceRoot":""}
--------------------------------------------------------------------------------
/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
4 | }
5 |
--------------------------------------------------------------------------------
/commons/public-module/roles/role.enum.ts:
--------------------------------------------------------------------------------
1 | export enum Role {
2 | User = 'USER',
3 | Admin = 'ADMIN',
4 | System = 'SYSTEM',
5 | }
6 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { AppModule } from './app.module';
2 | import { bootstrap } from '../commons/public-tool';
3 | // 启动服务
4 | bootstrap(AppModule, { cors: true });
5 |
--------------------------------------------------------------------------------
/config/config.jwt.yaml:
--------------------------------------------------------------------------------
1 | # 鉴权配置
2 |
3 | jwt:
4 | secret: simplePanda886 # 加密串,生产环境必须更改
5 | expiresIn: 36000 # Token过期时间
6 | refreshExpiresIn: 42000 # 续约token过期时间
7 |
--------------------------------------------------------------------------------
/commons/public-decorator/index.ts:
--------------------------------------------------------------------------------
1 | export * from './swagger'; // swagger 装饰器
2 | export * from './transformer'; // 转化装饰器
3 | export * from './validator'; // 数据验证装饰器
4 | export * from './util'; // 工具方法
5 |
--------------------------------------------------------------------------------
/commons/public-module/jwtAuth/index.ts:
--------------------------------------------------------------------------------
1 | export * from './jwt-auth.guard';
2 | export * from './local-auth.guard';
3 | export * from './local.strategy';
4 | export * from './jwt.strategy';
5 | export * from './gql-auth.guard';
6 |
--------------------------------------------------------------------------------
/commons/public-module/jwtAuth/local-auth.guard.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import { AuthGuard } from '@nestjs/passport';
3 |
4 | @Injectable()
5 | export class LocalAuthGuard extends AuthGuard('local') {}
6 |
--------------------------------------------------------------------------------
/public/playground-offline/@apollographql/graphql-playground-react@1.7.42/build/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mark-Panda/nest-prisma-graphql/HEAD/public/playground-offline/@apollographql/graphql-playground-react@1.7.42/build/favicon.png
--------------------------------------------------------------------------------
/test/jest-e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "moduleFileExtensions": ["js", "json", "ts"],
3 | "rootDir": ".",
4 | "testEnvironment": "node",
5 | "testRegex": ".e2e-spec.ts$",
6 | "transform": {
7 | "^.+\\.(t|j)s$": "ts-jest"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "all",
4 | "tabWidth": 4,
5 | "overrides": [{
6 | "files": "*.{json,yml}",
7 | "options": {
8 | "tabWidth": 2
9 | }
10 | }]
11 | }
--------------------------------------------------------------------------------
/commons/public-module/roles/role.decorator.ts:
--------------------------------------------------------------------------------
1 | import { SetMetadata } from '@nestjs/common';
2 | import { Role } from './role.enum';
3 |
4 | export const ROLES_KEY = 'roles';
5 | export const Roles = (...roles: Role[]) => SetMetadata(ROLES_KEY, roles);
6 |
--------------------------------------------------------------------------------
/src/datos/common.dto.ts:
--------------------------------------------------------------------------------
1 | import { ApiProperty } from 'commons/public-decorator';
2 |
3 | export class CommonResponse {
4 | @ApiProperty('状态编码')
5 | code!: number;
6 |
7 | @ApiProperty('返回信息')
8 | data!: object | string;
9 | }
10 |
--------------------------------------------------------------------------------
/@generated/prisma/sort-order.enum.ts:
--------------------------------------------------------------------------------
1 | import { registerEnumType } from '@nestjs/graphql';
2 |
3 | export enum SortOrder {
4 | asc = 'asc',
5 | desc = 'desc',
6 | }
7 |
8 | registerEnumType(SortOrder, { name: 'SortOrder', description: undefined });
9 |
--------------------------------------------------------------------------------
/@generated/prisma/role.enum.ts:
--------------------------------------------------------------------------------
1 | import { registerEnumType } from '@nestjs/graphql';
2 |
3 | export enum Role {
4 | ADMIN = 'ADMIN',
5 | SYSTEM = 'SYSTEM',
6 | USER = 'USER',
7 | }
8 |
9 | registerEnumType(Role, { name: 'Role', description: '角色定义' });
10 |
--------------------------------------------------------------------------------
/src/resolver/index.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { AuthModule } from './auth/auth.module';
3 | import { PageModule } from './page/page.module';
4 | @Module({
5 | imports: [PageModule, AuthModule],
6 | })
7 | export class AllResolverModules {}
8 |
--------------------------------------------------------------------------------
/commons/public-module/index.ts:
--------------------------------------------------------------------------------
1 | export * from './global';
2 | export * from './logger';
3 | export * from './jwtAuth'; // 鉴权
4 | export * from './roles/role.guard'; // 鉴权
5 | export * from './limiter/limiter.middleware'; // 限流
6 | export * from './errors/errorsGql'; // GraphQL异常
7 |
--------------------------------------------------------------------------------
/@generated/prisma/string-field-update-operations.input.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { InputType } from '@nestjs/graphql';
3 |
4 | @InputType()
5 | export class StringFieldUpdateOperationsInput {
6 | @Field(() => String, { nullable: true })
7 | set?: string;
8 | }
9 |
--------------------------------------------------------------------------------
/src/wsResolver/events/events.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { LoggerService } from 'commons/public-module';
3 | import { EventsGateway } from './events.gateway';
4 |
5 | @Module({
6 | providers: [EventsGateway, LoggerService],
7 | })
8 | export class EventsModule {}
9 |
--------------------------------------------------------------------------------
/@generated/prisma/date-time-field-update-operations.input.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { InputType } from '@nestjs/graphql';
3 |
4 | @InputType()
5 | export class DateTimeFieldUpdateOperationsInput {
6 | @Field(() => Date, { nullable: true })
7 | set?: Date | string;
8 | }
9 |
--------------------------------------------------------------------------------
/src/graphqlResolver/index.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { SimpleModule } from './simple/simple.module';
3 | import { MultiTaskModule } from './multiTask/multiTask.module';
4 | @Module({
5 | imports: [SimpleModule, MultiTaskModule],
6 | })
7 | export class CustomGraphqlModules {}
8 |
--------------------------------------------------------------------------------
/src/datos/tasks.dto.ts:
--------------------------------------------------------------------------------
1 | import { ApiProperty } from 'commons/public-decorator';
2 |
3 | export class TasksBaseDto {
4 | @ApiProperty('定时作业名称')
5 | name!: string;
6 | }
7 |
8 | export class TasksExecDto extends TasksBaseDto {
9 | @ApiProperty('定时任务执行规则')
10 | seconds!: string;
11 | }
12 |
--------------------------------------------------------------------------------
/@generated/prisma/affected-rows.output.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { ObjectType } from '@nestjs/graphql';
3 | import { Int } from '@nestjs/graphql';
4 |
5 | @ObjectType()
6 | export class AffectedRows {
7 | @Field(() => Int, { nullable: false })
8 | count!: number;
9 | }
10 |
--------------------------------------------------------------------------------
/@generated/prisma/nullable-string-field-update-operations.input.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { InputType } from '@nestjs/graphql';
3 |
4 | @InputType()
5 | export class NullableStringFieldUpdateOperationsInput {
6 | @Field(() => String, { nullable: true })
7 | set?: string;
8 | }
9 |
--------------------------------------------------------------------------------
/@generated/user/delete-many-user.args.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { ArgsType } from '@nestjs/graphql';
3 | import { UserWhereInput } from './user-where.input';
4 |
5 | @ArgsType()
6 | export class DeleteManyUserArgs {
7 | @Field(() => UserWhereInput, { nullable: true })
8 | where?: UserWhereInput;
9 | }
10 |
--------------------------------------------------------------------------------
/@generated/user/delete-one-user.args.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { ArgsType } from '@nestjs/graphql';
3 | import { UserWhereUniqueInput } from './user-where-unique.input';
4 |
5 | @ArgsType()
6 | export class DeleteOneUserArgs {
7 | @Field(() => UserWhereUniqueInput, { nullable: false })
8 | where!: UserWhereUniqueInput;
9 | }
10 |
--------------------------------------------------------------------------------
/@generated/user/find-unique-user.args.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { ArgsType } from '@nestjs/graphql';
3 | import { UserWhereUniqueInput } from './user-where-unique.input';
4 |
5 | @ArgsType()
6 | export class FindUniqueUserArgs {
7 | @Field(() => UserWhereUniqueInput, { nullable: false })
8 | where!: UserWhereUniqueInput;
9 | }
10 |
--------------------------------------------------------------------------------
/views/index.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Document
9 |
10 |
11 |
12 | 这是ejs演示代码
13 |
14 | <%=message%>
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/resolver/page/page.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { ConfigModule } from '@nestjs/config';
3 | import { LoggerService } from 'commons/public-module';
4 | import { PageController } from './page.controller';
5 |
6 | @Module({
7 | imports: [ConfigModule],
8 | controllers: [PageController],
9 | providers: [LoggerService],
10 | })
11 | export class PageModule {}
12 |
--------------------------------------------------------------------------------
/commons/public-decorator/transformer.ts:
--------------------------------------------------------------------------------
1 | import { Transform, Type } from 'class-transformer';
2 |
3 | /**
4 | * 空字符串转undefined
5 | * 防止提交空字符串引起验证
6 | */
7 | export const ToUndefined = () =>
8 | Transform(({ value }) => (value === '' ? undefined : value));
9 |
10 | /**
11 | * 转数字类型
12 | */
13 | export const ToNumber = () => Type(() => Number);
14 |
15 | /**
16 | * 转时间类型
17 | */
18 | export const ToDate = () => Type(() => Date);
19 |
--------------------------------------------------------------------------------
/nest-cli.json:
--------------------------------------------------------------------------------
1 | {
2 | "collection": "@nestjs/schematics",
3 | "sourceRoot": "src",
4 | "compilerOptions": {
5 | "assets": [
6 | { "include": "../config/*.yaml", "outDir": "./dist/config" },
7 | { "include": "../public/**/*", "outDir": "./dist/public" },
8 | { "include": "../views/*", "outDir": "./dist/views" },
9 | { "include": "../prisma/**/*", "outDir": "./dist/prisma" }
10 | ]
11 | }
12 | }
--------------------------------------------------------------------------------
/@generated/user/user-order-by-relevance-field.enum.ts:
--------------------------------------------------------------------------------
1 | import { registerEnumType } from '@nestjs/graphql';
2 |
3 | export enum UserOrderByRelevanceFieldEnum {
4 | id = 'id',
5 | username = 'username',
6 | email = 'email',
7 | password = 'password',
8 | RFID = 'RFID',
9 | description = 'description',
10 | }
11 |
12 | registerEnumType(UserOrderByRelevanceFieldEnum, {
13 | name: 'UserOrderByRelevanceFieldEnum',
14 | description: undefined,
15 | });
16 |
--------------------------------------------------------------------------------
/@generated/user/user-where-unique.input.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { InputType } from '@nestjs/graphql';
3 | import * as Scalars from 'graphql-scalars';
4 |
5 | @InputType()
6 | export class UserWhereUniqueInput {
7 | @Field(() => String, { nullable: true })
8 | id?: string;
9 |
10 | @Field(() => String, { nullable: true })
11 | username?: string;
12 |
13 | @Field(() => Scalars.GraphQLEmailAddress, { nullable: true })
14 | email?: string;
15 | }
16 |
--------------------------------------------------------------------------------
/@generated/user/create-one-user.args.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { ArgsType } from '@nestjs/graphql';
3 | import { UserCreateInput } from './user-create.input';
4 | import { ValidateNested } from 'class-validator';
5 | import { Type } from 'class-transformer';
6 |
7 | @ArgsType()
8 | export class CreateOneUserArgs {
9 | @Field(() => UserCreateInput, { nullable: false })
10 | @ValidateNested()
11 | @Type(() => UserCreateInput)
12 | data!: UserCreateInput;
13 | }
14 |
--------------------------------------------------------------------------------
/commons/public-tool/index.ts:
--------------------------------------------------------------------------------
1 | import { join, resolve } from 'path';
2 |
3 | /**
4 | * 根目录
5 | */
6 | export const rootPath = resolve(process.cwd());
7 | export const uploadDir = join(rootPath, 'uploadResource/appResource');
8 |
9 | export * from './bootstrap';
10 | export * from './prisma';
11 | export * from './all.exception.filter';
12 | export * from './transform.interceptor';
13 | export * from './data';
14 | export * from './redis';
15 | export * from './config';
16 | export * from './plugins';
17 |
--------------------------------------------------------------------------------
/@generated/user/update-one-user.args.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { ArgsType } from '@nestjs/graphql';
3 | import { UserUpdateInput } from './user-update.input';
4 | import { UserWhereUniqueInput } from './user-where-unique.input';
5 |
6 | @ArgsType()
7 | export class UpdateOneUserArgs {
8 | @Field(() => UserUpdateInput, { nullable: false })
9 | data!: UserUpdateInput;
10 |
11 | @Field(() => UserWhereUniqueInput, { nullable: false })
12 | where!: UserWhereUniqueInput;
13 | }
14 |
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | // 有关 tasks.json 格式的文档,请参见
3 | // https://go.microsoft.com/fwlink/?LinkId=733558
4 | "version": "2.0.0",
5 | "tasks": [
6 | {
7 | "label": "dev-docker-up",
8 | "type": "shell",
9 | "command": "docker-compose -f ./docker-compose.yml up -d"
10 | },
11 | {
12 | "label": "dev-docker-down",
13 | "type": "shell",
14 | "command": "docker-compose -f ./docker-compose.yml down"
15 | }
16 | ],
17 | }
18 |
--------------------------------------------------------------------------------
/@generated/user/update-many-user.args.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { ArgsType } from '@nestjs/graphql';
3 | import { UserUpdateManyMutationInput } from './user-update-many-mutation.input';
4 | import { UserWhereInput } from './user-where.input';
5 |
6 | @ArgsType()
7 | export class UpdateManyUserArgs {
8 | @Field(() => UserUpdateManyMutationInput, { nullable: false })
9 | data!: UserUpdateManyMutationInput;
10 |
11 | @Field(() => UserWhereInput, { nullable: true })
12 | where?: UserWhereInput;
13 | }
14 |
--------------------------------------------------------------------------------
/src/graphqlResolver/simple/simple.model.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { ObjectType } from '@nestjs/graphql';
3 | import { Directive } from '@nestjs/graphql';
4 |
5 | /** 自定义查询模型 */
6 | @ObjectType({ description: 'Simple 描述' })
7 | export class Simple {
8 | @Field(() => String, { nullable: false, description: 'Simple的邮件地址' })
9 | email!: string;
10 |
11 | /** User's name */
12 | @Field(() => String, { nullable: false, description: 'simple 的名字' })
13 | @Directive('@upper')
14 | name!: string;
15 | }
16 |
--------------------------------------------------------------------------------
/ws-client.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # compiled output
2 | /dist
3 | /node_modules
4 | /@generated
5 | /volumes
6 | /prisma
7 | /services
8 | /uploadResource
9 | # Logs
10 | logs
11 | *.log
12 | npm-debug.log*
13 | pnpm-debug.log*
14 | yarn-debug.log*
15 | yarn-error.log*
16 | lerna-debug.log*
17 |
18 | # OS
19 | .DS_Store
20 |
21 | # Tests
22 | /coverage
23 | /.nyc_output
24 |
25 | # IDEs and editors
26 | /.idea
27 | .project
28 | .classpath
29 | .c9/
30 | *.launch
31 | .settings/
32 | *.sublime-workspace
33 |
34 | # IDE - VSCode
35 | .vscode/*
36 | !.vscode/settings.json
37 | !.vscode/tasks.json
38 | !.vscode/launch.json
39 | !.vscode/extensions.json
--------------------------------------------------------------------------------
/@generated/user/create-many-user.args.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { ArgsType } from '@nestjs/graphql';
3 | import { UserCreateManyInput } from './user-create-many.input';
4 | import { ValidateNested } from 'class-validator';
5 | import { Type } from 'class-transformer';
6 |
7 | @ArgsType()
8 | export class CreateManyUserArgs {
9 | @Field(() => [UserCreateManyInput], { nullable: false })
10 | @ValidateNested()
11 | @Type(() => UserCreateManyInput)
12 | data!: Array;
13 |
14 | @Field(() => Boolean, { nullable: true })
15 | skipDuplicates?: boolean;
16 | }
17 |
--------------------------------------------------------------------------------
/@generated/user/user-scalar-field.enum.ts:
--------------------------------------------------------------------------------
1 | import { registerEnumType } from '@nestjs/graphql';
2 |
3 | export enum UserScalarFieldEnum {
4 | id = 'id',
5 | create_date = 'create_date',
6 | update_date = 'update_date',
7 | isDelete = 'isDelete',
8 | username = 'username',
9 | email = 'email',
10 | password = 'password',
11 | role = 'role',
12 | RFID = 'RFID',
13 | description = 'description',
14 | expired = 'expired',
15 | status = 'status',
16 | }
17 |
18 | registerEnumType(UserScalarFieldEnum, {
19 | name: 'UserScalarFieldEnum',
20 | description: undefined,
21 | });
22 |
--------------------------------------------------------------------------------
/@generated/user/upsert-one-user.args.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { ArgsType } from '@nestjs/graphql';
3 | import { UserWhereUniqueInput } from './user-where-unique.input';
4 | import { UserCreateInput } from './user-create.input';
5 | import { UserUpdateInput } from './user-update.input';
6 |
7 | @ArgsType()
8 | export class UpsertOneUserArgs {
9 | @Field(() => UserWhereUniqueInput, { nullable: false })
10 | where!: UserWhereUniqueInput;
11 |
12 | @Field(() => UserCreateInput, { nullable: false })
13 | create!: UserCreateInput;
14 |
15 | @Field(() => UserUpdateInput, { nullable: false })
16 | update!: UserUpdateInput;
17 | }
18 |
--------------------------------------------------------------------------------
/@generated/user/user-order-by-relevance.input.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { InputType } from '@nestjs/graphql';
3 | import { UserOrderByRelevanceFieldEnum } from './user-order-by-relevance-field.enum';
4 | import { SortOrder } from '../prisma/sort-order.enum';
5 |
6 | @InputType()
7 | export class UserOrderByRelevanceInput {
8 | @Field(() => [UserOrderByRelevanceFieldEnum], { nullable: false })
9 | fields!: Array;
10 |
11 | @Field(() => SortOrder, { nullable: false })
12 | sort!: keyof typeof SortOrder;
13 |
14 | @Field(() => String, { nullable: false })
15 | search!: string;
16 | }
17 |
--------------------------------------------------------------------------------
/src/graphqlDirective/lower-case.directive.ts:
--------------------------------------------------------------------------------
1 | import { defaultFieldResolver } from 'graphql';
2 |
3 | export function lowerDirectiveTransformer(fieldConfig: any) {
4 | const { resolve = defaultFieldResolver } = fieldConfig;
5 | // Replace the original resolver with a function that *first* calls
6 | // the original resolver, then converts its result to upper case
7 | fieldConfig.resolve = async function (source, args, context, info) {
8 | const result = await resolve(source, args, context, info);
9 | if (typeof result === 'string') {
10 | return result.toLowerCase();
11 | }
12 | return result;
13 | };
14 | return fieldConfig;
15 | }
16 |
--------------------------------------------------------------------------------
/src/graphqlDirective/upper-case.directive.ts:
--------------------------------------------------------------------------------
1 | import { defaultFieldResolver } from 'graphql';
2 |
3 | export function upperDirectiveTransformer(fieldConfig: any) {
4 | const { resolve = defaultFieldResolver } = fieldConfig;
5 | // Replace the original resolver with a function that *first* calls
6 | // the original resolver, then converts its result to upper case
7 | fieldConfig.resolve = async function (source, args, context, info) {
8 | const result = await resolve(source, args, context, info);
9 | if (typeof result === 'string') {
10 | return result.toUpperCase();
11 | }
12 | return result;
13 | };
14 | return fieldConfig;
15 | }
16 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "declaration": true,
5 | "removeComments": true,
6 | "emitDecoratorMetadata": true,
7 | "experimentalDecorators": true,
8 | "allowSyntheticDefaultImports": true,
9 | "target": "es2017",
10 | "sourceMap": true,
11 | "outDir": "./dist",
12 | "baseUrl": "./",
13 | "incremental": true,
14 | "skipLibCheck": true,
15 | "strictNullChecks": false,
16 | "noImplicitAny": false,
17 | "strictBindCallApply": false,
18 | "forceConsistentCasingInFileNames": false,
19 | "noFallthroughCasesInSwitch": false,
20 | "resolveJsonModule": true,
21 | "esModuleInterop": true,
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/commons/public-tool/redis.ts:
--------------------------------------------------------------------------------
1 | import Redis from 'ioredis';
2 | import Redlock from 'redlock';
3 | import { configYml } from './config';
4 | /**
5 | * Redis连接客户端
6 | */
7 | export const redisClient = new Redis({
8 | port: configYml.cache.redis.port ? configYml.cache.redis.port : 6379,
9 | host: configYml.cache.redis.host ? configYml.cache.redis.host : 'localhost',
10 | password: configYml.cache.redis.password
11 | ? configYml.cache.redis.password
12 | : null,
13 | db: configYml.cache.redis.db ? configYml.cache.redis.db : null,
14 | });
15 |
16 | /**
17 | * Redis分布式锁
18 | */
19 | export const redlock = new Redlock([redisClient], {
20 | retryDelay: 200, // time in ms
21 | retryCount: 5,
22 | });
23 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: '@typescript-eslint/parser',
3 | parserOptions: {
4 | project: 'tsconfig.json',
5 | sourceType: 'module',
6 | },
7 | plugins: ['@typescript-eslint/eslint-plugin'],
8 | extends: [
9 | 'plugin:@typescript-eslint/recommended',
10 | 'plugin:prettier/recommended',
11 | ],
12 | root: true,
13 | env: {
14 | node: true,
15 | jest: true,
16 | },
17 | ignorePatterns: ['.eslintrc.js'],
18 | rules: {
19 | '@typescript-eslint/interface-name-prefix': 'off',
20 | '@typescript-eslint/explicit-function-return-type': 'off',
21 | '@typescript-eslint/explicit-module-boundary-types': 'off',
22 | '@typescript-eslint/no-explicit-any': 'off',
23 | },
24 | };
25 |
--------------------------------------------------------------------------------
/commons/public-module/jwtAuth/local.strategy.ts:
--------------------------------------------------------------------------------
1 | import { Strategy } from 'passport-local';
2 | import { PassportStrategy } from '@nestjs/passport';
3 | import { Injectable, UnauthorizedException } from '@nestjs/common';
4 | import { AuthService } from 'src/resolver/auth/auth.service';
5 |
6 | @Injectable()
7 | export class LocalStrategy extends PassportStrategy(Strategy) {
8 | constructor(private authService: AuthService) {
9 | super();
10 | }
11 |
12 | async validate(username: string, password: string): Promise {
13 | const user = await this.authService.validateUser(username, password);
14 | if (!user) {
15 | throw new UnauthorizedException();
16 | }
17 | return user;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/tools/config.ts:
--------------------------------------------------------------------------------
1 | import { load } from 'js-yaml';
2 | import { join, resolve } from 'path';
3 | import { existsSync, readFileSync } from 'fs';
4 | import { merge, cloneDeepWith } from 'lodash';
5 | const rootPath = resolve(process.cwd());
6 | let configs: any = {};
7 | const configPath = ['config.yaml', 'config.jwt.yaml'];
8 | for (const path of configPath) {
9 | try {
10 | // 读取并解析配置文件
11 | const filePath = join(rootPath, 'config', path);
12 | if (existsSync(filePath))
13 | configs = merge(configs, load(readFileSync(filePath, 'utf8')));
14 | } catch {}
15 | }
16 | // 递归将 null 转 空字符串
17 | configs = cloneDeepWith(configs, (value) => {
18 | if (value === null) return '';
19 | });
20 |
21 | export const configYml = configs;
22 |
--------------------------------------------------------------------------------
/test/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { INestApplication } from '@nestjs/common';
3 | import request from 'supertest';
4 | import { AppModule } from './../src/app.module';
5 |
6 | describe('AppController (e2e)', () => {
7 | let app: INestApplication;
8 |
9 | beforeEach(async () => {
10 | const moduleFixture: TestingModule = await Test.createTestingModule({
11 | imports: [AppModule],
12 | }).compile();
13 |
14 | app = moduleFixture.createNestApplication();
15 | await app.init();
16 | });
17 |
18 | it('/ (GET)', () => {
19 | return request(app.getHttpServer())
20 | .get('/')
21 | .expect(200)
22 | .expect('Hello World!');
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/src/wsResolver/events/events.gateway.ts:
--------------------------------------------------------------------------------
1 | import {
2 | SubscribeMessage,
3 | WebSocketGateway,
4 | WebSocketServer,
5 | WsResponse,
6 | } from '@nestjs/websockets';
7 | import { from, Observable } from 'rxjs';
8 | import { map } from 'rxjs/operators';
9 | import { Server } from 'ws';
10 | import { LoggerService } from 'commons/public-module';
11 |
12 | @WebSocketGateway(8099)
13 | export class EventsGateway {
14 | constructor(private readonly logger: LoggerService) {}
15 | @WebSocketServer()
16 | server: Server;
17 |
18 | @SubscribeMessage('events')
19 | onEvent(client: any, data: any): Observable> {
20 | this.logger.log(data, 'WS-消息信息');
21 | return from([1, 2, 3]).pipe(
22 | map((item) => ({ event: 'events', data: item })),
23 | );
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/config/config.yaml:
--------------------------------------------------------------------------------
1 | # 服务配置(每个单独的服务都需要配置对应的端口)
2 |
3 | serve:
4 | port: 3100
5 |
6 | graphql:
7 | queryMaxRows: 200
8 | path: '/data/ff/graphql' #GraphQL请求路径
9 |
10 | # 缓存配置
11 | cache:
12 | redis: # 有值时,使用 redis 做缓存服务器
13 | host: 'localhost'
14 | port: 6379
15 |
16 | # 加密
17 | encryption:
18 | saltOrRounds: 10
19 |
20 | # Swagger 配置
21 | swagger:
22 | title: 项目接口文档
23 | description: '[Base URL: localhost:3100]'
24 | path: v1/swagger
25 |
26 | # 限流
27 | httpLimiter:
28 | tokenKey: 'nestHttpLimiter' #剩余tokenKey
29 | timestampKey: 'nestHttpLimiterStamp' #刷新时间Key
30 | capacity: 100 #桶容量
31 | rate: 1 #每秒生成token数量即token生成速度
32 |
33 | # 角色使用方法权限
34 | rolePermission:
35 | system: [ user, users, person, persons, userGroupBy, userAggregate ]
36 | user: [ user, users, person, persons, userGroupBy, userAggregate ]
37 |
--------------------------------------------------------------------------------
/@generated/prisma/nested-int-filter.input.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { InputType } from '@nestjs/graphql';
3 | import { Int } from '@nestjs/graphql';
4 |
5 | @InputType()
6 | export class NestedIntFilter {
7 | @Field(() => Int, { nullable: true })
8 | equals?: number;
9 |
10 | @Field(() => [Int], { nullable: true })
11 | in?: Array;
12 |
13 | @Field(() => [Int], { nullable: true })
14 | notIn?: Array;
15 |
16 | @Field(() => Int, { nullable: true })
17 | lt?: number;
18 |
19 | @Field(() => Int, { nullable: true })
20 | lte?: number;
21 |
22 | @Field(() => Int, { nullable: true })
23 | gt?: number;
24 |
25 | @Field(() => Int, { nullable: true })
26 | gte?: number;
27 |
28 | @Field(() => NestedIntFilter, { nullable: true })
29 | not?: NestedIntFilter;
30 | }
31 |
--------------------------------------------------------------------------------
/@generated/prisma/nested-int-nullable-filter.input.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { InputType } from '@nestjs/graphql';
3 | import { Int } from '@nestjs/graphql';
4 |
5 | @InputType()
6 | export class NestedIntNullableFilter {
7 | @Field(() => Int, { nullable: true })
8 | equals?: number;
9 |
10 | @Field(() => [Int], { nullable: true })
11 | in?: Array;
12 |
13 | @Field(() => [Int], { nullable: true })
14 | notIn?: Array;
15 |
16 | @Field(() => Int, { nullable: true })
17 | lt?: number;
18 |
19 | @Field(() => Int, { nullable: true })
20 | lte?: number;
21 |
22 | @Field(() => Int, { nullable: true })
23 | gt?: number;
24 |
25 | @Field(() => Int, { nullable: true })
26 | gte?: number;
27 |
28 | @Field(() => NestedIntNullableFilter, { nullable: true })
29 | not?: NestedIntNullableFilter;
30 | }
31 |
--------------------------------------------------------------------------------
/@generated/prisma/nested-date-time-filter.input.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { InputType } from '@nestjs/graphql';
3 |
4 | @InputType()
5 | export class NestedDateTimeFilter {
6 | @Field(() => Date, { nullable: true })
7 | equals?: Date | string;
8 |
9 | @Field(() => [Date], { nullable: true })
10 | in?: Array | Array;
11 |
12 | @Field(() => [Date], { nullable: true })
13 | notIn?: Array | Array;
14 |
15 | @Field(() => Date, { nullable: true })
16 | lt?: Date | string;
17 |
18 | @Field(() => Date, { nullable: true })
19 | lte?: Date | string;
20 |
21 | @Field(() => Date, { nullable: true })
22 | gt?: Date | string;
23 |
24 | @Field(() => Date, { nullable: true })
25 | gte?: Date | string;
26 |
27 | @Field(() => NestedDateTimeFilter, { nullable: true })
28 | not?: NestedDateTimeFilter;
29 | }
30 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:18.4.0-alpine
2 |
3 | # Create app directory
4 | WORKDIR /app
5 |
6 | # A wildcard is used to ensure both package.json AND package-lock.json are copied
7 | COPY package*.json ./
8 | COPY ./prisma ./prisma
9 | COPY ./dist ./dist
10 |
11 | # bcrypt -> make build-base
12 | # 只有v3.4还有python2
13 | # sed -i 's/v3.15/v3.4/g' /etc/apk/repositories && \
14 | # node-gyp@9.0.0支持node16+
15 | RUN \
16 | sed -i "s/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g" /etc/apk/repositories && \
17 | apk --update --no-cache add python3 make build-base && \
18 | npm config set registry https://registry.npmmirror.com && \
19 | npm install -g node-gyp@9.0.0 && \
20 | npm install --production --unsafe-perm && \
21 | npm i -g prisma@3.14.0 && \
22 | prisma generate && \
23 | apk del python3 make build-base
24 |
25 |
26 | WORKDIR /app
27 | CMD [ "npm", "run", "start:prod" ]
28 |
29 | EXPOSE 3100
30 |
31 |
--------------------------------------------------------------------------------
/commons/public-decorator/validator/account.ts:
--------------------------------------------------------------------------------
1 | import { Matches } from './common';
2 |
3 | /**
4 | * 账号属性验证
5 | */
6 | export const ValidatorAccount = {
7 | USERNAME: Matches(
8 | /^[\d\w-_]{4,32}$/,
9 | '请输入正确的用户名,4-32位、字母、数字、下划线、减号',
10 | ),
11 | PASSWORD: Matches(
12 | /^[\d\w-_]{4,32}$/,
13 | '请输入正确的密码,4-32位、字母、数字、下划线、减号',
14 | ),
15 | NICKNAME: Matches(
16 | /^[\d\w\u4e00-\u9fa5-_]{2,15}$/,
17 | '请输入正确的昵称,2-32位、中文、字母、数字,下划线、减号',
18 | ),
19 | PHONE: Matches(
20 | /^(?:(?:\+|00)86)?1(?:(?:3[\d])|(?:4[5-79])|(?:5[0-35-9])|(?:6[5-7])|(?:7[0-8])|(?:8[\d])|(?:9[189]))\d{8}$/,
21 | '请输入正确格式的手机号',
22 | ),
23 | IP: Matches(
24 | /^((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])(?::(?:[0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?$/,
25 | '请输入正确的IP地址',
26 | ),
27 | };
28 |
--------------------------------------------------------------------------------
/services/user/user.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { LoggerService } from 'commons/public-module';
3 | import { PassportModule } from '@nestjs/passport';
4 | import { JwtModule } from '@nestjs/jwt';
5 | import { ConfigService } from '@nestjs/config';
6 | import { AuthService } from 'src/resolver/auth/auth.service';
7 | import { UserResolver } from './user.resolver';
8 |
9 | @Module({
10 | imports: [
11 | PassportModule,
12 | {
13 | ...JwtModule.registerAsync({
14 | useFactory: (configService: ConfigService) => {
15 | const { secret, expiresIn } = configService.get('jwt');
16 | return { secret, signOptions: { expiresIn } };
17 | },
18 | inject: [ConfigService],
19 | }),
20 | },
21 | ],
22 | providers: [UserResolver, LoggerService, AuthService],
23 | })
24 | export class UserModule {}
25 |
--------------------------------------------------------------------------------
/@generated/prisma/date-time-filter.input.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { InputType } from '@nestjs/graphql';
3 | import { NestedDateTimeFilter } from './nested-date-time-filter.input';
4 |
5 | @InputType()
6 | export class DateTimeFilter {
7 | @Field(() => Date, { nullable: true })
8 | equals?: Date | string;
9 |
10 | @Field(() => [Date], { nullable: true })
11 | in?: Array | Array;
12 |
13 | @Field(() => [Date], { nullable: true })
14 | notIn?: Array | Array;
15 |
16 | @Field(() => Date, { nullable: true })
17 | lt?: Date | string;
18 |
19 | @Field(() => Date, { nullable: true })
20 | lte?: Date | string;
21 |
22 | @Field(() => Date, { nullable: true })
23 | gt?: Date | string;
24 |
25 | @Field(() => Date, { nullable: true })
26 | gte?: Date | string;
27 |
28 | @Field(() => NestedDateTimeFilter, { nullable: true })
29 | not?: NestedDateTimeFilter;
30 | }
31 |
--------------------------------------------------------------------------------
/commons/public-tool/prisma.ts:
--------------------------------------------------------------------------------
1 | import { PrismaClient } from '@prisma/client';
2 | import { LoggerService } from '../public-module/logger/logger.service';
3 | const loggerService = new LoggerService();
4 | const prisma = new PrismaClient({
5 | errorFormat: 'colorless',
6 | log: [
7 | {
8 | emit: 'event',
9 | level: 'query',
10 | },
11 | ],
12 | });
13 |
14 | const { NODE_ENV } = process.env;
15 | if (NODE_ENV !== 'production') {
16 | prisma.$on('query', (event) => {
17 | loggerService.debug(event, '查询日志');
18 | });
19 | }
20 |
21 | /**
22 | * prisma中间件
23 | */
24 | prisma.$use(async (params, next) => {
25 | const { model, action, args } = params;
26 | loggerService.debug(model, 'prisma中间件拦截模型名称');
27 | loggerService.debug(action, 'prisma中间件拦截模型操作');
28 | loggerService.debug(args, 'prisma中间件拦截模型参数');
29 | const result = await next(params);
30 | return result;
31 | });
32 |
33 | export { prisma };
34 |
--------------------------------------------------------------------------------
/@generated/user/aggregate-user.output.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { ObjectType } from '@nestjs/graphql';
3 | import { UserCountAggregate } from './user-count-aggregate.output';
4 | import { UserAvgAggregate } from './user-avg-aggregate.output';
5 | import { UserSumAggregate } from './user-sum-aggregate.output';
6 | import { UserMinAggregate } from './user-min-aggregate.output';
7 | import { UserMaxAggregate } from './user-max-aggregate.output';
8 |
9 | @ObjectType()
10 | export class AggregateUser {
11 | @Field(() => UserCountAggregate, { nullable: true })
12 | _count?: UserCountAggregate;
13 |
14 | @Field(() => UserAvgAggregate, { nullable: true })
15 | _avg?: UserAvgAggregate;
16 |
17 | @Field(() => UserSumAggregate, { nullable: true })
18 | _sum?: UserSumAggregate;
19 |
20 | @Field(() => UserMinAggregate, { nullable: true })
21 | _min?: UserMinAggregate;
22 |
23 | @Field(() => UserMaxAggregate, { nullable: true })
24 | _max?: UserMaxAggregate;
25 | }
26 |
--------------------------------------------------------------------------------
/src/graphqlResolver/simple/simple.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { SimpleResolver } from './simple.resolver';
3 | import { LoggerService } from 'commons/public-module';
4 | import { PassportModule } from '@nestjs/passport';
5 | import { JwtModule } from '@nestjs/jwt';
6 | import { ConfigService } from '@nestjs/config';
7 | import { AuthService } from 'src/resolver/auth/auth.service';
8 | import { TasksService } from '../../tasks/tasks.service';
9 | @Module({
10 | imports: [
11 | PassportModule,
12 | {
13 | ...JwtModule.registerAsync({
14 | useFactory: (configService: ConfigService) => {
15 | const { secret, expiresIn } = configService.get('jwt');
16 | return { secret, signOptions: { expiresIn } };
17 | },
18 | inject: [ConfigService],
19 | }),
20 | },
21 | ],
22 | providers: [SimpleResolver, LoggerService, AuthService, TasksService],
23 | })
24 | export class SimpleModule {}
25 |
--------------------------------------------------------------------------------
/src/graphqlResolver/multiTask/multiTask.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { MultiTaskResolver } from './multiTask.resolver';
3 | import { LoggerService } from 'commons/public-module';
4 | import { PassportModule } from '@nestjs/passport';
5 | import { JwtModule } from '@nestjs/jwt';
6 | import { ConfigService } from '@nestjs/config';
7 | import { AuthService } from 'src/resolver/auth/auth.service';
8 | import { TasksService } from '../../tasks/tasks.service';
9 | @Module({
10 | imports: [
11 | PassportModule,
12 | {
13 | ...JwtModule.registerAsync({
14 | useFactory: (configService: ConfigService) => {
15 | const { secret, expiresIn } = configService.get('jwt');
16 | return { secret, signOptions: { expiresIn } };
17 | },
18 | inject: [ConfigService],
19 | }),
20 | },
21 | ],
22 | providers: [MultiTaskResolver, LoggerService, AuthService, TasksService],
23 | })
24 | export class MultiTaskModule {}
25 |
--------------------------------------------------------------------------------
/public/playground-offline/@apollographql/graphql-playground-react@1.7.42/build/static/css/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: sans-serif;
5 | overflow: hidden;
6 | }
7 |
8 | #root {
9 | height: 100%;
10 | }
11 |
12 | body {
13 | font-family: 'Open Sans', sans-serif;
14 | -webkit-font-smoothing: antialiased;
15 | -moz-osx-font-smoothing: grayscale;
16 | color: rgba(0, 0, 0, .8);
17 | line-height: 1.5;
18 | height: 100vh;
19 | letter-spacing: 0.53px;
20 | margin-right: -1px !important;
21 | }
22 |
23 | html,
24 | body,
25 | p,
26 | a,
27 | h1,
28 | h2,
29 | h3,
30 | h4,
31 | ul,
32 | pre,
33 | code {
34 | margin: 0;
35 | padding: 0;
36 | color: inherit;
37 | }
38 |
39 | a:active,
40 | a:focus,
41 | button:focus,
42 | input:focus {
43 | outline: none;
44 | }
45 |
46 | input,
47 | button,
48 | submit {
49 | border: none;
50 | }
51 |
52 | input,
53 | button,
54 | pre {
55 | font-family: 'Open Sans', sans-serif;
56 | }
57 |
58 | code {
59 | font-family: Consolas, monospace;
60 | }
--------------------------------------------------------------------------------
/src/graphqlResolver/simple/simple.resolver.ts:
--------------------------------------------------------------------------------
1 | import { Args, Info, Query, Resolver } from '@nestjs/graphql';
2 | import { Injectable, UseGuards } from '@nestjs/common';
3 | import { GraphQLResolveInfo } from 'graphql';
4 | import { PrismaSelect } from '@paljs/plugins';
5 | import { LoggerService, GqlAuthGuard } from 'commons/public-module';
6 | import { Simple } from './simple.model';
7 |
8 | /**
9 | * Resolves User object type.
10 | */
11 | @Injectable()
12 | @Resolver(() => Simple)
13 | export class SimpleResolver {
14 | constructor(private readonly logger: LoggerService) {}
15 | /**
16 | * 自定义GraphQL查询
17 | * @param args 请求参数
18 | * @param info 返回字段
19 | * @returns 返回
20 | */
21 | @Query(() => Simple)
22 | @UseGuards(GqlAuthGuard)
23 | async simple(@Args('data') args: string, @Info() info: GraphQLResolveInfo) {
24 | const select = new PrismaSelect(info).value;
25 | this.logger.log(select, 'GraphQL请求参数');
26 | args = Object.assign(args, select);
27 | return { email: '123@qq.com', name: 'Simple' };
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/commons/public-tool/transform.interceptor.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Injectable,
3 | NestInterceptor,
4 | ExecutionContext,
5 | CallHandler,
6 | } from '@nestjs/common';
7 | import { Observable } from 'rxjs';
8 | import { map, tap } from 'rxjs/operators';
9 | import { LoggerService } from 'commons/public-module';
10 | import { toIp } from './data';
11 |
12 | export interface Response {
13 | code: number;
14 | data: T;
15 | }
16 |
17 | let num = 0;
18 | const interval = '/'.repeat(50);
19 |
20 | @Injectable()
21 | export class TransformInterceptor
22 | implements NestInterceptor>
23 | {
24 | intercept(
25 | context: ExecutionContext,
26 | next: CallHandler,
27 | ): Observable> {
28 | const loggerService = new LoggerService();
29 | const ctx = context.switchToHttp();
30 | const res = ctx.getResponse();
31 |
32 | loggerService.log(interval, `第 ${++num} 次请求`);
33 | return next
34 | .handle()
35 | .pipe(map((data) => ({ code: res.statusCode, data })));
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/public/playground-offline/@apollographql/graphql-playground-react@1.7.42/build/static/css/middleware.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: sans-serif;
5 | overflow: hidden
6 | }
7 |
8 | #root {
9 | height: 100%
10 | }
11 |
12 | body {
13 | font-family: Open Sans, sans-serif;
14 | -webkit-font-smoothing: antialiased;
15 | -moz-osx-font-smoothing: grayscale;
16 | color: rgba(0, 0, 0, .8);
17 | line-height: 1.5;
18 | height: 100vh;
19 | letter-spacing: .53px;
20 | margin-right: -1px!important
21 | }
22 |
23 | a,
24 | body,
25 | code,
26 | h1,
27 | h2,
28 | h3,
29 | h4,
30 | html,
31 | p,
32 | pre,
33 | ul {
34 | margin: 0;
35 | padding: 0;
36 | color: inherit
37 | }
38 |
39 | a:active,
40 | a:focus,
41 | button:focus,
42 | input:focus {
43 | outline: none
44 | }
45 |
46 | button,
47 | input,
48 | submit {
49 | border: none
50 | }
51 |
52 | button,
53 | input,
54 | pre {
55 | font-family: Open Sans, sans-serif
56 | }
57 |
58 | code {
59 | font-family: Consolas, monospace
60 | }
61 |
62 |
63 | /*# sourceMappingURL=middleware.css.map*/
--------------------------------------------------------------------------------
/src/upload/upload.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { PassportModule } from '@nestjs/passport';
3 | import { JwtModule } from '@nestjs/jwt';
4 | import { ConfigModule, ConfigService } from '@nestjs/config';
5 | import { LoggerService, JwtStrategy } from 'commons/public-module';
6 | import { UploadController } from './upload.controller';
7 | import { UploadService } from './upload.service';
8 | import { AuthService } from 'src/resolver/auth/auth.service';
9 |
10 | @Module({
11 | imports: [
12 | ConfigModule,
13 | PassportModule,
14 | {
15 | ...JwtModule.registerAsync({
16 | useFactory: (configService: ConfigService) => {
17 | const { secret, expiresIn } = configService.get('jwt');
18 | return { secret, signOptions: { expiresIn } };
19 | },
20 | inject: [ConfigService],
21 | }),
22 | },
23 | ],
24 | controllers: [UploadController],
25 | providers: [AuthService, UploadService, JwtStrategy, LoggerService],
26 | })
27 | export class UploadModule {}
28 |
--------------------------------------------------------------------------------
/src/resolver/auth/auth.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { PassportModule } from '@nestjs/passport';
3 | import { JwtModule } from '@nestjs/jwt';
4 | import { ConfigModule } from '@nestjs/config';
5 | import {
6 | LoggerService,
7 | LocalStrategy,
8 | JwtStrategy,
9 | } from 'commons/public-module';
10 | import { ConfigService } from '@nestjs/config';
11 | import { AuthService } from './auth.service';
12 | import { AuthController } from './auth.controller';
13 |
14 | @Module({
15 | imports: [
16 | ConfigModule,
17 | PassportModule,
18 | {
19 | ...JwtModule.registerAsync({
20 | useFactory: (configService: ConfigService) => {
21 | const { secret, expiresIn } = configService.get('jwt');
22 | return { secret, signOptions: { expiresIn } };
23 | },
24 | inject: [ConfigService],
25 | }),
26 | },
27 | ],
28 | controllers: [AuthController],
29 | providers: [AuthService, LocalStrategy, JwtStrategy, LoggerService],
30 | })
31 | export class AuthModule {}
32 |
--------------------------------------------------------------------------------
/@generated/user/user-max-aggregate.input.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { InputType } from '@nestjs/graphql';
3 |
4 | @InputType()
5 | export class UserMaxAggregateInput {
6 | @Field(() => Boolean, { nullable: true })
7 | id?: true;
8 |
9 | @Field(() => Boolean, { nullable: true })
10 | create_date?: true;
11 |
12 | @Field(() => Boolean, { nullable: true })
13 | update_date?: true;
14 |
15 | @Field(() => Boolean, { nullable: true })
16 | isDelete?: true;
17 |
18 | @Field(() => Boolean, { nullable: true })
19 | username?: true;
20 |
21 | @Field(() => Boolean, { nullable: true })
22 | email?: true;
23 |
24 | @Field(() => Boolean, { nullable: true })
25 | password?: true;
26 |
27 | @Field(() => Boolean, { nullable: true })
28 | role?: true;
29 |
30 | @Field(() => Boolean, { nullable: true })
31 | RFID?: true;
32 |
33 | @Field(() => Boolean, { nullable: true })
34 | description?: true;
35 |
36 | @Field(() => Boolean, { nullable: true })
37 | expired?: true;
38 |
39 | @Field(() => Boolean, { nullable: true })
40 | status?: true;
41 | }
42 |
--------------------------------------------------------------------------------
/@generated/user/user-min-aggregate.input.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { InputType } from '@nestjs/graphql';
3 |
4 | @InputType()
5 | export class UserMinAggregateInput {
6 | @Field(() => Boolean, { nullable: true })
7 | id?: true;
8 |
9 | @Field(() => Boolean, { nullable: true })
10 | create_date?: true;
11 |
12 | @Field(() => Boolean, { nullable: true })
13 | update_date?: true;
14 |
15 | @Field(() => Boolean, { nullable: true })
16 | isDelete?: true;
17 |
18 | @Field(() => Boolean, { nullable: true })
19 | username?: true;
20 |
21 | @Field(() => Boolean, { nullable: true })
22 | email?: true;
23 |
24 | @Field(() => Boolean, { nullable: true })
25 | password?: true;
26 |
27 | @Field(() => Boolean, { nullable: true })
28 | role?: true;
29 |
30 | @Field(() => Boolean, { nullable: true })
31 | RFID?: true;
32 |
33 | @Field(() => Boolean, { nullable: true })
34 | description?: true;
35 |
36 | @Field(() => Boolean, { nullable: true })
37 | expired?: true;
38 |
39 | @Field(() => Boolean, { nullable: true })
40 | status?: true;
41 | }
42 |
--------------------------------------------------------------------------------
/public/graphql-voyager/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 APIs-guru
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/@generated/prisma/nested-string-filter.input.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { InputType } from '@nestjs/graphql';
3 |
4 | @InputType()
5 | export class NestedStringFilter {
6 | @Field(() => String, { nullable: true })
7 | equals?: string;
8 |
9 | @Field(() => [String], { nullable: true })
10 | in?: Array;
11 |
12 | @Field(() => [String], { nullable: true })
13 | notIn?: Array;
14 |
15 | @Field(() => String, { nullable: true })
16 | lt?: string;
17 |
18 | @Field(() => String, { nullable: true })
19 | lte?: string;
20 |
21 | @Field(() => String, { nullable: true })
22 | gt?: string;
23 |
24 | @Field(() => String, { nullable: true })
25 | gte?: string;
26 |
27 | @Field(() => String, { nullable: true })
28 | contains?: string;
29 |
30 | @Field(() => String, { nullable: true })
31 | startsWith?: string;
32 |
33 | @Field(() => String, { nullable: true })
34 | endsWith?: string;
35 |
36 | @Field(() => String, { nullable: true })
37 | search?: string;
38 |
39 | @Field(() => NestedStringFilter, { nullable: true })
40 | not?: NestedStringFilter;
41 | }
42 |
--------------------------------------------------------------------------------
/commons/public-module/limiter/limiter.middleware.ts:
--------------------------------------------------------------------------------
1 | import { Request, Response, NextFunction } from 'express';
2 | import { redisClient, configYml } from 'commons/public-tool';
3 | import { rootPath } from 'commons/public-tool';
4 | import { readFileSync } from 'fs';
5 | import moment from 'moment';
6 |
7 | export async function limiterMiddleware(
8 | req: Request,
9 | res: Response,
10 | next: NextFunction,
11 | ) {
12 | const redisLuaScript = readFileSync(
13 | rootPath + '/commons/public-module/limiter/tokenlimit.lua',
14 | );
15 | const nowtimestamp = moment().format('X'); //当前时间戳 秒级
16 | const isPass = await redisClient.eval(
17 | redisLuaScript, //redisLuaScript脚本
18 | 2, //表示后面参数有几个是Key
19 | configYml.httpLimiter.tokenKey, //剩余tokenKey
20 | configYml.httpLimiter.timestampKey, //刷新时间Key
21 | configYml.httpLimiter.rate, //每秒生成token数量即token生成速度
22 | configYml.httpLimiter.capacity, //桶容量
23 | nowtimestamp, //当前时间戳
24 | 1, //当前请求token数量
25 | );
26 | if (isPass) {
27 | next();
28 | } else {
29 | return res.json({ code: 401, error: '请求频率太高' });
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/@generated/prisma/nested-string-nullable-filter.input.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { InputType } from '@nestjs/graphql';
3 |
4 | @InputType()
5 | export class NestedStringNullableFilter {
6 | @Field(() => String, { nullable: true })
7 | equals?: string;
8 |
9 | @Field(() => [String], { nullable: true })
10 | in?: Array;
11 |
12 | @Field(() => [String], { nullable: true })
13 | notIn?: Array;
14 |
15 | @Field(() => String, { nullable: true })
16 | lt?: string;
17 |
18 | @Field(() => String, { nullable: true })
19 | lte?: string;
20 |
21 | @Field(() => String, { nullable: true })
22 | gt?: string;
23 |
24 | @Field(() => String, { nullable: true })
25 | gte?: string;
26 |
27 | @Field(() => String, { nullable: true })
28 | contains?: string;
29 |
30 | @Field(() => String, { nullable: true })
31 | startsWith?: string;
32 |
33 | @Field(() => String, { nullable: true })
34 | endsWith?: string;
35 |
36 | @Field(() => String, { nullable: true })
37 | search?: string;
38 |
39 | @Field(() => NestedStringNullableFilter, { nullable: true })
40 | not?: NestedStringNullableFilter;
41 | }
42 |
--------------------------------------------------------------------------------
/@generated/user/find-many-user.args.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { ArgsType } from '@nestjs/graphql';
3 | import { UserWhereInput } from './user-where.input';
4 | import { UserOrderByWithRelationAndSearchRelevanceInput } from './user-order-by-with-relation-and-search-relevance.input';
5 | import { UserWhereUniqueInput } from './user-where-unique.input';
6 | import { Int } from '@nestjs/graphql';
7 | import { UserScalarFieldEnum } from './user-scalar-field.enum';
8 |
9 | @ArgsType()
10 | export class FindManyUserArgs {
11 | @Field(() => UserWhereInput, { nullable: true })
12 | where?: UserWhereInput;
13 |
14 | @Field(() => [UserOrderByWithRelationAndSearchRelevanceInput], {
15 | nullable: true,
16 | })
17 | orderBy?: Array;
18 |
19 | @Field(() => UserWhereUniqueInput, { nullable: true })
20 | cursor?: UserWhereUniqueInput;
21 |
22 | @Field(() => Int, { nullable: true })
23 | take?: number;
24 |
25 | @Field(() => Int, { nullable: true })
26 | skip?: number;
27 |
28 | @Field(() => [UserScalarFieldEnum], { nullable: true })
29 | distinct?: Array;
30 | }
31 |
--------------------------------------------------------------------------------
/@generated/user/find-first-user.args.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { ArgsType } from '@nestjs/graphql';
3 | import { UserWhereInput } from './user-where.input';
4 | import { UserOrderByWithRelationAndSearchRelevanceInput } from './user-order-by-with-relation-and-search-relevance.input';
5 | import { UserWhereUniqueInput } from './user-where-unique.input';
6 | import { Int } from '@nestjs/graphql';
7 | import { UserScalarFieldEnum } from './user-scalar-field.enum';
8 |
9 | @ArgsType()
10 | export class FindFirstUserArgs {
11 | @Field(() => UserWhereInput, { nullable: true })
12 | where?: UserWhereInput;
13 |
14 | @Field(() => [UserOrderByWithRelationAndSearchRelevanceInput], {
15 | nullable: true,
16 | })
17 | orderBy?: Array;
18 |
19 | @Field(() => UserWhereUniqueInput, { nullable: true })
20 | cursor?: UserWhereUniqueInput;
21 |
22 | @Field(() => Int, { nullable: true })
23 | take?: number;
24 |
25 | @Field(() => Int, { nullable: true })
26 | skip?: number;
27 |
28 | @Field(() => [UserScalarFieldEnum], { nullable: true })
29 | distinct?: Array;
30 | }
31 |
--------------------------------------------------------------------------------
/commons/public-module/logger/logger.module.ts:
--------------------------------------------------------------------------------
1 | import { Module, DynamicModule } from '@nestjs/common';
2 | import { LoggerService, LoggerServiceOptions } from './logger.service';
3 |
4 | export interface LoggerModuleAsyncOptions {
5 | isGlobal?: boolean; // 是否导出到全局
6 | useFactory: (
7 | ...args: any[]
8 | ) => Promise | LoggerServiceOptions; // 动态配置
9 | inject?: any[];
10 | }
11 |
12 | /**
13 | * 日志模块
14 | */
15 | @Module({})
16 | export class LoggerModule {
17 | static forRoot({
18 | isGlobal = false,
19 | useFactory,
20 | inject,
21 | }: LoggerModuleAsyncOptions): DynamicModule {
22 | return {
23 | global: isGlobal,
24 | module: LoggerModule,
25 | providers: [
26 | {
27 | provide: LoggerService,
28 | useFactory: async (...args: any[]) => {
29 | const options = await useFactory(...args);
30 | return new LoggerService(options);
31 | },
32 | inject,
33 | },
34 | ],
35 | exports: [LoggerService],
36 | };
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | services:
3 | nest-api:
4 | container_name: nestServer-api
5 | build:
6 | context: .
7 | dockerfile: Dockerfile
8 | ports:
9 | - '3100:3100'
10 | environment:
11 | DATABASE_URL: 'mysql://root:prisma@localhost:3306/menuPermissions'
12 | depends_on:
13 | - postgres-db
14 | - redis
15 | volumes:
16 | - ./logs:/app/dist/logs
17 | - ./uploadResource:/app/dist/uploadResource
18 | postgres-db:
19 | image: postgres:10.3
20 | container_name: postgresDB-server
21 | restart: always
22 | ports:
23 | - '5432:5432'
24 | environment:
25 | POSTGRES_USER: prisma
26 | POSTGRES_PASSWORD: prisma
27 | volumes:
28 | - ./volumes/database_postgres-data:/var/lib/postgresql/data
29 | redis:
30 | image: registry.ebrserver:3060/library/redis:latest
31 | container_name: redisCache-server
32 | restart: always
33 | command: ['redis-server', '--appendonly', 'yes']
34 | hostname: redis
35 | volumes:
36 | - ./volumes/database_redis-data:/data
37 | ports:
38 | - '6379:6379'
39 | networks:
40 | prisma:
41 | ipam:
42 | config:
43 | - subnet: 192.100.0.0/16
44 |
--------------------------------------------------------------------------------
/@generated/user/user-count-aggregate.input.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { InputType } from '@nestjs/graphql';
3 |
4 | @InputType()
5 | export class UserCountAggregateInput {
6 | @Field(() => Boolean, { nullable: true })
7 | id?: true;
8 |
9 | @Field(() => Boolean, { nullable: true })
10 | create_date?: true;
11 |
12 | @Field(() => Boolean, { nullable: true })
13 | update_date?: true;
14 |
15 | @Field(() => Boolean, { nullable: true })
16 | isDelete?: true;
17 |
18 | @Field(() => Boolean, { nullable: true })
19 | username?: true;
20 |
21 | @Field(() => Boolean, { nullable: true })
22 | email?: true;
23 |
24 | @Field(() => Boolean, { nullable: true })
25 | password?: true;
26 |
27 | @Field(() => Boolean, { nullable: true })
28 | role?: true;
29 |
30 | @Field(() => Boolean, { nullable: true })
31 | RFID?: true;
32 |
33 | @Field(() => Boolean, { nullable: true })
34 | description?: true;
35 |
36 | @Field(() => Boolean, { nullable: true })
37 | expired?: true;
38 |
39 | @Field(() => Boolean, { nullable: true })
40 | status?: true;
41 |
42 | @Field(() => Boolean, { nullable: true })
43 | _all?: true;
44 | }
45 |
--------------------------------------------------------------------------------
/@generated/prisma/string-filter.input.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { InputType } from '@nestjs/graphql';
3 | import { NestedStringFilter } from './nested-string-filter.input';
4 |
5 | @InputType()
6 | export class StringFilter {
7 | @Field(() => String, { nullable: true })
8 | equals?: string;
9 |
10 | @Field(() => [String], { nullable: true })
11 | in?: Array;
12 |
13 | @Field(() => [String], { nullable: true })
14 | notIn?: Array;
15 |
16 | @Field(() => String, { nullable: true })
17 | lt?: string;
18 |
19 | @Field(() => String, { nullable: true })
20 | lte?: string;
21 |
22 | @Field(() => String, { nullable: true })
23 | gt?: string;
24 |
25 | @Field(() => String, { nullable: true })
26 | gte?: string;
27 |
28 | @Field(() => String, { nullable: true })
29 | contains?: string;
30 |
31 | @Field(() => String, { nullable: true })
32 | startsWith?: string;
33 |
34 | @Field(() => String, { nullable: true })
35 | endsWith?: string;
36 |
37 | @Field(() => String, { nullable: true })
38 | search?: string;
39 |
40 | @Field(() => NestedStringFilter, { nullable: true })
41 | not?: NestedStringFilter;
42 | }
43 |
--------------------------------------------------------------------------------
/public/playground-offline/@apollographql/graphql-playground-react@1.7.42/build/static/css/index.css.map:
--------------------------------------------------------------------------------
1 | {
2 | "version": 3,
3 | "sources": [
4 | "index.css"
5 | ],
6 | "names": [],
7 | "mappings": "AAAA,KACE,SACA,UACA,uBACA,eAAiB,CAGnB,MACE,WAAa,CAGf,KACE,iCACA,mCACA,kCACA,qBACA,gBACA,aACA,qBACA,2BAA8B,CAGhC,sCACE,SACA,UACA,aAAe,CAGjB,0CACE,YAAc,CAGhB,oBACE,WAAa,CAGf,iBACE,gCAAqC,CAGvC,KACE,8BAAiC",
8 | "file": "static/css/index.css",
9 | "sourcesContent": [
10 | "body {\n margin: 0;\n padding: 0;\n font-family: sans-serif;\n overflow: hidden;\n}\n\n#root {\n height: 100%;\n}\n\nbody {\n font-family: 'Open Sans', sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n color: rgba(0,0,0,.8);\n line-height: 1.5;\n height: 100vh;\n letter-spacing: 0.53px;\n margin-right: -1px !important;\n}\n\nhtml, body, p, a, h1, h2, h3, h4, ul, pre, code {\n margin: 0;\n padding: 0;\n color: inherit;\n}\n\na:active, a:focus, button:focus, input:focus {\n outline: none;\n}\n\ninput, button, submit {\n border: none;\n}\n\ninput, button, pre {\n font-family: 'Open Sans', sans-serif;\n}\n\ncode {\n font-family: Consolas, monospace;\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/index.css"
11 | ],
12 | "sourceRoot": ""
13 | }
--------------------------------------------------------------------------------
/commons/public-module/jwtAuth/jwt.strategy.ts:
--------------------------------------------------------------------------------
1 | import { ExtractJwt, Strategy } from 'passport-jwt';
2 | import { PassportStrategy } from '@nestjs/passport';
3 | import { ConfigService } from '@nestjs/config';
4 | import { Injectable, UnauthorizedException } from '@nestjs/common';
5 | import { AuthService } from 'src/resolver/auth/auth.service';
6 |
7 | @Injectable()
8 | export class JwtStrategy extends PassportStrategy(Strategy) {
9 | constructor(
10 | readonly configService: ConfigService,
11 | private readonly authService: AuthService,
12 | ) {
13 | super({
14 | jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
15 | ignoreExpiration: false,
16 | secretOrKey: configService.get('jwt.secret'),
17 | });
18 | }
19 |
20 | async validate(payload: any) {
21 | const user = await this.authService.validateUserByJwt(
22 | payload[`secret-${this.configService.get('jwt.secret')}`],
23 | );
24 | // 如果有用户信息,代表 token 没有过期,没有则 token 已失效
25 | if (!user) throw new UnauthorizedException();
26 | return {
27 | id: payload[`secret-${this.configService.get('jwt.secret')}`],
28 | username: payload.username,
29 | };
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/public/playground-offline/@apollographql/graphql-playground-react@1.7.42/build/static/css/middleware.css.map:
--------------------------------------------------------------------------------
1 | {
2 | "version": 3,
3 | "sources": [
4 | "index.css"
5 | ],
6 | "names": [],
7 | "mappings": "AAAA,KACE,SACA,UACA,uBACA,eAAiB,CAGnB,MACE,WAAa,CAGf,KACE,iCACA,mCACA,kCACA,qBACA,gBACA,aACA,qBACA,2BAA8B,CAGhC,sCACE,SACA,UACA,aAAe,CAGjB,0CACE,YAAc,CAGhB,oBACE,WAAa,CAGf,iBACE,gCAAqC,CAGvC,KACE,8BAAiC",
8 | "file": "static/css/middleware.css",
9 | "sourcesContent": [
10 | "body {\n margin: 0;\n padding: 0;\n font-family: sans-serif;\n overflow: hidden;\n}\n\n#root {\n height: 100%;\n}\n\nbody {\n font-family: 'Open Sans', sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n color: rgba(0,0,0,.8);\n line-height: 1.5;\n height: 100vh;\n letter-spacing: 0.53px;\n margin-right: -1px !important;\n}\n\nhtml, body, p, a, h1, h2, h3, h4, ul, pre, code {\n margin: 0;\n padding: 0;\n color: inherit;\n}\n\na:active, a:focus, button:focus, input:focus {\n outline: none;\n}\n\ninput, button, submit {\n border: none;\n}\n\ninput, button, pre {\n font-family: 'Open Sans', sans-serif;\n}\n\ncode {\n font-family: Consolas, monospace;\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/index.css"
11 | ],
12 | "sourceRoot": ""
13 | }
--------------------------------------------------------------------------------
/@generated/user/user-count-aggregate.output.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { ObjectType } from '@nestjs/graphql';
3 | import { Int } from '@nestjs/graphql';
4 | import { HideField } from '@nestjs/graphql';
5 |
6 | @ObjectType()
7 | export class UserCountAggregate {
8 | @Field(() => Int, { nullable: false })
9 | id!: number;
10 |
11 | @Field(() => Int, { nullable: false })
12 | create_date!: number;
13 |
14 | @Field(() => Int, { nullable: false })
15 | update_date!: number;
16 |
17 | @Field(() => Int, { nullable: false })
18 | isDelete!: number;
19 |
20 | @Field(() => Int, { nullable: false })
21 | username!: number;
22 |
23 | @Field(() => Int, { nullable: false })
24 | email!: number;
25 |
26 | @HideField()
27 | password!: number;
28 |
29 | @Field(() => Int, { nullable: false })
30 | role!: number;
31 |
32 | @Field(() => Int, { nullable: false })
33 | RFID!: number;
34 |
35 | @Field(() => Int, { nullable: false })
36 | description!: number;
37 |
38 | @Field(() => Int, { nullable: false })
39 | expired!: number;
40 |
41 | @Field(() => Int, { nullable: false })
42 | status!: number;
43 |
44 | @Field(() => Int, { nullable: false })
45 | _all!: number;
46 | }
47 |
--------------------------------------------------------------------------------
/commons/public-module/limiter/tokenlimit.lua:
--------------------------------------------------------------------------------
1 | -- 每秒生成token数量即token生成速度
2 | local rate = tonumber(ARGV[1])
3 | -- 桶容量
4 | local capacity = tonumber(ARGV[2])
5 | -- 当前时间戳
6 | local now = tonumber(ARGV[3])
7 | -- 当前请求token数量
8 | local requested = tonumber(ARGV[4])
9 | -- 需要多少秒才能填满桶
10 | local fill_time = capacity/rate
11 | -- 向下取整,ttl为填满时间的2倍
12 | local ttl = math.floor(fill_time*2)
13 | -- 当前时间桶容量
14 | local last_tokens = tonumber(redis.call("get", KEYS[1]))
15 | -- 如果当前桶容量为0,说明是第一次进入,则默认容量为桶的最大容量
16 | if last_tokens == nil then
17 | last_tokens = capacity
18 | end
19 | -- 上一次刷新的时间
20 | local last_refreshed = tonumber(redis.call("get", KEYS[2]))
21 | -- 第一次进入则设置刷新时间为0
22 | if last_refreshed == nil then
23 | last_refreshed = 0
24 | end
25 | -- 距离上次请求的时间跨度
26 | local delta = math.max(0, now-last_refreshed)
27 | -- 距离上次请求的时间跨度,总共能生产token的数量,如果超多最大容量则丢弃多余的token
28 | local filled_tokens = math.min(capacity, last_tokens+(delta*rate))
29 | -- 本次请求token数量是否足够
30 | local allowed = filled_tokens >= requested
31 | -- 桶剩余数量
32 | local new_tokens = filled_tokens
33 | -- 允许本次token申请,计算剩余数量
34 | if allowed then
35 | new_tokens = filled_tokens - requested
36 | end
37 | -- 设置剩余token数量
38 | redis.call("setex", KEYS[1], ttl, new_tokens)
39 | -- 设置刷新时间
40 | redis.call("setex", KEYS[2], ttl, now)
41 |
42 | return allowed
--------------------------------------------------------------------------------
/src/tasks/tasks.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { PassportModule } from '@nestjs/passport';
3 | import { JwtModule } from '@nestjs/jwt';
4 | import { ConfigModule } from '@nestjs/config';
5 | import {
6 | LoggerService,
7 | LocalStrategy,
8 | JwtStrategy,
9 | } from 'commons/public-module';
10 | import { ConfigService } from '@nestjs/config';
11 | import { AuthService } from '../resolver/auth/auth.service';
12 | import { TasksService } from './tasks.service';
13 | import { TasksController } from './tasks.controller';
14 |
15 | @Module({
16 | imports: [
17 | ConfigModule,
18 | PassportModule,
19 | {
20 | ...JwtModule.registerAsync({
21 | useFactory: (configService: ConfigService) => {
22 | const { secret, expiresIn } = configService.get('jwt');
23 | return { secret, signOptions: { expiresIn } };
24 | },
25 | inject: [ConfigService],
26 | }),
27 | },
28 | ],
29 | controllers: [TasksController],
30 | providers: [
31 | AuthService,
32 | TasksService,
33 | LocalStrategy,
34 | JwtStrategy,
35 | LoggerService,
36 | ],
37 | })
38 | export class TasksModule {}
39 |
--------------------------------------------------------------------------------
/@generated/prisma/string-nullable-filter.input.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { InputType } from '@nestjs/graphql';
3 | import { NestedStringNullableFilter } from './nested-string-nullable-filter.input';
4 |
5 | @InputType()
6 | export class StringNullableFilter {
7 | @Field(() => String, { nullable: true })
8 | equals?: string;
9 |
10 | @Field(() => [String], { nullable: true })
11 | in?: Array;
12 |
13 | @Field(() => [String], { nullable: true })
14 | notIn?: Array;
15 |
16 | @Field(() => String, { nullable: true })
17 | lt?: string;
18 |
19 | @Field(() => String, { nullable: true })
20 | lte?: string;
21 |
22 | @Field(() => String, { nullable: true })
23 | gt?: string;
24 |
25 | @Field(() => String, { nullable: true })
26 | gte?: string;
27 |
28 | @Field(() => String, { nullable: true })
29 | contains?: string;
30 |
31 | @Field(() => String, { nullable: true })
32 | startsWith?: string;
33 |
34 | @Field(() => String, { nullable: true })
35 | endsWith?: string;
36 |
37 | @Field(() => String, { nullable: true })
38 | search?: string;
39 |
40 | @Field(() => NestedStringNullableFilter, { nullable: true })
41 | not?: NestedStringNullableFilter;
42 | }
43 |
--------------------------------------------------------------------------------
/src/upload/upload.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller, Get, Post, Req, Res, UseGuards } from '@nestjs/common';
2 | import { ApiTags } from '@nestjs/swagger';
3 | import { JwtAuthGuard } from 'commons/public-module';
4 | import { ApiOperation } from 'commons/public-decorator';
5 | import { UploadService } from './upload.service';
6 |
7 | @ApiTags('文件上传')
8 | @Controller('chunk')
9 | export class UploadController {
10 | constructor(private readonly uploadService: UploadService) {}
11 |
12 | @Get('checkChunk')
13 | @UseGuards(JwtAuthGuard)
14 | @ApiOperation('检查chunk文件, 断点续传验证片段')
15 | checkChunk(@Req() req, @Res() res) {
16 | return this.uploadService.checkChunk(req, res);
17 | }
18 |
19 | @Get('checkFile')
20 | @UseGuards(JwtAuthGuard)
21 | @ApiOperation('检查文件')
22 | checkFile(@Req() req, @Res() res) {
23 | return this.uploadService.checkFile(req, res);
24 | }
25 |
26 | @Post('upload')
27 | @UseGuards(JwtAuthGuard)
28 | @ApiOperation('上传文件')
29 | uploadChunk(@Req() req, @Res() res) {
30 | return this.uploadService.uploadChunk(req, res);
31 | }
32 |
33 | @Get('merge')
34 | @UseGuards(JwtAuthGuard)
35 | @ApiOperation('合并文件')
36 | merageChuank(@Req() req, @Res() res) {
37 | return this.uploadService.merageChuank(req, res);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/@generated/user/user-max-aggregate.output.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { ObjectType } from '@nestjs/graphql';
3 | import { HideField } from '@nestjs/graphql';
4 | import { Role } from '../prisma/role.enum';
5 | import { Float } from '@nestjs/graphql';
6 | import { UserStatus } from '../prisma/user-status.enum';
7 |
8 | @ObjectType()
9 | export class UserMaxAggregate {
10 | @Field(() => String, { nullable: true })
11 | id?: string;
12 |
13 | @Field(() => Date, { nullable: true })
14 | create_date?: Date | string;
15 |
16 | @Field(() => Date, { nullable: true })
17 | update_date?: Date | string;
18 |
19 | @Field(() => Boolean, { nullable: true })
20 | isDelete?: boolean;
21 |
22 | @Field(() => String, { nullable: true })
23 | username?: string;
24 |
25 | @Field(() => String, { nullable: true })
26 | email?: string;
27 |
28 | @HideField()
29 | password?: string;
30 |
31 | @Field(() => Role, { nullable: true })
32 | role?: keyof typeof Role;
33 |
34 | @Field(() => String, { nullable: true })
35 | RFID?: string;
36 |
37 | @Field(() => String, { nullable: true })
38 | description?: string;
39 |
40 | @Field(() => Float, { nullable: true })
41 | expired?: number;
42 |
43 | @Field(() => UserStatus, { nullable: true })
44 | status?: keyof typeof UserStatus;
45 | }
46 |
--------------------------------------------------------------------------------
/@generated/user/user-min-aggregate.output.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { ObjectType } from '@nestjs/graphql';
3 | import { HideField } from '@nestjs/graphql';
4 | import { Role } from '../prisma/role.enum';
5 | import { Float } from '@nestjs/graphql';
6 | import { UserStatus } from '../prisma/user-status.enum';
7 |
8 | @ObjectType()
9 | export class UserMinAggregate {
10 | @Field(() => String, { nullable: true })
11 | id?: string;
12 |
13 | @Field(() => Date, { nullable: true })
14 | create_date?: Date | string;
15 |
16 | @Field(() => Date, { nullable: true })
17 | update_date?: Date | string;
18 |
19 | @Field(() => Boolean, { nullable: true })
20 | isDelete?: boolean;
21 |
22 | @Field(() => String, { nullable: true })
23 | username?: string;
24 |
25 | @Field(() => String, { nullable: true })
26 | email?: string;
27 |
28 | @HideField()
29 | password?: string;
30 |
31 | @Field(() => Role, { nullable: true })
32 | role?: keyof typeof Role;
33 |
34 | @Field(() => String, { nullable: true })
35 | RFID?: string;
36 |
37 | @Field(() => String, { nullable: true })
38 | description?: string;
39 |
40 | @Field(() => Float, { nullable: true })
41 | expired?: number;
42 |
43 | @Field(() => UserStatus, { nullable: true })
44 | status?: keyof typeof UserStatus;
45 | }
46 |
--------------------------------------------------------------------------------
/src/resolver/page/page.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller, Get, Res } from '@nestjs/common';
2 | import { ApiTags } from '@nestjs/swagger';
3 | import { configYml } from 'commons/public-tool';
4 |
5 | @ApiTags('静态页面')
6 | @Controller()
7 | export class PageController {
8 | @Get()
9 | async indexHtml(@Res() res) {
10 | return res.redirect('dev');
11 | }
12 |
13 | @Get('login')
14 | async loginHtml(@Res() res) {
15 | return res.render('login');
16 | }
17 |
18 | @Get('dev')
19 | async devHtml(@Res() res) {
20 | return res.render('dev', { swaggerPath: configYml.swagger.path });
21 | }
22 |
23 | @Get('upload')
24 | async uploadHtml(@Res() res) {
25 | return res.render('upload');
26 | }
27 |
28 | @Get('graph')
29 | async graph(@Res() res) {
30 | if (process.env.NODE_ENV === 'production') {
31 | return res.json({ message: '生产环境不允许使用graph!' });
32 | }
33 | return res.render('view', { path: configYml.graphql.path });
34 | }
35 |
36 | @Get('playground')
37 | async playgroundHtml(@Res() res) {
38 | if (process.env.NODE_ENV === 'production') {
39 | return res.json({ message: '生产环境不允许使用playground!' });
40 | }
41 | return res.render('playground', {
42 | path: configYml.graphql.path,
43 | });
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/@generated/prisma/nested-date-time-with-aggregates-filter.input.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { InputType } from '@nestjs/graphql';
3 | import { NestedIntFilter } from './nested-int-filter.input';
4 | import { NestedDateTimeFilter } from './nested-date-time-filter.input';
5 |
6 | @InputType()
7 | export class NestedDateTimeWithAggregatesFilter {
8 | @Field(() => Date, { nullable: true })
9 | equals?: Date | string;
10 |
11 | @Field(() => [Date], { nullable: true })
12 | in?: Array | Array;
13 |
14 | @Field(() => [Date], { nullable: true })
15 | notIn?: Array | Array;
16 |
17 | @Field(() => Date, { nullable: true })
18 | lt?: Date | string;
19 |
20 | @Field(() => Date, { nullable: true })
21 | lte?: Date | string;
22 |
23 | @Field(() => Date, { nullable: true })
24 | gt?: Date | string;
25 |
26 | @Field(() => Date, { nullable: true })
27 | gte?: Date | string;
28 |
29 | @Field(() => NestedDateTimeWithAggregatesFilter, { nullable: true })
30 | not?: NestedDateTimeWithAggregatesFilter;
31 |
32 | @Field(() => NestedIntFilter, { nullable: true })
33 | _count?: NestedIntFilter;
34 |
35 | @Field(() => NestedDateTimeFilter, { nullable: true })
36 | _min?: NestedDateTimeFilter;
37 |
38 | @Field(() => NestedDateTimeFilter, { nullable: true })
39 | _max?: NestedDateTimeFilter;
40 | }
41 |
--------------------------------------------------------------------------------
/src/graphqlDirective/index.directive.ts:
--------------------------------------------------------------------------------
1 | import { upperDirectiveTransformer } from './upper-case.directive';
2 | import { lowerDirectiveTransformer } from './lower-case.directive';
3 | import { getDirective, MapperKind, mapSchema } from '@graphql-tools/utils';
4 | import { GraphQLSchema } from 'graphql';
5 | import { applyMiddleware } from 'graphql-middleware';
6 | import { conditionFilterMiddleware } from 'commons/public-module/middlewareGql/conditionFilterMiddleware';
7 |
8 | export function mergeDirectiveTransformer(schema: GraphQLSchema) {
9 | //GraphQL中间件
10 | const middlewares = [conditionFilterMiddleware];
11 | //合并中间件
12 | schema = applyMiddleware(schema, ...middlewares);
13 | // GraphQL指令
14 | return mapSchema(schema, {
15 | [MapperKind.OBJECT_FIELD]: (fieldConfig) => {
16 | const upperDirective = getDirective(
17 | schema,
18 | fieldConfig,
19 | 'upper',
20 | )?.[0];
21 | const lowerDirective = getDirective(
22 | schema,
23 | fieldConfig,
24 | 'lower',
25 | )?.[0];
26 | if (lowerDirective) {
27 | return lowerDirectiveTransformer(fieldConfig);
28 | }
29 | if (upperDirective) {
30 | return upperDirectiveTransformer(fieldConfig);
31 | }
32 | },
33 | });
34 | }
35 |
--------------------------------------------------------------------------------
/@generated/user/user-create-many.input.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { InputType } from '@nestjs/graphql';
3 | import * as Scalars from 'graphql-scalars';
4 | import { Role } from '../prisma/role.enum';
5 | import { Float } from '@nestjs/graphql';
6 | import { UserStatus } from '../prisma/user-status.enum';
7 |
8 | @InputType()
9 | export class UserCreateManyInput {
10 | @Field(() => String, { nullable: true })
11 | id?: string;
12 |
13 | @Field(() => Date, { nullable: true })
14 | create_date?: Date | string;
15 |
16 | @Field(() => Date, { nullable: true })
17 | update_date?: Date | string;
18 |
19 | @Field(() => Boolean, { nullable: true })
20 | isDelete?: boolean;
21 |
22 | @Field(() => String, { nullable: false })
23 | username!: string;
24 |
25 | @Field(() => Scalars.GraphQLEmailAddress, { nullable: false })
26 | email!: string;
27 |
28 | @Field(() => String, { nullable: false })
29 | password!: string;
30 |
31 | @Field(() => Role, { nullable: true })
32 | role?: keyof typeof Role;
33 |
34 | @Field(() => String, { nullable: true })
35 | RFID?: string;
36 |
37 | @Field(() => String, { nullable: true })
38 | description?: string;
39 |
40 | @Field(() => Float, { nullable: true })
41 | expired?: number;
42 |
43 | @Field(() => UserStatus, { nullable: true })
44 | status?: keyof typeof UserStatus;
45 | }
46 |
--------------------------------------------------------------------------------
/commons/public-decorator/swagger.ts:
--------------------------------------------------------------------------------
1 | import {
2 | ApiProperty as ApiPropertySource,
3 | ApiOperation as ApiOperationSource,
4 | ApiPropertyOptions,
5 | ApiOperationOptions,
6 | } from '@nestjs/swagger';
7 | import { getKeys, getEnumRemark } from '../public-tool';
8 |
9 | /**
10 | * swagger 标注
11 | */
12 | export const ApiProperty = (
13 | description: string,
14 | options?: ApiPropertyOptions,
15 | ) => {
16 | return ApiPropertySource({ description, ...options });
17 | };
18 |
19 | /**
20 | * swagger 枚举标注
21 | */
22 | export const ApiPropertyEnum = (
23 | description: string,
24 | object: object,
25 | options?: ApiPropertyOptions,
26 | ) => {
27 | return ApiPropertySource({
28 | enum: getKeys(object),
29 | description: `${description},${getEnumRemark(object)}`,
30 | ...options,
31 | });
32 | };
33 |
34 | /**
35 | * swagger 数组标注
36 | */
37 | export const ApiPropertyArray = (
38 | description: string,
39 | object: object,
40 | options?: ApiPropertyOptions,
41 | ) => {
42 | return ApiPropertySource({
43 | description: `${description},${getEnumRemark(object)}`,
44 | type: [String],
45 | ...options,
46 | });
47 | };
48 |
49 | /**
50 | * swagger 路由标注
51 | */
52 | export const ApiOperation = (
53 | summary: string,
54 | options?: ApiOperationOptions,
55 | ) => {
56 | return ApiOperationSource({ summary, ...options });
57 | };
58 |
--------------------------------------------------------------------------------
/example/rabbit/init.ts:
--------------------------------------------------------------------------------
1 | import { connect } from 'amqplib';
2 | import { run } from './consumer';
3 | let connection = null;
4 | let isConnection = false;
5 | let reconnectingCount = 0;
6 |
7 | export const init = () => {
8 | connect('amqp://rabbit:rabbit@localhost:5672')
9 | .then((conn: any) => {
10 | connection = conn;
11 | conn.on('error', function (err) {
12 | reconnecting(err, 'error');
13 | });
14 | conn.on('close', function (err) {
15 | reconnecting(err, 'close');
16 | });
17 |
18 | console.log('rabbitmq 连接成功');
19 | isConnection = false;
20 | reconnectingCount = 0;
21 | run(connection); // 开启消费者
22 | return connection;
23 | })
24 | .catch((err: any) => {
25 | isConnection = false;
26 | reconnecting(err, 'catch');
27 | });
28 | };
29 |
30 | /**
31 | * 重连
32 | * @param { Object } err
33 | */
34 | const reconnecting = (err: any, event: any) => {
35 | if (!isConnection) {
36 | isConnection = true;
37 | reconnectingCount++;
38 | console.error(
39 | `Lost connection to RMQ. reconnectingCount: ${reconnectingCount}. Reconnecting in 10 seconds...`,
40 | );
41 | console.error('Rabbitmq close: ', event, err);
42 | // 定时重连
43 | return setTimeout(init, 10 * 1000);
44 | }
45 | };
46 |
--------------------------------------------------------------------------------
/example/rabbit/producer.ts:
--------------------------------------------------------------------------------
1 | import { connect } from 'amqplib';
2 | export const producerDLX = async ({
3 | testExchange,
4 | testQueue,
5 | testExchangeDLX,
6 | testRoutingKeyDLX,
7 | }) => {
8 | try {
9 | const connection = await connect('amqp://rabbit:rabbit@localhost:5672');
10 | const ch = await connection.createChannel();
11 | await ch.assertExchange(testExchange, 'direct', { durable: true });
12 | const queueResult = await ch.assertQueue(testQueue, {
13 | exclusive: false,
14 | // 设置 DLX,当正常队列的消息成为死信后会被路由到 DLX 中
15 | deadLetterExchange: testExchangeDLX,
16 | // 设置 DLX 指定的路由键
17 | deadLetterRoutingKey: testRoutingKeyDLX,
18 | });
19 | // 绑定队列
20 | await ch.bindQueue(queueResult.queue, testExchange, 'direct');
21 | const msg = 'hello world!';
22 | console.log('生产者 msg:', testQueue + msg);
23 | // 发送到队列
24 | await ch.sendToQueue(queueResult.queue, Buffer.from(msg), {
25 | expiration: '10000',
26 | });
27 | ch.close();
28 | } catch (error) {
29 | console.error('生产消息 Error:', error);
30 | }
31 | };
32 |
33 | export const run = () => {
34 | producerDLX({
35 | testExchange: 'first-Ex',
36 | testQueue: 'first-Queue',
37 | testExchangeDLX: 'first-ExDLX',
38 | testRoutingKeyDLX: 'first-RoutingKeyDLX',
39 | });
40 | };
41 |
--------------------------------------------------------------------------------
/@generated/user/user-max-order-by-aggregate.input.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { InputType } from '@nestjs/graphql';
3 | import { SortOrder } from '../prisma/sort-order.enum';
4 |
5 | @InputType()
6 | export class UserMaxOrderByAggregateInput {
7 | @Field(() => SortOrder, { nullable: true })
8 | id?: keyof typeof SortOrder;
9 |
10 | @Field(() => SortOrder, { nullable: true })
11 | create_date?: keyof typeof SortOrder;
12 |
13 | @Field(() => SortOrder, { nullable: true })
14 | update_date?: keyof typeof SortOrder;
15 |
16 | @Field(() => SortOrder, { nullable: true })
17 | isDelete?: keyof typeof SortOrder;
18 |
19 | @Field(() => SortOrder, { nullable: true })
20 | username?: keyof typeof SortOrder;
21 |
22 | @Field(() => SortOrder, { nullable: true })
23 | email?: keyof typeof SortOrder;
24 |
25 | @Field(() => SortOrder, { nullable: true })
26 | password?: keyof typeof SortOrder;
27 |
28 | @Field(() => SortOrder, { nullable: true })
29 | role?: keyof typeof SortOrder;
30 |
31 | @Field(() => SortOrder, { nullable: true })
32 | RFID?: keyof typeof SortOrder;
33 |
34 | @Field(() => SortOrder, { nullable: true })
35 | description?: keyof typeof SortOrder;
36 |
37 | @Field(() => SortOrder, { nullable: true })
38 | expired?: keyof typeof SortOrder;
39 |
40 | @Field(() => SortOrder, { nullable: true })
41 | status?: keyof typeof SortOrder;
42 | }
43 |
--------------------------------------------------------------------------------
/@generated/user/user-min-order-by-aggregate.input.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { InputType } from '@nestjs/graphql';
3 | import { SortOrder } from '../prisma/sort-order.enum';
4 |
5 | @InputType()
6 | export class UserMinOrderByAggregateInput {
7 | @Field(() => SortOrder, { nullable: true })
8 | id?: keyof typeof SortOrder;
9 |
10 | @Field(() => SortOrder, { nullable: true })
11 | create_date?: keyof typeof SortOrder;
12 |
13 | @Field(() => SortOrder, { nullable: true })
14 | update_date?: keyof typeof SortOrder;
15 |
16 | @Field(() => SortOrder, { nullable: true })
17 | isDelete?: keyof typeof SortOrder;
18 |
19 | @Field(() => SortOrder, { nullable: true })
20 | username?: keyof typeof SortOrder;
21 |
22 | @Field(() => SortOrder, { nullable: true })
23 | email?: keyof typeof SortOrder;
24 |
25 | @Field(() => SortOrder, { nullable: true })
26 | password?: keyof typeof SortOrder;
27 |
28 | @Field(() => SortOrder, { nullable: true })
29 | role?: keyof typeof SortOrder;
30 |
31 | @Field(() => SortOrder, { nullable: true })
32 | RFID?: keyof typeof SortOrder;
33 |
34 | @Field(() => SortOrder, { nullable: true })
35 | description?: keyof typeof SortOrder;
36 |
37 | @Field(() => SortOrder, { nullable: true })
38 | expired?: keyof typeof SortOrder;
39 |
40 | @Field(() => SortOrder, { nullable: true })
41 | status?: keyof typeof SortOrder;
42 | }
43 |
--------------------------------------------------------------------------------
/@generated/user/user-count-order-by-aggregate.input.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { InputType } from '@nestjs/graphql';
3 | import { SortOrder } from '../prisma/sort-order.enum';
4 |
5 | @InputType()
6 | export class UserCountOrderByAggregateInput {
7 | @Field(() => SortOrder, { nullable: true })
8 | id?: keyof typeof SortOrder;
9 |
10 | @Field(() => SortOrder, { nullable: true })
11 | create_date?: keyof typeof SortOrder;
12 |
13 | @Field(() => SortOrder, { nullable: true })
14 | update_date?: keyof typeof SortOrder;
15 |
16 | @Field(() => SortOrder, { nullable: true })
17 | isDelete?: keyof typeof SortOrder;
18 |
19 | @Field(() => SortOrder, { nullable: true })
20 | username?: keyof typeof SortOrder;
21 |
22 | @Field(() => SortOrder, { nullable: true })
23 | email?: keyof typeof SortOrder;
24 |
25 | @Field(() => SortOrder, { nullable: true })
26 | password?: keyof typeof SortOrder;
27 |
28 | @Field(() => SortOrder, { nullable: true })
29 | role?: keyof typeof SortOrder;
30 |
31 | @Field(() => SortOrder, { nullable: true })
32 | RFID?: keyof typeof SortOrder;
33 |
34 | @Field(() => SortOrder, { nullable: true })
35 | description?: keyof typeof SortOrder;
36 |
37 | @Field(() => SortOrder, { nullable: true })
38 | expired?: keyof typeof SortOrder;
39 |
40 | @Field(() => SortOrder, { nullable: true })
41 | status?: keyof typeof SortOrder;
42 | }
43 |
--------------------------------------------------------------------------------
/@generated/prisma/date-time-with-aggregates-filter.input.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { InputType } from '@nestjs/graphql';
3 | import { NestedDateTimeWithAggregatesFilter } from './nested-date-time-with-aggregates-filter.input';
4 | import { NestedIntFilter } from './nested-int-filter.input';
5 | import { NestedDateTimeFilter } from './nested-date-time-filter.input';
6 |
7 | @InputType()
8 | export class DateTimeWithAggregatesFilter {
9 | @Field(() => Date, { nullable: true })
10 | equals?: Date | string;
11 |
12 | @Field(() => [Date], { nullable: true })
13 | in?: Array | Array;
14 |
15 | @Field(() => [Date], { nullable: true })
16 | notIn?: Array | Array;
17 |
18 | @Field(() => Date, { nullable: true })
19 | lt?: Date | string;
20 |
21 | @Field(() => Date, { nullable: true })
22 | lte?: Date | string;
23 |
24 | @Field(() => Date, { nullable: true })
25 | gt?: Date | string;
26 |
27 | @Field(() => Date, { nullable: true })
28 | gte?: Date | string;
29 |
30 | @Field(() => NestedDateTimeWithAggregatesFilter, { nullable: true })
31 | not?: NestedDateTimeWithAggregatesFilter;
32 |
33 | @Field(() => NestedIntFilter, { nullable: true })
34 | _count?: NestedIntFilter;
35 |
36 | @Field(() => NestedDateTimeFilter, { nullable: true })
37 | _min?: NestedDateTimeFilter;
38 |
39 | @Field(() => NestedDateTimeFilter, { nullable: true })
40 | _max?: NestedDateTimeFilter;
41 | }
42 |
--------------------------------------------------------------------------------
/src/resolver/auth/auth.entity.ts:
--------------------------------------------------------------------------------
1 | import { ApiProperty, ApiPropertyEnum } from 'commons/public-decorator';
2 |
3 | /**
4 | * 状态
5 | */
6 | export const ACCOUNT_ROLE = ['USER', 'ADMIN', 'SYSTEM'];
7 |
8 | export class LoginDto {
9 | @ApiProperty('用户名')
10 | username!: string;
11 |
12 | @ApiProperty('密码')
13 | password!: string;
14 | }
15 |
16 | export class UserInfo {
17 | @ApiProperty('用户ID')
18 | id!: string;
19 |
20 | @ApiProperty('用户名')
21 | username!: string;
22 |
23 | @ApiProperty('邮箱')
24 | email!: string;
25 |
26 | @ApiProperty('手机号')
27 | phone!: string;
28 |
29 | @ApiPropertyEnum('角色', ACCOUNT_ROLE)
30 | role: string;
31 | }
32 |
33 | export class LoginInfo extends UserInfo {
34 | @ApiProperty('token')
35 | accessToken: string;
36 |
37 | @ApiProperty('续租token')
38 | refreshToken: string;
39 | }
40 |
41 | export class UserInfoResponse {
42 | @ApiProperty('状态编码')
43 | code!: number;
44 |
45 | @ApiProperty('返回信息')
46 | data!: UserInfo;
47 | }
48 |
49 | export class LoginInfoResponse {
50 | @ApiProperty('状态编码')
51 | code!: number;
52 |
53 | @ApiProperty('返回信息')
54 | data!: LoginInfo;
55 | }
56 |
57 | export class RegisternInfoDto {
58 | @ApiProperty('用户名')
59 | username!: string;
60 |
61 | @ApiProperty('用户名')
62 | password!: string;
63 |
64 | @ApiProperty('邮箱')
65 | email!: string;
66 | }
67 |
68 | export class AccountAdmin {
69 | @ApiProperty('用户ID')
70 | id!: string;
71 |
72 | @ApiProperty('用户名')
73 | username!: string;
74 |
75 | // @ApiProperty('用户名')
76 | // password!: string;
77 | }
78 |
--------------------------------------------------------------------------------
/@generated/prisma/nested-string-with-aggregates-filter.input.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { InputType } from '@nestjs/graphql';
3 | import { NestedIntFilter } from './nested-int-filter.input';
4 | import { NestedStringFilter } from './nested-string-filter.input';
5 |
6 | @InputType()
7 | export class NestedStringWithAggregatesFilter {
8 | @Field(() => String, { nullable: true })
9 | equals?: string;
10 |
11 | @Field(() => [String], { nullable: true })
12 | in?: Array;
13 |
14 | @Field(() => [String], { nullable: true })
15 | notIn?: Array;
16 |
17 | @Field(() => String, { nullable: true })
18 | lt?: string;
19 |
20 | @Field(() => String, { nullable: true })
21 | lte?: string;
22 |
23 | @Field(() => String, { nullable: true })
24 | gt?: string;
25 |
26 | @Field(() => String, { nullable: true })
27 | gte?: string;
28 |
29 | @Field(() => String, { nullable: true })
30 | contains?: string;
31 |
32 | @Field(() => String, { nullable: true })
33 | startsWith?: string;
34 |
35 | @Field(() => String, { nullable: true })
36 | endsWith?: string;
37 |
38 | @Field(() => String, { nullable: true })
39 | search?: string;
40 |
41 | @Field(() => NestedStringWithAggregatesFilter, { nullable: true })
42 | not?: NestedStringWithAggregatesFilter;
43 |
44 | @Field(() => NestedIntFilter, { nullable: true })
45 | _count?: NestedIntFilter;
46 |
47 | @Field(() => NestedStringFilter, { nullable: true })
48 | _min?: NestedStringFilter;
49 |
50 | @Field(() => NestedStringFilter, { nullable: true })
51 | _max?: NestedStringFilter;
52 | }
53 |
--------------------------------------------------------------------------------
/commons/public-decorator/util.ts:
--------------------------------------------------------------------------------
1 | import { CharacterFilterWarn } from 'commons/public-module/errors/errorsGql';
2 | import { isObject } from 'util';
3 | /**
4 | * 查询字符串检查
5 | * @param {Object} values 查询条件
6 | */
7 | export const queryCharacterCheck = (values) => {
8 | if (isObject(values)) {
9 | for (const key in values) {
10 | const value = values[key];
11 | return queryCharacterCheck(value);
12 | }
13 | } else if (Array.isArray(values)) {
14 | for (let index = 0; index < values.length; index++) {
15 | const value = values[index];
16 | return queryCharacterCheck(value);
17 | }
18 | }
19 | // 查询输入条件不能包含非法字符;
20 | if (isString(values) && /[`~!#$%^&*(){}\[\]|\\'"<>?,]|^_$/.test(values)) {
21 | throw new CharacterFilterWarn(
22 | '查询输入条件不能包含非法字符[\\`~!#$%^&*(){}[]|\'"<>?,]',
23 | {
24 | values,
25 | },
26 | );
27 | }
28 |
29 | return true;
30 | };
31 |
32 | /**
33 | * 是否字符串
34 | * @param {Object} obj 任意对象
35 | */
36 | export const isString = (obj) => {
37 | return Object.prototype.toString.call(obj) === '[object String]';
38 | };
39 |
40 | /**
41 | * 构造prisma查询字段select格式
42 | * @param list
43 | * @param selectObj select构造对象
44 | * @returns
45 | */
46 | export function selectInfo(selectList: any, selectObj: any) {
47 | for (const item of selectList) {
48 | if (item.selectionSet) {
49 | const signInfo = selectInfo(item.selectionSet.selections, {});
50 | selectObj[`${item.name.value}`] = { select: signInfo };
51 | } else {
52 | selectObj[`${item.name.value}`] = true;
53 | }
54 | }
55 | return selectObj;
56 | }
57 |
--------------------------------------------------------------------------------
/@generated/prisma/string-with-aggregates-filter.input.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { InputType } from '@nestjs/graphql';
3 | import { NestedStringWithAggregatesFilter } from './nested-string-with-aggregates-filter.input';
4 | import { NestedIntFilter } from './nested-int-filter.input';
5 | import { NestedStringFilter } from './nested-string-filter.input';
6 |
7 | @InputType()
8 | export class StringWithAggregatesFilter {
9 | @Field(() => String, { nullable: true })
10 | equals?: string;
11 |
12 | @Field(() => [String], { nullable: true })
13 | in?: Array;
14 |
15 | @Field(() => [String], { nullable: true })
16 | notIn?: Array;
17 |
18 | @Field(() => String, { nullable: true })
19 | lt?: string;
20 |
21 | @Field(() => String, { nullable: true })
22 | lte?: string;
23 |
24 | @Field(() => String, { nullable: true })
25 | gt?: string;
26 |
27 | @Field(() => String, { nullable: true })
28 | gte?: string;
29 |
30 | @Field(() => String, { nullable: true })
31 | contains?: string;
32 |
33 | @Field(() => String, { nullable: true })
34 | startsWith?: string;
35 |
36 | @Field(() => String, { nullable: true })
37 | endsWith?: string;
38 |
39 | @Field(() => String, { nullable: true })
40 | search?: string;
41 |
42 | @Field(() => NestedStringWithAggregatesFilter, { nullable: true })
43 | not?: NestedStringWithAggregatesFilter;
44 |
45 | @Field(() => NestedIntFilter, { nullable: true })
46 | _count?: NestedIntFilter;
47 |
48 | @Field(() => NestedStringFilter, { nullable: true })
49 | _min?: NestedStringFilter;
50 |
51 | @Field(() => NestedStringFilter, { nullable: true })
52 | _max?: NestedStringFilter;
53 | }
54 |
--------------------------------------------------------------------------------
/@generated/prisma/nested-string-nullable-with-aggregates-filter.input.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { InputType } from '@nestjs/graphql';
3 | import { NestedIntNullableFilter } from './nested-int-nullable-filter.input';
4 | import { NestedStringNullableFilter } from './nested-string-nullable-filter.input';
5 |
6 | @InputType()
7 | export class NestedStringNullableWithAggregatesFilter {
8 | @Field(() => String, { nullable: true })
9 | equals?: string;
10 |
11 | @Field(() => [String], { nullable: true })
12 | in?: Array;
13 |
14 | @Field(() => [String], { nullable: true })
15 | notIn?: Array;
16 |
17 | @Field(() => String, { nullable: true })
18 | lt?: string;
19 |
20 | @Field(() => String, { nullable: true })
21 | lte?: string;
22 |
23 | @Field(() => String, { nullable: true })
24 | gt?: string;
25 |
26 | @Field(() => String, { nullable: true })
27 | gte?: string;
28 |
29 | @Field(() => String, { nullable: true })
30 | contains?: string;
31 |
32 | @Field(() => String, { nullable: true })
33 | startsWith?: string;
34 |
35 | @Field(() => String, { nullable: true })
36 | endsWith?: string;
37 |
38 | @Field(() => String, { nullable: true })
39 | search?: string;
40 |
41 | @Field(() => NestedStringNullableWithAggregatesFilter, { nullable: true })
42 | not?: NestedStringNullableWithAggregatesFilter;
43 |
44 | @Field(() => NestedIntNullableFilter, { nullable: true })
45 | _count?: NestedIntNullableFilter;
46 |
47 | @Field(() => NestedStringNullableFilter, { nullable: true })
48 | _min?: NestedStringNullableFilter;
49 |
50 | @Field(() => NestedStringNullableFilter, { nullable: true })
51 | _max?: NestedStringNullableFilter;
52 | }
53 |
--------------------------------------------------------------------------------
/services/index.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { OrganizeModule } from './organize/organize.module';
3 | import { PersonModule } from './person/person.module';
4 | import { UserModule } from './user/user.module';
5 | import { UserGroupModule } from './user-group/user-group.module';
6 | import { ModuleModule } from './module/module.module';
7 | import { MenuModule } from './menu/menu.module';
8 | import { ActionGroupModule } from './action-group/action-group.module';
9 | import { ActionModule } from './action/action.module';
10 | import { ActionBasisModule } from './action-basis/action-basis.module';
11 | import { PermissionModule } from './permission/permission.module';
12 | import { PermissionGroupModule } from './permission-group/permission-group.module';
13 | import { SignatureRuleModule } from './signature-rule/signature-rule.module';
14 | import { SignaturePermissionModule } from './signature-permission/signature-permission.module';
15 | import { SignatureGroupModule } from './signature-group/signature-group.module';
16 | import { SignatureMethodModule } from './signature-method/signature-method.module';
17 | import { AnimalModule } from './animal/animal.module';
18 | import { DogModule } from './dog/dog.module';
19 | @Module({
20 | imports: [
21 | OrganizeModule,
22 | PersonModule,
23 | UserModule,
24 | UserGroupModule,
25 | ModuleModule,
26 | MenuModule,
27 | ActionGroupModule,
28 | ActionModule,
29 | ActionBasisModule,
30 | PermissionModule,
31 | PermissionGroupModule,
32 | SignatureRuleModule,
33 | SignaturePermissionModule,
34 | SignatureGroupModule,
35 | SignatureMethodModule,
36 | AnimalModule,
37 | DogModule,
38 | ],
39 | })
40 | export class AllModules {}
41 |
--------------------------------------------------------------------------------
/@generated/prisma/string-nullable-with-aggregates-filter.input.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { InputType } from '@nestjs/graphql';
3 | import { NestedStringNullableWithAggregatesFilter } from './nested-string-nullable-with-aggregates-filter.input';
4 | import { NestedIntNullableFilter } from './nested-int-nullable-filter.input';
5 | import { NestedStringNullableFilter } from './nested-string-nullable-filter.input';
6 |
7 | @InputType()
8 | export class StringNullableWithAggregatesFilter {
9 | @Field(() => String, { nullable: true })
10 | equals?: string;
11 |
12 | @Field(() => [String], { nullable: true })
13 | in?: Array;
14 |
15 | @Field(() => [String], { nullable: true })
16 | notIn?: Array;
17 |
18 | @Field(() => String, { nullable: true })
19 | lt?: string;
20 |
21 | @Field(() => String, { nullable: true })
22 | lte?: string;
23 |
24 | @Field(() => String, { nullable: true })
25 | gt?: string;
26 |
27 | @Field(() => String, { nullable: true })
28 | gte?: string;
29 |
30 | @Field(() => String, { nullable: true })
31 | contains?: string;
32 |
33 | @Field(() => String, { nullable: true })
34 | startsWith?: string;
35 |
36 | @Field(() => String, { nullable: true })
37 | endsWith?: string;
38 |
39 | @Field(() => String, { nullable: true })
40 | search?: string;
41 |
42 | @Field(() => NestedStringNullableWithAggregatesFilter, { nullable: true })
43 | not?: NestedStringNullableWithAggregatesFilter;
44 |
45 | @Field(() => NestedIntNullableFilter, { nullable: true })
46 | _count?: NestedIntNullableFilter;
47 |
48 | @Field(() => NestedStringNullableFilter, { nullable: true })
49 | _min?: NestedStringNullableFilter;
50 |
51 | @Field(() => NestedStringNullableFilter, { nullable: true })
52 | _max?: NestedStringNullableFilter;
53 | }
54 |
--------------------------------------------------------------------------------
/@generated/user/user-aggregate.args.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { ArgsType } from '@nestjs/graphql';
3 | import { UserWhereInput } from './user-where.input';
4 | import { UserOrderByWithRelationAndSearchRelevanceInput } from './user-order-by-with-relation-and-search-relevance.input';
5 | import { UserWhereUniqueInput } from './user-where-unique.input';
6 | import { Int } from '@nestjs/graphql';
7 | import { UserCountAggregateInput } from './user-count-aggregate.input';
8 | import { UserAvgAggregateInput } from './user-avg-aggregate.input';
9 | import { UserSumAggregateInput } from './user-sum-aggregate.input';
10 | import { UserMinAggregateInput } from './user-min-aggregate.input';
11 | import { UserMaxAggregateInput } from './user-max-aggregate.input';
12 |
13 | @ArgsType()
14 | export class UserAggregateArgs {
15 | @Field(() => UserWhereInput, { nullable: true })
16 | where?: UserWhereInput;
17 |
18 | @Field(() => [UserOrderByWithRelationAndSearchRelevanceInput], {
19 | nullable: true,
20 | })
21 | orderBy?: Array;
22 |
23 | @Field(() => UserWhereUniqueInput, { nullable: true })
24 | cursor?: UserWhereUniqueInput;
25 |
26 | @Field(() => Int, { nullable: true })
27 | take?: number;
28 |
29 | @Field(() => Int, { nullable: true })
30 | skip?: number;
31 |
32 | @Field(() => UserCountAggregateInput, { nullable: true })
33 | _count?: UserCountAggregateInput;
34 |
35 | @Field(() => UserAvgAggregateInput, { nullable: true })
36 | _avg?: UserAvgAggregateInput;
37 |
38 | @Field(() => UserSumAggregateInput, { nullable: true })
39 | _sum?: UserSumAggregateInput;
40 |
41 | @Field(() => UserMinAggregateInput, { nullable: true })
42 | _min?: UserMinAggregateInput;
43 |
44 | @Field(() => UserMaxAggregateInput, { nullable: true })
45 | _max?: UserMaxAggregateInput;
46 | }
47 |
--------------------------------------------------------------------------------
/@generated/user/user-create.input.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { InputType } from '@nestjs/graphql';
3 | import * as Scalars from 'graphql-scalars';
4 | import { Role } from '../prisma/role.enum';
5 | import { UserGroupCreateNestedManyWithoutUsersInput } from '../user-group/user-group-create-nested-many-without-users.input';
6 | import { PersonCreateNestedManyWithoutUserInput } from '../person/person-create-nested-many-without-user.input';
7 | import { Float } from '@nestjs/graphql';
8 | import { UserStatus } from '../prisma/user-status.enum';
9 |
10 | @InputType()
11 | export class UserCreateInput {
12 | @Field(() => String, { nullable: true })
13 | id?: string;
14 |
15 | @Field(() => Date, { nullable: true })
16 | create_date?: Date | string;
17 |
18 | @Field(() => Date, { nullable: true })
19 | update_date?: Date | string;
20 |
21 | @Field(() => Boolean, { nullable: true })
22 | isDelete?: boolean;
23 |
24 | @Field(() => String, { nullable: false })
25 | username!: string;
26 |
27 | @Field(() => Scalars.GraphQLEmailAddress, { nullable: false })
28 | email!: string;
29 |
30 | @Field(() => String, { nullable: false })
31 | password!: string;
32 |
33 | @Field(() => Role, { nullable: true })
34 | role?: keyof typeof Role;
35 |
36 | @Field(() => UserGroupCreateNestedManyWithoutUsersInput, { nullable: true })
37 | group?: UserGroupCreateNestedManyWithoutUsersInput;
38 |
39 | @Field(() => String, { nullable: true })
40 | RFID?: string;
41 |
42 | @Field(() => PersonCreateNestedManyWithoutUserInput, { nullable: true })
43 | person?: PersonCreateNestedManyWithoutUserInput;
44 |
45 | @Field(() => String, { nullable: true })
46 | description?: string;
47 |
48 | @Field(() => Float, { nullable: true })
49 | expired?: number;
50 |
51 | @Field(() => UserStatus, { nullable: true })
52 | status?: keyof typeof UserStatus;
53 | }
54 |
--------------------------------------------------------------------------------
/commons/public-decorator/validator/common.ts:
--------------------------------------------------------------------------------
1 | import {
2 | IsIn as IsInSource,
3 | Matches as MatchesSource,
4 | IsInt as IsIntSource,
5 | IsString as IsStringSource,
6 | IsObject as IsObjectSource,
7 | IsNotEmpty as IsNotEmptySource,
8 | IsDate as IsDateSource,
9 | ValidationOptions,
10 | } from 'class-validator';
11 | import { getKeys, getEnumRemark } from '../../public-tool';
12 |
13 | // 为了统一引用,导出常用装饰器
14 | export { IsOptional } from 'class-validator';
15 |
16 | /**
17 | * 验证是否输入
18 | */
19 | export const IsNotEmpty = (message: string, options?: ValidationOptions) => {
20 | return IsNotEmptySource({ message: `请输入${message}`, ...options });
21 | };
22 |
23 | /**
24 | * 验证在数值范围内
25 | */
26 | export const IsIn = (
27 | object: object,
28 | message = '值',
29 | options?: ValidationOptions,
30 | ) => {
31 | return IsInSource(getKeys(object), {
32 | message: `请选择正确的${message},${getEnumRemark(object)}`,
33 | ...options,
34 | });
35 | };
36 |
37 | /**
38 | * 正则验证
39 | */
40 | export const Matches = (
41 | pattern: RegExp,
42 | message: string,
43 | options?: ValidationOptions,
44 | ) => {
45 | return MatchesSource(pattern, { message, ...options });
46 | };
47 |
48 | /**
49 | * 验证是否数字
50 | */
51 | export const IsInt = (message: string, options?: ValidationOptions) => {
52 | return IsIntSource({ message: `${message}只能为数字`, ...options });
53 | };
54 |
55 | /**
56 | * 验证是否字符串
57 | */
58 | export const IsString = (message: string, options?: ValidationOptions) => {
59 | return IsStringSource({ message: `${message}只能为字符串`, ...options });
60 | };
61 |
62 | /**
63 | * 验证是否时间类型
64 | */
65 | export const IsDate = (message: string, options?: ValidationOptions) => {
66 | return IsDateSource({ message: `${message}只能为时间类型`, ...options });
67 | };
68 |
69 | /**
70 | * 验证是否对象类型
71 | */
72 | export const IsObject = (message: string, options?: ValidationOptions) => {
73 | return IsObjectSource({ message: `${message}只能为对象类型`, ...options });
74 | };
75 |
--------------------------------------------------------------------------------
/@generated/user/user-group-by.args.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { ArgsType } from '@nestjs/graphql';
3 | import { UserWhereInput } from './user-where.input';
4 | import { UserOrderByWithAggregationInput } from './user-order-by-with-aggregation.input';
5 | import { UserScalarFieldEnum } from './user-scalar-field.enum';
6 | import { UserScalarWhereWithAggregatesInput } from './user-scalar-where-with-aggregates.input';
7 | import { Int } from '@nestjs/graphql';
8 | import { UserCountAggregateInput } from './user-count-aggregate.input';
9 | import { UserAvgAggregateInput } from './user-avg-aggregate.input';
10 | import { UserSumAggregateInput } from './user-sum-aggregate.input';
11 | import { UserMinAggregateInput } from './user-min-aggregate.input';
12 | import { UserMaxAggregateInput } from './user-max-aggregate.input';
13 |
14 | @ArgsType()
15 | export class UserGroupByArgs {
16 | @Field(() => UserWhereInput, { nullable: true })
17 | where?: UserWhereInput;
18 |
19 | @Field(() => [UserOrderByWithAggregationInput], { nullable: true })
20 | orderBy?: Array;
21 |
22 | @Field(() => [UserScalarFieldEnum], { nullable: false })
23 | by!: Array;
24 |
25 | @Field(() => UserScalarWhereWithAggregatesInput, { nullable: true })
26 | having?: UserScalarWhereWithAggregatesInput;
27 |
28 | @Field(() => Int, { nullable: true })
29 | take?: number;
30 |
31 | @Field(() => Int, { nullable: true })
32 | skip?: number;
33 |
34 | @Field(() => UserCountAggregateInput, { nullable: true })
35 | _count?: UserCountAggregateInput;
36 |
37 | @Field(() => UserAvgAggregateInput, { nullable: true })
38 | _avg?: UserAvgAggregateInput;
39 |
40 | @Field(() => UserSumAggregateInput, { nullable: true })
41 | _sum?: UserSumAggregateInput;
42 |
43 | @Field(() => UserMinAggregateInput, { nullable: true })
44 | _min?: UserMinAggregateInput;
45 |
46 | @Field(() => UserMaxAggregateInput, { nullable: true })
47 | _max?: UserMaxAggregateInput;
48 | }
49 |
--------------------------------------------------------------------------------
/@generated/user/user-unchecked-create.input.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { InputType } from '@nestjs/graphql';
3 | import * as Scalars from 'graphql-scalars';
4 | import { Role } from '../prisma/role.enum';
5 | import { UserGroupUncheckedCreateNestedManyWithoutUsersInput } from '../user-group/user-group-unchecked-create-nested-many-without-users.input';
6 | import { PersonUncheckedCreateNestedManyWithoutUserInput } from '../person/person-unchecked-create-nested-many-without-user.input';
7 | import { Float } from '@nestjs/graphql';
8 | import { UserStatus } from '../prisma/user-status.enum';
9 |
10 | @InputType()
11 | export class UserUncheckedCreateInput {
12 | @Field(() => String, { nullable: true })
13 | id?: string;
14 |
15 | @Field(() => Date, { nullable: true })
16 | create_date?: Date | string;
17 |
18 | @Field(() => Date, { nullable: true })
19 | update_date?: Date | string;
20 |
21 | @Field(() => Boolean, { nullable: true })
22 | isDelete?: boolean;
23 |
24 | @Field(() => String, { nullable: false })
25 | username!: string;
26 |
27 | @Field(() => Scalars.GraphQLEmailAddress, { nullable: false })
28 | email!: string;
29 |
30 | @Field(() => String, { nullable: false })
31 | password!: string;
32 |
33 | @Field(() => Role, { nullable: true })
34 | role?: keyof typeof Role;
35 |
36 | @Field(() => UserGroupUncheckedCreateNestedManyWithoutUsersInput, {
37 | nullable: true,
38 | })
39 | group?: UserGroupUncheckedCreateNestedManyWithoutUsersInput;
40 |
41 | @Field(() => String, { nullable: true })
42 | RFID?: string;
43 |
44 | @Field(() => PersonUncheckedCreateNestedManyWithoutUserInput, {
45 | nullable: true,
46 | })
47 | person?: PersonUncheckedCreateNestedManyWithoutUserInput;
48 |
49 | @Field(() => String, { nullable: true })
50 | description?: string;
51 |
52 | @Field(() => Float, { nullable: true })
53 | expired?: number;
54 |
55 | @Field(() => UserStatus, { nullable: true })
56 | status?: keyof typeof UserStatus;
57 | }
58 |
--------------------------------------------------------------------------------
/commons/public-module/roles/role.guard.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
2 | import { GqlExecutionContext } from '@nestjs/graphql';
3 | import { Reflector } from '@nestjs/core';
4 | import { ROLES_KEY } from './role.decorator';
5 | import { Role } from './role.enum';
6 | import { prisma } from 'commons/public-tool/prisma';
7 | import {
8 | ForbiddenError,
9 | AuthenticationError,
10 | DataQueryError,
11 | } from 'commons/public-module/errors/errorsGql';
12 |
13 | @Injectable()
14 | export class RolesGuard implements CanActivate {
15 | constructor(private reflector: Reflector) {}
16 |
17 | async canActivate(context: ExecutionContext): Promise {
18 | const ctx = GqlExecutionContext.create(context);
19 | const requiredRoles = this.reflector.getAllAndOverride(
20 | ROLES_KEY,
21 | [context.getHandler(), context.getClass()],
22 | );
23 | if (!requiredRoles) {
24 | return true;
25 | }
26 | // 查询来自JWT中给req中增加的userInfo,判断用户是谁
27 | const username = context.switchToHttp().getRequest()
28 | ? context.switchToHttp().getRequest().userInfo.username
29 | : ctx.getContext().req.userInfo.username;
30 | if (!username) {
31 | if (!context.switchToHttp().getRequest()) {
32 | throw new AuthenticationError('当前用户禁止访问');
33 | }
34 | return false;
35 | }
36 | const userRole = await prisma.user.findUnique({
37 | where: { username },
38 | select: { role: true },
39 | });
40 | if (!userRole) {
41 | if (!context.switchToHttp().getRequest()) {
42 | throw new DataQueryError('用户信息不匹配', null);
43 | }
44 | return false;
45 | }
46 | const isPass = requiredRoles.some((role) =>
47 | userRole.role?.includes(role),
48 | );
49 | if (!isPass) {
50 | if (!context.switchToHttp().getRequest()) {
51 | throw new ForbiddenError('当前用户无权访问');
52 | }
53 | }
54 | return isPass;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/@generated/user/user-order-by-with-relation-and-search-relevance.input.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { InputType } from '@nestjs/graphql';
3 | import { SortOrder } from '../prisma/sort-order.enum';
4 | import { UserGroupOrderByRelationAggregateInput } from '../user-group/user-group-order-by-relation-aggregate.input';
5 | import { PersonOrderByRelationAggregateInput } from '../person/person-order-by-relation-aggregate.input';
6 | import { UserOrderByRelevanceInput } from './user-order-by-relevance.input';
7 |
8 | @InputType()
9 | export class UserOrderByWithRelationAndSearchRelevanceInput {
10 | @Field(() => SortOrder, { nullable: true })
11 | id?: keyof typeof SortOrder;
12 |
13 | @Field(() => SortOrder, { nullable: true })
14 | create_date?: keyof typeof SortOrder;
15 |
16 | @Field(() => SortOrder, { nullable: true })
17 | update_date?: keyof typeof SortOrder;
18 |
19 | @Field(() => SortOrder, { nullable: true })
20 | isDelete?: keyof typeof SortOrder;
21 |
22 | @Field(() => SortOrder, { nullable: true })
23 | username?: keyof typeof SortOrder;
24 |
25 | @Field(() => SortOrder, { nullable: true })
26 | email?: keyof typeof SortOrder;
27 |
28 | @Field(() => SortOrder, { nullable: true })
29 | password?: keyof typeof SortOrder;
30 |
31 | @Field(() => SortOrder, { nullable: true })
32 | role?: keyof typeof SortOrder;
33 |
34 | @Field(() => UserGroupOrderByRelationAggregateInput, { nullable: true })
35 | group?: UserGroupOrderByRelationAggregateInput;
36 |
37 | @Field(() => SortOrder, { nullable: true })
38 | RFID?: keyof typeof SortOrder;
39 |
40 | @Field(() => PersonOrderByRelationAggregateInput, { nullable: true })
41 | person?: PersonOrderByRelationAggregateInput;
42 |
43 | @Field(() => SortOrder, { nullable: true })
44 | description?: keyof typeof SortOrder;
45 |
46 | @Field(() => SortOrder, { nullable: true })
47 | expired?: keyof typeof SortOrder;
48 |
49 | @Field(() => SortOrder, { nullable: true })
50 | status?: keyof typeof SortOrder;
51 |
52 | @Field(() => UserOrderByRelevanceInput, { nullable: true })
53 | _relevance?: UserOrderByRelevanceInput;
54 | }
55 |
--------------------------------------------------------------------------------
/@generated/user/user-group-by.output.ts:
--------------------------------------------------------------------------------
1 | import { Field } from '@nestjs/graphql';
2 | import { ObjectType } from '@nestjs/graphql';
3 | import { HideField } from '@nestjs/graphql';
4 | import { Role } from '../prisma/role.enum';
5 | import { Float } from '@nestjs/graphql';
6 | import { UserStatus } from '../prisma/user-status.enum';
7 | import { UserCountAggregate } from './user-count-aggregate.output';
8 | import { UserAvgAggregate } from './user-avg-aggregate.output';
9 | import { UserSumAggregate } from './user-sum-aggregate.output';
10 | import { UserMinAggregate } from './user-min-aggregate.output';
11 | import { UserMaxAggregate } from './user-max-aggregate.output';
12 |
13 | @ObjectType()
14 | export class UserGroupBy {
15 | @Field(() => String, { nullable: false })
16 | id!: string;
17 |
18 | @Field(() => Date, { nullable: false })
19 | create_date!: Date | string;
20 |
21 | @Field(() => Date, { nullable: false })
22 | update_date!: Date | string;
23 |
24 | @Field(() => Boolean, { nullable: false })
25 | isDelete!: boolean;
26 |
27 | @Field(() => String, { nullable: false })
28 | username!: string;
29 |
30 | @Field(() => String, { nullable: false })
31 | email!: string;
32 |
33 | @HideField()
34 | password!: string;
35 |
36 | @Field(() => Role, { nullable: false })
37 | role!: keyof typeof Role;
38 |
39 | @Field(() => String, { nullable: true })
40 | RFID?: string;
41 |
42 | @Field(() => String, { nullable: true })
43 | description?: string;
44 |
45 | @Field(() => Float, { nullable: true })
46 | expired?: number;
47 |
48 | @Field(() => UserStatus, { nullable: false })
49 | status!: keyof typeof UserStatus;
50 |
51 | @Field(() => UserCountAggregate, { nullable: true })
52 | _count?: UserCountAggregate;
53 |
54 | @Field(() => UserAvgAggregate, { nullable: true })
55 | _avg?: UserAvgAggregate;
56 |
57 | @Field(() => UserSumAggregate, { nullable: true })
58 | _sum?: UserSumAggregate;
59 |
60 | @Field(() => UserMinAggregate, { nullable: true })
61 | _min?: UserMinAggregate;
62 |
63 | @Field(() => UserMaxAggregate, { nullable: true })
64 | _max?: UserMaxAggregate;
65 | }
66 |
--------------------------------------------------------------------------------
/src/resolver/auth/auth.controller.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Controller,
3 | UseGuards,
4 | Post,
5 | Get,
6 | Req,
7 | UseInterceptors,
8 | UseFilters,
9 | } from '@nestjs/common';
10 | import {
11 | ApiTags,
12 | ApiBody,
13 | ApiBearerAuth,
14 | ApiResponse,
15 | ApiHeaders,
16 | } from '@nestjs/swagger';
17 | import { JwtAuthGuard, LocalAuthGuard } from 'commons/public-module';
18 | import { ApiOperation } from 'commons/public-decorator';
19 | import { AuthService } from './auth.service';
20 | import {
21 | LoginInfoResponse,
22 | UserInfoResponse,
23 | LoginDto,
24 | RegisternInfoDto,
25 | } from './auth.entity';
26 | import { CommonResponse } from 'src/datos/common.dto';
27 | import { AllExceptionFilter, TransformInterceptor } from 'commons/public-tool';
28 |
29 | @ApiTags('登录鉴权')
30 | @Controller('auth')
31 | @UseInterceptors(TransformInterceptor)
32 | @UseFilters(AllExceptionFilter)
33 | export class AuthController {
34 | constructor(private readonly authService: AuthService) {}
35 |
36 | @Post('login')
37 | @UseGuards(LocalAuthGuard)
38 | @ApiBody({ type: LoginDto })
39 | @ApiResponse({ status: 201, type: LoginInfoResponse })
40 | @ApiOperation('登录')
41 | async login(@Req() req) {
42 | return this.authService.login(req);
43 | }
44 |
45 | @Get('profile')
46 | @ApiBearerAuth('Authorization')
47 | @ApiHeaders([
48 | {
49 | name: 'RefreshToken',
50 | description: 'Custom header',
51 | },
52 | ])
53 | @UseGuards(JwtAuthGuard)
54 | @ApiResponse({ status: 201, type: UserInfoResponse })
55 | @ApiOperation('获取帐号信息')
56 | getInfo(@Req() req) {
57 | const userInfo = this.authService.getInfo(req.user.id);
58 | req.user = userInfo;
59 | return userInfo;
60 | }
61 |
62 | @Get('logout')
63 | @ApiResponse({ status: 200, type: CommonResponse })
64 | @ApiOperation('退出登录')
65 | @ApiBearerAuth('Authorization')
66 | @UseGuards(JwtAuthGuard)
67 | async logout(@Req() req) {
68 | return this.authService.logout(req);
69 | }
70 |
71 | @Post('register')
72 | @ApiResponse({ status: 200, type: CommonResponse })
73 | @ApiBody({ type: RegisternInfoDto })
74 | @ApiOperation('注册')
75 | async register(@Req() req): Promise