Merge branch 'develop' into sonic
This commit is contained in:
		
						commit
						838066a308
					
				
					 406 changed files with 3521 additions and 3234 deletions
				
			
		
							
								
								
									
										10
									
								
								CHANGELOG.md
									
										
									
									
									
								
							
							
						
						
									
										10
									
								
								CHANGELOG.md
									
										
									
									
									
								
							| 
						 | 
					@ -10,6 +10,16 @@
 | 
				
			||||||
You should also include the user name that made the change.
 | 
					You should also include the user name that made the change.
 | 
				
			||||||
-->
 | 
					-->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 12.x.x (unreleased)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Improvements
 | 
				
			||||||
 | 
					- Bull Dashboardを組み込み、ジョブキューの確認や操作を行えるように @syuilo
 | 
				
			||||||
 | 
					- Check that installed Node.js version fulfills version requirement @ThatOneCalculator
 | 
				
			||||||
 | 
					- Server: performance improvements @syuilo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Bugfixes
 | 
				
			||||||
 | 
					- API: fix endpoint endpoint @Johann150
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## 12.108.1 (2022/03/12)
 | 
					## 12.108.1 (2022/03/12)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Bugfixes
 | 
					### Bugfixes
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -198,11 +198,13 @@ MongoDBの時とは違い、findOneでレコードを取得する時に対象レ
 | 
				
			||||||
MongoDBは`null`で返してきてたので、その感覚で`if (x === null)`とか書くとバグる。代わりに`if (x == null)`と書いてください
 | 
					MongoDBは`null`で返してきてたので、その感覚で`if (x === null)`とか書くとバグる。代わりに`if (x == null)`と書いてください
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Migration作成方法
 | 
					### Migration作成方法
 | 
				
			||||||
```
 | 
					packages/backendで:
 | 
				
			||||||
npx ts-node ./node_modules/typeorm/cli.js migration:generate -n 変更の名前 -o
 | 
					```sh
 | 
				
			||||||
 | 
					npx typeorm migration:generate -d ormconfig.js -o <migration name>
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
作成されたスクリプトは不必要な変更を含むため除去してください。
 | 
					- 生成後、ファイルをmigration下に移してください
 | 
				
			||||||
 | 
					- 作成されたスクリプトは不必要な変更を含むため除去してください
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### コネクションには`markRaw`せよ
 | 
					### コネクションには`markRaw`せよ
 | 
				
			||||||
**Vueのコンポーネントのdataオプションとして**misskey.jsのコネクションを設定するとき、必ず`markRaw`でラップしてください。インスタンスが不必要にリアクティブ化されることで、misskey.js内の処理で不具合が発生するとともに、パフォーマンス上の問題にも繋がる。なお、Composition APIを使う場合はこの限りではない(リアクティブ化はマニュアルなため)。
 | 
					**Vueのコンポーネントのdataオプションとして**misskey.jsのコネクションを設定するとき、必ず`markRaw`でラップしてください。インスタンスが不必要にリアクティブ化されることで、misskey.js内の処理で不具合が発生するとともに、パフォーマンス上の問題にも繋がる。なお、Composition APIを使う場合はこの限りではない(リアクティブ化はマニュアルなため)。
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -840,6 +840,8 @@ tenMinutes: "10分"
 | 
				
			||||||
oneHour: "1時間"
 | 
					oneHour: "1時間"
 | 
				
			||||||
oneDay: "1日"
 | 
					oneDay: "1日"
 | 
				
			||||||
oneWeek: "1週間"
 | 
					oneWeek: "1週間"
 | 
				
			||||||
 | 
					reflectMayTakeTime: "反映されるまで時間がかかる場合があります。"
 | 
				
			||||||
 | 
					failedToFetchAccountInformation: "アカウント情報の取得に失敗しました"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
_emailUnavailable:
 | 
					_emailUnavailable:
 | 
				
			||||||
  used: "既に使用されています"
 | 
					  used: "既に使用されています"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,8 +13,7 @@
 | 
				
			||||||
		"start": "cd packages/backend && node --experimental-json-modules ./built/index.js",
 | 
							"start": "cd packages/backend && node --experimental-json-modules ./built/index.js",
 | 
				
			||||||
		"start:test": "cd packages/backend && cross-env NODE_ENV=test node --experimental-json-modules ./built/index.js",
 | 
							"start:test": "cd packages/backend && cross-env NODE_ENV=test node --experimental-json-modules ./built/index.js",
 | 
				
			||||||
		"init": "npm run migrate",
 | 
							"init": "npm run migrate",
 | 
				
			||||||
		"ormconfig": "node ./packages/backend/ormconfig.js",
 | 
							"migrate": "cd packages/backend && npx typeorm migration:run -d ormconfig.js",
 | 
				
			||||||
		"migrate": "cd packages/backend && npx typeorm migration:run",
 | 
					 | 
				
			||||||
		"migrateandstart": "npm run migrate && npm run start",
 | 
							"migrateandstart": "npm run migrate && npm run start",
 | 
				
			||||||
		"gulp": "gulp build",
 | 
							"gulp": "gulp build",
 | 
				
			||||||
		"watch": "npm run dev",
 | 
							"watch": "npm run dev",
 | 
				
			||||||
