Commit 8c937300 authored by nanahira's avatar nanahira

postgres and abandon nest-koishijs

parent 49e26b35
Pipeline #4736 passed with stages
in 1 minute and 9 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
# 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
stages:
- build
- combine
- deploy
variables:
GIT_DEPTH: "1"
CONTAINER_TEST_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
CONTAINER_TEST_ARM_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-arm
CONTAINER_TEST_X86_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-x86
CONTAINER_RELEASE_IMAGE: $CI_REGISTRY_IMAGE:latest
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
build-x86:
stage: build
tags:
- docker
script:
- docker build --pull -t $CONTAINER_TEST_X86_IMAGE .
- docker push $CONTAINER_TEST_X86_IMAGE
build-arm:
stage: build
tags:
- docker-arm
script:
- docker build --pull -t $CONTAINER_TEST_ARM_IMAGE .
- docker push $CONTAINER_TEST_ARM_IMAGE
combine:
stage: combine
tags:
- docker
script:
- docker pull $CONTAINER_TEST_X86_IMAGE
- docker pull $CONTAINER_TEST_ARM_IMAGE
- docker manifest create $CONTAINER_TEST_IMAGE --amend $CONTAINER_TEST_X86_IMAGE --amend $CONTAINER_TEST_ARM_IMAGE
- docker manifest push $CONTAINER_TEST_IMAGE
deploy_latest:
stage: deploy
tags:
- docker
script:
- docker pull $CONTAINER_TEST_IMAGE
- docker tag $CONTAINER_TEST_IMAGE $CONTAINER_RELEASE_IMAGE
- docker push $CONTAINER_RELEASE_IMAGE
only:
- master
deploy_tag:
stage: deploy
tags:
- docker
variables:
CONTAINER_TAG_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
script:
- docker pull $CONTAINER_TEST_IMAGE
- docker tag $CONTAINER_TEST_IMAGE $CONTAINER_TAG_IMAGE
- docker push $CONTAINER_TAG_IMAGE
only:
- tags
/install-npm.sh
.git*
/output
/dest
/config.yaml
.idea
.dockerignore
Dockerfile
\ No newline at end of file
FROM node:bullseye-slim
LABEL Author="Nanahira <nanahira@momobako.com>"
RUN apt update && apt -y install python3 build-essential && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
WORKDIR /usr/src/app
COPY ./package*.json ./
RUN npm ci
COPY . ./
RUN npm run build
CMD ["npm", "run", "start:prod"]
This diff is collapsed.
# App name
App description.
## Environment
* `DB_HOST` `DB_PORT` `DB_USER` `DB_PASS` `DB_NAME` Database configs.
* `CQ_ID` QQ account.
* `CQ_SERVER` OneBot server address.
* `CQ_TOKEN` OneBot server token.
## Installation
```bash
$ npm install
```
## Running the app
```bash
# development
$ npm run start
# watch mode
$ npm run start:dev
# production mode
$ npm run start:prod
```
## License
AGPLv3
#!/bin/bash
npm install --save \
class-validator \
class-transformer \
@nestjs/swagger \
swagger-ui-express \
lodash \
typeorm \
@nestjs/typeorm \
mysql \
reflect-metadata \
koishi \
koishi-adapter-onebot \
koishi-plugin-common
npm install --save-dev \
@types/lodash \
@types/express
{
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"plugins": ["@nestjs/swagger"]
}
}
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';
describe('AppController', () => {
let appController: AppController;
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();
appController = app.get<AppController>(AppController);
});
describe('root', () => {
it('should be defined', () => {
expect(appController).toBeDefined();
});
});
});
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { typeormConfig } from './config';
import { BotService } from './bot/bot.service';
@Module({
imports: [TypeOrmModule.forRoot(typeormConfig())],
controllers: [AppController],
providers: [AppService, BotService],
})
export class AppModule {}
import { Injectable, ConsoleLogger } from '@nestjs/common';
import { Connection } from 'typeorm';
import { InjectConnection } from '@nestjs/typeorm';
import { BotService } from './bot/bot.service';
@Injectable()
export class AppService extends ConsoleLogger {
constructor(
@InjectConnection('app')
private db: Connection,
private botService: BotService
) {
super('app', true)
}
getHello(): string {
return 'Hello World!';
}
}
import { Test, TestingModule } from '@nestjs/testing';
import { BotService } from './bot.service';
describe('BotService', () => {
let service: BotService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [BotService],
}).compile();
service = module.get<BotService>(BotService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
import { Injectable, ConsoleLogger } from '@nestjs/common';
import { InjectConnection } from '@nestjs/typeorm';
import { Connection } from 'typeorm';
import { User } from '../entities/User.entity';
import { Group } from '../entities/Group.entity';
import { App, AppConfig } from 'koishi';
import { AppService } from '../app.service';
import * as koishiCommonPlugin from 'koishi-plugin-common';
import * as adapter from 'koishi-adapter-onebot';
import { CQBot } from 'koishi-adapter-onebot';
const __ = typeof adapter; // just for import
@Injectable()
export class BotService extends ConsoleLogger {
bot: App;
botConfig: AppConfig;
constructor(
@InjectConnection('app')
private db: Connection,
private readonly appService: AppService,
) {
super('bot');
this.initializeBot();
}
async initializeBot() {
this.botConfig = {
type: 'onebot:ws',
selfId: process.env.CQ_ID,
server: process.env.CQ_SERVER,
token: process.env.CQ_TOKEN,
prefix: process.env.CQ_PREFIX || '.',
};
this.bot = new App(this.botConfig);
this.bot.plugin(koishiCommonPlugin, {
onFriendRequest: true,
});
this.loadBotRouters();
await this.bot.start();
this.log.log(`Bot started.`);
}
loadBotRouters() {
// all middlewares should be here.
this.bot.command('echo <msg:text>').action((argv, msg) => {
return msg;
});
}
async findOrCreateUser(id: string, name?: string) {
const repo = this.db.getRepository(User);
let ent = await repo.findOne({ where: { id } });
if (ent) {
if (!ent.name && name) {
ent.name = name;
return await repo.save(ent);
} else {
return ent;
}
}
ent = new User();
ent.id = id;
ent.name = name;
try {
ent = await repo.save(ent);
} catch (e) {
this.log.error(`Failed to save user ${id}: ${e.toString()}`);
return null;
}
return ent;
}
async findOrCreateGroup(id: string) {
const repo = this.db.getRepository(Group);
let ent = await repo.findOne({ where: { id } });
if (ent) {
return ent;
}
ent = new Group();
ent.id = id;
try {
ent = await repo.save(ent);
} catch (e) {
this.log.error(`Failed to save group ${id}: ${e.toString()}`);
return null;
}
return ent;
}
getBot() {
return (this.bot.bots[0] as unknown) as CQBot;
}
async sendPrivateMessage(id: string, message: string) {
try {
await this.getBot().$sendPrivateMsg(id, message);
return true;
} catch (e) {
this.log.error(
`Failed to send private message ${message} to ${id}: ${e.toString()}`,
);
return false;
}
}
async sendGroupMessage(id: string, message: string) {
try {
await this.getBot().$sendGroupMsg(id, message);
return true;
} catch (e) {
this.log.error(
`Failed to send group message ${message} to ${id}: ${e.toString()}`,
);
return false;
}
}
}
import { TypeOrmModuleOptions } from '@nestjs/typeorm';
import { User } from './entities/User.entity';
import { Group } from './entities/Group.entity';
export function dbConfig() {
return {
host: process.env.DB_HOST,
port: process.env.DB_PORT ? parseInt(process.env.DB_PORT) : 3306,
username: process.env.DB_USER,
password: process.env.DB_PASS,
database: process.env.DB_NAME,
};
}
export function typeormConfig(): TypeOrmModuleOptions {
return {
name: 'app',
type: 'mysql',
entities: [User, Group], // entities here
synchronize: true,
...dbConfig(),
};
}
import { ApiProperty } from '@nestjs/swagger';
import { HttpException } from '@nestjs/common';
export class BlankReturnMessageDto {
@ApiProperty({ description: '返回状态' })
statusCode: number;
@ApiProperty({ description: '返回信息' })
message: string;
@ApiProperty({ description: '是否成功' })
success: boolean;
constructor(statusCode: number, message?: string) {
this.statusCode = statusCode;
this.message = message || 'success';
this.success = statusCode < 400;
}
toException() {
return new HttpException(this, this.statusCode);
}
}
export class ReturnMessageDto<T> extends BlankReturnMessageDto {
@ApiProperty({ description: '返回内容' })
data?: T;
constructor(statusCode: number, message?: string, data?: T) {
super(statusCode, message);
this.data = data;
}
}
import { QQIDBase } from './QQIDBase.entity';
import { Entity } from 'typeorm';
@Entity()
export class Group extends QQIDBase {}
import { TimeBase } from './TimeBase.entity';
import { Column, Index, PrimaryColumn } from 'typeorm';
export class QQIDBase extends TimeBase {
@PrimaryColumn('varchar', { length: 12 })
id: string;
}
import { CreateDateColumn, UpdateDateColumn } from 'typeorm';
import { Exclude } from 'class-transformer';
export class TimeBase {
@CreateDateColumn({ select: false })
@Exclude()
createTime: Date;
@UpdateDateColumn({ select: false })
@Exclude()
updateTime: Date;
toObject() {
return JSON.parse(JSON.stringify(this));
}
}
import { QQIDBase } from './QQIDBase.entity';
import { Column, Entity, Index } from 'typeorm';
@Entity()
export class User extends QQIDBase {
@Index()
@Column('varchar', { length: 32, nullable: true })
name: string;
}
import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { NestExpressApplication } from '@nestjs/platform-express';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create<NestExpressApplication>(AppModule);
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);
await app.listen(3000);
}
bootstrap();
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"
}
}
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "es2017",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"esModuleInterop": true
},
"compileOnSave": true,
"allowJs": true
}
......@@ -7,9 +7,12 @@ npm install --save \
lodash \
typeorm \
@nestjs/typeorm \
mysql \
@nestjs/config \
pg \
pg-native \
reflect-metadata
npm install --save-dev \
@types/lodash \
@types/express
@types/express \
@types/multer
......@@ -2,10 +2,32 @@ import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { typeormConfig } from './config';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { User } from './entities/User.entity';
const configModule = ConfigModule.forRoot();
@Module({
imports: [TypeOrmModule.forRoot(typeormConfig())],
imports: [
configModule,
TypeOrmModule.forRootAsync({
imports: [configModule],
inject: [ConfigService],
useFactory: async (config: ConfigService) => {
return {
name: 'app',
type: 'postgres',
entities: [User], // entities here
synchronize: true,
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'),
};
},
}),
],
controllers: [AppController],
providers: [AppService],
})
......
import { TypeOrmModuleOptions } from '@nestjs/typeorm';
import { User } from './entities/User.entity';
export function dbConfig() {
return {
host: process.env.DB_HOST,
port: process.env.DB_PORT ? parseInt(process.env.DB_PORT) : 3306,
username: process.env.DB_USER,
password: process.env.DB_PASS,
database: process.env.DB_NAME,
};
}
export function typeormConfig(): TypeOrmModuleOptions {
return {
name: 'app',
type: 'mysql',
entities: [User], // entities here
synchronize: true,
...dbConfig(),
};
}
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