Merge branch 'develop' of misskey-dev into merge-upstream
This commit is contained in:
commit
abe95d9b67
376 changed files with 5293 additions and 2636 deletions
|
@ -87,6 +87,8 @@ export const ACHIEVEMENT_TYPES = [
|
|||
'brainDiver',
|
||||
'smashTestNotificationButton',
|
||||
'tutorialCompleted',
|
||||
'bubbleGameExplodingHead',
|
||||
'bubbleGameDoubleExplodingHead',
|
||||
] as const;
|
||||
|
||||
@Injectable()
|
||||
|
|
|
@ -73,6 +73,37 @@ export class CaptchaService {
|
|||
}
|
||||
}
|
||||
|
||||
// https://codeberg.org/Gusted/mCaptcha/src/branch/main/mcaptcha.go
|
||||
@bindThis
|
||||
public async verifyMcaptcha(secret: string, siteKey: string, instanceHost: string, response: string | null | undefined): Promise<void> {
|
||||
if (response == null) {
|
||||
throw new Error('mcaptcha-failed: no response provided');
|
||||
}
|
||||
|
||||
const endpointUrl = new URL('/api/v1/pow/siteverify', instanceHost);
|
||||
const result = await this.httpRequestService.send(endpointUrl.toString(), {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
key: siteKey,
|
||||
secret: secret,
|
||||
token: response,
|
||||
}),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
if (result.status !== 200) {
|
||||
throw new Error('mcaptcha-failed: mcaptcha didn\'t return 200 OK');
|
||||
}
|
||||
|
||||
const resp = (await result.json()) as { valid: boolean };
|
||||
|
||||
if (!resp.valid) {
|
||||
throw new Error('mcaptcha-request-failed');
|
||||
}
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async verifyTurnstile(secret: string, response: string | null | undefined): Promise<void> {
|
||||
if (response == null) {
|
||||
|
|
|
@ -7,7 +7,7 @@ import { randomUUID } from 'node:crypto';
|
|||
import * as fs from 'node:fs';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import sharp from 'sharp';
|
||||
import { sharpBmp } from 'sharp-read-bmp';
|
||||
import { sharpBmp } from '@misskey-dev/sharp-read-bmp';
|
||||
import { IsNull } from 'typeorm';
|
||||
import { DeleteObjectCommandInput, PutObjectCommandInput, NoSuchKey } from '@aws-sdk/client-s3';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
|
@ -655,7 +655,7 @@ export class DriveService {
|
|||
public async updateFile(file: MiDriveFile, values: Partial<MiDriveFile>, updater: MiUser) {
|
||||
const alwaysMarkNsfw = (await this.roleService.getUserPolicies(file.userId)).alwaysMarkNsfw;
|
||||
|
||||
if (values.name && !this.driveFileEntityService.validateFileName(file.name)) {
|
||||
if (values.name != null && !this.driveFileEntityService.validateFileName(values.name)) {
|
||||
throw new DriveService.InvalidFileNameError();
|
||||
}
|
||||
|
||||
|
|
|
@ -156,7 +156,7 @@ export class EmailService {
|
|||
@bindThis
|
||||
public async validateEmailForAccount(emailAddress: string): Promise<{
|
||||
available: boolean;
|
||||
reason: null | 'used' | 'format' | 'disposable' | 'mx' | 'smtp' | 'banned';
|
||||
reason: null | 'used' | 'format' | 'disposable' | 'mx' | 'smtp' | 'banned' | 'network' | 'blacklist';
|
||||
}> {
|
||||
const meta = await this.metaService.fetch();
|
||||
|
||||
|
@ -183,6 +183,10 @@ export class EmailService {
|
|||
if (validated.valid && meta.enableVerifymailApi && meta.verifymailAuthKey != null) {
|
||||
validated = await this.verifyMail(emailAddress, meta.verifymailAuthKey);
|
||||
}
|
||||
|
||||
if (validated.valid && meta.enableTruemailApi && meta.truemailInstance && meta.truemailAuthKey != null) {
|
||||
validated = await this.trueMail(meta.truemailInstance, emailAddress, meta.truemailAuthKey);
|
||||
}
|
||||
} else {
|
||||
validated = { valid: true, reason: null };
|
||||
}
|
||||
|
@ -201,6 +205,8 @@ export class EmailService {
|
|||
validated.reason === 'disposable' ? 'disposable' :
|
||||
validated.reason === 'mx' ? 'mx' :
|
||||
validated.reason === 'smtp' ? 'smtp' :
|
||||
validated.reason === 'network' ? 'network' :
|
||||
validated.reason === 'blacklist' ? 'blacklist' :
|
||||
null,
|
||||
};
|
||||
}
|
||||
|
@ -265,4 +271,67 @@ export class EmailService {
|
|||
reason: null,
|
||||
};
|
||||
}
|
||||
|
||||
private async trueMail<T>(truemailInstance: string, emailAddress: string, truemailAuthKey: string): Promise<{
|
||||
valid: boolean;
|
||||
reason: 'used' | 'format' | 'blacklist' | 'mx' | 'smtp' | 'network' | T | null;
|
||||
}> {
|
||||
const endpoint = truemailInstance + '?email=' + emailAddress;
|
||||
try {
|
||||
const res = await this.httpRequestService.send(endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json',
|
||||
Authorization: truemailAuthKey
|
||||
},
|
||||
});
|
||||
|
||||
const json = (await res.json()) as {
|
||||
email: string;
|
||||
success: boolean;
|
||||
errors?: {
|
||||
list_match?: string;
|
||||
regex?: string;
|
||||
mx?: string;
|
||||
smtp?: string;
|
||||
} | null;
|
||||
};
|
||||
|
||||
if (json.email === undefined || (json.email !== undefined && json.errors?.regex)) {
|
||||
return {
|
||||
valid: false,
|
||||
reason: 'format',
|
||||
};
|
||||
}
|
||||
if (json.errors?.smtp) {
|
||||
return {
|
||||
valid: false,
|
||||
reason: 'smtp',
|
||||
};
|
||||
}
|
||||
if (json.errors?.mx) {
|
||||
return {
|
||||
valid: false,
|
||||
reason: 'mx',
|
||||
};
|
||||
}
|
||||
if (!json.success) {
|
||||
return {
|
||||
valid: false,
|
||||
reason: json.errors?.list_match as T || 'blacklist',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
valid: true,
|
||||
reason: null,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
valid: false,
|
||||
reason: 'network',
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,6 +58,7 @@ import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
|
|||
import { UtilityService } from '@/core/UtilityService.js';
|
||||
import { UserBlockingService } from '@/core/UserBlockingService.js';
|
||||
import { isReply } from '@/misc/is-reply.js';
|
||||
import { trackPromise } from '@/misc/promise-tracker.js';
|
||||
|
||||
type NotificationType = 'reply' | 'renote' | 'quote' | 'mention';
|
||||
|
||||
|
@ -677,7 +678,7 @@ export class NoteCreateService implements OnApplicationShutdown {
|
|||
this.relayService.deliverToRelays(user, noteActivity);
|
||||
}
|
||||
|
||||
dm.execute();
|
||||
trackPromise(dm.execute());
|
||||
})();
|
||||
}
|
||||
//#endregion
|
||||
|
|
|
@ -14,6 +14,7 @@ import { IdService } from '@/core/IdService.js';
|
|||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||
import type { NoteUnreadsRepository, MutingsRepository, NoteThreadMutingsRepository } from '@/models/_.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { trackPromise } from '@/misc/promise-tracker.js';
|
||||
|
||||
@Injectable()
|
||||
export class NoteReadService implements OnApplicationShutdown {
|
||||
|
@ -107,7 +108,7 @@ export class NoteReadService implements OnApplicationShutdown {
|
|||
|
||||
// TODO: ↓まとめてクエリしたい
|
||||
|
||||
this.noteUnreadsRepository.countBy({
|
||||
trackPromise(this.noteUnreadsRepository.countBy({
|
||||
userId: userId,
|
||||
isMentioned: true,
|
||||
}).then(mentionsCount => {
|
||||
|
@ -115,9 +116,9 @@ export class NoteReadService implements OnApplicationShutdown {
|
|||
// 全て既読になったイベントを発行
|
||||
this.globalEventService.publishMainStream(userId, 'readAllUnreadMentions');
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
this.noteUnreadsRepository.countBy({
|
||||
trackPromise(this.noteUnreadsRepository.countBy({
|
||||
userId: userId,
|
||||
isSpecified: true,
|
||||
}).then(specifiedCount => {
|
||||
|
@ -125,7 +126,7 @@ export class NoteReadService implements OnApplicationShutdown {
|
|||
// 全て既読になったイベントを発行
|
||||
this.globalEventService.publishMainStream(userId, 'readAllUnreadSpecifiedNotes');
|
||||
}
|
||||
});
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ import { CacheService } from '@/core/CacheService.js';
|
|||
import type { Config } from '@/config.js';
|
||||
import { UserListService } from '@/core/UserListService.js';
|
||||
import type { FilterUnionByProperty } from '@/types.js';
|
||||
import { trackPromise } from '@/misc/promise-tracker.js';
|
||||
|
||||
@Injectable()
|
||||
export class NotificationService implements OnApplicationShutdown {
|
||||
|
@ -74,7 +75,18 @@ export class NotificationService implements OnApplicationShutdown {
|
|||
}
|
||||
|
||||
@bindThis
|
||||
public async createNotification<T extends MiNotification['type']>(
|
||||
public createNotification<T extends MiNotification['type']>(
|
||||
notifieeId: MiUser['id'],
|
||||
type: T,
|
||||
data: Omit<FilterUnionByProperty<MiNotification, 'type', T>, 'type' | 'id' | 'createdAt' | 'notifierId'>,
|
||||
notifierId?: MiUser['id'] | null,
|
||||
) {
|
||||
trackPromise(
|
||||
this.#createNotificationInternal(notifieeId, type, data, notifierId),
|
||||
);
|
||||
}
|
||||
|
||||
async #createNotificationInternal<T extends MiNotification['type']>(
|
||||
notifieeId: MiUser['id'],
|
||||
type: T,
|
||||
data: Omit<FilterUnionByProperty<MiNotification, 'type', T>, 'type' | 'id' | 'createdAt' | 'notifierId'>,
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { setTimeout } from 'node:timers/promises';
|
||||
import { Inject, Module, OnApplicationShutdown } from '@nestjs/common';
|
||||
import * as Bull from 'bullmq';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type { Config } from '@/config.js';
|
||||
import { QUEUE, baseQueueOptions } from '@/queue/const.js';
|
||||
import { allSettled } from '@/misc/promise-tracker.js';
|
||||
import type { Provider } from '@nestjs/common';
|
||||
import type { DeliverJobData, InboxJobData, EndedPollNotificationJobData, WebhookDeliverJobData, RelationshipJobData } from '../queue/types.js';
|
||||
|
||||
|
@ -106,14 +106,9 @@ export class QueueModule implements OnApplicationShutdown {
|
|||
) {}
|
||||
|
||||
public async dispose(): Promise<void> {
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
// XXX:
|
||||
// Shutting down the existing connections causes errors on Jest as
|
||||
// Misskey has asynchronous postgres/redis connections that are not
|
||||
// awaited.
|
||||
// Let's wait for some random time for them to finish.
|
||||
await setTimeout(5000);
|
||||
}
|
||||
// Wait for all potential queue jobs
|
||||
await allSettled();
|
||||
// And then close all queues
|
||||
await Promise.all([
|
||||
this.systemQueue.close(),
|
||||
this.endedPollNotificationQueue.close(),
|
||||
|
|
|
@ -183,6 +183,16 @@ export class QueueService {
|
|||
});
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public createExportClipsJob(user: ThinUser) {
|
||||
return this.dbQueue.add('exportClips', {
|
||||
user: { id: user.id },
|
||||
}, {
|
||||
removeOnComplete: true,
|
||||
removeOnFail: true,
|
||||
});
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public createExportFavoritesJob(user: ThinUser) {
|
||||
return this.dbQueue.add('exportFavorites', {
|
||||
|
|
|
@ -28,6 +28,7 @@ import { UserBlockingService } from '@/core/UserBlockingService.js';
|
|||
import { CustomEmojiService } from '@/core/CustomEmojiService.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
import { FeaturedService } from '@/core/FeaturedService.js';
|
||||
import { trackPromise } from '@/misc/promise-tracker.js';
|
||||
|
||||
const FALLBACK = '❤';
|
||||
const PER_NOTE_REACTION_USER_PAIR_CACHE_MAX = 16;
|
||||
|
@ -273,7 +274,7 @@ export class ReactionService {
|
|||
}
|
||||
}
|
||||
|
||||
dm.execute();
|
||||
trackPromise(dm.execute());
|
||||
}
|
||||
//#endregion
|
||||
}
|
||||
|
@ -321,7 +322,7 @@ export class ReactionService {
|
|||
dm.addDirectRecipe(reactee as MiRemoteUser);
|
||||
}
|
||||
dm.addFollowersRecipe();
|
||||
dm.execute();
|
||||
trackPromise(dm.execute());
|
||||
}
|
||||
//#endregion
|
||||
}
|
||||
|
|
|
@ -144,7 +144,7 @@ class DeliverManager {
|
|||
}
|
||||
|
||||
// deliver
|
||||
this.queueService.deliverMany(this.actor, this.activity, inboxes);
|
||||
await this.queueService.deliverMany(this.actor, this.activity, inboxes);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -356,6 +356,7 @@ export class NoteEntityService implements OnModuleInit {
|
|||
color: channel.color,
|
||||
isSensitive: channel.isSensitive,
|
||||
allowRenoteToExternal: channel.allowRenoteToExternal,
|
||||
userId: channel.userId,
|
||||
} : undefined,
|
||||
mentions: note.mentions.length > 0 ? note.mentions : undefined,
|
||||
uri: note.uri ?? undefined,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue