Compare commits
36 Commits
2024.5.0-r
...
fix/invali
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d02198417b | ||
![]() |
9366515565 | ||
![]() |
623eb410bb | ||
![]() |
b233444ed6 | ||
![]() |
2b8056a852 | ||
![]() |
ecf7945fe8 | ||
![]() |
cc1ee0106f | ||
![]() |
6078081c33 | ||
![]() |
a59aa20be8 | ||
![]() |
61eec93f4e | ||
![]() |
27d1b7e615 | ||
![]() |
316d192bc0 | ||
![]() |
2eaa3e256f | ||
![]() |
46164f879b | ||
![]() |
374c8791d7 | ||
![]() |
e8f523f00a | ||
![]() |
030082f756 | ||
![]() |
dc55adbaf7 | ||
![]() |
90ba1ca1f9 | ||
![]() |
514a65e453 | ||
![]() |
a3468fd05b | ||
![]() |
97be1a53ad | ||
![]() |
1e007b63aa | ||
![]() |
a0c596b030 | ||
![]() |
eaa85f5aa3 | ||
![]() |
dfeaa1145b | ||
![]() |
0082747237 | ||
![]() |
5b8f8e7087 | ||
![]() |
be11fd7508 | ||
![]() |
ac4a001e9f | ||
![]() |
24d4124ffc | ||
![]() |
eaadd643eb | ||
![]() |
cf670e8a3d | ||
![]() |
e57ce4fa0f | ||
![]() |
44cafbb9f2 | ||
![]() |
f75e46752e |
14
CHANGELOG.md
14
CHANGELOG.md
@@ -1,3 +1,15 @@
|
||||
## Unreleased
|
||||
|
||||
### General
|
||||
-
|
||||
|
||||
### Client
|
||||
-
|
||||
|
||||
### Server
|
||||
-
|
||||
|
||||
|
||||
## 2024.5.0
|
||||
|
||||
### Note
|
||||
@@ -20,6 +32,7 @@
|
||||
- Enhance: Goneを出さずに終了したサーバーへの配信停止を自動的に行うように
|
||||
- もしそのようなサーバーからから配信が届いた場合には自動的に配信を再開します
|
||||
- Enhance: 配信停止の理由を表示するように
|
||||
- Enhance: サーバーのお問い合わせ先URLを設定できるようになりました
|
||||
- Fix: Play作成時に設定した公開範囲が機能していない問題を修正
|
||||
- Fix: 正規化されていない状態のhashtagが連合されてきたhtmlに含まれているとhashtagが正しくhashtagに復元されない問題を修正
|
||||
- Fix: みつけるのアンケート欄にてチャンネルのアンケートが含まれてしまう問題を修正
|
||||
@@ -73,6 +86,7 @@
|
||||
- Fix: 通知をグループ化している際に、人数が正常に表示されないことがある問題を修正
|
||||
- Fix: 連合なしの状態の読み書きができない問題を修正
|
||||
- Fix: `/share` で日本語等を含むurlがurlエンコードされない問題を修正
|
||||
- Fix: ファイルを5つ以上添付してもテキストがないとノートが折りたたまれない問題を修正
|
||||
|
||||
### Server
|
||||
- Enhance: エンドポイント`antennas/update`の必須項目を`antennaId`のみに
|
||||
|
@@ -28,6 +28,10 @@
|
||||
|
||||
## Thanks
|
||||
|
||||
<a href="https://sentry.io/"><img src="https://github.com/misskey-dev/misskey/assets/4439005/98576556-222f-467a-94be-e98dbda1d852" height="30" alt="Sentry" /></a>
|
||||
|
||||
Thanks to [Sentry](https://sentry.io/) for providing the error tracking platform that helps us catch unexpected errors.
|
||||
|
||||
<a href="https://www.chromatic.com/"><img src="https://user-images.githubusercontent.com/321738/84662277-e3db4f80-af1b-11ea-88f5-91d67a5e59f6.png" height="30" alt="Chromatic" /></a>
|
||||
|
||||
Thanks to [Chromatic](https://www.chromatic.com/) for providing the visual testing platform that helps us review UI changes and catch visual regressions.
|
||||
|
12
locales/index.d.ts
vendored
12
locales/index.d.ts
vendored
@@ -3364,6 +3364,10 @@ export interface Locale extends ILocale {
|
||||
* 管理者情報が設定されていません。
|
||||
*/
|
||||
"noMaintainerInformationWarning": string;
|
||||
/**
|
||||
* 問い合わせ先URLが設定されていません。
|
||||
*/
|
||||
"noInquiryUrlWarning": string;
|
||||
/**
|
||||
* Botプロテクションが設定されていません。
|
||||
*/
|
||||
@@ -5471,6 +5475,14 @@ export interface Locale extends ILocale {
|
||||
* 有効にすると、タイムラインがキャッシュされていない場合にDBへ追加で問い合わせを行うフォールバック処理を行います。無効にすると、フォールバック処理を行わないことでさらにサーバーの負荷を軽減することができますが、タイムラインが取得できる範囲に制限が生じます。
|
||||
*/
|
||||
"fanoutTimelineDbFallbackDescription": string;
|
||||
/**
|
||||
* 問い合わせ先URL
|
||||
*/
|
||||
"inquiryUrl": string;
|
||||
/**
|
||||
* サーバー運営者へのお問い合わせフォームのURLや、運営者の連絡先等が記載されたWebページのURLを指定します。
|
||||
*/
|
||||
"inquiryUrlDescription": string;
|
||||
};
|
||||
"_accountMigration": {
|
||||
/**
|
||||
|
@@ -837,6 +837,7 @@ administration: "管理"
|
||||
accounts: "アカウント"
|
||||
switch: "切り替え"
|
||||
noMaintainerInformationWarning: "管理者情報が設定されていません。"
|
||||
noInquiryUrlWarning: "問い合わせ先URLが設定されていません。"
|
||||
noBotProtectionWarning: "Botプロテクションが設定されていません。"
|
||||
configure: "設定する"
|
||||
postToGallery: "ギャラリーへ投稿"
|
||||
@@ -1383,6 +1384,8 @@ _serverSettings:
|
||||
fanoutTimelineDescription: "有効にすると、各種タイムラインを取得する際のパフォーマンスが大幅に向上し、データベースへの負荷を軽減することが可能です。ただし、Redisのメモリ使用量は増加します。サーバーのメモリ容量が少ない場合、または動作が不安定な場合は無効にすることができます。"
|
||||
fanoutTimelineDbFallback: "データベースへのフォールバック"
|
||||
fanoutTimelineDbFallbackDescription: "有効にすると、タイムラインがキャッシュされていない場合にDBへ追加で問い合わせを行うフォールバック処理を行います。無効にすると、フォールバック処理を行わないことでさらにサーバーの負荷を軽減することができますが、タイムラインが取得できる範囲に制限が生じます。"
|
||||
inquiryUrl: "問い合わせ先URL"
|
||||
inquiryUrlDescription: "サーバー運営者へのお問い合わせフォームのURLや、運営者の連絡先等が記載されたWebページのURLを指定します。"
|
||||
|
||||
_accountMigration:
|
||||
moveFrom: "別のアカウントからこのアカウントに移行"
|
||||
|
@@ -316,6 +316,7 @@ selectFile: "选择文件"
|
||||
selectFiles: "选择文件"
|
||||
selectFolder: "选择文件夹"
|
||||
selectFolders: "选择多个文件夹"
|
||||
fileNotSelected: "未选择文件"
|
||||
renameFile: "重命名文件"
|
||||
folderName: "文件夹名称"
|
||||
createFolder: "创建文件夹"
|
||||
@@ -2358,6 +2359,7 @@ _deck:
|
||||
alwaysShowMainColumn: "总是显示主列"
|
||||
columnAlign: "列对齐"
|
||||
addColumn: "添加列"
|
||||
newNoteNotificationSettings: "新帖子通知设定"
|
||||
configureColumn: "列设置"
|
||||
swapLeft: "向左移动"
|
||||
swapRight: "向右移动"
|
||||
|
@@ -316,6 +316,7 @@ selectFile: "選擇檔案"
|
||||
selectFiles: "選擇檔案"
|
||||
selectFolder: "選擇資料夾"
|
||||
selectFolders: "選擇資料夾"
|
||||
fileNotSelected: "尚未選擇檔案"
|
||||
renameFile: "重新命名檔案"
|
||||
folderName: "資料夾名稱"
|
||||
createFolder: "新增資料夾"
|
||||
@@ -471,7 +472,7 @@ retype: "重新輸入"
|
||||
noteOf: "{user}的貼文"
|
||||
quoteAttached: "引用"
|
||||
quoteQuestion: "是否要引用?"
|
||||
attachAsFileQuestion: "剪貼簿的文字較長。請問是否要改成附加檔案呢?"
|
||||
attachAsFileQuestion: "剪貼簿的文字較長。請問是否要將其以文字檔的方式附加呢?"
|
||||
noMessagesYet: "沒有訊息"
|
||||
newMessageExists: "有新的訊息"
|
||||
onlyOneFileCanBeAttached: "只能加入一個附件"
|
||||
@@ -1025,6 +1026,7 @@ thisPostMayBeAnnoyingHome: "發佈到首頁"
|
||||
thisPostMayBeAnnoyingCancel: "退出"
|
||||
thisPostMayBeAnnoyingIgnore: "直接發佈貼文"
|
||||
collapseRenotes: "省略顯示已看過的轉發貼文"
|
||||
collapseRenotesDescription: "將已做過反應和轉發的貼文折疊顯示。"
|
||||
internalServerError: "內部伺服器錯誤"
|
||||
internalServerErrorDescription: "內部伺服器出現意外錯誤。"
|
||||
copyErrorInfo: "複製錯誤資訊"
|
||||
@@ -1241,8 +1243,8 @@ alwaysConfirmFollow: "點擊追隨時總是顯示確認訊息"
|
||||
inquiry: "聯絡我們"
|
||||
_delivery:
|
||||
status: "傳送狀態"
|
||||
stop: "已凍結"
|
||||
resume: "繼續傳送"
|
||||
stop: "停止傳送"
|
||||
resume: "恢復傳送"
|
||||
_type:
|
||||
none: "直播中"
|
||||
manuallySuspended: "手動暫停中"
|
||||
@@ -1373,6 +1375,8 @@ _serverSettings:
|
||||
fanoutTimelineDescription: "如果啟用的話,檢索各個時間軸的性能會顯著提昇,資料庫的負荷也會減少。不過,Redis 的記憶體使用量會增加。如果伺服器的記憶體容量比較少或者運行不穩定,可以停用。"
|
||||
fanoutTimelineDbFallback: "資料庫的回退"
|
||||
fanoutTimelineDbFallbackDescription: "若啟用,在時間軸沒有快取的情況下將執行回退處理以額外查詢資料庫。若停用,可以透過不執行回退處理來進一步減少伺服器的負荷,但會限制可取得的時間軸範圍。"
|
||||
inquiryUrl: "聯絡表單網址"
|
||||
inquiryUrlDescription: "指定伺服器運營者的聯絡表單網址或包含運營者聯絡資訊網頁的網址。"
|
||||
_accountMigration:
|
||||
moveFrom: "從其他帳戶遷移到這個帳戶"
|
||||
moveFromSub: "為另一個帳戶建立別名"
|
||||
@@ -2358,6 +2362,7 @@ _deck:
|
||||
alwaysShowMainColumn: "總是顯示主欄"
|
||||
columnAlign: "對齊欄位"
|
||||
addColumn: "新增欄位"
|
||||
newNoteNotificationSettings: "新貼文通知的設定"
|
||||
configureColumn: "欄位的設定"
|
||||
swapLeft: "向左移動"
|
||||
swapRight: "向右移動"
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"version": "2024.5.0-rc.6",
|
||||
"version": "2024.5.0",
|
||||
"codename": "nasubi",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
16
packages/backend/migration/1717117195275-inquiryUrl.js
Normal file
16
packages/backend/migration/1717117195275-inquiryUrl.js
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export class InquiryUrl1717117195275 {
|
||||
name = 'InquiryUrl1717117195275'
|
||||
|
||||
async up(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "inquiryUrl" character varying(1024)`);
|
||||
}
|
||||
|
||||
async down(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "inquiryUrl"`);
|
||||
}
|
||||
}
|
@@ -67,7 +67,7 @@ export class AnnouncementService {
|
||||
|
||||
@bindThis
|
||||
public async create(values: Partial<MiAnnouncement>, moderator?: MiUser): Promise<{ raw: MiAnnouncement; packed: Packed<'Announcement'> }> {
|
||||
const announcement = await this.announcementsRepository.insert({
|
||||
const announcement = await this.announcementsRepository.insertOne({
|
||||
id: this.idService.gen(),
|
||||
updatedAt: null,
|
||||
title: values.title,
|
||||
@@ -79,7 +79,7 @@ export class AnnouncementService {
|
||||
silence: values.silence,
|
||||
needConfirmationToRead: values.needConfirmationToRead,
|
||||
userId: values.userId,
|
||||
}).then(x => this.announcementsRepository.findOneByOrFail(x.identifiers[0]));
|
||||
});
|
||||
|
||||
const packed = await this.announcementEntityService.pack(announcement);
|
||||
|
||||
|
@@ -55,10 +55,10 @@ export class AvatarDecorationService implements OnApplicationShutdown {
|
||||
|
||||
@bindThis
|
||||
public async create(options: Partial<MiAvatarDecoration>, moderator?: MiUser): Promise<MiAvatarDecoration> {
|
||||
const created = await this.avatarDecorationsRepository.insert({
|
||||
const created = await this.avatarDecorationsRepository.insertOne({
|
||||
id: this.idService.gen(),
|
||||
...options,
|
||||
}).then(x => this.avatarDecorationsRepository.findOneByOrFail(x.identifiers[0]));
|
||||
});
|
||||
|
||||
this.globalEventService.publishInternalEvent('avatarDecorationCreated', created);
|
||||
|
||||
|
@@ -45,13 +45,13 @@ export class ClipService {
|
||||
throw new ClipService.TooManyClipsError();
|
||||
}
|
||||
|
||||
const clip = await this.clipsRepository.insert({
|
||||
const clip = await this.clipsRepository.insertOne({
|
||||
id: this.idService.gen(),
|
||||
userId: me.id,
|
||||
name: name,
|
||||
isPublic: isPublic,
|
||||
description: description,
|
||||
}).then(x => this.clipsRepository.findOneByOrFail(x.identifiers[0]));
|
||||
});
|
||||
|
||||
return clip;
|
||||
}
|
||||
|
@@ -68,7 +68,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
||||
localOnly: boolean;
|
||||
roleIdsThatCanBeUsedThisEmojiAsReaction: MiRole['id'][];
|
||||
}, moderator?: MiUser): Promise<MiEmoji> {
|
||||
const emoji = await this.emojisRepository.insert({
|
||||
const emoji = await this.emojisRepository.insertOne({
|
||||
id: this.idService.gen(),
|
||||
updatedAt: new Date(),
|
||||
name: data.name,
|
||||
@@ -82,7 +82,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
||||
isSensitive: data.isSensitive,
|
||||
localOnly: data.localOnly,
|
||||
roleIdsThatCanBeUsedThisEmojiAsReaction: data.roleIdsThatCanBeUsedThisEmojiAsReaction,
|
||||
}).then(x => this.emojisRepository.findOneByOrFail(x.identifiers[0]));
|
||||
});
|
||||
|
||||
if (data.host == null) {
|
||||
this.localEmojisCache.refresh();
|
||||
@@ -346,10 +346,11 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
||||
@bindThis
|
||||
public async populateEmojis(emojiNames: string[], noteUserHost: string | null): Promise<Record<string, string>> {
|
||||
const emojis = await Promise.all(emojiNames.map(x => this.populateEmoji(x, noteUserHost)));
|
||||
const res = {} as any;
|
||||
const res = {} as Record<string, string>;
|
||||
for (let i = 0; i < emojiNames.length; i++) {
|
||||
if (emojis[i] != null) {
|
||||
res[emojiNames[i]] = emojis[i];
|
||||
const resolvedEmoji = emojis[i];
|
||||
if (resolvedEmoji != null) {
|
||||
res[emojiNames[i]] = resolvedEmoji;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
|
@@ -220,7 +220,7 @@ export class DriveService {
|
||||
file.size = size;
|
||||
file.storedInternal = false;
|
||||
|
||||
return await this.driveFilesRepository.insert(file).then(x => this.driveFilesRepository.findOneByOrFail(x.identifiers[0]));
|
||||
return await this.driveFilesRepository.insertOne(file);
|
||||
} else { // use internal storage
|
||||
const accessKey = randomUUID();
|
||||
const thumbnailAccessKey = 'thumbnail-' + randomUUID();
|
||||
@@ -254,7 +254,7 @@ export class DriveService {
|
||||
file.md5 = hash;
|
||||
file.size = size;
|
||||
|
||||
return await this.driveFilesRepository.insert(file).then(x => this.driveFilesRepository.findOneByOrFail(x.identifiers[0]));
|
||||
return await this.driveFilesRepository.insertOne(file);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -497,20 +497,20 @@ export class DriveService {
|
||||
|
||||
if (user && !force) {
|
||||
// Check if there is a file with the same hash
|
||||
const much = await this.driveFilesRepository.findOneBy({
|
||||
const matched = await this.driveFilesRepository.findOneBy({
|
||||
md5: info.md5,
|
||||
userId: user.id,
|
||||
});
|
||||
|
||||
if (much) {
|
||||
this.registerLogger.info(`file with same hash is found: ${much.id}`);
|
||||
if (sensitive && !much.isSensitive) {
|
||||
if (matched) {
|
||||
this.registerLogger.info(`file with same hash is found: ${matched.id}`);
|
||||
if (sensitive && !matched.isSensitive) {
|
||||
// The file is federated as sensitive for this time, but was federated as non-sensitive before.
|
||||
// Therefore, update the file to sensitive.
|
||||
await this.driveFilesRepository.update({ id: much.id }, { isSensitive: true });
|
||||
much.isSensitive = true;
|
||||
await this.driveFilesRepository.update({ id: matched.id }, { isSensitive: true });
|
||||
matched.isSensitive = true;
|
||||
}
|
||||
return much;
|
||||
return matched;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -615,7 +615,7 @@ export class DriveService {
|
||||
file.type = info.type.mime;
|
||||
file.storedInternal = false;
|
||||
|
||||
file = await this.driveFilesRepository.insert(file).then(x => this.driveFilesRepository.findOneByOrFail(x.identifiers[0]));
|
||||
file = await this.driveFilesRepository.insertOne(file);
|
||||
} catch (err) {
|
||||
// duplicate key error (when already registered)
|
||||
if (isDuplicateKeyValueError(err)) {
|
||||
|
@@ -55,11 +55,11 @@ export class FederatedInstanceService implements OnApplicationShutdown {
|
||||
const index = await this.instancesRepository.findOneBy({ host });
|
||||
|
||||
if (index == null) {
|
||||
const i = await this.instancesRepository.insert({
|
||||
const i = await this.instancesRepository.insertOne({
|
||||
id: this.idService.gen(),
|
||||
host,
|
||||
firstRetrievedAt: new Date(),
|
||||
}).then(x => this.instancesRepository.findOneByOrFail(x.identifiers[0]));
|
||||
});
|
||||
|
||||
this.federatedInstanceCache.set(host, i);
|
||||
return i;
|
||||
|
@@ -154,7 +154,7 @@ export class FetchInstanceMetadataService {
|
||||
throw new Error('No wellknown links');
|
||||
}
|
||||
|
||||
const links = wellknown.links as any[];
|
||||
const links = wellknown.links as ({ rel: string, href: string; })[];
|
||||
|
||||
const link1_0 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/1.0');
|
||||
const link2_0 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/2.0');
|
||||
|
@@ -53,11 +53,11 @@ export class RelayService {
|
||||
|
||||
@bindThis
|
||||
public async addRelay(inbox: string): Promise<MiRelay> {
|
||||
const relay = await this.relaysRepository.insert({
|
||||
const relay = await this.relaysRepository.insertOne({
|
||||
id: this.idService.gen(),
|
||||
inbox,
|
||||
status: 'requesting',
|
||||
}).then(x => this.relaysRepository.findOneByOrFail(x.identifiers[0]));
|
||||
});
|
||||
|
||||
const relayActor = await this.getRelayActor();
|
||||
const follow = await this.apRendererService.renderFollowRelay(relay, relayActor);
|
||||
|
@@ -281,7 +281,7 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit {
|
||||
|
||||
@bindThis
|
||||
private async matched(parentId: MiUser['id'], childId: MiUser['id'], options: { noIrregularRules: boolean; }): Promise<MiReversiGame> {
|
||||
const game = await this.reversiGamesRepository.insert({
|
||||
const game = await this.reversiGamesRepository.insertOne({
|
||||
id: this.idService.gen(),
|
||||
user1Id: parentId,
|
||||
user2Id: childId,
|
||||
@@ -294,10 +294,7 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit {
|
||||
bw: 'random',
|
||||
isLlotheo: false,
|
||||
noIrregularRules: options.noIrregularRules,
|
||||
}).then(x => this.reversiGamesRepository.findOneOrFail({
|
||||
where: { id: x.identifiers[0].id },
|
||||
relations: ['user1', 'user2'],
|
||||
}));
|
||||
}, { relations: ['user1', 'user2'] });
|
||||
this.cacheGame(game);
|
||||
|
||||
const packed = await this.reversiGameEntityService.packDetail(game);
|
||||
|
@@ -471,12 +471,12 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
|
||||
}
|
||||
}
|
||||
|
||||
const created = await this.roleAssignmentsRepository.insert({
|
||||
const created = await this.roleAssignmentsRepository.insertOne({
|
||||
id: this.idService.gen(now),
|
||||
expiresAt: expiresAt,
|
||||
roleId: roleId,
|
||||
userId: userId,
|
||||
}).then(x => this.roleAssignmentsRepository.findOneByOrFail(x.identifiers[0]));
|
||||
});
|
||||
|
||||
this.rolesRepository.update(roleId, {
|
||||
lastUsedAt: new Date(),
|
||||
@@ -558,7 +558,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
|
||||
@bindThis
|
||||
public async create(values: Partial<MiRole>, moderator?: MiUser): Promise<MiRole> {
|
||||
const date = new Date();
|
||||
const created = await this.rolesRepository.insert({
|
||||
const created = await this.rolesRepository.insertOne({
|
||||
id: this.idService.gen(date.getTime()),
|
||||
updatedAt: date,
|
||||
lastUsedAt: date,
|
||||
@@ -576,7 +576,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
|
||||
canEditMembersByModerator: values.canEditMembersByModerator,
|
||||
displayOrder: values.displayOrder,
|
||||
policies: values.policies,
|
||||
}).then(x => this.rolesRepository.findOneByOrFail(x.identifiers[0]));
|
||||
});
|
||||
|
||||
this.globalEventService.publishInternalEvent('roleCreated', created);
|
||||
|
||||
|
@@ -517,7 +517,7 @@ export class UserFollowingService implements OnModuleInit {
|
||||
followerId: follower.id,
|
||||
});
|
||||
|
||||
const followRequest = await this.followRequestsRepository.insert({
|
||||
const followRequest = await this.followRequestsRepository.insertOne({
|
||||
id: this.idService.gen(),
|
||||
followerId: follower.id,
|
||||
followeeId: followee.id,
|
||||
@@ -531,7 +531,7 @@ export class UserFollowingService implements OnModuleInit {
|
||||
followeeHost: followee.host,
|
||||
followeeInbox: this.userEntityService.isRemoteUser(followee) ? followee.inbox : undefined,
|
||||
followeeSharedInbox: this.userEntityService.isRemoteUser(followee) ? followee.sharedInbox : undefined,
|
||||
}).then(x => this.followRequestsRepository.findOneByOrFail(x.identifiers[0]));
|
||||
});
|
||||
|
||||
// Publish receiveRequest event
|
||||
if (this.userEntityService.isLocalUser(followee)) {
|
||||
|
@@ -407,7 +407,7 @@ export class ApNoteService {
|
||||
|
||||
this.logger.info(`register emoji host=${host}, name=${name}`);
|
||||
|
||||
return await this.emojisRepository.insert({
|
||||
return await this.emojisRepository.insertOne({
|
||||
id: this.idService.gen(),
|
||||
host,
|
||||
name,
|
||||
@@ -416,7 +416,7 @@ export class ApNoteService {
|
||||
publicUrl: tag.icon.url,
|
||||
updatedAt: new Date(),
|
||||
aliases: [],
|
||||
}).then(x => this.emojisRepository.findOneByOrFail(x.identifiers[0]));
|
||||
});
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
@@ -14,7 +14,8 @@ import { EntitySchema, LessThan, Between } from 'typeorm';
|
||||
import { dateUTC, isTimeSame, isTimeBefore, subtractTime, addTime } from '@/misc/prelude/time.js';
|
||||
import type Logger from '@/logger.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import type { Repository, DataSource } from 'typeorm';
|
||||
import { MiRepository, miRepository } from '@/models/_.js';
|
||||
import type { DataSource, Repository } from 'typeorm';
|
||||
|
||||
const COLUMN_PREFIX = '___' as const;
|
||||
const UNIQUE_TEMP_COLUMN_PREFIX = 'unique_temp___' as const;
|
||||
@@ -145,10 +146,10 @@ export default abstract class Chart<T extends Schema> {
|
||||
group: string | null;
|
||||
}[] = [];
|
||||
// ↓にしたいけどfindOneとかで型エラーになる
|
||||
//private repositoryForHour: Repository<RawRecord<T>>;
|
||||
//private repositoryForDay: Repository<RawRecord<T>>;
|
||||
private repositoryForHour: Repository<{ id: number; group?: string | null; date: number; }>;
|
||||
private repositoryForDay: Repository<{ id: number; group?: string | null; date: number; }>;
|
||||
//private repositoryForHour: Repository<RawRecord<T>> & MiRepository<RawRecord<T>>;
|
||||
//private repositoryForDay: Repository<RawRecord<T>> & MiRepository<RawRecord<T>>;
|
||||
private repositoryForHour: Repository<{ id: number; group?: string | null; date: number; }> & MiRepository<{ id: number; group?: string | null; date: number; }>;
|
||||
private repositoryForDay: Repository<{ id: number; group?: string | null; date: number; }> & MiRepository<{ id: number; group?: string | null; date: number; }>;
|
||||
|
||||
/**
|
||||
* 1日に一回程度実行されれば良いような計算処理を入れる(主にCASCADE削除などアプリケーション側で感知できない変動によるズレの修正用)
|
||||
@@ -211,6 +212,10 @@ export default abstract class Chart<T extends Schema> {
|
||||
} {
|
||||
const createEntity = (span: 'hour' | 'day'): EntitySchema => new EntitySchema({
|
||||
name:
|
||||
span === 'hour' ? `ChartX${name}` :
|
||||
span === 'day' ? `ChartDayX${name}` :
|
||||
new Error('not happen') as never,
|
||||
tableName:
|
||||
span === 'hour' ? `__chart__${camelToSnake(name)}` :
|
||||
span === 'day' ? `__chart_day__${camelToSnake(name)}` :
|
||||
new Error('not happen') as never,
|
||||
@@ -271,8 +276,8 @@ export default abstract class Chart<T extends Schema> {
|
||||
this.logger = logger;
|
||||
|
||||
const { hour, day } = Chart.schemaToEntity(name, schema, grouped);
|
||||
this.repositoryForHour = db.getRepository<{ id: number; group?: string | null; date: number; }>(hour);
|
||||
this.repositoryForDay = db.getRepository<{ id: number; group?: string | null; date: number; }>(day);
|
||||
this.repositoryForHour = db.getRepository<{ id: number; group?: string | null; date: number; }>(hour).extend(miRepository as MiRepository<{ id: number; group?: string | null; date: number; }>);
|
||||
this.repositoryForDay = db.getRepository<{ id: number; group?: string | null; date: number; }>(day).extend(miRepository as MiRepository<{ id: number; group?: string | null; date: number; }>);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
@@ -387,11 +392,11 @@ export default abstract class Chart<T extends Schema> {
|
||||
}
|
||||
|
||||
// 新規ログ挿入
|
||||
log = await repository.insert({
|
||||
log = await repository.insertOne({
|
||||
date: date,
|
||||
...(group ? { group: group } : {}),
|
||||
...columns,
|
||||
}).then(x => repository.findOneByOrFail(x.identifiers[0])) as RawRecord<T>;
|
||||
}) as RawRecord<T>;
|
||||
|
||||
this.logger.info(`${this.name + (group ? `:${group}` : '')}(${span}): New commit created`);
|
||||
|
||||
|
@@ -10,6 +10,8 @@ import { awaitAll } from '@/misc/prelude/await-all.js';
|
||||
import type { MiAbuseUserReport } from '@/models/AbuseUserReport.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { isNotNull } from '@/misc/is-not-null.js';
|
||||
import type { Packed } from '@/misc/json-schema.js';
|
||||
import { UserEntityService } from './UserEntityService.js';
|
||||
|
||||
@Injectable()
|
||||
@@ -26,6 +28,11 @@ export class AbuseUserReportEntityService {
|
||||
@bindThis
|
||||
public async pack(
|
||||
src: MiAbuseUserReport['id'] | MiAbuseUserReport,
|
||||
hint?: {
|
||||
packedReporter?: Packed<'UserDetailedNotMe'>,
|
||||
packedTargetUser?: Packed<'UserDetailedNotMe'>,
|
||||
packedAssignee?: Packed<'UserDetailedNotMe'>,
|
||||
},
|
||||
) {
|
||||
const report = typeof src === 'object' ? src : await this.abuseUserReportsRepository.findOneByOrFail({ id: src });
|
||||
|
||||
@@ -37,13 +44,13 @@ export class AbuseUserReportEntityService {
|
||||
reporterId: report.reporterId,
|
||||
targetUserId: report.targetUserId,
|
||||
assigneeId: report.assigneeId,
|
||||
reporter: this.userEntityService.pack(report.reporter ?? report.reporterId, null, {
|
||||
reporter: hint?.packedReporter ?? this.userEntityService.pack(report.reporter ?? report.reporterId, null, {
|
||||
schema: 'UserDetailedNotMe',
|
||||
}),
|
||||
targetUser: this.userEntityService.pack(report.targetUser ?? report.targetUserId, null, {
|
||||
targetUser: hint?.packedTargetUser ?? this.userEntityService.pack(report.targetUser ?? report.targetUserId, null, {
|
||||
schema: 'UserDetailedNotMe',
|
||||
}),
|
||||
assignee: report.assigneeId ? this.userEntityService.pack(report.assignee ?? report.assigneeId, null, {
|
||||
assignee: report.assigneeId ? hint?.packedAssignee ?? this.userEntityService.pack(report.assignee ?? report.assigneeId, null, {
|
||||
schema: 'UserDetailedNotMe',
|
||||
}) : null,
|
||||
forwarded: report.forwarded,
|
||||
@@ -51,9 +58,24 @@ export class AbuseUserReportEntityService {
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public packMany(
|
||||
reports: any[],
|
||||
public async packMany(
|
||||
reports: MiAbuseUserReport[],
|
||||
) {
|
||||
return Promise.all(reports.map(x => this.pack(x)));
|
||||
const _reporters = reports.map(({ reporter, reporterId }) => reporter ?? reporterId);
|
||||
const _targetUsers = reports.map(({ targetUser, targetUserId }) => targetUser ?? targetUserId);
|
||||
const _assignees = reports.map(({ assignee, assigneeId }) => assignee ?? assigneeId).filter(isNotNull);
|
||||
const _userMap = await this.userEntityService.packMany(
|
||||
[..._reporters, ..._targetUsers, ..._assignees],
|
||||
null,
|
||||
{ schema: 'UserDetailedNotMe' },
|
||||
).then(users => new Map(users.map(u => [u.id, u])));
|
||||
return Promise.all(
|
||||
reports.map(report => {
|
||||
const packedReporter = _userMap.get(report.reporterId);
|
||||
const packedTargetUser = _userMap.get(report.targetUserId);
|
||||
const packedAssignee = report.assigneeId != null ? _userMap.get(report.assigneeId) : undefined;
|
||||
return this.pack(report, { packedReporter, packedTargetUser, packedAssignee });
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -43,6 +43,7 @@ export class AntennaEntityService {
|
||||
withFile: antenna.withFile,
|
||||
isActive: antenna.isActive,
|
||||
hasUnreadNote: false, // TODO
|
||||
notify: false, // 後方互換性のため
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@@ -29,6 +29,9 @@ export class BlockingEntityService {
|
||||
public async pack(
|
||||
src: MiBlocking['id'] | MiBlocking,
|
||||
me?: { id: MiUser['id'] } | null | undefined,
|
||||
hint?: {
|
||||
blockee?: Packed<'UserDetailedNotMe'>,
|
||||
},
|
||||
): Promise<Packed<'Blocking'>> {
|
||||
const blocking = typeof src === 'object' ? src : await this.blockingsRepository.findOneByOrFail({ id: src });
|
||||
|
||||
@@ -36,17 +39,20 @@ export class BlockingEntityService {
|
||||
id: blocking.id,
|
||||
createdAt: this.idService.parse(blocking.id).date.toISOString(),
|
||||
blockeeId: blocking.blockeeId,
|
||||
blockee: this.userEntityService.pack(blocking.blockeeId, me, {
|
||||
blockee: hint?.blockee ?? this.userEntityService.pack(blocking.blockeeId, me, {
|
||||
schema: 'UserDetailedNotMe',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public packMany(
|
||||
blockings: any[],
|
||||
public async packMany(
|
||||
blockings: MiBlocking[],
|
||||
me: { id: MiUser['id'] },
|
||||
) {
|
||||
return Promise.all(blockings.map(x => this.pack(x, me)));
|
||||
const _blockees = blockings.map(({ blockee, blockeeId }) => blockee ?? blockeeId);
|
||||
const _userMap = await this.userEntityService.packMany(_blockees, me, { schema: 'UserDetailedNotMe' })
|
||||
.then(users => new Map(users.map(u => [u.id, u])));
|
||||
return Promise.all(blockings.map(blocking => this.pack(blocking, me, { blockee: _userMap.get(blocking.blockeeId) })));
|
||||
}
|
||||
}
|
||||
|
@@ -35,6 +35,9 @@ export class ClipEntityService {
|
||||
public async pack(
|
||||
src: MiClip['id'] | MiClip,
|
||||
me?: { id: MiUser['id'] } | null | undefined,
|
||||
hint?: {
|
||||
packedUser?: Packed<'UserLite'>
|
||||
},
|
||||
): Promise<Packed<'Clip'>> {
|
||||
const meId = me ? me.id : null;
|
||||
const clip = typeof src === 'object' ? src : await this.clipsRepository.findOneByOrFail({ id: src });
|
||||
@@ -44,7 +47,7 @@ export class ClipEntityService {
|
||||
createdAt: this.idService.parse(clip.id).date.toISOString(),
|
||||
lastClippedAt: clip.lastClippedAt ? clip.lastClippedAt.toISOString() : null,
|
||||
userId: clip.userId,
|
||||
user: this.userEntityService.pack(clip.user ?? clip.userId),
|
||||
user: hint?.packedUser ?? this.userEntityService.pack(clip.user ?? clip.userId),
|
||||
name: clip.name,
|
||||
description: clip.description,
|
||||
isPublic: clip.isPublic,
|
||||
@@ -55,11 +58,14 @@ export class ClipEntityService {
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public packMany(
|
||||
public async packMany(
|
||||
clips: MiClip[],
|
||||
me?: { id: MiUser['id'] } | null | undefined,
|
||||
) {
|
||||
return Promise.all(clips.map(x => this.pack(x, me)));
|
||||
const _users = clips.map(({ user, userId }) => user ?? userId);
|
||||
const _userMap = await this.userEntityService.packMany(_users, me)
|
||||
.then(users => new Map(users.map(u => [u.id, u])));
|
||||
return Promise.all(clips.map(clip => this.pack(clip, me, { packedUser: _userMap.get(clip.userId) })));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -222,6 +222,9 @@ export class DriveFileEntityService {
|
||||
public async packNullable(
|
||||
src: MiDriveFile['id'] | MiDriveFile,
|
||||
options?: PackOptions,
|
||||
hint?: {
|
||||
packedUser?: Packed<'UserLite'>
|
||||
},
|
||||
): Promise<Packed<'DriveFile'> | null> {
|
||||
const opts = Object.assign({
|
||||
detail: false,
|
||||
@@ -249,7 +252,7 @@ export class DriveFileEntityService {
|
||||
detail: true,
|
||||
}) : null,
|
||||
userId: file.userId,
|
||||
user: (opts.withUser && file.userId) ? this.userEntityService.pack(file.userId) : null,
|
||||
user: (opts.withUser && file.userId) ? hint?.packedUser ?? this.userEntityService.pack(file.userId) : null,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -258,7 +261,10 @@ export class DriveFileEntityService {
|
||||
files: MiDriveFile[],
|
||||
options?: PackOptions,
|
||||
): Promise<Packed<'DriveFile'>[]> {
|
||||
const items = await Promise.all(files.map(f => this.packNullable(f, options)));
|
||||
const _user = files.map(({ user, userId }) => user ?? userId).filter(isNotNull);
|
||||
const _userMap = await this.userEntityService.packMany(_user)
|
||||
.then(users => new Map(users.map(user => [user.id, user])));
|
||||
const items = await Promise.all(files.map(f => this.packNullable(f, options, f.userId ? { packedUser: _userMap.get(f.userId) } : {})));
|
||||
return items.filter(isNotNull);
|
||||
}
|
||||
|
||||
|
@@ -33,6 +33,9 @@ export class FlashEntityService {
|
||||
public async pack(
|
||||
src: MiFlash['id'] | MiFlash,
|
||||
me?: { id: MiUser['id'] } | null | undefined,
|
||||
hint?: {
|
||||
packedUser?: Packed<'UserLite'>
|
||||
},
|
||||
): Promise<Packed<'Flash'>> {
|
||||
const meId = me ? me.id : null;
|
||||
const flash = typeof src === 'object' ? src : await this.flashsRepository.findOneByOrFail({ id: src });
|
||||
@@ -42,7 +45,7 @@ export class FlashEntityService {
|
||||
createdAt: this.idService.parse(flash.id).date.toISOString(),
|
||||
updatedAt: flash.updatedAt.toISOString(),
|
||||
userId: flash.userId,
|
||||
user: this.userEntityService.pack(flash.user ?? flash.userId, me), // { schema: 'UserDetailed' } すると無限ループするので注意
|
||||
user: hint?.packedUser ?? this.userEntityService.pack(flash.user ?? flash.userId, me), // { schema: 'UserDetailed' } すると無限ループするので注意
|
||||
title: flash.title,
|
||||
summary: flash.summary,
|
||||
script: flash.script,
|
||||
@@ -52,11 +55,14 @@ export class FlashEntityService {
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public packMany(
|
||||
flashs: MiFlash[],
|
||||
public async packMany(
|
||||
flashes: MiFlash[],
|
||||
me?: { id: MiUser['id'] } | null | undefined,
|
||||
) {
|
||||
return Promise.all(flashs.map(x => this.pack(x, me)));
|
||||
const _users = flashes.map(({ user, userId }) => user ?? userId);
|
||||
const _userMap = await this.userEntityService.packMany(_users, me)
|
||||
.then(users => new Map(users.map(u => [u.id, u])));
|
||||
return Promise.all(flashes.map(flash => this.pack(flash, me, { packedUser: _userMap.get(flash.userId) })));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -10,6 +10,7 @@ import type { } from '@/models/Blocking.js';
|
||||
import type { MiUser } from '@/models/User.js';
|
||||
import type { MiFollowRequest } from '@/models/FollowRequest.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import type { Packed } from '@/misc/json-schema.js';
|
||||
import { UserEntityService } from './UserEntityService.js';
|
||||
|
||||
@Injectable()
|
||||
@@ -26,14 +27,36 @@ export class FollowRequestEntityService {
|
||||
public async pack(
|
||||
src: MiFollowRequest['id'] | MiFollowRequest,
|
||||
me?: { id: MiUser['id'] } | null | undefined,
|
||||
hint?: {
|
||||
packedFollower?: Packed<'UserLite'>,
|
||||
packedFollowee?: Packed<'UserLite'>,
|
||||
},
|
||||
) {
|
||||
const request = typeof src === 'object' ? src : await this.followRequestsRepository.findOneByOrFail({ id: src });
|
||||
|
||||
return {
|
||||
id: request.id,
|
||||
follower: await this.userEntityService.pack(request.followerId, me),
|
||||
followee: await this.userEntityService.pack(request.followeeId, me),
|
||||
follower: hint?.packedFollower ?? await this.userEntityService.pack(request.followerId, me),
|
||||
followee: hint?.packedFollowee ?? await this.userEntityService.pack(request.followeeId, me),
|
||||
};
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async packMany(
|
||||
requests: MiFollowRequest[],
|
||||
me?: { id: MiUser['id'] } | null | undefined,
|
||||
) {
|
||||
const _followers = requests.map(({ follower, followerId }) => follower ?? followerId);
|
||||
const _followees = requests.map(({ followee, followeeId }) => followee ?? followeeId);
|
||||
const _userMap = await this.userEntityService.packMany([..._followers, ..._followees], me)
|
||||
.then(users => new Map(users.map(u => [u.id, u])));
|
||||
return Promise.all(
|
||||
requests.map(req => {
|
||||
const packedFollower = _userMap.get(req.followerId);
|
||||
const packedFollowee = _userMap.get(req.followeeId);
|
||||
return this.pack(req, me, { packedFollower, packedFollowee });
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -78,6 +78,10 @@ export class FollowingEntityService {
|
||||
populateFollowee?: boolean;
|
||||
populateFollower?: boolean;
|
||||
},
|
||||
hint?: {
|
||||
packedFollowee?: Packed<'UserDetailedNotMe'>,
|
||||
packedFollower?: Packed<'UserDetailedNotMe'>,
|
||||
},
|
||||
): Promise<Packed<'Following'>> {
|
||||
const following = typeof src === 'object' ? src : await this.followingsRepository.findOneByOrFail({ id: src });
|
||||
|
||||
@@ -88,25 +92,35 @@ export class FollowingEntityService {
|
||||
createdAt: this.idService.parse(following.id).date.toISOString(),
|
||||
followeeId: following.followeeId,
|
||||
followerId: following.followerId,
|
||||
followee: opts.populateFollowee ? this.userEntityService.pack(following.followee ?? following.followeeId, me, {
|
||||
followee: opts.populateFollowee ? hint?.packedFollowee ?? this.userEntityService.pack(following.followee ?? following.followeeId, me, {
|
||||
schema: 'UserDetailedNotMe',
|
||||
}) : undefined,
|
||||
follower: opts.populateFollower ? this.userEntityService.pack(following.follower ?? following.followerId, me, {
|
||||
follower: opts.populateFollower ? hint?.packedFollower ?? this.userEntityService.pack(following.follower ?? following.followerId, me, {
|
||||
schema: 'UserDetailedNotMe',
|
||||
}) : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public packMany(
|
||||
followings: any[],
|
||||
public async packMany(
|
||||
followings: MiFollowing[],
|
||||
me?: { id: MiUser['id'] } | null | undefined,
|
||||
opts?: {
|
||||
populateFollowee?: boolean;
|
||||
populateFollower?: boolean;
|
||||
},
|
||||
) {
|
||||
return Promise.all(followings.map(x => this.pack(x, me, opts)));
|
||||
const _followees = opts?.populateFollowee ? followings.map(({ followee, followeeId }) => followee ?? followeeId) : [];
|
||||
const _followers = opts?.populateFollower ? followings.map(({ follower, followerId }) => follower ?? followerId) : [];
|
||||
const _userMap = await this.userEntityService.packMany([..._followees, ..._followers], me, { schema: 'UserDetailedNotMe' })
|
||||
.then(users => new Map(users.map(u => [u.id, u])));
|
||||
return Promise.all(
|
||||
followings.map(following => {
|
||||
const packedFollowee = opts?.populateFollowee ? _userMap.get(following.followeeId) : undefined;
|
||||
const packedFollower = opts?.populateFollower ? _userMap.get(following.followerId) : undefined;
|
||||
return this.pack(following, me, opts, { packedFollowee, packedFollower });
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -35,6 +35,9 @@ export class GalleryPostEntityService {
|
||||
public async pack(
|
||||
src: MiGalleryPost['id'] | MiGalleryPost,
|
||||
me?: { id: MiUser['id'] } | null | undefined,
|
||||
hint?: {
|
||||
packedUser?: Packed<'UserLite'>
|
||||
},
|
||||
): Promise<Packed<'GalleryPost'>> {
|
||||
const meId = me ? me.id : null;
|
||||
const post = typeof src === 'object' ? src : await this.galleryPostsRepository.findOneByOrFail({ id: src });
|
||||
@@ -44,7 +47,7 @@ export class GalleryPostEntityService {
|
||||
createdAt: this.idService.parse(post.id).date.toISOString(),
|
||||
updatedAt: post.updatedAt.toISOString(),
|
||||
userId: post.userId,
|
||||
user: this.userEntityService.pack(post.user ?? post.userId, me),
|
||||
user: hint?.packedUser ?? this.userEntityService.pack(post.user ?? post.userId, me),
|
||||
title: post.title,
|
||||
description: post.description,
|
||||
fileIds: post.fileIds,
|
||||
@@ -58,11 +61,14 @@ export class GalleryPostEntityService {
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public packMany(
|
||||
public async packMany(
|
||||
posts: MiGalleryPost[],
|
||||
me?: { id: MiUser['id'] } | null | undefined,
|
||||
) {
|
||||
return Promise.all(posts.map(x => this.pack(x, me)));
|
||||
const _users = posts.map(({ user, userId }) => user ?? userId);
|
||||
const _userMap = await this.userEntityService.packMany(_users, me)
|
||||
.then(users => new Map(users.map(u => [u.id, u])));
|
||||
return Promise.all(posts.map(post => this.pack(post, me, { packedUser: _userMap.get(post.userId) })));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -12,6 +12,7 @@ import type { MiUser } from '@/models/User.js';
|
||||
import type { MiRegistrationTicket } from '@/models/RegistrationTicket.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { isNotNull } from '@/misc/is-not-null.js';
|
||||
import { UserEntityService } from './UserEntityService.js';
|
||||
|
||||
@Injectable()
|
||||
@@ -29,6 +30,10 @@ export class InviteCodeEntityService {
|
||||
public async pack(
|
||||
src: MiRegistrationTicket['id'] | MiRegistrationTicket,
|
||||
me?: { id: MiUser['id'] } | null | undefined,
|
||||
hints?: {
|
||||
packedCreatedBy?: Packed<'UserLite'>,
|
||||
packedUsedBy?: Packed<'UserLite'>,
|
||||
},
|
||||
): Promise<Packed<'InviteCode'>> {
|
||||
const target = typeof src === 'object' ? src : await this.registrationTicketsRepository.findOneOrFail({
|
||||
where: {
|
||||
@@ -42,18 +47,28 @@ export class InviteCodeEntityService {
|
||||
code: target.code,
|
||||
expiresAt: target.expiresAt ? target.expiresAt.toISOString() : null,
|
||||
createdAt: this.idService.parse(target.id).date.toISOString(),
|
||||
createdBy: target.createdBy ? await this.userEntityService.pack(target.createdBy, me) : null,
|
||||
usedBy: target.usedBy ? await this.userEntityService.pack(target.usedBy, me) : null,
|
||||
createdBy: target.createdBy ? hints?.packedCreatedBy ?? await this.userEntityService.pack(target.createdBy, me) : null,
|
||||
usedBy: target.usedBy ? hints?.packedUsedBy ?? await this.userEntityService.pack(target.usedBy, me) : null,
|
||||
usedAt: target.usedAt ? target.usedAt.toISOString() : null,
|
||||
used: !!target.usedAt,
|
||||
});
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public packMany(
|
||||
targets: any[],
|
||||
public async packMany(
|
||||
tickets: MiRegistrationTicket[],
|
||||
me: { id: MiUser['id'] },
|
||||
) {
|
||||
return Promise.all(targets.map(x => this.pack(x, me)));
|
||||
const _createdBys = tickets.map(({ createdBy, createdById }) => createdBy ?? createdById).filter(isNotNull);
|
||||
const _usedBys = tickets.map(({ usedBy, usedById }) => usedBy ?? usedById).filter(isNotNull);
|
||||
const _userMap = await this.userEntityService.packMany([..._createdBys, ..._usedBys], me)
|
||||
.then(users => new Map(users.map(u => [u.id, u])));
|
||||
return Promise.all(
|
||||
tickets.map(ticket => {
|
||||
const packedCreatedBy = ticket.createdById != null ? _userMap.get(ticket.createdById) : undefined;
|
||||
const packedUsedBy = ticket.usedById != null ? _userMap.get(ticket.usedById) : undefined;
|
||||
return this.pack(ticket, me, { packedCreatedBy, packedUsedBy });
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -67,6 +67,7 @@ export class MetaEntityService {
|
||||
feedbackUrl: instance.feedbackUrl,
|
||||
impressumUrl: instance.impressumUrl,
|
||||
privacyPolicyUrl: instance.privacyPolicyUrl,
|
||||
inquiryUrl: instance.inquiryUrl,
|
||||
disableRegistration: instance.disableRegistration,
|
||||
emailRequiredForSignup: instance.emailRequiredForSignup,
|
||||
enableHcaptcha: instance.enableHcaptcha,
|
||||
|
@@ -8,9 +8,10 @@ import { DI } from '@/di-symbols.js';
|
||||
import type { ModerationLogsRepository } from '@/models/_.js';
|
||||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
||||
import type { } from '@/models/Blocking.js';
|
||||
import type { MiModerationLog } from '@/models/ModerationLog.js';
|
||||
import { MiModerationLog } from '@/models/ModerationLog.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import type { Packed } from '@/misc/json-schema.js';
|
||||
import { UserEntityService } from './UserEntityService.js';
|
||||
|
||||
@Injectable()
|
||||
@@ -27,6 +28,9 @@ export class ModerationLogEntityService {
|
||||
@bindThis
|
||||
public async pack(
|
||||
src: MiModerationLog['id'] | MiModerationLog,
|
||||
hint?: {
|
||||
packedUser?: Packed<'UserDetailedNotMe'>,
|
||||
},
|
||||
) {
|
||||
const log = typeof src === 'object' ? src : await this.moderationLogsRepository.findOneByOrFail({ id: src });
|
||||
|
||||
@@ -36,17 +40,20 @@ export class ModerationLogEntityService {
|
||||
type: log.type,
|
||||
info: log.info,
|
||||
userId: log.userId,
|
||||
user: this.userEntityService.pack(log.user ?? log.userId, null, {
|
||||
user: hint?.packedUser ?? this.userEntityService.pack(log.user ?? log.userId, null, {
|
||||
schema: 'UserDetailedNotMe',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public packMany(
|
||||
reports: any[],
|
||||
public async packMany(
|
||||
reports: MiModerationLog[],
|
||||
) {
|
||||
return Promise.all(reports.map(x => this.pack(x)));
|
||||
const _users = reports.map(({ user, userId }) => user ?? userId);
|
||||
const _userMap = await this.userEntityService.packMany(_users, null, { schema: 'UserDetailedNotMe' })
|
||||
.then(users => new Map(users.map(u => [u.id, u])));
|
||||
return Promise.all(reports.map(report => this.pack(report, { packedUser: _userMap.get(report.userId) })));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -30,6 +30,9 @@ export class MutingEntityService {
|
||||
public async pack(
|
||||
src: MiMuting['id'] | MiMuting,
|
||||
me?: { id: MiUser['id'] } | null | undefined,
|
||||
hints?: {
|
||||
packedMutee?: Packed<'UserDetailedNotMe'>,
|
||||
},
|
||||
): Promise<Packed<'Muting'>> {
|
||||
const muting = typeof src === 'object' ? src : await this.mutingsRepository.findOneByOrFail({ id: src });
|
||||
|
||||
@@ -38,18 +41,21 @@ export class MutingEntityService {
|
||||
createdAt: this.idService.parse(muting.id).date.toISOString(),
|
||||
expiresAt: muting.expiresAt ? muting.expiresAt.toISOString() : null,
|
||||
muteeId: muting.muteeId,
|
||||
mutee: this.userEntityService.pack(muting.muteeId, me, {
|
||||
mutee: hints?.packedMutee ?? this.userEntityService.pack(muting.muteeId, me, {
|
||||
schema: 'UserDetailedNotMe',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public packMany(
|
||||
mutings: any[],
|
||||
public async packMany(
|
||||
mutings: MiMuting[],
|
||||
me: { id: MiUser['id'] },
|
||||
) {
|
||||
return Promise.all(mutings.map(x => this.pack(x, me)));
|
||||
const _mutees = mutings.map(({ mutee, muteeId }) => mutee ?? muteeId);
|
||||
const _userMap = await this.userEntityService.packMany(_mutees, me, { schema: 'UserDetailedNotMe' })
|
||||
.then(users => new Map(users.map(u => [u.id, u])));
|
||||
return Promise.all(mutings.map(muting => this.pack(muting, me, { packedMutee: _userMap.get(muting.muteeId) })));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -290,6 +290,7 @@ export class NoteEntityService implements OnModuleInit {
|
||||
_hint_?: {
|
||||
myReactions: Map<MiNote['id'], string | null>;
|
||||
packedFiles: Map<MiNote['fileIds'][number], Packed<'DriveFile'> | null>;
|
||||
packedUsers: Map<MiUser['id'], Packed<'UserLite'>>
|
||||
};
|
||||
},
|
||||
): Promise<Packed<'Note'>> {
|
||||
@@ -319,12 +320,13 @@ export class NoteEntityService implements OnModuleInit {
|
||||
.filter(x => x.startsWith(':') && x.includes('@') && !x.includes('@.')) // リモートカスタム絵文字のみ
|
||||
.map(x => this.reactionService.decodeReaction(x).reaction.replaceAll(':', ''));
|
||||
const packedFiles = options?._hint_?.packedFiles;
|
||||
const packedUsers = options?._hint_?.packedUsers;
|
||||
|
||||
const packed: Packed<'Note'> = await awaitAll({
|
||||
id: note.id,
|
||||
createdAt: this.idService.parse(note.id).date.toISOString(),
|
||||
userId: note.userId,
|
||||
user: this.userEntityService.pack(note.user ?? note.userId, me),
|
||||
user: packedUsers?.get(note.userId) ?? this.userEntityService.pack(note.user ?? note.userId, me),
|
||||
text: text,
|
||||
cw: note.cw,
|
||||
visibility: note.visibility,
|
||||
@@ -449,12 +451,20 @@ export class NoteEntityService implements OnModuleInit {
|
||||
// TODO: 本当は renote とか reply がないのに renoteId とか replyId があったらここで解決しておく
|
||||
const fileIds = notes.map(n => [n.fileIds, n.renote?.fileIds, n.reply?.fileIds]).flat(2).filter(isNotNull);
|
||||
const packedFiles = fileIds.length > 0 ? await this.driveFileEntityService.packManyByIdsMap(fileIds) : new Map();
|
||||
const users = [
|
||||
...notes.map(({ user, userId }) => user ?? userId),
|
||||
...notes.map(({ replyUserId }) => replyUserId).filter(isNotNull),
|
||||
...notes.map(({ renoteUserId }) => renoteUserId).filter(isNotNull),
|
||||
];
|
||||
const packedUsers = await this.userEntityService.packMany(users, me)
|
||||
.then(users => new Map(users.map(u => [u.id, u])));
|
||||
|
||||
return await Promise.all(notes.map(n => this.pack(n, me, {
|
||||
...options,
|
||||
_hint_: {
|
||||
myReactions: myReactionsMap,
|
||||
packedFiles,
|
||||
packedUsers,
|
||||
},
|
||||
})));
|
||||
}
|
||||
|
@@ -52,6 +52,9 @@ export class NoteReactionEntityService implements OnModuleInit {
|
||||
options?: {
|
||||
withNote: boolean;
|
||||
},
|
||||
hints?: {
|
||||
packedUser?: Packed<'UserLite'>
|
||||
},
|
||||
): Promise<Packed<'NoteReaction'>> {
|
||||
const opts = Object.assign({
|
||||
withNote: false,
|
||||
@@ -62,7 +65,7 @@ export class NoteReactionEntityService implements OnModuleInit {
|
||||
return {
|
||||
id: reaction.id,
|
||||
createdAt: this.idService.parse(reaction.id).date.toISOString(),
|
||||
user: await this.userEntityService.pack(reaction.user ?? reaction.userId, me),
|
||||
user: hints?.packedUser ?? await this.userEntityService.pack(reaction.user ?? reaction.userId, me),
|
||||
type: this.reactionService.convertLegacyReaction(reaction.reaction),
|
||||
...(opts.withNote ? {
|
||||
note: await this.noteEntityService.pack(reaction.note ?? reaction.noteId, me),
|
||||
@@ -81,7 +84,9 @@ export class NoteReactionEntityService implements OnModuleInit {
|
||||
const opts = Object.assign({
|
||||
withNote: false,
|
||||
}, options);
|
||||
|
||||
return Promise.all(reactions.map(reaction => this.pack(reaction, me, opts)));
|
||||
const _users = reactions.map(({ user, userId }) => user ?? userId);
|
||||
const _userMap = await this.userEntityService.packMany(_users, me)
|
||||
.then(users => new Map(users.map(u => [u.id, u])));
|
||||
return Promise.all(reactions.map(reaction => this.pack(reaction, me, opts, { packedUser: _userMap.get(reaction.userId) })));
|
||||
}
|
||||
}
|
||||
|
@@ -40,6 +40,9 @@ export class PageEntityService {
|
||||
public async pack(
|
||||
src: MiPage['id'] | MiPage,
|
||||
me?: { id: MiUser['id'] } | null | undefined,
|
||||
hint?: {
|
||||
packedUser?: Packed<'UserLite'>
|
||||
},
|
||||
): Promise<Packed<'Page'>> {
|
||||
const meId = me ? me.id : null;
|
||||
const page = typeof src === 'object' ? src : await this.pagesRepository.findOneByOrFail({ id: src });
|
||||
@@ -91,7 +94,7 @@ export class PageEntityService {
|
||||
createdAt: this.idService.parse(page.id).date.toISOString(),
|
||||
updatedAt: page.updatedAt.toISOString(),
|
||||
userId: page.userId,
|
||||
user: this.userEntityService.pack(page.user ?? page.userId, me), // { schema: 'UserDetailed' } すると無限ループするので注意
|
||||
user: hint?.packedUser ?? this.userEntityService.pack(page.user ?? page.userId, me), // { schema: 'UserDetailed' } すると無限ループするので注意
|
||||
content: page.content,
|
||||
variables: page.variables,
|
||||
title: page.title,
|
||||
@@ -110,11 +113,14 @@ export class PageEntityService {
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public packMany(
|
||||
public async packMany(
|
||||
pages: MiPage[],
|
||||
me?: { id: MiUser['id'] } | null | undefined,
|
||||
) {
|
||||
return Promise.all(pages.map(x => this.pack(x, me)));
|
||||
const _users = pages.map(({ user, userId }) => user ?? userId);
|
||||
const _userMap = await this.userEntityService.packMany(_users, me)
|
||||
.then(users => new Map(users.map(u => [u.id, u])));
|
||||
return Promise.all(pages.map(page => this.pack(page, me, { packedUser: _userMap.get(page.userId) })));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -30,6 +30,9 @@ export class RenoteMutingEntityService {
|
||||
public async pack(
|
||||
src: MiRenoteMuting['id'] | MiRenoteMuting,
|
||||
me?: { id: MiUser['id'] } | null | undefined,
|
||||
hints?: {
|
||||
packedMutee?: Packed<'UserDetailedNotMe'>
|
||||
},
|
||||
): Promise<Packed<'RenoteMuting'>> {
|
||||
const muting = typeof src === 'object' ? src : await this.renoteMutingsRepository.findOneByOrFail({ id: src });
|
||||
|
||||
@@ -37,18 +40,21 @@ export class RenoteMutingEntityService {
|
||||
id: muting.id,
|
||||
createdAt: this.idService.parse(muting.id).date.toISOString(),
|
||||
muteeId: muting.muteeId,
|
||||
mutee: this.userEntityService.pack(muting.muteeId, me, {
|
||||
mutee: hints?.packedMutee ?? this.userEntityService.pack(muting.muteeId, me, {
|
||||
schema: 'UserDetailedNotMe',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public packMany(
|
||||
mutings: any[],
|
||||
public async packMany(
|
||||
mutings: MiRenoteMuting[],
|
||||
me: { id: MiUser['id'] },
|
||||
) {
|
||||
return Promise.all(mutings.map(x => this.pack(x, me)));
|
||||
const _users = mutings.map(({ mutee, muteeId }) => mutee ?? muteeId);
|
||||
const _userMap = await this.userEntityService.packMany(_users, me, { schema: 'UserDetailedNotMe' })
|
||||
.then(users => new Map(users.map(u => [u.id, u])));
|
||||
return Promise.all(mutings.map(muting => this.pack(muting, me, { packedMutee: _userMap.get(muting.muteeId) })));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -28,13 +28,15 @@ export class ReversiGameEntityService {
|
||||
@bindThis
|
||||
public async packDetail(
|
||||
src: MiReversiGame['id'] | MiReversiGame,
|
||||
hint?: {
|
||||
packedUser1?: Packed<'UserLite'>,
|
||||
packedUser2?: Packed<'UserLite'>,
|
||||
},
|
||||
): Promise<Packed<'ReversiGameDetailed'>> {
|
||||
const game = typeof src === 'object' ? src : await this.reversiGamesRepository.findOneByOrFail({ id: src });
|
||||
|
||||
const users = await Promise.all([
|
||||
this.userEntityService.pack(game.user1 ?? game.user1Id),
|
||||
this.userEntityService.pack(game.user2 ?? game.user2Id),
|
||||
]);
|
||||
const user1 = hint?.packedUser1 ?? await this.userEntityService.pack(game.user1 ?? game.user1Id);
|
||||
const user2 = hint?.packedUser2 ?? await this.userEntityService.pack(game.user2 ?? game.user2Id);
|
||||
|
||||
return await awaitAll({
|
||||
id: game.id,
|
||||
@@ -49,10 +51,10 @@ export class ReversiGameEntityService {
|
||||
user2Ready: game.user2Ready,
|
||||
user1Id: game.user1Id,
|
||||
user2Id: game.user2Id,
|
||||
user1: users[0],
|
||||
user2: users[1],
|
||||
user1,
|
||||
user2,
|
||||
winnerId: game.winnerId,
|
||||
winner: game.winnerId ? users.find(u => u.id === game.winnerId)! : null,
|
||||
winner: game.winnerId ? [user1, user2].find(u => u.id === game.winnerId)! : null,
|
||||
surrenderedUserId: game.surrenderedUserId,
|
||||
timeoutUserId: game.timeoutUserId,
|
||||
black: game.black,
|
||||
@@ -68,22 +70,35 @@ export class ReversiGameEntityService {
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public packDetailMany(
|
||||
xs: MiReversiGame[],
|
||||
public async packDetailMany(
|
||||
games: MiReversiGame[],
|
||||
) {
|
||||
return Promise.all(xs.map(x => this.packDetail(x)));
|
||||
const _user1s = games.map(({ user1, user1Id }) => user1 ?? user1Id);
|
||||
const _user2s = games.map(({ user2, user2Id }) => user2 ?? user2Id);
|
||||
const _userMap = await this.userEntityService.packMany([..._user1s, ..._user2s])
|
||||
.then(users => new Map(users.map(u => [u.id, u])));
|
||||
return Promise.all(
|
||||
games.map(game => {
|
||||
return this.packDetail(game, {
|
||||
packedUser1: _userMap.get(game.user1Id),
|
||||
packedUser2: _userMap.get(game.user2Id),
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async packLite(
|
||||
src: MiReversiGame['id'] | MiReversiGame,
|
||||
hint?: {
|
||||
packedUser1?: Packed<'UserLite'>,
|
||||
packedUser2?: Packed<'UserLite'>,
|
||||
},
|
||||
): Promise<Packed<'ReversiGameLite'>> {
|
||||
const game = typeof src === 'object' ? src : await this.reversiGamesRepository.findOneByOrFail({ id: src });
|
||||
|
||||
const users = await Promise.all([
|
||||
this.userEntityService.pack(game.user1 ?? game.user1Id),
|
||||
this.userEntityService.pack(game.user2 ?? game.user2Id),
|
||||
]);
|
||||
const user1 = hint?.packedUser1 ?? await this.userEntityService.pack(game.user1 ?? game.user1Id);
|
||||
const user2 = hint?.packedUser2 ?? await this.userEntityService.pack(game.user2 ?? game.user2Id);
|
||||
|
||||
return await awaitAll({
|
||||
id: game.id,
|
||||
@@ -94,10 +109,10 @@ export class ReversiGameEntityService {
|
||||
isEnded: game.isEnded,
|
||||
user1Id: game.user1Id,
|
||||
user2Id: game.user2Id,
|
||||
user1: users[0],
|
||||
user2: users[1],
|
||||
user1,
|
||||
user2,
|
||||
winnerId: game.winnerId,
|
||||
winner: game.winnerId ? users.find(u => u.id === game.winnerId)! : null,
|
||||
winner: game.winnerId ? [user1, user2].find(u => u.id === game.winnerId)! : null,
|
||||
surrenderedUserId: game.surrenderedUserId,
|
||||
timeoutUserId: game.timeoutUserId,
|
||||
black: game.black,
|
||||
@@ -111,10 +126,21 @@ export class ReversiGameEntityService {
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public packLiteMany(
|
||||
xs: MiReversiGame[],
|
||||
public async packLiteMany(
|
||||
games: MiReversiGame[],
|
||||
) {
|
||||
return Promise.all(xs.map(x => this.packLite(x)));
|
||||
const _user1s = games.map(({ user1, user1Id }) => user1 ?? user1Id);
|
||||
const _user2s = games.map(({ user2, user2Id }) => user2 ?? user2Id);
|
||||
const _userMap = await this.userEntityService.packMany([..._user1s, ..._user2s])
|
||||
.then(users => new Map(users.map(u => [u.id, u])));
|
||||
return Promise.all(
|
||||
games.map(game => {
|
||||
return this.packLite(game, {
|
||||
packedUser1: _userMap.get(game.user1Id),
|
||||
packedUser2: _userMap.get(game.user2Id),
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -50,11 +50,14 @@ export class UserListEntityService {
|
||||
public async packMembershipsMany(
|
||||
memberships: MiUserListMembership[],
|
||||
) {
|
||||
const _users = memberships.map(({ user, userId }) => user ?? userId);
|
||||
const _userMap = await this.userEntityService.packMany(_users)
|
||||
.then(users => new Map(users.map(u => [u.id, u])));
|
||||
return Promise.all(memberships.map(async x => ({
|
||||
id: x.id,
|
||||
createdAt: this.idService.parse(x.id).date.toISOString(),
|
||||
userId: x.userId,
|
||||
user: await this.userEntityService.pack(x.userId),
|
||||
user: _userMap.get(x.userId) ?? await this.userEntityService.pack(x.userId),
|
||||
withReplies: x.withReplies,
|
||||
})));
|
||||
}
|
||||
|
@@ -376,6 +376,12 @@ export class MiMeta {
|
||||
})
|
||||
public privacyPolicyUrl: string | null;
|
||||
|
||||
@Column('varchar', {
|
||||
length: 1024,
|
||||
nullable: true,
|
||||
})
|
||||
public inquiryUrl: string | null;
|
||||
|
||||
@Column('varchar', {
|
||||
length: 8192,
|
||||
nullable: true,
|
||||
|
@@ -5,409 +5,409 @@
|
||||
|
||||
import { Module } from '@nestjs/common';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { MiAbuseUserReport, MiAccessToken, MiAd, MiAnnouncement, MiAnnouncementRead, MiAntenna, MiApp, MiAuthSession, MiAvatarDecoration, MiBlocking, MiChannel, MiChannelFavorite, MiChannelFollowing, MiClip, MiClipFavorite, MiClipNote, MiDriveFile, MiDriveFolder, MiEmoji, MiFlash, MiFlashLike, MiFollowRequest, MiFollowing, MiGalleryLike, MiGalleryPost, MiHashtag, MiInstance, MiMeta, MiModerationLog, MiMuting, MiNote, MiNoteFavorite, MiNoteReaction, MiNoteThreadMuting, MiNoteUnread, MiPage, MiPageLike, MiPasswordResetRequest, MiPoll, MiPollVote, MiPromoNote, MiPromoRead, MiRegistrationTicket, MiRegistryItem, MiRelay, MiRenoteMuting, MiRetentionAggregation, MiRole, MiRoleAssignment, MiSignin, MiSwSubscription, MiUsedUsername, MiUser, MiUserIp, MiUserKeypair, MiUserList, MiUserListFavorite, MiUserListMembership, MiUserMemo, MiUserNotePining, MiUserPending, MiUserProfile, MiUserPublickey, MiUserSecurityKey, MiWebhook, MiBubbleGameRecord, MiReversiGame } from './_.js';
|
||||
import { MiRepository, MiAbuseUserReport, MiAccessToken, MiAd, MiAnnouncement, MiAnnouncementRead, MiAntenna, MiApp, MiAuthSession, MiAvatarDecoration, MiBlocking, MiChannel, MiChannelFavorite, MiChannelFollowing, MiClip, MiClipFavorite, MiClipNote, MiDriveFile, MiDriveFolder, MiEmoji, MiFlash, MiFlashLike, MiFollowRequest, MiFollowing, MiGalleryLike, MiGalleryPost, MiHashtag, MiInstance, MiMeta, MiModerationLog, MiMuting, MiNote, MiNoteFavorite, MiNoteReaction, MiNoteThreadMuting, MiNoteUnread, MiPage, MiPageLike, MiPasswordResetRequest, MiPoll, MiPollVote, MiPromoNote, MiPromoRead, MiRegistrationTicket, MiRegistryItem, MiRelay, MiRenoteMuting, MiRetentionAggregation, MiRole, MiRoleAssignment, MiSignin, MiSwSubscription, MiUsedUsername, MiUser, MiUserIp, MiUserKeypair, MiUserList, MiUserListFavorite, MiUserListMembership, MiUserMemo, MiUserNotePining, MiUserPending, MiUserProfile, MiUserPublickey, MiUserSecurityKey, MiWebhook, MiBubbleGameRecord, MiReversiGame, miRepository } from './_.js';
|
||||
import type { DataSource } from 'typeorm';
|
||||
import type { Provider } from '@nestjs/common';
|
||||
|
||||
const $usersRepository: Provider = {
|
||||
provide: DI.usersRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiUser),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiUser).extend(miRepository as MiRepository<MiUser>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $notesRepository: Provider = {
|
||||
provide: DI.notesRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiNote),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiNote).extend(miRepository as MiRepository<MiNote>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $announcementsRepository: Provider = {
|
||||
provide: DI.announcementsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiAnnouncement),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiAnnouncement).extend(miRepository as MiRepository<MiAnnouncement>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $announcementReadsRepository: Provider = {
|
||||
provide: DI.announcementReadsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiAnnouncementRead),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiAnnouncementRead).extend(miRepository as MiRepository<MiAnnouncementRead>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $appsRepository: Provider = {
|
||||
provide: DI.appsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiApp),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiApp).extend(miRepository as MiRepository<MiApp>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $avatarDecorationsRepository: Provider = {
|
||||
provide: DI.avatarDecorationsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiAvatarDecoration),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiAvatarDecoration).extend(miRepository as MiRepository<MiAvatarDecoration>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $noteFavoritesRepository: Provider = {
|
||||
provide: DI.noteFavoritesRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiNoteFavorite),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiNoteFavorite).extend(miRepository as MiRepository<MiNoteFavorite>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $noteThreadMutingsRepository: Provider = {
|
||||
provide: DI.noteThreadMutingsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiNoteThreadMuting),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiNoteThreadMuting).extend(miRepository as MiRepository<MiNoteThreadMuting>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $noteReactionsRepository: Provider = {
|
||||
provide: DI.noteReactionsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiNoteReaction),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiNoteReaction).extend(miRepository as MiRepository<MiNoteReaction>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $noteUnreadsRepository: Provider = {
|
||||
provide: DI.noteUnreadsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiNoteUnread),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiNoteUnread).extend(miRepository as MiRepository<MiNoteUnread>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $pollsRepository: Provider = {
|
||||
provide: DI.pollsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiPoll),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiPoll).extend(miRepository as MiRepository<MiPoll>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $pollVotesRepository: Provider = {
|
||||
provide: DI.pollVotesRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiPollVote),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiPollVote).extend(miRepository as MiRepository<MiPollVote>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $userProfilesRepository: Provider = {
|
||||
provide: DI.userProfilesRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiUserProfile),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiUserProfile).extend(miRepository as MiRepository<MiUserProfile>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $userKeypairsRepository: Provider = {
|
||||
provide: DI.userKeypairsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiUserKeypair),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiUserKeypair).extend(miRepository as MiRepository<MiUserKeypair>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $userPendingsRepository: Provider = {
|
||||
provide: DI.userPendingsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiUserPending),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiUserPending).extend(miRepository as MiRepository<MiUserPending>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $userSecurityKeysRepository: Provider = {
|
||||
provide: DI.userSecurityKeysRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiUserSecurityKey),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiUserSecurityKey).extend(miRepository as MiRepository<MiUserSecurityKey>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $userPublickeysRepository: Provider = {
|
||||
provide: DI.userPublickeysRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiUserPublickey),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiUserPublickey).extend(miRepository as MiRepository<MiUserPublickey>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $userListsRepository: Provider = {
|
||||
provide: DI.userListsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiUserList),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiUserList).extend(miRepository as MiRepository<MiUserList>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $userListFavoritesRepository: Provider = {
|
||||
provide: DI.userListFavoritesRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiUserListFavorite),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiUserListFavorite).extend(miRepository as MiRepository<MiUserListFavorite>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $userListMembershipsRepository: Provider = {
|
||||
provide: DI.userListMembershipsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiUserListMembership),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiUserListMembership).extend(miRepository as MiRepository<MiUserListMembership>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $userNotePiningsRepository: Provider = {
|
||||
provide: DI.userNotePiningsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiUserNotePining),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiUserNotePining).extend(miRepository as MiRepository<MiUserNotePining>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $userIpsRepository: Provider = {
|
||||
provide: DI.userIpsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiUserIp),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiUserIp).extend(miRepository as MiRepository<MiUserIp>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $usedUsernamesRepository: Provider = {
|
||||
provide: DI.usedUsernamesRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiUsedUsername),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiUsedUsername).extend(miRepository as MiRepository<MiUsedUsername>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $followingsRepository: Provider = {
|
||||
provide: DI.followingsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiFollowing),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiFollowing).extend(miRepository as MiRepository<MiFollowing>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $followRequestsRepository: Provider = {
|
||||
provide: DI.followRequestsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiFollowRequest),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiFollowRequest).extend(miRepository as MiRepository<MiFollowRequest>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $instancesRepository: Provider = {
|
||||
provide: DI.instancesRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiInstance),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiInstance).extend(miRepository as MiRepository<MiInstance>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $emojisRepository: Provider = {
|
||||
provide: DI.emojisRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiEmoji),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiEmoji).extend(miRepository as MiRepository<MiEmoji>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $driveFilesRepository: Provider = {
|
||||
provide: DI.driveFilesRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiDriveFile),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiDriveFile).extend(miRepository as MiRepository<MiDriveFile>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $driveFoldersRepository: Provider = {
|
||||
provide: DI.driveFoldersRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiDriveFolder),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiDriveFolder).extend(miRepository as MiRepository<MiDriveFolder>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $metasRepository: Provider = {
|
||||
provide: DI.metasRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiMeta),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiMeta).extend(miRepository as MiRepository<MiMeta>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $mutingsRepository: Provider = {
|
||||
provide: DI.mutingsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiMuting),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiMuting).extend(miRepository as MiRepository<MiMuting>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $renoteMutingsRepository: Provider = {
|
||||
provide: DI.renoteMutingsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiRenoteMuting),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiRenoteMuting).extend(miRepository as MiRepository<MiRenoteMuting>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $blockingsRepository: Provider = {
|
||||
provide: DI.blockingsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiBlocking),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiBlocking).extend(miRepository as MiRepository<MiBlocking>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $swSubscriptionsRepository: Provider = {
|
||||
provide: DI.swSubscriptionsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiSwSubscription),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiSwSubscription).extend(miRepository as MiRepository<MiSwSubscription>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $hashtagsRepository: Provider = {
|
||||
provide: DI.hashtagsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiHashtag),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiHashtag).extend(miRepository as MiRepository<MiHashtag>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $abuseUserReportsRepository: Provider = {
|
||||
provide: DI.abuseUserReportsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiAbuseUserReport),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiAbuseUserReport).extend(miRepository as MiRepository<MiAbuseUserReport>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $registrationTicketsRepository: Provider = {
|
||||
provide: DI.registrationTicketsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiRegistrationTicket),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiRegistrationTicket).extend(miRepository as MiRepository<MiRegistrationTicket>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $authSessionsRepository: Provider = {
|
||||
provide: DI.authSessionsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiAuthSession),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiAuthSession).extend(miRepository as MiRepository<MiAuthSession>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $accessTokensRepository: Provider = {
|
||||
provide: DI.accessTokensRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiAccessToken),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiAccessToken).extend(miRepository as MiRepository<MiAccessToken>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $signinsRepository: Provider = {
|
||||
provide: DI.signinsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiSignin),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiSignin).extend(miRepository as MiRepository<MiSignin>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $pagesRepository: Provider = {
|
||||
provide: DI.pagesRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiPage),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiPage).extend(miRepository as MiRepository<MiPage>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $pageLikesRepository: Provider = {
|
||||
provide: DI.pageLikesRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiPageLike),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiPageLike).extend(miRepository as MiRepository<MiPageLike>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $galleryPostsRepository: Provider = {
|
||||
provide: DI.galleryPostsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiGalleryPost),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiGalleryPost).extend(miRepository as MiRepository<MiGalleryPost>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $galleryLikesRepository: Provider = {
|
||||
provide: DI.galleryLikesRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiGalleryLike),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiGalleryLike).extend(miRepository as MiRepository<MiGalleryLike>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $moderationLogsRepository: Provider = {
|
||||
provide: DI.moderationLogsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiModerationLog),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiModerationLog).extend(miRepository as MiRepository<MiModerationLog>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $clipsRepository: Provider = {
|
||||
provide: DI.clipsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiClip),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiClip).extend(miRepository as MiRepository<MiClip>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $clipNotesRepository: Provider = {
|
||||
provide: DI.clipNotesRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiClipNote),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiClipNote).extend(miRepository as MiRepository<MiClipNote>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $clipFavoritesRepository: Provider = {
|
||||
provide: DI.clipFavoritesRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiClipFavorite),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiClipFavorite).extend(miRepository as MiRepository<MiClipFavorite>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $antennasRepository: Provider = {
|
||||
provide: DI.antennasRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiAntenna),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiAntenna).extend(miRepository as MiRepository<MiAntenna>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $promoNotesRepository: Provider = {
|
||||
provide: DI.promoNotesRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiPromoNote),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiPromoNote).extend(miRepository as MiRepository<MiPromoNote>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $promoReadsRepository: Provider = {
|
||||
provide: DI.promoReadsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiPromoRead),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiPromoRead).extend(miRepository as MiRepository<MiPromoRead>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $relaysRepository: Provider = {
|
||||
provide: DI.relaysRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiRelay),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiRelay).extend(miRepository as MiRepository<MiRelay>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $channelsRepository: Provider = {
|
||||
provide: DI.channelsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiChannel),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiChannel).extend(miRepository as MiRepository<MiChannel>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $channelFollowingsRepository: Provider = {
|
||||
provide: DI.channelFollowingsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiChannelFollowing),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiChannelFollowing).extend(miRepository as MiRepository<MiChannelFollowing>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $channelFavoritesRepository: Provider = {
|
||||
provide: DI.channelFavoritesRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiChannelFavorite),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiChannelFavorite).extend(miRepository as MiRepository<MiChannelFavorite>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $registryItemsRepository: Provider = {
|
||||
provide: DI.registryItemsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiRegistryItem),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiRegistryItem).extend(miRepository as MiRepository<MiRegistryItem>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $webhooksRepository: Provider = {
|
||||
provide: DI.webhooksRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiWebhook),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiWebhook).extend(miRepository as MiRepository<MiWebhook>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $adsRepository: Provider = {
|
||||
provide: DI.adsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiAd),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiAd).extend(miRepository as MiRepository<MiAd>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $passwordResetRequestsRepository: Provider = {
|
||||
provide: DI.passwordResetRequestsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiPasswordResetRequest),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiPasswordResetRequest).extend(miRepository as MiRepository<MiPasswordResetRequest>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $retentionAggregationsRepository: Provider = {
|
||||
provide: DI.retentionAggregationsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiRetentionAggregation),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiRetentionAggregation).extend(miRepository as MiRepository<MiRetentionAggregation>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $flashsRepository: Provider = {
|
||||
provide: DI.flashsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiFlash),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiFlash).extend(miRepository as MiRepository<MiFlash>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $flashLikesRepository: Provider = {
|
||||
provide: DI.flashLikesRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiFlashLike),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiFlashLike).extend(miRepository as MiRepository<MiFlashLike>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $rolesRepository: Provider = {
|
||||
provide: DI.rolesRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiRole),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiRole).extend(miRepository as MiRepository<MiRole>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $roleAssignmentsRepository: Provider = {
|
||||
provide: DI.roleAssignmentsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiRoleAssignment),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiRoleAssignment).extend(miRepository as MiRepository<MiRoleAssignment>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $userMemosRepository: Provider = {
|
||||
provide: DI.userMemosRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiUserMemo),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiUserMemo).extend(miRepository as MiRepository<MiUserMemo>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $bubbleGameRecordsRepository: Provider = {
|
||||
provide: DI.bubbleGameRecordsRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiBubbleGameRecord),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiBubbleGameRecord).extend(miRepository as MiRepository<MiBubbleGameRecord>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
const $reversiGamesRepository: Provider = {
|
||||
provide: DI.reversiGamesRepository,
|
||||
useFactory: (db: DataSource) => db.getRepository(MiReversiGame),
|
||||
useFactory: (db: DataSource) => db.getRepository(MiReversiGame).extend(miRepository as MiRepository<MiReversiGame>),
|
||||
inject: [DI.db],
|
||||
};
|
||||
|
||||
|
@@ -3,6 +3,10 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { FindOneOptions, InsertQueryBuilder, ObjectLiteral, QueryRunner, ReplicationMode, Repository, SelectQueryBuilder } from 'typeorm';
|
||||
import { RelationCountLoader } from 'typeorm/query-builder/relation-count/RelationCountLoader.js';
|
||||
import { RelationIdLoader } from 'typeorm/query-builder/relation-id/RelationIdLoader.js';
|
||||
import { RawSqlResultsToEntityTransformer } from 'typeorm/query-builder/transformer/RawSqlResultsToEntityTransformer.js';
|
||||
import { MiAbuseUserReport } from '@/models/AbuseUserReport.js';
|
||||
import { MiAccessToken } from '@/models/AccessToken.js';
|
||||
import { MiAd } from '@/models/Ad.js';
|
||||
@@ -70,8 +74,102 @@ import { MiFlashLike } from '@/models/FlashLike.js';
|
||||
import { MiUserListFavorite } from '@/models/UserListFavorite.js';
|
||||
import { MiBubbleGameRecord } from '@/models/BubbleGameRecord.js';
|
||||
import { MiReversiGame } from '@/models/ReversiGame.js';
|
||||
import type { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity.js';
|
||||
|
||||
import type { Repository } from 'typeorm';
|
||||
interface AsyncDisposableReference<T> extends AsyncDisposable {
|
||||
readonly value: T;
|
||||
}
|
||||
|
||||
// SEEALSO: <https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html#using-declarations-and-explicit-resource-management>
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
Symbol.dispose ??= Symbol('Symbol.dispose');
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
Symbol.asyncDispose ??= Symbol('Symbol.asyncDispose');
|
||||
|
||||
export interface MiRepository<T extends ObjectLiteral> {
|
||||
createTableColumnNames(this: Repository<T> & MiRepository<T>, queryBuilder: InsertQueryBuilder<T>): string[];
|
||||
createTableColumnNamesWithPrimaryKey(this: Repository<T> & MiRepository<T>, queryBuilder: InsertQueryBuilder<T>): string[];
|
||||
insertOne(this: Repository<T> & MiRepository<T>, entity: QueryDeepPartialEntity<T>, findOptions?: Pick<FindOneOptions<T>, 'relations'>): Promise<T>;
|
||||
selectAliasColumnNames(this: Repository<T> & MiRepository<T>, queryBuilder: InsertQueryBuilder<T>, builder: SelectQueryBuilder<T>): void;
|
||||
useQueryRunner(this: Repository<T> & MiRepository<T>, mode: ReplicationMode): AsyncDisposableReference<QueryRunner>;
|
||||
}
|
||||
|
||||
export const miRepository = {
|
||||
createTableColumnNames(queryBuilder) {
|
||||
// @ts-expect-error -- protected
|
||||
const insertedColumns = queryBuilder.getInsertedColumns();
|
||||
if (insertedColumns.length) {
|
||||
return insertedColumns.map(column => column.databaseName);
|
||||
}
|
||||
if (!queryBuilder.expressionMap.mainAlias?.hasMetadata && !queryBuilder.expressionMap.insertColumns.length) {
|
||||
// @ts-expect-error -- protected
|
||||
const valueSets = queryBuilder.getValueSets();
|
||||
if (valueSets.length === 1) {
|
||||
return Object.keys(valueSets[0]);
|
||||
}
|
||||
}
|
||||
return queryBuilder.expressionMap.insertColumns;
|
||||
},
|
||||
createTableColumnNamesWithPrimaryKey(queryBuilder) {
|
||||
const columnNames = this.createTableColumnNames(queryBuilder);
|
||||
if (!columnNames.includes('id')) {
|
||||
columnNames.unshift('id');
|
||||
}
|
||||
return columnNames;
|
||||
},
|
||||
async insertOne(entity, findOptions?) {
|
||||
await using queryRunnerADR = this.useQueryRunner('master');
|
||||
const queryRunner = queryRunnerADR.value;
|
||||
const queryBuilder = this.createQueryBuilder(undefined, queryRunner).insert().values(entity);
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const mainAlias = queryBuilder.expressionMap.mainAlias!;
|
||||
const name = mainAlias.name;
|
||||
mainAlias.name = 't';
|
||||
const columnNames = this.createTableColumnNamesWithPrimaryKey(queryBuilder);
|
||||
queryBuilder.returning(columnNames.reduce((a, c) => `${a}, ${queryBuilder.escape(c)}`, '').slice(2));
|
||||
const builder = this.createQueryBuilder(undefined, queryRunner).addCommonTableExpression(queryBuilder, 'cte', { columnNames });
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
builder.expressionMap.mainAlias!.tablePath = 'cte';
|
||||
this.selectAliasColumnNames(queryBuilder, builder);
|
||||
if (findOptions) {
|
||||
builder.setFindOptions(findOptions);
|
||||
}
|
||||
const raw = await builder.execute();
|
||||
mainAlias.name = name;
|
||||
const relationId = await new RelationIdLoader(builder.connection, queryRunner, builder.expressionMap.relationIdAttributes).load(raw);
|
||||
const relationCount = await new RelationCountLoader(builder.connection, queryRunner, builder.expressionMap.relationCountAttributes).load(raw);
|
||||
const result = new RawSqlResultsToEntityTransformer(builder.expressionMap, builder.connection.driver, relationId, relationCount, queryRunner).transform(raw, mainAlias);
|
||||
return result[0];
|
||||
},
|
||||
selectAliasColumnNames(queryBuilder, builder) {
|
||||
let selectOrAddSelect = (selection: string, selectionAliasName?: string) => {
|
||||
selectOrAddSelect = (selection, selectionAliasName) => builder.addSelect(selection, selectionAliasName);
|
||||
return builder.select(selection, selectionAliasName);
|
||||
};
|
||||
for (const columnName of this.createTableColumnNamesWithPrimaryKey(queryBuilder)) {
|
||||
selectOrAddSelect(`${builder.alias}.${columnName}`, `${builder.alias}_${columnName}`);
|
||||
}
|
||||
},
|
||||
useQueryRunner(mode) {
|
||||
if (this.queryRunner?.getReplicationMode() === mode) {
|
||||
return {
|
||||
value: this.queryRunner,
|
||||
[Symbol.asyncDispose]() {
|
||||
return Promise.resolve();
|
||||
},
|
||||
};
|
||||
}
|
||||
const queryRunner = this.manager.connection.createQueryRunner(mode);
|
||||
return {
|
||||
value: queryRunner,
|
||||
[Symbol.asyncDispose]() {
|
||||
return queryRunner.release();
|
||||
},
|
||||
};
|
||||
},
|
||||
} satisfies MiRepository<ObjectLiteral>;
|
||||
|
||||
export {
|
||||
MiAbuseUserReport,
|
||||
@@ -143,70 +241,70 @@ export {
|
||||
MiReversiGame,
|
||||
};
|
||||
|
||||
export type AbuseUserReportsRepository = Repository<MiAbuseUserReport>;
|
||||
export type AccessTokensRepository = Repository<MiAccessToken>;
|
||||
export type AdsRepository = Repository<MiAd>;
|
||||
export type AnnouncementsRepository = Repository<MiAnnouncement>;
|
||||
export type AnnouncementReadsRepository = Repository<MiAnnouncementRead>;
|
||||
export type AntennasRepository = Repository<MiAntenna>;
|
||||
export type AppsRepository = Repository<MiApp>;
|
||||
export type AvatarDecorationsRepository = Repository<MiAvatarDecoration>;
|
||||
export type AuthSessionsRepository = Repository<MiAuthSession>;
|
||||
export type BlockingsRepository = Repository<MiBlocking>;
|
||||
export type ChannelFollowingsRepository = Repository<MiChannelFollowing>;
|
||||
export type ChannelFavoritesRepository = Repository<MiChannelFavorite>;
|
||||
export type ClipsRepository = Repository<MiClip>;
|
||||
export type ClipNotesRepository = Repository<MiClipNote>;
|
||||
export type ClipFavoritesRepository = Repository<MiClipFavorite>;
|
||||
export type DriveFilesRepository = Repository<MiDriveFile>;
|
||||
export type DriveFoldersRepository = Repository<MiDriveFolder>;
|
||||
export type EmojisRepository = Repository<MiEmoji>;
|
||||
export type FollowingsRepository = Repository<MiFollowing>;
|
||||
export type FollowRequestsRepository = Repository<MiFollowRequest>;
|
||||
export type GalleryLikesRepository = Repository<MiGalleryLike>;
|
||||
export type GalleryPostsRepository = Repository<MiGalleryPost>;
|
||||
export type HashtagsRepository = Repository<MiHashtag>;
|
||||
export type InstancesRepository = Repository<MiInstance>;
|
||||
export type MetasRepository = Repository<MiMeta>;
|
||||
export type ModerationLogsRepository = Repository<MiModerationLog>;
|
||||
export type MutingsRepository = Repository<MiMuting>;
|
||||
export type RenoteMutingsRepository = Repository<MiRenoteMuting>;
|
||||
export type NotesRepository = Repository<MiNote>;
|
||||
export type NoteFavoritesRepository = Repository<MiNoteFavorite>;
|
||||
export type NoteReactionsRepository = Repository<MiNoteReaction>;
|
||||
export type NoteThreadMutingsRepository = Repository<MiNoteThreadMuting>;
|
||||
export type NoteUnreadsRepository = Repository<MiNoteUnread>;
|
||||
export type PagesRepository = Repository<MiPage>;
|
||||
export type PageLikesRepository = Repository<MiPageLike>;
|
||||
export type PasswordResetRequestsRepository = Repository<MiPasswordResetRequest>;
|
||||
export type PollsRepository = Repository<MiPoll>;
|
||||
export type PollVotesRepository = Repository<MiPollVote>;
|
||||
export type PromoNotesRepository = Repository<MiPromoNote>;
|
||||
export type PromoReadsRepository = Repository<MiPromoRead>;
|
||||
export type RegistrationTicketsRepository = Repository<MiRegistrationTicket>;
|
||||
export type RegistryItemsRepository = Repository<MiRegistryItem>;
|
||||
export type RelaysRepository = Repository<MiRelay>;
|
||||
export type SigninsRepository = Repository<MiSignin>;
|
||||
export type SwSubscriptionsRepository = Repository<MiSwSubscription>;
|
||||
export type UsedUsernamesRepository = Repository<MiUsedUsername>;
|
||||
export type UsersRepository = Repository<MiUser>;
|
||||
export type UserIpsRepository = Repository<MiUserIp>;
|
||||
export type UserKeypairsRepository = Repository<MiUserKeypair>;
|
||||
export type UserListsRepository = Repository<MiUserList>;
|
||||
export type UserListFavoritesRepository = Repository<MiUserListFavorite>;
|
||||
export type UserListMembershipsRepository = Repository<MiUserListMembership>;
|
||||
export type UserNotePiningsRepository = Repository<MiUserNotePining>;
|
||||
export type UserPendingsRepository = Repository<MiUserPending>;
|
||||
export type UserProfilesRepository = Repository<MiUserProfile>;
|
||||
export type UserPublickeysRepository = Repository<MiUserPublickey>;
|
||||
export type UserSecurityKeysRepository = Repository<MiUserSecurityKey>;
|
||||
export type WebhooksRepository = Repository<MiWebhook>;
|
||||
export type ChannelsRepository = Repository<MiChannel>;
|
||||
export type RetentionAggregationsRepository = Repository<MiRetentionAggregation>;
|
||||
export type RolesRepository = Repository<MiRole>;
|
||||
export type RoleAssignmentsRepository = Repository<MiRoleAssignment>;
|
||||
export type FlashsRepository = Repository<MiFlash>;
|
||||
export type FlashLikesRepository = Repository<MiFlashLike>;
|
||||
export type UserMemoRepository = Repository<MiUserMemo>;
|
||||
export type BubbleGameRecordsRepository = Repository<MiBubbleGameRecord>;
|
||||
export type ReversiGamesRepository = Repository<MiReversiGame>;
|
||||
export type AbuseUserReportsRepository = Repository<MiAbuseUserReport> & MiRepository<MiAbuseUserReport>;
|
||||
export type AccessTokensRepository = Repository<MiAccessToken> & MiRepository<MiAccessToken>;
|
||||
export type AdsRepository = Repository<MiAd> & MiRepository<MiAd>;
|
||||
export type AnnouncementsRepository = Repository<MiAnnouncement> & MiRepository<MiAnnouncement>;
|
||||
export type AnnouncementReadsRepository = Repository<MiAnnouncementRead> & MiRepository<MiAnnouncementRead>;
|
||||
export type AntennasRepository = Repository<MiAntenna> & MiRepository<MiAntenna>;
|
||||
export type AppsRepository = Repository<MiApp> & MiRepository<MiApp>;
|
||||
export type AvatarDecorationsRepository = Repository<MiAvatarDecoration> & MiRepository<MiAvatarDecoration>;
|
||||
export type AuthSessionsRepository = Repository<MiAuthSession> & MiRepository<MiAuthSession>;
|
||||
export type BlockingsRepository = Repository<MiBlocking> & MiRepository<MiBlocking>;
|
||||
export type ChannelFollowingsRepository = Repository<MiChannelFollowing> & MiRepository<MiChannelFollowing>;
|
||||
export type ChannelFavoritesRepository = Repository<MiChannelFavorite> & MiRepository<MiChannelFavorite>;
|
||||
export type ClipsRepository = Repository<MiClip> & MiRepository<MiClip>;
|
||||
export type ClipNotesRepository = Repository<MiClipNote> & MiRepository<MiClipNote>;
|
||||
export type ClipFavoritesRepository = Repository<MiClipFavorite> & MiRepository<MiClipFavorite>;
|
||||
export type DriveFilesRepository = Repository<MiDriveFile> & MiRepository<MiDriveFile>;
|
||||
export type DriveFoldersRepository = Repository<MiDriveFolder> & MiRepository<MiDriveFolder>;
|
||||
export type EmojisRepository = Repository<MiEmoji> & MiRepository<MiEmoji>;
|
||||
export type FollowingsRepository = Repository<MiFollowing> & MiRepository<MiFollowing>;
|
||||
export type FollowRequestsRepository = Repository<MiFollowRequest> & MiRepository<MiFollowRequest>;
|
||||
export type GalleryLikesRepository = Repository<MiGalleryLike> & MiRepository<MiGalleryLike>;
|
||||
export type GalleryPostsRepository = Repository<MiGalleryPost> & MiRepository<MiGalleryPost>;
|
||||
export type HashtagsRepository = Repository<MiHashtag> & MiRepository<MiHashtag>;
|
||||
export type InstancesRepository = Repository<MiInstance> & MiRepository<MiInstance>;
|
||||
export type MetasRepository = Repository<MiMeta> & MiRepository<MiMeta>;
|
||||
export type ModerationLogsRepository = Repository<MiModerationLog> & MiRepository<MiModerationLog>;
|
||||
export type MutingsRepository = Repository<MiMuting> & MiRepository<MiMuting>;
|
||||
export type RenoteMutingsRepository = Repository<MiRenoteMuting> & MiRepository<MiRenoteMuting>;
|
||||
export type NotesRepository = Repository<MiNote> & MiRepository<MiNote>;
|
||||
export type NoteFavoritesRepository = Repository<MiNoteFavorite> & MiRepository<MiNoteFavorite>;
|
||||
export type NoteReactionsRepository = Repository<MiNoteReaction> & MiRepository<MiNoteReaction>;
|
||||
export type NoteThreadMutingsRepository = Repository<MiNoteThreadMuting> & MiRepository<MiNoteThreadMuting>;
|
||||
export type NoteUnreadsRepository = Repository<MiNoteUnread> & MiRepository<MiNoteUnread>;
|
||||
export type PagesRepository = Repository<MiPage> & MiRepository<MiPage>;
|
||||
export type PageLikesRepository = Repository<MiPageLike> & MiRepository<MiPageLike>;
|
||||
export type PasswordResetRequestsRepository = Repository<MiPasswordResetRequest> & MiRepository<MiPasswordResetRequest>;
|
||||
export type PollsRepository = Repository<MiPoll> & MiRepository<MiPoll>;
|
||||
export type PollVotesRepository = Repository<MiPollVote> & MiRepository<MiPollVote>;
|
||||
export type PromoNotesRepository = Repository<MiPromoNote> & MiRepository<MiPromoNote>;
|
||||
export type PromoReadsRepository = Repository<MiPromoRead> & MiRepository<MiPromoRead>;
|
||||
export type RegistrationTicketsRepository = Repository<MiRegistrationTicket> & MiRepository<MiRegistrationTicket>;
|
||||
export type RegistryItemsRepository = Repository<MiRegistryItem> & MiRepository<MiRegistryItem>;
|
||||
export type RelaysRepository = Repository<MiRelay> & MiRepository<MiRelay>;
|
||||
export type SigninsRepository = Repository<MiSignin> & MiRepository<MiSignin>;
|
||||
export type SwSubscriptionsRepository = Repository<MiSwSubscription> & MiRepository<MiSwSubscription>;
|
||||
export type UsedUsernamesRepository = Repository<MiUsedUsername> & MiRepository<MiUsedUsername>;
|
||||
export type UsersRepository = Repository<MiUser> & MiRepository<MiUser>;
|
||||
export type UserIpsRepository = Repository<MiUserIp> & MiRepository<MiUserIp>;
|
||||
export type UserKeypairsRepository = Repository<MiUserKeypair> & MiRepository<MiUserKeypair>;
|
||||
export type UserListsRepository = Repository<MiUserList> & MiRepository<MiUserList>;
|
||||
export type UserListFavoritesRepository = Repository<MiUserListFavorite> & MiRepository<MiUserListFavorite>;
|
||||
export type UserListMembershipsRepository = Repository<MiUserListMembership> & MiRepository<MiUserListMembership>;
|
||||
export type UserNotePiningsRepository = Repository<MiUserNotePining> & MiRepository<MiUserNotePining>;
|
||||
export type UserPendingsRepository = Repository<MiUserPending> & MiRepository<MiUserPending>;
|
||||
export type UserProfilesRepository = Repository<MiUserProfile> & MiRepository<MiUserProfile>;
|
||||
export type UserPublickeysRepository = Repository<MiUserPublickey> & MiRepository<MiUserPublickey>;
|
||||
export type UserSecurityKeysRepository = Repository<MiUserSecurityKey> & MiRepository<MiUserSecurityKey>;
|
||||
export type WebhooksRepository = Repository<MiWebhook> & MiRepository<MiWebhook>;
|
||||
export type ChannelsRepository = Repository<MiChannel> & MiRepository<MiChannel>;
|
||||
export type RetentionAggregationsRepository = Repository<MiRetentionAggregation> & MiRepository<MiRetentionAggregation>;
|
||||
export type RolesRepository = Repository<MiRole> & MiRepository<MiRole>;
|
||||
export type RoleAssignmentsRepository = Repository<MiRoleAssignment> & MiRepository<MiRoleAssignment>;
|
||||
export type FlashsRepository = Repository<MiFlash> & MiRepository<MiFlash>;
|
||||
export type FlashLikesRepository = Repository<MiFlashLike> & MiRepository<MiFlashLike>;
|
||||
export type UserMemoRepository = Repository<MiUserMemo> & MiRepository<MiUserMemo>;
|
||||
export type BubbleGameRecordsRepository = Repository<MiBubbleGameRecord> & MiRepository<MiBubbleGameRecord>;
|
||||
export type ReversiGamesRepository = Repository<MiReversiGame> & MiRepository<MiReversiGame>;
|
||||
|
@@ -95,5 +95,10 @@ export const packedAntennaSchema = {
|
||||
optional: false, nullable: false,
|
||||
default: false,
|
||||
},
|
||||
notify: {
|
||||
type: 'boolean',
|
||||
optional: false, nullable: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
@@ -227,6 +227,10 @@ export const packedMetaLiteSchema = {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
},
|
||||
inquiryUrl: {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
},
|
||||
serverRules: {
|
||||
type: 'array',
|
||||
optional: false, nullable: false,
|
||||
|
@@ -76,7 +76,7 @@ export class ImportAntennasProcessorService {
|
||||
this.logger.warn('Validation Failed');
|
||||
continue;
|
||||
}
|
||||
const result = await this.antennasRepository.insert({
|
||||
const result = await this.antennasRepository.insertOne({
|
||||
id: this.idService.gen(now.getTime()),
|
||||
lastUsedAt: now,
|
||||
userId: job.data.user.id,
|
||||
@@ -91,7 +91,7 @@ export class ImportAntennasProcessorService {
|
||||
excludeBots: antenna.excludeBots,
|
||||
withReplies: antenna.withReplies,
|
||||
withFile: antenna.withFile,
|
||||
}).then(x => this.antennasRepository.findOneByOrFail(x.identifiers[0]));
|
||||
});
|
||||
this.logger.succ('Antenna created: ' + result.id);
|
||||
this.globalEventService.publishInternalEvent('antennaCreated', result);
|
||||
}
|
||||
|
@@ -79,11 +79,11 @@ export class ImportUserListsProcessorService {
|
||||
});
|
||||
|
||||
if (list == null) {
|
||||
list = await this.userListsRepository.insert({
|
||||
list = await this.userListsRepository.insertOne({
|
||||
id: this.idService.gen(),
|
||||
userId: user.id,
|
||||
name: listName,
|
||||
}).then(x => this.userListsRepository.findOneByOrFail(x.identifiers[0]));
|
||||
});
|
||||
}
|
||||
|
||||
let target = this.utilityService.isSelfHost(host!) ? await this.usersRepository.findOneBy({
|
||||
|
@@ -37,12 +37,12 @@ export class NodeinfoServerService {
|
||||
@bindThis
|
||||
public getLinks() {
|
||||
return [{
|
||||
rel: 'http://nodeinfo.diaspora.software/ns/schema/2.1',
|
||||
href: this.config.url + nodeinfo2_1path
|
||||
}, {
|
||||
rel: 'http://nodeinfo.diaspora.software/ns/schema/2.0',
|
||||
href: this.config.url + nodeinfo2_0path,
|
||||
}];
|
||||
rel: 'http://nodeinfo.diaspora.software/ns/schema/2.1',
|
||||
href: this.config.url + nodeinfo2_1path,
|
||||
}, {
|
||||
rel: 'http://nodeinfo.diaspora.software/ns/schema/2.0',
|
||||
href: this.config.url + nodeinfo2_0path,
|
||||
}];
|
||||
}
|
||||
|
||||
@bindThis
|
||||
@@ -108,6 +108,7 @@ export class NodeinfoServerService {
|
||||
langs: meta.langs,
|
||||
tosUrl: meta.termsOfServiceUrl,
|
||||
privacyPolicyUrl: meta.privacyPolicyUrl,
|
||||
inquiryUrl: meta.inquiryUrl,
|
||||
impressumUrl: meta.impressumUrl,
|
||||
repositoryUrl: meta.repositoryUrl,
|
||||
feedbackUrl: meta.feedbackUrl,
|
||||
|
@@ -29,13 +29,13 @@ export class SigninService {
|
||||
public signin(request: FastifyRequest, reply: FastifyReply, user: MiLocalUser) {
|
||||
setImmediate(async () => {
|
||||
// Append signin history
|
||||
const record = await this.signinsRepository.insert({
|
||||
const record = await this.signinsRepository.insertOne({
|
||||
id: this.idService.gen(),
|
||||
userId: user.id,
|
||||
ip: request.ip,
|
||||
headers: request.headers as any,
|
||||
success: true,
|
||||
}).then(x => this.signinsRepository.findOneByOrFail(x.identifiers[0]));
|
||||
});
|
||||
|
||||
// Publish signin event
|
||||
this.globalEventService.publishMainStream(user.id, 'signin', await this.signinEntityService.pack(record));
|
||||
|
@@ -183,13 +183,13 @@ export class SignupApiService {
|
||||
const salt = await bcrypt.genSalt(8);
|
||||
const hash = await bcrypt.hash(password, salt);
|
||||
|
||||
const pendingUser = await this.userPendingsRepository.insert({
|
||||
const pendingUser = await this.userPendingsRepository.insertOne({
|
||||
id: this.idService.gen(),
|
||||
code,
|
||||
email: emailAddress!,
|
||||
username: username,
|
||||
password: hash,
|
||||
}).then(x => this.userPendingsRepository.findOneByOrFail(x.identifiers[0]));
|
||||
});
|
||||
|
||||
const link = `${this.config.url}/signup-complete/${code}`;
|
||||
|
||||
|
@@ -50,7 +50,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
private moderationLogService: ModerationLogService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const ad = await this.adsRepository.insert({
|
||||
const ad = await this.adsRepository.insertOne({
|
||||
id: this.idService.gen(),
|
||||
expiresAt: new Date(ps.expiresAt),
|
||||
startsAt: new Date(ps.startsAt),
|
||||
@@ -61,7 +61,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
ratio: ps.ratio,
|
||||
place: ps.place,
|
||||
memo: ps.memo,
|
||||
}).then(r => this.adsRepository.findOneByOrFail({ id: r.identifiers[0].id }));
|
||||
});
|
||||
|
||||
this.moderationLogService.log(me, 'createAd', {
|
||||
adId: ad.id,
|
||||
|
@@ -66,11 +66,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
const ticketsPromises = [];
|
||||
|
||||
for (let i = 0; i < ps.count; i++) {
|
||||
ticketsPromises.push(this.registrationTicketsRepository.insert({
|
||||
ticketsPromises.push(this.registrationTicketsRepository.insertOne({
|
||||
id: this.idService.gen(),
|
||||
expiresAt: ps.expiresAt ? new Date(ps.expiresAt) : null,
|
||||
code: generateInviteCode(),
|
||||
}).then(x => this.registrationTicketsRepository.findOneByOrFail(x.identifiers[0])));
|
||||
}));
|
||||
}
|
||||
|
||||
const tickets = await Promise.all(ticketsPromises);
|
||||
|
@@ -427,6 +427,10 @@ export const meta = {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
},
|
||||
inquiryUrl: {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
},
|
||||
repositoryUrl: {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
@@ -513,6 +517,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
feedbackUrl: instance.feedbackUrl,
|
||||
impressumUrl: instance.impressumUrl,
|
||||
privacyPolicyUrl: instance.privacyPolicyUrl,
|
||||
inquiryUrl: instance.inquiryUrl,
|
||||
disableRegistration: instance.disableRegistration,
|
||||
emailRequiredForSignup: instance.emailRequiredForSignup,
|
||||
enableHcaptcha: instance.enableHcaptcha,
|
||||
|
@@ -89,10 +89,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
.limit(ps.limit)
|
||||
.getMany();
|
||||
|
||||
const _users = assigns.map(({ user, userId }) => user ?? userId);
|
||||
const _userMap = await this.userEntityService.packMany(_users, me, { schema: 'UserDetailed' })
|
||||
.then(users => new Map(users.map(u => [u.id, u])));
|
||||
return await Promise.all(assigns.map(async assign => ({
|
||||
id: assign.id,
|
||||
createdAt: this.idService.parse(assign.id).date.toISOString(),
|
||||
user: await this.userEntityService.pack(assign.user!, me, { schema: 'UserDetailed' }),
|
||||
user: _userMap.get(assign.userId) ?? await this.userEntityService.pack(assign.user!, me, { schema: 'UserDetailed' }),
|
||||
expiresAt: assign.expiresAt?.toISOString() ?? null,
|
||||
})));
|
||||
});
|
||||
|
@@ -107,6 +107,7 @@ export const paramDef = {
|
||||
feedbackUrl: { type: 'string', nullable: true },
|
||||
impressumUrl: { type: 'string', nullable: true },
|
||||
privacyPolicyUrl: { type: 'string', nullable: true },
|
||||
inquiryUrl: { type: 'string', nullable: true },
|
||||
useObjectStorage: { type: 'boolean' },
|
||||
objectStorageBaseUrl: { type: 'string', nullable: true },
|
||||
objectStorageBucket: { type: 'string', nullable: true },
|
||||
@@ -422,6 +423,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
set.privacyPolicyUrl = ps.privacyPolicyUrl;
|
||||
}
|
||||
|
||||
if (ps.inquiryUrl !== undefined) {
|
||||
set.inquiryUrl = ps.inquiryUrl;
|
||||
}
|
||||
|
||||
if (ps.useObjectStorage !== undefined) {
|
||||
set.useObjectStorage = ps.useObjectStorage;
|
||||
}
|
||||
|
@@ -112,7 +112,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
|
||||
const now = new Date();
|
||||
|
||||
const antenna = await this.antennasRepository.insert({
|
||||
const antenna = await this.antennasRepository.insertOne({
|
||||
id: this.idService.gen(now.getTime()),
|
||||
lastUsedAt: now,
|
||||
userId: me.id,
|
||||
@@ -127,7 +127,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
excludeBots: ps.excludeBots,
|
||||
withReplies: ps.withReplies,
|
||||
withFile: ps.withFile,
|
||||
}).then(x => this.antennasRepository.findOneByOrFail(x.identifiers[0]));
|
||||
});
|
||||
|
||||
this.globalEventService.publishInternalEvent('antennaCreated', antenna);
|
||||
|
||||
|
@@ -54,7 +54,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
const permission = unique(ps.permission.map(v => v.replace(/^(.+)(\/|-)(read|write)$/, '$3:$1')));
|
||||
|
||||
// Create account
|
||||
const app = await this.appsRepository.insert({
|
||||
const app = await this.appsRepository.insertOne({
|
||||
id: this.idService.gen(),
|
||||
userId: me ? me.id : null,
|
||||
name: ps.name,
|
||||
@@ -62,7 +62,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
permission,
|
||||
callbackUrl: ps.callbackUrl,
|
||||
secret: secret,
|
||||
}).then(x => this.appsRepository.findOneByOrFail(x.identifiers[0]));
|
||||
});
|
||||
|
||||
return await this.appEntityService.pack(app, null, {
|
||||
detail: true,
|
||||
|
@@ -78,11 +78,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
const token = randomUUID();
|
||||
|
||||
// Create session token document
|
||||
const doc = await this.authSessionsRepository.insert({
|
||||
const doc = await this.authSessionsRepository.insertOne({
|
||||
id: this.idService.gen(),
|
||||
appId: app.id,
|
||||
token: token,
|
||||
}).then(x => this.authSessionsRepository.findOneByOrFail(x.identifiers[0]));
|
||||
});
|
||||
|
||||
return {
|
||||
token: doc.token,
|
||||
|
@@ -80,7 +80,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
}
|
||||
}
|
||||
|
||||
const channel = await this.channelsRepository.insert({
|
||||
const channel = await this.channelsRepository.insertOne({
|
||||
id: this.idService.gen(),
|
||||
userId: me.id,
|
||||
name: ps.name,
|
||||
@@ -89,7 +89,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
isSensitive: ps.isSensitive ?? false,
|
||||
...(ps.color !== undefined ? { color: ps.color } : {}),
|
||||
allowRenoteToExternal: ps.allowRenoteToExternal ?? true,
|
||||
} as MiChannel).then(x => this.channelsRepository.findOneByOrFail(x.identifiers[0]));
|
||||
} as MiChannel);
|
||||
|
||||
return await this.channelEntityService.pack(channel, me);
|
||||
});
|
||||
|
@@ -54,7 +54,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
folderId: ps.folderId ?? IsNull(),
|
||||
});
|
||||
|
||||
return await Promise.all(files.map(file => this.driveFileEntityService.pack(file, { self: true })));
|
||||
return await this.driveFileEntityService.packMany(files, { self: true });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -75,12 +75,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
}
|
||||
|
||||
// Create folder
|
||||
const folder = await this.driveFoldersRepository.insert({
|
||||
const folder = await this.driveFoldersRepository.insertOne({
|
||||
id: this.idService.gen(),
|
||||
name: ps.name,
|
||||
parentId: parent !== null ? parent.id : null,
|
||||
userId: me.id,
|
||||
}).then(x => this.driveFoldersRepository.findOneByOrFail(x.identifiers[0]));
|
||||
});
|
||||
|
||||
const folderObj = await this.driveFolderEntityService.pack(folder);
|
||||
|
||||
|
@@ -59,7 +59,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
private idService: IdService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const flash = await this.flashsRepository.insert({
|
||||
const flash = await this.flashsRepository.insertOne({
|
||||
id: this.idService.gen(),
|
||||
userId: me.id,
|
||||
updatedAt: new Date(),
|
||||
@@ -68,7 +68,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
script: ps.script,
|
||||
permissions: ps.permissions,
|
||||
visibility: ps.visibility,
|
||||
}).then(x => this.flashsRepository.findOneByOrFail(x.identifiers[0]));
|
||||
});
|
||||
|
||||
return await this.flashEntityService.pack(flash);
|
||||
});
|
||||
|
@@ -71,7 +71,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
.limit(ps.limit)
|
||||
.getMany();
|
||||
|
||||
return await Promise.all(requests.map(req => this.followRequestEntityService.pack(req)));
|
||||
return await this.followRequestEntityService.packMany(requests, me);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -76,7 +76,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
const post = await this.galleryPostsRepository.insert(new MiGalleryPost({
|
||||
const post = await this.galleryPostsRepository.insertOne(new MiGalleryPost({
|
||||
id: this.idService.gen(),
|
||||
updatedAt: new Date(),
|
||||
title: ps.title,
|
||||
@@ -84,7 +84,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
userId: me.id,
|
||||
isSensitive: ps.isSensitive,
|
||||
fileIds: files.map(file => file.id),
|
||||
})).then(x => this.galleryPostsRepository.findOneByOrFail(x.identifiers[0]));
|
||||
}));
|
||||
|
||||
return await this.galleryPostEntityService.pack(post, me);
|
||||
});
|
||||
|
@@ -89,14 +89,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
throw new ApiError(meta.errors.tooManyWebhooks);
|
||||
}
|
||||
|
||||
const webhook = await this.webhooksRepository.insert({
|
||||
const webhook = await this.webhooksRepository.insertOne({
|
||||
id: this.idService.gen(),
|
||||
userId: me.id,
|
||||
name: ps.name,
|
||||
url: ps.url,
|
||||
secret: ps.secret,
|
||||
on: ps.on,
|
||||
}).then(x => this.webhooksRepository.findOneByOrFail(x.identifiers[0]));
|
||||
});
|
||||
|
||||
this.globalEventService.publishInternalEvent('webhookCreated', webhook);
|
||||
|
||||
|
@@ -66,13 +66,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
}
|
||||
}
|
||||
|
||||
const ticket = await this.registrationTicketsRepository.insert({
|
||||
const ticket = await this.registrationTicketsRepository.insertOne({
|
||||
id: this.idService.gen(),
|
||||
createdBy: me,
|
||||
createdById: me.id,
|
||||
expiresAt: policies.inviteExpirationTime ? new Date(Date.now() + (policies.inviteExpirationTime * 1000 * 60)) : null,
|
||||
code: generateInviteCode(),
|
||||
}).then(x => this.registrationTicketsRepository.findOneByOrFail(x.identifiers[0]));
|
||||
});
|
||||
|
||||
return await this.inviteCodeEntityService.pack(ticket, me);
|
||||
});
|
||||
|
@@ -144,12 +144,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
}
|
||||
|
||||
// Create vote
|
||||
const vote = await this.pollVotesRepository.insert({
|
||||
const vote = await this.pollVotesRepository.insertOne({
|
||||
id: this.idService.gen(createdAt.getTime()),
|
||||
noteId: note.id,
|
||||
userId: me.id,
|
||||
choice: ps.choice,
|
||||
}).then(x => this.pollVotesRepository.findOneByOrFail(x.identifiers[0]));
|
||||
});
|
||||
|
||||
// Increment votes count
|
||||
const index = ps.choice + 1; // In SQL, array index is 1 based
|
||||
|
@@ -76,7 +76,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
|
||||
const reactions = await query.limit(ps.limit).getMany();
|
||||
|
||||
return await Promise.all(reactions.map(reaction => this.noteReactionEntityService.pack(reaction, me)));
|
||||
return await this.noteReactionEntityService.packMany(reactions, me);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -102,7 +102,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
}
|
||||
});
|
||||
|
||||
const page = await this.pagesRepository.insert(new MiPage({
|
||||
const page = await this.pagesRepository.insertOne(new MiPage({
|
||||
id: this.idService.gen(),
|
||||
updatedAt: new Date(),
|
||||
title: ps.title,
|
||||
@@ -117,7 +117,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
alignCenter: ps.alignCenter,
|
||||
hideTitleWhenPinned: ps.hideTitleWhenPinned,
|
||||
font: ps.font,
|
||||
})).then(x => this.pagesRepository.findOneByOrFail(x.identifiers[0]));
|
||||
}));
|
||||
|
||||
return await this.pageEntityService.pack(page);
|
||||
});
|
||||
|
@@ -92,9 +92,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
.limit(ps.limit)
|
||||
.getMany();
|
||||
|
||||
const _users = assigns.map(({ user, userId }) => user ?? userId);
|
||||
const _userMap = await this.userEntityService.packMany(_users, me, { schema: 'UserDetailed' })
|
||||
.then(users => new Map(users.map(u => [u.id, u])));
|
||||
return await Promise.all(assigns.map(async assign => ({
|
||||
id: assign.id,
|
||||
user: await this.userEntityService.pack(assign.user!, me, { schema: 'UserDetailed' }),
|
||||
user: _userMap.get(assign.userId) ?? await this.userEntityService.pack(assign.user!, me, { schema: 'UserDetailed' }),
|
||||
})));
|
||||
});
|
||||
}
|
||||
|
@@ -118,12 +118,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
const repliedUsersSorted = Object.keys(repliedUsers).sort((a, b) => repliedUsers[b] - repliedUsers[a]);
|
||||
|
||||
// Extract top replied users
|
||||
const topRepliedUsers = repliedUsersSorted.slice(0, ps.limit);
|
||||
const topRepliedUserIds = repliedUsersSorted.slice(0, ps.limit);
|
||||
|
||||
// Make replies object (includes weights)
|
||||
const repliesObj = await Promise.all(topRepliedUsers.map(async (user) => ({
|
||||
user: await this.userEntityService.pack(user, me, { schema: 'UserDetailed' }),
|
||||
weight: repliedUsers[user] / peak,
|
||||
const _userMap = await this.userEntityService.packMany(topRepliedUserIds, me, { schema: 'UserDetailed' })
|
||||
.then(users => new Map(users.map(u => [u.id, u])));
|
||||
const repliesObj = await Promise.all(topRepliedUserIds.map(async (userId) => ({
|
||||
user: _userMap.get(userId) ?? await this.userEntityService.pack(userId, me, { schema: 'UserDetailed' }),
|
||||
weight: repliedUsers[userId] / peak,
|
||||
})));
|
||||
|
||||
return repliesObj;
|
||||
|
@@ -104,11 +104,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
throw new ApiError(meta.errors.tooManyUserLists);
|
||||
}
|
||||
|
||||
const userList = await this.userListsRepository.insert({
|
||||
const userList = await this.userListsRepository.insertOne({
|
||||
id: this.idService.gen(),
|
||||
userId: me.id,
|
||||
name: ps.name,
|
||||
} as MiUserList).then(x => this.userListsRepository.findOneByOrFail(x.identifiers[0]));
|
||||
} as MiUserList);
|
||||
|
||||
const users = (await this.userListMembershipsRepository.findBy({
|
||||
userListId: ps.listId,
|
||||
|
@@ -65,11 +65,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
throw new ApiError(meta.errors.tooManyUserLists);
|
||||
}
|
||||
|
||||
const userList = await this.userListsRepository.insert({
|
||||
const userList = await this.userListsRepository.insertOne({
|
||||
id: this.idService.gen(),
|
||||
userId: me.id,
|
||||
name: ps.name,
|
||||
} as MiUserList).then(x => this.userListsRepository.findOneByOrFail(x.identifiers[0]));
|
||||
} as MiUserList);
|
||||
|
||||
return await this.userListEntityService.pack(userList);
|
||||
});
|
||||
|
@@ -82,14 +82,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
throw new ApiError(meta.errors.cannotReportAdmin);
|
||||
}
|
||||
|
||||
const report = await this.abuseUserReportsRepository.insert({
|
||||
const report = await this.abuseUserReportsRepository.insertOne({
|
||||
id: this.idService.gen(),
|
||||
targetUserId: user.id,
|
||||
targetUserHost: user.host,
|
||||
reporterId: me.id,
|
||||
reporterHost: null,
|
||||
comment: ps.comment,
|
||||
}).then(x => this.abuseUserReportsRepository.findOneByOrFail(x.identifiers[0]));
|
||||
});
|
||||
|
||||
// Publish event to moderators
|
||||
setImmediate(async () => {
|
||||
|
@@ -117,9 +117,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
if (user != null) _users.push(user);
|
||||
}
|
||||
|
||||
return await Promise.all(_users.map(u => this.userEntityService.pack(u, me, {
|
||||
schema: 'UserDetailed',
|
||||
})));
|
||||
const _userMap = await this.userEntityService.packMany(_users, me, { schema: 'UserDetailed' })
|
||||
.then(users => new Map(users.map(u => [u.id, u])));
|
||||
return _users.map(u => _userMap.get(u.id)!);
|
||||
} else {
|
||||
// Lookup user
|
||||
if (typeof ps.host === 'string' && typeof ps.username === 'string') {
|
||||
|
@@ -157,6 +157,7 @@ describe('アンテナ', () => {
|
||||
withReplies: false,
|
||||
excludeBots: false,
|
||||
localOnly: false,
|
||||
notify: false,
|
||||
};
|
||||
assert.deepStrictEqual(response, expected);
|
||||
});
|
||||
|
@@ -9,7 +9,7 @@ process.env.NODE_ENV = 'test';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { loadConfig } from '@/config.js';
|
||||
import { MiUser, UsersRepository } from '@/models/_.js';
|
||||
import { MiRepository, MiUser, UsersRepository, miRepository } from '@/models/_.js';
|
||||
import { secureRndstr } from '@/misc/secure-rndstr.js';
|
||||
import { jobQueue } from '@/boot/common.js';
|
||||
import { api, initTestDb, signup, sleep, successfulApiCall, uploadFile } from '../utils.js';
|
||||
@@ -42,7 +42,7 @@ describe('Account Move', () => {
|
||||
dave = await signup({ username: 'dave' });
|
||||
eve = await signup({ username: 'eve' });
|
||||
frank = await signup({ username: 'frank' });
|
||||
Users = connection.getRepository(MiUser);
|
||||
Users = connection.getRepository(MiUser).extend(miRepository as MiRepository<MiUser>);
|
||||
}, 1000 * 60 * 2);
|
||||
|
||||
afterAll(async () => {
|
||||
|
33
packages/backend/test/e2e/reversi-game.ts
Normal file
33
packages/backend/test/e2e/reversi-game.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
process.env.NODE_ENV = 'test';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { ReversiMatchResponse } from 'misskey-js/entities.js';
|
||||
import { api, signup } from '../utils.js';
|
||||
import type * as misskey from 'misskey-js';
|
||||
|
||||
describe('ReversiGame', () => {
|
||||
let alice: misskey.entities.SignupResponse;
|
||||
let bob: misskey.entities.SignupResponse;
|
||||
|
||||
beforeAll(async () => {
|
||||
alice = await signup({ username: 'alice' });
|
||||
bob = await signup({ username: 'bob' });
|
||||
}, 1000 * 60 * 2);
|
||||
|
||||
test('matches when alice invites bob and bob accepts', async () => {
|
||||
const response1 = await api('reversi/match', { userId: bob.id }, alice);
|
||||
assert.strictEqual(response1.status, 204);
|
||||
assert.strictEqual(response1.body, null);
|
||||
const response2 = await api('reversi/match', { userId: alice.id }, bob);
|
||||
assert.strictEqual(response2.status, 200);
|
||||
assert.notStrictEqual(response2.body, null);
|
||||
const body = response2.body as ReversiMatchResponse;
|
||||
assert.strictEqual(body.user1.id, alice.id);
|
||||
assert.strictEqual(body.user2.id, bob.id);
|
||||
});
|
||||
});
|
@@ -4,77 +4,81 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<MkModalWindow ref="dialogEl" @close="cancel()" @closed="$emit('closed')">
|
||||
<template #header>:{{ emoji.name }}:</template>
|
||||
<template #default>
|
||||
<MkSpacer>
|
||||
<div style="display: flex; flex-direction: column; gap: 1em;">
|
||||
<div :class="$style.emojiImgWrapper">
|
||||
<MkCustomEmoji :name="emoji.name" :normal="true" :useOriginalSize="true" style="height: 100%;"></MkCustomEmoji>
|
||||
</div>
|
||||
<MkKeyValue :copy="`:${emoji.name}:`">
|
||||
<template #key>{{ i18n.ts.name }}</template>
|
||||
<template #value>{{ emoji.name }}</template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue>
|
||||
<template #key>{{ i18n.ts.tags }}</template>
|
||||
<template #value>
|
||||
<div v-if="emoji.aliases.length === 0">{{ i18n.ts.none }}</div>
|
||||
<div v-else :class="$style.aliases">
|
||||
<span v-for="alias in emoji.aliases" :key="alias" :class="$style.alias">
|
||||
{{ alias }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue>
|
||||
<template #key>{{ i18n.ts.category }}</template>
|
||||
<template #value>{{ emoji.category ?? i18n.ts.none }}</template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue>
|
||||
<template #key>{{ i18n.ts.sensitive }}</template>
|
||||
<template #value>{{ emoji.isSensitive ? i18n.ts.yes : i18n.ts.no }}</template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue>
|
||||
<template #key>{{ i18n.ts.localOnly }}</template>
|
||||
<template #value>{{ emoji.localOnly ? i18n.ts.yes : i18n.ts.no }}</template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue>
|
||||
<template #key>{{ i18n.ts.license }}</template>
|
||||
<template #value><Mfm :text="emoji.license ?? i18n.ts.none" /></template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue :copy="emoji.url">
|
||||
<template #key>{{ i18n.ts.emojiUrl }}</template>
|
||||
<template #value>
|
||||
<MkLink :url="emoji.url" target="_blank">{{ emoji.url }}</MkLink>
|
||||
</template>
|
||||
</MkKeyValue>
|
||||
</div>
|
||||
</MkSpacer>
|
||||
</template>
|
||||
</MkModalWindow>
|
||||
<MkModalWindow ref="dialogEl" @close="cancel()" @closed="$emit('closed')">
|
||||
<template #header>:{{ emoji.name }}:</template>
|
||||
<template #default>
|
||||
<MkSpacer>
|
||||
<div style="display: flex; flex-direction: column; gap: 1em;">
|
||||
<div :class="$style.emojiImgWrapper">
|
||||
<MkCustomEmoji :name="emoji.name" :normal="true" :useOriginalSize="true" style="height: 100%;"></MkCustomEmoji>
|
||||
</div>
|
||||
<MkKeyValue :copy="`:${emoji.name}:`">
|
||||
<template #key>{{ i18n.ts.name }}</template>
|
||||
<template #value>{{ emoji.name }}</template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue>
|
||||
<template #key>{{ i18n.ts.tags }}</template>
|
||||
<template #value>
|
||||
<div v-if="emoji.aliases.length === 0">{{ i18n.ts.none }}</div>
|
||||
<div v-else :class="$style.aliases">
|
||||
<span v-for="alias in emoji.aliases" :key="alias" :class="$style.alias">
|
||||
{{ alias }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue>
|
||||
<template #key>{{ i18n.ts.category }}</template>
|
||||
<template #value>{{ emoji.category ?? i18n.ts.none }}</template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue>
|
||||
<template #key>{{ i18n.ts.sensitive }}</template>
|
||||
<template #value>{{ emoji.isSensitive ? i18n.ts.yes : i18n.ts.no }}</template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue>
|
||||
<template #key>{{ i18n.ts.localOnly }}</template>
|
||||
<template #value>{{ emoji.localOnly ? i18n.ts.yes : i18n.ts.no }}</template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue>
|
||||
<template #key>{{ i18n.ts.license }}</template>
|
||||
<template #value><Mfm :text="emoji.license ?? i18n.ts.none"/></template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue :copy="emoji.url">
|
||||
<template #key>{{ i18n.ts.emojiUrl }}</template>
|
||||
<template #value>
|
||||
<MkLink :url="emoji.url" target="_blank">{{ emoji.url }}</MkLink>
|
||||
</template>
|
||||
</MkKeyValue>
|
||||
</div>
|
||||
</MkSpacer>
|
||||
</template>
|
||||
</MkModalWindow>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { defineProps, shallowRef } from 'vue';
|
||||
import MkLink from '@/components/MkLink.vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import MkModalWindow from '@/components/MkModalWindow.vue';
|
||||
import MkKeyValue from '@/components/MkKeyValue.vue';
|
||||
import MkLink from './MkLink.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
emoji: Misskey.entities.EmojiDetailed,
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: 'ok', cropped: Misskey.entities.DriveFile): void;
|
||||
(ev: 'cancel'): void;
|
||||
(ev: 'closed'): void;
|
||||
}>();
|
||||
|
||||
const dialogEl = shallowRef<InstanceType<typeof MkModalWindow>>();
|
||||
const cancel = () => {
|
||||
|
||||
function cancel() {
|
||||
emit('cancel');
|
||||
dialogEl.value!.close();
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
|
@@ -12,10 +12,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<img :src="instance.iconUrl || '/favicon.ico'" alt="" class="icon"/>
|
||||
</div>
|
||||
|
||||
<MkInfo v-if="thereIsUnresolvedAbuseReport" warn class="info">{{ i18n.ts.thereIsUnresolvedAbuseReportWarning }} <MkA to="/admin/abuses" class="_link">{{ i18n.ts.check }}</MkA></MkInfo>
|
||||
<MkInfo v-if="noMaintainerInformation" warn class="info">{{ i18n.ts.noMaintainerInformationWarning }} <MkA to="/admin/settings" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
|
||||
<MkInfo v-if="noBotProtection" warn class="info">{{ i18n.ts.noBotProtectionWarning }} <MkA to="/admin/security" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
|
||||
<MkInfo v-if="noEmailServer" warn class="info">{{ i18n.ts.noEmailServerWarning }} <MkA to="/admin/email-settings" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
|
||||
<div class="_gaps_s">
|
||||
<MkInfo v-if="thereIsUnresolvedAbuseReport" warn>{{ i18n.ts.thereIsUnresolvedAbuseReportWarning }} <MkA to="/admin/abuses" class="_link">{{ i18n.ts.check }}</MkA></MkInfo>
|
||||
<MkInfo v-if="noMaintainerInformation" warn>{{ i18n.ts.noMaintainerInformationWarning }} <MkA to="/admin/settings" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
|
||||
<MkInfo v-if="noInquiryUrl" warn>{{ i18n.ts.noInquiryUrlWarning }} <MkA to="/admin/moderation" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
|
||||
<MkInfo v-if="noBotProtection" warn>{{ i18n.ts.noBotProtectionWarning }} <MkA to="/admin/security" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
|
||||
<MkInfo v-if="noEmailServer" warn>{{ i18n.ts.noEmailServerWarning }} <MkA to="/admin/email-settings" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
|
||||
</div>
|
||||
|
||||
<MkSuperMenu :def="menuDef" :grid="narrow"></MkSuperMenu>
|
||||
</div>
|
||||
@@ -61,6 +64,7 @@ const pageProps = ref({});
|
||||
let noMaintainerInformation = isEmpty(instance.maintainerName) || isEmpty(instance.maintainerEmail);
|
||||
let noBotProtection = !instance.disableRegistration && !instance.enableHcaptcha && !instance.enableRecaptcha && !instance.enableTurnstile;
|
||||
let noEmailServer = !instance.enableEmail;
|
||||
let noInquiryUrl = isEmpty(instance.inquiryUrl);
|
||||
const thereIsUnresolvedAbuseReport = ref(false);
|
||||
const currentPage = computed(() => router.currentRef.value.child);
|
||||
|
||||
@@ -348,10 +352,6 @@ defineExpose({
|
||||
|
||||
> .nav {
|
||||
.lxpfedzu {
|
||||
> .info {
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
> .banner {
|
||||
margin: 16px;
|
||||
|
||||
|
@@ -30,6 +30,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template #label>{{ i18n.ts.privacyPolicyUrl }}</template>
|
||||
</MkInput>
|
||||
|
||||
<MkInput v-model="inquiryUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label>{{ i18n.ts._serverSettings.inquiryUrl }}</template>
|
||||
<template #caption>{{ i18n.ts._serverSettings.inquiryUrlDescription }}</template>
|
||||
</MkInput>
|
||||
|
||||
<MkTextarea v-model="preservedUsernames">
|
||||
<template #label>{{ i18n.ts.preservedUsernames }}</template>
|
||||
<template #caption>{{ i18n.ts.preservedUsernamesDescription }}</template>
|
||||
@@ -86,6 +92,7 @@ const hiddenTags = ref<string>('');
|
||||
const preservedUsernames = ref<string>('');
|
||||
const tosUrl = ref<string | null>(null);
|
||||
const privacyPolicyUrl = ref<string | null>(null);
|
||||
const inquiryUrl = ref<string | null>(null);
|
||||
|
||||
async function init() {
|
||||
const meta = await misskeyApi('admin/meta');
|
||||
@@ -97,6 +104,7 @@ async function init() {
|
||||
preservedUsernames.value = meta.preservedUsernames.join('\n');
|
||||
tosUrl.value = meta.tosUrl;
|
||||
privacyPolicyUrl.value = meta.privacyPolicyUrl;
|
||||
inquiryUrl.value = meta.inquiryUrl;
|
||||
}
|
||||
|
||||
function save() {
|
||||
@@ -105,6 +113,7 @@ function save() {
|
||||
emailRequiredForSignup: emailRequiredForSignup.value,
|
||||
tosUrl: tosUrl.value,
|
||||
privacyPolicyUrl: privacyPolicyUrl.value,
|
||||
inquiryUrl: inquiryUrl.value,
|
||||
sensitiveWords: sensitiveWords.value.split('\n'),
|
||||
prohibitedWords: prohibitedWords.value.split('\n'),
|
||||
hiddenTags: hiddenTags.value.split('\n'),
|
||||
|
@@ -7,7 +7,21 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkStickyContainer>
|
||||
<template #header><MkPageHeader/></template>
|
||||
<MkSpacer :contentMax="600" :marginMin="20">
|
||||
<div>{{ instance.maintainerEmail }}</div>
|
||||
<div class="_gaps">
|
||||
<MkKeyValue>
|
||||
<template #key>{{ i18n.ts.inquiry }}</template>
|
||||
<template #value>
|
||||
<MkLink :url="instance.inquiryUrl" target="_blank">{{ instance.inquiryUrl }}</MkLink>
|
||||
</template>
|
||||
</MkKeyValue>
|
||||
|
||||
<MkKeyValue>
|
||||
<template #key>{{ i18n.ts.email }}</template>
|
||||
<template #value>
|
||||
<div>{{ instance.maintainerEmail }}</div>
|
||||
</template>
|
||||
</MkKeyValue>
|
||||
</div>
|
||||
</MkSpacer>
|
||||
</MkStickyContainer>
|
||||
</template>
|
||||
@@ -16,6 +30,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||
import { instance } from '@/instance.js';
|
||||
import MkKeyValue from '@/components/MkKeyValue.vue';
|
||||
import MkLink from '@/components/MkLink.vue';
|
||||
|
||||
definePageMetadata(() => ({
|
||||
title: i18n.ts.inquiry,
|
||||
|
@@ -6,15 +6,16 @@
|
||||
import * as Misskey from 'misskey-js';
|
||||
|
||||
export function shouldCollapsed(note: Misskey.entities.Note, urls: string[]): boolean {
|
||||
const collapsed = note.cw == null && note.text != null && (
|
||||
(note.text.includes('$[x2')) ||
|
||||
(note.text.includes('$[x3')) ||
|
||||
(note.text.includes('$[x4')) ||
|
||||
(note.text.includes('$[scale')) ||
|
||||
(note.text.split('\n').length > 9) ||
|
||||
(note.text.length > 500) ||
|
||||
(note.files.length >= 5) ||
|
||||
(urls.length >= 4)
|
||||
const collapsed = note.cw == null && (
|
||||
note.text != null && (
|
||||
(note.text.includes('$[x2')) ||
|
||||
(note.text.includes('$[x3')) ||
|
||||
(note.text.includes('$[x4')) ||
|
||||
(note.text.includes('$[scale')) ||
|
||||
(note.text.split('\n').length > 9) ||
|
||||
(note.text.length > 500) ||
|
||||
(urls.length >= 4)
|
||||
) || note.files.length >= 5
|
||||
);
|
||||
|
||||
return collapsed;
|
||||
|
@@ -1,8 +1,9 @@
|
||||
{
|
||||
"type": "module",
|
||||
"name": "misskey-js",
|
||||
"version": "2024.5.0-rc.6",
|
||||
"version": "2024.5.0",
|
||||
"description": "Misskey SDK for JavaScript",
|
||||
"license": "MIT",
|
||||
"main": "./built/index.js",
|
||||
"types": "./built/index.d.ts",
|
||||
"exports": {
|
||||
@@ -30,7 +31,8 @@
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/misskey-dev/misskey.js.git"
|
||||
"url": "https://github.com/misskey-dev/misskey.git",
|
||||
"directory": "packages/misskey-js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@microsoft/api-extractor": "7.43.1",
|
||||
|
@@ -4449,6 +4449,8 @@ export type components = {
|
||||
isActive: boolean;
|
||||
/** @default false */
|
||||
hasUnreadNote: boolean;
|
||||
/** @default false */
|
||||
notify: boolean;
|
||||
};
|
||||
Clip: {
|
||||
/**
|
||||
@@ -4831,6 +4833,7 @@ export type components = {
|
||||
impressumUrl: string | null;
|
||||
logoImageUrl: string | null;
|
||||
privacyPolicyUrl: string | null;
|
||||
inquiryUrl: string | null;
|
||||
serverRules: string[];
|
||||
themeColor: string | null;
|
||||
policies: components['schemas']['RolePolicies'];
|
||||
@@ -4978,6 +4981,7 @@ export type operations = {
|
||||
shortName: string | null;
|
||||
objectStorageS3ForcePathStyle: boolean;
|
||||
privacyPolicyUrl: string | null;
|
||||
inquiryUrl: string | null;
|
||||
repositoryUrl: string | null;
|
||||
/**
|
||||
* @deprecated
|
||||
@@ -8906,6 +8910,7 @@ export type operations = {
|
||||
feedbackUrl?: string | null;
|
||||
impressumUrl?: string | null;
|
||||
privacyPolicyUrl?: string | null;
|
||||
inquiryUrl?: string | null;
|
||||
useObjectStorage?: boolean;
|
||||
objectStorageBaseUrl?: string | null;
|
||||
objectStorageBucket?: string | null;
|
||||
|
Reference in New Issue
Block a user