| 
						 | 
					@ -42,10 +41,10 @@
 | 
				
			||||||
		"js-yaml": "4.1.0"
 | 
							"js-yaml": "4.1.0"
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	"devDependencies": {
 | 
						"devDependencies": {
 | 
				
			||||||
		"@typescript-eslint/parser": "5.14.0",
 | 
							"@typescript-eslint/parser": "5.16.0",
 | 
				
			||||||
		"cross-env": "7.0.3",
 | 
							"cross-env": "7.0.3",
 | 
				
			||||||
		"cypress": "9.5.0",
 | 
							"cypress": "9.5.2",
 | 
				
			||||||
		"start-server-and-test": "1.14.0",
 | 
							"start-server-and-test": "1.14.0",
 | 
				
			||||||
		"typescript": "4.6.2"
 | 
							"typescript": "4.6.3"
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,8 @@
 | 
				
			||||||
 | 
					import { DataSource } from 'typeorm';
 | 
				
			||||||
import config from './built/config/index.js';
 | 
					import config from './built/config/index.js';
 | 
				
			||||||
import { entities } from './built/db/postgre.js';
 | 
					import { entities } from './built/db/postgre.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default new DataSource({
 | 
				
			||||||
	type: 'postgres',
 | 
						type: 'postgres',
 | 
				
			||||||
	host: config.db.host,
 | 
						host: config.db.host,
 | 
				
			||||||
	port: config.db.port,
 | 
						port: config.db.port,
 | 
				
			||||||
| 
						 | 
					@ -11,7 +12,4 @@ export default {
 | 
				
			||||||
	extra: config.db.extra,
 | 
						extra: config.db.extra,
 | 
				
			||||||
	entities: entities,
 | 
						entities: entities,
 | 
				
			||||||
	migrations: ['migration/*.js'],
 | 
						migrations: ['migration/*.js'],
 | 
				
			||||||
	cli: {
 | 
					});
 | 
				
			||||||
		migrationsDir: 'migration'
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,7 +3,6 @@
 | 
				
			||||||
	"private": true,
 | 
						"private": true,
 | 
				
			||||||
	"type": "module",
 | 
						"type": "module",
 | 
				
			||||||
	"scripts": {
 | 
						"scripts": {
 | 
				
			||||||
		"init": "npm run migrate",
 | 
					 | 
				
			||||||
		"build": "tsc -p tsconfig.json || echo done. && tsc-alias -p tsconfig.json",
 | 
							"build": "tsc -p tsconfig.json || echo done. && tsc-alias -p tsconfig.json",
 | 
				
			||||||
		"watch": "node watch.mjs",
 | 
							"watch": "node watch.mjs",
 | 
				
			||||||
		"lint": "eslint --quiet src/**/*.ts",
 | 
							"lint": "eslint --quiet src/**/*.ts",
 | 
				
			||||||
| 
						 | 
					@ -15,7 +14,7 @@
 | 
				
			||||||
		"lodash": "^4.17.21"
 | 
							"lodash": "^4.17.21"
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	"dependencies": {
 | 
						"dependencies": {
 | 
				
			||||||
		"@discordapp/twemoji": "13.1.0",
 | 
							"@discordapp/twemoji": "13.1.1",
 | 
				
			||||||
		"@elastic/elasticsearch": "7.11.0",
 | 
							"@elastic/elasticsearch": "7.11.0",
 | 
				
			||||||
		"@koa/cors": "3.1.0",
 | 
							"@koa/cors": "3.1.0",
 | 
				
			||||||
		"@koa/multer": "3.0.0",
 | 
							"@koa/multer": "3.0.0",
 | 
				
			||||||
| 
						 | 
					@ -31,7 +30,7 @@
 | 
				
			||||||
		"@types/jsdom": "16.2.14",
 | 
							"@types/jsdom": "16.2.14",
 | 
				
			||||||
		"@types/jsonld": "1.5.6",
 | 
							"@types/jsonld": "1.5.6",
 | 
				
			||||||
		"@types/koa": "2.13.4",
 | 
							"@types/koa": "2.13.4",
 | 
				
			||||||
		"@types/koa-bodyparser": "4.3.6",
 | 
							"@types/koa-bodyparser": "4.3.7",
 | 
				
			||||||
		"@types/koa-cors": "0.0.2",
 | 
							"@types/koa-cors": "0.0.2",
 | 
				
			||||||
		"@types/koa-favicon": "2.0.21",
 | 
							"@types/koa-favicon": "2.0.21",
 | 
				
			||||||
		"@types/koa-logger": "3.1.2",
 | 
							"@types/koa-logger": "3.1.2",
 | 
				
			||||||
| 
						 | 
					@ -42,7 +41,7 @@
 | 
				
			||||||
		"@types/koa__multer": "2.0.4",
 | 
							"@types/koa__multer": "2.0.4",
 | 
				
			||||||
		"@types/koa__router": "8.0.11",
 | 
							"@types/koa__router": "8.0.11",
 | 
				
			||||||
		"@types/mocha": "9.1.0",
 | 
							"@types/mocha": "9.1.0",
 | 
				
			||||||
		"@types/node": "17.0.21",
 | 
							"@types/node": "17.0.23",
 | 
				
			||||||
		"@types/node-fetch": "3.0.3",
 | 
							"@types/node-fetch": "3.0.3",
 | 
				
			||||||
		"@types/nodemailer": "6.4.4",
 | 
							"@types/nodemailer": "6.4.4",
 | 
				
			||||||
		"@types/oauth": "0.9.1",
 | 
							"@types/oauth": "0.9.1",
 | 
				
			||||||
| 
						 | 
					@ -56,8 +55,8 @@
 | 
				
			||||||
		"@types/redis": "4.0.11",
 | 
							"@types/redis": "4.0.11",
 | 
				
			||||||
		"@types/rename": "1.0.4",
 | 
							"@types/rename": "1.0.4",
 | 
				
			||||||
		"@types/sanitize-html": "2.6.2",
 | 
							"@types/sanitize-html": "2.6.2",
 | 
				
			||||||
		"@types/sharp": "0.29.5",
 | 
							"@types/sharp": "0.30.0",
 | 
				
			||||||
		"@types/sinonjs__fake-timers": "8.1.1",
 | 
							"@types/sinonjs__fake-timers": "8.1.2",
 | 
				
			||||||
		"@types/speakeasy": "2.0.7",
 | 
							"@types/speakeasy": "2.0.7",
 | 
				
			||||||
		"@types/throttle-debounce": "2.1.0",
 | 
							"@types/throttle-debounce": "2.1.0",
 | 
				
			||||||
		"@types/tinycolor2": "1.4.3",
 | 
							"@types/tinycolor2": "1.4.3",
 | 
				
			||||||
| 
						 | 
					@ -65,19 +64,20 @@
 | 
				
			||||||
		"@types/uuid": "8.3.4",
 | 
							"@types/uuid": "8.3.4",
 | 
				
			||||||
		"@types/web-push": "3.3.2",
 | 
							"@types/web-push": "3.3.2",
 | 
				
			||||||
		"@types/websocket": "1.0.5",
 | 
							"@types/websocket": "1.0.5",
 | 
				
			||||||
		"@types/ws": "8.5.2",
 | 
							"@types/ws": "8.5.3",
 | 
				
			||||||
		"@typescript-eslint/eslint-plugin": "5.14.0",
 | 
							"@typescript-eslint/eslint-plugin": "5.16.0",
 | 
				
			||||||
		"@typescript-eslint/parser": "5.14.0",
 | 
							"@typescript-eslint/parser": "5.16.0",
 | 
				
			||||||
 | 
							"@bull-board/koa": "3.10.1",
 | 
				
			||||||
		"abort-controller": "3.0.0",
 | 
							"abort-controller": "3.0.0",
 | 
				
			||||||
		"ajv": "8.10.0",
 | 
							"ajv": "8.11.0",
 | 
				
			||||||
		"archiver": "5.3.0",
 | 
							"archiver": "5.3.0",
 | 
				
			||||||
		"autobind-decorator": "2.4.0",
 | 
							"autobind-decorator": "2.4.0",
 | 
				
			||||||
		"autwh": "0.1.0",
 | 
							"autwh": "0.1.0",
 | 
				
			||||||
		"aws-sdk": "2.1079.0",
 | 
							"aws-sdk": "2.1100.0",
 | 
				
			||||||
		"bcryptjs": "2.4.3",
 | 
							"bcryptjs": "2.4.3",
 | 
				
			||||||
		"blurhash": "1.1.5",
 | 
							"blurhash": "1.1.5",
 | 
				
			||||||
		"broadcast-channel": "4.10.0",
 | 
							"broadcast-channel": "4.10.0",
 | 
				
			||||||
		"bull": "4.7.0",
 | 
							"bull": "4.8.1",
 | 
				
			||||||
		"cacheable-lookup": "6.0.4",
 | 
							"cacheable-lookup": "6.0.4",
 | 
				
			||||||
		"cafy": "15.2.1",
 | 
							"cafy": "15.2.1",
 | 
				
			||||||
		"cbor": "8.1.0",
 | 
							"cbor": "8.1.0",
 | 
				
			||||||
| 
						 | 
					@ -89,19 +89,19 @@
 | 
				
			||||||
		"date-fns": "2.28.0",
 | 
							"date-fns": "2.28.0",
 | 
				
			||||||
		"deep-email-validator": "0.1.21",
 | 
							"deep-email-validator": "0.1.21",
 | 
				
			||||||
		"escape-regexp": "0.0.1",
 | 
							"escape-regexp": "0.0.1",
 | 
				
			||||||
		"eslint": "8.10.0",
 | 
							"eslint": "8.11.0",
 | 
				
			||||||
		"eslint-plugin-import": "2.25.4",
 | 
							"eslint-plugin-import": "2.25.4",
 | 
				
			||||||
		"feed": "4.2.2",
 | 
							"feed": "4.2.2",
 | 
				
			||||||
		"file-type": "17.1.1",
 | 
							"file-type": "17.1.1",
 | 
				
			||||||
		"fluent-ffmpeg": "2.1.2",
 | 
							"fluent-ffmpeg": "2.1.2",
 | 
				
			||||||
		"got": "12.0.1",
 | 
							"got": "12.0.2",
 | 
				
			||||||
		"hpagent": "0.1.2",
 | 
							"hpagent": "0.1.2",
 | 
				
			||||||
		"http-signature": "1.3.6",
 | 
							"http-signature": "1.3.6",
 | 
				
			||||||
		"ip-cidr": "3.0.4",
 | 
							"ip-cidr": "3.0.4",
 | 
				
			||||||
		"is-svg": "4.3.2",
 | 
							"is-svg": "4.3.2",
 | 
				
			||||||
		"js-yaml": "4.1.0",
 | 
							"js-yaml": "4.1.0",
 | 
				
			||||||
		"jsdom": "19.0.0",
 | 
							"jsdom": "19.0.0",
 | 
				
			||||||
		"json5": "2.2.0",
 | 
							"json5": "2.2.1",
 | 
				
			||||||
		"json5-loader": "4.0.1",
 | 
							"json5-loader": "4.0.1",
 | 
				
			||||||
		"jsonld": "5.2.0",
 | 
							"jsonld": "5.2.0",
 | 
				
			||||||
		"jsrsasign": "8.0.20",
 | 
							"jsrsasign": "8.0.20",
 | 
				
			||||||
| 
						 | 
					@ -115,14 +115,14 @@
 | 
				
			||||||
		"koa-slow": "2.1.0",
 | 
							"koa-slow": "2.1.0",
 | 
				
			||||||
		"koa-views": "7.0.2",
 | 
							"koa-views": "7.0.2",
 | 
				
			||||||
		"mfm-js": "0.21.0",
 | 
							"mfm-js": "0.21.0",
 | 
				
			||||||
		"mime-types": "2.1.34",
 | 
							"mime-types": "2.1.35",
 | 
				
			||||||
		"misskey-js": "0.0.14",
 | 
							"misskey-js": "0.0.14",
 | 
				
			||||||
		"mocha": "9.2.1",
 | 
							"mocha": "9.2.2",
 | 
				
			||||||
		"ms": "3.0.0-canary.1",
 | 
							"ms": "3.0.0-canary.1",
 | 
				
			||||||
		"multer": "1.4.4",
 | 
							"multer": "1.4.4",
 | 
				
			||||||
		"nested-property": "4.0.0",
 | 
							"nested-property": "4.0.0",
 | 
				
			||||||
		"node-fetch": "3.2.2",
 | 
							"node-fetch": "3.2.3",
 | 
				
			||||||
		"nodemailer": "6.7.2",
 | 
							"nodemailer": "6.7.3",
 | 
				
			||||||
		"os-utils": "0.0.14",
 | 
							"os-utils": "0.0.14",
 | 
				
			||||||
		"parse5": "6.0.1",
 | 
							"parse5": "6.0.1",
 | 
				
			||||||
		"pg": "8.7.3",
 | 
							"pg": "8.7.3",
 | 
				
			||||||
| 
						 | 
					@ -150,23 +150,25 @@
 | 
				
			||||||
		"sonic-channel": "1.2.6",
 | 
							"sonic-channel": "1.2.6",
 | 
				
			||||||
		"sharp": "0.30.1",
 | 
							"sharp": "0.30.1",
 | 
				
			||||||
		"sharp": "0.30.2",
 | 
							"sharp": "0.30.2",
 | 
				
			||||||
 | 
							"semver": "7.3.5",
 | 
				
			||||||
 | 
							"sharp": "0.30.3",
 | 
				
			||||||
		"speakeasy": "2.0.0",
 | 
							"speakeasy": "2.0.0",
 | 
				
			||||||
		"strict-event-emitter-types": "2.0.0",
 | 
							"strict-event-emitter-types": "2.0.0",
 | 
				
			||||||
		"stringz": "2.1.0",
 | 
							"stringz": "2.1.0",
 | 
				
			||||||
		"style-loader": "3.3.1",
 | 
							"style-loader": "3.3.1",
 | 
				
			||||||
		"summaly": "2.5.0",
 | 
							"summaly": "2.5.0",
 | 
				
			||||||
		"syslog-pro": "1.0.0",
 | 
							"syslog-pro": "1.0.0",
 | 
				
			||||||
		"systeminformation": "5.11.6",
 | 
							"systeminformation": "5.11.9",
 | 
				
			||||||
		"throttle-debounce": "3.0.1",
 | 
							"throttle-debounce": "3.0.1",
 | 
				
			||||||
		"tinycolor2": "1.4.2",
 | 
							"tinycolor2": "1.4.2",
 | 
				
			||||||
		"tmp": "0.2.1",
 | 
							"tmp": "0.2.1",
 | 
				
			||||||
		"ts-loader": "9.2.7",
 | 
							"ts-loader": "9.2.8",
 | 
				
			||||||
		"ts-node": "10.7.0",
 | 
							"ts-node": "10.7.0",
 | 
				
			||||||
		"tsc-alias": "1.4.1",
 | 
							"tsc-alias": "1.4.1",
 | 
				
			||||||
		"tsconfig-paths": "3.13.0",
 | 
							"tsconfig-paths": "3.14.0",
 | 
				
			||||||
		"twemoji-parser": "13.1.0",
 | 
							"twemoji-parser": "14.0.0",
 | 
				
			||||||
		"typeorm": "0.2.45",
 | 
							"typeorm": "0.3.3",
 | 
				
			||||||
		"typescript": "4.6.2",
 | 
							"typescript": "4.6.3",
 | 
				
			||||||
		"ulid": "2.3.0",
 | 
							"ulid": "2.3.0",
 | 
				
			||||||
		"unzipper": "0.10.11",
 | 
							"unzipper": "0.10.11",
 | 
				
			||||||
		"uuid": "8.3.2",
 | 
							"uuid": "8.3.2",
 | 
				
			||||||
| 
						 | 
					@ -176,7 +178,7 @@
 | 
				
			||||||
		"xev": "2.0.1"
 | 
							"xev": "2.0.1"
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	"devDependencies": {
 | 
						"devDependencies": {
 | 
				
			||||||
		"@redocly/openapi-core": "1.0.0-beta.83",
 | 
							"@redocly/openapi-core": "1.0.0-beta.90",
 | 
				
			||||||
		"@types/fluent-ffmpeg": "2.1.20",
 | 
							"@types/fluent-ffmpeg": "2.1.20",
 | 
				
			||||||
		"cross-env": "7.0.3",
 | 
							"cross-env": "7.0.3",
 | 
				
			||||||
		"execa": "6.1.0"
 | 
							"execa": "6.1.0"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,7 +6,7 @@ import cluster from 'node:cluster';
 | 
				
			||||||
import chalk from 'chalk';
 | 
					import chalk from 'chalk';
 | 
				
			||||||
import chalkTemplate from 'chalk-template';
 | 
					import chalkTemplate from 'chalk-template';
 | 
				
			||||||
import * as portscanner from 'portscanner';
 | 
					import * as portscanner from 'portscanner';
 | 
				
			||||||
import { getConnection } from 'typeorm';
 | 
					import semver from 'semver';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Logger from '@/services/logger.js';
 | 
					import Logger from '@/services/logger.js';
 | 
				
			||||||
import loadConfig from '@/config/load.js';
 | 
					import loadConfig from '@/config/load.js';
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@ import { Config } from '@/config/types.js';
 | 
				
			||||||
import { lessThan } from '@/prelude/array.js';
 | 
					import { lessThan } from '@/prelude/array.js';
 | 
				
			||||||
import { envOption } from '../env.js';
 | 
					import { envOption } from '../env.js';
 | 
				
			||||||
import { showMachineInfo } from '@/misc/show-machine-info.js';
 | 
					import { showMachineInfo } from '@/misc/show-machine-info.js';
 | 
				
			||||||
import { initDb } from '../db/postgre.js';
 | 
					import { db, initDb } from '../db/postgre.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const _filename = fileURLToPath(import.meta.url);
 | 
					const _filename = fileURLToPath(import.meta.url);
 | 
				
			||||||
const _dirname = dirname(_filename);
 | 
					const _dirname = dirname(_filename);
 | 
				
			||||||
| 
						 | 
					@ -88,10 +88,6 @@ export async function masterMain() {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const runningNodejsVersion = process.version.slice(1).split('.').map(x => parseInt(x, 10));
 | 
					 | 
				
			||||||
const requiredNodejsVersion = [11, 7, 0];
 | 
					 | 
				
			||||||
const satisfyNodejsVersion = !lessThan(runningNodejsVersion, requiredNodejsVersion);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function showEnvironment(): void {
 | 
					function showEnvironment(): void {
 | 
				
			||||||
	const env = process.env.NODE_ENV;
 | 
						const env = process.env.NODE_ENV;
 | 
				
			||||||
	const logger = bootLogger.createSubLogger('env');
 | 
						const logger = bootLogger.createSubLogger('env');
 | 
				
			||||||
| 
						 | 
					@ -108,10 +104,11 @@ function showEnvironment(): void {
 | 
				
			||||||
function showNodejsVersion(): void {
 | 
					function showNodejsVersion(): void {
 | 
				
			||||||
	const nodejsLogger = bootLogger.createSubLogger('nodejs');
 | 
						const nodejsLogger = bootLogger.createSubLogger('nodejs');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	nodejsLogger.info(`Version ${runningNodejsVersion.join('.')}`);
 | 
						nodejsLogger.info(`Version ${process.version} detected.`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!satisfyNodejsVersion) {
 | 
						const minVersion = fs.readFileSync(`${_dirname}/../../../../.node-version`, 'utf-8').trim();
 | 
				
			||||||
		nodejsLogger.error(`Node.js version is less than ${requiredNodejsVersion.join('.')}. Please upgrade it.`, null, true);
 | 
						if (semver.lt(process.version, minVersion)) {
 | 
				
			||||||
 | 
							nodejsLogger.error(`At least Node.js ${minVersion} required!`);
 | 
				
			||||||
		process.exit(1);
 | 
							process.exit(1);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -146,7 +143,7 @@ async function connectDb(): Promise<void> {
 | 
				
			||||||
	try {
 | 
						try {
 | 
				
			||||||
		dbLogger.info('Connecting...');
 | 
							dbLogger.info('Connecting...');
 | 
				
			||||||
		await initDb();
 | 
							await initDb();
 | 
				
			||||||
		const v = await getConnection().query('SHOW server_version').then(x => x[0].server_version);
 | 
							const v = await db.query('SHOW server_version').then(x => x[0].server_version);
 | 
				
			||||||
		dbLogger.succ(`Connected: v${v}`);
 | 
							dbLogger.succ(`Connected: v${v}`);
 | 
				
			||||||
	} catch (e) {
 | 
						} catch (e) {
 | 
				
			||||||
		dbLogger.error('Cannot connect', null, true);
 | 
							dbLogger.error('Cannot connect', null, true);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,9 +2,10 @@
 | 
				
			||||||
import pg from 'pg';
 | 
					import pg from 'pg';
 | 
				
			||||||
pg.types.setTypeParser(20, Number);
 | 
					pg.types.setTypeParser(20, Number);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { createConnection, Logger, getConnection } from 'typeorm';
 | 
					import { Logger, DataSource } from 'typeorm';
 | 
				
			||||||
import * as highlight from 'cli-highlight';
 | 
					import * as highlight from 'cli-highlight';
 | 
				
			||||||
import config from '@/config/index.js';
 | 
					import config from '@/config/index.js';
 | 
				
			||||||
 | 
					import { envOption } from '../env.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { dbLogger } from './logger.js';
 | 
					import { dbLogger } from './logger.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -61,7 +62,6 @@ import { Antenna } from '@/models/entities/antenna.js';
 | 
				
			||||||
import { AntennaNote } from '@/models/entities/antenna-note.js';
 | 
					import { AntennaNote } from '@/models/entities/antenna-note.js';
 | 
				
			||||||
import { PromoNote } from '@/models/entities/promo-note.js';
 | 
					import { PromoNote } from '@/models/entities/promo-note.js';
 | 
				
			||||||
import { PromoRead } from '@/models/entities/promo-read.js';
 | 
					import { PromoRead } from '@/models/entities/promo-read.js';
 | 
				
			||||||
import { envOption } from '../env.js';
 | 
					 | 
				
			||||||
import { Relay } from '@/models/entities/relay.js';
 | 
					import { Relay } from '@/models/entities/relay.js';
 | 
				
			||||||
import { MutedNote } from '@/models/entities/muted-note.js';
 | 
					import { MutedNote } from '@/models/entities/muted-note.js';
 | 
				
			||||||
import { Channel } from '@/models/entities/channel.js';
 | 
					import { Channel } from '@/models/entities/channel.js';
 | 
				
			||||||
| 
						 | 
					@ -74,7 +74,7 @@ import { UserPending } from '@/models/entities/user-pending.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { entities as charts } from '@/services/chart/entities.js';
 | 
					import { entities as charts } from '@/services/chart/entities.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const sqlLogger = dbLogger.createSubLogger('sql', 'white', false);
 | 
					const sqlLogger = dbLogger.createSubLogger('sql', 'gray', false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class MyCustomLogger implements Logger {
 | 
					class MyCustomLogger implements Logger {
 | 
				
			||||||
	private highlight(sql: string) {
 | 
						private highlight(sql: string) {
 | 
				
			||||||
| 
						 | 
					@ -84,9 +84,7 @@ class MyCustomLogger implements Logger {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public logQuery(query: string, parameters?: any[]) {
 | 
						public logQuery(query: string, parameters?: any[]) {
 | 
				
			||||||
		if (envOption.verbose) {
 | 
							sqlLogger.info(this.highlight(query).substring(0, 100));
 | 
				
			||||||
			sqlLogger.info(this.highlight(query));
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public logQueryError(error: string, query: string, parameters?: any[]) {
 | 
						public logQueryError(error: string, query: string, parameters?: any[]) {
 | 
				
			||||||
| 
						 | 
					@ -176,17 +174,9 @@ export const entities = [
 | 
				
			||||||
	...charts,
 | 
						...charts,
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function initDb(justBorrow = false, sync = false, forceRecreate = false) {
 | 
					 | 
				
			||||||
	if (!forceRecreate) {
 | 
					 | 
				
			||||||
		try {
 | 
					 | 
				
			||||||
			const conn = getConnection();
 | 
					 | 
				
			||||||
			return Promise.resolve(conn);
 | 
					 | 
				
			||||||
		} catch (e) {}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const log = process.env.NODE_ENV !== 'production';
 | 
					const log = process.env.NODE_ENV !== 'production';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return createConnection({
 | 
					export const db = new DataSource({
 | 
				
			||||||
	type: 'postgres',
 | 
						type: 'postgres',
 | 
				
			||||||
	host: config.db.host,
 | 
						host: config.db.host,
 | 
				
			||||||
	port: config.db.port,
 | 
						port: config.db.port,
 | 
				
			||||||
| 
						 | 
					@ -197,8 +187,8 @@ export function initDb(justBorrow = false, sync = false, forceRecreate = false)
 | 
				
			||||||
		statement_timeout: 1000 * 10,
 | 
							statement_timeout: 1000 * 10,
 | 
				
			||||||
		...config.db.extra,
 | 
							...config.db.extra,
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
		synchronize: process.env.NODE_ENV === 'test' || sync,
 | 
						synchronize: process.env.NODE_ENV === 'test',
 | 
				
			||||||
		dropSchema: process.env.NODE_ENV === 'test' && !justBorrow,
 | 
						dropSchema: process.env.NODE_ENV === 'test',
 | 
				
			||||||
	cache: !config.db.disableCache ? {
 | 
						cache: !config.db.disableCache ? {
 | 
				
			||||||
		type: 'redis',
 | 
							type: 'redis',
 | 
				
			||||||
		options: {
 | 
							options: {
 | 
				
			||||||
| 
						 | 
					@ -211,20 +201,24 @@ export function initDb(justBorrow = false, sync = false, forceRecreate = false)
 | 
				
			||||||
	} : false,
 | 
						} : false,
 | 
				
			||||||
	logging: log,
 | 
						logging: log,
 | 
				
			||||||
	logger: log ? new MyCustomLogger() : undefined,
 | 
						logger: log ? new MyCustomLogger() : undefined,
 | 
				
			||||||
 | 
						maxQueryExecutionTime: 300,
 | 
				
			||||||
	entities: entities,
 | 
						entities: entities,
 | 
				
			||||||
 | 
						migrations: ['../../migration/*.js'],
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function initDb() {
 | 
				
			||||||
 | 
						await db.connect();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function resetDb() {
 | 
					export async function resetDb() {
 | 
				
			||||||
	const reset = async () => {
 | 
						const reset = async () => {
 | 
				
			||||||
		const conn = await getConnection();
 | 
							const tables = await db.query(`SELECT relname AS "table"
 | 
				
			||||||
		const tables = await conn.query(`SELECT relname AS "table"
 | 
					 | 
				
			||||||
		FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
 | 
							FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
 | 
				
			||||||
		WHERE nspname NOT IN ('pg_catalog', 'information_schema')
 | 
							WHERE nspname NOT IN ('pg_catalog', 'information_schema')
 | 
				
			||||||
			AND C.relkind = 'r'
 | 
								AND C.relkind = 'r'
 | 
				
			||||||
			AND nspname !~ '^pg_toast';`);
 | 
								AND nspname !~ '^pg_toast';`);
 | 
				
			||||||
		for (const table of tables) {
 | 
							for (const table of tables) {
 | 
				
			||||||
			await conn.query(`DELETE FROM "${table.table}" CASCADE`);
 | 
								await db.query(`DELETE FROM "${table.table}" CASCADE`);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
export class Cache<T> {
 | 
					export class Cache<T> {
 | 
				
			||||||
	private cache: Map<string | null, { date: number; value: T; }>;
 | 
						public cache: Map<string | null, { date: number; value: T; }>;
 | 
				
			||||||
	private lifetime: number;
 | 
						private lifetime: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	constructor(lifetime: Cache<never>['lifetime']) {
 | 
						constructor(lifetime: Cache<never>['lifetime']) {
 | 
				
			||||||
| 
						 | 
					@ -28,16 +28,52 @@ export class Cache<T> {
 | 
				
			||||||
		this.cache.delete(key);
 | 
							this.cache.delete(key);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public async fetch(key: string | null, fetcher: () => Promise<T>): Promise<T> {
 | 
						/**
 | 
				
			||||||
 | 
						 * キャッシュがあればそれを返し、無ければfetcherを呼び出して結果をキャッシュ&返します
 | 
				
			||||||
 | 
						 * optional: キャッシュが存在してもvalidatorでfalseを返すとキャッシュ無効扱いにします
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public async fetch(key: string | null, fetcher: () => Promise<T>, validator?: (cachedValue: T) => boolean): Promise<T> {
 | 
				
			||||||
		const cachedValue = this.get(key);
 | 
							const cachedValue = this.get(key);
 | 
				
			||||||
		if (cachedValue !== undefined) {
 | 
							if (cachedValue !== undefined) {
 | 
				
			||||||
 | 
								if (validator) {
 | 
				
			||||||
 | 
									if (validator(cachedValue)) {
 | 
				
			||||||
					// Cache HIT
 | 
										// Cache HIT
 | 
				
			||||||
					return cachedValue;
 | 
										return cachedValue;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									// Cache HIT
 | 
				
			||||||
 | 
									return cachedValue;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Cache MISS
 | 
							// Cache MISS
 | 
				
			||||||
		const value = await fetcher();
 | 
							const value = await fetcher();
 | 
				
			||||||
 | 
							return value;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * キャッシュがあればそれを返し、無ければfetcherを呼び出して結果をキャッシュ&返します
 | 
				
			||||||
 | 
						 * optional: キャッシュが存在してもvalidatorでfalseを返すとキャッシュ無効扱いにします
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public async fetchMaybe(key: string | null, fetcher: () => Promise<T | undefined>, validator?: (cachedValue: T) => boolean): Promise<T | undefined> {
 | 
				
			||||||
 | 
							const cachedValue = this.get(key);
 | 
				
			||||||
 | 
							if (cachedValue !== undefined) {
 | 
				
			||||||
 | 
								if (validator) {
 | 
				
			||||||
 | 
									if (validator(cachedValue)) {
 | 
				
			||||||
 | 
										// Cache HIT
 | 
				
			||||||
 | 
										return cachedValue;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									// Cache HIT
 | 
				
			||||||
 | 
									return cachedValue;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Cache MISS
 | 
				
			||||||
 | 
							const value = await fetcher();
 | 
				
			||||||
 | 
							if (value !== undefined) {
 | 
				
			||||||
			this.set(key, value);
 | 
								this.set(key, value);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		return value;
 | 
							return value;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,17 +1,26 @@
 | 
				
			||||||
import { Antenna } from '@/models/entities/antenna.js';
 | 
					import { Antenna } from '@/models/entities/antenna.js';
 | 
				
			||||||
import { Note } from '@/models/entities/note.js';
 | 
					import { Note } from '@/models/entities/note.js';
 | 
				
			||||||
import { User } from '@/models/entities/user.js';
 | 
					import { User } from '@/models/entities/user.js';
 | 
				
			||||||
import { UserListJoinings, UserGroupJoinings } from '@/models/index.js';
 | 
					import { UserListJoinings, UserGroupJoinings, Blockings } from '@/models/index.js';
 | 
				
			||||||
import { getFullApAccount } from './convert-host.js';
 | 
					import { getFullApAccount } from './convert-host.js';
 | 
				
			||||||
import * as Acct from '@/misc/acct.js';
 | 
					import * as Acct from '@/misc/acct.js';
 | 
				
			||||||
import { Packed } from './schema.js';
 | 
					import { Packed } from './schema.js';
 | 
				
			||||||
 | 
					import { Cache } from './cache.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const blockingCache = new Cache<User['id'][]>(1000 * 60 * 5);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NOTE: フォローしているユーザーのノート、リストのユーザーのノート、グループのユーザーのノート指定はパフォーマンス上の理由で無効になっている
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * noteUserFollowers / antennaUserFollowing はどちらか一方が指定されていればよい
 | 
					 * noteUserFollowers / antennaUserFollowing はどちらか一方が指定されていればよい
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export async function checkHitAntenna(antenna: Antenna, note: (Note | Packed<'Note'>), noteUser: { username: string; host: string | null; }, noteUserFollowers?: User['id'][], antennaUserFollowing?: User['id'][]): Promise<boolean> {
 | 
					export async function checkHitAntenna(antenna: Antenna, note: (Note | Packed<'Note'>), noteUser: { id: User['id']; username: string; host: string | null; }, noteUserFollowers?: User['id'][], antennaUserFollowing?: User['id'][]): Promise<boolean> {
 | 
				
			||||||
	if (note.visibility === 'specified') return false;
 | 
						if (note.visibility === 'specified') return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// アンテナ作成者がノート作成者にブロックされていたらスキップ
 | 
				
			||||||
 | 
						const blockings = await blockingCache.fetch(noteUser.id, () => Blockings.findBy({ blockerId: noteUser.id }).then(res => res.map(x => x.blockeeId)));
 | 
				
			||||||
 | 
						if (blockings.some(blocking => blocking === antenna.userId)) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (note.visibility === 'followers') {
 | 
						if (note.visibility === 'followers') {
 | 
				
			||||||
		if (noteUserFollowers && !noteUserFollowers.includes(antenna.userId)) return false;
 | 
							if (noteUserFollowers && !noteUserFollowers.includes(antenna.userId)) return false;
 | 
				
			||||||
		if (antennaUserFollowing && !antennaUserFollowing.includes(note.userId)) return false;
 | 
							if (antennaUserFollowing && !antennaUserFollowing.includes(note.userId)) return false;
 | 
				
			||||||
| 
						 | 
					@ -23,15 +32,15 @@ export async function checkHitAntenna(antenna: Antenna, note: (Note | Packed<'No
 | 
				
			||||||
		if (noteUserFollowers && !noteUserFollowers.includes(antenna.userId)) return false;
 | 
							if (noteUserFollowers && !noteUserFollowers.includes(antenna.userId)) return false;
 | 
				
			||||||
		if (antennaUserFollowing && !antennaUserFollowing.includes(note.userId)) return false;
 | 
							if (antennaUserFollowing && !antennaUserFollowing.includes(note.userId)) return false;
 | 
				
			||||||
	} else if (antenna.src === 'list') {
 | 
						} else if (antenna.src === 'list') {
 | 
				
			||||||
		const listUsers = (await UserListJoinings.find({
 | 
							const listUsers = (await UserListJoinings.findBy({
 | 
				
			||||||
			userListId: antenna.userListId!,
 | 
								userListId: antenna.userListId!,
 | 
				
			||||||
		})).map(x => x.userId);
 | 
							})).map(x => x.userId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!listUsers.includes(note.userId)) return false;
 | 
							if (!listUsers.includes(note.userId)) return false;
 | 
				
			||||||
	} else if (antenna.src === 'group') {
 | 
						} else if (antenna.src === 'group') {
 | 
				
			||||||
		const joining = await UserGroupJoinings.findOneOrFail(antenna.userGroupJoiningId!);
 | 
							const joining = await UserGroupJoinings.findOneByOrFail({ id: antenna.userGroupJoiningId! });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const groupUsers = (await UserGroupJoinings.find({
 | 
							const groupUsers = (await UserGroupJoinings.findBy({
 | 
				
			||||||
			userGroupId: joining.userGroupId,
 | 
								userGroupId: joining.userGroupId,
 | 
				
			||||||
		})).map(x => x.userId);
 | 
							})).map(x => x.userId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,12 +1,12 @@
 | 
				
			||||||
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
import { Meta } from '@/models/entities/meta.js';
 | 
					import { Meta } from '@/models/entities/meta.js';
 | 
				
			||||||
import { getConnection } from 'typeorm';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
let cache: Meta;
 | 
					let cache: Meta;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function fetchMeta(noCache = false): Promise<Meta> {
 | 
					export async function fetchMeta(noCache = false): Promise<Meta> {
 | 
				
			||||||
	if (!noCache && cache) return cache;
 | 
						if (!noCache && cache) return cache;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return await getConnection().transaction(async transactionalEntityManager => {
 | 
						return await db.transaction(async transactionalEntityManager => {
 | 
				
			||||||
		// 過去のバグでレコードが複数出来てしまっている可能性があるので新しいIDを優先する
 | 
							// 過去のバグでレコードが複数出来てしまっている可能性があるので新しいIDを優先する
 | 
				
			||||||
		const meta = await transactionalEntityManager.findOne(Meta, {
 | 
							const meta = await transactionalEntityManager.findOne(Meta, {
 | 
				
			||||||
			order: {
 | 
								order: {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,5 +5,5 @@ import { Users } from '@/models/index.js';
 | 
				
			||||||
export async function fetchProxyAccount(): Promise<ILocalUser | null> {
 | 
					export async function fetchProxyAccount(): Promise<ILocalUser | null> {
 | 
				
			||||||
	const meta = await fetchMeta();
 | 
						const meta = await fetchMeta();
 | 
				
			||||||
	if (meta.proxyAccountId == null) return null;
 | 
						if (meta.proxyAccountId == null) return null;
 | 
				
			||||||
	return await Users.findOneOrFail(meta.proxyAccountId) as ILocalUser;
 | 
						return await Users.findOneByOrFail({ id: meta.proxyAccountId }) as ILocalUser;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,5 +6,5 @@ import { Cache } from './cache.js';
 | 
				
			||||||
const cache = new Cache<UserKeypair>(Infinity);
 | 
					const cache = new Cache<UserKeypair>(Infinity);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function getUserKeypair(userId: User['id']): Promise<UserKeypair> {
 | 
					export async function getUserKeypair(userId: User['id']): Promise<UserKeypair> {
 | 
				
			||||||
	return await cache.fetch(userId, () => UserKeypairs.findOneOrFail(userId));
 | 
						return await cache.fetch(userId, () => UserKeypairs.findOneByOrFail({ userId: userId }));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
import { In } from 'typeorm';
 | 
					import { In, IsNull } from 'typeorm';
 | 
				
			||||||
import { Emojis } from '@/models/index.js';
 | 
					import { Emojis } from '@/models/index.js';
 | 
				
			||||||
import { Emoji } from '@/models/entities/emoji.js';
 | 
					import { Emoji } from '@/models/entities/emoji.js';
 | 
				
			||||||
import { Note } from '@/models/entities/note.js';
 | 
					import { Note } from '@/models/entities/note.js';
 | 
				
			||||||
| 
						 | 
					@ -52,9 +52,9 @@ export async function populateEmoji(emojiName: string, noteUserHost: string | nu
 | 
				
			||||||
	const { name, host } = parseEmojiStr(emojiName, noteUserHost);
 | 
						const { name, host } = parseEmojiStr(emojiName, noteUserHost);
 | 
				
			||||||
	if (name == null) return null;
 | 
						if (name == null) return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const queryOrNull = async () => (await Emojis.findOne({
 | 
						const queryOrNull = async () => (await Emojis.findOneBy({
 | 
				
			||||||
		name,
 | 
							name,
 | 
				
			||||||
		host,
 | 
							host: host ?? IsNull(),
 | 
				
			||||||
	})) || null;
 | 
						})) || null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const emoji = await cache.fetch(`${name} ${host}`, queryOrNull);
 | 
						const emoji = await cache.fetch(`${name} ${host}`, queryOrNull);
 | 
				
			||||||
| 
						 | 
					@ -112,7 +112,7 @@ export async function prefetchEmojis(emojis: { name: string; host: string | null
 | 
				
			||||||
	for (const host of hosts) {
 | 
						for (const host of hosts) {
 | 
				
			||||||
		emojisQuery.push({
 | 
							emojisQuery.push({
 | 
				
			||||||
			name: In(notCachedEmojis.filter(e => e.host === host).map(e => e.name)),
 | 
								name: In(notCachedEmojis.filter(e => e.host === host).map(e => e.name)),
 | 
				
			||||||
			host: host,
 | 
								host: host ?? IsNull(),
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	const _emojis = emojisQuery.length > 0 ? await Emojis.find({
 | 
						const _emojis = emojisQuery.length > 0 ? await Emojis.find({
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,7 @@ import { emojiRegex } from './emoji-regex.js';
 | 
				
			||||||
import { fetchMeta } from './fetch-meta.js';
 | 
					import { fetchMeta } from './fetch-meta.js';
 | 
				
			||||||
import { Emojis } from '@/models/index.js';
 | 
					import { Emojis } from '@/models/index.js';
 | 
				
			||||||
import { toPunyNullable } from './convert-host.js';
 | 
					import { toPunyNullable } from './convert-host.js';
 | 
				
			||||||
 | 
					import { IsNull } from 'typeorm';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const legacies: Record<string, string> = {
 | 
					const legacies: Record<string, string> = {
 | 
				
			||||||
	'like':     '👍',
 | 
						'like':     '👍',
 | 
				
			||||||
| 
						 | 
					@ -74,8 +75,8 @@ export async function toDbReaction(reaction?: string | null, reacterHost?: strin
 | 
				
			||||||
	const custom = reaction.match(/^:([\w+-]+)(?:@\.)?:$/);
 | 
						const custom = reaction.match(/^:([\w+-]+)(?:@\.)?:$/);
 | 
				
			||||||
	if (custom) {
 | 
						if (custom) {
 | 
				
			||||||
		const name = custom[1];
 | 
							const name = custom[1];
 | 
				
			||||||
		const emoji = await Emojis.findOne({
 | 
							const emoji = await Emojis.findOneBy({
 | 
				
			||||||
			host: reacterHost || null,
 | 
								host: reacterHost ?? IsNull(),
 | 
				
			||||||
			name,
 | 
								name,
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -234,3 +234,9 @@ export interface ILocalUser extends User {
 | 
				
			||||||
export interface IRemoteUser extends User {
 | 
					export interface IRemoteUser extends User {
 | 
				
			||||||
	host: string;
 | 
						host: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type CacheableLocalUser = ILocalUser;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type CacheableRemoteUser = IRemoteUser;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type CacheableUser = CacheableLocalUser | CacheableRemoteUser;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,6 @@
 | 
				
			||||||
import { getRepository, getCustomRepository } from 'typeorm';
 | 
					import { } from 'typeorm';
 | 
				
			||||||
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { Announcement } from './entities/announcement.js';
 | 
					import { Announcement } from './entities/announcement.js';
 | 
				
			||||||
import { AnnouncementRead } from './entities/announcement-read.js';
 | 
					import { AnnouncementRead } from './entities/announcement-read.js';
 | 
				
			||||||
import { Instance } from './entities/instance.js';
 | 
					import { Instance } from './entities/instance.js';
 | 
				
			||||||
| 
						 | 
					@ -63,65 +65,65 @@ import { PasswordResetRequest } from './entities/password-reset-request.js';
 | 
				
			||||||
import { UserPending } from './entities/user-pending.js';
 | 
					import { UserPending } from './entities/user-pending.js';
 | 
				
			||||||
import { InstanceRepository } from './repositories/instance.js';
 | 
					import { InstanceRepository } from './repositories/instance.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const Announcements = getRepository(Announcement);
 | 
					export const Announcements = db.getRepository(Announcement);
 | 
				
			||||||
export const AnnouncementReads = getRepository(AnnouncementRead);
 | 
					export const AnnouncementReads = db.getRepository(AnnouncementRead);
 | 
				
			||||||
export const Apps = getCustomRepository(AppRepository);
 | 
					export const Apps = (AppRepository);
 | 
				
			||||||
export const Notes = getCustomRepository(NoteRepository);
 | 
					export const Notes = (NoteRepository);
 | 
				
			||||||
export const NoteFavorites = getCustomRepository(NoteFavoriteRepository);
 | 
					export const NoteFavorites = (NoteFavoriteRepository);
 | 
				
			||||||
export const NoteWatchings = getRepository(NoteWatching);
 | 
					export const NoteWatchings = db.getRepository(NoteWatching);
 | 
				
			||||||
export const NoteThreadMutings = getRepository(NoteThreadMuting);
 | 
					export const NoteThreadMutings = db.getRepository(NoteThreadMuting);
 | 
				
			||||||
export const NoteReactions = getCustomRepository(NoteReactionRepository);
 | 
					export const NoteReactions = (NoteReactionRepository);
 | 
				
			||||||
export const NoteUnreads = getRepository(NoteUnread);
 | 
					export const NoteUnreads = db.getRepository(NoteUnread);
 | 
				
			||||||
export const Polls = getRepository(Poll);
 | 
					export const Polls = db.getRepository(Poll);
 | 
				
			||||||
export const PollVotes = getRepository(PollVote);
 | 
					export const PollVotes = db.getRepository(PollVote);
 | 
				
			||||||
export const Users = getCustomRepository(UserRepository);
 | 
					export const Users = (UserRepository);
 | 
				
			||||||
export const UserProfiles = getRepository(UserProfile);
 | 
					export const UserProfiles = db.getRepository(UserProfile);
 | 
				
			||||||
export const UserKeypairs = getRepository(UserKeypair);
 | 
					export const UserKeypairs = db.getRepository(UserKeypair);
 | 
				
			||||||
export const UserPendings = getRepository(UserPending);
 | 
					export const UserPendings = db.getRepository(UserPending);
 | 
				
			||||||
export const AttestationChallenges = getRepository(AttestationChallenge);
 | 
					export const AttestationChallenges = db.getRepository(AttestationChallenge);
 | 
				
			||||||
export const UserSecurityKeys = getRepository(UserSecurityKey);
 | 
					export const UserSecurityKeys = db.getRepository(UserSecurityKey);
 | 
				
			||||||
export const UserPublickeys = getRepository(UserPublickey);
 | 
					export const UserPublickeys = db.getRepository(UserPublickey);
 | 
				
			||||||
export const UserLists = getCustomRepository(UserListRepository);
 | 
					export const UserLists = (UserListRepository);
 | 
				
			||||||
export const UserListJoinings = getRepository(UserListJoining);
 | 
					export const UserListJoinings = db.getRepository(UserListJoining);
 | 
				
			||||||
export const UserGroups = getCustomRepository(UserGroupRepository);
 | 
					export const UserGroups = (UserGroupRepository);
 | 
				
			||||||
export const UserGroupJoinings = getRepository(UserGroupJoining);
 | 
					export const UserGroupJoinings = db.getRepository(UserGroupJoining);
 | 
				
			||||||
export const UserGroupInvitations = getCustomRepository(UserGroupInvitationRepository);
 | 
					export const UserGroupInvitations = (UserGroupInvitationRepository);
 | 
				
			||||||
export const UserNotePinings = getRepository(UserNotePining);
 | 
					export const UserNotePinings = db.getRepository(UserNotePining);
 | 
				
			||||||
export const UsedUsernames = getRepository(UsedUsername);
 | 
					export const UsedUsernames = db.getRepository(UsedUsername);
 | 
				
			||||||
export const Followings = getCustomRepository(FollowingRepository);
 | 
					export const Followings = (FollowingRepository);
 | 
				
			||||||
export const FollowRequests = getCustomRepository(FollowRequestRepository);
 | 
					export const FollowRequests = (FollowRequestRepository);
 | 
				
			||||||
export const Instances = getCustomRepository(InstanceRepository);
 | 
					export const Instances = (InstanceRepository);
 | 
				
			||||||
export const Emojis = getCustomRepository(EmojiRepository);
 | 
					export const Emojis = (EmojiRepository);
 | 
				
			||||||
export const DriveFiles = getCustomRepository(DriveFileRepository);
 | 
					export const DriveFiles = (DriveFileRepository);
 | 
				
			||||||
export const DriveFolders = getCustomRepository(DriveFolderRepository);
 | 
					export const DriveFolders = (DriveFolderRepository);
 | 
				
			||||||
export const Notifications = getCustomRepository(NotificationRepository);
 | 
					export const Notifications = (NotificationRepository);
 | 
				
			||||||
export const Metas = getRepository(Meta);
 | 
					export const Metas = db.getRepository(Meta);
 | 
				
			||||||
export const Mutings = getCustomRepository(MutingRepository);
 | 
					export const Mutings = (MutingRepository);
 | 
				
			||||||
export const Blockings = getCustomRepository(BlockingRepository);
 | 
					export const Blockings = (BlockingRepository);
 | 
				
			||||||
export const SwSubscriptions = getRepository(SwSubscription);
 | 
					export const SwSubscriptions = db.getRepository(SwSubscription);
 | 
				
			||||||
export const Hashtags = getCustomRepository(HashtagRepository);
 | 
					export const Hashtags = (HashtagRepository);
 | 
				
			||||||
export const AbuseUserReports = getCustomRepository(AbuseUserReportRepository);
 | 
					export const AbuseUserReports = (AbuseUserReportRepository);
 | 
				
			||||||
export const RegistrationTickets = getRepository(RegistrationTicket);
 | 
					export const RegistrationTickets = db.getRepository(RegistrationTicket);
 | 
				
			||||||
export const AuthSessions = getCustomRepository(AuthSessionRepository);
 | 
					export const AuthSessions = (AuthSessionRepository);
 | 
				
			||||||
export const AccessTokens = getRepository(AccessToken);
 | 
					export const AccessTokens = db.getRepository(AccessToken);
 | 
				
			||||||
export const Signins = getCustomRepository(SigninRepository);
 | 
					export const Signins = (SigninRepository);
 | 
				
			||||||
export const MessagingMessages = getCustomRepository(MessagingMessageRepository);
 | 
					export const MessagingMessages = (MessagingMessageRepository);
 | 
				
			||||||
export const Pages = getCustomRepository(PageRepository);
 | 
					export const Pages = (PageRepository);
 | 
				
			||||||
export const PageLikes = getCustomRepository(PageLikeRepository);
 | 
					export const PageLikes = (PageLikeRepository);
 | 
				
			||||||
export const GalleryPosts = getCustomRepository(GalleryPostRepository);
 | 
					export const GalleryPosts = (GalleryPostRepository);
 | 
				
			||||||
export const GalleryLikes = getCustomRepository(GalleryLikeRepository);
 | 
					export const GalleryLikes = (GalleryLikeRepository);
 | 
				
			||||||
export const ModerationLogs = getCustomRepository(ModerationLogRepository);
 | 
					export const ModerationLogs = (ModerationLogRepository);
 | 
				
			||||||
export const Clips = getCustomRepository(ClipRepository);
 | 
					export const Clips = (ClipRepository);
 | 
				
			||||||
export const ClipNotes = getRepository(ClipNote);
 | 
					export const ClipNotes = db.getRepository(ClipNote);
 | 
				
			||||||
export const Antennas = getCustomRepository(AntennaRepository);
 | 
					export const Antennas = (AntennaRepository);
 | 
				
			||||||
export const AntennaNotes = getRepository(AntennaNote);
 | 
					export const AntennaNotes = db.getRepository(AntennaNote);
 | 
				
			||||||
export const PromoNotes = getRepository(PromoNote);
 | 
					export const PromoNotes = db.getRepository(PromoNote);
 | 
				
			||||||
export const PromoReads = getRepository(PromoRead);
 | 
					export const PromoReads = db.getRepository(PromoRead);
 | 
				
			||||||
export const Relays = getCustomRepository(RelayRepository);
 | 
					export const Relays = (RelayRepository);
 | 
				
			||||||
export const MutedNotes = getRepository(MutedNote);
 | 
					export const MutedNotes = db.getRepository(MutedNote);
 | 
				
			||||||
export const Channels = getCustomRepository(ChannelRepository);
 | 
					export const Channels = (ChannelRepository);
 | 
				
			||||||
export const ChannelFollowings = getRepository(ChannelFollowing);
 | 
					export const ChannelFollowings = db.getRepository(ChannelFollowing);
 | 
				
			||||||
export const ChannelNotePinings = getRepository(ChannelNotePining);
 | 
					export const ChannelNotePinings = db.getRepository(ChannelNotePining);
 | 
				
			||||||
export const RegistryItems = getRepository(RegistryItem);
 | 
					export const RegistryItems = db.getRepository(RegistryItem);
 | 
				
			||||||
export const Ads = getRepository(Ad);
 | 
					export const Ads = db.getRepository(Ad);
 | 
				
			||||||
export const PasswordResetRequests = getRepository(PasswordResetRequest);
 | 
					export const PasswordResetRequests = db.getRepository(PasswordResetRequest);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,14 +1,13 @@
 | 
				
			||||||
import { EntityRepository, Repository } from 'typeorm';
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
import { Users } from '../index.js';
 | 
					import { Users } from '../index.js';
 | 
				
			||||||
import { AbuseUserReport } from '@/models/entities/abuse-user-report.js';
 | 
					import { AbuseUserReport } from '@/models/entities/abuse-user-report.js';
 | 
				
			||||||
import { awaitAll } from '@/prelude/await-all.js';
 | 
					import { awaitAll } from '@/prelude/await-all.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@EntityRepository(AbuseUserReport)
 | 
					export const AbuseUserReportRepository = db.getRepository(AbuseUserReport).extend({
 | 
				
			||||||
export class AbuseUserReportRepository extends Repository<AbuseUserReport> {
 | 
						async pack(
 | 
				
			||||||
	public async pack(
 | 
					 | 
				
			||||||
		src: AbuseUserReport['id'] | AbuseUserReport,
 | 
							src: AbuseUserReport['id'] | AbuseUserReport,
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		const report = typeof src === 'object' ? src : await this.findOneOrFail(src);
 | 
							const report = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return await awaitAll({
 | 
							return await awaitAll({
 | 
				
			||||||
			id: report.id,
 | 
								id: report.id,
 | 
				
			||||||
| 
						 | 
					@ -29,11 +28,11 @@ export class AbuseUserReportRepository extends Repository<AbuseUserReport> {
 | 
				
			||||||
			}) : null,
 | 
								}) : null,
 | 
				
			||||||
			forwarded: report.forwarded,
 | 
								forwarded: report.forwarded,
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public packMany(
 | 
						packMany(
 | 
				
			||||||
		reports: any[],
 | 
							reports: any[],
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		return Promise.all(reports.map(x => this.pack(x)));
 | 
							return Promise.all(reports.map(x => this.pack(x)));
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
}
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,17 +1,16 @@
 | 
				
			||||||
import { EntityRepository, Repository } from 'typeorm';
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
import { Antenna } from '@/models/entities/antenna.js';
 | 
					import { Antenna } from '@/models/entities/antenna.js';
 | 
				
			||||||
import { Packed } from '@/misc/schema.js';
 | 
					import { Packed } from '@/misc/schema.js';
 | 
				
			||||||
import { AntennaNotes, UserGroupJoinings } from '../index.js';
 | 
					import { AntennaNotes, UserGroupJoinings } from '../index.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@EntityRepository(Antenna)
 | 
					export const AntennaRepository = db.getRepository(Antenna).extend({
 | 
				
			||||||
export class AntennaRepository extends Repository<Antenna> {
 | 
						async pack(
 | 
				
			||||||
	public async pack(
 | 
					 | 
				
			||||||
		src: Antenna['id'] | Antenna,
 | 
							src: Antenna['id'] | Antenna,
 | 
				
			||||||
	): Promise<Packed<'Antenna'>> {
 | 
						): Promise<Packed<'Antenna'>> {
 | 
				
			||||||
		const antenna = typeof src === 'object' ? src : await this.findOneOrFail(src);
 | 
							const antenna = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const hasUnreadNote = (await AntennaNotes.findOne({ antennaId: antenna.id, read: false })) != null;
 | 
							const hasUnreadNote = (await AntennaNotes.findOneBy({ antennaId: antenna.id, read: false })) != null;
 | 
				
			||||||
		const userGroupJoining = antenna.userGroupJoiningId ? await UserGroupJoinings.findOne(antenna.userGroupJoiningId) : null;
 | 
							const userGroupJoining = antenna.userGroupJoiningId ? await UserGroupJoinings.findOneBy({ id: antenna.userGroupJoiningId }) : null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			id: antenna.id,
 | 
								id: antenna.id,
 | 
				
			||||||
| 
						 | 
					@ -29,5 +28,5 @@ export class AntennaRepository extends Repository<Antenna> {
 | 
				
			||||||
			withFile: antenna.withFile,
 | 
								withFile: antenna.withFile,
 | 
				
			||||||
			hasUnreadNote,
 | 
								hasUnreadNote,
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
}
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,12 +1,11 @@
 | 
				
			||||||
import { EntityRepository, Repository } from 'typeorm';
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
import { App } from '@/models/entities/app.js';
 | 
					import { App } from '@/models/entities/app.js';
 | 
				
			||||||
import { AccessTokens } from '../index.js';
 | 
					import { AccessTokens } from '../index.js';
 | 
				
			||||||
import { Packed } from '@/misc/schema.js';
 | 
					import { Packed } from '@/misc/schema.js';
 | 
				
			||||||
import { User } from '../entities/user.js';
 | 
					import { User } from '../entities/user.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@EntityRepository(App)
 | 
					export const AppRepository = db.getRepository(App).extend({
 | 
				
			||||||
export class AppRepository extends Repository<App> {
 | 
						async pack(
 | 
				
			||||||
	public async pack(
 | 
					 | 
				
			||||||
		src: App['id'] | App,
 | 
							src: App['id'] | App,
 | 
				
			||||||
		me?: { id: User['id'] } | null | undefined,
 | 
							me?: { id: User['id'] } | null | undefined,
 | 
				
			||||||
		options?: {
 | 
							options?: {
 | 
				
			||||||
| 
						 | 
					@ -21,7 +20,7 @@ export class AppRepository extends Repository<App> {
 | 
				
			||||||
			includeProfileImageIds: false,
 | 
								includeProfileImageIds: false,
 | 
				
			||||||
		}, options);
 | 
							}, options);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const app = typeof src === 'object' ? src : await this.findOneOrFail(src);
 | 
							const app = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			id: app.id,
 | 
								id: app.id,
 | 
				
			||||||
| 
						 | 
					@ -30,11 +29,11 @@ export class AppRepository extends Repository<App> {
 | 
				
			||||||
			permission: app.permission,
 | 
								permission: app.permission,
 | 
				
			||||||
			...(opts.includeSecret ? { secret: app.secret } : {}),
 | 
								...(opts.includeSecret ? { secret: app.secret } : {}),
 | 
				
			||||||
			...(me ? {
 | 
								...(me ? {
 | 
				
			||||||
				isAuthorized: await AccessTokens.count({
 | 
									isAuthorized: await AccessTokens.countBy({
 | 
				
			||||||
					appId: app.id,
 | 
										appId: app.id,
 | 
				
			||||||
					userId: me.id,
 | 
										userId: me.id,
 | 
				
			||||||
				}).then(count => count > 0),
 | 
									}).then(count => count > 0),
 | 
				
			||||||
			} : {}),
 | 
								} : {}),
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
}
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,21 +1,20 @@
 | 
				
			||||||
import { EntityRepository, Repository } from 'typeorm';
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
import { Apps } from '../index.js';
 | 
					import { Apps } from '../index.js';
 | 
				
			||||||
import { AuthSession } from '@/models/entities/auth-session.js';
 | 
					import { AuthSession } from '@/models/entities/auth-session.js';
 | 
				
			||||||
import { awaitAll } from '@/prelude/await-all.js';
 | 
					import { awaitAll } from '@/prelude/await-all.js';
 | 
				
			||||||
import { User } from '@/models/entities/user.js';
 | 
					import { User } from '@/models/entities/user.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@EntityRepository(AuthSession)
 | 
					export const AuthSessionRepository = db.getRepository(AuthSession).extend({
 | 
				
			||||||
export class AuthSessionRepository extends Repository<AuthSession> {
 | 
						async pack(
 | 
				
			||||||
	public async pack(
 | 
					 | 
				
			||||||
		src: AuthSession['id'] | AuthSession,
 | 
							src: AuthSession['id'] | AuthSession,
 | 
				
			||||||
		me?: { id: User['id'] } | null | undefined
 | 
							me?: { id: User['id'] } | null | undefined
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		const session = typeof src === 'object' ? src : await this.findOneOrFail(src);
 | 
							const session = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return await awaitAll({
 | 
							return await awaitAll({
 | 
				
			||||||
			id: session.id,
 | 
								id: session.id,
 | 
				
			||||||
			app: Apps.pack(session.appId, me),
 | 
								app: Apps.pack(session.appId, me),
 | 
				
			||||||
			token: session.token,
 | 
								token: session.token,
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
}
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,17 +1,16 @@
 | 
				
			||||||
import { EntityRepository, Repository } from 'typeorm';
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
import { Users } from '../index.js';
 | 
					import { Users } from '../index.js';
 | 
				
			||||||
import { Blocking } from '@/models/entities/blocking.js';
 | 
					import { Blocking } from '@/models/entities/blocking.js';
 | 
				
			||||||
import { awaitAll } from '@/prelude/await-all.js';
 | 
					import { awaitAll } from '@/prelude/await-all.js';
 | 
				
			||||||
import { Packed } from '@/misc/schema.js';
 | 
					import { Packed } from '@/misc/schema.js';
 | 
				
			||||||
import { User } from '@/models/entities/user.js';
 | 
					import { User } from '@/models/entities/user.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@EntityRepository(Blocking)
 | 
					export const BlockingRepository = db.getRepository(Blocking).extend({
 | 
				
			||||||
export class BlockingRepository extends Repository<Blocking> {
 | 
						async pack(
 | 
				
			||||||
	public async pack(
 | 
					 | 
				
			||||||
		src: Blocking['id'] | Blocking,
 | 
							src: Blocking['id'] | Blocking,
 | 
				
			||||||
		me?: { id: User['id'] } | null | undefined
 | 
							me?: { id: User['id'] } | null | undefined
 | 
				
			||||||
	): Promise<Packed<'Blocking'>> {
 | 
						): Promise<Packed<'Blocking'>> {
 | 
				
			||||||
		const blocking = typeof src === 'object' ? src : await this.findOneOrFail(src);
 | 
							const blocking = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return await awaitAll({
 | 
							return await awaitAll({
 | 
				
			||||||
			id: blocking.id,
 | 
								id: blocking.id,
 | 
				
			||||||
| 
						 | 
					@ -21,12 +20,12 @@ export class BlockingRepository extends Repository<Blocking> {
 | 
				
			||||||
				detail: true,
 | 
									detail: true,
 | 
				
			||||||
			}),
 | 
								}),
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public packMany(
 | 
						packMany(
 | 
				
			||||||
		blockings: any[],
 | 
							blockings: any[],
 | 
				
			||||||
		me: { id: User['id'] }
 | 
							me: { id: User['id'] }
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		return Promise.all(blockings.map(x => this.pack(x, me)));
 | 
							return Promise.all(blockings.map(x => this.pack(x, me)));
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
}
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,23 +1,22 @@
 | 
				
			||||||
import { EntityRepository, Repository } from 'typeorm';
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
import { Channel } from '@/models/entities/channel.js';
 | 
					import { Channel } from '@/models/entities/channel.js';
 | 
				
			||||||
import { Packed } from '@/misc/schema.js';
 | 
					import { Packed } from '@/misc/schema.js';
 | 
				
			||||||
import { DriveFiles, ChannelFollowings, NoteUnreads } from '../index.js';
 | 
					import { DriveFiles, ChannelFollowings, NoteUnreads } from '../index.js';
 | 
				
			||||||
import { User } from '@/models/entities/user.js';
 | 
					import { User } from '@/models/entities/user.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@EntityRepository(Channel)
 | 
					export const ChannelRepository = db.getRepository(Channel).extend({
 | 
				
			||||||
export class ChannelRepository extends Repository<Channel> {
 | 
						async pack(
 | 
				
			||||||
	public async pack(
 | 
					 | 
				
			||||||
		src: Channel['id'] | Channel,
 | 
							src: Channel['id'] | Channel,
 | 
				
			||||||
		me?: { id: User['id'] } | null | undefined,
 | 
							me?: { id: User['id'] } | null | undefined,
 | 
				
			||||||
	): Promise<Packed<'Channel'>> {
 | 
						): Promise<Packed<'Channel'>> {
 | 
				
			||||||
		const channel = typeof src === 'object' ? src : await this.findOneOrFail(src);
 | 
							const channel = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
 | 
				
			||||||
		const meId = me ? me.id : null;
 | 
							const meId = me ? me.id : null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const banner = channel.bannerId ? await DriveFiles.findOne(channel.bannerId) : null;
 | 
							const banner = channel.bannerId ? await DriveFiles.findOneBy({ id: channel.bannerId }) : null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const hasUnreadNote = meId ? (await NoteUnreads.findOne({ noteChannelId: channel.id, userId: meId })) != null : undefined;
 | 
							const hasUnreadNote = meId ? (await NoteUnreads.findOneBy({ noteChannelId: channel.id, userId: meId })) != null : undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const following = meId ? await ChannelFollowings.findOne({
 | 
							const following = meId ? await ChannelFollowings.findOneBy({
 | 
				
			||||||
			followerId: meId,
 | 
								followerId: meId,
 | 
				
			||||||
			followeeId: channel.id,
 | 
								followeeId: channel.id,
 | 
				
			||||||
		}) : null;
 | 
							}) : null;
 | 
				
			||||||
| 
						 | 
					@ -38,5 +37,5 @@ export class ChannelRepository extends Repository<Channel> {
 | 
				
			||||||
				hasUnreadNote,
 | 
									hasUnreadNote,
 | 
				
			||||||
			} : {}),
 | 
								} : {}),
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
}
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,15 +1,14 @@
 | 
				
			||||||
import { EntityRepository, Repository } from 'typeorm';
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
import { Clip } from '@/models/entities/clip.js';
 | 
					import { Clip } from '@/models/entities/clip.js';
 | 
				
			||||||
import { Packed } from '@/misc/schema.js';
 | 
					import { Packed } from '@/misc/schema.js';
 | 
				
			||||||
import { Users } from '../index.js';
 | 
					import { Users } from '../index.js';
 | 
				
			||||||
import { awaitAll } from '@/prelude/await-all.js';
 | 
					import { awaitAll } from '@/prelude/await-all.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@EntityRepository(Clip)
 | 
					export const ClipRepository = db.getRepository(Clip).extend({
 | 
				
			||||||
export class ClipRepository extends Repository<Clip> {
 | 
						async pack(
 | 
				
			||||||
	public async pack(
 | 
					 | 
				
			||||||
		src: Clip['id'] | Clip,
 | 
							src: Clip['id'] | Clip,
 | 
				
			||||||
	): Promise<Packed<'Clip'>> {
 | 
						): Promise<Packed<'Clip'>> {
 | 
				
			||||||
		const clip = typeof src === 'object' ? src : await this.findOneOrFail(src);
 | 
							const clip = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return await awaitAll({
 | 
							return await awaitAll({
 | 
				
			||||||
			id: clip.id,
 | 
								id: clip.id,
 | 
				
			||||||
| 
						 | 
					@ -20,12 +19,12 @@ export class ClipRepository extends Repository<Clip> {
 | 
				
			||||||
			description: clip.description,
 | 
								description: clip.description,
 | 
				
			||||||
			isPublic: clip.isPublic,
 | 
								isPublic: clip.isPublic,
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public packMany(
 | 
						packMany(
 | 
				
			||||||
		clips: Clip[],
 | 
							clips: Clip[],
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		return Promise.all(clips.map(x => this.pack(x)));
 | 
							return Promise.all(clips.map(x => this.pack(x)));
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
}
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
import { EntityRepository, Repository } from 'typeorm';
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
import { DriveFile } from '@/models/entities/drive-file.js';
 | 
					import { DriveFile } from '@/models/entities/drive-file.js';
 | 
				
			||||||
import { Users, DriveFolders } from '../index.js';
 | 
					import { Users, DriveFolders } from '../index.js';
 | 
				
			||||||
import { User } from '@/models/entities/user.js';
 | 
					import { User } from '@/models/entities/user.js';
 | 
				
			||||||
| 
						 | 
					@ -16,9 +16,8 @@ type PackOptions = {
 | 
				
			||||||
	withUser?: boolean,
 | 
						withUser?: boolean,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@EntityRepository(DriveFile)
 | 
					export const DriveFileRepository = db.getRepository(DriveFile).extend({
 | 
				
			||||||
export class DriveFileRepository extends Repository<DriveFile> {
 | 
						validateFileName(name: string): boolean {
 | 
				
			||||||
	public validateFileName(name: string): boolean {
 | 
					 | 
				
			||||||
		return (
 | 
							return (
 | 
				
			||||||
			(name.trim().length > 0) &&
 | 
								(name.trim().length > 0) &&
 | 
				
			||||||
			(name.length <= 200) &&
 | 
								(name.length <= 200) &&
 | 
				
			||||||
| 
						 | 
					@ -26,9 +25,9 @@ export class DriveFileRepository extends Repository<DriveFile> {
 | 
				
			||||||
			(name.indexOf('/') === -1) &&
 | 
								(name.indexOf('/') === -1) &&
 | 
				
			||||||
			(name.indexOf('..') === -1)
 | 
								(name.indexOf('..') === -1)
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public getPublicProperties(file: DriveFile): DriveFile['properties'] {
 | 
						getPublicProperties(file: DriveFile): DriveFile['properties'] {
 | 
				
			||||||
		if (file.properties.orientation != null) {
 | 
							if (file.properties.orientation != null) {
 | 
				
			||||||
			const properties = JSON.parse(JSON.stringify(file.properties));
 | 
								const properties = JSON.parse(JSON.stringify(file.properties));
 | 
				
			||||||
			if (file.properties.orientation >= 5) {
 | 
								if (file.properties.orientation >= 5) {
 | 
				
			||||||
| 
						 | 
					@ -39,9 +38,9 @@ export class DriveFileRepository extends Repository<DriveFile> {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return file.properties;
 | 
							return file.properties;
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public getPublicUrl(file: DriveFile, thumbnail = false): string | null {
 | 
						getPublicUrl(file: DriveFile, thumbnail = false): string | null {
 | 
				
			||||||
		// リモートかつメディアプロキシ
 | 
							// リモートかつメディアプロキシ
 | 
				
			||||||
		if (file.uri != null && file.userHost != null && config.mediaProxy != null) {
 | 
							if (file.uri != null && file.userHost != null && config.mediaProxy != null) {
 | 
				
			||||||
			return appendQuery(config.mediaProxy, query({
 | 
								return appendQuery(config.mediaProxy, query({
 | 
				
			||||||
| 
						 | 
					@ -62,9 +61,9 @@ export class DriveFileRepository extends Repository<DriveFile> {
 | 
				
			||||||
		const isImage = file.type && ['image/png', 'image/apng', 'image/gif', 'image/jpeg', 'image/webp', 'image/svg+xml'].includes(file.type);
 | 
							const isImage = file.type && ['image/png', 'image/apng', 'image/gif', 'image/jpeg', 'image/webp', 'image/svg+xml'].includes(file.type);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return thumbnail ? (file.thumbnailUrl || (isImage ? (file.webpublicUrl || file.url) : null)) : (file.webpublicUrl || file.url);
 | 
							return thumbnail ? (file.thumbnailUrl || (isImage ? (file.webpublicUrl || file.url) : null)) : (file.webpublicUrl || file.url);
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public async calcDriveUsageOf(user: User['id'] | { id: User['id'] }): Promise<number> {
 | 
						async calcDriveUsageOf(user: User['id'] | { id: User['id'] }): Promise<number> {
 | 
				
			||||||
		const id = typeof user === 'object' ? user.id : user;
 | 
							const id = typeof user === 'object' ? user.id : user;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const { sum } = await this
 | 
							const { sum } = await this
 | 
				
			||||||
| 
						 | 
					@ -75,9 +74,9 @@ export class DriveFileRepository extends Repository<DriveFile> {
 | 
				
			||||||
			.getRawOne();
 | 
								.getRawOne();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return parseInt(sum, 10) || 0;
 | 
							return parseInt(sum, 10) || 0;
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public async calcDriveUsageOfHost(host: string): Promise<number> {
 | 
						async calcDriveUsageOfHost(host: string): Promise<number> {
 | 
				
			||||||
		const { sum } = await this
 | 
							const { sum } = await this
 | 
				
			||||||
			.createQueryBuilder('file')
 | 
								.createQueryBuilder('file')
 | 
				
			||||||
			.where('file.userHost = :host', { host: toPuny(host) })
 | 
								.where('file.userHost = :host', { host: toPuny(host) })
 | 
				
			||||||
| 
						 | 
					@ -86,9 +85,9 @@ export class DriveFileRepository extends Repository<DriveFile> {
 | 
				
			||||||
			.getRawOne();
 | 
								.getRawOne();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return parseInt(sum, 10) || 0;
 | 
							return parseInt(sum, 10) || 0;
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public async calcDriveUsageOfLocal(): Promise<number> {
 | 
						async calcDriveUsageOfLocal(): Promise<number> {
 | 
				
			||||||
		const { sum } = await this
 | 
							const { sum } = await this
 | 
				
			||||||
			.createQueryBuilder('file')
 | 
								.createQueryBuilder('file')
 | 
				
			||||||
			.where('file.userHost IS NULL')
 | 
								.where('file.userHost IS NULL')
 | 
				
			||||||
| 
						 | 
					@ -97,9 +96,9 @@ export class DriveFileRepository extends Repository<DriveFile> {
 | 
				
			||||||
			.getRawOne();
 | 
								.getRawOne();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return parseInt(sum, 10) || 0;
 | 
							return parseInt(sum, 10) || 0;
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public async calcDriveUsageOfRemote(): Promise<number> {
 | 
						async calcDriveUsageOfRemote(): Promise<number> {
 | 
				
			||||||
		const { sum } = await this
 | 
							const { sum } = await this
 | 
				
			||||||
			.createQueryBuilder('file')
 | 
								.createQueryBuilder('file')
 | 
				
			||||||
			.where('file.userHost IS NOT NULL')
 | 
								.where('file.userHost IS NOT NULL')
 | 
				
			||||||
| 
						 | 
					@ -108,11 +107,9 @@ export class DriveFileRepository extends Repository<DriveFile> {
 | 
				
			||||||
			.getRawOne();
 | 
								.getRawOne();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return parseInt(sum, 10) || 0;
 | 
							return parseInt(sum, 10) || 0;
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public async pack(src: DriveFile['id'], options?: PackOptions): Promise<Packed<'DriveFile'> | null>;
 | 
						async pack(
 | 
				
			||||||
	public async pack(src: DriveFile, options?: PackOptions): Promise<Packed<'DriveFile'>>;
 | 
					 | 
				
			||||||
	public async pack(
 | 
					 | 
				
			||||||
		src: DriveFile['id'] | DriveFile,
 | 
							src: DriveFile['id'] | DriveFile,
 | 
				
			||||||
		options?: PackOptions
 | 
							options?: PackOptions
 | 
				
			||||||
	): Promise<Packed<'DriveFile'> | null> {
 | 
						): Promise<Packed<'DriveFile'> | null> {
 | 
				
			||||||
| 
						 | 
					@ -121,11 +118,9 @@ export class DriveFileRepository extends Repository<DriveFile> {
 | 
				
			||||||
			self: false,
 | 
								self: false,
 | 
				
			||||||
		}, options);
 | 
							}, options);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const file = typeof src === 'object' ? src : await this.findOne(src);
 | 
							const file = typeof src === 'object' ? src : await this.findOneBy({ id: src });
 | 
				
			||||||
		if (file == null) return null;
 | 
							if (file == null) return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const meta = await fetchMeta();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return await awaitAll<Packed<'DriveFile'>>({
 | 
							return await awaitAll<Packed<'DriveFile'>>({
 | 
				
			||||||
			id: file.id,
 | 
								id: file.id,
 | 
				
			||||||
			createdAt: file.createdAt.toISOString(),
 | 
								createdAt: file.createdAt.toISOString(),
 | 
				
			||||||
| 
						 | 
					@ -146,13 +141,13 @@ export class DriveFileRepository extends Repository<DriveFile> {
 | 
				
			||||||
			userId: opts.withUser ? file.userId : null,
 | 
								userId: opts.withUser ? file.userId : null,
 | 
				
			||||||
			user: (opts.withUser && file.userId) ? Users.pack(file.userId) : null,
 | 
								user: (opts.withUser && file.userId) ? Users.pack(file.userId) : null,
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public async packMany(
 | 
						async packMany(
 | 
				
			||||||
		files: (DriveFile['id'] | DriveFile)[],
 | 
							files: (DriveFile['id'] | DriveFile)[],
 | 
				
			||||||
		options?: PackOptions
 | 
							options?: PackOptions
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		const items = await Promise.all(files.map(f => this.pack(f, options)));
 | 
							const items = await Promise.all(files.map(f => this.pack(f, options)));
 | 
				
			||||||
		return items.filter(x => x != null);
 | 
							return items.filter(x => x != null);
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
}
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,12 +1,11 @@
 | 
				
			||||||
import { EntityRepository, Repository } from 'typeorm';
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
import { DriveFolders, DriveFiles } from '../index.js';
 | 
					import { DriveFolders, DriveFiles } from '../index.js';
 | 
				
			||||||
import { DriveFolder } from '@/models/entities/drive-folder.js';
 | 
					import { DriveFolder } from '@/models/entities/drive-folder.js';
 | 
				
			||||||
import { awaitAll } from '@/prelude/await-all.js';
 | 
					import { awaitAll } from '@/prelude/await-all.js';
 | 
				
			||||||
import { Packed } from '@/misc/schema.js';
 | 
					import { Packed } from '@/misc/schema.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@EntityRepository(DriveFolder)
 | 
					export const DriveFolderRepository = db.getRepository(DriveFolder).extend({
 | 
				
			||||||
export class DriveFolderRepository extends Repository<DriveFolder> {
 | 
						async pack(
 | 
				
			||||||
	public async pack(
 | 
					 | 
				
			||||||
		src: DriveFolder['id'] | DriveFolder,
 | 
							src: DriveFolder['id'] | DriveFolder,
 | 
				
			||||||
		options?: {
 | 
							options?: {
 | 
				
			||||||
			detail: boolean
 | 
								detail: boolean
 | 
				
			||||||
| 
						 | 
					@ -16,7 +15,7 @@ export class DriveFolderRepository extends Repository<DriveFolder> {
 | 
				
			||||||
			detail: false,
 | 
								detail: false,
 | 
				
			||||||
		}, options);
 | 
							}, options);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const folder = typeof src === 'object' ? src : await this.findOneOrFail(src);
 | 
							const folder = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return await awaitAll({
 | 
							return await awaitAll({
 | 
				
			||||||
			id: folder.id,
 | 
								id: folder.id,
 | 
				
			||||||
| 
						 | 
					@ -25,10 +24,10 @@ export class DriveFolderRepository extends Repository<DriveFolder> {
 | 
				
			||||||
			parentId: folder.parentId,
 | 
								parentId: folder.parentId,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			...(opts.detail ? {
 | 
								...(opts.detail ? {
 | 
				
			||||||
				foldersCount: DriveFolders.count({
 | 
									foldersCount: DriveFolders.countBy({
 | 
				
			||||||
					parentId: folder.id,
 | 
										parentId: folder.id,
 | 
				
			||||||
				}),
 | 
									}),
 | 
				
			||||||
				filesCount: DriveFiles.count({
 | 
									filesCount: DriveFiles.countBy({
 | 
				
			||||||
					folderId: folder.id,
 | 
										folderId: folder.id,
 | 
				
			||||||
				}),
 | 
									}),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,5 +38,5 @@ export class DriveFolderRepository extends Repository<DriveFolder> {
 | 
				
			||||||
				} : {}),
 | 
									} : {}),
 | 
				
			||||||
			} : {}),
 | 
								} : {}),
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
}
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,13 +1,12 @@
 | 
				
			||||||
import { EntityRepository, Repository } from 'typeorm';
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
import { Emoji } from '@/models/entities/emoji.js';
 | 
					import { Emoji } from '@/models/entities/emoji.js';
 | 
				
			||||||
import { Packed } from '@/misc/schema.js';
 | 
					import { Packed } from '@/misc/schema.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@EntityRepository(Emoji)
 | 
					export const EmojiRepository = db.getRepository(Emoji).extend({
 | 
				
			||||||
export class EmojiRepository extends Repository<Emoji> {
 | 
						async pack(
 | 
				
			||||||
	public async pack(
 | 
					 | 
				
			||||||
		src: Emoji['id'] | Emoji,
 | 
							src: Emoji['id'] | Emoji,
 | 
				
			||||||
	): Promise<Packed<'Emoji'>> {
 | 
						): Promise<Packed<'Emoji'>> {
 | 
				
			||||||
		const emoji = typeof src === 'object' ? src : await this.findOneOrFail(src);
 | 
							const emoji = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			id: emoji.id,
 | 
								id: emoji.id,
 | 
				
			||||||
| 
						 | 
					@ -18,11 +17,11 @@ export class EmojiRepository extends Repository<Emoji> {
 | 
				
			||||||
			// || emoji.originalUrl してるのは後方互換性のため
 | 
								// || emoji.originalUrl してるのは後方互換性のため
 | 
				
			||||||
			url: emoji.publicUrl || emoji.originalUrl,
 | 
								url: emoji.publicUrl || emoji.originalUrl,
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public packMany(
 | 
						packMany(
 | 
				
			||||||
		emojis: any[],
 | 
							emojis: any[],
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		return Promise.all(emojis.map(x => this.pack(x)));
 | 
							return Promise.all(emojis.map(x => this.pack(x)));
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
}
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,20 +1,19 @@
 | 
				
			||||||
import { EntityRepository, Repository } from 'typeorm';
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
import { FollowRequest } from '@/models/entities/follow-request.js';
 | 
					import { FollowRequest } from '@/models/entities/follow-request.js';
 | 
				
			||||||
import { Users } from '../index.js';
 | 
					import { Users } from '../index.js';
 | 
				
			||||||
import { User } from '@/models/entities/user.js';
 | 
					import { User } from '@/models/entities/user.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@EntityRepository(FollowRequest)
 | 
					export const FollowRequestRepository = db.getRepository(FollowRequest).extend({
 | 
				
			||||||
export class FollowRequestRepository extends Repository<FollowRequest> {
 | 
						async pack(
 | 
				
			||||||
	public async pack(
 | 
					 | 
				
			||||||
		src: FollowRequest['id'] | FollowRequest,
 | 
							src: FollowRequest['id'] | FollowRequest,
 | 
				
			||||||
		me?: { id: User['id'] } | null | undefined
 | 
							me?: { id: User['id'] } | null | undefined
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		const request = typeof src === 'object' ? src : await this.findOneOrFail(src);
 | 
							const request = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			id: request.id,
 | 
								id: request.id,
 | 
				
			||||||
			follower: await Users.pack(request.followerId, me),
 | 
								follower: await Users.pack(request.followerId, me),
 | 
				
			||||||
			followee: await Users.pack(request.followeeId, me),
 | 
								followee: await Users.pack(request.followeeId, me),
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
}
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
import { EntityRepository, Repository } from 'typeorm';
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
import { Users } from '../index.js';
 | 
					import { Users } from '../index.js';
 | 
				
			||||||
import { Following } from '@/models/entities/following.js';
 | 
					import { Following } from '@/models/entities/following.js';
 | 
				
			||||||
import { awaitAll } from '@/prelude/await-all.js';
 | 
					import { awaitAll } from '@/prelude/await-all.js';
 | 
				
			||||||
| 
						 | 
					@ -29,25 +29,24 @@ type RemoteFolloweeFollowing = Following & {
 | 
				
			||||||
	followeeSharedInbox: string;
 | 
						followeeSharedInbox: string;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@EntityRepository(Following)
 | 
					export const FollowingRepository = db.getRepository(Following).extend({
 | 
				
			||||||
export class FollowingRepository extends Repository<Following> {
 | 
						isLocalFollower(following: Following): following is LocalFollowerFollowing {
 | 
				
			||||||
	public isLocalFollower(following: Following): following is LocalFollowerFollowing {
 | 
					 | 
				
			||||||
		return following.followerHost == null;
 | 
							return following.followerHost == null;
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public isRemoteFollower(following: Following): following is RemoteFollowerFollowing {
 | 
						isRemoteFollower(following: Following): following is RemoteFollowerFollowing {
 | 
				
			||||||
		return following.followerHost != null;
 | 
							return following.followerHost != null;
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public isLocalFollowee(following: Following): following is LocalFolloweeFollowing {
 | 
						isLocalFollowee(following: Following): following is LocalFolloweeFollowing {
 | 
				
			||||||
		return following.followeeHost == null;
 | 
							return following.followeeHost == null;
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public isRemoteFollowee(following: Following): following is RemoteFolloweeFollowing {
 | 
						isRemoteFollowee(following: Following): following is RemoteFolloweeFollowing {
 | 
				
			||||||
		return following.followeeHost != null;
 | 
							return following.followeeHost != null;
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public async pack(
 | 
						async pack(
 | 
				
			||||||
		src: Following['id'] | Following,
 | 
							src: Following['id'] | Following,
 | 
				
			||||||
		me?: { id: User['id'] } | null | undefined,
 | 
							me?: { id: User['id'] } | null | undefined,
 | 
				
			||||||
		opts?: {
 | 
							opts?: {
 | 
				
			||||||
| 
						 | 
					@ -55,7 +54,7 @@ export class FollowingRepository extends Repository<Following> {
 | 
				
			||||||
			populateFollower?: boolean;
 | 
								populateFollower?: boolean;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	): Promise<Packed<'Following'>> {
 | 
						): Promise<Packed<'Following'>> {
 | 
				
			||||||
		const following = typeof src === 'object' ? src : await this.findOneOrFail(src);
 | 
							const following = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (opts == null) opts = {};
 | 
							if (opts == null) opts = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -71,9 +70,9 @@ export class FollowingRepository extends Repository<Following> {
 | 
				
			||||||
				detail: true,
 | 
									detail: true,
 | 
				
			||||||
			}) : undefined,
 | 
								}) : undefined,
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public packMany(
 | 
						packMany(
 | 
				
			||||||
		followings: any[],
 | 
							followings: any[],
 | 
				
			||||||
		me?: { id: User['id'] } | null | undefined,
 | 
							me?: { id: User['id'] } | null | undefined,
 | 
				
			||||||
		opts?: {
 | 
							opts?: {
 | 
				
			||||||
| 
						 | 
					@ -82,5 +81,5 @@ export class FollowingRepository extends Repository<Following> {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		return Promise.all(followings.map(x => this.pack(x, me, opts)));
 | 
							return Promise.all(followings.map(x => this.pack(x, me, opts)));
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
}
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,25 +1,24 @@
 | 
				
			||||||
import { EntityRepository, Repository } from 'typeorm';
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
import { GalleryLike } from '@/models/entities/gallery-like.js';
 | 
					import { GalleryLike } from '@/models/entities/gallery-like.js';
 | 
				
			||||||
import { GalleryPosts } from '../index.js';
 | 
					import { GalleryPosts } from '../index.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@EntityRepository(GalleryLike)
 | 
					export const GalleryLikeRepository = db.getRepository(GalleryLike).extend({
 | 
				
			||||||
export class GalleryLikeRepository extends Repository<GalleryLike> {
 | 
						async pack(
 | 
				
			||||||
	public async pack(
 | 
					 | 
				
			||||||
		src: GalleryLike['id'] | GalleryLike,
 | 
							src: GalleryLike['id'] | GalleryLike,
 | 
				
			||||||
		me?: any
 | 
							me?: any
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		const like = typeof src === 'object' ? src : await this.findOneOrFail(src);
 | 
							const like = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			id: like.id,
 | 
								id: like.id,
 | 
				
			||||||
			post: await GalleryPosts.pack(like.post || like.postId, me),
 | 
								post: await GalleryPosts.pack(like.post || like.postId, me),
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public packMany(
 | 
						packMany(
 | 
				
			||||||
		likes: any[],
 | 
							likes: any[],
 | 
				
			||||||
		me: any
 | 
							me: any
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		return Promise.all(likes.map(x => this.pack(x, me)));
 | 
							return Promise.all(likes.map(x => this.pack(x, me)));
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
}
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,18 +1,17 @@
 | 
				
			||||||
import { EntityRepository, Repository } from 'typeorm';
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
import { GalleryPost } from '@/models/entities/gallery-post.js';
 | 
					import { GalleryPost } from '@/models/entities/gallery-post.js';
 | 
				
			||||||
import { Packed } from '@/misc/schema.js';
 | 
					import { Packed } from '@/misc/schema.js';
 | 
				
			||||||
import { Users, DriveFiles, GalleryLikes } from '../index.js';
 | 
					import { Users, DriveFiles, GalleryLikes } from '../index.js';
 | 
				
			||||||
import { awaitAll } from '@/prelude/await-all.js';
 | 
					import { awaitAll } from '@/prelude/await-all.js';
 | 
				
			||||||
import { User } from '@/models/entities/user.js';
 | 
					import { User } from '@/models/entities/user.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@EntityRepository(GalleryPost)
 | 
					export const GalleryPostRepository = db.getRepository(GalleryPost).extend({
 | 
				
			||||||
export class GalleryPostRepository extends Repository<GalleryPost> {
 | 
						async pack(
 | 
				
			||||||
	public async pack(
 | 
					 | 
				
			||||||
		src: GalleryPost['id'] | GalleryPost,
 | 
							src: GalleryPost['id'] | GalleryPost,
 | 
				
			||||||
		me?: { id: User['id'] } | null | undefined,
 | 
							me?: { id: User['id'] } | null | undefined,
 | 
				
			||||||
	): 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.findOneOrFail(src);
 | 
							const post = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return await awaitAll({
 | 
							return await awaitAll({
 | 
				
			||||||
			id: post.id,
 | 
								id: post.id,
 | 
				
			||||||
| 
						 | 
					@ -27,14 +26,14 @@ export class GalleryPostRepository extends Repository<GalleryPost> {
 | 
				
			||||||
			tags: post.tags.length > 0 ? post.tags : undefined,
 | 
								tags: post.tags.length > 0 ? post.tags : undefined,
 | 
				
			||||||
			isSensitive: post.isSensitive,
 | 
								isSensitive: post.isSensitive,
 | 
				
			||||||
			likedCount: post.likedCount,
 | 
								likedCount: post.likedCount,
 | 
				
			||||||
			isLiked: meId ? await GalleryLikes.findOne({ postId: post.id, userId: meId }).then(x => x != null) : undefined,
 | 
								isLiked: meId ? await GalleryLikes.findOneBy({ postId: post.id, userId: meId }).then(x => x != null) : undefined,
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public packMany(
 | 
						packMany(
 | 
				
			||||||
		posts: GalleryPost[],
 | 
							posts: GalleryPost[],
 | 
				
			||||||
		me?: { id: User['id'] } | null | undefined,
 | 
							me?: { id: User['id'] } | null | undefined,
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		return Promise.all(posts.map(x => this.pack(x, me)));
 | 
							return Promise.all(posts.map(x => this.pack(x, me)));
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
}
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,10 +1,9 @@
 | 
				
			||||||
import { EntityRepository, Repository } from 'typeorm';
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
import { Hashtag } from '@/models/entities/hashtag.js';
 | 
					import { Hashtag } from '@/models/entities/hashtag.js';
 | 
				
			||||||
import { Packed } from '@/misc/schema.js';
 | 
					import { Packed } from '@/misc/schema.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@EntityRepository(Hashtag)
 | 
					export const HashtagRepository = db.getRepository(Hashtag).extend({
 | 
				
			||||||
export class HashtagRepository extends Repository<Hashtag> {
 | 
						async pack(
 | 
				
			||||||
	public async pack(
 | 
					 | 
				
			||||||
		src: Hashtag,
 | 
							src: Hashtag,
 | 
				
			||||||
	): Promise<Packed<'Hashtag'>> {
 | 
						): Promise<Packed<'Hashtag'>> {
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
| 
						 | 
					@ -16,11 +15,11 @@ export class HashtagRepository extends Repository<Hashtag> {
 | 
				
			||||||
			attachedLocalUsersCount: src.attachedLocalUsersCount,
 | 
								attachedLocalUsersCount: src.attachedLocalUsersCount,
 | 
				
			||||||
			attachedRemoteUsersCount: src.attachedRemoteUsersCount,
 | 
								attachedRemoteUsersCount: src.attachedRemoteUsersCount,
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public packMany(
 | 
						packMany(
 | 
				
			||||||
		hashtags: Hashtag[],
 | 
							hashtags: Hashtag[],
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		return Promise.all(hashtags.map(x => this.pack(x)));
 | 
							return Promise.all(hashtags.map(x => this.pack(x)));
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
}
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,10 +1,9 @@
 | 
				
			||||||
import { EntityRepository, Repository } from 'typeorm';
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
import { Instance } from '@/models/entities/instance.js';
 | 
					import { Instance } from '@/models/entities/instance.js';
 | 
				
			||||||
import { Packed } from '@/misc/schema.js';
 | 
					import { Packed } from '@/misc/schema.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@EntityRepository(Instance)
 | 
					export const InstanceRepository = db.getRepository(Instance).extend({
 | 
				
			||||||
export class InstanceRepository extends Repository<Instance> {
 | 
						async pack(
 | 
				
			||||||
	public async pack(
 | 
					 | 
				
			||||||
		instance: Instance,
 | 
							instance: Instance,
 | 
				
			||||||
	): Promise<Packed<'FederationInstance'>> {
 | 
						): Promise<Packed<'FederationInstance'>> {
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
| 
						 | 
					@ -29,11 +28,11 @@ export class InstanceRepository extends Repository<Instance> {
 | 
				
			||||||
			iconUrl: instance.iconUrl,
 | 
								iconUrl: instance.iconUrl,
 | 
				
			||||||
			infoUpdatedAt: instance.infoUpdatedAt ? instance.infoUpdatedAt.toISOString() : null,
 | 
								infoUpdatedAt: instance.infoUpdatedAt ? instance.infoUpdatedAt.toISOString() : null,
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public packMany(
 | 
						packMany(
 | 
				
			||||||
		instances: Instance[],
 | 
							instances: Instance[],
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		return Promise.all(instances.map(x => this.pack(x)));
 | 
							return Promise.all(instances.map(x => this.pack(x)));
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
}
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,12 +1,11 @@
 | 
				
			||||||
import { EntityRepository, Repository } from 'typeorm';
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
import { MessagingMessage } from '@/models/entities/messaging-message.js';
 | 
					import { MessagingMessage } from '@/models/entities/messaging-message.js';
 | 
				
			||||||
import { Users, DriveFiles, UserGroups } from '../index.js';
 | 
					import { Users, DriveFiles, UserGroups } from '../index.js';
 | 
				
			||||||
import { Packed } from '@/misc/schema.js';
 | 
					import { Packed } from '@/misc/schema.js';
 | 
				
			||||||
import { User } from '@/models/entities/user.js';
 | 
					import { User } from '@/models/entities/user.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@EntityRepository(MessagingMessage)
 | 
					export const MessagingMessageRepository = db.getRepository(MessagingMessage).extend({
 | 
				
			||||||
export class MessagingMessageRepository extends Repository<MessagingMessage> {
 | 
						async pack(
 | 
				
			||||||
	public async pack(
 | 
					 | 
				
			||||||
		src: MessagingMessage['id'] | MessagingMessage,
 | 
							src: MessagingMessage['id'] | MessagingMessage,
 | 
				
			||||||
		me?: { id: User['id'] } | null | undefined,
 | 
							me?: { id: User['id'] } | null | undefined,
 | 
				
			||||||
		options?: {
 | 
							options?: {
 | 
				
			||||||
| 
						 | 
					@ -19,7 +18,7 @@ export class MessagingMessageRepository extends Repository<MessagingMessage> {
 | 
				
			||||||
			populateGroup: true,
 | 
								populateGroup: true,
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const message = typeof src === 'object' ? src : await this.findOneOrFail(src);
 | 
							const message = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			id: message.id,
 | 
								id: message.id,
 | 
				
			||||||
| 
						 | 
					@ -36,5 +35,5 @@ export class MessagingMessageRepository extends Repository<MessagingMessage> {
 | 
				
			||||||
			isRead: message.isRead,
 | 
								isRead: message.isRead,
 | 
				
			||||||
			reads: message.reads,
 | 
								reads: message.reads,
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
}
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,14 +1,13 @@
 | 
				
			||||||
import { EntityRepository, Repository } from 'typeorm';
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
import { Users } from '../index.js';
 | 
					import { Users } from '../index.js';
 | 
				
			||||||
import { ModerationLog } from '@/models/entities/moderation-log.js';
 | 
					import { ModerationLog } from '@/models/entities/moderation-log.js';
 | 
				
			||||||
import { awaitAll } from '@/prelude/await-all.js';
 | 
					import { awaitAll } from '@/prelude/await-all.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@EntityRepository(ModerationLog)
 | 
					export const ModerationLogRepository = db.getRepository(ModerationLog).extend({
 | 
				
			||||||
export class ModerationLogRepository extends Repository<ModerationLog> {
 | 
						async pack(
 | 
				
			||||||
	public async pack(
 | 
					 | 
				
			||||||
		src: ModerationLog['id'] | ModerationLog,
 | 
							src: ModerationLog['id'] | ModerationLog,
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		const log = typeof src === 'object' ? src : await this.findOneOrFail(src);
 | 
							const log = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return await awaitAll({
 | 
							return await awaitAll({
 | 
				
			||||||
			id: log.id,
 | 
								id: log.id,
 | 
				
			||||||
| 
						 | 
					@ -20,11 +19,11 @@ export class ModerationLogRepository extends Repository<ModerationLog> {
 | 
				
			||||||
				detail: true,
 | 
									detail: true,
 | 
				
			||||||
			}),
 | 
								}),
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public packMany(
 | 
						packMany(
 | 
				
			||||||
		reports: any[],
 | 
							reports: any[],
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		return Promise.all(reports.map(x => this.pack(x)));
 | 
							return Promise.all(reports.map(x => this.pack(x)));
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
}
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,17 +1,16 @@
 | 
				
			||||||
import { EntityRepository, Repository } from 'typeorm';
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
import { Users } from '../index.js';
 | 
					import { Users } from '../index.js';
 | 
				
			||||||
import { Muting } from '@/models/entities/muting.js';
 | 
					import { Muting } from '@/models/entities/muting.js';
 | 
				
			||||||
import { awaitAll } from '@/prelude/await-all.js';
 | 
					import { awaitAll } from '@/prelude/await-all.js';
 | 
				
			||||||
import { Packed } from '@/misc/schema.js';
 | 
					import { Packed } from '@/misc/schema.js';
 | 
				
			||||||
import { User } from '@/models/entities/user.js';
 | 
					import { User } from '@/models/entities/user.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@EntityRepository(Muting)
 | 
					export const MutingRepository = db.getRepository(Muting).extend({
 | 
				
			||||||
export class MutingRepository extends Repository<Muting> {
 | 
						async pack(
 | 
				
			||||||
	public async pack(
 | 
					 | 
				
			||||||
		src: Muting['id'] | Muting,
 | 
							src: Muting['id'] | Muting,
 | 
				
			||||||
		me?: { id: User['id'] } | null | undefined
 | 
							me?: { id: User['id'] } | null | undefined
 | 
				
			||||||
	): Promise<Packed<'Muting'>> {
 | 
						): Promise<Packed<'Muting'>> {
 | 
				
			||||||
		const muting = typeof src === 'object' ? src : await this.findOneOrFail(src);
 | 
							const muting = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return await awaitAll({
 | 
							return await awaitAll({
 | 
				
			||||||
			id: muting.id,
 | 
								id: muting.id,
 | 
				
			||||||
| 
						 | 
					@ -22,12 +21,12 @@ export class MutingRepository extends Repository<Muting> {
 | 
				
			||||||
				detail: true,
 | 
									detail: true,
 | 
				
			||||||
			}),
 | 
								}),
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public packMany(
 | 
						packMany(
 | 
				
			||||||
		mutings: any[],
 | 
							mutings: any[],
 | 
				
			||||||
		me: { id: User['id'] }
 | 
							me: { id: User['id'] }
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		return Promise.all(mutings.map(x => this.pack(x, me)));
 | 
							return Promise.all(mutings.map(x => this.pack(x, me)));
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
}
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,15 +1,14 @@
 | 
				
			||||||
import { EntityRepository, Repository } from 'typeorm';
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
import { NoteFavorite } from '@/models/entities/note-favorite.js';
 | 
					import { NoteFavorite } from '@/models/entities/note-favorite.js';
 | 
				
			||||||
import { Notes } from '../index.js';
 | 
					import { Notes } from '../index.js';
 | 
				
			||||||
import { User } from '@/models/entities/user.js';
 | 
					import { User } from '@/models/entities/user.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@EntityRepository(NoteFavorite)
 | 
					export const NoteFavoriteRepository = db.getRepository(NoteFavorite).extend({
 | 
				
			||||||
export class NoteFavoriteRepository extends Repository<NoteFavorite> {
 | 
						async pack(
 | 
				
			||||||
	public async pack(
 | 
					 | 
				
			||||||
		src: NoteFavorite['id'] | NoteFavorite,
 | 
							src: NoteFavorite['id'] | NoteFavorite,
 | 
				
			||||||
		me?: { id: User['id'] } | null | undefined
 | 
							me?: { id: User['id'] } | null | undefined
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		const favorite = typeof src === 'object' ? src : await this.findOneOrFail(src);
 | 
							const favorite = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			id: favorite.id,
 | 
								id: favorite.id,
 | 
				
			||||||
| 
						 | 
					@ -17,12 +16,12 @@ export class NoteFavoriteRepository extends Repository<NoteFavorite> {
 | 
				
			||||||
			noteId: favorite.noteId,
 | 
								noteId: favorite.noteId,
 | 
				
			||||||
			note: await Notes.pack(favorite.note || favorite.noteId, me),
 | 
								note: await Notes.pack(favorite.note || favorite.noteId, me),
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public packMany(
 | 
						packMany(
 | 
				
			||||||
		favorites: any[],
 | 
							favorites: any[],
 | 
				
			||||||
		me: { id: User['id'] }
 | 
							me: { id: User['id'] }
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		return Promise.all(favorites.map(x => this.pack(x, me)));
 | 
							return Promise.all(favorites.map(x => this.pack(x, me)));
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
}
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,13 +1,12 @@
 | 
				
			||||||
import { EntityRepository, Repository } from 'typeorm';
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
import { NoteReaction } from '@/models/entities/note-reaction.js';
 | 
					import { NoteReaction } from '@/models/entities/note-reaction.js';
 | 
				
			||||||
import { Notes, Users } from '../index.js';
 | 
					import { Notes, Users } from '../index.js';
 | 
				
			||||||
import { Packed } from '@/misc/schema.js';
 | 
					import { Packed } from '@/misc/schema.js';
 | 
				
			||||||
import { convertLegacyReaction } from '@/misc/reaction-lib.js';
 | 
					import { convertLegacyReaction } from '@/misc/reaction-lib.js';
 | 
				
			||||||
import { User } from '@/models/entities/user.js';
 | 
					import { User } from '@/models/entities/user.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@EntityRepository(NoteReaction)
 | 
					export const NoteReactionRepository = db.getRepository(NoteReaction).extend({
 | 
				
			||||||
export class NoteReactionRepository extends Repository<NoteReaction> {
 | 
						async pack(
 | 
				
			||||||
	public async pack(
 | 
					 | 
				
			||||||
		src: NoteReaction['id'] | NoteReaction,
 | 
							src: NoteReaction['id'] | NoteReaction,
 | 
				
			||||||
		me?: { id: User['id'] } | null | undefined,
 | 
							me?: { id: User['id'] } | null | undefined,
 | 
				
			||||||
		options?: {
 | 
							options?: {
 | 
				
			||||||
| 
						 | 
					@ -18,7 +17,7 @@ export class NoteReactionRepository extends Repository<NoteReaction> {
 | 
				
			||||||
			withNote: false,
 | 
								withNote: false,
 | 
				
			||||||
		}, options);
 | 
							}, options);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const reaction = typeof src === 'object' ? src : await this.findOneOrFail(src);
 | 
							const reaction = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			id: reaction.id,
 | 
								id: reaction.id,
 | 
				
			||||||
| 
						 | 
					@ -29,5 +28,5 @@ export class NoteReactionRepository extends Repository<NoteReaction> {
 | 
				
			||||||
				note: await Notes.pack(reaction.note ?? reaction.noteId, me),
 | 
									note: await Notes.pack(reaction.note ?? reaction.noteId, me),
 | 
				
			||||||
			} : {}),
 | 
								} : {}),
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
}
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
import { EntityRepository, Repository, In } from 'typeorm';
 | 
					import { In } from 'typeorm';
 | 
				
			||||||
import * as mfm from 'mfm-js';
 | 
					import * as mfm from 'mfm-js';
 | 
				
			||||||
import { Note } from '@/models/entities/note.js';
 | 
					import { Note } from '@/models/entities/note.js';
 | 
				
			||||||
import { User } from '@/models/entities/user.js';
 | 
					import { User } from '@/models/entities/user.js';
 | 
				
			||||||
| 
						 | 
					@ -9,59 +9,9 @@ import { awaitAll } from '@/prelude/await-all.js';
 | 
				
			||||||
import { convertLegacyReaction, convertLegacyReactions, decodeReaction } from '@/misc/reaction-lib.js';
 | 
					import { convertLegacyReaction, convertLegacyReactions, decodeReaction } from '@/misc/reaction-lib.js';
 | 
				
			||||||
import { NoteReaction } from '@/models/entities/note-reaction.js';
 | 
					import { NoteReaction } from '@/models/entities/note-reaction.js';
 | 
				
			||||||
import { aggregateNoteEmojis, populateEmojis, prefetchEmojis } from '@/misc/populate-emojis.js';
 | 
					import { aggregateNoteEmojis, populateEmojis, prefetchEmojis } from '@/misc/populate-emojis.js';
 | 
				
			||||||
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@EntityRepository(Note)
 | 
					async function hideNote(packedNote: Packed<'Note'>, meId: User['id'] | null) {
 | 
				
			||||||
export class NoteRepository extends Repository<Note> {
 | 
					 | 
				
			||||||
	public async isVisibleForMe(note: Note, meId: User['id'] | null): Promise<boolean> {
 | 
					 | 
				
			||||||
		// visibility が specified かつ自分が指定されていなかったら非表示
 | 
					 | 
				
			||||||
		if (note.visibility === 'specified') {
 | 
					 | 
				
			||||||
			if (meId == null) {
 | 
					 | 
				
			||||||
				return false;
 | 
					 | 
				
			||||||
			} else if (meId === note.userId) {
 | 
					 | 
				
			||||||
				return true;
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				// 指定されているかどうか
 | 
					 | 
				
			||||||
				const specified = note.visibleUserIds.some((id: any) => meId === id);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				if (specified) {
 | 
					 | 
				
			||||||
					return true;
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					return false;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// visibility が followers かつ自分が投稿者のフォロワーでなかったら非表示
 | 
					 | 
				
			||||||
		if (note.visibility === 'followers') {
 | 
					 | 
				
			||||||
			if (meId == null) {
 | 
					 | 
				
			||||||
				return false;
 | 
					 | 
				
			||||||
			} else if (meId === note.userId) {
 | 
					 | 
				
			||||||
				return true;
 | 
					 | 
				
			||||||
			} else if (note.reply && (meId === note.reply.userId)) {
 | 
					 | 
				
			||||||
				// 自分の投稿に対するリプライ
 | 
					 | 
				
			||||||
				return true;
 | 
					 | 
				
			||||||
			} else if (note.mentions && note.mentions.some(id => meId === id)) {
 | 
					 | 
				
			||||||
				// 自分へのメンション
 | 
					 | 
				
			||||||
				return true;
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				// フォロワーかどうか
 | 
					 | 
				
			||||||
				const following = await Followings.findOne({
 | 
					 | 
				
			||||||
					followeeId: note.userId,
 | 
					 | 
				
			||||||
					followerId: meId,
 | 
					 | 
				
			||||||
				});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				if (following == null) {
 | 
					 | 
				
			||||||
					return false;
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					return true;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	private async hideNote(packedNote: Packed<'Note'>, meId: User['id'] | null) {
 | 
					 | 
				
			||||||
	// TODO: isVisibleForMe を使うようにしても良さそう(型違うけど)
 | 
						// TODO: isVisibleForMe を使うようにしても良さそう(型違うけど)
 | 
				
			||||||
	let hide = false;
 | 
						let hide = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -97,7 +47,7 @@ export class NoteRepository extends Repository<Note> {
 | 
				
			||||||
			hide = false;
 | 
								hide = false;
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			// フォロワーかどうか
 | 
								// フォロワーかどうか
 | 
				
			||||||
				const following = await Followings.findOne({
 | 
								const following = await Followings.findOneBy({
 | 
				
			||||||
				followeeId: packedNote.userId,
 | 
									followeeId: packedNote.userId,
 | 
				
			||||||
				followerId: meId,
 | 
									followerId: meId,
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
| 
						 | 
					@ -121,7 +71,120 @@ export class NoteRepository extends Repository<Note> {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public async pack(
 | 
					async function populatePoll(note: Note, meId: User['id'] | null) {
 | 
				
			||||||
 | 
						const poll = await Polls.findOneByOrFail({ noteId: note.id });
 | 
				
			||||||
 | 
						const choices = poll.choices.map(c => ({
 | 
				
			||||||
 | 
							text: c,
 | 
				
			||||||
 | 
							votes: poll.votes[poll.choices.indexOf(c)],
 | 
				
			||||||
 | 
							isVoted: false,
 | 
				
			||||||
 | 
						}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (meId) {
 | 
				
			||||||
 | 
							if (poll.multiple) {
 | 
				
			||||||
 | 
								const votes = await PollVotes.findBy({
 | 
				
			||||||
 | 
									userId: meId,
 | 
				
			||||||
 | 
									noteId: note.id,
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								const myChoices = votes.map(v => v.choice);
 | 
				
			||||||
 | 
								for (const myChoice of myChoices) {
 | 
				
			||||||
 | 
									choices[myChoice].isVoted = true;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								const vote = await PollVotes.findOneBy({
 | 
				
			||||||
 | 
									userId: meId,
 | 
				
			||||||
 | 
									noteId: note.id,
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (vote) {
 | 
				
			||||||
 | 
									choices[vote.choice].isVoted = true;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return {
 | 
				
			||||||
 | 
							multiple: poll.multiple,
 | 
				
			||||||
 | 
							expiresAt: poll.expiresAt,
 | 
				
			||||||
 | 
							choices,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function populateMyReaction(note: Note, meId: User['id'], _hint_?: {
 | 
				
			||||||
 | 
						myReactions: Map<Note['id'], NoteReaction | null>;
 | 
				
			||||||
 | 
					}) {
 | 
				
			||||||
 | 
						if (_hint_?.myReactions) {
 | 
				
			||||||
 | 
							const reaction = _hint_.myReactions.get(note.id);
 | 
				
			||||||
 | 
							if (reaction) {
 | 
				
			||||||
 | 
								return convertLegacyReaction(reaction.reaction);
 | 
				
			||||||
 | 
							} else if (reaction === null) {
 | 
				
			||||||
 | 
								return undefined;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// 実装上抜けがあるだけかもしれないので、「ヒントに含まれてなかったら(=undefinedなら)return」のようにはしない
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const reaction = await NoteReactions.findOneBy({
 | 
				
			||||||
 | 
							userId: meId,
 | 
				
			||||||
 | 
							noteId: note.id,
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (reaction) {
 | 
				
			||||||
 | 
							return convertLegacyReaction(reaction.reaction);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return undefined;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const NoteRepository = db.getRepository(Note).extend({
 | 
				
			||||||
 | 
						async isVisibleForMe(note: Note, meId: User['id'] | null): Promise<boolean> {
 | 
				
			||||||
 | 
							// visibility が specified かつ自分が指定されていなかったら非表示
 | 
				
			||||||
 | 
							if (note.visibility === 'specified') {
 | 
				
			||||||
 | 
								if (meId == null) {
 | 
				
			||||||
 | 
									return false;
 | 
				
			||||||
 | 
								} else if (meId === note.userId) {
 | 
				
			||||||
 | 
									return true;
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									// 指定されているかどうか
 | 
				
			||||||
 | 
									const specified = note.visibleUserIds.some((id: any) => meId === id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (specified) {
 | 
				
			||||||
 | 
										return true;
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										return false;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// visibility が followers かつ自分が投稿者のフォロワーでなかったら非表示
 | 
				
			||||||
 | 
							if (note.visibility === 'followers') {
 | 
				
			||||||
 | 
								if (meId == null) {
 | 
				
			||||||
 | 
									return false;
 | 
				
			||||||
 | 
								} else if (meId === note.userId) {
 | 
				
			||||||
 | 
									return true;
 | 
				
			||||||
 | 
								} else if (note.reply && (meId === note.reply.userId)) {
 | 
				
			||||||
 | 
									// 自分の投稿に対するリプライ
 | 
				
			||||||
 | 
									return true;
 | 
				
			||||||
 | 
								} else if (note.mentions && note.mentions.some(id => meId === id)) {
 | 
				
			||||||
 | 
									// 自分へのメンション
 | 
				
			||||||
 | 
									return true;
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									// フォロワーかどうか
 | 
				
			||||||
 | 
									const following = await Followings.findOneBy({
 | 
				
			||||||
 | 
										followeeId: note.userId,
 | 
				
			||||||
 | 
										followerId: meId,
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (following == null) {
 | 
				
			||||||
 | 
										return false;
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										return true;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						async pack(
 | 
				
			||||||
		src: Note['id'] | Note,
 | 
							src: Note['id'] | Note,
 | 
				
			||||||
		me?: { id: User['id'] } | null | undefined,
 | 
							me?: { id: User['id'] } | null | undefined,
 | 
				
			||||||
		options?: {
 | 
							options?: {
 | 
				
			||||||
| 
						 | 
					@ -138,68 +201,9 @@ export class NoteRepository extends Repository<Note> {
 | 
				
			||||||
		}, options);
 | 
							}, options);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const meId = me ? me.id : null;
 | 
							const meId = me ? me.id : null;
 | 
				
			||||||
		const note = typeof src === 'object' ? src : await this.findOneOrFail(src);
 | 
							const note = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
 | 
				
			||||||
		const host = note.userHost;
 | 
							const host = note.userHost;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		async function populatePoll() {
 | 
					 | 
				
			||||||
			const poll = await Polls.findOneOrFail(note.id);
 | 
					 | 
				
			||||||
			const choices = poll.choices.map(c => ({
 | 
					 | 
				
			||||||
				text: c,
 | 
					 | 
				
			||||||
				votes: poll.votes[poll.choices.indexOf(c)],
 | 
					 | 
				
			||||||
				isVoted: false,
 | 
					 | 
				
			||||||
			}));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (poll.multiple) {
 | 
					 | 
				
			||||||
				const votes = await PollVotes.find({
 | 
					 | 
				
			||||||
					userId: meId!,
 | 
					 | 
				
			||||||
					noteId: note.id,
 | 
					 | 
				
			||||||
				});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				const myChoices = votes.map(v => v.choice);
 | 
					 | 
				
			||||||
				for (const myChoice of myChoices) {
 | 
					 | 
				
			||||||
					choices[myChoice].isVoted = true;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				const vote = await PollVotes.findOne({
 | 
					 | 
				
			||||||
					userId: meId!,
 | 
					 | 
				
			||||||
					noteId: note.id,
 | 
					 | 
				
			||||||
				});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				if (vote) {
 | 
					 | 
				
			||||||
					choices[vote.choice].isVoted = true;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			return {
 | 
					 | 
				
			||||||
				multiple: poll.multiple,
 | 
					 | 
				
			||||||
				expiresAt: poll.expiresAt,
 | 
					 | 
				
			||||||
				choices,
 | 
					 | 
				
			||||||
			};
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		async function populateMyReaction() {
 | 
					 | 
				
			||||||
			if (options?._hint_?.myReactions) {
 | 
					 | 
				
			||||||
				const reaction = options._hint_.myReactions.get(note.id);
 | 
					 | 
				
			||||||
				if (reaction) {
 | 
					 | 
				
			||||||
					return convertLegacyReaction(reaction.reaction);
 | 
					 | 
				
			||||||
				} else if (reaction === null) {
 | 
					 | 
				
			||||||
					return undefined;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				// 実装上抜けがあるだけかもしれないので、「ヒントに含まれてなかったら(=undefinedなら)return」のようにはしない
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			const reaction = await NoteReactions.findOne({
 | 
					 | 
				
			||||||
				userId: meId!,
 | 
					 | 
				
			||||||
				noteId: note.id,
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (reaction) {
 | 
					 | 
				
			||||||
				return convertLegacyReaction(reaction.reaction);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			return undefined;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		let text = note.text;
 | 
							let text = note.text;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (note.name && (note.url ?? note.uri)) {
 | 
							if (note.name && (note.url ?? note.uri)) {
 | 
				
			||||||
| 
						 | 
					@ -209,7 +213,7 @@ export class NoteRepository extends Repository<Note> {
 | 
				
			||||||
		const channel = note.channelId
 | 
							const channel = note.channelId
 | 
				
			||||||
			? note.channel
 | 
								? note.channel
 | 
				
			||||||
				? note.channel
 | 
									? note.channel
 | 
				
			||||||
				: await Channels.findOne(note.channelId)
 | 
									: await Channels.findOneBy({ id: note.channelId })
 | 
				
			||||||
			: null;
 | 
								: null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const reactionEmojiNames = Object.keys(note.reactions).filter(x => x?.startsWith(':')).map(x => decodeReaction(x).reaction).map(x => x.replace(/:/g, ''));
 | 
							const reactionEmojiNames = Object.keys(note.reactions).filter(x => x?.startsWith(':')).map(x => decodeReaction(x).reaction).map(x => x.replace(/:/g, ''));
 | 
				
			||||||
| 
						 | 
					@ -255,10 +259,10 @@ export class NoteRepository extends Repository<Note> {
 | 
				
			||||||
					_hint_: options?._hint_,
 | 
										_hint_: options?._hint_,
 | 
				
			||||||
				}) : undefined,
 | 
									}) : undefined,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				poll: note.hasPoll ? populatePoll() : undefined,
 | 
									poll: note.hasPoll ? populatePoll(note, meId) : undefined,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				...(meId ? {
 | 
									...(meId ? {
 | 
				
			||||||
					myReaction: populateMyReaction(),
 | 
										myReaction: populateMyReaction(note, meId, options?._hint_),
 | 
				
			||||||
				} : {}),
 | 
									} : {}),
 | 
				
			||||||
			} : {}),
 | 
								} : {}),
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
| 
						 | 
					@ -275,13 +279,13 @@ export class NoteRepository extends Repository<Note> {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!opts.skipHide) {
 | 
							if (!opts.skipHide) {
 | 
				
			||||||
			await this.hideNote(packed, meId);
 | 
								await hideNote(packed, meId);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return packed;
 | 
							return packed;
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public async packMany(
 | 
						async packMany(
 | 
				
			||||||
		notes: Note[],
 | 
							notes: Note[],
 | 
				
			||||||
		me?: { id: User['id'] } | null | undefined,
 | 
							me?: { id: User['id'] } | null | undefined,
 | 
				
			||||||
		options?: {
 | 
							options?: {
 | 
				
			||||||
| 
						 | 
					@ -296,7 +300,7 @@ export class NoteRepository extends Repository<Note> {
 | 
				
			||||||
		if (meId) {
 | 
							if (meId) {
 | 
				
			||||||
			const renoteIds = notes.filter(n => n.renoteId != null).map(n => n.renoteId!);
 | 
								const renoteIds = notes.filter(n => n.renoteId != null).map(n => n.renoteId!);
 | 
				
			||||||
			const targets = [...notes.map(n => n.id), ...renoteIds];
 | 
								const targets = [...notes.map(n => n.id), ...renoteIds];
 | 
				
			||||||
			const myReactions = await NoteReactions.find({
 | 
								const myReactions = await NoteReactions.findBy({
 | 
				
			||||||
				userId: meId,
 | 
									userId: meId,
 | 
				
			||||||
				noteId: In(targets),
 | 
									noteId: In(targets),
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
| 
						 | 
					@ -314,5 +318,5 @@ export class NoteRepository extends Repository<Note> {
 | 
				
			||||||
				myReactions: myReactionsMap,
 | 
									myReactions: myReactionsMap,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		})));
 | 
							})));
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
}
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
import { EntityRepository, In, Repository } from 'typeorm';
 | 
					import { In, Repository } from 'typeorm';
 | 
				
			||||||
import { Users, Notes, UserGroupInvitations, AccessTokens, NoteReactions } from '../index.js';
 | 
					import { Users, Notes, UserGroupInvitations, AccessTokens, NoteReactions } from '../index.js';
 | 
				
			||||||
import { Notification } from '@/models/entities/notification.js';
 | 
					import { Notification } from '@/models/entities/notification.js';
 | 
				
			||||||
import { awaitAll } from '@/prelude/await-all.js';
 | 
					import { awaitAll } from '@/prelude/await-all.js';
 | 
				
			||||||
| 
						 | 
					@ -8,10 +8,10 @@ import { NoteReaction } from '@/models/entities/note-reaction.js';
 | 
				
			||||||
import { User } from '@/models/entities/user.js';
 | 
					import { User } from '@/models/entities/user.js';
 | 
				
			||||||
import { aggregateNoteEmojis, prefetchEmojis } from '@/misc/populate-emojis.js';
 | 
					import { aggregateNoteEmojis, prefetchEmojis } from '@/misc/populate-emojis.js';
 | 
				
			||||||
import { notificationTypes } from '@/types.js';
 | 
					import { notificationTypes } from '@/types.js';
 | 
				
			||||||
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@EntityRepository(Notification)
 | 
					export const NotificationRepository = db.getRepository(Notification).extend({
 | 
				
			||||||
export class NotificationRepository extends Repository<Notification> {
 | 
						async pack(
 | 
				
			||||||
	public async pack(
 | 
					 | 
				
			||||||
		src: Notification['id'] | Notification,
 | 
							src: Notification['id'] | Notification,
 | 
				
			||||||
		options: {
 | 
							options: {
 | 
				
			||||||
			_hintForEachNotes_?: {
 | 
								_hintForEachNotes_?: {
 | 
				
			||||||
| 
						 | 
					@ -19,8 +19,8 @@ export class NotificationRepository extends Repository<Notification> {
 | 
				
			||||||
			};
 | 
								};
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	): Promise<Packed<'Notification'>> {
 | 
						): Promise<Packed<'Notification'>> {
 | 
				
			||||||
		const notification = typeof src === 'object' ? src : await this.findOneOrFail(src);
 | 
							const notification = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
 | 
				
			||||||
		const token = notification.appAccessTokenId ? await AccessTokens.findOneOrFail(notification.appAccessTokenId) : null;
 | 
							const token = notification.appAccessTokenId ? await AccessTokens.findOneByOrFail({ id: notification.appAccessTokenId }) : null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return await awaitAll({
 | 
							return await awaitAll({
 | 
				
			||||||
			id: notification.id,
 | 
								id: notification.id,
 | 
				
			||||||
| 
						 | 
					@ -82,9 +82,9 @@ export class NotificationRepository extends Repository<Notification> {
 | 
				
			||||||
				icon: notification.customIcon || token?.iconUrl,
 | 
									icon: notification.customIcon || token?.iconUrl,
 | 
				
			||||||
			} : {}),
 | 
								} : {}),
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public async packMany(
 | 
						async packMany(
 | 
				
			||||||
		notifications: Notification[],
 | 
							notifications: Notification[],
 | 
				
			||||||
		meId: User['id']
 | 
							meId: User['id']
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
| 
						 | 
					@ -95,7 +95,7 @@ export class NotificationRepository extends Repository<Notification> {
 | 
				
			||||||
		const myReactionsMap = new Map<Note['id'], NoteReaction | null>();
 | 
							const myReactionsMap = new Map<Note['id'], NoteReaction | null>();
 | 
				
			||||||
		const renoteIds = notes.filter(n => n.renoteId != null).map(n => n.renoteId!);
 | 
							const renoteIds = notes.filter(n => n.renoteId != null).map(n => n.renoteId!);
 | 
				
			||||||
		const targets = [...noteIds, ...renoteIds];
 | 
							const targets = [...noteIds, ...renoteIds];
 | 
				
			||||||
		const myReactions = await NoteReactions.find({
 | 
							const myReactions = await NoteReactions.findBy({
 | 
				
			||||||
			userId: meId,
 | 
								userId: meId,
 | 
				
			||||||
			noteId: In(targets),
 | 
								noteId: In(targets),
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
| 
						 | 
					@ -111,5 +111,5 @@ export class NotificationRepository extends Repository<Notification> {
 | 
				
			||||||
				myReactions: myReactionsMap,
 | 
									myReactions: myReactionsMap,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		})));
 | 
							})));
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
}
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,26 +1,25 @@
 | 
				
			||||||
import { EntityRepository, Repository } from 'typeorm';
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
import { PageLike } from '@/models/entities/page-like.js';
 | 
					import { PageLike } from '@/models/entities/page-like.js';
 | 
				
			||||||
import { Pages } from '../index.js';
 | 
					import { Pages } from '../index.js';
 | 
				
			||||||
import { User } from '@/models/entities/user.js';
 | 
					import { User } from '@/models/entities/user.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@EntityRepository(PageLike)
 | 
					export const PageLikeRepository = db.getRepository(PageLike).extend({
 | 
				
			||||||
export class PageLikeRepository extends Repository<PageLike> {
 | 
						async pack(
 | 
				
			||||||
	public async pack(
 | 
					 | 
				
			||||||
		src: PageLike['id'] | PageLike,
 | 
							src: PageLike['id'] | PageLike,
 | 
				
			||||||
		me?: { id: User['id'] } | null | undefined
 | 
							me?: { id: User['id'] } | null | undefined
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		const like = typeof src === 'object' ? src : await this.findOneOrFail(src);
 | 
							const like = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			id: like.id,
 | 
								id: like.id,
 | 
				
			||||||
			page: await Pages.pack(like.page || like.pageId, me),
 | 
								page: await Pages.pack(like.page || like.pageId, me),
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public packMany(
 | 
						packMany(
 | 
				
			||||||
		likes: any[],
 | 
							likes: any[],
 | 
				
			||||||
		me: { id: User['id'] }
 | 
							me: { id: User['id'] }
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		return Promise.all(likes.map(x => this.pack(x, me)));
 | 
							return Promise.all(likes.map(x => this.pack(x, me)));
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
}
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
import { EntityRepository, Repository } from 'typeorm';
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
import { Page } from '@/models/entities/page.js';
 | 
					import { Page } from '@/models/entities/page.js';
 | 
				
			||||||
import { Packed } from '@/misc/schema.js';
 | 
					import { Packed } from '@/misc/schema.js';
 | 
				
			||||||
import { Users, DriveFiles, PageLikes } from '../index.js';
 | 
					import { Users, DriveFiles, PageLikes } from '../index.js';
 | 
				
			||||||
| 
						 | 
					@ -6,20 +6,19 @@ import { awaitAll } from '@/prelude/await-all.js';
 | 
				
			||||||
import { DriveFile } from '@/models/entities/drive-file.js';
 | 
					import { DriveFile } from '@/models/entities/drive-file.js';
 | 
				
			||||||
import { User } from '@/models/entities/user.js';
 | 
					import { User } from '@/models/entities/user.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@EntityRepository(Page)
 | 
					export const PageRepository = db.getRepository(Page).extend({
 | 
				
			||||||
export class PageRepository extends Repository<Page> {
 | 
						async pack(
 | 
				
			||||||
	public async pack(
 | 
					 | 
				
			||||||
		src: Page['id'] | Page,
 | 
							src: Page['id'] | Page,
 | 
				
			||||||
		me?: { id: User['id'] } | null | undefined,
 | 
							me?: { id: User['id'] } | null | undefined,
 | 
				
			||||||
	): 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.findOneOrFail(src);
 | 
							const page = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const attachedFiles: Promise<DriveFile | undefined>[] = [];
 | 
							const attachedFiles: Promise<DriveFile | undefined>[] = [];
 | 
				
			||||||
		const collectFile = (xs: any[]) => {
 | 
							const collectFile = (xs: any[]) => {
 | 
				
			||||||
			for (const x of xs) {
 | 
								for (const x of xs) {
 | 
				
			||||||
				if (x.type === 'image') {
 | 
									if (x.type === 'image') {
 | 
				
			||||||
					attachedFiles.push(DriveFiles.findOne({
 | 
										attachedFiles.push(DriveFiles.findOneBy({
 | 
				
			||||||
						id: x.fileId,
 | 
											id: x.fileId,
 | 
				
			||||||
						userId: page.userId,
 | 
											userId: page.userId,
 | 
				
			||||||
					}));
 | 
										}));
 | 
				
			||||||
| 
						 | 
					@ -76,14 +75,14 @@ export class PageRepository extends Repository<Page> {
 | 
				
			||||||
			eyeCatchingImage: page.eyeCatchingImageId ? await DriveFiles.pack(page.eyeCatchingImageId) : null,
 | 
								eyeCatchingImage: page.eyeCatchingImageId ? await DriveFiles.pack(page.eyeCatchingImageId) : null,
 | 
				
			||||||
			attachedFiles: DriveFiles.packMany(await Promise.all(attachedFiles)),
 | 
								attachedFiles: DriveFiles.packMany(await Promise.all(attachedFiles)),
 | 
				
			||||||
			likedCount: page.likedCount,
 | 
								likedCount: page.likedCount,
 | 
				
			||||||
			isLiked: meId ? await PageLikes.findOne({ pageId: page.id, userId: meId }).then(x => x != null) : undefined,
 | 
								isLiked: meId ? await PageLikes.findOneBy({ pageId: page.id, userId: meId }).then(x => x != null) : undefined,
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public packMany(
 | 
						packMany(
 | 
				
			||||||
		pages: Page[],
 | 
							pages: Page[],
 | 
				
			||||||
		me?: { id: User['id'] } | null | undefined,
 | 
							me?: { id: User['id'] } | null | undefined,
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		return Promise.all(pages.map(x => this.pack(x, me)));
 | 
							return Promise.all(pages.map(x => this.pack(x, me)));
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
}
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,5 @@
 | 
				
			||||||
import { EntityRepository, Repository } from 'typeorm';
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
import { Relay } from '@/models/entities/relay.js';
 | 
					import { Relay } from '@/models/entities/relay.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@EntityRepository(Relay)
 | 
					export const RelayRepository = db.getRepository(Relay).extend({
 | 
				
			||||||
export class RelayRepository extends Repository<Relay> {
 | 
					});
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,10 @@
 | 
				
			||||||
import { EntityRepository, Repository } from 'typeorm';
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
import { Signin } from '@/models/entities/signin.js';
 | 
					import { Signin } from '@/models/entities/signin.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@EntityRepository(Signin)
 | 
					export const SigninRepository = db.getRepository(Signin).extend({
 | 
				
			||||||
export class SigninRepository extends Repository<Signin> {
 | 
						async pack(
 | 
				
			||||||
	public async pack(
 | 
					 | 
				
			||||||
		src: Signin,
 | 
							src: Signin,
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		return src;
 | 
							return src;
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
}
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,23 +1,22 @@
 | 
				
			||||||
import { EntityRepository, Repository } from 'typeorm';
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
import { UserGroupInvitation } from '@/models/entities/user-group-invitation.js';
 | 
					import { UserGroupInvitation } from '@/models/entities/user-group-invitation.js';
 | 
				
			||||||
import { UserGroups } from '../index.js';
 | 
					import { UserGroups } from '../index.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@EntityRepository(UserGroupInvitation)
 | 
					export const UserGroupInvitationRepository = db.getRepository(UserGroupInvitation).extend({
 | 
				
			||||||
export class UserGroupInvitationRepository extends Repository<UserGroupInvitation> {
 | 
						async pack(
 | 
				
			||||||
	public async pack(
 | 
					 | 
				
			||||||
		src: UserGroupInvitation['id'] | UserGroupInvitation,
 | 
							src: UserGroupInvitation['id'] | UserGroupInvitation,
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		const invitation = typeof src === 'object' ? src : await this.findOneOrFail(src);
 | 
							const invitation = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			id: invitation.id,
 | 
								id: invitation.id,
 | 
				
			||||||
			group: await UserGroups.pack(invitation.userGroup || invitation.userGroupId),
 | 
								group: await UserGroups.pack(invitation.userGroup || invitation.userGroupId),
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public packMany(
 | 
						packMany(
 | 
				
			||||||
		invitations: any[],
 | 
							invitations: any[],
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		return Promise.all(invitations.map(x => this.pack(x)));
 | 
							return Promise.all(invitations.map(x => this.pack(x)));
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
}
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,16 +1,15 @@
 | 
				
			||||||
import { EntityRepository, Repository } from 'typeorm';
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
import { UserGroup } from '@/models/entities/user-group.js';
 | 
					import { UserGroup } from '@/models/entities/user-group.js';
 | 
				
			||||||
import { UserGroupJoinings } from '../index.js';
 | 
					import { UserGroupJoinings } from '../index.js';
 | 
				
			||||||
import { Packed } from '@/misc/schema.js';
 | 
					import { Packed } from '@/misc/schema.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@EntityRepository(UserGroup)
 | 
					export const UserGroupRepository = db.getRepository(UserGroup).extend({
 | 
				
			||||||
export class UserGroupRepository extends Repository<UserGroup> {
 | 
						async pack(
 | 
				
			||||||
	public async pack(
 | 
					 | 
				
			||||||
		src: UserGroup['id'] | UserGroup,
 | 
							src: UserGroup['id'] | UserGroup,
 | 
				
			||||||
	): Promise<Packed<'UserGroup'>> {
 | 
						): Promise<Packed<'UserGroup'>> {
 | 
				
			||||||
		const userGroup = typeof src === 'object' ? src : await this.findOneOrFail(src);
 | 
							const userGroup = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const users = await UserGroupJoinings.find({
 | 
							const users = await UserGroupJoinings.findBy({
 | 
				
			||||||
			userGroupId: userGroup.id,
 | 
								userGroupId: userGroup.id,
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,5 +20,5 @@ export class UserGroupRepository extends Repository<UserGroup> {
 | 
				
			||||||
			ownerId: userGroup.userId,
 | 
								ownerId: userGroup.userId,
 | 
				
			||||||
			userIds: users.map(x => x.userId),
 | 
								userIds: users.map(x => x.userId),
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
}
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,16 +1,15 @@
 | 
				
			||||||
import { EntityRepository, Repository } from 'typeorm';
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
import { UserList } from '@/models/entities/user-list.js';
 | 
					import { UserList } from '@/models/entities/user-list.js';
 | 
				
			||||||
import { UserListJoinings } from '../index.js';
 | 
					import { UserListJoinings } from '../index.js';
 | 
				
			||||||
import { Packed } from '@/misc/schema.js';
 | 
					import { Packed } from '@/misc/schema.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@EntityRepository(UserList)
 | 
					export const UserListRepository = db.getRepository(UserList).extend({
 | 
				
			||||||
export class UserListRepository extends Repository<UserList> {
 | 
						async pack(
 | 
				
			||||||
	public async pack(
 | 
					 | 
				
			||||||
		src: UserList['id'] | UserList,
 | 
							src: UserList['id'] | UserList,
 | 
				
			||||||
	): Promise<Packed<'UserList'>> {
 | 
						): Promise<Packed<'UserList'>> {
 | 
				
			||||||
		const userList = typeof src === 'object' ? src : await this.findOneOrFail(src);
 | 
							const userList = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const users = await UserListJoinings.find({
 | 
							const users = await UserListJoinings.findBy({
 | 
				
			||||||
			userListId: userList.id,
 | 
								userListId: userList.id,
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,5 +19,5 @@ export class UserListRepository extends Repository<UserList> {
 | 
				
			||||||
			name: userList.name,
 | 
								name: userList.name,
 | 
				
			||||||
			userIds: users.map(x => x.userId),
 | 
								userIds: users.map(x => x.userId),
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
}
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,6 +8,11 @@ import { awaitAll, Promiseable } from '@/prelude/await-all.js';
 | 
				
			||||||
import { populateEmojis } from '@/misc/populate-emojis.js';
 | 
					import { populateEmojis } from '@/misc/populate-emojis.js';
 | 
				
			||||||
import { getAntennas } from '@/misc/antenna-cache.js';
 | 
					import { getAntennas } from '@/misc/antenna-cache.js';
 | 
				
			||||||
import { USER_ACTIVE_THRESHOLD, USER_ONLINE_THRESHOLD } from '@/const.js';
 | 
					import { USER_ACTIVE_THRESHOLD, USER_ONLINE_THRESHOLD } from '@/const.js';
 | 
				
			||||||
 | 
					import { Cache } from '@/misc/cache.js';
 | 
				
			||||||
 | 
					import { Instance } from '../entities/instance.js';
 | 
				
			||||||
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const userInstanceCache = new Cache<Instance | null>(1000 * 60 * 60 * 3);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type IsUserDetailed<Detailed extends boolean> = Detailed extends true ? Packed<'UserDetailed'> : Packed<'UserLite'>;
 | 
					type IsUserDetailed<Detailed extends boolean> = Detailed extends true ? Packed<'UserDetailed'> : Packed<'UserLite'>;
 | 
				
			||||||
type IsMeAndIsUserDetailed<ExpectsMe extends boolean | null, Detailed extends boolean> =
 | 
					type IsMeAndIsUserDetailed<ExpectsMe extends boolean | null, Detailed extends boolean> =
 | 
				
			||||||
| 
						 | 
					@ -19,51 +24,69 @@ type IsMeAndIsUserDetailed<ExpectsMe extends boolean | null, Detailed extends bo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const ajv = new Ajv();
 | 
					const ajv = new Ajv();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@EntityRepository(User)
 | 
					const localUsernameSchema = { type: 'string', pattern: /^\w{1,20}$/.toString().slice(1, -1) } as const;
 | 
				
			||||||
export class UserRepository extends Repository<User> {
 | 
					const passwordSchema = { type: 'string', minLength: 1 } as const;
 | 
				
			||||||
	public localUsernameSchema = { type: 'string', pattern: /^\w{1,20}$/.toString().slice(1, -1) } as const;
 | 
					const nameSchema = { type: 'string', minLength: 1, maxLength: 50 } as const;
 | 
				
			||||||
	public passwordSchema = { type: 'string', minLength: 1 } as const;
 | 
					const descriptionSchema = { type: 'string', minLength: 1, maxLength: 500 } as const;
 | 
				
			||||||
	public nameSchema = { type: 'string', minLength: 1, maxLength: 50 } as const;
 | 
					const locationSchema = { type: 'string', minLength: 1, maxLength: 50 } as const;
 | 
				
			||||||
	public descriptionSchema = { type: 'string', minLength: 1, maxLength: 500 } as const;
 | 
					const birthdaySchema = { type: 'string', pattern: /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/.toString().slice(1, -1) } as const;
 | 
				
			||||||
	public locationSchema = { type: 'string', minLength: 1, maxLength: 50 } as const;
 | 
					
 | 
				
			||||||
	public birthdaySchema = { type: 'string', pattern: /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/.toString().slice(1, -1) } as const;
 | 
					function isLocalUser(user: User): user is ILocalUser;
 | 
				
			||||||
 | 
					function isLocalUser<T extends { host: User['host'] }>(user: T): user is T & { host: null; };
 | 
				
			||||||
 | 
					function isLocalUser(user: User | { host: User['host'] }): boolean {
 | 
				
			||||||
 | 
						return user.host == null;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function isRemoteUser(user: User): user is IRemoteUser;
 | 
				
			||||||
 | 
					function isRemoteUser<T extends { host: User['host'] }>(user: T): user is T & { host: string; };
 | 
				
			||||||
 | 
					function isRemoteUser(user: User | { host: User['host'] }): boolean {
 | 
				
			||||||
 | 
						return !isLocalUser(user);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const UserRepository = db.getRepository(User).extend({
 | 
				
			||||||
 | 
						localUsernameSchema,
 | 
				
			||||||
 | 
						passwordSchema,
 | 
				
			||||||
 | 
						nameSchema,
 | 
				
			||||||
 | 
						descriptionSchema,
 | 
				
			||||||
 | 
						locationSchema,
 | 
				
			||||||
 | 
						birthdaySchema,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	//#region Validators
 | 
						//#region Validators
 | 
				
			||||||
	public validateLocalUsername = ajv.compile(this.localUsernameSchema);
 | 
						validateLocalUsername: ajv.compile(localUsernameSchema),
 | 
				
			||||||
	public validatePassword = ajv.compile(this.passwordSchema);
 | 
						validatePassword: ajv.compile(passwordSchema),
 | 
				
			||||||
	public validateName = ajv.compile(this.nameSchema);
 | 
						validateName: ajv.compile(nameSchema),
 | 
				
			||||||
	public validateDescription = ajv.compile(this.descriptionSchema);
 | 
						validateDescription: ajv.compile(descriptionSchema),
 | 
				
			||||||
	public validateLocation = ajv.compile(this.locationSchema);
 | 
						validateLocation: ajv.compile(locationSchema),
 | 
				
			||||||
	public validateBirthday = ajv.compile(this.birthdaySchema);
 | 
						validateBirthday: ajv.compile(birthdaySchema),
 | 
				
			||||||
	//#endregion
 | 
						//#endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public async getRelation(me: User['id'], target: User['id']) {
 | 
						async getRelation(me: User['id'], target: User['id']) {
 | 
				
			||||||
		const [following1, following2, followReq1, followReq2, toBlocking, fromBlocked, mute] = await Promise.all([
 | 
							const [following1, following2, followReq1, followReq2, toBlocking, fromBlocked, mute] = await Promise.all([
 | 
				
			||||||
			Followings.findOne({
 | 
								Followings.findOneBy({
 | 
				
			||||||
				followerId: me,
 | 
									followerId: me,
 | 
				
			||||||
				followeeId: target,
 | 
									followeeId: target,
 | 
				
			||||||
			}),
 | 
								}),
 | 
				
			||||||
			Followings.findOne({
 | 
								Followings.findOneBy({
 | 
				
			||||||
				followerId: target,
 | 
									followerId: target,
 | 
				
			||||||
				followeeId: me,
 | 
									followeeId: me,
 | 
				
			||||||
			}),
 | 
								}),
 | 
				
			||||||
			FollowRequests.findOne({
 | 
								FollowRequests.findOneBy({
 | 
				
			||||||
				followerId: me,
 | 
									followerId: me,
 | 
				
			||||||
				followeeId: target,
 | 
									followeeId: target,
 | 
				
			||||||
			}),
 | 
								}),
 | 
				
			||||||
			FollowRequests.findOne({
 | 
								FollowRequests.findOneBy({
 | 
				
			||||||
				followerId: target,
 | 
									followerId: target,
 | 
				
			||||||
				followeeId: me,
 | 
									followeeId: me,
 | 
				
			||||||
			}),
 | 
								}),
 | 
				
			||||||
			Blockings.findOne({
 | 
								Blockings.findOneBy({
 | 
				
			||||||
				blockerId: me,
 | 
									blockerId: me,
 | 
				
			||||||
				blockeeId: target,
 | 
									blockeeId: target,
 | 
				
			||||||
			}),
 | 
								}),
 | 
				
			||||||
			Blockings.findOne({
 | 
								Blockings.findOneBy({
 | 
				
			||||||
				blockerId: target,
 | 
									blockerId: target,
 | 
				
			||||||
				blockeeId: me,
 | 
									blockeeId: me,
 | 
				
			||||||
			}),
 | 
								}),
 | 
				
			||||||
			Mutings.findOne({
 | 
								Mutings.findOneBy({
 | 
				
			||||||
				muterId: me,
 | 
									muterId: me,
 | 
				
			||||||
				muteeId: target,
 | 
									muteeId: target,
 | 
				
			||||||
			}),
 | 
								}),
 | 
				
			||||||
| 
						 | 
					@ -79,14 +102,14 @@ export class UserRepository extends Repository<User> {
 | 
				
			||||||
			isBlocked: fromBlocked != null,
 | 
								isBlocked: fromBlocked != null,
 | 
				
			||||||
			isMuted: mute != null,
 | 
								isMuted: mute != null,
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public async getHasUnreadMessagingMessage(userId: User['id']): Promise<boolean> {
 | 
						async getHasUnreadMessagingMessage(userId: User['id']): Promise<boolean> {
 | 
				
			||||||
		const mute = await Mutings.find({
 | 
							const mute = await Mutings.findBy({
 | 
				
			||||||
			muterId: userId,
 | 
								muterId: userId,
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const joinings = await UserGroupJoinings.find({ userId: userId });
 | 
							const joinings = await UserGroupJoinings.findBy({ userId: userId });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const groupQs = Promise.all(joinings.map(j => MessagingMessages.createQueryBuilder('message')
 | 
							const groupQs = Promise.all(joinings.map(j => MessagingMessages.createQueryBuilder('message')
 | 
				
			||||||
			.where(`message.groupId = :groupId`, { groupId: j.userGroupId })
 | 
								.where(`message.groupId = :groupId`, { groupId: j.userGroupId })
 | 
				
			||||||
| 
						 | 
					@ -108,44 +131,44 @@ export class UserRepository extends Repository<User> {
 | 
				
			||||||
		]);
 | 
							]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return withUser || withGroups.some(x => x);
 | 
							return withUser || withGroups.some(x => x);
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public async getHasUnreadAnnouncement(userId: User['id']): Promise<boolean> {
 | 
						async getHasUnreadAnnouncement(userId: User['id']): Promise<boolean> {
 | 
				
			||||||
		const reads = await AnnouncementReads.find({
 | 
							const reads = await AnnouncementReads.findBy({
 | 
				
			||||||
			userId: userId,
 | 
								userId: userId,
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const count = await Announcements.count(reads.length > 0 ? {
 | 
							const count = await Announcements.countBy(reads.length > 0 ? {
 | 
				
			||||||
			id: Not(In(reads.map(read => read.announcementId))),
 | 
								id: Not(In(reads.map(read => read.announcementId))),
 | 
				
			||||||
		} : {});
 | 
							} : {});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return count > 0;
 | 
							return count > 0;
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public async getHasUnreadAntenna(userId: User['id']): Promise<boolean> {
 | 
						async getHasUnreadAntenna(userId: User['id']): Promise<boolean> {
 | 
				
			||||||
		const myAntennas = (await getAntennas()).filter(a => a.userId === userId);
 | 
							const myAntennas = (await getAntennas()).filter(a => a.userId === userId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const unread = myAntennas.length > 0 ? await AntennaNotes.findOne({
 | 
							const unread = myAntennas.length > 0 ? await AntennaNotes.findOneBy({
 | 
				
			||||||
			antennaId: In(myAntennas.map(x => x.id)),
 | 
								antennaId: In(myAntennas.map(x => x.id)),
 | 
				
			||||||
			read: false,
 | 
								read: false,
 | 
				
			||||||
		}) : null;
 | 
							}) : null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return unread != null;
 | 
							return unread != null;
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public async getHasUnreadChannel(userId: User['id']): Promise<boolean> {
 | 
						async getHasUnreadChannel(userId: User['id']): Promise<boolean> {
 | 
				
			||||||
		const channels = await ChannelFollowings.find({ followerId: userId });
 | 
							const channels = await ChannelFollowings.findBy({ followerId: userId });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const unread = channels.length > 0 ? await NoteUnreads.findOne({
 | 
							const unread = channels.length > 0 ? await NoteUnreads.findOneBy({
 | 
				
			||||||
			userId: userId,
 | 
								userId: userId,
 | 
				
			||||||
			noteChannelId: In(channels.map(x => x.followeeId)),
 | 
								noteChannelId: In(channels.map(x => x.followeeId)),
 | 
				
			||||||
		}) : null;
 | 
							}) : null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return unread != null;
 | 
							return unread != null;
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public async getHasUnreadNotification(userId: User['id']): Promise<boolean> {
 | 
						async getHasUnreadNotification(userId: User['id']): Promise<boolean> {
 | 
				
			||||||
		const mute = await Mutings.find({
 | 
							const mute = await Mutings.findBy({
 | 
				
			||||||
			muterId: userId,
 | 
								muterId: userId,
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
		const mutedUserIds = mute.map(m => m.muteeId);
 | 
							const mutedUserIds = mute.map(m => m.muteeId);
 | 
				
			||||||
| 
						 | 
					@ -160,17 +183,17 @@ export class UserRepository extends Repository<User> {
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return count > 0;
 | 
							return count > 0;
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public async getHasPendingReceivedFollowRequest(userId: User['id']): Promise<boolean> {
 | 
						async getHasPendingReceivedFollowRequest(userId: User['id']): Promise<boolean> {
 | 
				
			||||||
		const count = await FollowRequests.count({
 | 
							const count = await FollowRequests.countBy({
 | 
				
			||||||
			followeeId: userId,
 | 
								followeeId: userId,
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return count > 0;
 | 
							return count > 0;
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public getOnlineStatus(user: User): 'unknown' | 'online' | 'active' | 'offline' {
 | 
						getOnlineStatus(user: User): 'unknown' | 'online' | 'active' | 'offline' {
 | 
				
			||||||
		if (user.hideOnlineStatus) return 'unknown';
 | 
							if (user.hideOnlineStatus) return 'unknown';
 | 
				
			||||||
		if (user.lastActiveDate == null) return 'unknown';
 | 
							if (user.lastActiveDate == null) return 'unknown';
 | 
				
			||||||
		const elapsed = Date.now() - user.lastActiveDate.getTime();
 | 
							const elapsed = Date.now() - user.lastActiveDate.getTime();
 | 
				
			||||||
| 
						 | 
					@ -179,22 +202,22 @@ export class UserRepository extends Repository<User> {
 | 
				
			||||||
			elapsed < USER_ACTIVE_THRESHOLD ? 'active' :
 | 
								elapsed < USER_ACTIVE_THRESHOLD ? 'active' :
 | 
				
			||||||
			'offline'
 | 
								'offline'
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public getAvatarUrl(user: User): string {
 | 
						getAvatarUrl(user: User): string {
 | 
				
			||||||
		// TODO: avatarIdがあるがavatarがない(JOINされてない)場合のハンドリング
 | 
							// TODO: avatarIdがあるがavatarがない(JOINされてない)場合のハンドリング
 | 
				
			||||||
		if (user.avatar) {
 | 
							if (user.avatar) {
 | 
				
			||||||
			return DriveFiles.getPublicUrl(user.avatar, true) || this.getIdenticonUrl(user.id);
 | 
								return DriveFiles.getPublicUrl(user.avatar, true) || this.getIdenticonUrl(user.id);
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			return this.getIdenticonUrl(user.id);
 | 
								return this.getIdenticonUrl(user.id);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public getIdenticonUrl(userId: User['id']): string {
 | 
						getIdenticonUrl(userId: User['id']): string {
 | 
				
			||||||
		return `${config.url}/identicon/${userId}`;
 | 
							return `${config.url}/identicon/${userId}`;
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public async pack<ExpectsMe extends boolean | null = null, D extends boolean = false>(
 | 
						async pack<ExpectsMe extends boolean | null = null, D extends boolean = false>(
 | 
				
			||||||
		src: User['id'] | User,
 | 
							src: User['id'] | User,
 | 
				
			||||||
		me?: { id: User['id'] } | null | undefined,
 | 
							me?: { id: User['id'] } | null | undefined,
 | 
				
			||||||
		options?: {
 | 
							options?: {
 | 
				
			||||||
| 
						 | 
					@ -211,11 +234,15 @@ export class UserRepository extends Repository<User> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (typeof src === 'object') {
 | 
							if (typeof src === 'object') {
 | 
				
			||||||
			user = src;
 | 
								user = src;
 | 
				
			||||||
			if (src.avatar === undefined && src.avatarId) src.avatar = await DriveFiles.findOne(src.avatarId) ?? null;
 | 
								if (src.avatar === undefined && src.avatarId) src.avatar = await DriveFiles.findOneBy({ id: src.avatarId }) ?? null;
 | 
				
			||||||
			if (src.banner === undefined && src.bannerId) src.banner = await DriveFiles.findOne(src.bannerId) ?? null;
 | 
								if (src.banner === undefined && src.bannerId) src.banner = await DriveFiles.findOneBy({ id: src.bannerId }) ?? null;
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			user = await this.findOneOrFail(src, {
 | 
								user = await this.findOneOrFail({
 | 
				
			||||||
				relations: ['avatar', 'banner'],
 | 
									where: { id: src },
 | 
				
			||||||
 | 
									relations: {
 | 
				
			||||||
 | 
										avatar: true,
 | 
				
			||||||
 | 
										banner: true,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -228,7 +255,7 @@ export class UserRepository extends Repository<User> {
 | 
				
			||||||
			.innerJoinAndSelect('pin.note', 'note')
 | 
								.innerJoinAndSelect('pin.note', 'note')
 | 
				
			||||||
			.orderBy('pin.id', 'DESC')
 | 
								.orderBy('pin.id', 'DESC')
 | 
				
			||||||
			.getMany() : [];
 | 
								.getMany() : [];
 | 
				
			||||||
		const profile = opts.detail ? await UserProfiles.findOneOrFail(user.id) : null;
 | 
							const profile = opts.detail ? await UserProfiles.findOneByOrFail({ userId: user.id }) : null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const followingCount = profile == null ? null :
 | 
							const followingCount = profile == null ? null :
 | 
				
			||||||
			(profile.ffVisibility === 'public') || isMe ? user.followingCount :
 | 
								(profile.ffVisibility === 'public') || isMe ? user.followingCount :
 | 
				
			||||||
| 
						 | 
					@ -254,8 +281,10 @@ export class UserRepository extends Repository<User> {
 | 
				
			||||||
			isModerator: user.isModerator || falsy,
 | 
								isModerator: user.isModerator || falsy,
 | 
				
			||||||
			isBot: user.isBot || falsy,
 | 
								isBot: user.isBot || falsy,
 | 
				
			||||||
			isCat: user.isCat || falsy,
 | 
								isCat: user.isCat || falsy,
 | 
				
			||||||
			showTimelineReplies: user.showTimelineReplies || falsy,
 | 
								instance: user.host ? userInstanceCache.fetch(user.host,
 | 
				
			||||||
			instance: user.host ? Instances.findOne({ host: user.host }).then(instance => instance ? {
 | 
									() => Instances.findOneBy({ host: user.host! }),
 | 
				
			||||||
 | 
									v => v != null
 | 
				
			||||||
 | 
								).then(instance => instance ? {
 | 
				
			||||||
				name: instance.name,
 | 
									name: instance.name,
 | 
				
			||||||
				softwareName: instance.softwareName,
 | 
									softwareName: instance.softwareName,
 | 
				
			||||||
				softwareVersion: instance.softwareVersion,
 | 
									softwareVersion: instance.softwareVersion,
 | 
				
			||||||
| 
						 | 
					@ -297,7 +326,7 @@ export class UserRepository extends Repository<User> {
 | 
				
			||||||
				twoFactorEnabled: profile!.twoFactorEnabled,
 | 
									twoFactorEnabled: profile!.twoFactorEnabled,
 | 
				
			||||||
				usePasswordLessLogin: profile!.usePasswordLessLogin,
 | 
									usePasswordLessLogin: profile!.usePasswordLessLogin,
 | 
				
			||||||
				securityKeys: profile!.twoFactorEnabled
 | 
									securityKeys: profile!.twoFactorEnabled
 | 
				
			||||||
					? UserSecurityKeys.count({
 | 
										? UserSecurityKeys.countBy({
 | 
				
			||||||
						userId: user.id,
 | 
											userId: user.id,
 | 
				
			||||||
					}).then(result => result >= 1)
 | 
										}).then(result => result >= 1)
 | 
				
			||||||
					: false,
 | 
										: false,
 | 
				
			||||||
| 
						 | 
					@ -334,6 +363,7 @@ export class UserRepository extends Repository<User> {
 | 
				
			||||||
				mutedInstances: profile!.mutedInstances,
 | 
									mutedInstances: profile!.mutedInstances,
 | 
				
			||||||
				mutingNotificationTypes: profile!.mutingNotificationTypes,
 | 
									mutingNotificationTypes: profile!.mutingNotificationTypes,
 | 
				
			||||||
				emailNotificationTypes: profile!.emailNotificationTypes,
 | 
									emailNotificationTypes: profile!.emailNotificationTypes,
 | 
				
			||||||
 | 
									showTimelineReplies: user.showTimelineReplies || falsy,
 | 
				
			||||||
			} : {}),
 | 
								} : {}),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			...(opts.includeSecrets ? {
 | 
								...(opts.includeSecrets ? {
 | 
				
			||||||
| 
						 | 
					@ -344,7 +374,11 @@ export class UserRepository extends Repository<User> {
 | 
				
			||||||
						where: {
 | 
											where: {
 | 
				
			||||||
							userId: user.id,
 | 
												userId: user.id,
 | 
				
			||||||
						},
 | 
											},
 | 
				
			||||||
						select: ['id', 'name', 'lastUsed'],
 | 
											select: {
 | 
				
			||||||
 | 
												id: true,
 | 
				
			||||||
 | 
												name: true,
 | 
				
			||||||
 | 
												lastUsed: true,
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
					})
 | 
										})
 | 
				
			||||||
					: [],
 | 
										: [],
 | 
				
			||||||
			} : {}),
 | 
								} : {}),
 | 
				
			||||||
| 
						 | 
					@ -361,9 +395,9 @@ export class UserRepository extends Repository<User> {
 | 
				
			||||||
		} as Promiseable<Packed<'User'>> as Promiseable<IsMeAndIsUserDetailed<ExpectsMe, D>>;
 | 
							} as Promiseable<Packed<'User'>> as Promiseable<IsMeAndIsUserDetailed<ExpectsMe, D>>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return await awaitAll(packed);
 | 
							return await awaitAll(packed);
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public packMany<D extends boolean = false>(
 | 
						packMany<D extends boolean = false>(
 | 
				
			||||||
		users: (User['id'] | User)[],
 | 
							users: (User['id'] | User)[],
 | 
				
			||||||
		me?: { id: User['id'] } | null | undefined,
 | 
							me?: { id: User['id'] } | null | undefined,
 | 
				
			||||||
		options?: {
 | 
							options?: {
 | 
				
			||||||
| 
						 | 
					@ -372,17 +406,8 @@ export class UserRepository extends Repository<User> {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	): Promise<IsUserDetailed<D>[]> {
 | 
						): Promise<IsUserDetailed<D>[]> {
 | 
				
			||||||
		return Promise.all(users.map(u => this.pack(u, me, options)));
 | 
							return Promise.all(users.map(u => this.pack(u, me, options)));
 | 
				
			||||||
	}
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public isLocalUser(user: User): user is ILocalUser;
 | 
						isLocalUser,
 | 
				
			||||||
	public isLocalUser<T extends { host: User['host'] }>(user: T): user is T & { host: null; };
 | 
						isRemoteUser,
 | 
				
			||||||
	public isLocalUser(user: User | { host: User['host'] }): boolean {
 | 
					});
 | 
				
			||||||
		return user.host == null;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	public isRemoteUser(user: User): user is IRemoteUser;
 | 
					 | 
				
			||||||
	public isRemoteUser<T extends { host: User['host'] }>(user: T): user is T & { host: string; };
 | 
					 | 
				
			||||||
	public isRemoteUser(user: User | { host: User['host'] }): boolean {
 | 
					 | 
				
			||||||
		return !this.isLocalUser(user);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -263,6 +263,7 @@ export default function() {
 | 
				
			||||||
	systemQueue.add('tickCharts', {
 | 
						systemQueue.add('tickCharts', {
 | 
				
			||||||
	}, {
 | 
						}, {
 | 
				
			||||||
		repeat: { cron: '55 * * * *' },
 | 
							repeat: { cron: '55 * * * *' },
 | 
				
			||||||
 | 
							removeOnComplete: true,
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	systemQueue.add('resyncCharts', {
 | 
						systemQueue.add('resyncCharts', {
 | 
				
			||||||
| 
						 | 
					@ -278,6 +279,7 @@ export default function() {
 | 
				
			||||||
	systemQueue.add('checkExpiredMutings', {
 | 
						systemQueue.add('checkExpiredMutings', {
 | 
				
			||||||
	}, {
 | 
						}, {
 | 
				
			||||||
		repeat: { cron: '*/5 * * * *' },
 | 
							repeat: { cron: '*/5 * * * *' },
 | 
				
			||||||
 | 
							removeOnComplete: true,
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	processSystemQueue(systemQueue);
 | 
						processSystemQueue(systemQueue);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,7 +13,7 @@ const logger = queueLogger.createSubLogger('delete-account');
 | 
				
			||||||
export async function deleteAccount(job: Bull.Job<DbUserDeleteJobData>): Promise<string | void> {
 | 
					export async function deleteAccount(job: Bull.Job<DbUserDeleteJobData>): Promise<string | void> {
 | 
				
			||||||
	logger.info(`Deleting account of ${job.data.user.id} ...`);
 | 
						logger.info(`Deleting account of ${job.data.user.id} ...`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const user = await Users.findOne(job.data.user.id);
 | 
						const user = await Users.findOneBy({ id: job.data.user.id });
 | 
				
			||||||
	if (user == null) {
 | 
						if (user == null) {
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -75,7 +75,7 @@ export async function deleteAccount(job: Bull.Job<DbUserDeleteJobData>): Promise
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	{ // Send email notification
 | 
						{ // Send email notification
 | 
				
			||||||
		const profile = await UserProfiles.findOneOrFail(user.id);
 | 
							const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
 | 
				
			||||||
		if (profile.email && profile.emailVerified) {
 | 
							if (profile.email && profile.emailVerified) {
 | 
				
			||||||
			sendEmail(profile.email, 'Account deleted',
 | 
								sendEmail(profile.email, 'Account deleted',
 | 
				
			||||||
				`Your account has been deleted.`,
 | 
									`Your account has been deleted.`,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,7 +11,7 @@ const logger = queueLogger.createSubLogger('delete-drive-files');
 | 
				
			||||||
export async function deleteDriveFiles(job: Bull.Job<DbUserJobData>, done: any): Promise<void> {
 | 
					export async function deleteDriveFiles(job: Bull.Job<DbUserJobData>, done: any): Promise<void> {
 | 
				
			||||||
	logger.info(`Deleting drive files of ${job.data.user.id} ...`);
 | 
						logger.info(`Deleting drive files of ${job.data.user.id} ...`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const user = await Users.findOne(job.data.user.id);
 | 
						const user = await Users.findOneBy({ id: job.data.user.id });
 | 
				
			||||||
	if (user == null) {
 | 
						if (user == null) {
 | 
				
			||||||
		done();
 | 
							done();
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
| 
						 | 
					@ -44,7 +44,7 @@ export async function deleteDriveFiles(job: Bull.Job<DbUserJobData>, done: any):
 | 
				
			||||||
			deletedCount++;
 | 
								deletedCount++;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const total = await DriveFiles.count({
 | 
							const total = await DriveFiles.countBy({
 | 
				
			||||||
			userId: user.id,
 | 
								userId: user.id,
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,7 +15,7 @@ const logger = queueLogger.createSubLogger('export-blocking');
 | 
				
			||||||
export async function exportBlocking(job: Bull.Job<DbUserJobData>, done: any): Promise<void> {
 | 
					export async function exportBlocking(job: Bull.Job<DbUserJobData>, done: any): Promise<void> {
 | 
				
			||||||
	logger.info(`Exporting blocking of ${job.data.user.id} ...`);
 | 
						logger.info(`Exporting blocking of ${job.data.user.id} ...`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const user = await Users.findOne(job.data.user.id);
 | 
						const user = await Users.findOneBy({ id: job.data.user.id });
 | 
				
			||||||
	if (user == null) {
 | 
						if (user == null) {
 | 
				
			||||||
		done();
 | 
							done();
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
| 
						 | 
					@ -56,7 +56,7 @@ export async function exportBlocking(job: Bull.Job<DbUserJobData>, done: any): P
 | 
				
			||||||
		cursor = blockings[blockings.length - 1].id;
 | 
							cursor = blockings[blockings.length - 1].id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for (const block of blockings) {
 | 
							for (const block of blockings) {
 | 
				
			||||||
			const u = await Users.findOne({ id: block.blockeeId });
 | 
								const u = await Users.findOneBy({ id: block.blockeeId });
 | 
				
			||||||
			if (u == null) {
 | 
								if (u == null) {
 | 
				
			||||||
				exportedCount++; continue;
 | 
									exportedCount++; continue;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					@ -75,7 +75,7 @@ export async function exportBlocking(job: Bull.Job<DbUserJobData>, done: any): P
 | 
				
			||||||
			exportedCount++;
 | 
								exportedCount++;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const total = await Blockings.count({
 | 
							const total = await Blockings.countBy({
 | 
				
			||||||
			blockerId: user.id,
 | 
								blockerId: user.id,
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,13 +12,14 @@ import { Users, Emojis } from '@/models/index.js';
 | 
				
			||||||
import {  } from '@/queue/types.js';
 | 
					import {  } from '@/queue/types.js';
 | 
				
			||||||
import { downloadUrl } from '@/misc/download-url.js';
 | 
					import { downloadUrl } from '@/misc/download-url.js';
 | 
				
			||||||
import config from '@/config/index.js';
 | 
					import config from '@/config/index.js';
 | 
				
			||||||
 | 
					import { IsNull } from 'typeorm';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const logger = queueLogger.createSubLogger('export-custom-emojis');
 | 
					const logger = queueLogger.createSubLogger('export-custom-emojis');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function exportCustomEmojis(job: Bull.Job, done: () => void): Promise<void> {
 | 
					export async function exportCustomEmojis(job: Bull.Job, done: () => void): Promise<void> {
 | 
				
			||||||
	logger.info(`Exporting custom emojis ...`);
 | 
						logger.info(`Exporting custom emojis ...`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const user = await Users.findOne(job.data.user.id);
 | 
						const user = await Users.findOneBy({ id: job.data.user.id });
 | 
				
			||||||
	if (user == null) {
 | 
						if (user == null) {
 | 
				
			||||||
		done();
 | 
							done();
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
| 
						 | 
					@ -57,7 +58,7 @@ export async function exportCustomEmojis(job: Bull.Job, done: () => void): Promi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const customEmojis = await Emojis.find({
 | 
						const customEmojis = await Emojis.find({
 | 
				
			||||||
		where: {
 | 
							where: {
 | 
				
			||||||
			host: null,
 | 
								host: IsNull(),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		order: {
 | 
							order: {
 | 
				
			||||||
			id: 'ASC',
 | 
								id: 'ASC',
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,7 +16,7 @@ const logger = queueLogger.createSubLogger('export-following');
 | 
				
			||||||
export async function exportFollowing(job: Bull.Job<DbUserJobData>, done: () => void): Promise<void> {
 | 
					export async function exportFollowing(job: Bull.Job<DbUserJobData>, done: () => void): Promise<void> {
 | 
				
			||||||
	logger.info(`Exporting following of ${job.data.user.id} ...`);
 | 
						logger.info(`Exporting following of ${job.data.user.id} ...`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const user = await Users.findOne(job.data.user.id);
 | 
						const user = await Users.findOneBy({ id: job.data.user.id });
 | 
				
			||||||
	if (user == null) {
 | 
						if (user == null) {
 | 
				
			||||||
		done();
 | 
							done();
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
| 
						 | 
					@ -36,7 +36,7 @@ export async function exportFollowing(job: Bull.Job<DbUserJobData>, done: () =>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let cursor: Following['id'] | null = null;
 | 
						let cursor: Following['id'] | null = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const mutings = job.data.excludeMuting ? await Mutings.find({
 | 
						const mutings = job.data.excludeMuting ? await Mutings.findBy({
 | 
				
			||||||
		muterId: user.id,
 | 
							muterId: user.id,
 | 
				
			||||||
	}) : [];
 | 
						}) : [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -60,7 +60,7 @@ export async function exportFollowing(job: Bull.Job<DbUserJobData>, done: () =>
 | 
				
			||||||
		cursor = followings[followings.length - 1].id;
 | 
							cursor = followings[followings.length - 1].id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for (const following of followings) {
 | 
							for (const following of followings) {
 | 
				
			||||||
			const u = await Users.findOne({ id: following.followeeId });
 | 
								const u = await Users.findOneBy({ id: following.followeeId });
 | 
				
			||||||
			if (u == null) {
 | 
								if (u == null) {
 | 
				
			||||||
				continue;
 | 
									continue;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,7 +15,7 @@ const logger = queueLogger.createSubLogger('export-mute');
 | 
				
			||||||
export async function exportMute(job: Bull.Job<DbUserJobData>, done: any): Promise<void> {
 | 
					export async function exportMute(job: Bull.Job<DbUserJobData>, done: any): Promise<void> {
 | 
				
			||||||
	logger.info(`Exporting mute of ${job.data.user.id} ...`);
 | 
						logger.info(`Exporting mute of ${job.data.user.id} ...`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const user = await Users.findOne(job.data.user.id);
 | 
						const user = await Users.findOneBy({ id: job.data.user.id });
 | 
				
			||||||
	if (user == null) {
 | 
						if (user == null) {
 | 
				
			||||||
		done();
 | 
							done();
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
| 
						 | 
					@ -57,7 +57,7 @@ export async function exportMute(job: Bull.Job<DbUserJobData>, done: any): Promi
 | 
				
			||||||
		cursor = mutes[mutes.length - 1].id;
 | 
							cursor = mutes[mutes.length - 1].id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for (const mute of mutes) {
 | 
							for (const mute of mutes) {
 | 
				
			||||||
			const u = await Users.findOne({ id: mute.muteeId });
 | 
								const u = await Users.findOneBy({ id: mute.muteeId });
 | 
				
			||||||
			if (u == null) {
 | 
								if (u == null) {
 | 
				
			||||||
				exportedCount++; continue;
 | 
									exportedCount++; continue;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					@ -76,7 +76,7 @@ export async function exportMute(job: Bull.Job<DbUserJobData>, done: any): Promi
 | 
				
			||||||
			exportedCount++;
 | 
								exportedCount++;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const total = await Mutings.count({
 | 
							const total = await Mutings.countBy({
 | 
				
			||||||
			muterId: user.id,
 | 
								muterId: user.id,
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,7 +16,7 @@ const logger = queueLogger.createSubLogger('export-notes');
 | 
				
			||||||
export async function exportNotes(job: Bull.Job<DbUserJobData>, done: any): Promise<void> {
 | 
					export async function exportNotes(job: Bull.Job<DbUserJobData>, done: any): Promise<void> {
 | 
				
			||||||
	logger.info(`Exporting notes of ${job.data.user.id} ...`);
 | 
						logger.info(`Exporting notes of ${job.data.user.id} ...`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const user = await Users.findOne(job.data.user.id);
 | 
						const user = await Users.findOneBy({ id: job.data.user.id });
 | 
				
			||||||
	if (user == null) {
 | 
						if (user == null) {
 | 
				
			||||||
		done();
 | 
							done();
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
| 
						 | 
					@ -74,7 +74,7 @@ export async function exportNotes(job: Bull.Job<DbUserJobData>, done: any): Prom
 | 
				
			||||||
		for (const note of notes) {
 | 
							for (const note of notes) {
 | 
				
			||||||
			let poll: Poll | undefined;
 | 
								let poll: Poll | undefined;
 | 
				
			||||||
			if (note.hasPoll) {
 | 
								if (note.hasPoll) {
 | 
				
			||||||
				poll = await Polls.findOneOrFail({ noteId: note.id });
 | 
									poll = await Polls.findOneByOrFail({ noteId: note.id });
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			const content = JSON.stringify(serialize(note, poll));
 | 
								const content = JSON.stringify(serialize(note, poll));
 | 
				
			||||||
			const isFirst = exportedNotesCount === 0;
 | 
								const isFirst = exportedNotesCount === 0;
 | 
				
			||||||
| 
						 | 
					@ -82,7 +82,7 @@ export async function exportNotes(job: Bull.Job<DbUserJobData>, done: any): Prom
 | 
				
			||||||
			exportedNotesCount++;
 | 
								exportedNotesCount++;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const total = await Notes.count({
 | 
							const total = await Notes.countBy({
 | 
				
			||||||
			userId: user.id,
 | 
								userId: user.id,
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,13 +15,13 @@ const logger = queueLogger.createSubLogger('export-user-lists');
 | 
				
			||||||
export async function exportUserLists(job: Bull.Job<DbUserJobData>, done: any): Promise<void> {
 | 
					export async function exportUserLists(job: Bull.Job<DbUserJobData>, done: any): Promise<void> {
 | 
				
			||||||
	logger.info(`Exporting user lists of ${job.data.user.id} ...`);
 | 
						logger.info(`Exporting user lists of ${job.data.user.id} ...`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const user = await Users.findOne(job.data.user.id);
 | 
						const user = await Users.findOneBy({ id: job.data.user.id });
 | 
				
			||||||
	if (user == null) {
 | 
						if (user == null) {
 | 
				
			||||||
		done();
 | 
							done();
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const lists = await UserLists.find({
 | 
						const lists = await UserLists.findBy({
 | 
				
			||||||
		userId: user.id,
 | 
							userId: user.id,
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -38,8 +38,8 @@ export async function exportUserLists(job: Bull.Job<DbUserJobData>, done: any):
 | 
				
			||||||
	const stream = fs.createWriteStream(path, { flags: 'a' });
 | 
						const stream = fs.createWriteStream(path, { flags: 'a' });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (const list of lists) {
 | 
						for (const list of lists) {
 | 
				
			||||||
		const joinings = await UserListJoinings.find({ userListId: list.id });
 | 
							const joinings = await UserListJoinings.findBy({ userListId: list.id });
 | 
				
			||||||
		const users = await Users.find({
 | 
							const users = await Users.findBy({
 | 
				
			||||||
			id: In(joinings.map(j => j.userId)),
 | 
								id: In(joinings.map(j => j.userId)),
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,19 +8,20 @@ import { isSelfHost, toPuny } from '@/misc/convert-host.js';
 | 
				
			||||||
import { Users, DriveFiles, Blockings } from '@/models/index.js';
 | 
					import { Users, DriveFiles, Blockings } from '@/models/index.js';
 | 
				
			||||||
import { DbUserImportJobData } from '@/queue/types.js';
 | 
					import { DbUserImportJobData } from '@/queue/types.js';
 | 
				
			||||||
import block from '@/services/blocking/create.js';
 | 
					import block from '@/services/blocking/create.js';
 | 
				
			||||||
 | 
					import { IsNull } from 'typeorm';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const logger = queueLogger.createSubLogger('import-blocking');
 | 
					const logger = queueLogger.createSubLogger('import-blocking');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function importBlocking(job: Bull.Job<DbUserImportJobData>, done: any): Promise<void> {
 | 
					export async function importBlocking(job: Bull.Job<DbUserImportJobData>, done: any): Promise<void> {
 | 
				
			||||||
	logger.info(`Importing blocking of ${job.data.user.id} ...`);
 | 
						logger.info(`Importing blocking of ${job.data.user.id} ...`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const user = await Users.findOne(job.data.user.id);
 | 
						const user = await Users.findOneBy({ id: job.data.user.id });
 | 
				
			||||||
	if (user == null) {
 | 
						if (user == null) {
 | 
				
			||||||
		done();
 | 
							done();
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const file = await DriveFiles.findOne({
 | 
						const file = await DriveFiles.findOneBy({
 | 
				
			||||||
		id: job.data.fileId,
 | 
							id: job.data.fileId,
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
	if (file == null) {
 | 
						if (file == null) {
 | 
				
			||||||
| 
						 | 
					@ -39,10 +40,10 @@ export async function importBlocking(job: Bull.Job<DbUserImportJobData>, done: a
 | 
				
			||||||
			const acct = line.split(',')[0].trim();
 | 
								const acct = line.split(',')[0].trim();
 | 
				
			||||||
			const { username, host } = Acct.parse(acct);
 | 
								const { username, host } = Acct.parse(acct);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			let target = isSelfHost(host!) ? await Users.findOne({
 | 
								let target = isSelfHost(host!) ? await Users.findOneBy({
 | 
				
			||||||
				host: null,
 | 
									host: IsNull(),
 | 
				
			||||||
				usernameLower: username.toLowerCase(),
 | 
									usernameLower: username.toLowerCase(),
 | 
				
			||||||
			}) : await Users.findOne({
 | 
								}) : await Users.findOneBy({
 | 
				
			||||||
				host: toPuny(host!),
 | 
									host: toPuny(host!),
 | 
				
			||||||
				usernameLower: username.toLowerCase(),
 | 
									usernameLower: username.toLowerCase(),
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,6 @@ import Bull from 'bull';
 | 
				
			||||||
import * as tmp from 'tmp';
 | 
					import * as tmp from 'tmp';
 | 
				
			||||||
import * as fs from 'node:fs';
 | 
					import * as fs from 'node:fs';
 | 
				
			||||||
import unzipper from 'unzipper';
 | 
					import unzipper from 'unzipper';
 | 
				
			||||||
import { getConnection } from 'typeorm';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { queueLogger } from '../../logger.js';
 | 
					import { queueLogger } from '../../logger.js';
 | 
				
			||||||
import { downloadUrl } from '@/misc/download-url.js';
 | 
					import { downloadUrl } from '@/misc/download-url.js';
 | 
				
			||||||
| 
						 | 
					@ -10,6 +9,7 @@ import { DriveFiles, Emojis } from '@/models/index.js';
 | 
				
			||||||
import { DbUserImportJobData } from '@/queue/types.js';
 | 
					import { DbUserImportJobData } from '@/queue/types.js';
 | 
				
			||||||
import { addFile } from '@/services/drive/add-file.js';
 | 
					import { addFile } from '@/services/drive/add-file.js';
 | 
				
			||||||
import { genId } from '@/misc/gen-id.js';
 | 
					import { genId } from '@/misc/gen-id.js';
 | 
				
			||||||
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const logger = queueLogger.createSubLogger('import-custom-emojis');
 | 
					const logger = queueLogger.createSubLogger('import-custom-emojis');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,7 +17,7 @@ const logger = queueLogger.createSubLogger('import-custom-emojis');
 | 
				
			||||||
export async function importCustomEmojis(job: Bull.Job<DbUserImportJobData>, done: any): Promise<void> {
 | 
					export async function importCustomEmojis(job: Bull.Job<DbUserImportJobData>, done: any): Promise<void> {
 | 
				
			||||||
	logger.info(`Importing custom emojis ...`);
 | 
						logger.info(`Importing custom emojis ...`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const file = await DriveFiles.findOne({
 | 
						const file = await DriveFiles.findOneBy({
 | 
				
			||||||
		id: job.data.fileId,
 | 
							id: job.data.fileId,
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
	if (file == null) {
 | 
						if (file == null) {
 | 
				
			||||||
| 
						 | 
					@ -72,10 +72,10 @@ export async function importCustomEmojis(job: Bull.Job<DbUserImportJobData>, don
 | 
				
			||||||
				originalUrl: driveFile.url,
 | 
									originalUrl: driveFile.url,
 | 
				
			||||||
				publicUrl: driveFile.webpublicUrl ?? driveFile.url,
 | 
									publicUrl: driveFile.webpublicUrl ?? driveFile.url,
 | 
				
			||||||
				type: driveFile.webpublicType ?? driveFile.type,
 | 
									type: driveFile.webpublicType ?? driveFile.type,
 | 
				
			||||||
			}).then(x => Emojis.findOneOrFail(x.identifiers[0]));
 | 
								}).then(x => Emojis.findOneByOrFail(x.identifiers[0]));
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		await getConnection().queryResultCache!.remove(['meta_emojis']);
 | 
							await db.queryResultCache!.remove(['meta_emojis']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		cleanup();
 | 
							cleanup();
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,19 +8,20 @@ import { downloadTextFile } from '@/misc/download-text-file.js';
 | 
				
			||||||
import { isSelfHost, toPuny } from '@/misc/convert-host.js';
 | 
					import { isSelfHost, toPuny } from '@/misc/convert-host.js';
 | 
				
			||||||
import { Users, DriveFiles } from '@/models/index.js';
 | 
					import { Users, DriveFiles } from '@/models/index.js';
 | 
				
			||||||
import { DbUserImportJobData } from '@/queue/types.js';
 | 
					import { DbUserImportJobData } from '@/queue/types.js';
 | 
				
			||||||
 | 
					import { IsNull } from 'typeorm';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const logger = queueLogger.createSubLogger('import-following');
 | 
					const logger = queueLogger.createSubLogger('import-following');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function importFollowing(job: Bull.Job<DbUserImportJobData>, done: any): Promise<void> {
 | 
					export async function importFollowing(job: Bull.Job<DbUserImportJobData>, done: any): Promise<void> {
 | 
				
			||||||
	logger.info(`Importing following of ${job.data.user.id} ...`);
 | 
						logger.info(`Importing following of ${job.data.user.id} ...`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const user = await Users.findOne(job.data.user.id);
 | 
						const user = await Users.findOneBy({ id: job.data.user.id });
 | 
				
			||||||
	if (user == null) {
 | 
						if (user == null) {
 | 
				
			||||||
		done();
 | 
							done();
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const file = await DriveFiles.findOne({
 | 
						const file = await DriveFiles.findOneBy({
 | 
				
			||||||
		id: job.data.fileId,
 | 
							id: job.data.fileId,
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
	if (file == null) {
 | 
						if (file == null) {
 | 
				
			||||||
| 
						 | 
					@ -39,10 +40,10 @@ export async function importFollowing(job: Bull.Job<DbUserImportJobData>, done:
 | 
				
			||||||
			const acct = line.split(',')[0].trim();
 | 
								const acct = line.split(',')[0].trim();
 | 
				
			||||||
			const { username, host } = Acct.parse(acct);
 | 
								const { username, host } = Acct.parse(acct);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			let target = isSelfHost(host!) ? await Users.findOne({
 | 
								let target = isSelfHost(host!) ? await Users.findOneBy({
 | 
				
			||||||
				host: null,
 | 
									host: IsNull(),
 | 
				
			||||||
				usernameLower: username.toLowerCase(),
 | 
									usernameLower: username.toLowerCase(),
 | 
				
			||||||
			}) : await Users.findOne({
 | 
								}) : await Users.findOneBy({
 | 
				
			||||||
				host: toPuny(host!),
 | 
									host: toPuny(host!),
 | 
				
			||||||
				usernameLower: username.toLowerCase(),
 | 
									usernameLower: username.toLowerCase(),
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,19 +9,20 @@ import { Users, DriveFiles, Mutings } from '@/models/index.js';
 | 
				
			||||||
import { DbUserImportJobData } from '@/queue/types.js';
 | 
					import { DbUserImportJobData } from '@/queue/types.js';
 | 
				
			||||||
import { User } from '@/models/entities/user.js';
 | 
					import { User } from '@/models/entities/user.js';
 | 
				
			||||||
import { genId } from '@/misc/gen-id.js';
 | 
					import { genId } from '@/misc/gen-id.js';
 | 
				
			||||||
 | 
					import { IsNull } from 'typeorm';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const logger = queueLogger.createSubLogger('import-muting');
 | 
					const logger = queueLogger.createSubLogger('import-muting');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function importMuting(job: Bull.Job<DbUserImportJobData>, done: any): Promise<void> {
 | 
					export async function importMuting(job: Bull.Job<DbUserImportJobData>, done: any): Promise<void> {
 | 
				
			||||||
	logger.info(`Importing muting of ${job.data.user.id} ...`);
 | 
						logger.info(`Importing muting of ${job.data.user.id} ...`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const user = await Users.findOne(job.data.user.id);
 | 
						const user = await Users.findOneBy({ id: job.data.user.id });
 | 
				
			||||||
	if (user == null) {
 | 
						if (user == null) {
 | 
				
			||||||
		done();
 | 
							done();
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const file = await DriveFiles.findOne({
 | 
						const file = await DriveFiles.findOneBy({
 | 
				
			||||||
		id: job.data.fileId,
 | 
							id: job.data.fileId,
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
	if (file == null) {
 | 
						if (file == null) {
 | 
				
			||||||
| 
						 | 
					@ -40,10 +41,10 @@ export async function importMuting(job: Bull.Job<DbUserImportJobData>, done: any
 | 
				
			||||||
			const acct = line.split(',')[0].trim();
 | 
								const acct = line.split(',')[0].trim();
 | 
				
			||||||
			const { username, host } = Acct.parse(acct);
 | 
								const { username, host } = Acct.parse(acct);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			let target = isSelfHost(host!) ? await Users.findOne({
 | 
								let target = isSelfHost(host!) ? await Users.findOneBy({
 | 
				
			||||||
				host: null,
 | 
									host: IsNull(),
 | 
				
			||||||
				usernameLower: username.toLowerCase(),
 | 
									usernameLower: username.toLowerCase(),
 | 
				
			||||||
			}) : await Users.findOne({
 | 
								}) : await Users.findOneBy({
 | 
				
			||||||
				host: toPuny(host!),
 | 
									host: toPuny(host!),
 | 
				
			||||||
				usernameLower: username.toLowerCase(),
 | 
									usernameLower: username.toLowerCase(),
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,19 +9,20 @@ import { isSelfHost, toPuny } from '@/misc/convert-host.js';
 | 
				
			||||||
import { DriveFiles, Users, UserLists, UserListJoinings } from '@/models/index.js';
 | 
					import { DriveFiles, Users, UserLists, UserListJoinings } from '@/models/index.js';
 | 
				
			||||||
import { genId } from '@/misc/gen-id.js';
 | 
					import { genId } from '@/misc/gen-id.js';
 | 
				
			||||||
import { DbUserImportJobData } from '@/queue/types.js';
 | 
					import { DbUserImportJobData } from '@/queue/types.js';
 | 
				
			||||||
 | 
					import { IsNull } from 'typeorm';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const logger = queueLogger.createSubLogger('import-user-lists');
 | 
					const logger = queueLogger.createSubLogger('import-user-lists');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function importUserLists(job: Bull.Job<DbUserImportJobData>, done: any): Promise<void> {
 | 
					export async function importUserLists(job: Bull.Job<DbUserImportJobData>, done: any): Promise<void> {
 | 
				
			||||||
	logger.info(`Importing user lists of ${job.data.user.id} ...`);
 | 
						logger.info(`Importing user lists of ${job.data.user.id} ...`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const user = await Users.findOne(job.data.user.id);
 | 
						const user = await Users.findOneBy({ id: job.data.user.id });
 | 
				
			||||||
	if (user == null) {
 | 
						if (user == null) {
 | 
				
			||||||
		done();
 | 
							done();
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const file = await DriveFiles.findOne({
 | 
						const file = await DriveFiles.findOneBy({
 | 
				
			||||||
		id: job.data.fileId,
 | 
							id: job.data.fileId,
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
	if (file == null) {
 | 
						if (file == null) {
 | 
				
			||||||
| 
						 | 
					@ -40,7 +41,7 @@ export async function importUserLists(job: Bull.Job<DbUserImportJobData>, done:
 | 
				
			||||||
			const listName = line.split(',')[0].trim();
 | 
								const listName = line.split(',')[0].trim();
 | 
				
			||||||
			const { username, host } = Acct.parse(line.split(',')[1].trim());
 | 
								const { username, host } = Acct.parse(line.split(',')[1].trim());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			let list = await UserLists.findOne({
 | 
								let list = await UserLists.findOneBy({
 | 
				
			||||||
				userId: user.id,
 | 
									userId: user.id,
 | 
				
			||||||
				name: listName,
 | 
									name: listName,
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
| 
						 | 
					@ -51,13 +52,13 @@ export async function importUserLists(job: Bull.Job<DbUserImportJobData>, done:
 | 
				
			||||||
					createdAt: new Date(),
 | 
										createdAt: new Date(),
 | 
				
			||||||
					userId: user.id,
 | 
										userId: user.id,
 | 
				
			||||||
					name: listName,
 | 
										name: listName,
 | 
				
			||||||
				}).then(x => UserLists.findOneOrFail(x.identifiers[0]));
 | 
									}).then(x => UserLists.findOneByOrFail(x.identifiers[0]));
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			let target = isSelfHost(host!) ? await Users.findOne({
 | 
								let target = isSelfHost(host!) ? await Users.findOneBy({
 | 
				
			||||||
				host: null,
 | 
									host: IsNull(),
 | 
				
			||||||
				usernameLower: username.toLowerCase(),
 | 
									usernameLower: username.toLowerCase(),
 | 
				
			||||||
			}) : await Users.findOne({
 | 
								}) : await Users.findOneBy({
 | 
				
			||||||
				host: toPuny(host!),
 | 
									host: toPuny(host!),
 | 
				
			||||||
				usernameLower: username.toLowerCase(),
 | 
									usernameLower: username.toLowerCase(),
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
| 
						 | 
					@ -66,7 +67,7 @@ export async function importUserLists(job: Bull.Job<DbUserImportJobData>, done:
 | 
				
			||||||
				target = await resolveUser(username, host);
 | 
									target = await resolveUser(username, host);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (await UserListJoinings.findOne({ userListId: list!.id, userId: target.id }) != null) continue;
 | 
								if (await UserListJoinings.findOneBy({ userListId: list!.id, userId: target.id }) != null) continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			pushUserToUserList(target, list!);
 | 
								pushUserToUserList(target, list!);
 | 
				
			||||||
		} catch (e) {
 | 
							} catch (e) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,7 @@ import { createNotification } from '@/services/create-notification.js';
 | 
				
			||||||
const logger = queueLogger.createSubLogger('ended-poll-notification');
 | 
					const logger = queueLogger.createSubLogger('ended-poll-notification');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function endedPollNotification(job: Bull.Job<EndedPollNotificationJobData>, done: any): Promise<void> {
 | 
					export async function endedPollNotification(job: Bull.Job<EndedPollNotificationJobData>, done: any): Promise<void> {
 | 
				
			||||||
	const note = await Notes.findOne(job.data.noteId);
 | 
						const note = await Notes.findOneBy({ id: job.data.noteId });
 | 
				
			||||||
	if (note == null || !note.hasPoll) {
 | 
						if (note == null || !note.hasPoll) {
 | 
				
			||||||
		done();
 | 
							done();
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,6 +15,8 @@ import DbResolver from '@/remote/activitypub/db-resolver.js';
 | 
				
			||||||
import { resolvePerson } from '@/remote/activitypub/models/person.js';
 | 
					import { resolvePerson } from '@/remote/activitypub/models/person.js';
 | 
				
			||||||
import { LdSignature } from '@/remote/activitypub/misc/ld-signature.js';
 | 
					import { LdSignature } from '@/remote/activitypub/misc/ld-signature.js';
 | 
				
			||||||
import { StatusError } from '@/misc/fetch.js';
 | 
					import { StatusError } from '@/misc/fetch.js';
 | 
				
			||||||
 | 
					import { CacheableRemoteUser } from '@/models/entities/user.js';
 | 
				
			||||||
 | 
					import { UserPublickey } from '@/models/entities/user-publickey.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const logger = new Logger('inbox');
 | 
					const logger = new Logger('inbox');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -42,11 +44,13 @@ export default async (job: Bull.Job<InboxJobData>): Promise<string> => {
 | 
				
			||||||
		return `Old keyId is no longer supported. ${keyIdLower}`;
 | 
							return `Old keyId is no longer supported. ${keyIdLower}`;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TDOO: キャッシュ
 | 
					 | 
				
			||||||
	const dbResolver = new DbResolver();
 | 
						const dbResolver = new DbResolver();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// HTTP-Signature keyIdを元にDBから取得
 | 
						// HTTP-Signature keyIdを元にDBから取得
 | 
				
			||||||
	let authUser = await dbResolver.getAuthUserFromKeyId(signature.keyId);
 | 
						let authUser: {
 | 
				
			||||||
 | 
							user: CacheableRemoteUser;
 | 
				
			||||||
 | 
							key: UserPublickey | null;
 | 
				
			||||||
 | 
						} | null = await dbResolver.getAuthUserFromKeyId(signature.keyId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// keyIdでわからなければ、activity.actorを元にDBから取得 || activity.actorを元にリモートから取得
 | 
						// keyIdでわからなければ、activity.actorを元にDBから取得 || activity.actorを元にリモートから取得
 | 
				
			||||||
	if (authUser == null) {
 | 
						if (authUser == null) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -37,7 +37,7 @@ export default async function cleanRemoteFiles(job: Bull.Job<Record<string, unkn
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		deletedCount += 8;
 | 
							deletedCount += 8;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const total = await DriveFiles.count({
 | 
							const total = await DriveFiles.countBy({
 | 
				
			||||||
			userHost: Not(IsNull()),
 | 
								userHost: Not(IsNull()),
 | 
				
			||||||
			isLink: false,
 | 
								isLink: false,
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,3 +8,12 @@ export const deliverQueue = initializeQueue<DeliverJobData>('deliver', config.de
 | 
				
			||||||
export const inboxQueue = initializeQueue<InboxJobData>('inbox', config.inboxJobPerSec || 16);
 | 
					export const inboxQueue = initializeQueue<InboxJobData>('inbox', config.inboxJobPerSec || 16);
 | 
				
			||||||
export const dbQueue = initializeQueue<DbJobData>('db');
 | 
					export const dbQueue = initializeQueue<DbJobData>('db');
 | 
				
			||||||
export const objectStorageQueue = initializeQueue<ObjectStorageJobData>('objectStorage');
 | 
					export const objectStorageQueue = initializeQueue<ObjectStorageJobData>('objectStorage');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const queues = [
 | 
				
			||||||
 | 
						systemQueue,
 | 
				
			||||||
 | 
						endedPollNotificationQueue,
 | 
				
			||||||
 | 
						deliverQueue,
 | 
				
			||||||
 | 
						inboxQueue,
 | 
				
			||||||
 | 
						dbQueue,
 | 
				
			||||||
 | 
						objectStorageQueue,
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,26 +3,26 @@ import Resolver from './resolver.js';
 | 
				
			||||||
import { resolvePerson } from './models/person.js';
 | 
					import { resolvePerson } from './models/person.js';
 | 
				
			||||||
import { unique, concat } from '@/prelude/array.js';
 | 
					import { unique, concat } from '@/prelude/array.js';
 | 
				
			||||||
import promiseLimit from 'promise-limit';
 | 
					import promiseLimit from 'promise-limit';
 | 
				
			||||||
import { User, IRemoteUser } from '@/models/entities/user.js';
 | 
					import { User, CacheableRemoteUser, CacheableUser } from '@/models/entities/user.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Visibility = 'public' | 'home' | 'followers' | 'specified';
 | 
					type Visibility = 'public' | 'home' | 'followers' | 'specified';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type AudienceInfo = {
 | 
					type AudienceInfo = {
 | 
				
			||||||
	visibility: Visibility,
 | 
						visibility: Visibility,
 | 
				
			||||||
	mentionedUsers: User[],
 | 
						mentionedUsers: CacheableUser[],
 | 
				
			||||||
	visibleUsers: User[],
 | 
						visibleUsers: CacheableUser[],
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function parseAudience(actor: IRemoteUser, to?: ApObject, cc?: ApObject, resolver?: Resolver): Promise<AudienceInfo> {
 | 
					export async function parseAudience(actor: CacheableRemoteUser, to?: ApObject, cc?: ApObject, resolver?: Resolver): Promise<AudienceInfo> {
 | 
				
			||||||
	const toGroups = groupingAudience(getApIds(to), actor);
 | 
						const toGroups = groupingAudience(getApIds(to), actor);
 | 
				
			||||||
	const ccGroups = groupingAudience(getApIds(cc), actor);
 | 
						const ccGroups = groupingAudience(getApIds(cc), actor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const others = unique(concat([toGroups.other, ccGroups.other]));
 | 
						const others = unique(concat([toGroups.other, ccGroups.other]));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const limit = promiseLimit<User | null>(2);
 | 
						const limit = promiseLimit<CacheableUser | null>(2);
 | 
				
			||||||
	const mentionedUsers = (await Promise.all(
 | 
						const mentionedUsers = (await Promise.all(
 | 
				
			||||||
		others.map(id => limit(() => resolvePerson(id, resolver).catch(() => null)))
 | 
							others.map(id => limit(() => resolvePerson(id, resolver).catch(() => null)))
 | 
				
			||||||
	)).filter((x): x is User => x != null);
 | 
						)).filter((x): x is CacheableUser => x != null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (toGroups.public.length > 0) {
 | 
						if (toGroups.public.length > 0) {
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
| 
						 | 
					@ -55,7 +55,7 @@ export async function parseAudience(actor: IRemoteUser, to?: ApObject, cc?: ApOb
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function groupingAudience(ids: string[], actor: IRemoteUser) {
 | 
					function groupingAudience(ids: string[], actor: CacheableRemoteUser) {
 | 
				
			||||||
	const groups = {
 | 
						const groups = {
 | 
				
			||||||
		public: [] as string[],
 | 
							public: [] as string[],
 | 
				
			||||||
		followers: [] as string[],
 | 
							followers: [] as string[],
 | 
				
			||||||
| 
						 | 
					@ -85,7 +85,7 @@ function isPublic(id: string) {
 | 
				
			||||||
	].includes(id);
 | 
						].includes(id);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function isFollowers(id: string, actor: IRemoteUser) {
 | 
					function isFollowers(id: string, actor: CacheableRemoteUser) {
 | 
				
			||||||
	return (
 | 
						return (
 | 
				
			||||||
		id === (actor.followersUri || `${actor.uri}/followers`)
 | 
							id === (actor.followersUri || `${actor.uri}/followers`)
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,12 +1,17 @@
 | 
				
			||||||
 | 
					import escapeRegexp from 'escape-regexp';
 | 
				
			||||||
import config from '@/config/index.js';
 | 
					import config from '@/config/index.js';
 | 
				
			||||||
import { Note } from '@/models/entities/note.js';
 | 
					import { Note } from '@/models/entities/note.js';
 | 
				
			||||||
import { User, IRemoteUser } from '@/models/entities/user.js';
 | 
					import { User, IRemoteUser, CacheableRemoteUser, CacheableUser } from '@/models/entities/user.js';
 | 
				
			||||||
import { UserPublickey } from '@/models/entities/user-publickey.js';
 | 
					import { UserPublickey } from '@/models/entities/user-publickey.js';
 | 
				
			||||||
import { MessagingMessage } from '@/models/entities/messaging-message.js';
 | 
					import { MessagingMessage } from '@/models/entities/messaging-message.js';
 | 
				
			||||||
import { Notes, Users, UserPublickeys, MessagingMessages } from '@/models/index.js';
 | 
					import { Notes, Users, UserPublickeys, MessagingMessages } from '@/models/index.js';
 | 
				
			||||||
import { IObject, getApId } from './type.js';
 | 
					import { IObject, getApId } from './type.js';
 | 
				
			||||||
import { resolvePerson } from './models/person.js';
 | 
					import { resolvePerson } from './models/person.js';
 | 
				
			||||||
import escapeRegexp from 'escape-regexp';
 | 
					import { Cache } from '@/misc/cache.js';
 | 
				
			||||||
 | 
					import { uriPersonCache, userByIdCache } from '@/services/user-cache.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const publicKeyCache = new Cache<UserPublickey | null>(Infinity);
 | 
				
			||||||
 | 
					const publicKeyByUserIdCache = new Cache<UserPublickey | null>(Infinity);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class DbResolver {
 | 
					export default class DbResolver {
 | 
				
			||||||
	constructor() {
 | 
						constructor() {
 | 
				
			||||||
| 
						 | 
					@ -19,15 +24,15 @@ export default class DbResolver {
 | 
				
			||||||
		const parsed = this.parseUri(value);
 | 
							const parsed = this.parseUri(value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (parsed.id) {
 | 
							if (parsed.id) {
 | 
				
			||||||
			return (await Notes.findOne({
 | 
								return await Notes.findOneBy({
 | 
				
			||||||
				id: parsed.id,
 | 
									id: parsed.id,
 | 
				
			||||||
			})) || null;
 | 
								});
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (parsed.uri) {
 | 
							if (parsed.uri) {
 | 
				
			||||||
			return (await Notes.findOne({
 | 
								return await Notes.findOneBy({
 | 
				
			||||||
				uri: parsed.uri,
 | 
									uri: parsed.uri,
 | 
				
			||||||
			})) || null;
 | 
								});
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return null;
 | 
							return null;
 | 
				
			||||||
| 
						 | 
					@ -37,15 +42,15 @@ export default class DbResolver {
 | 
				
			||||||
		const parsed = this.parseUri(value);
 | 
							const parsed = this.parseUri(value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (parsed.id) {
 | 
							if (parsed.id) {
 | 
				
			||||||
			return (await MessagingMessages.findOne({
 | 
								return await MessagingMessages.findOneBy({
 | 
				
			||||||
				id: parsed.id,
 | 
									id: parsed.id,
 | 
				
			||||||
			})) || null;
 | 
								});
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (parsed.uri) {
 | 
							if (parsed.uri) {
 | 
				
			||||||
			return (await MessagingMessages.findOne({
 | 
								return await MessagingMessages.findOneBy({
 | 
				
			||||||
				uri: parsed.uri,
 | 
									uri: parsed.uri,
 | 
				
			||||||
			})) || null;
 | 
								});
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return null;
 | 
							return null;
 | 
				
			||||||
| 
						 | 
					@ -54,19 +59,19 @@ export default class DbResolver {
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * AP Person => Misskey User in DB
 | 
						 * AP Person => Misskey User in DB
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public async getUserFromApId(value: string | IObject): Promise<User | null> {
 | 
						public async getUserFromApId(value: string | IObject): Promise<CacheableUser | null> {
 | 
				
			||||||
		const parsed = this.parseUri(value);
 | 
							const parsed = this.parseUri(value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (parsed.id) {
 | 
							if (parsed.id) {
 | 
				
			||||||
			return (await Users.findOne({
 | 
								return await userByIdCache.fetchMaybe(parsed.id, () => Users.findOneBy({
 | 
				
			||||||
				id: parsed.id,
 | 
									id: parsed.id,
 | 
				
			||||||
			})) || null;
 | 
								}).then(x => x ?? undefined)) ?? null;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (parsed.uri) {
 | 
							if (parsed.uri) {
 | 
				
			||||||
			return (await Users.findOne({
 | 
								return await uriPersonCache.fetch(parsed.uri, () => Users.findOneBy({
 | 
				
			||||||
				uri: parsed.uri,
 | 
									uri: parsed.uri,
 | 
				
			||||||
			})) || null;
 | 
								}));
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return null;
 | 
							return null;
 | 
				
			||||||
| 
						 | 
					@ -75,17 +80,24 @@ export default class DbResolver {
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * AP KeyId => Misskey User and Key
 | 
						 * AP KeyId => Misskey User and Key
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public async getAuthUserFromKeyId(keyId: string): Promise<AuthUser | null> {
 | 
						public async getAuthUserFromKeyId(keyId: string): Promise<{
 | 
				
			||||||
		const key = await UserPublickeys.findOne({
 | 
							user: CacheableRemoteUser;
 | 
				
			||||||
 | 
							key: UserPublickey;
 | 
				
			||||||
 | 
						} | null> {
 | 
				
			||||||
 | 
							const key = await publicKeyCache.fetch(keyId, async () => {
 | 
				
			||||||
 | 
								const key = await UserPublickeys.findOneBy({
 | 
				
			||||||
				keyId,
 | 
									keyId,
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
			if (key == null) return null;
 | 
								if (key == null) return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const user = await Users.findOne(key.userId) as IRemoteUser;
 | 
								return key;
 | 
				
			||||||
 | 
							}, key => key != null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (key == null) return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			user,
 | 
								user: await userByIdCache.fetch(key.userId, () => Users.findOneByOrFail({ id: key.userId })) as CacheableRemoteUser,
 | 
				
			||||||
			key,
 | 
								key,
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -93,12 +105,15 @@ export default class DbResolver {
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * AP Actor id => Misskey User and Key
 | 
						 * AP Actor id => Misskey User and Key
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public async getAuthUserFromApId(uri: string): Promise<AuthUser | null> {
 | 
						public async getAuthUserFromApId(uri: string): Promise<{
 | 
				
			||||||
		const user = await resolvePerson(uri) as IRemoteUser;
 | 
							user: CacheableRemoteUser;
 | 
				
			||||||
 | 
							key: UserPublickey | null;
 | 
				
			||||||
 | 
						} | null> {
 | 
				
			||||||
 | 
							const user = await resolvePerson(uri) as CacheableRemoteUser;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (user == null) return null;
 | 
							if (user == null) return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const key = await UserPublickeys.findOne(user.id);
 | 
							const key = await publicKeyByUserIdCache.fetch(user.id, () => UserPublickeys.findOneBy({ userId: user.id }), v => v != null); 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			user,
 | 
								user,
 | 
				
			||||||
| 
						 | 
					@ -125,11 +140,6 @@ export default class DbResolver {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type AuthUser = {
 | 
					 | 
				
			||||||
	user: IRemoteUser;
 | 
					 | 
				
			||||||
	key?: UserPublickey;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type UriParseResult = {
 | 
					type UriParseResult = {
 | 
				
			||||||
	/** id in DB (local object only) */
 | 
						/** id in DB (local object only) */
 | 
				
			||||||
	id?: string;
 | 
						id?: string;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
import { Users, Followings } from '@/models/index.js';
 | 
					import { Users, Followings } from '@/models/index.js';
 | 
				
			||||||
import { ILocalUser, IRemoteUser, User } from '@/models/entities/user.js';
 | 
					import { ILocalUser, IRemoteUser, User } from '@/models/entities/user.js';
 | 
				
			||||||
import { deliver } from '@/queue/index.js';
 | 
					import { deliver } from '@/queue/index.js';
 | 
				
			||||||
 | 
					import { IsNull, Not } from 'typeorm';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//#region types
 | 
					//#region types
 | 
				
			||||||
interface IRecipe {
 | 
					interface IRecipe {
 | 
				
			||||||
| 
						 | 
					@ -82,16 +83,26 @@ export default class DeliverManager {
 | 
				
			||||||
		for (const recipe of this.recipes) {
 | 
							for (const recipe of this.recipes) {
 | 
				
			||||||
			if (isFollowers(recipe)) {
 | 
								if (isFollowers(recipe)) {
 | 
				
			||||||
				// followers deliver
 | 
									// followers deliver
 | 
				
			||||||
 | 
									// TODO: SELECT DISTINCT ON ("followerSharedInbox") "followerSharedInbox" みたいな問い合わせにすればよりパフォーマンス向上できそう
 | 
				
			||||||
 | 
									// ただ、sharedInboxがnullなリモートユーザーも稀におり、その対応ができなさそう?
 | 
				
			||||||
				const followers = await Followings.find({
 | 
									const followers = await Followings.find({
 | 
				
			||||||
 | 
										where: {
 | 
				
			||||||
						followeeId: this.actor.id,
 | 
											followeeId: this.actor.id,
 | 
				
			||||||
				});
 | 
											followerHost: Not(IsNull()),
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										select: {
 | 
				
			||||||
 | 
											followerSharedInbox: true,
 | 
				
			||||||
 | 
											followerInbox: true,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									}) as {
 | 
				
			||||||
 | 
										followerSharedInbox: string | null;
 | 
				
			||||||
 | 
										followerInbox: string;
 | 
				
			||||||
 | 
									}[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				for (const following of followers) {
 | 
									for (const following of followers) {
 | 
				
			||||||
					if (Followings.isRemoteFollower(following)) {
 | 
					 | 
				
			||||||
					const inbox = following.followerSharedInbox || following.followerInbox;
 | 
										const inbox = following.followerSharedInbox || following.followerInbox;
 | 
				
			||||||
					inboxes.add(inbox);
 | 
										inboxes.add(inbox);
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			} else if (isDirect(recipe)) {
 | 
								} else if (isDirect(recipe)) {
 | 
				
			||||||
				// direct deliver
 | 
									// direct deliver
 | 
				
			||||||
				const inbox = recipe.to.inbox;
 | 
									const inbox = recipe.to.inbox;
 | 
				
			||||||
| 
						 | 
					@ -112,7 +123,7 @@ export default class DeliverManager {
 | 
				
			||||||
 * @param activity Activity
 | 
					 * @param activity Activity
 | 
				
			||||||
 * @param from Followee
 | 
					 * @param from Followee
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export async function deliverToFollowers(actor: ILocalUser, activity: any) {
 | 
					export async function deliverToFollowers(actor: { id: ILocalUser['id']; host: null; }, activity: any) {
 | 
				
			||||||
	const manager = new DeliverManager(actor, activity);
 | 
						const manager = new DeliverManager(actor, activity);
 | 
				
			||||||
	manager.addFollowersRecipe();
 | 
						manager.addFollowersRecipe();
 | 
				
			||||||
	await manager.execute();
 | 
						await manager.execute();
 | 
				
			||||||
| 
						 | 
					@ -123,7 +134,7 @@ export async function deliverToFollowers(actor: ILocalUser, activity: any) {
 | 
				
			||||||
 * @param activity Activity
 | 
					 * @param activity Activity
 | 
				
			||||||
 * @param to Target user
 | 
					 * @param to Target user
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export async function deliverToUser(actor: ILocalUser, activity: any, to: IRemoteUser) {
 | 
					export async function deliverToUser(actor: { id: ILocalUser['id']; host: null; }, activity: any, to: IRemoteUser) {
 | 
				
			||||||
	const manager = new DeliverManager(actor, activity);
 | 
						const manager = new DeliverManager(actor, activity);
 | 
				
			||||||
	manager.addDirectRecipe(to);
 | 
						manager.addDirectRecipe(to);
 | 
				
			||||||
	await manager.execute();
 | 
						await manager.execute();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,10 +1,10 @@
 | 
				
			||||||
import { IRemoteUser } from '@/models/entities/user.js';
 | 
					import { CacheableRemoteUser } from '@/models/entities/user.js';
 | 
				
			||||||
import accept from '@/services/following/requests/accept.js';
 | 
					import accept from '@/services/following/requests/accept.js';
 | 
				
			||||||
import { IFollow } from '../../type.js';
 | 
					import { IFollow } from '../../type.js';
 | 
				
			||||||
import DbResolver from '../../db-resolver.js';
 | 
					import DbResolver from '../../db-resolver.js';
 | 
				
			||||||
import { relayAccepted } from '@/services/relay.js';
 | 
					import { relayAccepted } from '@/services/relay.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default async (actor: IRemoteUser, activity: IFollow): Promise<string> => {
 | 
					export default async (actor: CacheableRemoteUser, activity: IFollow): Promise<string> => {
 | 
				
			||||||
	// ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある
 | 
						// ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const dbResolver = new DbResolver();
 | 
						const dbResolver = new DbResolver();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,12 +1,12 @@
 | 
				
			||||||
import Resolver from '../../resolver.js';
 | 
					import Resolver from '../../resolver.js';
 | 
				
			||||||
import { IRemoteUser } from '@/models/entities/user.js';
 | 
					import { CacheableRemoteUser } from '@/models/entities/user.js';
 | 
				
			||||||
import acceptFollow from './follow.js';
 | 
					import acceptFollow from './follow.js';
 | 
				
			||||||
import { IAccept, isFollow, getApType } from '../../type.js';
 | 
					import { IAccept, isFollow, getApType } from '../../type.js';
 | 
				
			||||||
import { apLogger } from '../../logger.js';
 | 
					import { apLogger } from '../../logger.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const logger = apLogger;
 | 
					const logger = apLogger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default async (actor: IRemoteUser, activity: IAccept): Promise<string> => {
 | 
					export default async (actor: CacheableRemoteUser, activity: IAccept): Promise<string> => {
 | 
				
			||||||
	const uri = activity.id || activity;
 | 
						const uri = activity.id || activity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	logger.info(`Accept: ${uri}`);
 | 
						logger.info(`Accept: ${uri}`);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,9 @@
 | 
				
			||||||
import { IRemoteUser } from '@/models/entities/user.js';
 | 
					import { CacheableRemoteUser } from '@/models/entities/user.js';
 | 
				
			||||||
import { IAdd } from '../../type.js';
 | 
					import { IAdd } from '../../type.js';
 | 
				
			||||||
import { resolveNote } from '../../models/note.js';
 | 
					import { resolveNote } from '../../models/note.js';
 | 
				
			||||||
import { addPinned } from '@/services/i/pin.js';
 | 
					import { addPinned } from '@/services/i/pin.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default async (actor: IRemoteUser, activity: IAdd): Promise<void> => {
 | 
					export default async (actor: CacheableRemoteUser, activity: IAdd): Promise<void> => {
 | 
				
			||||||
	if ('actor' in activity && actor.uri !== activity.actor) {
 | 
						if ('actor' in activity && actor.uri !== activity.actor) {
 | 
				
			||||||
		throw new Error('invalid actor');
 | 
							throw new Error('invalid actor');
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,12 +1,12 @@
 | 
				
			||||||
import Resolver from '../../resolver.js';
 | 
					import Resolver from '../../resolver.js';
 | 
				
			||||||
import { IRemoteUser } from '@/models/entities/user.js';
 | 
					import { CacheableRemoteUser } from '@/models/entities/user.js';
 | 
				
			||||||
import announceNote from './note.js';
 | 
					import announceNote from './note.js';
 | 
				
			||||||
import { IAnnounce, getApId } from '../../type.js';
 | 
					import { IAnnounce, getApId } from '../../type.js';
 | 
				
			||||||
import { apLogger } from '../../logger.js';
 | 
					import { apLogger } from '../../logger.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const logger = apLogger;
 | 
					const logger = apLogger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default async (actor: IRemoteUser, activity: IAnnounce): Promise<void> => {
 | 
					export default async (actor: CacheableRemoteUser, activity: IAnnounce): Promise<void> => {
 | 
				
			||||||
	const uri = getApId(activity);
 | 
						const uri = getApId(activity);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	logger.info(`Announce: ${uri}`);
 | 
						logger.info(`Announce: ${uri}`);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
import Resolver from '../../resolver.js';
 | 
					import Resolver from '../../resolver.js';
 | 
				
			||||||
import post from '@/services/note/create.js';
 | 
					import post from '@/services/note/create.js';
 | 
				
			||||||
import { IRemoteUser } from '@/models/entities/user.js';
 | 
					import { CacheableRemoteUser } from '@/models/entities/user.js';
 | 
				
			||||||
import { IAnnounce, getApId } from '../../type.js';
 | 
					import { IAnnounce, getApId } from '../../type.js';
 | 
				
			||||||
import { fetchNote, resolveNote } from '../../models/note.js';
 | 
					import { fetchNote, resolveNote } from '../../models/note.js';
 | 
				
			||||||
import { apLogger } from '../../logger.js';
 | 
					import { apLogger } from '../../logger.js';
 | 
				
			||||||
| 
						 | 
					@ -15,10 +15,9 @@ const logger = apLogger;
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * アナウンスアクティビティを捌きます
 | 
					 * アナウンスアクティビティを捌きます
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export default async function(resolver: Resolver, actor: IRemoteUser, activity: IAnnounce, targetUri: string): Promise<void> {
 | 
					export default async function(resolver: Resolver, actor: CacheableRemoteUser, activity: IAnnounce, targetUri: string): Promise<void> {
 | 
				
			||||||
	const uri = getApId(activity);
 | 
						const uri = getApId(activity);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// アナウンサーが凍結されていたらスキップ
 | 
					 | 
				
			||||||
	if (actor.isSuspended) {
 | 
						if (actor.isSuspended) {
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,10 @@
 | 
				
			||||||
import { IBlock } from '../../type.js';
 | 
					import { IBlock } from '../../type.js';
 | 
				
			||||||
import block from '@/services/blocking/create.js';
 | 
					import block from '@/services/blocking/create.js';
 | 
				
			||||||
import { IRemoteUser } from '@/models/entities/user.js';
 | 
					import { CacheableRemoteUser } from '@/models/entities/user.js';
 | 
				
			||||||
import DbResolver from '../../db-resolver.js';
 | 
					import DbResolver from '../../db-resolver.js';
 | 
				
			||||||
 | 
					import { Users } from '@/models/index.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default async (actor: IRemoteUser, activity: IBlock): Promise<string> => {
 | 
					export default async (actor: CacheableRemoteUser, activity: IBlock): Promise<string> => {
 | 
				
			||||||
	// ※ activity.objectにブロック対象があり、それは存在するローカルユーザーのはず
 | 
						// ※ activity.objectにブロック対象があり、それは存在するローカルユーザーのはず
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const dbResolver = new DbResolver();
 | 
						const dbResolver = new DbResolver();
 | 
				
			||||||
| 
						 | 
					@ -17,6 +18,6 @@ export default async (actor: IRemoteUser, activity: IBlock): Promise<string> =>
 | 
				
			||||||
		return `skip: ブロックしようとしているユーザーはローカルユーザーではありません`;
 | 
							return `skip: ブロックしようとしているユーザーはローカルユーザーではありません`;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	await block(actor, blockee);
 | 
						await block(await Users.findOneByOrFail({ id: actor.id }), await Users.findOneByOrFail({ id: blockee.id }));
 | 
				
			||||||
	return `ok`;
 | 
						return `ok`;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
import Resolver from '../../resolver.js';
 | 
					import Resolver from '../../resolver.js';
 | 
				
			||||||
import { IRemoteUser } from '@/models/entities/user.js';
 | 
					import { CacheableRemoteUser } from '@/models/entities/user.js';
 | 
				
			||||||
import createNote from './note.js';
 | 
					import createNote from './note.js';
 | 
				
			||||||
import { ICreate, getApId, isPost, getApType } from '../../type.js';
 | 
					import { ICreate, getApId, isPost, getApType } from '../../type.js';
 | 
				
			||||||
import { apLogger } from '../../logger.js';
 | 
					import { apLogger } from '../../logger.js';
 | 
				
			||||||
| 
						 | 
					@ -7,7 +7,7 @@ import { toArray, concat, unique } from '@/prelude/array.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const logger = apLogger;
 | 
					const logger = apLogger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default async (actor: IRemoteUser, activity: ICreate): Promise<void> => {
 | 
					export default async (actor: CacheableRemoteUser, activity: ICreate): Promise<void> => {
 | 
				
			||||||
	const uri = getApId(activity);
 | 
						const uri = getApId(activity);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	logger.info(`Create: ${uri}`);
 | 
						logger.info(`Create: ${uri}`);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
import Resolver from '../../resolver.js';
 | 
					import Resolver from '../../resolver.js';
 | 
				
			||||||
import { IRemoteUser } from '@/models/entities/user.js';
 | 
					import { CacheableRemoteUser } from '@/models/entities/user.js';
 | 
				
			||||||
import { createNote, fetchNote } from '../../models/note.js';
 | 
					import { createNote, fetchNote } from '../../models/note.js';
 | 
				
			||||||
import { getApId, IObject, ICreate } from '../../type.js';
 | 
					import { getApId, IObject, ICreate } from '../../type.js';
 | 
				
			||||||
import { getApLock } from '@/misc/app-lock.js';
 | 
					import { getApLock } from '@/misc/app-lock.js';
 | 
				
			||||||
| 
						 | 
					@ -9,7 +9,7 @@ import { StatusError } from '@/misc/fetch.js';
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 投稿作成アクティビティを捌きます
 | 
					 * 投稿作成アクティビティを捌きます
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export default async function(resolver: Resolver, actor: IRemoteUser, note: IObject, silent = false, activity?: ICreate): Promise<string> {
 | 
					export default async function(resolver: Resolver, actor: CacheableRemoteUser, note: IObject, silent = false, activity?: ICreate): Promise<string> {
 | 
				
			||||||
	const uri = getApId(note);
 | 
						const uri = getApId(note);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (typeof note === 'object') {
 | 
						if (typeof note === 'object') {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,18 +1,19 @@
 | 
				
			||||||
import { apLogger } from '../../logger.js';
 | 
					import { apLogger } from '../../logger.js';
 | 
				
			||||||
import { createDeleteAccountJob } from '@/queue/index.js';
 | 
					import { createDeleteAccountJob } from '@/queue/index.js';
 | 
				
			||||||
import { IRemoteUser } from '@/models/entities/user.js';
 | 
					import { CacheableRemoteUser } from '@/models/entities/user.js';
 | 
				
			||||||
import { Users } from '@/models/index.js';
 | 
					import { Users } from '@/models/index.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const logger = apLogger;
 | 
					const logger = apLogger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function deleteActor(actor: IRemoteUser, uri: string): Promise<string> {
 | 
					export async function deleteActor(actor: CacheableRemoteUser, uri: string): Promise<string> {
 | 
				
			||||||
	logger.info(`Deleting the Actor: ${uri}`);
 | 
						logger.info(`Deleting the Actor: ${uri}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (actor.uri !== uri) {
 | 
						if (actor.uri !== uri) {
 | 
				
			||||||
		return `skip: delete actor ${actor.uri} !== ${uri}`;
 | 
							return `skip: delete actor ${actor.uri} !== ${uri}`;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (actor.isDeleted) {
 | 
						const user = await Users.findOneByOrFail({ id: actor.id });
 | 
				
			||||||
 | 
						if (user.isDeleted) {
 | 
				
			||||||
		logger.info(`skip: already deleted`);
 | 
							logger.info(`skip: already deleted`);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
import deleteNote from './note.js';
 | 
					import deleteNote from './note.js';
 | 
				
			||||||
import { IRemoteUser } from '@/models/entities/user.js';
 | 
					import { CacheableRemoteUser } from '@/models/entities/user.js';
 | 
				
			||||||
import { IDelete, getApId, isTombstone, IObject, validPost, validActor } from '../../type.js';
 | 
					import { IDelete, getApId, isTombstone, IObject, validPost, validActor } from '../../type.js';
 | 
				
			||||||
import { toSingle } from '@/prelude/array.js';
 | 
					import { toSingle } from '@/prelude/array.js';
 | 
				
			||||||
import { deleteActor } from './actor.js';
 | 
					import { deleteActor } from './actor.js';
 | 
				
			||||||
| 
						 | 
					@ -7,7 +7,7 @@ import { deleteActor } from './actor.js';
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 削除アクティビティを捌きます
 | 
					 * 削除アクティビティを捌きます
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export default async (actor: IRemoteUser, activity: IDelete): Promise<string> => {
 | 
					export default async (actor: CacheableRemoteUser, activity: IDelete): Promise<string> => {
 | 
				
			||||||
	if ('actor' in activity && actor.uri !== activity.actor) {
 | 
						if ('actor' in activity && actor.uri !== activity.actor) {
 | 
				
			||||||
		throw new Error('invalid actor');
 | 
							throw new Error('invalid actor');
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
import { IRemoteUser } from '@/models/entities/user.js';
 | 
					import { CacheableRemoteUser } from '@/models/entities/user.js';
 | 
				
			||||||
import deleteNode from '@/services/note/delete.js';
 | 
					import deleteNode from '@/services/note/delete.js';
 | 
				
			||||||
import { apLogger } from '../../logger.js';
 | 
					import { apLogger } from '../../logger.js';
 | 
				
			||||||
import DbResolver from '../../db-resolver.js';
 | 
					import DbResolver from '../../db-resolver.js';
 | 
				
			||||||
| 
						 | 
					@ -7,7 +7,7 @@ import { deleteMessage } from '@/services/messages/delete.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const logger = apLogger;
 | 
					const logger = apLogger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default async function(actor: IRemoteUser, uri: string): Promise<string> {
 | 
					export default async function(actor: CacheableRemoteUser, uri: string): Promise<string> {
 | 
				
			||||||
	logger.info(`Deleting the Note: ${uri}`);
 | 
						logger.info(`Deleting the Note: ${uri}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const unlock = await getApLock(uri);
 | 
						const unlock = await getApLock(uri);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,17 +1,17 @@
 | 
				
			||||||
import { IRemoteUser } from '@/models/entities/user.js';
 | 
					import { CacheableRemoteUser } from '@/models/entities/user.js';
 | 
				
			||||||
import config from '@/config/index.js';
 | 
					import config from '@/config/index.js';
 | 
				
			||||||
import { IFlag, getApIds } from '../../type.js';
 | 
					import { IFlag, getApIds } from '../../type.js';
 | 
				
			||||||
import { AbuseUserReports, Users } from '@/models/index.js';
 | 
					import { AbuseUserReports, Users } from '@/models/index.js';
 | 
				
			||||||
import { In } from 'typeorm';
 | 
					import { In } from 'typeorm';
 | 
				
			||||||
import { genId } from '@/misc/gen-id.js';
 | 
					import { genId } from '@/misc/gen-id.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default async (actor: IRemoteUser, activity: IFlag): Promise<string> => {
 | 
					export default async (actor: CacheableRemoteUser, activity: IFlag): Promise<string> => {
 | 
				
			||||||
	// objectは `(User|Note) | (User|Note)[]` だけど、全パターンDBスキーマと対応させられないので
 | 
						// objectは `(User|Note) | (User|Note)[]` だけど、全パターンDBスキーマと対応させられないので
 | 
				
			||||||
	// 対象ユーザーは一番最初のユーザー として あとはコメントとして格納する
 | 
						// 対象ユーザーは一番最初のユーザー として あとはコメントとして格納する
 | 
				
			||||||
	const uris = getApIds(activity.object);
 | 
						const uris = getApIds(activity.object);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const userIds = uris.filter(uri => uri.startsWith(config.url + '/users/')).map(uri => uri.split('/').pop()!);
 | 
						const userIds = uris.filter(uri => uri.startsWith(config.url + '/users/')).map(uri => uri.split('/').pop()!);
 | 
				
			||||||
	const users = await Users.find({
 | 
						const users = await Users.findBy({
 | 
				
			||||||
		id: In(userIds),
 | 
							id: In(userIds),
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
	if (users.length < 1) return `skip`;
 | 
						if (users.length < 1) return `skip`;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,9 @@
 | 
				
			||||||
import { IRemoteUser } from '@/models/entities/user.js';
 | 
					import { CacheableRemoteUser } from '@/models/entities/user.js';
 | 
				
			||||||
import follow from '@/services/following/create.js';
 | 
					import follow from '@/services/following/create.js';
 | 
				
			||||||
import { IFollow } from '../type.js';
 | 
					import { IFollow } from '../type.js';
 | 
				
			||||||
import DbResolver from '../db-resolver.js';
 | 
					import DbResolver from '../db-resolver.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default async (actor: IRemoteUser, activity: IFollow): Promise<string> => {
 | 
					export default async (actor: CacheableRemoteUser, activity: IFollow): Promise<string> => {
 | 
				
			||||||
	const dbResolver = new DbResolver();
 | 
						const dbResolver = new DbResolver();
 | 
				
			||||||
	const followee = await dbResolver.getUserFromApId(activity.object);
 | 
						const followee = await dbResolver.getUserFromApId(activity.object);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
import { IObject, isCreate, isDelete, isUpdate, isRead, isFollow, isAccept, isReject, isAdd, isRemove, isAnnounce, isLike, isUndo, isBlock, isCollectionOrOrderedCollection, isCollection, isFlag } from '../type.js';
 | 
					import { IObject, isCreate, isDelete, isUpdate, isRead, isFollow, isAccept, isReject, isAdd, isRemove, isAnnounce, isLike, isUndo, isBlock, isCollectionOrOrderedCollection, isCollection, isFlag } from '../type.js';
 | 
				
			||||||
import { IRemoteUser } from '@/models/entities/user.js';
 | 
					import { CacheableRemoteUser } from '@/models/entities/user.js';
 | 
				
			||||||
import create from './create/index.js';
 | 
					import create from './create/index.js';
 | 
				
			||||||
import performDeleteActivity from './delete/index.js';
 | 
					import performDeleteActivity from './delete/index.js';
 | 
				
			||||||
import performUpdateActivity from './update/index.js';
 | 
					import performUpdateActivity from './update/index.js';
 | 
				
			||||||
| 
						 | 
					@ -17,8 +17,9 @@ import flag from './flag/index.js';
 | 
				
			||||||
import { apLogger } from '../logger.js';
 | 
					import { apLogger } from '../logger.js';
 | 
				
			||||||
import Resolver from '../resolver.js';
 | 
					import Resolver from '../resolver.js';
 | 
				
			||||||
import { toArray } from '@/prelude/array.js';
 | 
					import { toArray } from '@/prelude/array.js';
 | 
				
			||||||
 | 
					import { Users } from '@/models/index.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function performActivity(actor: IRemoteUser, activity: IObject) {
 | 
					export async function performActivity(actor: CacheableRemoteUser, activity: IObject) {
 | 
				
			||||||
	if (isCollectionOrOrderedCollection(activity)) {
 | 
						if (isCollectionOrOrderedCollection(activity)) {
 | 
				
			||||||
		const resolver = new Resolver();
 | 
							const resolver = new Resolver();
 | 
				
			||||||
		for (const item of toArray(isCollection(activity) ? activity.items : activity.orderedItems)) {
 | 
							for (const item of toArray(isCollection(activity) ? activity.items : activity.orderedItems)) {
 | 
				
			||||||
| 
						 | 
					@ -36,7 +37,7 @@ export async function performActivity(actor: IRemoteUser, activity: IObject) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function performOneActivity(actor: IRemoteUser, activity: IObject): Promise<void> {
 | 
					async function performOneActivity(actor: CacheableRemoteUser, activity: IObject): Promise<void> {
 | 
				
			||||||
	if (actor.isSuspended) return;
 | 
						if (actor.isSuspended) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (isCreate(activity)) {
 | 
						if (isCreate(activity)) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,9 @@
 | 
				
			||||||
import { IRemoteUser } from '@/models/entities/user.js';
 | 
					import { CacheableRemoteUser } from '@/models/entities/user.js';
 | 
				
			||||||
import { ILike, getApId } from '../type.js';
 | 
					import { ILike, getApId } from '../type.js';
 | 
				
			||||||
import create from '@/services/note/reaction/create.js';
 | 
					import create from '@/services/note/reaction/create.js';
 | 
				
			||||||
import { fetchNote, extractEmojis } from '../models/note.js';
 | 
					import { fetchNote, extractEmojis } from '../models/note.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default async (actor: IRemoteUser, activity: ILike) => {
 | 
					export default async (actor: CacheableRemoteUser, activity: ILike) => {
 | 
				
			||||||
	const targetUri = getApId(activity.object);
 | 
						const targetUri = getApId(activity.object);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const note = await fetchNote(targetUri);
 | 
						const note = await fetchNote(targetUri);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,10 +1,10 @@
 | 
				
			||||||
import { IRemoteUser } from '@/models/entities/user.js';
 | 
					import { CacheableRemoteUser } from '@/models/entities/user.js';
 | 
				
			||||||
import { IRead, getApId } from '../type.js';
 | 
					import { IRead, getApId } from '../type.js';
 | 
				
			||||||
import { isSelfHost, extractDbHost } from '@/misc/convert-host.js';
 | 
					import { isSelfHost, extractDbHost } from '@/misc/convert-host.js';
 | 
				
			||||||
import { MessagingMessages } from '@/models/index.js';
 | 
					import { MessagingMessages } from '@/models/index.js';
 | 
				
			||||||
import { readUserMessagingMessage } from '../../../server/api/common/read-messaging-message.js';
 | 
					import { readUserMessagingMessage } from '../../../server/api/common/read-messaging-message.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const performReadActivity = async (actor: IRemoteUser, activity: IRead): Promise<string> => {
 | 
					export const performReadActivity = async (actor: CacheableRemoteUser, activity: IRead): Promise<string> => {
 | 
				
			||||||
	const id = await getApId(activity.object);
 | 
						const id = await getApId(activity.object);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!isSelfHost(extractDbHost(id))) {
 | 
						if (!isSelfHost(extractDbHost(id))) {
 | 
				
			||||||
| 
						 | 
					@ -13,7 +13,7 @@ export const performReadActivity = async (actor: IRemoteUser, activity: IRead):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const messageId = id.split('/').pop();
 | 
						const messageId = id.split('/').pop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const message = await MessagingMessages.findOne(messageId);
 | 
						const message = await MessagingMessages.findOneBy({ id: messageId });
 | 
				
			||||||
	if (message == null) {
 | 
						if (message == null) {
 | 
				
			||||||
		return `skip: message not found`;
 | 
							return `skip: message not found`;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,11 @@
 | 
				
			||||||
import { IRemoteUser } from '@/models/entities/user.js';
 | 
					import { CacheableRemoteUser } from '@/models/entities/user.js';
 | 
				
			||||||
import { remoteReject } from '@/services/following/reject.js';
 | 
					import { remoteReject } from '@/services/following/reject.js';
 | 
				
			||||||
import { IFollow } from '../../type.js';
 | 
					import { IFollow } from '../../type.js';
 | 
				
			||||||
import DbResolver from '../../db-resolver.js';
 | 
					import DbResolver from '../../db-resolver.js';
 | 
				
			||||||
import { relayRejected } from '@/services/relay.js';
 | 
					import { relayRejected } from '@/services/relay.js';
 | 
				
			||||||
import { Users } from '@/models/index.js';
 | 
					import { Users } from '@/models/index.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default async (actor: IRemoteUser, activity: IFollow): Promise<string> => {
 | 
					export default async (actor: CacheableRemoteUser, activity: IFollow): Promise<string> => {
 | 
				
			||||||
	// ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある
 | 
						// ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const dbResolver = new DbResolver();
 | 
						const dbResolver = new DbResolver();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,12 +1,12 @@
 | 
				
			||||||
import Resolver from '../../resolver.js';
 | 
					import Resolver from '../../resolver.js';
 | 
				
			||||||
import { IRemoteUser } from '@/models/entities/user.js';
 | 
					import { CacheableRemoteUser } from '@/models/entities/user.js';
 | 
				
			||||||
import rejectFollow from './follow.js';
 | 
					import rejectFollow from './follow.js';
 | 
				
			||||||
import { IReject, isFollow, getApType } from '../../type.js';
 | 
					import { IReject, isFollow, getApType } from '../../type.js';
 | 
				
			||||||
import { apLogger } from '../../logger.js';
 | 
					import { apLogger } from '../../logger.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const logger = apLogger;
 | 
					const logger = apLogger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default async (actor: IRemoteUser, activity: IReject): Promise<string> => {
 | 
					export default async (actor: CacheableRemoteUser, activity: IReject): Promise<string> => {
 | 
				
			||||||
	const uri = activity.id || activity;
 | 
						const uri = activity.id || activity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	logger.info(`Reject: ${uri}`);
 | 
						logger.info(`Reject: ${uri}`);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,9 @@
 | 
				
			||||||
import { IRemoteUser } from '@/models/entities/user.js';
 | 
					import { CacheableRemoteUser } from '@/models/entities/user.js';
 | 
				
			||||||
import { IRemove } from '../../type.js';
 | 
					import { IRemove } from '../../type.js';
 | 
				
			||||||
import { resolveNote } from '../../models/note.js';
 | 
					import { resolveNote } from '../../models/note.js';
 | 
				
			||||||
import { removePinned } from '@/services/i/pin.js';
 | 
					import { removePinned } from '@/services/i/pin.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default async (actor: IRemoteUser, activity: IRemove): Promise<void> => {
 | 
					export default async (actor: CacheableRemoteUser, activity: IRemove): Promise<void> => {
 | 
				
			||||||
	if ('actor' in activity && actor.uri !== activity.actor) {
 | 
						if ('actor' in activity && actor.uri !== activity.actor) {
 | 
				
			||||||
		throw new Error('invalid actor');
 | 
							throw new Error('invalid actor');
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,11 @@
 | 
				
			||||||
import unfollow from '@/services/following/delete.js';
 | 
					import unfollow from '@/services/following/delete.js';
 | 
				
			||||||
import cancelRequest from '@/services/following/requests/cancel.js';
 | 
					import cancelRequest from '@/services/following/requests/cancel.js';
 | 
				
			||||||
import {IAccept} from '../../type.js';
 | 
					import {IAccept} from '../../type.js';
 | 
				
			||||||
import { IRemoteUser } from '@/models/entities/user.js';
 | 
					import { CacheableRemoteUser } from '@/models/entities/user.js';
 | 
				
			||||||
import { Followings } from '@/models/index.js';
 | 
					import { Followings } from '@/models/index.js';
 | 
				
			||||||
import DbResolver from '../../db-resolver.js';
 | 
					import DbResolver from '../../db-resolver.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default async (actor: IRemoteUser, activity: IAccept): Promise<string> => {
 | 
					export default async (actor: CacheableRemoteUser, activity: IAccept): Promise<string> => {
 | 
				
			||||||
	const dbResolver = new DbResolver();
 | 
						const dbResolver = new DbResolver();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const follower = await dbResolver.getUserFromApId(activity.object);
 | 
						const follower = await dbResolver.getUserFromApId(activity.object);
 | 
				
			||||||
| 
						 | 
					@ -13,7 +13,7 @@ export default async (actor: IRemoteUser, activity: IAccept): Promise<string> =>
 | 
				
			||||||
		return `skip: follower not found`;
 | 
							return `skip: follower not found`;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const following = await Followings.findOne({
 | 
						const following = await Followings.findOneBy({
 | 
				
			||||||
		followerId: follower.id,
 | 
							followerId: follower.id,
 | 
				
			||||||
		followeeId: actor.id,
 | 
							followeeId: actor.id,
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,12 +1,12 @@
 | 
				
			||||||
import { Notes } from '@/models/index.js';
 | 
					import { Notes } from '@/models/index.js';
 | 
				
			||||||
import { IRemoteUser } from '@/models/entities/user.js';
 | 
					import { CacheableRemoteUser } from '@/models/entities/user.js';
 | 
				
			||||||
import { IAnnounce, getApId } from '../../type.js';
 | 
					import { IAnnounce, getApId } from '../../type.js';
 | 
				
			||||||
import deleteNote from '@/services/note/delete.js';
 | 
					import deleteNote from '@/services/note/delete.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const undoAnnounce = async (actor: IRemoteUser, activity: IAnnounce): Promise<string> => {
 | 
					export const undoAnnounce = async (actor: CacheableRemoteUser, activity: IAnnounce): Promise<string> => {
 | 
				
			||||||
	const uri = getApId(activity);
 | 
						const uri = getApId(activity);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const note = await Notes.findOne({
 | 
						const note = await Notes.findOneBy({
 | 
				
			||||||
		uri,
 | 
							uri,
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,10 @@
 | 
				
			||||||
import { IBlock } from '../../type.js';
 | 
					import { IBlock } from '../../type.js';
 | 
				
			||||||
import unblock from '@/services/blocking/delete.js';
 | 
					import unblock from '@/services/blocking/delete.js';
 | 
				
			||||||
import { IRemoteUser } from '@/models/entities/user.js';
 | 
					import { CacheableRemoteUser } from '@/models/entities/user.js';
 | 
				
			||||||
import DbResolver from '../../db-resolver.js';
 | 
					import DbResolver from '../../db-resolver.js';
 | 
				
			||||||
 | 
					import { Users } from '@/models/index.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default async (actor: IRemoteUser, activity: IBlock): Promise<string> => {
 | 
					export default async (actor: CacheableRemoteUser, activity: IBlock): Promise<string> => {
 | 
				
			||||||
	const dbResolver = new DbResolver();
 | 
						const dbResolver = new DbResolver();
 | 
				
			||||||
	const blockee = await dbResolver.getUserFromApId(activity.object);
 | 
						const blockee = await dbResolver.getUserFromApId(activity.object);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,6 +16,6 @@ export default async (actor: IRemoteUser, activity: IBlock): Promise<string> =>
 | 
				
			||||||
		return `skip: ブロック解除しようとしているユーザーはローカルユーザーではありません`;
 | 
							return `skip: ブロック解除しようとしているユーザーはローカルユーザーではありません`;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	await unblock(actor, blockee);
 | 
						await unblock(await Users.findOneByOrFail({ id: actor.id }), blockee);
 | 
				
			||||||
	return `ok`;
 | 
						return `ok`;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,11 @@
 | 
				
			||||||
import unfollow from '@/services/following/delete.js';
 | 
					import unfollow from '@/services/following/delete.js';
 | 
				
			||||||
import cancelRequest from '@/services/following/requests/cancel.js';
 | 
					import cancelRequest from '@/services/following/requests/cancel.js';
 | 
				
			||||||
import { IFollow } from '../../type.js';
 | 
					import { IFollow } from '../../type.js';
 | 
				
			||||||
import { IRemoteUser } from '@/models/entities/user.js';
 | 
					import { CacheableRemoteUser } from '@/models/entities/user.js';
 | 
				
			||||||
import { FollowRequests, Followings } from '@/models/index.js';
 | 
					import { FollowRequests, Followings } from '@/models/index.js';
 | 
				
			||||||
import DbResolver from '../../db-resolver.js';
 | 
					import DbResolver from '../../db-resolver.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default async (actor: IRemoteUser, activity: IFollow): Promise<string> => {
 | 
					export default async (actor: CacheableRemoteUser, activity: IFollow): Promise<string> => {
 | 
				
			||||||
	const dbResolver = new DbResolver();
 | 
						const dbResolver = new DbResolver();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const followee = await dbResolver.getUserFromApId(activity.object);
 | 
						const followee = await dbResolver.getUserFromApId(activity.object);
 | 
				
			||||||
| 
						 | 
					@ -17,12 +17,12 @@ export default async (actor: IRemoteUser, activity: IFollow): Promise<string> =>
 | 
				
			||||||
		return `skip: フォロー解除しようとしているユーザーはローカルユーザーではありません`;
 | 
							return `skip: フォロー解除しようとしているユーザーはローカルユーザーではありません`;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const req = await FollowRequests.findOne({
 | 
						const req = await FollowRequests.findOneBy({
 | 
				
			||||||
		followerId: actor.id,
 | 
							followerId: actor.id,
 | 
				
			||||||
		followeeId: followee.id,
 | 
							followeeId: followee.id,
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const following = await Followings.findOne({
 | 
						const following = await Followings.findOneBy({
 | 
				
			||||||
		followerId: actor.id,
 | 
							followerId: actor.id,
 | 
				
			||||||
		followeeId: followee.id,
 | 
							followeeId: followee.id,
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
import { IRemoteUser } from '@/models/entities/user.js';
 | 
					import { CacheableRemoteUser } from '@/models/entities/user.js';
 | 
				
			||||||
import { IUndo, isFollow, isBlock, isLike, isAnnounce, getApType, isAccept } from '../../type.js';
 | 
					import { IUndo, isFollow, isBlock, isLike, isAnnounce, getApType, isAccept } from '../../type.js';
 | 
				
			||||||
import unfollow from './follow.js';
 | 
					import unfollow from './follow.js';
 | 
				
			||||||
import unblock from './block.js';
 | 
					import unblock from './block.js';
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,7 @@ import { apLogger } from '../../logger.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const logger = apLogger;
 | 
					const logger = apLogger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default async (actor: IRemoteUser, activity: IUndo): Promise<string> => {
 | 
					export default async (actor: CacheableRemoteUser, activity: IUndo): Promise<string> => {
 | 
				
			||||||
	if ('actor' in activity && actor.uri !== activity.actor) {
 | 
						if ('actor' in activity && actor.uri !== activity.actor) {
 | 
				
			||||||
		throw new Error('invalid actor');
 | 
							throw new Error('invalid actor');
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
import { IRemoteUser } from '@/models/entities/user.js';
 | 
					import { CacheableRemoteUser } from '@/models/entities/user.js';
 | 
				
			||||||
import { ILike, getApId } from '../../type.js';
 | 
					import { ILike, getApId } from '../../type.js';
 | 
				
			||||||
import deleteReaction from '@/services/note/reaction/delete.js';
 | 
					import deleteReaction from '@/services/note/reaction/delete.js';
 | 
				
			||||||
import { fetchNote } from '../../models/note.js';
 | 
					import { fetchNote } from '../../models/note.js';
 | 
				
			||||||
| 
						 | 
					@ -6,7 +6,7 @@ import { fetchNote } from '../../models/note.js';
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Process Undo.Like activity
 | 
					 * Process Undo.Like activity
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export default async (actor: IRemoteUser, activity: ILike) => {
 | 
					export default async (actor: CacheableRemoteUser, activity: ILike) => {
 | 
				
			||||||
	const targetUri = getApId(activity.object);
 | 
						const targetUri = getApId(activity.object);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const note = await fetchNote(targetUri);
 | 
						const note = await fetchNote(targetUri);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
import { IRemoteUser } from '@/models/entities/user.js';
 | 
					import { CacheableRemoteUser } from '@/models/entities/user.js';
 | 
				
			||||||
import { getApType, IUpdate, isActor } from '../../type.js';
 | 
					import { getApType, IUpdate, isActor } from '../../type.js';
 | 
				
			||||||
import { apLogger } from '../../logger.js';
 | 
					import { apLogger } from '../../logger.js';
 | 
				
			||||||
import { updateQuestion } from '../../models/question.js';
 | 
					import { updateQuestion } from '../../models/question.js';
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,7 @@ import { updatePerson } from '../../models/person.js';
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Updateアクティビティを捌きます
 | 
					 * Updateアクティビティを捌きます
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export default async (actor: IRemoteUser, activity: IUpdate): Promise<string> => {
 | 
					export default async (actor: CacheableRemoteUser, activity: IUpdate): Promise<string> => {
 | 
				
			||||||
	if ('actor' in activity && actor.uri !== activity.actor) {
 | 
						if ('actor' in activity && actor.uri !== activity.actor) {
 | 
				
			||||||
		return `skip: invalid actor`;
 | 
							return `skip: invalid actor`;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,10 +1,10 @@
 | 
				
			||||||
import { uploadFromUrl } from '@/services/drive/upload-from-url.js';
 | 
					import { uploadFromUrl } from '@/services/drive/upload-from-url.js';
 | 
				
			||||||
import { IRemoteUser } from '@/models/entities/user.js';
 | 
					import { CacheableRemoteUser, IRemoteUser } from '@/models/entities/user.js';
 | 
				
			||||||
import Resolver from '../resolver.js';
 | 
					import Resolver from '../resolver.js';
 | 
				
			||||||
import { fetchMeta } from '@/misc/fetch-meta.js';
 | 
					import { fetchMeta } from '@/misc/fetch-meta.js';
 | 
				
			||||||
import { apLogger } from '../logger.js';
 | 
					import { apLogger } from '../logger.js';
 | 
				
			||||||
import { DriveFile } from '@/models/entities/drive-file.js';
 | 
					import { DriveFile } from '@/models/entities/drive-file.js';
 | 
				
			||||||
import { DriveFiles } from '@/models/index.js';
 | 
					import { DriveFiles, Users } from '@/models/index.js';
 | 
				
			||||||
import { truncate } from '@/misc/truncate.js';
 | 
					import { truncate } from '@/misc/truncate.js';
 | 
				
			||||||
import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/misc/hard-limits.js';
 | 
					import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/misc/hard-limits.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,7 +13,7 @@ const logger = apLogger;
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Imageを作成します。
 | 
					 * Imageを作成します。
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export async function createImage(actor: IRemoteUser, value: any): Promise<DriveFile> {
 | 
					export async function createImage(actor: CacheableRemoteUser, value: any): Promise<DriveFile> {
 | 
				
			||||||
	// 投稿者が凍結されていたらスキップ
 | 
						// 投稿者が凍結されていたらスキップ
 | 
				
			||||||
	if (actor.isSuspended) {
 | 
						if (actor.isSuspended) {
 | 
				
			||||||
		throw new Error('actor has been suspended');
 | 
							throw new Error('actor has been suspended');
 | 
				
			||||||
| 
						 | 
					@ -47,7 +47,7 @@ export async function createImage(actor: IRemoteUser, value: any): Promise<Drive
 | 
				
			||||||
				uri: image.url,
 | 
									uri: image.url,
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			file = await DriveFiles.findOneOrFail(file.id);
 | 
								file = await DriveFiles.findOneByOrFail({ id: file.id });
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -60,7 +60,7 @@ export async function createImage(actor: IRemoteUser, value: any): Promise<Drive
 | 
				
			||||||
 * Misskeyに対象のImageが登録されていればそれを返し、そうでなければ
 | 
					 * Misskeyに対象のImageが登録されていればそれを返し、そうでなければ
 | 
				
			||||||
 * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。
 | 
					 * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export async function resolveImage(actor: IRemoteUser, value: any): Promise<DriveFile> {
 | 
					export async function resolveImage(actor: CacheableRemoteUser, value: any): Promise<DriveFile> {
 | 
				
			||||||
	// TODO
 | 
						// TODO
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// リモートサーバーからフェッチしてきて登録
 | 
						// リモートサーバーからフェッチしてきて登録
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,17 +3,17 @@ import { IObject, isMention, IApMention } from '../type.js';
 | 
				
			||||||
import { resolvePerson } from './person.js';
 | 
					import { resolvePerson } from './person.js';
 | 
				
			||||||
import promiseLimit from 'promise-limit';
 | 
					import promiseLimit from 'promise-limit';
 | 
				
			||||||
import Resolver from '../resolver.js';
 | 
					import Resolver from '../resolver.js';
 | 
				
			||||||
import { User } from '@/models/entities/user.js';
 | 
					import { CacheableUser, User } from '@/models/entities/user.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function extractApMentions(tags: IObject | IObject[] | null | undefined) {
 | 
					export async function extractApMentions(tags: IObject | IObject[] | null | undefined) {
 | 
				
			||||||
	const hrefs = unique(extractApMentionObjects(tags).map(x => x.href as string));
 | 
						const hrefs = unique(extractApMentionObjects(tags).map(x => x.href as string));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const resolver = new Resolver();
 | 
						const resolver = new Resolver();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const limit = promiseLimit<User | null>(2);
 | 
						const limit = promiseLimit<CacheableUser | null>(2);
 | 
				
			||||||
	const mentionedUsers = (await Promise.all(
 | 
						const mentionedUsers = (await Promise.all(
 | 
				
			||||||
		hrefs.map(x => limit(() => resolvePerson(x, resolver).catch(() => null)))
 | 
							hrefs.map(x => limit(() => resolvePerson(x, resolver).catch(() => null)))
 | 
				
			||||||
	)).filter((x): x is User => x != null);
 | 
						)).filter((x): x is CacheableUser => x != null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return mentionedUsers;
 | 
						return mentionedUsers;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,7 @@ import Resolver from '../resolver.js';
 | 
				
			||||||
import post from '@/services/note/create.js';
 | 
					import post from '@/services/note/create.js';
 | 
				
			||||||
import { resolvePerson, updatePerson } from './person.js';
 | 
					import { resolvePerson, updatePerson } from './person.js';
 | 
				
			||||||
import { resolveImage } from './image.js';
 | 
					import { resolveImage } from './image.js';
 | 
				
			||||||
import { IRemoteUser } from '@/models/entities/user.js';
 | 
					import { CacheableRemoteUser, IRemoteUser } from '@/models/entities/user.js';
 | 
				
			||||||
import { htmlToMfm } from '../misc/html-to-mfm.js';
 | 
					import { htmlToMfm } from '../misc/html-to-mfm.js';
 | 
				
			||||||
import { extractApHashtags } from './tag.js';
 | 
					import { extractApHashtags } from './tag.js';
 | 
				
			||||||
import { unique, toArray, toSingle } from '@/prelude/array.js';
 | 
					import { unique, toArray, toSingle } from '@/prelude/array.js';
 | 
				
			||||||
| 
						 | 
					@ -15,7 +15,7 @@ import { apLogger } from '../logger.js';
 | 
				
			||||||
import { DriveFile } from '@/models/entities/drive-file.js';
 | 
					import { DriveFile } from '@/models/entities/drive-file.js';
 | 
				
			||||||
import { deliverQuestionUpdate } from '@/services/note/polls/update.js';
 | 
					import { deliverQuestionUpdate } from '@/services/note/polls/update.js';
 | 
				
			||||||
import { extractDbHost, toPuny } from '@/misc/convert-host.js';
 | 
					import { extractDbHost, toPuny } from '@/misc/convert-host.js';
 | 
				
			||||||
import { Emojis, Polls, MessagingMessages } from '@/models/index.js';
 | 
					import { Emojis, Polls, MessagingMessages, Users } from '@/models/index.js';
 | 
				
			||||||
import { Note } from '@/models/entities/note.js';
 | 
					import { Note } from '@/models/entities/note.js';
 | 
				
			||||||
import { IObject, getOneApId, getApId, getOneApHrefNullable, validPost, IPost, isEmoji, getApType } from '../type.js';
 | 
					import { IObject, getOneApId, getApId, getOneApHrefNullable, validPost, IPost, isEmoji, getApType } from '../type.js';
 | 
				
			||||||
import { Emoji } from '@/models/entities/emoji.js';
 | 
					import { Emoji } from '@/models/entities/emoji.js';
 | 
				
			||||||
| 
						 | 
					@ -90,7 +90,7 @@ export async function createNote(value: string | IObject, resolver?: Resolver, s
 | 
				
			||||||
	logger.info(`Creating the Note: ${note.id}`);
 | 
						logger.info(`Creating the Note: ${note.id}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 投稿者をフェッチ
 | 
						// 投稿者をフェッチ
 | 
				
			||||||
	const actor = await resolvePerson(getOneApId(note.attributedTo), resolver) as IRemoteUser;
 | 
						const actor = await resolvePerson(getOneApId(note.attributedTo), resolver) as CacheableRemoteUser;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 投稿者が凍結されていたらスキップ
 | 
						// 投稿者が凍結されていたらスキップ
 | 
				
			||||||
	if (actor.isSuspended) {
 | 
						if (actor.isSuspended) {
 | 
				
			||||||
| 
						 | 
					@ -141,7 +141,7 @@ export async function createNote(value: string | IObject, resolver?: Resolver, s
 | 
				
			||||||
			const uri = getApId(note.inReplyTo);
 | 
								const uri = getApId(note.inReplyTo);
 | 
				
			||||||
			if (uri.startsWith(config.url + '/')) {
 | 
								if (uri.startsWith(config.url + '/')) {
 | 
				
			||||||
				const id = uri.split('/').pop();
 | 
									const id = uri.split('/').pop();
 | 
				
			||||||
				const talk = await MessagingMessages.findOne(id);
 | 
									const talk = await MessagingMessages.findOneBy({ id });
 | 
				
			||||||
				if (talk) {
 | 
									if (talk) {
 | 
				
			||||||
					isTalk = true;
 | 
										isTalk = true;
 | 
				
			||||||
					return null;
 | 
										return null;
 | 
				
			||||||
| 
						 | 
					@ -197,11 +197,11 @@ export async function createNote(value: string | IObject, resolver?: Resolver, s
 | 
				
			||||||
	const cw = note.summary === '' ? null : note.summary;
 | 
						const cw = note.summary === '' ? null : note.summary;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// テキストのパース
 | 
						// テキストのパース
 | 
				
			||||||
	const text = note._misskey_content || (note.content ? htmlToMfm(note.content, note.tag) : null);
 | 
						const text = typeof note._misskey_content !== 'undefined' ? note._misskey_content : (note.content ? htmlToMfm(note.content, note.tag) : null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// vote
 | 
						// vote
 | 
				
			||||||
	if (reply && reply.hasPoll) {
 | 
						if (reply && reply.hasPoll) {
 | 
				
			||||||
		const poll = await Polls.findOneOrFail(reply.id);
 | 
							const poll = await Polls.findOneByOrFail({ noteId: reply.id });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const tryCreateVote = async (name: string, index: number): Promise<null> => {
 | 
							const tryCreateVote = async (name: string, index: number): Promise<null> => {
 | 
				
			||||||
			if (poll.expiresAt && Date.now() > new Date(poll.expiresAt).getTime()) {
 | 
								if (poll.expiresAt && Date.now() > new Date(poll.expiresAt).getTime()) {
 | 
				
			||||||
| 
						 | 
					@ -230,11 +230,6 @@ export async function createNote(value: string | IObject, resolver?: Resolver, s
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const poll = await extractPollFromQuestion(note, resolver).catch(() => undefined);
 | 
						const poll = await extractPollFromQuestion(note, resolver).catch(() => undefined);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// ユーザーの情報が古かったらついでに更新しておく
 | 
					 | 
				
			||||||
	if (actor.lastFetchedAt == null || Date.now() - actor.lastFetchedAt.getTime() > 1000 * 60 * 60 * 24) {
 | 
					 | 
				
			||||||
		if (actor.uri) updatePerson(actor.uri);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (isTalk) {
 | 
						if (isTalk) {
 | 
				
			||||||
		for (const recipient of visibleUsers) {
 | 
							for (const recipient of visibleUsers) {
 | 
				
			||||||
			await createMessage(actor, recipient, undefined, text || undefined, (files && files.length > 0) ? files[0] : null, object.id);
 | 
								await createMessage(actor, recipient, undefined, text || undefined, (files && files.length > 0) ? files[0] : null, object.id);
 | 
				
			||||||
| 
						 | 
					@ -311,7 +306,7 @@ export async function extractEmojis(tags: IObject | IObject[], host: string): Pr
 | 
				
			||||||
		const name = tag.name!.replace(/^:/, '').replace(/:$/, '');
 | 
							const name = tag.name!.replace(/^:/, '').replace(/:$/, '');
 | 
				
			||||||
		tag.icon = toSingle(tag.icon);
 | 
							tag.icon = toSingle(tag.icon);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const exists = await Emojis.findOne({
 | 
							const exists = await Emojis.findOneBy({
 | 
				
			||||||
			host,
 | 
								host,
 | 
				
			||||||
			name,
 | 
								name,
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
| 
						 | 
					@ -332,7 +327,7 @@ export async function extractEmojis(tags: IObject | IObject[], host: string): Pr
 | 
				
			||||||
					updatedAt: new Date(),
 | 
										updatedAt: new Date(),
 | 
				
			||||||
				});
 | 
									});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				return await Emojis.findOne({
 | 
									return await Emojis.findOneBy({
 | 
				
			||||||
					host,
 | 
										host,
 | 
				
			||||||
					name,
 | 
										name,
 | 
				
			||||||
				}) as Emoji;
 | 
									}) as Emoji;
 | 
				
			||||||
| 
						 | 
					@ -352,6 +347,6 @@ export async function extractEmojis(tags: IObject | IObject[], host: string): Pr
 | 
				
			||||||
			publicUrl: tag.icon!.url,
 | 
								publicUrl: tag.icon!.url,
 | 
				
			||||||
			updatedAt: new Date(),
 | 
								updatedAt: new Date(),
 | 
				
			||||||
			aliases: [],
 | 
								aliases: [],
 | 
				
			||||||
		} as Partial<Emoji>).then(x => Emojis.findOneOrFail(x.identifiers[0]));
 | 
							} as Partial<Emoji>).then(x => Emojis.findOneByOrFail(x.identifiers[0]));
 | 
				
			||||||
	}));
 | 
						}));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,7 +15,7 @@ import { apLogger } from '../logger.js';
 | 
				
			||||||
import { Note } from '@/models/entities/note.js';
 | 
					import { Note } from '@/models/entities/note.js';
 | 
				
			||||||
import { updateUsertags } from '@/services/update-hashtag.js';
 | 
					import { updateUsertags } from '@/services/update-hashtag.js';
 | 
				
			||||||
import { Users, Instances, DriveFiles, Followings, UserProfiles, UserPublickeys } from '@/models/index.js';
 | 
					import { Users, Instances, DriveFiles, Followings, UserProfiles, UserPublickeys } from '@/models/index.js';
 | 
				
			||||||
import { User, IRemoteUser } from '@/models/entities/user.js';
 | 
					import { User, IRemoteUser, CacheableUser } from '@/models/entities/user.js';
 | 
				
			||||||
import { Emoji } from '@/models/entities/emoji.js';
 | 
					import { Emoji } from '@/models/entities/emoji.js';
 | 
				
			||||||
import { UserNotePining } from '@/models/entities/user-note-pining.js';
 | 
					import { UserNotePining } from '@/models/entities/user-note-pining.js';
 | 
				
			||||||
import { genId } from '@/misc/gen-id.js';
 | 
					import { genId } from '@/misc/gen-id.js';
 | 
				
			||||||
| 
						 | 
					@ -24,12 +24,14 @@ import { UserPublickey } from '@/models/entities/user-publickey.js';
 | 
				
			||||||
import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js';
 | 
					import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js';
 | 
				
			||||||
import { toPuny } from '@/misc/convert-host.js';
 | 
					import { toPuny } from '@/misc/convert-host.js';
 | 
				
			||||||
import { UserProfile } from '@/models/entities/user-profile.js';
 | 
					import { UserProfile } from '@/models/entities/user-profile.js';
 | 
				
			||||||
import { getConnection } from 'typeorm';
 | 
					 | 
				
			||||||
import { toArray } from '@/prelude/array.js';
 | 
					import { toArray } from '@/prelude/array.js';
 | 
				
			||||||
import { fetchInstanceMetadata } from '@/services/fetch-instance-metadata.js';
 | 
					import { fetchInstanceMetadata } from '@/services/fetch-instance-metadata.js';
 | 
				
			||||||
import { normalizeForSearch } from '@/misc/normalize-for-search.js';
 | 
					import { normalizeForSearch } from '@/misc/normalize-for-search.js';
 | 
				
			||||||
import { truncate } from '@/misc/truncate.js';
 | 
					import { truncate } from '@/misc/truncate.js';
 | 
				
			||||||
import { StatusError } from '@/misc/fetch.js';
 | 
					import { StatusError } from '@/misc/fetch.js';
 | 
				
			||||||
 | 
					import { uriPersonCache } from '@/services/user-cache.js';
 | 
				
			||||||
 | 
					import { publishInternalEvent } from '@/services/stream.js';
 | 
				
			||||||
 | 
					import { db } from '@/db/postgre.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const logger = apLogger;
 | 
					const logger = apLogger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -91,19 +93,25 @@ function validateActor(x: IObject, uri: string): IActor {
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Misskeyに対象のPersonが登録されていればそれを返します。
 | 
					 * Misskeyに対象のPersonが登録されていればそれを返します。
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export async function fetchPerson(uri: string, resolver?: Resolver): Promise<User | null> {
 | 
					export async function fetchPerson(uri: string, resolver?: Resolver): Promise<CacheableUser | null> {
 | 
				
			||||||
	if (typeof uri !== 'string') throw new Error('uri is not string');
 | 
						if (typeof uri !== 'string') throw new Error('uri is not string');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const cached = uriPersonCache.get(uri);
 | 
				
			||||||
 | 
						if (cached) return cached;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// URIがこのサーバーを指しているならデータベースからフェッチ
 | 
						// URIがこのサーバーを指しているならデータベースからフェッチ
 | 
				
			||||||
	if (uri.startsWith(config.url + '/')) {
 | 
						if (uri.startsWith(config.url + '/')) {
 | 
				
			||||||
		const id = uri.split('/').pop();
 | 
							const id = uri.split('/').pop();
 | 
				
			||||||
		return await Users.findOne(id).then(x => x || null);
 | 
							const u = await Users.findOneBy({ id });
 | 
				
			||||||
 | 
							if (u) uriPersonCache.set(uri, u);
 | 
				
			||||||
 | 
							return u;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	//#region このサーバーに既に登録されていたらそれを返す
 | 
						//#region このサーバーに既に登録されていたらそれを返す
 | 
				
			||||||
	const exist = await Users.findOne({ uri });
 | 
						const exist = await Users.findOneBy({ uri });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (exist) {
 | 
						if (exist) {
 | 
				
			||||||
 | 
							uriPersonCache.set(uri, exist);
 | 
				
			||||||
		return exist;
 | 
							return exist;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	//#endregion
 | 
						//#endregion
 | 
				
			||||||
| 
						 | 
					@ -143,7 +151,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us
 | 
				
			||||||
	let user: IRemoteUser;
 | 
						let user: IRemoteUser;
 | 
				
			||||||
	try {
 | 
						try {
 | 
				
			||||||
		// Start transaction
 | 
							// Start transaction
 | 
				
			||||||
		await getConnection().transaction(async transactionalEntityManager => {
 | 
							await db.transaction(async transactionalEntityManager => {
 | 
				
			||||||
			user = await transactionalEntityManager.save(new User({
 | 
								user = await transactionalEntityManager.save(new User({
 | 
				
			||||||
				id: genId(),
 | 
									id: genId(),
 | 
				
			||||||
				avatarId: null,
 | 
									avatarId: null,
 | 
				
			||||||
| 
						 | 
					@ -189,7 +197,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us
 | 
				
			||||||
		// duplicate key error
 | 
							// duplicate key error
 | 
				
			||||||
		if (isDuplicateKeyValueError(e)) {
 | 
							if (isDuplicateKeyValueError(e)) {
 | 
				
			||||||
			// /users/@a => /users/:id のように入力がaliasなときにエラーになることがあるのを対応
 | 
								// /users/@a => /users/:id のように入力がaliasなときにエラーになることがあるのを対応
 | 
				
			||||||
			const u = await Users.findOne({
 | 
								const u = await Users.findOneBy({
 | 
				
			||||||
				uri: person.id,
 | 
									uri: person.id,
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -272,7 +280,7 @@ export async function updatePerson(uri: string, resolver?: Resolver | null, hint
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	//#region このサーバーに既に登録されているか
 | 
						//#region このサーバーに既に登録されているか
 | 
				
			||||||
	const exist = await Users.findOne({ uri }) as IRemoteUser;
 | 
						const exist = await Users.findOneBy({ uri }) as IRemoteUser;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (exist == null) {
 | 
						if (exist == null) {
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
| 
						 | 
					@ -352,6 +360,8 @@ export async function updatePerson(uri: string, resolver?: Resolver | null, hint
 | 
				
			||||||
		location: person['vcard:Address'] || null,
 | 
							location: person['vcard:Address'] || null,
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						publishInternalEvent('remoteUserUpdated', { id: exist.id });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// ハッシュタグ更新
 | 
						// ハッシュタグ更新
 | 
				
			||||||
	updateUsertags(exist, tags);
 | 
						updateUsertags(exist, tags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -371,7 +381,7 @@ export async function updatePerson(uri: string, resolver?: Resolver | null, hint
 | 
				
			||||||
 * Misskeyに対象のPersonが登録されていればそれを返し、そうでなければ
 | 
					 * Misskeyに対象のPersonが登録されていればそれを返し、そうでなければ
 | 
				
			||||||
 * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。
 | 
					 * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export async function resolvePerson(uri: string, resolver?: Resolver): Promise<User> {
 | 
					export async function resolvePerson(uri: string, resolver?: Resolver): Promise<CacheableUser> {
 | 
				
			||||||
	if (typeof uri !== 'string') throw new Error('uri is not string');
 | 
						if (typeof uri !== 'string') throw new Error('uri is not string');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	//#region このサーバーに既に登録されていたらそれを返す
 | 
						//#region このサーバーに既に登録されていたらそれを返す
 | 
				
			||||||
| 
						 | 
					@ -441,7 +451,7 @@ export function analyzeAttachments(attachments: IObject | IObject[] | undefined)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function updateFeatured(userId: User['id']) {
 | 
					export async function updateFeatured(userId: User['id']) {
 | 
				
			||||||
	const user = await Users.findOneOrFail(userId);
 | 
						const user = await Users.findOneByOrFail({ id: userId });
 | 
				
			||||||
	if (!Users.isRemoteUser(user)) return;
 | 
						if (!Users.isRemoteUser(user)) return;
 | 
				
			||||||
	if (!user.featured) return;
 | 
						if (!user.featured) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -464,7 +474,7 @@ export async function updateFeatured(userId: User['id']) {
 | 
				
			||||||
		.slice(0, 5)
 | 
							.slice(0, 5)
 | 
				
			||||||
		.map(item => limit(() => resolveNote(item, resolver))));
 | 
							.map(item => limit(() => resolveNote(item, resolver))));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	await getConnection().transaction(async transactionalEntityManager => {
 | 
						await db.transaction(async transactionalEntityManager => {
 | 
				
			||||||
		await transactionalEntityManager.delete(UserNotePining, { userId: user.id });
 | 
							await transactionalEntityManager.delete(UserNotePining, { userId: user.id });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// とりあえずidを別の時間で生成して順番を維持
 | 
							// とりあえずidを別の時間で生成して順番を維持
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -47,10 +47,10 @@ export async function updateQuestion(value: any) {
 | 
				
			||||||
	if (uri.startsWith(config.url + '/')) throw new Error('uri points local');
 | 
						if (uri.startsWith(config.url + '/')) throw new Error('uri points local');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	//#region このサーバーに既に登録されているか
 | 
						//#region このサーバーに既に登録されているか
 | 
				
			||||||
	const note = await Notes.findOne({ uri });
 | 
						const note = await Notes.findOneBy({ uri });
 | 
				
			||||||
	if (note == null) throw new Error('Question is not registed');
 | 
						if (note == null) throw new Error('Question is not registed');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const poll = await Polls.findOne({ noteId: note.id });
 | 
						const poll = await Polls.findOneBy({ noteId: note.id });
 | 
				
			||||||
	if (poll == null) throw new Error('Question is not registed');
 | 
						if (poll == null) throw new Error('Question is not registed');
 | 
				
			||||||
	//#endregion
 | 
						//#endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue