From fdc682e810988fd02c963345b74f9a849102a19e Mon Sep 17 00:00:00 2001 From: Francis Dinh Date: Wed, 7 Dec 2022 14:55:09 -0500 Subject: [PATCH 1/7] server: remove sendEmailNotification The functions have their bodies completely comented out, which means they are doing nothing. --- .../src/services/create-notification.ts | 4 --- .../src/services/send-email-notification.ts | 32 ------------------- 2 files changed, 36 deletions(-) delete mode 100644 packages/backend/src/services/send-email-notification.ts diff --git a/packages/backend/src/services/create-notification.ts b/packages/backend/src/services/create-notification.ts index 6b04bb946..7b27c0261 100644 --- a/packages/backend/src/services/create-notification.ts +++ b/packages/backend/src/services/create-notification.ts @@ -4,7 +4,6 @@ import { Notifications, Mutings, UserProfiles, Users } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; import { User } from '@/models/entities/user.js'; import { Notification } from '@/models/entities/notification.js'; -import { sendEmailNotification } from './send-email-notification.js'; export async function createNotification( notifieeId: User['id'], @@ -53,9 +52,6 @@ export async function createNotification( publishMainStream(notifieeId, 'unreadNotification', packed); pushNotification(notifieeId, 'notification', packed); - - if (type === 'follow') sendEmailNotification.follow(notifieeId, await Users.findOneByOrFail({ id: data.notifierId! })); - if (type === 'receiveFollowRequest') sendEmailNotification.receiveFollowRequest(notifieeId, await Users.findOneByOrFail({ id: data.notifierId! })); }, 2000); return notification; diff --git a/packages/backend/src/services/send-email-notification.ts b/packages/backend/src/services/send-email-notification.ts deleted file mode 100644 index cd3c670ac..000000000 --- a/packages/backend/src/services/send-email-notification.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { UserProfiles } from '@/models/index.js'; -import { User } from '@/models/entities/user.js'; -import { I18n } from '@/misc/i18n.js'; -import * as Acct from '@/misc/acct.js'; -import { sendEmail } from './send-email.js'; - -// TODO: locale ファイルをクライアント用とサーバー用で分けたい - -async function follow(userId: User['id'], follower: User) { - /* - const userProfile = await UserProfiles.findOneByOrFail({ userId: userId }); - if (!userProfile.email || !userProfile.emailNotificationTypes.includes('follow')) return; - const i18n = new I18n(userProfile.lang ?? 'en-US'); - // TODO: render user information html - sendEmail(userProfile.email, i18n.t('_email._follow.title'), `${follower.name} (@${Acct.toString(follower)})`, `${follower.name} (@${Acct.toString(follower)})`); - */ -} - -async function receiveFollowRequest(userId: User['id'], follower: User) { - /* - const userProfile = await UserProfiles.findOneByOrFail({ userId: userId }); - if (!userProfile.email || !userProfile.emailNotificationTypes.includes('receiveFollowRequest')) return; - const i18n = new I18n(userProfile.lang ?? 'en-US'); - // TODO: render user information html - sendEmail(userProfile.email, i18n.t('_email._receiveFollowRequest.title'), `${follower.name} (@${Acct.toString(follower)})`, `${follower.name} (@${Acct.toString(follower)})`); - */ -} - -export const sendEmailNotification = { - follow, - receiveFollowRequest, -}; From b8fb7a38ccc3c38a4a1c70428a7d7f1981f75682 Mon Sep 17 00:00:00 2001 From: Francis Dinh Date: Wed, 7 Dec 2022 14:56:11 -0500 Subject: [PATCH 2/7] server: improve Logger typing information and docs --- packages/backend/package.json | 2 + packages/backend/src/config/types.ts | 2 +- packages/backend/src/services/logger.ts | 98 ++++++++++++++++++------- yarn.lock | 20 ++++- 4 files changed, 95 insertions(+), 27 deletions(-) diff --git a/packages/backend/package.json b/packages/backend/package.json index ee4162f8f..647173ec7 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -122,6 +122,7 @@ "@types/bcryptjs": "2.4.2", "@types/bull": "3.15.8", "@types/cbor": "6.0.0", + "@types/color-convert": "^2.0.0", "@types/escape-regexp": "0.0.1", "@types/fluent-ffmpeg": "2.1.20", "@types/is-url": "1.2.30", @@ -158,6 +159,7 @@ "@types/sinon": "^10.0.13", "@types/sinonjs__fake-timers": "8.1.2", "@types/speakeasy": "2.0.7", + "@types/syslog-pro": "^1.0.0", "@types/tinycolor2": "1.4.3", "@types/tmp": "0.2.3", "@types/uuid": "8.3.4", diff --git a/packages/backend/src/config/types.ts b/packages/backend/src/config/types.ts index 7308fd7c7..08198926b 100644 --- a/packages/backend/src/config/types.ts +++ b/packages/backend/src/config/types.ts @@ -59,7 +59,7 @@ export type Source = { deliverJobMaxAttempts?: number; inboxJobMaxAttempts?: number; - syslog: { + syslog?: { host: string; port: number; }; diff --git a/packages/backend/src/services/logger.ts b/packages/backend/src/services/logger.ts index bdae3f876..a43476c54 100644 --- a/packages/backend/src/services/logger.ts +++ b/packages/backend/src/services/logger.ts @@ -1,25 +1,36 @@ import cluster from 'node:cluster'; import chalk from 'chalk'; -import { default as convertColor } from 'color-convert'; +import convertColor from 'color-convert'; import { format as dateFormat } from 'date-fns'; import * as SyslogPro from 'syslog-pro'; import config from '@/config/index.js'; import { envOption } from '@/env.js'; +type Color = Parameters[0]; + type Domain = { name: string; - color?: string; + color?: Color; }; type Level = 'error' | 'success' | 'warning' | 'debug' | 'info'; +/** + * Class that facilitates recording log messages to the console and optionally a syslog server. + */ export default class Logger { private domain: Domain; private parentLogger: Logger | null = null; private store: boolean; - private syslogClient: any | null = null; + private syslogClient: SyslogPro.RFC5424 | null = null; - constructor(domain: string, color?: string, store = true) { + /** + * Create a logger instance. + * @param domain Logging domain + * @param color Log message color + * @param store Whether to store messages + */ + constructor(domain: string, color?: Color, store = true) { this.domain = { name: domain, color, @@ -41,7 +52,14 @@ export default class Logger { } } - public createSubLogger(domain: string, color?: string, store = true): Logger { + /** + * Create a child logger instance. + * @param domain Logging domain + * @param color Log message color + * @param store Whether to store messages + * @returns A Logger instance whose parent logger is this instance. + */ + public createSubLogger(domain: string, color?: Color, store = true): Logger { const logger = new Logger(domain, color, store); logger.parentLogger = this; return logger; @@ -57,22 +75,20 @@ export default class Logger { } const time = dateFormat(new Date(), 'HH:mm:ss'); - const worker = cluster.isPrimary ? '*' : cluster.worker.id; + const worker = cluster.isPrimary ? '*' : cluster.worker?.id; const l = level === 'error' ? important ? chalk.bgRed.white('ERR ') : chalk.red('ERR ') : level === 'warning' ? chalk.yellow('WARN') : level === 'success' ? important ? chalk.bgGreen.white('DONE') : chalk.green('DONE') : level === 'debug' ? chalk.gray('VERB') : - level === 'info' ? chalk.blue('INFO') : - null; + chalk.blue('INFO'); const domains = [this.domain].concat(subDomains).map(d => d.color ? chalk.rgb(...convertColor.keyword.rgb(d.color))(d.name) : chalk.white(d.name)); const m = level === 'error' ? chalk.red(message) : level === 'warning' ? chalk.yellow(message) : level === 'success' ? chalk.green(message) : level === 'debug' ? chalk.gray(message) : - level === 'info' ? message : - null; + message; let log = `${l} ${worker}\t[${domains.join(' ')}]\t${m}`; if (envOption.withLogTime) log = chalk.gray(time) + ' ' + log; @@ -84,42 +100,74 @@ export default class Logger { const send = level === 'error' ? this.syslogClient.error : level === 'warning' ? this.syslogClient.warning : - level === 'success' ? this.syslogClient.info : - level === 'debug' ? this.syslogClient.info : - level === 'info' ? this.syslogClient.info : - null as never; + this.syslogClient.info; send.bind(this.syslogClient)(message).catch(() => {}); } } } - public error(x: string | Error, data?: Record = {}, important = false): void { // 実行を継続できない状況で使う - if (x instanceof Error) { - data.e = x; - this.log('error', x.toString(), data, important); - } else if (typeof x === 'object') { - this.log('error', `${(x as any).message || (x as any).name || x}`, data, important); + /** + * Log an error message. + * Use in situations where execution cannot be continued. + * @param err Error or string containing an error message + * @param data Data relating to the error + * @param important Whether this error is important + */ + public error(err: string | Error, data: Record = {}, important = false): void { + if (err instanceof Error) { + data.e = err; + this.log('error', err.toString(), data, important); + } else if (typeof err === 'object') { + this.log('error', `${(err as any).message || (err as any).name || err}`, data, important); } else { - this.log('error', `${x}`, data, important); + this.log('error', `${err}`, data, important); } } - public warn(message: string, data?: Record | null, important = false): void { // 実行を継続できるが改善すべき状況で使う + /** + * Log a warning message. + * Use in situations where execution can continue but needs to be improved. + * @param message Warning message + * @param data Data relating to the warning + * @param important Whether this warning is important + */ + public warn(message: string, data?: Record | null, important = false): void { this.log('warning', message, data, important); } - public succ(message: string, data?: Record | null, important = false): void { // 何かに成功した状況で使う + /** + * Log a success message. + * Use in situations where something has been successfully done. + * @param message Success message + * @param data Data relating to the success + * @param important Whether this success message is important + */ + public succ(message: string, data?: Record | null, important = false): void { this.log('success', message, data, important); } - public debug(message: string, data?: Record | null, important = false): void { // デバッグ用に使う(開発者に必要だが利用者に不要な情報) + /** + * Log a debug message. + * Use for debugging (information needed by developers but not required by users). + * @param message Debug message + * @param data Data relating to the debug message + * @param important Whether this debug message is important + */ + public debug(message: string, data?: Record | null, important = false): void { if (process.env.NODE_ENV !== 'production' || envOption.verbose) { this.log('debug', message, data, important); } } - public info(message: string, data?: Record | null, important = false): void { // それ以外 + /** + * Log an informational message. + * Use when something needs to be logged but doesn't fit into other levels. + * @param message Info message + * @param data Data relating to the info message + * @param important Whether this info message is important + */ + public info(message: string, data?: Record | null, important = false): void { this.log('info', message, data, important); } } diff --git a/yarn.lock b/yarn.lock index 8076d1ab6..89f902e5d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1678,7 +1678,16 @@ __metadata: languageName: node linkType: hard -"@types/color-name@npm:^1.1.1": +"@types/color-convert@npm:^2.0.0": + version: 2.0.0 + resolution: "@types/color-convert@npm:2.0.0" + dependencies: + "@types/color-name": "*" + checksum: 027b68665dc2278cc2d83e796ada0a05a08aa5a11297e227c48c7f9f6eac518dec98578ab0072bd211963d3e4b431da70b20ea28d6c3136d0badfd3f9913baee + languageName: node + linkType: hard + +"@types/color-name@npm:*, @types/color-name@npm:^1.1.1": version: 1.1.1 resolution: "@types/color-name@npm:1.1.1" checksum: b71fcad728cc68abcba1d405742134410c8f8eb3c2ef18113b047afca158ad23a4f2c229bcf71a38f4a818dead375c45b20db121d0e69259c2d81e97a740daa6 @@ -2425,6 +2434,13 @@ __metadata: languageName: node linkType: hard +"@types/syslog-pro@npm:^1.0.0": + version: 1.0.0 + resolution: "@types/syslog-pro@npm:1.0.0" + checksum: d0dcd87efad8a629bba449f86a617605a3fbffa5c18a8b309c82e7b85036ac21cfd34711fd522f50528dd0f0d07bdb66261a6f9ef20f2a9133e847b2e717c1bc + languageName: node + linkType: hard + "@types/throttle-debounce@npm:5.0.0": version: 5.0.0 resolution: "@types/throttle-debounce@npm:5.0.0" @@ -3635,6 +3651,7 @@ __metadata: "@types/bcryptjs": 2.4.2 "@types/bull": 3.15.8 "@types/cbor": 6.0.0 + "@types/color-convert": ^2.0.0 "@types/escape-regexp": 0.0.1 "@types/fluent-ffmpeg": 2.1.20 "@types/is-url": 1.2.30 @@ -3671,6 +3688,7 @@ __metadata: "@types/sinon": ^10.0.13 "@types/sinonjs__fake-timers": 8.1.2 "@types/speakeasy": 2.0.7 + "@types/syslog-pro": ^1.0.0 "@types/tinycolor2": 1.4.3 "@types/tmp": 0.2.3 "@types/uuid": 8.3.4 From 3dec9a47f0651d25b0c4a9d4f7a095b72544a6d3 Mon Sep 17 00:00:00 2001 From: Francis Dinh Date: Wed, 7 Dec 2022 14:59:08 -0500 Subject: [PATCH 3/7] server: fix various type errors in services --- .../backend/src/services/add-note-to-antenna.ts | 2 +- .../backend/src/services/create-notification.ts | 2 +- .../src/services/fetch-instance-metadata.ts | 4 +++- .../backend/src/services/insert-moderation-log.ts | 2 +- packages/backend/src/services/push-notification.ts | 4 ++-- packages/backend/src/services/relay.ts | 14 +++++++------- packages/backend/src/services/unsuspend-user.ts | 2 +- packages/backend/src/services/update-hashtag.ts | 2 +- packages/backend/src/services/user-cache.ts | 7 ++++--- 9 files changed, 21 insertions(+), 18 deletions(-) diff --git a/packages/backend/src/services/add-note-to-antenna.ts b/packages/backend/src/services/add-note-to-antenna.ts index 99bf1630d..b5bcd2304 100644 --- a/packages/backend/src/services/add-note-to-antenna.ts +++ b/packages/backend/src/services/add-note-to-antenna.ts @@ -6,7 +6,7 @@ import { isUserRelated } from '@/misc/is-user-related.js'; import { publishAntennaStream, publishMainStream } from '@/services/stream.js'; import { User } from '@/models/entities/user.js'; -export async function addNoteToAntenna(antenna: Antenna, note: Note, noteUser: { id: User['id']; }) { +export async function addNoteToAntenna(antenna: Antenna, note: Note, noteUser: { id: User['id']; }): Promise { // 通知しない設定になっているか、自分自身の投稿なら既読にする const read = !antenna.notify || (antenna.userId === noteUser.id); diff --git a/packages/backend/src/services/create-notification.ts b/packages/backend/src/services/create-notification.ts index 7b27c0261..ab4fbfa10 100644 --- a/packages/backend/src/services/create-notification.ts +++ b/packages/backend/src/services/create-notification.ts @@ -9,7 +9,7 @@ export async function createNotification( notifieeId: User['id'], type: Notification['type'], data: Partial, -) { +): Promise { if (data.notifierId && (notifieeId === data.notifierId)) { return null; } diff --git a/packages/backend/src/services/fetch-instance-metadata.ts b/packages/backend/src/services/fetch-instance-metadata.ts index d7f551d93..ad312c3c8 100644 --- a/packages/backend/src/services/fetch-instance-metadata.ts +++ b/packages/backend/src/services/fetch-instance-metadata.ts @@ -81,6 +81,7 @@ type NodeInfo = { nodeName?: any; nodeDescription?: any; description?: any; + themeColor?: any; maintainer?: { name?: any; email?: any; @@ -125,7 +126,8 @@ async function fetchNodeinfo(instance: Instance): Promise { return info as NodeInfo; } catch (e) { - logger.error(`Failed to fetch nodeinfo of ${instance.host}: ${e.message}`); + const message = e instanceof Error ? e.message : e; + logger.error(`Failed to fetch nodeinfo of ${instance.host}: ${message}`); throw e; } diff --git a/packages/backend/src/services/insert-moderation-log.ts b/packages/backend/src/services/insert-moderation-log.ts index 821199962..6c225eb5b 100644 --- a/packages/backend/src/services/insert-moderation-log.ts +++ b/packages/backend/src/services/insert-moderation-log.ts @@ -2,7 +2,7 @@ import { ModerationLogs } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; import { User } from '@/models/entities/user.js'; -export async function insertModerationLog(moderator: { id: User['id'] }, type: string, info?: Record) { +export async function insertModerationLog(moderator: { id: User['id'] }, type: string, info?: Record): Promise { await ModerationLogs.insert({ id: genId(), createdAt: new Date(), diff --git a/packages/backend/src/services/push-notification.ts b/packages/backend/src/services/push-notification.ts index e258c217a..6607c05d9 100644 --- a/packages/backend/src/services/push-notification.ts +++ b/packages/backend/src/services/push-notification.ts @@ -16,7 +16,7 @@ type pushNotificationsTypes = { }; // Reduce the content of the push message because of the character limit -function truncateNotification(notification: Packed<'Notification'>): any { +function truncateNotification(notification: Packed<'Notification'>): Record { if (notification.note) { return { ...notification, @@ -37,7 +37,7 @@ function truncateNotification(notification: Packed<'Notification'>): any { return notification; } -export async function pushNotification(userId: string, type: T, body: pushNotificationsTypes[T]) { +export async function pushNotification(userId: string, type: T, body: pushNotificationsTypes[T]): Promise { const meta = await fetchMeta(); // Register key pair information diff --git a/packages/backend/src/services/relay.ts b/packages/backend/src/services/relay.ts index 36bf88b9c..725ce30ce 100644 --- a/packages/backend/src/services/relay.ts +++ b/packages/backend/src/services/relay.ts @@ -36,7 +36,7 @@ export async function getRelayActor(): Promise { return created as ILocalUser; } -export async function addRelay(inbox: string) { +export async function addRelay(inbox: string): Promise { const relay = await Relays.insert({ id: genId(), inbox, @@ -51,7 +51,7 @@ export async function addRelay(inbox: string) { return relay; } -export async function removeRelay(inbox: string) { +export async function removeRelay(inbox: string): Promise { const relay = await Relays.findOneBy({ inbox, }); @@ -69,12 +69,12 @@ export async function removeRelay(inbox: string) { await Relays.delete(relay.id); } -export async function listRelay() { +export async function listRelay(): Promise { const relays = await Relays.find(); return relays; } -export async function relayAccepted(id: string) { +export async function relayAccepted(id: string): Promise { const result = await Relays.update(id, { status: 'accepted', }); @@ -82,7 +82,7 @@ export async function relayAccepted(id: string) { return JSON.stringify(result); } -export async function relayRejected(id: string) { +export async function relayRejected(id: string): Promise { const result = await Relays.update(id, { status: 'rejected', }); @@ -90,11 +90,11 @@ export async function relayRejected(id: string) { return JSON.stringify(result); } -export async function deliverToRelays(user: { id: User['id']; host: null; }, activity: any) { +export async function deliverToRelays(user: { id: User['id']; host: null; }, activity: any): Promise { if (activity == null) return; const relays = await relaysCache.fetch(null); - if (relays.length === 0) return; + if (relays == null || relays.length === 0) return; // TODO //const copy = structuredClone(activity); diff --git a/packages/backend/src/services/unsuspend-user.ts b/packages/backend/src/services/unsuspend-user.ts index 3ed7ce850..766b10f21 100644 --- a/packages/backend/src/services/unsuspend-user.ts +++ b/packages/backend/src/services/unsuspend-user.ts @@ -8,7 +8,7 @@ import { User } from '@/models/entities/user.js'; import { Users, Followings } from '@/models/index.js'; import { publishInternalEvent } from '@/services/stream.js'; -export async function doPostUnsuspend(user: User) { +export async function doPostUnsuspend(user: User): Promise { publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: false }); if (Users.isLocalUser(user)) { diff --git a/packages/backend/src/services/update-hashtag.ts b/packages/backend/src/services/update-hashtag.ts index 9f549c3f4..7a77cb124 100644 --- a/packages/backend/src/services/update-hashtag.ts +++ b/packages/backend/src/services/update-hashtag.ts @@ -16,7 +16,7 @@ export async function updateUsertags(user: User, tags: string[]): Promise await updateHashtag(user, tag, true, true); } - for (const tag of (user.tags || []).filter(x => !tags.includes(x))) { + for (const tag of user.tags.filter(x => !tags.includes(x))) { await updateHashtag(user, tag, true, false); } } diff --git a/packages/backend/src/services/user-cache.ts b/packages/backend/src/services/user-cache.ts index d30b76a24..d67931c2c 100644 --- a/packages/backend/src/services/user-cache.ts +++ b/packages/backend/src/services/user-cache.ts @@ -1,3 +1,4 @@ +import { IsNull } from 'typeorm'; import { CacheableLocalUser, CacheableUser, ILocalUser } from '@/models/entities/user.js'; import { Users } from '@/models/index.js'; import { Cache } from '@/misc/cache.js'; @@ -5,15 +6,15 @@ import { subscriber } from '@/db/redis.js'; export const userByIdCache = new Cache( Infinity, - (id) => Users.findOneBy({ id }).then(x => x ?? undefined), + async (id) => id ? (await Users.findOneBy({ id }) ?? undefined) : undefined, ); export const localUserByNativeTokenCache = new Cache( Infinity, - (token) => Users.findOneBy({ token }).then(x => x ?? undefined), + async (token) => token ? (await Users.findOneBy({ token, host: IsNull() }) as ILocalUser | null ?? undefined) : undefined, ); export const uriPersonCache = new Cache( Infinity, - (uri) => Users.findOneBy({ uri }).then(x => x ?? undefined), + async (uri) => uri ? (await Users.findOneBy({ uri }) ?? undefined) : undefined, ); subscriber.on('message', async (_, data) => { From 80a73a7510b2b30411988f516e94bba70fda694e Mon Sep 17 00:00:00 2001 From: Francis Dinh Date: Wed, 7 Dec 2022 14:59:54 -0500 Subject: [PATCH 4/7] server: remove unused imports from suspend-user.ts --- packages/backend/src/services/suspend-user.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/backend/src/services/suspend-user.ts b/packages/backend/src/services/suspend-user.ts index 80806cd3e..6293d8a07 100644 --- a/packages/backend/src/services/suspend-user.ts +++ b/packages/backend/src/services/suspend-user.ts @@ -1,10 +1,9 @@ -import { Not, IsNull } from 'typeorm'; import renderDelete from '@/remote/activitypub/renderer/delete.js'; import { renderActivity } from '@/remote/activitypub/renderer/index.js'; import DeliverManager from '@/remote/activitypub/deliver-manager.js'; import config from '@/config/index.js'; import { User } from '@/models/entities/user.js'; -import { Users, Followings } from '@/models/index.js'; +import { Users } from '@/models/index.js'; import { publishInternalEvent } from '@/services/stream.js'; export async function doPostSuspend(user: { id: User['id']; host: User['host'] }): Promise { From b23a8dbaed21d5325674f7822b14082f4ea4633d Mon Sep 17 00:00:00 2001 From: Francis Dinh Date: Wed, 7 Dec 2022 15:00:02 -0500 Subject: [PATCH 5/7] server: translate comments --- packages/backend/src/services/add-note-to-antenna.ts | 4 ++-- packages/backend/src/services/create-notification.ts | 8 ++++---- packages/backend/src/services/delete-account.ts | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/backend/src/services/add-note-to-antenna.ts b/packages/backend/src/services/add-note-to-antenna.ts index b5bcd2304..d502be254 100644 --- a/packages/backend/src/services/add-note-to-antenna.ts +++ b/packages/backend/src/services/add-note-to-antenna.ts @@ -7,7 +7,7 @@ import { publishAntennaStream, publishMainStream } from '@/services/stream.js'; import { User } from '@/models/entities/user.js'; export async function addNoteToAntenna(antenna: Antenna, note: Note, noteUser: { id: User['id']; }): Promise { - // 通知しない設定になっているか、自分自身の投稿なら既読にする + // If it's set to not notify the user, or if it's the user's own post, read it. const read = !antenna.notify || (antenna.userId === noteUser.id); AntennaNotes.insert({ @@ -43,7 +43,7 @@ export async function addNoteToAntenna(antenna: Antenna, note: Note, noteUser: { return; } - // 2秒経っても既読にならなかったら通知 + // Notify if not read after 2 seconds setTimeout(async () => { const unread = await AntennaNotes.findOneBy({ antennaId: antenna.id, read: false }); if (unread) { diff --git a/packages/backend/src/services/create-notification.ts b/packages/backend/src/services/create-notification.ts index ab4fbfa10..bbb4ab356 100644 --- a/packages/backend/src/services/create-notification.ts +++ b/packages/backend/src/services/create-notification.ts @@ -24,7 +24,7 @@ export async function createNotification( createdAt: new Date(), notifieeId, type, - // 相手がこの通知をミュートしているようなら、既読を予めつけておく + // If the other party seems to have muted this notification, pre-read it. isRead: isMuted, ...data, } as Partial) @@ -35,13 +35,13 @@ export async function createNotification( // Publish notification event publishMainStream(notifieeId, 'notification', packed); - // 2秒経っても(今回作成した)通知が既読にならなかったら「未読の通知がありますよ」イベントを発行する + // If the notification (created this time) has not been read after 2 seconds, issue a "You have unread notifications" event. setTimeout(async () => { const fresh = await Notifications.findOneBy({ id: notification.id }); - if (fresh == null) return; // 既に削除されているかもしれない + if (fresh == null) return; // It may have already been deleted. if (fresh.isRead) return; - //#region ただしミュートしているユーザーからの通知なら無視 + //#region However, if the notification comes from a muted user, ignore it. const mutings = await Mutings.findBy({ muterId: notifieeId, }); diff --git a/packages/backend/src/services/delete-account.ts b/packages/backend/src/services/delete-account.ts index c1ab9e2be..ed3e381c4 100644 --- a/packages/backend/src/services/delete-account.ts +++ b/packages/backend/src/services/delete-account.ts @@ -7,7 +7,7 @@ export async function deleteAccount(user: { id: string; host: string | null; }): Promise { - // 物理削除する前にDelete activityを送信する + // Send Delete activity before physical deletion await doPostSuspend(user).catch(() => {}); createDeleteAccountJob(user, { From cbfd866122dc9a0eaedb75959d5b714e7147d4f0 Mon Sep 17 00:00:00 2001 From: Francis Dinh Date: Wed, 7 Dec 2022 17:10:54 -0500 Subject: [PATCH 6/7] server: make fetcher key non-null --- packages/backend/src/misc/cache.ts | 12 ++++++------ packages/backend/src/services/note/create.ts | 2 +- packages/backend/src/services/relay.ts | 2 +- packages/backend/src/services/user-cache.ts | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/backend/src/misc/cache.ts b/packages/backend/src/misc/cache.ts index e472acd38..ee6e90a66 100644 --- a/packages/backend/src/misc/cache.ts +++ b/packages/backend/src/misc/cache.ts @@ -1,7 +1,7 @@ export class Cache { - public cache: Map; + public cache: Map; private lifetime: number; - public fetcher: (key: string | null) => Promise; + public fetcher: (key: string) => Promise; constructor(lifetime: number, fetcher: Cache['fetcher']) { this.cache = new Map(); @@ -9,14 +9,14 @@ export class Cache { this.fetcher = fetcher; } - public set(key: string | null, value: T): void { + public set(key: string, value: T): void { this.cache.set(key, { date: Date.now(), value, }); } - public get(key: string | null): T | undefined { + public get(key: string): T | undefined { const cached = this.cache.get(key); if (cached == null) return undefined; @@ -29,7 +29,7 @@ export class Cache { return cached.value; } - public delete(key: string | null): void { + public delete(key: string): void { this.cache.delete(key); } @@ -38,7 +38,7 @@ export class Cache { * run to get the value. If the fetcher returns undefined, it is * returned but not cached. */ - public async fetch(key: string | null): Promise { + public async fetch(key: string): Promise { const cached = this.get(key); if (cached !== undefined) { return cached; diff --git a/packages/backend/src/services/note/create.ts b/packages/backend/src/services/note/create.ts index e7dac0886..77a6e4c47 100644 --- a/packages/backend/src/services/note/create.ts +++ b/packages/backend/src/services/note/create.ts @@ -266,7 +266,7 @@ export default async (user: { id: User['id']; username: User['username']; host: incNotesCountOfUser(user); // Word mute - mutedWordsCache.fetch(null).then(us => { + mutedWordsCache.fetch('').then(us => { for (const u of us) { checkWordMute(note, { id: u.userId }, u.mutedWords).then(shouldMute => { if (shouldMute) { diff --git a/packages/backend/src/services/relay.ts b/packages/backend/src/services/relay.ts index 725ce30ce..35779e79a 100644 --- a/packages/backend/src/services/relay.ts +++ b/packages/backend/src/services/relay.ts @@ -93,7 +93,7 @@ export async function relayRejected(id: string): Promise { export async function deliverToRelays(user: { id: User['id']; host: null; }, activity: any): Promise { if (activity == null) return; - const relays = await relaysCache.fetch(null); + const relays = await relaysCache.fetch(''); if (relays == null || relays.length === 0) return; // TODO diff --git a/packages/backend/src/services/user-cache.ts b/packages/backend/src/services/user-cache.ts index d67931c2c..2d2ec5711 100644 --- a/packages/backend/src/services/user-cache.ts +++ b/packages/backend/src/services/user-cache.ts @@ -6,15 +6,15 @@ import { subscriber } from '@/db/redis.js'; export const userByIdCache = new Cache( Infinity, - async (id) => id ? (await Users.findOneBy({ id }) ?? undefined) : undefined, + async (id) => await Users.findOneBy({ id }) ?? undefined, ); export const localUserByNativeTokenCache = new Cache( Infinity, - async (token) => token ? (await Users.findOneBy({ token, host: IsNull() }) as ILocalUser | null ?? undefined) : undefined, + async (token) => await Users.findOneBy({ token, host: IsNull() }) as ILocalUser | null ?? undefined, ); export const uriPersonCache = new Cache( Infinity, - async (uri) => uri ? (await Users.findOneBy({ uri }) ?? undefined) : undefined, + async (uri) => await Users.findOneBy({ uri }) ?? undefined, ); subscriber.on('message', async (_, data) => { From 3cf673960b8ef8bc577ad5626270ff2306d45401 Mon Sep 17 00:00:00 2001 From: Francis Dinh Date: Wed, 7 Dec 2022 19:32:05 -0500 Subject: [PATCH 7/7] server: Fix typing for user token Also fix a comment in the User model that wrongly states that the token is null if the user is local, when it's the opposite. --- packages/backend/src/models/entities/user.ts | 4 +++- packages/backend/src/models/repositories/user.ts | 2 +- packages/backend/src/services/user-cache.ts | 6 +++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/backend/src/models/entities/user.ts b/packages/backend/src/models/entities/user.ts index dd2598322..ae905c580 100644 --- a/packages/backend/src/models/entities/user.ts +++ b/packages/backend/src/models/entities/user.ts @@ -214,7 +214,7 @@ export class User { @Index({ unique: true }) @Column('char', { length: 16, nullable: true, unique: true, - comment: 'The native access token of the User. It will be null if the origin of the user is local.', + comment: 'The native access token of local users, or null.', }) public token: string | null; @@ -234,10 +234,12 @@ export class User { export interface ILocalUser extends User { host: null; + token: string; } export interface IRemoteUser extends User { host: string; + token: null; } export type CacheableLocalUser = ILocalUser; diff --git a/packages/backend/src/models/repositories/user.ts b/packages/backend/src/models/repositories/user.ts index 00dcf7f87..13ddfd428 100644 --- a/packages/backend/src/models/repositories/user.ts +++ b/packages/backend/src/models/repositories/user.ts @@ -35,7 +35,7 @@ const locationSchema = { type: 'string', minLength: 1, maxLength: 50 } as const; const birthdaySchema = { type: 'string', pattern: /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/.toString().slice(1, -1) } as const; function isLocalUser(user: User): user is ILocalUser; -function isLocalUser(user: T): user is T & { host: null; }; +function isLocalUser(user: T): user is T & { host: null; token: string; }; function isLocalUser(user: User | { host: User['host'] }): boolean { return user.host == null; } diff --git a/packages/backend/src/services/user-cache.ts b/packages/backend/src/services/user-cache.ts index 2d2ec5711..1266ef5f0 100644 --- a/packages/backend/src/services/user-cache.ts +++ b/packages/backend/src/services/user-cache.ts @@ -1,10 +1,10 @@ import { IsNull } from 'typeorm'; -import { CacheableLocalUser, CacheableUser, ILocalUser } from '@/models/entities/user.js'; +import { CacheableLocalUser, ILocalUser, User } from '@/models/entities/user.js'; import { Users } from '@/models/index.js'; import { Cache } from '@/misc/cache.js'; import { subscriber } from '@/db/redis.js'; -export const userByIdCache = new Cache( +export const userByIdCache = new Cache( Infinity, async (id) => await Users.findOneBy({ id }) ?? undefined, ); @@ -12,7 +12,7 @@ export const localUserByNativeTokenCache = new Cache( Infinity, async (token) => await Users.findOneBy({ token, host: IsNull() }) as ILocalUser | null ?? undefined, ); -export const uriPersonCache = new Cache( +export const uriPersonCache = new Cache( Infinity, async (uri) => await Users.findOneBy({ uri }) ?? undefined, );