forked from FoundKeyGang/FoundKey
Merge pull request 'server: misc services code cleanup' (#275) from refactor/services into main
Reviewed-on: FoundKeyGang/FoundKey#275
This commit is contained in:
commit
f46ba3f700
20 changed files with 139 additions and 103 deletions
|
@ -122,6 +122,7 @@
|
||||||
"@types/bcryptjs": "2.4.2",
|
"@types/bcryptjs": "2.4.2",
|
||||||
"@types/bull": "3.15.8",
|
"@types/bull": "3.15.8",
|
||||||
"@types/cbor": "6.0.0",
|
"@types/cbor": "6.0.0",
|
||||||
|
"@types/color-convert": "^2.0.0",
|
||||||
"@types/escape-regexp": "0.0.1",
|
"@types/escape-regexp": "0.0.1",
|
||||||
"@types/fluent-ffmpeg": "2.1.20",
|
"@types/fluent-ffmpeg": "2.1.20",
|
||||||
"@types/is-url": "1.2.30",
|
"@types/is-url": "1.2.30",
|
||||||
|
@ -158,6 +159,7 @@
|
||||||
"@types/sinon": "^10.0.13",
|
"@types/sinon": "^10.0.13",
|
||||||
"@types/sinonjs__fake-timers": "8.1.2",
|
"@types/sinonjs__fake-timers": "8.1.2",
|
||||||
"@types/speakeasy": "2.0.7",
|
"@types/speakeasy": "2.0.7",
|
||||||
|
"@types/syslog-pro": "^1.0.0",
|
||||||
"@types/tinycolor2": "1.4.3",
|
"@types/tinycolor2": "1.4.3",
|
||||||
"@types/tmp": "0.2.3",
|
"@types/tmp": "0.2.3",
|
||||||
"@types/uuid": "8.3.4",
|
"@types/uuid": "8.3.4",
|
||||||
|
|
|
@ -59,7 +59,7 @@ export type Source = {
|
||||||
deliverJobMaxAttempts?: number;
|
deliverJobMaxAttempts?: number;
|
||||||
inboxJobMaxAttempts?: number;
|
inboxJobMaxAttempts?: number;
|
||||||
|
|
||||||
syslog: {
|
syslog?: {
|
||||||
host: string;
|
host: string;
|
||||||
port: number;
|
port: number;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
export class Cache<T> {
|
export class Cache<T> {
|
||||||
public cache: Map<string | null, { date: number; value: T; }>;
|
public cache: Map<string, { date: number; value: T; }>;
|
||||||
private lifetime: number;
|
private lifetime: number;
|
||||||
public fetcher: (key: string | null) => Promise<T | undefined>;
|
public fetcher: (key: string) => Promise<T | undefined>;
|
||||||
|
|
||||||
constructor(lifetime: number, fetcher: Cache<T>['fetcher']) {
|
constructor(lifetime: number, fetcher: Cache<T>['fetcher']) {
|
||||||
this.cache = new Map();
|
this.cache = new Map();
|
||||||
|
@ -9,14 +9,14 @@ export class Cache<T> {
|
||||||
this.fetcher = fetcher;
|
this.fetcher = fetcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
public set(key: string | null, value: T): void {
|
public set(key: string, value: T): void {
|
||||||
this.cache.set(key, {
|
this.cache.set(key, {
|
||||||
date: Date.now(),
|
date: Date.now(),
|
||||||
value,
|
value,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public get(key: string | null): T | undefined {
|
public get(key: string): T | undefined {
|
||||||
const cached = this.cache.get(key);
|
const cached = this.cache.get(key);
|
||||||
if (cached == null) return undefined;
|
if (cached == null) return undefined;
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ export class Cache<T> {
|
||||||
return cached.value;
|
return cached.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public delete(key: string | null): void {
|
public delete(key: string): void {
|
||||||
this.cache.delete(key);
|
this.cache.delete(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ export class Cache<T> {
|
||||||
* run to get the value. If the fetcher returns undefined, it is
|
* run to get the value. If the fetcher returns undefined, it is
|
||||||
* returned but not cached.
|
* returned but not cached.
|
||||||
*/
|
*/
|
||||||
public async fetch(key: string | null): Promise<T | undefined> {
|
public async fetch(key: string): Promise<T | undefined> {
|
||||||
const cached = this.get(key);
|
const cached = this.get(key);
|
||||||
if (cached !== undefined) {
|
if (cached !== undefined) {
|
||||||
return cached;
|
return cached;
|
||||||
|
|
|
@ -214,7 +214,7 @@ export class User {
|
||||||
@Index({ unique: true })
|
@Index({ unique: true })
|
||||||
@Column('char', {
|
@Column('char', {
|
||||||
length: 16, nullable: true, unique: true,
|
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;
|
public token: string | null;
|
||||||
|
|
||||||
|
@ -234,10 +234,12 @@ export class User {
|
||||||
|
|
||||||
export interface ILocalUser extends User {
|
export interface ILocalUser extends User {
|
||||||
host: null;
|
host: null;
|
||||||
|
token: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IRemoteUser extends User {
|
export interface IRemoteUser extends User {
|
||||||
host: string;
|
host: string;
|
||||||
|
token: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CacheableLocalUser = ILocalUser;
|
export type CacheableLocalUser = ILocalUser;
|
||||||
|
|
|
@ -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;
|
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: User): user is ILocalUser;
|
||||||
function isLocalUser<T extends { host: User['host'] }>(user: T): user is T & { host: null; };
|
function isLocalUser<T extends { host: User['host'] }>(user: T): user is T & { host: null; token: string; };
|
||||||
function isLocalUser(user: User | { host: User['host'] }): boolean {
|
function isLocalUser(user: User | { host: User['host'] }): boolean {
|
||||||
return user.host == null;
|
return user.host == null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,8 @@ import { isUserRelated } from '@/misc/is-user-related.js';
|
||||||
import { publishAntennaStream, publishMainStream } from '@/services/stream.js';
|
import { publishAntennaStream, publishMainStream } from '@/services/stream.js';
|
||||||
import { User } from '@/models/entities/user.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<void> {
|
||||||
// 通知しない設定になっているか、自分自身の投稿なら既読にする
|
// 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);
|
const read = !antenna.notify || (antenna.userId === noteUser.id);
|
||||||
|
|
||||||
AntennaNotes.insert({
|
AntennaNotes.insert({
|
||||||
|
@ -43,7 +43,7 @@ export async function addNoteToAntenna(antenna: Antenna, note: Note, noteUser: {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2秒経っても既読にならなかったら通知
|
// Notify if not read after 2 seconds
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
const unread = await AntennaNotes.findOneBy({ antennaId: antenna.id, read: false });
|
const unread = await AntennaNotes.findOneBy({ antennaId: antenna.id, read: false });
|
||||||
if (unread) {
|
if (unread) {
|
||||||
|
|
|
@ -4,13 +4,12 @@ import { Notifications, Mutings, UserProfiles, Users } from '@/models/index.js';
|
||||||
import { genId } from '@/misc/gen-id.js';
|
import { genId } from '@/misc/gen-id.js';
|
||||||
import { User } from '@/models/entities/user.js';
|
import { User } from '@/models/entities/user.js';
|
||||||
import { Notification } from '@/models/entities/notification.js';
|
import { Notification } from '@/models/entities/notification.js';
|
||||||
import { sendEmailNotification } from './send-email-notification.js';
|
|
||||||
|
|
||||||
export async function createNotification(
|
export async function createNotification(
|
||||||
notifieeId: User['id'],
|
notifieeId: User['id'],
|
||||||
type: Notification['type'],
|
type: Notification['type'],
|
||||||
data: Partial<Notification>,
|
data: Partial<Notification>,
|
||||||
) {
|
): Promise<Notification | null> {
|
||||||
if (data.notifierId && (notifieeId === data.notifierId)) {
|
if (data.notifierId && (notifieeId === data.notifierId)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -25,7 +24,7 @@ export async function createNotification(
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
notifieeId,
|
notifieeId,
|
||||||
type,
|
type,
|
||||||
// 相手がこの通知をミュートしているようなら、既読を予めつけておく
|
// If the other party seems to have muted this notification, pre-read it.
|
||||||
isRead: isMuted,
|
isRead: isMuted,
|
||||||
...data,
|
...data,
|
||||||
} as Partial<Notification>)
|
} as Partial<Notification>)
|
||||||
|
@ -36,13 +35,13 @@ export async function createNotification(
|
||||||
// Publish notification event
|
// Publish notification event
|
||||||
publishMainStream(notifieeId, 'notification', packed);
|
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 () => {
|
setTimeout(async () => {
|
||||||
const fresh = await Notifications.findOneBy({ id: notification.id });
|
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;
|
if (fresh.isRead) return;
|
||||||
|
|
||||||
//#region ただしミュートしているユーザーからの通知なら無視
|
//#region However, if the notification comes from a muted user, ignore it.
|
||||||
const mutings = await Mutings.findBy({
|
const mutings = await Mutings.findBy({
|
||||||
muterId: notifieeId,
|
muterId: notifieeId,
|
||||||
});
|
});
|
||||||
|
@ -53,9 +52,6 @@ export async function createNotification(
|
||||||
|
|
||||||
publishMainStream(notifieeId, 'unreadNotification', packed);
|
publishMainStream(notifieeId, 'unreadNotification', packed);
|
||||||
pushNotification(notifieeId, 'notification', 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);
|
}, 2000);
|
||||||
|
|
||||||
return notification;
|
return notification;
|
||||||
|
|
|
@ -7,7 +7,7 @@ export async function deleteAccount(user: {
|
||||||
id: string;
|
id: string;
|
||||||
host: string | null;
|
host: string | null;
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
// 物理削除する前にDelete activityを送信する
|
// Send Delete activity before physical deletion
|
||||||
await doPostSuspend(user).catch(() => {});
|
await doPostSuspend(user).catch(() => {});
|
||||||
|
|
||||||
createDeleteAccountJob(user, {
|
createDeleteAccountJob(user, {
|
||||||
|
|
|
@ -81,6 +81,7 @@ type NodeInfo = {
|
||||||
nodeName?: any;
|
nodeName?: any;
|
||||||
nodeDescription?: any;
|
nodeDescription?: any;
|
||||||
description?: any;
|
description?: any;
|
||||||
|
themeColor?: any;
|
||||||
maintainer?: {
|
maintainer?: {
|
||||||
name?: any;
|
name?: any;
|
||||||
email?: any;
|
email?: any;
|
||||||
|
@ -125,7 +126,8 @@ async function fetchNodeinfo(instance: Instance): Promise<NodeInfo> {
|
||||||
|
|
||||||
return info as NodeInfo;
|
return info as NodeInfo;
|
||||||
} catch (e) {
|
} 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;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { ModerationLogs } from '@/models/index.js';
|
||||||
import { genId } from '@/misc/gen-id.js';
|
import { genId } from '@/misc/gen-id.js';
|
||||||
import { User } from '@/models/entities/user.js';
|
import { User } from '@/models/entities/user.js';
|
||||||
|
|
||||||
export async function insertModerationLog(moderator: { id: User['id'] }, type: string, info?: Record<string, any>) {
|
export async function insertModerationLog(moderator: { id: User['id'] }, type: string, info?: Record<string, any>): Promise<void> {
|
||||||
await ModerationLogs.insert({
|
await ModerationLogs.insert({
|
||||||
id: genId(),
|
id: genId(),
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
|
|
|
@ -1,25 +1,36 @@
|
||||||
import cluster from 'node:cluster';
|
import cluster from 'node:cluster';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import { default as convertColor } from 'color-convert';
|
import convertColor from 'color-convert';
|
||||||
import { format as dateFormat } from 'date-fns';
|
import { format as dateFormat } from 'date-fns';
|
||||||
import * as SyslogPro from 'syslog-pro';
|
import * as SyslogPro from 'syslog-pro';
|
||||||
import config from '@/config/index.js';
|
import config from '@/config/index.js';
|
||||||
import { envOption } from '@/env.js';
|
import { envOption } from '@/env.js';
|
||||||
|
|
||||||
|
type Color = Parameters<typeof convertColor.keyword.rgb>[0];
|
||||||
|
|
||||||
type Domain = {
|
type Domain = {
|
||||||
name: string;
|
name: string;
|
||||||
color?: string;
|
color?: Color;
|
||||||
};
|
};
|
||||||
|
|
||||||
type Level = 'error' | 'success' | 'warning' | 'debug' | 'info';
|
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 {
|
export default class Logger {
|
||||||
private domain: Domain;
|
private domain: Domain;
|
||||||
private parentLogger: Logger | null = null;
|
private parentLogger: Logger | null = null;
|
||||||
private store: boolean;
|
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 = {
|
this.domain = {
|
||||||
name: domain,
|
name: domain,
|
||||||
color,
|
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);
|
const logger = new Logger(domain, color, store);
|
||||||
logger.parentLogger = this;
|
logger.parentLogger = this;
|
||||||
return logger;
|
return logger;
|
||||||
|
@ -57,22 +75,20 @@ export default class Logger {
|
||||||
}
|
}
|
||||||
|
|
||||||
const time = dateFormat(new Date(), 'HH:mm:ss');
|
const time = dateFormat(new Date(), 'HH:mm:ss');
|
||||||
const worker = cluster.isPrimary ? '*' : cluster.worker.id;
|
const worker = cluster.isPrimary ? '*' : cluster.worker?.id;
|
||||||
const l =
|
const l =
|
||||||
level === 'error' ? important ? chalk.bgRed.white('ERR ') : chalk.red('ERR ') :
|
level === 'error' ? important ? chalk.bgRed.white('ERR ') : chalk.red('ERR ') :
|
||||||
level === 'warning' ? chalk.yellow('WARN') :
|
level === 'warning' ? chalk.yellow('WARN') :
|
||||||
level === 'success' ? important ? chalk.bgGreen.white('DONE') : chalk.green('DONE') :
|
level === 'success' ? important ? chalk.bgGreen.white('DONE') : chalk.green('DONE') :
|
||||||
level === 'debug' ? chalk.gray('VERB') :
|
level === 'debug' ? chalk.gray('VERB') :
|
||||||
level === 'info' ? chalk.blue('INFO') :
|
chalk.blue('INFO');
|
||||||
null;
|
|
||||||
const domains = [this.domain].concat(subDomains).map(d => d.color ? chalk.rgb(...convertColor.keyword.rgb(d.color))(d.name) : chalk.white(d.name));
|
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 =
|
const m =
|
||||||
level === 'error' ? chalk.red(message) :
|
level === 'error' ? chalk.red(message) :
|
||||||
level === 'warning' ? chalk.yellow(message) :
|
level === 'warning' ? chalk.yellow(message) :
|
||||||
level === 'success' ? chalk.green(message) :
|
level === 'success' ? chalk.green(message) :
|
||||||
level === 'debug' ? chalk.gray(message) :
|
level === 'debug' ? chalk.gray(message) :
|
||||||
level === 'info' ? message :
|
message;
|
||||||
null;
|
|
||||||
|
|
||||||
let log = `${l} ${worker}\t[${domains.join(' ')}]\t${m}`;
|
let log = `${l} ${worker}\t[${domains.join(' ')}]\t${m}`;
|
||||||
if (envOption.withLogTime) log = chalk.gray(time) + ' ' + log;
|
if (envOption.withLogTime) log = chalk.gray(time) + ' ' + log;
|
||||||
|
@ -84,42 +100,74 @@ export default class Logger {
|
||||||
const send =
|
const send =
|
||||||
level === 'error' ? this.syslogClient.error :
|
level === 'error' ? this.syslogClient.error :
|
||||||
level === 'warning' ? this.syslogClient.warning :
|
level === 'warning' ? this.syslogClient.warning :
|
||||||
level === 'success' ? this.syslogClient.info :
|
this.syslogClient.info;
|
||||||
level === 'debug' ? this.syslogClient.info :
|
|
||||||
level === 'info' ? this.syslogClient.info :
|
|
||||||
null as never;
|
|
||||||
|
|
||||||
send.bind(this.syslogClient)(message).catch(() => {});
|
send.bind(this.syslogClient)(message).catch(() => {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public error(x: string | Error, data?: Record<string, any> = {}, important = false): void { // 実行を継続できない状況で使う
|
/**
|
||||||
if (x instanceof Error) {
|
* Log an error message.
|
||||||
data.e = x;
|
* Use in situations where execution cannot be continued.
|
||||||
this.log('error', x.toString(), data, important);
|
* @param err Error or string containing an error message
|
||||||
} else if (typeof x === 'object') {
|
* @param data Data relating to the error
|
||||||
this.log('error', `${(x as any).message || (x as any).name || x}`, data, important);
|
* @param important Whether this error is important
|
||||||
|
*/
|
||||||
|
public error(err: string | Error, data: Record<string, any> = {}, 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 {
|
} else {
|
||||||
this.log('error', `${x}`, data, important);
|
this.log('error', `${err}`, data, important);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public warn(message: string, data?: Record<string, any> | 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<string, any> | null, important = false): void {
|
||||||
this.log('warning', message, data, important);
|
this.log('warning', message, data, important);
|
||||||
}
|
}
|
||||||
|
|
||||||
public succ(message: string, data?: Record<string, any> | 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<string, any> | null, important = false): void {
|
||||||
this.log('success', message, data, important);
|
this.log('success', message, data, important);
|
||||||
}
|
}
|
||||||
|
|
||||||
public debug(message: string, data?: Record<string, any> | 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<string, any> | null, important = false): void {
|
||||||
if (process.env.NODE_ENV !== 'production' || envOption.verbose) {
|
if (process.env.NODE_ENV !== 'production' || envOption.verbose) {
|
||||||
this.log('debug', message, data, important);
|
this.log('debug', message, data, important);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public info(message: string, data?: Record<string, any> | 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<string, any> | null, important = false): void {
|
||||||
this.log('info', message, data, important);
|
this.log('info', message, data, important);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -266,7 +266,7 @@ export default async (user: { id: User['id']; username: User['username']; host:
|
||||||
incNotesCountOfUser(user);
|
incNotesCountOfUser(user);
|
||||||
|
|
||||||
// Word mute
|
// Word mute
|
||||||
mutedWordsCache.fetch(null).then(us => {
|
mutedWordsCache.fetch('').then(us => {
|
||||||
for (const u of us) {
|
for (const u of us) {
|
||||||
checkWordMute(note, { id: u.userId }, u.mutedWords).then(shouldMute => {
|
checkWordMute(note, { id: u.userId }, u.mutedWords).then(shouldMute => {
|
||||||
if (shouldMute) {
|
if (shouldMute) {
|
||||||
|
|
|
@ -16,7 +16,7 @@ type pushNotificationsTypes = {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Reduce the content of the push message because of the character limit
|
// Reduce the content of the push message because of the character limit
|
||||||
function truncateNotification(notification: Packed<'Notification'>): any {
|
function truncateNotification(notification: Packed<'Notification'>): Record<string, any> {
|
||||||
if (notification.note) {
|
if (notification.note) {
|
||||||
return {
|
return {
|
||||||
...notification,
|
...notification,
|
||||||
|
@ -37,7 +37,7 @@ function truncateNotification(notification: Packed<'Notification'>): any {
|
||||||
return notification;
|
return notification;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function pushNotification<T extends keyof pushNotificationsTypes>(userId: string, type: T, body: pushNotificationsTypes[T]) {
|
export async function pushNotification<T extends keyof pushNotificationsTypes>(userId: string, type: T, body: pushNotificationsTypes[T]): Promise<void> {
|
||||||
const meta = await fetchMeta();
|
const meta = await fetchMeta();
|
||||||
|
|
||||||
// Register key pair information
|
// Register key pair information
|
||||||
|
|
|
@ -36,7 +36,7 @@ export async function getRelayActor(): Promise<ILocalUser> {
|
||||||
return created as ILocalUser;
|
return created as ILocalUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function addRelay(inbox: string) {
|
export async function addRelay(inbox: string): Promise<Relay> {
|
||||||
const relay = await Relays.insert({
|
const relay = await Relays.insert({
|
||||||
id: genId(),
|
id: genId(),
|
||||||
inbox,
|
inbox,
|
||||||
|
@ -51,7 +51,7 @@ export async function addRelay(inbox: string) {
|
||||||
return relay;
|
return relay;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function removeRelay(inbox: string) {
|
export async function removeRelay(inbox: string): Promise<void> {
|
||||||
const relay = await Relays.findOneBy({
|
const relay = await Relays.findOneBy({
|
||||||
inbox,
|
inbox,
|
||||||
});
|
});
|
||||||
|
@ -69,12 +69,12 @@ export async function removeRelay(inbox: string) {
|
||||||
await Relays.delete(relay.id);
|
await Relays.delete(relay.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function listRelay() {
|
export async function listRelay(): Promise<Relay[]> {
|
||||||
const relays = await Relays.find();
|
const relays = await Relays.find();
|
||||||
return relays;
|
return relays;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function relayAccepted(id: string) {
|
export async function relayAccepted(id: string): Promise<string> {
|
||||||
const result = await Relays.update(id, {
|
const result = await Relays.update(id, {
|
||||||
status: 'accepted',
|
status: 'accepted',
|
||||||
});
|
});
|
||||||
|
@ -82,7 +82,7 @@ export async function relayAccepted(id: string) {
|
||||||
return JSON.stringify(result);
|
return JSON.stringify(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function relayRejected(id: string) {
|
export async function relayRejected(id: string): Promise<string> {
|
||||||
const result = await Relays.update(id, {
|
const result = await Relays.update(id, {
|
||||||
status: 'rejected',
|
status: 'rejected',
|
||||||
});
|
});
|
||||||
|
@ -90,11 +90,11 @@ export async function relayRejected(id: string) {
|
||||||
return JSON.stringify(result);
|
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<void> {
|
||||||
if (activity == null) return;
|
if (activity == null) return;
|
||||||
|
|
||||||
const relays = await relaysCache.fetch(null);
|
const relays = await relaysCache.fetch('');
|
||||||
if (relays.length === 0) return;
|
if (relays == null || relays.length === 0) return;
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
//const copy = structuredClone(activity);
|
//const copy = structuredClone(activity);
|
||||||
|
|
|
@ -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,
|
|
||||||
};
|
|
|
@ -1,10 +1,9 @@
|
||||||
import { Not, IsNull } from 'typeorm';
|
|
||||||
import renderDelete from '@/remote/activitypub/renderer/delete.js';
|
import renderDelete from '@/remote/activitypub/renderer/delete.js';
|
||||||
import { renderActivity } from '@/remote/activitypub/renderer/index.js';
|
import { renderActivity } from '@/remote/activitypub/renderer/index.js';
|
||||||
import DeliverManager from '@/remote/activitypub/deliver-manager.js';
|
import DeliverManager from '@/remote/activitypub/deliver-manager.js';
|
||||||
import config from '@/config/index.js';
|
import config from '@/config/index.js';
|
||||||
import { User } from '@/models/entities/user.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';
|
import { publishInternalEvent } from '@/services/stream.js';
|
||||||
|
|
||||||
export async function doPostSuspend(user: { id: User['id']; host: User['host'] }): Promise<void> {
|
export async function doPostSuspend(user: { id: User['id']; host: User['host'] }): Promise<void> {
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { User } from '@/models/entities/user.js';
|
||||||
import { Users, Followings } from '@/models/index.js';
|
import { Users, Followings } from '@/models/index.js';
|
||||||
import { publishInternalEvent } from '@/services/stream.js';
|
import { publishInternalEvent } from '@/services/stream.js';
|
||||||
|
|
||||||
export async function doPostUnsuspend(user: User) {
|
export async function doPostUnsuspend(user: User): Promise<void> {
|
||||||
publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: false });
|
publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: false });
|
||||||
|
|
||||||
if (Users.isLocalUser(user)) {
|
if (Users.isLocalUser(user)) {
|
||||||
|
|
|
@ -16,7 +16,7 @@ export async function updateUsertags(user: User, tags: string[]): Promise<void>
|
||||||
await updateHashtag(user, tag, true, true);
|
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);
|
await updateHashtag(user, tag, true, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,20 @@
|
||||||
import { CacheableLocalUser, CacheableUser, ILocalUser } from '@/models/entities/user.js';
|
import { IsNull } from 'typeorm';
|
||||||
|
import { CacheableLocalUser, ILocalUser, User } from '@/models/entities/user.js';
|
||||||
import { Users } from '@/models/index.js';
|
import { Users } from '@/models/index.js';
|
||||||
import { Cache } from '@/misc/cache.js';
|
import { Cache } from '@/misc/cache.js';
|
||||||
import { subscriber } from '@/db/redis.js';
|
import { subscriber } from '@/db/redis.js';
|
||||||
|
|
||||||
export const userByIdCache = new Cache<CacheableUser>(
|
export const userByIdCache = new Cache<User>(
|
||||||
Infinity,
|
Infinity,
|
||||||
(id) => Users.findOneBy({ id }).then(x => x ?? undefined),
|
async (id) => await Users.findOneBy({ id }) ?? undefined,
|
||||||
);
|
);
|
||||||
export const localUserByNativeTokenCache = new Cache<CacheableLocalUser>(
|
export const localUserByNativeTokenCache = new Cache<CacheableLocalUser>(
|
||||||
Infinity,
|
Infinity,
|
||||||
(token) => Users.findOneBy({ token }).then(x => x ?? undefined),
|
async (token) => await Users.findOneBy({ token, host: IsNull() }) as ILocalUser | null ?? undefined,
|
||||||
);
|
);
|
||||||
export const uriPersonCache = new Cache<CacheableUser>(
|
export const uriPersonCache = new Cache<User>(
|
||||||
Infinity,
|
Infinity,
|
||||||
(uri) => Users.findOneBy({ uri }).then(x => x ?? undefined),
|
async (uri) => await Users.findOneBy({ uri }) ?? undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
subscriber.on('message', async (_, data) => {
|
subscriber.on('message', async (_, data) => {
|
||||||
|
|
20
yarn.lock
20
yarn.lock
|
@ -1678,7 +1678,16 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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
|
version: 1.1.1
|
||||||
resolution: "@types/color-name@npm:1.1.1"
|
resolution: "@types/color-name@npm:1.1.1"
|
||||||
checksum: b71fcad728cc68abcba1d405742134410c8f8eb3c2ef18113b047afca158ad23a4f2c229bcf71a38f4a818dead375c45b20db121d0e69259c2d81e97a740daa6
|
checksum: b71fcad728cc68abcba1d405742134410c8f8eb3c2ef18113b047afca158ad23a4f2c229bcf71a38f4a818dead375c45b20db121d0e69259c2d81e97a740daa6
|
||||||
|
@ -2425,6 +2434,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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":
|
"@types/throttle-debounce@npm:5.0.0":
|
||||||
version: 5.0.0
|
version: 5.0.0
|
||||||
resolution: "@types/throttle-debounce@npm:5.0.0"
|
resolution: "@types/throttle-debounce@npm:5.0.0"
|
||||||
|
@ -3635,6 +3651,7 @@ __metadata:
|
||||||
"@types/bcryptjs": 2.4.2
|
"@types/bcryptjs": 2.4.2
|
||||||
"@types/bull": 3.15.8
|
"@types/bull": 3.15.8
|
||||||
"@types/cbor": 6.0.0
|
"@types/cbor": 6.0.0
|
||||||
|
"@types/color-convert": ^2.0.0
|
||||||
"@types/escape-regexp": 0.0.1
|
"@types/escape-regexp": 0.0.1
|
||||||
"@types/fluent-ffmpeg": 2.1.20
|
"@types/fluent-ffmpeg": 2.1.20
|
||||||
"@types/is-url": 1.2.30
|
"@types/is-url": 1.2.30
|
||||||
|
@ -3671,6 +3688,7 @@ __metadata:
|
||||||
"@types/sinon": ^10.0.13
|
"@types/sinon": ^10.0.13
|
||||||
"@types/sinonjs__fake-timers": 8.1.2
|
"@types/sinonjs__fake-timers": 8.1.2
|
||||||
"@types/speakeasy": 2.0.7
|
"@types/speakeasy": 2.0.7
|
||||||
|
"@types/syslog-pro": ^1.0.0
|
||||||
"@types/tinycolor2": 1.4.3
|
"@types/tinycolor2": 1.4.3
|
||||||
"@types/tmp": 0.2.3
|
"@types/tmp": 0.2.3
|
||||||
"@types/uuid": 8.3.4
|
"@types/uuid": 8.3.4
|
||||||
|
|
Loading…
Reference in a new issue