Commit f7d37863 authored by nanahira's avatar nanahira

first

parents
Pipeline #19512 passed with stages
in 3 minutes and 31 seconds
# compiled output
/dist
/node_modules
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# OS
.DS_Store
# Tests
/coverage
/.nyc_output
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
/data
/output
/config.yaml
.git*
Dockerfile
.dockerignore
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir : __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
};
# compiled output
/dist
/node_modules
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# OS
.DS_Store
# Tests
/coverage
/.nyc_output
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
/data
/output
/config.yaml
\ No newline at end of file
stages:
- build
- deploy
variables:
GIT_DEPTH: "1"
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
.build-image:
stage: build
script:
- docker build --pull -t $TARGET_IMAGE .
- docker push $TARGET_IMAGE
build-x86:
extends: .build-image
tags:
- docker
variables:
TARGET_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-x86
build-arm:
extends: .build-image
tags:
- docker-arm
variables:
TARGET_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-arm
.deploy:
stage: deploy
tags:
- docker
script:
- docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-x86
- docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-arm
- docker manifest create $TARGET_IMAGE --amend $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-x86 --amend
$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-arm
- docker manifest push $TARGET_IMAGE
deploy_latest:
extends: .deploy
variables:
TARGET_IMAGE: $CI_REGISTRY_IMAGE:latest
only:
- master
deploy_branch:
extends: .deploy
variables:
TARGET_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
/install-npm.sh
.git*
/data
/output
/config.yaml
.idea
.dockerignore
Dockerfile
/src
{
"singleQuote": true,
"trailingComma": "all"
}
\ No newline at end of file
FROM node:lts-bullseye-slim as base
LABEL Author="Nanahira <nanahira@momobako.com>"
RUN apt update && apt -y install python3 build-essential libpq-dev && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /var/log/*
WORKDIR /usr/src/app
COPY ./package*.json ./
FROM base as builder
RUN npm ci && npm cache clean --force
COPY . ./
RUN npm run build
FROM base
ENV NODE_ENV production
RUN npm ci && npm cache clean --force
COPY --from=builder /usr/src/app/dist ./dist
COPY ./config.example.yaml ./config.yaml
ENV NODE_PG_FORCE_NATIVE=true
EXPOSE 3000
CMD [ "npm", "run", "start:prod" ]
This diff is collapsed.
# tabulator-backend
Backend of MyCard tabulator.
## Installation
```bash
$ npm install
```
## Config
Make a copy of `config.example.yaml` to `config.yaml`.
## Running the app
```bash
# development
$ npm run start
# watch mode
$ npm run start:dev
# production mode
$ npm run start:prod
```
## License
AGPLv3
host: '::'
port: 3000
DB_HOST: 'localhost'
DB_PORT: 5432
DB_USER: 'postgres'
DB_PASS: 'postgres'
DB_NAME: 'postgres'
\ No newline at end of file
#!/bin/bash
npm install --save typeorm @nestjs/typeorm pg pg-native nicot
#!/bin/bash
npm install --save \
class-validator \
class-transformer \
@nestjs/swagger \
@nestjs/config \
yaml
npm install --save-dev \
@types/express
npm i --save-exact --save-dev eslint@8.22.0
{
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"plugins": ["@nestjs/swagger"]
}
}
This diff is collapsed.
{
"name": "tabulator-backend",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"prebuild": "rimraf dist",
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs/common": "^9.0.0",
"@nestjs/config": "^2.2.0",
"@nestjs/core": "^9.0.0",
"@nestjs/platform-express": "^9.0.0",
"@nestjs/swagger": "^6.1.4",
"@nestjs/typeorm": "^9.0.1",
"class-transformer": "^0.5.1",
"class-validator": "^0.13.2",
"nestjs-mycard": "^3.0.2",
"nicot": "^1.0.26",
"pg": "^8.8.0",
"pg-native": "^3.0.1",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.2.0",
"typeorm": "^0.3.11",
"yaml": "^2.2.1"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
"@nestjs/schematics": "^9.0.0",
"@nestjs/testing": "^9.0.0",
"@types/express": "^4.17.15",
"@types/jest": "28.1.8",
"@types/node": "^16.0.0",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"eslint": "8.22.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "28.1.3",
"prettier": "^2.3.2",
"source-map-support": "^0.5.20",
"supertest": "^6.1.3",
"ts-jest": "28.0.8",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.1.0",
"typescript": "^4.7.4"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { loadConfig } from './utility/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { TournamentModule } from './tournament/tournament.module';
import { MycardAuthModule } from 'nestjs-mycard';
import { ParticipantModule } from './participant/participant.module';
@Module({
imports: [
{
...MycardAuthModule.register(),
global: true,
},
ConfigModule.forRoot({
load: [loadConfig],
isGlobal: true,
ignoreEnvVars: true,
ignoreEnvFile: true,
}),
TypeOrmModule.forRootAsync({
inject: [ConfigService],
useFactory: async (config: ConfigService) => ({
type: 'postgres',
entities: [],
autoLoadEntities: true,
synchronize: !config.get('DB_NO_INIT'),
host: config.get('DB_HOST'),
port: parseInt(config.get('DB_PORT')) || 5432,
username: config.get('DB_USER'),
password: config.get('DB_PASS'),
database: config.get('DB_NAME'),
supportBigNumbers: true,
bigNumberStrings: false,
}),
}),
TournamentModule,
ParticipantModule,
],
})
export class AppModule {}
// Blank for nicot use.
import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { NestExpressApplication } from '@nestjs/platform-express';
import { AppModule } from './app.module';
import { ConfigService } from '@nestjs/config';
async function bootstrap() {
const app = await NestFactory.create<NestExpressApplication>(AppModule);
app.setGlobalPrefix('api');
app.enableCors();
app.set('trust proxy', ['172.16.0.0/12', 'loopback']);
const documentConfig = new DocumentBuilder()
.setTitle('app')
.setDescription('The app')
.setVersion('1.0')
.build();
const document = SwaggerModule.createDocument(app, documentConfig);
SwaggerModule.setup('docs', app, document);
const config = app.get(ConfigService);
await app.listen(
config.get<number>('port') || 3000,
config.get<string>('host') || '::',
);
}
bootstrap();
import { Entity, ManyToOne, SelectQueryBuilder } from 'typeorm';
import { NamedBase } from '../../utility/NamedBase.entity';
import {
applyQueryProperty,
BoolColumn,
NotChangeable,
NotColumn,
} from 'nicot';
import { TournamentIdColumn } from '../../utility/decorators/tournament-id-column';
import {
Tournament,
TournamentVisibility,
} from '../../tournament/entities/Tournament.entity';
import { MycardUser } from 'nestjs-mycard';
@Entity()
export class Participant extends NamedBase {
@BoolColumn({ default: false, description: '是否已经退赛' })
quit: boolean;
@NotChangeable()
@TournamentIdColumn(true)
tournamentId: number;
@NotColumn()
@ManyToOne(() => Tournament, (tournament) => tournament.participants)
tournament: Tournament;
checkPermission(user: MycardUser) {
return this.tournament?.checkPermission(user);
}
applyQuery(qb: SelectQueryBuilder<Participant>, entityName: string) {
super.applyQuery(qb, entityName);
applyQueryProperty(this, qb, entityName, 'quit', 'tournamentId');
}
}
import { Test, TestingModule } from '@nestjs/testing';
import { ParticipantController } from './participant.controller';
import { ParticipantService } from './participant.service';
describe('ParticipantController', () => {
let controller: ParticipantController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [ParticipantController],
providers: [ParticipantService],
}).compile();
controller = module.get<ParticipantController>(ParticipantController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
import { Controller } from '@nestjs/common';
import { ParticipantService } from './participant.service';
import { Participant } from './entities/participant.entity';
import { RestfulFactory } from 'nicot';
import { MycardUser, PutMycardUser } from 'nestjs-mycard';
import { ApiHeader, ApiTags } from '@nestjs/swagger';
const factory = new RestfulFactory(Participant);
class FindParticipantDto extends factory.findAllDto {}
class UpdateParticipantDto extends factory.updateDto {}
class ImportParticipantDto extends factory.importDto {}
@ApiTags('participant')
@Controller('participant')
@ApiHeader({ name: 'Authorization', required: false, description: 'MC Token' })
export class ParticipantController {
constructor(private readonly participantService: ParticipantService) {}
@factory.create()
async create(
@factory.createParam() participant: Participant,
@PutMycardUser() user: MycardUser,
) {
return this.participantService.createParticipant(participant, user);
}
@factory.findOne()
async findOne(
@factory.idParam() id: number,
@PutMycardUser(false) user: MycardUser,
) {
return this.participantService.getParticipant(id, user);
}
@factory.findAll()
async findAll(
@factory.findAllParam() dto: FindParticipantDto,
@PutMycardUser(false) user: MycardUser,
) {
return this.participantService.getParticipants(dto, user);
}
@factory.update()
async update(
@factory.idParam() id: number,
@factory.updateParam() dto: UpdateParticipantDto,
@PutMycardUser() user: MycardUser,
) {
return this.participantService.updateParticipant(id, dto, user);
}
@factory.delete()
async delete(
@factory.idParam() id: number,
@PutMycardUser() user: MycardUser,
) {
return this.participantService.deleteParticipant(id, user);
}
@factory.import()
async import(
@PutMycardUser() user: MycardUser,
@factory.createParam() data: ImportParticipantDto,
) {
return this.participantService.importParticipants(data.data, user);
}
}
import { Module } from '@nestjs/common';
import { ParticipantService } from './participant.service';
import { ParticipantController } from './participant.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Participant } from './entities/participant.entity';
import { TournamentModule } from '../tournament/tournament.module';
@Module({
controllers: [ParticipantController],
providers: [ParticipantService],
imports: [TypeOrmModule.forFeature([Participant]), TournamentModule],
})
export class ParticipantModule {}
import { Test, TestingModule } from '@nestjs/testing';
import { ParticipantService } from './participant.service';
describe('ParticipantService', () => {
let service: ParticipantService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [ParticipantService],
}).compile();
service = module.get<ParticipantService>(ParticipantService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
import { Injectable } from '@nestjs/common';
import { BlankReturnMessageDto, CrudService, Inner } from 'nicot';
import { Participant } from './entities/participant.entity';
import { InjectRepository } from '@nestjs/typeorm';
import {
Tournament,
TournamentStatus,
} from '../tournament/entities/Tournament.entity';
import { MycardUser } from 'nestjs-mycard';
import { TournamentService } from '../tournament/tournament.service';
@Injectable()
export class ParticipantService extends CrudService(Participant, {
relations: [Inner('tournament')],
}) {
constructor(
@InjectRepository(Participant) repo,
private readonly tournamentService: TournamentService,
) {
super(repo);
}
getParticipant(id: number, user: MycardUser) {
return this.findOne(id, (qb) =>
Tournament.extraQueryForUser(user, qb, 'tournament'),
);
}
getParticipants(dto: Partial<Participant>, user: MycardUser) {
return this.findAll(dto, (qb) =>
Tournament.extraQueryForUser(user, qb, 'tournament'),
);
}
async createParticipant(dto: Participant, user: MycardUser) {
await this.tournamentService.canModifyParticipants(dto.tournamentId, user);
return this.create(dto);
}
async updateParticipant(
id: number,
dto: Partial<Participant>,
user: MycardUser,
) {
await this.checkPermissionOfParticipant(id, user);
return this.update(id, dto);
}
async deleteParticipant(id: number, user: MycardUser) {
await this.checkPermissionOfParticipant(id, user);
return this.delete(id);
}
async checkPermissionOfParticipant(id: number, user: MycardUser) {
const participant = await this.repo.findOne({
where: { id },
select: {
id: true,
tournament: {
id: true,
creator: true,
status: true,
},
},
relations: ['tournament'],
});
if (!participant?.tournament) {
throw new BlankReturnMessageDto(404, '未找到该参赛者。').toException();
}
if (participant.tournament.status !== TournamentStatus.Ready) {
throw new BlankReturnMessageDto(
400,
'比赛已开始,无法修改参赛者。',
).toException();
}
return participant.tournament.checkPermission(user);
}
async importParticipants(participants: Participant[], user: MycardUser) {
return this.importEntities(participants, async (p) => {
try {
await this.tournamentService.checkPermissionOfTournament(
p.tournamentId,
user,
);
} catch (e) {
return `玩家 ${p.name} 对应的比赛 ${p.tournamentId} 不存在或您没有权限。`;
}
});
}
}
import { Entity, Index, OneToMany, SelectQueryBuilder } from 'typeorm';
import {
applyQueryProperty,
BlankReturnMessageDto,
DateColumn,
EnumColumn,
IntColumn,
NotChangeable,
NotColumn,
NotWritable,
} from 'nicot';
import { MycardUser } from 'nestjs-mycard';
import { DescBase } from '../../utility/NamedBase.entity';
import { Participant } from '../../participant/entities/participant.entity';
export enum TournamentRule {
SingleElimination = 'SingleElimination',
Swiss = 'Swiss',
}
export enum TournamentVisibility {
Public = 'Public',
Internal = 'Internal',
Private = 'Private',
}
export enum TournamentStatus {
Ready = 'Ready',
Running = 'Running',
Finished = 'Finished',
}
@Entity()
export class Tournament extends DescBase {
@NotChangeable()
@EnumColumn(TournamentRule, {
default: TournamentRule.SingleElimination,
description: '规则',
})
rule: TournamentRule;
@EnumColumn(TournamentVisibility, {
default: TournamentVisibility.Public,
description: '可见性',
})
visibility: TournamentVisibility;
@NotWritable()
@EnumColumn(TournamentStatus, {
default: TournamentStatus.Ready,
description: '状态',
})
status: TournamentStatus;
@NotWritable()
@Index()
@IntColumn('int', {
description: '创建者 MC ID',
unsigned: true,
columnExtras: { nullable: false },
})
creator: number;
@NotWritable()
@Index()
@DateColumn({ description: '创建时间', columnExtras: { nullable: false } })
createdAt: Date;
@NotColumn()
@OneToMany(() => Participant, (participant) => participant.tournament)
participants: Participant[];
async beforeCreate() {
this.createdAt = new Date();
}
applyQuery(qb: SelectQueryBuilder<Tournament>, entityName: string) {
super.applyQuery(qb, entityName);
applyQueryProperty(
this,
qb,
entityName,
'rule',
'visibility',
'creator',
'createdAt',
);
}
static extraQueryForUser(
user: MycardUser,
qb: SelectQueryBuilder<any>,
entityName: string,
) {
if (!user) {
qb.andWhere(`${entityName}.visibility = :public`, {
public: TournamentVisibility.Public,
});
} else {
qb.andWhere(
`(${entityName}.visibility != :private OR ${entityName}.creator = :self)`,
{
private: TournamentVisibility.Private,
self: user.id,
},
);
}
}
checkPermission(user: MycardUser) {
if (this.creator !== user.id && !user.admin) {
throw new BlankReturnMessageDto(403, '您无权操作该比赛。').toException();
}
return this;
}
}
import { Test, TestingModule } from '@nestjs/testing';
import { TournamentController } from './tournament.controller';
import { TournamentService } from './tournament.service';
describe('TournamentController', () => {
let controller: TournamentController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [TournamentController],
providers: [TournamentService],
}).compile();
controller = module.get<TournamentController>(TournamentController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
import { Controller } from '@nestjs/common';
import { TournamentService } from './tournament.service';
import { RestfulFactory } from 'nicot';
import { Tournament } from './entities/Tournament.entity';
import { MycardUser, PutMycardUser } from 'nestjs-mycard';
import { ApiHeader, ApiTags } from '@nestjs/swagger';
const factory = new RestfulFactory(Tournament);
class FindTournamentDto extends factory.findAllDto {}
class UpdateTournamentDto extends factory.updateDto {}
@ApiTags('tournament')
@Controller('tournament')
@ApiHeader({ name: 'Authorization', required: false, description: 'MC Token' })
export class TournamentController {
constructor(private readonly tournamentService: TournamentService) {}
@factory.create()
async create(
@factory.createParam() tournament: Tournament,
@PutMycardUser() user: MycardUser,
) {
return this.tournamentService.createTournament(tournament, user);
}
@factory.findOne()
async findOne(
@factory.idParam() id: number,
@PutMycardUser(false) user: MycardUser,
) {
return this.tournamentService.getTournament(id, user);
}
@factory.findAll()
async findAll(
@factory.findAllParam() dto: FindTournamentDto,
@PutMycardUser(false) user: MycardUser,
) {
return this.tournamentService.getTournaments(dto, user);
}
@factory.update()
async update(
@factory.idParam() id: number,
@factory.updateParam() dto: UpdateTournamentDto,
@PutMycardUser() user: MycardUser,
) {
return this.tournamentService.updateTournament(id, dto, user);
}
@factory.delete()
async delete(
@factory.idParam() id: number,
@PutMycardUser() user: MycardUser,
) {
return this.tournamentService.deleteTournament(id, user);
}
}
import { Module } from '@nestjs/common';
import { TournamentService } from './tournament.service';
import { TournamentController } from './tournament.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Tournament } from './entities/Tournament.entity';
@Module({
controllers: [TournamentController],
providers: [TournamentService],
imports: [TypeOrmModule.forFeature([Tournament])],
exports: [TournamentService],
})
export class TournamentModule {}
import { Test, TestingModule } from '@nestjs/testing';
import { TournamentService } from './tournament.service';
describe('TournamentService', () => {
let service: TournamentService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [TournamentService],
}).compile();
service = module.get<TournamentService>(TournamentService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
import { Injectable } from '@nestjs/common';
import { BlankReturnMessageDto, CrudService } from 'nicot';
import { Tournament, TournamentStatus } from './entities/Tournament.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { MycardUser } from 'nestjs-mycard';
@Injectable()
export class TournamentService extends CrudService(Tournament, {
relations: ['participants'],
}) {
constructor(@InjectRepository(Tournament) repo) {
super(repo);
}
getTournament(id: number, user: MycardUser) {
return this.findOne(id, (qb) =>
Tournament.extraQueryForUser(user, qb, this.entityAliasName),
);
}
getTournaments(dto: Partial<Tournament>, user: MycardUser) {
return this.findAll(dto, (qb) =>
Tournament.extraQueryForUser(user, qb, this.entityAliasName),
);
}
createTournament(tournament: Tournament, user: MycardUser) {
tournament.creator = user.id;
return this.create(tournament);
}
async updateTournament(
id: number,
dto: Partial<Tournament>,
user: MycardUser,
) {
await this.checkPermissionOfTournament(id, user);
return this.update(id, dto);
}
async deleteTournament(id: number, user: MycardUser) {
await this.checkPermissionOfTournament(id, user);
return this.delete(id);
}
async checkPermissionOfTournament(id: number, user: MycardUser) {
const tournament = await this.repo.findOne({
where: { id },
select: ['id', 'creator'],
});
if (!tournament) {
throw new BlankReturnMessageDto(404, '未找到该比赛。').toException();
}
return tournament.checkPermission(user);
}
async canModifyParticipants(id: number, user: MycardUser) {
const tournamnt = await this.checkPermissionOfTournament(id, user);
if (tournamnt.status !== TournamentStatus.Ready) {
throw new BlankReturnMessageDto(
403,
'比赛已经开始,不能修改参赛者。',
).toException();
}
return tournamnt;
}
}
import {
applyQueryPropertyLike,
applyQueryPropertySearch,
IdBase,
StringColumn,
} from 'nicot';
import { Index, SelectQueryBuilder } from 'typeorm';
export class NamedBase extends IdBase() {
@Index()
@StringColumn(128, { required: true, description: '名称' })
name: string;
applyQuery(qb: SelectQueryBuilder<NamedBase>, entityName: string) {
super.applyQuery(qb, entityName);
applyQueryPropertyLike(this, qb, entityName, 'name');
}
}
export class DescBase extends NamedBase {
@StringColumn(10000, { default: '暂无介绍。', description: '描述' })
description: string;
applyQuery(qb: SelectQueryBuilder<DescBase>, entityName: string) {
super.applyQuery(qb, entityName);
applyQueryPropertySearch(this, qb, entityName, 'description');
}
}
import yaml from 'yaml';
import * as fs from 'fs';
const defaultConfig = {
host: '::',
port: 3000,
DB_HOST: 'localhost',
DB_PORT: 5432,
DB_USER: 'mycard',
DB_PASS: 'mycard',
DB_NAME: 'mycard',
};
export type Config = typeof defaultConfig;
export async function loadConfig(): Promise<Config> {
let readConfig: Partial<Config> = {};
try {
const configText = await fs.promises.readFile('./config.yaml', 'utf-8');
readConfig = yaml.parse(configText);
} catch (e) {
console.error(`Failed to read config: ${e.toString()}`);
}
return {
...defaultConfig,
...readConfig,
...process.env,
};
}
import { IntColumn } from 'nicot';
export const TournamentIdColumn = (required = false) =>
IntColumn('bigint', { unsigned: true, description: '比赛 ID。', required });
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
describe('AppController (e2e)', () => {
let app: INestApplication;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
/* it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect('Hello World!');
}); */
});
{
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": ".",
"testEnvironment": "node",
"testRegex": ".e2e-spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
}
}
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
}
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "es2021",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"esModuleInterop": true
},
"compileOnSave": true,
"allowJs": true
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment