diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 3c7645ec9..b1f484f6b 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -777,6 +777,7 @@ popularPosts: "人気の投稿" shareWithNote: "ノートで共有" ads: "広告" expiration: "期限" +startingperiod: "開始期間" memo: "メモ" priority: "優先度" high: "高" diff --git a/packages/backend/migration/1676438468213-ad3.js b/packages/backend/migration/1676438468213-ad3.js new file mode 100644 index 000000000..082bb3132 --- /dev/null +++ b/packages/backend/migration/1676438468213-ad3.js @@ -0,0 +1,9 @@ +export class ad1676438468213 { + name = 'ad1676438468213'; + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "ad" ADD "startAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "role" DROP COLUMN "startAt"`); + } +} diff --git a/packages/backend/src/models/entities/Ad.ts b/packages/backend/src/models/entities/Ad.ts index 36b758f20..c87932adb 100644 --- a/packages/backend/src/models/entities/Ad.ts +++ b/packages/backend/src/models/entities/Ad.ts @@ -18,6 +18,12 @@ export class Ad { }) public expiresAt: Date; + @Index() + @Column('timestamp with time zone', { + comment: 'The expired date of the Ad.', + }) + public startAt: Date; + @Column('varchar', { length: 32, nullable: false, }) diff --git a/packages/backend/src/server/api/endpoints/admin/ad/create.ts b/packages/backend/src/server/api/endpoints/admin/ad/create.ts index 8fcbde591..ba128c75a 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/create.ts @@ -20,9 +20,10 @@ export const paramDef = { priority: { type: 'string' }, ratio: { type: 'integer' }, expiresAt: { type: 'integer' }, + startAt: { type: 'integer' }, imageUrl: { type: 'string', minLength: 1 }, }, - required: ['url', 'memo', 'place', 'priority', 'ratio', 'expiresAt', 'imageUrl'], + required: ['url', 'memo', 'place', 'priority', 'ratio', 'expiresAt', 'startAt', 'imageUrl'], } as const; // eslint-disable-next-line import/no-default-export @@ -39,6 +40,7 @@ export default class extends Endpoint { id: this.idService.genId(), createdAt: new Date(), expiresAt: new Date(ps.expiresAt), + startAt: new Date(ps.startAt), url: ps.url, imageUrl: ps.imageUrl, priority: ps.priority, diff --git a/packages/backend/src/server/api/endpoints/admin/ad/list.ts b/packages/backend/src/server/api/endpoints/admin/ad/list.ts index 29e245ab9..905036c40 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/list.ts @@ -32,8 +32,6 @@ export default class extends Endpoint { ) { super(meta, paramDef, async (ps, me) => { const query = this.queryService.makePaginationQuery(this.adsRepository.createQueryBuilder('ad'), ps.sinceId, ps.untilId) - .andWhere('ad.expiresAt > :now', { now: new Date() }); - const ads = await query.take(ps.limit).getMany(); return ads; diff --git a/packages/backend/src/server/api/endpoints/admin/ad/update.ts b/packages/backend/src/server/api/endpoints/admin/ad/update.ts index 08e3c96ca..75f6f1d5a 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/update.ts @@ -30,8 +30,9 @@ export const paramDef = { priority: { type: 'string' }, ratio: { type: 'integer' }, expiresAt: { type: 'integer' }, + startAt: { type: 'integer' }, }, - required: ['id', 'memo', 'url', 'imageUrl', 'place', 'priority', 'ratio', 'expiresAt'], + required: ['id', 'memo', 'url', 'imageUrl', 'place', 'priority', 'ratio', 'expiresAt', 'startAt'], } as const; // eslint-disable-next-line import/no-default-export @@ -54,6 +55,7 @@ export default class extends Endpoint { memo: ps.memo, imageUrl: ps.imageUrl, expiresAt: new Date(ps.expiresAt), + startAt: new Date(ps.startAt), }); }); } diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index 2fa7a09d4..823c3f72b 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -1,4 +1,4 @@ -import { IsNull, MoreThan } from 'typeorm'; +import { IsNull, LessThanOrEqual, MoreThan } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import type { AdsRepository, EmojisRepository, UsersRepository } from '@/models/index.js'; import { MAX_NOTE_TEXT_LENGTH, DB_MAX_NOTE_TEXT_LENGTH } from '@/const.js'; @@ -262,6 +262,7 @@ export default class extends Endpoint { const ads = await this.adsRepository.find({ where: { expiresAt: MoreThan(new Date()), + startAt: LessThanOrEqual(new Date()), }, }); diff --git a/packages/frontend/src/pages/admin/ads.vue b/packages/frontend/src/pages/admin/ads.vue index 701ec31b6..88131fce8 100644 --- a/packages/frontend/src/pages/admin/ads.vue +++ b/packages/frontend/src/pages/admin/ads.vue @@ -29,6 +29,9 @@ + + + @@ -66,11 +69,14 @@ const localTimeDiff = localTime.getTimezoneOffset() * 60 * 1000; os.api('admin/ad/list').then(adsResponse => { ads = adsResponse.map(r => { - const date = new Date(r.expiresAt); - date.setMilliseconds(date.getMilliseconds() - localTimeDiff); + const exdate = new Date(r.expiresAt); + const stdate = new Date(r.startAt); + exdate.setMilliseconds(exdate.getMilliseconds() - localTimeDiff); + stdate.setMilliseconds(stdate.getMilliseconds() - localTimeDiff); return { ...r, - expiresAt: date.toISOString().slice(0, 16), + expiresAt: exdate.toISOString().slice(0, 16), + startAt: stdate.toISOString().slice(0, 16), }; }); }); @@ -85,6 +91,7 @@ function add() { url: '', imageUrl: null, expiresAt: null, + startAt: null, }); } @@ -106,11 +113,13 @@ function save(ad) { os.apiWithDialog('admin/ad/create', { ...ad, expiresAt: new Date(ad.expiresAt).getTime(), + startAt: new Date(ad.startAt).getTime(), }); } else { os.apiWithDialog('admin/ad/update', { ...ad, expiresAt: new Date(ad.expiresAt).getTime(), + startAt: new Date(ad.startAt).getTime(), }); } }