backend: fix various type lints in services/note

`createdAt` in `insertNote` now will default to the current date.

Also refactor poll insert:
Instead of testing hasPoll, just do a null check on data.poll since it's
a more reliable indicator for whether a poll exists (and also tsc won't
complain about data.poll being possibly null).
This commit is contained in:
Norm 2022-10-16 17:50:25 -04:00
parent d83c1c3851
commit bfba54524d
Signed by untrusted user: norm
GPG key ID: 7123E30E441E80DE
2 changed files with 43 additions and 41 deletions

View file

@ -35,6 +35,7 @@ import { webhookDeliver } from '@/queue/index.js';
import { Cache } from '@/misc/cache.js'; import { Cache } from '@/misc/cache.js';
import { UserProfile } from '@/models/entities/user-profile.js'; import { UserProfile } from '@/models/entities/user-profile.js';
import { getActiveWebhooks } from '@/misc/webhook-cache.js'; import { getActiveWebhooks } from '@/misc/webhook-cache.js';
import { IActivity } from '@/remote/activitypub/type.js';
import { updateHashtags } from '../update-hashtag.js'; import { updateHashtags } from '../update-hashtag.js';
import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc.js'; import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc.js';
import { createNotification } from '../create-notification.js'; import { createNotification } from '../create-notification.js';
@ -59,8 +60,8 @@ class NotificationManager {
this.queue = []; this.queue = [];
} }
public push(notifiee: ILocalUser['id'], reason: NotificationType) { public push(notifiee: ILocalUser['id'], reason: NotificationType): void {
// 自分自身へは通知しない // No notification to yourself.
if (this.notifier.id === notifiee) return; if (this.notifier.id === notifiee) return;
const exist = this.queue.find(x => x.target === notifiee); const exist = this.queue.find(x => x.target === notifiee);
@ -78,7 +79,7 @@ class NotificationManager {
} }
} }
public async deliver() { public async deliver(): Promise<void> {
for (const x of this.queue) { for (const x of this.queue) {
// check if the sender or thread are muted // check if the sender or thread are muted
const userMuted = await Mutings.findOneBy({ const userMuted = await Mutings.findOneBy({
@ -119,7 +120,7 @@ type Option = {
poll?: IPoll | null; poll?: IPoll | null;
localOnly?: boolean | null; localOnly?: boolean | null;
cw?: string | null; cw?: string | null;
visibility?: string; visibility?: 'home' | 'public' | 'followers' | 'specified';
visibleUsers?: MinimumUser[] | null; visibleUsers?: MinimumUser[] | null;
channel?: Channel | null; channel?: Channel | null;
apMentions?: MinimumUser[] | null; apMentions?: MinimumUser[] | null;
@ -130,7 +131,7 @@ type Option = {
app?: App | null; app?: App | null;
}; };
export default async (user: { id: User['id']; username: User['username']; host: User['host']; isSilenced: User['isSilenced']; createdAt: User['createdAt']; }, data: Option, silent = false) => new Promise<Note>(async (res, rej) => { export default async (user: { id: User['id']; username: User['username']; host: User['host']; isSilenced: User['isSilenced']; createdAt: User['createdAt']; }, data: Option, silent = false): Promise<Note> => new Promise<Note>(async (res, rej) => {
// チャンネル外にリプライしたら対象のスコープに合わせる // チャンネル外にリプライしたら対象のスコープに合わせる
// (クライアントサイドでやっても良い処理だと思うけどとりあえずサーバーサイドで) // (クライアントサイドでやっても良い処理だと思うけどとりあえずサーバーサイドで)
if (data.reply && data.channel && data.reply.channelId !== data.channel.id) { if (data.reply && data.channel && data.reply.channelId !== data.channel.id) {
@ -214,7 +215,7 @@ export default async (user: { id: User['id']; username: User['username']; host:
tags = tags.filter(tag => Array.from(tag || '').length <= 128).splice(0, 32); tags = tags.filter(tag => Array.from(tag || '').length <= 128).splice(0, 32);
if (data.reply && (user.id !== data.reply.userId) && !mentionedUsers.some(u => u.id === data.reply!.userId)) { if (data.reply && (user.id !== data.reply.userId) && !mentionedUsers.some(u => u.id === data.reply!.userId)) {
mentionedUsers.push(await Users.findOneByOrFail({ id: data.reply!.userId })); mentionedUsers.push(await Users.findOneByOrFail({ id: data.reply.userId }));
} }
if (data.visibility === 'specified') { if (data.visibility === 'specified') {
@ -226,8 +227,8 @@ export default async (user: { id: User['id']; username: User['username']; host:
} }
} }
if (data.reply && !data.visibleUsers.some(x => x.id === data.reply!.userId)) { if (data.reply && !data.visibleUsers.some(x => x.id === data.reply?.userId)) {
data.visibleUsers.push(await Users.findOneByOrFail({ id: data.reply!.userId })); data.visibleUsers.push(await Users.findOneByOrFail({ id: data.reply.userId }));
} }
} }
@ -477,7 +478,7 @@ export default async (user: { id: User['id']; username: User['username']; host:
index(note); index(note);
}); });
async function renderNoteOrRenoteActivity(data: Option, note: Note) { async function renderNoteOrRenoteActivity(data: Option, note: Note): Promise<IActivity | null> {
if (data.localOnly) return null; if (data.localOnly) return null;
const content = data.renote && data.text == null && data.poll == null && (data.files == null || data.files.length === 0) const content = data.renote && data.text == null && data.poll == null && (data.files == null || data.files.length === 0)
@ -487,7 +488,7 @@ async function renderNoteOrRenoteActivity(data: Option, note: Note) {
return renderActivity(content); return renderActivity(content);
} }
function incRenoteCount(renote: Note) { function incRenoteCount(renote: Note): void {
Notes.createQueryBuilder().update() Notes.createQueryBuilder().update()
.set({ .set({
renoteCount: () => '"renoteCount" + 1', renoteCount: () => '"renoteCount" + 1',
@ -497,10 +498,12 @@ function incRenoteCount(renote: Note) {
.execute(); .execute();
} }
async function insertNote(user: { id: User['id']; host: User['host']; }, data: Option, tags: string[], emojis: string[], mentionedUsers: MinimumUser[]) { async function insertNote(user: { id: User['id']; host: User['host']; }, data: Option, tags: string[], emojis: string[], mentionedUsers: MinimumUser[]): Promise<Note> {
const createdAt = data.createdAt ?? new Date();
const insert = new Note({ const insert = new Note({
id: genId(data.createdAt!), id: genId(createdAt),
createdAt: data.createdAt!, createdAt,
fileIds: data.files ? data.files.map(file => file.id) : [], fileIds: data.files ? data.files.map(file => file.id) : [],
replyId: data.reply ? data.reply.id : null, replyId: data.reply ? data.reply.id : null,
renoteId: data.renote ? data.renote.id : null, renoteId: data.renote ? data.renote.id : null,
@ -517,8 +520,8 @@ async function insertNote(user: { id: User['id']; host: User['host']; }, data: O
tags: tags.map(tag => normalizeForSearch(tag)), tags: tags.map(tag => normalizeForSearch(tag)),
emojis, emojis,
userId: user.id, userId: user.id,
localOnly: data.localOnly!, localOnly: data.localOnly ?? false,
visibility: data.visibility as any, visibility: data.visibility,
visibleUserIds: data.visibility === 'specified' visibleUserIds: data.visibility === 'specified'
? data.visibleUsers ? data.visibleUsers
? data.visibleUsers.map(u => u.id) ? data.visibleUsers.map(u => u.id)
@ -543,29 +546,26 @@ async function insertNote(user: { id: User['id']; host: User['host']; }, data: O
insert.mentions = mentionedUsers.map(u => u.id); insert.mentions = mentionedUsers.map(u => u.id);
} }
// 投稿を作成 // Create a post
try { try {
if (insert.hasPoll) { // Start transaction
// Start transaction await db.transaction(async transactionalEntityManager => {
await db.transaction(async transactionalEntityManager => { await transactionalEntityManager.insert(Note, insert);
await transactionalEntityManager.insert(Note, insert);
if (data.poll != null) {
const poll = new Poll({ const poll = new Poll({
noteId: insert.id, noteId: insert.id,
choices: data.poll!.choices, choices: data.poll.choices,
expiresAt: data.poll!.expiresAt, expiresAt: data.poll.expiresAt,
multiple: data.poll!.multiple, multiple: data.poll.multiple,
votes: new Array(data.poll!.choices.length).fill(0), votes: new Array(data.poll.choices.length).fill(0),
noteVisibility: insert.visibility, noteVisibility: insert.visibility,
userId: user.id, userId: user.id,
userHost: user.host, userHost: user.host,
}); });
await transactionalEntityManager.insert(Poll, poll); await transactionalEntityManager.insert(Poll, poll);
}); }
} else { });
await Notes.insert(insert);
}
return insert; return insert;
} catch (e) { } catch (e) {
@ -582,10 +582,10 @@ async function insertNote(user: { id: User['id']; host: User['host']; }, data: O
} }
} }
function index(note: Note) { function index(note: Note): void {
if (note.text == null || config.elasticsearch == null) return; if (note.text == null || config.elasticsearch == null) return;
es!.index({ es.index({
index: config.elasticsearch.index || 'misskey_note', index: config.elasticsearch.index || 'misskey_note',
id: note.id.toString(), id: note.id.toString(),
body: { body: {
@ -596,7 +596,7 @@ function index(note: Note) {
}); });
} }
async function notifyToWatchersOfRenotee(renote: Note, user: { id: User['id']; }, nm: NotificationManager, type: NotificationType) { async function notifyToWatchersOfRenotee(renote: Note, user: { id: User['id']; }, nm: NotificationManager, type: NotificationType): Promise<void> {
const watchers = await NoteWatchings.findBy({ const watchers = await NoteWatchings.findBy({
noteId: renote.id, noteId: renote.id,
userId: Not(user.id), userId: Not(user.id),
@ -607,7 +607,7 @@ async function notifyToWatchersOfRenotee(renote: Note, user: { id: User['id']; }
} }
} }
async function notifyToWatchersOfReplyee(reply: Note, user: { id: User['id']; }, nm: NotificationManager) { async function notifyToWatchersOfReplyee(reply: Note, user: { id: User['id']; }, nm: NotificationManager): Promise<void> {
const watchers = await NoteWatchings.findBy({ const watchers = await NoteWatchings.findBy({
noteId: reply.id, noteId: reply.id,
userId: Not(user.id), userId: Not(user.id),
@ -618,7 +618,7 @@ async function notifyToWatchersOfReplyee(reply: Note, user: { id: User['id']; },
} }
} }
async function createMentionedEvents(mentionedUsers: MinimumUser[], note: Note, nm: NotificationManager) { async function createMentionedEvents(mentionedUsers: MinimumUser[], note: Note, nm: NotificationManager): Promise<void> {
for (const u of mentionedUsers.filter(u => Users.isLocalUser(u))) { for (const u of mentionedUsers.filter(u => Users.isLocalUser(u))) {
const threadMuted = await NoteThreadMutings.findOneBy({ const threadMuted = await NoteThreadMutings.findOneBy({
userId: u.id, userId: u.id,
@ -653,11 +653,11 @@ async function createMentionedEvents(mentionedUsers: MinimumUser[], note: Note,
} }
} }
function saveReply(reply: Note, note: Note) { function saveReply(reply: Note, note: Note): void {
Notes.increment({ id: reply.id }, 'repliesCount', 1); Notes.increment({ id: reply.id }, 'repliesCount', 1);
} }
function incNotesCountOfUser(user: { id: User['id']; }) { function incNotesCountOfUser(user: { id: User['id']; }): void {
Users.createQueryBuilder().update() Users.createQueryBuilder().update()
.set({ .set({
updatedAt: new Date(), updatedAt: new Date(),
@ -668,7 +668,7 @@ function incNotesCountOfUser(user: { id: User['id']; }) {
} }
async function extractMentionedUsers(user: { host: User['host']; }, tokens: mfm.MfmNode[]): Promise<User[]> { async function extractMentionedUsers(user: { host: User['host']; }, tokens: mfm.MfmNode[]): Promise<User[]> {
if (tokens == null) return []; if (tokens.length === 0) return [];
const mentions = extractMentions(tokens); const mentions = extractMentions(tokens);

View file

@ -1,4 +1,4 @@
import { Brackets, In, IsNull, Not } from 'typeorm'; import { Brackets, FindOptionsWhere, In, IsNull, Not } from 'typeorm';
import { publishNoteStream } from '@/services/stream.js'; import { publishNoteStream } from '@/services/stream.js';
import renderDelete from '@/remote/activitypub/renderer/delete.js'; import renderDelete from '@/remote/activitypub/renderer/delete.js';
import renderAnnounce from '@/remote/activitypub/renderer/announce.js'; import renderAnnounce from '@/remote/activitypub/renderer/announce.js';
@ -41,10 +41,12 @@ export default async function(user: { id: User['id']; uri: User['uri']; host: Us
if (Users.isLocalUser(user) && !note.localOnly) { if (Users.isLocalUser(user) && !note.localOnly) {
let renote: Note | null = null; let renote: Note | null = null;
// if deletd note is renote // if deleted note is renote
if (isPureRenote(note)) { if (isPureRenote(note)) {
renote = await Notes.findOneBy({ renote = await Notes.findOneBy({
id: note.renoteId, // isPureRenote checks if note.renoteId is null already, so renoteId should be non-null.
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
id: note.renoteId!,
}); });
} }
@ -106,7 +108,7 @@ async function findCascadingNotes(note: Note): Promise<Note[]> {
} }
async function getMentionedRemoteUsers(note: Note): Promise<IRemoteUser[]> { async function getMentionedRemoteUsers(note: Note): Promise<IRemoteUser[]> {
const where = [] as any[]; const where: FindOptionsWhere<User>[] = [];
// mention / reply / dm // mention / reply / dm
if (note.mentions.length > 0) { if (note.mentions.length > 0) {