Merge pull request '[Chore] Partial translating of ActivityPub/Boot code + Formatting' (#9229) from prettykool/calckey:develop into develop
Reviewed-on: https://codeberg.org/calckey/calckey/pulls/9229
This commit is contained in:
commit
b1933d00b9
21 changed files with 132 additions and 129 deletions
|
@ -18,62 +18,62 @@ const ev = new Xev();
|
|||
* Init process
|
||||
*/
|
||||
export default async function() {
|
||||
process.title = `Calckey (${cluster.isPrimary ? 'master' : 'worker'})`;
|
||||
|
||||
if (cluster.isPrimary || envOption.disableClustering) {
|
||||
process.title = `Calckey (${cluster.isPrimary ? 'master' : 'worker'})`;
|
||||
|
||||
if (cluster.isPrimary || envOption.disableClustering) {
|
||||
await masterMain();
|
||||
|
||||
|
||||
if (cluster.isPrimary) {
|
||||
ev.mount();
|
||||
ev.mount();
|
||||
}
|
||||
}
|
||||
|
||||
if (cluster.isWorker || envOption.disableClustering) {
|
||||
}
|
||||
|
||||
if (cluster.isWorker || envOption.disableClustering) {
|
||||
await workerMain();
|
||||
}
|
||||
|
||||
// ユニットテスト時にMisskeyが子プロセスで起動された時のため
|
||||
// それ以外のときは process.send は使えないので弾く
|
||||
if (process.send) {
|
||||
}
|
||||
|
||||
// For when Calckey is started in a child process during unit testing.
|
||||
// Otherwise, process.send cannot be used, so start it.
|
||||
if (process.send) {
|
||||
process.send('ok');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//#region Events
|
||||
|
||||
// Listen new workers
|
||||
cluster.on('fork', worker => {
|
||||
clusterLogger.debug(`Process forked: [${worker.id}]`);
|
||||
clusterLogger.debug(`Process forked: [${worker.id}]`);
|
||||
});
|
||||
|
||||
// Listen online workers
|
||||
cluster.on('online', worker => {
|
||||
clusterLogger.debug(`Process is now online: [${worker.id}]`);
|
||||
clusterLogger.debug(`Process is now online: [${worker.id}]`);
|
||||
});
|
||||
|
||||
// Listen for dying workers
|
||||
cluster.on('exit', worker => {
|
||||
// Replace the dead worker,
|
||||
// we're not sentimental
|
||||
clusterLogger.error(chalk.red(`[${worker.id}] died :(`));
|
||||
cluster.fork();
|
||||
// Replace the dead worker,
|
||||
// we're not sentimental
|
||||
clusterLogger.error(chalk.red(`[${worker.id}] died :(`));
|
||||
cluster.fork();
|
||||
});
|
||||
|
||||
// Display detail of unhandled promise rejection
|
||||
if (!envOption.quiet) {
|
||||
process.on('unhandledRejection', console.dir);
|
||||
process.on('unhandledRejection', console.dir);
|
||||
}
|
||||
|
||||
// Display detail of uncaught exception
|
||||
process.on('uncaughtException', err => {
|
||||
try {
|
||||
try {
|
||||
logger.error(err);
|
||||
} catch { }
|
||||
} catch { }
|
||||
});
|
||||
|
||||
// Dying away...
|
||||
process.on('exit', code => {
|
||||
logger.info(`The process is going to exit with code ${code}`);
|
||||
logger.info(`The process is going to exit with code ${code}`);
|
||||
});
|
||||
|
||||
//#endregion
|
||||
|
|
|
@ -19,7 +19,7 @@ export class Notification {
|
|||
public createdAt: Date;
|
||||
|
||||
/**
|
||||
* 通知の受信者
|
||||
* Notification Recipient ID
|
||||
*/
|
||||
@Index()
|
||||
@Column({
|
||||
|
@ -35,7 +35,7 @@ export class Notification {
|
|||
public notifiee: User | null;
|
||||
|
||||
/**
|
||||
* 通知の送信者(initiator)
|
||||
* Notification sender (initiator)
|
||||
*/
|
||||
@Index()
|
||||
@Column({
|
||||
|
@ -52,19 +52,19 @@ export class Notification {
|
|||
public notifier: User | null;
|
||||
|
||||
/**
|
||||
* 通知の種類。
|
||||
* follow - フォローされた
|
||||
* mention - 投稿で自分が言及された
|
||||
* reply - (自分または自分がWatchしている)投稿が返信された
|
||||
* renote - (自分または自分がWatchしている)投稿がRenoteされた
|
||||
* quote - (自分または自分がWatchしている)投稿が引用Renoteされた
|
||||
* Notification types:
|
||||
* follow - Follow request
|
||||
* mention - User was referenced in a post.
|
||||
* reply - A post that a user made (or was watching) has been replied to.
|
||||
* renote - A post that a user made (or was watching) has been renoted.
|
||||
* quote - A post that a user made (or was watching) has been quoted and renoted.
|
||||
* reaction - (自分または自分がWatchしている)投稿にリアクションされた
|
||||
* pollVote - (自分または自分がWatchしている)投稿のアンケートに投票された
|
||||
* pollEnded - 自分のアンケートもしくは自分が投票したアンケートが終了した
|
||||
* receiveFollowRequest - フォローリクエストされた
|
||||
* followRequestAccepted - 自分の送ったフォローリクエストが承認された
|
||||
* followRequestAccepted - A follow request has been accepted.
|
||||
* groupInvited - グループに招待された
|
||||
* app - アプリ通知
|
||||
* app - App notifications.
|
||||
*/
|
||||
@Index()
|
||||
@Column('enum', {
|
||||
|
@ -74,12 +74,12 @@ export class Notification {
|
|||
public type: typeof notificationTypes[number];
|
||||
|
||||
/**
|
||||
* 通知が読まれたかどうか
|
||||
* Whether the notification was read.
|
||||
*/
|
||||
@Index()
|
||||
@Column('boolean', {
|
||||
default: false,
|
||||
comment: 'Whether the Notification is read.',
|
||||
comment: 'Whether the notification was read.',
|
||||
})
|
||||
public isRead: boolean;
|
||||
|
||||
|
@ -130,7 +130,7 @@ export class Notification {
|
|||
public choice: number | null;
|
||||
|
||||
/**
|
||||
* アプリ通知のbody
|
||||
* App notification body
|
||||
*/
|
||||
@Column('varchar', {
|
||||
length: 2048, nullable: true,
|
||||
|
@ -138,8 +138,8 @@ export class Notification {
|
|||
public customBody: string | null;
|
||||
|
||||
/**
|
||||
* アプリ通知のheader
|
||||
* (省略時はアプリ名で表示されることを期待)
|
||||
* App notification header
|
||||
* (If omitted, it is expected to be displayed with the app name)
|
||||
*/
|
||||
@Column('varchar', {
|
||||
length: 256, nullable: true,
|
||||
|
@ -147,8 +147,8 @@ export class Notification {
|
|||
public customHeader: string | null;
|
||||
|
||||
/**
|
||||
* アプリ通知のicon(URL)
|
||||
* (省略時はアプリアイコンで表示されることを期待)
|
||||
* App notification icon (URL)
|
||||
* (If omitted, it is expected to be displayed as an app icon)
|
||||
*/
|
||||
@Column('varchar', {
|
||||
length: 1024, nullable: true,
|
||||
|
@ -156,7 +156,7 @@ export class Notification {
|
|||
public customIcon: string | null;
|
||||
|
||||
/**
|
||||
* アプリ通知のアプリ(のトークン)
|
||||
* App notification app (token for)
|
||||
*/
|
||||
@Index()
|
||||
@Column({
|
||||
|
|
|
@ -14,7 +14,7 @@ import { Notes } from '@/models/index.js';
|
|||
const logger = apLogger;
|
||||
|
||||
/**
|
||||
* アナウンスアクティビティを捌きます
|
||||
* Handle announcement activities
|
||||
*/
|
||||
export default async function(resolver: Resolver, actor: CacheableRemoteUser, activity: IAnnounce, targetUri: string): Promise<void> {
|
||||
const uri = getApId(activity);
|
||||
|
@ -23,25 +23,25 @@ export default async function(resolver: Resolver, actor: CacheableRemoteUser, ac
|
|||
return;
|
||||
}
|
||||
|
||||
// アナウンス先をブロックしてたら中断
|
||||
// Interrupt if you block the announcement destination
|
||||
const meta = await fetchMeta();
|
||||
if (meta.blockedHosts.includes(extractDbHost(uri))) return;
|
||||
|
||||
const unlock = await getApLock(uri);
|
||||
|
||||
try {
|
||||
// 既に同じURIを持つものが登録されていないかチェック
|
||||
// Check if something with the same URI is already registered
|
||||
const exist = await fetchNote(uri);
|
||||
if (exist) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Announce対象をresolve
|
||||
// Resolve Announce target
|
||||
let renote;
|
||||
try {
|
||||
renote = await resolveNote(targetUri);
|
||||
} catch (e) {
|
||||
// 対象が4xxならスキップ
|
||||
// Skip if target is 4xx
|
||||
if (e instanceof StatusError) {
|
||||
if (e.isClientError) {
|
||||
logger.warn(`Ignored announce target ${targetUri} - ${e.statusCode}`);
|
||||
|
|
|
@ -7,7 +7,7 @@ import { extractDbHost } from '@/misc/convert-host.js';
|
|||
import { StatusError } from '@/misc/fetch.js';
|
||||
|
||||
/**
|
||||
* 投稿作成アクティビティを捌きます
|
||||
* Handle post creation activity
|
||||
*/
|
||||
export default async function(resolver: Resolver, actor: CacheableRemoteUser, note: IObject, silent = false, activity?: ICreate): Promise<string> {
|
||||
const uri = getApId(note);
|
||||
|
|
|
@ -5,45 +5,47 @@ import { toSingle } from '@/prelude/array.js';
|
|||
import { deleteActor } from './actor.js';
|
||||
|
||||
/**
|
||||
* 削除アクティビティを捌きます
|
||||
* Handle delete activity
|
||||
*/
|
||||
export default async (actor: CacheableRemoteUser, activity: IDelete): Promise<string> => {
|
||||
if ('actor' in activity && actor.uri !== activity.actor) {
|
||||
if ('actor' in activity && actor.uri !== activity.actor) {
|
||||
throw new Error('invalid actor');
|
||||
}
|
||||
|
||||
// 削除対象objectのtype
|
||||
let formerType: string | undefined;
|
||||
|
||||
}
|
||||
|
||||
// Type of object to be deleted
|
||||
let formerType: string | undefined;
|
||||
|
||||
if (typeof activity.object === 'string') {
|
||||
// typeが不明だけど、どうせ消えてるのでremote resolveしない
|
||||
formerType = undefined;
|
||||
// The type is unknown, but it has disappeared
|
||||
// anyway, so it does not remote resolve
|
||||
formerType = undefined;
|
||||
} else {
|
||||
const object = activity.object as IObject;
|
||||
if (isTombstone(object)) {
|
||||
const object = activity.object as IObject;
|
||||
if (isTombstone(object)) {
|
||||
formerType = toSingle(object.formerType);
|
||||
} else {
|
||||
} else {
|
||||
formerType = toSingle(object.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const uri = getApId(activity.object);
|
||||
|
||||
// type不明でもactorとobjectが同じならばそれはPersonに違いない
|
||||
if (!formerType && actor.uri === uri) {
|
||||
|
||||
const uri = getApId(activity.object);
|
||||
|
||||
// Even if type is unknown, if actor and object are the same,
|
||||
// it must be `Person`.
|
||||
if (!formerType && actor.uri === uri) {
|
||||
formerType = 'Person';
|
||||
}
|
||||
}
|
||||
|
||||
// それでもなかったらおそらくNote
|
||||
if (!formerType) {
|
||||
// If not, fallback to `Note`.
|
||||
if (!formerType) {
|
||||
formerType = 'Note';
|
||||
}
|
||||
}
|
||||
|
||||
if (validPost.includes(formerType)) {
|
||||
if (validPost.includes(formerType)) {
|
||||
return await deleteNote(actor, uri);
|
||||
} else if (validActor.includes(formerType)) {
|
||||
} else if (validActor.includes(formerType)) {
|
||||
return await deleteActor(actor, uri);
|
||||
} else {
|
||||
} else {
|
||||
return `Unknown type ${formerType}`;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -21,7 +21,7 @@ export default async function(actor: CacheableRemoteUser, uri: string): Promise<
|
|||
if (message == null) return 'message not found';
|
||||
|
||||
if (message.userId !== actor.id) {
|
||||
return '投稿を削除しようとしているユーザーは投稿の作成者ではありません';
|
||||
return 'The user trying to delete the post is not the post author';
|
||||
}
|
||||
|
||||
await deleteMessage(message);
|
||||
|
@ -30,7 +30,7 @@ export default async function(actor: CacheableRemoteUser, uri: string): Promise<
|
|||
}
|
||||
|
||||
if (note.userId !== actor.id) {
|
||||
return '投稿を削除しようとしているユーザーは投稿の作成者ではありません';
|
||||
return 'The user trying to delete the post is not the post author';
|
||||
}
|
||||
|
||||
await deleteNode(actor, note);
|
||||
|
|
|
@ -6,8 +6,9 @@ import { In } from 'typeorm';
|
|||
import { genId } from '@/misc/gen-id.js';
|
||||
|
||||
export default async (actor: CacheableRemoteUser, activity: IFlag): Promise<string> => {
|
||||
// objectは `(User|Note) | (User|Note)[]` だけど、全パターンDBスキーマと対応させられないので
|
||||
// 対象ユーザーは一番最初のユーザー として あとはコメントとして格納する
|
||||
// The object is `(User | Note) | (User | Note) []`, but it cannot be
|
||||
// matched with all patterns of the DB schema, so the target user is the first
|
||||
// user and it is stored as a comment.
|
||||
const uris = getApIds(activity.object);
|
||||
|
||||
const userIds = uris.filter(uri => uri.startsWith(config.url + '/users/')).map(uri => uri.split('/').pop()!);
|
||||
|
|
|
@ -12,7 +12,7 @@ export default async (actor: CacheableRemoteUser, activity: IFollow): Promise<st
|
|||
}
|
||||
|
||||
if (followee.host != null) {
|
||||
return `skip: フォローしようとしているユーザーはローカルユーザーではありません`;
|
||||
return `skip: user you are trying to follow is not a local user`;
|
||||
}
|
||||
|
||||
await follow(actor, followee, activity.id);
|
||||
|
|
|
@ -6,7 +6,7 @@ import { relayRejected } from '@/services/relay.js';
|
|||
import { Users } from '@/models/index.js';
|
||||
|
||||
export default async (actor: CacheableRemoteUser, activity: IFollow): Promise<string> => {
|
||||
// ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある
|
||||
// ※ `activity.actor` must be an existing local user, since `activity` is a follow request thrown from us.
|
||||
|
||||
const dbResolver = new DbResolver();
|
||||
const follower = await dbResolver.getUserFromApId(activity.actor);
|
||||
|
|
|
@ -23,5 +23,5 @@ export default async (actor: CacheableRemoteUser, activity: IAccept): Promise<st
|
|||
return `ok: unfollowed`;
|
||||
}
|
||||
|
||||
return `skip: フォローされていない`;
|
||||
return `skip: skip: not followed`;
|
||||
};
|
||||
|
|
|
@ -13,7 +13,7 @@ export default async (actor: CacheableRemoteUser, activity: IBlock): Promise<str
|
|||
}
|
||||
|
||||
if (blockee.host != null) {
|
||||
return `skip: ブロック解除しようとしているユーザーはローカルユーザーではありません`;
|
||||
return `skip: The user you are trying to unblock is not a local user`;
|
||||
}
|
||||
|
||||
await unblock(await Users.findOneByOrFail({ id: actor.id }), blockee);
|
||||
|
|
|
@ -14,7 +14,7 @@ export default async (actor: CacheableRemoteUser, activity: IFollow): Promise<st
|
|||
}
|
||||
|
||||
if (followee.host != null) {
|
||||
return `skip: フォロー解除しようとしているユーザーはローカルユーザーではありません`;
|
||||
return `skip: The user you are trying to unfollow is not a local user`;
|
||||
}
|
||||
|
||||
const req = await FollowRequests.findOneBy({
|
||||
|
@ -37,5 +37,5 @@ export default async (actor: CacheableRemoteUser, activity: IFollow): Promise<st
|
|||
return `ok: unfollowed`;
|
||||
}
|
||||
|
||||
return `skip: リクエストもフォローもされていない`;
|
||||
return `skip: Not requested or followed`;
|
||||
};
|
||||
|
|
|
@ -6,7 +6,7 @@ import Resolver from '../../resolver.js';
|
|||
import { updatePerson } from '../../models/person.js';
|
||||
|
||||
/**
|
||||
* Updateアクティビティを捌きます
|
||||
* Handler for the Update activity
|
||||
*/
|
||||
export default async (actor: CacheableRemoteUser, activity: IUpdate): Promise<string> => {
|
||||
if ('actor' in activity && actor.uri !== activity.actor) {
|
||||
|
|
|
@ -11,10 +11,10 @@ import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/misc/hard-limits.js';
|
|||
const logger = apLogger;
|
||||
|
||||
/**
|
||||
* Imageを作成します。
|
||||
* create an Image.
|
||||
*/
|
||||
export async function createImage(actor: CacheableRemoteUser, value: any): Promise<DriveFile> {
|
||||
// 投稿者が凍結されていたらスキップ
|
||||
// Skip if author is frozen.
|
||||
if (actor.isSuspended) {
|
||||
throw new Error('actor has been suspended');
|
||||
}
|
||||
|
@ -39,8 +39,8 @@ export async function createImage(actor: CacheableRemoteUser, value: any): Promi
|
|||
});
|
||||
|
||||
if (file.isLink) {
|
||||
// URLが異なっている場合、同じ画像が以前に異なるURLで登録されていたということなので、
|
||||
// URLを更新する
|
||||
// If the URL is different, it means that the same image was previously
|
||||
// registered with a different URL, so update the URL
|
||||
if (file.url !== image.url) {
|
||||
await DriveFiles.update({ id: file.id }, {
|
||||
url: image.url,
|
||||
|
@ -55,14 +55,14 @@ export async function createImage(actor: CacheableRemoteUser, value: any): Promi
|
|||
}
|
||||
|
||||
/**
|
||||
* Imageを解決します。
|
||||
* Resolve Image.
|
||||
*
|
||||
* Misskeyに対象のImageが登録されていればそれを返し、そうでなければ
|
||||
* リモートサーバーからフェッチしてMisskeyに登録しそれを返します。
|
||||
* If the target Image is registered in Calckey, return it, otherwise
|
||||
* Fetch from remote server, register with Calckey and return it.
|
||||
*/
|
||||
export async function resolveImage(actor: CacheableRemoteUser, value: any): Promise<DriveFile> {
|
||||
// TODO
|
||||
|
||||
// リモートサーバーからフェッチしてきて登録
|
||||
// Fetch from remote server and register
|
||||
return await createImage(actor, value);
|
||||
}
|
||||
|
|
|
@ -53,9 +53,9 @@ export function validateNote(object: any, uri: string) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Noteをフェッチします。
|
||||
* Fetch Notes.
|
||||
*
|
||||
* Misskeyに対象のNoteが登録されていればそれを返します。
|
||||
* If the target Note is registered in Calckey, it will be returned.
|
||||
*/
|
||||
export async function fetchNote(object: string | IObject): Promise<Note | null> {
|
||||
const dbResolver = new DbResolver();
|
||||
|
@ -63,7 +63,7 @@ export async function fetchNote(object: string | IObject): Promise<Note | null>
|
|||
}
|
||||
|
||||
/**
|
||||
* Noteを作成します。
|
||||
* Create a Note.
|
||||
*/
|
||||
export async function createNote(value: string | IObject, resolver?: Resolver, silent = false): Promise<Note | null> {
|
||||
if (resolver == null) resolver = new Resolver();
|
||||
|
@ -89,10 +89,10 @@ export async function createNote(value: string | IObject, resolver?: Resolver, s
|
|||
|
||||
logger.info(`Creating the Note: ${note.id}`);
|
||||
|
||||
// 投稿者をフェッチ
|
||||
// Fetch author
|
||||
const actor = await resolvePerson(getOneApId(note.attributedTo), resolver) as CacheableRemoteUser;
|
||||
|
||||
// 投稿者が凍結されていたらスキップ
|
||||
// Skip if author is suspended.
|
||||
if (actor.isSuspended) {
|
||||
throw new Error('actor has been suspended');
|
||||
}
|
||||
|
@ -101,10 +101,10 @@ export async function createNote(value: string | IObject, resolver?: Resolver, s
|
|||
let visibility = noteAudience.visibility;
|
||||
const visibleUsers = noteAudience.visibleUsers;
|
||||
|
||||
// Audience (to, cc) が指定されてなかった場合
|
||||
// If Audience (to, cc) was not specified
|
||||
if (visibility === 'specified' && visibleUsers.length === 0) {
|
||||
if (typeof value === 'string') { // 入力がstringならばresolverでGETが発生している
|
||||
// こちらから匿名GET出来たものならばpublic
|
||||
if (typeof value === 'string') { // If the input is a string, GET occurs in resolver
|
||||
// Public if you can GET anonymously from here
|
||||
visibility = 'public';
|
||||
}
|
||||
}
|
||||
|
@ -114,7 +114,7 @@ export async function createNote(value: string | IObject, resolver?: Resolver, s
|
|||
const apMentions = await extractApMentions(note.tag);
|
||||
const apHashtags = await extractApHashtags(note.tag);
|
||||
|
||||
// 添付ファイル
|
||||
// Attachments
|
||||
// TODO: attachmentは必ずしもImageではない
|
||||
// TODO: attachmentは必ずしも配列ではない
|
||||
// Noteがsensitiveなら添付もsensitiveにする
|
||||
|
@ -127,7 +127,7 @@ export async function createNote(value: string | IObject, resolver?: Resolver, s
|
|||
.filter(image => image != null)
|
||||
: [];
|
||||
|
||||
// リプライ
|
||||
// Reply
|
||||
const reply: Note | null = note.inReplyTo
|
||||
? await resolveNote(note.inReplyTo, resolver).then(x => {
|
||||
if (x == null) {
|
||||
|
@ -153,7 +153,7 @@ export async function createNote(value: string | IObject, resolver?: Resolver, s
|
|||
})
|
||||
: null;
|
||||
|
||||
// 引用
|
||||
// Quote
|
||||
let quote: Note | undefined | null;
|
||||
|
||||
if (note._misskey_quote || note.quoteUrl) {
|
||||
|
@ -196,7 +196,7 @@ export async function createNote(value: string | IObject, resolver?: Resolver, s
|
|||
|
||||
const cw = note.summary === '' ? null : note.summary;
|
||||
|
||||
// テキストのパース
|
||||
// Text parsing
|
||||
let text: string | null = null;
|
||||
if (note.source?.mediaType === 'text/x.misskeymarkdown' && typeof note.source?.content === 'string') {
|
||||
text = note.source.content;
|
||||
|
@ -265,23 +265,23 @@ export async function createNote(value: string | IObject, resolver?: Resolver, s
|
|||
}
|
||||
|
||||
/**
|
||||
* Noteを解決します。
|
||||
* Resolve Note.
|
||||
*
|
||||
* Misskeyに対象のNoteが登録されていればそれを返し、そうでなければ
|
||||
* リモートサーバーからフェッチしてMisskeyに登録しそれを返します。
|
||||
* If the target Note is registered in Calckey, return it, otherwise
|
||||
* Fetch from remote server, register with Calckey and return it.
|
||||
*/
|
||||
export async function resolveNote(value: string | IObject, resolver?: Resolver): Promise<Note | null> {
|
||||
const uri = typeof value === 'string' ? value : value.id;
|
||||
if (uri == null) throw new Error('missing uri');
|
||||
|
||||
// ブロックしてたら中断
|
||||
// Abort if origin host is blocked
|
||||
const meta = await fetchMeta();
|
||||
if (meta.blockedHosts.includes(extractDbHost(uri))) throw new StatusError('host blocked', 451, `host ${extractDbHost(uri)} is blocked`);
|
||||
|
||||
const unlock = await getApLock(uri);
|
||||
|
||||
try {
|
||||
//#region このサーバーに既に登録されていたらそれを返す
|
||||
//#region Returns if already registered with this server
|
||||
const exist = await fetchNote(uri);
|
||||
|
||||
if (exist) {
|
||||
|
@ -293,9 +293,9 @@ export async function resolveNote(value: string | IObject, resolver?: Resolver):
|
|||
throw new StatusError('cannot resolve local note', 400, 'cannot resolve local note');
|
||||
}
|
||||
|
||||
// リモートサーバーからフェッチしてきて登録
|
||||
// ここでuriの代わりに添付されてきたNote Objectが指定されていると、サーバーフェッチを経ずにノートが生成されるが
|
||||
// 添付されてきたNote Objectは偽装されている可能性があるため、常にuriを指定してサーバーフェッチを行う。
|
||||
// Fetch from remote server and register
|
||||
// If the attached `Note` Object is specified here instead of the uri, the note will be generated without going through the server fetch.
|
||||
// Since the attached Note Object may be disguised, always specify the uri and fetch it from the server.
|
||||
return await createNote(uri, resolver, true);
|
||||
} finally {
|
||||
unlock();
|
||||
|
|
|
@ -101,9 +101,9 @@ function validateActor(x: IObject, uri: string): IActor {
|
|||
}
|
||||
|
||||
/**
|
||||
* Personをフェッチします。
|
||||
* Fetch a Person.
|
||||
*
|
||||
* Misskeyに対象のPersonが登録されていればそれを返します。
|
||||
* If the target Person is registered in Calckey, it will be returned.
|
||||
*/
|
||||
export async function fetchPerson(uri: string, resolver?: Resolver): Promise<CacheableUser | null> {
|
||||
if (typeof uri !== 'string') throw new Error('uri is not string');
|
||||
|
@ -111,7 +111,7 @@ export async function fetchPerson(uri: string, resolver?: Resolver): Promise<Cac
|
|||
const cached = uriPersonCache.get(uri);
|
||||
if (cached) return cached;
|
||||
|
||||
// URIがこのサーバーを指しているならデータベースからフェッチ
|
||||
// Fetch from the database if the URI points to this server
|
||||
if (uri.startsWith(config.url + '/')) {
|
||||
const id = uri.split('/').pop();
|
||||
const u = await Users.findOneBy({ id });
|
||||
|
@ -119,7 +119,7 @@ export async function fetchPerson(uri: string, resolver?: Resolver): Promise<Cac
|
|||
return u;
|
||||
}
|
||||
|
||||
//#region このサーバーに既に登録されていたらそれを返す
|
||||
//#region Returns if already registered with this server
|
||||
const exist = await Users.findOneBy({ uri });
|
||||
|
||||
if (exist) {
|
||||
|
@ -132,7 +132,7 @@ export async function fetchPerson(uri: string, resolver?: Resolver): Promise<Cac
|
|||
}
|
||||
|
||||
/**
|
||||
* Personを作成します。
|
||||
* Create Person.
|
||||
*/
|
||||
export async function createPerson(uri: string, resolver?: Resolver): Promise<User> {
|
||||
if (typeof uri !== 'string') throw new Error('uri is not string');
|
||||
|
@ -210,7 +210,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us
|
|||
} catch (e) {
|
||||
// duplicate key error
|
||||
if (isDuplicateKeyValueError(e)) {
|
||||
// /users/@a => /users/:id のように入力がaliasなときにエラーになることがあるのを対応
|
||||
// /users/@a => /users/:id Corresponds to an error that may occur when the input is an alias like
|
||||
const u = await Users.findOneBy({
|
||||
uri: person.id,
|
||||
});
|
||||
|
@ -235,10 +235,10 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us
|
|||
|
||||
usersChart.update(user!, true);
|
||||
|
||||
// ハッシュタグ更新
|
||||
// Hashtag update
|
||||
updateUsertags(user!, tags);
|
||||
|
||||
//#region アバターとヘッダー画像をフェッチ
|
||||
//#region Fetch avatar and header image
|
||||
const [avatar, banner] = await Promise.all([
|
||||
person.icon,
|
||||
person.image,
|
||||
|
@ -260,7 +260,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us
|
|||
user!.bannerId = bannerId;
|
||||
//#endregion
|
||||
|
||||
//#region カスタム絵文字取得
|
||||
//#region Get custom emoji
|
||||
const emojis = await extractEmojis(person.tag || [], host).catch(e => {
|
||||
logger.info(`extractEmojis: ${e}`);
|
||||
return [] as Emoji[];
|
||||
|
|
|
@ -43,10 +43,10 @@ export async function extractPollFromQuestion(source: string | IObject, resolver
|
|||
export async function updateQuestion(value: any, resolver?: Resolver) {
|
||||
const uri = typeof value === 'string' ? value : value.id;
|
||||
|
||||
// URIがこのサーバーを指しているならスキップ
|
||||
// Skip if URI points to this server
|
||||
if (uri.startsWith(config.url + '/')) throw new Error('uri points local');
|
||||
|
||||
//#region このサーバーに既に登録されているか
|
||||
//#region Already registered with this server?
|
||||
const note = await Notes.findOneBy({ uri });
|
||||
if (note == null) throw new Error('Question is not registed');
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import { updatePerson } from './models/person.js';
|
|||
export default async (actor: CacheableRemoteUser, activity: IObject): Promise<void> => {
|
||||
await performActivity(actor, activity);
|
||||
|
||||
// ついでにリモートユーザーの情報が古かったら更新しておく
|
||||
// Update the remote user information if it is out of date
|
||||
if (actor.uri) {
|
||||
if (actor.lastFetchedAt == null || Date.now() - actor.lastFetchedAt.getTime() > 1000 * 60 * 60 * 24) {
|
||||
setImmediate(() => {
|
||||
|
|
|
@ -58,7 +58,7 @@ export async function insertFollowingDoc(followee: { id: User['id']; host: User[
|
|||
followerId: follower.id,
|
||||
});
|
||||
|
||||
// 通知を作成
|
||||
// Create notification that request was accepted.
|
||||
createNotification(follower.id, 'followRequestAccepted', {
|
||||
notifierId: followee.id,
|
||||
});
|
||||
|
|
|
@ -3,8 +3,8 @@ import { User } from '@/models/entities/user.js';
|
|||
import { FollowRequests, Users } from '@/models/index.js';
|
||||
|
||||
/**
|
||||
* 指定したユーザー宛てのフォローリクエストをすべて承認
|
||||
* @param user ユーザー
|
||||
* Approve all follow requests for the specified user
|
||||
* @param user User.
|
||||
*/
|
||||
export default async function(user: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; }) {
|
||||
const requests = await FollowRequests.findBy({
|
||||
|
|
|
@ -11,7 +11,7 @@ export async function doPostSuspend(user: { id: User['id']; host: User['host'] }
|
|||
publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: true });
|
||||
|
||||
if (Users.isLocalUser(user)) {
|
||||
// 知り得る全SharedInboxにDelete配信
|
||||
// Send Delete to all known SharedInboxes
|
||||
const content = renderActivity(renderDelete(`${config.url}/users/${user.id}`, user));
|
||||
|
||||
const queue: string[] = [];
|
||||
|
|
Loading…
Reference in a new issue