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