Merge pull request MisskeyIO#246 from MisskeyIO/merge-upstream
Merge misskey-dev/develop
This commit is contained in:
commit
9fb84a2254
46 changed files with 903 additions and 325 deletions
|
@ -61,12 +61,23 @@ export class AntennaService implements OnApplicationShutdown {
|
|||
lastUsedAt: new Date(body.lastUsedAt),
|
||||
});
|
||||
break;
|
||||
case 'antennaUpdated':
|
||||
this.antennas[this.antennas.findIndex(a => a.id === body.id)] = {
|
||||
...body,
|
||||
createdAt: new Date(body.createdAt),
|
||||
lastUsedAt: new Date(body.lastUsedAt),
|
||||
};
|
||||
case 'antennaUpdated': {
|
||||
const idx = this.antennas.findIndex(a => a.id === body.id);
|
||||
if (idx >= 0) {
|
||||
this.antennas[idx] = {
|
||||
...body,
|
||||
createdAt: new Date(body.createdAt),
|
||||
lastUsedAt: new Date(body.lastUsedAt),
|
||||
};
|
||||
} else {
|
||||
// サーバ起動時にactiveじゃなかった場合、リストに持っていないので追加する必要あり
|
||||
this.antennas.push({
|
||||
...body,
|
||||
createdAt: new Date(body.createdAt),
|
||||
lastUsedAt: new Date(body.lastUsedAt),
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'antennaDeleted':
|
||||
this.antennas = this.antennas.filter(a => a.id !== body.id);
|
||||
|
|
|
@ -3,9 +3,11 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { URLSearchParams } from 'node:url';
|
||||
import * as nodemailer from 'nodemailer';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { validate as validateEmail } from 'deep-email-validator';
|
||||
import { SubOutputFormat } from 'deep-email-validator/dist/output/output.js';
|
||||
import { MetaService } from '@/core/MetaService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type { Config } from '@/config.js';
|
||||
|
@ -13,6 +15,7 @@ import type Logger from '@/logger.js';
|
|||
import type { UserProfilesRepository } from '@/models/_.js';
|
||||
import { LoggerService } from '@/core/LoggerService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { HttpRequestService } from '@/core/HttpRequestService.js';
|
||||
|
||||
@Injectable()
|
||||
export class EmailService {
|
||||
|
@ -27,6 +30,7 @@ export class EmailService {
|
|||
|
||||
private metaService: MetaService,
|
||||
private loggerService: LoggerService,
|
||||
private httpRequestService: HttpRequestService,
|
||||
) {
|
||||
this.logger = this.loggerService.getLogger('email');
|
||||
}
|
||||
|
@ -160,14 +164,25 @@ export class EmailService {
|
|||
email: emailAddress,
|
||||
});
|
||||
|
||||
const validated = meta.enableActiveEmailValidation ? await validateEmail({
|
||||
email: emailAddress,
|
||||
validateRegex: true,
|
||||
validateMx: true,
|
||||
validateTypo: false, // TLDを見ているみたいだけどclubとか弾かれるので
|
||||
validateDisposable: true, // 捨てアドかどうかチェック
|
||||
validateSMTP: false, // 日本だと25ポートが殆どのプロバイダーで塞がれていてタイムアウトになるので
|
||||
}) : { valid: true, reason: null };
|
||||
const verifymailApi = meta.enableVerifymailApi && meta.verifymailAuthKey != null;
|
||||
let validated;
|
||||
|
||||
if (meta.enableActiveEmailValidation && meta.verifymailAuthKey) {
|
||||
if (verifymailApi) {
|
||||
validated = await this.verifyMail(emailAddress, meta.verifymailAuthKey);
|
||||
} else {
|
||||
validated = meta.enableActiveEmailValidation ? await validateEmail({
|
||||
email: emailAddress,
|
||||
validateRegex: true,
|
||||
validateMx: true,
|
||||
validateTypo: false, // TLDを見ているみたいだけどclubとか弾かれるので
|
||||
validateDisposable: true, // 捨てアドかどうかチェック
|
||||
validateSMTP: false, // 日本だと25ポートが殆どのプロバイダーで塞がれていてタイムアウトになるので
|
||||
}) : { valid: true, reason: null };
|
||||
}
|
||||
} else {
|
||||
validated = { valid: true, reason: null };
|
||||
}
|
||||
|
||||
const available = exist === 0 && validated.valid;
|
||||
|
||||
|
@ -182,4 +197,65 @@ export class EmailService {
|
|||
null,
|
||||
};
|
||||
}
|
||||
|
||||
private async verifyMail(emailAddress: string, verifymailAuthKey: string): Promise<{
|
||||
valid: boolean;
|
||||
reason: 'used' | 'format' | 'disposable' | 'mx' | 'smtp' | null;
|
||||
}> {
|
||||
const endpoint = 'https://verifymail.io/api/' + emailAddress + '?key=' + verifymailAuthKey;
|
||||
const res = await this.httpRequestService.send(endpoint, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
Accept: 'application/json, */*',
|
||||
},
|
||||
});
|
||||
|
||||
const json = (await res.json()) as {
|
||||
block: boolean;
|
||||
catch_all: boolean;
|
||||
deliverable_email: boolean;
|
||||
disposable: boolean;
|
||||
domain: string;
|
||||
email_address: string;
|
||||
email_provider: string;
|
||||
mx: boolean;
|
||||
mx_fallback: boolean;
|
||||
mx_host: string[];
|
||||
mx_ip: string[];
|
||||
mx_priority: { [key: string]: number };
|
||||
privacy: boolean;
|
||||
related_domains: string[];
|
||||
};
|
||||
|
||||
if (json.email_address === undefined) {
|
||||
return {
|
||||
valid: false,
|
||||
reason: 'format',
|
||||
};
|
||||
}
|
||||
if (json.deliverable_email !== undefined && !json.deliverable_email) {
|
||||
return {
|
||||
valid: false,
|
||||
reason: 'smtp',
|
||||
};
|
||||
}
|
||||
if (json.disposable) {
|
||||
return {
|
||||
valid: false,
|
||||
reason: 'disposable',
|
||||
};
|
||||
}
|
||||
if (json.mx !== undefined && !json.mx) {
|
||||
return {
|
||||
valid: false,
|
||||
reason: 'mx',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
valid: true,
|
||||
reason: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -177,7 +177,7 @@ export class NotificationEntityService implements OnModuleInit {
|
|||
) : undefined;
|
||||
|
||||
if (notification.type === 'reaction:grouped') {
|
||||
const reactions = await Promise.all(notification.reactions.map(async reaction => {
|
||||
const reactions = await Promise.allSettled(notification.reactions.map(async reaction => {
|
||||
const user = hint?.packedUsers != null
|
||||
? hint.packedUsers.get(reaction.userId)!
|
||||
: await this.userEntityService.pack(reaction.userId, { id: meId }, {
|
||||
|
@ -193,23 +193,27 @@ export class NotificationEntityService implements OnModuleInit {
|
|||
createdAt: new Date(notification.createdAt).toISOString(),
|
||||
type: notification.type,
|
||||
note: noteIfNeed,
|
||||
reactions,
|
||||
reactions: reactions.filter(result => result.status === 'fulfilled')
|
||||
.map(result => (result as PromiseFulfilledResult<{ user: Packed<'User'>; reaction: string; }>).value),
|
||||
});
|
||||
} else if (notification.type === 'renote:grouped') {
|
||||
const users = await Promise.all(notification.userIds.map(userId => {
|
||||
const user = hint?.packedUsers != null
|
||||
? hint.packedUsers.get(userId)
|
||||
: this.userEntityService.pack(userId!, { id: meId }, {
|
||||
detail: false,
|
||||
});
|
||||
return user;
|
||||
const users = await Promise.allSettled(notification.userIds.map(userId => {
|
||||
const packedUser = hint?.packedUsers != null ? hint.packedUsers.get(userId) : null;
|
||||
if (packedUser) {
|
||||
return packedUser;
|
||||
}
|
||||
|
||||
return this.userEntityService.pack(userId, { id: meId }, {
|
||||
detail: false,
|
||||
});
|
||||
}));
|
||||
return await awaitAll({
|
||||
id: notification.id,
|
||||
createdAt: new Date(notification.createdAt).toISOString(),
|
||||
type: notification.type,
|
||||
note: noteIfNeed,
|
||||
users,
|
||||
users: users.filter(result => result.status === 'fulfilled')
|
||||
.map(result => (result as PromiseFulfilledResult<Packed<'User'>>).value),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -278,9 +282,11 @@ export class NotificationEntityService implements OnModuleInit {
|
|||
validNotifications = validNotifications.filter(x => (x.type !== 'receiveFollowRequest') || reqs.some(r => r.followerId === x.notifierId));
|
||||
}
|
||||
|
||||
return await Promise.all(validNotifications.map(x => this.packGrouped(x, meId, {}, {
|
||||
return (await Promise.allSettled(validNotifications.map(x => this.packGrouped(x, meId, {}, {
|
||||
packedNotes,
|
||||
packedUsers,
|
||||
})));
|
||||
}))))
|
||||
.filter(result => result.status === 'fulfilled')
|
||||
.map(result => (result as PromiseFulfilledResult<Packed<'Notification'>>).value);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue