summaryrefslogtreecommitdiff
path: root/packages/backend/src/server/api/endpoints/notes/polls
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2022-09-18 03:27:08 +0900
committerGitHub <noreply@github.com>2022-09-18 03:27:08 +0900
commitb75184ec8e3436200bacdcd832e3324702553d20 (patch)
tree8b7e316f29e95df921db57289c8b8da476d18f07 /packages/backend/src/server/api/endpoints/notes/polls
parentUpdate ROADMAP.md (diff)
downloadmisskey-b75184ec8e3436200bacdcd832e3324702553d20.tar.gz
misskey-b75184ec8e3436200bacdcd832e3324702553d20.tar.bz2
misskey-b75184ec8e3436200bacdcd832e3324702553d20.zip
なんかもうめっちゃ変えた
Diffstat (limited to 'packages/backend/src/server/api/endpoints/notes/polls')
-rw-r--r--packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts112
-rw-r--r--packages/backend/src/server/api/endpoints/notes/polls/vote.ts194
2 files changed, 171 insertions, 135 deletions
diff --git a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts
index 5a04d68f3e..11bfdbba0f 100644
--- a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts
+++ b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts
@@ -1,6 +1,9 @@
import { Brackets, In } from 'typeorm';
-import { Polls, Mutings, Notes, PollVotes } from '@/models/index.js';
-import define from '../../../define.js';
+import { Inject, Injectable } from '@nestjs/common';
+import { NotesRepository, MutingsRepository, PollsRepository, PollVotesRepository } from '@/models/index.js';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
+import { DI } from '@/di-symbols.js';
export const meta = {
tags: ['notes'],
@@ -28,56 +31,75 @@ export const paramDef = {
} as const;
// eslint-disable-next-line import/no-default-export
-export default define(meta, paramDef, async (ps, user) => {
- const query = Polls.createQueryBuilder('poll')
- .where('poll.userHost IS NULL')
- .andWhere('poll.userId != :meId', { meId: user.id })
- .andWhere('poll.noteVisibility = \'public\'')
- .andWhere(new Brackets(qb => { qb
- .where('poll.expiresAt IS NULL')
- .orWhere('poll.expiresAt > :now', { now: new Date() });
- }));
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> {
+ constructor(
+ @Inject(DI.notesRepository)
+ private notesRepository: NotesRepository,
- //#region exclude arleady voted polls
- const votedQuery = PollVotes.createQueryBuilder('vote')
- .select('vote.noteId')
- .where('vote.userId = :meId', { meId: user.id });
+ @Inject(DI.pollsRepository)
+ private pollsRepository: PollsRepository,
- query
- .andWhere(`poll.noteId NOT IN (${ votedQuery.getQuery() })`);
+ @Inject(DI.pollVotesRepository)
+ private pollVotesRepository: PollVotesRepository,
- query.setParameters(votedQuery.getParameters());
- //#endregion
+ @Inject(DI.mutingsRepository)
+ private mutingsRepository: MutingsRepository,
- //#region mute
- const mutingQuery = Mutings.createQueryBuilder('muting')
- .select('muting.muteeId')
- .where('muting.muterId = :muterId', { muterId: user.id });
+ private noteEntityService: NoteEntityService,
+ ) {
+ super(meta, paramDef, async (ps, me) => {
+ const query = this.pollsRepository.createQueryBuilder('poll')
+ .where('poll.userHost IS NULL')
+ .andWhere('poll.userId != :meId', { meId: me.id })
+ .andWhere('poll.noteVisibility = \'public\'')
+ .andWhere(new Brackets(qb => { qb
+ .where('poll.expiresAt IS NULL')
+ .orWhere('poll.expiresAt > :now', { now: new Date() });
+ }));
- query
- .andWhere(`poll.userId NOT IN (${ mutingQuery.getQuery() })`);
+ //#region exclude arleady voted polls
+ const votedQuery = this.pollVotesRepository.createQueryBuilder('vote')
+ .select('vote.noteId')
+ .where('vote.userId = :meId', { meId: me.id });
- query.setParameters(mutingQuery.getParameters());
- //#endregion
+ query
+ .andWhere(`poll.noteId NOT IN (${ votedQuery.getQuery() })`);
- const polls = await query
- .orderBy('poll.noteId', 'DESC')
- .take(ps.limit)
- .skip(ps.offset)
- .getMany();
+ query.setParameters(votedQuery.getParameters());
+ //#endregion
- if (polls.length === 0) return [];
+ //#region mute
+ const mutingQuery = this.mutingsRepository.createQueryBuilder('muting')
+ .select('muting.muteeId')
+ .where('muting.muterId = :muterId', { muterId: me.id });
- const notes = await Notes.find({
- where: {
- id: In(polls.map(poll => poll.noteId)),
- },
- order: {
- createdAt: 'DESC',
- },
- });
+ query
+ .andWhere(`poll.userId NOT IN (${ mutingQuery.getQuery() })`);
+
+ query.setParameters(mutingQuery.getParameters());
+ //#endregion
+
+ const polls = await query
+ .orderBy('poll.noteId', 'DESC')
+ .take(ps.limit)
+ .skip(ps.offset)
+ .getMany();
+
+ if (polls.length === 0) return [];
+
+ const notes = await this.notesRepository.find({
+ where: {
+ id: In(polls.map(poll => poll.noteId)),
+ },
+ order: {
+ createdAt: 'DESC',
+ },
+ });
- return await Notes.packMany(notes, user, {
- detail: true,
- });
-});
+ return await this.noteEntityService.packMany(notes, me, {
+ detail: true,
+ });
+ });
+ }
+}
diff --git a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts
index 45a832cbd2..76f07528d7 100644
--- a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts
+++ b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts
@@ -1,16 +1,17 @@
import { Not } from 'typeorm';
-import { publishNoteStream } from '@/services/stream.js';
-import { createNotification } from '@/services/create-notification.js';
-import { deliver } from '@/queue/index.js';
-import { renderActivity } from '@/remote/activitypub/renderer/index.js';
-import renderVote from '@/remote/activitypub/renderer/vote.js';
-import { deliverQuestionUpdate } from '@/services/note/polls/update.js';
-import { PollVotes, NoteWatchings, Users, Polls, Blockings } from '@/models/index.js';
-import { IRemoteUser } from '@/models/entities/user.js';
-import { genId } from '@/misc/gen-id.js';
-import { getNote } from '../../../common/getters.js';
+import { Inject, Injectable } from '@nestjs/common';
+import { UsersRepository, BlockingsRepository, PollsRepository, PollVotesRepository } from '@/models/index.js';
+import type { IRemoteUser } from '@/models/entities/User.js';
+import { IdService } from '@/core/IdService.js';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { GetterService } from '@/server/api/common/GetterService.js';
+import { QueueService } from '@/core/QueueService.js';
+import { PollService } from '@/core/PollService.js';
+import { ApRendererService } from '@/core/remote/activitypub/ApRendererService.js';
+import { GlobalEventService } from '@/core/GlobalEventService.js';
+import { CreateNotificationService } from '@/core/CreateNotificationService.js';
+import { DI } from '@/di-symbols.js';
import { ApiError } from '../../../error.js';
-import define from '../../../define.js';
export const meta = {
tags: ['notes'],
@@ -67,103 +68,116 @@ export const paramDef = {
required: ['noteId', 'choice'],
} as const;
+// TODO: ロジックをサービスに切り出す
+
// eslint-disable-next-line import/no-default-export
-export default define(meta, paramDef, async (ps, user) => {
- const createdAt = new Date();
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> {
+ constructor(
+ @Inject(DI.usersRepository)
+ private usersRepository: UsersRepository,
- // Get votee
- const note = await getNote(ps.noteId).catch(e => {
- if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
- throw e;
- });
+ @Inject(DI.blockingsRepository)
+ private blockingsRepository: BlockingsRepository,
- if (!note.hasPoll) {
- throw new ApiError(meta.errors.noPoll);
- }
+ @Inject(DI.pollsRepository)
+ private pollsRepository: PollsRepository,
- // Check blocking
- if (note.userId !== user.id) {
- const block = await Blockings.findOneBy({
- blockerId: note.userId,
- blockeeId: user.id,
- });
- if (block) {
- throw new ApiError(meta.errors.youHaveBeenBlocked);
- }
- }
+ @Inject(DI.pollVotesRepository)
+ private pollVotesRepository: PollVotesRepository,
- const poll = await Polls.findOneByOrFail({ noteId: note.id });
+ private idService: IdService,
+ private getterService: GetterService,
+ private queueService: QueueService,
+ private pollService: PollService,
+ private apRendererService: ApRendererService,
+ private globalEventService: GlobalEventService,
+ private createNotificationService: CreateNotificationService,
+ ) {
+ super(meta, paramDef, async (ps, me) => {
+ const createdAt = new Date();
- if (poll.expiresAt && poll.expiresAt < createdAt) {
- throw new ApiError(meta.errors.alreadyExpired);
- }
+ // Get votee
+ const note = await this.getterService.getNote(ps.noteId).catch(err => {
+ if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
+ throw err;
+ });
- if (poll.choices[ps.choice] == null) {
- throw new ApiError(meta.errors.invalidChoice);
- }
+ if (!note.hasPoll) {
+ throw new ApiError(meta.errors.noPoll);
+ }
+
+ // Check blocking
+ if (note.userId !== me.id) {
+ const block = await this.blockingsRepository.findOneBy({
+ blockerId: note.userId,
+ blockeeId: me.id,
+ });
+ if (block) {
+ throw new ApiError(meta.errors.youHaveBeenBlocked);
+ }
+ }
- // if already voted
- const exist = await PollVotes.findBy({
- noteId: note.id,
- userId: user.id,
- });
+ const poll = await this.pollsRepository.findOneByOrFail({ noteId: note.id });
- if (exist.length) {
- if (poll.multiple) {
- if (exist.some(x => x.choice === ps.choice)) {
- throw new ApiError(meta.errors.alreadyVoted);
+ if (poll.expiresAt && poll.expiresAt < createdAt) {
+ throw new ApiError(meta.errors.alreadyExpired);
}
- } else {
- throw new ApiError(meta.errors.alreadyVoted);
- }
- }
- // Create vote
- const vote = await PollVotes.insert({
- id: genId(),
- createdAt,
- noteId: note.id,
- userId: user.id,
- choice: ps.choice,
- }).then(x => PollVotes.findOneByOrFail(x.identifiers[0]));
+ if (poll.choices[ps.choice] == null) {
+ throw new ApiError(meta.errors.invalidChoice);
+ }
- // Increment votes count
- const index = ps.choice + 1; // In SQL, array index is 1 based
- await Polls.query(`UPDATE poll SET votes[${index}] = votes[${index}] + 1 WHERE "noteId" = '${poll.noteId}'`);
+ // if already voted
+ const exist = await this.pollVotesRepository.findBy({
+ noteId: note.id,
+ userId: me.id,
+ });
+
+ if (exist.length) {
+ if (poll.multiple) {
+ if (exist.some(x => x.choice === ps.choice)) {
+ throw new ApiError(meta.errors.alreadyVoted);
+ }
+ } else {
+ throw new ApiError(meta.errors.alreadyVoted);
+ }
+ }
+
+ // Create vote
+ const vote = await this.pollVotesRepository.insert({
+ id: this.idService.genId(),
+ createdAt,
+ noteId: note.id,
+ userId: me.id,
+ choice: ps.choice,
+ }).then(x => this.pollVotesRepository.findOneByOrFail(x.identifiers[0]));
- publishNoteStream(note.id, 'pollVoted', {
- choice: ps.choice,
- userId: user.id,
- });
+ // Increment votes count
+ const index = ps.choice + 1; // In SQL, array index is 1 based
+ await this.pollsRepository.query(`UPDATE poll SET votes[${index}] = votes[${index}] + 1 WHERE "noteId" = '${poll.noteId}'`);
- // Notify
- createNotification(note.userId, 'pollVote', {
- notifierId: user.id,
- noteId: note.id,
- choice: ps.choice,
- });
+ this.globalEventService.publishNoteStream(note.id, 'pollVoted', {
+ choice: ps.choice,
+ userId: me.id,
+ });
- // Fetch watchers
- NoteWatchings.findBy({
- noteId: note.id,
- userId: Not(user.id),
- }).then(watchers => {
- for (const watcher of watchers) {
- createNotification(watcher.userId, 'pollVote', {
- notifierId: user.id,
+ // Notify
+ this.createNotificationService.createNotification(note.userId, 'pollVote', {
+ notifierId: me.id,
noteId: note.id,
choice: ps.choice,
});
- }
- });
- // リモート投票の場合リプライ送信
- if (note.userHost != null) {
- const pollOwner = await Users.findOneByOrFail({ id: note.userId }) as IRemoteUser;
+ // リモート投票の場合リプライ送信
+ if (note.userHost != null) {
+ const pollOwner = await this.usersRepository.findOneByOrFail({ id: note.userId }) as IRemoteUser;
- deliver(user, renderActivity(await renderVote(user, vote, note, poll, pollOwner)), pollOwner.inbox);
- }
+ this.queueService.deliver(me, this.apRendererService.renderActivity(await this.apRendererService.renderVote(me, vote, note, poll, pollOwner)), pollOwner.inbox);
+ }
- // リモートフォロワーにUpdate配信
- deliverQuestionUpdate(note.id);
-});
+ // リモートフォロワーにUpdate配信
+ this.pollService.deliverQuestionUpdate(note.id);
+ });
+ }
+}