mirror of
https://github.com/kokonect-link/cherrypick
synced 2024-12-18 16:48:30 +09:00
Merge branch 'develop' into pr/10628
This commit is contained in:
commit
e29736b4b8
@ -165,8 +165,8 @@ proxyBypassHosts:
|
|||||||
# Media Proxy
|
# Media Proxy
|
||||||
#mediaProxy: https://example.com/proxy
|
#mediaProxy: https://example.com/proxy
|
||||||
|
|
||||||
# Proxy remote files (default: false)
|
# Proxy remote files (default: true)
|
||||||
#proxyRemoteFiles: true
|
proxyRemoteFiles: true
|
||||||
|
|
||||||
# Sign to ActivityPub GET request (default: true)
|
# Sign to ActivityPub GET request (default: true)
|
||||||
signToActivityPubGet: true
|
signToActivityPubGet: true
|
||||||
|
@ -159,6 +159,9 @@ id: 'aid'
|
|||||||
#deliverJobMaxAttempts: 12
|
#deliverJobMaxAttempts: 12
|
||||||
#inboxJobMaxAttempts: 8
|
#inboxJobMaxAttempts: 8
|
||||||
|
|
||||||
|
# Local address used for outgoing requests
|
||||||
|
#outgoingAddress: 127.0.0.1
|
||||||
|
|
||||||
# IP address family used for outgoing request (ipv4, ipv6 or dual)
|
# IP address family used for outgoing request (ipv4, ipv6 or dual)
|
||||||
#outgoingAddressFamily: ipv4
|
#outgoingAddressFamily: ipv4
|
||||||
|
|
||||||
@ -183,9 +186,9 @@ proxyBypassHosts:
|
|||||||
# * Perform image compression (on a different server resource than the main process)
|
# * Perform image compression (on a different server resource than the main process)
|
||||||
#mediaProxy: https://example.com/proxy
|
#mediaProxy: https://example.com/proxy
|
||||||
|
|
||||||
# Proxy remote files (default: false)
|
# Proxy remote files (default: true)
|
||||||
# Proxy remote files by this instance or mediaProxy to prevent remote files from running in remote domains.
|
# Proxy remote files by this instance or mediaProxy to prevent remote files from running in remote domains.
|
||||||
#proxyRemoteFiles: true
|
proxyRemoteFiles: true
|
||||||
|
|
||||||
# Movie Thumbnail Generation URL
|
# Movie Thumbnail Generation URL
|
||||||
# There is no reference implementation.
|
# There is no reference implementation.
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
"features": {
|
"features": {
|
||||||
"ghcr.io/devcontainers-contrib/features/pnpm:2": {},
|
"ghcr.io/devcontainers-contrib/features/pnpm:2": {},
|
||||||
"ghcr.io/devcontainers/features/node:1": {
|
"ghcr.io/devcontainers/features/node:1": {
|
||||||
"version": "20.3.1"
|
"version": "20.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"forwardPorts": [3000],
|
"forwardPorts": [3000],
|
||||||
|
@ -165,8 +165,8 @@ proxyBypassHosts:
|
|||||||
# Media Proxy
|
# Media Proxy
|
||||||
#mediaProxy: https://example.com/proxy
|
#mediaProxy: https://example.com/proxy
|
||||||
|
|
||||||
# Proxy remote files (default: false)
|
# Proxy remote files (default: true)
|
||||||
#proxyRemoteFiles: true
|
proxyRemoteFiles: true
|
||||||
|
|
||||||
# Sign to ActivityPub GET request (default: true)
|
# Sign to ActivityPub GET request (default: true)
|
||||||
signToActivityPubGet: true
|
signToActivityPubGet: true
|
||||||
|
3
.github/ISSUE_TEMPLATE/config.yml
vendored
3
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1,7 +1,4 @@
|
|||||||
contact_links:
|
contact_links:
|
||||||
- name: 👪 Misskey Forum
|
|
||||||
url: https://forum.misskey.io/
|
|
||||||
about: Ask questions and share knowledge
|
|
||||||
- name: 💬 Misskey official Discord
|
- name: 💬 Misskey official Discord
|
||||||
url: https://discord.gg/Wp8gVStHW3
|
url: https://discord.gg/Wp8gVStHW3
|
||||||
about: Chat freely about Misskey
|
about: Chat freely about Misskey
|
||||||
|
32
.github/dependabot.yml
vendored
32
.github/dependabot.yml
vendored
@ -9,24 +9,24 @@ updates:
|
|||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: daily
|
interval: daily
|
||||||
open-pull-requests-limit: 0
|
open-pull-requests-limit: 100
|
||||||
|
|
||||||
|
# Add only the root, not each workspace item
|
||||||
|
# https://github.com/dependabot/dependabot-core/issues/4993#issuecomment-1289133027
|
||||||
- package-ecosystem: npm
|
- package-ecosystem: npm
|
||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: daily
|
interval: daily
|
||||||
|
# PNPM has an issue with dependabot. See:
|
||||||
|
# https://github.com/dependabot/dependabot-core/issues/7258
|
||||||
|
# https://github.com/pnpm/pnpm/issues/6530
|
||||||
|
# TODO: Restore this when the issue is solved
|
||||||
open-pull-requests-limit: 0
|
open-pull-requests-limit: 0
|
||||||
- package-ecosystem: npm
|
groups:
|
||||||
directory: "/packages/backend"
|
swc:
|
||||||
schedule:
|
patterns:
|
||||||
interval: daily
|
- "@swc/*"
|
||||||
open-pull-requests-limit: 0
|
storybook:
|
||||||
- package-ecosystem: npm
|
patterns:
|
||||||
directory: "/packages/frontend"
|
- "storybook*"
|
||||||
schedule:
|
- "@storybook/*"
|
||||||
interval: daily
|
|
||||||
open-pull-requests-limit: 0
|
|
||||||
- package-ecosystem: npm
|
|
||||||
directory: "/packages/sw"
|
|
||||||
schedule:
|
|
||||||
interval: daily
|
|
||||||
open-pull-requests-limit: 0
|
|
||||||
|
4
.github/workflows/api-misskey-js.yml
vendored
4
.github/workflows/api-misskey-js.yml
vendored
@ -9,12 +9,12 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3.3.0
|
uses: actions/checkout@v3.5.3
|
||||||
|
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v3.6.0
|
uses: actions/setup-node@v3.8.1
|
||||||
with:
|
with:
|
||||||
node-version-file: '.node-version'
|
node-version-file: '.node-version'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
2
.github/workflows/check_copyright_year.yml
vendored
2
.github/workflows/check_copyright_year.yml
vendored
@ -10,7 +10,7 @@ jobs:
|
|||||||
check_copyright_year:
|
check_copyright_year:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3.2.0
|
- uses: actions/checkout@v3.5.3
|
||||||
- run: |
|
- run: |
|
||||||
if [ "$(grep Copyright COPYING | sed -e 's/.*2014-\([0-9]*\) .*/\1/g')" -ne "$(date +%Y)" ]; then
|
if [ "$(grep Copyright COPYING | sed -e 's/.*2014-\([0-9]*\) .*/\1/g')" -ne "$(date +%Y)" ]; then
|
||||||
echo "Please change copyright year!"
|
echo "Please change copyright year!"
|
||||||
|
4
.github/workflows/docker-develop.yml
vendored
4
.github/workflows/docker-develop.yml
vendored
@ -13,10 +13,10 @@ jobs:
|
|||||||
if: github.repository == 'misskey-dev/misskey'
|
if: github.repository == 'misskey-dev/misskey'
|
||||||
steps:
|
steps:
|
||||||
- name: Check out the repo
|
- name: Check out the repo
|
||||||
uses: actions/checkout@v3.3.0
|
uses: actions/checkout@v3.5.3
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
id: buildx
|
id: buildx
|
||||||
uses: docker/setup-buildx-action@v2.3.0
|
uses: docker/setup-buildx-action@v2.9.1
|
||||||
with:
|
with:
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
|
4
.github/workflows/docker.yml
vendored
4
.github/workflows/docker.yml
vendored
@ -12,10 +12,10 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out the repo
|
- name: Check out the repo
|
||||||
uses: actions/checkout@v3.3.0
|
uses: actions/checkout@v3.5.3
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
id: buildx
|
id: buildx
|
||||||
uses: docker/setup-buildx-action@v2.3.0
|
uses: docker/setup-buildx-action@v2.9.1
|
||||||
with:
|
with:
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
|
2
.github/workflows/dockle.yml
vendored
2
.github/workflows/dockle.yml
vendored
@ -14,7 +14,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
DOCKER_CONTENT_TRUST: 1
|
DOCKER_CONTENT_TRUST: 1
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3.2.0
|
- uses: actions/checkout@v3.5.3
|
||||||
- run: |
|
- run: |
|
||||||
curl -L -o dockle.deb "https://github.com/goodwithtech/dockle/releases/download/v0.4.10/dockle_0.4.10_Linux-64bit.deb"
|
curl -L -o dockle.deb "https://github.com/goodwithtech/dockle/releases/download/v0.4.10/dockle_0.4.10_Linux-64bit.deb"
|
||||||
sudo dpkg -i dockle.deb
|
sudo dpkg -i dockle.deb
|
||||||
|
12
.github/workflows/lint.yml
vendored
12
.github/workflows/lint.yml
vendored
@ -11,7 +11,7 @@ jobs:
|
|||||||
pnpm_install:
|
pnpm_install:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3.3.0
|
- uses: actions/checkout@v3.5.3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: true
|
submodules: true
|
||||||
@ -19,7 +19,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
version: 8
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
- uses: actions/setup-node@v3.6.0
|
- uses: actions/setup-node@v3.8.1
|
||||||
with:
|
with:
|
||||||
node-version-file: '.node-version'
|
node-version-file: '.node-version'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@ -38,7 +38,7 @@ jobs:
|
|||||||
- sw
|
- sw
|
||||||
- misskey-js
|
- misskey-js
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3.3.0
|
- uses: actions/checkout@v3.5.3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: true
|
submodules: true
|
||||||
@ -46,7 +46,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
version: 7
|
version: 7
|
||||||
run_install: false
|
run_install: false
|
||||||
- uses: actions/setup-node@v3.6.0
|
- uses: actions/setup-node@v3.8.1
|
||||||
with:
|
with:
|
||||||
node-version-file: '.node-version'
|
node-version-file: '.node-version'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@ -64,7 +64,7 @@ jobs:
|
|||||||
- backend
|
- backend
|
||||||
- misskey-js
|
- misskey-js
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3.3.0
|
- uses: actions/checkout@v3.5.3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: true
|
submodules: true
|
||||||
@ -72,7 +72,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
version: 7
|
version: 7
|
||||||
run_install: false
|
run_install: false
|
||||||
- uses: actions/setup-node@v3.6.0
|
- uses: actions/setup-node@v3.8.1
|
||||||
with:
|
with:
|
||||||
node-version-file: '.node-version'
|
node-version-file: '.node-version'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
2
.github/workflows/ok-to-test.yml
vendored
2
.github/workflows/ok-to-test.yml
vendored
@ -23,7 +23,7 @@ jobs:
|
|||||||
private_key: ${{ secrets.DEPLOYBOT_PRIVATE_KEY }}
|
private_key: ${{ secrets.DEPLOYBOT_PRIVATE_KEY }}
|
||||||
|
|
||||||
- name: Slash Command Dispatch
|
- name: Slash Command Dispatch
|
||||||
uses: peter-evans/slash-command-dispatch@v1
|
uses: peter-evans/slash-command-dispatch@v3
|
||||||
env:
|
env:
|
||||||
TOKEN: ${{ steps.generate_token.outputs.token }}
|
TOKEN: ${{ steps.generate_token.outputs.token }}
|
||||||
with:
|
with:
|
||||||
|
2
.github/workflows/pr-preview-deploy.yml
vendored
2
.github/workflows/pr-preview-deploy.yml
vendored
@ -53,7 +53,7 @@ jobs:
|
|||||||
|
|
||||||
# Check out merge commit
|
# Check out merge commit
|
||||||
- name: Fork based /deploy checkout
|
- name: Fork based /deploy checkout
|
||||||
uses: actions/checkout@v3.3.0
|
uses: actions/checkout@v3.5.3
|
||||||
with:
|
with:
|
||||||
ref: 'refs/pull/${{ github.event.client_payload.pull_request.number }}/merge'
|
ref: 'refs/pull/${{ github.event.client_payload.pull_request.number }}/merge'
|
||||||
|
|
||||||
|
4
.github/workflows/reviewer_lottery.yml
vendored
4
.github/workflows/reviewer_lottery.yml
vendored
@ -7,7 +7,7 @@ jobs:
|
|||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v3.5.3
|
||||||
- uses: uesteibar/reviewer-lottery@v2
|
- uses: uesteibar/reviewer-lottery@v3
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
6
.github/workflows/storybook.yml
vendored
6
.github/workflows/storybook.yml
vendored
@ -15,12 +15,12 @@ jobs:
|
|||||||
NODE_OPTIONS: "--max_old_space_size=7168"
|
NODE_OPTIONS: "--max_old_space_size=7168"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3.3.0
|
- uses: actions/checkout@v3.5.3
|
||||||
if: github.event_name != 'pull_request_target'
|
if: github.event_name != 'pull_request_target'
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: true
|
submodules: true
|
||||||
- uses: actions/checkout@v3.3.0
|
- uses: actions/checkout@v3.5.3
|
||||||
if: github.event_name == 'pull_request_target'
|
if: github.event_name == 'pull_request_target'
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
@ -38,7 +38,7 @@ jobs:
|
|||||||
version: 8
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
- name: Use Node.js 20.x
|
- name: Use Node.js 20.x
|
||||||
uses: actions/setup-node@v3.6.0
|
uses: actions/setup-node@v3.8.1
|
||||||
with:
|
with:
|
||||||
node-version-file: '.node-version'
|
node-version-file: '.node-version'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
4
.github/workflows/test-backend.yml
vendored
4
.github/workflows/test-backend.yml
vendored
@ -29,7 +29,7 @@ jobs:
|
|||||||
- 56312:6379
|
- 56312:6379
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3.3.0
|
- uses: actions/checkout@v3.5.3
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
@ -38,7 +38,7 @@ jobs:
|
|||||||
version: 8
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v3.6.0
|
uses: actions/setup-node@v3.8.1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
8
.github/workflows/test-frontend.yml
vendored
8
.github/workflows/test-frontend.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
|||||||
node-version: [20.x]
|
node-version: [20.x]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3.3.0
|
- uses: actions/checkout@v3.5.3
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
@ -25,7 +25,7 @@ jobs:
|
|||||||
version: 8
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v3.6.0
|
uses: actions/setup-node@v3.8.1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@ -68,7 +68,7 @@ jobs:
|
|||||||
- 56312:6379
|
- 56312:6379
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3.3.0
|
- uses: actions/checkout@v3.5.3
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
# https://github.com/cypress-io/cypress-docker-images/issues/150
|
# https://github.com/cypress-io/cypress-docker-images/issues/150
|
||||||
@ -83,7 +83,7 @@ jobs:
|
|||||||
version: 7
|
version: 7
|
||||||
run_install: false
|
run_install: false
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v3.6.0
|
uses: actions/setup-node@v3.8.1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
4
.github/workflows/test-misskey-js.yml
vendored
4
.github/workflows/test-misskey-js.yml
vendored
@ -21,12 +21,12 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3.3.0
|
uses: actions/checkout@v3.5.3
|
||||||
|
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
|
|
||||||
- name: Setup Node.js ${{ matrix.node-version }}
|
- name: Setup Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v3.6.0
|
uses: actions/setup-node@v3.8.1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
4
.github/workflows/test-production.yml
vendored
4
.github/workflows/test-production.yml
vendored
@ -19,7 +19,7 @@ jobs:
|
|||||||
node-version: [20.x]
|
node-version: [20.x]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3.3.0
|
- uses: actions/checkout@v3.5.3
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
@ -28,7 +28,7 @@ jobs:
|
|||||||
version: 8
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v3.6.0
|
uses: actions/setup-node@v3.8.1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
@ -1 +1 @@
|
|||||||
20.3.1
|
20.5.0
|
||||||
|
23
CHANGELOG.md
23
CHANGELOG.md
@ -16,12 +16,30 @@
|
|||||||
|
|
||||||
### General
|
### General
|
||||||
- OAuth 2.0のサポート
|
- OAuth 2.0のサポート
|
||||||
|
- お知らせ機能の強化
|
||||||
|
- ユーザー個別のお知らせを作成可能に
|
||||||
|
- お知らせのバナー表示やダイアログ表示が可能に
|
||||||
|
- お知らせのアイコンを設定可能に
|
||||||
|
- チャンネルをセンシティブ指定できるようになりました
|
||||||
|
|
||||||
### Client
|
### Client
|
||||||
-
|
- メニューのスイッチの動作を改善
|
||||||
|
- 絵文字ピッカーの検索の表示件数を100件に増加
|
||||||
|
- Enhance: ユーザーメニューでスイッチでユーザーリストに追加・削除できるように
|
||||||
|
- Enhance: 自分が押したリアクションのデザインを改善
|
||||||
|
- Fix: サーバー情報画面(`/instance-info/{domain}`)でブロックができないのを修正
|
||||||
|
- Fix: 未読のお知らせの「わかった」をクリック・タップしてもその場で「わかった」が消えない問題を修正
|
||||||
|
- Fix: iOSで画面を回転させるとテキストサイズが変わる問題を修正
|
||||||
|
- Fix: word mute for sub note is not applied
|
||||||
|
- Fix: タイムラインを下にスクロールしてノート画面に移動して再び戻ったら以前のスクロール位置を失う問題を修正
|
||||||
|
- Fix: Misskeyプラグインをインストールする際のAiScriptバージョンのチェックが0.14.0以降に対応していない問題を修正
|
||||||
|
|
||||||
### Server
|
### Server
|
||||||
-
|
- cacheRemoteFilesの初期値はfalseになりました
|
||||||
|
- ファイルアップロード時等にファイル名の拡張子を修正する関数(correctFilename)の挙動を改善
|
||||||
|
- Fix: 一部のfeatured noteを照会できない問題を修正
|
||||||
|
- Fix: muteがapiからのuser list timeline取得で機能しない問題を修正
|
||||||
|
- Fix: ジョブキュー管理画面の認証を回避できる問題を修正
|
||||||
|
|
||||||
## 13.14.2
|
## 13.14.2
|
||||||
|
|
||||||
@ -36,6 +54,7 @@
|
|||||||
### Server
|
### Server
|
||||||
- Fix: APIのオフセットが壊れていたせいで「もっと見る」でもっと見れない問題を修正
|
- Fix: APIのオフセットが壊れていたせいで「もっと見る」でもっと見れない問題を修正
|
||||||
- Fix: 外部サーバーの投稿がタイムラインに表示されないことがある問題を修正
|
- Fix: 外部サーバーの投稿がタイムラインに表示されないことがある問題を修正
|
||||||
|
- Enhance: Add address bind config option (outgoingAddress)
|
||||||
|
|
||||||
## 13.14.1
|
## 13.14.1
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# syntax = docker/dockerfile:1.4
|
# syntax = docker/dockerfile:1.4
|
||||||
|
|
||||||
ARG NODE_VERSION=20.3.1-bullseye
|
ARG NODE_VERSION=20.5.0-bullseye
|
||||||
|
|
||||||
# build assets & compile TypeScript
|
# build assets & compile TypeScript
|
||||||
|
|
||||||
|
17
locales/index.d.ts
vendored
17
locales/index.d.ts
vendored
@ -159,6 +159,7 @@ export interface Locale {
|
|||||||
"settingGuide": string;
|
"settingGuide": string;
|
||||||
"cacheRemoteFiles": string;
|
"cacheRemoteFiles": string;
|
||||||
"cacheRemoteFilesDescription": string;
|
"cacheRemoteFilesDescription": string;
|
||||||
|
"youCanCleanRemoteFilesCache": string;
|
||||||
"cacheRemoteSensitiveFiles": string;
|
"cacheRemoteSensitiveFiles": string;
|
||||||
"cacheRemoteSensitiveFilesDescription": string;
|
"cacheRemoteSensitiveFilesDescription": string;
|
||||||
"flagAsBot": string;
|
"flagAsBot": string;
|
||||||
@ -1100,6 +1101,22 @@ export interface Locale {
|
|||||||
"doYouAgree": string;
|
"doYouAgree": string;
|
||||||
"beSureToReadThisAsItIsImportant": string;
|
"beSureToReadThisAsItIsImportant": string;
|
||||||
"iHaveReadXCarefullyAndAgree": string;
|
"iHaveReadXCarefullyAndAgree": string;
|
||||||
|
"dialog": string;
|
||||||
|
"icon": string;
|
||||||
|
"forYou": string;
|
||||||
|
"currentAnnouncements": string;
|
||||||
|
"pastAnnouncements": string;
|
||||||
|
"youHaveUnreadAnnouncements": string;
|
||||||
|
"_announcement": {
|
||||||
|
"forExistingUsers": string;
|
||||||
|
"forExistingUsersDescription": string;
|
||||||
|
"needConfirmationToRead": string;
|
||||||
|
"needConfirmationToReadDescription": string;
|
||||||
|
"end": string;
|
||||||
|
"tooManyActiveAnnouncementDescription": string;
|
||||||
|
"readConfirmTitle": string;
|
||||||
|
"readConfirmText": string;
|
||||||
|
};
|
||||||
"_initialAccountSetting": {
|
"_initialAccountSetting": {
|
||||||
"accountCreated": string;
|
"accountCreated": string;
|
||||||
"letsStartAccountSetup": string;
|
"letsStartAccountSetup": string;
|
||||||
|
@ -74,7 +74,7 @@ import: "インポート"
|
|||||||
export: "エクスポート"
|
export: "エクスポート"
|
||||||
files: "ファイル"
|
files: "ファイル"
|
||||||
download: "ダウンロード"
|
download: "ダウンロード"
|
||||||
driveFileDeleteConfirm: "ファイル「{name}」を削除しますか?このファイルを使用した全てのコンテンツからも削除されます。"
|
driveFileDeleteConfirm: "ファイル「{name}」を削除しますか?このファイルを使用した一部のコンテンツも削除されます。"
|
||||||
unfollowConfirm: "{name}のフォローを解除しますか?"
|
unfollowConfirm: "{name}のフォローを解除しますか?"
|
||||||
exportRequested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、「ドライブ」に追加されます。"
|
exportRequested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、「ドライブ」に追加されます。"
|
||||||
importRequested: "インポートをリクエストしました。これには時間がかかる場合があります。"
|
importRequested: "インポートをリクエストしました。これには時間がかかる場合があります。"
|
||||||
@ -155,7 +155,8 @@ emojiUrl: "絵文字画像URL"
|
|||||||
addEmoji: "絵文字を追加"
|
addEmoji: "絵文字を追加"
|
||||||
settingGuide: "おすすめ設定"
|
settingGuide: "おすすめ設定"
|
||||||
cacheRemoteFiles: "リモートのファイルをキャッシュする"
|
cacheRemoteFiles: "リモートのファイルをキャッシュする"
|
||||||
cacheRemoteFilesDescription: "この設定を無効にすると、リモートファイルをキャッシュせず直リンクするようになります。サーバーのストレージを節約できますが、サムネイルが生成されないので通信量が増加します。"
|
cacheRemoteFilesDescription: "この設定を有効にすると、リモートファイルをこのサーバーのストレージにキャッシュするようになります。画像の表示が高速になりますが、サーバーのストレージを多く消費します。リモートユーザーがどれほどキャッシュを保持するかは、ロールによるドライブ容量制限によって決定されます。この制限を超えた場合、古いファイルからキャッシュが削除されリンクになります。この設定が無効の場合、リモートのファイルを最初からリンクとして保持しますが、画像のサムネイル生成やユーザーのプライバシー保護のために、default.ymlでproxyRemoteFilesをtrueにすることをお勧めします。"
|
||||||
|
youCanCleanRemoteFilesCache: "ファイル管理の🗑️ボタンで全てのキャッシュを削除できます。"
|
||||||
cacheRemoteSensitiveFiles: "リモートのセンシティブなファイルをキャッシュする"
|
cacheRemoteSensitiveFiles: "リモートのセンシティブなファイルをキャッシュする"
|
||||||
cacheRemoteSensitiveFilesDescription: "この設定を無効にすると、リモートのセンシティブなファイルはキャッシュせず直リンクするようになります。"
|
cacheRemoteSensitiveFilesDescription: "この設定を無効にすると、リモートのセンシティブなファイルはキャッシュせず直リンクするようになります。"
|
||||||
flagAsBot: "Botとして設定"
|
flagAsBot: "Botとして設定"
|
||||||
@ -329,7 +330,7 @@ watch: "ウォッチ"
|
|||||||
unwatch: "ウォッチ解除"
|
unwatch: "ウォッチ解除"
|
||||||
accept: "許可"
|
accept: "許可"
|
||||||
reject: "拒否"
|
reject: "拒否"
|
||||||
normal: "正常"
|
normal: "通常"
|
||||||
instanceName: "サーバー名"
|
instanceName: "サーバー名"
|
||||||
instanceDescription: "サーバーの紹介"
|
instanceDescription: "サーバーの紹介"
|
||||||
maintainerName: "管理者の名前"
|
maintainerName: "管理者の名前"
|
||||||
@ -1097,6 +1098,22 @@ expired: "期限切れ"
|
|||||||
doYouAgree: "同意しますか?"
|
doYouAgree: "同意しますか?"
|
||||||
beSureToReadThisAsItIsImportant: "重要ですので必ずお読みください。"
|
beSureToReadThisAsItIsImportant: "重要ですので必ずお読みください。"
|
||||||
iHaveReadXCarefullyAndAgree: "「{x}」の内容をよく読み、同意します。"
|
iHaveReadXCarefullyAndAgree: "「{x}」の内容をよく読み、同意します。"
|
||||||
|
dialog: "ダイアログ"
|
||||||
|
icon: "アイコン"
|
||||||
|
forYou: "あなたへ"
|
||||||
|
currentAnnouncements: "現在のお知らせ"
|
||||||
|
pastAnnouncements: "過去のお知らせ"
|
||||||
|
youHaveUnreadAnnouncements: "未読のお知らせがあります。"
|
||||||
|
|
||||||
|
_announcement:
|
||||||
|
forExistingUsers: "既存ユーザーのみ"
|
||||||
|
forExistingUsersDescription: "有効にすると、このお知らせ作成時点で存在するユーザーにのみお知らせが表示されます。無効にすると、このお知らせ作成後にアカウントを作成したユーザーにもお知らせが表示されます。"
|
||||||
|
needConfirmationToRead: "既読にするのに確認が必要"
|
||||||
|
needConfirmationToReadDescription: "有効にすると、このお知らせを既読にする際に確認ダイアログが表示されます。また、一括既読操作の対象になりません。"
|
||||||
|
end: "お知らせを終了"
|
||||||
|
tooManyActiveAnnouncementDescription: "アクティブなお知らせが多いため、UXが低下する可能性があります。終了したお知らせはアーカイブすることを検討してください。"
|
||||||
|
readConfirmTitle: "既読にしますか?"
|
||||||
|
readConfirmText: "「{title}」の内容を読み、既読にします。"
|
||||||
|
|
||||||
_initialAccountSetting:
|
_initialAccountSetting:
|
||||||
accountCreated: "アカウントの作成が完了しました!"
|
accountCreated: "アカウントの作成が完了しました!"
|
||||||
|
14
package.json
14
package.json
@ -44,7 +44,7 @@
|
|||||||
"lodash": "4.17.21"
|
"lodash": "4.17.21"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"execa": "7.1.1",
|
"execa": "7.2.0",
|
||||||
"gulp": "4.0.2",
|
"gulp": "4.0.2",
|
||||||
"gulp-cssnano": "2.1.3",
|
"gulp-cssnano": "2.1.3",
|
||||||
"gulp-rename": "2.0.0",
|
"gulp-rename": "2.0.0",
|
||||||
@ -54,13 +54,13 @@
|
|||||||
"typescript": "5.1.6"
|
"typescript": "5.1.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/gulp": "4.0.10",
|
"@types/gulp": "4.0.13",
|
||||||
"@types/gulp-rename": "2.0.1",
|
"@types/gulp-rename": "2.0.2",
|
||||||
"@typescript-eslint/eslint-plugin": "5.61.0",
|
"@typescript-eslint/eslint-plugin": "6.2.0",
|
||||||
"@typescript-eslint/parser": "5.61.0",
|
"@typescript-eslint/parser": "6.2.0",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"cypress": "12.17.1",
|
"cypress": "12.17.2",
|
||||||
"eslint": "8.45.0",
|
"eslint": "8.46.0",
|
||||||
"start-server-and-test": "2.0.0"
|
"start-server-and-test": "2.0.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
|
@ -16,5 +16,9 @@ export class addRenoteMuting1665091090561 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async down(queryRunner) {
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`DROP INDEX "IDX_renote_muting_muterId"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "IDX_renote_muting_muteeId"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "IDX_renote_muting_createdAt"`);
|
||||||
|
await queryRunner.query(`DROP TABLE "renote_muting"`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
export class FixRenoteMuting1690417561185 {
|
||||||
|
name = 'FixRenoteMuting1690417561185'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`DELETE FROM "renote_muting" WHERE "muteeId" NOT IN (SELECT "id" FROM "user")`);
|
||||||
|
await queryRunner.query(`DELETE FROM "renote_muting" WHERE "muterId" NOT IN (SELECT "id" FROM "user")`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
export class ChangeCacheRemoteFilesDefault1690417561186 {
|
||||||
|
name = 'ChangeCacheRemoteFilesDefault1690417561186'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "cacheRemoteFiles" SET DEFAULT false`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "cacheRemoteFiles" SET DEFAULT true`);
|
||||||
|
}
|
||||||
|
}
|
81
packages/backend/migration/1690417561187-Fix.js
Normal file
81
packages/backend/migration/1690417561187-Fix.js
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
export class Fix1690417561187 {
|
||||||
|
name = 'Fix1690417561187'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_2cd3b2a6b4cf0b910b260afe08"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_renote_muting_createdAt"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_renote_muting_muteeId"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_renote_muting_muterId"`);
|
||||||
|
await queryRunner.query(`COMMENT ON COLUMN "user"."isRoot" IS 'Whether the User is the root.'`);
|
||||||
|
await queryRunner.query(`COMMENT ON COLUMN "ad"."startsAt" IS 'The expired date of the Ad.'`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "antenna" ALTER COLUMN "lastUsedAt" DROP DEFAULT`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "mascotImageUrl" DROP DEFAULT`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "preservedUsernames" SET DEFAULT '{ "admin", "administrator", "root", "system", "maintainer", "host", "mod", "moderator", "owner", "superuser", "staff", "auth", "i", "me", "everyone", "all", "mention", "mentions", "example", "user", "users", "account", "accounts", "official", "help", "helps", "support", "supports", "info", "information", "informations", "announce", "announces", "announcement", "announcements", "notice", "notification", "notifications", "dev", "developer", "developers", "tech", "misskey" }'`);
|
||||||
|
await queryRunner.query(`COMMENT ON COLUMN "renote_muting"."createdAt" IS 'The created date of the Muting.'`);
|
||||||
|
await queryRunner.query(`COMMENT ON COLUMN "renote_muting"."muteeId" IS 'The mutee user ID.'`);
|
||||||
|
await queryRunner.query(`COMMENT ON COLUMN "renote_muting"."muterId" IS 'The muter user ID.'`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "poll" DROP CONSTRAINT "FK_da851e06d0dfe2ef397d8b1bf1b"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "poll" DROP CONSTRAINT "UQ_da851e06d0dfe2ef397d8b1bf1b"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "promo_note" DROP CONSTRAINT "FK_e263909ca4fe5d57f8d4230dd5c"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "promo_note" DROP CONSTRAINT "UQ_e263909ca4fe5d57f8d4230dd5c"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_keypair" DROP CONSTRAINT "FK_f4853eb41ab722fe05f81cedeb6"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_keypair" DROP CONSTRAINT "UQ_f4853eb41ab722fe05f81cedeb6"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_profile" DROP CONSTRAINT "FK_51cb79b5555effaf7d69ba1cff9"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_profile" DROP CONSTRAINT "UQ_51cb79b5555effaf7d69ba1cff9"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "FK_10c146e4b39b443ede016f6736d"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "UQ_10c146e4b39b443ede016f6736d"`);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_3fcc2c589eaefc205e0714b99c" ON "ad" ("startsAt") `);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_c71faf11f0a28a5c0bb506203c" ON "channel_favorite" ("userId", "channelId") `);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_f7b9d338207e40e768e4a5265a" ON "instance" ("firstRetrievedAt") `);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_d1259a2c2b7bb413ff449e8711" ON "renote_muting" ("createdAt") `);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_7eac97594bcac5ffcf2068089b" ON "renote_muting" ("muteeId") `);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_7aa72a5fe76019bfe8e5e0e8b7" ON "renote_muting" ("muterId") `);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_0d801c609cec4e9eb4b6b4490c" ON "renote_muting" ("muterId", "muteeId") `);
|
||||||
|
await queryRunner.query(`ALTER TABLE "renote_muting" ADD CONSTRAINT "FK_7eac97594bcac5ffcf2068089b6" FOREIGN KEY ("muteeId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "renote_muting" ADD CONSTRAINT "FK_7aa72a5fe76019bfe8e5e0e8b7d" FOREIGN KEY ("muterId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "poll" ADD CONSTRAINT "FK_da851e06d0dfe2ef397d8b1bf1b" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "promo_note" ADD CONSTRAINT "FK_e263909ca4fe5d57f8d4230dd5c" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_keypair" ADD CONSTRAINT "FK_f4853eb41ab722fe05f81cedeb6" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_profile" ADD CONSTRAINT "FK_51cb79b5555effaf7d69ba1cff9" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_publickey" ADD CONSTRAINT "FK_10c146e4b39b443ede016f6736d" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "FK_10c146e4b39b443ede016f6736d"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_profile" DROP CONSTRAINT "FK_51cb79b5555effaf7d69ba1cff9"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_keypair" DROP CONSTRAINT "FK_f4853eb41ab722fe05f81cedeb6"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "promo_note" DROP CONSTRAINT "FK_e263909ca4fe5d57f8d4230dd5c"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "poll" DROP CONSTRAINT "FK_da851e06d0dfe2ef397d8b1bf1b"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "renote_muting" DROP CONSTRAINT "FK_7aa72a5fe76019bfe8e5e0e8b7d"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "renote_muting" DROP CONSTRAINT "FK_7eac97594bcac5ffcf2068089b6"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_0d801c609cec4e9eb4b6b4490c"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_7aa72a5fe76019bfe8e5e0e8b7"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_7eac97594bcac5ffcf2068089b"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_d1259a2c2b7bb413ff449e8711"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_f7b9d338207e40e768e4a5265a"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_c71faf11f0a28a5c0bb506203c"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_3fcc2c589eaefc205e0714b99c"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_publickey" ADD CONSTRAINT "UQ_10c146e4b39b443ede016f6736d" UNIQUE ("userId")`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_publickey" ADD CONSTRAINT "FK_10c146e4b39b443ede016f6736d" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_profile" ADD CONSTRAINT "UQ_51cb79b5555effaf7d69ba1cff9" UNIQUE ("userId")`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_profile" ADD CONSTRAINT "FK_51cb79b5555effaf7d69ba1cff9" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_keypair" ADD CONSTRAINT "UQ_f4853eb41ab722fe05f81cedeb6" UNIQUE ("userId")`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_keypair" ADD CONSTRAINT "FK_f4853eb41ab722fe05f81cedeb6" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "promo_note" ADD CONSTRAINT "UQ_e263909ca4fe5d57f8d4230dd5c" UNIQUE ("noteId")`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "promo_note" ADD CONSTRAINT "FK_e263909ca4fe5d57f8d4230dd5c" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "poll" ADD CONSTRAINT "UQ_da851e06d0dfe2ef397d8b1bf1b" UNIQUE ("noteId")`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "poll" ADD CONSTRAINT "FK_da851e06d0dfe2ef397d8b1bf1b" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||||
|
await queryRunner.query(`COMMENT ON COLUMN "renote_muting"."muterId" IS NULL`);
|
||||||
|
await queryRunner.query(`COMMENT ON COLUMN "renote_muting"."muteeId" IS NULL`);
|
||||||
|
await queryRunner.query(`COMMENT ON COLUMN "renote_muting"."createdAt" IS NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "preservedUsernames" SET DEFAULT '{admin,administrator,root,system,maintainer,host,mod,moderator,owner,superuser,staff,auth,i,me,everyone,all,mention,mentions,example,user,users,account,accounts,official,help,helps,support,supports,info,information,informations,announce,announces,announcement,announcements,notice,notification,notifications,dev,developer,developers,tech,misskey}'`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "mascotImageUrl" SET DEFAULT '/assets/ai.png'`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "antenna" ALTER COLUMN "lastUsedAt" SET DEFAULT '2023-04-25 06:51:20.985478+00'`);
|
||||||
|
await queryRunner.query(`COMMENT ON COLUMN "ad"."startsAt" IS NULL`);
|
||||||
|
await queryRunner.query(`COMMENT ON COLUMN "user"."isRoot" IS 'Whether the User is the admin.'`);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_renote_muting_muterId" ON "muting" ("muterId") `);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_renote_muting_muteeId" ON "muting" ("muteeId") `);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_renote_muting_createdAt" ON "muting" ("createdAt") `);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_2cd3b2a6b4cf0b910b260afe08" ON "instance" ("firstRetrievedAt") `);
|
||||||
|
}
|
||||||
|
}
|
17
packages/backend/migration/1690782653311-SensitiveChannel.js
Normal file
17
packages/backend/migration/1690782653311-SensitiveChannel.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class SensitiveChannel1690782653311 {
|
||||||
|
name = 'SensitiveChannel1690782653311'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "channel"
|
||||||
|
ADD "isSensitive" boolean NOT NULL DEFAULT false`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "channel" DROP COLUMN "isSensitive"`);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
export class RefineAnnouncement1691649257651 {
|
||||||
|
name = 'RefineAnnouncement1691649257651'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "announcement" ADD "display" character varying(256) NOT NULL DEFAULT 'normal'`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "announcement" ADD "needConfirmationToRead" boolean NOT NULL DEFAULT false`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "announcement" ADD "isActive" boolean NOT NULL DEFAULT true`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "announcement" ADD "forExistingUsers" boolean NOT NULL DEFAULT false`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "announcement" ADD "userId" character varying(32)`);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_bc1afcc8ef7e9400cdc3c0a87e" ON "announcement" ("isActive") `);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_da795d3a83187e8832005ba19d" ON "announcement" ("forExistingUsers") `);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_fd25dfe3da37df1715f11ba6ec" ON "announcement" ("userId") `);
|
||||||
|
await queryRunner.query(`ALTER TABLE "announcement" ADD CONSTRAINT "FK_fd25dfe3da37df1715f11ba6ec8" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "announcement" DROP CONSTRAINT "FK_fd25dfe3da37df1715f11ba6ec8"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_fd25dfe3da37df1715f11ba6ec"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_da795d3a83187e8832005ba19d"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_bc1afcc8ef7e9400cdc3c0a87e"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "announcement" DROP COLUMN "userId"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "announcement" DROP COLUMN "forExistingUsers"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "announcement" DROP COLUMN "isActive"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "announcement" DROP COLUMN "needConfirmationToRead"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "announcement" DROP COLUMN "display"`);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
export class RefineAnnouncement21691657412740 {
|
||||||
|
name = 'RefineAnnouncement21691657412740'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "announcement" ADD "icon" character varying(256) NOT NULL DEFAULT 'info'`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "announcement" DROP COLUMN "icon"`);
|
||||||
|
}
|
||||||
|
}
|
@ -59,25 +59,25 @@
|
|||||||
"@aws-sdk/client-s3": "3.367.0",
|
"@aws-sdk/client-s3": "3.367.0",
|
||||||
"@aws-sdk/lib-storage": "3.367.0",
|
"@aws-sdk/lib-storage": "3.367.0",
|
||||||
"@aws-sdk/node-http-handler": "3.360.0",
|
"@aws-sdk/node-http-handler": "3.360.0",
|
||||||
"@bull-board/api": "5.6.1",
|
"@bull-board/api": "5.7.1",
|
||||||
"@bull-board/fastify": "5.6.1",
|
"@bull-board/fastify": "5.7.1",
|
||||||
"@bull-board/ui": "5.6.1",
|
"@bull-board/ui": "5.7.1",
|
||||||
"@discordapp/twemoji": "14.1.2",
|
"@discordapp/twemoji": "14.1.2",
|
||||||
"@fastify/accepts": "4.2.0",
|
"@fastify/accepts": "4.2.0",
|
||||||
"@fastify/cookie": "8.3.0",
|
"@fastify/cookie": "8.3.0",
|
||||||
"@fastify/cors": "8.3.0",
|
"@fastify/cors": "8.3.0",
|
||||||
"@fastify/express": "2.3.0",
|
"@fastify/express": "2.3.0",
|
||||||
"@fastify/http-proxy": "9.2.1",
|
"@fastify/http-proxy": "9.2.1",
|
||||||
"@fastify/multipart": "7.7.1",
|
"@fastify/multipart": "7.7.3",
|
||||||
"@fastify/static": "6.10.2",
|
"@fastify/static": "6.10.2",
|
||||||
"@fastify/view": "8.0.0",
|
"@fastify/view": "8.0.0",
|
||||||
"@nestjs/common": "10.1.0",
|
"@nestjs/common": "10.1.2",
|
||||||
"@nestjs/core": "10.1.0",
|
"@nestjs/core": "10.1.2",
|
||||||
"@nestjs/testing": "10.1.0",
|
"@nestjs/testing": "10.1.2",
|
||||||
"@peertube/http-signature": "1.7.0",
|
"@peertube/http-signature": "1.7.0",
|
||||||
"@sinonjs/fake-timers": "10.3.0",
|
"@sinonjs/fake-timers": "10.3.0",
|
||||||
"@swc/cli": "0.1.62",
|
"@swc/cli": "0.1.62",
|
||||||
"@swc/core": "1.3.70",
|
"@swc/core": "1.3.72",
|
||||||
"accepts": "1.3.8",
|
"accepts": "1.3.8",
|
||||||
"ajv": "8.12.0",
|
"ajv": "8.12.0",
|
||||||
"archiver": "5.3.1",
|
"archiver": "5.3.1",
|
||||||
@ -85,9 +85,9 @@
|
|||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"blurhash": "2.0.5",
|
"blurhash": "2.0.5",
|
||||||
"body-parser": "1.20.2",
|
"body-parser": "1.20.2",
|
||||||
"bullmq": "4.4.0",
|
"bullmq": "4.6.3",
|
||||||
"cacheable-lookup": "7.0.0",
|
"cacheable-lookup": "7.0.0",
|
||||||
"cbor": "9.0.0",
|
"cbor": "9.0.1",
|
||||||
"chalk": "5.3.0",
|
"chalk": "5.3.0",
|
||||||
"chalk-template": "1.1.0",
|
"chalk-template": "1.1.0",
|
||||||
"chokidar": "3.5.3",
|
"chokidar": "3.5.3",
|
||||||
@ -96,7 +96,7 @@
|
|||||||
"content-disposition": "0.5.4",
|
"content-disposition": "0.5.4",
|
||||||
"date-fns": "2.30.0",
|
"date-fns": "2.30.0",
|
||||||
"deep-email-validator": "0.1.21",
|
"deep-email-validator": "0.1.21",
|
||||||
"fastify": "4.20.0",
|
"fastify": "4.21.0",
|
||||||
"feed": "4.2.2",
|
"feed": "4.2.2",
|
||||||
"file-type": "18.5.0",
|
"file-type": "18.5.0",
|
||||||
"fluent-ffmpeg": "2.1.2",
|
"fluent-ffmpeg": "2.1.2",
|
||||||
@ -104,7 +104,7 @@
|
|||||||
"got": "13.0.0",
|
"got": "13.0.0",
|
||||||
"happy-dom": "10.0.3",
|
"happy-dom": "10.0.3",
|
||||||
"hpagent": "1.2.0",
|
"hpagent": "1.2.0",
|
||||||
"http-link-header": "1.1.0",
|
"http-link-header": "1.1.1",
|
||||||
"ioredis": "5.3.2",
|
"ioredis": "5.3.2",
|
||||||
"ip-cidr": "3.1.0",
|
"ip-cidr": "3.1.0",
|
||||||
"ipaddr.js": "2.1.0",
|
"ipaddr.js": "2.1.0",
|
||||||
@ -121,14 +121,14 @@
|
|||||||
"misskey-js": "workspace:*",
|
"misskey-js": "workspace:*",
|
||||||
"ms": "3.0.0-canary.1",
|
"ms": "3.0.0-canary.1",
|
||||||
"nested-property": "4.0.0",
|
"nested-property": "4.0.0",
|
||||||
"node-fetch": "3.3.1",
|
"node-fetch": "3.3.2",
|
||||||
"nodemailer": "6.9.3",
|
"nodemailer": "6.9.4",
|
||||||
"nsfwjs": "2.4.2",
|
"nsfwjs": "2.4.2",
|
||||||
"oauth": "0.10.0",
|
"oauth": "0.10.0",
|
||||||
"oauth2orize": "1.11.1",
|
"oauth2orize": "1.11.1",
|
||||||
"oauth2orize-pkce": "0.1.2",
|
"oauth2orize-pkce": "0.1.2",
|
||||||
"os-utils": "0.0.14",
|
"os-utils": "0.0.14",
|
||||||
"otpauth": "9.1.3",
|
"otpauth": "9.1.4",
|
||||||
"parse5": "7.1.2",
|
"parse5": "7.1.2",
|
||||||
"pg": "8.11.1",
|
"pg": "8.11.1",
|
||||||
"pkce-challenge": "4.0.1",
|
"pkce-challenge": "4.0.1",
|
||||||
@ -140,21 +140,20 @@
|
|||||||
"qrcode": "1.5.3",
|
"qrcode": "1.5.3",
|
||||||
"random-seed": "0.3.0",
|
"random-seed": "0.3.0",
|
||||||
"ratelimiter": "3.4.1",
|
"ratelimiter": "3.4.1",
|
||||||
"re2": "1.19.1",
|
"re2": "1.20.1",
|
||||||
"redis-lock": "0.1.4",
|
"redis-lock": "0.1.4",
|
||||||
"reflect-metadata": "0.1.13",
|
"reflect-metadata": "0.1.13",
|
||||||
"rename": "1.0.4",
|
"rename": "1.0.4",
|
||||||
"rss-parser": "3.13.0",
|
"rss-parser": "3.13.0",
|
||||||
"rxjs": "7.8.1",
|
"rxjs": "7.8.1",
|
||||||
"sanitize-html": "2.11.0",
|
"sanitize-html": "2.11.0",
|
||||||
"semver": "7.5.4",
|
"sharp": "0.32.4",
|
||||||
"sharp": "0.32.3",
|
|
||||||
"sharp-read-bmp": "github:misskey-dev/sharp-read-bmp",
|
"sharp-read-bmp": "github:misskey-dev/sharp-read-bmp",
|
||||||
"slacc": "0.0.10",
|
"slacc": "0.0.10",
|
||||||
"strict-event-emitter-types": "2.0.0",
|
"strict-event-emitter-types": "2.0.0",
|
||||||
"stringz": "2.1.0",
|
"stringz": "2.1.0",
|
||||||
"summaly": "github:misskey-dev/summaly",
|
"summaly": "github:misskey-dev/summaly",
|
||||||
"systeminformation": "5.18.7",
|
"systeminformation": "5.18.9",
|
||||||
"tinycolor2": "1.6.0",
|
"tinycolor2": "1.6.0",
|
||||||
"tmp": "0.2.1",
|
"tmp": "0.2.1",
|
||||||
"tsc-alias": "1.8.7",
|
"tsc-alias": "1.8.7",
|
||||||
@ -164,13 +163,13 @@
|
|||||||
"typescript": "5.1.6",
|
"typescript": "5.1.6",
|
||||||
"ulid": "2.3.0",
|
"ulid": "2.3.0",
|
||||||
"vary": "1.1.2",
|
"vary": "1.1.2",
|
||||||
"web-push": "3.6.3",
|
"web-push": "3.6.4",
|
||||||
"ws": "8.13.0",
|
"ws": "8.13.0",
|
||||||
"xev": "3.0.2"
|
"xev": "3.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@jest/globals": "29.6.1",
|
"@jest/globals": "29.6.2",
|
||||||
"@swc/jest": "0.2.26",
|
"@swc/jest": "0.2.27",
|
||||||
"@types/accepts": "1.3.5",
|
"@types/accepts": "1.3.5",
|
||||||
"@types/archiver": "5.3.2",
|
"@types/archiver": "5.3.2",
|
||||||
"@types/bcryptjs": "2.4.2",
|
"@types/bcryptjs": "2.4.2",
|
||||||
@ -187,9 +186,9 @@
|
|||||||
"@types/jsrsasign": "10.5.8",
|
"@types/jsrsasign": "10.5.8",
|
||||||
"@types/mime-types": "2.1.1",
|
"@types/mime-types": "2.1.1",
|
||||||
"@types/ms": "0.7.31",
|
"@types/ms": "0.7.31",
|
||||||
"@types/node": "20.4.2",
|
"@types/node": "20.4.5",
|
||||||
"@types/node-fetch": "3.0.3",
|
"@types/node-fetch": "3.0.3",
|
||||||
"@types/nodemailer": "6.4.8",
|
"@types/nodemailer": "6.4.9",
|
||||||
"@types/oauth": "0.9.1",
|
"@types/oauth": "0.9.1",
|
||||||
"@types/oauth2orize": "1.11.0",
|
"@types/oauth2orize": "1.11.0",
|
||||||
"@types/oauth2orize-pkce": "0.1.0",
|
"@types/oauth2orize-pkce": "0.1.0",
|
||||||
@ -210,15 +209,15 @@
|
|||||||
"@types/vary": "1.1.0",
|
"@types/vary": "1.1.0",
|
||||||
"@types/web-push": "3.3.2",
|
"@types/web-push": "3.3.2",
|
||||||
"@types/ws": "8.5.5",
|
"@types/ws": "8.5.5",
|
||||||
"@typescript-eslint/eslint-plugin": "5.61.0",
|
"@typescript-eslint/eslint-plugin": "6.2.0",
|
||||||
"@typescript-eslint/parser": "5.61.0",
|
"@typescript-eslint/parser": "6.2.0",
|
||||||
"aws-sdk-client-mock": "3.0.0",
|
"aws-sdk-client-mock": "3.0.0",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"eslint": "8.45.0",
|
"eslint": "8.46.0",
|
||||||
"eslint-plugin-import": "2.27.5",
|
"eslint-plugin-import": "2.28.0",
|
||||||
"execa": "7.1.1",
|
"execa": "7.2.0",
|
||||||
"jest": "29.6.1",
|
"jest": "29.6.2",
|
||||||
"jest-mock": "29.6.1",
|
"jest-mock": "29.6.2",
|
||||||
"simple-oauth2": "5.0.0"
|
"simple-oauth2": "5.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ import * as os from 'node:os';
|
|||||||
import cluster from 'node:cluster';
|
import cluster from 'node:cluster';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import chalkTemplate from 'chalk-template';
|
import chalkTemplate from 'chalk-template';
|
||||||
import semver from 'semver';
|
|
||||||
import Logger from '@/logger.js';
|
import Logger from '@/logger.js';
|
||||||
import { loadConfig } from '@/config.js';
|
import { loadConfig } from '@/config.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
|
@ -76,6 +76,7 @@ export type Source = {
|
|||||||
|
|
||||||
id: string;
|
id: string;
|
||||||
|
|
||||||
|
outgoingAddress?: string;
|
||||||
outgoingAddressFamily?: 'ipv4' | 'ipv6' | 'dual';
|
outgoingAddressFamily?: 'ipv4' | 'ipv6' | 'dual';
|
||||||
|
|
||||||
deliverJobConcurrency?: number;
|
deliverJobConcurrency?: number;
|
||||||
@ -197,7 +198,7 @@ function convertRedisOptions(options: RedisOptionsSource, host: string): RedisOp
|
|||||||
...options,
|
...options,
|
||||||
password: options.pass,
|
password: options.pass,
|
||||||
prefix: options.prefix ?? host,
|
prefix: options.prefix ?? host,
|
||||||
family: options.family == null ? 0 : options.family,
|
family: options.family ?? 0,
|
||||||
keyPrefix: `${options.prefix ?? host}:`,
|
keyPrefix: `${options.prefix ?? host}:`,
|
||||||
db: options.db ?? 0,
|
db: options.db ?? 0,
|
||||||
};
|
};
|
||||||
|
@ -8,9 +8,8 @@ import { IsNull, In, MoreThan, Not } from 'typeorm';
|
|||||||
|
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { MiLocalUser, MiRemoteUser, MiUser } from '@/models/entities/User.js';
|
||||||
import type { LocalUser, RemoteUser, User } from '@/models/entities/User.js';
|
import type { BlockingsRepository, FollowingsRepository, InstancesRepository, MutingsRepository, UserListJoiningsRepository, UsersRepository } from '@/models/index.js';
|
||||||
import type { BlockingsRepository, FollowingsRepository, InstancesRepository, Muting, MutingsRepository, UserListJoiningsRepository, UsersRepository } from '@/models/index.js';
|
|
||||||
import type { RelationshipJobData, ThinUser } from '@/queue/types.js';
|
import type { RelationshipJobData, ThinUser } from '@/queue/types.js';
|
||||||
|
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
@ -31,9 +30,6 @@ import PerUserFollowingChart from '@/core/chart/charts/per-user-following.js';
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class AccountMoveService {
|
export class AccountMoveService {
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.config)
|
|
||||||
private config: Config,
|
|
||||||
|
|
||||||
@Inject(DI.usersRepository)
|
@Inject(DI.usersRepository)
|
||||||
private usersRepository: UsersRepository,
|
private usersRepository: UsersRepository,
|
||||||
|
|
||||||
@ -75,12 +71,12 @@ export class AccountMoveService {
|
|||||||
* After delivering Move activity, its local followers unfollow the old account and then follow the new one.
|
* After delivering Move activity, its local followers unfollow the old account and then follow the new one.
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async moveFromLocal(src: LocalUser, dst: LocalUser | RemoteUser): Promise<unknown> {
|
public async moveFromLocal(src: MiLocalUser, dst: MiLocalUser | MiRemoteUser): Promise<unknown> {
|
||||||
const srcUri = this.userEntityService.getUserUri(src);
|
const srcUri = this.userEntityService.getUserUri(src);
|
||||||
const dstUri = this.userEntityService.getUserUri(dst);
|
const dstUri = this.userEntityService.getUserUri(dst);
|
||||||
|
|
||||||
// add movedToUri to indicate that the user has moved
|
// add movedToUri to indicate that the user has moved
|
||||||
const update = {} as Partial<LocalUser>;
|
const update = {} as Partial<MiLocalUser>;
|
||||||
update.alsoKnownAs = src.alsoKnownAs?.includes(dstUri) ? src.alsoKnownAs : src.alsoKnownAs?.concat([dstUri]) ?? [dstUri];
|
update.alsoKnownAs = src.alsoKnownAs?.includes(dstUri) ? src.alsoKnownAs : src.alsoKnownAs?.concat([dstUri]) ?? [dstUri];
|
||||||
update.movedToUri = dstUri;
|
update.movedToUri = dstUri;
|
||||||
update.movedAt = new Date();
|
update.movedAt = new Date();
|
||||||
@ -118,7 +114,7 @@ export class AccountMoveService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async postMoveProcess(src: User, dst: User): Promise<void> {
|
public async postMoveProcess(src: MiUser, dst: MiUser): Promise<void> {
|
||||||
// Copy blockings and mutings, and update lists
|
// Copy blockings and mutings, and update lists
|
||||||
try {
|
try {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
@ -217,7 +213,7 @@ export class AccountMoveService {
|
|||||||
* @returns Promise<void>
|
* @returns Promise<void>
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async updateLists(src: ThinUser, dst: User): Promise<void> {
|
public async updateLists(src: ThinUser, dst: MiUser): Promise<void> {
|
||||||
// Return if there is no list to be updated.
|
// Return if there is no list to be updated.
|
||||||
const oldJoinings = await this.userListJoiningsRepository.find({
|
const oldJoinings = await this.userListJoiningsRepository.find({
|
||||||
where: {
|
where: {
|
||||||
@ -264,7 +260,7 @@ export class AccountMoveService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async adjustFollowingCounts(localFollowerIds: string[], oldAccount: User): Promise<void> {
|
private async adjustFollowingCounts(localFollowerIds: string[], oldAccount: MiUser): Promise<void> {
|
||||||
if (localFollowerIds.length === 0) return;
|
if (localFollowerIds.length === 0) return;
|
||||||
|
|
||||||
// Set the old account's following and followers counts to 0.
|
// Set the old account's following and followers counts to 0.
|
||||||
@ -305,11 +301,11 @@ export class AccountMoveService {
|
|||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async validateAlsoKnownAs(
|
public async validateAlsoKnownAs(
|
||||||
dst: LocalUser | RemoteUser,
|
dst: MiLocalUser | MiRemoteUser,
|
||||||
check: (oldUser: LocalUser | RemoteUser | null, newUser: LocalUser | RemoteUser) => boolean | Promise<boolean> = () => true,
|
check: (oldUser: MiLocalUser | MiRemoteUser | null, newUser: MiLocalUser | MiRemoteUser) => boolean | Promise<boolean> = () => true,
|
||||||
instant = false,
|
instant = false,
|
||||||
): Promise<LocalUser | RemoteUser | null> {
|
): Promise<MiLocalUser | MiRemoteUser | null> {
|
||||||
let resultUser: LocalUser | RemoteUser | null = null;
|
let resultUser: MiLocalUser | MiRemoteUser | null = null;
|
||||||
|
|
||||||
if (this.userEntityService.isRemoteUser(dst)) {
|
if (this.userEntityService.isRemoteUser(dst)) {
|
||||||
if ((new Date()).getTime() - (dst.lastFetchedAt?.getTime() ?? 0) > 10 * 1000) {
|
if ((new Date()).getTime() - (dst.lastFetchedAt?.getTime() ?? 0) > 10 * 1000) {
|
||||||
|
@ -6,8 +6,7 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { UsersRepository } from '@/models/index.js';
|
import type { UsersRepository } from '@/models/index.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { MiUser } from '@/models/entities/User.js';
|
||||||
import type { User } from '@/models/entities/User.js';
|
|
||||||
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
|
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
|
||||||
import { RelayService } from '@/core/RelayService.js';
|
import { RelayService } from '@/core/RelayService.js';
|
||||||
import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js';
|
import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js';
|
||||||
@ -17,9 +16,6 @@ import { bindThis } from '@/decorators.js';
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class AccountUpdateService {
|
export class AccountUpdateService {
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.config)
|
|
||||||
private config: Config,
|
|
||||||
|
|
||||||
@Inject(DI.usersRepository)
|
@Inject(DI.usersRepository)
|
||||||
private usersRepository: UsersRepository,
|
private usersRepository: UsersRepository,
|
||||||
|
|
||||||
@ -31,7 +27,7 @@ export class AccountUpdateService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async publishToFollowers(userId: User['id']) {
|
public async publishToFollowers(userId: MiUser['id']) {
|
||||||
const user = await this.usersRepository.findOneBy({ id: userId });
|
const user = await this.usersRepository.findOneBy({ id: userId });
|
||||||
if (user == null) throw new Error('user not found');
|
if (user == null) throw new Error('user not found');
|
||||||
|
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import type { UserProfilesRepository, UsersRepository } from '@/models/index.js';
|
import type { UserProfilesRepository } from '@/models/index.js';
|
||||||
import type { User } from '@/models/entities/User.js';
|
import type { MiUser } from '@/models/entities/User.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { NotificationService } from '@/core/NotificationService.js';
|
import { NotificationService } from '@/core/NotificationService.js';
|
||||||
@ -90,9 +90,6 @@ export const ACHIEVEMENT_TYPES = [
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class AchievementService {
|
export class AchievementService {
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.usersRepository)
|
|
||||||
private usersRepository: UsersRepository,
|
|
||||||
|
|
||||||
@Inject(DI.userProfilesRepository)
|
@Inject(DI.userProfilesRepository)
|
||||||
private userProfilesRepository: UserProfilesRepository,
|
private userProfilesRepository: UserProfilesRepository,
|
||||||
|
|
||||||
@ -102,7 +99,7 @@ export class AchievementService {
|
|||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async create(
|
public async create(
|
||||||
userId: User['id'],
|
userId: MiUser['id'],
|
||||||
type: typeof ACHIEVEMENT_TYPES[number],
|
type: typeof ACHIEVEMENT_TYPES[number],
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (!ACHIEVEMENT_TYPES.includes(type)) return;
|
if (!ACHIEVEMENT_TYPES.includes(type)) return;
|
||||||
|
@ -6,12 +6,10 @@
|
|||||||
import * as fs from 'node:fs';
|
import * as fs from 'node:fs';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { dirname } from 'node:path';
|
import { dirname } from 'node:path';
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import * as nsfw from 'nsfwjs';
|
import * as nsfw from 'nsfwjs';
|
||||||
import si from 'systeminformation';
|
import si from 'systeminformation';
|
||||||
import { Mutex } from 'async-mutex';
|
import { Mutex } from 'async-mutex';
|
||||||
import type { Config } from '@/config.js';
|
|
||||||
import { DI } from '@/di-symbols.js';
|
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
|
||||||
const _filename = fileURLToPath(import.meta.url);
|
const _filename = fileURLToPath(import.meta.url);
|
||||||
@ -26,8 +24,6 @@ export class AiService {
|
|||||||
private modelLoadMutex: Mutex = new Mutex();
|
private modelLoadMutex: Mutex = new Mutex();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.config)
|
|
||||||
private config: Config,
|
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
135
packages/backend/src/core/AnnouncementService.ts
Normal file
135
packages/backend/src/core/AnnouncementService.ts
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { Brackets } from 'typeorm';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import type { MiUser } from '@/models/entities/User.js';
|
||||||
|
import type { AnnouncementReadsRepository, AnnouncementsRepository, MiAnnouncement, MiAnnouncementRead } from '@/models/index.js';
|
||||||
|
import { bindThis } from '@/decorators.js';
|
||||||
|
import { Packed } from '@/misc/json-schema.js';
|
||||||
|
import { IdService } from '@/core/IdService.js';
|
||||||
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AnnouncementService {
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.announcementsRepository)
|
||||||
|
private announcementsRepository: AnnouncementsRepository,
|
||||||
|
|
||||||
|
@Inject(DI.announcementReadsRepository)
|
||||||
|
private announcementReadsRepository: AnnouncementReadsRepository,
|
||||||
|
|
||||||
|
private idService: IdService,
|
||||||
|
private globalEventService: GlobalEventService,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async getReads(userId: MiUser['id']): Promise<MiAnnouncementRead[]> {
|
||||||
|
return this.announcementReadsRepository.findBy({
|
||||||
|
userId: userId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async getUnreadAnnouncements(user: MiUser): Promise<MiAnnouncement[]> {
|
||||||
|
const readsQuery = this.announcementReadsRepository.createQueryBuilder('read')
|
||||||
|
.select('read.announcementId')
|
||||||
|
.where('read.userId = :userId', { userId: user.id });
|
||||||
|
|
||||||
|
const q = this.announcementsRepository.createQueryBuilder('announcement')
|
||||||
|
.where('announcement.isActive = true')
|
||||||
|
.andWhere(new Brackets(qb => {
|
||||||
|
qb.orWhere('announcement.userId = :userId', { userId: user.id });
|
||||||
|
qb.orWhere('announcement.userId IS NULL');
|
||||||
|
}))
|
||||||
|
.andWhere(new Brackets(qb => {
|
||||||
|
qb.orWhere('announcement.forExistingUsers = false');
|
||||||
|
qb.orWhere('announcement.createdAt > :createdAt', { createdAt: user.createdAt });
|
||||||
|
}))
|
||||||
|
.andWhere(`announcement.id NOT IN (${ readsQuery.getQuery() })`);
|
||||||
|
|
||||||
|
q.setParameters(readsQuery.getParameters());
|
||||||
|
|
||||||
|
return q.getMany();
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async create(values: Partial<MiAnnouncement>): Promise<{ raw: MiAnnouncement; packed: Packed<'Announcement'> }> {
|
||||||
|
const announcement = await this.announcementsRepository.insert({
|
||||||
|
id: this.idService.genId(),
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: null,
|
||||||
|
title: values.title,
|
||||||
|
text: values.text,
|
||||||
|
imageUrl: values.imageUrl,
|
||||||
|
icon: values.icon,
|
||||||
|
display: values.display,
|
||||||
|
forExistingUsers: values.forExistingUsers,
|
||||||
|
needConfirmationToRead: values.needConfirmationToRead,
|
||||||
|
userId: values.userId,
|
||||||
|
}).then(x => this.announcementsRepository.findOneByOrFail(x.identifiers[0]));
|
||||||
|
|
||||||
|
const packed = (await this.packMany([announcement]))[0];
|
||||||
|
|
||||||
|
if (values.userId) {
|
||||||
|
this.globalEventService.publishMainStream(values.userId, 'announcementCreated', {
|
||||||
|
announcement: packed,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.globalEventService.publishBroadcastStream('announcementCreated', {
|
||||||
|
announcement: packed,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
raw: announcement,
|
||||||
|
packed: packed,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async read(user: MiUser, announcementId: MiAnnouncement['id']): Promise<void> {
|
||||||
|
try {
|
||||||
|
await this.announcementReadsRepository.insert({
|
||||||
|
id: this.idService.genId(),
|
||||||
|
createdAt: new Date(),
|
||||||
|
announcementId: announcementId,
|
||||||
|
userId: user.id,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((await this.getUnreadAnnouncements(user)).length === 0) {
|
||||||
|
this.globalEventService.publishMainStream(user.id, 'readAllAnnouncements');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async packMany(
|
||||||
|
announcements: MiAnnouncement[],
|
||||||
|
me?: { id: MiUser['id'] } | null | undefined,
|
||||||
|
options?: {
|
||||||
|
reads?: MiAnnouncementRead[];
|
||||||
|
},
|
||||||
|
): Promise<Packed<'Announcement'>[]> {
|
||||||
|
const reads = me ? (options?.reads ?? await this.getReads(me.id)) : [];
|
||||||
|
return announcements.map(announcement => ({
|
||||||
|
id: announcement.id,
|
||||||
|
createdAt: announcement.createdAt.toISOString(),
|
||||||
|
updatedAt: announcement.updatedAt?.toISOString() ?? null,
|
||||||
|
text: announcement.text,
|
||||||
|
title: announcement.title,
|
||||||
|
imageUrl: announcement.imageUrl,
|
||||||
|
icon: announcement.icon,
|
||||||
|
display: announcement.display,
|
||||||
|
needConfirmationToRead: announcement.needConfirmationToRead,
|
||||||
|
forYou: announcement.userId === me?.id,
|
||||||
|
isRead: reads.some(read => read.announcementId === announcement.id),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
@ -5,19 +5,14 @@
|
|||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import * as Redis from 'ioredis';
|
import * as Redis from 'ioredis';
|
||||||
import type { Antenna } from '@/models/entities/Antenna.js';
|
import type { MiAntenna } from '@/models/entities/Antenna.js';
|
||||||
import type { Note } from '@/models/entities/Note.js';
|
import type { MiNote } from '@/models/entities/Note.js';
|
||||||
import type { User } from '@/models/entities/User.js';
|
import type { MiUser } from '@/models/entities/User.js';
|
||||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
|
||||||
import { AntennaEntityService } from '@/core/entities/AntennaEntityService.js';
|
|
||||||
import { IdService } from '@/core/IdService.js';
|
|
||||||
import { isUserRelated } from '@/misc/is-user-related.js';
|
|
||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
import { PushNotificationService } from '@/core/PushNotificationService.js';
|
|
||||||
import * as Acct from '@/misc/acct.js';
|
import * as Acct from '@/misc/acct.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from '@/misc/json-schema.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { MutingsRepository, NotesRepository, AntennasRepository, UserListJoiningsRepository } from '@/models/index.js';
|
import type { AntennasRepository, UserListJoiningsRepository } from '@/models/index.js';
|
||||||
import { UtilityService } from '@/core/UtilityService.js';
|
import { UtilityService } from '@/core/UtilityService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { StreamMessages } from '@/server/api/stream/types.js';
|
import { StreamMessages } from '@/server/api/stream/types.js';
|
||||||
@ -26,7 +21,7 @@ import type { OnApplicationShutdown } from '@nestjs/common';
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class AntennaService implements OnApplicationShutdown {
|
export class AntennaService implements OnApplicationShutdown {
|
||||||
private antennasFetched: boolean;
|
private antennasFetched: boolean;
|
||||||
private antennas: Antenna[];
|
private antennas: MiAntenna[];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.redis)
|
@Inject(DI.redis)
|
||||||
@ -35,12 +30,6 @@ export class AntennaService implements OnApplicationShutdown {
|
|||||||
@Inject(DI.redisForSub)
|
@Inject(DI.redisForSub)
|
||||||
private redisForSub: Redis.Redis,
|
private redisForSub: Redis.Redis,
|
||||||
|
|
||||||
@Inject(DI.mutingsRepository)
|
|
||||||
private mutingsRepository: MutingsRepository,
|
|
||||||
|
|
||||||
@Inject(DI.notesRepository)
|
|
||||||
private notesRepository: NotesRepository,
|
|
||||||
|
|
||||||
@Inject(DI.antennasRepository)
|
@Inject(DI.antennasRepository)
|
||||||
private antennasRepository: AntennasRepository,
|
private antennasRepository: AntennasRepository,
|
||||||
|
|
||||||
@ -48,11 +37,7 @@ export class AntennaService implements OnApplicationShutdown {
|
|||||||
private userListJoiningsRepository: UserListJoiningsRepository,
|
private userListJoiningsRepository: UserListJoiningsRepository,
|
||||||
|
|
||||||
private utilityService: UtilityService,
|
private utilityService: UtilityService,
|
||||||
private idService: IdService,
|
|
||||||
private globalEventService: GlobalEventService,
|
private globalEventService: GlobalEventService,
|
||||||
private pushNotificationService: PushNotificationService,
|
|
||||||
private noteEntityService: NoteEntityService,
|
|
||||||
private antennaEntityService: AntennaEntityService,
|
|
||||||
) {
|
) {
|
||||||
this.antennasFetched = false;
|
this.antennasFetched = false;
|
||||||
this.antennas = [];
|
this.antennas = [];
|
||||||
@ -91,7 +76,7 @@ export class AntennaService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async addNoteToAntennas(note: Note, noteUser: { id: User['id']; username: string; host: string | null; }): Promise<void> {
|
public async addNoteToAntennas(note: MiNote, noteUser: { id: MiUser['id']; username: string; host: string | null; }): Promise<void> {
|
||||||
const antennas = await this.getAntennas();
|
const antennas = await this.getAntennas();
|
||||||
const antennasWithMatchResult = await Promise.all(antennas.map(antenna => this.checkHitAntenna(antenna, note, noteUser).then(hit => [antenna, hit] as const)));
|
const antennasWithMatchResult = await Promise.all(antennas.map(antenna => this.checkHitAntenna(antenna, note, noteUser).then(hit => [antenna, hit] as const)));
|
||||||
const matchedAntennas = antennasWithMatchResult.filter(([, hit]) => hit).map(([antenna]) => antenna);
|
const matchedAntennas = antennasWithMatchResult.filter(([, hit]) => hit).map(([antenna]) => antenna);
|
||||||
@ -114,7 +99,7 @@ export class AntennaService implements OnApplicationShutdown {
|
|||||||
// NOTE: フォローしているユーザーのノート、リストのユーザーのノート、グループのユーザーのノート指定はパフォーマンス上の理由で無効になっている
|
// NOTE: フォローしているユーザーのノート、リストのユーザーのノート、グループのユーザーのノート指定はパフォーマンス上の理由で無効になっている
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async checkHitAntenna(antenna: Antenna, note: (Note | Packed<'Note'>), noteUser: { id: User['id']; username: string; host: string | null; }): Promise<boolean> {
|
public async checkHitAntenna(antenna: MiAntenna, note: (MiNote | Packed<'Note'>), noteUser: { id: MiUser['id']; username: string; host: string | null; }): Promise<boolean> {
|
||||||
if (note.visibility === 'specified') return false;
|
if (note.visibility === 'specified') return false;
|
||||||
if (note.visibility === 'followers') return false;
|
if (note.visibility === 'followers') return false;
|
||||||
|
|
||||||
|
@ -5,9 +5,9 @@
|
|||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import * as Redis from 'ioredis';
|
import * as Redis from 'ioredis';
|
||||||
import type { BlockingsRepository, ChannelFollowingsRepository, FollowingsRepository, MutingsRepository, RenoteMutingsRepository, UserProfile, UserProfilesRepository, UsersRepository } from '@/models/index.js';
|
import type { BlockingsRepository, ChannelFollowingsRepository, FollowingsRepository, MutingsRepository, RenoteMutingsRepository, MiUserProfile, UserProfilesRepository, UsersRepository } from '@/models/index.js';
|
||||||
import { MemoryKVCache, RedisKVCache } from '@/misc/cache.js';
|
import { MemoryKVCache, RedisKVCache } from '@/misc/cache.js';
|
||||||
import type { LocalUser, User } from '@/models/entities/User.js';
|
import type { MiLocalUser, MiUser } from '@/models/entities/User.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
@ -16,11 +16,11 @@ import type { OnApplicationShutdown } from '@nestjs/common';
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CacheService implements OnApplicationShutdown {
|
export class CacheService implements OnApplicationShutdown {
|
||||||
public userByIdCache: MemoryKVCache<User, User | string>;
|
public userByIdCache: MemoryKVCache<MiUser, MiUser | string>;
|
||||||
public localUserByNativeTokenCache: MemoryKVCache<LocalUser | null, string | null>;
|
public localUserByNativeTokenCache: MemoryKVCache<MiLocalUser | null, string | null>;
|
||||||
public localUserByIdCache: MemoryKVCache<LocalUser>;
|
public localUserByIdCache: MemoryKVCache<MiLocalUser>;
|
||||||
public uriPersonCache: MemoryKVCache<User | null, string | null>;
|
public uriPersonCache: MemoryKVCache<MiUser | null, string | null>;
|
||||||
public userProfileCache: RedisKVCache<UserProfile>;
|
public userProfileCache: RedisKVCache<MiUserProfile>;
|
||||||
public userMutingsCache: RedisKVCache<Set<string>>;
|
public userMutingsCache: RedisKVCache<Set<string>>;
|
||||||
public userBlockingCache: RedisKVCache<Set<string>>;
|
public userBlockingCache: RedisKVCache<Set<string>>;
|
||||||
public userBlockedCache: RedisKVCache<Set<string>>; // NOTE: 「被」Blockキャッシュ
|
public userBlockedCache: RedisKVCache<Set<string>>; // NOTE: 「被」Blockキャッシュ
|
||||||
@ -60,14 +60,14 @@ export class CacheService implements OnApplicationShutdown {
|
|||||||
) {
|
) {
|
||||||
//this.onMessage = this.onMessage.bind(this);
|
//this.onMessage = this.onMessage.bind(this);
|
||||||
|
|
||||||
const localUserByIdCache = new MemoryKVCache<LocalUser>(1000 * 60 * 60 * 6 /* 6h */);
|
const localUserByIdCache = new MemoryKVCache<MiLocalUser>(1000 * 60 * 60 * 6 /* 6h */);
|
||||||
this.localUserByIdCache = localUserByIdCache;
|
this.localUserByIdCache = localUserByIdCache;
|
||||||
|
|
||||||
// ローカルユーザーならlocalUserByIdCacheにデータを追加し、こちらにはid(文字列)だけを追加する
|
// ローカルユーザーならlocalUserByIdCacheにデータを追加し、こちらにはid(文字列)だけを追加する
|
||||||
const userByIdCache = new MemoryKVCache<User, User | string>(1000 * 60 * 60 * 6 /* 6h */, {
|
const userByIdCache = new MemoryKVCache<MiUser, MiUser | string>(1000 * 60 * 60 * 6 /* 6h */, {
|
||||||
toMapConverter: user => {
|
toMapConverter: user => {
|
||||||
if (user.host === null) {
|
if (user.host === null) {
|
||||||
localUserByIdCache.set(user.id, user as LocalUser);
|
localUserByIdCache.set(user.id, user as MiLocalUser);
|
||||||
return user.id;
|
return user.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ export class CacheService implements OnApplicationShutdown {
|
|||||||
});
|
});
|
||||||
this.userByIdCache = userByIdCache;
|
this.userByIdCache = userByIdCache;
|
||||||
|
|
||||||
this.localUserByNativeTokenCache = new MemoryKVCache<LocalUser | null, string | null>(Infinity, {
|
this.localUserByNativeTokenCache = new MemoryKVCache<MiLocalUser | null, string | null>(Infinity, {
|
||||||
toMapConverter: user => {
|
toMapConverter: user => {
|
||||||
if (user === null) return null;
|
if (user === null) return null;
|
||||||
|
|
||||||
@ -86,7 +86,7 @@ export class CacheService implements OnApplicationShutdown {
|
|||||||
},
|
},
|
||||||
fromMapConverter: id => id === null ? null : localUserByIdCache.get(id),
|
fromMapConverter: id => id === null ? null : localUserByIdCache.get(id),
|
||||||
});
|
});
|
||||||
this.uriPersonCache = new MemoryKVCache<User | null, string | null>(Infinity, {
|
this.uriPersonCache = new MemoryKVCache<MiUser | null, string | null>(Infinity, {
|
||||||
toMapConverter: user => {
|
toMapConverter: user => {
|
||||||
if (user === null) return null;
|
if (user === null) return null;
|
||||||
|
|
||||||
@ -96,7 +96,7 @@ export class CacheService implements OnApplicationShutdown {
|
|||||||
fromMapConverter: id => id === null ? null : userByIdCache.get(id),
|
fromMapConverter: id => id === null ? null : userByIdCache.get(id),
|
||||||
});
|
});
|
||||||
|
|
||||||
this.userProfileCache = new RedisKVCache<UserProfile>(this.redisClient, 'userProfile', {
|
this.userProfileCache = new RedisKVCache<MiUserProfile>(this.redisClient, 'userProfile', {
|
||||||
lifetime: 1000 * 60 * 30, // 30m
|
lifetime: 1000 * 60 * 30, // 30m
|
||||||
memoryCacheLifetime: 1000 * 60, // 1m
|
memoryCacheLifetime: 1000 * 60, // 1m
|
||||||
fetcher: (key) => this.userProfilesRepository.findOneByOrFail({ userId: key }),
|
fetcher: (key) => this.userProfilesRepository.findOneByOrFail({ userId: key }),
|
||||||
@ -178,7 +178,7 @@ export class CacheService implements OnApplicationShutdown {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'userTokenRegenerated': {
|
case 'userTokenRegenerated': {
|
||||||
const user = await this.usersRepository.findOneByOrFail({ id: body.id }) as LocalUser;
|
const user = await this.usersRepository.findOneByOrFail({ id: body.id }) as MiLocalUser;
|
||||||
this.localUserByNativeTokenCache.delete(body.oldToken);
|
this.localUserByNativeTokenCache.delete(body.oldToken);
|
||||||
this.localUserByNativeTokenCache.set(body.newToken, user);
|
this.localUserByNativeTokenCache.set(body.newToken, user);
|
||||||
break;
|
break;
|
||||||
@ -197,7 +197,7 @@ export class CacheService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public findUserById(userId: User['id']) {
|
public findUserById(userId: MiUser['id']) {
|
||||||
return this.userByIdCache.fetch(userId, () => this.usersRepository.findOneByOrFail({ id: userId }));
|
return this.userByIdCache.fetch(userId, () => this.usersRepository.findOneByOrFail({ id: userId }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import { Module } from '@nestjs/common';
|
|||||||
import { AccountMoveService } from './AccountMoveService.js';
|
import { AccountMoveService } from './AccountMoveService.js';
|
||||||
import { AccountUpdateService } from './AccountUpdateService.js';
|
import { AccountUpdateService } from './AccountUpdateService.js';
|
||||||
import { AiService } from './AiService.js';
|
import { AiService } from './AiService.js';
|
||||||
|
import { AnnouncementService } from './AnnouncementService.js';
|
||||||
import { AntennaService } from './AntennaService.js';
|
import { AntennaService } from './AntennaService.js';
|
||||||
import { AppLockService } from './AppLockService.js';
|
import { AppLockService } from './AppLockService.js';
|
||||||
import { AchievementService } from './AchievementService.js';
|
import { AchievementService } from './AchievementService.js';
|
||||||
@ -131,6 +132,7 @@ const $LoggerService: Provider = { provide: 'LoggerService', useExisting: Logger
|
|||||||
const $AccountMoveService: Provider = { provide: 'AccountMoveService', useExisting: AccountMoveService };
|
const $AccountMoveService: Provider = { provide: 'AccountMoveService', useExisting: AccountMoveService };
|
||||||
const $AccountUpdateService: Provider = { provide: 'AccountUpdateService', useExisting: AccountUpdateService };
|
const $AccountUpdateService: Provider = { provide: 'AccountUpdateService', useExisting: AccountUpdateService };
|
||||||
const $AiService: Provider = { provide: 'AiService', useExisting: AiService };
|
const $AiService: Provider = { provide: 'AiService', useExisting: AiService };
|
||||||
|
const $AnnouncementService: Provider = { provide: 'AnnouncementService', useExisting: AnnouncementService };
|
||||||
const $AntennaService: Provider = { provide: 'AntennaService', useExisting: AntennaService };
|
const $AntennaService: Provider = { provide: 'AntennaService', useExisting: AntennaService };
|
||||||
const $AppLockService: Provider = { provide: 'AppLockService', useExisting: AppLockService };
|
const $AppLockService: Provider = { provide: 'AppLockService', useExisting: AppLockService };
|
||||||
const $AchievementService: Provider = { provide: 'AchievementService', useExisting: AchievementService };
|
const $AchievementService: Provider = { provide: 'AchievementService', useExisting: AchievementService };
|
||||||
@ -259,6 +261,7 @@ const $ApEventService: Provider = { provide: 'ApEventService', useExisting: ApEv
|
|||||||
AccountMoveService,
|
AccountMoveService,
|
||||||
AccountUpdateService,
|
AccountUpdateService,
|
||||||
AiService,
|
AiService,
|
||||||
|
AnnouncementService,
|
||||||
AntennaService,
|
AntennaService,
|
||||||
AppLockService,
|
AppLockService,
|
||||||
AchievementService,
|
AchievementService,
|
||||||
@ -380,6 +383,7 @@ const $ApEventService: Provider = { provide: 'ApEventService', useExisting: ApEv
|
|||||||
$AccountMoveService,
|
$AccountMoveService,
|
||||||
$AccountUpdateService,
|
$AccountUpdateService,
|
||||||
$AiService,
|
$AiService,
|
||||||
|
$AnnouncementService,
|
||||||
$AntennaService,
|
$AntennaService,
|
||||||
$AppLockService,
|
$AppLockService,
|
||||||
$AchievementService,
|
$AchievementService,
|
||||||
@ -502,6 +506,7 @@ const $ApEventService: Provider = { provide: 'ApEventService', useExisting: ApEv
|
|||||||
AccountMoveService,
|
AccountMoveService,
|
||||||
AccountUpdateService,
|
AccountUpdateService,
|
||||||
AiService,
|
AiService,
|
||||||
|
AnnouncementService,
|
||||||
AntennaService,
|
AntennaService,
|
||||||
AppLockService,
|
AppLockService,
|
||||||
AchievementService,
|
AchievementService,
|
||||||
@ -622,6 +627,7 @@ const $ApEventService: Provider = { provide: 'ApEventService', useExisting: ApEv
|
|||||||
$AccountMoveService,
|
$AccountMoveService,
|
||||||
$AccountUpdateService,
|
$AccountUpdateService,
|
||||||
$AiService,
|
$AiService,
|
||||||
|
$AnnouncementService,
|
||||||
$AntennaService,
|
$AntennaService,
|
||||||
$AppLockService,
|
$AppLockService,
|
||||||
$AchievementService,
|
$AchievementService,
|
||||||
|
@ -8,11 +8,11 @@ import { Inject, Injectable } from '@nestjs/common';
|
|||||||
import bcrypt from 'bcryptjs';
|
import bcrypt from 'bcryptjs';
|
||||||
import { IsNull, DataSource } from 'typeorm';
|
import { IsNull, DataSource } from 'typeorm';
|
||||||
import { genRsaKeyPair } from '@/misc/gen-key-pair.js';
|
import { genRsaKeyPair } from '@/misc/gen-key-pair.js';
|
||||||
import { User } from '@/models/entities/User.js';
|
import { MiUser } from '@/models/entities/User.js';
|
||||||
import { UserProfile } from '@/models/entities/UserProfile.js';
|
import { MiUserProfile } from '@/models/entities/UserProfile.js';
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
import { UserKeypair } from '@/models/entities/UserKeypair.js';
|
import { MiUserKeypair } from '@/models/entities/UserKeypair.js';
|
||||||
import { UsedUsername } from '@/models/entities/UsedUsername.js';
|
import { MiUsedUsername } from '@/models/entities/UsedUsername.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import generateNativeUserToken from '@/misc/generate-native-user-token.js';
|
import generateNativeUserToken from '@/misc/generate-native-user-token.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
@ -28,7 +28,7 @@ export class CreateSystemUserService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async createSystemUser(username: string): Promise<User> {
|
public async createSystemUser(username: string): Promise<MiUser> {
|
||||||
const password = randomUUID();
|
const password = randomUUID();
|
||||||
|
|
||||||
// Generate hash of password
|
// Generate hash of password
|
||||||
@ -40,18 +40,18 @@ export class CreateSystemUserService {
|
|||||||
|
|
||||||
const keyPair = await genRsaKeyPair();
|
const keyPair = await genRsaKeyPair();
|
||||||
|
|
||||||
let account!: User;
|
let account!: MiUser;
|
||||||
|
|
||||||
// Start transaction
|
// Start transaction
|
||||||
await this.db.transaction(async transactionalEntityManager => {
|
await this.db.transaction(async transactionalEntityManager => {
|
||||||
const exist = await transactionalEntityManager.findOneBy(User, {
|
const exist = await transactionalEntityManager.findOneBy(MiUser, {
|
||||||
usernameLower: username.toLowerCase(),
|
usernameLower: username.toLowerCase(),
|
||||||
host: IsNull(),
|
host: IsNull(),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (exist) throw new Error('the user is already exists');
|
if (exist) throw new Error('the user is already exists');
|
||||||
|
|
||||||
account = await transactionalEntityManager.insert(User, {
|
account = await transactionalEntityManager.insert(MiUser, {
|
||||||
id: this.idService.genId(),
|
id: this.idService.genId(),
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
username: username,
|
username: username,
|
||||||
@ -62,21 +62,21 @@ export class CreateSystemUserService {
|
|||||||
isLocked: true,
|
isLocked: true,
|
||||||
isExplorable: false,
|
isExplorable: false,
|
||||||
isBot: true,
|
isBot: true,
|
||||||
}).then(x => transactionalEntityManager.findOneByOrFail(User, x.identifiers[0]));
|
}).then(x => transactionalEntityManager.findOneByOrFail(MiUser, x.identifiers[0]));
|
||||||
|
|
||||||
await transactionalEntityManager.insert(UserKeypair, {
|
await transactionalEntityManager.insert(MiUserKeypair, {
|
||||||
publicKey: keyPair.publicKey,
|
publicKey: keyPair.publicKey,
|
||||||
privateKey: keyPair.privateKey,
|
privateKey: keyPair.privateKey,
|
||||||
userId: account.id,
|
userId: account.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
await transactionalEntityManager.insert(UserProfile, {
|
await transactionalEntityManager.insert(MiUserProfile, {
|
||||||
userId: account.id,
|
userId: account.id,
|
||||||
autoAcceptFollowed: false,
|
autoAcceptFollowed: false,
|
||||||
password: hash,
|
password: hash,
|
||||||
});
|
});
|
||||||
|
|
||||||
await transactionalEntityManager.insert(UsedUsername, {
|
await transactionalEntityManager.insert(MiUsedUsername, {
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
username: username.toLowerCase(),
|
username: username.toLowerCase(),
|
||||||
});
|
});
|
||||||
|
@ -4,19 +4,18 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
|
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
|
||||||
import { DataSource, In, IsNull } from 'typeorm';
|
import { In, IsNull } from 'typeorm';
|
||||||
import * as Redis from 'ioredis';
|
import * as Redis from 'ioredis';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';
|
import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';
|
||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
import type { DriveFile } from '@/models/entities/DriveFile.js';
|
import type { MiDriveFile } from '@/models/entities/DriveFile.js';
|
||||||
import type { Emoji } from '@/models/entities/Emoji.js';
|
import type { MiEmoji } from '@/models/entities/Emoji.js';
|
||||||
import type { EmojisRepository, Role } from '@/models/index.js';
|
import type { EmojisRepository, MiRole } from '@/models/index.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { MemoryKVCache, RedisSingleCache } from '@/misc/cache.js';
|
import { MemoryKVCache, RedisSingleCache } from '@/misc/cache.js';
|
||||||
import { UtilityService } from '@/core/UtilityService.js';
|
import { UtilityService } from '@/core/UtilityService.js';
|
||||||
import type { Config } from '@/config.js';
|
|
||||||
import { query } from '@/misc/prelude/url.js';
|
import { query } from '@/misc/prelude/url.js';
|
||||||
import type { Serialized } from '@/server/api/stream/types.js';
|
import type { Serialized } from '@/server/api/stream/types.js';
|
||||||
|
|
||||||
@ -24,19 +23,13 @@ const parseEmojiStrRegexp = /^(\w+)(?:@([\w.-]+))?$/;
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CustomEmojiService implements OnApplicationShutdown {
|
export class CustomEmojiService implements OnApplicationShutdown {
|
||||||
private cache: MemoryKVCache<Emoji | null>;
|
private cache: MemoryKVCache<MiEmoji | null>;
|
||||||
public localEmojisCache: RedisSingleCache<Map<string, Emoji>>;
|
public localEmojisCache: RedisSingleCache<Map<string, MiEmoji>>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.redis)
|
@Inject(DI.redis)
|
||||||
private redisClient: Redis.Redis,
|
private redisClient: Redis.Redis,
|
||||||
|
|
||||||
@Inject(DI.config)
|
|
||||||
private config: Config,
|
|
||||||
|
|
||||||
@Inject(DI.db)
|
|
||||||
private db: DataSource,
|
|
||||||
|
|
||||||
@Inject(DI.emojisRepository)
|
@Inject(DI.emojisRepository)
|
||||||
private emojisRepository: EmojisRepository,
|
private emojisRepository: EmojisRepository,
|
||||||
|
|
||||||
@ -45,16 +38,16 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
|||||||
private emojiEntityService: EmojiEntityService,
|
private emojiEntityService: EmojiEntityService,
|
||||||
private globalEventService: GlobalEventService,
|
private globalEventService: GlobalEventService,
|
||||||
) {
|
) {
|
||||||
this.cache = new MemoryKVCache<Emoji | null>(1000 * 60 * 60 * 12);
|
this.cache = new MemoryKVCache<MiEmoji | null>(1000 * 60 * 60 * 12);
|
||||||
|
|
||||||
this.localEmojisCache = new RedisSingleCache<Map<string, Emoji>>(this.redisClient, 'localEmojis', {
|
this.localEmojisCache = new RedisSingleCache<Map<string, MiEmoji>>(this.redisClient, 'localEmojis', {
|
||||||
lifetime: 1000 * 60 * 30, // 30m
|
lifetime: 1000 * 60 * 30, // 30m
|
||||||
memoryCacheLifetime: 1000 * 60 * 3, // 3m
|
memoryCacheLifetime: 1000 * 60 * 3, // 3m
|
||||||
fetcher: () => this.emojisRepository.find({ where: { host: IsNull() } }).then(emojis => new Map(emojis.map(emoji => [emoji.name, emoji]))),
|
fetcher: () => this.emojisRepository.find({ where: { host: IsNull() } }).then(emojis => new Map(emojis.map(emoji => [emoji.name, emoji]))),
|
||||||
toRedisConverter: (value) => JSON.stringify(Array.from(value.values())),
|
toRedisConverter: (value) => JSON.stringify(Array.from(value.values())),
|
||||||
fromRedisConverter: (value) => {
|
fromRedisConverter: (value) => {
|
||||||
if (!Array.isArray(JSON.parse(value))) return undefined; // 古いバージョンの壊れたキャッシュが残っていることがある(そのうち消す)
|
if (!Array.isArray(JSON.parse(value))) return undefined; // 古いバージョンの壊れたキャッシュが残っていることがある(そのうち消す)
|
||||||
return new Map(JSON.parse(value).map((x: Serialized<Emoji>) => [x.name, {
|
return new Map(JSON.parse(value).map((x: Serialized<MiEmoji>) => [x.name, {
|
||||||
...x,
|
...x,
|
||||||
updatedAt: x.updatedAt ? new Date(x.updatedAt) : null,
|
updatedAt: x.updatedAt ? new Date(x.updatedAt) : null,
|
||||||
}]));
|
}]));
|
||||||
@ -64,7 +57,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
|||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async add(data: {
|
public async add(data: {
|
||||||
driveFile: DriveFile;
|
driveFile: MiDriveFile;
|
||||||
name: string;
|
name: string;
|
||||||
category: string | null;
|
category: string | null;
|
||||||
aliases: string[];
|
aliases: string[];
|
||||||
@ -72,8 +65,8 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
|||||||
license: string | null;
|
license: string | null;
|
||||||
isSensitive: boolean;
|
isSensitive: boolean;
|
||||||
localOnly: boolean;
|
localOnly: boolean;
|
||||||
roleIdsThatCanBeUsedThisEmojiAsReaction: Role['id'][];
|
roleIdsThatCanBeUsedThisEmojiAsReaction: MiRole['id'][];
|
||||||
}): Promise<Emoji> {
|
}): Promise<MiEmoji> {
|
||||||
const emoji = await this.emojisRepository.insert({
|
const emoji = await this.emojisRepository.insert({
|
||||||
id: this.idService.genId(),
|
id: this.idService.genId(),
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
@ -102,15 +95,15 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async update(id: Emoji['id'], data: {
|
public async update(id: MiEmoji['id'], data: {
|
||||||
driveFile?: DriveFile;
|
driveFile?: MiDriveFile;
|
||||||
name?: string;
|
name?: string;
|
||||||
category?: string | null;
|
category?: string | null;
|
||||||
aliases?: string[];
|
aliases?: string[];
|
||||||
license?: string | null;
|
license?: string | null;
|
||||||
isSensitive?: boolean;
|
isSensitive?: boolean;
|
||||||
localOnly?: boolean;
|
localOnly?: boolean;
|
||||||
roleIdsThatCanBeUsedThisEmojiAsReaction?: Role['id'][];
|
roleIdsThatCanBeUsedThisEmojiAsReaction?: MiRole['id'][];
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
const emoji = await this.emojisRepository.findOneByOrFail({ id: id });
|
const emoji = await this.emojisRepository.findOneByOrFail({ id: id });
|
||||||
const sameNameEmoji = await this.emojisRepository.findOneBy({ name: data.name, host: IsNull() });
|
const sameNameEmoji = await this.emojisRepository.findOneBy({ name: data.name, host: IsNull() });
|
||||||
@ -150,7 +143,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async addAliasesBulk(ids: Emoji['id'][], aliases: string[]) {
|
public async addAliasesBulk(ids: MiEmoji['id'][], aliases: string[]) {
|
||||||
const emojis = await this.emojisRepository.findBy({
|
const emojis = await this.emojisRepository.findBy({
|
||||||
id: In(ids),
|
id: In(ids),
|
||||||
});
|
});
|
||||||
@ -170,7 +163,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async setAliasesBulk(ids: Emoji['id'][], aliases: string[]) {
|
public async setAliasesBulk(ids: MiEmoji['id'][], aliases: string[]) {
|
||||||
await this.emojisRepository.update({
|
await this.emojisRepository.update({
|
||||||
id: In(ids),
|
id: In(ids),
|
||||||
}, {
|
}, {
|
||||||
@ -186,7 +179,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async removeAliasesBulk(ids: Emoji['id'][], aliases: string[]) {
|
public async removeAliasesBulk(ids: MiEmoji['id'][], aliases: string[]) {
|
||||||
const emojis = await this.emojisRepository.findBy({
|
const emojis = await this.emojisRepository.findBy({
|
||||||
id: In(ids),
|
id: In(ids),
|
||||||
});
|
});
|
||||||
@ -206,7 +199,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async setCategoryBulk(ids: Emoji['id'][], category: string | null) {
|
public async setCategoryBulk(ids: MiEmoji['id'][], category: string | null) {
|
||||||
await this.emojisRepository.update({
|
await this.emojisRepository.update({
|
||||||
id: In(ids),
|
id: In(ids),
|
||||||
}, {
|
}, {
|
||||||
@ -222,7 +215,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async setLicenseBulk(ids: Emoji['id'][], license: string | null) {
|
public async setLicenseBulk(ids: MiEmoji['id'][], license: string | null) {
|
||||||
await this.emojisRepository.update({
|
await this.emojisRepository.update({
|
||||||
id: In(ids),
|
id: In(ids),
|
||||||
}, {
|
}, {
|
||||||
@ -238,7 +231,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async delete(id: Emoji['id']) {
|
public async delete(id: MiEmoji['id']) {
|
||||||
const emoji = await this.emojisRepository.findOneByOrFail({ id: id });
|
const emoji = await this.emojisRepository.findOneByOrFail({ id: id });
|
||||||
|
|
||||||
await this.emojisRepository.delete(emoji.id);
|
await this.emojisRepository.delete(emoji.id);
|
||||||
@ -251,7 +244,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async deleteBulk(ids: Emoji['id'][]) {
|
public async deleteBulk(ids: MiEmoji['id'][]) {
|
||||||
const emojis = await this.emojisRepository.findBy({
|
const emojis = await this.emojisRepository.findBy({
|
||||||
id: In(ids),
|
id: In(ids),
|
||||||
});
|
});
|
||||||
|
@ -7,7 +7,6 @@ import { Inject, Injectable } from '@nestjs/common';
|
|||||||
import type { UsersRepository } from '@/models/index.js';
|
import type { UsersRepository } from '@/models/index.js';
|
||||||
import { QueueService } from '@/core/QueueService.js';
|
import { QueueService } from '@/core/QueueService.js';
|
||||||
import { UserSuspendService } from '@/core/UserSuspendService.js';
|
import { UserSuspendService } from '@/core/UserSuspendService.js';
|
||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
|
||||||
@ -19,7 +18,6 @@ export class DeleteAccountService {
|
|||||||
|
|
||||||
private userSuspendService: UserSuspendService,
|
private userSuspendService: UserSuspendService,
|
||||||
private queueService: QueueService,
|
private queueService: QueueService,
|
||||||
private globalEventService: GlobalEventService,
|
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,9 +14,9 @@ import { DI } from '@/di-symbols.js';
|
|||||||
import type { DriveFilesRepository, UsersRepository, DriveFoldersRepository, UserProfilesRepository } from '@/models/index.js';
|
import type { DriveFilesRepository, UsersRepository, DriveFoldersRepository, UserProfilesRepository } from '@/models/index.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import Logger from '@/logger.js';
|
import Logger from '@/logger.js';
|
||||||
import type { RemoteUser, User } from '@/models/entities/User.js';
|
import type { MiRemoteUser, MiUser } from '@/models/entities/User.js';
|
||||||
import { MetaService } from '@/core/MetaService.js';
|
import { MetaService } from '@/core/MetaService.js';
|
||||||
import { DriveFile } from '@/models/entities/DriveFile.js';
|
import { MiDriveFile } from '@/models/entities/DriveFile.js';
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js';
|
import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js';
|
||||||
import { FILE_TYPE_BROWSERSAFE } from '@/const.js';
|
import { FILE_TYPE_BROWSERSAFE } from '@/const.js';
|
||||||
@ -27,7 +27,7 @@ import { VideoProcessingService } from '@/core/VideoProcessingService.js';
|
|||||||
import { ImageProcessingService } from '@/core/ImageProcessingService.js';
|
import { ImageProcessingService } from '@/core/ImageProcessingService.js';
|
||||||
import type { IImage } from '@/core/ImageProcessingService.js';
|
import type { IImage } from '@/core/ImageProcessingService.js';
|
||||||
import { QueueService } from '@/core/QueueService.js';
|
import { QueueService } from '@/core/QueueService.js';
|
||||||
import type { DriveFolder } from '@/models/entities/DriveFolder.js';
|
import type { MiDriveFolder } from '@/models/entities/DriveFolder.js';
|
||||||
import { createTemp } from '@/misc/create-temp.js';
|
import { createTemp } from '@/misc/create-temp.js';
|
||||||
import DriveChart from '@/core/chart/charts/drive.js';
|
import DriveChart from '@/core/chart/charts/drive.js';
|
||||||
import PerUserDriveChart from '@/core/chart/charts/per-user-drive.js';
|
import PerUserDriveChart from '@/core/chart/charts/per-user-drive.js';
|
||||||
@ -45,7 +45,7 @@ import { isMimeImage } from '@/misc/is-mime-image.js';
|
|||||||
|
|
||||||
type AddFileArgs = {
|
type AddFileArgs = {
|
||||||
/** User who wish to add file */
|
/** User who wish to add file */
|
||||||
user: { id: User['id']; host: User['host'] } | null;
|
user: { id: MiUser['id']; host: MiUser['host'] } | null;
|
||||||
/** File path */
|
/** File path */
|
||||||
path: string;
|
path: string;
|
||||||
/** Name */
|
/** Name */
|
||||||
@ -73,8 +73,8 @@ type AddFileArgs = {
|
|||||||
|
|
||||||
type UploadFromUrlArgs = {
|
type UploadFromUrlArgs = {
|
||||||
url: string;
|
url: string;
|
||||||
user: { id: User['id']; host: User['host'] } | null;
|
user: { id: MiUser['id']; host: MiUser['host'] } | null;
|
||||||
folderId?: DriveFolder['id'] | null;
|
folderId?: MiDriveFolder['id'] | null;
|
||||||
uri?: string | null;
|
uri?: string | null;
|
||||||
sensitive?: boolean;
|
sensitive?: boolean;
|
||||||
force?: boolean;
|
force?: boolean;
|
||||||
@ -138,7 +138,7 @@ export class DriveService {
|
|||||||
* @param size Size for original
|
* @param size Size for original
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
private async save(file: DriveFile, path: string, name: string, type: string, hash: string, size: number): Promise<DriveFile> {
|
private async save(file: MiDriveFile, path: string, name: string, type: string, hash: string, size: number): Promise<MiDriveFile> {
|
||||||
// thunbnail, webpublic を必要なら生成
|
// thunbnail, webpublic を必要なら生成
|
||||||
const alts = await this.generateAlts(path, type, !file.uri);
|
const alts = await this.generateAlts(path, type, !file.uri);
|
||||||
|
|
||||||
@ -332,7 +332,7 @@ export class DriveService {
|
|||||||
this.registerLogger.debug('web image not created (not an required image)');
|
this.registerLogger.debug('web image not created (not an required image)');
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.registerLogger.warn('web image not created (an error occured)', err as Error);
|
this.registerLogger.warn('web image not created (an error occurred)', err as Error);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (satisfyWebpublic) this.registerLogger.info('web image not created (original satisfies webpublic)');
|
if (satisfyWebpublic) this.registerLogger.info('web image not created (original satisfies webpublic)');
|
||||||
@ -351,7 +351,7 @@ export class DriveService {
|
|||||||
thumbnail = await this.imageProcessingService.convertSharpToWebp(img, 498, 422);
|
thumbnail = await this.imageProcessingService.convertSharpToWebp(img, 498, 422);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.registerLogger.warn('thumbnail not created (an error occured)', err as Error);
|
this.registerLogger.warn('thumbnail not created (an error occurred)', err as Error);
|
||||||
}
|
}
|
||||||
// #endregion thumbnail
|
// #endregion thumbnail
|
||||||
|
|
||||||
@ -405,7 +405,7 @@ export class DriveService {
|
|||||||
|
|
||||||
// Expire oldest file (without avatar or banner) of remote user
|
// Expire oldest file (without avatar or banner) of remote user
|
||||||
@bindThis
|
@bindThis
|
||||||
private async expireOldFile(user: RemoteUser, driveCapacity: number) {
|
private async expireOldFile(user: MiRemoteUser, driveCapacity: number) {
|
||||||
const q = this.driveFilesRepository.createQueryBuilder('file')
|
const q = this.driveFilesRepository.createQueryBuilder('file')
|
||||||
.where('file.userId = :userId', { userId: user.id })
|
.where('file.userId = :userId', { userId: user.id })
|
||||||
.andWhere('file.isLink = FALSE');
|
.andWhere('file.isLink = FALSE');
|
||||||
@ -451,7 +451,7 @@ export class DriveService {
|
|||||||
requestIp = null,
|
requestIp = null,
|
||||||
requestHeaders = null,
|
requestHeaders = null,
|
||||||
ext = null,
|
ext = null,
|
||||||
}: AddFileArgs): Promise<DriveFile> {
|
}: AddFileArgs): Promise<MiDriveFile> {
|
||||||
let skipNsfwCheck = false;
|
let skipNsfwCheck = false;
|
||||||
const instance = await this.metaService.fetch();
|
const instance = await this.metaService.fetch();
|
||||||
const userRoleNSFW = user && (await this.roleService.getUserPolicies(user.id)).alwaysMarkNsfw;
|
const userRoleNSFW = user && (await this.roleService.getUserPolicies(user.id)).alwaysMarkNsfw;
|
||||||
@ -520,7 +520,7 @@ export class DriveService {
|
|||||||
if (isLocalUser) {
|
if (isLocalUser) {
|
||||||
throw new IdentifiableError('c6244ed2-a39a-4e1c-bf93-f0fbd7764fa6', 'No free space.');
|
throw new IdentifiableError('c6244ed2-a39a-4e1c-bf93-f0fbd7764fa6', 'No free space.');
|
||||||
}
|
}
|
||||||
await this.expireOldFile(await this.usersRepository.findOneByOrFail({ id: user.id }) as RemoteUser, driveCapacity - info.size);
|
await this.expireOldFile(await this.usersRepository.findOneByOrFail({ id: user.id }) as MiRemoteUser, driveCapacity - info.size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
@ -558,7 +558,7 @@ export class DriveService {
|
|||||||
|
|
||||||
const folder = await fetchFolder();
|
const folder = await fetchFolder();
|
||||||
|
|
||||||
let file = new DriveFile();
|
let file = new MiDriveFile();
|
||||||
file.id = this.idService.genId();
|
file.id = this.idService.genId();
|
||||||
file.createdAt = new Date();
|
file.createdAt = new Date();
|
||||||
file.userId = user ? user.id : null;
|
file.userId = user ? user.id : null;
|
||||||
@ -574,9 +574,7 @@ export class DriveService {
|
|||||||
file.maybePorn = info.porn;
|
file.maybePorn = info.porn;
|
||||||
file.isSensitive = user
|
file.isSensitive = user
|
||||||
? this.userEntityService.isLocalUser(user) && profile!.alwaysMarkNsfw ? true :
|
? this.userEntityService.isLocalUser(user) && profile!.alwaysMarkNsfw ? true :
|
||||||
(sensitive !== null && sensitive !== undefined)
|
sensitive ?? false
|
||||||
? sensitive
|
|
||||||
: false
|
|
||||||
: false;
|
: false;
|
||||||
|
|
||||||
if (info.sensitive && profile!.autoSensitive) file.isSensitive = true;
|
if (info.sensitive && profile!.autoSensitive) file.isSensitive = true;
|
||||||
@ -616,7 +614,7 @@ export class DriveService {
|
|||||||
file = await this.driveFilesRepository.findOneBy({
|
file = await this.driveFilesRepository.findOneBy({
|
||||||
uri: file.uri!,
|
uri: file.uri!,
|
||||||
userId: user ? user.id : IsNull(),
|
userId: user ? user.id : IsNull(),
|
||||||
}) as DriveFile;
|
}) as MiDriveFile;
|
||||||
} else {
|
} else {
|
||||||
this.registerLogger.error(err as Error);
|
this.registerLogger.error(err as Error);
|
||||||
throw err;
|
throw err;
|
||||||
@ -650,7 +648,7 @@ export class DriveService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async deleteFile(file: DriveFile, isExpired = false) {
|
public async deleteFile(file: MiDriveFile, isExpired = false) {
|
||||||
if (file.storedInternal) {
|
if (file.storedInternal) {
|
||||||
this.internalStorageService.del(file.accessKey!);
|
this.internalStorageService.del(file.accessKey!);
|
||||||
|
|
||||||
@ -677,7 +675,7 @@ export class DriveService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async deleteFileSync(file: DriveFile, isExpired = false) {
|
public async deleteFileSync(file: MiDriveFile, isExpired = false) {
|
||||||
if (file.storedInternal) {
|
if (file.storedInternal) {
|
||||||
this.internalStorageService.del(file.accessKey!);
|
this.internalStorageService.del(file.accessKey!);
|
||||||
|
|
||||||
@ -708,7 +706,7 @@ export class DriveService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async deletePostProcess(file: DriveFile, isExpired = false) {
|
private async deletePostProcess(file: MiDriveFile, isExpired = false) {
|
||||||
// リモートファイル期限切れ削除後は直リンクにする
|
// リモートファイル期限切れ削除後は直リンクにする
|
||||||
if (isExpired && file.userHost !== null && file.uri != null) {
|
if (isExpired && file.userHost !== null && file.uri != null) {
|
||||||
this.driveFilesRepository.update(file.id, {
|
this.driveFilesRepository.update(file.id, {
|
||||||
@ -771,7 +769,7 @@ export class DriveService {
|
|||||||
comment = null,
|
comment = null,
|
||||||
requestIp = null,
|
requestIp = null,
|
||||||
requestHeaders = null,
|
requestHeaders = null,
|
||||||
}: UploadFromUrlArgs): Promise<DriveFile> {
|
}: UploadFromUrlArgs): Promise<MiDriveFile> {
|
||||||
// Create temp file
|
// Create temp file
|
||||||
const [path, cleanup] = await createTemp();
|
const [path, cleanup] = await createTemp();
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
|
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
|
||||||
import * as Redis from 'ioredis';
|
import * as Redis from 'ioredis';
|
||||||
import type { InstancesRepository } from '@/models/index.js';
|
import type { InstancesRepository } from '@/models/index.js';
|
||||||
import type { Instance } from '@/models/entities/Instance.js';
|
import type { MiInstance } from '@/models/entities/Instance.js';
|
||||||
import { MemoryKVCache, RedisKVCache } from '@/misc/cache.js';
|
import { MemoryKVCache, RedisKVCache } from '@/misc/cache.js';
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
@ -15,7 +15,7 @@ import { bindThis } from '@/decorators.js';
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class FederatedInstanceService implements OnApplicationShutdown {
|
export class FederatedInstanceService implements OnApplicationShutdown {
|
||||||
public federatedInstanceCache: RedisKVCache<Instance | null>;
|
public federatedInstanceCache: RedisKVCache<MiInstance | null>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.redis)
|
@Inject(DI.redis)
|
||||||
@ -27,7 +27,7 @@ export class FederatedInstanceService implements OnApplicationShutdown {
|
|||||||
private utilityService: UtilityService,
|
private utilityService: UtilityService,
|
||||||
private idService: IdService,
|
private idService: IdService,
|
||||||
) {
|
) {
|
||||||
this.federatedInstanceCache = new RedisKVCache<Instance | null>(this.redisClient, 'federatedInstance', {
|
this.federatedInstanceCache = new RedisKVCache<MiInstance | null>(this.redisClient, 'federatedInstance', {
|
||||||
lifetime: 1000 * 60 * 30, // 30m
|
lifetime: 1000 * 60 * 30, // 30m
|
||||||
memoryCacheLifetime: 1000 * 60 * 3, // 3m
|
memoryCacheLifetime: 1000 * 60 * 3, // 3m
|
||||||
fetcher: (key) => this.instancesRepository.findOneBy({ host: key }),
|
fetcher: (key) => this.instancesRepository.findOneBy({ host: key }),
|
||||||
@ -46,7 +46,7 @@ export class FederatedInstanceService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async fetch(host: string): Promise<Instance> {
|
public async fetch(host: string): Promise<MiInstance> {
|
||||||
host = this.utilityService.toPuny(host);
|
host = this.utilityService.toPuny(host);
|
||||||
|
|
||||||
const cached = await this.federatedInstanceCache.get(host);
|
const cached = await this.federatedInstanceCache.get(host);
|
||||||
@ -70,7 +70,7 @@ export class FederatedInstanceService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async update(id: Instance['id'], data: Partial<Instance>): Promise<void> {
|
public async update(id: MiInstance['id'], data: Partial<MiInstance>): Promise<void> {
|
||||||
const result = await this.instancesRepository.createQueryBuilder().update()
|
const result = await this.instancesRepository.createQueryBuilder().update()
|
||||||
.set(data)
|
.set(data)
|
||||||
.where('id = :id', { id })
|
.where('id = :id', { id })
|
||||||
|
@ -8,7 +8,7 @@ import { Inject, Injectable } from '@nestjs/common';
|
|||||||
import { JSDOM } from 'jsdom';
|
import { JSDOM } from 'jsdom';
|
||||||
import tinycolor from 'tinycolor2';
|
import tinycolor from 'tinycolor2';
|
||||||
import * as Redis from 'ioredis';
|
import * as Redis from 'ioredis';
|
||||||
import type { Instance } from '@/models/entities/Instance.js';
|
import type { MiInstance } from '@/models/entities/Instance.js';
|
||||||
import type Logger from '@/logger.js';
|
import type Logger from '@/logger.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { LoggerService } from '@/core/LoggerService.js';
|
import { LoggerService } from '@/core/LoggerService.js';
|
||||||
@ -62,7 +62,7 @@ export class FetchInstanceMetadataService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async fetchInstanceMetadata(instance: Instance, force = false): Promise<void> {
|
public async fetchInstanceMetadata(instance: MiInstance, force = false): Promise<void> {
|
||||||
const host = instance.host;
|
const host = instance.host;
|
||||||
// Acquire mutex to ensure no parallel runs
|
// Acquire mutex to ensure no parallel runs
|
||||||
if (!await this.tryLock(host)) return;
|
if (!await this.tryLock(host)) return;
|
||||||
@ -108,7 +108,7 @@ export class FetchInstanceMetadataService {
|
|||||||
|
|
||||||
if (name) updates.name = name;
|
if (name) updates.name = name;
|
||||||
if (description) updates.description = description;
|
if (description) updates.description = description;
|
||||||
if (icon || favicon) updates.iconUrl = (icon && !icon.includes('data:image/png;base64')) ? icon : favicon;
|
if (icon ?? favicon) updates.iconUrl = (icon && !icon.includes('data:image/png;base64')) ? icon : favicon;
|
||||||
if (favicon) updates.faviconUrl = favicon;
|
if (favicon) updates.faviconUrl = favicon;
|
||||||
if (themeColor) updates.themeColor = themeColor;
|
if (themeColor) updates.themeColor = themeColor;
|
||||||
|
|
||||||
@ -123,7 +123,7 @@ export class FetchInstanceMetadataService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async fetchNodeinfo(instance: Instance): Promise<NodeInfo> {
|
private async fetchNodeinfo(instance: MiInstance): Promise<NodeInfo> {
|
||||||
this.logger.info(`Fetching nodeinfo of ${instance.host} ...`);
|
this.logger.info(`Fetching nodeinfo of ${instance.host} ...`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -142,10 +142,10 @@ export class FetchInstanceMetadataService {
|
|||||||
|
|
||||||
const links = wellknown.links as any[];
|
const links = wellknown.links as any[];
|
||||||
|
|
||||||
const lnik1_0 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/1.0');
|
const link1_0 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/1.0');
|
||||||
const lnik2_0 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/2.0');
|
const link2_0 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/2.0');
|
||||||
const lnik2_1 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/2.1');
|
const link2_1 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/2.1');
|
||||||
const link = lnik2_1 ?? lnik2_0 ?? lnik1_0;
|
const link = link2_1 ?? link2_0 ?? link1_0;
|
||||||
|
|
||||||
if (link == null) {
|
if (link == null) {
|
||||||
throw new Error('No nodeinfo link provided');
|
throw new Error('No nodeinfo link provided');
|
||||||
@ -167,7 +167,7 @@ export class FetchInstanceMetadataService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async fetchDom(instance: Instance): Promise<DOMWindow['document']> {
|
private async fetchDom(instance: MiInstance): Promise<DOMWindow['document']> {
|
||||||
this.logger.info(`Fetching HTML of ${instance.host} ...`);
|
this.logger.info(`Fetching HTML of ${instance.host} ...`);
|
||||||
|
|
||||||
const url = 'https://' + instance.host;
|
const url = 'https://' + instance.host;
|
||||||
@ -181,7 +181,7 @@ export class FetchInstanceMetadataService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async fetchManifest(instance: Instance): Promise<Record<string, unknown> | null> {
|
private async fetchManifest(instance: MiInstance): Promise<Record<string, unknown> | null> {
|
||||||
const url = 'https://' + instance.host;
|
const url = 'https://' + instance.host;
|
||||||
|
|
||||||
const manifestUrl = url + '/manifest.json';
|
const manifestUrl = url + '/manifest.json';
|
||||||
@ -192,7 +192,7 @@ export class FetchInstanceMetadataService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async fetchFaviconUrl(instance: Instance, doc: DOMWindow['document'] | null): Promise<string | null> {
|
private async fetchFaviconUrl(instance: MiInstance, doc: DOMWindow['document'] | null): Promise<string | null> {
|
||||||
const url = 'https://' + instance.host;
|
const url = 'https://' + instance.host;
|
||||||
|
|
||||||
if (doc) {
|
if (doc) {
|
||||||
@ -218,7 +218,7 @@ export class FetchInstanceMetadataService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async fetchIconUrl(instance: Instance, doc: DOMWindow['document'] | null, manifest: Record<string, any> | null): Promise<string | null> {
|
private async fetchIconUrl(instance: MiInstance, doc: DOMWindow['document'] | null, manifest: Record<string, any> | null): Promise<string | null> {
|
||||||
if (manifest && manifest.icons && manifest.icons.length > 0 && manifest.icons[0].src) {
|
if (manifest && manifest.icons && manifest.icons.length > 0 && manifest.icons[0].src) {
|
||||||
const url = 'https://' + instance.host;
|
const url = 'https://' + instance.host;
|
||||||
return (new URL(manifest.icons[0].src, url)).href;
|
return (new URL(manifest.icons[0].src, url)).href;
|
||||||
|
@ -5,10 +5,10 @@
|
|||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import * as Redis from 'ioredis';
|
import * as Redis from 'ioredis';
|
||||||
import type { User } from '@/models/entities/User.js';
|
import type { MiUser } from '@/models/entities/User.js';
|
||||||
import type { Note } from '@/models/entities/Note.js';
|
import type { MiNote } from '@/models/entities/Note.js';
|
||||||
import type { UserList } from '@/models/entities/UserList.js';
|
import type { MiUserList } from '@/models/entities/UserList.js';
|
||||||
import type { Antenna } from '@/models/entities/Antenna.js';
|
import type { MiAntenna } from '@/models/entities/Antenna.js';
|
||||||
import type {
|
import type {
|
||||||
StreamChannels,
|
StreamChannels,
|
||||||
AdminStreamTypes,
|
AdminStreamTypes,
|
||||||
@ -25,7 +25,7 @@ import type { Packed } from '@/misc/json-schema.js';
|
|||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { Role } from '@/models/index.js';
|
import { MiRole } from '@/models/index.js';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class GlobalEventService {
|
export class GlobalEventService {
|
||||||
@ -61,17 +61,17 @@ export class GlobalEventService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public publishMainStream<K extends keyof MainStreamTypes>(userId: User['id'], type: K, value?: MainStreamTypes[K]): void {
|
public publishMainStream<K extends keyof MainStreamTypes>(userId: MiUser['id'], type: K, value?: MainStreamTypes[K]): void {
|
||||||
this.publish(`mainStream:${userId}`, type, typeof value === 'undefined' ? null : value);
|
this.publish(`mainStream:${userId}`, type, typeof value === 'undefined' ? null : value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public publishDriveStream<K extends keyof DriveStreamTypes>(userId: User['id'], type: K, value?: DriveStreamTypes[K]): void {
|
public publishDriveStream<K extends keyof DriveStreamTypes>(userId: MiUser['id'], type: K, value?: DriveStreamTypes[K]): void {
|
||||||
this.publish(`driveStream:${userId}`, type, typeof value === 'undefined' ? null : value);
|
this.publish(`driveStream:${userId}`, type, typeof value === 'undefined' ? null : value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public publishNoteStream<K extends keyof NoteStreamTypes>(noteId: Note['id'], type: K, value?: NoteStreamTypes[K]): void {
|
public publishNoteStream<K extends keyof NoteStreamTypes>(noteId: MiNote['id'], type: K, value?: NoteStreamTypes[K]): void {
|
||||||
this.publish(`noteStream:${noteId}`, type, {
|
this.publish(`noteStream:${noteId}`, type, {
|
||||||
id: noteId,
|
id: noteId,
|
||||||
body: value,
|
body: value,
|
||||||
@ -79,17 +79,17 @@ export class GlobalEventService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public publishUserListStream<K extends keyof UserListStreamTypes>(listId: UserList['id'], type: K, value?: UserListStreamTypes[K]): void {
|
public publishUserListStream<K extends keyof UserListStreamTypes>(listId: MiUserList['id'], type: K, value?: UserListStreamTypes[K]): void {
|
||||||
this.publish(`userListStream:${listId}`, type, typeof value === 'undefined' ? null : value);
|
this.publish(`userListStream:${listId}`, type, typeof value === 'undefined' ? null : value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public publishAntennaStream<K extends keyof AntennaStreamTypes>(antennaId: Antenna['id'], type: K, value?: AntennaStreamTypes[K]): void {
|
public publishAntennaStream<K extends keyof AntennaStreamTypes>(antennaId: MiAntenna['id'], type: K, value?: AntennaStreamTypes[K]): void {
|
||||||
this.publish(`antennaStream:${antennaId}`, type, typeof value === 'undefined' ? null : value);
|
this.publish(`antennaStream:${antennaId}`, type, typeof value === 'undefined' ? null : value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public publishRoleTimelineStream<K extends keyof RoleTimelineStreamTypes>(roleId: Role['id'], type: K, value?: RoleTimelineStreamTypes[K]): void {
|
public publishRoleTimelineStream<K extends keyof RoleTimelineStreamTypes>(roleId: MiRole['id'], type: K, value?: RoleTimelineStreamTypes[K]): void {
|
||||||
this.publish(`roleTimelineStream:${roleId}`, type, typeof value === 'undefined' ? null : value);
|
this.publish(`roleTimelineStream:${roleId}`, type, typeof value === 'undefined' ? null : value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,7 +99,7 @@ export class GlobalEventService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public publishAdminStream<K extends keyof AdminStreamTypes>(userId: User['id'], type: K, value?: AdminStreamTypes[K]): void {
|
public publishAdminStream<K extends keyof AdminStreamTypes>(userId: MiUser['id'], type: K, value?: AdminStreamTypes[K]): void {
|
||||||
this.publish(`adminStream:${userId}`, type, typeof value === 'undefined' ? null : value);
|
this.publish(`adminStream:${userId}`, type, typeof value === 'undefined' ? null : value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,20 +5,17 @@
|
|||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { User } from '@/models/entities/User.js';
|
import type { MiUser } from '@/models/entities/User.js';
|
||||||
import { normalizeForSearch } from '@/misc/normalize-for-search.js';
|
import { normalizeForSearch } from '@/misc/normalize-for-search.js';
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
import type { Hashtag } from '@/models/entities/Hashtag.js';
|
import type { MiHashtag } from '@/models/entities/Hashtag.js';
|
||||||
import type { HashtagsRepository, UsersRepository } from '@/models/index.js';
|
import type { HashtagsRepository } from '@/models/index.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class HashtagService {
|
export class HashtagService {
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.usersRepository)
|
|
||||||
private usersRepository: UsersRepository,
|
|
||||||
|
|
||||||
@Inject(DI.hashtagsRepository)
|
@Inject(DI.hashtagsRepository)
|
||||||
private hashtagsRepository: HashtagsRepository,
|
private hashtagsRepository: HashtagsRepository,
|
||||||
|
|
||||||
@ -28,14 +25,14 @@ export class HashtagService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async updateHashtags(user: { id: User['id']; host: User['host']; }, tags: string[]) {
|
public async updateHashtags(user: { id: MiUser['id']; host: MiUser['host']; }, tags: string[]) {
|
||||||
for (const tag of tags) {
|
for (const tag of tags) {
|
||||||
await this.updateHashtag(user, tag);
|
await this.updateHashtag(user, tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async updateUsertags(user: User, tags: string[]) {
|
public async updateUsertags(user: MiUser, tags: string[]) {
|
||||||
for (const tag of tags) {
|
for (const tag of tags) {
|
||||||
await this.updateHashtag(user, tag, true, true);
|
await this.updateHashtag(user, tag, true, true);
|
||||||
}
|
}
|
||||||
@ -46,7 +43,7 @@ export class HashtagService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async updateHashtag(user: { id: User['id']; host: User['host']; }, tag: string, isUserAttached = false, inc = true) {
|
public async updateHashtag(user: { id: MiUser['id']; host: MiUser['host']; }, tag: string, isUserAttached = false, inc = true) {
|
||||||
tag = normalizeForSearch(tag);
|
tag = normalizeForSearch(tag);
|
||||||
|
|
||||||
const index = await this.hashtagsRepository.findOneBy({ name: tag });
|
const index = await this.hashtagsRepository.findOneBy({ name: tag });
|
||||||
@ -126,7 +123,7 @@ export class HashtagService {
|
|||||||
attachedLocalUsersCount: this.userEntityService.isLocalUser(user) ? 1 : 0,
|
attachedLocalUsersCount: this.userEntityService.isLocalUser(user) ? 1 : 0,
|
||||||
attachedRemoteUserIds: this.userEntityService.isRemoteUser(user) ? [user.id] : [],
|
attachedRemoteUserIds: this.userEntityService.isRemoteUser(user) ? [user.id] : [],
|
||||||
attachedRemoteUsersCount: this.userEntityService.isRemoteUser(user) ? 1 : 0,
|
attachedRemoteUsersCount: this.userEntityService.isRemoteUser(user) ? 1 : 0,
|
||||||
} as Hashtag);
|
} as MiHashtag);
|
||||||
} else {
|
} else {
|
||||||
this.hashtagsRepository.insert({
|
this.hashtagsRepository.insert({
|
||||||
id: this.idService.genId(),
|
id: this.idService.genId(),
|
||||||
@ -143,7 +140,7 @@ export class HashtagService {
|
|||||||
attachedLocalUsersCount: 0,
|
attachedLocalUsersCount: 0,
|
||||||
attachedRemoteUserIds: [],
|
attachedRemoteUserIds: [],
|
||||||
attachedRemoteUsersCount: 0,
|
attachedRemoteUsersCount: 0,
|
||||||
} as Hashtag);
|
} as MiHashtag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,12 +53,14 @@ export class HttpRequestService {
|
|||||||
keepAlive: true,
|
keepAlive: true,
|
||||||
keepAliveMsecs: 30 * 1000,
|
keepAliveMsecs: 30 * 1000,
|
||||||
lookup: cache.lookup as unknown as net.LookupFunction,
|
lookup: cache.lookup as unknown as net.LookupFunction,
|
||||||
|
localAddress: config.outgoingAddress,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.https = new https.Agent({
|
this.https = new https.Agent({
|
||||||
keepAlive: true,
|
keepAlive: true,
|
||||||
keepAliveMsecs: 30 * 1000,
|
keepAliveMsecs: 30 * 1000,
|
||||||
lookup: cache.lookup as unknown as net.LookupFunction,
|
lookup: cache.lookup as unknown as net.LookupFunction,
|
||||||
|
localAddress: config.outgoingAddress,
|
||||||
});
|
});
|
||||||
|
|
||||||
const maxSockets = Math.max(256, config.deliverJobConcurrency ?? 128);
|
const maxSockets = Math.max(256, config.deliverJobConcurrency ?? 128);
|
||||||
@ -71,6 +73,7 @@ export class HttpRequestService {
|
|||||||
maxFreeSockets: 256,
|
maxFreeSockets: 256,
|
||||||
scheduling: 'lifo',
|
scheduling: 'lifo',
|
||||||
proxy: config.proxy,
|
proxy: config.proxy,
|
||||||
|
localAddress: config.outgoingAddress,
|
||||||
})
|
})
|
||||||
: this.http;
|
: this.http;
|
||||||
|
|
||||||
@ -82,6 +85,7 @@ export class HttpRequestService {
|
|||||||
maxFreeSockets: 256,
|
maxFreeSockets: 256,
|
||||||
scheduling: 'lifo',
|
scheduling: 'lifo',
|
||||||
proxy: config.proxy,
|
proxy: config.proxy,
|
||||||
|
localAddress: config.outgoingAddress,
|
||||||
})
|
})
|
||||||
: this.https;
|
: this.https;
|
||||||
}
|
}
|
||||||
@ -93,7 +97,7 @@ export class HttpRequestService {
|
|||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public getAgentByUrl(url: URL, bypassProxy = false): http.Agent | https.Agent {
|
public getAgentByUrl(url: URL, bypassProxy = false): http.Agent | https.Agent {
|
||||||
if (bypassProxy || (this.config.proxyBypassHosts || []).includes(url.hostname)) {
|
if (bypassProxy || (this.config.proxyBypassHosts ?? []).includes(url.hostname)) {
|
||||||
return url.protocol === 'http:' ? this.http : this.https;
|
return url.protocol === 'http:' ? this.http : this.https;
|
||||||
} else {
|
} else {
|
||||||
return url.protocol === 'http:' ? this.httpAgent : this.httpsAgent;
|
return url.protocol === 'http:' ? this.httpAgent : this.httpsAgent;
|
||||||
|
@ -3,10 +3,8 @@
|
|||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import sharp from 'sharp';
|
import sharp from 'sharp';
|
||||||
import { DI } from '@/di-symbols.js';
|
|
||||||
import type { Config } from '@/config.js';
|
|
||||||
|
|
||||||
export type IImage = {
|
export type IImage = {
|
||||||
data: Buffer;
|
data: Buffer;
|
||||||
@ -50,8 +48,6 @@ import { Readable } from 'node:stream';
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class ImageProcessingService {
|
export class ImageProcessingService {
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.config)
|
|
||||||
private config: Config,
|
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { IsNull } from 'typeorm';
|
import { IsNull } from 'typeorm';
|
||||||
import type { LocalUser } from '@/models/entities/User.js';
|
import type { MiLocalUser } from '@/models/entities/User.js';
|
||||||
import type { UsersRepository } from '@/models/index.js';
|
import type { UsersRepository } from '@/models/index.js';
|
||||||
import { MemorySingleCache } from '@/misc/cache.js';
|
import { MemorySingleCache } from '@/misc/cache.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
@ -16,7 +16,7 @@ const ACTOR_USERNAME = 'instance.actor' as const;
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class InstanceActorService {
|
export class InstanceActorService {
|
||||||
private cache: MemorySingleCache<LocalUser>;
|
private cache: MemorySingleCache<MiLocalUser>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.usersRepository)
|
@Inject(DI.usersRepository)
|
||||||
@ -24,24 +24,24 @@ export class InstanceActorService {
|
|||||||
|
|
||||||
private createSystemUserService: CreateSystemUserService,
|
private createSystemUserService: CreateSystemUserService,
|
||||||
) {
|
) {
|
||||||
this.cache = new MemorySingleCache<LocalUser>(Infinity);
|
this.cache = new MemorySingleCache<MiLocalUser>(Infinity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async getInstanceActor(): Promise<LocalUser> {
|
public async getInstanceActor(): Promise<MiLocalUser> {
|
||||||
const cached = this.cache.get();
|
const cached = this.cache.get();
|
||||||
if (cached) return cached;
|
if (cached) return cached;
|
||||||
|
|
||||||
const user = await this.usersRepository.findOneBy({
|
const user = await this.usersRepository.findOneBy({
|
||||||
host: IsNull(),
|
host: IsNull(),
|
||||||
username: ACTOR_USERNAME,
|
username: ACTOR_USERNAME,
|
||||||
}) as LocalUser | undefined;
|
}) as MiLocalUser | undefined;
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
this.cache.set(user);
|
this.cache.set(user);
|
||||||
return user;
|
return user;
|
||||||
} else {
|
} else {
|
||||||
const created = await this.createSystemUserService.createSystemUser(ACTOR_USERNAME) as LocalUser;
|
const created = await this.createSystemUserService.createSystemUser(ACTOR_USERNAME) as MiLocalUser;
|
||||||
this.cache.set(created);
|
this.cache.set(created);
|
||||||
return created;
|
return created;
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,7 @@
|
|||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { DI } from '@/di-symbols.js';
|
|
||||||
import type { Config } from '@/config.js';
|
|
||||||
import Logger from '@/logger.js';
|
import Logger from '@/logger.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import type { KEYWORD } from 'color-convert/conversions.js';
|
import type { KEYWORD } from 'color-convert/conversions.js';
|
||||||
@ -13,8 +11,6 @@ import type { KEYWORD } from 'color-convert/conversions.js';
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class LoggerService {
|
export class LoggerService {
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.config)
|
|
||||||
private config: Config,
|
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import { Inject, Injectable } from '@nestjs/common';
|
|||||||
import { DataSource } from 'typeorm';
|
import { DataSource } from 'typeorm';
|
||||||
import * as Redis from 'ioredis';
|
import * as Redis from 'ioredis';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { Meta } from '@/models/entities/Meta.js';
|
import { MiMeta } from '@/models/entities/Meta.js';
|
||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { StreamMessages } from '@/server/api/stream/types.js';
|
import { StreamMessages } from '@/server/api/stream/types.js';
|
||||||
@ -15,7 +15,7 @@ import type { OnApplicationShutdown } from '@nestjs/common';
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MetaService implements OnApplicationShutdown {
|
export class MetaService implements OnApplicationShutdown {
|
||||||
private cache: Meta | undefined;
|
private cache: MiMeta | undefined;
|
||||||
private intervalId: NodeJS.Timer;
|
private intervalId: NodeJS.Timer;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -59,12 +59,12 @@ export class MetaService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async fetch(noCache = false): Promise<Meta> {
|
public async fetch(noCache = false): Promise<MiMeta> {
|
||||||
if (!noCache && this.cache) return this.cache;
|
if (!noCache && this.cache) return this.cache;
|
||||||
|
|
||||||
return await this.db.transaction(async transactionalEntityManager => {
|
return await this.db.transaction(async transactionalEntityManager => {
|
||||||
// 過去のバグでレコードが複数出来てしまっている可能性があるので新しいIDを優先する
|
// 過去のバグでレコードが複数出来てしまっている可能性があるので新しいIDを優先する
|
||||||
const metas = await transactionalEntityManager.find(Meta, {
|
const metas = await transactionalEntityManager.find(MiMeta, {
|
||||||
order: {
|
order: {
|
||||||
id: 'DESC',
|
id: 'DESC',
|
||||||
},
|
},
|
||||||
@ -79,13 +79,13 @@ export class MetaService implements OnApplicationShutdown {
|
|||||||
// metaが空のときfetchMetaが同時に呼ばれるとここが同時に呼ばれてしまうことがあるのでフェイルセーフなupsertを使う
|
// metaが空のときfetchMetaが同時に呼ばれるとここが同時に呼ばれてしまうことがあるのでフェイルセーフなupsertを使う
|
||||||
const saved = await transactionalEntityManager
|
const saved = await transactionalEntityManager
|
||||||
.upsert(
|
.upsert(
|
||||||
Meta,
|
MiMeta,
|
||||||
{
|
{
|
||||||
id: 'x',
|
id: 'x',
|
||||||
},
|
},
|
||||||
['id'],
|
['id'],
|
||||||
)
|
)
|
||||||
.then((x) => transactionalEntityManager.findOneByOrFail(Meta, x.identifiers[0]));
|
.then((x) => transactionalEntityManager.findOneByOrFail(MiMeta, x.identifiers[0]));
|
||||||
|
|
||||||
this.cache = saved;
|
this.cache = saved;
|
||||||
return saved;
|
return saved;
|
||||||
@ -94,9 +94,9 @@ export class MetaService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async update(data: Partial<Meta>): Promise<Meta> {
|
public async update(data: Partial<MiMeta>): Promise<MiMeta> {
|
||||||
const updated = await this.db.transaction(async transactionalEntityManager => {
|
const updated = await this.db.transaction(async transactionalEntityManager => {
|
||||||
const metas = await transactionalEntityManager.find(Meta, {
|
const metas = await transactionalEntityManager.find(MiMeta, {
|
||||||
order: {
|
order: {
|
||||||
id: 'DESC',
|
id: 'DESC',
|
||||||
},
|
},
|
||||||
@ -105,9 +105,9 @@ export class MetaService implements OnApplicationShutdown {
|
|||||||
const meta = metas[0];
|
const meta = metas[0];
|
||||||
|
|
||||||
if (meta) {
|
if (meta) {
|
||||||
await transactionalEntityManager.update(Meta, meta.id, data);
|
await transactionalEntityManager.update(MiMeta, meta.id, data);
|
||||||
|
|
||||||
const metas = await transactionalEntityManager.find(Meta, {
|
const metas = await transactionalEntityManager.find(MiMeta, {
|
||||||
order: {
|
order: {
|
||||||
id: 'DESC',
|
id: 'DESC',
|
||||||
},
|
},
|
||||||
@ -115,7 +115,7 @@ export class MetaService implements OnApplicationShutdown {
|
|||||||
|
|
||||||
return metas[0];
|
return metas[0];
|
||||||
} else {
|
} else {
|
||||||
return await transactionalEntityManager.save(Meta, data);
|
return await transactionalEntityManager.save(MiMeta, data);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { ModerationLogsRepository } from '@/models/index.js';
|
import type { ModerationLogsRepository } from '@/models/index.js';
|
||||||
import type { User } from '@/models/entities/User.js';
|
import type { MiUser } from '@/models/entities/User.js';
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ export class ModerationLogService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async insertModerationLog(moderator: { id: User['id'] }, type: string, info?: Record<string, any>) {
|
public async insertModerationLog(moderator: { id: MiUser['id'] }, type: string, info?: Record<string, any>) {
|
||||||
await this.moderationLogsRepository.insert({
|
await this.moderationLogsRepository.insert({
|
||||||
id: this.idService.genId(),
|
id: this.idService.genId(),
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
|
@ -13,22 +13,21 @@ import { extractMentions } from '@/misc/extract-mentions.js';
|
|||||||
import { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mfm.js';
|
import { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mfm.js';
|
||||||
import { extractHashtags } from '@/misc/extract-hashtags.js';
|
import { extractHashtags } from '@/misc/extract-hashtags.js';
|
||||||
import type { IMentionedRemoteUsers } from '@/models/entities/Note.js';
|
import type { IMentionedRemoteUsers } from '@/models/entities/Note.js';
|
||||||
import { Note } from '@/models/entities/Note.js';
|
import { MiNote } from '@/models/entities/Note.js';
|
||||||
import { Event, IEvent } from '@/models/entities/Event.js';
|
import type { ChannelsRepository, InstancesRepository, MutedNotesRepository, MutingsRepository, NotesRepository, NoteThreadMutingsRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js';
|
||||||
import type { ChannelFollowingsRepository, ChannelsRepository, InstancesRepository, MutedNotesRepository, MutingsRepository, NotesRepository, NoteThreadMutingsRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js';
|
import type { MiDriveFile } from '@/models/entities/DriveFile.js';
|
||||||
import type { DriveFile } from '@/models/entities/DriveFile.js';
|
import type { MiApp } from '@/models/entities/App.js';
|
||||||
import type { App } from '@/models/entities/App.js';
|
|
||||||
import { concat } from '@/misc/prelude/array.js';
|
import { concat } from '@/misc/prelude/array.js';
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
import type { User, LocalUser, RemoteUser } from '@/models/entities/User.js';
|
import type { MiUser, MiLocalUser, MiRemoteUser } from '@/models/entities/User.js';
|
||||||
import type { IPoll } from '@/models/entities/Poll.js';
|
import type { IPoll } from '@/models/entities/Poll.js';
|
||||||
import { Poll } from '@/models/entities/Poll.js';
|
import { MiPoll } from '@/models/entities/Poll.js';
|
||||||
import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js';
|
import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js';
|
||||||
import { checkWordMute } from '@/misc/check-word-mute.js';
|
import { checkWordMute } from '@/misc/check-word-mute.js';
|
||||||
import type { Channel } from '@/models/entities/Channel.js';
|
import type { MiChannel } from '@/models/entities/Channel.js';
|
||||||
import { normalizeForSearch } from '@/misc/normalize-for-search.js';
|
import { normalizeForSearch } from '@/misc/normalize-for-search.js';
|
||||||
import { MemorySingleCache } from '@/misc/cache.js';
|
import { MemorySingleCache } from '@/misc/cache.js';
|
||||||
import type { UserProfile } from '@/models/entities/UserProfile.js';
|
import type { MiUserProfile } from '@/models/entities/UserProfile.js';
|
||||||
import { RelayService } from '@/core/RelayService.js';
|
import { RelayService } from '@/core/RelayService.js';
|
||||||
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
|
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
@ -55,23 +54,23 @@ import { RoleService } from '@/core/RoleService.js';
|
|||||||
import { MetaService } from '@/core/MetaService.js';
|
import { MetaService } from '@/core/MetaService.js';
|
||||||
import { SearchService } from '@/core/SearchService.js';
|
import { SearchService } from '@/core/SearchService.js';
|
||||||
|
|
||||||
const mutedWordsCache = new MemorySingleCache<{ userId: UserProfile['userId']; mutedWords: UserProfile['mutedWords']; }[]>(1000 * 60 * 5);
|
const mutedWordsCache = new MemorySingleCache<{ userId: MiUserProfile['userId']; mutedWords: MiUserProfile['mutedWords']; }[]>(1000 * 60 * 5);
|
||||||
|
|
||||||
type NotificationType = 'reply' | 'renote' | 'quote' | 'mention';
|
type NotificationType = 'reply' | 'renote' | 'quote' | 'mention';
|
||||||
|
|
||||||
class NotificationManager {
|
class NotificationManager {
|
||||||
private notifier: { id: User['id']; };
|
private notifier: { id: MiUser['id']; };
|
||||||
private note: Note;
|
private note: MiNote;
|
||||||
private queue: {
|
private queue: {
|
||||||
target: LocalUser['id'];
|
target: MiLocalUser['id'];
|
||||||
reason: NotificationType;
|
reason: NotificationType;
|
||||||
}[];
|
}[];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private mutingsRepository: MutingsRepository,
|
private mutingsRepository: MutingsRepository,
|
||||||
private notificationService: NotificationService,
|
private notificationService: NotificationService,
|
||||||
notifier: { id: User['id']; },
|
notifier: { id: MiUser['id']; },
|
||||||
note: Note,
|
note: MiNote,
|
||||||
) {
|
) {
|
||||||
this.notifier = notifier;
|
this.notifier = notifier;
|
||||||
this.note = note;
|
this.note = note;
|
||||||
@ -79,7 +78,7 @@ class NotificationManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public push(notifiee: LocalUser['id'], reason: NotificationType) {
|
public push(notifiee: MiLocalUser['id'], reason: NotificationType) {
|
||||||
// 自分自身へは通知しない
|
// 自分自身へは通知しない
|
||||||
if (this.notifier.id === notifiee) return;
|
if (this.notifier.id === notifiee) return;
|
||||||
|
|
||||||
@ -120,33 +119,33 @@ class NotificationManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type MinimumUser = {
|
type MinimumUser = {
|
||||||
id: User['id'];
|
id: MiUser['id'];
|
||||||
host: User['host'];
|
host: MiUser['host'];
|
||||||
username: User['username'];
|
username: MiUser['username'];
|
||||||
uri: User['uri'];
|
uri: MiUser['uri'];
|
||||||
};
|
};
|
||||||
|
|
||||||
type Option = {
|
type Option = {
|
||||||
createdAt?: Date | null;
|
createdAt?: Date | null;
|
||||||
name?: string | null;
|
name?: string | null;
|
||||||
text?: string | null;
|
text?: string | null;
|
||||||
reply?: Note | null;
|
reply?: MiNote | null;
|
||||||
renote?: Note | null;
|
renote?: MiNote | null;
|
||||||
files?: DriveFile[] | null;
|
files?: MiDriveFile[] | null;
|
||||||
poll?: IPoll | null;
|
poll?: IPoll | null;
|
||||||
event?: IEvent | null;
|
event?: IEvent | null;
|
||||||
localOnly?: boolean | null;
|
localOnly?: boolean | null;
|
||||||
reactionAcceptance?: Note['reactionAcceptance'];
|
reactionAcceptance?: MiNote['reactionAcceptance'];
|
||||||
cw?: string | null;
|
cw?: string | null;
|
||||||
visibility?: string;
|
visibility?: string;
|
||||||
visibleUsers?: MinimumUser[] | null;
|
visibleUsers?: MinimumUser[] | null;
|
||||||
channel?: Channel | null;
|
channel?: MiChannel | null;
|
||||||
apMentions?: MinimumUser[] | null;
|
apMentions?: MinimumUser[] | null;
|
||||||
apHashtags?: string[] | null;
|
apHashtags?: string[] | null;
|
||||||
apEmojis?: string[] | null;
|
apEmojis?: string[] | null;
|
||||||
uri?: string | null;
|
uri?: string | null;
|
||||||
url?: string | null;
|
url?: string | null;
|
||||||
app?: App | null;
|
app?: MiApp | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -184,9 +183,6 @@ export class NoteCreateService implements OnApplicationShutdown {
|
|||||||
@Inject(DI.channelsRepository)
|
@Inject(DI.channelsRepository)
|
||||||
private channelsRepository: ChannelsRepository,
|
private channelsRepository: ChannelsRepository,
|
||||||
|
|
||||||
@Inject(DI.channelFollowingsRepository)
|
|
||||||
private channelFollowingsRepository: ChannelFollowingsRepository,
|
|
||||||
|
|
||||||
@Inject(DI.noteThreadMutingsRepository)
|
@Inject(DI.noteThreadMutingsRepository)
|
||||||
private noteThreadMutingsRepository: NoteThreadMutingsRepository,
|
private noteThreadMutingsRepository: NoteThreadMutingsRepository,
|
||||||
|
|
||||||
@ -216,12 +212,12 @@ export class NoteCreateService implements OnApplicationShutdown {
|
|||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async create(user: {
|
public async create(user: {
|
||||||
id: User['id'];
|
id: MiUser['id'];
|
||||||
username: User['username'];
|
username: MiUser['username'];
|
||||||
host: User['host'];
|
host: MiUser['host'];
|
||||||
createdAt: User['createdAt'];
|
createdAt: MiUser['createdAt'];
|
||||||
isBot: User['isBot'];
|
isBot: MiUser['isBot'];
|
||||||
}, data: Option, silent = false): Promise<Note> {
|
}, data: Option, silent = false): Promise<MiNote> {
|
||||||
// チャンネル外にリプライしたら対象のスコープに合わせる
|
// チャンネル外にリプライしたら対象のスコープに合わせる
|
||||||
// (クライアントサイドでやっても良い処理だと思うけどとりあえずサーバーサイドで)
|
// (クライアントサイドでやっても良い処理だと思うけどとりあえずサーバーサイドで)
|
||||||
if (data.reply && data.channel && data.reply.channelId !== data.channel.id) {
|
if (data.reply && data.channel && data.reply.channelId !== data.channel.id) {
|
||||||
@ -353,8 +349,8 @@ export class NoteCreateService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async insertNote(user: { id: User['id']; host: User['host']; }, data: Option, tags: string[], emojis: string[], mentionedUsers: MinimumUser[]) {
|
private async insertNote(user: { id: MiUser['id']; host: MiUser['host']; }, data: Option, tags: string[], emojis: string[], mentionedUsers: MinimumUser[]) {
|
||||||
const insert = new Note({
|
const insert = new MiNote({
|
||||||
id: this.idService.genId(data.createdAt!),
|
id: this.idService.genId(data.createdAt!),
|
||||||
createdAt: data.createdAt!,
|
createdAt: data.createdAt!,
|
||||||
fileIds: data.files ? data.files.map(file => file.id) : [],
|
fileIds: data.files ? data.files.map(file => file.id) : [],
|
||||||
@ -370,7 +366,7 @@ export class NoteCreateService implements OnApplicationShutdown {
|
|||||||
text: data.text,
|
text: data.text,
|
||||||
hasPoll: data.poll != null,
|
hasPoll: data.poll != null,
|
||||||
hasEvent: data.event != null,
|
hasEvent: data.event != null,
|
||||||
cw: data.cw == null ? null : data.cw,
|
cw: data.cw ?? null,
|
||||||
tags: tags.map(tag => normalizeForSearch(tag)),
|
tags: tags.map(tag => normalizeForSearch(tag)),
|
||||||
emojis,
|
emojis,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
@ -405,7 +401,7 @@ export class NoteCreateService implements OnApplicationShutdown {
|
|||||||
const url = profile != null ? profile.url : null;
|
const url = profile != null ? profile.url : null;
|
||||||
return {
|
return {
|
||||||
uri: u.uri,
|
uri: u.uri,
|
||||||
url: url == null ? undefined : url,
|
url: url ?? undefined,
|
||||||
username: u.username,
|
username: u.username,
|
||||||
host: u.host,
|
host: u.host,
|
||||||
} as IMentionedRemoteUsers[0];
|
} as IMentionedRemoteUsers[0];
|
||||||
@ -417,10 +413,10 @@ export class NoteCreateService implements OnApplicationShutdown {
|
|||||||
if (insert.hasPoll || insert.hasEvent) {
|
if (insert.hasPoll || insert.hasEvent) {
|
||||||
// Start transaction
|
// Start transaction
|
||||||
await this.db.transaction(async transactionalEntityManager => {
|
await this.db.transaction(async transactionalEntityManager => {
|
||||||
await transactionalEntityManager.insert(Note, insert);
|
await transactionalEntityManager.insert(MiNote, insert);
|
||||||
|
|
||||||
if (insert.hasPoll) {
|
if (insert.hasPoll) {
|
||||||
const poll = new Poll({
|
const poll = new MiPoll({
|
||||||
noteId: insert.id,
|
noteId: insert.id,
|
||||||
choices: data.poll!.choices,
|
choices: data.poll!.choices,
|
||||||
expiresAt: data.poll!.expiresAt,
|
expiresAt: data.poll!.expiresAt,
|
||||||
@ -431,11 +427,11 @@ export class NoteCreateService implements OnApplicationShutdown {
|
|||||||
userHost: user.host,
|
userHost: user.host,
|
||||||
});
|
});
|
||||||
|
|
||||||
await transactionalEntityManager.insert(Poll, poll);
|
await transactionalEntityManager.insert(MiPoll, poll);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (insert.hasEvent) {
|
if (insert.hasEvent) {
|
||||||
const event = new Event({
|
const event = new MiEvent({
|
||||||
noteId: insert.id,
|
noteId: insert.id,
|
||||||
start: data.event!.start,
|
start: data.event!.start,
|
||||||
end: data.event!.end ?? undefined,
|
end: data.event!.end ?? undefined,
|
||||||
@ -446,7 +442,7 @@ export class NoteCreateService implements OnApplicationShutdown {
|
|||||||
userHost: user.host,
|
userHost: user.host,
|
||||||
});
|
});
|
||||||
|
|
||||||
await transactionalEntityManager.insert(Event, event);
|
await transactionalEntityManager.insert(MiEvent, event);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -469,12 +465,12 @@ export class NoteCreateService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async postNoteCreated(note: Note, user: {
|
private async postNoteCreated(note: MiNote, user: {
|
||||||
id: User['id'];
|
id: MiUser['id'];
|
||||||
username: User['username'];
|
username: MiUser['username'];
|
||||||
host: User['host'];
|
host: MiUser['host'];
|
||||||
createdAt: User['createdAt'];
|
createdAt: MiUser['createdAt'];
|
||||||
isBot: User['isBot'];
|
isBot: MiUser['isBot'];
|
||||||
}, data: Option, silent: boolean, tags: string[], mentionedUsers: MinimumUser[]) {
|
}, data: Option, silent: boolean, tags: string[], mentionedUsers: MinimumUser[]) {
|
||||||
const meta = await this.metaService.fetch();
|
const meta = await this.metaService.fetch();
|
||||||
|
|
||||||
@ -648,7 +644,7 @@ export class NoteCreateService implements OnApplicationShutdown {
|
|||||||
|
|
||||||
// メンションされたリモートユーザーに配送
|
// メンションされたリモートユーザーに配送
|
||||||
for (const u of mentionedUsers.filter(u => this.userEntityService.isRemoteUser(u))) {
|
for (const u of mentionedUsers.filter(u => this.userEntityService.isRemoteUser(u))) {
|
||||||
dm.addDirectRecipe(u as RemoteUser);
|
dm.addDirectRecipe(u as MiRemoteUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 投稿がリプライかつ投稿者がローカルユーザーかつリプライ先の投稿の投稿者がリモートユーザーなら配送
|
// 投稿がリプライかつ投稿者がローカルユーザーかつリプライ先の投稿の投稿者がリモートユーザーなら配送
|
||||||
@ -726,7 +722,7 @@ export class NoteCreateService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private incRenoteCount(renote: Note) {
|
private incRenoteCount(renote: MiNote) {
|
||||||
this.notesRepository.createQueryBuilder().update()
|
this.notesRepository.createQueryBuilder().update()
|
||||||
.set({
|
.set({
|
||||||
renoteCount: () => '"renoteCount" + 1',
|
renoteCount: () => '"renoteCount" + 1',
|
||||||
@ -737,7 +733,7 @@ export class NoteCreateService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async createMentionedEvents(mentionedUsers: MinimumUser[], note: Note, nm: NotificationManager) {
|
private async createMentionedEvents(mentionedUsers: MinimumUser[], note: MiNote, nm: NotificationManager) {
|
||||||
for (const u of mentionedUsers.filter(u => this.userEntityService.isLocalUser(u))) {
|
for (const u of mentionedUsers.filter(u => this.userEntityService.isLocalUser(u))) {
|
||||||
const isThreadMuted = await this.noteThreadMutingsRepository.exist({
|
const isThreadMuted = await this.noteThreadMutingsRepository.exist({
|
||||||
where: {
|
where: {
|
||||||
@ -769,12 +765,12 @@ export class NoteCreateService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private saveReply(reply: Note, note: Note) {
|
private saveReply(reply: MiNote, note: MiNote) {
|
||||||
this.notesRepository.increment({ id: reply.id }, 'repliesCount', 1);
|
this.notesRepository.increment({ id: reply.id }, 'repliesCount', 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async renderNoteOrRenoteActivity(data: Option, note: Note) {
|
private async renderNoteOrRenoteActivity(data: Option, note: MiNote) {
|
||||||
if (data.localOnly) return null;
|
if (data.localOnly) return null;
|
||||||
|
|
||||||
const content = data.renote && data.text == null && data.poll == null && (data.files == null || data.files.length === 0)
|
const content = data.renote && data.text == null && data.poll == null && (data.files == null || data.files.length === 0)
|
||||||
@ -785,14 +781,14 @@ export class NoteCreateService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private index(note: Note) {
|
private index(note: MiNote) {
|
||||||
if (note.text == null && note.cw == null) return;
|
if (note.text == null && note.cw == null) return;
|
||||||
|
|
||||||
this.searchService.indexNote(note);
|
this.searchService.indexNote(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private incNotesCountOfUser(user: { id: User['id']; }) {
|
private incNotesCountOfUser(user: { id: MiUser['id']; }) {
|
||||||
this.usersRepository.createQueryBuilder().update()
|
this.usersRepository.createQueryBuilder().update()
|
||||||
.set({
|
.set({
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
@ -803,13 +799,13 @@ export class NoteCreateService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async extractMentionedUsers(user: { host: User['host']; }, tokens: mfm.MfmNode[]): Promise<User[]> {
|
private async extractMentionedUsers(user: { host: MiUser['host']; }, tokens: mfm.MfmNode[]): Promise<MiUser[]> {
|
||||||
if (tokens == null) return [];
|
if (tokens == null) return [];
|
||||||
|
|
||||||
const mentions = extractMentions(tokens);
|
const mentions = extractMentions(tokens);
|
||||||
let mentionedUsers = (await Promise.all(mentions.map(m =>
|
let mentionedUsers = (await Promise.all(mentions.map(m =>
|
||||||
this.remoteUserResolveService.resolveUser(m.username, m.host ?? user.host).catch(() => null),
|
this.remoteUserResolveService.resolveUser(m.username, m.host ?? user.host).catch(() => null),
|
||||||
))).filter(x => x != null) as User[];
|
))).filter(x => x != null) as MiUser[];
|
||||||
|
|
||||||
// Drop duplicate users
|
// Drop duplicate users
|
||||||
mentionedUsers = mentionedUsers.filter((u, i, self) =>
|
mentionedUsers = mentionedUsers.filter((u, i, self) =>
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
|
|
||||||
import { Brackets, In } from 'typeorm';
|
import { Brackets, In } from 'typeorm';
|
||||||
import { Injectable, Inject } from '@nestjs/common';
|
import { Injectable, Inject } from '@nestjs/common';
|
||||||
import type { User, LocalUser, RemoteUser } from '@/models/entities/User.js';
|
import type { MiUser, MiLocalUser, MiRemoteUser } from '@/models/entities/User.js';
|
||||||
import type { Note, IMentionedRemoteUsers } from '@/models/entities/Note.js';
|
import type { MiNote, IMentionedRemoteUsers } from '@/models/entities/Note.js';
|
||||||
import type { InstancesRepository, NotesRepository, UsersRepository } from '@/models/index.js';
|
import type { InstancesRepository, NotesRepository, UsersRepository } from '@/models/index.js';
|
||||||
import { RelayService } from '@/core/RelayService.js';
|
import { RelayService } from '@/core/RelayService.js';
|
||||||
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
|
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
|
||||||
@ -58,7 +58,7 @@ export class NoteDeleteService {
|
|||||||
* @param user 投稿者
|
* @param user 投稿者
|
||||||
* @param note 投稿
|
* @param note 投稿
|
||||||
*/
|
*/
|
||||||
async delete(user: { id: User['id']; uri: User['uri']; host: User['host']; isBot: User['isBot']; }, note: Note, quiet = false) {
|
async delete(user: { id: MiUser['id']; uri: MiUser['uri']; host: MiUser['host']; isBot: MiUser['isBot']; }, note: MiNote, quiet = false) {
|
||||||
const deletedAt = new Date();
|
const deletedAt = new Date();
|
||||||
const cascadingNotes = await this.findCascadingNotes(note);
|
const cascadingNotes = await this.findCascadingNotes(note);
|
||||||
|
|
||||||
@ -79,7 +79,7 @@ export class NoteDeleteService {
|
|||||||
|
|
||||||
//#region ローカルの投稿なら削除アクティビティを配送
|
//#region ローカルの投稿なら削除アクティビティを配送
|
||||||
if (this.userEntityService.isLocalUser(user) && !note.localOnly) {
|
if (this.userEntityService.isLocalUser(user) && !note.localOnly) {
|
||||||
let renote: Note | null = null;
|
let renote: MiNote | null = null;
|
||||||
|
|
||||||
// if deletd note is renote
|
// if deletd note is renote
|
||||||
if (note.renoteId && note.text == null && !note.hasPoll && (note.fileIds == null || note.fileIds.length === 0)) {
|
if (note.renoteId && note.text == null && !note.hasPoll && (note.fileIds == null || note.fileIds.length === 0)) {
|
||||||
@ -134,8 +134,8 @@ export class NoteDeleteService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async findCascadingNotes(note: Note): Promise<Note[]> {
|
private async findCascadingNotes(note: MiNote): Promise<MiNote[]> {
|
||||||
const recursive = async (noteId: string): Promise<Note[]> => {
|
const recursive = async (noteId: string): Promise<MiNote[]> => {
|
||||||
const query = this.notesRepository.createQueryBuilder('note')
|
const query = this.notesRepository.createQueryBuilder('note')
|
||||||
.where('note.replyId = :noteId', { noteId })
|
.where('note.replyId = :noteId', { noteId })
|
||||||
.orWhere(new Brackets(q => {
|
.orWhere(new Brackets(q => {
|
||||||
@ -151,13 +151,13 @@ export class NoteDeleteService {
|
|||||||
].flat();
|
].flat();
|
||||||
};
|
};
|
||||||
|
|
||||||
const cascadingNotes: Note[] = await recursive(note.id);
|
const cascadingNotes: MiNote[] = await recursive(note.id);
|
||||||
|
|
||||||
return cascadingNotes;
|
return cascadingNotes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async getMentionedRemoteUsers(note: Note) {
|
private async getMentionedRemoteUsers(note: MiNote) {
|
||||||
const where = [] as any[];
|
const where = [] as any[];
|
||||||
|
|
||||||
// mention / reply / dm
|
// mention / reply / dm
|
||||||
@ -179,11 +179,11 @@ export class NoteDeleteService {
|
|||||||
|
|
||||||
return await this.usersRepository.find({
|
return await this.usersRepository.find({
|
||||||
where,
|
where,
|
||||||
}) as RemoteUser[];
|
}) as MiRemoteUser[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async deliverToConcerned(user: { id: LocalUser['id']; host: null; }, note: Note, content: any) {
|
private async deliverToConcerned(user: { id: MiLocalUser['id']; host: null; }, note: MiNote, content: any) {
|
||||||
this.apDeliverManagerService.deliverToFollowers(user, content);
|
this.apDeliverManagerService.deliverToFollowers(user, content);
|
||||||
this.relayService.deliverToRelays(user, content);
|
this.relayService.deliverToRelays(user, content);
|
||||||
const remoteUsers = await this.getMentionedRemoteUsers(note);
|
const remoteUsers = await this.getMentionedRemoteUsers(note);
|
||||||
|
@ -7,10 +7,10 @@ import { Inject, Injectable } from '@nestjs/common';
|
|||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { NotesRepository, UserNotePiningsRepository, UsersRepository } from '@/models/index.js';
|
import type { NotesRepository, UserNotePiningsRepository, UsersRepository } from '@/models/index.js';
|
||||||
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
||||||
import type { User } from '@/models/entities/User.js';
|
import type { MiUser } from '@/models/entities/User.js';
|
||||||
import type { Note } from '@/models/entities/Note.js';
|
import type { MiNote } from '@/models/entities/Note.js';
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
import type { UserNotePining } from '@/models/entities/UserNotePining.js';
|
import type { MiUserNotePining } from '@/models/entities/UserNotePining.js';
|
||||||
import { RelayService } from '@/core/RelayService.js';
|
import { RelayService } from '@/core/RelayService.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
@ -49,7 +49,7 @@ export class NotePiningService {
|
|||||||
* @param noteId
|
* @param noteId
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async addPinned(user: { id: User['id']; host: User['host']; }, noteId: Note['id']) {
|
public async addPinned(user: { id: MiUser['id']; host: MiUser['host']; }, noteId: MiNote['id']) {
|
||||||
// Fetch pinee
|
// Fetch pinee
|
||||||
const note = await this.notesRepository.findOneBy({
|
const note = await this.notesRepository.findOneBy({
|
||||||
id: noteId,
|
id: noteId,
|
||||||
@ -75,7 +75,7 @@ export class NotePiningService {
|
|||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
noteId: note.id,
|
noteId: note.id,
|
||||||
} as UserNotePining);
|
} as MiUserNotePining);
|
||||||
|
|
||||||
// Deliver to remote followers
|
// Deliver to remote followers
|
||||||
if (this.userEntityService.isLocalUser(user)) {
|
if (this.userEntityService.isLocalUser(user)) {
|
||||||
@ -89,7 +89,7 @@ export class NotePiningService {
|
|||||||
* @param noteId
|
* @param noteId
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async removePinned(user: { id: User['id']; host: User['host']; }, noteId: Note['id']) {
|
public async removePinned(user: { id: MiUser['id']; host: MiUser['host']; }, noteId: MiNote['id']) {
|
||||||
// Fetch unpinee
|
// Fetch unpinee
|
||||||
const note = await this.notesRepository.findOneBy({
|
const note = await this.notesRepository.findOneBy({
|
||||||
id: noteId,
|
id: noteId,
|
||||||
@ -112,7 +112,7 @@ export class NotePiningService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async deliverPinnedChange(userId: User['id'], noteId: Note['id'], isAddition: boolean) {
|
public async deliverPinnedChange(userId: MiUser['id'], noteId: MiNote['id'], isAddition: boolean) {
|
||||||
const user = await this.usersRepository.findOneBy({ id: userId });
|
const user = await this.usersRepository.findOneBy({ id: userId });
|
||||||
if (user == null) throw new Error('user not found');
|
if (user == null) throw new Error('user not found');
|
||||||
|
|
||||||
|
@ -7,9 +7,9 @@ import { setTimeout } from 'node:timers/promises';
|
|||||||
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
|
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
|
||||||
import { In } from 'typeorm';
|
import { In } from 'typeorm';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { User } from '@/models/entities/User.js';
|
import type { MiUser } from '@/models/entities/User.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from '@/misc/json-schema.js';
|
||||||
import type { Note } from '@/models/entities/Note.js';
|
import type { MiNote } from '@/models/entities/Note.js';
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
import type { NoteUnreadsRepository, MutingsRepository, NoteThreadMutingsRepository } from '@/models/index.js';
|
import type { NoteUnreadsRepository, MutingsRepository, NoteThreadMutingsRepository } from '@/models/index.js';
|
||||||
@ -35,7 +35,7 @@ export class NoteReadService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async insertNoteUnread(userId: User['id'], note: Note, params: {
|
public async insertNoteUnread(userId: MiUser['id'], note: MiNote, params: {
|
||||||
// NOTE: isSpecifiedがtrueならisMentionedは必ずfalse
|
// NOTE: isSpecifiedがtrueならisMentionedは必ずfalse
|
||||||
isSpecified: boolean;
|
isSpecified: boolean;
|
||||||
isMentioned: boolean;
|
isMentioned: boolean;
|
||||||
@ -84,11 +84,11 @@ export class NoteReadService implements OnApplicationShutdown {
|
|||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async read(
|
public async read(
|
||||||
userId: User['id'],
|
userId: MiUser['id'],
|
||||||
notes: (Note | Packed<'Note'>)[],
|
notes: (MiNote | Packed<'Note'>)[],
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const readMentions: (Note | Packed<'Note'>)[] = [];
|
const readMentions: (MiNote | Packed<'Note'>)[] = [];
|
||||||
const readSpecifiedNotes: (Note | Packed<'Note'>)[] = [];
|
const readSpecifiedNotes: (MiNote | Packed<'Note'>)[] = [];
|
||||||
|
|
||||||
for (const note of notes) {
|
for (const note of notes) {
|
||||||
if (note.mentions && note.mentions.includes(userId)) {
|
if (note.mentions && note.mentions.includes(userId)) {
|
||||||
|
@ -8,10 +8,9 @@ import * as Redis from 'ioredis';
|
|||||||
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
|
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
|
||||||
import { In } from 'typeorm';
|
import { In } from 'typeorm';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { MutingsRepository, UserProfile, UserProfilesRepository, UsersRepository } from '@/models/index.js';
|
import type { UsersRepository } from '@/models/index.js';
|
||||||
import type { User } from '@/models/entities/User.js';
|
import type { MiUser } from '@/models/entities/User.js';
|
||||||
import type { Notification } from '@/models/entities/Notification.js';
|
import type { MiNotification } from '@/models/entities/Notification.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
import { PushNotificationService } from '@/core/PushNotificationService.js';
|
import { PushNotificationService } from '@/core/PushNotificationService.js';
|
||||||
@ -30,14 +29,7 @@ export class NotificationService implements OnApplicationShutdown {
|
|||||||
@Inject(DI.usersRepository)
|
@Inject(DI.usersRepository)
|
||||||
private usersRepository: UsersRepository,
|
private usersRepository: UsersRepository,
|
||||||
|
|
||||||
@Inject(DI.userProfilesRepository)
|
|
||||||
private userProfilesRepository: UserProfilesRepository,
|
|
||||||
|
|
||||||
@Inject(DI.mutingsRepository)
|
|
||||||
private mutingsRepository: MutingsRepository,
|
|
||||||
|
|
||||||
private notificationEntityService: NotificationEntityService,
|
private notificationEntityService: NotificationEntityService,
|
||||||
private userEntityService: UserEntityService,
|
|
||||||
private idService: IdService,
|
private idService: IdService,
|
||||||
private globalEventService: GlobalEventService,
|
private globalEventService: GlobalEventService,
|
||||||
private pushNotificationService: PushNotificationService,
|
private pushNotificationService: PushNotificationService,
|
||||||
@ -47,7 +39,7 @@ export class NotificationService implements OnApplicationShutdown {
|
|||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async readAllNotification(
|
public async readAllNotification(
|
||||||
userId: User['id'],
|
userId: MiUser['id'],
|
||||||
force = false,
|
force = false,
|
||||||
) {
|
) {
|
||||||
const latestReadNotificationId = await this.redisClient.get(`latestReadNotification:${userId}`);
|
const latestReadNotificationId = await this.redisClient.get(`latestReadNotification:${userId}`);
|
||||||
@ -69,17 +61,17 @@ export class NotificationService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private postReadAllNotifications(userId: User['id']) {
|
private postReadAllNotifications(userId: MiUser['id']) {
|
||||||
this.globalEventService.publishMainStream(userId, 'readAllNotifications');
|
this.globalEventService.publishMainStream(userId, 'readAllNotifications');
|
||||||
this.pushNotificationService.pushNotification(userId, 'readAllNotifications', undefined);
|
this.pushNotificationService.pushNotification(userId, 'readAllNotifications', undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async createNotification(
|
public async createNotification(
|
||||||
notifieeId: User['id'],
|
notifieeId: MiUser['id'],
|
||||||
type: Notification['type'],
|
type: MiNotification['type'],
|
||||||
data: Partial<Notification>,
|
data: Partial<MiNotification>,
|
||||||
): Promise<Notification | null> {
|
): Promise<MiNotification | null> {
|
||||||
const profile = await this.cacheService.userProfileCache.fetch(notifieeId);
|
const profile = await this.cacheService.userProfileCache.fetch(notifieeId);
|
||||||
const isMuted = profile.mutingNotificationTypes.includes(type);
|
const isMuted = profile.mutingNotificationTypes.includes(type);
|
||||||
if (isMuted) return null;
|
if (isMuted) return null;
|
||||||
@ -100,7 +92,7 @@ export class NotificationService implements OnApplicationShutdown {
|
|||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
type: type,
|
type: type,
|
||||||
...data,
|
...data,
|
||||||
} as Notification;
|
} as MiNotification;
|
||||||
|
|
||||||
const redisIdPromise = this.redisClient.xadd(
|
const redisIdPromise = this.redisClient.xadd(
|
||||||
`notificationTimeline:${notifieeId}`,
|
`notificationTimeline:${notifieeId}`,
|
||||||
@ -134,7 +126,7 @@ export class NotificationService implements OnApplicationShutdown {
|
|||||||
// TODO: locale ファイルをクライアント用とサーバー用で分けたい
|
// TODO: locale ファイルをクライアント用とサーバー用で分けたい
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async emailNotificationFollow(userId: User['id'], follower: User) {
|
private async emailNotificationFollow(userId: MiUser['id'], follower: MiUser) {
|
||||||
/*
|
/*
|
||||||
const userProfile = await UserProfiles.findOneByOrFail({ userId: userId });
|
const userProfile = await UserProfiles.findOneByOrFail({ userId: userId });
|
||||||
if (!userProfile.email || !userProfile.emailNotificationTypes.includes('follow')) return;
|
if (!userProfile.email || !userProfile.emailNotificationTypes.includes('follow')) return;
|
||||||
@ -146,7 +138,7 @@ export class NotificationService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async emailNotificationReceiveFollowRequest(userId: User['id'], follower: User) {
|
private async emailNotificationReceiveFollowRequest(userId: MiUser['id'], follower: MiUser) {
|
||||||
/*
|
/*
|
||||||
const userProfile = await UserProfiles.findOneByOrFail({ userId: userId });
|
const userProfile = await UserProfiles.findOneByOrFail({ userId: userId });
|
||||||
if (!userProfile.email || !userProfile.emailNotificationTypes.includes('receiveFollowRequest')) return;
|
if (!userProfile.email || !userProfile.emailNotificationTypes.includes('receiveFollowRequest')) return;
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { NotesRepository, UsersRepository, PollsRepository, PollVotesRepository, User } from '@/models/index.js';
|
import type { NotesRepository, UsersRepository, PollsRepository, PollVotesRepository, MiUser } from '@/models/index.js';
|
||||||
import type { Note } from '@/models/entities/Note.js';
|
import type { MiNote } from '@/models/entities/Note.js';
|
||||||
import { RelayService } from '@/core/RelayService.js';
|
import { RelayService } from '@/core/RelayService.js';
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
@ -42,7 +42,7 @@ export class PollService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async vote(user: User, note: Note, choice: number) {
|
public async vote(user: MiUser, note: MiNote, choice: number) {
|
||||||
const poll = await this.pollsRepository.findOneBy({ noteId: note.id });
|
const poll = await this.pollsRepository.findOneBy({ noteId: note.id });
|
||||||
|
|
||||||
if (poll == null) throw new Error('poll not found');
|
if (poll == null) throw new Error('poll not found');
|
||||||
@ -92,7 +92,7 @@ export class PollService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async deliverQuestionUpdate(noteId: Note['id']) {
|
public async deliverQuestionUpdate(noteId: MiNote['id']) {
|
||||||
const note = await this.notesRepository.findOneBy({ id: noteId });
|
const note = await this.notesRepository.findOneBy({ id: noteId });
|
||||||
if (note == null) throw new Error('note not found');
|
if (note == null) throw new Error('note not found');
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import type { UsersRepository } from '@/models/index.js';
|
import type { UsersRepository } from '@/models/index.js';
|
||||||
import type { LocalUser } from '@/models/entities/User.js';
|
import type { MiLocalUser } from '@/models/entities/User.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { MetaService } from '@/core/MetaService.js';
|
import { MetaService } from '@/core/MetaService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
@ -21,9 +21,9 @@ export class ProxyAccountService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async fetch(): Promise<LocalUser | null> {
|
public async fetch(): Promise<MiLocalUser | null> {
|
||||||
const meta = await this.metaService.fetch();
|
const meta = await this.metaService.fetch();
|
||||||
if (meta.proxyAccountId == null) return null;
|
if (meta.proxyAccountId == null) return null;
|
||||||
return await this.usersRepository.findOneByOrFail({ id: meta.proxyAccountId }) as LocalUser;
|
return await this.usersRepository.findOneByOrFail({ id: meta.proxyAccountId }) as MiLocalUser;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import { DI } from '@/di-symbols.js';
|
|||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from '@/misc/json-schema.js';
|
||||||
import { getNoteSummary } from '@/misc/get-note-summary.js';
|
import { getNoteSummary } from '@/misc/get-note-summary.js';
|
||||||
import type { SwSubscription, SwSubscriptionsRepository } from '@/models/index.js';
|
import type { MiSwSubscription, SwSubscriptionsRepository } from '@/models/index.js';
|
||||||
import { MetaService } from '@/core/MetaService.js';
|
import { MetaService } from '@/core/MetaService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { RedisKVCache } from '@/misc/cache.js';
|
import { RedisKVCache } from '@/misc/cache.js';
|
||||||
@ -48,7 +48,7 @@ function truncateBody<T extends keyof PushNotificationsTypes>(type: T, body: Pus
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PushNotificationService implements OnApplicationShutdown {
|
export class PushNotificationService implements OnApplicationShutdown {
|
||||||
private subscriptionsCache: RedisKVCache<SwSubscription[]>;
|
private subscriptionsCache: RedisKVCache<MiSwSubscription[]>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.config)
|
@Inject(DI.config)
|
||||||
@ -62,7 +62,7 @@ export class PushNotificationService implements OnApplicationShutdown {
|
|||||||
|
|
||||||
private metaService: MetaService,
|
private metaService: MetaService,
|
||||||
) {
|
) {
|
||||||
this.subscriptionsCache = new RedisKVCache<SwSubscription[]>(this.redisClient, 'userSwSubscriptions', {
|
this.subscriptionsCache = new RedisKVCache<MiSwSubscription[]>(this.redisClient, 'userSwSubscriptions', {
|
||||||
lifetime: 1000 * 60 * 60 * 1, // 1h
|
lifetime: 1000 * 60 * 60 * 1, // 1h
|
||||||
memoryCacheLifetime: 1000 * 60 * 3, // 3m
|
memoryCacheLifetime: 1000 * 60 * 3, // 3m
|
||||||
fetcher: (key) => this.swSubscriptionsRepository.findBy({ userId: key }),
|
fetcher: (key) => this.swSubscriptionsRepository.findBy({ userId: key }),
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { Brackets, ObjectLiteral } from 'typeorm';
|
import { Brackets, ObjectLiteral } from 'typeorm';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { User } from '@/models/entities/User.js';
|
import type { MiUser } from '@/models/entities/User.js';
|
||||||
import type { UserProfilesRepository, FollowingsRepository, ChannelFollowingsRepository, MutedNotesRepository, BlockingsRepository, NoteThreadMutingsRepository, MutingsRepository, RenoteMutingsRepository } from '@/models/index.js';
|
import type { UserProfilesRepository, FollowingsRepository, ChannelFollowingsRepository, MutedNotesRepository, BlockingsRepository, NoteThreadMutingsRepository, MutingsRepository, RenoteMutingsRepository } from '@/models/index.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import type { SelectQueryBuilder } from 'typeorm';
|
import type { SelectQueryBuilder } from 'typeorm';
|
||||||
@ -69,7 +69,7 @@ export class QueryService {
|
|||||||
|
|
||||||
// ここでいうBlockedは被Blockedの意
|
// ここでいうBlockedは被Blockedの意
|
||||||
@bindThis
|
@bindThis
|
||||||
public generateBlockedUserQuery(q: SelectQueryBuilder<any>, me: { id: User['id'] }): void {
|
public generateBlockedUserQuery(q: SelectQueryBuilder<any>, me: { id: MiUser['id'] }): void {
|
||||||
const blockingQuery = this.blockingsRepository.createQueryBuilder('blocking')
|
const blockingQuery = this.blockingsRepository.createQueryBuilder('blocking')
|
||||||
.select('blocking.blockerId')
|
.select('blocking.blockerId')
|
||||||
.where('blocking.blockeeId = :blockeeId', { blockeeId: me.id });
|
.where('blocking.blockeeId = :blockeeId', { blockeeId: me.id });
|
||||||
@ -92,7 +92,7 @@ export class QueryService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public generateBlockQueryForUsers(q: SelectQueryBuilder<any>, me: { id: User['id'] }): void {
|
public generateBlockQueryForUsers(q: SelectQueryBuilder<any>, me: { id: MiUser['id'] }): void {
|
||||||
const blockingQuery = this.blockingsRepository.createQueryBuilder('blocking')
|
const blockingQuery = this.blockingsRepository.createQueryBuilder('blocking')
|
||||||
.select('blocking.blockeeId')
|
.select('blocking.blockeeId')
|
||||||
.where('blocking.blockerId = :blockerId', { blockerId: me.id });
|
.where('blocking.blockerId = :blockerId', { blockerId: me.id });
|
||||||
@ -109,7 +109,7 @@ export class QueryService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public generateChannelQuery(q: SelectQueryBuilder<any>, me?: { id: User['id'] } | null): void {
|
public generateChannelQuery(q: SelectQueryBuilder<any>, me?: { id: MiUser['id'] } | null): void {
|
||||||
if (me == null) {
|
if (me == null) {
|
||||||
q.andWhere('note.channelId IS NULL');
|
q.andWhere('note.channelId IS NULL');
|
||||||
} else {
|
} else {
|
||||||
@ -131,7 +131,7 @@ export class QueryService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public generateMutedNoteQuery(q: SelectQueryBuilder<any>, me: { id: User['id'] }): void {
|
public generateMutedNoteQuery(q: SelectQueryBuilder<any>, me: { id: MiUser['id'] }): void {
|
||||||
const mutedQuery = this.mutedNotesRepository.createQueryBuilder('muted')
|
const mutedQuery = this.mutedNotesRepository.createQueryBuilder('muted')
|
||||||
.select('muted.noteId')
|
.select('muted.noteId')
|
||||||
.where('muted.userId = :userId', { userId: me.id });
|
.where('muted.userId = :userId', { userId: me.id });
|
||||||
@ -142,7 +142,7 @@ export class QueryService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public generateMutedNoteThreadQuery(q: SelectQueryBuilder<any>, me: { id: User['id'] }): void {
|
public generateMutedNoteThreadQuery(q: SelectQueryBuilder<any>, me: { id: MiUser['id'] }): void {
|
||||||
const mutedQuery = this.noteThreadMutingsRepository.createQueryBuilder('threadMuted')
|
const mutedQuery = this.noteThreadMutingsRepository.createQueryBuilder('threadMuted')
|
||||||
.select('threadMuted.threadId')
|
.select('threadMuted.threadId')
|
||||||
.where('threadMuted.userId = :userId', { userId: me.id });
|
.where('threadMuted.userId = :userId', { userId: me.id });
|
||||||
@ -157,7 +157,7 @@ export class QueryService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public generateMutedUserQuery(q: SelectQueryBuilder<any>, me: { id: User['id'] }, exclude?: User): void {
|
public generateMutedUserQuery(q: SelectQueryBuilder<any>, me: { id: MiUser['id'] }, exclude?: MiUser): void {
|
||||||
const mutingQuery = this.mutingsRepository.createQueryBuilder('muting')
|
const mutingQuery = this.mutingsRepository.createQueryBuilder('muting')
|
||||||
.select('muting.muteeId')
|
.select('muting.muteeId')
|
||||||
.where('muting.muterId = :muterId', { muterId: me.id });
|
.where('muting.muterId = :muterId', { muterId: me.id });
|
||||||
@ -202,7 +202,7 @@ export class QueryService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public generateMutedUserQueryForUsers(q: SelectQueryBuilder<any>, me: { id: User['id'] }): void {
|
public generateMutedUserQueryForUsers(q: SelectQueryBuilder<any>, me: { id: MiUser['id'] }): void {
|
||||||
const mutingQuery = this.mutingsRepository.createQueryBuilder('muting')
|
const mutingQuery = this.mutingsRepository.createQueryBuilder('muting')
|
||||||
.select('muting.muteeId')
|
.select('muting.muteeId')
|
||||||
.where('muting.muterId = :muterId', { muterId: me.id });
|
.where('muting.muterId = :muterId', { muterId: me.id });
|
||||||
@ -213,7 +213,7 @@ export class QueryService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public generateRepliesQuery(q: SelectQueryBuilder<any>, withReplies: boolean, me?: Pick<User, 'id'> | null): void {
|
public generateRepliesQuery(q: SelectQueryBuilder<any>, withReplies: boolean, me?: Pick<MiUser, 'id'> | null): void {
|
||||||
if (me == null) {
|
if (me == null) {
|
||||||
q.andWhere(new Brackets(qb => { qb
|
q.andWhere(new Brackets(qb => { qb
|
||||||
.where('note.replyId IS NULL') // 返信ではない
|
.where('note.replyId IS NULL') // 返信ではない
|
||||||
@ -239,7 +239,7 @@ export class QueryService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public generateVisibilityQuery(q: SelectQueryBuilder<any>, me?: { id: User['id'] } | null): void {
|
public generateVisibilityQuery(q: SelectQueryBuilder<any>, me?: { id: MiUser['id'] } | null): void {
|
||||||
// This code must always be synchronized with the checks in Notes.isVisibleForMe.
|
// This code must always be synchronized with the checks in Notes.isVisibleForMe.
|
||||||
if (me == null) {
|
if (me == null) {
|
||||||
q.andWhere(new Brackets(qb => { qb
|
q.andWhere(new Brackets(qb => { qb
|
||||||
@ -279,7 +279,7 @@ export class QueryService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public generateMutedUserRenotesQueryForNotes(q: SelectQueryBuilder<any>, me: { id: User['id'] }): void {
|
public generateMutedUserRenotesQueryForNotes(q: SelectQueryBuilder<any>, me: { id: MiUser['id'] }): void {
|
||||||
const mutingQuery = this.renoteMutingsRepository.createQueryBuilder('renote_muting')
|
const mutingQuery = this.renoteMutingsRepository.createQueryBuilder('renote_muting')
|
||||||
.select('renote_muting.muteeId')
|
.select('renote_muting.muteeId')
|
||||||
.where('renote_muting.muterId = :muterId', { muterId: me.id });
|
.where('renote_muting.muterId = :muterId', { muterId: me.id });
|
||||||
|
@ -6,8 +6,8 @@
|
|||||||
import { randomUUID } from 'node:crypto';
|
import { randomUUID } from 'node:crypto';
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import type { IActivity } from '@/core/activitypub/type.js';
|
import type { IActivity } from '@/core/activitypub/type.js';
|
||||||
import type { DriveFile } from '@/models/entities/DriveFile.js';
|
import type { MiDriveFile } from '@/models/entities/DriveFile.js';
|
||||||
import type { Webhook, webhookEventTypes } from '@/models/entities/Webhook.js';
|
import type { MiWebhook, webhookEventTypes } from '@/models/entities/Webhook.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
@ -237,7 +237,7 @@ export class QueueService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public createImportFollowingJob(user: ThinUser, fileId: DriveFile['id']) {
|
public createImportFollowingJob(user: ThinUser, fileId: MiDriveFile['id']) {
|
||||||
return this.dbQueue.add('importFollowing', {
|
return this.dbQueue.add('importFollowing', {
|
||||||
user: { id: user.id },
|
user: { id: user.id },
|
||||||
fileId: fileId,
|
fileId: fileId,
|
||||||
@ -254,7 +254,7 @@ export class QueueService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public createImportMutingJob(user: ThinUser, fileId: DriveFile['id']) {
|
public createImportMutingJob(user: ThinUser, fileId: MiDriveFile['id']) {
|
||||||
return this.dbQueue.add('importMuting', {
|
return this.dbQueue.add('importMuting', {
|
||||||
user: { id: user.id },
|
user: { id: user.id },
|
||||||
fileId: fileId,
|
fileId: fileId,
|
||||||
@ -265,7 +265,7 @@ export class QueueService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public createImportBlockingJob(user: ThinUser, fileId: DriveFile['id']) {
|
public createImportBlockingJob(user: ThinUser, fileId: MiDriveFile['id']) {
|
||||||
return this.dbQueue.add('importBlocking', {
|
return this.dbQueue.add('importBlocking', {
|
||||||
user: { id: user.id },
|
user: { id: user.id },
|
||||||
fileId: fileId,
|
fileId: fileId,
|
||||||
@ -298,7 +298,7 @@ export class QueueService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public createImportUserListsJob(user: ThinUser, fileId: DriveFile['id']) {
|
public createImportUserListsJob(user: ThinUser, fileId: MiDriveFile['id']) {
|
||||||
return this.dbQueue.add('importUserLists', {
|
return this.dbQueue.add('importUserLists', {
|
||||||
user: { id: user.id },
|
user: { id: user.id },
|
||||||
fileId: fileId,
|
fileId: fileId,
|
||||||
@ -309,7 +309,7 @@ export class QueueService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public createImportCustomEmojisJob(user: ThinUser, fileId: DriveFile['id']) {
|
public createImportCustomEmojisJob(user: ThinUser, fileId: MiDriveFile['id']) {
|
||||||
return this.dbQueue.add('importCustomEmojis', {
|
return this.dbQueue.add('importCustomEmojis', {
|
||||||
user: { id: user.id },
|
user: { id: user.id },
|
||||||
fileId: fileId,
|
fileId: fileId,
|
||||||
@ -412,7 +412,7 @@ export class QueueService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public webhookDeliver(webhook: Webhook, type: typeof webhookEventTypes[number], content: unknown) {
|
public webhookDeliver(webhook: MiWebhook, type: typeof webhookEventTypes[number], content: unknown) {
|
||||||
const data = {
|
const data = {
|
||||||
type,
|
type,
|
||||||
content,
|
content,
|
||||||
|
@ -7,10 +7,10 @@ import { Inject, Injectable } from '@nestjs/common';
|
|||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { EmojisRepository, NoteReactionsRepository, UsersRepository, NotesRepository } from '@/models/index.js';
|
import type { EmojisRepository, NoteReactionsRepository, UsersRepository, NotesRepository } from '@/models/index.js';
|
||||||
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
||||||
import type { RemoteUser, User } from '@/models/entities/User.js';
|
import type { MiRemoteUser, MiUser } from '@/models/entities/User.js';
|
||||||
import type { Note } from '@/models/entities/Note.js';
|
import type { MiNote } from '@/models/entities/Note.js';
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
import type { NoteReaction } from '@/models/entities/NoteReaction.js';
|
import type { MiNoteReaction } from '@/models/entities/NoteReaction.js';
|
||||||
import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js';
|
import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js';
|
||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
import { NotificationService } from '@/core/NotificationService.js';
|
import { NotificationService } from '@/core/NotificationService.js';
|
||||||
@ -95,7 +95,7 @@ export class ReactionService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async create(user: { id: User['id']; host: User['host']; isBot: User['isBot'] }, note: Note, _reaction?: string | null) {
|
public async create(user: { id: MiUser['id']; host: MiUser['host']; isBot: MiUser['isBot'] }, note: MiNote, _reaction?: string | null) {
|
||||||
// Check blocking
|
// Check blocking
|
||||||
if (note.userId !== user.id) {
|
if (note.userId !== user.id) {
|
||||||
const blocked = await this.userBlockingService.checkBlocked(note.userId, user.id);
|
const blocked = await this.userBlockingService.checkBlocked(note.userId, user.id);
|
||||||
@ -146,7 +146,7 @@ export class ReactionService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const record: NoteReaction = {
|
const record: MiNoteReaction = {
|
||||||
id: this.idService.genId(),
|
id: this.idService.genId(),
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
noteId: note.id,
|
noteId: note.id,
|
||||||
@ -231,7 +231,7 @@ export class ReactionService {
|
|||||||
const dm = this.apDeliverManagerService.createDeliverManager(user, content);
|
const dm = this.apDeliverManagerService.createDeliverManager(user, content);
|
||||||
if (note.userHost !== null) {
|
if (note.userHost !== null) {
|
||||||
const reactee = await this.usersRepository.findOneBy({ id: note.userId });
|
const reactee = await this.usersRepository.findOneBy({ id: note.userId });
|
||||||
dm.addDirectRecipe(reactee as RemoteUser);
|
dm.addDirectRecipe(reactee as MiRemoteUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (['public', 'home', 'followers'].includes(note.visibility)) {
|
if (['public', 'home', 'followers'].includes(note.visibility)) {
|
||||||
@ -239,7 +239,7 @@ export class ReactionService {
|
|||||||
} else if (note.visibility === 'specified') {
|
} else if (note.visibility === 'specified') {
|
||||||
const visibleUsers = await Promise.all(note.visibleUserIds.map(id => this.usersRepository.findOneBy({ id })));
|
const visibleUsers = await Promise.all(note.visibleUserIds.map(id => this.usersRepository.findOneBy({ id })));
|
||||||
for (const u of visibleUsers.filter(u => u && this.userEntityService.isRemoteUser(u))) {
|
for (const u of visibleUsers.filter(u => u && this.userEntityService.isRemoteUser(u))) {
|
||||||
dm.addDirectRecipe(u as RemoteUser);
|
dm.addDirectRecipe(u as MiRemoteUser);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,7 +249,7 @@ export class ReactionService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async delete(user: { id: User['id']; host: User['host']; isBot: User['isBot']; }, note: Note) {
|
public async delete(user: { id: MiUser['id']; host: MiUser['host']; isBot: MiUser['isBot']; }, note: MiNote) {
|
||||||
// if already unreacted
|
// if already unreacted
|
||||||
const exist = await this.noteReactionsRepository.findOneBy({
|
const exist = await this.noteReactionsRepository.findOneBy({
|
||||||
noteId: note.id,
|
noteId: note.id,
|
||||||
@ -289,7 +289,7 @@ export class ReactionService {
|
|||||||
const dm = this.apDeliverManagerService.createDeliverManager(user, content);
|
const dm = this.apDeliverManagerService.createDeliverManager(user, content);
|
||||||
if (note.userHost !== null) {
|
if (note.userHost !== null) {
|
||||||
const reactee = await this.usersRepository.findOneBy({ id: note.userId });
|
const reactee = await this.usersRepository.findOneBy({ id: note.userId });
|
||||||
dm.addDirectRecipe(reactee as RemoteUser);
|
dm.addDirectRecipe(reactee as MiRemoteUser);
|
||||||
}
|
}
|
||||||
dm.addFollowersRecipe();
|
dm.addFollowersRecipe();
|
||||||
dm.execute();
|
dm.execute();
|
||||||
|
@ -5,11 +5,11 @@
|
|||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { IsNull } from 'typeorm';
|
import { IsNull } from 'typeorm';
|
||||||
import type { LocalUser, User } from '@/models/entities/User.js';
|
import type { MiLocalUser, MiUser } from '@/models/entities/User.js';
|
||||||
import type { RelaysRepository, UsersRepository } from '@/models/index.js';
|
import type { RelaysRepository, UsersRepository } from '@/models/index.js';
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
import { MemorySingleCache } from '@/misc/cache.js';
|
import { MemorySingleCache } from '@/misc/cache.js';
|
||||||
import type { Relay } from '@/models/entities/Relay.js';
|
import type { MiRelay } from '@/models/entities/Relay.js';
|
||||||
import { QueueService } from '@/core/QueueService.js';
|
import { QueueService } from '@/core/QueueService.js';
|
||||||
import { CreateSystemUserService } from '@/core/CreateSystemUserService.js';
|
import { CreateSystemUserService } from '@/core/CreateSystemUserService.js';
|
||||||
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
|
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
|
||||||
@ -21,7 +21,7 @@ const ACTOR_USERNAME = 'relay.actor' as const;
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RelayService {
|
export class RelayService {
|
||||||
private relaysCache: MemorySingleCache<Relay[]>;
|
private relaysCache: MemorySingleCache<MiRelay[]>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.usersRepository)
|
@Inject(DI.usersRepository)
|
||||||
@ -35,24 +35,24 @@ export class RelayService {
|
|||||||
private createSystemUserService: CreateSystemUserService,
|
private createSystemUserService: CreateSystemUserService,
|
||||||
private apRendererService: ApRendererService,
|
private apRendererService: ApRendererService,
|
||||||
) {
|
) {
|
||||||
this.relaysCache = new MemorySingleCache<Relay[]>(1000 * 60 * 10);
|
this.relaysCache = new MemorySingleCache<MiRelay[]>(1000 * 60 * 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async getRelayActor(): Promise<LocalUser> {
|
private async getRelayActor(): Promise<MiLocalUser> {
|
||||||
const user = await this.usersRepository.findOneBy({
|
const user = await this.usersRepository.findOneBy({
|
||||||
host: IsNull(),
|
host: IsNull(),
|
||||||
username: ACTOR_USERNAME,
|
username: ACTOR_USERNAME,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (user) return user as LocalUser;
|
if (user) return user as MiLocalUser;
|
||||||
|
|
||||||
const created = await this.createSystemUserService.createSystemUser(ACTOR_USERNAME);
|
const created = await this.createSystemUserService.createSystemUser(ACTOR_USERNAME);
|
||||||
return created as LocalUser;
|
return created as MiLocalUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async addRelay(inbox: string): Promise<Relay> {
|
public async addRelay(inbox: string): Promise<MiRelay> {
|
||||||
const relay = await this.relaysRepository.insert({
|
const relay = await this.relaysRepository.insert({
|
||||||
id: this.idService.genId(),
|
id: this.idService.genId(),
|
||||||
inbox,
|
inbox,
|
||||||
@ -87,7 +87,7 @@ export class RelayService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async listRelay(): Promise<Relay[]> {
|
public async listRelay(): Promise<MiRelay[]> {
|
||||||
const relays = await this.relaysRepository.find();
|
const relays = await this.relaysRepository.find();
|
||||||
return relays;
|
return relays;
|
||||||
}
|
}
|
||||||
@ -111,7 +111,7 @@ export class RelayService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async deliverToRelays(user: { id: User['id']; host: null; }, activity: any): Promise<void> {
|
public async deliverToRelays(user: { id: MiUser['id']; host: null; }, activity: any): Promise<void> {
|
||||||
if (activity == null) return;
|
if (activity == null) return;
|
||||||
|
|
||||||
const relays = await this.relaysCache.fetch(() => this.relaysRepository.findBy({
|
const relays = await this.relaysCache.fetch(() => this.relaysRepository.findBy({
|
||||||
|
@ -9,7 +9,7 @@ import chalk from 'chalk';
|
|||||||
import { IsNull } from 'typeorm';
|
import { IsNull } from 'typeorm';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { UsersRepository } from '@/models/index.js';
|
import type { UsersRepository } from '@/models/index.js';
|
||||||
import type { LocalUser, RemoteUser } from '@/models/entities/User.js';
|
import type { MiLocalUser, MiRemoteUser } from '@/models/entities/User.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import type Logger from '@/logger.js';
|
import type Logger from '@/logger.js';
|
||||||
import { UtilityService } from '@/core/UtilityService.js';
|
import { UtilityService } from '@/core/UtilityService.js';
|
||||||
@ -40,7 +40,7 @@ export class RemoteUserResolveService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async resolveUser(username: string, host: string | null): Promise<LocalUser | RemoteUser> {
|
public async resolveUser(username: string, host: string | null): Promise<MiLocalUser | MiRemoteUser> {
|
||||||
const usernameLower = username.toLowerCase();
|
const usernameLower = username.toLowerCase();
|
||||||
|
|
||||||
if (host == null) {
|
if (host == null) {
|
||||||
@ -51,7 +51,7 @@ export class RemoteUserResolveService {
|
|||||||
} else {
|
} else {
|
||||||
return u;
|
return u;
|
||||||
}
|
}
|
||||||
}) as LocalUser;
|
}) as MiLocalUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
host = this.utilityService.toPuny(host);
|
host = this.utilityService.toPuny(host);
|
||||||
@ -64,10 +64,10 @@ export class RemoteUserResolveService {
|
|||||||
} else {
|
} else {
|
||||||
return u;
|
return u;
|
||||||
}
|
}
|
||||||
}) as LocalUser;
|
}) as MiLocalUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await this.usersRepository.findOneBy({ usernameLower, host }) as RemoteUser | null;
|
const user = await this.usersRepository.findOneBy({ usernameLower, host }) as MiRemoteUser | null;
|
||||||
|
|
||||||
const acctLower = `${usernameLower}@${host}`;
|
const acctLower = `${usernameLower}@${host}`;
|
||||||
|
|
||||||
@ -86,7 +86,7 @@ export class RemoteUserResolveService {
|
|||||||
} else {
|
} else {
|
||||||
return u;
|
return u;
|
||||||
}
|
}
|
||||||
})) as LocalUser;
|
})) as MiLocalUser;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,7 +132,7 @@ export class RemoteUserResolveService {
|
|||||||
if (u == null) {
|
if (u == null) {
|
||||||
throw new Error('user not found');
|
throw new Error('user not found');
|
||||||
} else {
|
} else {
|
||||||
return u as LocalUser | RemoteUser;
|
return u as MiLocalUser | MiRemoteUser;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,9 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import * as Redis from 'ioredis';
|
import * as Redis from 'ioredis';
|
||||||
import { In } from 'typeorm';
|
import { In } from 'typeorm';
|
||||||
import type { Role, RoleAssignment, RoleAssignmentsRepository, RolesRepository, UsersRepository } from '@/models/index.js';
|
import type { MiRole, MiRoleAssignment, RoleAssignmentsRepository, RolesRepository, UsersRepository } from '@/models/index.js';
|
||||||
import { MemoryKVCache, MemorySingleCache } from '@/misc/cache.js';
|
import { MemoryKVCache, MemorySingleCache } from '@/misc/cache.js';
|
||||||
import type { User } from '@/models/entities/User.js';
|
import type { MiUser } from '@/models/entities/User.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { MetaService } from '@/core/MetaService.js';
|
import { MetaService } from '@/core/MetaService.js';
|
||||||
@ -71,8 +71,8 @@ export const DEFAULT_POLICIES: RolePolicies = {
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RoleService implements OnApplicationShutdown {
|
export class RoleService implements OnApplicationShutdown {
|
||||||
private rolesCache: MemorySingleCache<Role[]>;
|
private rolesCache: MemorySingleCache<MiRole[]>;
|
||||||
private roleAssignmentByUserIdCache: MemoryKVCache<RoleAssignment[]>;
|
private roleAssignmentByUserIdCache: MemoryKVCache<MiRoleAssignment[]>;
|
||||||
|
|
||||||
public static AlreadyAssignedError = class extends Error {};
|
public static AlreadyAssignedError = class extends Error {};
|
||||||
public static NotAssignedError = class extends Error {};
|
public static NotAssignedError = class extends Error {};
|
||||||
@ -101,8 +101,8 @@ export class RoleService implements OnApplicationShutdown {
|
|||||||
) {
|
) {
|
||||||
//this.onMessage = this.onMessage.bind(this);
|
//this.onMessage = this.onMessage.bind(this);
|
||||||
|
|
||||||
this.rolesCache = new MemorySingleCache<Role[]>(1000 * 60 * 60 * 1);
|
this.rolesCache = new MemorySingleCache<MiRole[]>(1000 * 60 * 60 * 1);
|
||||||
this.roleAssignmentByUserIdCache = new MemoryKVCache<RoleAssignment[]>(1000 * 60 * 60 * 1);
|
this.roleAssignmentByUserIdCache = new MemoryKVCache<MiRoleAssignment[]>(1000 * 60 * 60 * 1);
|
||||||
|
|
||||||
this.redisForSub.on('message', this.onMessage);
|
this.redisForSub.on('message', this.onMessage);
|
||||||
}
|
}
|
||||||
@ -173,7 +173,7 @@ export class RoleService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private evalCond(user: User, value: RoleCondFormulaValue): boolean {
|
private evalCond(user: MiUser, value: RoleCondFormulaValue): boolean {
|
||||||
try {
|
try {
|
||||||
switch (value.type) {
|
switch (value.type) {
|
||||||
case 'and': {
|
case 'and': {
|
||||||
@ -225,7 +225,7 @@ export class RoleService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async getUserAssigns(userId: User['id']) {
|
public async getUserAssigns(userId: MiUser['id']) {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
let assigns = await this.roleAssignmentByUserIdCache.fetch(userId, () => this.roleAssignmentsRepository.findBy({ userId }));
|
let assigns = await this.roleAssignmentByUserIdCache.fetch(userId, () => this.roleAssignmentsRepository.findBy({ userId }));
|
||||||
// 期限切れのロールを除外
|
// 期限切れのロールを除外
|
||||||
@ -234,7 +234,7 @@ export class RoleService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async getUserRoles(userId: User['id']) {
|
public async getUserRoles(userId: MiUser['id']) {
|
||||||
const roles = await this.rolesCache.fetch(() => this.rolesRepository.findBy({}));
|
const roles = await this.rolesCache.fetch(() => this.rolesRepository.findBy({}));
|
||||||
const assigns = await this.getUserAssigns(userId);
|
const assigns = await this.getUserAssigns(userId);
|
||||||
const assignedRoles = roles.filter(r => assigns.map(x => x.roleId).includes(r.id));
|
const assignedRoles = roles.filter(r => assigns.map(x => x.roleId).includes(r.id));
|
||||||
@ -247,7 +247,7 @@ export class RoleService implements OnApplicationShutdown {
|
|||||||
* 指定ユーザーのバッジロール一覧取得
|
* 指定ユーザーのバッジロール一覧取得
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async getUserBadgeRoles(userId: User['id']) {
|
public async getUserBadgeRoles(userId: MiUser['id']) {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
let assigns = await this.roleAssignmentByUserIdCache.fetch(userId, () => this.roleAssignmentsRepository.findBy({ userId }));
|
let assigns = await this.roleAssignmentByUserIdCache.fetch(userId, () => this.roleAssignmentsRepository.findBy({ userId }));
|
||||||
// 期限切れのロールを除外
|
// 期限切れのロールを除外
|
||||||
@ -266,7 +266,7 @@ export class RoleService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async getUserPolicies(userId: User['id'] | null): Promise<RolePolicies> {
|
public async getUserPolicies(userId: MiUser['id'] | null): Promise<RolePolicies> {
|
||||||
const meta = await this.metaService.fetch();
|
const meta = await this.metaService.fetch();
|
||||||
const basePolicies = { ...DEFAULT_POLICIES, ...meta.policies };
|
const basePolicies = { ...DEFAULT_POLICIES, ...meta.policies };
|
||||||
|
|
||||||
@ -314,19 +314,19 @@ export class RoleService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async isModerator(user: { id: User['id']; isRoot: User['isRoot'] } | null): Promise<boolean> {
|
public async isModerator(user: { id: MiUser['id']; isRoot: MiUser['isRoot'] } | null): Promise<boolean> {
|
||||||
if (user == null) return false;
|
if (user == null) return false;
|
||||||
return user.isRoot || (await this.getUserRoles(user.id)).some(r => r.isModerator || r.isAdministrator);
|
return user.isRoot || (await this.getUserRoles(user.id)).some(r => r.isModerator || r.isAdministrator);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async isAdministrator(user: { id: User['id']; isRoot: User['isRoot'] } | null): Promise<boolean> {
|
public async isAdministrator(user: { id: MiUser['id']; isRoot: MiUser['isRoot'] } | null): Promise<boolean> {
|
||||||
if (user == null) return false;
|
if (user == null) return false;
|
||||||
return user.isRoot || (await this.getUserRoles(user.id)).some(r => r.isAdministrator);
|
return user.isRoot || (await this.getUserRoles(user.id)).some(r => r.isAdministrator);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async isExplorable(role: { id: Role['id']} | null): Promise<boolean> {
|
public async isExplorable(role: { id: MiRole['id']} | null): Promise<boolean> {
|
||||||
if (role == null) return false;
|
if (role == null) return false;
|
||||||
const check = await this.rolesRepository.findOneBy({ id: role.id });
|
const check = await this.rolesRepository.findOneBy({ id: role.id });
|
||||||
if (check == null) return false;
|
if (check == null) return false;
|
||||||
@ -334,7 +334,7 @@ export class RoleService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async getModeratorIds(includeAdmins = true): Promise<User['id'][]> {
|
public async getModeratorIds(includeAdmins = true): Promise<MiUser['id'][]> {
|
||||||
const roles = await this.rolesCache.fetch(() => this.rolesRepository.findBy({}));
|
const roles = await this.rolesCache.fetch(() => this.rolesRepository.findBy({}));
|
||||||
const moderatorRoles = includeAdmins ? roles.filter(r => r.isModerator || r.isAdministrator) : roles.filter(r => r.isModerator);
|
const moderatorRoles = includeAdmins ? roles.filter(r => r.isModerator || r.isAdministrator) : roles.filter(r => r.isModerator);
|
||||||
const assigns = moderatorRoles.length > 0 ? await this.roleAssignmentsRepository.findBy({
|
const assigns = moderatorRoles.length > 0 ? await this.roleAssignmentsRepository.findBy({
|
||||||
@ -345,7 +345,7 @@ export class RoleService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async getModerators(includeAdmins = true): Promise<User[]> {
|
public async getModerators(includeAdmins = true): Promise<MiUser[]> {
|
||||||
const ids = await this.getModeratorIds(includeAdmins);
|
const ids = await this.getModeratorIds(includeAdmins);
|
||||||
const users = ids.length > 0 ? await this.usersRepository.findBy({
|
const users = ids.length > 0 ? await this.usersRepository.findBy({
|
||||||
id: In(ids),
|
id: In(ids),
|
||||||
@ -354,7 +354,7 @@ export class RoleService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async getAdministratorIds(): Promise<User['id'][]> {
|
public async getAdministratorIds(): Promise<MiUser['id'][]> {
|
||||||
const roles = await this.rolesCache.fetch(() => this.rolesRepository.findBy({}));
|
const roles = await this.rolesCache.fetch(() => this.rolesRepository.findBy({}));
|
||||||
const administratorRoles = roles.filter(r => r.isAdministrator);
|
const administratorRoles = roles.filter(r => r.isAdministrator);
|
||||||
const assigns = administratorRoles.length > 0 ? await this.roleAssignmentsRepository.findBy({
|
const assigns = administratorRoles.length > 0 ? await this.roleAssignmentsRepository.findBy({
|
||||||
@ -365,7 +365,7 @@ export class RoleService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async getAdministrators(): Promise<User[]> {
|
public async getAdministrators(): Promise<MiUser[]> {
|
||||||
const ids = await this.getAdministratorIds();
|
const ids = await this.getAdministratorIds();
|
||||||
const users = ids.length > 0 ? await this.usersRepository.findBy({
|
const users = ids.length > 0 ? await this.usersRepository.findBy({
|
||||||
id: In(ids),
|
id: In(ids),
|
||||||
@ -374,7 +374,7 @@ export class RoleService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async assign(userId: User['id'], roleId: Role['id'], expiresAt: Date | null = null): Promise<void> {
|
public async assign(userId: MiUser['id'], roleId: MiRole['id'], expiresAt: Date | null = null): Promise<void> {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
|
|
||||||
const existing = await this.roleAssignmentsRepository.findOneBy({
|
const existing = await this.roleAssignmentsRepository.findOneBy({
|
||||||
@ -409,7 +409,7 @@ export class RoleService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async unassign(userId: User['id'], roleId: Role['id']): Promise<void> {
|
public async unassign(userId: MiUser['id'], roleId: MiRole['id']): Promise<void> {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
|
|
||||||
const existing = await this.roleAssignmentsRepository.findOneBy({ roleId, userId });
|
const existing = await this.roleAssignmentsRepository.findOneBy({ roleId, userId });
|
||||||
|
@ -6,13 +6,11 @@
|
|||||||
import { URL } from 'node:url';
|
import { URL } from 'node:url';
|
||||||
import * as http from 'node:http';
|
import * as http from 'node:http';
|
||||||
import * as https from 'node:https';
|
import * as https from 'node:https';
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { DeleteObjectCommand, S3Client } from '@aws-sdk/client-s3';
|
import { DeleteObjectCommand, S3Client } from '@aws-sdk/client-s3';
|
||||||
import { Upload } from '@aws-sdk/lib-storage';
|
import { Upload } from '@aws-sdk/lib-storage';
|
||||||
import { NodeHttpHandler, NodeHttpHandlerOptions } from '@aws-sdk/node-http-handler';
|
import { NodeHttpHandler, NodeHttpHandlerOptions } from '@aws-sdk/node-http-handler';
|
||||||
import { DI } from '@/di-symbols.js';
|
import type { MiMeta } from '@/models/entities/Meta.js';
|
||||||
import type { Config } from '@/config.js';
|
|
||||||
import type { Meta } from '@/models/entities/Meta.js';
|
|
||||||
import { HttpRequestService } from '@/core/HttpRequestService.js';
|
import { HttpRequestService } from '@/core/HttpRequestService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import type { DeleteObjectCommandInput, PutObjectCommandInput } from '@aws-sdk/client-s3';
|
import type { DeleteObjectCommandInput, PutObjectCommandInput } from '@aws-sdk/client-s3';
|
||||||
@ -20,15 +18,12 @@ import type { DeleteObjectCommandInput, PutObjectCommandInput } from '@aws-sdk/c
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class S3Service {
|
export class S3Service {
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.config)
|
|
||||||
private config: Config,
|
|
||||||
|
|
||||||
private httpRequestService: HttpRequestService,
|
private httpRequestService: HttpRequestService,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public getS3Client(meta: Meta): S3Client {
|
public getS3Client(meta: MiMeta): S3Client {
|
||||||
const u = meta.objectStorageEndpoint
|
const u = meta.objectStorageEndpoint
|
||||||
? `${meta.objectStorageUseSSL ? 'https' : 'http'}://${meta.objectStorageEndpoint}`
|
? `${meta.objectStorageUseSSL ? 'https' : 'http'}://${meta.objectStorageEndpoint}`
|
||||||
: `${meta.objectStorageUseSSL ? 'https' : 'http'}://example.net`; // dummy url to select http(s) agent
|
: `${meta.objectStorageUseSSL ? 'https' : 'http'}://example.net`; // dummy url to select http(s) agent
|
||||||
@ -55,7 +50,7 @@ export class S3Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async upload(meta: Meta, input: PutObjectCommandInput) {
|
public async upload(meta: MiMeta, input: PutObjectCommandInput) {
|
||||||
const client = this.getS3Client(meta);
|
const client = this.getS3Client(meta);
|
||||||
return new Upload({
|
return new Upload({
|
||||||
client,
|
client,
|
||||||
@ -67,7 +62,7 @@ export class S3Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public delete(meta: Meta, input: DeleteObjectCommandInput) {
|
public delete(meta: MiMeta, input: DeleteObjectCommandInput) {
|
||||||
const client = this.getS3Client(meta);
|
const client = this.getS3Client(meta);
|
||||||
return client.send(new DeleteObjectCommand(input));
|
return client.send(new DeleteObjectCommand(input));
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,8 @@ import { In } from 'typeorm';
|
|||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { Note } from '@/models/entities/Note.js';
|
import { MiNote } from '@/models/entities/Note.js';
|
||||||
import { User } from '@/models/index.js';
|
import { MiUser } from '@/models/index.js';
|
||||||
import type { NotesRepository } from '@/models/index.js';
|
import type { NotesRepository } from '@/models/index.js';
|
||||||
import { sqlLikeEscape } from '@/misc/sql-like-escape.js';
|
import { sqlLikeEscape } from '@/misc/sql-like-escape.js';
|
||||||
import { QueryService } from '@/core/QueryService.js';
|
import { QueryService } from '@/core/QueryService.js';
|
||||||
@ -105,7 +105,7 @@ export class SearchService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async indexNote(note: Note): Promise<void> {
|
public async indexNote(note: MiNote): Promise<void> {
|
||||||
if (note.text == null && note.cw == null) return;
|
if (note.text == null && note.cw == null) return;
|
||||||
if (!['home', 'public'].includes(note.visibility)) return;
|
if (!['home', 'public'].includes(note.visibility)) return;
|
||||||
|
|
||||||
@ -141,7 +141,7 @@ export class SearchService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async unindexNote(note: Note): Promise<void> {
|
public async unindexNote(note: MiNote): Promise<void> {
|
||||||
if (!['home', 'public'].includes(note.visibility)) return;
|
if (!['home', 'public'].includes(note.visibility)) return;
|
||||||
|
|
||||||
if (this.meilisearch) {
|
if (this.meilisearch) {
|
||||||
@ -150,15 +150,15 @@ export class SearchService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async searchNote(q: string, me: User | null, opts: {
|
public async searchNote(q: string, me: MiUser | null, opts: {
|
||||||
userId?: Note['userId'] | null;
|
userId?: MiNote['userId'] | null;
|
||||||
channelId?: Note['channelId'] | null;
|
channelId?: MiNote['channelId'] | null;
|
||||||
host?: string | null;
|
host?: string | null;
|
||||||
}, pagination: {
|
}, pagination: {
|
||||||
untilId?: Note['id'];
|
untilId?: MiNote['id'];
|
||||||
sinceId?: Note['id'];
|
sinceId?: MiNote['id'];
|
||||||
limit?: number;
|
limit?: number;
|
||||||
}): Promise<Note[]> {
|
}): Promise<MiNote[]> {
|
||||||
if (this.meilisearch) {
|
if (this.meilisearch) {
|
||||||
const filter: Q = {
|
const filter: Q = {
|
||||||
op: 'and',
|
op: 'and',
|
||||||
|
@ -9,12 +9,11 @@ import bcrypt from 'bcryptjs';
|
|||||||
import { DataSource, IsNull } from 'typeorm';
|
import { DataSource, IsNull } from 'typeorm';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { UsedUsernamesRepository, UsersRepository } from '@/models/index.js';
|
import type { UsedUsernamesRepository, UsersRepository } from '@/models/index.js';
|
||||||
import type { Config } from '@/config.js';
|
import { MiUser } from '@/models/entities/User.js';
|
||||||
import { User } from '@/models/entities/User.js';
|
import { MiUserProfile } from '@/models/entities/UserProfile.js';
|
||||||
import { UserProfile } from '@/models/entities/UserProfile.js';
|
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
import { UserKeypair } from '@/models/entities/UserKeypair.js';
|
import { MiUserKeypair } from '@/models/entities/UserKeypair.js';
|
||||||
import { UsedUsername } from '@/models/entities/UsedUsername.js';
|
import { MiUsedUsername } from '@/models/entities/UsedUsername.js';
|
||||||
import generateUserToken from '@/misc/generate-native-user-token.js';
|
import generateUserToken from '@/misc/generate-native-user-token.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
@ -28,9 +27,6 @@ export class SignupService {
|
|||||||
@Inject(DI.db)
|
@Inject(DI.db)
|
||||||
private db: DataSource,
|
private db: DataSource,
|
||||||
|
|
||||||
@Inject(DI.config)
|
|
||||||
private config: Config,
|
|
||||||
|
|
||||||
@Inject(DI.usersRepository)
|
@Inject(DI.usersRepository)
|
||||||
private usersRepository: UsersRepository,
|
private usersRepository: UsersRepository,
|
||||||
|
|
||||||
@ -47,9 +43,9 @@ export class SignupService {
|
|||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async signup(opts: {
|
public async signup(opts: {
|
||||||
username: User['username'];
|
username: MiUser['username'];
|
||||||
password?: string | null;
|
password?: string | null;
|
||||||
passwordHash?: UserProfile['password'] | null;
|
passwordHash?: MiUserProfile['password'] | null;
|
||||||
host?: string | null;
|
host?: string | null;
|
||||||
ignorePreservedUsernames?: boolean;
|
ignorePreservedUsernames?: boolean;
|
||||||
}) {
|
}) {
|
||||||
@ -112,18 +108,18 @@ export class SignupService {
|
|||||||
err ? rej(err) : res([publicKey, privateKey]),
|
err ? rej(err) : res([publicKey, privateKey]),
|
||||||
));
|
));
|
||||||
|
|
||||||
let account!: User;
|
let account!: MiUser;
|
||||||
|
|
||||||
// Start transaction
|
// Start transaction
|
||||||
await this.db.transaction(async transactionalEntityManager => {
|
await this.db.transaction(async transactionalEntityManager => {
|
||||||
const exist = await transactionalEntityManager.findOneBy(User, {
|
const exist = await transactionalEntityManager.findOneBy(MiUser, {
|
||||||
usernameLower: username.toLowerCase(),
|
usernameLower: username.toLowerCase(),
|
||||||
host: IsNull(),
|
host: IsNull(),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (exist) throw new Error(' the username is already used');
|
if (exist) throw new Error(' the username is already used');
|
||||||
|
|
||||||
account = await transactionalEntityManager.save(new User({
|
account = await transactionalEntityManager.save(new MiUser({
|
||||||
id: this.idService.genId(),
|
id: this.idService.genId(),
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
username: username,
|
username: username,
|
||||||
@ -133,19 +129,19 @@ export class SignupService {
|
|||||||
isRoot: isTheFirstUser,
|
isRoot: isTheFirstUser,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
await transactionalEntityManager.save(new UserKeypair({
|
await transactionalEntityManager.save(new MiUserKeypair({
|
||||||
publicKey: keyPair[0],
|
publicKey: keyPair[0],
|
||||||
privateKey: keyPair[1],
|
privateKey: keyPair[1],
|
||||||
userId: account.id,
|
userId: account.id,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
await transactionalEntityManager.save(new UserProfile({
|
await transactionalEntityManager.save(new MiUserProfile({
|
||||||
userId: account.id,
|
userId: account.id,
|
||||||
autoAcceptFollowed: true,
|
autoAcceptFollowed: true,
|
||||||
password: hash,
|
password: hash,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
await transactionalEntityManager.save(new UsedUsername({
|
await transactionalEntityManager.save(new MiUsedUsername({
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
username: username.toLowerCase(),
|
username: username.toLowerCase(),
|
||||||
}));
|
}));
|
||||||
|
@ -7,7 +7,6 @@ import * as crypto from 'node:crypto';
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import * as jsrsasign from 'jsrsasign';
|
import * as jsrsasign from 'jsrsasign';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { UsersRepository } from '@/models/index.js';
|
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
|
||||||
@ -115,9 +114,6 @@ export class TwoFactorAuthenticationService {
|
|||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.config)
|
@Inject(DI.config)
|
||||||
private config: Config,
|
private config: Config,
|
||||||
|
|
||||||
@Inject(DI.usersRepository)
|
|
||||||
private usersRepository: UsersRepository,
|
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,8 +6,8 @@
|
|||||||
import { Inject, Injectable, OnModuleInit } from '@nestjs/common';
|
import { Inject, Injectable, OnModuleInit } from '@nestjs/common';
|
||||||
import { ModuleRef } from '@nestjs/core';
|
import { ModuleRef } from '@nestjs/core';
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
import type { User } from '@/models/entities/User.js';
|
import type { MiUser } from '@/models/entities/User.js';
|
||||||
import type { Blocking } from '@/models/entities/Blocking.js';
|
import type { MiBlocking } from '@/models/entities/Blocking.js';
|
||||||
import { QueueService } from '@/core/QueueService.js';
|
import { QueueService } from '@/core/QueueService.js';
|
||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
@ -58,7 +58,7 @@ export class UserBlockingService implements OnModuleInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async block(blocker: User, blockee: User, silent = false) {
|
public async block(blocker: MiUser, blockee: MiUser, silent = false) {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this.cancelRequest(blocker, blockee, silent),
|
this.cancelRequest(blocker, blockee, silent),
|
||||||
this.cancelRequest(blockee, blocker, silent),
|
this.cancelRequest(blockee, blocker, silent),
|
||||||
@ -74,7 +74,7 @@ export class UserBlockingService implements OnModuleInit {
|
|||||||
blockerId: blocker.id,
|
blockerId: blocker.id,
|
||||||
blockee,
|
blockee,
|
||||||
blockeeId: blockee.id,
|
blockeeId: blockee.id,
|
||||||
} as Blocking;
|
} as MiBlocking;
|
||||||
|
|
||||||
await this.blockingsRepository.insert(blocking);
|
await this.blockingsRepository.insert(blocking);
|
||||||
|
|
||||||
@ -93,7 +93,7 @@ export class UserBlockingService implements OnModuleInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async cancelRequest(follower: User, followee: User, silent = false) {
|
private async cancelRequest(follower: MiUser, followee: MiUser, silent = false) {
|
||||||
const request = await this.followRequestsRepository.findOneBy({
|
const request = await this.followRequestsRepository.findOneBy({
|
||||||
followeeId: followee.id,
|
followeeId: followee.id,
|
||||||
followerId: follower.id,
|
followerId: follower.id,
|
||||||
@ -143,7 +143,7 @@ export class UserBlockingService implements OnModuleInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async removeFromList(listOwner: User, user: User) {
|
private async removeFromList(listOwner: MiUser, user: MiUser) {
|
||||||
const userLists = await this.userListsRepository.findBy({
|
const userLists = await this.userListsRepository.findBy({
|
||||||
userId: listOwner.id,
|
userId: listOwner.id,
|
||||||
});
|
});
|
||||||
@ -157,7 +157,7 @@ export class UserBlockingService implements OnModuleInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async unblock(blocker: User, blockee: User) {
|
public async unblock(blocker: MiUser, blockee: MiUser) {
|
||||||
const blocking = await this.blockingsRepository.findOneBy({
|
const blocking = await this.blockingsRepository.findOneBy({
|
||||||
blockerId: blocker.id,
|
blockerId: blocker.id,
|
||||||
blockeeId: blockee.id,
|
blockeeId: blockee.id,
|
||||||
@ -191,7 +191,7 @@ export class UserBlockingService implements OnModuleInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async checkBlocked(blockerId: User['id'], blockeeId: User['id']): Promise<boolean> {
|
public async checkBlocked(blockerId: MiUser['id'], blockeeId: MiUser['id']): Promise<boolean> {
|
||||||
return (await this.cacheService.userBlockingCache.fetch(blockerId)).has(blockeeId);
|
return (await this.cacheService.userBlockingCache.fetch(blockerId)).has(blockeeId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
import { Inject, Injectable, OnModuleInit, forwardRef } from '@nestjs/common';
|
import { Inject, Injectable, OnModuleInit, forwardRef } from '@nestjs/common';
|
||||||
import { ModuleRef } from '@nestjs/core';
|
import { ModuleRef } from '@nestjs/core';
|
||||||
import { IsNull } from 'typeorm';
|
import { IsNull } from 'typeorm';
|
||||||
import type { LocalUser, PartialLocalUser, PartialRemoteUser, RemoteUser, User } from '@/models/entities/User.js';
|
import type { MiLocalUser, MiPartialLocalUser, MiPartialRemoteUser, MiRemoteUser, MiUser } from '@/models/entities/User.js';
|
||||||
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
||||||
import { QueueService } from '@/core/QueueService.js';
|
import { QueueService } from '@/core/QueueService.js';
|
||||||
import PerUserFollowingChart from '@/core/chart/charts/per-user-following.js';
|
import PerUserFollowingChart from '@/core/chart/charts/per-user-following.js';
|
||||||
@ -32,16 +32,16 @@ import Logger from '../logger.js';
|
|||||||
|
|
||||||
const logger = new Logger('following/create');
|
const logger = new Logger('following/create');
|
||||||
|
|
||||||
type Local = LocalUser | {
|
type Local = MiLocalUser | {
|
||||||
id: LocalUser['id'];
|
id: MiLocalUser['id'];
|
||||||
host: LocalUser['host'];
|
host: MiLocalUser['host'];
|
||||||
uri: LocalUser['uri']
|
uri: MiLocalUser['uri']
|
||||||
};
|
};
|
||||||
type Remote = RemoteUser | {
|
type Remote = MiRemoteUser | {
|
||||||
id: RemoteUser['id'];
|
id: MiRemoteUser['id'];
|
||||||
host: RemoteUser['host'];
|
host: MiRemoteUser['host'];
|
||||||
uri: RemoteUser['uri'];
|
uri: MiRemoteUser['uri'];
|
||||||
inbox: RemoteUser['inbox'];
|
inbox: MiRemoteUser['inbox'];
|
||||||
};
|
};
|
||||||
type Both = Local | Remote;
|
type Both = Local | Remote;
|
||||||
|
|
||||||
@ -91,11 +91,11 @@ export class UserFollowingService implements OnModuleInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async follow(_follower: { id: User['id'] }, _followee: { id: User['id'] }, requestId?: string, silent = false): Promise<void> {
|
public async follow(_follower: { id: MiUser['id'] }, _followee: { id: MiUser['id'] }, requestId?: string, silent = false): Promise<void> {
|
||||||
const [follower, followee] = await Promise.all([
|
const [follower, followee] = await Promise.all([
|
||||||
this.usersRepository.findOneByOrFail({ id: _follower.id }),
|
this.usersRepository.findOneByOrFail({ id: _follower.id }),
|
||||||
this.usersRepository.findOneByOrFail({ id: _followee.id }),
|
this.usersRepository.findOneByOrFail({ id: _followee.id }),
|
||||||
]) as [LocalUser | RemoteUser, LocalUser | RemoteUser];
|
]) as [MiLocalUser | MiRemoteUser, MiLocalUser | MiRemoteUser];
|
||||||
|
|
||||||
// check blocking
|
// check blocking
|
||||||
const [blocking, blocked] = await Promise.all([
|
const [blocking, blocked] = await Promise.all([
|
||||||
@ -180,10 +180,10 @@ export class UserFollowingService implements OnModuleInit {
|
|||||||
@bindThis
|
@bindThis
|
||||||
private async insertFollowingDoc(
|
private async insertFollowingDoc(
|
||||||
followee: {
|
followee: {
|
||||||
id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']
|
id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox']
|
||||||
},
|
},
|
||||||
follower: {
|
follower: {
|
||||||
id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']
|
id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox']
|
||||||
},
|
},
|
||||||
silent = false,
|
silent = false,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
@ -312,10 +312,10 @@ export class UserFollowingService implements OnModuleInit {
|
|||||||
@bindThis
|
@bindThis
|
||||||
public async unfollow(
|
public async unfollow(
|
||||||
follower: {
|
follower: {
|
||||||
id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox'];
|
id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox'];
|
||||||
},
|
},
|
||||||
followee: {
|
followee: {
|
||||||
id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox'];
|
id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox'];
|
||||||
},
|
},
|
||||||
silent = false,
|
silent = false,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
@ -358,21 +358,21 @@ export class UserFollowingService implements OnModuleInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) {
|
if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) {
|
||||||
const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower as PartialLocalUser, followee as PartialRemoteUser), follower));
|
const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower as MiPartialLocalUser, followee as MiPartialRemoteUser), follower));
|
||||||
this.queueService.deliver(follower, content, followee.inbox, false);
|
this.queueService.deliver(follower, content, followee.inbox, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.userEntityService.isLocalUser(followee) && this.userEntityService.isRemoteUser(follower)) {
|
if (this.userEntityService.isLocalUser(followee) && this.userEntityService.isRemoteUser(follower)) {
|
||||||
// local user has null host
|
// local user has null host
|
||||||
const content = this.apRendererService.addContext(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower as PartialRemoteUser, followee as PartialLocalUser), followee));
|
const content = this.apRendererService.addContext(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower as MiPartialRemoteUser, followee as MiPartialLocalUser), followee));
|
||||||
this.queueService.deliver(followee, content, follower.inbox, false);
|
this.queueService.deliver(followee, content, follower.inbox, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async decrementFollowing(
|
private async decrementFollowing(
|
||||||
follower: User,
|
follower: MiUser,
|
||||||
followee: User,
|
followee: MiUser,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
this.globalEventService.publishInternalEvent('unfollow', { followerId: follower.id, followeeId: followee.id });
|
this.globalEventService.publishInternalEvent('unfollow', { followerId: follower.id, followeeId: followee.id });
|
||||||
|
|
||||||
@ -444,10 +444,10 @@ export class UserFollowingService implements OnModuleInit {
|
|||||||
@bindThis
|
@bindThis
|
||||||
public async createFollowRequest(
|
public async createFollowRequest(
|
||||||
follower: {
|
follower: {
|
||||||
id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox'];
|
id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox'];
|
||||||
},
|
},
|
||||||
followee: {
|
followee: {
|
||||||
id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox'];
|
id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox'];
|
||||||
},
|
},
|
||||||
requestId?: string,
|
requestId?: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
@ -494,7 +494,7 @@ export class UserFollowingService implements OnModuleInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) {
|
if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) {
|
||||||
const content = this.apRendererService.addContext(this.apRendererService.renderFollow(follower as PartialLocalUser, followee as PartialRemoteUser, requestId ?? `${this.config.url}/follows/${followRequest.id}`));
|
const content = this.apRendererService.addContext(this.apRendererService.renderFollow(follower as MiPartialLocalUser, followee as MiPartialRemoteUser, requestId ?? `${this.config.url}/follows/${followRequest.id}`));
|
||||||
this.queueService.deliver(follower, content, followee.inbox, false);
|
this.queueService.deliver(follower, content, followee.inbox, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -502,14 +502,14 @@ export class UserFollowingService implements OnModuleInit {
|
|||||||
@bindThis
|
@bindThis
|
||||||
public async cancelFollowRequest(
|
public async cancelFollowRequest(
|
||||||
followee: {
|
followee: {
|
||||||
id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']
|
id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']
|
||||||
},
|
},
|
||||||
follower: {
|
follower: {
|
||||||
id: User['id']; host: User['host']; uri: User['host']
|
id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']
|
||||||
},
|
},
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (this.userEntityService.isRemoteUser(followee)) {
|
if (this.userEntityService.isRemoteUser(followee)) {
|
||||||
const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower as PartialLocalUser | PartialRemoteUser, followee as PartialRemoteUser), follower));
|
const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower as MiPartialLocalUser | MiPartialRemoteUser, followee as MiPartialRemoteUser), follower));
|
||||||
|
|
||||||
if (this.userEntityService.isLocalUser(follower)) { // 本来このチェックは不要だけどTSに怒られるので
|
if (this.userEntityService.isLocalUser(follower)) { // 本来このチェックは不要だけどTSに怒られるので
|
||||||
this.queueService.deliver(follower, content, followee.inbox, false);
|
this.queueService.deliver(follower, content, followee.inbox, false);
|
||||||
@ -540,9 +540,9 @@ export class UserFollowingService implements OnModuleInit {
|
|||||||
@bindThis
|
@bindThis
|
||||||
public async acceptFollowRequest(
|
public async acceptFollowRequest(
|
||||||
followee: {
|
followee: {
|
||||||
id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox'];
|
id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox'];
|
||||||
},
|
},
|
||||||
follower: User,
|
follower: MiUser,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const request = await this.followRequestsRepository.findOneBy({
|
const request = await this.followRequestsRepository.findOneBy({
|
||||||
followeeId: followee.id,
|
followeeId: followee.id,
|
||||||
@ -556,7 +556,7 @@ export class UserFollowingService implements OnModuleInit {
|
|||||||
await this.insertFollowingDoc(followee, follower);
|
await this.insertFollowingDoc(followee, follower);
|
||||||
|
|
||||||
if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) {
|
if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) {
|
||||||
const content = this.apRendererService.addContext(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee as PartialLocalUser, request.requestId!), followee));
|
const content = this.apRendererService.addContext(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee as MiPartialLocalUser, request.requestId!), followee));
|
||||||
this.queueService.deliver(followee, content, follower.inbox, false);
|
this.queueService.deliver(followee, content, follower.inbox, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -568,7 +568,7 @@ export class UserFollowingService implements OnModuleInit {
|
|||||||
@bindThis
|
@bindThis
|
||||||
public async acceptAllFollowRequests(
|
public async acceptAllFollowRequests(
|
||||||
user: {
|
user: {
|
||||||
id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox'];
|
id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox'];
|
||||||
},
|
},
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const requests = await this.followRequestsRepository.findBy({
|
const requests = await this.followRequestsRepository.findBy({
|
||||||
|
@ -5,16 +5,16 @@
|
|||||||
|
|
||||||
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
|
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
|
||||||
import * as Redis from 'ioredis';
|
import * as Redis from 'ioredis';
|
||||||
import type { User } from '@/models/entities/User.js';
|
import type { MiUser } from '@/models/entities/User.js';
|
||||||
import type { UserKeypairsRepository } from '@/models/index.js';
|
import type { UserKeypairsRepository } from '@/models/index.js';
|
||||||
import { RedisKVCache } from '@/misc/cache.js';
|
import { RedisKVCache } from '@/misc/cache.js';
|
||||||
import type { UserKeypair } from '@/models/entities/UserKeypair.js';
|
import type { MiUserKeypair } from '@/models/entities/UserKeypair.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UserKeypairService implements OnApplicationShutdown {
|
export class UserKeypairService implements OnApplicationShutdown {
|
||||||
private cache: RedisKVCache<UserKeypair>;
|
private cache: RedisKVCache<MiUserKeypair>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.redis)
|
@Inject(DI.redis)
|
||||||
@ -23,7 +23,7 @@ export class UserKeypairService implements OnApplicationShutdown {
|
|||||||
@Inject(DI.userKeypairsRepository)
|
@Inject(DI.userKeypairsRepository)
|
||||||
private userKeypairsRepository: UserKeypairsRepository,
|
private userKeypairsRepository: UserKeypairsRepository,
|
||||||
) {
|
) {
|
||||||
this.cache = new RedisKVCache<UserKeypair>(this.redisClient, 'userKeypair', {
|
this.cache = new RedisKVCache<MiUserKeypair>(this.redisClient, 'userKeypair', {
|
||||||
lifetime: 1000 * 60 * 60 * 24, // 24h
|
lifetime: 1000 * 60 * 60 * 24, // 24h
|
||||||
memoryCacheLifetime: Infinity,
|
memoryCacheLifetime: Infinity,
|
||||||
fetcher: (key) => this.userKeypairsRepository.findOneByOrFail({ userId: key }),
|
fetcher: (key) => this.userKeypairsRepository.findOneByOrFail({ userId: key }),
|
||||||
@ -33,7 +33,7 @@ export class UserKeypairService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async getUserKeypair(userId: User['id']): Promise<UserKeypair> {
|
public async getUserKeypair(userId: MiUser['id']): Promise<MiUserKeypair> {
|
||||||
return await this.cache.fetch(userId);
|
return await this.cache.fetch(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,12 +4,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import type { UserListJoiningsRepository, UsersRepository } from '@/models/index.js';
|
import type { UserListJoiningsRepository } from '@/models/index.js';
|
||||||
import type { User } from '@/models/entities/User.js';
|
import type { MiUser } from '@/models/entities/User.js';
|
||||||
import type { UserList } from '@/models/entities/UserList.js';
|
import type { MiUserList } from '@/models/entities/UserList.js';
|
||||||
import type { UserListJoining } from '@/models/entities/UserListJoining.js';
|
import type { MiUserListJoining } from '@/models/entities/UserListJoining.js';
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
import { UserFollowingService } from '@/core/UserFollowingService.js';
|
|
||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
@ -23,15 +22,11 @@ export class UserListService {
|
|||||||
public static TooManyUsersError = class extends Error {};
|
public static TooManyUsersError = class extends Error {};
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.usersRepository)
|
|
||||||
private usersRepository: UsersRepository,
|
|
||||||
|
|
||||||
@Inject(DI.userListJoiningsRepository)
|
@Inject(DI.userListJoiningsRepository)
|
||||||
private userListJoiningsRepository: UserListJoiningsRepository,
|
private userListJoiningsRepository: UserListJoiningsRepository,
|
||||||
|
|
||||||
private userEntityService: UserEntityService,
|
private userEntityService: UserEntityService,
|
||||||
private idService: IdService,
|
private idService: IdService,
|
||||||
private userFollowingService: UserFollowingService,
|
|
||||||
private roleService: RoleService,
|
private roleService: RoleService,
|
||||||
private globalEventService: GlobalEventService,
|
private globalEventService: GlobalEventService,
|
||||||
private proxyAccountService: ProxyAccountService,
|
private proxyAccountService: ProxyAccountService,
|
||||||
@ -40,7 +35,7 @@ export class UserListService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async push(target: User, list: UserList, me: User) {
|
public async push(target: MiUser, list: MiUserList, me: MiUser) {
|
||||||
const currentCount = await this.userListJoiningsRepository.countBy({
|
const currentCount = await this.userListJoiningsRepository.countBy({
|
||||||
userListId: list.id,
|
userListId: list.id,
|
||||||
});
|
});
|
||||||
@ -53,7 +48,7 @@ export class UserListService {
|
|||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
userId: target.id,
|
userId: target.id,
|
||||||
userListId: list.id,
|
userListId: list.id,
|
||||||
} as UserListJoining);
|
} as MiUserListJoining);
|
||||||
|
|
||||||
this.globalEventService.publishUserListStream(list.id, 'userAdded', await this.userEntityService.pack(target));
|
this.globalEventService.publishUserListStream(list.id, 'userAdded', await this.userEntityService.pack(target));
|
||||||
|
|
||||||
|
@ -5,9 +5,9 @@
|
|||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { In } from 'typeorm';
|
import { In } from 'typeorm';
|
||||||
import type { MutingsRepository, Muting } from '@/models/index.js';
|
import type { MutingsRepository, MiMuting } from '@/models/index.js';
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
import type { User } from '@/models/entities/User.js';
|
import type { MiUser } from '@/models/entities/User.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { CacheService } from '@/core/CacheService.js';
|
import { CacheService } from '@/core/CacheService.js';
|
||||||
@ -24,7 +24,7 @@ export class UserMutingService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async mute(user: User, target: User, expiresAt: Date | null = null): Promise<void> {
|
public async mute(user: MiUser, target: MiUser, expiresAt: Date | null = null): Promise<void> {
|
||||||
await this.mutingsRepository.insert({
|
await this.mutingsRepository.insert({
|
||||||
id: this.idService.genId(),
|
id: this.idService.genId(),
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
@ -37,7 +37,7 @@ export class UserMutingService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async unmute(mutings: Muting[]): Promise<void> {
|
public async unmute(mutings: MiMuting[]): Promise<void> {
|
||||||
if (mutings.length === 0) return;
|
if (mutings.length === 0) return;
|
||||||
|
|
||||||
await this.mutingsRepository.delete({
|
await this.mutingsRepository.delete({
|
||||||
|
@ -5,12 +5,11 @@
|
|||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { Not, IsNull } from 'typeorm';
|
import { Not, IsNull } from 'typeorm';
|
||||||
import type { FollowingsRepository, UsersRepository } from '@/models/index.js';
|
import type { FollowingsRepository } from '@/models/index.js';
|
||||||
import type { User } from '@/models/entities/User.js';
|
import type { MiUser } from '@/models/entities/User.js';
|
||||||
import { QueueService } from '@/core/QueueService.js';
|
import { QueueService } from '@/core/QueueService.js';
|
||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { Config } from '@/config.js';
|
|
||||||
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
|
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
@ -18,12 +17,6 @@ import { bindThis } from '@/decorators.js';
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class UserSuspendService {
|
export class UserSuspendService {
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.config)
|
|
||||||
private config: Config,
|
|
||||||
|
|
||||||
@Inject(DI.usersRepository)
|
|
||||||
private usersRepository: UsersRepository,
|
|
||||||
|
|
||||||
@Inject(DI.followingsRepository)
|
@Inject(DI.followingsRepository)
|
||||||
private followingsRepository: FollowingsRepository,
|
private followingsRepository: FollowingsRepository,
|
||||||
|
|
||||||
@ -35,7 +28,7 @@ export class UserSuspendService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async doPostSuspend(user: { id: User['id']; host: User['host'] }): Promise<void> {
|
public async doPostSuspend(user: { id: MiUser['id']; host: MiUser['host'] }): Promise<void> {
|
||||||
this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: true });
|
this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: true });
|
||||||
|
|
||||||
if (this.userEntityService.isLocalUser(user)) {
|
if (this.userEntityService.isLocalUser(user)) {
|
||||||
@ -65,7 +58,7 @@ export class UserSuspendService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async doPostUnsuspend(user: User): Promise<void> {
|
public async doPostUnsuspend(user: MiUser): Promise<void> {
|
||||||
this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: false });
|
this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: false });
|
||||||
|
|
||||||
if (this.userEntityService.isLocalUser(user)) {
|
if (this.userEntityService.isLocalUser(user)) {
|
||||||
|
@ -4,9 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { URL } from 'node:url';
|
import { URL } from 'node:url';
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { DI } from '@/di-symbols.js';
|
|
||||||
import type { Config } from '@/config.js';
|
|
||||||
import { query as urlQuery } from '@/misc/prelude/url.js';
|
import { query as urlQuery } from '@/misc/prelude/url.js';
|
||||||
import { HttpRequestService } from '@/core/HttpRequestService.js';
|
import { HttpRequestService } from '@/core/HttpRequestService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
@ -27,9 +25,6 @@ const mRegex = /^([^@]+)@(.*)/;
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class WebfingerService {
|
export class WebfingerService {
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.config)
|
|
||||||
private config: Config,
|
|
||||||
|
|
||||||
private httpRequestService: HttpRequestService,
|
private httpRequestService: HttpRequestService,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import * as Redis from 'ioredis';
|
import * as Redis from 'ioredis';
|
||||||
import type { WebhooksRepository } from '@/models/index.js';
|
import type { WebhooksRepository } from '@/models/index.js';
|
||||||
import type { Webhook } from '@/models/entities/Webhook.js';
|
import type { MiWebhook } from '@/models/entities/Webhook.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { StreamMessages } from '@/server/api/stream/types.js';
|
import { StreamMessages } from '@/server/api/stream/types.js';
|
||||||
@ -15,7 +15,7 @@ import type { OnApplicationShutdown } from '@nestjs/common';
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class WebhookService implements OnApplicationShutdown {
|
export class WebhookService implements OnApplicationShutdown {
|
||||||
private webhooksFetched = false;
|
private webhooksFetched = false;
|
||||||
private webhooks: Webhook[] = [];
|
private webhooks: MiWebhook[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.redisForSub)
|
@Inject(DI.redisForSub)
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import promiseLimit from 'promise-limit';
|
import promiseLimit from 'promise-limit';
|
||||||
import type { RemoteUser, User } from '@/models/entities/User.js';
|
import type { MiRemoteUser, MiUser } from '@/models/entities/User.js';
|
||||||
import { concat, unique } from '@/misc/prelude/array.js';
|
import { concat, unique } from '@/misc/prelude/array.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { getApIds } from './type.js';
|
import { getApIds } from './type.js';
|
||||||
@ -17,8 +17,8 @@ type Visibility = 'public' | 'home' | 'followers' | 'specified';
|
|||||||
|
|
||||||
type AudienceInfo = {
|
type AudienceInfo = {
|
||||||
visibility: Visibility,
|
visibility: Visibility,
|
||||||
mentionedUsers: User[],
|
mentionedUsers: MiUser[],
|
||||||
visibleUsers: User[],
|
visibleUsers: MiUser[],
|
||||||
};
|
};
|
||||||
|
|
||||||
type GroupedAudience = Record<'public' | 'followers' | 'other', string[]>;
|
type GroupedAudience = Record<'public' | 'followers' | 'other', string[]>;
|
||||||
@ -31,16 +31,16 @@ export class ApAudienceService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async parseAudience(actor: RemoteUser, to?: ApObject, cc?: ApObject, resolver?: Resolver): Promise<AudienceInfo> {
|
public async parseAudience(actor: MiRemoteUser, to?: ApObject, cc?: ApObject, resolver?: Resolver): Promise<AudienceInfo> {
|
||||||
const toGroups = this.groupingAudience(getApIds(to), actor);
|
const toGroups = this.groupingAudience(getApIds(to), actor);
|
||||||
const ccGroups = this.groupingAudience(getApIds(cc), actor);
|
const ccGroups = this.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<MiUser | null>(2);
|
||||||
const mentionedUsers = (await Promise.all(
|
const mentionedUsers = (await Promise.all(
|
||||||
others.map(id => limit(() => this.apPersonService.resolvePerson(id, resolver).catch(() => null))),
|
others.map(id => limit(() => this.apPersonService.resolvePerson(id, resolver).catch(() => null))),
|
||||||
)).filter((x): x is User => x != null);
|
)).filter((x): x is MiUser => x != null);
|
||||||
|
|
||||||
if (toGroups.public.length > 0) {
|
if (toGroups.public.length > 0) {
|
||||||
return {
|
return {
|
||||||
@ -74,7 +74,7 @@ export class ApAudienceService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private groupingAudience(ids: string[], actor: RemoteUser): GroupedAudience {
|
private groupingAudience(ids: string[], actor: MiRemoteUser): GroupedAudience {
|
||||||
const groups: GroupedAudience = {
|
const groups: GroupedAudience = {
|
||||||
public: [],
|
public: [],
|
||||||
followers: [],
|
followers: [],
|
||||||
@ -106,7 +106,7 @@ export class ApAudienceService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private isFollowers(id: string, actor: RemoteUser): boolean {
|
private isFollowers(id: string, actor: MiRemoteUser): boolean {
|
||||||
return id === (actor.followersUri ?? `${actor.uri}/followers`);
|
return id === (actor.followersUri ?? `${actor.uri}/followers`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,11 +8,11 @@ import { DI } from '@/di-symbols.js';
|
|||||||
import type { NotesRepository, UserPublickeysRepository, UsersRepository } from '@/models/index.js';
|
import type { NotesRepository, UserPublickeysRepository, UsersRepository } from '@/models/index.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import { MemoryKVCache } from '@/misc/cache.js';
|
import { MemoryKVCache } from '@/misc/cache.js';
|
||||||
import type { UserPublickey } from '@/models/entities/UserPublickey.js';
|
import type { MiUserPublickey } from '@/models/entities/UserPublickey.js';
|
||||||
import { CacheService } from '@/core/CacheService.js';
|
import { CacheService } from '@/core/CacheService.js';
|
||||||
import type { Note } from '@/models/entities/Note.js';
|
import type { MiNote } from '@/models/entities/Note.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { LocalUser, RemoteUser } from '@/models/entities/User.js';
|
import { MiLocalUser, MiRemoteUser } from '@/models/entities/User.js';
|
||||||
import { getApId } from './type.js';
|
import { getApId } from './type.js';
|
||||||
import { ApPersonService } from './models/ApPersonService.js';
|
import { ApPersonService } from './models/ApPersonService.js';
|
||||||
import type { IObject } from './type.js';
|
import type { IObject } from './type.js';
|
||||||
@ -35,8 +35,8 @@ export type UriParseResult = {
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ApDbResolverService implements OnApplicationShutdown {
|
export class ApDbResolverService implements OnApplicationShutdown {
|
||||||
private publicKeyCache: MemoryKVCache<UserPublickey | null>;
|
private publicKeyCache: MemoryKVCache<MiUserPublickey | null>;
|
||||||
private publicKeyByUserIdCache: MemoryKVCache<UserPublickey | null>;
|
private publicKeyByUserIdCache: MemoryKVCache<MiUserPublickey | null>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.config)
|
@Inject(DI.config)
|
||||||
@ -54,8 +54,8 @@ export class ApDbResolverService implements OnApplicationShutdown {
|
|||||||
private cacheService: CacheService,
|
private cacheService: CacheService,
|
||||||
private apPersonService: ApPersonService,
|
private apPersonService: ApPersonService,
|
||||||
) {
|
) {
|
||||||
this.publicKeyCache = new MemoryKVCache<UserPublickey | null>(Infinity);
|
this.publicKeyCache = new MemoryKVCache<MiUserPublickey | null>(Infinity);
|
||||||
this.publicKeyByUserIdCache = new MemoryKVCache<UserPublickey | null>(Infinity);
|
this.publicKeyByUserIdCache = new MemoryKVCache<MiUserPublickey | null>(Infinity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
@ -78,7 +78,7 @@ export class ApDbResolverService implements OnApplicationShutdown {
|
|||||||
* AP Note => Misskey Note in DB
|
* AP Note => Misskey Note in DB
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async getNoteFromApId(value: string | IObject): Promise<Note | null> {
|
public async getNoteFromApId(value: string | IObject): Promise<MiNote | null> {
|
||||||
const parsed = this.parseUri(value);
|
const parsed = this.parseUri(value);
|
||||||
|
|
||||||
if (parsed.local) {
|
if (parsed.local) {
|
||||||
@ -98,7 +98,7 @@ export class ApDbResolverService implements OnApplicationShutdown {
|
|||||||
* AP Person => Misskey User in DB
|
* AP Person => Misskey User in DB
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async getUserFromApId(value: string | IObject): Promise<LocalUser | RemoteUser | null> {
|
public async getUserFromApId(value: string | IObject): Promise<MiLocalUser | MiRemoteUser | null> {
|
||||||
const parsed = this.parseUri(value);
|
const parsed = this.parseUri(value);
|
||||||
|
|
||||||
if (parsed.local) {
|
if (parsed.local) {
|
||||||
@ -107,12 +107,12 @@ export class ApDbResolverService implements OnApplicationShutdown {
|
|||||||
return await this.cacheService.userByIdCache.fetchMaybe(
|
return await this.cacheService.userByIdCache.fetchMaybe(
|
||||||
parsed.id,
|
parsed.id,
|
||||||
() => this.usersRepository.findOneBy({ id: parsed.id }).then(x => x ?? undefined),
|
() => this.usersRepository.findOneBy({ id: parsed.id }).then(x => x ?? undefined),
|
||||||
) as LocalUser | undefined ?? null;
|
) as MiLocalUser | undefined ?? null;
|
||||||
} else {
|
} else {
|
||||||
return await this.cacheService.uriPersonCache.fetch(
|
return await this.cacheService.uriPersonCache.fetch(
|
||||||
parsed.uri,
|
parsed.uri,
|
||||||
() => this.usersRepository.findOneBy({ uri: parsed.uri }),
|
() => this.usersRepository.findOneBy({ uri: parsed.uri }),
|
||||||
) as RemoteUser | null;
|
) as MiRemoteUser | null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,8 +121,8 @@ export class ApDbResolverService implements OnApplicationShutdown {
|
|||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async getAuthUserFromKeyId(keyId: string): Promise<{
|
public async getAuthUserFromKeyId(keyId: string): Promise<{
|
||||||
user: RemoteUser;
|
user: MiRemoteUser;
|
||||||
key: UserPublickey;
|
key: MiUserPublickey;
|
||||||
} | null> {
|
} | null> {
|
||||||
const key = await this.publicKeyCache.fetch(keyId, async () => {
|
const key = await this.publicKeyCache.fetch(keyId, async () => {
|
||||||
const key = await this.userPublickeysRepository.findOneBy({
|
const key = await this.userPublickeysRepository.findOneBy({
|
||||||
@ -137,7 +137,7 @@ export class ApDbResolverService implements OnApplicationShutdown {
|
|||||||
if (key == null) return null;
|
if (key == null) return null;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
user: await this.cacheService.findUserById(key.userId) as RemoteUser,
|
user: await this.cacheService.findUserById(key.userId) as MiRemoteUser,
|
||||||
key,
|
key,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -147,10 +147,10 @@ export class ApDbResolverService implements OnApplicationShutdown {
|
|||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async getAuthUserFromApId(uri: string): Promise<{
|
public async getAuthUserFromApId(uri: string): Promise<{
|
||||||
user: RemoteUser;
|
user: MiRemoteUser;
|
||||||
key: UserPublickey | null;
|
key: MiUserPublickey | null;
|
||||||
} | null> {
|
} | null> {
|
||||||
const user = await this.apPersonService.resolvePerson(uri) as RemoteUser;
|
const user = await this.apPersonService.resolvePerson(uri) as MiRemoteUser;
|
||||||
|
|
||||||
const key = await this.publicKeyByUserIdCache.fetch(
|
const key = await this.publicKeyByUserIdCache.fetch(
|
||||||
user.id,
|
user.id,
|
||||||
|
@ -6,9 +6,8 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { IsNull, Not } from 'typeorm';
|
import { IsNull, Not } from 'typeorm';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { FollowingsRepository, UsersRepository } from '@/models/index.js';
|
import type { FollowingsRepository } from '@/models/index.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { MiLocalUser, MiRemoteUser, MiUser } from '@/models/entities/User.js';
|
||||||
import type { LocalUser, RemoteUser, User } from '@/models/entities/User.js';
|
|
||||||
import { QueueService } from '@/core/QueueService.js';
|
import { QueueService } from '@/core/QueueService.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
@ -25,7 +24,7 @@ interface IFollowersRecipe extends IRecipe {
|
|||||||
|
|
||||||
interface IDirectRecipe extends IRecipe {
|
interface IDirectRecipe extends IRecipe {
|
||||||
type: 'Direct';
|
type: 'Direct';
|
||||||
to: RemoteUser;
|
to: MiRemoteUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isFollowers = (recipe: IRecipe): recipe is IFollowersRecipe =>
|
const isFollowers = (recipe: IRecipe): recipe is IFollowersRecipe =>
|
||||||
@ -52,7 +51,7 @@ class DeliverManager {
|
|||||||
private followingsRepository: FollowingsRepository,
|
private followingsRepository: FollowingsRepository,
|
||||||
private queueService: QueueService,
|
private queueService: QueueService,
|
||||||
|
|
||||||
actor: { id: User['id']; host: null; },
|
actor: { id: MiUser['id']; host: null; },
|
||||||
activity: IActivity | null,
|
activity: IActivity | null,
|
||||||
) {
|
) {
|
||||||
// 型で弾いてはいるが一応ローカルユーザーかチェック
|
// 型で弾いてはいるが一応ローカルユーザーかチェック
|
||||||
@ -83,7 +82,7 @@ class DeliverManager {
|
|||||||
* @param to To
|
* @param to To
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public addDirectRecipe(to: RemoteUser): void {
|
public addDirectRecipe(to: MiRemoteUser): void {
|
||||||
const recipe: IDirectRecipe = {
|
const recipe: IDirectRecipe = {
|
||||||
type: 'Direct',
|
type: 'Direct',
|
||||||
to,
|
to,
|
||||||
@ -152,12 +151,6 @@ class DeliverManager {
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class ApDeliverManagerService {
|
export class ApDeliverManagerService {
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.config)
|
|
||||||
private config: Config,
|
|
||||||
|
|
||||||
@Inject(DI.usersRepository)
|
|
||||||
private usersRepository: UsersRepository,
|
|
||||||
|
|
||||||
@Inject(DI.followingsRepository)
|
@Inject(DI.followingsRepository)
|
||||||
private followingsRepository: FollowingsRepository,
|
private followingsRepository: FollowingsRepository,
|
||||||
|
|
||||||
@ -172,7 +165,7 @@ export class ApDeliverManagerService {
|
|||||||
* @param activity Activity
|
* @param activity Activity
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async deliverToFollowers(actor: { id: LocalUser['id']; host: null; }, activity: IActivity): Promise<void> {
|
public async deliverToFollowers(actor: { id: MiLocalUser['id']; host: null; }, activity: IActivity): Promise<void> {
|
||||||
const manager = new DeliverManager(
|
const manager = new DeliverManager(
|
||||||
this.userEntityService,
|
this.userEntityService,
|
||||||
this.followingsRepository,
|
this.followingsRepository,
|
||||||
@ -191,7 +184,7 @@ export class ApDeliverManagerService {
|
|||||||
* @param to Target user
|
* @param to Target user
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async deliverToUser(actor: { id: LocalUser['id']; host: null; }, activity: IActivity, to: RemoteUser): Promise<void> {
|
public async deliverToUser(actor: { id: MiLocalUser['id']; host: null; }, activity: IActivity, to: MiRemoteUser): Promise<void> {
|
||||||
const manager = new DeliverManager(
|
const manager = new DeliverManager(
|
||||||
this.userEntityService,
|
this.userEntityService,
|
||||||
this.followingsRepository,
|
this.followingsRepository,
|
||||||
@ -204,7 +197,7 @@ export class ApDeliverManagerService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public createDeliverManager(actor: { id: User['id']; host: null; }, activity: IActivity | null): DeliverManager {
|
public createDeliverManager(actor: { id: MiUser['id']; host: null; }, activity: IActivity | null): DeliverManager {
|
||||||
return new DeliverManager(
|
return new DeliverManager(
|
||||||
this.userEntityService,
|
this.userEntityService,
|
||||||
this.followingsRepository,
|
this.followingsRepository,
|
||||||
|
@ -18,17 +18,15 @@ import { concat, toArray, toSingle, unique } from '@/misc/prelude/array.js';
|
|||||||
import { AppLockService } from '@/core/AppLockService.js';
|
import { AppLockService } from '@/core/AppLockService.js';
|
||||||
import type Logger from '@/logger.js';
|
import type Logger from '@/logger.js';
|
||||||
import { MetaService } from '@/core/MetaService.js';
|
import { MetaService } from '@/core/MetaService.js';
|
||||||
import { AccountMoveService } from '@/core/AccountMoveService.js';
|
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
import { StatusError } from '@/misc/status-error.js';
|
import { StatusError } from '@/misc/status-error.js';
|
||||||
import { UtilityService } from '@/core/UtilityService.js';
|
import { UtilityService } from '@/core/UtilityService.js';
|
||||||
import { CacheService } from '@/core/CacheService.js';
|
|
||||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
import { QueueService } from '@/core/QueueService.js';
|
import { QueueService } from '@/core/QueueService.js';
|
||||||
import type { UsersRepository, NotesRepository, FollowingsRepository, AbuseUserReportsRepository, FollowRequestsRepository } from '@/models/index.js';
|
import type { UsersRepository, NotesRepository, FollowingsRepository, AbuseUserReportsRepository, FollowRequestsRepository } from '@/models/index.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import type { RemoteUser } from '@/models/entities/User.js';
|
import type { MiRemoteUser } from '@/models/entities/User.js';
|
||||||
import { getApHrefNullable, getApId, getApIds, getApType, isAccept, isActor, isAdd, isAnnounce, isBlock, isCollection, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isMove, isPost, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost } from './type.js';
|
import { getApHrefNullable, getApId, getApIds, getApType, isAccept, isActor, isAdd, isAnnounce, isBlock, isCollection, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isMove, isPost, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost } from './type.js';
|
||||||
import { ApNoteService } from './models/ApNoteService.js';
|
import { ApNoteService } from './models/ApNoteService.js';
|
||||||
import { ApLoggerService } from './ApLoggerService.js';
|
import { ApLoggerService } from './ApLoggerService.js';
|
||||||
@ -83,15 +81,13 @@ export class ApInboxService {
|
|||||||
private apNoteService: ApNoteService,
|
private apNoteService: ApNoteService,
|
||||||
private apPersonService: ApPersonService,
|
private apPersonService: ApPersonService,
|
||||||
private apQuestionService: ApQuestionService,
|
private apQuestionService: ApQuestionService,
|
||||||
private accountMoveService: AccountMoveService,
|
|
||||||
private cacheService: CacheService,
|
|
||||||
private queueService: QueueService,
|
private queueService: QueueService,
|
||||||
) {
|
) {
|
||||||
this.logger = this.apLoggerService.logger;
|
this.logger = this.apLoggerService.logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async performActivity(actor: RemoteUser, activity: IObject): Promise<void> {
|
public async performActivity(actor: MiRemoteUser, activity: IObject): Promise<void> {
|
||||||
if (isCollectionOrOrderedCollection(activity)) {
|
if (isCollectionOrOrderedCollection(activity)) {
|
||||||
const resolver = this.apResolverService.createResolver();
|
const resolver = this.apResolverService.createResolver();
|
||||||
for (const item of toArray(isCollection(activity) ? activity.items : activity.orderedItems)) {
|
for (const item of toArray(isCollection(activity) ? activity.items : activity.orderedItems)) {
|
||||||
@ -119,7 +115,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async performOneActivity(actor: RemoteUser, activity: IObject): Promise<void> {
|
public async performOneActivity(actor: MiRemoteUser, activity: IObject): Promise<void> {
|
||||||
if (actor.isSuspended) return;
|
if (actor.isSuspended) return;
|
||||||
|
|
||||||
if (isCreate(activity)) {
|
if (isCreate(activity)) {
|
||||||
@ -156,7 +152,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async follow(actor: RemoteUser, activity: IFollow): Promise<string> {
|
private async follow(actor: MiRemoteUser, activity: IFollow): Promise<string> {
|
||||||
const followee = await this.apDbResolverService.getUserFromApId(activity.object);
|
const followee = await this.apDbResolverService.getUserFromApId(activity.object);
|
||||||
|
|
||||||
if (followee == null) {
|
if (followee == null) {
|
||||||
@ -173,7 +169,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async like(actor: RemoteUser, activity: ILike): Promise<string> {
|
private async like(actor: MiRemoteUser, activity: ILike): Promise<string> {
|
||||||
const targetUri = getApId(activity.object);
|
const targetUri = getApId(activity.object);
|
||||||
|
|
||||||
const note = await this.apNoteService.fetchNote(targetUri);
|
const note = await this.apNoteService.fetchNote(targetUri);
|
||||||
@ -191,7 +187,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async accept(actor: RemoteUser, activity: IAccept): Promise<string> {
|
private async accept(actor: MiRemoteUser, activity: IAccept): Promise<string> {
|
||||||
const uri = activity.id ?? activity;
|
const uri = activity.id ?? activity;
|
||||||
|
|
||||||
this.logger.info(`Accept: ${uri}`);
|
this.logger.info(`Accept: ${uri}`);
|
||||||
@ -209,7 +205,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async acceptFollow(actor: RemoteUser, activity: IFollow): Promise<string> {
|
private async acceptFollow(actor: MiRemoteUser, activity: IFollow): Promise<string> {
|
||||||
// ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある
|
// ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある
|
||||||
|
|
||||||
const follower = await this.apDbResolverService.getUserFromApId(activity.actor);
|
const follower = await this.apDbResolverService.getUserFromApId(activity.actor);
|
||||||
@ -233,7 +229,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async add(actor: RemoteUser, activity: IAdd): Promise<void> {
|
private async add(actor: MiRemoteUser, activity: IAdd): Promise<void> {
|
||||||
if (actor.uri !== activity.actor) {
|
if (actor.uri !== activity.actor) {
|
||||||
throw new Error('invalid actor');
|
throw new Error('invalid actor');
|
||||||
}
|
}
|
||||||
@ -253,7 +249,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async announce(actor: RemoteUser, activity: IAnnounce): Promise<void> {
|
private async announce(actor: MiRemoteUser, activity: IAnnounce): Promise<void> {
|
||||||
const uri = getApId(activity);
|
const uri = getApId(activity);
|
||||||
|
|
||||||
this.logger.info(`Announce: ${uri}`);
|
this.logger.info(`Announce: ${uri}`);
|
||||||
@ -264,7 +260,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async announceNote(actor: RemoteUser, activity: IAnnounce, targetUri: string): Promise<void> {
|
private async announceNote(actor: MiRemoteUser, activity: IAnnounce, targetUri: string): Promise<void> {
|
||||||
const uri = getApId(activity);
|
const uri = getApId(activity);
|
||||||
|
|
||||||
if (actor.isSuspended) {
|
if (actor.isSuspended) {
|
||||||
@ -324,7 +320,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async block(actor: RemoteUser, activity: IBlock): Promise<string> {
|
private async block(actor: MiRemoteUser, activity: IBlock): Promise<string> {
|
||||||
// ※ activity.objectにブロック対象があり、それは存在するローカルユーザーのはず
|
// ※ activity.objectにブロック対象があり、それは存在するローカルユーザーのはず
|
||||||
|
|
||||||
const blockee = await this.apDbResolverService.getUserFromApId(activity.object);
|
const blockee = await this.apDbResolverService.getUserFromApId(activity.object);
|
||||||
@ -342,7 +338,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async create(actor: RemoteUser, activity: ICreate): Promise<void> {
|
private async create(actor: MiRemoteUser, activity: ICreate): Promise<void> {
|
||||||
const uri = getApId(activity);
|
const uri = getApId(activity);
|
||||||
|
|
||||||
this.logger.info(`Create: ${uri}`);
|
this.logger.info(`Create: ${uri}`);
|
||||||
@ -378,7 +374,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async createNote(resolver: Resolver, actor: RemoteUser, note: IObject, silent = false, activity?: ICreate): Promise<string> {
|
private async createNote(resolver: Resolver, actor: MiRemoteUser, note: IObject, silent = false, activity?: ICreate): Promise<string> {
|
||||||
const uri = getApId(note);
|
const uri = getApId(note);
|
||||||
|
|
||||||
if (typeof note === 'object') {
|
if (typeof note === 'object') {
|
||||||
@ -413,7 +409,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async delete(actor: RemoteUser, activity: IDelete): Promise<string> {
|
private async delete(actor: MiRemoteUser, activity: IDelete): Promise<string> {
|
||||||
if (actor.uri !== activity.actor) {
|
if (actor.uri !== activity.actor) {
|
||||||
throw new Error('invalid actor');
|
throw new Error('invalid actor');
|
||||||
}
|
}
|
||||||
@ -455,7 +451,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async deleteActor(actor: RemoteUser, uri: string): Promise<string> {
|
private async deleteActor(actor: MiRemoteUser, uri: string): Promise<string> {
|
||||||
this.logger.info(`Deleting the Actor: ${uri}`);
|
this.logger.info(`Deleting the Actor: ${uri}`);
|
||||||
|
|
||||||
if (actor.uri !== uri) {
|
if (actor.uri !== uri) {
|
||||||
@ -479,7 +475,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async deleteNote(actor: RemoteUser, uri: string): Promise<string> {
|
private async deleteNote(actor: MiRemoteUser, uri: string): Promise<string> {
|
||||||
this.logger.info(`Deleting the Note: ${uri}`);
|
this.logger.info(`Deleting the Note: ${uri}`);
|
||||||
|
|
||||||
const unlock = await this.appLockService.getApLock(uri);
|
const unlock = await this.appLockService.getApLock(uri);
|
||||||
@ -503,7 +499,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async flag(actor: RemoteUser, activity: IFlag): Promise<string> {
|
private async flag(actor: MiRemoteUser, 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);
|
||||||
@ -531,7 +527,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async reject(actor: RemoteUser, activity: IReject): Promise<string> {
|
private async reject(actor: MiRemoteUser, activity: IReject): Promise<string> {
|
||||||
const uri = activity.id ?? activity;
|
const uri = activity.id ?? activity;
|
||||||
|
|
||||||
this.logger.info(`Reject: ${uri}`);
|
this.logger.info(`Reject: ${uri}`);
|
||||||
@ -549,7 +545,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async rejectFollow(actor: RemoteUser, activity: IFollow): Promise<string> {
|
private async rejectFollow(actor: MiRemoteUser, activity: IFollow): Promise<string> {
|
||||||
// ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある
|
// ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある
|
||||||
|
|
||||||
const follower = await this.apDbResolverService.getUserFromApId(activity.actor);
|
const follower = await this.apDbResolverService.getUserFromApId(activity.actor);
|
||||||
@ -573,7 +569,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async remove(actor: RemoteUser, activity: IRemove): Promise<void> {
|
private async remove(actor: MiRemoteUser, activity: IRemove): Promise<void> {
|
||||||
if (actor.uri !== activity.actor) {
|
if (actor.uri !== activity.actor) {
|
||||||
throw new Error('invalid actor');
|
throw new Error('invalid actor');
|
||||||
}
|
}
|
||||||
@ -593,7 +589,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async undo(actor: RemoteUser, activity: IUndo): Promise<string> {
|
private async undo(actor: MiRemoteUser, activity: IUndo): Promise<string> {
|
||||||
if (actor.uri !== activity.actor) {
|
if (actor.uri !== activity.actor) {
|
||||||
throw new Error('invalid actor');
|
throw new Error('invalid actor');
|
||||||
}
|
}
|
||||||
@ -620,7 +616,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async undoAccept(actor: RemoteUser, activity: IAccept): Promise<string> {
|
private async undoAccept(actor: MiRemoteUser, activity: IAccept): Promise<string> {
|
||||||
const follower = await this.apDbResolverService.getUserFromApId(activity.object);
|
const follower = await this.apDbResolverService.getUserFromApId(activity.object);
|
||||||
if (follower == null) {
|
if (follower == null) {
|
||||||
return 'skip: follower not found';
|
return 'skip: follower not found';
|
||||||
@ -642,7 +638,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async undoAnnounce(actor: RemoteUser, activity: IAnnounce): Promise<string> {
|
private async undoAnnounce(actor: MiRemoteUser, activity: IAnnounce): Promise<string> {
|
||||||
const uri = getApId(activity);
|
const uri = getApId(activity);
|
||||||
|
|
||||||
const note = await this.notesRepository.findOneBy({
|
const note = await this.notesRepository.findOneBy({
|
||||||
@ -657,7 +653,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async undoBlock(actor: RemoteUser, activity: IBlock): Promise<string> {
|
private async undoBlock(actor: MiRemoteUser, activity: IBlock): Promise<string> {
|
||||||
const blockee = await this.apDbResolverService.getUserFromApId(activity.object);
|
const blockee = await this.apDbResolverService.getUserFromApId(activity.object);
|
||||||
|
|
||||||
if (blockee == null) {
|
if (blockee == null) {
|
||||||
@ -673,7 +669,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async undoFollow(actor: RemoteUser, activity: IFollow): Promise<string> {
|
private async undoFollow(actor: MiRemoteUser, activity: IFollow): Promise<string> {
|
||||||
const followee = await this.apDbResolverService.getUserFromApId(activity.object);
|
const followee = await this.apDbResolverService.getUserFromApId(activity.object);
|
||||||
if (followee == null) {
|
if (followee == null) {
|
||||||
return 'skip: followee not found';
|
return 'skip: followee not found';
|
||||||
@ -711,7 +707,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async undoLike(actor: RemoteUser, activity: ILike): Promise<string> {
|
private async undoLike(actor: MiRemoteUser, activity: ILike): Promise<string> {
|
||||||
const targetUri = getApId(activity.object);
|
const targetUri = getApId(activity.object);
|
||||||
|
|
||||||
const note = await this.apNoteService.fetchNote(targetUri);
|
const note = await this.apNoteService.fetchNote(targetUri);
|
||||||
@ -726,7 +722,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async update(actor: RemoteUser, activity: IUpdate): Promise<string> {
|
private async update(actor: MiRemoteUser, activity: IUpdate): Promise<string> {
|
||||||
if (actor.uri !== activity.actor) {
|
if (actor.uri !== activity.actor) {
|
||||||
return 'skip: invalid actor';
|
return 'skip: invalid actor';
|
||||||
}
|
}
|
||||||
@ -752,7 +748,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async move(actor: RemoteUser, activity: IMove): Promise<string> {
|
private async move(actor: MiRemoteUser, activity: IMove): Promise<string> {
|
||||||
// fetch the new and old accounts
|
// fetch the new and old accounts
|
||||||
const targetUri = getApHrefNullable(activity.target);
|
const targetUri = getApHrefNullable(activity.target);
|
||||||
if (!targetUri) return 'skip: invalid activity target';
|
if (!targetUri) return 'skip: invalid activity target';
|
||||||
|
@ -3,12 +3,10 @@
|
|||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import * as mfm from 'mfm-js';
|
import * as mfm from 'mfm-js';
|
||||||
import { DI } from '@/di-symbols.js';
|
|
||||||
import type { Config } from '@/config.js';
|
|
||||||
import { MfmService } from '@/core/MfmService.js';
|
import { MfmService } from '@/core/MfmService.js';
|
||||||
import type { Note } from '@/models/entities/Note.js';
|
import type { MiNote } from '@/models/entities/Note.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { extractApHashtagObjects } from './models/tag.js';
|
import { extractApHashtagObjects } from './models/tag.js';
|
||||||
import type { IObject } from './type.js';
|
import type { IObject } from './type.js';
|
||||||
@ -16,9 +14,6 @@ import type { IObject } from './type.js';
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class ApMfmService {
|
export class ApMfmService {
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.config)
|
|
||||||
private config: Config,
|
|
||||||
|
|
||||||
private mfmService: MfmService,
|
private mfmService: MfmService,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
@ -30,7 +25,7 @@ export class ApMfmService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public getNoteHtml(note: Note): string | null {
|
public getNoteHtml(note: MiNote): string | null {
|
||||||
if (!note.text) return '';
|
if (!note.text) return '';
|
||||||
return this.mfmService.toHtml(mfm.parse(note.text), JSON.parse(note.mentionedRemoteUsers));
|
return this.mfmService.toHtml(mfm.parse(note.text), JSON.parse(note.mentionedRemoteUsers));
|
||||||
}
|
}
|
||||||
|
@ -9,21 +9,21 @@ import { In } from 'typeorm';
|
|||||||
import * as mfm from 'mfm-js';
|
import * as mfm from 'mfm-js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import type { PartialLocalUser, LocalUser, PartialRemoteUser, RemoteUser, User } from '@/models/entities/User.js';
|
import type { MiPartialLocalUser, MiLocalUser, MiPartialRemoteUser, MiRemoteUser, MiUser } from '@/models/entities/User.js';
|
||||||
import type { IMentionedRemoteUsers, Note } from '@/models/entities/Note.js';
|
import type { IMentionedRemoteUsers, MiNote } from '@/models/entities/Note.js';
|
||||||
import type { Blocking } from '@/models/entities/Blocking.js';
|
import type { MiBlocking } from '@/models/entities/Blocking.js';
|
||||||
import type { Relay } from '@/models/entities/Relay.js';
|
import type { MiRelay } from '@/models/entities/Relay.js';
|
||||||
import type { DriveFile } from '@/models/entities/DriveFile.js';
|
import type { MiDriveFile } from '@/models/entities/DriveFile.js';
|
||||||
import type { NoteReaction } from '@/models/entities/NoteReaction.js';
|
import type { MiNoteReaction } from '@/models/entities/NoteReaction.js';
|
||||||
import type { Emoji } from '@/models/entities/Emoji.js';
|
import type { MiEmoji } from '@/models/entities/Emoji.js';
|
||||||
import type { Poll } from '@/models/entities/Poll.js';
|
import type { MiPoll } from '@/models/entities/Poll.js';
|
||||||
import type { PollVote } from '@/models/entities/PollVote.js';
|
import type { MiPollVote } from '@/models/entities/PollVote.js';
|
||||||
import { UserKeypairService } from '@/core/UserKeypairService.js';
|
import { UserKeypairService } from '@/core/UserKeypairService.js';
|
||||||
import { MfmService } from '@/core/MfmService.js';
|
import { MfmService } from '@/core/MfmService.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
|
import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
|
||||||
import type { UserKeypair } from '@/models/entities/UserKeypair.js';
|
import type { MiUserKeypair } from '@/models/entities/UserKeypair.js';
|
||||||
import type { UsersRepository, UserProfilesRepository, NotesRepository, DriveFilesRepository, EmojisRepository, PollsRepository, EventsRepository } from '@/models/index.js';
|
import type { UsersRepository, UserProfilesRepository, NotesRepository, DriveFilesRepository, PollsRepository } from '@/models/index.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { CustomEmojiService } from '@/core/CustomEmojiService.js';
|
import { CustomEmojiService } from '@/core/CustomEmojiService.js';
|
||||||
import { isNotNull } from '@/misc/is-not-null.js';
|
import { isNotNull } from '@/misc/is-not-null.js';
|
||||||
@ -49,9 +49,6 @@ export class ApRendererService {
|
|||||||
@Inject(DI.driveFilesRepository)
|
@Inject(DI.driveFilesRepository)
|
||||||
private driveFilesRepository: DriveFilesRepository,
|
private driveFilesRepository: DriveFilesRepository,
|
||||||
|
|
||||||
@Inject(DI.emojisRepository)
|
|
||||||
private emojisRepository: EmojisRepository,
|
|
||||||
|
|
||||||
@Inject(DI.pollsRepository)
|
@Inject(DI.pollsRepository)
|
||||||
private pollsRepository: PollsRepository,
|
private pollsRepository: PollsRepository,
|
||||||
|
|
||||||
@ -69,7 +66,7 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderAccept(object: string | IObject, user: { id: User['id']; host: null }): IAccept {
|
public renderAccept(object: string | IObject, user: { id: MiUser['id']; host: null }): IAccept {
|
||||||
return {
|
return {
|
||||||
type: 'Accept',
|
type: 'Accept',
|
||||||
actor: this.userEntityService.genLocalUserUri(user.id),
|
actor: this.userEntityService.genLocalUserUri(user.id),
|
||||||
@ -78,7 +75,7 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderAdd(user: LocalUser, target: string | IObject | undefined, object: string | IObject): IAdd {
|
public renderAdd(user: MiLocalUser, target: string | IObject | undefined, object: string | IObject): IAdd {
|
||||||
return {
|
return {
|
||||||
type: 'Add',
|
type: 'Add',
|
||||||
actor: this.userEntityService.genLocalUserUri(user.id),
|
actor: this.userEntityService.genLocalUserUri(user.id),
|
||||||
@ -88,7 +85,7 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderAnnounce(object: string | IObject, note: Note): IAnnounce {
|
public renderAnnounce(object: string | IObject, note: MiNote): IAnnounce {
|
||||||
const attributedTo = this.userEntityService.genLocalUserUri(note.userId);
|
const attributedTo = this.userEntityService.genLocalUserUri(note.userId);
|
||||||
|
|
||||||
let to: string[] = [];
|
let to: string[] = [];
|
||||||
@ -124,7 +121,7 @@ export class ApRendererService {
|
|||||||
* @param block The block to be rendered. The blockee relation must be loaded.
|
* @param block The block to be rendered. The blockee relation must be loaded.
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderBlock(block: Blocking): IBlock {
|
public renderBlock(block: MiBlocking): IBlock {
|
||||||
if (block.blockee?.uri == null) {
|
if (block.blockee?.uri == null) {
|
||||||
throw new Error('renderBlock: missing blockee uri');
|
throw new Error('renderBlock: missing blockee uri');
|
||||||
}
|
}
|
||||||
@ -138,7 +135,7 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderCreate(object: IObject, note: Note): ICreate {
|
public renderCreate(object: IObject, note: MiNote): ICreate {
|
||||||
const activity: ICreate = {
|
const activity: ICreate = {
|
||||||
id: `${this.config.url}/notes/${note.id}/activity`,
|
id: `${this.config.url}/notes/${note.id}/activity`,
|
||||||
actor: this.userEntityService.genLocalUserUri(note.userId),
|
actor: this.userEntityService.genLocalUserUri(note.userId),
|
||||||
@ -154,7 +151,7 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderDelete(object: IObject | string, user: { id: User['id']; host: null }): IDelete {
|
public renderDelete(object: IObject | string, user: { id: MiUser['id']; host: null }): IDelete {
|
||||||
return {
|
return {
|
||||||
type: 'Delete',
|
type: 'Delete',
|
||||||
actor: this.userEntityService.genLocalUserUri(user.id),
|
actor: this.userEntityService.genLocalUserUri(user.id),
|
||||||
@ -164,7 +161,7 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderDocument(file: DriveFile): IApDocument {
|
public renderDocument(file: MiDriveFile): IApDocument {
|
||||||
return {
|
return {
|
||||||
type: 'Document',
|
type: 'Document',
|
||||||
mediaType: file.webpublicType ?? file.type,
|
mediaType: file.webpublicType ?? file.type,
|
||||||
@ -174,7 +171,7 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderEmoji(emoji: Emoji): IApEmoji {
|
public renderEmoji(emoji: MiEmoji): IApEmoji {
|
||||||
return {
|
return {
|
||||||
id: `${this.config.url}/emojis/${emoji.name}`,
|
id: `${this.config.url}/emojis/${emoji.name}`,
|
||||||
type: 'Emoji',
|
type: 'Emoji',
|
||||||
@ -191,7 +188,7 @@ export class ApRendererService {
|
|||||||
|
|
||||||
// to anonymise reporters, the reporting actor must be a system user
|
// to anonymise reporters, the reporting actor must be a system user
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderFlag(user: LocalUser, object: IObject | string, content: string): IFlag {
|
public renderFlag(user: MiLocalUser, object: IObject | string, content: string): IFlag {
|
||||||
return {
|
return {
|
||||||
type: 'Flag',
|
type: 'Flag',
|
||||||
actor: this.userEntityService.genLocalUserUri(user.id),
|
actor: this.userEntityService.genLocalUserUri(user.id),
|
||||||
@ -201,7 +198,7 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderFollowRelay(relay: Relay, relayActor: LocalUser): IFollow {
|
public renderFollowRelay(relay: MiRelay, relayActor: MiLocalUser): IFollow {
|
||||||
return {
|
return {
|
||||||
id: `${this.config.url}/activities/follow-relay/${relay.id}`,
|
id: `${this.config.url}/activities/follow-relay/${relay.id}`,
|
||||||
type: 'Follow',
|
type: 'Follow',
|
||||||
@ -215,15 +212,15 @@ export class ApRendererService {
|
|||||||
* @param id Follower|Followee ID
|
* @param id Follower|Followee ID
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async renderFollowUser(id: User['id']): Promise<string> {
|
public async renderFollowUser(id: MiUser['id']): Promise<string> {
|
||||||
const user = await this.usersRepository.findOneByOrFail({ id: id }) as PartialLocalUser | PartialRemoteUser;
|
const user = await this.usersRepository.findOneByOrFail({ id: id }) as MiPartialLocalUser | MiPartialRemoteUser;
|
||||||
return this.userEntityService.getUserUri(user);
|
return this.userEntityService.getUserUri(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderFollow(
|
public renderFollow(
|
||||||
follower: PartialLocalUser | PartialRemoteUser,
|
follower: MiPartialLocalUser | MiPartialRemoteUser,
|
||||||
followee: PartialLocalUser | PartialRemoteUser,
|
followee: MiPartialLocalUser | MiPartialRemoteUser,
|
||||||
requestId?: string,
|
requestId?: string,
|
||||||
): IFollow {
|
): IFollow {
|
||||||
return {
|
return {
|
||||||
@ -244,7 +241,7 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderImage(file: DriveFile): IApImage {
|
public renderImage(file: MiDriveFile): IApImage {
|
||||||
return {
|
return {
|
||||||
type: 'Image',
|
type: 'Image',
|
||||||
url: this.driveFileEntityService.getPublicUrl(file),
|
url: this.driveFileEntityService.getPublicUrl(file),
|
||||||
@ -254,7 +251,7 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderKey(user: LocalUser, key: UserKeypair, postfix?: string): IKey {
|
public renderKey(user: MiLocalUser, key: MiUserKeypair, postfix?: string): IKey {
|
||||||
return {
|
return {
|
||||||
id: `${this.config.url}/users/${user.id}${postfix ?? '/publickey'}`,
|
id: `${this.config.url}/users/${user.id}${postfix ?? '/publickey'}`,
|
||||||
type: 'Key',
|
type: 'Key',
|
||||||
@ -267,7 +264,7 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async renderLike(noteReaction: NoteReaction, note: { uri: string | null }): Promise<ILike> {
|
public async renderLike(noteReaction: MiNoteReaction, note: { uri: string | null }): Promise<ILike> {
|
||||||
const reaction = noteReaction.reaction;
|
const reaction = noteReaction.reaction;
|
||||||
|
|
||||||
const object: ILike = {
|
const object: ILike = {
|
||||||
@ -290,18 +287,18 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderMention(mention: PartialLocalUser | PartialRemoteUser): IApMention {
|
public renderMention(mention: MiPartialLocalUser | MiPartialRemoteUser): IApMention {
|
||||||
return {
|
return {
|
||||||
type: 'Mention',
|
type: 'Mention',
|
||||||
href: this.userEntityService.getUserUri(mention),
|
href: this.userEntityService.getUserUri(mention),
|
||||||
name: this.userEntityService.isRemoteUser(mention) ? `@${mention.username}@${mention.host}` : `@${(mention as LocalUser).username}`,
|
name: this.userEntityService.isRemoteUser(mention) ? `@${mention.username}@${mention.host}` : `@${(mention as MiLocalUser).username}`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderMove(
|
public renderMove(
|
||||||
src: PartialLocalUser | PartialRemoteUser,
|
src: MiPartialLocalUser | MiPartialRemoteUser,
|
||||||
dst: PartialLocalUser | PartialRemoteUser,
|
dst: MiPartialLocalUser | MiPartialRemoteUser,
|
||||||
): IMove {
|
): IMove {
|
||||||
const actor = this.userEntityService.getUserUri(src);
|
const actor = this.userEntityService.getUserUri(src);
|
||||||
const target = this.userEntityService.getUserUri(dst);
|
const target = this.userEntityService.getUserUri(dst);
|
||||||
@ -315,15 +312,15 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async renderNote(note: Note, dive = true): Promise<IPost> {
|
public async renderNote(note: MiNote, dive = true): Promise<IPost> {
|
||||||
const getPromisedFiles = async (ids: string[]): Promise<DriveFile[]> => {
|
const getPromisedFiles = async (ids: string[]): Promise<MiDriveFile[]> => {
|
||||||
if (ids.length === 0) return [];
|
if (ids.length === 0) return [];
|
||||||
const items = await this.driveFilesRepository.findBy({ id: In(ids) });
|
const items = await this.driveFilesRepository.findBy({ id: In(ids) });
|
||||||
return ids.map(id => items.find(item => item.id === id)).filter((item): item is DriveFile => item != null);
|
return ids.map(id => items.find(item => item.id === id)).filter((item): item is MiDriveFile => item != null);
|
||||||
};
|
};
|
||||||
|
|
||||||
let inReplyTo;
|
let inReplyTo;
|
||||||
let inReplyToNote: Note | null;
|
let inReplyToNote: MiNote | null;
|
||||||
|
|
||||||
if (note.replyId) {
|
if (note.replyId) {
|
||||||
inReplyToNote = await this.notesRepository.findOneBy({ id: note.replyId });
|
inReplyToNote = await this.notesRepository.findOneBy({ id: note.replyId });
|
||||||
@ -382,12 +379,12 @@ export class ApRendererService {
|
|||||||
}) : [];
|
}) : [];
|
||||||
|
|
||||||
const hashtagTags = note.tags.map(tag => this.renderHashtag(tag));
|
const hashtagTags = note.tags.map(tag => this.renderHashtag(tag));
|
||||||
const mentionTags = mentionedUsers.map(u => this.renderMention(u as LocalUser | RemoteUser));
|
const mentionTags = mentionedUsers.map(u => this.renderMention(u as MiLocalUser | MiRemoteUser));
|
||||||
|
|
||||||
const files = await getPromisedFiles(note.fileIds);
|
const files = await getPromisedFiles(note.fileIds);
|
||||||
|
|
||||||
const text = note.text ?? '';
|
const text = note.text ?? '';
|
||||||
let poll: Poll | null = null;
|
let poll: MiPoll | null = null;
|
||||||
|
|
||||||
if (note.hasPoll) {
|
if (note.hasPoll) {
|
||||||
poll = await this.pollsRepository.findOneBy({ noteId: note.id });
|
poll = await this.pollsRepository.findOneBy({ noteId: note.id });
|
||||||
@ -468,7 +465,7 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async renderPerson(user: LocalUser) {
|
public async renderPerson(user: MiLocalUser) {
|
||||||
const id = this.userEntityService.genLocalUserUri(user.id);
|
const id = this.userEntityService.genLocalUserUri(user.id);
|
||||||
const isSystem = user.username.includes('.');
|
const isSystem = user.username.includes('.');
|
||||||
|
|
||||||
@ -542,7 +539,7 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderQuestion(user: { id: User['id'] }, note: Note, poll: Poll): IQuestion {
|
public renderQuestion(user: { id: MiUser['id'] }, note: MiNote, poll: MiPoll): IQuestion {
|
||||||
return {
|
return {
|
||||||
type: 'Question',
|
type: 'Question',
|
||||||
id: `${this.config.url}/questions/${note.id}`,
|
id: `${this.config.url}/questions/${note.id}`,
|
||||||
@ -560,7 +557,7 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderReject(object: string | IObject, user: { id: User['id'] }): IReject {
|
public renderReject(object: string | IObject, user: { id: MiUser['id'] }): IReject {
|
||||||
return {
|
return {
|
||||||
type: 'Reject',
|
type: 'Reject',
|
||||||
actor: this.userEntityService.genLocalUserUri(user.id),
|
actor: this.userEntityService.genLocalUserUri(user.id),
|
||||||
@ -569,7 +566,7 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderRemove(user: { id: User['id'] }, target: string | IObject | undefined, object: string | IObject): IRemove {
|
public renderRemove(user: { id: MiUser['id'] }, target: string | IObject | undefined, object: string | IObject): IRemove {
|
||||||
return {
|
return {
|
||||||
type: 'Remove',
|
type: 'Remove',
|
||||||
actor: this.userEntityService.genLocalUserUri(user.id),
|
actor: this.userEntityService.genLocalUserUri(user.id),
|
||||||
@ -587,7 +584,7 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderUndo(object: string | IObject, user: { id: User['id'] }): IUndo {
|
public renderUndo(object: string | IObject, user: { id: MiUser['id'] }): IUndo {
|
||||||
const id = typeof object !== 'string' && typeof object.id === 'string' && object.id.startsWith(this.config.url) ? `${object.id}/undo` : undefined;
|
const id = typeof object !== 'string' && typeof object.id === 'string' && object.id.startsWith(this.config.url) ? `${object.id}/undo` : undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -600,7 +597,7 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderUpdate(object: string | IObject, user: { id: User['id'] }): IUpdate {
|
public renderUpdate(object: string | IObject, user: { id: MiUser['id'] }): IUpdate {
|
||||||
return {
|
return {
|
||||||
id: `${this.config.url}/users/${user.id}#updates/${new Date().getTime()}`,
|
id: `${this.config.url}/users/${user.id}#updates/${new Date().getTime()}`,
|
||||||
actor: this.userEntityService.genLocalUserUri(user.id),
|
actor: this.userEntityService.genLocalUserUri(user.id),
|
||||||
@ -612,7 +609,7 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderVote(user: { id: User['id'] }, vote: PollVote, note: Note, poll: Poll, pollOwner: RemoteUser): ICreate {
|
public renderVote(user: { id: MiUser['id'] }, vote: MiPollVote, note: MiNote, poll: MiPoll, pollOwner: MiRemoteUser): ICreate {
|
||||||
return {
|
return {
|
||||||
id: `${this.config.url}/users/${user.id}#votes/${vote.id}/activity`,
|
id: `${this.config.url}/users/${user.id}#votes/${vote.id}/activity`,
|
||||||
actor: this.userEntityService.genLocalUserUri(user.id),
|
actor: this.userEntityService.genLocalUserUri(user.id),
|
||||||
@ -670,7 +667,7 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async attachLdSignature(activity: any, user: { id: User['id']; host: null; }): Promise<IActivity> {
|
public async attachLdSignature(activity: any, user: { id: MiUser['id']; host: null; }): Promise<IActivity> {
|
||||||
const keypair = await this.userKeypairService.getUserKeypair(user.id);
|
const keypair = await this.userKeypairService.getUserKeypair(user.id);
|
||||||
|
|
||||||
const ldSignature = this.ldSignatureService.use();
|
const ldSignature = this.ldSignatureService.use();
|
||||||
@ -729,7 +726,7 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async getEmojis(names: string[]): Promise<Emoji[]> {
|
private async getEmojis(names: string[]): Promise<MiEmoji[]> {
|
||||||
if (names.length === 0) return [];
|
if (names.length === 0) return [];
|
||||||
|
|
||||||
const allEmojis = await this.customEmojiService.localEmojisCache.fetch();
|
const allEmojis = await this.customEmojiService.localEmojisCache.fetch();
|
||||||
|
@ -8,7 +8,7 @@ import { URL } from 'node:url';
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import type { User } from '@/models/entities/User.js';
|
import type { MiUser } from '@/models/entities/User.js';
|
||||||
import { UserKeypairService } from '@/core/UserKeypairService.js';
|
import { UserKeypairService } from '@/core/UserKeypairService.js';
|
||||||
import { HttpRequestService } from '@/core/HttpRequestService.js';
|
import { HttpRequestService } from '@/core/HttpRequestService.js';
|
||||||
import { LoggerService } from '@/core/LoggerService.js';
|
import { LoggerService } from '@/core/LoggerService.js';
|
||||||
@ -145,7 +145,7 @@ export class ApRequestService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async signedPost(user: { id: User['id'] }, url: string, object: unknown): Promise<void> {
|
public async signedPost(user: { id: MiUser['id'] }, url: string, object: unknown): Promise<void> {
|
||||||
const body = JSON.stringify(object);
|
const body = JSON.stringify(object);
|
||||||
|
|
||||||
const keypair = await this.userKeypairService.getUserKeypair(user.id);
|
const keypair = await this.userKeypairService.getUserKeypair(user.id);
|
||||||
@ -174,7 +174,7 @@ export class ApRequestService {
|
|||||||
* @param url URL to fetch
|
* @param url URL to fetch
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async signedGet(url: string, user: { id: User['id'] }): Promise<unknown> {
|
public async signedGet(url: string, user: { id: MiUser['id'] }): Promise<unknown> {
|
||||||
const keypair = await this.userKeypairService.getUserKeypair(user.id);
|
const keypair = await this.userKeypairService.getUserKeypair(user.id);
|
||||||
|
|
||||||
const req = ApRequestCreator.createSignedGet({
|
const req = ApRequestCreator.createSignedGet({
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import type { LocalUser, RemoteUser } from '@/models/entities/User.js';
|
import type { MiLocalUser, MiRemoteUser } from '@/models/entities/User.js';
|
||||||
import { InstanceActorService } from '@/core/InstanceActorService.js';
|
import { InstanceActorService } from '@/core/InstanceActorService.js';
|
||||||
import type { NotesRepository, PollsRepository, NoteReactionsRepository, UsersRepository } from '@/models/index.js';
|
import type { NotesRepository, PollsRepository, NoteReactionsRepository, UsersRepository } from '@/models/index.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
@ -23,7 +23,7 @@ import type { IObject, ICollection, IOrderedCollection } from './type.js';
|
|||||||
|
|
||||||
export class Resolver {
|
export class Resolver {
|
||||||
private history: Set<string>;
|
private history: Set<string>;
|
||||||
private user?: LocalUser;
|
private user?: MiLocalUser;
|
||||||
private logger: Logger;
|
private logger: Logger;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -134,7 +134,7 @@ export class Resolver {
|
|||||||
});
|
});
|
||||||
case 'users':
|
case 'users':
|
||||||
return this.usersRepository.findOneByOrFail({ id: parsed.id })
|
return this.usersRepository.findOneByOrFail({ id: parsed.id })
|
||||||
.then(user => this.apRendererService.renderPerson(user as LocalUser));
|
.then(user => this.apRendererService.renderPerson(user as MiLocalUser));
|
||||||
case 'questions':
|
case 'questions':
|
||||||
// Polls are indexed by the note they are attached to.
|
// Polls are indexed by the note they are attached to.
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
@ -152,7 +152,7 @@ export class Resolver {
|
|||||||
return Promise.all(
|
return Promise.all(
|
||||||
[parsed.id, parsed.rest].map(id => this.usersRepository.findOneByOrFail({ id })),
|
[parsed.id, parsed.rest].map(id => this.usersRepository.findOneByOrFail({ id })),
|
||||||
)
|
)
|
||||||
.then(([follower, followee]) => this.apRendererService.addContext(this.apRendererService.renderFollow(follower as LocalUser | RemoteUser, followee as LocalUser | RemoteUser, url)));
|
.then(([follower, followee]) => this.apRendererService.addContext(this.apRendererService.renderFollow(follower as MiLocalUser | MiRemoteUser, followee as MiLocalUser | MiRemoteUser, url)));
|
||||||
default:
|
default:
|
||||||
throw new Error(`resolveLocal: type ${parsed.type} unhandled`);
|
throw new Error(`resolveLocal: type ${parsed.type} unhandled`);
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,8 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { DriveFilesRepository } from '@/models/index.js';
|
import type { DriveFilesRepository } from '@/models/index.js';
|
||||||
import type { RemoteUser } from '@/models/entities/User.js';
|
import type { MiRemoteUser } from '@/models/entities/User.js';
|
||||||
import type { DriveFile } from '@/models/entities/DriveFile.js';
|
import type { MiDriveFile } from '@/models/entities/DriveFile.js';
|
||||||
import { MetaService } from '@/core/MetaService.js';
|
import { MetaService } from '@/core/MetaService.js';
|
||||||
import { truncate } from '@/misc/truncate.js';
|
import { truncate } from '@/misc/truncate.js';
|
||||||
import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/const.js';
|
import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/const.js';
|
||||||
@ -39,7 +39,7 @@ export class ApImageService {
|
|||||||
* Imageを作成します。
|
* Imageを作成します。
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async createImage(actor: RemoteUser, value: string | IObject): Promise<DriveFile> {
|
public async createImage(actor: MiRemoteUser, value: string | IObject): Promise<MiDriveFile> {
|
||||||
// 投稿者が凍結されていたらスキップ
|
// 投稿者が凍結されていたらスキップ
|
||||||
if (actor.isSuspended) {
|
if (actor.isSuspended) {
|
||||||
throw new Error('actor has been suspended');
|
throw new Error('actor has been suspended');
|
||||||
@ -90,7 +90,7 @@ export class ApImageService {
|
|||||||
* リモートサーバーからフェッチしてMisskeyに登録しそれを返します。
|
* リモートサーバーからフェッチしてMisskeyに登録しそれを返します。
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async resolveImage(actor: RemoteUser, value: string | IObject): Promise<DriveFile> {
|
public async resolveImage(actor: MiRemoteUser, value: string | IObject): Promise<MiDriveFile> {
|
||||||
// TODO
|
// TODO
|
||||||
|
|
||||||
// リモートサーバーからフェッチしてきて登録
|
// リモートサーバーからフェッチしてきて登録
|
||||||
|
@ -3,37 +3,31 @@
|
|||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import promiseLimit from 'promise-limit';
|
import promiseLimit from 'promise-limit';
|
||||||
import { DI } from '@/di-symbols.js';
|
import type { MiUser } from '@/models/index.js';
|
||||||
import type { User } from '@/models/index.js';
|
|
||||||
import type { Config } from '@/config.js';
|
|
||||||
import { toArray, unique } from '@/misc/prelude/array.js';
|
import { toArray, unique } from '@/misc/prelude/array.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { isMention } from '../type.js';
|
import { isMention } from '../type.js';
|
||||||
import { ApResolverService, Resolver } from '../ApResolverService.js';
|
import { Resolver } from '../ApResolverService.js';
|
||||||
import { ApPersonService } from './ApPersonService.js';
|
import { ApPersonService } from './ApPersonService.js';
|
||||||
import type { IObject, IApMention } from '../type.js';
|
import type { IObject, IApMention } from '../type.js';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ApMentionService {
|
export class ApMentionService {
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.config)
|
|
||||||
private config: Config,
|
|
||||||
|
|
||||||
private apResolverService: ApResolverService,
|
|
||||||
private apPersonService: ApPersonService,
|
private apPersonService: ApPersonService,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async extractApMentions(tags: IObject | IObject[] | null | undefined, resolver: Resolver): Promise<User[]> {
|
public async extractApMentions(tags: IObject | IObject[] | null | undefined, resolver: Resolver): Promise<MiUser[]> {
|
||||||
const hrefs = unique(this.extractApMentionObjects(tags).map(x => x.href));
|
const hrefs = unique(this.extractApMentionObjects(tags).map(x => x.href));
|
||||||
|
|
||||||
const limit = promiseLimit<User | null>(2);
|
const limit = promiseLimit<MiUser | null>(2);
|
||||||
const mentionedUsers = (await Promise.all(
|
const mentionedUsers = (await Promise.all(
|
||||||
hrefs.map(x => limit(() => this.apPersonService.resolvePerson(x, resolver).catch(() => null))),
|
hrefs.map(x => limit(() => this.apPersonService.resolvePerson(x, resolver).catch(() => null))),
|
||||||
)).filter((x): x is User => x != null);
|
)).filter((x): x is MiUser => x != null);
|
||||||
|
|
||||||
return mentionedUsers;
|
return mentionedUsers;
|
||||||
}
|
}
|
||||||
|
@ -9,13 +9,13 @@ import { In } from 'typeorm';
|
|||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { PollsRepository, EmojisRepository } from '@/models/index.js';
|
import type { PollsRepository, EmojisRepository } from '@/models/index.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import type { RemoteUser } from '@/models/entities/User.js';
|
import type { MiRemoteUser } from '@/models/entities/User.js';
|
||||||
import type { Note } from '@/models/entities/Note.js';
|
import type { MiNote } from '@/models/entities/Note.js';
|
||||||
import { toArray, toSingle, unique } from '@/misc/prelude/array.js';
|
import { toArray, toSingle, unique } from '@/misc/prelude/array.js';
|
||||||
import type { Emoji } from '@/models/entities/Emoji.js';
|
import type { MiEmoji } from '@/models/entities/Emoji.js';
|
||||||
import { MetaService } from '@/core/MetaService.js';
|
import { MetaService } from '@/core/MetaService.js';
|
||||||
import { AppLockService } from '@/core/AppLockService.js';
|
import { AppLockService } from '@/core/AppLockService.js';
|
||||||
import type { DriveFile } from '@/models/entities/DriveFile.js';
|
import type { MiDriveFile } from '@/models/entities/DriveFile.js';
|
||||||
import { NoteCreateService } from '@/core/NoteCreateService.js';
|
import { NoteCreateService } from '@/core/NoteCreateService.js';
|
||||||
import type Logger from '@/logger.js';
|
import type Logger from '@/logger.js';
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
@ -103,7 +103,7 @@ export class ApNoteService {
|
|||||||
* Misskeyに対象のNoteが登録されていればそれを返します。
|
* Misskeyに対象のNoteが登録されていればそれを返します。
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async fetchNote(object: string | IObject): Promise<Note | null> {
|
public async fetchNote(object: string | IObject): Promise<MiNote | null> {
|
||||||
return await this.apDbResolverService.getNoteFromApId(object);
|
return await this.apDbResolverService.getNoteFromApId(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,7 +111,7 @@ export class ApNoteService {
|
|||||||
* Noteを作成します。
|
* Noteを作成します。
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async createNote(value: string | IObject, resolver?: Resolver, silent = false): Promise<Note | null> {
|
public async createNote(value: string | IObject, resolver?: Resolver, silent = false): Promise<MiNote | null> {
|
||||||
// eslint-disable-next-line no-param-reassign
|
// eslint-disable-next-line no-param-reassign
|
||||||
if (resolver == null) resolver = this.apResolverService.createResolver();
|
if (resolver == null) resolver = this.apResolverService.createResolver();
|
||||||
|
|
||||||
@ -133,13 +133,13 @@ export class ApNoteService {
|
|||||||
this.logger.debug(`Note fetched: ${JSON.stringify(note, null, 2)}`);
|
this.logger.debug(`Note fetched: ${JSON.stringify(note, null, 2)}`);
|
||||||
|
|
||||||
if (note.id && !checkHttps(note.id)) {
|
if (note.id && !checkHttps(note.id)) {
|
||||||
throw new Error('unexpected shcema of note.id: ' + note.id);
|
throw new Error('unexpected schema of note.id: ' + note.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = getOneApHrefNullable(note.url);
|
const url = getOneApHrefNullable(note.url);
|
||||||
|
|
||||||
if (url && !checkHttps(url)) {
|
if (url && !checkHttps(url)) {
|
||||||
throw new Error('unexpected shcema of note url: ' + url);
|
throw new Error('unexpected schema of note url: ' + url);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.info(`Creating the Note: ${note.id}`);
|
this.logger.info(`Creating the Note: ${note.id}`);
|
||||||
@ -149,7 +149,7 @@ export class ApNoteService {
|
|||||||
throw new Error('invalid note.attributedTo: ' + note.attributedTo);
|
throw new Error('invalid note.attributedTo: ' + note.attributedTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
const actor = await this.apPersonService.resolvePerson(getOneApId(note.attributedTo), resolver) as RemoteUser;
|
const actor = await this.apPersonService.resolvePerson(getOneApId(note.attributedTo), resolver) as MiRemoteUser;
|
||||||
|
|
||||||
// 投稿者が凍結されていたらスキップ
|
// 投稿者が凍結されていたらスキップ
|
||||||
if (actor.isSuspended) {
|
if (actor.isSuspended) {
|
||||||
@ -174,7 +174,7 @@ export class ApNoteService {
|
|||||||
// 添付ファイル
|
// 添付ファイル
|
||||||
// TODO: attachmentは必ずしもImageではない
|
// TODO: attachmentは必ずしもImageではない
|
||||||
// TODO: attachmentは必ずしも配列ではない
|
// TODO: attachmentは必ずしも配列ではない
|
||||||
const limit = promiseLimit<DriveFile>(2);
|
const limit = promiseLimit<MiDriveFile>(2);
|
||||||
const files = (await Promise.all(toArray(note.attachment).map(attach => (
|
const files = (await Promise.all(toArray(note.attachment).map(attach => (
|
||||||
limit(() => this.apImageService.resolveImage(actor, {
|
limit(() => this.apImageService.resolveImage(actor, {
|
||||||
...attach,
|
...attach,
|
||||||
@ -183,7 +183,7 @@ export class ApNoteService {
|
|||||||
))));
|
))));
|
||||||
|
|
||||||
// リプライ
|
// リプライ
|
||||||
const reply: Note | null = note.inReplyTo
|
const reply: MiNote | null = note.inReplyTo
|
||||||
? await this.resolveNote(note.inReplyTo, { resolver })
|
? await this.resolveNote(note.inReplyTo, { resolver })
|
||||||
.then(x => {
|
.then(x => {
|
||||||
if (x == null) {
|
if (x == null) {
|
||||||
@ -200,11 +200,11 @@ export class ApNoteService {
|
|||||||
: null;
|
: null;
|
||||||
|
|
||||||
// 引用
|
// 引用
|
||||||
let quote: Note | undefined | null = null;
|
let quote: MiNote | undefined | null = null;
|
||||||
|
|
||||||
if (note._misskey_quote || note.quoteUrl) {
|
if (note._misskey_quote ?? note.quoteUrl) {
|
||||||
const tryResolveNote = async (uri: string): Promise<
|
const tryResolveNote = async (uri: string): Promise<
|
||||||
| { status: 'ok'; res: Note }
|
| { status: 'ok'; res: MiNote }
|
||||||
| { status: 'permerror' | 'temperror' }
|
| { status: 'permerror' | 'temperror' }
|
||||||
> => {
|
> => {
|
||||||
if (!/^https?:/.test(uri)) return { status: 'permerror' };
|
if (!/^https?:/.test(uri)) return { status: 'permerror' };
|
||||||
@ -222,7 +222,7 @@ export class ApNoteService {
|
|||||||
const uris = unique([note._misskey_quote, note.quoteUrl].filter((x): x is string => typeof x === 'string'));
|
const uris = unique([note._misskey_quote, note.quoteUrl].filter((x): x is string => typeof x === 'string'));
|
||||||
const results = await Promise.all(uris.map(tryResolveNote));
|
const results = await Promise.all(uris.map(tryResolveNote));
|
||||||
|
|
||||||
quote = results.filter((x): x is { status: 'ok', res: Note } => x.status === 'ok').map(x => x.res).at(0);
|
quote = results.filter((x): x is { status: 'ok', res: MiNote } => x.status === 'ok').map(x => x.res).at(0);
|
||||||
if (!quote) {
|
if (!quote) {
|
||||||
if (results.some(x => x.status === 'temperror')) {
|
if (results.some(x => x.status === 'temperror')) {
|
||||||
throw new Error('quote resolve failed');
|
throw new Error('quote resolve failed');
|
||||||
@ -274,6 +274,7 @@ export class ApNoteService {
|
|||||||
const poll = await this.apQuestionService.extractPollFromQuestion(note, resolver).catch(() => undefined);
|
const poll = await this.apQuestionService.extractPollFromQuestion(note, resolver).catch(() => undefined);
|
||||||
const event = await this.apEventService.extractEventFromNote(note, resolver).catch(() => undefined);
|
const event = await this.apEventService.extractEventFromNote(note, resolver).catch(() => undefined);
|
||||||
|
|
||||||
|
try {
|
||||||
return await this.noteCreateService.create(actor, {
|
return await this.noteCreateService.create(actor, {
|
||||||
createdAt: note.published ? new Date(note.published) : null,
|
createdAt: note.published ? new Date(note.published) : null,
|
||||||
files,
|
files,
|
||||||
@ -293,6 +294,17 @@ export class ApNoteService {
|
|||||||
uri: note.id,
|
uri: note.id,
|
||||||
url: url,
|
url: url,
|
||||||
}, silent);
|
}, silent);
|
||||||
|
} catch (err: any) {
|
||||||
|
if (err.name !== 'duplicated') {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
this.logger.info('The note is already inserted while creating itself, reading again');
|
||||||
|
const duplicate = await this.fetchNote(value);
|
||||||
|
if (!duplicate) {
|
||||||
|
throw new Error('The note creation failed with duplication error even when there is no duplication');
|
||||||
|
}
|
||||||
|
return duplicate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -302,7 +314,7 @@ export class ApNoteService {
|
|||||||
* リモートサーバーからフェッチしてMisskeyに登録しそれを返します。
|
* リモートサーバーからフェッチしてMisskeyに登録しそれを返します。
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async resolveNote(value: string | IObject, options: { sentFrom?: URL, resolver?: Resolver } = {}): Promise<Note | null> {
|
public async resolveNote(value: string | IObject, options: { sentFrom?: URL, resolver?: Resolver } = {}): Promise<MiNote | null> {
|
||||||
const uri = getApId(value);
|
const uri = getApId(value);
|
||||||
|
|
||||||
// ブロックしていたら中断
|
// ブロックしていたら中断
|
||||||
@ -334,7 +346,7 @@ export class ApNoteService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async extractEmojis(tags: IObject | IObject[], host: string): Promise<Emoji[]> {
|
public async extractEmojis(tags: IObject | IObject[], host: string): Promise<MiEmoji[]> {
|
||||||
// eslint-disable-next-line no-param-reassign
|
// eslint-disable-next-line no-param-reassign
|
||||||
host = this.utilityService.toPuny(host);
|
host = this.utilityService.toPuny(host);
|
||||||
|
|
||||||
|
@ -10,26 +10,26 @@ import { ModuleRef } from '@nestjs/core';
|
|||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { FollowingsRepository, InstancesRepository, UserProfilesRepository, UserPublickeysRepository, UsersRepository } from '@/models/index.js';
|
import type { FollowingsRepository, InstancesRepository, UserProfilesRepository, UserPublickeysRepository, UsersRepository } from '@/models/index.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import type { LocalUser, RemoteUser } from '@/models/entities/User.js';
|
import type { MiLocalUser, MiRemoteUser } from '@/models/entities/User.js';
|
||||||
import { User } from '@/models/entities/User.js';
|
import { MiUser } from '@/models/entities/User.js';
|
||||||
import { truncate } from '@/misc/truncate.js';
|
import { truncate } from '@/misc/truncate.js';
|
||||||
import type { CacheService } from '@/core/CacheService.js';
|
import type { CacheService } from '@/core/CacheService.js';
|
||||||
import { normalizeForSearch } from '@/misc/normalize-for-search.js';
|
import { normalizeForSearch } from '@/misc/normalize-for-search.js';
|
||||||
import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js';
|
import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js';
|
||||||
import type Logger from '@/logger.js';
|
import type Logger from '@/logger.js';
|
||||||
import type { Note } from '@/models/entities/Note.js';
|
import type { MiNote } from '@/models/entities/Note.js';
|
||||||
import type { IdService } from '@/core/IdService.js';
|
import type { IdService } from '@/core/IdService.js';
|
||||||
import type { MfmService } from '@/core/MfmService.js';
|
import type { MfmService } from '@/core/MfmService.js';
|
||||||
import { toArray } from '@/misc/prelude/array.js';
|
import { toArray } from '@/misc/prelude/array.js';
|
||||||
import type { GlobalEventService } from '@/core/GlobalEventService.js';
|
import type { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
import type { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
|
import type { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
|
||||||
import type { FetchInstanceMetadataService } from '@/core/FetchInstanceMetadataService.js';
|
import type { FetchInstanceMetadataService } from '@/core/FetchInstanceMetadataService.js';
|
||||||
import { UserProfile } from '@/models/entities/UserProfile.js';
|
import { MiUserProfile } from '@/models/entities/UserProfile.js';
|
||||||
import { UserPublickey } from '@/models/entities/UserPublickey.js';
|
import { MiUserPublickey } from '@/models/entities/UserPublickey.js';
|
||||||
import type UsersChart from '@/core/chart/charts/users.js';
|
import type UsersChart from '@/core/chart/charts/users.js';
|
||||||
import type InstanceChart from '@/core/chart/charts/instance.js';
|
import type InstanceChart from '@/core/chart/charts/instance.js';
|
||||||
import type { HashtagService } from '@/core/HashtagService.js';
|
import type { HashtagService } from '@/core/HashtagService.js';
|
||||||
import { UserNotePining } from '@/models/entities/UserNotePining.js';
|
import { MiUserNotePining } from '@/models/entities/UserNotePining.js';
|
||||||
import { StatusError } from '@/misc/status-error.js';
|
import { StatusError } from '@/misc/status-error.js';
|
||||||
import type { UtilityService } from '@/core/UtilityService.js';
|
import type { UtilityService } from '@/core/UtilityService.js';
|
||||||
import type { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import type { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
@ -201,20 +201,20 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
* Misskeyに対象のPersonが登録されていればそれを返し、登録がなければnullを返します。
|
* Misskeyに対象のPersonが登録されていればそれを返し、登録がなければnullを返します。
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async fetchPerson(uri: string): Promise<LocalUser | RemoteUser | null> {
|
public async fetchPerson(uri: string): Promise<MiLocalUser | MiRemoteUser | null> {
|
||||||
const cached = this.cacheService.uriPersonCache.get(uri) as LocalUser | RemoteUser | null | undefined;
|
const cached = this.cacheService.uriPersonCache.get(uri) as MiLocalUser | MiRemoteUser | null | undefined;
|
||||||
if (cached) return cached;
|
if (cached) return cached;
|
||||||
|
|
||||||
// URIがこのサーバーを指しているならデータベースからフェッチ
|
// URIがこのサーバーを指しているならデータベースからフェッチ
|
||||||
if (uri.startsWith(`${this.config.url}/`)) {
|
if (uri.startsWith(`${this.config.url}/`)) {
|
||||||
const id = uri.split('/').pop();
|
const id = uri.split('/').pop();
|
||||||
const u = await this.usersRepository.findOneBy({ id }) as LocalUser | null;
|
const u = await this.usersRepository.findOneBy({ id }) as MiLocalUser | null;
|
||||||
if (u) this.cacheService.uriPersonCache.set(uri, u);
|
if (u) this.cacheService.uriPersonCache.set(uri, u);
|
||||||
return u;
|
return u;
|
||||||
}
|
}
|
||||||
|
|
||||||
//#region このサーバーに既に登録されていたらそれを返す
|
//#region このサーバーに既に登録されていたらそれを返す
|
||||||
const exist = await this.usersRepository.findOneBy({ uri }) as LocalUser | RemoteUser | null;
|
const exist = await this.usersRepository.findOneBy({ uri }) as MiLocalUser | MiRemoteUser | null;
|
||||||
|
|
||||||
if (exist) {
|
if (exist) {
|
||||||
this.cacheService.uriPersonCache.set(uri, exist);
|
this.cacheService.uriPersonCache.set(uri, exist);
|
||||||
@ -225,7 +225,7 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async resolveAvatarAndBanner(user: RemoteUser, icon: any, image: any): Promise<Pick<RemoteUser, 'avatarId' | 'bannerId' | 'avatarUrl' | 'bannerUrl' | 'avatarBlurhash' | 'bannerBlurhash'>> {
|
private async resolveAvatarAndBanner(user: MiRemoteUser, icon: any, image: any): Promise<Pick<MiRemoteUser, 'avatarId' | 'bannerId' | 'avatarUrl' | 'bannerUrl' | 'avatarBlurhash' | 'bannerBlurhash'>> {
|
||||||
const [avatar, banner] = await Promise.all([icon, image].map(img => {
|
const [avatar, banner] = await Promise.all([icon, image].map(img => {
|
||||||
if (img == null) return null;
|
if (img == null) return null;
|
||||||
if (user == null) throw new Error('failed to create user: user is null');
|
if (user == null) throw new Error('failed to create user: user is null');
|
||||||
@ -246,7 +246,7 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
* Personを作成します。
|
* Personを作成します。
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async createPerson(uri: string, resolver?: Resolver): Promise<RemoteUser> {
|
public async createPerson(uri: string, resolver?: Resolver): Promise<MiRemoteUser> {
|
||||||
if (typeof uri !== 'string') throw new Error('uri is not string');
|
if (typeof uri !== 'string') throw new Error('uri is not string');
|
||||||
|
|
||||||
if (uri.startsWith(this.config.url)) {
|
if (uri.startsWith(this.config.url)) {
|
||||||
@ -280,13 +280,13 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create user
|
// Create user
|
||||||
let user: RemoteUser | null = null;
|
let user: MiRemoteUser | null = null;
|
||||||
|
|
||||||
//#region カスタム絵文字取得
|
//#region カスタム絵文字取得
|
||||||
const emojis = await this.apNoteService.extractEmojis(person.tag ?? [], host)
|
const emojis = await this.apNoteService.extractEmojis(person.tag ?? [], host)
|
||||||
.then(_emojis => _emojis.map(emoji => emoji.name))
|
.then(_emojis => _emojis.map(emoji => emoji.name))
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
this.logger.error('error occured while fetching user emojis', { stack: err });
|
this.logger.error('error occurred while fetching user emojis', { stack: err });
|
||||||
return [];
|
return [];
|
||||||
});
|
});
|
||||||
//#endregion
|
//#endregion
|
||||||
@ -294,7 +294,7 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
try {
|
try {
|
||||||
// Start transaction
|
// Start transaction
|
||||||
await this.db.transaction(async transactionalEntityManager => {
|
await this.db.transaction(async transactionalEntityManager => {
|
||||||
user = await transactionalEntityManager.save(new User({
|
user = await transactionalEntityManager.save(new MiUser({
|
||||||
id: this.idService.genId(),
|
id: this.idService.genId(),
|
||||||
avatarId: null,
|
avatarId: null,
|
||||||
bannerId: null,
|
bannerId: null,
|
||||||
@ -318,9 +318,9 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
isBot,
|
isBot,
|
||||||
isCat: (person as any).isCat === true,
|
isCat: (person as any).isCat === true,
|
||||||
emojis,
|
emojis,
|
||||||
})) as RemoteUser;
|
})) as MiRemoteUser;
|
||||||
|
|
||||||
await transactionalEntityManager.save(new UserProfile({
|
await transactionalEntityManager.save(new MiUserProfile({
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
description: person.summary ? this.apMfmService.htmlToMfm(truncate(person.summary, summaryLength), person.tag) : null,
|
description: person.summary ? this.apMfmService.htmlToMfm(truncate(person.summary, summaryLength), person.tag) : null,
|
||||||
url,
|
url,
|
||||||
@ -331,7 +331,7 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
if (person.publicKey) {
|
if (person.publicKey) {
|
||||||
await transactionalEntityManager.save(new UserPublickey({
|
await transactionalEntityManager.save(new MiUserPublickey({
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
keyId: person.publicKey.id,
|
keyId: person.publicKey.id,
|
||||||
keyPem: person.publicKey.publicKeyPem,
|
keyPem: person.publicKey.publicKeyPem,
|
||||||
@ -345,7 +345,7 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
const u = await this.usersRepository.findOneBy({ uri: person.id });
|
const u = await this.usersRepository.findOneBy({ uri: person.id });
|
||||||
if (u == null) throw new Error('already registered');
|
if (u == null) throw new Error('already registered');
|
||||||
|
|
||||||
user = u as RemoteUser;
|
user = u as MiRemoteUser;
|
||||||
} else {
|
} else {
|
||||||
this.logger.error(e instanceof Error ? e : new Error(e as string));
|
this.logger.error(e instanceof Error ? e : new Error(e as string));
|
||||||
throw e;
|
throw e;
|
||||||
@ -380,7 +380,7 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
// Register to the cache
|
// Register to the cache
|
||||||
this.cacheService.uriPersonCache.set(user.uri, user);
|
this.cacheService.uriPersonCache.set(user.uri, user);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.logger.error('error occured while fetching user avatar/banner', { stack: err });
|
this.logger.error('error occurred while fetching user avatar/banner', { stack: err });
|
||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
@ -407,7 +407,7 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
if (uri.startsWith(`${this.config.url}/`)) return;
|
if (uri.startsWith(`${this.config.url}/`)) return;
|
||||||
|
|
||||||
//#region このサーバーに既に登録されているか
|
//#region このサーバーに既に登録されているか
|
||||||
const exist = await this.fetchPerson(uri) as RemoteUser | null;
|
const exist = await this.fetchPerson(uri) as MiRemoteUser | null;
|
||||||
if (exist === null) return;
|
if (exist === null) return;
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
@ -456,7 +456,7 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
alsoKnownAs: person.alsoKnownAs ?? null,
|
alsoKnownAs: person.alsoKnownAs ?? null,
|
||||||
isExplorable: person.discoverable,
|
isExplorable: person.discoverable,
|
||||||
...(await this.resolveAvatarAndBanner(exist, person.icon, person.image).catch(() => ({}))),
|
...(await this.resolveAvatarAndBanner(exist, person.icon, person.image).catch(() => ({}))),
|
||||||
} as Partial<RemoteUser> & Pick<RemoteUser, 'isBot' | 'isCat' | 'isLocked' | 'movedToUri' | 'alsoKnownAs' | 'isExplorable'>;
|
} as Partial<MiRemoteUser> & Pick<MiRemoteUser, 'isBot' | 'isCat' | 'isLocked' | 'movedToUri' | 'alsoKnownAs' | 'isExplorable'>;
|
||||||
|
|
||||||
const moving = ((): boolean => {
|
const moving = ((): boolean => {
|
||||||
// 移行先がない→ある
|
// 移行先がない→ある
|
||||||
@ -542,7 +542,7 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
* リモートサーバーからフェッチしてMisskeyに登録しそれを返します。
|
* リモートサーバーからフェッチしてMisskeyに登録しそれを返します。
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async resolvePerson(uri: string, resolver?: Resolver): Promise<LocalUser | RemoteUser> {
|
public async resolvePerson(uri: string, resolver?: Resolver): Promise<MiLocalUser | MiRemoteUser> {
|
||||||
//#region このサーバーに既に登録されていたらそれを返す
|
//#region このサーバーに既に登録されていたらそれを返す
|
||||||
const exist = await this.fetchPerson(uri);
|
const exist = await this.fetchPerson(uri);
|
||||||
if (exist) return exist;
|
if (exist) return exist;
|
||||||
@ -572,7 +572,7 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async updateFeatured(userId: User['id'], resolver?: Resolver): Promise<void> {
|
public async updateFeatured(userId: MiUser['id'], resolver?: Resolver): Promise<void> {
|
||||||
const user = await this.usersRepository.findOneByOrFail({ id: userId });
|
const user = await this.usersRepository.findOneByOrFail({ id: userId });
|
||||||
if (!this.userEntityService.isRemoteUser(user)) return;
|
if (!this.userEntityService.isRemoteUser(user)) return;
|
||||||
if (!user.featured) return;
|
if (!user.featured) return;
|
||||||
@ -590,7 +590,7 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
const items = await Promise.all(toArray(unresolvedItems).map(x => _resolver.resolve(x)));
|
const items = await Promise.all(toArray(unresolvedItems).map(x => _resolver.resolve(x)));
|
||||||
|
|
||||||
// Resolve and regist Notes
|
// Resolve and regist Notes
|
||||||
const limit = promiseLimit<Note | null>(2);
|
const limit = promiseLimit<MiNote | null>(2);
|
||||||
const featuredNotes = await Promise.all(items
|
const featuredNotes = await Promise.all(items
|
||||||
.filter(item => getApType(item) === 'Note') // TODO: Noteでなくてもいいかも
|
.filter(item => getApType(item) === 'Note') // TODO: Noteでなくてもいいかも
|
||||||
.slice(0, 5)
|
.slice(0, 5)
|
||||||
@ -600,13 +600,13 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
}))));
|
}))));
|
||||||
|
|
||||||
await this.db.transaction(async transactionalEntityManager => {
|
await this.db.transaction(async transactionalEntityManager => {
|
||||||
await transactionalEntityManager.delete(UserNotePining, { userId: user.id });
|
await transactionalEntityManager.delete(MiUserNotePining, { userId: user.id });
|
||||||
|
|
||||||
// とりあえずidを別の時間で生成して順番を維持
|
// とりあえずidを別の時間で生成して順番を維持
|
||||||
let td = 0;
|
let td = 0;
|
||||||
for (const note of featuredNotes.filter((note): note is Note => note != null)) {
|
for (const note of featuredNotes.filter((note): note is MiNote => note != null)) {
|
||||||
td -= 1000;
|
td -= 1000;
|
||||||
transactionalEntityManager.insert(UserNotePining, {
|
transactionalEntityManager.insert(MiUserNotePining, {
|
||||||
id: this.idService.genId(new Date(Date.now() + td)),
|
id: this.idService.genId(new Date(Date.now() + td)),
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
@ -622,7 +622,7 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
* @param movePreventUris ここに列挙されたURIにsrc.movedToUriが含まれる場合、移行処理はしない(無限ループ防止)
|
* @param movePreventUris ここに列挙されたURIにsrc.movedToUriが含まれる場合、移行処理はしない(無限ループ防止)
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
private async processRemoteMove(src: RemoteUser, movePreventUris: string[] = []): Promise<string> {
|
private async processRemoteMove(src: MiRemoteUser, movePreventUris: string[] = []): Promise<string> {
|
||||||
if (!src.movedToUri) return 'skip: no movedToUri';
|
if (!src.movedToUri) return 'skip: no movedToUri';
|
||||||
if (src.uri === src.movedToUri) return 'skip: movedTo itself (src)'; // ???
|
if (src.uri === src.movedToUri) return 'skip: movedTo itself (src)'; // ???
|
||||||
if (movePreventUris.length > 10) return 'skip: too many moves';
|
if (movePreventUris.length > 10) return 'skip: too many moves';
|
||||||
@ -632,7 +632,7 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
|
|
||||||
if (dst && this.userEntityService.isLocalUser(dst)) {
|
if (dst && this.userEntityService.isLocalUser(dst)) {
|
||||||
// targetがローカルユーザーだった場合データベースから引っ張ってくる
|
// targetがローカルユーザーだった場合データベースから引っ張ってくる
|
||||||
dst = await this.usersRepository.findOneByOrFail({ uri: src.movedToUri }) as LocalUser;
|
dst = await this.usersRepository.findOneByOrFail({ uri: src.movedToUri }) as MiLocalUser;
|
||||||
} else if (dst) {
|
} else if (dst) {
|
||||||
if (movePreventUris.includes(src.movedToUri)) return 'skip: circular move';
|
if (movePreventUris.includes(src.movedToUri)) return 'skip: circular move';
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
import { Injectable, Inject } from '@nestjs/common';
|
import { Injectable, Inject } from '@nestjs/common';
|
||||||
import { DataSource } from 'typeorm';
|
import { DataSource } from 'typeorm';
|
||||||
import { AppLockService } from '@/core/AppLockService.js';
|
import { AppLockService } from '@/core/AppLockService.js';
|
||||||
import type { User } from '@/models/entities/User.js';
|
import type { MiUser } from '@/models/entities/User.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import Chart from '../core.js';
|
import Chart from '../core.js';
|
||||||
@ -21,9 +21,8 @@ const year = 1000 * 60 * 60 * 24 * 365;
|
|||||||
/**
|
/**
|
||||||
* アクティブユーザーに関するチャート
|
* アクティブユーザーに関するチャート
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line import/no-default-export
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export default class ActiveUsersChart extends Chart<typeof schema> {
|
export default class ActiveUsersChart extends Chart<typeof schema> { // eslint-disable-line import/no-default-export
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.db)
|
@Inject(DI.db)
|
||||||
private db: DataSource,
|
private db: DataSource,
|
||||||
@ -43,7 +42,7 @@ export default class ActiveUsersChart extends Chart<typeof schema> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async read(user: { id: User['id'], host: null, createdAt: User['createdAt'] }): Promise<void> {
|
public async read(user: { id: MiUser['id'], host: null, createdAt: MiUser['createdAt'] }): Promise<void> {
|
||||||
await this.commit({
|
await this.commit({
|
||||||
'read': [user.id],
|
'read': [user.id],
|
||||||
'registeredWithinWeek': (Date.now() - user.createdAt.getTime() < week) ? [user.id] : [],
|
'registeredWithinWeek': (Date.now() - user.createdAt.getTime() < week) ? [user.id] : [],
|
||||||
@ -56,7 +55,7 @@ export default class ActiveUsersChart extends Chart<typeof schema> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async write(user: { id: User['id'], host: null, createdAt: User['createdAt'] }): Promise<void> {
|
public async write(user: { id: MiUser['id'], host: null, createdAt: MiUser['createdAt'] }): Promise<void> {
|
||||||
await this.commit({
|
await this.commit({
|
||||||
'write': [user.id],
|
'write': [user.id],
|
||||||
});
|
});
|
||||||
|
@ -16,9 +16,8 @@ import type { KVs } from '../core.js';
|
|||||||
/**
|
/**
|
||||||
* Chart about ActivityPub requests
|
* Chart about ActivityPub requests
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line import/no-default-export
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export default class ApRequestChart extends Chart<typeof schema> {
|
export default class ApRequestChart extends Chart<typeof schema> { // eslint-disable-line import/no-default-export
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.db)
|
@Inject(DI.db)
|
||||||
private db: DataSource,
|
private db: DataSource,
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import { Injectable, Inject } from '@nestjs/common';
|
import { Injectable, Inject } from '@nestjs/common';
|
||||||
import { DataSource } from 'typeorm';
|
import { DataSource } from 'typeorm';
|
||||||
import type { DriveFile } from '@/models/entities/DriveFile.js';
|
import type { MiDriveFile } from '@/models/entities/DriveFile.js';
|
||||||
import { AppLockService } from '@/core/AppLockService.js';
|
import { AppLockService } from '@/core/AppLockService.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
@ -17,9 +17,8 @@ import type { KVs } from '../core.js';
|
|||||||
/**
|
/**
|
||||||
* ドライブに関するチャート
|
* ドライブに関するチャート
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line import/no-default-export
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export default class DriveChart extends Chart<typeof schema> {
|
export default class DriveChart extends Chart<typeof schema> { // eslint-disable-line import/no-default-export
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.db)
|
@Inject(DI.db)
|
||||||
private db: DataSource,
|
private db: DataSource,
|
||||||
@ -39,7 +38,7 @@ export default class DriveChart extends Chart<typeof schema> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async update(file: DriveFile, isAdditional: boolean): Promise<void> {
|
public async update(file: MiDriveFile, isAdditional: boolean): Promise<void> {
|
||||||
const fileSizeKb = file.size / 1000;
|
const fileSizeKb = file.size / 1000;
|
||||||
await this.commit(file.userHost === null ? {
|
await this.commit(file.userHost === null ? {
|
||||||
'local.incCount': isAdditional ? 1 : 0,
|
'local.incCount': isAdditional ? 1 : 0,
|
||||||
|
@ -18,9 +18,8 @@ import type { KVs } from '../core.js';
|
|||||||
/**
|
/**
|
||||||
* フェデレーションに関するチャート
|
* フェデレーションに関するチャート
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line import/no-default-export
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export default class FederationChart extends Chart<typeof schema> {
|
export default class FederationChart extends Chart<typeof schema> { // eslint-disable-line import/no-default-export
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.db)
|
@Inject(DI.db)
|
||||||
private db: DataSource,
|
private db: DataSource,
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user