Commit 0f49e7d0 authored by nanahira's avatar nanahira

ad

parent 793b3245
Pipeline #17958 passed with stages
in 4 minutes and 52 seconds
......@@ -7,5 +7,4 @@ DB_NAME: wall
INITIAL_TIME: '2021-12-01 00:00:00'
FETCHER_URL: http://wenyuanwall-fetcher:3000
CRON: '0 0 1 * * *'
AD: ''
# http:
......@@ -20,14 +20,14 @@
"class-validator": "^0.13.2",
"lodash": "^4.17.21",
"moment": "^2.29.4",
"nicot": "^1.0.9",
"nicot": "^1.0.17",
"node-schedule": "^2.1.0",
"pg": "^8.7.3",
"pg-native": "^3.0.0",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.2.0",
"typeorm": "^0.3.7",
"typeorm": "^0.3.10",
"yaml": "^2.1.1"
},
"devDependencies": {
......@@ -37,7 +37,7 @@
"@types/express": "^4.17.13",
"@types/jest": "28.1.4",
"@types/lodash": "^4.14.182",
"@types/node": "^16.0.0",
"@types/node": "^18.11.9",
"@types/node-schedule": "^2.1.0",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
......@@ -53,7 +53,7 @@
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.0.0",
"typescript": "^4.3.5"
"typescript": "^4.9.3"
}
},
"node_modules/@ampproject/remapping": {
......@@ -1442,6 +1442,19 @@
"strip-bom": "^3.0.0"
}
},
"node_modules/@nestjs/cli/node_modules/typescript": {
"version": "4.7.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
"integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=4.2.0"
}
},
"node_modules/@nestjs/common": {
"version": "9.0.3",
"resolved": "https://registry.npmjs.org/@nestjs/common/-/common-9.0.3.tgz",
......@@ -1951,9 +1964,9 @@
"dev": true
},
"node_modules/@types/node": {
"version": "16.11.44",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.44.tgz",
"integrity": "sha512-gwP6+QDgL5TDBIWh1lbYh3EFPU11pa+8xcamcsA3ROkp3A9X+/3Y5cRgq93VPEEE+CGfxlQnqkg1kkWGBgh3fw==",
"version": "18.11.9",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz",
"integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==",
"devOptional": true
},
"node_modules/@types/node-schedule": {
......@@ -6363,9 +6376,9 @@
"dev": true
},
"node_modules/nicot": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/nicot/-/nicot-1.0.9.tgz",
"integrity": "sha512-9G1dtl+nXrypMjdbY0GYFUNF0SxRO/1HPMPiOCPDjZK1LTMaQpQImNqgBaqSjhgHTUE6A+jmui3bbbFdl+YDkQ==",
"version": "1.0.17",
"resolved": "https://registry.npmjs.org/nicot/-/nicot-1.0.17.tgz",
"integrity": "sha512-SnohOuhceU1FJVXppU/pdpbmelqg/OMq4G8FNwjhWQSVZnxNHl90PCkvjVApvEhHXfOts+ToeZ0GCweVgDhBGQ==",
"dependencies": {
"lodash": "^4.17.21"
},
......@@ -6374,7 +6387,7 @@
"@nestjs/swagger": "^6.0.4",
"class-transformer": "^0.5.1",
"class-validator": "^0.13.2",
"typeorm": "^0.3.7"
"typeorm": "^0.3.10"
}
},
"node_modules/node-emoji": {
......@@ -8376,9 +8389,9 @@
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
},
"node_modules/typeorm": {
"version": "0.3.7",
"resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.7.tgz",
"integrity": "sha512-MsPJeP6Zuwfe64c++l80+VRqpGEGxf0CkztIEnehQ+CMmQPSHjOnFbFxwBuZ2jiLqZTjLk2ZqQdVF0RmvxNF3Q==",
"version": "0.3.10",
"resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.10.tgz",
"integrity": "sha512-VMKiM84EpJQ+Mz9xDIPqnfplWhyUy1d8ccaKdMY9obifxJOTFnv8GYVyPsGwG8Lk7Nb8MlttHyHWENGAhBA3WA==",
"dependencies": {
"@sqltools/formatter": "^1.2.2",
"app-root-path": "^3.0.0",
......@@ -8532,9 +8545,9 @@
}
},
"node_modules/typescript": {
"version": "4.7.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
"integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
"version": "4.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz",
"integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==",
"devOptional": true,
"bin": {
"tsc": "bin/tsc",
......@@ -10072,6 +10085,12 @@
"minimist": "^1.2.6",
"strip-bom": "^3.0.0"
}
},
"typescript": {
"version": "4.7.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
"integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
"dev": true
}
}
},
......@@ -10453,9 +10472,9 @@
"dev": true
},
"@types/node": {
"version": "16.11.44",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.44.tgz",
"integrity": "sha512-gwP6+QDgL5TDBIWh1lbYh3EFPU11pa+8xcamcsA3ROkp3A9X+/3Y5cRgq93VPEEE+CGfxlQnqkg1kkWGBgh3fw==",
"version": "18.11.9",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz",
"integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==",
"devOptional": true
},
"@types/node-schedule": {
......@@ -13771,9 +13790,9 @@
"dev": true
},
"nicot": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/nicot/-/nicot-1.0.9.tgz",
"integrity": "sha512-9G1dtl+nXrypMjdbY0GYFUNF0SxRO/1HPMPiOCPDjZK1LTMaQpQImNqgBaqSjhgHTUE6A+jmui3bbbFdl+YDkQ==",
"version": "1.0.17",
"resolved": "https://registry.npmjs.org/nicot/-/nicot-1.0.17.tgz",
"integrity": "sha512-SnohOuhceU1FJVXppU/pdpbmelqg/OMq4G8FNwjhWQSVZnxNHl90PCkvjVApvEhHXfOts+ToeZ0GCweVgDhBGQ==",
"requires": {
"lodash": "^4.17.21"
}
......@@ -15244,9 +15263,9 @@
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
},
"typeorm": {
"version": "0.3.7",
"resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.7.tgz",
"integrity": "sha512-MsPJeP6Zuwfe64c++l80+VRqpGEGxf0CkztIEnehQ+CMmQPSHjOnFbFxwBuZ2jiLqZTjLk2ZqQdVF0RmvxNF3Q==",
"version": "0.3.10",
"resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.10.tgz",
"integrity": "sha512-VMKiM84EpJQ+Mz9xDIPqnfplWhyUy1d8ccaKdMY9obifxJOTFnv8GYVyPsGwG8Lk7Nb8MlttHyHWENGAhBA3WA==",
"requires": {
"@sqltools/formatter": "^1.2.2",
"app-root-path": "^3.0.0",
......@@ -15293,9 +15312,9 @@
}
},
"typescript": {
"version": "4.7.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
"integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
"version": "4.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz",
"integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==",
"devOptional": true
},
"universalify": {
......
......@@ -32,14 +32,14 @@
"class-validator": "^0.13.2",
"lodash": "^4.17.21",
"moment": "^2.29.4",
"nicot": "^1.0.9",
"nicot": "^1.0.17",
"node-schedule": "^2.1.0",
"pg": "^8.7.3",
"pg-native": "^3.0.0",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.2.0",
"typeorm": "^0.3.7",
"typeorm": "^0.3.10",
"yaml": "^2.1.1"
},
"devDependencies": {
......@@ -49,7 +49,7 @@
"@types/express": "^4.17.13",
"@types/jest": "28.1.4",
"@types/lodash": "^4.14.182",
"@types/node": "^16.0.0",
"@types/node": "^18.11.9",
"@types/node-schedule": "^2.1.0",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
......@@ -65,7 +65,7 @@
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.0.0",
"typescript": "^4.3.5"
"typescript": "^4.9.3"
},
"jest": {
"moduleFileExtensions": [
......
import { Controller, Get } from '@nestjs/common';
import { Controller, Get, Param, ParseIntPipe } from '@nestjs/common';
import { AdService } from './ad.service';
import { ApiOkResponse, ApiOperation } from '@nestjs/swagger';
import { StringReturnMessageDto } from 'nicot';
import { ApiOkResponse, ApiOperation, ApiParam } from '@nestjs/swagger';
import { ReturnMessageDto, StringReturnMessageDto } from 'nicot';
import { Ad } from '../entities/ad.entity';
const AdReturnMessageDto = ReturnMessageDto(Ad);
@Controller('ad')
export class AdController {
constructor(private ad: AdService) {}
@Get()
@Get('random')
@ApiOperation({ summary: 'Get ad' })
@ApiOkResponse({ type: AdReturnMessageDto })
async getAd() {
return new AdReturnMessageDto(200, 'success', await this.ad.getAd());
}
@Get('click/:id')
@ApiOperation({ summary: 'Click ad' })
@ApiParam({ name: 'id', type: Number })
@ApiOkResponse({ type: StringReturnMessageDto })
getAd() {
return new StringReturnMessageDto(200, 'success', this.ad.getAd());
async clickAd(@Param('id', new ParseIntPipe()) id: number) {
return new StringReturnMessageDto(
200,
'success',
await this.ad.clickAd(id),
);
}
}
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { BlankReturnMessageDto, CrudService } from 'nicot';
import { Ad } from '../entities/ad.entity';
import { InjectDataSource } from '@nestjs/typeorm';
import { DataSource } from 'typeorm';
@Injectable()
export class AdService {
constructor(private config: ConfigService) {}
export class AdService extends CrudService(Ad) {
constructor(@InjectDataSource() db: DataSource) {
super(db.getRepository(Ad));
}
async getAd() {
const activeAds = await this.repo.find({
where: {
enabled: true,
},
select: {
id: true,
content: true,
},
});
if (!activeAds.length) {
return null;
}
const randomIndex = Math.floor(Math.random() * activeAds.length);
await this.repo.increment(
{ id: activeAds[randomIndex].id },
'viewCount',
1,
);
return activeAds[randomIndex];
}
getAd() {
return this.config.get<string>('AD', '');
async clickAd(id: number) {
const ad = await this.repo.findOne({
where: {
enabled: true,
id,
},
select: {
link: true,
},
});
if (!ad) {
throw new BlankReturnMessageDto(404, 'Ad not found').toException();
}
await this.repo.increment({ id }, 'clickCount', 1);
return ad.link;
}
}
......@@ -11,6 +11,7 @@ import { FetchService } from './fetch/fetch.service';
import { HttpModule } from '@nestjs/axios';
import { AdService } from './ad/ad.service';
import { AdController } from './ad/ad.controller';
import { Ad } from './entities/ad.entity';
@Module({
imports: [
......@@ -28,7 +29,7 @@ import { AdController } from './ad/ad.controller';
inject: [ConfigService],
useFactory: async (config: ConfigService) => ({
type: 'postgres',
entities: [Blacklist, BlacklistAccount],
entities: [Blacklist, BlacklistAccount, Ad],
autoLoadEntities: true,
synchronize: !config.get('DB_NO_INIT') || !!config.get('REFETCH'),
dropSchema: !!config.get('REFETCH'),
......@@ -42,7 +43,12 @@ import { AdController } from './ad/ad.controller';
}),
}),
],
providers: [BlacklistAccountService, BlacklistService, FetchService, AdService],
providers: [
BlacklistAccountService,
BlacklistService,
FetchService,
AdService,
],
controllers: [BlacklistAccountController, AdController],
})
export class AppModule {}
......@@ -39,7 +39,7 @@ export class BlacklistService extends CrudService(Blacklist, {
const blacklists = chunk.map((d) => new Blacklist().fromData(d));
this.log.log(`Saving current chunk of ${blacklists.length} entries...`);
try {
const result = await this.batchCreate(blacklists, undefined, true);
const result = await this._batchCreate(blacklists);
this.log.log(
`Saved ${result.results.length} entries, and skipped ${result.skipped.length} duplicated entries.`,
);
......
import { Entity } from 'typeorm';
import { BoolColumn, IdBase, IntColumn, StringColumn } from 'nicot';
@Entity()
export class Ad extends IdBase() {
@StringColumn(100, {
description: 'Ad link',
required: false,
})
link: string;
@StringColumn(10000, {
description: 'Ad content',
required: true,
})
content: string;
@IntColumn('int', {
description: 'Ad view count',
default: 0,
unsigned: true,
})
viewCount: number;
@IntColumn('int', {
description: 'Ad click count',
default: 0,
unsigned: true,
})
clickCount: number;
@BoolColumn({
description: 'Ad is enabled',
default: true,
})
enabled: boolean;
}
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