mirror of
https://github.com/sim1222/misskey.git
synced 2025-04-29 02:37:22 +09:00
Merge remote-tracking branch 'upstream/develop' into develop
This commit is contained in:
commit
3c1f987dcb
@ -131,11 +131,20 @@ proxyBypassHosts:
|
|||||||
|
|
||||||
# Media Proxy
|
# Media Proxy
|
||||||
# Reference Implementation: https://github.com/misskey-dev/media-proxy
|
# Reference Implementation: https://github.com/misskey-dev/media-proxy
|
||||||
|
# * Deliver a common cache between instances
|
||||||
|
# * 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: false)
|
||||||
|
# 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
|
||||||
|
# There is no reference implementation.
|
||||||
|
# For example, Misskey will point to the following URL:
|
||||||
|
# https://example.com/thumbnail.webp?thumbnail=1&url=https%3A%2F%2Fstorage.example.com%2Fpath%2Fto%2Fvideo.mp4
|
||||||
|
#videoThumbnailGenerator: https://example.com
|
||||||
|
|
||||||
# Sign to ActivityPub GET request (default: true)
|
# Sign to ActivityPub GET request (default: true)
|
||||||
signToActivityPubGet: true
|
signToActivityPubGet: true
|
||||||
|
|
||||||
|
1
.devcontainer/Dockerfile
Normal file
1
.devcontainer/Dockerfile
Normal file
@ -0,0 +1 @@
|
|||||||
|
FROM mcr.microsoft.com/devcontainers/javascript-node:0-18
|
11
.devcontainer/devcontainer.json
Normal file
11
.devcontainer/devcontainer.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "Misskey",
|
||||||
|
"dockerComposeFile": "docker-compose.yml",
|
||||||
|
"service": "app",
|
||||||
|
"workspaceFolder": "/workspace",
|
||||||
|
"features": {
|
||||||
|
"ghcr.io/devcontainers-contrib/features/pnpm:2": {}
|
||||||
|
},
|
||||||
|
"forwardPorts": [3000],
|
||||||
|
"postCreateCommand": "sudo chmod 755 .devcontainer/init.sh && .devcontainer/init.sh"
|
||||||
|
}
|
146
.devcontainer/devcontainer.yml
Normal file
146
.devcontainer/devcontainer.yml
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
# Misskey configuration
|
||||||
|
#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
|
||||||
|
# ┌─────┐
|
||||||
|
#───┘ URL └─────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
# Final accessible URL seen by a user.
|
||||||
|
url: http://127.0.0.1:3000/
|
||||||
|
|
||||||
|
# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE
|
||||||
|
# URL SETTINGS AFTER THAT!
|
||||||
|
|
||||||
|
# ┌───────────────────────┐
|
||||||
|
#───┘ Port and TLS settings └───────────────────────────────────
|
||||||
|
|
||||||
|
#
|
||||||
|
# Misskey requires a reverse proxy to support HTTPS connections.
|
||||||
|
#
|
||||||
|
# +----- https://example.tld/ ------------+
|
||||||
|
# +------+ |+-------------+ +----------------+|
|
||||||
|
# | User | ---> || Proxy (443) | ---> | Misskey (3000) ||
|
||||||
|
# +------+ |+-------------+ +----------------+|
|
||||||
|
# +---------------------------------------+
|
||||||
|
#
|
||||||
|
# You need to set up a reverse proxy. (e.g. nginx)
|
||||||
|
# An encrypted connection with HTTPS is highly recommended
|
||||||
|
# because tokens may be transferred in GET requests.
|
||||||
|
|
||||||
|
# The port that your Misskey server should listen on.
|
||||||
|
port: 3000
|
||||||
|
|
||||||
|
# ┌──────────────────────────┐
|
||||||
|
#───┘ PostgreSQL configuration └────────────────────────────────
|
||||||
|
|
||||||
|
db:
|
||||||
|
host: db
|
||||||
|
port: 5432
|
||||||
|
|
||||||
|
# Database name
|
||||||
|
db: misskey
|
||||||
|
|
||||||
|
# Auth
|
||||||
|
user: postgres
|
||||||
|
pass: postgres
|
||||||
|
|
||||||
|
# Whether disable Caching queries
|
||||||
|
#disableCache: true
|
||||||
|
|
||||||
|
# Extra Connection options
|
||||||
|
#extra:
|
||||||
|
# ssl: true
|
||||||
|
|
||||||
|
# ┌─────────────────────┐
|
||||||
|
#───┘ Redis configuration └─────────────────────────────────────
|
||||||
|
|
||||||
|
redis:
|
||||||
|
host: redis
|
||||||
|
port: 6379
|
||||||
|
#family: 0 # 0=Both, 4=IPv4, 6=IPv6
|
||||||
|
#pass: example-pass
|
||||||
|
#prefix: example-prefix
|
||||||
|
#db: 1
|
||||||
|
|
||||||
|
# ┌─────────────────────────────┐
|
||||||
|
#───┘ Elasticsearch configuration └─────────────────────────────
|
||||||
|
|
||||||
|
#elasticsearch:
|
||||||
|
# host: localhost
|
||||||
|
# port: 9200
|
||||||
|
# ssl: false
|
||||||
|
# user:
|
||||||
|
# pass:
|
||||||
|
|
||||||
|
# ┌───────────────┐
|
||||||
|
#───┘ ID generation └───────────────────────────────────────────
|
||||||
|
|
||||||
|
# You can select the ID generation method.
|
||||||
|
# You don't usually need to change this setting, but you can
|
||||||
|
# change it according to your preferences.
|
||||||
|
|
||||||
|
# Available methods:
|
||||||
|
# aid ... Short, Millisecond accuracy
|
||||||
|
# meid ... Similar to ObjectID, Millisecond accuracy
|
||||||
|
# ulid ... Millisecond accuracy
|
||||||
|
# objectid ... This is left for backward compatibility
|
||||||
|
|
||||||
|
# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE
|
||||||
|
# ID SETTINGS AFTER THAT!
|
||||||
|
|
||||||
|
id: 'aid'
|
||||||
|
|
||||||
|
# ┌─────────────────────┐
|
||||||
|
#───┘ Other configuration └─────────────────────────────────────
|
||||||
|
|
||||||
|
# Whether disable HSTS
|
||||||
|
#disableHsts: true
|
||||||
|
|
||||||
|
# Number of worker processes
|
||||||
|
#clusterLimit: 1
|
||||||
|
|
||||||
|
# Job concurrency per worker
|
||||||
|
# deliverJobConcurrency: 128
|
||||||
|
# inboxJobConcurrency: 16
|
||||||
|
|
||||||
|
# Job rate limiter
|
||||||
|
# deliverJobPerSec: 128
|
||||||
|
# inboxJobPerSec: 16
|
||||||
|
|
||||||
|
# Job attempts
|
||||||
|
# deliverJobMaxAttempts: 12
|
||||||
|
# inboxJobMaxAttempts: 8
|
||||||
|
|
||||||
|
# IP address family used for outgoing request (ipv4, ipv6 or dual)
|
||||||
|
#outgoingAddressFamily: ipv4
|
||||||
|
|
||||||
|
# Proxy for HTTP/HTTPS
|
||||||
|
#proxy: http://127.0.0.1:3128
|
||||||
|
|
||||||
|
proxyBypassHosts:
|
||||||
|
- api.deepl.com
|
||||||
|
- api-free.deepl.com
|
||||||
|
- www.recaptcha.net
|
||||||
|
- hcaptcha.com
|
||||||
|
- challenges.cloudflare.com
|
||||||
|
|
||||||
|
# Proxy for SMTP/SMTPS
|
||||||
|
#proxySmtp: http://127.0.0.1:3128 # use HTTP/1.1 CONNECT
|
||||||
|
#proxySmtp: socks4://127.0.0.1:1080 # use SOCKS4
|
||||||
|
#proxySmtp: socks5://127.0.0.1:1080 # use SOCKS5
|
||||||
|
|
||||||
|
# Media Proxy
|
||||||
|
#mediaProxy: https://example.com/proxy
|
||||||
|
|
||||||
|
# Proxy remote files (default: false)
|
||||||
|
#proxyRemoteFiles: true
|
||||||
|
|
||||||
|
# Sign to ActivityPub GET request (default: true)
|
||||||
|
signToActivityPubGet: true
|
||||||
|
|
||||||
|
allowedPrivateNetworks: [
|
||||||
|
'127.0.0.1/32'
|
||||||
|
]
|
||||||
|
|
||||||
|
# Upload or download file size limits (bytes)
|
||||||
|
#maxFileSize: 262144000
|
53
.devcontainer/docker-compose.yml
Normal file
53
.devcontainer/docker-compose.yml
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- ../:/workspace:cached
|
||||||
|
|
||||||
|
command: sleep infinity
|
||||||
|
|
||||||
|
networks:
|
||||||
|
- internal_network
|
||||||
|
- external_network
|
||||||
|
|
||||||
|
redis:
|
||||||
|
restart: always
|
||||||
|
image: redis:7-alpine
|
||||||
|
networks:
|
||||||
|
- internal_network
|
||||||
|
volumes:
|
||||||
|
- redis-data:/data
|
||||||
|
healthcheck:
|
||||||
|
test: "redis-cli ping"
|
||||||
|
interval: 5s
|
||||||
|
retries: 20
|
||||||
|
|
||||||
|
db:
|
||||||
|
restart: unless-stopped
|
||||||
|
image: postgres:15-alpine
|
||||||
|
networks:
|
||||||
|
- internal_network
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
POSTGRES_DB: misskey
|
||||||
|
volumes:
|
||||||
|
- postgres-data:/var/lib/postgresql/data
|
||||||
|
healthcheck:
|
||||||
|
test: "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"
|
||||||
|
interval: 5s
|
||||||
|
retries: 20
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres-data:
|
||||||
|
redis-data:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
internal_network:
|
||||||
|
internal: true
|
||||||
|
external_network:
|
10
.devcontainer/init.sh
Executable file
10
.devcontainer/init.sh
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -xe
|
||||||
|
|
||||||
|
sudo chown -R node /workspace
|
||||||
|
git submodule update --init
|
||||||
|
pnpm install --frozen-lockfile
|
||||||
|
cp .devcontainer/devcontainer.yml .config/default.yml
|
||||||
|
pnpm build
|
||||||
|
pnpm migrate
|
@ -5,6 +5,7 @@ indent_style = tab
|
|||||||
indent_size = 2
|
indent_size = 2
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
|
end_of_line = lf
|
||||||
|
|
||||||
[*.yml]
|
[*.yml]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
|
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -5,3 +5,4 @@
|
|||||||
*.glb -diff -text
|
*.glb -diff -text
|
||||||
*.blend -diff -text
|
*.blend -diff -text
|
||||||
*.afdesign -diff -text
|
*.afdesign -diff -text
|
||||||
|
* text=auto eol=lf
|
||||||
|
36
.github/workflows/check_copyright_year.yml
vendored
36
.github/workflows/check_copyright_year.yml
vendored
@ -1,18 +1,18 @@
|
|||||||
name: Check copyright year
|
name: Check copyright year
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
- develop
|
- develop
|
||||||
|
|
||||||
jobs:
|
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.2.0
|
||||||
- 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!"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
8
.github/workflows/docker-develop.yml
vendored
8
.github/workflows/docker-develop.yml
vendored
@ -15,7 +15,10 @@ jobs:
|
|||||||
- name: Check out the repo
|
- name: Check out the repo
|
||||||
uses: actions/checkout@v3.3.0
|
uses: actions/checkout@v3.3.0
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
|
id: buildx
|
||||||
uses: docker/setup-buildx-action@v2.3.0
|
uses: docker/setup-buildx-action@v2.3.0
|
||||||
|
with:
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v4
|
uses: docker/metadata-action@v4
|
||||||
@ -27,10 +30,13 @@ jobs:
|
|||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
- name: Build and Push to Docker Hub
|
- name: Build and Push to Docker Hub
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v4
|
||||||
with:
|
with:
|
||||||
|
builder: ${{ steps.buildx.outputs.name }}
|
||||||
context: .
|
context: .
|
||||||
push: true
|
push: true
|
||||||
|
platforms: ${{ steps.buildx.outputs.platforms }}
|
||||||
|
provenance: false
|
||||||
tags: sim1222/misskey:develop
|
tags: sim1222/misskey:develop
|
||||||
labels: develop
|
labels: develop
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
|
12
.github/workflows/docker.yml
vendored
12
.github/workflows/docker.yml
vendored
@ -13,6 +13,11 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Check out the repo
|
- name: Check out the repo
|
||||||
uses: actions/checkout@v3.3.0
|
uses: actions/checkout@v3.3.0
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
id: buildx
|
||||||
|
uses: docker/setup-buildx-action@v2.3.0
|
||||||
|
with:
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v4
|
uses: docker/metadata-action@v4
|
||||||
@ -31,9 +36,14 @@ jobs:
|
|||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
- name: Build and Push to Docker Hub
|
- name: Build and Push to Docker Hub
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v4
|
||||||
with:
|
with:
|
||||||
|
builder: ${{ steps.buildx.outputs.name }}
|
||||||
context: .
|
context: .
|
||||||
push: true
|
push: true
|
||||||
|
platforms: ${{ steps.buildx.outputs.platforms }}
|
||||||
|
provenance: false
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
108
.github/workflows/lint.yml
vendored
108
.github/workflows/lint.yml
vendored
@ -1,54 +1,54 @@
|
|||||||
name: Lint
|
name: Lint
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
- develop
|
- develop
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
jobs:
|
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.3.0
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: true
|
submodules: true
|
||||||
- uses: pnpm/action-setup@v2
|
- uses: pnpm/action-setup@v2
|
||||||
with:
|
with:
|
||||||
version: 7
|
version: 7
|
||||||
run_install: false
|
run_install: false
|
||||||
- uses: actions/setup-node@v3.6.0
|
- uses: actions/setup-node@v3.6.0
|
||||||
with:
|
with:
|
||||||
node-version: 18.x
|
node-version: 18.x
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
- run: pnpm i --frozen-lockfile
|
- run: pnpm i --frozen-lockfile
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
needs: [pnpm_install]
|
needs: [pnpm_install]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
workspace:
|
workspace:
|
||||||
- backend
|
- backend
|
||||||
- frontend
|
- frontend
|
||||||
- sw
|
- sw
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3.3.0
|
- uses: actions/checkout@v3.3.0
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: true
|
submodules: true
|
||||||
- uses: pnpm/action-setup@v2
|
- uses: pnpm/action-setup@v2
|
||||||
with:
|
with:
|
||||||
version: 7
|
version: 7
|
||||||
run_install: false
|
run_install: false
|
||||||
- uses: actions/setup-node@v3.6.0
|
- uses: actions/setup-node@v3.6.0
|
||||||
with:
|
with:
|
||||||
node-version: 18.x
|
node-version: 18.x
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
- run: pnpm i --frozen-lockfile
|
- run: pnpm i --frozen-lockfile
|
||||||
- run: pnpm --filter ${{ matrix.workspace }} run lint
|
- run: pnpm --filter ${{ matrix.workspace }} run lint
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -33,6 +33,7 @@ coverage
|
|||||||
!/.config/docker_example.yml
|
!/.config/docker_example.yml
|
||||||
!/.config/docker_example.env
|
!/.config/docker_example.env
|
||||||
docker-compose.yml
|
docker-compose.yml
|
||||||
|
!/.devcontainer/docker-compose.yml
|
||||||
|
|
||||||
# misskey
|
# misskey
|
||||||
/build
|
/build
|
||||||
|
11
CHANGELOG.md
11
CHANGELOG.md
@ -10,8 +10,19 @@ You should also include the user name that made the change.
|
|||||||
-->
|
-->
|
||||||
## 13.x.x (unreleased)
|
## 13.x.x (unreleased)
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
- Server: URLプレビュー(summaly)はプロキシを通すように
|
||||||
|
|
||||||
|
### Bugfixes
|
||||||
|
-
|
||||||
|
|
||||||
|
## 13.6.1 (2023/02/12)
|
||||||
|
|
||||||
### Improvements
|
### Improvements
|
||||||
- アニメーションを少なくする設定の時、MkPageHeaderのタブアニメーションを無効化
|
- アニメーションを少なくする設定の時、MkPageHeaderのタブアニメーションを無効化
|
||||||
|
- Backend: activitypub情報がcorsでブロックされないようヘッダーを追加
|
||||||
|
- enhance: レートリミットを0%にできるように
|
||||||
|
- チャンネル内Renoteを行えるように
|
||||||
|
|
||||||
### Bugfixes
|
### Bugfixes
|
||||||
- Client: ユーザーページでアクティビティを見ることができない問題を修正
|
- Client: ユーザーページでアクティビティを見ることができない問題を修正
|
||||||
|
@ -111,6 +111,26 @@ command.
|
|||||||
- Vite HMR (just the `vite` command) is available. The behavior may be different from production.
|
- Vite HMR (just the `vite` command) is available. The behavior may be different from production.
|
||||||
- Service Worker is watched by esbuild.
|
- Service Worker is watched by esbuild.
|
||||||
|
|
||||||
|
### Dev Container
|
||||||
|
Instead of running `pnpm` locally, you can use Dev Container to set up your development environment.
|
||||||
|
To use Dev Container, open the project directory on VSCode with Dev Containers installed.
|
||||||
|
**Note:** If you are using Windows, please clone the repository with WSL. Using Git for Windows will result in broken files due to the difference in how newlines are handled.
|
||||||
|
|
||||||
|
It will run the following command automatically inside the container.
|
||||||
|
``` bash
|
||||||
|
git submodule update --init
|
||||||
|
pnpm install --frozen-lockfile
|
||||||
|
cp .devcontainer/devcontainer.yml .config/default.yml
|
||||||
|
pnpm build
|
||||||
|
pnpm migrate
|
||||||
|
```
|
||||||
|
|
||||||
|
After finishing the migration, run the `pnpm dev` command to start the development server.
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
pnpm dev
|
||||||
|
```
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
- Test codes are located in [`/packages/backend/test`](/packages/backend/test).
|
- Test codes are located in [`/packages/backend/test`](/packages/backend/test).
|
||||||
|
|
||||||
|
14
Dockerfile
14
Dockerfile
@ -1,3 +1,5 @@
|
|||||||
|
# syntax = docker/dockerfile:1.4
|
||||||
|
|
||||||
ARG NODE_VERSION=18.13.0-bullseye
|
ARG NODE_VERSION=18.13.0-bullseye
|
||||||
|
|
||||||
FROM node:${NODE_VERSION} AS builder
|
FROM node:${NODE_VERSION} AS builder
|
||||||
@ -14,16 +16,16 @@ RUN corepack enable
|
|||||||
|
|
||||||
WORKDIR /misskey
|
WORKDIR /misskey
|
||||||
|
|
||||||
COPY ["pnpm-lock.yaml", "pnpm-workspace.yaml", "package.json", "./"]
|
COPY --link ["pnpm-lock.yaml", "pnpm-workspace.yaml", "package.json", "./"]
|
||||||
COPY ["scripts", "./scripts"]
|
COPY --link ["scripts", "./scripts"]
|
||||||
COPY ["packages/backend/package.json", "./packages/backend/"]
|
COPY --link ["packages/backend/package.json", "./packages/backend/"]
|
||||||
COPY ["packages/frontend/package.json", "./packages/frontend/"]
|
COPY --link ["packages/frontend/package.json", "./packages/frontend/"]
|
||||||
COPY ["packages/sw/package.json", "./packages/sw/"]
|
COPY --link ["packages/sw/package.json", "./packages/sw/"]
|
||||||
|
|
||||||
RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \
|
RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \
|
||||||
pnpm i --frozen-lockfile --aggregate-output
|
pnpm i --frozen-lockfile --aggregate-output
|
||||||
|
|
||||||
COPY . ./
|
COPY --link . ./
|
||||||
|
|
||||||
ARG NODE_ENV=production
|
ARG NODE_ENV=production
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
apiVersion: v2
|
apiVersion: v2
|
||||||
name: misskey
|
name: misskey
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
|
description: This chart is created for the purpose of previewing Pull Requests. Do not use this for production use.
|
||||||
|
@ -467,6 +467,8 @@ youHaveNoGroups: "You have no groups"
|
|||||||
joinOrCreateGroup: "Get invited to a group or create your own."
|
joinOrCreateGroup: "Get invited to a group or create your own."
|
||||||
noHistory: "No history available"
|
noHistory: "No history available"
|
||||||
signinHistory: "Login history"
|
signinHistory: "Login history"
|
||||||
|
enableAdvancedMfm: "Enable advanced MFM"
|
||||||
|
enableAnimatedMfm: "Enable MFM with animation"
|
||||||
doing: "Processing..."
|
doing: "Processing..."
|
||||||
category: "Category"
|
category: "Category"
|
||||||
tags: "Tags"
|
tags: "Tags"
|
||||||
@ -945,6 +947,10 @@ selectFromPresets: "Choose from presets"
|
|||||||
achievements: "Achievements"
|
achievements: "Achievements"
|
||||||
gotInvalidResponseError: "Invalid server response"
|
gotInvalidResponseError: "Invalid server response"
|
||||||
gotInvalidResponseErrorDescription: "The server may be unreachable or undergoing maintenance. Please try again later."
|
gotInvalidResponseErrorDescription: "The server may be unreachable or undergoing maintenance. Please try again later."
|
||||||
|
thisPostMayBeAnnoying: "This note may annoy others."
|
||||||
|
thisPostMayBeAnnoyingHome: "Post to home timeline"
|
||||||
|
thisPostMayBeAnnoyingCancel: "Cancel"
|
||||||
|
thisPostMayBeAnnoyingIgnore: "Post anyway"
|
||||||
_achievements:
|
_achievements:
|
||||||
earnedAt: "Unlocked at"
|
earnedAt: "Unlocked at"
|
||||||
_types:
|
_types:
|
||||||
|
@ -103,6 +103,8 @@ renoted: "Renoteしました。"
|
|||||||
cantRenote: "この投稿はRenoteできません。"
|
cantRenote: "この投稿はRenoteできません。"
|
||||||
cantReRenote: "RenoteをRenoteすることはできません。"
|
cantReRenote: "RenoteをRenoteすることはできません。"
|
||||||
quote: "引用"
|
quote: "引用"
|
||||||
|
inChannelRenote: "チャンネル内Renote"
|
||||||
|
inChannelQuote: "チャンネル内引用"
|
||||||
pinnedNote: "ピン留めされたノート"
|
pinnedNote: "ピン留めされたノート"
|
||||||
pinned: "ピン留め"
|
pinned: "ピン留め"
|
||||||
you: "あなた"
|
you: "あなた"
|
||||||
@ -417,24 +419,15 @@ markAsReadAllTalkMessages: "すべてのチャットを既読にする"
|
|||||||
help: "ヘルプ"
|
help: "ヘルプ"
|
||||||
inputMessageHere: "ここにメッセージを入力"
|
inputMessageHere: "ここにメッセージを入力"
|
||||||
close: "閉じる"
|
close: "閉じる"
|
||||||
group: "グループ"
|
|
||||||
groups: "グループ"
|
|
||||||
createGroup: "グループを作成"
|
|
||||||
ownedGroups: "所有グループ"
|
|
||||||
joinedGroups: "参加しているグループ"
|
|
||||||
invites: "招待"
|
invites: "招待"
|
||||||
groupName: "グループ名"
|
|
||||||
members: "メンバー"
|
members: "メンバー"
|
||||||
transfer: "譲渡"
|
transfer: "譲渡"
|
||||||
messagingWithUser: "ユーザーとチャット"
|
|
||||||
messagingWithGroup: "グループでチャット"
|
|
||||||
title: "タイトル"
|
title: "タイトル"
|
||||||
text: "テキスト"
|
text: "テキスト"
|
||||||
enable: "有効にする"
|
enable: "有効にする"
|
||||||
next: "次"
|
next: "次"
|
||||||
retype: "再入力"
|
retype: "再入力"
|
||||||
noteOf: "{user}のノート"
|
noteOf: "{user}のノート"
|
||||||
inviteToGroup: "グループに招待"
|
|
||||||
quoteAttached: "引用付き"
|
quoteAttached: "引用付き"
|
||||||
quoteQuestion: "引用として添付しますか?"
|
quoteQuestion: "引用として添付しますか?"
|
||||||
noMessagesYet: "まだチャットはありません"
|
noMessagesYet: "まだチャットはありません"
|
||||||
@ -460,13 +453,10 @@ tapSecurityKey: "セキュリティキーにタッチ"
|
|||||||
or: "もしくは"
|
or: "もしくは"
|
||||||
language: "言語"
|
language: "言語"
|
||||||
uiLanguage: "UIの表示言語"
|
uiLanguage: "UIの表示言語"
|
||||||
groupInvited: "グループに招待されました"
|
|
||||||
aboutX: "{x}について"
|
aboutX: "{x}について"
|
||||||
emojiStyle: "絵文字のスタイル"
|
emojiStyle: "絵文字のスタイル"
|
||||||
native: "ネイティブ"
|
native: "ネイティブ"
|
||||||
disableDrawer: "メニューをドロワーで表示しない"
|
disableDrawer: "メニューをドロワーで表示しない"
|
||||||
youHaveNoGroups: "グループがありません"
|
|
||||||
joinOrCreateGroup: "既存のグループに招待してもらうか、新しくグループを作成してください。"
|
|
||||||
noHistory: "履歴はありません"
|
noHistory: "履歴はありません"
|
||||||
signinHistory: "ログイン履歴"
|
signinHistory: "ログイン履歴"
|
||||||
enableAdvancedMfm: "高度なMFMを有効にする"
|
enableAdvancedMfm: "高度なMFMを有効にする"
|
||||||
@ -789,6 +779,7 @@ popularPosts: "人気の投稿"
|
|||||||
shareWithNote: "ノートで共有"
|
shareWithNote: "ノートで共有"
|
||||||
ads: "広告"
|
ads: "広告"
|
||||||
expiration: "期限"
|
expiration: "期限"
|
||||||
|
startingperiod: "開始期間"
|
||||||
memo: "メモ"
|
memo: "メモ"
|
||||||
priority: "優先度"
|
priority: "優先度"
|
||||||
high: "高"
|
high: "高"
|
||||||
@ -840,8 +831,6 @@ deleteAccountConfirm: "アカウントが削除されます。よろしいです
|
|||||||
incorrectPassword: "パスワードが間違っています。"
|
incorrectPassword: "パスワードが間違っています。"
|
||||||
voteConfirm: "「{choice}」に投票しますか?"
|
voteConfirm: "「{choice}」に投票しますか?"
|
||||||
hide: "隠す"
|
hide: "隠す"
|
||||||
leaveGroup: "グループから抜ける"
|
|
||||||
leaveGroupConfirm: "「{name}」から抜けますか?"
|
|
||||||
useDrawerReactionPickerForMobile: "モバイルデバイスのときドロワーで表示"
|
useDrawerReactionPickerForMobile: "モバイルデバイスのときドロワーで表示"
|
||||||
welcomeBackWithName: "おかえりなさい、{name}さん"
|
welcomeBackWithName: "おかえりなさい、{name}さん"
|
||||||
clickToFinishEmailVerification: "[{ok}]を押して、メールアドレスの確認を完了してください。"
|
clickToFinishEmailVerification: "[{ok}]を押して、メールアドレスの確認を完了してください。"
|
||||||
@ -953,6 +942,10 @@ thisPostMayBeAnnoying: "この投稿は迷惑になる可能性があります
|
|||||||
thisPostMayBeAnnoyingHome: "ホームに投稿"
|
thisPostMayBeAnnoyingHome: "ホームに投稿"
|
||||||
thisPostMayBeAnnoyingCancel: "やめる"
|
thisPostMayBeAnnoyingCancel: "やめる"
|
||||||
thisPostMayBeAnnoyingIgnore: "このまま投稿"
|
thisPostMayBeAnnoyingIgnore: "このまま投稿"
|
||||||
|
collapseRenotes: "見たことのあるRenoteを省略して表示"
|
||||||
|
internalServerError: "サーバー内部エラー"
|
||||||
|
internalServerErrorDescription: "サーバー内部で予期しないエラーが発生しました。"
|
||||||
|
copyErrorInfo: "エラー情報をコピー"
|
||||||
|
|
||||||
_achievements:
|
_achievements:
|
||||||
earnedAt: "獲得日時"
|
earnedAt: "獲得日時"
|
||||||
@ -1595,7 +1588,6 @@ _antennaSources:
|
|||||||
homeTimeline: "フォローしているユーザーのノート"
|
homeTimeline: "フォローしているユーザーのノート"
|
||||||
users: "指定した一人または複数のユーザーのノート"
|
users: "指定した一人または複数のユーザーのノート"
|
||||||
userList: "指定したリストのユーザーのノート"
|
userList: "指定したリストのユーザーのノート"
|
||||||
userGroup: "指定したグループのユーザーのノート"
|
|
||||||
|
|
||||||
_weekday:
|
_weekday:
|
||||||
sunday: "日曜日"
|
sunday: "日曜日"
|
||||||
@ -1825,12 +1817,9 @@ _notification:
|
|||||||
youGotReply: "{name}からのリプライ"
|
youGotReply: "{name}からのリプライ"
|
||||||
youGotQuote: "{name}による引用"
|
youGotQuote: "{name}による引用"
|
||||||
youRenoted: "{name}がRenoteしました"
|
youRenoted: "{name}がRenoteしました"
|
||||||
youGotMessagingMessageFromUser: "{name}からのチャットがあります"
|
|
||||||
youGotMessagingMessageFromGroup: "{name}のチャットがあります"
|
|
||||||
youWereFollowed: "フォローされました"
|
youWereFollowed: "フォローされました"
|
||||||
youReceivedFollowRequest: "フォローリクエストが来ました"
|
youReceivedFollowRequest: "フォローリクエストが来ました"
|
||||||
yourFollowRequestAccepted: "フォローリクエストが承認されました"
|
yourFollowRequestAccepted: "フォローリクエストが承認されました"
|
||||||
youWereInvitedToGroup: "{userName}があなたをグループに招待しました"
|
|
||||||
pollEnded: "アンケートの結果が出ました"
|
pollEnded: "アンケートの結果が出ました"
|
||||||
unreadAntennaNote: "アンテナ {name}"
|
unreadAntennaNote: "アンテナ {name}"
|
||||||
emptyPushNotificationMessage: "プッシュ通知の更新をしました"
|
emptyPushNotificationMessage: "プッシュ通知の更新をしました"
|
||||||
@ -1847,7 +1836,6 @@ _notification:
|
|||||||
pollEnded: "アンケートが終了"
|
pollEnded: "アンケートが終了"
|
||||||
receiveFollowRequest: "フォロー申請を受け取った"
|
receiveFollowRequest: "フォロー申請を受け取った"
|
||||||
followRequestAccepted: "フォローが受理された"
|
followRequestAccepted: "フォローが受理された"
|
||||||
groupInvited: "グループに招待された"
|
|
||||||
app: "連携アプリからの通知"
|
app: "連携アプリからの通知"
|
||||||
|
|
||||||
_actions:
|
_actions:
|
||||||
|
@ -947,6 +947,8 @@ selectFromPresets: "プリセットから選ぶ"
|
|||||||
achievements: "実績"
|
achievements: "実績"
|
||||||
gotInvalidResponseError: "サーバー黙っとるわ、知らんけど"
|
gotInvalidResponseError: "サーバー黙っとるわ、知らんけど"
|
||||||
gotInvalidResponseErrorDescription: "サーバーいま日曜日。またきて月曜日。"
|
gotInvalidResponseErrorDescription: "サーバーいま日曜日。またきて月曜日。"
|
||||||
|
thisPostMayBeAnnoying: "この投稿は迷惑かもしらんで。"
|
||||||
|
collapseRenotes: "見たことあるRenoteは省略やで"
|
||||||
_achievements:
|
_achievements:
|
||||||
earnedAt: "貰った日ぃ"
|
earnedAt: "貰った日ぃ"
|
||||||
_types:
|
_types:
|
||||||
|
@ -129,6 +129,7 @@ unblockConfirm: "이 계정의 차단을 해제하시겠습니까?"
|
|||||||
suspendConfirm: "이 계정을 정지하시겠습니까?"
|
suspendConfirm: "이 계정을 정지하시겠습니까?"
|
||||||
unsuspendConfirm: "이 계정의 정지를 해제하시겠습니까?"
|
unsuspendConfirm: "이 계정의 정지를 해제하시겠습니까?"
|
||||||
selectList: "리스트 선택"
|
selectList: "리스트 선택"
|
||||||
|
selectChannel: "채널 선택"
|
||||||
selectAntenna: "안테나 선택"
|
selectAntenna: "안테나 선택"
|
||||||
selectWidget: "위젯 선택"
|
selectWidget: "위젯 선택"
|
||||||
editWidgets: "위젯 편집"
|
editWidgets: "위젯 편집"
|
||||||
@ -256,6 +257,8 @@ noMoreHistory: "이것보다 과거의 기록이 없습니다"
|
|||||||
startMessaging: "대화 시작하기"
|
startMessaging: "대화 시작하기"
|
||||||
nUsersRead: "{n}명이 읽음"
|
nUsersRead: "{n}명이 읽음"
|
||||||
agreeTo: "{0}에 동의"
|
agreeTo: "{0}에 동의"
|
||||||
|
agreeBelow: "아래 내용에 동의합니다"
|
||||||
|
basicNotesBeforeCreateAccount: "기본적인 주의사항"
|
||||||
tos: "이용 약관"
|
tos: "이용 약관"
|
||||||
start: "시작하기"
|
start: "시작하기"
|
||||||
home: "홈"
|
home: "홈"
|
||||||
@ -464,6 +467,8 @@ youHaveNoGroups: "그룹이 없습니다"
|
|||||||
joinOrCreateGroup: "다른 그룹의 초대를 받거나, 직접 새 그룹을 만들어 보세요."
|
joinOrCreateGroup: "다른 그룹의 초대를 받거나, 직접 새 그룹을 만들어 보세요."
|
||||||
noHistory: "기록이 없습니다"
|
noHistory: "기록이 없습니다"
|
||||||
signinHistory: "로그인 기록"
|
signinHistory: "로그인 기록"
|
||||||
|
enableAdvancedMfm: "고급 MFM을 활성화"
|
||||||
|
enableAnimatedMfm: "움직임이 있는 MFM을 활성화"
|
||||||
doing: "잠시만요"
|
doing: "잠시만요"
|
||||||
category: "카테고리"
|
category: "카테고리"
|
||||||
tags: "태그"
|
tags: "태그"
|
||||||
@ -860,6 +865,8 @@ failedToFetchAccountInformation: "계정 정보를 가져오지 못했습니다"
|
|||||||
rateLimitExceeded: "요청 제한 횟수를 초과하였습니다"
|
rateLimitExceeded: "요청 제한 횟수를 초과하였습니다"
|
||||||
cropImage: "이미지 자르기"
|
cropImage: "이미지 자르기"
|
||||||
cropImageAsk: "이미지를 자르시겠습니까?"
|
cropImageAsk: "이미지를 자르시겠습니까?"
|
||||||
|
cropYes: "잘라내기"
|
||||||
|
cropNo: "그대로 사용"
|
||||||
file: "파일"
|
file: "파일"
|
||||||
recentNHours: "최근 {n}시간"
|
recentNHours: "최근 {n}시간"
|
||||||
recentNDays: "최근 {n}일"
|
recentNDays: "최근 {n}일"
|
||||||
@ -938,6 +945,12 @@ cannotPerformTemporaryDescription: "조작 횟수 제한을 초과하여 일시
|
|||||||
preset: "프리셋"
|
preset: "프리셋"
|
||||||
selectFromPresets: "프리셋에서 선택"
|
selectFromPresets: "프리셋에서 선택"
|
||||||
achievements: "도전 과제"
|
achievements: "도전 과제"
|
||||||
|
gotInvalidResponseError: "서버의 응답이 올바르지 않습니다"
|
||||||
|
gotInvalidResponseErrorDescription: " 서버가 다운되었거나 점검중일 가능성이 있습니다. 잠시후에 다시 시도해 주십시오."
|
||||||
|
thisPostMayBeAnnoying: "이 게시물은 다른 유저에게 피해를 줄 가능성이 있습니다."
|
||||||
|
thisPostMayBeAnnoyingHome: "홈에 게시"
|
||||||
|
thisPostMayBeAnnoyingCancel: "그만두기"
|
||||||
|
thisPostMayBeAnnoyingIgnore: "이대로 게시"
|
||||||
_achievements:
|
_achievements:
|
||||||
earnedAt: "달성 일시"
|
earnedAt: "달성 일시"
|
||||||
_types:
|
_types:
|
||||||
@ -1194,6 +1207,9 @@ _role:
|
|||||||
baseRole: "기본 역할"
|
baseRole: "기본 역할"
|
||||||
useBaseValue: "기본값 사용"
|
useBaseValue: "기본값 사용"
|
||||||
chooseRoleToAssign: "할당할 역할 선택"
|
chooseRoleToAssign: "할당할 역할 선택"
|
||||||
|
iconUrl: "아이콘 URL"
|
||||||
|
asBadge: "뱃지로 표시"
|
||||||
|
descriptionOfAsBadge: "활성화하면 유저명 옆에 역할의 아이콘이 표시됩니다."
|
||||||
canEditMembersByModerator: "모더레이터의 역할 수정 허용"
|
canEditMembersByModerator: "모더레이터의 역할 수정 허용"
|
||||||
descriptionOfCanEditMembersByModerator: "이 옵션을 켜면 모더레이터도 이 역할에 사용자를 할당하거나 삭제할 수 있습니다. 꺼져 있으면 관리자만 할당이 가능합니다."
|
descriptionOfCanEditMembersByModerator: "이 옵션을 켜면 모더레이터도 이 역할에 사용자를 할당하거나 삭제할 수 있습니다. 꺼져 있으면 관리자만 할당이 가능합니다."
|
||||||
priority: "우선순위"
|
priority: "우선순위"
|
||||||
@ -1523,12 +1539,15 @@ _permissions:
|
|||||||
"read:gallery-likes": "갤러리의 좋아요를 확인합니다"
|
"read:gallery-likes": "갤러리의 좋아요를 확인합니다"
|
||||||
"write:gallery-likes": "갤러리에 좋아요를 추가하거나 취소합니다"
|
"write:gallery-likes": "갤러리에 좋아요를 추가하거나 취소합니다"
|
||||||
_auth:
|
_auth:
|
||||||
|
shareAccessTitle: "어플리케이션의 접근 허가"
|
||||||
shareAccess: "\"{name}\" 이 계정에 접근하는 것을 허용하시겠습니까?"
|
shareAccess: "\"{name}\" 이 계정에 접근하는 것을 허용하시겠습니까?"
|
||||||
shareAccessAsk: "이 애플리케이션이 계정에 접근하는 것을 허용하시겠습니까?"
|
shareAccessAsk: "이 애플리케이션이 계정에 접근하는 것을 허용하시겠습니까?"
|
||||||
|
permission: "{name}에서 다음 권한을 요청하였습니다"
|
||||||
permissionAsk: "이 앱은 다음의 권한을 요청합니다"
|
permissionAsk: "이 앱은 다음의 권한을 요청합니다"
|
||||||
pleaseGoBack: "앱으로 돌아가서 시도해 주세요"
|
pleaseGoBack: "앱으로 돌아가서 시도해 주세요"
|
||||||
callback: "앱으로 돌아갑니다"
|
callback: "앱으로 돌아갑니다"
|
||||||
denied: "접근이 거부되었습니다"
|
denied: "접근이 거부되었습니다"
|
||||||
|
pleaseLogin: "어플리케이션의 접근을 허가하려면 로그인하십시오."
|
||||||
_antennaSources:
|
_antennaSources:
|
||||||
all: "모든 노트"
|
all: "모든 노트"
|
||||||
homeTimeline: "팔로우중인 유저의 노트"
|
homeTimeline: "팔로우중인 유저의 노트"
|
||||||
|
@ -166,7 +166,7 @@ recipient: "Отримувач"
|
|||||||
annotation: "Коментарі"
|
annotation: "Коментарі"
|
||||||
federation: "Федіверс"
|
federation: "Федіверс"
|
||||||
instances: "Інстанс"
|
instances: "Інстанс"
|
||||||
registeredAt: "Приєднався(лась)"
|
registeredAt: "Реєстрація"
|
||||||
latestRequestReceivedAt: "Останній запит прийнято"
|
latestRequestReceivedAt: "Останній запит прийнято"
|
||||||
latestStatus: "Останній статус"
|
latestStatus: "Останній статус"
|
||||||
storageUsage: "Використання простору"
|
storageUsage: "Використання простору"
|
||||||
@ -263,7 +263,7 @@ activity: "Активність"
|
|||||||
images: "Зображення"
|
images: "Зображення"
|
||||||
birthday: "День народження"
|
birthday: "День народження"
|
||||||
yearsOld: "{age} років"
|
yearsOld: "{age} років"
|
||||||
registeredDate: "Приєднався(лась)"
|
registeredDate: "Приєднання"
|
||||||
location: "Локація"
|
location: "Локація"
|
||||||
theme: "Тема"
|
theme: "Тема"
|
||||||
themeForLightMode: "Світла тема"
|
themeForLightMode: "Світла тема"
|
||||||
@ -1086,6 +1086,9 @@ _achievements:
|
|||||||
_outputHelloWorldOnScratchpad:
|
_outputHelloWorldOnScratchpad:
|
||||||
title: "Hello, world!"
|
title: "Hello, world!"
|
||||||
description: "Вивести \"hello world\" у Скретчпаді"
|
description: "Вивести \"hello world\" у Скретчпаді"
|
||||||
|
_reactWithoutRead:
|
||||||
|
title: "Прочитали як слід?"
|
||||||
|
description: "Реакція на нотатку, що містить понад 100 символів, протягом 3 секунд після її публікації"
|
||||||
_clickedClickHere:
|
_clickedClickHere:
|
||||||
title: "Натисніть тут"
|
title: "Натисніть тут"
|
||||||
description: "Натиснуто тут"
|
description: "Натиснуто тут"
|
||||||
|
@ -467,6 +467,8 @@ youHaveNoGroups: "没有群组"
|
|||||||
joinOrCreateGroup: "请加入一个现有的群组,或者创建新群组。"
|
joinOrCreateGroup: "请加入一个现有的群组,或者创建新群组。"
|
||||||
noHistory: "没有历史记录"
|
noHistory: "没有历史记录"
|
||||||
signinHistory: "登录历史"
|
signinHistory: "登录历史"
|
||||||
|
enableAdvancedMfm: "启用扩展MFM"
|
||||||
|
enableAnimatedMfm: "启用MFM动画"
|
||||||
doing: "正在进行"
|
doing: "正在进行"
|
||||||
category: "类别"
|
category: "类别"
|
||||||
tags: "标签"
|
tags: "标签"
|
||||||
@ -945,6 +947,10 @@ selectFromPresets: "從預設值中選擇"
|
|||||||
achievements: "成就"
|
achievements: "成就"
|
||||||
gotInvalidResponseError: "服务器无应答"
|
gotInvalidResponseError: "服务器无应答"
|
||||||
gotInvalidResponseErrorDescription: "您的网络连接可能出现了问题, 或是远程服务器暂时不可用. 请稍后重试。"
|
gotInvalidResponseErrorDescription: "您的网络连接可能出现了问题, 或是远程服务器暂时不可用. 请稍后重试。"
|
||||||
|
thisPostMayBeAnnoying: "这个帖子可能会让其他人感到困扰。"
|
||||||
|
thisPostMayBeAnnoyingHome: "发到首页"
|
||||||
|
thisPostMayBeAnnoyingCancel: "取消"
|
||||||
|
thisPostMayBeAnnoyingIgnore: "就这样发布"
|
||||||
_achievements:
|
_achievements:
|
||||||
earnedAt: "达成时间"
|
earnedAt: "达成时间"
|
||||||
_types:
|
_types:
|
||||||
|
@ -467,6 +467,8 @@ youHaveNoGroups: "找不到群組"
|
|||||||
joinOrCreateGroup: "請加入現有群組,或創建新群組。"
|
joinOrCreateGroup: "請加入現有群組,或創建新群組。"
|
||||||
noHistory: "沒有歷史紀錄"
|
noHistory: "沒有歷史紀錄"
|
||||||
signinHistory: "登入歷史"
|
signinHistory: "登入歷史"
|
||||||
|
enableAdvancedMfm: "啟用高級MFM"
|
||||||
|
enableAnimatedMfm: "啟用MFM動畫"
|
||||||
doing: "正在進行"
|
doing: "正在進行"
|
||||||
category: "類別"
|
category: "類別"
|
||||||
tags: "標籤"
|
tags: "標籤"
|
||||||
@ -945,6 +947,11 @@ selectFromPresets: "從預設值中選擇"
|
|||||||
achievements: "成就"
|
achievements: "成就"
|
||||||
gotInvalidResponseError: "伺服器的回應無效"
|
gotInvalidResponseError: "伺服器的回應無效"
|
||||||
gotInvalidResponseErrorDescription: "伺服器可能已關閉或者在維護中,請稍後再試。"
|
gotInvalidResponseErrorDescription: "伺服器可能已關閉或者在維護中,請稍後再試。"
|
||||||
|
thisPostMayBeAnnoying: "這篇貼文可能會造成別人的困擾。"
|
||||||
|
thisPostMayBeAnnoyingHome: "發布到首頁"
|
||||||
|
thisPostMayBeAnnoyingCancel: "退出"
|
||||||
|
thisPostMayBeAnnoyingIgnore: "直接發布貼文"
|
||||||
|
collapseRenotes: "省略顯示已看過的轉發貼文"
|
||||||
_achievements:
|
_achievements:
|
||||||
earnedAt: "獲得日期"
|
earnedAt: "獲得日期"
|
||||||
_types:
|
_types:
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"version": "13.6.0-simkey",
|
"version": "13.6.1-simkey",
|
||||||
"codename": "nasubi",
|
"codename": "nasubi",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
// https://github.com/facebook/jest/issues/12270#issuecomment-1194746382
|
|
||||||
|
|
||||||
const nativeModule = require('node:module');
|
|
||||||
|
|
||||||
function resolver(module, options) {
|
|
||||||
const { basedir, defaultResolver } = options;
|
|
||||||
try {
|
|
||||||
return defaultResolver(module, options);
|
|
||||||
} catch (error) {
|
|
||||||
return nativeModule.createRequire(basedir).resolve(module);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = resolver;
|
|
@ -83,7 +83,14 @@ module.exports = {
|
|||||||
|
|
||||||
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
|
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
|
||||||
moduleNameMapper: {
|
moduleNameMapper: {
|
||||||
"^@/(.*?).js": "<rootDir>/src/$1.ts",
|
// Do not resolve .wasm.js to .wasm by the rule below
|
||||||
|
'^(.+)\\.wasm\\.js$': '$1.wasm.js',
|
||||||
|
// SWC converts @/foo/bar.js to `../../src/foo/bar.js`, and then this rule
|
||||||
|
// converts it again to `../../src/foo/bar` which then can be resolved to
|
||||||
|
// `.ts` files.
|
||||||
|
// See https://github.com/swc-project/jest/issues/64#issuecomment-1029753225
|
||||||
|
// TODO: Use `--allowImportingTsExtensions` on TypeScript 5.0 so that we can
|
||||||
|
// directly import `.ts` files without this hack.
|
||||||
'^(\\.{1,2}/.*)\\.js$': '$1',
|
'^(\\.{1,2}/.*)\\.js$': '$1',
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -112,7 +119,7 @@ module.exports = {
|
|||||||
// resetModules: false,
|
// resetModules: false,
|
||||||
|
|
||||||
// A path to a custom resolver
|
// A path to a custom resolver
|
||||||
resolver: './jest-resolver.cjs',
|
// resolver: './jest-resolver.cjs',
|
||||||
|
|
||||||
// Automatically restore mock state between every test
|
// Automatically restore mock state between every test
|
||||||
restoreMocks: true,
|
restoreMocks: true,
|
||||||
|
47
packages/backend/migration/1676434944993-drop-group.js
Normal file
47
packages/backend/migration/1676434944993-drop-group.js
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
export class dropGroup1676434944993 {
|
||||||
|
name = 'dropGroup1676434944993'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "antenna" DROP CONSTRAINT "FK_ccbf5a8c0be4511133dcc50ddeb"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "notification" DROP CONSTRAINT "FK_8fe87814e978053a53b1beb7e98"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "userGroupJoiningId"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "notification" DROP COLUMN "userGroupInvitationId"`);
|
||||||
|
await queryRunner.query(`ALTER TYPE "public"."antenna_src_enum" RENAME TO "antenna_src_enum_old"`);
|
||||||
|
await queryRunner.query(`CREATE TYPE "public"."antenna_src_enum" AS ENUM('home', 'all', 'users', 'list')`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "antenna" ALTER COLUMN "src" TYPE "public"."antenna_src_enum" USING "src"::"text"::"public"."antenna_src_enum"`);
|
||||||
|
await queryRunner.query(`DROP TYPE "public"."antenna_src_enum_old"`);
|
||||||
|
await queryRunner.query(`ALTER TYPE "public"."notification_type_enum" RENAME TO "notification_type_enum_old"`);
|
||||||
|
await queryRunner.query(`CREATE TYPE "public"."notification_type_enum" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'achievementEarned', 'app')`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "notification" ALTER COLUMN "type" TYPE "public"."notification_type_enum" USING "type"::"text"::"public"."notification_type_enum"`);
|
||||||
|
await queryRunner.query(`DROP TYPE "public"."notification_type_enum_old"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "emailNotificationTypes" SET DEFAULT '["follow","receiveFollowRequest"]'`);
|
||||||
|
await queryRunner.query(`ALTER TYPE "public"."user_profile_mutingnotificationtypes_enum" RENAME TO "user_profile_mutingnotificationtypes_enum_old"`);
|
||||||
|
await queryRunner.query(`CREATE TYPE "public"."user_profile_mutingnotificationtypes_enum" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'achievementEarned', 'app')`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" DROP DEFAULT`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" TYPE "public"."user_profile_mutingnotificationtypes_enum"[] USING "mutingNotificationTypes"::"text"::"public"."user_profile_mutingnotificationtypes_enum"[]`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" SET DEFAULT '{}'`);
|
||||||
|
await queryRunner.query(`DROP TYPE "public"."user_profile_mutingnotificationtypes_enum_old"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`CREATE TYPE "public"."user_profile_mutingnotificationtypes_enum_old" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'achievementEarned', 'app')`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" DROP DEFAULT`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" TYPE "public"."user_profile_mutingnotificationtypes_enum_old"[] USING "mutingNotificationTypes"::"text"::"public"."user_profile_mutingnotificationtypes_enum_old"[]`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" SET DEFAULT '{}'`);
|
||||||
|
await queryRunner.query(`DROP TYPE "public"."user_profile_mutingnotificationtypes_enum"`);
|
||||||
|
await queryRunner.query(`ALTER TYPE "public"."user_profile_mutingnotificationtypes_enum_old" RENAME TO "user_profile_mutingnotificationtypes_enum"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "emailNotificationTypes" SET DEFAULT '["follow", "receiveFollowRequest", "groupInvited"]'`);
|
||||||
|
await queryRunner.query(`CREATE TYPE "public"."notification_type_enum_old" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'achievementEarned', 'app')`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "notification" ALTER COLUMN "type" TYPE "public"."notification_type_enum_old" USING "type"::"text"::"public"."notification_type_enum_old"`);
|
||||||
|
await queryRunner.query(`DROP TYPE "public"."notification_type_enum"`);
|
||||||
|
await queryRunner.query(`ALTER TYPE "public"."notification_type_enum_old" RENAME TO "notification_type_enum"`);
|
||||||
|
await queryRunner.query(`CREATE TYPE "public"."antenna_src_enum_old" AS ENUM('home', 'all', 'users', 'list', 'group')`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "antenna" ALTER COLUMN "src" TYPE "public"."antenna_src_enum_old" USING "src"::"text"::"public"."antenna_src_enum_old"`);
|
||||||
|
await queryRunner.query(`DROP TYPE "public"."antenna_src_enum"`);
|
||||||
|
await queryRunner.query(`ALTER TYPE "public"."antenna_src_enum_old" RENAME TO "antenna_src_enum"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "notification" ADD "userGroupInvitationId" character varying(32)`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "antenna" ADD "userGroupJoiningId" character varying(32)`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "notification" ADD CONSTRAINT "FK_8fe87814e978053a53b1beb7e98" FOREIGN KEY ("userGroupInvitationId") REFERENCES "user_group_invitation"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "antenna" ADD CONSTRAINT "FK_ccbf5a8c0be4511133dcc50ddeb" FOREIGN KEY ("userGroupJoiningId") REFERENCES "user_group_joining"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||||
|
}
|
||||||
|
}
|
9
packages/backend/migration/1676438468213-ad3.js
Normal file
9
packages/backend/migration/1676438468213-ad3.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export class ad1676438468213 {
|
||||||
|
name = 'ad1676438468213';
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "ad" ADD "startsAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
|
||||||
|
}
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "role" DROP COLUMN "startsAt"`);
|
||||||
|
}
|
||||||
|
}
|
@ -109,7 +109,7 @@
|
|||||||
"speakeasy": "2.0.0",
|
"speakeasy": "2.0.0",
|
||||||
"strict-event-emitter-types": "2.0.0",
|
"strict-event-emitter-types": "2.0.0",
|
||||||
"stringz": "2.1.0",
|
"stringz": "2.1.0",
|
||||||
"summaly": "2.7.0",
|
"summaly": "github:misskey-dev/summaly",
|
||||||
"systeminformation": "5.17.8",
|
"systeminformation": "5.17.8",
|
||||||
"tinycolor2": "1.6.0",
|
"tinycolor2": "1.6.0",
|
||||||
"tmp": "0.2.1",
|
"tmp": "0.2.1",
|
||||||
|
@ -67,6 +67,7 @@ export type Source = {
|
|||||||
|
|
||||||
mediaProxy?: string;
|
mediaProxy?: string;
|
||||||
proxyRemoteFiles?: boolean;
|
proxyRemoteFiles?: boolean;
|
||||||
|
videoThumbnailGenerator?: string;
|
||||||
|
|
||||||
signToActivityPubGet?: boolean;
|
signToActivityPubGet?: boolean;
|
||||||
};
|
};
|
||||||
@ -89,6 +90,7 @@ export type Mixin = {
|
|||||||
clientManifestExists: boolean;
|
clientManifestExists: boolean;
|
||||||
mediaProxy: string;
|
mediaProxy: string;
|
||||||
externalMediaProxyEnabled: boolean;
|
externalMediaProxyEnabled: boolean;
|
||||||
|
videoThumbnailGenerator: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Config = Source & Mixin;
|
export type Config = Source & Mixin;
|
||||||
@ -144,6 +146,10 @@ export function loadConfig() {
|
|||||||
mixin.mediaProxy = externalMediaProxy ?? internalMediaProxy;
|
mixin.mediaProxy = externalMediaProxy ?? internalMediaProxy;
|
||||||
mixin.externalMediaProxyEnabled = externalMediaProxy !== null && externalMediaProxy !== internalMediaProxy;
|
mixin.externalMediaProxyEnabled = externalMediaProxy !== null && externalMediaProxy !== internalMediaProxy;
|
||||||
|
|
||||||
|
mixin.videoThumbnailGenerator = config.videoThumbnailGenerator ?
|
||||||
|
config.videoThumbnailGenerator.endsWith('/') ? config.videoThumbnailGenerator.substring(0, config.videoThumbnailGenerator.length - 1) : config.videoThumbnailGenerator
|
||||||
|
: null;
|
||||||
|
|
||||||
if (!config.redis.prefix) config.redis.prefix = mixin.host;
|
if (!config.redis.prefix) config.redis.prefix = mixin.host;
|
||||||
|
|
||||||
return Object.assign(config, mixin);
|
return Object.assign(config, mixin);
|
||||||
|
@ -32,7 +32,7 @@ export class AccountUpdateService {
|
|||||||
|
|
||||||
// フォロワーがリモートユーザーかつ投稿者がローカルユーザーならUpdateを配信
|
// フォロワーがリモートユーザーかつ投稿者がローカルユーザーならUpdateを配信
|
||||||
if (this.userEntityService.isLocalUser(user)) {
|
if (this.userEntityService.isLocalUser(user)) {
|
||||||
const content = this.apRendererService.renderActivity(this.apRendererService.renderUpdate(await this.apRendererService.renderPerson(user), user));
|
const content = this.apRendererService.addContext(this.apRendererService.renderUpdate(await this.apRendererService.renderPerson(user), user));
|
||||||
this.apDeliverManagerService.deliverToFollowers(user, content);
|
this.apDeliverManagerService.deliverToFollowers(user, content);
|
||||||
this.relayService.deliverToRelays(user, content);
|
this.relayService.deliverToRelays(user, content);
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ 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/schema.js';
|
import type { Packed } from '@/misc/schema.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { MutingsRepository, NotesRepository, AntennaNotesRepository, AntennasRepository, UserGroupJoiningsRepository, UserListJoiningsRepository } from '@/models/index.js';
|
import type { MutingsRepository, NotesRepository, AntennaNotesRepository, 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';
|
||||||
@ -39,9 +39,6 @@ export class AntennaService implements OnApplicationShutdown {
|
|||||||
@Inject(DI.antennasRepository)
|
@Inject(DI.antennasRepository)
|
||||||
private antennasRepository: AntennasRepository,
|
private antennasRepository: AntennasRepository,
|
||||||
|
|
||||||
@Inject(DI.userGroupJoiningsRepository)
|
|
||||||
private userGroupJoiningsRepository: UserGroupJoiningsRepository,
|
|
||||||
|
|
||||||
@Inject(DI.userListJoiningsRepository)
|
@Inject(DI.userListJoiningsRepository)
|
||||||
private userListJoiningsRepository: UserListJoiningsRepository,
|
private userListJoiningsRepository: UserListJoiningsRepository,
|
||||||
|
|
||||||
@ -160,14 +157,6 @@ export class AntennaService implements OnApplicationShutdown {
|
|||||||
})).map(x => x.userId);
|
})).map(x => x.userId);
|
||||||
|
|
||||||
if (!listUsers.includes(note.userId)) return false;
|
if (!listUsers.includes(note.userId)) return false;
|
||||||
} else if (antenna.src === 'group') {
|
|
||||||
const joining = await this.userGroupJoiningsRepository.findOneByOrFail({ id: antenna.userGroupJoiningId! });
|
|
||||||
|
|
||||||
const groupUsers = (await this.userGroupJoiningsRepository.findBy({
|
|
||||||
userGroupId: joining.userGroupId,
|
|
||||||
})).map(x => x.userId);
|
|
||||||
|
|
||||||
if (!groupUsers.includes(note.userId)) return false;
|
|
||||||
} else if (antenna.src === 'users') {
|
} else if (antenna.src === 'users') {
|
||||||
const accts = antenna.users.map(x => {
|
const accts = antenna.users.map(x => {
|
||||||
const { username, host } = Acct.parse(x);
|
const { username, host } = Acct.parse(x);
|
||||||
|
@ -22,7 +22,6 @@ import { IdService } from './IdService.js';
|
|||||||
import { ImageProcessingService } from './ImageProcessingService.js';
|
import { ImageProcessingService } from './ImageProcessingService.js';
|
||||||
import { InstanceActorService } from './InstanceActorService.js';
|
import { InstanceActorService } from './InstanceActorService.js';
|
||||||
import { InternalStorageService } from './InternalStorageService.js';
|
import { InternalStorageService } from './InternalStorageService.js';
|
||||||
import { MessagingService } from './MessagingService.js';
|
|
||||||
import { MetaService } from './MetaService.js';
|
import { MetaService } from './MetaService.js';
|
||||||
import { MfmService } from './MfmService.js';
|
import { MfmService } from './MfmService.js';
|
||||||
import { ModerationLogService } from './ModerationLogService.js';
|
import { ModerationLogService } from './ModerationLogService.js';
|
||||||
@ -82,7 +81,6 @@ import { GalleryLikeEntityService } from './entities/GalleryLikeEntityService.js
|
|||||||
import { GalleryPostEntityService } from './entities/GalleryPostEntityService.js';
|
import { GalleryPostEntityService } from './entities/GalleryPostEntityService.js';
|
||||||
import { HashtagEntityService } from './entities/HashtagEntityService.js';
|
import { HashtagEntityService } from './entities/HashtagEntityService.js';
|
||||||
import { InstanceEntityService } from './entities/InstanceEntityService.js';
|
import { InstanceEntityService } from './entities/InstanceEntityService.js';
|
||||||
import { MessagingMessageEntityService } from './entities/MessagingMessageEntityService.js';
|
|
||||||
import { ModerationLogEntityService } from './entities/ModerationLogEntityService.js';
|
import { ModerationLogEntityService } from './entities/ModerationLogEntityService.js';
|
||||||
import { MutingEntityService } from './entities/MutingEntityService.js';
|
import { MutingEntityService } from './entities/MutingEntityService.js';
|
||||||
import { NoteEntityService } from './entities/NoteEntityService.js';
|
import { NoteEntityService } from './entities/NoteEntityService.js';
|
||||||
@ -93,8 +91,6 @@ import { PageEntityService } from './entities/PageEntityService.js';
|
|||||||
import { PageLikeEntityService } from './entities/PageLikeEntityService.js';
|
import { PageLikeEntityService } from './entities/PageLikeEntityService.js';
|
||||||
import { SigninEntityService } from './entities/SigninEntityService.js';
|
import { SigninEntityService } from './entities/SigninEntityService.js';
|
||||||
import { UserEntityService } from './entities/UserEntityService.js';
|
import { UserEntityService } from './entities/UserEntityService.js';
|
||||||
import { UserGroupEntityService } from './entities/UserGroupEntityService.js';
|
|
||||||
import { UserGroupInvitationEntityService } from './entities/UserGroupInvitationEntityService.js';
|
|
||||||
import { UserListEntityService } from './entities/UserListEntityService.js';
|
import { UserListEntityService } from './entities/UserListEntityService.js';
|
||||||
import { FlashEntityService } from './entities/FlashEntityService.js';
|
import { FlashEntityService } from './entities/FlashEntityService.js';
|
||||||
import { FlashLikeEntityService } from './entities/FlashLikeEntityService.js';
|
import { FlashLikeEntityService } from './entities/FlashLikeEntityService.js';
|
||||||
@ -146,7 +142,6 @@ const $IdService: Provider = { provide: 'IdService', useExisting: IdService };
|
|||||||
const $ImageProcessingService: Provider = { provide: 'ImageProcessingService', useExisting: ImageProcessingService };
|
const $ImageProcessingService: Provider = { provide: 'ImageProcessingService', useExisting: ImageProcessingService };
|
||||||
const $InstanceActorService: Provider = { provide: 'InstanceActorService', useExisting: InstanceActorService };
|
const $InstanceActorService: Provider = { provide: 'InstanceActorService', useExisting: InstanceActorService };
|
||||||
const $InternalStorageService: Provider = { provide: 'InternalStorageService', useExisting: InternalStorageService };
|
const $InternalStorageService: Provider = { provide: 'InternalStorageService', useExisting: InternalStorageService };
|
||||||
const $MessagingService: Provider = { provide: 'MessagingService', useExisting: MessagingService };
|
|
||||||
const $MetaService: Provider = { provide: 'MetaService', useExisting: MetaService };
|
const $MetaService: Provider = { provide: 'MetaService', useExisting: MetaService };
|
||||||
const $MfmService: Provider = { provide: 'MfmService', useExisting: MfmService };
|
const $MfmService: Provider = { provide: 'MfmService', useExisting: MfmService };
|
||||||
const $ModerationLogService: Provider = { provide: 'ModerationLogService', useExisting: ModerationLogService };
|
const $ModerationLogService: Provider = { provide: 'ModerationLogService', useExisting: ModerationLogService };
|
||||||
@ -207,7 +202,6 @@ const $GalleryLikeEntityService: Provider = { provide: 'GalleryLikeEntityService
|
|||||||
const $GalleryPostEntityService: Provider = { provide: 'GalleryPostEntityService', useExisting: GalleryPostEntityService };
|
const $GalleryPostEntityService: Provider = { provide: 'GalleryPostEntityService', useExisting: GalleryPostEntityService };
|
||||||
const $HashtagEntityService: Provider = { provide: 'HashtagEntityService', useExisting: HashtagEntityService };
|
const $HashtagEntityService: Provider = { provide: 'HashtagEntityService', useExisting: HashtagEntityService };
|
||||||
const $InstanceEntityService: Provider = { provide: 'InstanceEntityService', useExisting: InstanceEntityService };
|
const $InstanceEntityService: Provider = { provide: 'InstanceEntityService', useExisting: InstanceEntityService };
|
||||||
const $MessagingMessageEntityService: Provider = { provide: 'MessagingMessageEntityService', useExisting: MessagingMessageEntityService };
|
|
||||||
const $ModerationLogEntityService: Provider = { provide: 'ModerationLogEntityService', useExisting: ModerationLogEntityService };
|
const $ModerationLogEntityService: Provider = { provide: 'ModerationLogEntityService', useExisting: ModerationLogEntityService };
|
||||||
const $MutingEntityService: Provider = { provide: 'MutingEntityService', useExisting: MutingEntityService };
|
const $MutingEntityService: Provider = { provide: 'MutingEntityService', useExisting: MutingEntityService };
|
||||||
const $NoteEntityService: Provider = { provide: 'NoteEntityService', useExisting: NoteEntityService };
|
const $NoteEntityService: Provider = { provide: 'NoteEntityService', useExisting: NoteEntityService };
|
||||||
@ -218,8 +212,6 @@ const $PageEntityService: Provider = { provide: 'PageEntityService', useExisting
|
|||||||
const $PageLikeEntityService: Provider = { provide: 'PageLikeEntityService', useExisting: PageLikeEntityService };
|
const $PageLikeEntityService: Provider = { provide: 'PageLikeEntityService', useExisting: PageLikeEntityService };
|
||||||
const $SigninEntityService: Provider = { provide: 'SigninEntityService', useExisting: SigninEntityService };
|
const $SigninEntityService: Provider = { provide: 'SigninEntityService', useExisting: SigninEntityService };
|
||||||
const $UserEntityService: Provider = { provide: 'UserEntityService', useExisting: UserEntityService };
|
const $UserEntityService: Provider = { provide: 'UserEntityService', useExisting: UserEntityService };
|
||||||
const $UserGroupEntityService: Provider = { provide: 'UserGroupEntityService', useExisting: UserGroupEntityService };
|
|
||||||
const $UserGroupInvitationEntityService: Provider = { provide: 'UserGroupInvitationEntityService', useExisting: UserGroupInvitationEntityService };
|
|
||||||
const $UserListEntityService: Provider = { provide: 'UserListEntityService', useExisting: UserListEntityService };
|
const $UserListEntityService: Provider = { provide: 'UserListEntityService', useExisting: UserListEntityService };
|
||||||
const $FlashEntityService: Provider = { provide: 'FlashEntityService', useExisting: FlashEntityService };
|
const $FlashEntityService: Provider = { provide: 'FlashEntityService', useExisting: FlashEntityService };
|
||||||
const $FlashLikeEntityService: Provider = { provide: 'FlashLikeEntityService', useExisting: FlashLikeEntityService };
|
const $FlashLikeEntityService: Provider = { provide: 'FlashLikeEntityService', useExisting: FlashLikeEntityService };
|
||||||
@ -273,7 +265,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||||||
ImageProcessingService,
|
ImageProcessingService,
|
||||||
InstanceActorService,
|
InstanceActorService,
|
||||||
InternalStorageService,
|
InternalStorageService,
|
||||||
MessagingService,
|
|
||||||
MetaService,
|
MetaService,
|
||||||
MfmService,
|
MfmService,
|
||||||
ModerationLogService,
|
ModerationLogService,
|
||||||
@ -333,7 +324,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||||||
GalleryPostEntityService,
|
GalleryPostEntityService,
|
||||||
HashtagEntityService,
|
HashtagEntityService,
|
||||||
InstanceEntityService,
|
InstanceEntityService,
|
||||||
MessagingMessageEntityService,
|
|
||||||
ModerationLogEntityService,
|
ModerationLogEntityService,
|
||||||
MutingEntityService,
|
MutingEntityService,
|
||||||
NoteEntityService,
|
NoteEntityService,
|
||||||
@ -344,8 +334,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||||||
PageLikeEntityService,
|
PageLikeEntityService,
|
||||||
SigninEntityService,
|
SigninEntityService,
|
||||||
UserEntityService,
|
UserEntityService,
|
||||||
UserGroupEntityService,
|
|
||||||
UserGroupInvitationEntityService,
|
|
||||||
UserListEntityService,
|
UserListEntityService,
|
||||||
FlashEntityService,
|
FlashEntityService,
|
||||||
FlashLikeEntityService,
|
FlashLikeEntityService,
|
||||||
@ -394,7 +382,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||||||
$ImageProcessingService,
|
$ImageProcessingService,
|
||||||
$InstanceActorService,
|
$InstanceActorService,
|
||||||
$InternalStorageService,
|
$InternalStorageService,
|
||||||
$MessagingService,
|
|
||||||
$MetaService,
|
$MetaService,
|
||||||
$MfmService,
|
$MfmService,
|
||||||
$ModerationLogService,
|
$ModerationLogService,
|
||||||
@ -454,7 +441,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||||||
$GalleryPostEntityService,
|
$GalleryPostEntityService,
|
||||||
$HashtagEntityService,
|
$HashtagEntityService,
|
||||||
$InstanceEntityService,
|
$InstanceEntityService,
|
||||||
$MessagingMessageEntityService,
|
|
||||||
$ModerationLogEntityService,
|
$ModerationLogEntityService,
|
||||||
$MutingEntityService,
|
$MutingEntityService,
|
||||||
$NoteEntityService,
|
$NoteEntityService,
|
||||||
@ -465,8 +451,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||||||
$PageLikeEntityService,
|
$PageLikeEntityService,
|
||||||
$SigninEntityService,
|
$SigninEntityService,
|
||||||
$UserEntityService,
|
$UserEntityService,
|
||||||
$UserGroupEntityService,
|
|
||||||
$UserGroupInvitationEntityService,
|
|
||||||
$UserListEntityService,
|
$UserListEntityService,
|
||||||
$FlashEntityService,
|
$FlashEntityService,
|
||||||
$FlashLikeEntityService,
|
$FlashLikeEntityService,
|
||||||
@ -516,7 +500,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||||||
ImageProcessingService,
|
ImageProcessingService,
|
||||||
InstanceActorService,
|
InstanceActorService,
|
||||||
InternalStorageService,
|
InternalStorageService,
|
||||||
MessagingService,
|
|
||||||
MetaService,
|
MetaService,
|
||||||
MfmService,
|
MfmService,
|
||||||
ModerationLogService,
|
ModerationLogService,
|
||||||
@ -575,7 +558,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||||||
GalleryPostEntityService,
|
GalleryPostEntityService,
|
||||||
HashtagEntityService,
|
HashtagEntityService,
|
||||||
InstanceEntityService,
|
InstanceEntityService,
|
||||||
MessagingMessageEntityService,
|
|
||||||
ModerationLogEntityService,
|
ModerationLogEntityService,
|
||||||
MutingEntityService,
|
MutingEntityService,
|
||||||
NoteEntityService,
|
NoteEntityService,
|
||||||
@ -586,8 +568,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||||||
PageLikeEntityService,
|
PageLikeEntityService,
|
||||||
SigninEntityService,
|
SigninEntityService,
|
||||||
UserEntityService,
|
UserEntityService,
|
||||||
UserGroupEntityService,
|
|
||||||
UserGroupInvitationEntityService,
|
|
||||||
UserListEntityService,
|
UserListEntityService,
|
||||||
FlashEntityService,
|
FlashEntityService,
|
||||||
FlashLikeEntityService,
|
FlashLikeEntityService,
|
||||||
@ -636,7 +616,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||||||
$ImageProcessingService,
|
$ImageProcessingService,
|
||||||
$InstanceActorService,
|
$InstanceActorService,
|
||||||
$InternalStorageService,
|
$InternalStorageService,
|
||||||
$MessagingService,
|
|
||||||
$MetaService,
|
$MetaService,
|
||||||
$MfmService,
|
$MfmService,
|
||||||
$ModerationLogService,
|
$ModerationLogService,
|
||||||
@ -695,7 +674,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||||||
$GalleryPostEntityService,
|
$GalleryPostEntityService,
|
||||||
$HashtagEntityService,
|
$HashtagEntityService,
|
||||||
$InstanceEntityService,
|
$InstanceEntityService,
|
||||||
$MessagingMessageEntityService,
|
|
||||||
$ModerationLogEntityService,
|
$ModerationLogEntityService,
|
||||||
$MutingEntityService,
|
$MutingEntityService,
|
||||||
$NoteEntityService,
|
$NoteEntityService,
|
||||||
@ -706,8 +684,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||||||
$PageLikeEntityService,
|
$PageLikeEntityService,
|
||||||
$SigninEntityService,
|
$SigninEntityService,
|
||||||
$UserEntityService,
|
$UserEntityService,
|
||||||
$UserGroupEntityService,
|
|
||||||
$UserGroupInvitationEntityService,
|
|
||||||
$UserListEntityService,
|
$UserListEntityService,
|
||||||
$FlashEntityService,
|
$FlashEntityService,
|
||||||
$FlashLikeEntityService,
|
$FlashLikeEntityService,
|
||||||
|
@ -7,7 +7,7 @@ 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 { IRemoteUser, User } from '@/models/entities/User.js';
|
import type { RemoteUser, User } 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 { DriveFile } from '@/models/entities/DriveFile.js';
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
@ -250,6 +250,14 @@ export class DriveService {
|
|||||||
@bindThis
|
@bindThis
|
||||||
public async generateAlts(path: string, type: string, generateWeb: boolean) {
|
public async generateAlts(path: string, type: string, generateWeb: boolean) {
|
||||||
if (type.startsWith('video/')) {
|
if (type.startsWith('video/')) {
|
||||||
|
if (this.config.videoThumbnailGenerator != null) {
|
||||||
|
// videoThumbnailGeneratorが指定されていたら動画サムネイル生成はスキップ
|
||||||
|
return {
|
||||||
|
webpublic: null,
|
||||||
|
thumbnail: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const thumbnail = await this.videoProcessingService.generateVideoThumbnail(path);
|
const thumbnail = await this.videoProcessingService.generateVideoThumbnail(path);
|
||||||
return {
|
return {
|
||||||
@ -391,7 +399,7 @@ export class DriveService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async deleteOldFile(user: IRemoteUser) {
|
private async deleteOldFile(user: RemoteUser) {
|
||||||
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');
|
||||||
@ -492,7 +500,7 @@ export class DriveService {
|
|||||||
throw new IdentifiableError('c6244ed2-a39a-4e1c-bf93-f0fbd7764fa6', 'No free space.');
|
throw new IdentifiableError('c6244ed2-a39a-4e1c-bf93-f0fbd7764fa6', 'No free space.');
|
||||||
} else {
|
} else {
|
||||||
// (アバターまたはバナーを含まず)最も古いファイルを削除する
|
// (アバターまたはバナーを含まず)最も古いファイルを削除する
|
||||||
this.deleteOldFile(await this.usersRepository.findOneByOrFail({ id: user.id }) as IRemoteUser);
|
this.deleteOldFile(await this.usersRepository.findOneByOrFail({ id: user.id }) as RemoteUser);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ import Redis from 'ioredis';
|
|||||||
import type { User } from '@/models/entities/User.js';
|
import type { User } from '@/models/entities/User.js';
|
||||||
import type { Note } from '@/models/entities/Note.js';
|
import type { Note } from '@/models/entities/Note.js';
|
||||||
import type { UserList } from '@/models/entities/UserList.js';
|
import type { UserList } from '@/models/entities/UserList.js';
|
||||||
import type { UserGroup } from '@/models/entities/UserGroup.js';
|
|
||||||
import type { Antenna } from '@/models/entities/Antenna.js';
|
import type { Antenna } from '@/models/entities/Antenna.js';
|
||||||
import type { Channel } from '@/models/entities/Channel.js';
|
import type { Channel } from '@/models/entities/Channel.js';
|
||||||
import type {
|
import type {
|
||||||
@ -11,13 +10,9 @@ import type {
|
|||||||
AdminStreamTypes,
|
AdminStreamTypes,
|
||||||
AntennaStreamTypes,
|
AntennaStreamTypes,
|
||||||
BroadcastTypes,
|
BroadcastTypes,
|
||||||
ChannelStreamTypes,
|
|
||||||
DriveStreamTypes,
|
DriveStreamTypes,
|
||||||
GroupMessagingStreamTypes,
|
|
||||||
InternalStreamTypes,
|
InternalStreamTypes,
|
||||||
MainStreamTypes,
|
MainStreamTypes,
|
||||||
MessagingIndexStreamTypes,
|
|
||||||
MessagingStreamTypes,
|
|
||||||
NoteStreamTypes,
|
NoteStreamTypes,
|
||||||
UserListStreamTypes,
|
UserListStreamTypes,
|
||||||
UserStreamTypes,
|
UserStreamTypes,
|
||||||
@ -83,11 +78,6 @@ export class GlobalEventService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
|
||||||
public publishChannelStream<K extends keyof ChannelStreamTypes>(channelId: Channel['id'], type: K, value?: ChannelStreamTypes[K]): void {
|
|
||||||
this.publish(`channelStream:${channelId}`, type, typeof value === 'undefined' ? null : value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public publishUserListStream<K extends keyof UserListStreamTypes>(listId: UserList['id'], type: K, value?: UserListStreamTypes[K]): void {
|
public publishUserListStream<K extends keyof UserListStreamTypes>(listId: UserList['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);
|
||||||
@ -98,21 +88,6 @@ export class GlobalEventService {
|
|||||||
this.publish(`antennaStream:${antennaId}`, type, typeof value === 'undefined' ? null : value);
|
this.publish(`antennaStream:${antennaId}`, type, typeof value === 'undefined' ? null : value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
|
||||||
public publishMessagingStream<K extends keyof MessagingStreamTypes>(userId: User['id'], otherpartyId: User['id'], type: K, value?: MessagingStreamTypes[K]): void {
|
|
||||||
this.publish(`messagingStream:${userId}-${otherpartyId}`, type, typeof value === 'undefined' ? null : value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@bindThis
|
|
||||||
public publishGroupMessagingStream<K extends keyof GroupMessagingStreamTypes>(groupId: UserGroup['id'], type: K, value?: GroupMessagingStreamTypes[K]): void {
|
|
||||||
this.publish(`messagingStream:${groupId}`, type, typeof value === 'undefined' ? null : value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@bindThis
|
|
||||||
public publishMessagingIndexStream<K extends keyof MessagingIndexStreamTypes>(userId: User['id'], type: K, value?: MessagingIndexStreamTypes[K]): void {
|
|
||||||
this.publish(`messagingIndexStream:${userId}`, type, typeof value === 'undefined' ? null : value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public publishNotesStream(note: Packed<'Note'>): void {
|
public publishNotesStream(note: Packed<'Note'>): void {
|
||||||
this.publish('notesStream', null, note);
|
this.publish('notesStream', null, note);
|
||||||
|
@ -99,7 +99,6 @@ export class HttpRequestService {
|
|||||||
const res = await this.send(url, {
|
const res = await this.send(url, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: Object.assign({
|
headers: Object.assign({
|
||||||
'User-Agent': this.config.userAgent,
|
|
||||||
Accept: accept,
|
Accept: accept,
|
||||||
}, headers ?? {}),
|
}, headers ?? {}),
|
||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
@ -114,7 +113,6 @@ export class HttpRequestService {
|
|||||||
const res = await this.send(url, {
|
const res = await this.send(url, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: Object.assign({
|
headers: Object.assign({
|
||||||
'User-Agent': this.config.userAgent,
|
|
||||||
Accept: accept,
|
Accept: accept,
|
||||||
}, headers ?? {}),
|
}, headers ?? {}),
|
||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
@ -144,7 +142,10 @@ export class HttpRequestService {
|
|||||||
|
|
||||||
const res = await fetch(url, {
|
const res = await fetch(url, {
|
||||||
method: args.method ?? 'GET',
|
method: args.method ?? 'GET',
|
||||||
headers: args.headers,
|
headers: {
|
||||||
|
'User-Agent': this.config.userAgent,
|
||||||
|
...(args.headers ?? {})
|
||||||
|
},
|
||||||
body: args.body,
|
body: args.body,
|
||||||
size: args.size ?? 10 * 1024 * 1024,
|
size: args.size ?? 10 * 1024 * 1024,
|
||||||
agent: (url) => this.getAgentByUrl(url),
|
agent: (url) => this.getAgentByUrl(url),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { IsNull } from 'typeorm';
|
import { IsNull } from 'typeorm';
|
||||||
import type { ILocalUser } from '@/models/entities/User.js';
|
import type { LocalUser } from '@/models/entities/User.js';
|
||||||
import type { UsersRepository } from '@/models/index.js';
|
import type { UsersRepository } from '@/models/index.js';
|
||||||
import { Cache } from '@/misc/cache.js';
|
import { Cache } from '@/misc/cache.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
@ -11,7 +11,7 @@ const ACTOR_USERNAME = 'instance.actor' as const;
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class InstanceActorService {
|
export class InstanceActorService {
|
||||||
private cache: Cache<ILocalUser>;
|
private cache: Cache<LocalUser>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.usersRepository)
|
@Inject(DI.usersRepository)
|
||||||
@ -19,24 +19,24 @@ export class InstanceActorService {
|
|||||||
|
|
||||||
private createSystemUserService: CreateSystemUserService,
|
private createSystemUserService: CreateSystemUserService,
|
||||||
) {
|
) {
|
||||||
this.cache = new Cache<ILocalUser>(Infinity);
|
this.cache = new Cache<LocalUser>(Infinity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async getInstanceActor(): Promise<ILocalUser> {
|
public async getInstanceActor(): Promise<LocalUser> {
|
||||||
const cached = this.cache.get(null);
|
const cached = this.cache.get(null);
|
||||||
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 ILocalUser | undefined;
|
}) as LocalUser | undefined;
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
this.cache.set(null, user);
|
this.cache.set(null, user);
|
||||||
return user;
|
return user;
|
||||||
} else {
|
} else {
|
||||||
const created = await this.createSystemUserService.createSystemUser(ACTOR_USERNAME) as ILocalUser;
|
const created = await this.createSystemUserService.createSystemUser(ACTOR_USERNAME) as LocalUser;
|
||||||
this.cache.set(null, created);
|
this.cache.set(null, created);
|
||||||
return created;
|
return created;
|
||||||
}
|
}
|
||||||
|
@ -1,307 +0,0 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
|
||||||
import { In, Not } from 'typeorm';
|
|
||||||
import { DI } from '@/di-symbols.js';
|
|
||||||
import type { Config } from '@/config.js';
|
|
||||||
import type { DriveFile } from '@/models/entities/DriveFile.js';
|
|
||||||
import type { MessagingMessage } from '@/models/entities/MessagingMessage.js';
|
|
||||||
import type { Note } from '@/models/entities/Note.js';
|
|
||||||
import type { User, CacheableUser, IRemoteUser } from '@/models/entities/User.js';
|
|
||||||
import type { UserGroup } from '@/models/entities/UserGroup.js';
|
|
||||||
import { QueueService } from '@/core/QueueService.js';
|
|
||||||
import { toArray } from '@/misc/prelude/array.js';
|
|
||||||
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
|
||||||
import type { MessagingMessagesRepository, MutingsRepository, UserGroupJoiningsRepository, UsersRepository } from '@/models/index.js';
|
|
||||||
import { IdService } from '@/core/IdService.js';
|
|
||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
|
||||||
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
|
|
||||||
import { MessagingMessageEntityService } from '@/core/entities/MessagingMessageEntityService.js';
|
|
||||||
import { PushNotificationService } from '@/core/PushNotificationService.js';
|
|
||||||
import { bindThis } from '@/decorators.js';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class MessagingService {
|
|
||||||
constructor(
|
|
||||||
@Inject(DI.config)
|
|
||||||
private config: Config,
|
|
||||||
|
|
||||||
@Inject(DI.usersRepository)
|
|
||||||
private usersRepository: UsersRepository,
|
|
||||||
|
|
||||||
@Inject(DI.messagingMessagesRepository)
|
|
||||||
private messagingMessagesRepository: MessagingMessagesRepository,
|
|
||||||
|
|
||||||
@Inject(DI.userGroupJoiningsRepository)
|
|
||||||
private userGroupJoiningsRepository: UserGroupJoiningsRepository,
|
|
||||||
|
|
||||||
@Inject(DI.mutingsRepository)
|
|
||||||
private mutingsRepository: MutingsRepository,
|
|
||||||
|
|
||||||
private userEntityService: UserEntityService,
|
|
||||||
private messagingMessageEntityService: MessagingMessageEntityService,
|
|
||||||
private idService: IdService,
|
|
||||||
private globalEventService: GlobalEventService,
|
|
||||||
private apRendererService: ApRendererService,
|
|
||||||
private queueService: QueueService,
|
|
||||||
private pushNotificationService: PushNotificationService,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@bindThis
|
|
||||||
public async createMessage(user: { id: User['id']; host: User['host']; }, recipientUser: CacheableUser | undefined, recipientGroup: UserGroup | undefined, text: string | null | undefined, file: DriveFile | null, uri?: string) {
|
|
||||||
const message = {
|
|
||||||
id: this.idService.genId(),
|
|
||||||
createdAt: new Date(),
|
|
||||||
fileId: file ? file.id : null,
|
|
||||||
recipientId: recipientUser ? recipientUser.id : null,
|
|
||||||
groupId: recipientGroup ? recipientGroup.id : null,
|
|
||||||
text: text ? text.trim() : null,
|
|
||||||
userId: user.id,
|
|
||||||
isRead: false,
|
|
||||||
reads: [] as any[],
|
|
||||||
uri,
|
|
||||||
} as MessagingMessage;
|
|
||||||
|
|
||||||
await this.messagingMessagesRepository.insert(message);
|
|
||||||
|
|
||||||
const messageObj = await this.messagingMessageEntityService.pack(message);
|
|
||||||
|
|
||||||
if (recipientUser) {
|
|
||||||
if (this.userEntityService.isLocalUser(user)) {
|
|
||||||
// 自分のストリーム
|
|
||||||
this.globalEventService.publishMessagingStream(message.userId, recipientUser.id, 'message', messageObj);
|
|
||||||
this.globalEventService.publishMessagingIndexStream(message.userId, 'message', messageObj);
|
|
||||||
this.globalEventService.publishMainStream(message.userId, 'messagingMessage', messageObj);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.userEntityService.isLocalUser(recipientUser)) {
|
|
||||||
// 相手のストリーム
|
|
||||||
this.globalEventService.publishMessagingStream(recipientUser.id, message.userId, 'message', messageObj);
|
|
||||||
this.globalEventService.publishMessagingIndexStream(recipientUser.id, 'message', messageObj);
|
|
||||||
this.globalEventService.publishMainStream(recipientUser.id, 'messagingMessage', messageObj);
|
|
||||||
}
|
|
||||||
} else if (recipientGroup) {
|
|
||||||
// グループのストリーム
|
|
||||||
this.globalEventService.publishGroupMessagingStream(recipientGroup.id, 'message', messageObj);
|
|
||||||
|
|
||||||
// メンバーのストリーム
|
|
||||||
const joinings = await this.userGroupJoiningsRepository.findBy({ userGroupId: recipientGroup.id });
|
|
||||||
for (const joining of joinings) {
|
|
||||||
this.globalEventService.publishMessagingIndexStream(joining.userId, 'message', messageObj);
|
|
||||||
this.globalEventService.publishMainStream(joining.userId, 'messagingMessage', messageObj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2秒経っても(今回作成した)メッセージが既読にならなかったら「未読のメッセージがありますよ」イベントを発行する
|
|
||||||
setTimeout(async () => {
|
|
||||||
const freshMessage = await this.messagingMessagesRepository.findOneBy({ id: message.id });
|
|
||||||
if (freshMessage == null) return; // メッセージが削除されている場合もある
|
|
||||||
|
|
||||||
if (recipientUser && this.userEntityService.isLocalUser(recipientUser)) {
|
|
||||||
if (freshMessage.isRead) return; // 既読
|
|
||||||
|
|
||||||
//#region ただしミュートされているなら発行しない
|
|
||||||
const mute = await this.mutingsRepository.findBy({
|
|
||||||
muterId: recipientUser.id,
|
|
||||||
});
|
|
||||||
if (mute.map(m => m.muteeId).includes(user.id)) return;
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
this.globalEventService.publishMainStream(recipientUser.id, 'unreadMessagingMessage', messageObj);
|
|
||||||
this.pushNotificationService.pushNotification(recipientUser.id, 'unreadMessagingMessage', messageObj);
|
|
||||||
} else if (recipientGroup) {
|
|
||||||
const joinings = await this.userGroupJoiningsRepository.findBy({ userGroupId: recipientGroup.id, userId: Not(user.id) });
|
|
||||||
for (const joining of joinings) {
|
|
||||||
if (freshMessage.reads.includes(joining.userId)) return; // 既読
|
|
||||||
this.globalEventService.publishMainStream(joining.userId, 'unreadMessagingMessage', messageObj);
|
|
||||||
this.pushNotificationService.pushNotification(joining.userId, 'unreadMessagingMessage', messageObj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 2000);
|
|
||||||
|
|
||||||
if (recipientUser && this.userEntityService.isLocalUser(user) && this.userEntityService.isRemoteUser(recipientUser)) {
|
|
||||||
const note = {
|
|
||||||
id: message.id,
|
|
||||||
createdAt: message.createdAt,
|
|
||||||
fileIds: message.fileId ? [message.fileId] : [],
|
|
||||||
text: message.text,
|
|
||||||
userId: message.userId,
|
|
||||||
visibility: 'specified',
|
|
||||||
mentions: [recipientUser].map(u => u.id),
|
|
||||||
mentionedRemoteUsers: JSON.stringify([recipientUser].map(u => ({
|
|
||||||
uri: u.uri,
|
|
||||||
username: u.username,
|
|
||||||
host: u.host,
|
|
||||||
}))),
|
|
||||||
} as Note;
|
|
||||||
|
|
||||||
const activity = this.apRendererService.renderActivity(this.apRendererService.renderCreate(await this.apRendererService.renderNote(note, false, true), note));
|
|
||||||
|
|
||||||
this.queueService.deliver(user, activity, recipientUser.inbox);
|
|
||||||
}
|
|
||||||
return messageObj;
|
|
||||||
}
|
|
||||||
|
|
||||||
@bindThis
|
|
||||||
public async deleteMessage(message: MessagingMessage) {
|
|
||||||
await this.messagingMessagesRepository.delete(message.id);
|
|
||||||
this.postDeleteMessage(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@bindThis
|
|
||||||
private async postDeleteMessage(message: MessagingMessage) {
|
|
||||||
if (message.recipientId) {
|
|
||||||
const user = await this.usersRepository.findOneByOrFail({ id: message.userId });
|
|
||||||
const recipient = await this.usersRepository.findOneByOrFail({ id: message.recipientId });
|
|
||||||
|
|
||||||
if (this.userEntityService.isLocalUser(user)) this.globalEventService.publishMessagingStream(message.userId, message.recipientId, 'deleted', message.id);
|
|
||||||
if (this.userEntityService.isLocalUser(recipient)) this.globalEventService.publishMessagingStream(message.recipientId, message.userId, 'deleted', message.id);
|
|
||||||
|
|
||||||
if (this.userEntityService.isLocalUser(user) && this.userEntityService.isRemoteUser(recipient)) {
|
|
||||||
const activity = this.apRendererService.renderActivity(this.apRendererService.renderDelete(this.apRendererService.renderTombstone(`${this.config.url}/notes/${message.id}`), user));
|
|
||||||
this.queueService.deliver(user, activity, recipient.inbox);
|
|
||||||
}
|
|
||||||
} else if (message.groupId) {
|
|
||||||
this.globalEventService.publishGroupMessagingStream(message.groupId, 'deleted', message.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mark messages as read
|
|
||||||
*/
|
|
||||||
@bindThis
|
|
||||||
public async readUserMessagingMessage(
|
|
||||||
userId: User['id'],
|
|
||||||
otherpartyId: User['id'],
|
|
||||||
messageIds: MessagingMessage['id'][],
|
|
||||||
) {
|
|
||||||
if (messageIds.length === 0) return;
|
|
||||||
|
|
||||||
const messages = await this.messagingMessagesRepository.findBy({
|
|
||||||
id: In(messageIds),
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const message of messages) {
|
|
||||||
if (message.recipientId !== userId) {
|
|
||||||
throw new IdentifiableError('e140a4bf-49ce-4fb6-b67c-b78dadf6b52f', 'Access denied (user).');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update documents
|
|
||||||
await this.messagingMessagesRepository.update({
|
|
||||||
id: In(messageIds),
|
|
||||||
userId: otherpartyId,
|
|
||||||
recipientId: userId,
|
|
||||||
isRead: false,
|
|
||||||
}, {
|
|
||||||
isRead: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Publish event
|
|
||||||
this.globalEventService.publishMessagingStream(otherpartyId, userId, 'read', messageIds);
|
|
||||||
this.globalEventService.publishMessagingIndexStream(userId, 'read', messageIds);
|
|
||||||
|
|
||||||
if (!await this.userEntityService.getHasUnreadMessagingMessage(userId)) {
|
|
||||||
// 全ての(いままで未読だった)自分宛てのメッセージを(これで)読みましたよというイベントを発行
|
|
||||||
this.globalEventService.publishMainStream(userId, 'readAllMessagingMessages');
|
|
||||||
this.pushNotificationService.pushNotification(userId, 'readAllMessagingMessages', undefined);
|
|
||||||
} else {
|
|
||||||
// そのユーザーとのメッセージで未読がなければイベント発行
|
|
||||||
const count = await this.messagingMessagesRepository.count({
|
|
||||||
where: {
|
|
||||||
userId: otherpartyId,
|
|
||||||
recipientId: userId,
|
|
||||||
isRead: false,
|
|
||||||
},
|
|
||||||
take: 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!count) {
|
|
||||||
this.pushNotificationService.pushNotification(userId, 'readAllMessagingMessagesOfARoom', { userId: otherpartyId });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mark messages as read
|
|
||||||
*/
|
|
||||||
@bindThis
|
|
||||||
public async readGroupMessagingMessage(
|
|
||||||
userId: User['id'],
|
|
||||||
groupId: UserGroup['id'],
|
|
||||||
messageIds: MessagingMessage['id'][],
|
|
||||||
) {
|
|
||||||
if (messageIds.length === 0) return;
|
|
||||||
|
|
||||||
// check joined
|
|
||||||
const joining = await this.userGroupJoiningsRepository.findOneBy({
|
|
||||||
userId: userId,
|
|
||||||
userGroupId: groupId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (joining == null) {
|
|
||||||
throw new IdentifiableError('930a270c-714a-46b2-b776-ad27276dc569', 'Access denied (group).');
|
|
||||||
}
|
|
||||||
|
|
||||||
const messages = await this.messagingMessagesRepository.findBy({
|
|
||||||
id: In(messageIds),
|
|
||||||
});
|
|
||||||
|
|
||||||
const reads: MessagingMessage['id'][] = [];
|
|
||||||
|
|
||||||
for (const message of messages) {
|
|
||||||
if (message.userId === userId) continue;
|
|
||||||
if (message.reads.includes(userId)) continue;
|
|
||||||
|
|
||||||
// Update document
|
|
||||||
await this.messagingMessagesRepository.createQueryBuilder().update()
|
|
||||||
.set({
|
|
||||||
reads: (() => `array_append("reads", '${joining.userId}')`) as any,
|
|
||||||
})
|
|
||||||
.where('id = :id', { id: message.id })
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
reads.push(message.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Publish event
|
|
||||||
this.globalEventService.publishGroupMessagingStream(groupId, 'read', {
|
|
||||||
ids: reads,
|
|
||||||
userId: userId,
|
|
||||||
});
|
|
||||||
this.globalEventService.publishMessagingIndexStream(userId, 'read', reads);
|
|
||||||
|
|
||||||
if (!await this.userEntityService.getHasUnreadMessagingMessage(userId)) {
|
|
||||||
// 全ての(いままで未読だった)自分宛てのメッセージを(これで)読みましたよというイベントを発行
|
|
||||||
this.globalEventService.publishMainStream(userId, 'readAllMessagingMessages');
|
|
||||||
this.pushNotificationService.pushNotification(userId, 'readAllMessagingMessages', undefined);
|
|
||||||
} else {
|
|
||||||
// そのグループにおいて未読がなければイベント発行
|
|
||||||
const unreadExist = await this.messagingMessagesRepository.createQueryBuilder('message')
|
|
||||||
.where('message.groupId = :groupId', { groupId: groupId })
|
|
||||||
.andWhere('message.userId != :userId', { userId: userId })
|
|
||||||
.andWhere('NOT (:userId = ANY(message.reads))', { userId: userId })
|
|
||||||
.andWhere('message.createdAt > :joinedAt', { joinedAt: joining.createdAt }) // 自分が加入する前の会話については、未読扱いしない
|
|
||||||
.getOne().then(x => x != null);
|
|
||||||
|
|
||||||
if (!unreadExist) {
|
|
||||||
this.pushNotificationService.pushNotification(userId, 'readAllMessagingMessagesOfARoom', { groupId });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@bindThis
|
|
||||||
public async deliverReadActivity(user: { id: User['id']; host: null; }, recipient: IRemoteUser, messages: MessagingMessage | MessagingMessage[]) {
|
|
||||||
messages = toArray(messages).filter(x => x.uri);
|
|
||||||
const contents = messages.map(x => this.apRendererService.renderRead(user, x));
|
|
||||||
|
|
||||||
if (contents.length > 1) {
|
|
||||||
const collection = this.apRendererService.renderOrderedCollection(null, contents.length, undefined, undefined, contents);
|
|
||||||
this.queueService.deliver(user, this.apRendererService.renderActivity(collection), recipient.inbox);
|
|
||||||
} else {
|
|
||||||
for (const content of contents) {
|
|
||||||
this.queueService.deliver(user, this.apRendererService.renderActivity(content), recipient.inbox);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -11,7 +11,7 @@ import type { DriveFile } from '@/models/entities/DriveFile.js';
|
|||||||
import type { App } 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, ILocalUser, IRemoteUser } from '@/models/entities/User.js';
|
import type { User, LocalUser, RemoteUser } 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 { Poll } 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';
|
||||||
@ -52,7 +52,7 @@ class NotificationManager {
|
|||||||
private notifier: { id: User['id']; };
|
private notifier: { id: User['id']; };
|
||||||
private note: Note;
|
private note: Note;
|
||||||
private queue: {
|
private queue: {
|
||||||
target: ILocalUser['id'];
|
target: LocalUser['id'];
|
||||||
reason: NotificationType;
|
reason: NotificationType;
|
||||||
}[];
|
}[];
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ class NotificationManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public push(notifiee: ILocalUser['id'], reason: NotificationType) {
|
public push(notifiee: LocalUser['id'], reason: NotificationType) {
|
||||||
// 自分自身へは通知しない
|
// 自分自身へは通知しない
|
||||||
if (this.notifier.id === notifiee) return;
|
if (this.notifier.id === notifiee) return;
|
||||||
|
|
||||||
@ -605,7 +605,7 @@ export class NoteCreateService {
|
|||||||
|
|
||||||
// メンションされたリモートユーザーに配送
|
// メンションされたリモートユーザーに配送
|
||||||
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 IRemoteUser);
|
dm.addDirectRecipe(u as RemoteUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 投稿がリプライかつ投稿者がローカルユーザーかつリプライ先の投稿の投稿者がリモートユーザーなら配送
|
// 投稿がリプライかつ投稿者がローカルユーザーかつリプライ先の投稿の投稿者がリモートユーザーなら配送
|
||||||
@ -711,7 +711,7 @@ export class NoteCreateService {
|
|||||||
? this.apRendererService.renderAnnounce(data.renote.uri ? data.renote.uri : `${this.config.url}/notes/${data.renote.id}`, note)
|
? this.apRendererService.renderAnnounce(data.renote.uri ? data.renote.uri : `${this.config.url}/notes/${data.renote.id}`, note)
|
||||||
: this.apRendererService.renderCreate(await this.apRendererService.renderNote(note, false), note);
|
: this.apRendererService.renderCreate(await this.apRendererService.renderNote(note, false), note);
|
||||||
|
|
||||||
return this.apRendererService.renderActivity(content);
|
return this.apRendererService.addContext(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
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, ILocalUser, IRemoteUser } from '@/models/entities/User.js';
|
import type { User, LocalUser, RemoteUser } from '@/models/entities/User.js';
|
||||||
import type { Note, IMentionedRemoteUsers } from '@/models/entities/Note.js';
|
import type { Note, 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';
|
||||||
@ -78,7 +78,7 @@ export class NoteDeleteService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = this.apRendererService.renderActivity(renote
|
const content = this.apRendererService.addContext(renote
|
||||||
? this.apRendererService.renderUndo(this.apRendererService.renderAnnounce(renote.uri ?? `${this.config.url}/notes/${renote.id}`, note), user)
|
? this.apRendererService.renderUndo(this.apRendererService.renderAnnounce(renote.uri ?? `${this.config.url}/notes/${renote.id}`, note), user)
|
||||||
: this.apRendererService.renderDelete(this.apRendererService.renderTombstone(`${this.config.url}/notes/${note.id}`), user));
|
: this.apRendererService.renderDelete(this.apRendererService.renderTombstone(`${this.config.url}/notes/${note.id}`), user));
|
||||||
|
|
||||||
@ -90,7 +90,7 @@ export class NoteDeleteService {
|
|||||||
for (const cascadingNote of cascadingNotes) {
|
for (const cascadingNote of cascadingNotes) {
|
||||||
if (!cascadingNote.user) continue;
|
if (!cascadingNote.user) continue;
|
||||||
if (!this.userEntityService.isLocalUser(cascadingNote.user)) continue;
|
if (!this.userEntityService.isLocalUser(cascadingNote.user)) continue;
|
||||||
const content = this.apRendererService.renderActivity(this.apRendererService.renderDelete(this.apRendererService.renderTombstone(`${this.config.url}/notes/${cascadingNote.id}`), cascadingNote.user));
|
const content = this.apRendererService.addContext(this.apRendererService.renderDelete(this.apRendererService.renderTombstone(`${this.config.url}/notes/${cascadingNote.id}`), cascadingNote.user));
|
||||||
this.deliverToConcerned(cascadingNote.user, cascadingNote, content);
|
this.deliverToConcerned(cascadingNote.user, cascadingNote, content);
|
||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
@ -159,11 +159,11 @@ export class NoteDeleteService {
|
|||||||
|
|
||||||
return await this.usersRepository.find({
|
return await this.usersRepository.find({
|
||||||
where,
|
where,
|
||||||
}) as IRemoteUser[];
|
}) as RemoteUser[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async deliverToConcerned(user: { id: ILocalUser['id']; host: null; }, note: Note, content: any) {
|
private async deliverToConcerned(user: { id: LocalUser['id']; host: null; }, note: Note, 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);
|
||||||
|
@ -115,7 +115,7 @@ export class NotePiningService {
|
|||||||
|
|
||||||
const target = `${this.config.url}/users/${user.id}/collections/featured`;
|
const target = `${this.config.url}/users/${user.id}/collections/featured`;
|
||||||
const item = `${this.config.url}/notes/${noteId}`;
|
const item = `${this.config.url}/notes/${noteId}`;
|
||||||
const content = this.apRendererService.renderActivity(isAddition ? this.apRendererService.renderAdd(user, target, item) : this.apRendererService.renderRemove(user, target, item));
|
const content = this.apRendererService.addContext(isAddition ? this.apRendererService.renderAdd(user, target, item) : this.apRendererService.renderRemove(user, target, item));
|
||||||
|
|
||||||
this.apDeliverManagerService.deliverToFollowers(user, content);
|
this.apDeliverManagerService.deliverToFollowers(user, content);
|
||||||
this.relayService.deliverToRelays(user, content);
|
this.relayService.deliverToRelays(user, content);
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { Not } from 'typeorm';
|
import { Not } from 'typeorm';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { NotesRepository, UsersRepository, PollsRepository, PollVotesRepository } from '@/models/index.js';
|
import type { NotesRepository, UsersRepository, PollsRepository, PollVotesRepository, User } from '@/models/index.js';
|
||||||
import type { Note } from '@/models/entities/Note.js';
|
import type { Note } from '@/models/entities/Note.js';
|
||||||
import { RelayService } from '@/core/RelayService.js';
|
import { RelayService } from '@/core/RelayService.js';
|
||||||
import type { CacheableUser } from '@/models/entities/User.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 { ApRendererService } from '@/core/activitypub/ApRendererService.js';
|
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
|
||||||
@ -39,7 +38,7 @@ export class PollService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async vote(user: CacheableUser, note: Note, choice: number) {
|
public async vote(user: User, note: Note, 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');
|
||||||
@ -97,7 +96,7 @@ export class PollService {
|
|||||||
if (user == null) throw new Error('note not found');
|
if (user == null) throw new Error('note not found');
|
||||||
|
|
||||||
if (this.userEntityService.isLocalUser(user)) {
|
if (this.userEntityService.isLocalUser(user)) {
|
||||||
const content = this.apRendererService.renderActivity(this.apRendererService.renderUpdate(await this.apRendererService.renderNote(note, false), user));
|
const content = this.apRendererService.addContext(this.apRendererService.renderUpdate(await this.apRendererService.renderNote(note, false), user));
|
||||||
this.apDeliverManagerService.deliverToFollowers(user, content);
|
this.apDeliverManagerService.deliverToFollowers(user, content);
|
||||||
this.relayService.deliverToRelays(user, content);
|
this.relayService.deliverToRelays(user, content);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
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 { ILocalUser, User } from '@/models/entities/User.js';
|
import type { LocalUser, User } 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';
|
||||||
@ -16,9 +16,9 @@ export class ProxyAccountService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async fetch(): Promise<ILocalUser | null> {
|
public async fetch(): Promise<LocalUser | 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 ILocalUser;
|
return await this.usersRepository.findOneByOrFail({ id: meta.proxyAccountId }) as LocalUser;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,15 +11,12 @@ import { bindThis } from '@/decorators.js';
|
|||||||
// Defined also packages/sw/types.ts#L13
|
// Defined also packages/sw/types.ts#L13
|
||||||
type pushNotificationsTypes = {
|
type pushNotificationsTypes = {
|
||||||
'notification': Packed<'Notification'>;
|
'notification': Packed<'Notification'>;
|
||||||
'unreadMessagingMessage': Packed<'MessagingMessage'>;
|
|
||||||
'unreadAntennaNote': {
|
'unreadAntennaNote': {
|
||||||
antenna: { id: string, name: string };
|
antenna: { id: string, name: string };
|
||||||
note: Packed<'Note'>;
|
note: Packed<'Note'>;
|
||||||
};
|
};
|
||||||
'readNotifications': { notificationIds: string[] };
|
'readNotifications': { notificationIds: string[] };
|
||||||
'readAllNotifications': undefined;
|
'readAllNotifications': undefined;
|
||||||
'readAllMessagingMessages': undefined;
|
|
||||||
'readAllMessagingMessagesOfARoom': { userId: string } | { groupId: string };
|
|
||||||
'readAntenna': { antennaId: string };
|
'readAntenna': { antennaId: string };
|
||||||
'readAllAntennas': undefined;
|
'readAllAntennas': undefined;
|
||||||
};
|
};
|
||||||
@ -40,11 +37,10 @@ function truncateBody<T extends keyof pushNotificationsTypes>(type: T, body: pus
|
|||||||
reply: undefined,
|
reply: undefined,
|
||||||
renote: undefined,
|
renote: undefined,
|
||||||
user: type === 'notification' ? undefined as any : body.note.user,
|
user: type === 'notification' ? undefined as any : body.note.user,
|
||||||
}
|
},
|
||||||
} : {}),
|
} : {}),
|
||||||
};
|
};
|
||||||
|
|
||||||
return body;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -81,8 +77,6 @@ export class PushNotificationService {
|
|||||||
if ([
|
if ([
|
||||||
'readNotifications',
|
'readNotifications',
|
||||||
'readAllNotifications',
|
'readAllNotifications',
|
||||||
'readAllMessagingMessages',
|
|
||||||
'readAllMessagingMessagesOfARoom',
|
|
||||||
'readAntenna',
|
'readAntenna',
|
||||||
'readAllAntennas',
|
'readAllAntennas',
|
||||||
].includes(type) && !subscription.sendReadMessage) continue;
|
].includes(type) && !subscription.sendReadMessage) continue;
|
||||||
|
@ -3,7 +3,7 @@ import { IsNull } from 'typeorm';
|
|||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { EmojisRepository, BlockingsRepository, NoteReactionsRepository, UsersRepository, NotesRepository } from '@/models/index.js';
|
import type { EmojisRepository, BlockingsRepository, NoteReactionsRepository, UsersRepository, NotesRepository } from '@/models/index.js';
|
||||||
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
||||||
import type { IRemoteUser, User } from '@/models/entities/User.js';
|
import type { RemoteUser, User } from '@/models/entities/User.js';
|
||||||
import type { Note } from '@/models/entities/Note.js';
|
import type { Note } 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 { NoteReaction } from '@/models/entities/NoteReaction.js';
|
||||||
@ -85,7 +85,7 @@ export class ReactionService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async create(user: { id: User['id']; host: User['host']; isBot: User['isBot'] }, note: Note, reaction?: string) {
|
public async create(user: { id: User['id']; host: User['host']; isBot: User['isBot'] }, note: Note, 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);
|
||||||
@ -177,11 +177,11 @@ export class ReactionService {
|
|||||||
|
|
||||||
//#region 配信
|
//#region 配信
|
||||||
if (this.userEntityService.isLocalUser(user) && !note.localOnly) {
|
if (this.userEntityService.isLocalUser(user) && !note.localOnly) {
|
||||||
const content = this.apRendererService.renderActivity(await this.apRendererService.renderLike(record, note));
|
const content = this.apRendererService.addContext(await this.apRendererService.renderLike(record, note));
|
||||||
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 IRemoteUser);
|
dm.addDirectRecipe(reactee as RemoteUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (['public', 'home', 'followers'].includes(note.visibility)) {
|
if (['public', 'home', 'followers'].includes(note.visibility)) {
|
||||||
@ -189,7 +189,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 IRemoteUser);
|
dm.addDirectRecipe(u as RemoteUser);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,11 +235,11 @@ export class ReactionService {
|
|||||||
|
|
||||||
//#region 配信
|
//#region 配信
|
||||||
if (this.userEntityService.isLocalUser(user) && !note.localOnly) {
|
if (this.userEntityService.isLocalUser(user) && !note.localOnly) {
|
||||||
const content = this.apRendererService.renderActivity(this.apRendererService.renderUndo(await this.apRendererService.renderLike(exist, note), user));
|
const content = this.apRendererService.addContext(this.apRendererService.renderUndo(await this.apRendererService.renderLike(exist, note), user));
|
||||||
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 IRemoteUser);
|
dm.addDirectRecipe(reactee as RemoteUser);
|
||||||
}
|
}
|
||||||
dm.addFollowersRecipe();
|
dm.addFollowersRecipe();
|
||||||
dm.execute();
|
dm.execute();
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { IsNull } from 'typeorm';
|
import { IsNull } from 'typeorm';
|
||||||
import type { ILocalUser, User } from '@/models/entities/User.js';
|
import type { LocalUser, User } 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 { Cache } from '@/misc/cache.js';
|
import { Cache } from '@/misc/cache.js';
|
||||||
@ -34,16 +34,16 @@ export class RelayService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async getRelayActor(): Promise<ILocalUser> {
|
private async getRelayActor(): Promise<LocalUser> {
|
||||||
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 ILocalUser;
|
if (user) return user as LocalUser;
|
||||||
|
|
||||||
const created = await this.createSystemUserService.createSystemUser(ACTOR_USERNAME);
|
const created = await this.createSystemUserService.createSystemUser(ACTOR_USERNAME);
|
||||||
return created as ILocalUser;
|
return created as LocalUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
@ -56,7 +56,7 @@ export class RelayService {
|
|||||||
|
|
||||||
const relayActor = await this.getRelayActor();
|
const relayActor = await this.getRelayActor();
|
||||||
const follow = await this.apRendererService.renderFollowRelay(relay, relayActor);
|
const follow = await this.apRendererService.renderFollowRelay(relay, relayActor);
|
||||||
const activity = this.apRendererService.renderActivity(follow);
|
const activity = this.apRendererService.addContext(follow);
|
||||||
this.queueService.deliver(relayActor, activity, relay.inbox);
|
this.queueService.deliver(relayActor, activity, relay.inbox);
|
||||||
|
|
||||||
return relay;
|
return relay;
|
||||||
@ -75,7 +75,7 @@ export class RelayService {
|
|||||||
const relayActor = await this.getRelayActor();
|
const relayActor = await this.getRelayActor();
|
||||||
const follow = this.apRendererService.renderFollowRelay(relay, relayActor);
|
const follow = this.apRendererService.renderFollowRelay(relay, relayActor);
|
||||||
const undo = this.apRendererService.renderUndo(follow, relayActor);
|
const undo = this.apRendererService.renderUndo(follow, relayActor);
|
||||||
const activity = this.apRendererService.renderActivity(undo);
|
const activity = this.apRendererService.addContext(undo);
|
||||||
this.queueService.deliver(relayActor, activity, relay.inbox);
|
this.queueService.deliver(relayActor, activity, relay.inbox);
|
||||||
|
|
||||||
await this.relaysRepository.delete(relay.id);
|
await this.relaysRepository.delete(relay.id);
|
||||||
|
@ -4,7 +4,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 { IRemoteUser, User } from '@/models/entities/User.js';
|
import type { RemoteUser, User } 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';
|
||||||
@ -60,7 +60,7 @@ export class RemoteUserResolveService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await this.usersRepository.findOneBy({ usernameLower, host }) as IRemoteUser | null;
|
const user = await this.usersRepository.findOneBy({ usernameLower, host }) as RemoteUser | null;
|
||||||
|
|
||||||
const acctLower = `${usernameLower}@${host}`;
|
const acctLower = `${usernameLower}@${host}`;
|
||||||
|
|
||||||
@ -82,7 +82,7 @@ export class RemoteUserResolveService {
|
|||||||
const self = await this.resolveSelf(acctLower);
|
const self = await this.resolveSelf(acctLower);
|
||||||
|
|
||||||
if (user.uri !== self.href) {
|
if (user.uri !== self.href) {
|
||||||
// if uri mismatch, Fix (user@host <=> AP's Person id(IRemoteUser.uri)) mapping.
|
// if uri mismatch, Fix (user@host <=> AP's Person id(RemoteUser.uri)) mapping.
|
||||||
this.logger.info(`uri missmatch: ${acctLower}`);
|
this.logger.info(`uri missmatch: ${acctLower}`);
|
||||||
this.logger.info(`recovery missmatch uri for (username=${username}, host=${host}) from ${user.uri} to ${self.href}`);
|
this.logger.info(`recovery missmatch uri for (username=${username}, host=${host}) from ${user.uri} to ${self.href}`);
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ import Redis from 'ioredis';
|
|||||||
import { In } from 'typeorm';
|
import { In } from 'typeorm';
|
||||||
import type { Role, RoleAssignment, RoleAssignmentsRepository, RolesRepository, UsersRepository } from '@/models/index.js';
|
import type { Role, RoleAssignment, RoleAssignmentsRepository, RolesRepository, UsersRepository } from '@/models/index.js';
|
||||||
import { Cache } from '@/misc/cache.js';
|
import { Cache } from '@/misc/cache.js';
|
||||||
import type { CacheableLocalUser, CacheableUser, ILocalUser, User } from '@/models/entities/User.js';
|
import type { User } 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';
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
|
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
|
||||||
import Redis from 'ioredis';
|
import Redis from 'ioredis';
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
import type { CacheableUser, User } from '@/models/entities/User.js';
|
import type { User } from '@/models/entities/User.js';
|
||||||
import type { Blocking } from '@/models/entities/Blocking.js';
|
import type { Blocking } 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';
|
||||||
@ -117,7 +117,7 @@ export class UserBlockingService implements OnApplicationShutdown {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (this.userEntityService.isLocalUser(blocker) && this.userEntityService.isRemoteUser(blockee)) {
|
if (this.userEntityService.isLocalUser(blocker) && this.userEntityService.isRemoteUser(blockee)) {
|
||||||
const content = this.apRendererService.renderActivity(this.apRendererService.renderBlock(blocking));
|
const content = this.apRendererService.addContext(this.apRendererService.renderBlock(blocking));
|
||||||
this.queueService.deliver(blocker, content, blockee.inbox);
|
this.queueService.deliver(blocker, content, blockee.inbox);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -162,13 +162,13 @@ export class UserBlockingService implements OnApplicationShutdown {
|
|||||||
|
|
||||||
// リモートにフォローリクエストをしていたらUndoFollow送信
|
// リモートにフォローリクエストをしていたらUndoFollow送信
|
||||||
if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) {
|
if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) {
|
||||||
const content = this.apRendererService.renderActivity(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower, followee), follower));
|
const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower, followee), follower));
|
||||||
this.queueService.deliver(follower, content, followee.inbox);
|
this.queueService.deliver(follower, content, followee.inbox);
|
||||||
}
|
}
|
||||||
|
|
||||||
// リモートからフォローリクエストを受けていたらReject送信
|
// リモートからフォローリクエストを受けていたらReject送信
|
||||||
if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) {
|
if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) {
|
||||||
const content = this.apRendererService.renderActivity(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower, followee, request.requestId!), followee));
|
const content = this.apRendererService.addContext(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower, followee, request.requestId!), followee));
|
||||||
this.queueService.deliver(followee, content, follower.inbox);
|
this.queueService.deliver(followee, content, follower.inbox);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -210,13 +210,13 @@ export class UserBlockingService implements OnApplicationShutdown {
|
|||||||
|
|
||||||
// リモートにフォローをしていたらUndoFollow送信
|
// リモートにフォローをしていたらUndoFollow送信
|
||||||
if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) {
|
if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) {
|
||||||
const content = this.apRendererService.renderActivity(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower, followee), follower));
|
const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower, followee), follower));
|
||||||
this.queueService.deliver(follower, content, followee.inbox);
|
this.queueService.deliver(follower, content, followee.inbox);
|
||||||
}
|
}
|
||||||
|
|
||||||
// リモートからフォローをされていたらRejectFollow送信
|
// リモートからフォローをされていたらRejectFollow送信
|
||||||
if (this.userEntityService.isLocalUser(followee) && this.userEntityService.isRemoteUser(follower)) {
|
if (this.userEntityService.isLocalUser(followee) && this.userEntityService.isRemoteUser(follower)) {
|
||||||
const content = this.apRendererService.renderActivity(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower, followee), followee));
|
const content = this.apRendererService.addContext(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower, followee), followee));
|
||||||
this.queueService.deliver(followee, content, follower.inbox);
|
this.queueService.deliver(followee, content, follower.inbox);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -236,7 +236,7 @@ export class UserBlockingService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async unblock(blocker: CacheableUser, blockee: CacheableUser) {
|
public async unblock(blocker: User, blockee: User) {
|
||||||
const blocking = await this.blockingsRepository.findOneBy({
|
const blocking = await this.blockingsRepository.findOneBy({
|
||||||
blockerId: blocker.id,
|
blockerId: blocker.id,
|
||||||
blockeeId: blockee.id,
|
blockeeId: blockee.id,
|
||||||
@ -261,7 +261,7 @@ export class UserBlockingService implements OnApplicationShutdown {
|
|||||||
|
|
||||||
// deliver if remote bloking
|
// deliver if remote bloking
|
||||||
if (this.userEntityService.isLocalUser(blocker) && this.userEntityService.isRemoteUser(blockee)) {
|
if (this.userEntityService.isLocalUser(blocker) && this.userEntityService.isRemoteUser(blockee)) {
|
||||||
const content = this.apRendererService.renderActivity(this.apRendererService.renderUndo(this.apRendererService.renderBlock(blocking), blocker));
|
const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderBlock(blocking), blocker));
|
||||||
this.queueService.deliver(blocker, content, blockee.inbox);
|
this.queueService.deliver(blocker, content, blockee.inbox);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common';
|
|||||||
import Redis from 'ioredis';
|
import Redis from 'ioredis';
|
||||||
import type { UsersRepository } from '@/models/index.js';
|
import type { UsersRepository } from '@/models/index.js';
|
||||||
import { Cache } from '@/misc/cache.js';
|
import { Cache } from '@/misc/cache.js';
|
||||||
import type { CacheableLocalUser, CacheableUser, ILocalUser, User } from '@/models/entities/User.js';
|
import type { LocalUser, User } 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';
|
||||||
@ -11,10 +11,10 @@ import type { OnApplicationShutdown } from '@nestjs/common';
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UserCacheService implements OnApplicationShutdown {
|
export class UserCacheService implements OnApplicationShutdown {
|
||||||
public userByIdCache: Cache<CacheableUser>;
|
public userByIdCache: Cache<User>;
|
||||||
public localUserByNativeTokenCache: Cache<CacheableLocalUser | null>;
|
public localUserByNativeTokenCache: Cache<LocalUser | null>;
|
||||||
public localUserByIdCache: Cache<CacheableLocalUser>;
|
public localUserByIdCache: Cache<LocalUser>;
|
||||||
public uriPersonCache: Cache<CacheableUser | null>;
|
public uriPersonCache: Cache<User | null>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.redisSubscriber)
|
@Inject(DI.redisSubscriber)
|
||||||
@ -27,10 +27,10 @@ export class UserCacheService implements OnApplicationShutdown {
|
|||||||
) {
|
) {
|
||||||
//this.onMessage = this.onMessage.bind(this);
|
//this.onMessage = this.onMessage.bind(this);
|
||||||
|
|
||||||
this.userByIdCache = new Cache<CacheableUser>(Infinity);
|
this.userByIdCache = new Cache<User>(Infinity);
|
||||||
this.localUserByNativeTokenCache = new Cache<CacheableLocalUser | null>(Infinity);
|
this.localUserByNativeTokenCache = new Cache<LocalUser | null>(Infinity);
|
||||||
this.localUserByIdCache = new Cache<CacheableLocalUser>(Infinity);
|
this.localUserByIdCache = new Cache<LocalUser>(Infinity);
|
||||||
this.uriPersonCache = new Cache<CacheableUser | null>(Infinity);
|
this.uriPersonCache = new Cache<User | null>(Infinity);
|
||||||
|
|
||||||
this.redisSubscriber.on('message', this.onMessage);
|
this.redisSubscriber.on('message', this.onMessage);
|
||||||
}
|
}
|
||||||
@ -58,7 +58,7 @@ export class UserCacheService implements OnApplicationShutdown {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'userTokenRegenerated': {
|
case 'userTokenRegenerated': {
|
||||||
const user = await this.usersRepository.findOneByOrFail({ id: body.id }) as ILocalUser;
|
const user = await this.usersRepository.findOneByOrFail({ id: body.id }) as LocalUser;
|
||||||
this.localUserByNativeTokenCache.delete(body.oldToken);
|
this.localUserByNativeTokenCache.delete(body.oldToken);
|
||||||
this.localUserByNativeTokenCache.set(body.newToken, user);
|
this.localUserByNativeTokenCache.set(body.newToken, user);
|
||||||
break;
|
break;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import type { CacheableUser, ILocalUser, IRemoteUser, User } from '@/models/entities/User.js';
|
import type { LocalUser, RemoteUser, User } 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';
|
||||||
@ -21,16 +21,16 @@ import Logger from '../logger.js';
|
|||||||
|
|
||||||
const logger = new Logger('following/create');
|
const logger = new Logger('following/create');
|
||||||
|
|
||||||
type Local = ILocalUser | {
|
type Local = LocalUser | {
|
||||||
id: ILocalUser['id'];
|
id: LocalUser['id'];
|
||||||
host: ILocalUser['host'];
|
host: LocalUser['host'];
|
||||||
uri: ILocalUser['uri']
|
uri: LocalUser['uri']
|
||||||
};
|
};
|
||||||
type Remote = IRemoteUser | {
|
type Remote = RemoteUser | {
|
||||||
id: IRemoteUser['id'];
|
id: RemoteUser['id'];
|
||||||
host: IRemoteUser['host'];
|
host: RemoteUser['host'];
|
||||||
uri: IRemoteUser['uri'];
|
uri: RemoteUser['uri'];
|
||||||
inbox: IRemoteUser['inbox'];
|
inbox: RemoteUser['inbox'];
|
||||||
};
|
};
|
||||||
type Both = Local | Remote;
|
type Both = Local | Remote;
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ export class UserFollowingService {
|
|||||||
|
|
||||||
if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee) && blocked) {
|
if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee) && blocked) {
|
||||||
// リモートフォローを受けてブロックしていた場合は、エラーにするのではなくRejectを送り返しておしまい。
|
// リモートフォローを受けてブロックしていた場合は、エラーにするのではなくRejectを送り返しておしまい。
|
||||||
const content = this.apRendererService.renderActivity(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower, followee, requestId), followee));
|
const content = this.apRendererService.addContext(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower, followee, requestId), followee));
|
||||||
this.queueService.deliver(followee, content, follower.inbox);
|
this.queueService.deliver(followee, content, follower.inbox);
|
||||||
return;
|
return;
|
||||||
} else if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee) && blocking) {
|
} else if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee) && blocking) {
|
||||||
@ -130,7 +130,7 @@ export class UserFollowingService {
|
|||||||
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.renderActivity(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee, requestId), followee));
|
const content = this.apRendererService.addContext(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee, requestId), followee));
|
||||||
this.queueService.deliver(followee, content, follower.inbox);
|
this.queueService.deliver(followee, content, follower.inbox);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -293,13 +293,13 @@ export class UserFollowingService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) {
|
if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) {
|
||||||
const content = this.apRendererService.renderActivity(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower, followee), follower));
|
const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower, followee), follower));
|
||||||
this.queueService.deliver(follower, content, followee.inbox);
|
this.queueService.deliver(follower, content, followee.inbox);
|
||||||
}
|
}
|
||||||
|
|
||||||
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.renderActivity(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower, followee), followee));
|
const content = this.apRendererService.addContext(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower, followee), followee));
|
||||||
this.queueService.deliver(followee, content, follower.inbox);
|
this.queueService.deliver(followee, content, follower.inbox);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -388,7 +388,7 @@ export class UserFollowingService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) {
|
if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) {
|
||||||
const content = this.apRendererService.renderActivity(this.apRendererService.renderFollow(follower, followee));
|
const content = this.apRendererService.addContext(this.apRendererService.renderFollow(follower, followee));
|
||||||
this.queueService.deliver(follower, content, followee.inbox);
|
this.queueService.deliver(follower, content, followee.inbox);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -403,7 +403,7 @@ export class UserFollowingService {
|
|||||||
},
|
},
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (this.userEntityService.isRemoteUser(followee)) {
|
if (this.userEntityService.isRemoteUser(followee)) {
|
||||||
const content = this.apRendererService.renderActivity(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower, followee), follower));
|
const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower, followee), follower));
|
||||||
|
|
||||||
if (this.userEntityService.isLocalUser(follower)) { // 本来このチェックは不要だけどTSに怒られるので
|
if (this.userEntityService.isLocalUser(follower)) { // 本来このチェックは不要だけどTSに怒られるので
|
||||||
this.queueService.deliver(follower, content, followee.inbox);
|
this.queueService.deliver(follower, content, followee.inbox);
|
||||||
@ -434,7 +434,7 @@ export class UserFollowingService {
|
|||||||
followee: {
|
followee: {
|
||||||
id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox'];
|
id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox'];
|
||||||
},
|
},
|
||||||
follower: CacheableUser,
|
follower: User,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const request = await this.followRequestsRepository.findOneBy({
|
const request = await this.followRequestsRepository.findOneBy({
|
||||||
followeeId: followee.id,
|
followeeId: followee.id,
|
||||||
@ -448,7 +448,7 @@ export class UserFollowingService {
|
|||||||
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.renderActivity(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee, request.requestId!), followee));
|
const content = this.apRendererService.addContext(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee, request.requestId!), followee));
|
||||||
this.queueService.deliver(followee, content, follower.inbox);
|
this.queueService.deliver(followee, content, follower.inbox);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -556,7 +556,7 @@ export class UserFollowingService {
|
|||||||
followerId: follower.id,
|
followerId: follower.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
const content = this.apRendererService.renderActivity(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower, followee, request?.requestId ?? undefined), followee));
|
const content = this.apRendererService.addContext(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower, followee, request?.requestId ?? undefined), followee));
|
||||||
this.queueService.deliver(followee, content, follower.inbox);
|
this.queueService.deliver(followee, content, follower.inbox);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,8 @@ import { RoleService } from '@/core/RoleService.js';
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UserListService {
|
export class UserListService {
|
||||||
|
public static TooManyUsersError = class extends Error {};
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.usersRepository)
|
@Inject(DI.usersRepository)
|
||||||
private usersRepository: UsersRepository,
|
private usersRepository: UsersRepository,
|
||||||
@ -36,7 +38,7 @@ export class UserListService {
|
|||||||
userListId: list.id,
|
userListId: list.id,
|
||||||
});
|
});
|
||||||
if (currentCount > (await this.roleService.getUserPolicies(me.id)).userEachUserListsLimit) {
|
if (currentCount > (await this.roleService.getUserPolicies(me.id)).userEachUserListsLimit) {
|
||||||
throw new Error('Too many users');
|
throw new UserListService.TooManyUsersError();
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.userListJoiningsRepository.insert({
|
await this.userListJoiningsRepository.insert({
|
||||||
|
@ -35,7 +35,7 @@ export class UserSuspendService {
|
|||||||
|
|
||||||
if (this.userEntityService.isLocalUser(user)) {
|
if (this.userEntityService.isLocalUser(user)) {
|
||||||
// 知り得る全SharedInboxにDelete配信
|
// 知り得る全SharedInboxにDelete配信
|
||||||
const content = this.apRendererService.renderActivity(this.apRendererService.renderDelete(`${this.config.url}/users/${user.id}`, user));
|
const content = this.apRendererService.addContext(this.apRendererService.renderDelete(`${this.config.url}/users/${user.id}`, user));
|
||||||
|
|
||||||
const queue: string[] = [];
|
const queue: string[] = [];
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ export class UserSuspendService {
|
|||||||
|
|
||||||
if (this.userEntityService.isLocalUser(user)) {
|
if (this.userEntityService.isLocalUser(user)) {
|
||||||
// 知り得る全SharedInboxにUndo Delete配信
|
// 知り得る全SharedInboxにUndo Delete配信
|
||||||
const content = this.apRendererService.renderActivity(this.apRendererService.renderUndo(this.apRendererService.renderDelete(`${this.config.url}/users/${user.id}`, user), user));
|
const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderDelete(`${this.config.url}/users/${user.id}`, user), user));
|
||||||
|
|
||||||
const queue: string[] = [];
|
const queue: string[] = [];
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import { ImageProcessingService } from '@/core/ImageProcessingService.js';
|
|||||||
import type { IImage } from '@/core/ImageProcessingService.js';
|
import type { IImage } from '@/core/ImageProcessingService.js';
|
||||||
import { createTempDir } from '@/misc/create-temp.js';
|
import { createTempDir } from '@/misc/create-temp.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
import { appendQuery, query } from '@/misc/prelude/url.js';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class VideoProcessingService {
|
export class VideoProcessingService {
|
||||||
@ -41,5 +42,18 @@ export class VideoProcessingService {
|
|||||||
cleanup();
|
cleanup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public getExternalVideoThumbnailUrl(url: string): string | null {
|
||||||
|
if (this.config.videoThumbnailGenerator == null) return null;
|
||||||
|
|
||||||
|
return appendQuery(
|
||||||
|
`${this.config.videoThumbnailGenerator}/thumbnail.webp`,
|
||||||
|
query({
|
||||||
|
thumbnail: '1',
|
||||||
|
url,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common';
|
|||||||
import { In } from 'typeorm';
|
import { In } from 'typeorm';
|
||||||
import promiseLimit from 'promise-limit';
|
import promiseLimit from 'promise-limit';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { CacheableRemoteUser, CacheableUser } from '@/models/entities/User.js';
|
import type { RemoteUser, User } from '@/models/entities/User.js';
|
||||||
import { concat, toArray, toSingle, unique } from '@/misc/prelude/array.js';
|
import { concat, toArray, toSingle, unique } from '@/misc/prelude/array.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { getApId, getApIds, getApType, isAccept, isActor, isAdd, isAnnounce, isBlock, isCollection, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isPost, isRead, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost } from './type.js';
|
import { getApId, getApIds, getApType, isAccept, isActor, isAdd, isAnnounce, isBlock, isCollection, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isPost, isRead, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost } from './type.js';
|
||||||
@ -14,8 +14,8 @@ type Visibility = 'public' | 'home' | 'followers' | 'specified';
|
|||||||
|
|
||||||
type AudienceInfo = {
|
type AudienceInfo = {
|
||||||
visibility: Visibility,
|
visibility: Visibility,
|
||||||
mentionedUsers: CacheableUser[],
|
mentionedUsers: User[],
|
||||||
visibleUsers: CacheableUser[],
|
visibleUsers: User[],
|
||||||
};
|
};
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -26,16 +26,16 @@ export class ApAudienceService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async parseAudience(actor: CacheableRemoteUser, to?: ApObject, cc?: ApObject, resolver?: Resolver): Promise<AudienceInfo> {
|
public async parseAudience(actor: RemoteUser, 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<CacheableUser | null>(2);
|
const limit = promiseLimit<User | 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 CacheableUser => x != null);
|
)).filter((x): x is User => x != null);
|
||||||
|
|
||||||
if (toGroups.public.length > 0) {
|
if (toGroups.public.length > 0) {
|
||||||
return {
|
return {
|
||||||
@ -69,7 +69,7 @@ export class ApAudienceService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private groupingAudience(ids: string[], actor: CacheableRemoteUser) {
|
private groupingAudience(ids: string[], actor: RemoteUser) {
|
||||||
const groups = {
|
const groups = {
|
||||||
public: [] as string[],
|
public: [] as string[],
|
||||||
followers: [] as string[],
|
followers: [] as string[],
|
||||||
@ -101,7 +101,7 @@ export class ApAudienceService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private isFollowers(id: string, actor: CacheableRemoteUser) {
|
private isFollowers(id: string, actor: RemoteUser) {
|
||||||
return (
|
return (
|
||||||
id === (actor.followersUri ?? `${actor.uri}/followers`)
|
id === (actor.followersUri ?? `${actor.uri}/followers`)
|
||||||
);
|
);
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import escapeRegexp from 'escape-regexp';
|
import escapeRegexp from 'escape-regexp';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { MessagingMessagesRepository, 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 type { CacheableRemoteUser, CacheableUser } from '@/models/entities/User.js';
|
|
||||||
import { Cache } from '@/misc/cache.js';
|
import { Cache } from '@/misc/cache.js';
|
||||||
import type { UserPublickey } from '@/models/entities/UserPublickey.js';
|
import type { UserPublickey } from '@/models/entities/UserPublickey.js';
|
||||||
import { UserCacheService } from '@/core/UserCacheService.js';
|
import { UserCacheService } from '@/core/UserCacheService.js';
|
||||||
import type { Note } from '@/models/entities/Note.js';
|
import type { Note } from '@/models/entities/Note.js';
|
||||||
import type { MessagingMessage } from '@/models/entities/MessagingMessage.js';
|
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
import { RemoteUser, User } 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';
|
||||||
@ -42,9 +41,6 @@ export class ApDbResolverService {
|
|||||||
@Inject(DI.usersRepository)
|
@Inject(DI.usersRepository)
|
||||||
private usersRepository: UsersRepository,
|
private usersRepository: UsersRepository,
|
||||||
|
|
||||||
@Inject(DI.messagingMessagesRepository)
|
|
||||||
private messagingMessagesRepository: MessagingMessagesRepository,
|
|
||||||
|
|
||||||
@Inject(DI.notesRepository)
|
@Inject(DI.notesRepository)
|
||||||
private notesRepository: NotesRepository,
|
private notesRepository: NotesRepository,
|
||||||
|
|
||||||
@ -101,28 +97,11 @@ export class ApDbResolverService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
|
||||||
public async getMessageFromApId(value: string | IObject): Promise<MessagingMessage | null> {
|
|
||||||
const parsed = this.parseUri(value);
|
|
||||||
|
|
||||||
if (parsed.local) {
|
|
||||||
if (parsed.type !== 'notes') return null;
|
|
||||||
|
|
||||||
return await this.messagingMessagesRepository.findOneBy({
|
|
||||||
id: parsed.id,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return await this.messagingMessagesRepository.findOneBy({
|
|
||||||
uri: parsed.uri,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AP Person => Misskey User in DB
|
* AP Person => Misskey User in DB
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async getUserFromApId(value: string | IObject): Promise<CacheableUser | null> {
|
public async getUserFromApId(value: string | IObject): Promise<User | null> {
|
||||||
const parsed = this.parseUri(value);
|
const parsed = this.parseUri(value);
|
||||||
|
|
||||||
if (parsed.local) {
|
if (parsed.local) {
|
||||||
@ -143,7 +122,7 @@ export class ApDbResolverService {
|
|||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async getAuthUserFromKeyId(keyId: string): Promise<{
|
public async getAuthUserFromKeyId(keyId: string): Promise<{
|
||||||
user: CacheableRemoteUser;
|
user: RemoteUser;
|
||||||
key: UserPublickey;
|
key: UserPublickey;
|
||||||
} | null> {
|
} | null> {
|
||||||
const key = await this.publicKeyCache.fetch(keyId, async () => {
|
const key = await this.publicKeyCache.fetch(keyId, async () => {
|
||||||
@ -159,7 +138,7 @@ export class ApDbResolverService {
|
|||||||
if (key == null) return null;
|
if (key == null) return null;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
user: await this.userCacheService.findById(key.userId) as CacheableRemoteUser,
|
user: await this.userCacheService.findById(key.userId) as RemoteUser,
|
||||||
key,
|
key,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -169,10 +148,10 @@ export class ApDbResolverService {
|
|||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async getAuthUserFromApId(uri: string): Promise<{
|
public async getAuthUserFromApId(uri: string): Promise<{
|
||||||
user: CacheableRemoteUser;
|
user: RemoteUser;
|
||||||
key: UserPublickey | null;
|
key: UserPublickey | null;
|
||||||
} | null> {
|
} | null> {
|
||||||
const user = await this.apPersonService.resolvePerson(uri) as CacheableRemoteUser;
|
const user = await this.apPersonService.resolvePerson(uri) as RemoteUser;
|
||||||
|
|
||||||
if (user == null) return null;
|
if (user == null) return null;
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ 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, UsersRepository } from '@/models/index.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import type { ILocalUser, IRemoteUser, User } 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';
|
||||||
@ -18,7 +18,7 @@ interface IFollowersRecipe extends IRecipe {
|
|||||||
|
|
||||||
interface IDirectRecipe extends IRecipe {
|
interface IDirectRecipe extends IRecipe {
|
||||||
type: 'Direct';
|
type: 'Direct';
|
||||||
to: IRemoteUser;
|
to: RemoteUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isFollowers = (recipe: any): recipe is IFollowersRecipe =>
|
const isFollowers = (recipe: any): recipe is IFollowersRecipe =>
|
||||||
@ -50,7 +50,7 @@ export class ApDeliverManagerService {
|
|||||||
* @param from Followee
|
* @param from Followee
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async deliverToFollowers(actor: { id: ILocalUser['id']; host: null; }, activity: any) {
|
public async deliverToFollowers(actor: { id: LocalUser['id']; host: null; }, activity: any) {
|
||||||
const manager = new DeliverManager(
|
const manager = new DeliverManager(
|
||||||
this.userEntityService,
|
this.userEntityService,
|
||||||
this.followingsRepository,
|
this.followingsRepository,
|
||||||
@ -68,7 +68,7 @@ export class ApDeliverManagerService {
|
|||||||
* @param to Target user
|
* @param to Target user
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async deliverToUser(actor: { id: ILocalUser['id']; host: null; }, activity: any, to: IRemoteUser) {
|
public async deliverToUser(actor: { id: LocalUser['id']; host: null; }, activity: any, to: RemoteUser) {
|
||||||
const manager = new DeliverManager(
|
const manager = new DeliverManager(
|
||||||
this.userEntityService,
|
this.userEntityService,
|
||||||
this.followingsRepository,
|
this.followingsRepository,
|
||||||
@ -132,7 +132,7 @@ class DeliverManager {
|
|||||||
* @param to To
|
* @param to To
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public addDirectRecipe(to: IRemoteUser) {
|
public addDirectRecipe(to: RemoteUser) {
|
||||||
const recipe = {
|
const recipe = {
|
||||||
type: 'Direct',
|
type: 'Direct',
|
||||||
to,
|
to,
|
||||||
|
@ -2,7 +2,6 @@ import { Inject, Injectable } 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 { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import type { CacheableRemoteUser } from '@/models/entities/User.js';
|
|
||||||
import { UserFollowingService } from '@/core/UserFollowingService.js';
|
import { UserFollowingService } from '@/core/UserFollowingService.js';
|
||||||
import { ReactionService } from '@/core/ReactionService.js';
|
import { ReactionService } from '@/core/ReactionService.js';
|
||||||
import { RelayService } from '@/core/RelayService.js';
|
import { RelayService } from '@/core/RelayService.js';
|
||||||
@ -20,8 +19,9 @@ import { UtilityService } from '@/core/UtilityService.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 { MessagingService } from '@/core/MessagingService.js';
|
import type { UsersRepository, NotesRepository, FollowingsRepository, AbuseUserReportsRepository, FollowRequestsRepository } from '@/models/index.js';
|
||||||
import type { UsersRepository, NotesRepository, FollowingsRepository, MessagingMessagesRepository, AbuseUserReportsRepository, FollowRequestsRepository } from '@/models/index.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
import type { RemoteUser } from '@/models/entities/User.js';
|
||||||
import { getApId, getApIds, getApType, isAccept, isActor, isAdd, isAnnounce, isBlock, isCollection, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isPost, isRead, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost } from './type.js';
|
import { getApId, getApIds, getApType, isAccept, isActor, isAdd, isAnnounce, isBlock, isCollection, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isPost, isRead, 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';
|
||||||
@ -32,7 +32,6 @@ import { ApPersonService } from './models/ApPersonService.js';
|
|||||||
import { ApQuestionService } from './models/ApQuestionService.js';
|
import { ApQuestionService } from './models/ApQuestionService.js';
|
||||||
import type { Resolver } from './ApResolverService.js';
|
import type { Resolver } from './ApResolverService.js';
|
||||||
import type { IAccept, IAdd, IAnnounce, IBlock, ICreate, IDelete, IFlag, IFollow, ILike, IObject, IRead, IReject, IRemove, IUndo, IUpdate } from './type.js';
|
import type { IAccept, IAdd, IAnnounce, IBlock, ICreate, IDelete, IFlag, IFollow, ILike, IObject, IRead, IReject, IRemove, IUndo, IUpdate } from './type.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ApInboxService {
|
export class ApInboxService {
|
||||||
@ -51,9 +50,6 @@ export class ApInboxService {
|
|||||||
@Inject(DI.followingsRepository)
|
@Inject(DI.followingsRepository)
|
||||||
private followingsRepository: FollowingsRepository,
|
private followingsRepository: FollowingsRepository,
|
||||||
|
|
||||||
@Inject(DI.messagingMessagesRepository)
|
|
||||||
private messagingMessagesRepository: MessagingMessagesRepository,
|
|
||||||
|
|
||||||
@Inject(DI.abuseUserReportsRepository)
|
@Inject(DI.abuseUserReportsRepository)
|
||||||
private abuseUserReportsRepository: AbuseUserReportsRepository,
|
private abuseUserReportsRepository: AbuseUserReportsRepository,
|
||||||
|
|
||||||
@ -81,13 +77,12 @@ export class ApInboxService {
|
|||||||
private apPersonService: ApPersonService,
|
private apPersonService: ApPersonService,
|
||||||
private apQuestionService: ApQuestionService,
|
private apQuestionService: ApQuestionService,
|
||||||
private queueService: QueueService,
|
private queueService: QueueService,
|
||||||
private messagingService: MessagingService,
|
|
||||||
) {
|
) {
|
||||||
this.logger = this.apLoggerService.logger;
|
this.logger = this.apLoggerService.logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async performActivity(actor: CacheableRemoteUser, activity: IObject) {
|
public async performActivity(actor: RemoteUser, activity: IObject) {
|
||||||
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)) {
|
||||||
@ -115,7 +110,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async performOneActivity(actor: CacheableRemoteUser, activity: IObject): Promise<void> {
|
public async performOneActivity(actor: RemoteUser, activity: IObject): Promise<void> {
|
||||||
if (actor.isSuspended) return;
|
if (actor.isSuspended) return;
|
||||||
|
|
||||||
if (isCreate(activity)) {
|
if (isCreate(activity)) {
|
||||||
@ -124,8 +119,6 @@ export class ApInboxService {
|
|||||||
await this.delete(actor, activity);
|
await this.delete(actor, activity);
|
||||||
} else if (isUpdate(activity)) {
|
} else if (isUpdate(activity)) {
|
||||||
await this.update(actor, activity);
|
await this.update(actor, activity);
|
||||||
} else if (isRead(activity)) {
|
|
||||||
await this.read(actor, activity);
|
|
||||||
} else if (isFollow(activity)) {
|
} else if (isFollow(activity)) {
|
||||||
await this.follow(actor, activity);
|
await this.follow(actor, activity);
|
||||||
} else if (isAccept(activity)) {
|
} else if (isAccept(activity)) {
|
||||||
@ -152,7 +145,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async follow(actor: CacheableRemoteUser, activity: IFollow): Promise<string> {
|
private async follow(actor: RemoteUser, 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) {
|
||||||
@ -168,7 +161,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async like(actor: CacheableRemoteUser, activity: ILike): Promise<string> {
|
private async like(actor: RemoteUser, 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);
|
||||||
@ -186,30 +179,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async read(actor: CacheableRemoteUser, activity: IRead): Promise<string> {
|
private async accept(actor: RemoteUser, activity: IAccept): Promise<string> {
|
||||||
const id = await getApId(activity.object);
|
|
||||||
|
|
||||||
if (!this.utilityService.isSelfHost(this.utilityService.extractDbHost(id))) {
|
|
||||||
return `skip: Read to foreign host (${id})`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const messageId = id.split('/').pop();
|
|
||||||
|
|
||||||
const message = await this.messagingMessagesRepository.findOneBy({ id: messageId });
|
|
||||||
if (message == null) {
|
|
||||||
return 'skip: message not found';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (actor.id !== message.recipientId) {
|
|
||||||
return 'skip: actor is not a message recipient';
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.messagingService.readUserMessagingMessage(message.recipientId!, message.userId, [message.id]);
|
|
||||||
return `ok: mark as read (${message.userId} => ${message.recipientId} ${message.id})`;
|
|
||||||
}
|
|
||||||
|
|
||||||
@bindThis
|
|
||||||
private async accept(actor: CacheableRemoteUser, activity: IAccept): Promise<string> {
|
|
||||||
const uri = activity.id ?? activity;
|
const uri = activity.id ?? activity;
|
||||||
|
|
||||||
this.logger.info(`Accept: ${uri}`);
|
this.logger.info(`Accept: ${uri}`);
|
||||||
@ -227,7 +197,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async acceptFollow(actor: CacheableRemoteUser, activity: IFollow): Promise<string> {
|
private async acceptFollow(actor: RemoteUser, 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);
|
||||||
@ -251,7 +221,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async add(actor: CacheableRemoteUser, activity: IAdd): Promise<void> {
|
private async add(actor: RemoteUser, activity: IAdd): Promise<void> {
|
||||||
if ('actor' in activity && actor.uri !== activity.actor) {
|
if ('actor' in activity && actor.uri !== activity.actor) {
|
||||||
throw new Error('invalid actor');
|
throw new Error('invalid actor');
|
||||||
}
|
}
|
||||||
@ -271,7 +241,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async announce(actor: CacheableRemoteUser, activity: IAnnounce): Promise<void> {
|
private async announce(actor: RemoteUser, activity: IAnnounce): Promise<void> {
|
||||||
const uri = getApId(activity);
|
const uri = getApId(activity);
|
||||||
|
|
||||||
this.logger.info(`Announce: ${uri}`);
|
this.logger.info(`Announce: ${uri}`);
|
||||||
@ -282,7 +252,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async announceNote(actor: CacheableRemoteUser, activity: IAnnounce, targetUri: string): Promise<void> {
|
private async announceNote(actor: RemoteUser, activity: IAnnounce, targetUri: string): Promise<void> {
|
||||||
const uri = getApId(activity);
|
const uri = getApId(activity);
|
||||||
|
|
||||||
if (actor.isSuspended) {
|
if (actor.isSuspended) {
|
||||||
@ -342,7 +312,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async block(actor: CacheableRemoteUser, activity: IBlock): Promise<string> {
|
private async block(actor: RemoteUser, activity: IBlock): Promise<string> {
|
||||||
// ※ activity.objectにブロック対象があり、それは存在するローカルユーザーのはず
|
// ※ activity.objectにブロック対象があり、それは存在するローカルユーザーのはず
|
||||||
|
|
||||||
const blockee = await this.apDbResolverService.getUserFromApId(activity.object);
|
const blockee = await this.apDbResolverService.getUserFromApId(activity.object);
|
||||||
@ -360,7 +330,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async create(actor: CacheableRemoteUser, activity: ICreate): Promise<void> {
|
private async create(actor: RemoteUser, activity: ICreate): Promise<void> {
|
||||||
const uri = getApId(activity);
|
const uri = getApId(activity);
|
||||||
|
|
||||||
this.logger.info(`Create: ${uri}`);
|
this.logger.info(`Create: ${uri}`);
|
||||||
@ -396,7 +366,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async createNote(resolver: Resolver, actor: CacheableRemoteUser, note: IObject, silent = false, activity?: ICreate): Promise<string> {
|
private async createNote(resolver: Resolver, actor: RemoteUser, note: IObject, silent = false, activity?: ICreate): Promise<string> {
|
||||||
const uri = getApId(note);
|
const uri = getApId(note);
|
||||||
|
|
||||||
if (typeof note === 'object') {
|
if (typeof note === 'object') {
|
||||||
@ -431,7 +401,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async delete(actor: CacheableRemoteUser, activity: IDelete): Promise<string> {
|
private async delete(actor: RemoteUser, activity: IDelete): Promise<string> {
|
||||||
if ('actor' in activity && actor.uri !== activity.actor) {
|
if ('actor' in activity && actor.uri !== activity.actor) {
|
||||||
throw new Error('invalid actor');
|
throw new Error('invalid actor');
|
||||||
}
|
}
|
||||||
@ -473,7 +443,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async deleteActor(actor: CacheableRemoteUser, uri: string): Promise<string> {
|
private async deleteActor(actor: RemoteUser, 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) {
|
||||||
@ -495,7 +465,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async deleteNote(actor: CacheableRemoteUser, uri: string): Promise<string> {
|
private async deleteNote(actor: RemoteUser, 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);
|
||||||
@ -504,16 +474,7 @@ export class ApInboxService {
|
|||||||
const note = await this.apDbResolverService.getNoteFromApId(uri);
|
const note = await this.apDbResolverService.getNoteFromApId(uri);
|
||||||
|
|
||||||
if (note == null) {
|
if (note == null) {
|
||||||
const message = await this.apDbResolverService.getMessageFromApId(uri);
|
return 'message not found';
|
||||||
if (message == null) return 'message not found';
|
|
||||||
|
|
||||||
if (message.userId !== actor.id) {
|
|
||||||
return '投稿を削除しようとしているユーザーは投稿の作成者ではありません';
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.messagingService.deleteMessage(message);
|
|
||||||
|
|
||||||
return 'ok: message deleted';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (note.userId !== actor.id) {
|
if (note.userId !== actor.id) {
|
||||||
@ -528,7 +489,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async flag(actor: CacheableRemoteUser, activity: IFlag): Promise<string> {
|
private async flag(actor: RemoteUser, 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);
|
||||||
@ -553,7 +514,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async reject(actor: CacheableRemoteUser, activity: IReject): Promise<string> {
|
private async reject(actor: RemoteUser, activity: IReject): Promise<string> {
|
||||||
const uri = activity.id ?? activity;
|
const uri = activity.id ?? activity;
|
||||||
|
|
||||||
this.logger.info(`Reject: ${uri}`);
|
this.logger.info(`Reject: ${uri}`);
|
||||||
@ -571,7 +532,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async rejectFollow(actor: CacheableRemoteUser, activity: IFollow): Promise<string> {
|
private async rejectFollow(actor: RemoteUser, 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);
|
||||||
@ -595,7 +556,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async remove(actor: CacheableRemoteUser, activity: IRemove): Promise<void> {
|
private async remove(actor: RemoteUser, activity: IRemove): Promise<void> {
|
||||||
if ('actor' in activity && actor.uri !== activity.actor) {
|
if ('actor' in activity && actor.uri !== activity.actor) {
|
||||||
throw new Error('invalid actor');
|
throw new Error('invalid actor');
|
||||||
}
|
}
|
||||||
@ -615,7 +576,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async undo(actor: CacheableRemoteUser, activity: IUndo): Promise<string> {
|
private async undo(actor: RemoteUser, activity: IUndo): Promise<string> {
|
||||||
if ('actor' in activity && actor.uri !== activity.actor) {
|
if ('actor' in activity && actor.uri !== activity.actor) {
|
||||||
throw new Error('invalid actor');
|
throw new Error('invalid actor');
|
||||||
}
|
}
|
||||||
@ -641,7 +602,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async undoAccept(actor: CacheableRemoteUser, activity: IAccept): Promise<string> {
|
private async undoAccept(actor: RemoteUser, 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';
|
||||||
@ -661,7 +622,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async undoAnnounce(actor: CacheableRemoteUser, activity: IAnnounce): Promise<string> {
|
private async undoAnnounce(actor: RemoteUser, activity: IAnnounce): Promise<string> {
|
||||||
const uri = getApId(activity);
|
const uri = getApId(activity);
|
||||||
|
|
||||||
const note = await this.notesRepository.findOneBy({
|
const note = await this.notesRepository.findOneBy({
|
||||||
@ -676,7 +637,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async undoBlock(actor: CacheableRemoteUser, activity: IBlock): Promise<string> {
|
private async undoBlock(actor: RemoteUser, 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) {
|
||||||
@ -692,7 +653,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async undoFollow(actor: CacheableRemoteUser, activity: IFollow): Promise<string> {
|
private async undoFollow(actor: RemoteUser, 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';
|
||||||
@ -726,7 +687,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async undoLike(actor: CacheableRemoteUser, activity: ILike): Promise<string> {
|
private async undoLike(actor: RemoteUser, 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);
|
||||||
@ -741,7 +702,7 @@ export class ApInboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async update(actor: CacheableRemoteUser, activity: IUpdate): Promise<string> {
|
private async update(actor: RemoteUser, activity: IUpdate): Promise<string> {
|
||||||
if ('actor' in activity && actor.uri !== activity.actor) {
|
if ('actor' in activity && actor.uri !== activity.actor) {
|
||||||
return 'skip: invalid actor';
|
return 'skip: invalid actor';
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import { v4 as uuid } from 'uuid';
|
|||||||
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 { ILocalUser, IRemoteUser, User } from '@/models/entities/User.js';
|
import type { LocalUser, RemoteUser, User } from '@/models/entities/User.js';
|
||||||
import type { IMentionedRemoteUsers, Note } from '@/models/entities/Note.js';
|
import type { IMentionedRemoteUsers, Note } from '@/models/entities/Note.js';
|
||||||
import type { Blocking } from '@/models/entities/Blocking.js';
|
import type { Blocking } from '@/models/entities/Blocking.js';
|
||||||
import type { Relay } from '@/models/entities/Relay.js';
|
import type { Relay } from '@/models/entities/Relay.js';
|
||||||
@ -13,7 +13,6 @@ import type { DriveFile } from '@/models/entities/DriveFile.js';
|
|||||||
import type { NoteReaction } from '@/models/entities/NoteReaction.js';
|
import type { NoteReaction } from '@/models/entities/NoteReaction.js';
|
||||||
import type { Emoji } from '@/models/entities/Emoji.js';
|
import type { Emoji } from '@/models/entities/Emoji.js';
|
||||||
import type { Poll } from '@/models/entities/Poll.js';
|
import type { Poll } from '@/models/entities/Poll.js';
|
||||||
import type { MessagingMessage } from '@/models/entities/MessagingMessage.js';
|
|
||||||
import type { PollVote } from '@/models/entities/PollVote.js';
|
import type { PollVote } from '@/models/entities/PollVote.js';
|
||||||
import { UserKeypairStoreService } from '@/core/UserKeypairStoreService.js';
|
import { UserKeypairStoreService } from '@/core/UserKeypairStoreService.js';
|
||||||
import { MfmService } from '@/core/MfmService.js';
|
import { MfmService } from '@/core/MfmService.js';
|
||||||
@ -24,7 +23,7 @@ import type { UsersRepository, UserProfilesRepository, NotesRepository, DriveFil
|
|||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { LdSignatureService } from './LdSignatureService.js';
|
import { LdSignatureService } from './LdSignatureService.js';
|
||||||
import { ApMfmService } from './ApMfmService.js';
|
import { ApMfmService } from './ApMfmService.js';
|
||||||
import type { IActivity, IObject } from './type.js';
|
import type { IAccept, IActivity, IAdd, IAnnounce, IApDocument, IApEmoji, IApHashtag, IApImage, IApMention, IBlock, ICreate, IDelete, IFlag, IFollow, IKey, ILike, IObject, IPost, IQuestion, IRead, IReject, IRemove, ITombstone, IUndo, IUpdate } from './type.js';
|
||||||
import type { IIdentifier } from './models/identifier.js';
|
import type { IIdentifier } from './models/identifier.js';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -61,7 +60,7 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderAccept(object: any, user: { id: User['id']; host: null }) {
|
public renderAccept(object: any, user: { id: User['id']; host: null }): IAccept {
|
||||||
return {
|
return {
|
||||||
type: 'Accept',
|
type: 'Accept',
|
||||||
actor: `${this.config.url}/users/${user.id}`,
|
actor: `${this.config.url}/users/${user.id}`,
|
||||||
@ -70,7 +69,7 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderAdd(user: ILocalUser, target: any, object: any) {
|
public renderAdd(user: LocalUser, target: any, object: any): IAdd {
|
||||||
return {
|
return {
|
||||||
type: 'Add',
|
type: 'Add',
|
||||||
actor: `${this.config.url}/users/${user.id}`,
|
actor: `${this.config.url}/users/${user.id}`,
|
||||||
@ -80,7 +79,7 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderAnnounce(object: any, note: Note) {
|
public renderAnnounce(object: any, note: Note): IAnnounce {
|
||||||
const attributedTo = `${this.config.url}/users/${note.userId}`;
|
const attributedTo = `${this.config.url}/users/${note.userId}`;
|
||||||
|
|
||||||
let to: string[] = [];
|
let to: string[] = [];
|
||||||
@ -93,7 +92,7 @@ export class ApRendererService {
|
|||||||
to = [`${attributedTo}/followers`];
|
to = [`${attributedTo}/followers`];
|
||||||
cc = ['https://www.w3.org/ns/activitystreams#Public'];
|
cc = ['https://www.w3.org/ns/activitystreams#Public'];
|
||||||
} else {
|
} else {
|
||||||
return null;
|
throw new Error('renderAnnounce: cannot render non-public note');
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -113,7 +112,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) {
|
public renderBlock(block: Blocking): 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');
|
||||||
}
|
}
|
||||||
@ -127,14 +126,14 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderCreate(object: any, note: Note) {
|
public renderCreate(object: IObject, note: Note): ICreate {
|
||||||
const activity = {
|
const activity = {
|
||||||
id: `${this.config.url}/notes/${note.id}/activity`,
|
id: `${this.config.url}/notes/${note.id}/activity`,
|
||||||
actor: `${this.config.url}/users/${note.userId}`,
|
actor: `${this.config.url}/users/${note.userId}`,
|
||||||
type: 'Create',
|
type: 'Create',
|
||||||
published: note.createdAt.toISOString(),
|
published: note.createdAt.toISOString(),
|
||||||
object,
|
object,
|
||||||
} as any;
|
} as ICreate;
|
||||||
|
|
||||||
if (object.to) activity.to = object.to;
|
if (object.to) activity.to = object.to;
|
||||||
if (object.cc) activity.cc = object.cc;
|
if (object.cc) activity.cc = object.cc;
|
||||||
@ -143,7 +142,7 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderDelete(object: any, user: { id: User['id']; host: null }) {
|
public renderDelete(object: IObject | string, user: { id: User['id']; host: null }): IDelete {
|
||||||
return {
|
return {
|
||||||
type: 'Delete',
|
type: 'Delete',
|
||||||
actor: `${this.config.url}/users/${user.id}`,
|
actor: `${this.config.url}/users/${user.id}`,
|
||||||
@ -153,7 +152,7 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderDocument(file: DriveFile) {
|
public renderDocument(file: DriveFile): IApDocument {
|
||||||
return {
|
return {
|
||||||
type: 'Document',
|
type: 'Document',
|
||||||
mediaType: file.type,
|
mediaType: file.type,
|
||||||
@ -163,12 +162,12 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderEmoji(emoji: Emoji) {
|
public renderEmoji(emoji: Emoji): IApEmoji {
|
||||||
return {
|
return {
|
||||||
id: `${this.config.url}/emojis/${emoji.name}`,
|
id: `${this.config.url}/emojis/${emoji.name}`,
|
||||||
type: 'Emoji',
|
type: 'Emoji',
|
||||||
name: `:${emoji.name}:`,
|
name: `:${emoji.name}:`,
|
||||||
updated: emoji.updatedAt != null ? emoji.updatedAt.toISOString() : new Date().toISOString,
|
updated: emoji.updatedAt != null ? emoji.updatedAt.toISOString() : new Date().toISOString(),
|
||||||
icon: {
|
icon: {
|
||||||
type: 'Image',
|
type: 'Image',
|
||||||
mediaType: emoji.type ?? 'image/png',
|
mediaType: emoji.type ?? 'image/png',
|
||||||
@ -181,7 +180,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
|
||||||
// object has to be a uri or array of uris
|
// object has to be a uri or array of uris
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderFlag(user: ILocalUser, object: [string], content: string) {
|
public renderFlag(user: LocalUser, object: IObject | string | string[], content: string): IFlag {
|
||||||
return {
|
return {
|
||||||
type: 'Flag',
|
type: 'Flag',
|
||||||
actor: `${this.config.url}/users/${user.id}`,
|
actor: `${this.config.url}/users/${user.id}`,
|
||||||
@ -191,15 +190,13 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderFollowRelay(relay: Relay, relayActor: ILocalUser) {
|
public renderFollowRelay(relay: Relay, relayActor: LocalUser): IFollow {
|
||||||
const follow = {
|
return {
|
||||||
id: `${this.config.url}/activities/follow-relay/${relay.id}`,
|
id: `${this.config.url}/activities/follow-relay/${relay.id}`,
|
||||||
type: 'Follow',
|
type: 'Follow',
|
||||||
actor: `${this.config.url}/users/${relayActor.id}`,
|
actor: `${this.config.url}/users/${relayActor.id}`,
|
||||||
object: 'https://www.w3.org/ns/activitystreams#Public',
|
object: 'https://www.w3.org/ns/activitystreams#Public',
|
||||||
};
|
};
|
||||||
|
|
||||||
return follow;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -217,19 +214,17 @@ export class ApRendererService {
|
|||||||
follower: { id: User['id']; host: User['host']; uri: User['host'] },
|
follower: { id: User['id']; host: User['host']; uri: User['host'] },
|
||||||
followee: { id: User['id']; host: User['host']; uri: User['host'] },
|
followee: { id: User['id']; host: User['host']; uri: User['host'] },
|
||||||
requestId?: string,
|
requestId?: string,
|
||||||
) {
|
): IFollow {
|
||||||
const follow = {
|
return {
|
||||||
id: requestId ?? `${this.config.url}/follows/${follower.id}/${followee.id}`,
|
id: requestId ?? `${this.config.url}/follows/${follower.id}/${followee.id}`,
|
||||||
type: 'Follow',
|
type: 'Follow',
|
||||||
actor: this.userEntityService.isLocalUser(follower) ? `${this.config.url}/users/${follower.id}` : follower.uri,
|
actor: this.userEntityService.isLocalUser(follower) ? `${this.config.url}/users/${follower.id}` : follower.uri!,
|
||||||
object: this.userEntityService.isLocalUser(followee) ? `${this.config.url}/users/${followee.id}` : followee.uri,
|
object: this.userEntityService.isLocalUser(followee) ? `${this.config.url}/users/${followee.id}` : followee.uri!,
|
||||||
} as any;
|
};
|
||||||
|
|
||||||
return follow;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderHashtag(tag: string) {
|
public renderHashtag(tag: string): IApHashtag {
|
||||||
return {
|
return {
|
||||||
type: 'Hashtag',
|
type: 'Hashtag',
|
||||||
href: `${this.config.url}/tags/${encodeURIComponent(tag)}`,
|
href: `${this.config.url}/tags/${encodeURIComponent(tag)}`,
|
||||||
@ -238,7 +233,7 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderImage(file: DriveFile) {
|
public renderImage(file: DriveFile): IApImage {
|
||||||
return {
|
return {
|
||||||
type: 'Image',
|
type: 'Image',
|
||||||
url: this.driveFileEntityService.getPublicUrl(file),
|
url: this.driveFileEntityService.getPublicUrl(file),
|
||||||
@ -248,7 +243,7 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderKey(user: ILocalUser, key: UserKeypair, postfix?: string) {
|
public renderKey(user: LocalUser, key: UserKeypair, 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',
|
||||||
@ -261,7 +256,7 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async renderLike(noteReaction: NoteReaction, note: { uri: string | null }) {
|
public async renderLike(noteReaction: NoteReaction, note: { uri: string | null }): Promise<ILike> {
|
||||||
const reaction = noteReaction.reaction;
|
const reaction = noteReaction.reaction;
|
||||||
|
|
||||||
const object = {
|
const object = {
|
||||||
@ -271,10 +266,11 @@ export class ApRendererService {
|
|||||||
object: note.uri ? note.uri : `${this.config.url}/notes/${noteReaction.noteId}`,
|
object: note.uri ? note.uri : `${this.config.url}/notes/${noteReaction.noteId}`,
|
||||||
content: reaction,
|
content: reaction,
|
||||||
_misskey_reaction: reaction,
|
_misskey_reaction: reaction,
|
||||||
} as any;
|
} as ILike;
|
||||||
|
|
||||||
if (reaction.startsWith(':')) {
|
if (reaction.startsWith(':')) {
|
||||||
const name = reaction.replaceAll(':', '');
|
const name = reaction.replaceAll(':', '');
|
||||||
|
// TODO: cache
|
||||||
const emoji = await this.emojisRepository.findOneBy({
|
const emoji = await this.emojisRepository.findOneBy({
|
||||||
name,
|
name,
|
||||||
host: IsNull(),
|
host: IsNull(),
|
||||||
@ -287,16 +283,16 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderMention(mention: User) {
|
public renderMention(mention: User): IApMention {
|
||||||
return {
|
return {
|
||||||
type: 'Mention',
|
type: 'Mention',
|
||||||
href: this.userEntityService.isRemoteUser(mention) ? mention.uri : `${this.config.url}/users/${(mention as ILocalUser).id}`,
|
href: this.userEntityService.isRemoteUser(mention) ? mention.uri! : `${this.config.url}/users/${(mention as LocalUser).id}`,
|
||||||
name: this.userEntityService.isRemoteUser(mention) ? `@${mention.username}@${mention.host}` : `@${(mention as ILocalUser).username}`,
|
name: this.userEntityService.isRemoteUser(mention) ? `@${mention.username}@${mention.host}` : `@${(mention as LocalUser).username}`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async renderNote(note: Note, dive = true, isTalk = false): Promise<IObject> {
|
public async renderNote(note: Note, dive = true): Promise<IPost> {
|
||||||
const getPromisedFiles = async (ids: string[]) => {
|
const getPromisedFiles = async (ids: string[]) => {
|
||||||
if (!ids || ids.length === 0) return [];
|
if (!ids || ids.length === 0) return [];
|
||||||
const items = await this.driveFilesRepository.findBy({ id: In(ids) });
|
const items = await this.driveFilesRepository.findBy({ id: In(ids) });
|
||||||
@ -409,12 +405,8 @@ export class ApRendererService {
|
|||||||
totalItems: poll!.votes[i],
|
totalItems: poll!.votes[i],
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
} : {};
|
} as const : {};
|
||||||
|
|
||||||
const asTalk = isTalk ? {
|
|
||||||
_misskey_talk: true,
|
|
||||||
} : {};
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: `${this.config.url}/notes/${note.id}`,
|
id: `${this.config.url}/notes/${note.id}`,
|
||||||
type: 'Note',
|
type: 'Note',
|
||||||
@ -436,12 +428,11 @@ export class ApRendererService {
|
|||||||
sensitive: note.cw != null || files.some(file => file.isSensitive),
|
sensitive: note.cw != null || files.some(file => file.isSensitive),
|
||||||
tag,
|
tag,
|
||||||
...asPoll,
|
...asPoll,
|
||||||
...asTalk,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async renderPerson(user: ILocalUser) {
|
public async renderPerson(user: LocalUser) {
|
||||||
const id = `${this.config.url}/users/${user.id}`;
|
const id = `${this.config.url}/users/${user.id}`;
|
||||||
const isSystem = !!user.username.match(/\./);
|
const isSystem = !!user.username.match(/\./);
|
||||||
|
|
||||||
@ -518,8 +509,8 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async renderQuestion(user: { id: User['id'] }, note: Note, poll: Poll) {
|
public renderQuestion(user: { id: User['id'] }, note: Note, poll: Poll): IQuestion {
|
||||||
const question = {
|
return {
|
||||||
type: 'Question',
|
type: 'Question',
|
||||||
id: `${this.config.url}/questions/${note.id}`,
|
id: `${this.config.url}/questions/${note.id}`,
|
||||||
actor: `${this.config.url}/users/${user.id}`,
|
actor: `${this.config.url}/users/${user.id}`,
|
||||||
@ -533,21 +524,10 @@ export class ApRendererService {
|
|||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
|
|
||||||
return question;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderRead(user: { id: User['id'] }, message: MessagingMessage) {
|
public renderReject(object: any, user: { id: User['id'] }): IReject {
|
||||||
return {
|
|
||||||
type: 'Read',
|
|
||||||
actor: `${this.config.url}/users/${user.id}`,
|
|
||||||
object: message.uri,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@bindThis
|
|
||||||
public renderReject(object: any, user: { id: User['id'] }) {
|
|
||||||
return {
|
return {
|
||||||
type: 'Reject',
|
type: 'Reject',
|
||||||
actor: `${this.config.url}/users/${user.id}`,
|
actor: `${this.config.url}/users/${user.id}`,
|
||||||
@ -556,7 +536,7 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderRemove(user: { id: User['id'] }, target: any, object: any) {
|
public renderRemove(user: { id: User['id'] }, target: any, object: any): IRemove {
|
||||||
return {
|
return {
|
||||||
type: 'Remove',
|
type: 'Remove',
|
||||||
actor: `${this.config.url}/users/${user.id}`,
|
actor: `${this.config.url}/users/${user.id}`,
|
||||||
@ -566,7 +546,7 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderTombstone(id: string) {
|
public renderTombstone(id: string): ITombstone {
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
type: 'Tombstone',
|
type: 'Tombstone',
|
||||||
@ -574,8 +554,7 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderUndo(object: any, user: { id: User['id'] }) {
|
public renderUndo(object: any, user: { id: User['id'] }): IUndo {
|
||||||
if (object == null) return null;
|
|
||||||
const id = typeof object.id === 'string' && object.id.startsWith(this.config.url) ? `${object.id}/undo` : undefined;
|
const id = typeof object.id === 'string' && object.id.startsWith(this.config.url) ? `${object.id}/undo` : undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -588,21 +567,19 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderUpdate(object: any, user: { id: User['id'] }) {
|
public renderUpdate(object: any, user: { id: User['id'] }): IUpdate {
|
||||||
const activity = {
|
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.config.url}/users/${user.id}`,
|
actor: `${this.config.url}/users/${user.id}`,
|
||||||
type: 'Update',
|
type: 'Update',
|
||||||
to: ['https://www.w3.org/ns/activitystreams#Public'],
|
to: ['https://www.w3.org/ns/activitystreams#Public'],
|
||||||
object,
|
object,
|
||||||
published: new Date().toISOString(),
|
published: new Date().toISOString(),
|
||||||
} as any;
|
};
|
||||||
|
|
||||||
return activity;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderVote(user: { id: User['id'] }, vote: PollVote, note: Note, poll: Poll, pollOwner: IRemoteUser) {
|
public renderVote(user: { id: User['id'] }, vote: PollVote, note: Note, poll: Poll, pollOwner: RemoteUser): 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.config.url}/users/${user.id}`,
|
actor: `${this.config.url}/users/${user.id}`,
|
||||||
@ -621,9 +598,7 @@ export class ApRendererService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public renderActivity(x: any): IActivity | null {
|
public addContext<T extends IObject>(x: T): T & { '@context': any; id: string; } {
|
||||||
if (x == null) return null;
|
|
||||||
|
|
||||||
if (typeof x === 'object' && x.id == null) {
|
if (typeof x === 'object' && x.id == null) {
|
||||||
x.id = `${this.config.url}/${uuid()}`;
|
x.id = `${this.config.url}/${uuid()}`;
|
||||||
}
|
}
|
||||||
@ -653,13 +628,12 @@ export class ApRendererService {
|
|||||||
'_misskey_quote': 'misskey:_misskey_quote',
|
'_misskey_quote': 'misskey:_misskey_quote',
|
||||||
'_misskey_reaction': 'misskey:_misskey_reaction',
|
'_misskey_reaction': 'misskey:_misskey_reaction',
|
||||||
'_misskey_votes': 'misskey:_misskey_votes',
|
'_misskey_votes': 'misskey:_misskey_votes',
|
||||||
'_misskey_talk': 'misskey:_misskey_talk',
|
|
||||||
'isCat': 'misskey:isCat',
|
'isCat': 'misskey:isCat',
|
||||||
// vcard
|
// vcard
|
||||||
vcard: 'http://www.w3.org/2006/vcard/ns#',
|
vcard: 'http://www.w3.org/2006/vcard/ns#',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}, x);
|
}, x as T & { id: string; });
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import type { ILocalUser } from '@/models/entities/User.js';
|
import type { LocalUser } 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';
|
||||||
@ -18,7 +18,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?: ILocalUser;
|
private user?: LocalUser;
|
||||||
private logger: Logger;
|
private logger: Logger;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -38,8 +38,7 @@ export class Resolver {
|
|||||||
private recursionLimit = 100,
|
private recursionLimit = 100,
|
||||||
) {
|
) {
|
||||||
this.history = new Set();
|
this.history = new Set();
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
this.logger = this.loggerService.getLogger('ap-resolve');
|
||||||
this.logger = this.loggerService?.getLogger('ap-resolve'); // なぜか TypeError: Cannot read properties of undefined (reading 'getLogger') と言われる
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
@ -124,17 +123,17 @@ export class Resolver {
|
|||||||
switch (parsed.type) {
|
switch (parsed.type) {
|
||||||
case 'notes':
|
case 'notes':
|
||||||
return this.notesRepository.findOneByOrFail({ id: parsed.id })
|
return this.notesRepository.findOneByOrFail({ id: parsed.id })
|
||||||
.then(note => {
|
.then(async note => {
|
||||||
if (parsed.rest === 'activity') {
|
if (parsed.rest === 'activity') {
|
||||||
// this refers to the create activity and not the note itself
|
// this refers to the create activity and not the note itself
|
||||||
return this.apRendererService.renderActivity(this.apRendererService.renderCreate(this.apRendererService.renderNote(note), note));
|
return this.apRendererService.addContext(this.apRendererService.renderCreate(await this.apRendererService.renderNote(note), note));
|
||||||
} else {
|
} else {
|
||||||
return this.apRendererService.renderNote(note);
|
return this.apRendererService.renderNote(note);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
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 ILocalUser));
|
.then(user => this.apRendererService.renderPerson(user as LocalUser));
|
||||||
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([
|
||||||
@ -143,8 +142,8 @@ export class Resolver {
|
|||||||
])
|
])
|
||||||
.then(([note, poll]) => this.apRendererService.renderQuestion({ id: note.userId }, note, poll));
|
.then(([note, poll]) => this.apRendererService.renderQuestion({ id: note.userId }, note, poll));
|
||||||
case 'likes':
|
case 'likes':
|
||||||
return this.noteReactionsRepository.findOneByOrFail({ id: parsed.id }).then(reaction =>
|
return this.noteReactionsRepository.findOneByOrFail({ id: parsed.id }).then(async reaction =>
|
||||||
this.apRendererService.renderActivity(this.apRendererService.renderLike(reaction, { uri: null }))!);
|
this.apRendererService.addContext(await this.apRendererService.renderLike(reaction, { uri: null })));
|
||||||
case 'follows':
|
case 'follows':
|
||||||
// rest should be <followee id>
|
// rest should be <followee id>
|
||||||
if (parsed.rest == null || !/^\w+$/.test(parsed.rest)) throw new Error('resolveLocal: invalid follow URI');
|
if (parsed.rest == null || !/^\w+$/.test(parsed.rest)) throw new Error('resolveLocal: invalid follow URI');
|
||||||
@ -152,7 +151,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.renderActivity(this.apRendererService.renderFollow(follower, followee, url)));
|
.then(([follower, followee]) => this.apRendererService.addContext(this.apRendererService.renderFollow(follower, followee, url)));
|
||||||
default:
|
default:
|
||||||
throw new Error(`resolveLocal: type ${parsed.type} unhandled`);
|
throw new Error(`resolveLocal: type ${parsed.type} unhandled`);
|
||||||
}
|
}
|
||||||
@ -184,6 +183,7 @@ export class ApResolverService {
|
|||||||
private httpRequestService: HttpRequestService,
|
private httpRequestService: HttpRequestService,
|
||||||
private apRendererService: ApRendererService,
|
private apRendererService: ApRendererService,
|
||||||
private apDbResolverService: ApDbResolverService,
|
private apDbResolverService: ApDbResolverService,
|
||||||
|
private loggerService: LoggerService,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,6 +202,7 @@ export class ApResolverService {
|
|||||||
this.httpRequestService,
|
this.httpRequestService,
|
||||||
this.apRendererService,
|
this.apRendererService,
|
||||||
this.apDbResolverService,
|
this.apDbResolverService,
|
||||||
|
this.loggerService,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import * as crypto from 'node:crypto';
|
import * as crypto from 'node:crypto';
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import jsonld from 'jsonld';
|
|
||||||
import { HttpRequestService } from '@/core/HttpRequestService.js';
|
import { HttpRequestService } from '@/core/HttpRequestService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { CONTEXTS } from './misc/contexts.js';
|
import { CONTEXTS } from './misc/contexts.js';
|
||||||
@ -85,7 +84,9 @@ class LdSignature {
|
|||||||
@bindThis
|
@bindThis
|
||||||
public async normalize(data: any) {
|
public async normalize(data: any) {
|
||||||
const customLoader = this.getLoader();
|
const customLoader = this.getLoader();
|
||||||
return await jsonld.normalize(data, {
|
// XXX: Importing jsonld dynamically since Jest frequently fails to import it statically
|
||||||
|
// https://github.com/misskey-dev/misskey/pull/9894#discussion_r1103753595
|
||||||
|
return (await import('jsonld')).default.normalize(data, {
|
||||||
documentLoader: customLoader,
|
documentLoader: customLoader,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ 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 { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import type { CacheableRemoteUser } from '@/models/entities/User.js';
|
import type { RemoteUser } from '@/models/entities/User.js';
|
||||||
import type { DriveFile } from '@/models/entities/DriveFile.js';
|
import type { DriveFile } 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';
|
||||||
@ -36,7 +36,7 @@ export class ApImageService {
|
|||||||
* Imageを作成します。
|
* Imageを作成します。
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async createImage(actor: CacheableRemoteUser, value: any): Promise<DriveFile> {
|
public async createImage(actor: RemoteUser, value: any): Promise<DriveFile> {
|
||||||
// 投稿者が凍結されていたらスキップ
|
// 投稿者が凍結されていたらスキップ
|
||||||
if (actor.isSuspended) {
|
if (actor.isSuspended) {
|
||||||
throw new Error('actor has been suspended');
|
throw new Error('actor has been suspended');
|
||||||
@ -88,7 +88,7 @@ export class ApImageService {
|
|||||||
* リモートサーバーからフェッチしてMisskeyに登録しそれを返します。
|
* リモートサーバーからフェッチしてMisskeyに登録しそれを返します。
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async resolveImage(actor: CacheableRemoteUser, value: any): Promise<DriveFile> {
|
public async resolveImage(actor: RemoteUser, value: any): Promise<DriveFile> {
|
||||||
// TODO
|
// TODO
|
||||||
|
|
||||||
// リモートサーバーからフェッチしてきて登録
|
// リモートサーバーからフェッチしてきて登録
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import promiseLimit from 'promise-limit';
|
import promiseLimit from 'promise-limit';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { UsersRepository } from '@/models/index.js';
|
import type { User, UsersRepository } from '@/models/index.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import { toArray, unique } from '@/misc/prelude/array.js';
|
import { toArray, unique } from '@/misc/prelude/array.js';
|
||||||
import type { CacheableUser } from '@/models/entities/User.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { isMention } from '../type.js';
|
import { isMention } from '../type.js';
|
||||||
import { ApResolverService, Resolver } from '../ApResolverService.js';
|
import { ApResolverService, 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';
|
||||||
import { bindThis } from '@/decorators.js';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ApMentionService {
|
export class ApMentionService {
|
||||||
@ -26,10 +25,10 @@ export class ApMentionService {
|
|||||||
public async extractApMentions(tags: IObject | IObject[] | null | undefined, resolver: Resolver) {
|
public async extractApMentions(tags: IObject | IObject[] | null | undefined, resolver: Resolver) {
|
||||||
const hrefs = unique(this.extractApMentionObjects(tags).map(x => x.href as string));
|
const hrefs = unique(this.extractApMentionObjects(tags).map(x => x.href as string));
|
||||||
|
|
||||||
const limit = promiseLimit<CacheableUser | null>(2);
|
const limit = promiseLimit<User | 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 CacheableUser => x != null);
|
)).filter((x): x is User => x != null);
|
||||||
|
|
||||||
return mentionedUsers;
|
return mentionedUsers;
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { forwardRef, Inject, Injectable } from '@nestjs/common';
|
import { forwardRef, Inject, Injectable } from '@nestjs/common';
|
||||||
import promiseLimit from 'promise-limit';
|
import promiseLimit from 'promise-limit';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { MessagingMessagesRepository, PollsRepository, EmojisRepository, UsersRepository } from '@/models/index.js';
|
import type { PollsRepository, EmojisRepository, UsersRepository } from '@/models/index.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import type { CacheableRemoteUser } from '@/models/entities/User.js';
|
import type { RemoteUser } from '@/models/entities/User.js';
|
||||||
import type { Note } from '@/models/entities/Note.js';
|
import type { Note } 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 { Emoji } from '@/models/entities/Emoji.js';
|
||||||
@ -16,7 +16,6 @@ import { IdService } from '@/core/IdService.js';
|
|||||||
import { PollService } from '@/core/PollService.js';
|
import { PollService } from '@/core/PollService.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 { MessagingService } from '@/core/MessagingService.js';
|
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { getOneApId, getApId, getOneApHrefNullable, validPost, isEmoji, getApType } from '../type.js';
|
import { getOneApId, getApId, getOneApHrefNullable, validPost, isEmoji, getApType } from '../type.js';
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
||||||
@ -47,9 +46,6 @@ export class ApNoteService {
|
|||||||
@Inject(DI.emojisRepository)
|
@Inject(DI.emojisRepository)
|
||||||
private emojisRepository: EmojisRepository,
|
private emojisRepository: EmojisRepository,
|
||||||
|
|
||||||
@Inject(DI.messagingMessagesRepository)
|
|
||||||
private messagingMessagesRepository: MessagingMessagesRepository,
|
|
||||||
|
|
||||||
private idService: IdService,
|
private idService: IdService,
|
||||||
private apMfmService: ApMfmService,
|
private apMfmService: ApMfmService,
|
||||||
private apResolverService: ApResolverService,
|
private apResolverService: ApResolverService,
|
||||||
@ -64,7 +60,6 @@ export class ApNoteService {
|
|||||||
private apImageService: ApImageService,
|
private apImageService: ApImageService,
|
||||||
private apQuestionService: ApQuestionService,
|
private apQuestionService: ApQuestionService,
|
||||||
private metaService: MetaService,
|
private metaService: MetaService,
|
||||||
private messagingService: MessagingService,
|
|
||||||
private appLockService: AppLockService,
|
private appLockService: AppLockService,
|
||||||
private pollService: PollService,
|
private pollService: PollService,
|
||||||
private noteCreateService: NoteCreateService,
|
private noteCreateService: NoteCreateService,
|
||||||
@ -114,7 +109,7 @@ export class ApNoteService {
|
|||||||
public async createNote(value: string | IObject, resolver?: Resolver, silent = false): Promise<Note | null> {
|
public async createNote(value: string | IObject, resolver?: Resolver, silent = false): Promise<Note | null> {
|
||||||
if (resolver == null) resolver = this.apResolverService.createResolver();
|
if (resolver == null) resolver = this.apResolverService.createResolver();
|
||||||
|
|
||||||
const object: any = await resolver.resolve(value);
|
const object = await resolver.resolve(value);
|
||||||
|
|
||||||
const entryUri = getApId(value);
|
const entryUri = getApId(value);
|
||||||
const err = this.validateNote(object, entryUri);
|
const err = this.validateNote(object, entryUri);
|
||||||
@ -129,7 +124,7 @@ export class ApNoteService {
|
|||||||
throw new Error('invalid note');
|
throw new Error('invalid note');
|
||||||
}
|
}
|
||||||
|
|
||||||
const note: IPost = object;
|
const note: IPost = object as any;
|
||||||
|
|
||||||
this.logger.debug(`Note fetched: ${JSON.stringify(note, null, 2)}`);
|
this.logger.debug(`Note fetched: ${JSON.stringify(note, null, 2)}`);
|
||||||
|
|
||||||
@ -146,7 +141,7 @@ export class ApNoteService {
|
|||||||
this.logger.info(`Creating the Note: ${note.id}`);
|
this.logger.info(`Creating the Note: ${note.id}`);
|
||||||
|
|
||||||
// 投稿者をフェッチ
|
// 投稿者をフェッチ
|
||||||
const actor = await this.apPersonService.resolvePerson(getOneApId(note.attributedTo), resolver) as CacheableRemoteUser;
|
const actor = await this.apPersonService.resolvePerson(getOneApId(note.attributedTo!), resolver) as RemoteUser;
|
||||||
|
|
||||||
// 投稿者が凍結されていたらスキップ
|
// 投稿者が凍結されていたらスキップ
|
||||||
if (actor.isSuspended) {
|
if (actor.isSuspended) {
|
||||||
@ -165,8 +160,6 @@ export class ApNoteService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let isMessaging = note._misskey_talk && visibility === 'specified';
|
|
||||||
|
|
||||||
const apMentions = await this.apMentionService.extractApMentions(note.tag, resolver);
|
const apMentions = await this.apMentionService.extractApMentions(note.tag, resolver);
|
||||||
const apHashtags = await extractApHashtags(note.tag);
|
const apHashtags = await extractApHashtags(note.tag);
|
||||||
|
|
||||||
@ -193,17 +186,6 @@ export class ApNoteService {
|
|||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
}).catch(async err => {
|
}).catch(async err => {
|
||||||
// トークだったらinReplyToのエラーは無視
|
|
||||||
const uri = getApId(note.inReplyTo);
|
|
||||||
if (uri.startsWith(this.config.url + '/')) {
|
|
||||||
const id = uri.split('/').pop();
|
|
||||||
const talk = await this.messagingMessagesRepository.findOneBy({ id });
|
|
||||||
if (talk) {
|
|
||||||
isMessaging = true;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.logger.warn(`Error in inReplyTo ${note.inReplyTo} - ${err.statusCode ?? err}`);
|
this.logger.warn(`Error in inReplyTo ${note.inReplyTo} - ${err.statusCode ?? err}`);
|
||||||
throw err;
|
throw err;
|
||||||
})
|
})
|
||||||
@ -292,14 +274,7 @@ export class ApNoteService {
|
|||||||
const apEmojis = emojis.map(emoji => emoji.name);
|
const apEmojis = emojis.map(emoji => emoji.name);
|
||||||
|
|
||||||
const poll = await this.apQuestionService.extractPollFromQuestion(note, resolver).catch(() => undefined);
|
const poll = await this.apQuestionService.extractPollFromQuestion(note, resolver).catch(() => undefined);
|
||||||
|
|
||||||
if (isMessaging) {
|
|
||||||
for (const recipient of visibleUsers) {
|
|
||||||
await this.messagingService.createMessage(actor, recipient, undefined, text ?? undefined, (files && files.length > 0) ? files[0] : null, object.id);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
||||||
|
@ -5,7 +5,7 @@ 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 { CacheableUser, IRemoteUser } from '@/models/entities/User.js';
|
import type { RemoteUser } from '@/models/entities/User.js';
|
||||||
import { User } from '@/models/entities/User.js';
|
import { User } from '@/models/entities/User.js';
|
||||||
import { truncate } from '@/misc/truncate.js';
|
import { truncate } from '@/misc/truncate.js';
|
||||||
import type { UserCacheService } from '@/core/UserCacheService.js';
|
import type { UserCacheService } from '@/core/UserCacheService.js';
|
||||||
@ -197,7 +197,7 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
* Misskeyに対象のPersonが登録されていればそれを返します。
|
* Misskeyに対象のPersonが登録されていればそれを返します。
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async fetchPerson(uri: string, resolver?: Resolver): Promise<CacheableUser | null> {
|
public async fetchPerson(uri: string, resolver?: Resolver): Promise<User | null> {
|
||||||
if (typeof uri !== 'string') throw new Error('uri is not string');
|
if (typeof uri !== 'string') throw new Error('uri is not string');
|
||||||
|
|
||||||
const cached = this.userCacheService.uriPersonCache.get(uri);
|
const cached = this.userCacheService.uriPersonCache.get(uri);
|
||||||
@ -259,7 +259,7 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create user
|
// Create user
|
||||||
let user: IRemoteUser;
|
let user: RemoteUser;
|
||||||
try {
|
try {
|
||||||
// Start transaction
|
// Start transaction
|
||||||
await this.db.transaction(async transactionalEntityManager => {
|
await this.db.transaction(async transactionalEntityManager => {
|
||||||
@ -284,7 +284,7 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
isBot,
|
isBot,
|
||||||
isCat: (person as any).isCat === true,
|
isCat: (person as any).isCat === true,
|
||||||
showTimelineReplies: false,
|
showTimelineReplies: false,
|
||||||
})) as IRemoteUser;
|
})) as RemoteUser;
|
||||||
|
|
||||||
await transactionalEntityManager.save(new UserProfile({
|
await transactionalEntityManager.save(new UserProfile({
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
@ -313,7 +313,7 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (u) {
|
if (u) {
|
||||||
user = u as IRemoteUser;
|
user = u as RemoteUser;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('already registered');
|
throw new Error('already registered');
|
||||||
}
|
}
|
||||||
@ -392,7 +392,7 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//#region このサーバーに既に登録されているか
|
//#region このサーバーに既に登録されているか
|
||||||
const exist = await this.usersRepository.findOneBy({ uri }) as IRemoteUser;
|
const exist = await this.usersRepository.findOneBy({ uri }) as RemoteUser;
|
||||||
|
|
||||||
if (exist == null) {
|
if (exist == null) {
|
||||||
return;
|
return;
|
||||||
@ -500,7 +500,7 @@ export class ApPersonService implements OnModuleInit {
|
|||||||
* リモートサーバーからフェッチしてMisskeyに登録しそれを返します。
|
* リモートサーバーからフェッチしてMisskeyに登録しそれを返します。
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async resolvePerson(uri: string, resolver?: Resolver): Promise<CacheableUser> {
|
public async resolvePerson(uri: string, resolver?: Resolver): Promise<User> {
|
||||||
if (typeof uri !== 'string') throw new Error('uri is not string');
|
if (typeof uri !== 'string') throw new Error('uri is not string');
|
||||||
|
|
||||||
//#region このサーバーに既に登録されていたらそれを返す
|
//#region このサーバーに既に登録されていたらそれを返す
|
||||||
|
@ -2,24 +2,24 @@ export type obj = { [x: string]: any };
|
|||||||
export type ApObject = IObject | string | (IObject | string)[];
|
export type ApObject = IObject | string | (IObject | string)[];
|
||||||
|
|
||||||
export interface IObject {
|
export interface IObject {
|
||||||
'@context': string | string[] | obj | obj[];
|
'@context'?: string | string[] | obj | obj[];
|
||||||
type: string | string[];
|
type: string | string[];
|
||||||
id?: string;
|
id?: string;
|
||||||
|
name?: string | null;
|
||||||
summary?: string;
|
summary?: string;
|
||||||
published?: string;
|
published?: string;
|
||||||
cc?: ApObject;
|
cc?: ApObject;
|
||||||
to?: ApObject;
|
to?: ApObject;
|
||||||
attributedTo: ApObject;
|
attributedTo?: ApObject;
|
||||||
attachment?: any[];
|
attachment?: any[];
|
||||||
inReplyTo?: any;
|
inReplyTo?: any;
|
||||||
replies?: ICollection;
|
replies?: ICollection;
|
||||||
content?: string;
|
content?: string | null;
|
||||||
name?: string;
|
|
||||||
startTime?: Date;
|
startTime?: Date;
|
||||||
endTime?: Date;
|
endTime?: Date;
|
||||||
icon?: any;
|
icon?: any;
|
||||||
image?: any;
|
image?: any;
|
||||||
url?: ApObject;
|
url?: ApObject | string;
|
||||||
href?: string;
|
href?: string;
|
||||||
tag?: IObject | IObject[];
|
tag?: IObject | IObject[];
|
||||||
sensitive?: boolean;
|
sensitive?: boolean;
|
||||||
@ -113,11 +113,11 @@ export interface IPost extends IObject {
|
|||||||
_misskey_quote?: string;
|
_misskey_quote?: string;
|
||||||
_misskey_content?: string;
|
_misskey_content?: string;
|
||||||
quoteUrl?: string;
|
quoteUrl?: string;
|
||||||
_misskey_talk?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IQuestion extends IObject {
|
export interface IQuestion extends IObject {
|
||||||
type: 'Note' | 'Question';
|
type: 'Note' | 'Question';
|
||||||
|
actor: string;
|
||||||
source?: {
|
source?: {
|
||||||
content: string;
|
content: string;
|
||||||
mediaType: string;
|
mediaType: string;
|
||||||
@ -200,6 +200,7 @@ export const isPropertyValue = (object: IObject): object is IApPropertyValue =>
|
|||||||
export interface IApMention extends IObject {
|
export interface IApMention extends IObject {
|
||||||
type: 'Mention';
|
type: 'Mention';
|
||||||
href: string;
|
href: string;
|
||||||
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isMention = (object: IObject): object is IApMention =>
|
export const isMention = (object: IObject): object is IApMention =>
|
||||||
@ -217,12 +218,30 @@ export const isHashtag = (object: IObject): object is IApHashtag =>
|
|||||||
|
|
||||||
export interface IApEmoji extends IObject {
|
export interface IApEmoji extends IObject {
|
||||||
type: 'Emoji';
|
type: 'Emoji';
|
||||||
updated: Date;
|
name: string;
|
||||||
|
updated: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isEmoji = (object: IObject): object is IApEmoji =>
|
export const isEmoji = (object: IObject): object is IApEmoji =>
|
||||||
getApType(object) === 'Emoji' && !Array.isArray(object.icon) && object.icon.url != null;
|
getApType(object) === 'Emoji' && !Array.isArray(object.icon) && object.icon.url != null;
|
||||||
|
|
||||||
|
export interface IKey extends IObject {
|
||||||
|
type: 'Key';
|
||||||
|
owner: string;
|
||||||
|
publicKeyPem: string | Buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IApDocument extends IObject {
|
||||||
|
type: 'Document';
|
||||||
|
name: string | null;
|
||||||
|
mediaType: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IApImage extends IObject {
|
||||||
|
type: 'Image';
|
||||||
|
name: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ICreate extends IActivity {
|
export interface ICreate extends IActivity {
|
||||||
type: 'Create';
|
type: 'Create';
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
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 { AntennaNotesRepository, AntennasRepository, UserGroupJoiningsRepository } from '@/models/index.js';
|
import type { AntennaNotesRepository, AntennasRepository } from '@/models/index.js';
|
||||||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
import { awaitAll } from '@/misc/prelude/await-all.js';
|
||||||
import type { Packed } from '@/misc/schema.js';
|
import type { Packed } from '@/misc/schema.js';
|
||||||
import type { Antenna } from '@/models/entities/Antenna.js';
|
import type { Antenna } from '@/models/entities/Antenna.js';
|
||||||
@ -14,9 +14,6 @@ export class AntennaEntityService {
|
|||||||
|
|
||||||
@Inject(DI.antennaNotesRepository)
|
@Inject(DI.antennaNotesRepository)
|
||||||
private antennaNotesRepository: AntennaNotesRepository,
|
private antennaNotesRepository: AntennaNotesRepository,
|
||||||
|
|
||||||
@Inject(DI.userGroupJoiningsRepository)
|
|
||||||
private userGroupJoiningsRepository: UserGroupJoiningsRepository,
|
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,7 +24,6 @@ export class AntennaEntityService {
|
|||||||
const antenna = typeof src === 'object' ? src : await this.antennasRepository.findOneByOrFail({ id: src });
|
const antenna = typeof src === 'object' ? src : await this.antennasRepository.findOneByOrFail({ id: src });
|
||||||
|
|
||||||
const hasUnreadNote = (await this.antennaNotesRepository.findOneBy({ antennaId: antenna.id, read: false })) != null;
|
const hasUnreadNote = (await this.antennaNotesRepository.findOneBy({ antennaId: antenna.id, read: false })) != null;
|
||||||
const userGroupJoining = antenna.userGroupJoiningId ? await this.userGroupJoiningsRepository.findOneBy({ id: antenna.userGroupJoiningId }) : null;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: antenna.id,
|
id: antenna.id,
|
||||||
@ -37,7 +33,6 @@ export class AntennaEntityService {
|
|||||||
excludeKeywords: antenna.excludeKeywords,
|
excludeKeywords: antenna.excludeKeywords,
|
||||||
src: antenna.src,
|
src: antenna.src,
|
||||||
userListId: antenna.userListId,
|
userListId: antenna.userListId,
|
||||||
userGroupId: userGroupJoining ? userGroupJoining.userGroupId : null,
|
|
||||||
users: antenna.users,
|
users: antenna.users,
|
||||||
caseSensitive: antenna.caseSensitive,
|
caseSensitive: antenna.caseSensitive,
|
||||||
notify: antenna.notify,
|
notify: antenna.notify,
|
||||||
|
@ -11,6 +11,7 @@ import type { DriveFile } from '@/models/entities/DriveFile.js';
|
|||||||
import { appendQuery, query } from '@/misc/prelude/url.js';
|
import { appendQuery, query } from '@/misc/prelude/url.js';
|
||||||
import { deepClone } from '@/misc/clone.js';
|
import { deepClone } from '@/misc/clone.js';
|
||||||
import { UtilityService } from '../UtilityService.js';
|
import { UtilityService } from '../UtilityService.js';
|
||||||
|
import { VideoProcessingService } from '../VideoProcessingService.js';
|
||||||
import { UserEntityService } from './UserEntityService.js';
|
import { UserEntityService } from './UserEntityService.js';
|
||||||
import { DriveFolderEntityService } from './DriveFolderEntityService.js';
|
import { DriveFolderEntityService } from './DriveFolderEntityService.js';
|
||||||
|
|
||||||
@ -43,6 +44,7 @@ export class DriveFileEntityService {
|
|||||||
|
|
||||||
private utilityService: UtilityService,
|
private utilityService: UtilityService,
|
||||||
private driveFolderEntityService: DriveFolderEntityService,
|
private driveFolderEntityService: DriveFolderEntityService,
|
||||||
|
private videoProcessingService: VideoProcessingService,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,40 +74,63 @@ export class DriveFileEntityService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public getPublicUrl(file: DriveFile, mode? : 'static' | 'avatar'): string | null { // static = thumbnail
|
private getProxiedUrl(url: string, mode?: 'static' | 'avatar'): string {
|
||||||
const proxiedUrl = (url: string) => appendQuery(
|
return appendQuery(
|
||||||
`${this.config.mediaProxy}/${mode ?? 'image'}.webp`,
|
`${this.config.mediaProxy}/${mode ?? 'image'}.webp`,
|
||||||
query({
|
query({
|
||||||
url,
|
url,
|
||||||
...(mode ? { [mode]: '1' } : {}),
|
...(mode ? { [mode]: '1' } : {}),
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public getThumbnailUrl(file: DriveFile): string | null {
|
||||||
|
if (file.type.startsWith('video')) {
|
||||||
|
if (file.thumbnailUrl) return file.thumbnailUrl;
|
||||||
|
|
||||||
|
if (this.config.videoThumbnailGenerator == null) {
|
||||||
|
return this.videoProcessingService.getExternalVideoThumbnailUrl(file.webpublicUrl ?? file.url ?? file.uri);
|
||||||
|
}
|
||||||
|
} else if (file.uri != null && file.userHost != null && this.config.externalMediaProxyEnabled) {
|
||||||
|
// 動画ではなくリモートかつメディアプロキシ
|
||||||
|
return this.getProxiedUrl(file.uri, 'static');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file.uri != null && file.isLink && this.config.proxyRemoteFiles) {
|
||||||
|
// リモートかつ期限切れはローカルプロキシを試みる
|
||||||
|
// 従来は/files/${thumbnailAccessKey}にアクセスしていたが、
|
||||||
|
// /filesはメディアプロキシにリダイレクトするようにしたため直接メディアプロキシを指定する
|
||||||
|
return this.getProxiedUrl(file.uri, 'static');
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = file.webpublicUrl ?? file.url;
|
||||||
|
|
||||||
|
return file.thumbnailUrl ?? (isMimeImage(file.type, 'sharp-convertible-image') ? this.getProxiedUrl(url, 'static') : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public getPublicUrl(file: DriveFile, mode?: 'avatar'): string { // static = thumbnail
|
||||||
// リモートかつメディアプロキシ
|
// リモートかつメディアプロキシ
|
||||||
if (file.uri != null && file.userHost != null && this.config.externalMediaProxyEnabled) {
|
if (file.uri != null && file.userHost != null && this.config.externalMediaProxyEnabled) {
|
||||||
if (!(mode === 'static' && file.type.startsWith('video'))) {
|
return this.getProxiedUrl(file.uri, mode);
|
||||||
return proxiedUrl(file.uri);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// リモートかつ期限切れはローカルプロキシを試みる
|
// リモートかつ期限切れはローカルプロキシを試みる
|
||||||
if (file.uri != null && file.isLink && this.config.proxyRemoteFiles) {
|
if (file.uri != null && file.isLink && this.config.proxyRemoteFiles) {
|
||||||
const key = mode === 'static' ? file.thumbnailAccessKey : file.webpublicAccessKey;
|
const key = file.webpublicAccessKey;
|
||||||
|
|
||||||
if (key && !key.match('/')) { // 古いものはここにオブジェクトストレージキーが入ってるので除外
|
if (key && !key.match('/')) { // 古いものはここにオブジェクトストレージキーが入ってるので除外
|
||||||
const url = `${this.config.url}/files/${key}`;
|
const url = `${this.config.url}/files/${key}`;
|
||||||
if (mode === 'avatar') return proxiedUrl(file.uri);
|
if (mode === 'avatar') return this.getProxiedUrl(file.uri, 'avatar');
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = file.webpublicUrl ?? file.url;
|
const url = file.webpublicUrl ?? file.url;
|
||||||
|
|
||||||
if (mode === 'static') {
|
|
||||||
return file.thumbnailUrl ?? (isMimeImage(file.type, 'sharp-convertible-image') ? proxiedUrl(url) : null);
|
|
||||||
}
|
|
||||||
if (mode === 'avatar') {
|
if (mode === 'avatar') {
|
||||||
return proxiedUrl(url);
|
return this.getProxiedUrl(url, 'avatar');
|
||||||
}
|
}
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
@ -183,7 +208,7 @@ export class DriveFileEntityService {
|
|||||||
blurhash: file.blurhash,
|
blurhash: file.blurhash,
|
||||||
properties: opts.self ? file.properties : this.getPublicProperties(file),
|
properties: opts.self ? file.properties : this.getPublicProperties(file),
|
||||||
url: opts.self ? file.url : this.getPublicUrl(file),
|
url: opts.self ? file.url : this.getPublicUrl(file),
|
||||||
thumbnailUrl: this.getPublicUrl(file, 'static'),
|
thumbnailUrl: this.getThumbnailUrl(file),
|
||||||
comment: file.comment,
|
comment: file.comment,
|
||||||
folderId: file.folderId,
|
folderId: file.folderId,
|
||||||
folder: opts.detail && file.folderId ? this.driveFolderEntityService.pack(file.folderId, {
|
folder: opts.detail && file.folderId ? this.driveFolderEntityService.pack(file.folderId, {
|
||||||
@ -218,7 +243,7 @@ export class DriveFileEntityService {
|
|||||||
blurhash: file.blurhash,
|
blurhash: file.blurhash,
|
||||||
properties: opts.self ? file.properties : this.getPublicProperties(file),
|
properties: opts.self ? file.properties : this.getPublicProperties(file),
|
||||||
url: opts.self ? file.url : this.getPublicUrl(file),
|
url: opts.self ? file.url : this.getPublicUrl(file),
|
||||||
thumbnailUrl: this.getPublicUrl(file, 'static'),
|
thumbnailUrl: this.getThumbnailUrl(file),
|
||||||
comment: file.comment,
|
comment: file.comment,
|
||||||
folderId: file.folderId,
|
folderId: file.folderId,
|
||||||
folder: opts.detail && file.folderId ? this.driveFolderEntityService.pack(file.folderId, {
|
folder: opts.detail && file.folderId ? this.driveFolderEntityService.pack(file.folderId, {
|
||||||
|
@ -1,59 +0,0 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
|
||||||
import { DI } from '@/di-symbols.js';
|
|
||||||
import type { MessagingMessagesRepository } from '@/models/index.js';
|
|
||||||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
|
||||||
import type { Packed } from '@/misc/schema.js';
|
|
||||||
import type { } from '@/models/entities/Blocking.js';
|
|
||||||
import type { User } from '@/models/entities/User.js';
|
|
||||||
import type { MessagingMessage } from '@/models/entities/MessagingMessage.js';
|
|
||||||
import { UserEntityService } from './UserEntityService.js';
|
|
||||||
import { DriveFileEntityService } from './DriveFileEntityService.js';
|
|
||||||
import { UserGroupEntityService } from './UserGroupEntityService.js';
|
|
||||||
import { bindThis } from '@/decorators.js';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class MessagingMessageEntityService {
|
|
||||||
constructor(
|
|
||||||
@Inject(DI.messagingMessagesRepository)
|
|
||||||
private messagingMessagesRepository: MessagingMessagesRepository,
|
|
||||||
|
|
||||||
private userEntityService: UserEntityService,
|
|
||||||
private userGroupEntityService: UserGroupEntityService,
|
|
||||||
private driveFileEntityService: DriveFileEntityService,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@bindThis
|
|
||||||
public async pack(
|
|
||||||
src: MessagingMessage['id'] | MessagingMessage,
|
|
||||||
me?: { id: User['id'] } | null | undefined,
|
|
||||||
options?: {
|
|
||||||
populateRecipient?: boolean,
|
|
||||||
populateGroup?: boolean,
|
|
||||||
},
|
|
||||||
): Promise<Packed<'MessagingMessage'>> {
|
|
||||||
const opts = options ?? {
|
|
||||||
populateRecipient: true,
|
|
||||||
populateGroup: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
const message = typeof src === 'object' ? src : await this.messagingMessagesRepository.findOneByOrFail({ id: src });
|
|
||||||
|
|
||||||
return {
|
|
||||||
id: message.id,
|
|
||||||
createdAt: message.createdAt.toISOString(),
|
|
||||||
text: message.text,
|
|
||||||
userId: message.userId,
|
|
||||||
user: await this.userEntityService.pack(message.user ?? message.userId, me),
|
|
||||||
recipientId: message.recipientId,
|
|
||||||
recipient: message.recipientId && opts.populateRecipient ? await this.userEntityService.pack(message.recipient ?? message.recipientId, me) : undefined,
|
|
||||||
groupId: message.groupId,
|
|
||||||
group: message.groupId && opts.populateGroup ? await this.userGroupEntityService.pack(message.group ?? message.groupId) : undefined,
|
|
||||||
fileId: message.fileId,
|
|
||||||
file: message.fileId ? await this.driveFileEntityService.pack(message.fileId) : null,
|
|
||||||
isRead: message.isRead,
|
|
||||||
reads: message.reads,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -13,13 +13,11 @@ import type { OnModuleInit } from '@nestjs/common';
|
|||||||
import type { CustomEmojiService } from '../CustomEmojiService.js';
|
import type { CustomEmojiService } from '../CustomEmojiService.js';
|
||||||
import type { UserEntityService } from './UserEntityService.js';
|
import type { UserEntityService } from './UserEntityService.js';
|
||||||
import type { NoteEntityService } from './NoteEntityService.js';
|
import type { NoteEntityService } from './NoteEntityService.js';
|
||||||
import type { UserGroupInvitationEntityService } from './UserGroupInvitationEntityService.js';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class NotificationEntityService implements OnModuleInit {
|
export class NotificationEntityService implements OnModuleInit {
|
||||||
private userEntityService: UserEntityService;
|
private userEntityService: UserEntityService;
|
||||||
private noteEntityService: NoteEntityService;
|
private noteEntityService: NoteEntityService;
|
||||||
private userGroupInvitationEntityService: UserGroupInvitationEntityService;
|
|
||||||
private customEmojiService: CustomEmojiService;
|
private customEmojiService: CustomEmojiService;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -36,7 +34,6 @@ export class NotificationEntityService implements OnModuleInit {
|
|||||||
|
|
||||||
//private userEntityService: UserEntityService,
|
//private userEntityService: UserEntityService,
|
||||||
//private noteEntityService: NoteEntityService,
|
//private noteEntityService: NoteEntityService,
|
||||||
//private userGroupInvitationEntityService: UserGroupInvitationEntityService,
|
|
||||||
//private customEmojiService: CustomEmojiService,
|
//private customEmojiService: CustomEmojiService,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
@ -44,7 +41,6 @@ export class NotificationEntityService implements OnModuleInit {
|
|||||||
onModuleInit() {
|
onModuleInit() {
|
||||||
this.userEntityService = this.moduleRef.get('UserEntityService');
|
this.userEntityService = this.moduleRef.get('UserEntityService');
|
||||||
this.noteEntityService = this.moduleRef.get('NoteEntityService');
|
this.noteEntityService = this.moduleRef.get('NoteEntityService');
|
||||||
this.userGroupInvitationEntityService = this.moduleRef.get('UserGroupInvitationEntityService');
|
|
||||||
this.customEmojiService = this.moduleRef.get('CustomEmojiService');
|
this.customEmojiService = this.moduleRef.get('CustomEmojiService');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,9 +107,6 @@ export class NotificationEntityService implements OnModuleInit {
|
|||||||
_hint_: options._hintForEachNotes_,
|
_hint_: options._hintForEachNotes_,
|
||||||
}),
|
}),
|
||||||
} : {}),
|
} : {}),
|
||||||
...(notification.type === 'groupInvited' ? {
|
|
||||||
invitation: this.userGroupInvitationEntityService.pack(notification.userGroupInvitationId!),
|
|
||||||
} : {}),
|
|
||||||
...(notification.type === 'achievementEarned' ? {
|
...(notification.type === 'achievementEarned' ? {
|
||||||
achievement: notification.achievement,
|
achievement: notification.achievement,
|
||||||
} : {}),
|
} : {}),
|
||||||
|
@ -10,9 +10,9 @@ import { awaitAll } from '@/misc/prelude/await-all.js';
|
|||||||
import { USER_ACTIVE_THRESHOLD, USER_ONLINE_THRESHOLD } from '@/const.js';
|
import { USER_ACTIVE_THRESHOLD, USER_ONLINE_THRESHOLD } from '@/const.js';
|
||||||
import { Cache } from '@/misc/cache.js';
|
import { Cache } from '@/misc/cache.js';
|
||||||
import type { Instance } from '@/models/entities/Instance.js';
|
import type { Instance } from '@/models/entities/Instance.js';
|
||||||
import type { ILocalUser, IRemoteUser, User } from '@/models/entities/User.js';
|
import type { LocalUser, RemoteUser, User } from '@/models/entities/User.js';
|
||||||
import { birthdaySchema, descriptionSchema, localUsernameSchema, locationSchema, nameSchema, passwordSchema } from '@/models/entities/User.js';
|
import { birthdaySchema, descriptionSchema, localUsernameSchema, locationSchema, nameSchema, passwordSchema } from '@/models/entities/User.js';
|
||||||
import type { UsersRepository, UserSecurityKeysRepository, FollowingsRepository, FollowRequestsRepository, BlockingsRepository, MutingsRepository, DriveFilesRepository, NoteUnreadsRepository, ChannelFollowingsRepository, NotificationsRepository, UserNotePiningsRepository, UserProfilesRepository, InstancesRepository, AnnouncementReadsRepository, MessagingMessagesRepository, UserGroupJoiningsRepository, AnnouncementsRepository, AntennaNotesRepository, PagesRepository, UserProfile } from '@/models/index.js';
|
import type { UsersRepository, UserSecurityKeysRepository, FollowingsRepository, FollowRequestsRepository, BlockingsRepository, MutingsRepository, DriveFilesRepository, NoteUnreadsRepository, ChannelFollowingsRepository, NotificationsRepository, UserNotePiningsRepository, UserProfilesRepository, InstancesRepository, AnnouncementReadsRepository, AnnouncementsRepository, AntennaNotesRepository, PagesRepository, UserProfile } from '@/models/index.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { RoleService } from '@/core/RoleService.js';
|
import { RoleService } from '@/core/RoleService.js';
|
||||||
import type { OnModuleInit } from '@nestjs/common';
|
import type { OnModuleInit } from '@nestjs/common';
|
||||||
@ -32,13 +32,13 @@ type IsMeAndIsUserDetailed<ExpectsMe extends boolean | null, Detailed extends bo
|
|||||||
|
|
||||||
const ajv = new Ajv();
|
const ajv = new Ajv();
|
||||||
|
|
||||||
function isLocalUser(user: User): user is ILocalUser;
|
function isLocalUser(user: User): user is LocalUser;
|
||||||
function isLocalUser<T extends { host: User['host'] }>(user: T): user is T & { host: null; };
|
function isLocalUser<T extends { host: User['host'] }>(user: T): user is T & { host: null; };
|
||||||
function isLocalUser(user: User | { host: User['host'] }): boolean {
|
function isLocalUser(user: User | { host: User['host'] }): boolean {
|
||||||
return user.host == null;
|
return user.host == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isRemoteUser(user: User): user is IRemoteUser;
|
function isRemoteUser(user: User): user is RemoteUser;
|
||||||
function isRemoteUser<T extends { host: User['host'] }>(user: T): user is T & { host: string; };
|
function isRemoteUser<T extends { host: User['host'] }>(user: T): user is T & { host: string; };
|
||||||
function isRemoteUser(user: User | { host: User['host'] }): boolean {
|
function isRemoteUser(user: User | { host: User['host'] }): boolean {
|
||||||
return !isLocalUser(user);
|
return !isLocalUser(user);
|
||||||
@ -102,12 +102,6 @@ export class UserEntityService implements OnModuleInit {
|
|||||||
@Inject(DI.announcementReadsRepository)
|
@Inject(DI.announcementReadsRepository)
|
||||||
private announcementReadsRepository: AnnouncementReadsRepository,
|
private announcementReadsRepository: AnnouncementReadsRepository,
|
||||||
|
|
||||||
@Inject(DI.messagingMessagesRepository)
|
|
||||||
private messagingMessagesRepository: MessagingMessagesRepository,
|
|
||||||
|
|
||||||
@Inject(DI.userGroupJoiningsRepository)
|
|
||||||
private userGroupJoiningsRepository: UserGroupJoiningsRepository,
|
|
||||||
|
|
||||||
@Inject(DI.announcementsRepository)
|
@Inject(DI.announcementsRepository)
|
||||||
private announcementsRepository: AnnouncementsRepository,
|
private announcementsRepository: AnnouncementsRepository,
|
||||||
|
|
||||||
@ -204,36 +198,6 @@ export class UserEntityService implements OnModuleInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
|
||||||
public async getHasUnreadMessagingMessage(userId: User['id']): Promise<boolean> {
|
|
||||||
const mute = await this.mutingsRepository.findBy({
|
|
||||||
muterId: userId,
|
|
||||||
});
|
|
||||||
|
|
||||||
const joinings = await this.userGroupJoiningsRepository.findBy({ userId: userId });
|
|
||||||
|
|
||||||
const groupQs = Promise.all(joinings.map(j => this.messagingMessagesRepository.createQueryBuilder('message')
|
|
||||||
.where('message.groupId = :groupId', { groupId: j.userGroupId })
|
|
||||||
.andWhere('message.userId != :userId', { userId: userId })
|
|
||||||
.andWhere('NOT (:userId = ANY(message.reads))', { userId: userId })
|
|
||||||
.andWhere('message.createdAt > :joinedAt', { joinedAt: j.createdAt }) // 自分が加入する前の会話については、未読扱いしない
|
|
||||||
.getOne().then(x => x != null)));
|
|
||||||
|
|
||||||
const [withUser, withGroups] = await Promise.all([
|
|
||||||
this.messagingMessagesRepository.count({
|
|
||||||
where: {
|
|
||||||
recipientId: userId,
|
|
||||||
isRead: false,
|
|
||||||
...(mute.length > 0 ? { userId: Not(In(mute.map(x => x.muteeId))) } : {}),
|
|
||||||
},
|
|
||||||
take: 1,
|
|
||||||
}).then(count => count > 0),
|
|
||||||
groupQs,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return withUser || withGroups.some(x => x);
|
|
||||||
}
|
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async getHasUnreadAnnouncement(userId: User['id']): Promise<boolean> {
|
public async getHasUnreadAnnouncement(userId: User['id']): Promise<boolean> {
|
||||||
const reads = await this.announcementReadsRepository.findBy({
|
const reads = await this.announcementReadsRepository.findBy({
|
||||||
@ -492,7 +456,6 @@ export class UserEntityService implements OnModuleInit {
|
|||||||
hasUnreadAnnouncement: this.getHasUnreadAnnouncement(user.id),
|
hasUnreadAnnouncement: this.getHasUnreadAnnouncement(user.id),
|
||||||
hasUnreadAntenna: this.getHasUnreadAntenna(user.id),
|
hasUnreadAntenna: this.getHasUnreadAntenna(user.id),
|
||||||
hasUnreadChannel: this.getHasUnreadChannel(user.id),
|
hasUnreadChannel: this.getHasUnreadChannel(user.id),
|
||||||
hasUnreadMessagingMessage: this.getHasUnreadMessagingMessage(user.id),
|
|
||||||
hasUnreadNotification: this.getHasUnreadNotification(user.id),
|
hasUnreadNotification: this.getHasUnreadNotification(user.id),
|
||||||
hasPendingReceivedFollowRequest: this.getHasPendingReceivedFollowRequest(user.id),
|
hasPendingReceivedFollowRequest: this.getHasPendingReceivedFollowRequest(user.id),
|
||||||
mutedWords: profile!.mutedWords,
|
mutedWords: profile!.mutedWords,
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
|
||||||
import { DI } from '@/di-symbols.js';
|
|
||||||
import type { UserGroupJoiningsRepository, UserGroupsRepository } from '@/models/index.js';
|
|
||||||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
|
||||||
import type { Packed } from '@/misc/schema.js';
|
|
||||||
import type { } from '@/models/entities/Blocking.js';
|
|
||||||
import type { User } from '@/models/entities/User.js';
|
|
||||||
import type { UserGroup } from '@/models/entities/UserGroup.js';
|
|
||||||
import { UserEntityService } from './UserEntityService.js';
|
|
||||||
import { bindThis } from '@/decorators.js';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class UserGroupEntityService {
|
|
||||||
constructor(
|
|
||||||
@Inject(DI.userGroupsRepository)
|
|
||||||
private userGroupsRepository: UserGroupsRepository,
|
|
||||||
|
|
||||||
@Inject(DI.userGroupJoiningsRepository)
|
|
||||||
private userGroupJoiningsRepository: UserGroupJoiningsRepository,
|
|
||||||
|
|
||||||
private userEntityService: UserEntityService,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@bindThis
|
|
||||||
public async pack(
|
|
||||||
src: UserGroup['id'] | UserGroup,
|
|
||||||
): Promise<Packed<'UserGroup'>> {
|
|
||||||
const userGroup = typeof src === 'object' ? src : await this.userGroupsRepository.findOneByOrFail({ id: src });
|
|
||||||
|
|
||||||
const users = await this.userGroupJoiningsRepository.findBy({
|
|
||||||
userGroupId: userGroup.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
id: userGroup.id,
|
|
||||||
createdAt: userGroup.createdAt.toISOString(),
|
|
||||||
name: userGroup.name,
|
|
||||||
ownerId: userGroup.userId,
|
|
||||||
userIds: users.map(x => x.userId),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
|
||||||
import { DI } from '@/di-symbols.js';
|
|
||||||
import type { UserGroupInvitationsRepository } from '@/models/index.js';
|
|
||||||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
|
||||||
import type { Packed } from '@/misc/schema.js';
|
|
||||||
import type { } from '@/models/entities/Blocking.js';
|
|
||||||
import type { User } from '@/models/entities/User.js';
|
|
||||||
import type { UserGroupInvitation } from '@/models/entities/UserGroupInvitation.js';
|
|
||||||
import { UserEntityService } from './UserEntityService.js';
|
|
||||||
import { UserGroupEntityService } from './UserGroupEntityService.js';
|
|
||||||
import { bindThis } from '@/decorators.js';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class UserGroupInvitationEntityService {
|
|
||||||
constructor(
|
|
||||||
@Inject(DI.userGroupInvitationsRepository)
|
|
||||||
private userGroupInvitationsRepository: UserGroupInvitationsRepository,
|
|
||||||
|
|
||||||
private userGroupEntityService: UserGroupEntityService,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@bindThis
|
|
||||||
public async pack(
|
|
||||||
src: UserGroupInvitation['id'] | UserGroupInvitation,
|
|
||||||
) {
|
|
||||||
const invitation = typeof src === 'object' ? src : await this.userGroupInvitationsRepository.findOneByOrFail({ id: src });
|
|
||||||
|
|
||||||
return {
|
|
||||||
id: invitation.id,
|
|
||||||
group: await this.userGroupEntityService.pack(invitation.userGroup ?? invitation.userGroupId),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@bindThis
|
|
||||||
public packMany(
|
|
||||||
invitations: any[],
|
|
||||||
) {
|
|
||||||
return Promise.all(invitations.map(x => this.pack(x)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -24,9 +24,6 @@ export const DI = {
|
|||||||
userPublickeysRepository: Symbol('userPublickeysRepository'),
|
userPublickeysRepository: Symbol('userPublickeysRepository'),
|
||||||
userListsRepository: Symbol('userListsRepository'),
|
userListsRepository: Symbol('userListsRepository'),
|
||||||
userListJoiningsRepository: Symbol('userListJoiningsRepository'),
|
userListJoiningsRepository: Symbol('userListJoiningsRepository'),
|
||||||
userGroupsRepository: Symbol('userGroupsRepository'),
|
|
||||||
userGroupJoiningsRepository: Symbol('userGroupJoiningsRepository'),
|
|
||||||
userGroupInvitationsRepository: Symbol('userGroupInvitationsRepository'),
|
|
||||||
userNotePiningsRepository: Symbol('userNotePiningsRepository'),
|
userNotePiningsRepository: Symbol('userNotePiningsRepository'),
|
||||||
userIpsRepository: Symbol('userIpsRepository'),
|
userIpsRepository: Symbol('userIpsRepository'),
|
||||||
usedUsernamesRepository: Symbol('usedUsernamesRepository'),
|
usedUsernamesRepository: Symbol('usedUsernamesRepository'),
|
||||||
@ -47,7 +44,6 @@ export const DI = {
|
|||||||
authSessionsRepository: Symbol('authSessionsRepository'),
|
authSessionsRepository: Symbol('authSessionsRepository'),
|
||||||
accessTokensRepository: Symbol('accessTokensRepository'),
|
accessTokensRepository: Symbol('accessTokensRepository'),
|
||||||
signinsRepository: Symbol('signinsRepository'),
|
signinsRepository: Symbol('signinsRepository'),
|
||||||
messagingMessagesRepository: Symbol('messagingMessagesRepository'),
|
|
||||||
pagesRepository: Symbol('pagesRepository'),
|
pagesRepository: Symbol('pagesRepository'),
|
||||||
pageLikesRepository: Symbol('pageLikesRepository'),
|
pageLikesRepository: Symbol('pageLikesRepository'),
|
||||||
galleryPostsRepository: Symbol('galleryPostsRepository'),
|
galleryPostsRepository: Symbol('galleryPostsRepository'),
|
||||||
|
@ -10,7 +10,6 @@ import {
|
|||||||
import { packedNoteSchema } from '@/models/schema/note.js';
|
import { packedNoteSchema } from '@/models/schema/note.js';
|
||||||
import { packedUserListSchema } from '@/models/schema/user-list.js';
|
import { packedUserListSchema } from '@/models/schema/user-list.js';
|
||||||
import { packedAppSchema } from '@/models/schema/app.js';
|
import { packedAppSchema } from '@/models/schema/app.js';
|
||||||
import { packedMessagingMessageSchema } from '@/models/schema/messaging-message.js';
|
|
||||||
import { packedNotificationSchema } from '@/models/schema/notification.js';
|
import { packedNotificationSchema } from '@/models/schema/notification.js';
|
||||||
import { packedDriveFileSchema } from '@/models/schema/drive-file.js';
|
import { packedDriveFileSchema } from '@/models/schema/drive-file.js';
|
||||||
import { packedDriveFolderSchema } from '@/models/schema/drive-folder.js';
|
import { packedDriveFolderSchema } from '@/models/schema/drive-folder.js';
|
||||||
@ -20,7 +19,6 @@ import { packedBlockingSchema } from '@/models/schema/blocking.js';
|
|||||||
import { packedNoteReactionSchema } from '@/models/schema/note-reaction.js';
|
import { packedNoteReactionSchema } from '@/models/schema/note-reaction.js';
|
||||||
import { packedHashtagSchema } from '@/models/schema/hashtag.js';
|
import { packedHashtagSchema } from '@/models/schema/hashtag.js';
|
||||||
import { packedPageSchema } from '@/models/schema/page.js';
|
import { packedPageSchema } from '@/models/schema/page.js';
|
||||||
import { packedUserGroupSchema } from '@/models/schema/user-group.js';
|
|
||||||
import { packedNoteFavoriteSchema } from '@/models/schema/note-favorite.js';
|
import { packedNoteFavoriteSchema } from '@/models/schema/note-favorite.js';
|
||||||
import { packedChannelSchema } from '@/models/schema/channel.js';
|
import { packedChannelSchema } from '@/models/schema/channel.js';
|
||||||
import { packedAntennaSchema } from '@/models/schema/antenna.js';
|
import { packedAntennaSchema } from '@/models/schema/antenna.js';
|
||||||
@ -40,9 +38,7 @@ export const refs = {
|
|||||||
User: packedUserSchema,
|
User: packedUserSchema,
|
||||||
|
|
||||||
UserList: packedUserListSchema,
|
UserList: packedUserListSchema,
|
||||||
UserGroup: packedUserGroupSchema,
|
|
||||||
App: packedAppSchema,
|
App: packedAppSchema,
|
||||||
MessagingMessage: packedMessagingMessageSchema,
|
|
||||||
Note: packedNoteSchema,
|
Note: packedNoteSchema,
|
||||||
NoteReaction: packedNoteReactionSchema,
|
NoteReaction: packedNoteReactionSchema,
|
||||||
NoteFavorite: packedNoteFavoriteSchema,
|
NoteFavorite: packedNoteFavoriteSchema,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { User, Note, Announcement, AnnouncementRead, App, NoteFavorite, NoteThreadMuting, NoteReaction, NoteUnread, Notification, Poll, PollVote, UserProfile, UserKeypair, UserPending, AttestationChallenge, UserSecurityKey, UserPublickey, UserList, UserListJoining, UserGroup, UserGroupJoining, UserGroupInvitation, UserNotePining, UserIp, UsedUsername, Following, FollowRequest, Instance, Emoji, DriveFile, DriveFolder, Meta, Muting, Blocking, SwSubscription, Hashtag, AbuseUserReport, RegistrationTicket, AuthSession, AccessToken, Signin, MessagingMessage, Page, PageLike, GalleryPost, GalleryLike, ModerationLog, Clip, ClipNote, Antenna, AntennaNote, PromoNote, PromoRead, Relay, MutedNote, Channel, ChannelFollowing, ChannelNotePining, RegistryItem, Webhook, Ad, PasswordResetRequest, RetentionAggregation, FlashLike, Flash, Role, RoleAssignment } from './index.js';
|
import { User, Note, Announcement, AnnouncementRead, App, NoteFavorite, NoteThreadMuting, NoteReaction, NoteUnread, Notification, Poll, PollVote, UserProfile, UserKeypair, UserPending, AttestationChallenge, UserSecurityKey, UserPublickey, UserList, UserListJoining, UserNotePining, UserIp, UsedUsername, Following, FollowRequest, Instance, Emoji, DriveFile, DriveFolder, Meta, Muting, Blocking, SwSubscription, Hashtag, AbuseUserReport, RegistrationTicket, AuthSession, AccessToken, Signin, Page, PageLike, GalleryPost, GalleryLike, ModerationLog, Clip, ClipNote, Antenna, AntennaNote, PromoNote, PromoRead, Relay, MutedNote, Channel, ChannelFollowing, ChannelNotePining, RegistryItem, Webhook, Ad, PasswordResetRequest, RetentionAggregation, FlashLike, Flash, Role, RoleAssignment } from './index.js';
|
||||||
import type { DataSource } from 'typeorm';
|
import type { DataSource } from 'typeorm';
|
||||||
import type { Provider } from '@nestjs/common';
|
import type { Provider } from '@nestjs/common';
|
||||||
|
|
||||||
@ -118,24 +118,6 @@ const $userListJoiningsRepository: Provider = {
|
|||||||
inject: [DI.db],
|
inject: [DI.db],
|
||||||
};
|
};
|
||||||
|
|
||||||
const $userGroupsRepository: Provider = {
|
|
||||||
provide: DI.userGroupsRepository,
|
|
||||||
useFactory: (db: DataSource) => db.getRepository(UserGroup),
|
|
||||||
inject: [DI.db],
|
|
||||||
};
|
|
||||||
|
|
||||||
const $userGroupJoiningsRepository: Provider = {
|
|
||||||
provide: DI.userGroupJoiningsRepository,
|
|
||||||
useFactory: (db: DataSource) => db.getRepository(UserGroupJoining),
|
|
||||||
inject: [DI.db],
|
|
||||||
};
|
|
||||||
|
|
||||||
const $userGroupInvitationsRepository: Provider = {
|
|
||||||
provide: DI.userGroupInvitationsRepository,
|
|
||||||
useFactory: (db: DataSource) => db.getRepository(UserGroupInvitation),
|
|
||||||
inject: [DI.db],
|
|
||||||
};
|
|
||||||
|
|
||||||
const $userNotePiningsRepository: Provider = {
|
const $userNotePiningsRepository: Provider = {
|
||||||
provide: DI.userNotePiningsRepository,
|
provide: DI.userNotePiningsRepository,
|
||||||
useFactory: (db: DataSource) => db.getRepository(UserNotePining),
|
useFactory: (db: DataSource) => db.getRepository(UserNotePining),
|
||||||
@ -256,12 +238,6 @@ const $signinsRepository: Provider = {
|
|||||||
inject: [DI.db],
|
inject: [DI.db],
|
||||||
};
|
};
|
||||||
|
|
||||||
const $messagingMessagesRepository: Provider = {
|
|
||||||
provide: DI.messagingMessagesRepository,
|
|
||||||
useFactory: (db: DataSource) => db.getRepository(MessagingMessage),
|
|
||||||
inject: [DI.db],
|
|
||||||
};
|
|
||||||
|
|
||||||
const $pagesRepository: Provider = {
|
const $pagesRepository: Provider = {
|
||||||
provide: DI.pagesRepository,
|
provide: DI.pagesRepository,
|
||||||
useFactory: (db: DataSource) => db.getRepository(Page),
|
useFactory: (db: DataSource) => db.getRepository(Page),
|
||||||
@ -435,9 +411,6 @@ const $roleAssignmentsRepository: Provider = {
|
|||||||
$userPublickeysRepository,
|
$userPublickeysRepository,
|
||||||
$userListsRepository,
|
$userListsRepository,
|
||||||
$userListJoiningsRepository,
|
$userListJoiningsRepository,
|
||||||
$userGroupsRepository,
|
|
||||||
$userGroupJoiningsRepository,
|
|
||||||
$userGroupInvitationsRepository,
|
|
||||||
$userNotePiningsRepository,
|
$userNotePiningsRepository,
|
||||||
$userIpsRepository,
|
$userIpsRepository,
|
||||||
$usedUsernamesRepository,
|
$usedUsernamesRepository,
|
||||||
@ -458,7 +431,6 @@ const $roleAssignmentsRepository: Provider = {
|
|||||||
$authSessionsRepository,
|
$authSessionsRepository,
|
||||||
$accessTokensRepository,
|
$accessTokensRepository,
|
||||||
$signinsRepository,
|
$signinsRepository,
|
||||||
$messagingMessagesRepository,
|
|
||||||
$pagesRepository,
|
$pagesRepository,
|
||||||
$pageLikesRepository,
|
$pageLikesRepository,
|
||||||
$galleryPostsRepository,
|
$galleryPostsRepository,
|
||||||
@ -505,9 +477,6 @@ const $roleAssignmentsRepository: Provider = {
|
|||||||
$userPublickeysRepository,
|
$userPublickeysRepository,
|
||||||
$userListsRepository,
|
$userListsRepository,
|
||||||
$userListJoiningsRepository,
|
$userListJoiningsRepository,
|
||||||
$userGroupsRepository,
|
|
||||||
$userGroupJoiningsRepository,
|
|
||||||
$userGroupInvitationsRepository,
|
|
||||||
$userNotePiningsRepository,
|
$userNotePiningsRepository,
|
||||||
$userIpsRepository,
|
$userIpsRepository,
|
||||||
$usedUsernamesRepository,
|
$usedUsernamesRepository,
|
||||||
@ -528,7 +497,6 @@ const $roleAssignmentsRepository: Provider = {
|
|||||||
$authSessionsRepository,
|
$authSessionsRepository,
|
||||||
$accessTokensRepository,
|
$accessTokensRepository,
|
||||||
$signinsRepository,
|
$signinsRepository,
|
||||||
$messagingMessagesRepository,
|
|
||||||
$pagesRepository,
|
$pagesRepository,
|
||||||
$pageLikesRepository,
|
$pageLikesRepository,
|
||||||
$galleryPostsRepository,
|
$galleryPostsRepository,
|
||||||
|
@ -18,6 +18,12 @@ export class Ad {
|
|||||||
})
|
})
|
||||||
public expiresAt: Date;
|
public expiresAt: Date;
|
||||||
|
|
||||||
|
@Index()
|
||||||
|
@Column('timestamp with time zone', {
|
||||||
|
comment: 'The expired date of the Ad.',
|
||||||
|
})
|
||||||
|
public startsAt: Date;
|
||||||
|
|
||||||
@Column('varchar', {
|
@Column('varchar', {
|
||||||
length: 32, nullable: false,
|
length: 32, nullable: false,
|
||||||
})
|
})
|
||||||
|
@ -2,7 +2,6 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typ
|
|||||||
import { id } from '../id.js';
|
import { id } from '../id.js';
|
||||||
import { User } from './User.js';
|
import { User } from './User.js';
|
||||||
import { UserList } from './UserList.js';
|
import { UserList } from './UserList.js';
|
||||||
import { UserGroupJoining } from './UserGroupJoining.js';
|
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class Antenna {
|
export class Antenna {
|
||||||
@ -33,8 +32,8 @@ export class Antenna {
|
|||||||
})
|
})
|
||||||
public name: string;
|
public name: string;
|
||||||
|
|
||||||
@Column('enum', { enum: ['home', 'all', 'users', 'list', 'group'] })
|
@Column('enum', { enum: ['home', 'all', 'users', 'list'] })
|
||||||
public src: 'home' | 'all' | 'users' | 'list' | 'group';
|
public src: 'home' | 'all' | 'users' | 'list';
|
||||||
|
|
||||||
@Column({
|
@Column({
|
||||||
...id(),
|
...id(),
|
||||||
@ -48,18 +47,6 @@ export class Antenna {
|
|||||||
@JoinColumn()
|
@JoinColumn()
|
||||||
public userList: UserList | null;
|
public userList: UserList | null;
|
||||||
|
|
||||||
@Column({
|
|
||||||
...id(),
|
|
||||||
nullable: true,
|
|
||||||
})
|
|
||||||
public userGroupJoiningId: UserGroupJoining['id'] | null;
|
|
||||||
|
|
||||||
@ManyToOne(type => UserGroupJoining, {
|
|
||||||
onDelete: 'CASCADE',
|
|
||||||
})
|
|
||||||
@JoinColumn()
|
|
||||||
public userGroupJoining: UserGroupJoining | null;
|
|
||||||
|
|
||||||
@Column('varchar', {
|
@Column('varchar', {
|
||||||
length: 1024, array: true,
|
length: 1024, array: true,
|
||||||
default: '{}',
|
default: '{}',
|
||||||
|
@ -1,89 +0,0 @@
|
|||||||
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
|
|
||||||
import { id } from '../id.js';
|
|
||||||
import { User } from './User.js';
|
|
||||||
import { DriveFile } from './DriveFile.js';
|
|
||||||
import { UserGroup } from './UserGroup.js';
|
|
||||||
|
|
||||||
@Entity()
|
|
||||||
export class MessagingMessage {
|
|
||||||
@PrimaryColumn(id())
|
|
||||||
public id: string;
|
|
||||||
|
|
||||||
@Index()
|
|
||||||
@Column('timestamp with time zone', {
|
|
||||||
comment: 'The created date of the MessagingMessage.',
|
|
||||||
})
|
|
||||||
public createdAt: Date;
|
|
||||||
|
|
||||||
@Index()
|
|
||||||
@Column({
|
|
||||||
...id(),
|
|
||||||
comment: 'The sender user ID.',
|
|
||||||
})
|
|
||||||
public userId: User['id'];
|
|
||||||
|
|
||||||
@ManyToOne(type => User, {
|
|
||||||
onDelete: 'CASCADE',
|
|
||||||
})
|
|
||||||
@JoinColumn()
|
|
||||||
public user: User | null;
|
|
||||||
|
|
||||||
@Index()
|
|
||||||
@Column({
|
|
||||||
...id(), nullable: true,
|
|
||||||
comment: 'The recipient user ID.',
|
|
||||||
})
|
|
||||||
public recipientId: User['id'] | null;
|
|
||||||
|
|
||||||
@ManyToOne(type => User, {
|
|
||||||
onDelete: 'CASCADE',
|
|
||||||
})
|
|
||||||
@JoinColumn()
|
|
||||||
public recipient: User | null;
|
|
||||||
|
|
||||||
@Index()
|
|
||||||
@Column({
|
|
||||||
...id(), nullable: true,
|
|
||||||
comment: 'The recipient group ID.',
|
|
||||||
})
|
|
||||||
public groupId: UserGroup['id'] | null;
|
|
||||||
|
|
||||||
@ManyToOne(type => UserGroup, {
|
|
||||||
onDelete: 'CASCADE',
|
|
||||||
})
|
|
||||||
@JoinColumn()
|
|
||||||
public group: UserGroup | null;
|
|
||||||
|
|
||||||
@Column('varchar', {
|
|
||||||
length: 4096, nullable: true,
|
|
||||||
})
|
|
||||||
public text: string | null;
|
|
||||||
|
|
||||||
@Column('boolean', {
|
|
||||||
default: false,
|
|
||||||
})
|
|
||||||
public isRead: boolean;
|
|
||||||
|
|
||||||
@Column('varchar', {
|
|
||||||
length: 512, nullable: true,
|
|
||||||
})
|
|
||||||
public uri: string | null;
|
|
||||||
|
|
||||||
@Column({
|
|
||||||
...id(),
|
|
||||||
array: true, default: '{}',
|
|
||||||
})
|
|
||||||
public reads: User['id'][];
|
|
||||||
|
|
||||||
@Column({
|
|
||||||
...id(),
|
|
||||||
nullable: true,
|
|
||||||
})
|
|
||||||
public fileId: DriveFile['id'] | null;
|
|
||||||
|
|
||||||
@ManyToOne(type => DriveFile, {
|
|
||||||
onDelete: 'CASCADE',
|
|
||||||
})
|
|
||||||
@JoinColumn()
|
|
||||||
public file: DriveFile | null;
|
|
||||||
}
|
|
@ -4,7 +4,6 @@ import { id } from '../id.js';
|
|||||||
import { User } from './User.js';
|
import { User } from './User.js';
|
||||||
import { Note } from './Note.js';
|
import { Note } from './Note.js';
|
||||||
import { FollowRequest } from './FollowRequest.js';
|
import { FollowRequest } from './FollowRequest.js';
|
||||||
import { UserGroupInvitation } from './UserGroupInvitation.js';
|
|
||||||
import { AccessToken } from './AccessToken.js';
|
import { AccessToken } from './AccessToken.js';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
@ -63,7 +62,6 @@ export class Notification {
|
|||||||
* pollEnded - 自分のアンケートもしくは自分が投票したアンケートが終了した
|
* pollEnded - 自分のアンケートもしくは自分が投票したアンケートが終了した
|
||||||
* receiveFollowRequest - フォローリクエストされた
|
* receiveFollowRequest - フォローリクエストされた
|
||||||
* followRequestAccepted - 自分の送ったフォローリクエストが承認された
|
* followRequestAccepted - 自分の送ったフォローリクエストが承認された
|
||||||
* groupInvited - グループに招待された
|
|
||||||
* achievementEarned - 実績を獲得
|
* achievementEarned - 実績を獲得
|
||||||
* app - アプリ通知
|
* app - アプリ通知
|
||||||
*/
|
*/
|
||||||
@ -108,18 +106,6 @@ export class Notification {
|
|||||||
@JoinColumn()
|
@JoinColumn()
|
||||||
public followRequest: FollowRequest | null;
|
public followRequest: FollowRequest | null;
|
||||||
|
|
||||||
@Column({
|
|
||||||
...id(),
|
|
||||||
nullable: true,
|
|
||||||
})
|
|
||||||
public userGroupInvitationId: UserGroupInvitation['id'] | null;
|
|
||||||
|
|
||||||
@ManyToOne(type => UserGroupInvitation, {
|
|
||||||
onDelete: 'CASCADE',
|
|
||||||
})
|
|
||||||
@JoinColumn()
|
|
||||||
public userGroupInvitation: UserGroupInvitation | null;
|
|
||||||
|
|
||||||
@Column('varchar', {
|
@Column('varchar', {
|
||||||
length: 128, nullable: true,
|
length: 128, nullable: true,
|
||||||
})
|
})
|
||||||
|
@ -215,20 +215,16 @@ export class User {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ILocalUser extends User {
|
export type LocalUser = User & {
|
||||||
host: null;
|
host: null;
|
||||||
|
uri: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IRemoteUser extends User {
|
export type RemoteUser = User & {
|
||||||
host: string;
|
host: string;
|
||||||
|
uri: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CacheableLocalUser = ILocalUser;
|
|
||||||
|
|
||||||
export type CacheableRemoteUser = IRemoteUser;
|
|
||||||
|
|
||||||
export type CacheableUser = CacheableLocalUser | CacheableRemoteUser;
|
|
||||||
|
|
||||||
export const localUsernameSchema = { type: 'string', pattern: /^\w{1,20}$/.toString().slice(1, -1) } as const;
|
export const localUsernameSchema = { type: 'string', pattern: /^\w{1,20}$/.toString().slice(1, -1) } as const;
|
||||||
export const passwordSchema = { type: 'string', minLength: 1 } as const;
|
export const passwordSchema = { type: 'string', minLength: 1 } as const;
|
||||||
export const nameSchema = { type: 'string', minLength: 1, maxLength: 50 } as const;
|
export const nameSchema = { type: 'string', minLength: 1, maxLength: 50 } as const;
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm';
|
|
||||||
import { id } from '../id.js';
|
|
||||||
import { User } from './User.js';
|
|
||||||
|
|
||||||
@Entity()
|
|
||||||
export class UserGroup {
|
|
||||||
@PrimaryColumn(id())
|
|
||||||
public id: string;
|
|
||||||
|
|
||||||
@Index()
|
|
||||||
@Column('timestamp with time zone', {
|
|
||||||
comment: 'The created date of the UserGroup.',
|
|
||||||
})
|
|
||||||
public createdAt: Date;
|
|
||||||
|
|
||||||
@Column('varchar', {
|
|
||||||
length: 256,
|
|
||||||
})
|
|
||||||
public name: string;
|
|
||||||
|
|
||||||
@Index()
|
|
||||||
@Column({
|
|
||||||
...id(),
|
|
||||||
comment: 'The ID of owner.',
|
|
||||||
})
|
|
||||||
public userId: User['id'];
|
|
||||||
|
|
||||||
@ManyToOne(type => User, {
|
|
||||||
onDelete: 'CASCADE',
|
|
||||||
})
|
|
||||||
@JoinColumn()
|
|
||||||
public user: User | null;
|
|
||||||
|
|
||||||
@Column('boolean', {
|
|
||||||
default: false,
|
|
||||||
})
|
|
||||||
public isPrivate: boolean;
|
|
||||||
|
|
||||||
constructor(data: Partial<UserGroup>) {
|
|
||||||
if (data == null) return;
|
|
||||||
|
|
||||||
for (const [k, v] of Object.entries(data)) {
|
|
||||||
(this as any)[k] = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
|
|
||||||
import { id } from '../id.js';
|
|
||||||
import { User } from './User.js';
|
|
||||||
import { UserGroup } from './UserGroup.js';
|
|
||||||
|
|
||||||
@Entity()
|
|
||||||
@Index(['userId', 'userGroupId'], { unique: true })
|
|
||||||
export class UserGroupInvitation {
|
|
||||||
@PrimaryColumn(id())
|
|
||||||
public id: string;
|
|
||||||
|
|
||||||
@Column('timestamp with time zone', {
|
|
||||||
comment: 'The created date of the UserGroupInvitation.',
|
|
||||||
})
|
|
||||||
public createdAt: Date;
|
|
||||||
|
|
||||||
@Index()
|
|
||||||
@Column({
|
|
||||||
...id(),
|
|
||||||
comment: 'The user ID.',
|
|
||||||
})
|
|
||||||
public userId: User['id'];
|
|
||||||
|
|
||||||
@ManyToOne(type => User, {
|
|
||||||
onDelete: 'CASCADE',
|
|
||||||
})
|
|
||||||
@JoinColumn()
|
|
||||||
public user: User | null;
|
|
||||||
|
|
||||||
@Index()
|
|
||||||
@Column({
|
|
||||||
...id(),
|
|
||||||
comment: 'The group ID.',
|
|
||||||
})
|
|
||||||
public userGroupId: UserGroup['id'];
|
|
||||||
|
|
||||||
@ManyToOne(type => UserGroup, {
|
|
||||||
onDelete: 'CASCADE',
|
|
||||||
})
|
|
||||||
@JoinColumn()
|
|
||||||
public userGroup: UserGroup | null;
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
|
|
||||||
import { id } from '../id.js';
|
|
||||||
import { User } from './User.js';
|
|
||||||
import { UserGroup } from './UserGroup.js';
|
|
||||||
|
|
||||||
@Entity()
|
|
||||||
@Index(['userId', 'userGroupId'], { unique: true })
|
|
||||||
export class UserGroupJoining {
|
|
||||||
@PrimaryColumn(id())
|
|
||||||
public id: string;
|
|
||||||
|
|
||||||
@Column('timestamp with time zone', {
|
|
||||||
comment: 'The created date of the UserGroupJoining.',
|
|
||||||
})
|
|
||||||
public createdAt: Date;
|
|
||||||
|
|
||||||
@Index()
|
|
||||||
@Column({
|
|
||||||
...id(),
|
|
||||||
comment: 'The user ID.',
|
|
||||||
})
|
|
||||||
public userId: User['id'];
|
|
||||||
|
|
||||||
@ManyToOne(type => User, {
|
|
||||||
onDelete: 'CASCADE',
|
|
||||||
})
|
|
||||||
@JoinColumn()
|
|
||||||
public user: User | null;
|
|
||||||
|
|
||||||
@Index()
|
|
||||||
@Column({
|
|
||||||
...id(),
|
|
||||||
comment: 'The group ID.',
|
|
||||||
})
|
|
||||||
public userGroupId: UserGroup['id'];
|
|
||||||
|
|
||||||
@ManyToOne(type => UserGroup, {
|
|
||||||
onDelete: 'CASCADE',
|
|
||||||
})
|
|
||||||
@JoinColumn()
|
|
||||||
public userGroup: UserGroup | null;
|
|
||||||
}
|
|
@ -71,7 +71,7 @@ export class UserProfile {
|
|||||||
public emailVerified: boolean;
|
public emailVerified: boolean;
|
||||||
|
|
||||||
@Column('jsonb', {
|
@Column('jsonb', {
|
||||||
default: ['follow', 'receiveFollowRequest', 'groupInvited'],
|
default: ['follow', 'receiveFollowRequest'],
|
||||||
})
|
})
|
||||||
public emailNotificationTypes: string[];
|
public emailNotificationTypes: string[];
|
||||||
|
|
||||||
|
@ -22,7 +22,6 @@ import { GalleryLike } from '@/models/entities/GalleryLike.js';
|
|||||||
import { GalleryPost } from '@/models/entities/GalleryPost.js';
|
import { GalleryPost } from '@/models/entities/GalleryPost.js';
|
||||||
import { Hashtag } from '@/models/entities/Hashtag.js';
|
import { Hashtag } from '@/models/entities/Hashtag.js';
|
||||||
import { Instance } from '@/models/entities/Instance.js';
|
import { Instance } from '@/models/entities/Instance.js';
|
||||||
import { MessagingMessage } from '@/models/entities/MessagingMessage.js';
|
|
||||||
import { Meta } from '@/models/entities/Meta.js';
|
import { Meta } from '@/models/entities/Meta.js';
|
||||||
import { ModerationLog } from '@/models/entities/ModerationLog.js';
|
import { ModerationLog } from '@/models/entities/ModerationLog.js';
|
||||||
import { MutedNote } from '@/models/entities/MutedNote.js';
|
import { MutedNote } from '@/models/entities/MutedNote.js';
|
||||||
@ -47,9 +46,6 @@ import { Signin } from '@/models/entities/Signin.js';
|
|||||||
import { SwSubscription } from '@/models/entities/SwSubscription.js';
|
import { SwSubscription } from '@/models/entities/SwSubscription.js';
|
||||||
import { UsedUsername } from '@/models/entities/UsedUsername.js';
|
import { UsedUsername } from '@/models/entities/UsedUsername.js';
|
||||||
import { User } from '@/models/entities/User.js';
|
import { User } from '@/models/entities/User.js';
|
||||||
import { UserGroup } from '@/models/entities/UserGroup.js';
|
|
||||||
import { UserGroupInvitation } from '@/models/entities/UserGroupInvitation.js';
|
|
||||||
import { UserGroupJoining } from '@/models/entities/UserGroupJoining.js';
|
|
||||||
import { UserIp } from '@/models/entities/UserIp.js';
|
import { UserIp } from '@/models/entities/UserIp.js';
|
||||||
import { UserKeypair } from '@/models/entities/UserKeypair.js';
|
import { UserKeypair } from '@/models/entities/UserKeypair.js';
|
||||||
import { UserList } from '@/models/entities/UserList.js';
|
import { UserList } from '@/models/entities/UserList.js';
|
||||||
@ -93,7 +89,6 @@ export {
|
|||||||
GalleryPost,
|
GalleryPost,
|
||||||
Hashtag,
|
Hashtag,
|
||||||
Instance,
|
Instance,
|
||||||
MessagingMessage,
|
|
||||||
Meta,
|
Meta,
|
||||||
ModerationLog,
|
ModerationLog,
|
||||||
MutedNote,
|
MutedNote,
|
||||||
@ -118,9 +113,6 @@ export {
|
|||||||
SwSubscription,
|
SwSubscription,
|
||||||
UsedUsername,
|
UsedUsername,
|
||||||
User,
|
User,
|
||||||
UserGroup,
|
|
||||||
UserGroupInvitation,
|
|
||||||
UserGroupJoining,
|
|
||||||
UserIp,
|
UserIp,
|
||||||
UserKeypair,
|
UserKeypair,
|
||||||
UserList,
|
UserList,
|
||||||
@ -163,7 +155,6 @@ export type GalleryLikesRepository = Repository<GalleryLike>;
|
|||||||
export type GalleryPostsRepository = Repository<GalleryPost>;
|
export type GalleryPostsRepository = Repository<GalleryPost>;
|
||||||
export type HashtagsRepository = Repository<Hashtag>;
|
export type HashtagsRepository = Repository<Hashtag>;
|
||||||
export type InstancesRepository = Repository<Instance>;
|
export type InstancesRepository = Repository<Instance>;
|
||||||
export type MessagingMessagesRepository = Repository<MessagingMessage>;
|
|
||||||
export type MetasRepository = Repository<Meta>;
|
export type MetasRepository = Repository<Meta>;
|
||||||
export type ModerationLogsRepository = Repository<ModerationLog>;
|
export type ModerationLogsRepository = Repository<ModerationLog>;
|
||||||
export type MutedNotesRepository = Repository<MutedNote>;
|
export type MutedNotesRepository = Repository<MutedNote>;
|
||||||
@ -188,9 +179,6 @@ export type SigninsRepository = Repository<Signin>;
|
|||||||
export type SwSubscriptionsRepository = Repository<SwSubscription>;
|
export type SwSubscriptionsRepository = Repository<SwSubscription>;
|
||||||
export type UsedUsernamesRepository = Repository<UsedUsername>;
|
export type UsedUsernamesRepository = Repository<UsedUsername>;
|
||||||
export type UsersRepository = Repository<User>;
|
export type UsersRepository = Repository<User>;
|
||||||
export type UserGroupsRepository = Repository<UserGroup>;
|
|
||||||
export type UserGroupInvitationsRepository = Repository<UserGroupInvitation>;
|
|
||||||
export type UserGroupJoiningsRepository = Repository<UserGroupJoining>;
|
|
||||||
export type UserIpsRepository = Repository<UserIp>;
|
export type UserIpsRepository = Repository<UserIp>;
|
||||||
export type UserKeypairsRepository = Repository<UserKeypair>;
|
export type UserKeypairsRepository = Repository<UserKeypair>;
|
||||||
export type UserListsRepository = Repository<UserList>;
|
export type UserListsRepository = Repository<UserList>;
|
||||||
|
@ -42,18 +42,13 @@ export const packedAntennaSchema = {
|
|||||||
src: {
|
src: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
enum: ['home', 'all', 'users', 'list', 'group'],
|
enum: ['home', 'all', 'users', 'list'],
|
||||||
},
|
},
|
||||||
userListId: {
|
userListId: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: true,
|
optional: false, nullable: true,
|
||||||
format: 'id',
|
format: 'id',
|
||||||
},
|
},
|
||||||
userGroupId: {
|
|
||||||
type: 'string',
|
|
||||||
optional: false, nullable: true,
|
|
||||||
format: 'id',
|
|
||||||
},
|
|
||||||
users: {
|
users: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
|
@ -1,73 +0,0 @@
|
|||||||
export const packedMessagingMessageSchema = {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
id: {
|
|
||||||
type: 'string',
|
|
||||||
optional: false, nullable: false,
|
|
||||||
format: 'id',
|
|
||||||
example: 'xxxxxxxxxx',
|
|
||||||
},
|
|
||||||
createdAt: {
|
|
||||||
type: 'string',
|
|
||||||
optional: false, nullable: false,
|
|
||||||
format: 'date-time',
|
|
||||||
},
|
|
||||||
userId: {
|
|
||||||
type: 'string',
|
|
||||||
optional: false, nullable: false,
|
|
||||||
format: 'id',
|
|
||||||
},
|
|
||||||
user: {
|
|
||||||
type: 'object',
|
|
||||||
ref: 'UserLite',
|
|
||||||
optional: true, nullable: false,
|
|
||||||
},
|
|
||||||
text: {
|
|
||||||
type: 'string',
|
|
||||||
optional: false, nullable: true,
|
|
||||||
},
|
|
||||||
fileId: {
|
|
||||||
type: 'string',
|
|
||||||
optional: true, nullable: true,
|
|
||||||
format: 'id',
|
|
||||||
},
|
|
||||||
file: {
|
|
||||||
type: 'object',
|
|
||||||
optional: true, nullable: true,
|
|
||||||
ref: 'DriveFile',
|
|
||||||
},
|
|
||||||
recipientId: {
|
|
||||||
type: 'string',
|
|
||||||
optional: false, nullable: true,
|
|
||||||
format: 'id',
|
|
||||||
},
|
|
||||||
recipient: {
|
|
||||||
type: 'object',
|
|
||||||
optional: true, nullable: true,
|
|
||||||
ref: 'UserLite',
|
|
||||||
},
|
|
||||||
groupId: {
|
|
||||||
type: 'string',
|
|
||||||
optional: false, nullable: true,
|
|
||||||
format: 'id',
|
|
||||||
},
|
|
||||||
group: {
|
|
||||||
type: 'object',
|
|
||||||
optional: true, nullable: true,
|
|
||||||
ref: 'UserGroup',
|
|
||||||
},
|
|
||||||
isRead: {
|
|
||||||
type: 'boolean',
|
|
||||||
optional: true, nullable: false,
|
|
||||||
},
|
|
||||||
reads: {
|
|
||||||
type: 'array',
|
|
||||||
optional: true, nullable: false,
|
|
||||||
items: {
|
|
||||||
type: 'string',
|
|
||||||
optional: false, nullable: false,
|
|
||||||
format: 'id',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} as const;
|
|
@ -1,34 +0,0 @@
|
|||||||
export const packedUserGroupSchema = {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
id: {
|
|
||||||
type: 'string',
|
|
||||||
optional: false, nullable: false,
|
|
||||||
format: 'id',
|
|
||||||
example: 'xxxxxxxxxx',
|
|
||||||
},
|
|
||||||
createdAt: {
|
|
||||||
type: 'string',
|
|
||||||
optional: false, nullable: false,
|
|
||||||
format: 'date-time',
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
type: 'string',
|
|
||||||
optional: false, nullable: false,
|
|
||||||
},
|
|
||||||
ownerId: {
|
|
||||||
type: 'string',
|
|
||||||
nullable: false, optional: false,
|
|
||||||
format: 'id',
|
|
||||||
},
|
|
||||||
userIds: {
|
|
||||||
type: 'array',
|
|
||||||
nullable: false, optional: true,
|
|
||||||
items: {
|
|
||||||
type: 'string',
|
|
||||||
nullable: false, optional: false,
|
|
||||||
format: 'id',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} as const;
|
|
@ -311,10 +311,6 @@ export const packedMeDetailedOnlySchema = {
|
|||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
nullable: false, optional: false,
|
nullable: false, optional: false,
|
||||||
},
|
},
|
||||||
hasUnreadMessagingMessage: {
|
|
||||||
type: 'boolean',
|
|
||||||
nullable: false, optional: false,
|
|
||||||
},
|
|
||||||
hasUnreadNotification: {
|
hasUnreadNotification: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
nullable: false, optional: false,
|
nullable: false, optional: false,
|
||||||
|
@ -30,7 +30,6 @@ import { GalleryLike } from '@/models/entities/GalleryLike.js';
|
|||||||
import { GalleryPost } from '@/models/entities/GalleryPost.js';
|
import { GalleryPost } from '@/models/entities/GalleryPost.js';
|
||||||
import { Hashtag } from '@/models/entities/Hashtag.js';
|
import { Hashtag } from '@/models/entities/Hashtag.js';
|
||||||
import { Instance } from '@/models/entities/Instance.js';
|
import { Instance } from '@/models/entities/Instance.js';
|
||||||
import { MessagingMessage } from '@/models/entities/MessagingMessage.js';
|
|
||||||
import { Meta } from '@/models/entities/Meta.js';
|
import { Meta } from '@/models/entities/Meta.js';
|
||||||
import { ModerationLog } from '@/models/entities/ModerationLog.js';
|
import { ModerationLog } from '@/models/entities/ModerationLog.js';
|
||||||
import { MutedNote } from '@/models/entities/MutedNote.js';
|
import { MutedNote } from '@/models/entities/MutedNote.js';
|
||||||
@ -55,9 +54,6 @@ import { Signin } from '@/models/entities/Signin.js';
|
|||||||
import { SwSubscription } from '@/models/entities/SwSubscription.js';
|
import { SwSubscription } from '@/models/entities/SwSubscription.js';
|
||||||
import { UsedUsername } from '@/models/entities/UsedUsername.js';
|
import { UsedUsername } from '@/models/entities/UsedUsername.js';
|
||||||
import { User } from '@/models/entities/User.js';
|
import { User } from '@/models/entities/User.js';
|
||||||
import { UserGroup } from '@/models/entities/UserGroup.js';
|
|
||||||
import { UserGroupInvitation } from '@/models/entities/UserGroupInvitation.js';
|
|
||||||
import { UserGroupJoining } from '@/models/entities/UserGroupJoining.js';
|
|
||||||
import { UserIp } from '@/models/entities/UserIp.js';
|
import { UserIp } from '@/models/entities/UserIp.js';
|
||||||
import { UserKeypair } from '@/models/entities/UserKeypair.js';
|
import { UserKeypair } from '@/models/entities/UserKeypair.js';
|
||||||
import { UserList } from '@/models/entities/UserList.js';
|
import { UserList } from '@/models/entities/UserList.js';
|
||||||
@ -137,9 +133,6 @@ export const entities = [
|
|||||||
UserPublickey,
|
UserPublickey,
|
||||||
UserList,
|
UserList,
|
||||||
UserListJoining,
|
UserListJoining,
|
||||||
UserGroup,
|
|
||||||
UserGroupJoining,
|
|
||||||
UserGroupInvitation,
|
|
||||||
UserNotePining,
|
UserNotePining,
|
||||||
UserSecurityKey,
|
UserSecurityKey,
|
||||||
UsedUsername,
|
UsedUsername,
|
||||||
@ -167,7 +160,6 @@ export const entities = [
|
|||||||
SwSubscription,
|
SwSubscription,
|
||||||
AbuseUserReport,
|
AbuseUserReport,
|
||||||
RegistrationTicket,
|
RegistrationTicket,
|
||||||
MessagingMessage,
|
|
||||||
Signin,
|
Signin,
|
||||||
ModerationLog,
|
ModerationLog,
|
||||||
Clip,
|
Clip,
|
||||||
|
@ -16,7 +16,7 @@ import InstanceChart from '@/core/chart/charts/instance.js';
|
|||||||
import ApRequestChart from '@/core/chart/charts/ap-request.js';
|
import ApRequestChart from '@/core/chart/charts/ap-request.js';
|
||||||
import FederationChart from '@/core/chart/charts/federation.js';
|
import FederationChart from '@/core/chart/charts/federation.js';
|
||||||
import { getApId } from '@/core/activitypub/type.js';
|
import { getApId } from '@/core/activitypub/type.js';
|
||||||
import type { CacheableRemoteUser } from '@/models/entities/User.js';
|
import type { RemoteUser } from '@/models/entities/User.js';
|
||||||
import type { UserPublickey } from '@/models/entities/UserPublickey.js';
|
import type { UserPublickey } from '@/models/entities/UserPublickey.js';
|
||||||
import { ApDbResolverService } from '@/core/activitypub/ApDbResolverService.js';
|
import { ApDbResolverService } from '@/core/activitypub/ApDbResolverService.js';
|
||||||
import { StatusError } from '@/misc/status-error.js';
|
import { StatusError } from '@/misc/status-error.js';
|
||||||
@ -87,7 +87,7 @@ export class InboxProcessorService {
|
|||||||
|
|
||||||
// HTTP-Signature keyIdを元にDBから取得
|
// HTTP-Signature keyIdを元にDBから取得
|
||||||
let authUser: {
|
let authUser: {
|
||||||
user: CacheableRemoteUser;
|
user: RemoteUser;
|
||||||
key: UserPublickey | null;
|
key: UserPublickey | null;
|
||||||
} | null = await this.apDbResolverService.getAuthUserFromKeyId(signature.keyId);
|
} | null = await this.apDbResolverService.getAuthUserFromKeyId(signature.keyId);
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ import * as url from '@/misc/prelude/url.js';
|
|||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
|
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
|
||||||
import { QueueService } from '@/core/QueueService.js';
|
import { QueueService } from '@/core/QueueService.js';
|
||||||
import type { ILocalUser, User } from '@/models/entities/User.js';
|
import type { LocalUser, User } from '@/models/entities/User.js';
|
||||||
import { UserKeypairStoreService } from '@/core/UserKeypairStoreService.js';
|
import { UserKeypairStoreService } from '@/core/UserKeypairStoreService.js';
|
||||||
import type { Following } from '@/models/entities/Following.js';
|
import type { Following } from '@/models/entities/Following.js';
|
||||||
import { countIf } from '@/misc/prelude/array.js';
|
import { countIf } from '@/misc/prelude/array.js';
|
||||||
@ -183,13 +183,13 @@ export class ActivityPubServerService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.setResponseType(request, reply);
|
this.setResponseType(request, reply);
|
||||||
return (this.apRendererService.renderActivity(rendered));
|
return (this.apRendererService.addContext(rendered));
|
||||||
} else {
|
} else {
|
||||||
// index page
|
// index page
|
||||||
const rendered = this.apRendererService.renderOrderedCollection(partOf, user.followersCount, `${partOf}?page=true`);
|
const rendered = this.apRendererService.renderOrderedCollection(partOf, user.followersCount, `${partOf}?page=true`);
|
||||||
reply.header('Cache-Control', 'public, max-age=180');
|
reply.header('Cache-Control', 'public, max-age=180');
|
||||||
this.setResponseType(request, reply);
|
this.setResponseType(request, reply);
|
||||||
return (this.apRendererService.renderActivity(rendered));
|
return (this.apRendererService.addContext(rendered));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,13 +271,13 @@ export class ActivityPubServerService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.setResponseType(request, reply);
|
this.setResponseType(request, reply);
|
||||||
return (this.apRendererService.renderActivity(rendered));
|
return (this.apRendererService.addContext(rendered));
|
||||||
} else {
|
} else {
|
||||||
// index page
|
// index page
|
||||||
const rendered = this.apRendererService.renderOrderedCollection(partOf, user.followingCount, `${partOf}?page=true`);
|
const rendered = this.apRendererService.renderOrderedCollection(partOf, user.followingCount, `${partOf}?page=true`);
|
||||||
reply.header('Cache-Control', 'public, max-age=180');
|
reply.header('Cache-Control', 'public, max-age=180');
|
||||||
this.setResponseType(request, reply);
|
this.setResponseType(request, reply);
|
||||||
return (this.apRendererService.renderActivity(rendered));
|
return (this.apRendererService.addContext(rendered));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -312,7 +312,7 @@ export class ActivityPubServerService {
|
|||||||
|
|
||||||
reply.header('Cache-Control', 'public, max-age=180');
|
reply.header('Cache-Control', 'public, max-age=180');
|
||||||
this.setResponseType(request, reply);
|
this.setResponseType(request, reply);
|
||||||
return (this.apRendererService.renderActivity(rendered));
|
return (this.apRendererService.addContext(rendered));
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
@ -389,7 +389,7 @@ export class ActivityPubServerService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.setResponseType(request, reply);
|
this.setResponseType(request, reply);
|
||||||
return (this.apRendererService.renderActivity(rendered));
|
return (this.apRendererService.addContext(rendered));
|
||||||
} else {
|
} else {
|
||||||
// index page
|
// index page
|
||||||
const rendered = this.apRendererService.renderOrderedCollection(partOf, user.notesCount,
|
const rendered = this.apRendererService.renderOrderedCollection(partOf, user.notesCount,
|
||||||
@ -398,7 +398,7 @@ export class ActivityPubServerService {
|
|||||||
);
|
);
|
||||||
reply.header('Cache-Control', 'public, max-age=180');
|
reply.header('Cache-Control', 'public, max-age=180');
|
||||||
this.setResponseType(request, reply);
|
this.setResponseType(request, reply);
|
||||||
return (this.apRendererService.renderActivity(rendered));
|
return (this.apRendererService.addContext(rendered));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -411,7 +411,7 @@ export class ActivityPubServerService {
|
|||||||
|
|
||||||
reply.header('Cache-Control', 'public, max-age=180');
|
reply.header('Cache-Control', 'public, max-age=180');
|
||||||
this.setResponseType(request, reply);
|
this.setResponseType(request, reply);
|
||||||
return (this.apRendererService.renderActivity(await this.apRendererService.renderPerson(user as ILocalUser)));
|
return (this.apRendererService.addContext(await this.apRendererService.renderPerson(user as LocalUser)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
@ -441,6 +441,14 @@ export class ActivityPubServerService {
|
|||||||
fastify.addContentTypeParser('application/activity+json', { parseAs: 'string' }, fastify.getDefaultJsonParser('ignore', 'ignore'));
|
fastify.addContentTypeParser('application/activity+json', { parseAs: 'string' }, fastify.getDefaultJsonParser('ignore', 'ignore'));
|
||||||
fastify.addContentTypeParser('application/ld+json', { parseAs: 'string' }, fastify.getDefaultJsonParser('ignore', 'ignore'));
|
fastify.addContentTypeParser('application/ld+json', { parseAs: 'string' }, fastify.getDefaultJsonParser('ignore', 'ignore'));
|
||||||
|
|
||||||
|
fastify.addHook('onRequest', (request, reply, done) => {
|
||||||
|
reply.header('Access-Control-Allow-Headers', 'Accept');
|
||||||
|
reply.header('Access-Control-Allow-Methods', 'GET, OPTIONS');
|
||||||
|
reply.header('Access-Control-Allow-Origin', '*');
|
||||||
|
reply.header('Access-Control-Expose-Headers', 'Vary');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
//#region Routing
|
//#region Routing
|
||||||
// inbox (limit: 64kb)
|
// inbox (limit: 64kb)
|
||||||
fastify.post('/inbox', { bodyLimit: 1024 * 64 }, async (request, reply) => await this.inbox(request, reply));
|
fastify.post('/inbox', { bodyLimit: 1024 * 64 }, async (request, reply) => await this.inbox(request, reply));
|
||||||
@ -473,7 +481,7 @@ export class ActivityPubServerService {
|
|||||||
|
|
||||||
reply.header('Cache-Control', 'public, max-age=180');
|
reply.header('Cache-Control', 'public, max-age=180');
|
||||||
this.setResponseType(request, reply);
|
this.setResponseType(request, reply);
|
||||||
return (this.apRendererService.renderActivity(await this.apRendererService.renderNote(note, false)));
|
return this.apRendererService.addContext(await this.apRendererService.renderNote(note, false));
|
||||||
});
|
});
|
||||||
|
|
||||||
// note activity
|
// note activity
|
||||||
@ -494,7 +502,7 @@ export class ActivityPubServerService {
|
|||||||
|
|
||||||
reply.header('Cache-Control', 'public, max-age=180');
|
reply.header('Cache-Control', 'public, max-age=180');
|
||||||
this.setResponseType(request, reply);
|
this.setResponseType(request, reply);
|
||||||
return (this.apRendererService.renderActivity(await this.packActivity(note)));
|
return (this.apRendererService.addContext(await this.packActivity(note)));
|
||||||
});
|
});
|
||||||
|
|
||||||
// outbox
|
// outbox
|
||||||
@ -537,7 +545,7 @@ export class ActivityPubServerService {
|
|||||||
if (this.userEntityService.isLocalUser(user)) {
|
if (this.userEntityService.isLocalUser(user)) {
|
||||||
reply.header('Cache-Control', 'public, max-age=180');
|
reply.header('Cache-Control', 'public, max-age=180');
|
||||||
this.setResponseType(request, reply);
|
this.setResponseType(request, reply);
|
||||||
return (this.apRendererService.renderActivity(this.apRendererService.renderKey(user, keypair)));
|
return (this.apRendererService.addContext(this.apRendererService.renderKey(user, keypair)));
|
||||||
} else {
|
} else {
|
||||||
reply.code(400);
|
reply.code(400);
|
||||||
return;
|
return;
|
||||||
@ -581,7 +589,7 @@ export class ActivityPubServerService {
|
|||||||
|
|
||||||
reply.header('Cache-Control', 'public, max-age=180');
|
reply.header('Cache-Control', 'public, max-age=180');
|
||||||
this.setResponseType(request, reply);
|
this.setResponseType(request, reply);
|
||||||
return (this.apRendererService.renderActivity(await this.apRendererService.renderEmoji(emoji)));
|
return (this.apRendererService.addContext(await this.apRendererService.renderEmoji(emoji)));
|
||||||
});
|
});
|
||||||
|
|
||||||
// like
|
// like
|
||||||
@ -602,7 +610,7 @@ export class ActivityPubServerService {
|
|||||||
|
|
||||||
reply.header('Cache-Control', 'public, max-age=180');
|
reply.header('Cache-Control', 'public, max-age=180');
|
||||||
this.setResponseType(request, reply);
|
this.setResponseType(request, reply);
|
||||||
return (this.apRendererService.renderActivity(await this.apRendererService.renderLike(reaction, note)));
|
return (this.apRendererService.addContext(await this.apRendererService.renderLike(reaction, note)));
|
||||||
});
|
});
|
||||||
|
|
||||||
// follow
|
// follow
|
||||||
@ -628,7 +636,7 @@ export class ActivityPubServerService {
|
|||||||
|
|
||||||
reply.header('Cache-Control', 'public, max-age=180');
|
reply.header('Cache-Control', 'public, max-age=180');
|
||||||
this.setResponseType(request, reply);
|
this.setResponseType(request, reply);
|
||||||
return (this.apRendererService.renderActivity(this.apRendererService.renderFollow(follower, followee)));
|
return (this.apRendererService.addContext(this.apRendererService.renderFollow(follower, followee)));
|
||||||
});
|
});
|
||||||
|
|
||||||
done();
|
done();
|
||||||
|
@ -150,6 +150,12 @@ export class FileServerService {
|
|||||||
file.cleanup();
|
file.cleanup();
|
||||||
return await reply.redirect(301, url.toString());
|
return await reply.redirect(301, url.toString());
|
||||||
} else if (file.mime.startsWith('video/')) {
|
} else if (file.mime.startsWith('video/')) {
|
||||||
|
const externalThumbnail = this.videoProcessingService.getExternalVideoThumbnailUrl(file.url);
|
||||||
|
if (externalThumbnail) {
|
||||||
|
file.cleanup();
|
||||||
|
return await reply.redirect(301, externalThumbnail);
|
||||||
|
}
|
||||||
|
|
||||||
image = await this.videoProcessingService.generateVideoThumbnail(file.path);
|
image = await this.videoProcessingService.generateVideoThumbnail(file.path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,8 +30,6 @@ import { HashtagChannelService } from './api/stream/channels/hashtag.js';
|
|||||||
import { HomeTimelineChannelService } from './api/stream/channels/home-timeline.js';
|
import { HomeTimelineChannelService } from './api/stream/channels/home-timeline.js';
|
||||||
import { HybridTimelineChannelService } from './api/stream/channels/hybrid-timeline.js';
|
import { HybridTimelineChannelService } from './api/stream/channels/hybrid-timeline.js';
|
||||||
import { LocalTimelineChannelService } from './api/stream/channels/local-timeline.js';
|
import { LocalTimelineChannelService } from './api/stream/channels/local-timeline.js';
|
||||||
import { MessagingIndexChannelService } from './api/stream/channels/messaging-index.js';
|
|
||||||
import { MessagingChannelService } from './api/stream/channels/messaging.js';
|
|
||||||
import { QueueStatsChannelService } from './api/stream/channels/queue-stats.js';
|
import { QueueStatsChannelService } from './api/stream/channels/queue-stats.js';
|
||||||
import { ServerStatsChannelService } from './api/stream/channels/server-stats.js';
|
import { ServerStatsChannelService } from './api/stream/channels/server-stats.js';
|
||||||
import { UserListChannelService } from './api/stream/channels/user-list.js';
|
import { UserListChannelService } from './api/stream/channels/user-list.js';
|
||||||
@ -71,8 +69,6 @@ import { UserListChannelService } from './api/stream/channels/user-list.js';
|
|||||||
HomeTimelineChannelService,
|
HomeTimelineChannelService,
|
||||||
HybridTimelineChannelService,
|
HybridTimelineChannelService,
|
||||||
LocalTimelineChannelService,
|
LocalTimelineChannelService,
|
||||||
MessagingIndexChannelService,
|
|
||||||
MessagingChannelService,
|
|
||||||
QueueStatsChannelService,
|
QueueStatsChannelService,
|
||||||
ServerStatsChannelService,
|
ServerStatsChannelService,
|
||||||
UserListChannelService,
|
UserListChannelService,
|
||||||
|
@ -5,7 +5,7 @@ import { promisify } from 'node:util';
|
|||||||
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 { getIpHash } from '@/misc/get-ip-hash.js';
|
import { getIpHash } from '@/misc/get-ip-hash.js';
|
||||||
import type { CacheableLocalUser, ILocalUser, User } from '@/models/entities/User.js';
|
import type { LocalUser, User } from '@/models/entities/User.js';
|
||||||
import type { AccessToken } from '@/models/entities/AccessToken.js';
|
import type { AccessToken } from '@/models/entities/AccessToken.js';
|
||||||
import type Logger from '@/logger.js';
|
import type Logger from '@/logger.js';
|
||||||
import type { UserIpsRepository } from '@/models/index.js';
|
import type { UserIpsRepository } from '@/models/index.js';
|
||||||
@ -168,7 +168,7 @@ export class ApiCallService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async logIp(request: FastifyRequest, user: ILocalUser) {
|
private async logIp(request: FastifyRequest, user: LocalUser) {
|
||||||
const meta = await this.metaService.fetch();
|
const meta = await this.metaService.fetch();
|
||||||
if (!meta.enableIpLogging) return;
|
if (!meta.enableIpLogging) return;
|
||||||
const ip = request.ip;
|
const ip = request.ip;
|
||||||
@ -194,7 +194,7 @@ export class ApiCallService implements OnApplicationShutdown {
|
|||||||
@bindThis
|
@bindThis
|
||||||
private async call(
|
private async call(
|
||||||
ep: IEndpoint & { exec: any },
|
ep: IEndpoint & { exec: any },
|
||||||
user: CacheableLocalUser | null | undefined,
|
user: LocalUser | null | undefined,
|
||||||
token: AccessToken | null | undefined,
|
token: AccessToken | null | undefined,
|
||||||
data: any,
|
data: any,
|
||||||
file: {
|
file: {
|
||||||
@ -227,15 +227,17 @@ export class ApiCallService implements OnApplicationShutdown {
|
|||||||
// TODO: 毎リクエスト計算するのもあれだしキャッシュしたい
|
// TODO: 毎リクエスト計算するのもあれだしキャッシュしたい
|
||||||
const factor = user ? (await this.roleService.getUserPolicies(user.id)).rateLimitFactor : 1;
|
const factor = user ? (await this.roleService.getUserPolicies(user.id)).rateLimitFactor : 1;
|
||||||
|
|
||||||
// Rate limit
|
if (factor > 0) {
|
||||||
await this.rateLimiterService.limit(limit as IEndpointMeta['limit'] & { key: NonNullable<string> }, limitActor, factor).catch(err => {
|
// Rate limit
|
||||||
throw new ApiError({
|
await this.rateLimiterService.limit(limit as IEndpointMeta['limit'] & { key: NonNullable<string> }, limitActor, factor).catch(err => {
|
||||||
message: 'Rate limit exceeded. Please try again later.',
|
throw new ApiError({
|
||||||
code: 'RATE_LIMIT_EXCEEDED',
|
message: 'Rate limit exceeded. Please try again later.',
|
||||||
id: 'd5826d14-3982-4d2e-8011-b9e9f02499ef',
|
code: 'RATE_LIMIT_EXCEEDED',
|
||||||
httpStatusCode: 429,
|
id: 'd5826d14-3982-4d2e-8011-b9e9f02499ef',
|
||||||
|
httpStatusCode: 429,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ep.meta.requireCredential || ep.meta.requireModerator || ep.meta.requireAdmin) {
|
if (ep.meta.requireCredential || ep.meta.requireModerator || ep.meta.requireAdmin) {
|
||||||
|
@ -1,7 +1,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 { AccessTokensRepository, AppsRepository, UsersRepository } from '@/models/index.js';
|
import type { AccessTokensRepository, AppsRepository, UsersRepository } from '@/models/index.js';
|
||||||
import type { CacheableLocalUser, ILocalUser } from '@/models/entities/User.js';
|
import type { LocalUser } from '@/models/entities/User.js';
|
||||||
import type { AccessToken } from '@/models/entities/AccessToken.js';
|
import type { AccessToken } from '@/models/entities/AccessToken.js';
|
||||||
import { Cache } from '@/misc/cache.js';
|
import { Cache } from '@/misc/cache.js';
|
||||||
import type { App } from '@/models/entities/App.js';
|
import type { App } from '@/models/entities/App.js';
|
||||||
@ -36,14 +36,14 @@ export class AuthenticateService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async authenticate(token: string | null | undefined): Promise<[CacheableLocalUser | null | undefined, AccessToken | null | undefined]> {
|
public async authenticate(token: string | null | undefined): Promise<[LocalUser | null | undefined, AccessToken | null | undefined]> {
|
||||||
if (token == null) {
|
if (token == null) {
|
||||||
return [null, null];
|
return [null, null];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isNativeToken(token)) {
|
if (isNativeToken(token)) {
|
||||||
const user = await this.userCacheService.localUserByNativeTokenCache.fetch(token,
|
const user = await this.userCacheService.localUserByNativeTokenCache.fetch(token,
|
||||||
() => this.usersRepository.findOneBy({ token }) as Promise<ILocalUser | null>);
|
() => this.usersRepository.findOneBy({ token }) as Promise<LocalUser | null>);
|
||||||
|
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw new AuthenticationError('user not found');
|
throw new AuthenticationError('user not found');
|
||||||
@ -70,7 +70,7 @@ export class AuthenticateService {
|
|||||||
const user = await this.userCacheService.localUserByIdCache.fetch(accessToken.userId,
|
const user = await this.userCacheService.localUserByIdCache.fetch(accessToken.userId,
|
||||||
() => this.usersRepository.findOneBy({
|
() => this.usersRepository.findOneBy({
|
||||||
id: accessToken.userId,
|
id: accessToken.userId,
|
||||||
}) as Promise<ILocalUser>);
|
}) as Promise<LocalUser>);
|
||||||
|
|
||||||
if (accessToken.appId) {
|
if (accessToken.appId) {
|
||||||
const app = await this.appCache.fetch(accessToken.appId,
|
const app = await this.appCache.fetch(accessToken.appId,
|
||||||
|
@ -195,7 +195,6 @@ import * as ep___i_notifications from './endpoints/i/notifications.js';
|
|||||||
import * as ep___i_pageLikes from './endpoints/i/page-likes.js';
|
import * as ep___i_pageLikes from './endpoints/i/page-likes.js';
|
||||||
import * as ep___i_pages from './endpoints/i/pages.js';
|
import * as ep___i_pages from './endpoints/i/pages.js';
|
||||||
import * as ep___i_pin from './endpoints/i/pin.js';
|
import * as ep___i_pin from './endpoints/i/pin.js';
|
||||||
import * as ep___i_readAllMessagingMessages from './endpoints/i/read-all-messaging-messages.js';
|
|
||||||
import * as ep___i_readAllUnreadNotes from './endpoints/i/read-all-unread-notes.js';
|
import * as ep___i_readAllUnreadNotes from './endpoints/i/read-all-unread-notes.js';
|
||||||
import * as ep___i_readAnnouncement from './endpoints/i/read-announcement.js';
|
import * as ep___i_readAnnouncement from './endpoints/i/read-announcement.js';
|
||||||
import * as ep___i_regenerateToken from './endpoints/i/regenerate-token.js';
|
import * as ep___i_regenerateToken from './endpoints/i/regenerate-token.js';
|
||||||
@ -212,17 +211,11 @@ import * as ep___i_signinHistory from './endpoints/i/signin-history.js';
|
|||||||
import * as ep___i_unpin from './endpoints/i/unpin.js';
|
import * as ep___i_unpin from './endpoints/i/unpin.js';
|
||||||
import * as ep___i_updateEmail from './endpoints/i/update-email.js';
|
import * as ep___i_updateEmail from './endpoints/i/update-email.js';
|
||||||
import * as ep___i_update from './endpoints/i/update.js';
|
import * as ep___i_update from './endpoints/i/update.js';
|
||||||
import * as ep___i_userGroupInvites from './endpoints/i/user-group-invites.js';
|
|
||||||
import * as ep___i_webhooks_create from './endpoints/i/webhooks/create.js';
|
import * as ep___i_webhooks_create from './endpoints/i/webhooks/create.js';
|
||||||
import * as ep___i_webhooks_show from './endpoints/i/webhooks/show.js';
|
import * as ep___i_webhooks_show from './endpoints/i/webhooks/show.js';
|
||||||
import * as ep___i_webhooks_list from './endpoints/i/webhooks/list.js';
|
import * as ep___i_webhooks_list from './endpoints/i/webhooks/list.js';
|
||||||
import * as ep___i_webhooks_update from './endpoints/i/webhooks/update.js';
|
import * as ep___i_webhooks_update from './endpoints/i/webhooks/update.js';
|
||||||
import * as ep___i_webhooks_delete from './endpoints/i/webhooks/delete.js';
|
import * as ep___i_webhooks_delete from './endpoints/i/webhooks/delete.js';
|
||||||
import * as ep___messaging_history from './endpoints/messaging/history.js';
|
|
||||||
import * as ep___messaging_messages from './endpoints/messaging/messages.js';
|
|
||||||
import * as ep___messaging_messages_create from './endpoints/messaging/messages/create.js';
|
|
||||||
import * as ep___messaging_messages_delete from './endpoints/messaging/messages/delete.js';
|
|
||||||
import * as ep___messaging_messages_read from './endpoints/messaging/messages/read.js';
|
|
||||||
import * as ep___meta from './endpoints/meta.js';
|
import * as ep___meta from './endpoints/meta.js';
|
||||||
import * as ep___emojis from './endpoints/emojis.js';
|
import * as ep___emojis from './endpoints/emojis.js';
|
||||||
import * as ep___miauth_genToken from './endpoints/miauth/gen-token.js';
|
import * as ep___miauth_genToken from './endpoints/miauth/gen-token.js';
|
||||||
@ -300,18 +293,6 @@ import * as ep___users_followers from './endpoints/users/followers.js';
|
|||||||
import * as ep___users_following from './endpoints/users/following.js';
|
import * as ep___users_following from './endpoints/users/following.js';
|
||||||
import * as ep___users_gallery_posts from './endpoints/users/gallery/posts.js';
|
import * as ep___users_gallery_posts from './endpoints/users/gallery/posts.js';
|
||||||
import * as ep___users_getFrequentlyRepliedUsers from './endpoints/users/get-frequently-replied-users.js';
|
import * as ep___users_getFrequentlyRepliedUsers from './endpoints/users/get-frequently-replied-users.js';
|
||||||
import * as ep___users_groups_create from './endpoints/users/groups/create.js';
|
|
||||||
import * as ep___users_groups_delete from './endpoints/users/groups/delete.js';
|
|
||||||
import * as ep___users_groups_invitations_accept from './endpoints/users/groups/invitations/accept.js';
|
|
||||||
import * as ep___users_groups_invitations_reject from './endpoints/users/groups/invitations/reject.js';
|
|
||||||
import * as ep___users_groups_invite from './endpoints/users/groups/invite.js';
|
|
||||||
import * as ep___users_groups_joined from './endpoints/users/groups/joined.js';
|
|
||||||
import * as ep___users_groups_leave from './endpoints/users/groups/leave.js';
|
|
||||||
import * as ep___users_groups_owned from './endpoints/users/groups/owned.js';
|
|
||||||
import * as ep___users_groups_pull from './endpoints/users/groups/pull.js';
|
|
||||||
import * as ep___users_groups_show from './endpoints/users/groups/show.js';
|
|
||||||
import * as ep___users_groups_transfer from './endpoints/users/groups/transfer.js';
|
|
||||||
import * as ep___users_groups_update from './endpoints/users/groups/update.js';
|
|
||||||
import * as ep___users_lists_create from './endpoints/users/lists/create.js';
|
import * as ep___users_lists_create from './endpoints/users/lists/create.js';
|
||||||
import * as ep___users_lists_delete from './endpoints/users/lists/delete.js';
|
import * as ep___users_lists_delete from './endpoints/users/lists/delete.js';
|
||||||
import * as ep___users_lists_list from './endpoints/users/lists/list.js';
|
import * as ep___users_lists_list from './endpoints/users/lists/list.js';
|
||||||
@ -530,7 +511,6 @@ const $i_notifications: Provider = { provide: 'ep:i/notifications', useClass: ep
|
|||||||
const $i_pageLikes: Provider = { provide: 'ep:i/page-likes', useClass: ep___i_pageLikes.default };
|
const $i_pageLikes: Provider = { provide: 'ep:i/page-likes', useClass: ep___i_pageLikes.default };
|
||||||
const $i_pages: Provider = { provide: 'ep:i/pages', useClass: ep___i_pages.default };
|
const $i_pages: Provider = { provide: 'ep:i/pages', useClass: ep___i_pages.default };
|
||||||
const $i_pin: Provider = { provide: 'ep:i/pin', useClass: ep___i_pin.default };
|
const $i_pin: Provider = { provide: 'ep:i/pin', useClass: ep___i_pin.default };
|
||||||
const $i_readAllMessagingMessages: Provider = { provide: 'ep:i/read-all-messaging-messages', useClass: ep___i_readAllMessagingMessages.default };
|
|
||||||
const $i_readAllUnreadNotes: Provider = { provide: 'ep:i/read-all-unread-notes', useClass: ep___i_readAllUnreadNotes.default };
|
const $i_readAllUnreadNotes: Provider = { provide: 'ep:i/read-all-unread-notes', useClass: ep___i_readAllUnreadNotes.default };
|
||||||
const $i_readAnnouncement: Provider = { provide: 'ep:i/read-announcement', useClass: ep___i_readAnnouncement.default };
|
const $i_readAnnouncement: Provider = { provide: 'ep:i/read-announcement', useClass: ep___i_readAnnouncement.default };
|
||||||
const $i_regenerateToken: Provider = { provide: 'ep:i/regenerate-token', useClass: ep___i_regenerateToken.default };
|
const $i_regenerateToken: Provider = { provide: 'ep:i/regenerate-token', useClass: ep___i_regenerateToken.default };
|
||||||
@ -547,17 +527,11 @@ const $i_signinHistory: Provider = { provide: 'ep:i/signin-history', useClass: e
|
|||||||
const $i_unpin: Provider = { provide: 'ep:i/unpin', useClass: ep___i_unpin.default };
|
const $i_unpin: Provider = { provide: 'ep:i/unpin', useClass: ep___i_unpin.default };
|
||||||
const $i_updateEmail: Provider = { provide: 'ep:i/update-email', useClass: ep___i_updateEmail.default };
|
const $i_updateEmail: Provider = { provide: 'ep:i/update-email', useClass: ep___i_updateEmail.default };
|
||||||
const $i_update: Provider = { provide: 'ep:i/update', useClass: ep___i_update.default };
|
const $i_update: Provider = { provide: 'ep:i/update', useClass: ep___i_update.default };
|
||||||
const $i_userGroupInvites: Provider = { provide: 'ep:i/user-group-invites', useClass: ep___i_userGroupInvites.default };
|
|
||||||
const $i_webhooks_create: Provider = { provide: 'ep:i/webhooks/create', useClass: ep___i_webhooks_create.default };
|
const $i_webhooks_create: Provider = { provide: 'ep:i/webhooks/create', useClass: ep___i_webhooks_create.default };
|
||||||
const $i_webhooks_list: Provider = { provide: 'ep:i/webhooks/list', useClass: ep___i_webhooks_list.default };
|
const $i_webhooks_list: Provider = { provide: 'ep:i/webhooks/list', useClass: ep___i_webhooks_list.default };
|
||||||
const $i_webhooks_show: Provider = { provide: 'ep:i/webhooks/show', useClass: ep___i_webhooks_show.default };
|
const $i_webhooks_show: Provider = { provide: 'ep:i/webhooks/show', useClass: ep___i_webhooks_show.default };
|
||||||
const $i_webhooks_update: Provider = { provide: 'ep:i/webhooks/update', useClass: ep___i_webhooks_update.default };
|
const $i_webhooks_update: Provider = { provide: 'ep:i/webhooks/update', useClass: ep___i_webhooks_update.default };
|
||||||
const $i_webhooks_delete: Provider = { provide: 'ep:i/webhooks/delete', useClass: ep___i_webhooks_delete.default };
|
const $i_webhooks_delete: Provider = { provide: 'ep:i/webhooks/delete', useClass: ep___i_webhooks_delete.default };
|
||||||
const $messaging_history: Provider = { provide: 'ep:messaging/history', useClass: ep___messaging_history.default };
|
|
||||||
const $messaging_messages: Provider = { provide: 'ep:messaging/messages', useClass: ep___messaging_messages.default };
|
|
||||||
const $messaging_messages_create: Provider = { provide: 'ep:messaging/messages/create', useClass: ep___messaging_messages_create.default };
|
|
||||||
const $messaging_messages_delete: Provider = { provide: 'ep:messaging/messages/delete', useClass: ep___messaging_messages_delete.default };
|
|
||||||
const $messaging_messages_read: Provider = { provide: 'ep:messaging/messages/read', useClass: ep___messaging_messages_read.default };
|
|
||||||
const $meta: Provider = { provide: 'ep:meta', useClass: ep___meta.default };
|
const $meta: Provider = { provide: 'ep:meta', useClass: ep___meta.default };
|
||||||
const $emojis: Provider = { provide: 'ep:emojis', useClass: ep___emojis.default };
|
const $emojis: Provider = { provide: 'ep:emojis', useClass: ep___emojis.default };
|
||||||
const $miauth_genToken: Provider = { provide: 'ep:miauth/gen-token', useClass: ep___miauth_genToken.default };
|
const $miauth_genToken: Provider = { provide: 'ep:miauth/gen-token', useClass: ep___miauth_genToken.default };
|
||||||
@ -635,18 +609,6 @@ const $users_followers: Provider = { provide: 'ep:users/followers', useClass: ep
|
|||||||
const $users_following: Provider = { provide: 'ep:users/following', useClass: ep___users_following.default };
|
const $users_following: Provider = { provide: 'ep:users/following', useClass: ep___users_following.default };
|
||||||
const $users_gallery_posts: Provider = { provide: 'ep:users/gallery/posts', useClass: ep___users_gallery_posts.default };
|
const $users_gallery_posts: Provider = { provide: 'ep:users/gallery/posts', useClass: ep___users_gallery_posts.default };
|
||||||
const $users_getFrequentlyRepliedUsers: Provider = { provide: 'ep:users/get-frequently-replied-users', useClass: ep___users_getFrequentlyRepliedUsers.default };
|
const $users_getFrequentlyRepliedUsers: Provider = { provide: 'ep:users/get-frequently-replied-users', useClass: ep___users_getFrequentlyRepliedUsers.default };
|
||||||
const $users_groups_create: Provider = { provide: 'ep:users/groups/create', useClass: ep___users_groups_create.default };
|
|
||||||
const $users_groups_delete: Provider = { provide: 'ep:users/groups/delete', useClass: ep___users_groups_delete.default };
|
|
||||||
const $users_groups_invitations_accept: Provider = { provide: 'ep:users/groups/invitations/accept', useClass: ep___users_groups_invitations_accept.default };
|
|
||||||
const $users_groups_invitations_reject: Provider = { provide: 'ep:users/groups/invitations/reject', useClass: ep___users_groups_invitations_reject.default };
|
|
||||||
const $users_groups_invite: Provider = { provide: 'ep:users/groups/invite', useClass: ep___users_groups_invite.default };
|
|
||||||
const $users_groups_joined: Provider = { provide: 'ep:users/groups/joined', useClass: ep___users_groups_joined.default };
|
|
||||||
const $users_groups_leave: Provider = { provide: 'ep:users/groups/leave', useClass: ep___users_groups_leave.default };
|
|
||||||
const $users_groups_owned: Provider = { provide: 'ep:users/groups/owned', useClass: ep___users_groups_owned.default };
|
|
||||||
const $users_groups_pull: Provider = { provide: 'ep:users/groups/pull', useClass: ep___users_groups_pull.default };
|
|
||||||
const $users_groups_show: Provider = { provide: 'ep:users/groups/show', useClass: ep___users_groups_show.default };
|
|
||||||
const $users_groups_transfer: Provider = { provide: 'ep:users/groups/transfer', useClass: ep___users_groups_transfer.default };
|
|
||||||
const $users_groups_update: Provider = { provide: 'ep:users/groups/update', useClass: ep___users_groups_update.default };
|
|
||||||
const $users_lists_create: Provider = { provide: 'ep:users/lists/create', useClass: ep___users_lists_create.default };
|
const $users_lists_create: Provider = { provide: 'ep:users/lists/create', useClass: ep___users_lists_create.default };
|
||||||
const $users_lists_delete: Provider = { provide: 'ep:users/lists/delete', useClass: ep___users_lists_delete.default };
|
const $users_lists_delete: Provider = { provide: 'ep:users/lists/delete', useClass: ep___users_lists_delete.default };
|
||||||
const $users_lists_list: Provider = { provide: 'ep:users/lists/list', useClass: ep___users_lists_list.default };
|
const $users_lists_list: Provider = { provide: 'ep:users/lists/list', useClass: ep___users_lists_list.default };
|
||||||
@ -869,7 +831,6 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
|
|||||||
$i_pageLikes,
|
$i_pageLikes,
|
||||||
$i_pages,
|
$i_pages,
|
||||||
$i_pin,
|
$i_pin,
|
||||||
$i_readAllMessagingMessages,
|
|
||||||
$i_readAllUnreadNotes,
|
$i_readAllUnreadNotes,
|
||||||
$i_readAnnouncement,
|
$i_readAnnouncement,
|
||||||
$i_regenerateToken,
|
$i_regenerateToken,
|
||||||
@ -886,17 +847,11 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
|
|||||||
$i_unpin,
|
$i_unpin,
|
||||||
$i_updateEmail,
|
$i_updateEmail,
|
||||||
$i_update,
|
$i_update,
|
||||||
$i_userGroupInvites,
|
|
||||||
$i_webhooks_create,
|
$i_webhooks_create,
|
||||||
$i_webhooks_list,
|
$i_webhooks_list,
|
||||||
$i_webhooks_show,
|
$i_webhooks_show,
|
||||||
$i_webhooks_update,
|
$i_webhooks_update,
|
||||||
$i_webhooks_delete,
|
$i_webhooks_delete,
|
||||||
$messaging_history,
|
|
||||||
$messaging_messages,
|
|
||||||
$messaging_messages_create,
|
|
||||||
$messaging_messages_delete,
|
|
||||||
$messaging_messages_read,
|
|
||||||
$meta,
|
$meta,
|
||||||
$emojis,
|
$emojis,
|
||||||
$miauth_genToken,
|
$miauth_genToken,
|
||||||
@ -974,18 +929,6 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
|
|||||||
$users_following,
|
$users_following,
|
||||||
$users_gallery_posts,
|
$users_gallery_posts,
|
||||||
$users_getFrequentlyRepliedUsers,
|
$users_getFrequentlyRepliedUsers,
|
||||||
$users_groups_create,
|
|
||||||
$users_groups_delete,
|
|
||||||
$users_groups_invitations_accept,
|
|
||||||
$users_groups_invitations_reject,
|
|
||||||
$users_groups_invite,
|
|
||||||
$users_groups_joined,
|
|
||||||
$users_groups_leave,
|
|
||||||
$users_groups_owned,
|
|
||||||
$users_groups_pull,
|
|
||||||
$users_groups_show,
|
|
||||||
$users_groups_transfer,
|
|
||||||
$users_groups_update,
|
|
||||||
$users_lists_create,
|
$users_lists_create,
|
||||||
$users_lists_delete,
|
$users_lists_delete,
|
||||||
$users_lists_list,
|
$users_lists_list,
|
||||||
@ -1202,7 +1145,6 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
|
|||||||
$i_pageLikes,
|
$i_pageLikes,
|
||||||
$i_pages,
|
$i_pages,
|
||||||
$i_pin,
|
$i_pin,
|
||||||
$i_readAllMessagingMessages,
|
|
||||||
$i_readAllUnreadNotes,
|
$i_readAllUnreadNotes,
|
||||||
$i_readAnnouncement,
|
$i_readAnnouncement,
|
||||||
$i_regenerateToken,
|
$i_regenerateToken,
|
||||||
@ -1219,17 +1161,11 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
|
|||||||
$i_unpin,
|
$i_unpin,
|
||||||
$i_updateEmail,
|
$i_updateEmail,
|
||||||
$i_update,
|
$i_update,
|
||||||
$i_userGroupInvites,
|
|
||||||
$i_webhooks_create,
|
$i_webhooks_create,
|
||||||
$i_webhooks_list,
|
$i_webhooks_list,
|
||||||
$i_webhooks_show,
|
$i_webhooks_show,
|
||||||
$i_webhooks_update,
|
$i_webhooks_update,
|
||||||
$i_webhooks_delete,
|
$i_webhooks_delete,
|
||||||
$messaging_history,
|
|
||||||
$messaging_messages,
|
|
||||||
$messaging_messages_create,
|
|
||||||
$messaging_messages_delete,
|
|
||||||
$messaging_messages_read,
|
|
||||||
$meta,
|
$meta,
|
||||||
$emojis,
|
$emojis,
|
||||||
$miauth_genToken,
|
$miauth_genToken,
|
||||||
@ -1305,18 +1241,6 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
|
|||||||
$users_following,
|
$users_following,
|
||||||
$users_gallery_posts,
|
$users_gallery_posts,
|
||||||
$users_getFrequentlyRepliedUsers,
|
$users_getFrequentlyRepliedUsers,
|
||||||
$users_groups_create,
|
|
||||||
$users_groups_delete,
|
|
||||||
$users_groups_invitations_accept,
|
|
||||||
$users_groups_invitations_reject,
|
|
||||||
$users_groups_invite,
|
|
||||||
$users_groups_joined,
|
|
||||||
$users_groups_leave,
|
|
||||||
$users_groups_owned,
|
|
||||||
$users_groups_pull,
|
|
||||||
$users_groups_show,
|
|
||||||
$users_groups_transfer,
|
|
||||||
$users_groups_update,
|
|
||||||
$users_lists_create,
|
$users_lists_create,
|
||||||
$users_lists_delete,
|
$users_lists_delete,
|
||||||
$users_lists_list,
|
$users_lists_list,
|
||||||
|
@ -7,7 +7,7 @@ import { DI } from '@/di-symbols.js';
|
|||||||
import type { UserSecurityKeysRepository, SigninsRepository, UserProfilesRepository, AttestationChallengesRepository, UsersRepository } from '@/models/index.js';
|
import type { UserSecurityKeysRepository, SigninsRepository, UserProfilesRepository, AttestationChallengesRepository, UsersRepository } from '@/models/index.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import { getIpHash } from '@/misc/get-ip-hash.js';
|
import { getIpHash } from '@/misc/get-ip-hash.js';
|
||||||
import type { ILocalUser } from '@/models/entities/User.js';
|
import type { LocalUser } from '@/models/entities/User.js';
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
import { TwoFactorAuthenticationService } from '@/core/TwoFactorAuthenticationService.js';
|
import { TwoFactorAuthenticationService } from '@/core/TwoFactorAuthenticationService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
@ -105,7 +105,7 @@ export class SigninApiService {
|
|||||||
const user = await this.usersRepository.findOneBy({
|
const user = await this.usersRepository.findOneBy({
|
||||||
usernameLower: username.toLowerCase(),
|
usernameLower: username.toLowerCase(),
|
||||||
host: IsNull(),
|
host: IsNull(),
|
||||||
}) as ILocalUser;
|
}) as LocalUser;
|
||||||
|
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
return error(404, {
|
return error(404, {
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user