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