mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-05-06 23:25:39 +02:00
Compare commits
90 Commits
2026.3.2-b
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a82ba0d775 | ||
|
|
b78e0168b0 | ||
|
|
33f59b3469 | ||
|
|
5b478dda9d | ||
|
|
90725d6a8c | ||
|
|
86542f07d3 | ||
|
|
45022bc766 | ||
|
|
35711fc8e1 | ||
|
|
45f140aa86 | ||
|
|
22ce7b58ca | ||
|
|
37107c9818 | ||
|
|
a5a43c8c06 | ||
|
|
723d8add2f | ||
|
|
9d20152e05 | ||
|
|
37412f0e1b | ||
|
|
712b51c142 | ||
|
|
2b4bdbfde7 | ||
|
|
39032c4b1b | ||
|
|
f5a3d8996d | ||
|
|
d55e936653 | ||
|
|
6229ac365e | ||
|
|
6d9412b338 | ||
|
|
a23a72b015 | ||
|
|
93bd9d551d | ||
|
|
35d6c20828 | ||
|
|
7c9942f014 | ||
|
|
665adfccb7 | ||
|
|
973b5b50a9 | ||
|
|
985de915b3 | ||
|
|
0227148c89 | ||
|
|
7bfd85cdba | ||
|
|
21f51be5b7 | ||
|
|
b45f18cd14 | ||
|
|
6176cca0a4 | ||
|
|
9569310adb | ||
|
|
b28338c812 | ||
|
|
0f5da63328 | ||
|
|
23715c649c | ||
|
|
1dc5c60b2b | ||
|
|
3a3057a1b1 | ||
|
|
8a85ee1d45 | ||
|
|
c1b73f947c | ||
|
|
bda98ba1d9 | ||
|
|
351fe2f70a | ||
|
|
6d15fe32d0 | ||
|
|
1391269a67 | ||
|
|
86092f2faf | ||
|
|
c640aeb27a | ||
|
|
0be3142d54 | ||
|
|
37bfcb604f | ||
|
|
024f8bb102 | ||
|
|
277a1ef31f | ||
|
|
8e536eb4b9 | ||
|
|
360e805638 | ||
|
|
c95aef7535 | ||
|
|
758a085812 | ||
|
|
a6de986f83 | ||
|
|
12e590a63f | ||
|
|
a5b1f839ac | ||
|
|
d7ceaa9c88 | ||
|
|
55b0fbd172 | ||
|
|
6532cb85e7 | ||
|
|
5dc508346c | ||
|
|
c9c6ef2772 | ||
|
|
4d6256e91d | ||
|
|
cdf0438154 | ||
|
|
29cecd750f | ||
|
|
68e3476a16 | ||
|
|
3cb003366f | ||
|
|
787de92c2f | ||
|
|
22577e2134 | ||
|
|
0355420c81 | ||
|
|
4750980cef | ||
|
|
92e0e8edf7 | ||
|
|
5cb3a91b15 | ||
|
|
60018d16da | ||
|
|
b63984893e | ||
|
|
d4a5048aae | ||
|
|
b9923d0a23 | ||
|
|
38be94b2a3 | ||
|
|
ae34578c6f | ||
|
|
367766d864 | ||
|
|
a18c909ba3 | ||
|
|
0b7b59f1e2 | ||
|
|
8169c57bd1 | ||
|
|
e601fcb729 | ||
|
|
5361a3819b | ||
|
|
dbc5fe2454 | ||
|
|
7bd7e5c4db | ||
|
|
b97683cdb2 |
@@ -36,7 +36,7 @@ services:
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: misskey
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
- postgres-data:/var/lib/postgresql
|
||||
healthcheck:
|
||||
test: "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"
|
||||
interval: 5s
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
"editorconfig.editorconfig",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"Vue.volar",
|
||||
"Orta.vscode-jest",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"mrmlnc.vscode-json5"
|
||||
]
|
||||
|
||||
3
.github/dependabot.yml
vendored
3
.github/dependabot.yml
vendored
@@ -34,9 +34,6 @@ updates:
|
||||
patterns:
|
||||
- "storybook*"
|
||||
- "@storybook/*"
|
||||
swc-core:
|
||||
patterns:
|
||||
- "@swc/core*"
|
||||
typescript-eslint:
|
||||
patterns:
|
||||
- "@typescript-eslint/*"
|
||||
|
||||
4
.github/workflows/api-misskey-js.yml
vendored
4
.github/workflows/api-misskey-js.yml
vendored
@@ -19,10 +19,10 @@ jobs:
|
||||
uses: actions/checkout@v6.0.2
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4.4.0
|
||||
uses: pnpm/action-setup@v6.0.3
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6.3.0
|
||||
uses: actions/setup-node@v6.4.0
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
cache: 'pnpm'
|
||||
|
||||
2
.github/workflows/changelog-check.yml
vendored
2
.github/workflows/changelog-check.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
- name: Checkout head
|
||||
uses: actions/checkout@v6.0.2
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6.3.0
|
||||
uses: actions/setup-node@v6.4.0
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
|
||||
|
||||
12
.github/workflows/check-misskey-js-autogen.yml
vendored
12
.github/workflows/check-misskey-js-autogen.yml
vendored
@@ -25,11 +25,11 @@ jobs:
|
||||
ref: refs/pull/${{ github.event.pull_request.number }}/merge
|
||||
|
||||
- name: setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
uses: pnpm/action-setup@v6
|
||||
|
||||
- name: setup node
|
||||
id: setup-node
|
||||
uses: actions/setup-node@v6.3.0
|
||||
uses: actions/setup-node@v6.4.0
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
cache: pnpm
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
|
||||
# packages/misskey-js/generator/built/autogen
|
||||
- name: Upload Generated
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: generated-misskey-js
|
||||
path: packages/misskey-js/generator/built/autogen
|
||||
@@ -73,7 +73,7 @@ jobs:
|
||||
ref: refs/pull/${{ github.event.pull_request.number }}/merge
|
||||
|
||||
- name: Upload From Merged
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: actual-misskey-js
|
||||
path: packages/misskey-js/src/autogen
|
||||
@@ -86,13 +86,13 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: download generated-misskey-js
|
||||
uses: actions/download-artifact@v7
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: generated-misskey-js
|
||||
path: misskey-js-generated
|
||||
|
||||
- name: download actual-misskey-js
|
||||
uses: actions/download-artifact@v7
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: actual-misskey-js
|
||||
path: misskey-js-actual
|
||||
|
||||
14
.github/workflows/docker-develop.yml
vendored
14
.github/workflows/docker-develop.yml
vendored
@@ -29,15 +29,15 @@ jobs:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v6.0.2
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v4
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Build and push by digest
|
||||
id: build
|
||||
uses: docker/build-push-action@v6
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "/tmp/digests/${digest#sha256:}"
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: digests-${{ env.PLATFORM_PAIR }}
|
||||
path: /tmp/digests/*
|
||||
@@ -66,15 +66,15 @@ jobs:
|
||||
- build
|
||||
steps:
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@v7
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
path: /tmp/digests
|
||||
pattern: digests-*
|
||||
merge-multiple: true
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v4
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
18
.github/workflows/docker.yml
vendored
18
.github/workflows/docker.yml
vendored
@@ -34,21 +34,21 @@ jobs:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v6.0.2
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v4
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
uses: docker/metadata-action@v6
|
||||
with:
|
||||
images: ${{ env.REGISTRY_IMAGE }}
|
||||
tags: ${{ env.TAGS }}
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Build and Push to Docker Hub
|
||||
id: build
|
||||
uses: docker/build-push-action@v6
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "/tmp/digests/${digest#sha256:}"
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: digests-${{ env.PLATFORM_PAIR }}
|
||||
path: /tmp/digests/*
|
||||
@@ -77,21 +77,21 @@ jobs:
|
||||
- build
|
||||
steps:
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@v7
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
path: /tmp/digests
|
||||
pattern: digests-*
|
||||
merge-multiple: true
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v4
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
uses: docker/metadata-action@v6
|
||||
with:
|
||||
images: ${{ env.REGISTRY_IMAGE }}
|
||||
tags: ${{ env.TAGS }}
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
8
.github/workflows/get-api-diff.yml
vendored
8
.github/workflows/get-api-diff.yml
vendored
@@ -30,9 +30,9 @@ jobs:
|
||||
ref: ${{ matrix.ref }}
|
||||
submodules: true
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4.4.0
|
||||
uses: pnpm/action-setup@v6.0.3
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v6.3.0
|
||||
uses: actions/setup-node@v6.4.0
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
cache: 'pnpm'
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
- name: Copy API.json
|
||||
run: cp packages/backend/built/api.json ${{ matrix.api-json-name }}
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: api-artifact-${{ matrix.api-json-name }}
|
||||
path: ${{ matrix.api-json-name }}
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
PR_NUMBER: ${{ github.event.number }}
|
||||
run: |
|
||||
echo "$PR_NUMBER" > ./pr_number
|
||||
- uses: actions/upload-artifact@v6
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: api-artifact-pr-number
|
||||
path: pr_number
|
||||
|
||||
10
.github/workflows/get-backend-memory.yml
vendored
10
.github/workflows/get-backend-memory.yml
vendored
@@ -35,7 +35,7 @@ jobs:
|
||||
POSTGRES_DB: test-misskey
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
redis:
|
||||
image: redis:7
|
||||
image: redis:8
|
||||
ports:
|
||||
- 56312:6379
|
||||
|
||||
@@ -45,9 +45,9 @@ jobs:
|
||||
ref: ${{ matrix.ref }}
|
||||
submodules: true
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4.4.0
|
||||
uses: pnpm/action-setup@v6.0.3
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v6.3.0
|
||||
uses: actions/setup-node@v6.4.0
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
cache: 'pnpm'
|
||||
@@ -67,7 +67,7 @@ jobs:
|
||||
# Start the server and measure memory usage
|
||||
node packages/backend/scripts/measure-memory.mjs > ${{ matrix.memory-json-name }}
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: memory-artifact-${{ matrix.memory-json-name }}
|
||||
path: ${{ matrix.memory-json-name }}
|
||||
@@ -81,7 +81,7 @@ jobs:
|
||||
PR_NUMBER: ${{ github.event.number }}
|
||||
run: |
|
||||
echo "$PR_NUMBER" > ./pr_number
|
||||
- uses: actions/upload-artifact@v6
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: memory-artifact-pr-number
|
||||
path: pr_number
|
||||
|
||||
14
.github/workflows/lint.yml
vendored
14
.github/workflows/lint.yml
vendored
@@ -41,8 +41,8 @@ jobs:
|
||||
fetch-depth: 0
|
||||
submodules: true
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4.4.0
|
||||
- uses: actions/setup-node@v6.3.0
|
||||
uses: pnpm/action-setup@v6.0.3
|
||||
- uses: actions/setup-node@v6.4.0
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
cache: 'pnpm'
|
||||
@@ -74,14 +74,14 @@ jobs:
|
||||
fetch-depth: 0
|
||||
submodules: true
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4.4.0
|
||||
- uses: actions/setup-node@v6.3.0
|
||||
uses: pnpm/action-setup@v6.0.3
|
||||
- uses: actions/setup-node@v6.4.0
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
cache: 'pnpm'
|
||||
- run: pnpm i --frozen-lockfile
|
||||
- name: Restore eslint cache
|
||||
uses: actions/cache@v4.3.0
|
||||
uses: actions/cache@v5.0.5
|
||||
with:
|
||||
path: ${{ env.eslint-cache-path }}
|
||||
key: eslint-${{ env.eslint-cache-version }}-${{ matrix.workspace }}-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ github.ref_name }}-${{ github.sha }}
|
||||
@@ -105,8 +105,8 @@ jobs:
|
||||
fetch-depth: 0
|
||||
submodules: true
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4.4.0
|
||||
- uses: actions/setup-node@v6.3.0
|
||||
uses: pnpm/action-setup@v6.0.3
|
||||
- uses: actions/setup-node@v6.4.0
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
cache: 'pnpm'
|
||||
|
||||
4
.github/workflows/locale.yml
vendored
4
.github/workflows/locale.yml
vendored
@@ -21,8 +21,8 @@ jobs:
|
||||
fetch-depth: 0
|
||||
submodules: true
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4.4.0
|
||||
- uses: actions/setup-node@v6.3.0
|
||||
uses: pnpm/action-setup@v6.0.3
|
||||
- uses: actions/setup-node@v6.4.0
|
||||
with:
|
||||
node-version-file: ".node-version"
|
||||
cache: "pnpm"
|
||||
|
||||
7
.github/workflows/on-release-created.yml
vendored
7
.github/workflows/on-release-created.yml
vendored
@@ -20,14 +20,17 @@ jobs:
|
||||
with:
|
||||
submodules: true
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4.4.0
|
||||
uses: pnpm/action-setup@v6.0.3
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v6.3.0
|
||||
uses: actions/setup-node@v6.4.0
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
cache: 'pnpm'
|
||||
# see https://docs.github.com/actions/use-cases-and-examples/publishing-packages/publishing-nodejs-packages#publishing-packages-to-the-npm-registry
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
# Ensure npm 11.5.1 or later is installed
|
||||
- name: Update npm
|
||||
run: npm install -g npm@latest
|
||||
- name: Publish package
|
||||
run: |
|
||||
pnpm i --frozen-lockfile
|
||||
|
||||
4
.github/workflows/report-api-diff.yml
vendored
4
.github/workflows/report-api-diff.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
# api-artifact
|
||||
steps:
|
||||
- name: Download artifact
|
||||
uses: actions/github-script@v8.0.0
|
||||
uses: actions/github-script@v9
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
@@ -60,7 +60,7 @@ jobs:
|
||||
- name: Echo full diff
|
||||
run: cat ./api-full.json.diff
|
||||
- name: Upload full diff to Artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: api-artifact
|
||||
path: |
|
||||
|
||||
2
.github/workflows/report-backend-memory.yml
vendored
2
.github/workflows/report-backend-memory.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Download artifact
|
||||
uses: actions/github-script@v8.0.0
|
||||
uses: actions/github-script@v9
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
|
||||
2
.github/workflows/request-release-review.yml
vendored
2
.github/workflows/request-release-review.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Reply
|
||||
uses: actions/github-script@v8
|
||||
uses: actions/github-script@v9
|
||||
with:
|
||||
script: |
|
||||
const body = `To dev team (@misskey-dev/dev):
|
||||
|
||||
8
.github/workflows/storybook.yml
vendored
8
.github/workflows/storybook.yml
vendored
@@ -37,9 +37,9 @@ jobs:
|
||||
if: github.event_name == 'pull_request_target'
|
||||
run: git checkout "$(git rev-list --parents -n1 HEAD | cut -d" " -f3)"
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4.4.0
|
||||
uses: pnpm/action-setup@v6.0.3
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v6.3.0
|
||||
uses: actions/setup-node@v6.4.0
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
cache: 'pnpm'
|
||||
@@ -90,7 +90,7 @@ jobs:
|
||||
env:
|
||||
CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
|
||||
- name: Notify that Chromatic detects changes
|
||||
uses: actions/github-script@v8.0.0
|
||||
uses: actions/github-script@v9
|
||||
if: github.event_name != 'pull_request_target' && steps.chromatic_push.outputs.success == 'false'
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -102,7 +102,7 @@ jobs:
|
||||
body: 'Chromatic detects changes. Please [review the changes on Chromatic](https://www.chromatic.com/builds?appId=6428f7d7b962f0b79f97d6e4).'
|
||||
})
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: storybook
|
||||
path: packages/frontend/storybook-static
|
||||
|
||||
24
.github/workflows/test-backend.yml
vendored
24
.github/workflows/test-backend.yml
vendored
@@ -45,11 +45,11 @@ jobs:
|
||||
POSTGRES_DB: test-misskey
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
redis:
|
||||
image: redis:7
|
||||
image: redis:8
|
||||
ports:
|
||||
- 56312:6379
|
||||
meilisearch:
|
||||
image: getmeili/meilisearch:v1.38.2
|
||||
image: getmeili/meilisearch:v1.42.1
|
||||
ports:
|
||||
- 57712:7700
|
||||
env:
|
||||
@@ -61,13 +61,13 @@ jobs:
|
||||
with:
|
||||
submodules: true
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4.4.0
|
||||
uses: pnpm/action-setup@v6.0.3
|
||||
- name: Get current date
|
||||
id: current-date
|
||||
run: echo "today=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
|
||||
- name: Setup and Restore ffmpeg/ffprobe Cache
|
||||
id: cache-ffmpeg
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: |
|
||||
/usr/local/bin/ffmpeg
|
||||
@@ -93,7 +93,7 @@ jobs:
|
||||
fi
|
||||
done
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v6.3.0
|
||||
uses: actions/setup-node@v6.4.0
|
||||
with:
|
||||
node-version-file: ${{ matrix.node-version-file }}
|
||||
cache: 'pnpm'
|
||||
@@ -107,7 +107,7 @@ jobs:
|
||||
- name: Test
|
||||
run: pnpm --filter backend test-and-coverage
|
||||
- name: Upload to Codecov
|
||||
uses: codecov/codecov-action@v5
|
||||
uses: codecov/codecov-action@v6
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: ./packages/backend/coverage/coverage-final.json
|
||||
@@ -131,7 +131,7 @@ jobs:
|
||||
POSTGRES_DB: test-misskey
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
redis:
|
||||
image: redis:7
|
||||
image: redis:8
|
||||
ports:
|
||||
- 56312:6379
|
||||
|
||||
@@ -140,9 +140,9 @@ jobs:
|
||||
with:
|
||||
submodules: true
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4.4.0
|
||||
uses: pnpm/action-setup@v6.0.3
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v6.3.0
|
||||
uses: actions/setup-node@v6.4.0
|
||||
with:
|
||||
node-version-file: ${{ matrix.node-version-file }}
|
||||
cache: 'pnpm'
|
||||
@@ -156,7 +156,7 @@ jobs:
|
||||
- name: Test
|
||||
run: pnpm --filter backend test-and-coverage:e2e
|
||||
- name: Upload to Codecov
|
||||
uses: codecov/codecov-action@v5
|
||||
uses: codecov/codecov-action@v6
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: ./packages/backend/coverage/coverage-final.json
|
||||
@@ -184,12 +184,12 @@ jobs:
|
||||
with:
|
||||
submodules: true
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4.4.0
|
||||
uses: pnpm/action-setup@v6.0.3
|
||||
- name: Get current date
|
||||
id: current-date
|
||||
run: echo "today=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v6.3.0
|
||||
uses: actions/setup-node@v6.4.0
|
||||
with:
|
||||
node-version-file: ${{ matrix.node-version-file }}
|
||||
cache: 'pnpm'
|
||||
|
||||
6
.github/workflows/test-federation.yml
vendored
6
.github/workflows/test-federation.yml
vendored
@@ -36,13 +36,13 @@ jobs:
|
||||
with:
|
||||
submodules: true
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4.4.0
|
||||
uses: pnpm/action-setup@v6.0.3
|
||||
- name: Get current date
|
||||
id: current-date
|
||||
run: echo "today=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
|
||||
- name: Setup and Restore ffmpeg/ffprobe Cache
|
||||
id: cache-ffmpeg
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: |
|
||||
/usr/local/bin/ffmpeg
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
fi
|
||||
done
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v6.3.0
|
||||
uses: actions/setup-node@v6.4.0
|
||||
with:
|
||||
node-version-file: ${{ matrix.node-version-file }}
|
||||
cache: 'pnpm'
|
||||
|
||||
18
.github/workflows/test-frontend.yml
vendored
18
.github/workflows/test-frontend.yml
vendored
@@ -32,9 +32,9 @@ jobs:
|
||||
with:
|
||||
submodules: true
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4.4.0
|
||||
uses: pnpm/action-setup@v6.0.3
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v6.3.0
|
||||
uses: actions/setup-node@v6.4.0
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
cache: 'pnpm'
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
- name: Test
|
||||
run: pnpm --filter frontend test-and-coverage
|
||||
- name: Upload Coverage
|
||||
uses: codecov/codecov-action@v5
|
||||
uses: codecov/codecov-action@v6
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: ./packages/frontend/coverage/coverage-final.json
|
||||
@@ -71,7 +71,7 @@ jobs:
|
||||
POSTGRES_DB: test-misskey
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
redis:
|
||||
image: redis:7
|
||||
image: redis:8
|
||||
ports:
|
||||
- 56312:6379
|
||||
|
||||
@@ -86,9 +86,9 @@ jobs:
|
||||
#- uses: browser-actions/setup-firefox@latest
|
||||
# if: ${{ matrix.browser == 'firefox' }}
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4.4.0
|
||||
uses: pnpm/action-setup@v6.0.3
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v6.3.0
|
||||
uses: actions/setup-node@v6.4.0
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
cache: 'pnpm'
|
||||
@@ -105,7 +105,7 @@ jobs:
|
||||
- name: Cypress install
|
||||
run: pnpm exec cypress install
|
||||
- name: Cypress run
|
||||
uses: cypress-io/github-action@v6
|
||||
uses: cypress-io/github-action@v7.1.9
|
||||
timeout-minutes: 15
|
||||
with:
|
||||
install: false
|
||||
@@ -113,12 +113,12 @@ jobs:
|
||||
wait-on: 'http://localhost:61812'
|
||||
headed: true
|
||||
browser: ${{ matrix.browser }}
|
||||
- uses: actions/upload-artifact@v6
|
||||
- uses: actions/upload-artifact@v7
|
||||
if: failure()
|
||||
with:
|
||||
name: ${{ matrix.browser }}-cypress-screenshots
|
||||
path: cypress/screenshots
|
||||
- uses: actions/upload-artifact@v6
|
||||
- uses: actions/upload-artifact@v7
|
||||
if: always()
|
||||
with:
|
||||
name: ${{ matrix.browser }}-cypress-videos
|
||||
|
||||
6
.github/workflows/test-misskey-js.yml
vendored
6
.github/workflows/test-misskey-js.yml
vendored
@@ -25,10 +25,10 @@ jobs:
|
||||
uses: actions/checkout@v6.0.2
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4.4.0
|
||||
uses: pnpm/action-setup@v6.0.3
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6.3.0
|
||||
uses: actions/setup-node@v6.4.0
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
cache: 'pnpm'
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
CI: true
|
||||
|
||||
- name: Upload Coverage
|
||||
uses: codecov/codecov-action@v5
|
||||
uses: codecov/codecov-action@v6
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: ./packages/misskey-js/coverage/coverage-final.json
|
||||
|
||||
4
.github/workflows/test-production.yml
vendored
4
.github/workflows/test-production.yml
vendored
@@ -20,9 +20,9 @@ jobs:
|
||||
with:
|
||||
submodules: true
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4.4.0
|
||||
uses: pnpm/action-setup@v6.0.3
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v6.3.0
|
||||
uses: actions/setup-node@v6.4.0
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
cache: 'pnpm'
|
||||
|
||||
4
.github/workflows/validate-api-json.yml
vendored
4
.github/workflows/validate-api-json.yml
vendored
@@ -21,9 +21,9 @@ jobs:
|
||||
with:
|
||||
submodules: true
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4.4.0
|
||||
uses: pnpm/action-setup@v6.0.3
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v6.3.0
|
||||
uses: actions/setup-node@v6.4.0
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
cache: 'pnpm'
|
||||
|
||||
227
CHANGELOG.md
227
CHANGELOG.md
@@ -1,3 +1,68 @@
|
||||
## Unreleased
|
||||
|
||||
### General
|
||||
-
|
||||
|
||||
### Client
|
||||
-
|
||||
|
||||
### Server
|
||||
-
|
||||
|
||||
|
||||
## 2026.5.1
|
||||
|
||||
### General
|
||||
- Enhance: チャンネルの作成の可否をロールポリシーで制御できるように
|
||||
- Fix: `.devcontainer/compose.yml`のvolumeのマウントパスを修正
|
||||
|
||||
### Client
|
||||
- Enhance: ノートの詳細表示での公開範囲の表示を改善
|
||||
(Cherry-picked from https://github.com/kokonect-link/cherrypick/commit/ecc75563f4e428b66adccc379bf317b5b21ed8e6)
|
||||
- Fix: ロール設定画面でロールをアサイン/アサイン解除した際、リロードしなくても画面に反映されるよう修正
|
||||
|
||||
### Server
|
||||
- Fix: ID生成アルゴリズムにULIDを使用している場合に通知が約10秒遅延する問題を修正
|
||||
- Fix: 公開範囲がフォロワーの投稿が通知されない問題を修正
|
||||
- Fix: URLプレビューが動作しない問題を修正
|
||||
|
||||
|
||||
## 2026.5.0
|
||||
|
||||
### General
|
||||
- Enhance: アバターデコレーションにカテゴリを設定できるように
|
||||
|
||||
### Client
|
||||
- Enhance: チャンネル指定リノートでリノート先のチャンネルに移動できるように
|
||||
- Enhance: ベータ版でのアップデート時のダイアログの更新情報リンクをGitHubのReleasesページに遷移するようにし、正しく閲覧できるように
|
||||
- Fix: 一部のページ内リンクが正しく動作しない問題を修正
|
||||
- Fix: ドライブへの画像アップロード時にファイル名の変更が無視される不具合を修正
|
||||
- Fix: 連合が無効化されたサーバーで一部の設定項目が空欄で表示される問題を修正
|
||||
- Fix: オーディオ、動画の再生速度メニューが開けない問題を修正
|
||||
|
||||
### Server
|
||||
- Enhance: メモリ使用量を削減
|
||||
- Enhance: 起動の高速化
|
||||
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/1410)
|
||||
- Enhance: バックエンドの開発モード時の安定性向上
|
||||
- Enhance: バックエンドビルド・テスト時に使用する依存関係の整理(swc/esbuild→Rolldown, Jest→Vitest)
|
||||
- Fix: ファイルシステムを用いる処理におけるパスの取り扱いを改善
|
||||
- Fix: `/api-doc` にアクセスできない問題を修正
|
||||
- Fix: support `alsoKnownAs` from remote actors as either array or unwrapped singleton
|
||||
- Fix: ローカルに存在しないリモートアカウントに対するアカウント削除リクエストを受信した際に、そのユーザーを新規作成して削除する挙動を修正
|
||||
- Fix: Inboxでの特定のエラーによる失敗はDelayedにしない
|
||||
- Fix: ID生成アルゴリズムにULIDを使用している場合にMisskeyが正しく動作しない問題を修正
|
||||
- Fix: リレー経由で届いたノートがリノートとして表示される問題を修正
|
||||
- Fix: robots.txtの内容を調整
|
||||
- Fix: 特定のユーザーに管理者権限を持つロールが複数ついている際に、取得できるユーザーIDが重複する問題を修正
|
||||
(Cherry-picked from https://github.com/lqvp/misskey-tempura/commit/17ed4108cec4b6bd2fd989db5a9091db91fa37a7)
|
||||
- Fix: ブロックしたサーバーからのInboxジョブが蓄積し続ける問題を修正
|
||||
(Cherry-picked from https://github.com/lqvp/misskey-tempura/commit/3f0f4bfe923f2b3a7837017b54841598f421c6ef)
|
||||
- Fix: support activity with `actor` as an id string or embedded object in inbox processor and ActivityPub inbox service
|
||||
- Fix: コンフィグファイルに `meilisearch` の設定がある状態でほかの検索プロバイダを利用すると、UI上からリモートのノートの検索ができない問題を修正
|
||||
- Fix: ノートに関する通知で公開範囲が考慮されていない問題を修正
|
||||
(Cherry-picked from https://github.com/lqvp/misskey-tempura/commit/cbce96c520a138b8bcd16890ff6f2952830fa166 originally presented in https://github.com/yojo-art/cherrypick/pull/743)
|
||||
|
||||
## 2026.3.2
|
||||
|
||||
### General
|
||||
@@ -31,7 +96,7 @@
|
||||
- `users/following` の `birthday` プロパティは非推奨になりました。代わりに `users/get-following-users-by-birthday` をご利用ください。
|
||||
|
||||
### General
|
||||
- Enhance: 「もうすぐ誕生日のユーザー」ウィジェットで、誕生日が至近のユーザーも表示できるように
|
||||
- Enhance: 「もうすぐ誕生日のユーザー」ウィジェットで、誕生日が至近のユーザーも表示できるように
|
||||
(Cherry-picked from https://github.com/MisskeyIO/misskey)
|
||||
- 「今日誕生日のユーザー」は「もうすぐ誕生日のユーザー」に名称変更されました
|
||||
- Fix: ユーザーハッシュタグページでユーザーの読み込みが重複する問題を修正
|
||||
@@ -87,9 +152,9 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
### Client
|
||||
- Enhance: デッキのUI説明を追加
|
||||
- Enhance: 設定がブラウザによって消去されないようにするオプションを追加
|
||||
- Fix: バージョン表記のないPlayが正しく動作しない問題を修正
|
||||
- Fix: バージョン表記のないPlayが正しく動作しない問題を修正
|
||||
バージョン表記のないものは v0.x 系として実行されます。v1.x 系で動作させたい場合は必ずバージョン表記を含めてください。
|
||||
- Fix: デッキUIでメニュー位置を下にしているとプロファイル削除ボタンが表示されないのを修正
|
||||
- Fix: デッキUIでメニュー位置を下にしているとプロファイル削除ボタンが表示されないのを修正
|
||||
- Fix: 一部のUnicode絵文字のリアクションがボタンにならない問題を修正
|
||||
|
||||
### Server
|
||||
@@ -134,11 +199,11 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
- Fix: ページの内容がはみ出ることがある問題を修正
|
||||
- Fix: ナビゲーションバーを下に表示しているときに、項目数が多いと表示が崩れる問題を修正
|
||||
- Fix: ヘッダーメニューのチャンネルの新規作成の項目でチャンネル作成ページに飛べない問題を修正 #16816
|
||||
- Fix: ラジオボタンに空白の選択肢が表示される問題を修正
|
||||
- Fix: ラジオボタンに空白の選択肢が表示される問題を修正
|
||||
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/1105)
|
||||
- Fix: 一部のシチュエーションで投稿フォームのツアーが正しく表示されない問題を修正
|
||||
- Fix: 投稿フォームのリセットボタンで注釈がリセットされない問題を修正
|
||||
- Fix: PlayのAiScriptバージョン判定(v0.x系・v1.x系の判定)が正しく動作しない問題を修正
|
||||
- Fix: PlayのAiScriptバージョン判定(v0.x系・v1.x系の判定)が正しく動作しない問題を修正
|
||||
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/1129)
|
||||
- Fix: フォロー申請をキャンセルする際の確認ダイアログの文言が不正確な問題を修正
|
||||
- Fix: 初回読み込み時にエラーになることがある問題を修正
|
||||
@@ -148,12 +213,12 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
### Server
|
||||
- Enhance: メモリ使用量を削減しました
|
||||
- Enhance: 依存関係の更新
|
||||
- Fix: ワードミュートの文字数計算を修正
|
||||
- Fix: ワードミュートの文字数計算を修正
|
||||
- Fix: チャンネルのリアルタイム更新時に、ロックダウン設定にて非ログイン時にノートを表示しない設定にしている場合でもノートが表示されてしまう問題を修正
|
||||
- Fix: DeepL APIのAPIキー指定方式変更に対応
|
||||
- Fix: DeepL APIのAPIキー指定方式変更に対応
|
||||
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/1096)
|
||||
- 内部実装の変更にて対応可能な更新です。Misskey側の設定方法に変更はありません。
|
||||
- Fix: DBレプリケーションを利用する環境でクエリーが失敗する問題を修正
|
||||
- Fix: DBレプリケーションを利用する環境でクエリーが失敗する問題を修正
|
||||
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/1123)
|
||||
|
||||
## 2025.11.0
|
||||
@@ -196,7 +261,7 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
## 2025.10.1
|
||||
|
||||
### General
|
||||
- Enhance: リモートユーザーに付与したロールバッジを表示できるように(オプトイン)
|
||||
- Enhance: リモートユーザーに付与したロールバッジを表示できるように(オプトイン)
|
||||
パフォーマンス上の問題からデフォルトで無効化されています。「コントロールパネル > パフォーマンス」から有効化できます。
|
||||
- 依存関係の更新
|
||||
|
||||
@@ -323,7 +388,7 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
- Enhance: レンダリングパフォーマンスの向上
|
||||
- Enhance: 依存ソフトウェアの更新
|
||||
- Fix: 投稿フォームでファイルのアップロードが中止または失敗した際のハンドリングを修正
|
||||
- Fix: 一部の設定検索結果が存在しないパスになる問題を修正
|
||||
- Fix: 一部の設定検索結果が存在しないパスになる問題を修正
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/1171)
|
||||
- Fix: テーマエディタが動作しない問題を修正
|
||||
- Fix: チャンネルのハイライトページにノートが表示されない問題を修正
|
||||
@@ -483,7 +548,7 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
- Enhance: 画像の高品質なプレースホルダを無効化してパフォーマンスを向上させるオプションを追加
|
||||
- Enhance: 招待されているが参加していないルームを開いたときに、招待を承認するかどうか尋ねるように
|
||||
- Enhance: リプライ元にアンケートがあることが表示されるように
|
||||
- Enhance: ノートのサーバー情報のデザインを改善・パフォーマンス向上
|
||||
- Enhance: ノートのサーバー情報のデザインを改善・パフォーマンス向上
|
||||
(Based on https://github.com/taiyme/misskey/pull/198, https://github.com/taiyme/misskey/pull/211, https://github.com/taiyme/misskey/pull/283)
|
||||
- Enhance: ユーザー設定でURLプレビューを無効化できるように
|
||||
- Enhance: ヒントとコツを追加
|
||||
@@ -572,7 +637,7 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
|
||||
### Server
|
||||
- Enhance: ジョブキューの成功/失敗したジョブも一定数・一定期間保存するようにし、後から問題を調査することを容易に
|
||||
- Enhance: フォローしているユーザーならフォロワー限定投稿のノートでもアンテナで検知できるように
|
||||
- Enhance: フォローしているユーザーならフォロワー限定投稿のノートでもアンテナで検知できるように
|
||||
(Cherry-picked from https://github.com/yojo-art/cherrypick/pull/568 and https://github.com/team-shahu/misskey/pull/38)
|
||||
- Enhance: ユーザーごとにノートの表示が高速化するように
|
||||
- Fix: システムアカウントの名前がサーバー名と同期されない問題を修正
|
||||
@@ -678,7 +743,7 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
|
||||
### General
|
||||
- Enhance: プロキシアカウントをシステムアカウントとして作成するように
|
||||
- Enhance: OAuthで外部アプリからロゴが提供されている場合、それを表示できるように
|
||||
- Enhance: OAuthで外部アプリからロゴが提供されている場合、それを表示できるように
|
||||
書式は https://indieauth.spec.indieweb.org/20220212/#example-2 に準じます。
|
||||
- Fix: システムアカウントが削除できる問題を修正
|
||||
|
||||
@@ -692,7 +757,7 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
|
||||
### Server
|
||||
- Fix: 特定のケースでActivityPubの処理がデッドロックになることがあるのを修正
|
||||
- Fix: S3互換オブジェクトストレージでファイルのアップロードに失敗することがある問題を修正
|
||||
- Fix: S3互換オブジェクトストレージでファイルのアップロードに失敗することがある問題を修正
|
||||
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/895)
|
||||
|
||||
|
||||
@@ -713,7 +778,7 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
- Enhance: リアクションする際に確認ダイアログを表示できるように
|
||||
- Enhance: コントロールパネルのユーザ検索で入力された情報をページ遷移で損なわないように `#15437`
|
||||
- Enhance: CWの注釈で入力済みの文字数を表示
|
||||
- Enhance: ノート検索ページのデザイン調整
|
||||
- Enhance: ノート検索ページのデザイン調整
|
||||
(Cherry-picked from https://github.com/taiyme/misskey/pull/273)
|
||||
- Fix: ノートページで、クリップ一覧が表示されないことがある問題を修正
|
||||
- Fix: コンディショナルロールを手動で割り当てできる導線を削除 `#13529`
|
||||
@@ -730,7 +795,7 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
- Fix: `following/invalidate`でフォロワーを解除しようとしているユーザーの情報を返すように
|
||||
- Fix: オブジェクトストレージの設定でPrefixを設定していなかった場合nullまたは空文字になる問題を修正
|
||||
- Fix: HTTPプロキシとその除外設定を行った状態でカスタム絵文字の一括インポートをしたとき、除外設定が効かないのを修正( #8766 )
|
||||
- Fix: pgroongaでの検索時にはじめのキーワードのみが検索に使用される問題を修正
|
||||
- Fix: pgroongaでの検索時にはじめのキーワードのみが検索に使用される問題を修正
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/886)
|
||||
- Fix: メールアドレスの形式が正しくなければ以降の処理を行わないように
|
||||
- Fix: `update-meta`でobjectStoragePrefixにS3_SAFEかつURL-safeでない文字列を使えないように
|
||||
@@ -740,12 +805,12 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
## 2025.2.0
|
||||
|
||||
### General
|
||||
- Fix: Docker のビルドに失敗する問題を修正
|
||||
- Fix: Docker のビルドに失敗する問題を修正
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/883)
|
||||
|
||||
### Client
|
||||
- Fix: パスキーでパスワードレスログインが出来ない問題を修正
|
||||
- Fix: 一部環境でセンシティブなファイルを含むノートの非表示が効かない問題
|
||||
- Fix: 一部環境でセンシティブなファイルを含むノートの非表示が効かない問題
|
||||
- Fix: データセーバー有効時にもユーザーページの「ファイル」タブで画像が読み込まれてしまう問題を修正
|
||||
- Fix: MFMの `sparkle` エフェクトが正しく表示されない問題を修正
|
||||
- Fix: ページのURLにスラッシュが含まれている場合にページが正しく表示されない問題を修正
|
||||
@@ -772,14 +837,14 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
* β版として公開のため、旧画面も引き続き利用可能です
|
||||
|
||||
### Client
|
||||
- Enhance: PC画面でチャンネルが複数列で表示されるように
|
||||
- Enhance: PC画面でチャンネルが複数列で表示されるように
|
||||
(Cherry-picked from https://github.com/Otaku-Social/maniakey/pull/13)
|
||||
- Enhance: 照会に失敗した場合、その理由を表示するように
|
||||
- Enhance: ワードミュートで検知されたワードを表示できるように
|
||||
- Enhance: リモートのノートのリンクをコピーできるように
|
||||
- Enhance: 連合がホワイトリスト化・無効化されているサーバー向けのデザイン修正
|
||||
- Enhance: AiScriptのセーブデータを明示的に削除する関数`Mk:remove`を追加
|
||||
- Enhance: ノートの添付ファイルを一覧で遡れる「ファイル」タブを追加
|
||||
- Enhance: ノートの添付ファイルを一覧で遡れる「ファイル」タブを追加
|
||||
(Based on https://github.com/Otaku-Social/maniakey/pull/14)
|
||||
- Enhance: AiScriptの拡張API関数において引数の型チェックをより厳格に
|
||||
- Enhance: クエリパラメータでuiを一時的に変更できるように #15240
|
||||
@@ -787,26 +852,26 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
- Fix: 画面サイズが変わった際にナビゲーションバーが自動で折りたたまれない問題を修正
|
||||
- Fix: サーバー情報メニューに区切り線が不足していたのを修正
|
||||
- Fix: ノートがログインしているユーザーしか見れない場合にログインダイアログを閉じるとその後の動線がなくなる問題を修正
|
||||
- Fix: 公開範囲がホームのノートの埋め込みウィジェットが読み込まれない問題を修正
|
||||
- Fix: 公開範囲がホームのノートの埋め込みウィジェットが読み込まれない問題を修正
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/803)
|
||||
- Fix: 絵文字管理画面で一部の絵文字が表示されない問題を修正
|
||||
- Fix: プラグイン `register_note_view_interruptor` でノートのサーバー情報の書き換えができない問題を修正
|
||||
- Fix: Botプロテクションの設定変更時は実際に検証を通過しないと保存できないように( #15137 )
|
||||
- Fix: ノート検索が使用できない場合でもチャンネルのノート検索欄がでていた問題を修正
|
||||
- Fix: `Ui:C:select`で値の変更が画面に反映されない問題を修正
|
||||
- Fix: MiAuth認可画面で、認可処理に失敗した場合でもコールバックURLに遷移してしまう問題を修正
|
||||
- Fix: MiAuth認可画面で、認可処理に失敗した場合でもコールバックURLに遷移してしまう問題を修正
|
||||
(Cherry-picked from https://github.com/TeamNijimiss/misskey/commit/800359623e41a662551d774de15b0437b6849bb4)
|
||||
- Fix: ノート作成画面でファイルの添付可能個数を超えてもノートボタンが押せていた問題を修正
|
||||
- Fix: 「アカウントを管理」画面で、ユーザー情報の取得に失敗したアカウント(削除されたアカウントなど)が表示されない問題を修正
|
||||
- Fix: MacOSでChrome系ブラウザを使用している場合に、Misskeyを閉じた際に他のタブのオーディオ機能と干渉する問題を修正
|
||||
- Fix: 言語データのキャッシュ状況によっては、埋め込みウィジェットが正しく起動しない問題を修正
|
||||
- Fix: 「削除して編集」でノートの引用を解除出来なかった問題を修正( #14476 )
|
||||
- Fix: RSSウィジェットが正しく表示されない問題を修正
|
||||
- Fix: RSSウィジェットが正しく表示されない問題を修正
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/857)
|
||||
- Fix: ワードミュートの保存失敗時にAPIエラーが握りつぶされる事があるのを修正
|
||||
- Fix: アンケートでリモートの絵文字が正しく描画できない問題の修正
|
||||
(Cherry-picked from https://github.com/yojo-art/cherrypick/pull/153)
|
||||
- Fix: 非ログイン時のサーバー概要画面のメニューボタンが押せないことがあるのを修正
|
||||
- Fix: 非ログイン時のサーバー概要画面のメニューボタンが押せないことがあるのを修正
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/656)
|
||||
- Fix: URLにはじめから`#pswp`が含まれている場合に画像ビューワーがブラウザの戻るボタンで閉じられない問題を修正
|
||||
- Fix: ロール作成画面で設定できるアイコンデコレーションの最大取付個数を16に制限
|
||||
@@ -815,18 +880,18 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
### Server
|
||||
- Enhance: pg_bigmが利用できるよう、ノートの検索をILIKE演算子でなくLIKE演算子でLOWER()をかけたテキストに対して行うように
|
||||
- Enhance: ノート検索の選択肢としてpgroongaに対応 ( #14730 )
|
||||
- Enhance: チャート更新時にDBに同時接続しないように
|
||||
- Enhance: チャート更新時にDBに同時接続しないように
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/830)
|
||||
- Enhance: config(default.yml)からSQLログ全文を出力するか否かを設定可能に ( #15266 )
|
||||
- Fix: ユーザーのプロフィール画面をアドレス入力などで直接表示した際に概要タブの描画に失敗する問題の修正( #15032 )
|
||||
- Fix: 起動前の疎通チェックが機能しなくなっていた問題を修正
|
||||
- Fix: 起動前の疎通チェックが機能しなくなっていた問題を修正
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/737)
|
||||
- Fix: ノートの閲覧にログイン必須にしてもFeedでノートが表示されてしまう問題を修正
|
||||
- Fix: 絵文字の連合でライセンス欄を相互にやり取りするように ( #10859, #14109 )
|
||||
- Fix: ロックダウンされた期間指定のノートがStreaming経由でLTLに出現するのを修正 ( #15200 )
|
||||
- Fix: disableClustering設定時の初期化ロジックを調整( #15223 )
|
||||
- Fix: URLとURIが異なるエンティティの照会に失敗する問題を修正( #15039 )
|
||||
- Fix: ActivityPubリクエストかどうかの判定が正しくない問題を修正
|
||||
- Fix: ActivityPubリクエストかどうかの判定が正しくない問題を修正
|
||||
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/869)
|
||||
- Fix: `/api/pages/update`にて`name`を指定せずにリクエストするとエラーが発生する問題を修正
|
||||
- Fix: AIセンシティブ判定が arm64 環境で動作しない問題を修正
|
||||
@@ -852,12 +917,12 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
- Fix: お知らせ作成時に画像URL入力欄を空欄に変更できないのを修正 ( #14976 )
|
||||
|
||||
### Client
|
||||
- Enhance: Bull DashboardでRelationship Queueの状態も確認できるように
|
||||
- Enhance: Bull DashboardでRelationship Queueの状態も確認できるように
|
||||
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/751)
|
||||
- Enhance: ドライブでソートができるように
|
||||
- Enhance: アイコンデコレーション管理画面の改善
|
||||
- Enhance: 「単なるラッキー」の取得条件を変更
|
||||
- Enhance: 投稿フォームでEscキーを押したときIME入力中ならフォームを閉じないように( #10866 )
|
||||
- Enhance: 投稿フォームでEscキーを押したときIME入力中ならフォームを閉じないように( #10866 )
|
||||
- Enhance: MiAuth, OAuthの認可画面の改善
|
||||
- どのアカウントで認証しようとしているのかがわかるように
|
||||
- 認証するアカウントを切り替えられるように
|
||||
@@ -865,29 +930,29 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
- Enhance: カタルーニャ語 (ca-ES) に対応
|
||||
- Enhance: 個別お知らせページではMetaタグを出力するように
|
||||
- Enhance: ノート詳細画面にロールのバッジを表示
|
||||
- Enhance: 過去に送信したフォローリクエストを確認できるように
|
||||
- Enhance: 過去に送信したフォローリクエストを確認できるように
|
||||
(Based on https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/663)
|
||||
- Enhance: サイドバーを簡単に展開・折りたたみできるように ( #14981 )
|
||||
- Enhance: リノートメニューに「リノートの詳細」を追加
|
||||
- Enhance: 非ログイン状態でMisskeyを開いた際のパフォーマンスを向上
|
||||
- Fix: 通知の範囲指定の設定項目が必要ない通知設定でも範囲指定の設定がでている問題を修正
|
||||
- Fix: Turnstileが失敗・期限切れした際にも成功扱いとなってしまう問題を修正
|
||||
- Fix: Turnstileが失敗・期限切れした際にも成功扱いとなってしまう問題を修正
|
||||
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/768)
|
||||
- Fix: デッキのタイムラインカラムで「センシティブなファイルを含むノートを表示」設定が使用できなかった問題を修正
|
||||
- Fix: Encode RSS urls with escape sequences before fetching allowing query parameters to be used
|
||||
- Fix: リンク切れを修正
|
||||
- Fix: ノート投稿ボタンにホバー時のスタイルが適用されていないのを修正
|
||||
- Fix: ノート投稿ボタンにホバー時のスタイルが適用されていないのを修正
|
||||
(Cherry-picked from https://github.com/taiyme/misskey/pull/305)
|
||||
- Fix: メールアドレス登録有効化時の「完了」ダイアログボックスの表示条件を修正
|
||||
- Fix: 画面幅が狭い環境でデザインが崩れる問題を修正
|
||||
- Fix: 画面幅が狭い環境でデザインが崩れる問題を修正
|
||||
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/815)
|
||||
- Fix: TypeScriptの型チェック対象ファイルを限定してビルドを高速化するように
|
||||
- Fix: TypeScriptの型チェック対象ファイルを限定してビルドを高速化するように
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/725)
|
||||
|
||||
### Server
|
||||
- Enhance: DockerのNode.jsを22.11.0に更新
|
||||
- Enhance: 起動前の疎通チェックで、DBとメイン以外のRedisの疎通確認も行うように
|
||||
(Based on https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/588)
|
||||
- Enhance: 起動前の疎通チェックで、DBとメイン以外のRedisの疎通確認も行うように
|
||||
(Based on https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/588)
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/715)
|
||||
- Enhance: リモートユーザーの照会をオリジナルにリダイレクトするように
|
||||
- Fix: sharedInboxが無いActorに紐づくリモートユーザーを照会できない
|
||||
@@ -895,18 +960,18 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
- Fix: フォロワーへのメッセージの絵文字をemojisに含めるように
|
||||
- Fix: Nested proxy requestsを検出した際にブロックするように
|
||||
[ghsa-gq5q-c77c-v236](https://github.com/misskey-dev/misskey/security/advisories/ghsa-gq5q-c77c-v236)
|
||||
- Fix: 招待コードの発行可能な残り数算出に使用すべきロールポリシーの値が違う問題を修正
|
||||
- Fix: 招待コードの発行可能な残り数算出に使用すべきロールポリシーの値が違う問題を修正
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/706)
|
||||
- Fix: 連合への配信時に、acctの大小文字が区別されてしまい正しくメンションが処理されないことがある問題を修正
|
||||
- Fix: 連合への配信時に、acctの大小文字が区別されてしまい正しくメンションが処理されないことがある問題を修正
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/711)
|
||||
- Fix: ローカルユーザーへのメンションを含むノートが連合される際に正しいURLに変換されないことがある問題を修正
|
||||
- Fix: ローカルユーザーへのメンションを含むノートが連合される際に正しいURLに変換されないことがある問題を修正
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/712)
|
||||
- Fix: FTT無効時にユーザーリストタイムラインが使用できない問題を修正
|
||||
- Fix: FTT無効時にユーザーリストタイムラインが使用できない問題を修正
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/709)
|
||||
- Fix: User Webhookテスト機能のMock Payloadを修正
|
||||
- Fix: アカウント削除のモデレーションログが動作していないのを修正 (#14996)
|
||||
- Fix: User Webhookテスト機能のMock Payloadを修正
|
||||
- Fix: アカウント削除のモデレーションログが動作していないのを修正 (#14996)
|
||||
- Fix: リノートミュートが新規投稿通知に対して作用していなかった問題を修正
|
||||
- Fix: Inboxの処理で生じるエラーを誤ってActivityとして処理することがある問題を修正
|
||||
- Fix: Inboxの処理で生じるエラーを誤ってActivityとして処理することがある問題を修正
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/730)
|
||||
- Fix: セキュリティに関する修正
|
||||
|
||||
@@ -933,13 +998,13 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
- Enhance: 個人宛のお知らせは「わかった」を押すと自動的にアーカイブされるように
|
||||
- Fix: `admin/emoji/update`エンドポイントのidのみ指定した時不正なエラーが発生するバグを修正
|
||||
- Fix: RBT有効時、リノートのリアクションが反映されない問題を修正
|
||||
- Fix: キューのエラーログを簡略化するように
|
||||
- Fix: キューのエラーログを簡略化するように
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/649)
|
||||
|
||||
## 2024.10.0
|
||||
|
||||
### Note
|
||||
- セキュリティ向上のため、サーバー初期設定時に使用する初期パスワードを設定できるようになりました。今後Misskeyサーバーを新たに設置する際には、初回の起動前にコンフィグファイルの`setupPassword`をコメントアウトし、初期パスワードを設定することをおすすめします。(すでに初期設定を完了しているサーバーについては、この変更に伴い対応する必要はありません)
|
||||
- セキュリティ向上のため、サーバー初期設定時に使用する初期パスワードを設定できるようになりました。今後Misskeyサーバーを新たに設置する際には、初回の起動前にコンフィグファイルの`setupPassword`をコメントアウトし、初期パスワードを設定することをおすすめします。(すでに初期設定を完了しているサーバーについては、この変更に伴い対応する必要はありません)
|
||||
- ホスティングサービスを運営している場合は、コンフィグファイルを構築する際に`setupPassword`をランダムな値に設定し、ユーザーに通知するようにシステムを更新することをおすすめします。
|
||||
- なお、初期パスワードが設定されていない場合でも初期設定を行うことが可能です(UI上で初期パスワードの入力欄を空欄にすると続行できます)。
|
||||
- ユーザーデータを読み込む際の型が一部変更されました。
|
||||
@@ -959,7 +1024,7 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
### Client
|
||||
- Enhance: デザインの調整
|
||||
- Enhance: ログイン画面の認証フローを改善
|
||||
- Fix: クライアント上での時間ベースの実績獲得動作が実績獲得後も発動していた問題を修正
|
||||
- Fix: クライアント上での時間ベースの実績獲得動作が実績獲得後も発動していた問題を修正
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/657)
|
||||
|
||||
### Server
|
||||
@@ -977,7 +1042,7 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
- Feat: フォローされた際のメッセージを設定できるように
|
||||
- Feat: 連合をホワイトリスト制にできるように
|
||||
- Feat: UserWebhookとSystemWebhookのテスト送信機能を追加 (#14445)
|
||||
- Feat: モデレーターはユーザーにかかわらずファイルが添付されているノートを検索できるように
|
||||
- Feat: モデレーターはユーザーにかかわらずファイルが添付されているノートを検索できるように
|
||||
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/680)
|
||||
- Feat: データエクスポートが完了した際に通知を発行するように
|
||||
- Enhance: ユーザーによるコンテンツインポートの可否をロールポリシーで制御できるように
|
||||
@@ -996,12 +1061,12 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
- Fix: サーバーメトリクスが2つ以上あるとリロード直後の表示がおかしくなる問題を修正
|
||||
- Fix: コントロールパネル内のAp requests内のチャートの表示がおかしかった問題を修正
|
||||
- Fix: 月の違う同じ日はセパレータが表示されないのを修正
|
||||
- Fix: タッチ画面でレンジスライダーを操作するとツールチップが複数表示される問題を修正
|
||||
- Fix: タッチ画面でレンジスライダーを操作するとツールチップが複数表示される問題を修正
|
||||
(Cherry-picked from https://github.com/taiyme/misskey/pull/265)
|
||||
- Fix: 縦横比が極端なカスタム絵文字を表示する際にレイアウトが崩れる箇所があるのを修正
|
||||
- Fix: 縦横比が極端なカスタム絵文字を表示する際にレイアウトが崩れる箇所があるのを修正
|
||||
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/725)
|
||||
- Fix: 設定変更時のリロード確認ダイアログが複数個表示されることがある問題を修正
|
||||
- Fix: ファイルの詳細ページのファイルの説明で改行が正しく表示されない問題を修正
|
||||
- Fix: ファイルの詳細ページのファイルの説明で改行が正しく表示されない問題を修正
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/commit/bde6bb0bd2e8b0d027e724d2acdb8ae0585a8110)
|
||||
- Fix: 一部画面のページネーションが動作しにくくなっていたのを修正 ( #12766 , #11449 )
|
||||
|
||||
@@ -1010,14 +1075,14 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
- Fix: アンテナの書き込み時にキーワードが与えられなかった場合のエラーをApiErrorとして投げるように
|
||||
- この変更により、公式フロントエンドでは入力の不備が内部エラーとして報告される代わりに一般的なエラーダイアログで報告されます
|
||||
- Fix: ファイルがサイズの制限を超えてアップロードされた際にエラーを返さなかった問題を修正
|
||||
- Fix: 外部ページを解析する際に、ページに紐づけられた関連リソースも読み込まれてしまう問題を修正
|
||||
- Fix: 外部ページを解析する際に、ページに紐づけられた関連リソースも読み込まれてしまう問題を修正
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/commit/26e0412fbb91447c37e8fb06ffb0487346063bb8)
|
||||
- Fix: Continue importing from file if single emoji import fails
|
||||
- Fix: `Retry-After`ヘッダーが送信されなかった問題を修正
|
||||
- Fix: `Retry-After`ヘッダーが送信されなかった問題を修正
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/commit/8a982c61c01909e7540ff1be9f019df07c3f0624)
|
||||
- Fix: サーバーサイドのDOM解析完了時にリソースを開放するように
|
||||
- Fix: サーバーサイドのDOM解析完了時にリソースを開放するように
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/634)
|
||||
- Fix: `<link rel="alternate">`を追って照会するのはOKレスポンスが返却された場合のみに
|
||||
- Fix: `<link rel="alternate">`を追って照会するのはOKレスポンスが返却された場合のみに
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/633)
|
||||
- Fix: メールにスタイルが適用されていなかった問題を修正
|
||||
|
||||
@@ -1046,15 +1111,15 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
- 通知ページや通知カラム(デッキ)を開いている状態において、新たに発生した通知が既読されない問題が修正されます。
|
||||
- これにより、プッシュ通知が有効な同条件下の環境において、プッシュ通知が常に発生してしまう問題も修正されます。
|
||||
- Fix: Play各種エンドポイントの返り値に`visibility`が含まれていない問題を修正
|
||||
- Fix: サーバー情報取得の際にモデレーター限定の情報が取得できないことがあるのを修正
|
||||
- Fix: サーバー情報取得の際にモデレーター限定の情報が取得できないことがあるのを修正
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/582)
|
||||
- Fix: 公開範囲がダイレクトのノートをユーザーアクティビティのチャート生成に使用しないように
|
||||
- Fix: 公開範囲がダイレクトのノートをユーザーアクティビティのチャート生成に使用しないように
|
||||
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/679)
|
||||
- Fix: ActivityPubのエンティティタイプ判定で不明なタイプを受け取った場合でも処理を継続するように
|
||||
- キュー処理のつまりが改善される可能性があります
|
||||
- Fix: リバーシの対局設定の変更が反映されないのを修正
|
||||
- Fix: 無制限にストリーミングのチャンネルに接続できる問題を修正
|
||||
- Fix: ベースロールのポリシーを変更した際にモデログに記録されないのを修正
|
||||
- Fix: ベースロールのポリシーを変更した際にモデログに記録されないのを修正
|
||||
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/700)
|
||||
- Fix: Prevent memory leak from memory caches (#14310)
|
||||
- Fix: More reliable memory cache eviction (#14311)
|
||||
@@ -1086,9 +1151,9 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
- Enhance: 内蔵APIドキュメントのデザイン・パフォーマンスを改善
|
||||
- Enhance: 非ログイン時に他サーバーに遷移するアクションを追加
|
||||
- Enhance: 非ログイン時のハイライトTLのデザインを改善
|
||||
- Enhance: フロントエンドのアクセシビリティ改善
|
||||
- Enhance: フロントエンドのアクセシビリティ改善
|
||||
(Based on https://github.com/taiyme/misskey/pull/226)
|
||||
- Enhance: サーバー情報ページ・お問い合わせページを改善
|
||||
- Enhance: サーバー情報ページ・お問い合わせページを改善
|
||||
(Cherry-picked from https://github.com/taiyme/misskey/pull/238)
|
||||
- Enhance: AiScriptを0.19.0にアップデート
|
||||
- Enhance: Allow negative delay for MFM animation elements (`tada`, `jelly`, `twitch`, `shake`, `spin`, `jump`, `bounce`, `rainbow`)
|
||||
@@ -1097,7 +1162,7 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
- Enhance: 検索(ノート/ユーザー)において、入力に空白が含まれている場合は照会を行わないように
|
||||
- Enhance: 検索(ノート/ユーザー)において、照会を行うかどうか、ハッシュタグのノート/ユーザー一覧ページを表示するかどうかの確認ダイアログを出すように
|
||||
- Enhance: 検索(ノート/ユーザー)で `@` から始まる文字列(`@user@host`など)を入力すると、そのユーザーを照会できるように
|
||||
- Enhance: ドライブのファイル・フォルダをドラッグしなくても移動できるように
|
||||
- Enhance: ドライブのファイル・フォルダをドラッグしなくても移動できるように
|
||||
(Cherry-picked from https://github.com/nafu-at/misskey/commit/b89c2af6945c6a9f9f10e83f54d2bcf0f240b0b4, https://github.com/nafu-at/misskey/commit/8a7d710c6acb83f50c83f050bd1423c764d60a99)
|
||||
- Enhance: デッキのアンテナ・リスト選択画面からそれぞれを新規作成できるように
|
||||
- Enhance: ブラウザのコンテキストメニューを使用できるように
|
||||
@@ -1105,19 +1170,19 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
- Fix: `/about#federation` ページなどで各インスタンスのチャートが表示されなくなっていた問題を修正
|
||||
- Fix: ユーザーページの追加情報のラベルを投稿者のサーバーの絵文字で表示する (#13968)
|
||||
- Fix: リバーシの対局を正しく共有できないことがある問題を修正
|
||||
- Fix: コントロールパネルでベースロールのポリシーを編集してもUI上では変更が反映されない問題を修正
|
||||
- Fix: コントロールパネルでベースロールのポリシーを編集してもUI上では変更が反映されない問題を修正
|
||||
- Fix: アンテナの編集画面のボタンに隙間を追加
|
||||
- Fix: テーマプレビューが見れない問題を修正
|
||||
- Fix: ショートカットキーが連打できる問題を修正
|
||||
- Fix: ショートカットキーが連打できる問題を修正
|
||||
(Cherry-picked from https://github.com/taiyme/misskey/pull/234)
|
||||
- Fix: MkSignin.vueのcredentialRequestからReactivityを削除(ProxyがPasskey認証処理に渡ることを避けるため)
|
||||
- Fix: 「アニメーション画像を再生しない」がオンのときでもサーバーのバナー画像・背景画像がアニメーションしてしまう問題を修正
|
||||
- Fix: 「アニメーション画像を再生しない」がオンのときでもサーバーのバナー画像・背景画像がアニメーションしてしまう問題を修正
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/574)
|
||||
- Fix: Twitchの埋め込みが開けない問題を修正
|
||||
- Fix: 子メニューの高さがウィンドウからはみ出ることがある問題を修正
|
||||
- Fix: 個人宛てのダイアログ形式のお知らせが即時表示されない問題を修正
|
||||
- Fix: 一部の画像がセンシティブ指定されているときに画面に何も表示されないことがあるのを修正
|
||||
- Fix: リアクションしたユーザー一覧のユーザー名がはみ出る問題を修正
|
||||
- Fix: リアクションしたユーザー一覧のユーザー名がはみ出る問題を修正
|
||||
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/672)
|
||||
- Fix: `/share`ページにおいて絵文字ピッカーを開くことができない問題を修正
|
||||
- Fix: deck uiの通知音が重なる問題 (#14029)
|
||||
@@ -1160,14 +1225,14 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
4. フォローしていない非アクティブなユーザ
|
||||
|
||||
また、自分自身のアカウントもサジェストされるようになりました。
|
||||
- Fix: 一般ユーザーから見たユーザーのバッジの一覧に公開されていないものが含まれることがある問題を修正
|
||||
- Fix: 一般ユーザーから見たユーザーのバッジの一覧に公開されていないものが含まれることがある問題を修正
|
||||
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/652)
|
||||
- Fix: ユーザーのリアクション一覧でミュート/ブロックが機能していなかった問題を修正
|
||||
- Fix: FTT有効時にリモートユーザーのノートがHTLにキャッシュされる問題を修正
|
||||
- Fix: 一部の通知がローカル上のリモートユーザーに対して行われていた問題を修正
|
||||
- Fix: エラーメッセージの誤字を修正 (#14213)
|
||||
- Fix: ソーシャルタイムラインにローカルタイムラインに表示される自分へのリプライが表示されない問題を修正
|
||||
- Fix: リノートのミュートが適用されるまでに時間がかかることがある問題を修正
|
||||
- Fix: リノートのミュートが適用されるまでに時間がかかることがある問題を修正
|
||||
(Cherry-picked from https://github.com/Type4ny-Project/Type4ny/commit/e9601029b52e0ad43d9131b555b614e56c84ebc1)
|
||||
- Fix: Steaming APIが不正なデータを受けた場合の動作が不安定である問題 #14251
|
||||
- Fix: `users/search`において `@` から始まる文字列が与えられた際の処理が正しくなかった問題を修正
|
||||
@@ -1194,7 +1259,7 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
### General
|
||||
- Feat: エラートラッキングにSentryを使用できるようになりました
|
||||
- Enhance: URLプレビューの有効化・無効化を設定できるように #13569
|
||||
- Enhance: アンテナでBotによるノートを除外できるように
|
||||
- Enhance: アンテナでBotによるノートを除外できるように
|
||||
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/545)
|
||||
- Enhance: クリップのノート数を表示するように
|
||||
- Enhance: コンディショナルロールの条件として以下を新たに追加 (#13667)
|
||||
@@ -1213,7 +1278,7 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
|
||||
### Client
|
||||
- Feat: アップロードするファイルの名前をランダム文字列にできるように
|
||||
- Feat: 個別のお知らせにリンクで飛べるように
|
||||
- Feat: 個別のお知らせにリンクで飛べるように
|
||||
(Based on https://github.com/MisskeyIO/misskey/pull/639)
|
||||
- Enhance: 自分のノートの添付ファイルから直接ファイルの詳細ページに飛べるように
|
||||
- Enhance: 広告がMisskeyと同一ドメインの場合はRouterで遷移するように
|
||||
@@ -1243,9 +1308,9 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
- Fix: 一部のページ内リンクが正しく動作しない問題を修正
|
||||
- Fix: 周年の実績が閏年を考慮しない問題を修正
|
||||
- Fix: ローカルURLのプレビューポップアップが左上に表示される
|
||||
- Fix: WebGL2をサポートしないブラウザで「季節に応じた画面の演出」が有効になっているとき、Misskeyが起動できなくなる問題を修正
|
||||
- Fix: WebGL2をサポートしないブラウザで「季節に応じた画面の演出」が有効になっているとき、Misskeyが起動できなくなる問題を修正
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/459)
|
||||
- Fix: ページタイトルでローカルユーザーとリモートユーザーの区別がつかない問題を修正
|
||||
- Fix: ページタイトルでローカルユーザーとリモートユーザーの区別がつかない問題を修正
|
||||
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/528)
|
||||
- Fix: コードブロックのシンタックスハイライトで使用される定義ファイルをCDNから取得するように #13177
|
||||
- CDNから取得せずMisskey本体にバンドルする場合は`pacakges/frontend/vite.config.ts`を修正してください。
|
||||
@@ -1268,13 +1333,13 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
- Enhance: ドライブのファイルがNSFWかどうか個別に連合されるように (#13756)
|
||||
- 可能な場合、ノートの添付ファイルのセンシティブ判定がファイル単位になります
|
||||
- Fix: リモートから配送されたアクティビティにJSON-LD compactionをかける
|
||||
- Fix: フォローリクエストを作成する際に既存のものは削除するように
|
||||
- Fix: フォローリクエストを作成する際に既存のものは削除するように
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/440)
|
||||
- Fix: エンドポイント`notes/translate`のエラーを改善
|
||||
- Fix: CleanRemoteFilesProcessorService report progress from 100% (#13632)
|
||||
- Fix: 一部の音声ファイルが映像ファイルとして扱われる問題を修正
|
||||
- Fix: リプライのみの引用リノートと、CWのみの引用リノートが純粋なリノートとして誤って扱われてしまう問題を修正
|
||||
- Fix: 登録にメール認証が必須になっている場合、登録されているメールアドレスを削除できないように
|
||||
- Fix: 登録にメール認証が必須になっている場合、登録されているメールアドレスを削除できないように
|
||||
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/606)
|
||||
- Fix: Add Cache-Control to Bull Board
|
||||
- Fix: nginx経由で/files/にRangeリクエストされた場合に正しく応答できないのを修正
|
||||
@@ -1467,10 +1532,10 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
### Note
|
||||
- 依存関係の更新に伴い、Node.js 20.10.0が最小要件になりました
|
||||
- 絵文字の追加辞書を既にインストールしている場合は、お手数ですが再インストールのほどお願いします
|
||||
- 絵文字ピッカーにピン留め表示する絵文字設定が「リアクション用」と「絵文字入力用」に分かれました。以前の設定は「リアクション用」として使用されます。
|
||||
- 絵文字ピッカーにピン留め表示する絵文字設定が「リアクション用」と「絵文字入力用」に分かれました。以前の設定は「リアクション用」として使用されます。
|
||||
|
||||
**影響:**
|
||||
それにより、投稿フォームから表示される絵文字ピッカーのピン留め絵文字がリセットされたように感じるかもしれません(新設された"ピン留め(全般)"の設定が使われるため)。
|
||||
**影響:**
|
||||
それにより、投稿フォームから表示される絵文字ピッカーのピン留め絵文字がリセットされたように感じるかもしれません(新設された"ピン留め(全般)"の設定が使われるため)。
|
||||
投稿用のピン留め絵文字をアップデート前の状態にするには、以下の手順で操作します。
|
||||
|
||||
1. 「設定」メニューに移動し、「絵文字ピッカー」タブを選択します。
|
||||
@@ -1517,7 +1582,7 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
- Enhance: Unicode 15.0のサポート
|
||||
- Enhance: コードブロックのハイライト機能を利用するには言語を明示的に指定させるように
|
||||
- MFMでコードブロックを利用する際に意図しないハイライトが起こらないようになりました
|
||||
- 逆に、MFMでコードハイライトを利用したい際は言語を明示的に指定する必要があります
|
||||
- 逆に、MFMでコードハイライトを利用したい際は言語を明示的に指定する必要があります
|
||||
(例: ` ```js ` → Javascript, ` ```ais ` → AiScript)
|
||||
- Enhance: 絵文字などのオートコンプリートでShift+Tabを押すと前の候補を選択できるように
|
||||
- Enhance: チャンネルに新規の投稿がある場合にバッジを表示させる
|
||||
@@ -1924,9 +1989,9 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
|
||||
|
||||
### General
|
||||
- 招待機能を改善しました
|
||||
* 過去に発行した招待コードを確認できるようになりました
|
||||
* ロールごとに招待コードの発行数制限と制限対象期間、有効期限を設定できるようになりました
|
||||
* 招待コードを作成したユーザーと使用したユーザーを確認できるようになりました
|
||||
* 過去に発行した招待コードを確認できるようになりました
|
||||
* ロールごとに招待コードの発行数制限と制限対象期間、有効期限を設定できるようになりました
|
||||
* 招待コードを作成したユーザーと使用したユーザーを確認できるようになりました
|
||||
- ユーザーにロールが期限付きでアサインされている場合、その期限をユーザーのモデレーションページで確認できるようになりました
|
||||
- identicon生成を無効にしてパフォーマンスを向上させることができるようになりました
|
||||
- サーバーのマシン情報の公開を無効にしてパフォーマンスを向上させることができるようになりました
|
||||
@@ -2089,9 +2154,9 @@ Meilisearchの設定に`index`が必要になりました。値はMisskeyサー
|
||||
* 「フォロワーのみ」の投稿は検索結果に表示されません。
|
||||
- 新規登録前に簡潔なルールをユーザーに表示できる、サーバールール機能を追加
|
||||
- ユーザーへの自分用メモ機能
|
||||
* ユーザーに対して、自分だけが見られるメモを追加できるようになりました。
|
||||
* ユーザーに対して、自分だけが見られるメモを追加できるようになりました。
|
||||
(自分自身に対してもメモを追加できます。)
|
||||
* ユーザーメニューから追加できます。
|
||||
* ユーザーメニューから追加できます。
|
||||
(デスクトップ表示ではusernameの右側のボタンからも追加可能)
|
||||
- チャンネルに色を設定できるようになりました。各ノートに設定した色のインジケーターが表示されます。
|
||||
- チャンネルをアーカイブできるようになりました。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# syntax = docker/dockerfile:1.21
|
||||
# syntax = docker/dockerfile:1.23
|
||||
|
||||
ARG NODE_VERSION=22.22.0-bookworm
|
||||
ARG NODE_VERSION=22.22.2-bookworm
|
||||
|
||||
# build assets & compile TypeScript
|
||||
|
||||
@@ -102,7 +102,6 @@ COPY --chown=misskey:misskey --from=native-builder /misskey/packages/misskey-js/
|
||||
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/misskey-reversi/built ./packages/misskey-reversi/built
|
||||
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/misskey-bubble-game/built ./packages/misskey-bubble-game/built
|
||||
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/backend/built ./packages/backend/built
|
||||
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/backend/src-js ./packages/backend/src-js
|
||||
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/i18n/built ./packages/i18n/built
|
||||
COPY --chown=misskey:misskey --from=native-builder /misskey/fluent-emojis /misskey/fluent-emojis
|
||||
COPY --chown=misskey:misskey . ./
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"lib": ["dom", "es5"],
|
||||
"target": "es5",
|
||||
"lib": ["dom"],
|
||||
"target": "esnext",
|
||||
"types": ["cypress", "node"]
|
||||
},
|
||||
"include": ["./**/*.ts"]
|
||||
|
||||
@@ -1073,8 +1073,8 @@ thisPostMayBeAnnoying: "Aquesta nota pot ser molesta per algú."
|
||||
thisPostMayBeAnnoyingHome: "Publicar a la línia de temps d'Inici"
|
||||
thisPostMayBeAnnoyingCancel: "Cancel·lar "
|
||||
thisPostMayBeAnnoyingIgnore: "Publicar de totes maneres"
|
||||
collapseRenotes: "Col·lapsar les renotes que ja has vist"
|
||||
collapseRenotesDescription: "Col·lapse les notes a les quals ja has reaccionat o que ja has renotat"
|
||||
collapseRenotes: "Col·lapsar els impulsos que ja has vist"
|
||||
collapseRenotesDescription: "Col·lapse les notes a les quals ja has reaccionat o que ja has impulsat."
|
||||
internalServerError: "Error intern del servidor"
|
||||
internalServerErrorDescription: "El servidor ha fallat de manera inexplicable."
|
||||
copyErrorInfo: "Copiar la informació de l'error "
|
||||
@@ -1408,6 +1408,7 @@ frame: "Marc"
|
||||
presets: "Predefinit"
|
||||
zeroPadding: "Sense omplir"
|
||||
nothingToConfigure: "No hi ha res a configurar"
|
||||
viewRenotedChannel: "Mirar el canal d'impulsos "
|
||||
_imageEditing:
|
||||
_vars:
|
||||
caption: "Títol de l'arxiu"
|
||||
@@ -1687,7 +1688,7 @@ _initialTutorial:
|
||||
description: "Pots limitar qui pot veure les teves notes."
|
||||
public: "La teva nota serà visible per a tots els usuaris."
|
||||
home: "Publicar només a línia de temps d'Inici. La gent que visiti el teu perfil o mitjançant les remotes també la podran veure."
|
||||
followers: "Només visible per a seguidors. Només els teus seguidors la podran veure i ningú més. Ningú més podrà fer renotes."
|
||||
followers: "Només visible per a seguidors. Només els teus seguidors la podran veure i ningú més. Ningú més podrà fer impulsos."
|
||||
direct: "Només visible per a alguns seguidors, el destinatari rebre una notificació. Es pot fer servir com una alternativa als missatges directes."
|
||||
doNotSendConfidencialOnDirect1: "Tingues cura quan enviïs informació sensible."
|
||||
doNotSendConfidencialOnDirect2: "Els administradors del servidor poden veure tot el que escrius. Ves compte quan enviïs informació sensible en enviar notes directes a altres usuaris en servidors de poca confiança."
|
||||
|
||||
@@ -1408,6 +1408,7 @@ frame: "Frame"
|
||||
presets: "Preset"
|
||||
zeroPadding: "Zero padding"
|
||||
nothingToConfigure: "No configurable options available"
|
||||
viewRenotedChannel: "Show renoted channel"
|
||||
_imageEditing:
|
||||
_vars:
|
||||
caption: "File caption"
|
||||
@@ -3401,6 +3402,8 @@ _imageEffector:
|
||||
threshold: "Threshold"
|
||||
centerX: "Center X"
|
||||
centerY: "Center Y"
|
||||
density: "Density"
|
||||
zoomLinesOutlineThickness: "Outline shadow thickness"
|
||||
zoomLinesMaskSize: "Center diameter"
|
||||
circle: "Circular"
|
||||
drafts: "Drafts"
|
||||
|
||||
@@ -1408,6 +1408,7 @@ frame: "Marco"
|
||||
presets: "Predefinido"
|
||||
zeroPadding: "Relleno cero"
|
||||
nothingToConfigure: "No hay nada que configurar"
|
||||
viewRenotedChannel: "Ver el canal al que te has suscrito"
|
||||
_imageEditing:
|
||||
_vars:
|
||||
caption: "Título del archivo"
|
||||
@@ -3312,7 +3313,7 @@ _clientPerformanceIssueTip:
|
||||
_clip:
|
||||
tip: "Clip es una función que permite organizar varias notas."
|
||||
_userLists:
|
||||
tip: "Las listas pueden contener cualquier usuario que especifiques al crearlas, la lista creada puede mostrarse entonces como una línea de tiempo mostrando solo los usuarios especificados."
|
||||
tip: "Puedes crear listas que incluyan a cualquier usuario. Las listas creadas se pueden visualizar en forma de cronología."
|
||||
watermark: "Marca de Agua"
|
||||
defaultPreset: "Por defecto"
|
||||
_watermarkEditor:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
_lang_: "Italiano"
|
||||
headlineMisskey: "Rete collegata tramite Note"
|
||||
introMisskey: "Eccoci! Misskey è un servizio di microblogging decentralizzato, libero e aperto. \n\n📡 Puoi pubblicare «Note» per condividere ciò che sta succedendo o per dire a tutti qualcosa su di te. \n\n👍 Puoi reagire inviando emoji rapidi alle «Note» provenienti da altri profili nel Fediverso.\n\n🚀 Esplora un nuovo mondo insieme a noi!"
|
||||
introMisskey: "Eccoci! Misskey è un servizio di microblogging decentralizzato, libero e aperto. \n📡 Puoi pubblicare «Note» per condividere ciò che sta succedendo o per dire a tutti qualcosa su di te. \n👍 Puoi reagire inviando emoji rapidi alle «Note» provenienti da altri profili nel Fediverso.\n🚀 Esplora un nuovo mondo insieme a noi!"
|
||||
poweredByMisskeyDescription: "{name} è uno dei servizi (chiamati istanze) che utilizzano la piattaforma open source <b>Misskey</b>."
|
||||
monthAndDay: "{day}/{month}"
|
||||
search: "Cerca"
|
||||
@@ -1408,6 +1408,7 @@ frame: "Cornice"
|
||||
presets: "Preimpostato"
|
||||
zeroPadding: "Al vivo"
|
||||
nothingToConfigure: "Niente da configurare"
|
||||
viewRenotedChannel: "Visualizza il canale del Rinota"
|
||||
_imageEditing:
|
||||
_vars:
|
||||
caption: "Didascalia dell'immagine"
|
||||
@@ -3338,7 +3339,7 @@ _watermarkEditor:
|
||||
stripeWidth: "Larghezza della linea"
|
||||
stripeFrequency: "Il numero di linee"
|
||||
polkadot: "A pallini"
|
||||
checker: "revisore"
|
||||
checker: "Scacchiera"
|
||||
polkadotMainDotOpacity: "Opacità del punto principale"
|
||||
polkadotMainDotRadius: "Dimensione del punto principale"
|
||||
polkadotSubDotOpacity: "Opacità del punto secondario"
|
||||
@@ -3367,7 +3368,7 @@ _imageEffector:
|
||||
zoomLines: "Linea di saturazione"
|
||||
stripe: "Strisce"
|
||||
polkadot: "A pallini"
|
||||
checker: "revisore"
|
||||
checker: "Scacchiera"
|
||||
blockNoise: "Attenua rumore"
|
||||
tearing: "Strappa immagine"
|
||||
fill: "Riempimento"
|
||||
|
||||
@@ -1408,6 +1408,7 @@ frame: "フレーム"
|
||||
presets: "プリセット"
|
||||
zeroPadding: "ゼロ埋め"
|
||||
nothingToConfigure: "設定項目はありません"
|
||||
viewRenotedChannel: "リノート先のチャンネルを見る"
|
||||
|
||||
_imageEditing:
|
||||
_vars:
|
||||
@@ -2121,6 +2122,7 @@ _role:
|
||||
canSearchNotes: "ノート検索の利用"
|
||||
canSearchUsers: "ユーザー検索の利用"
|
||||
canUseTranslator: "翻訳機能の利用"
|
||||
canCreateChannel: "チャンネルの作成"
|
||||
avatarDecorationLimit: "アイコンデコレーションの最大取付個数"
|
||||
canImportAntennas: "アンテナのインポートを許可"
|
||||
canImportBlocking: "ブロックのインポートを許可"
|
||||
|
||||
@@ -1408,6 +1408,7 @@ frame: "프레임"
|
||||
presets: "프리셋"
|
||||
zeroPadding: "0으로 채우기"
|
||||
nothingToConfigure: "설정 항목이 없습니다."
|
||||
viewRenotedChannel: "리노트된 채널 보기"
|
||||
_imageEditing:
|
||||
_vars:
|
||||
caption: "파일 설명"
|
||||
@@ -2097,6 +2098,7 @@ _role:
|
||||
canSearchNotes: "노트 검색 이용 가능 여부"
|
||||
canSearchUsers: "유저 검색 이용"
|
||||
canUseTranslator: "번역 기능의 사용"
|
||||
canCreateChannel: "패널 생성"
|
||||
avatarDecorationLimit: "아바타 장식의 최대 붙임 개수"
|
||||
canImportAntennas: "안테나 가져오기 허용"
|
||||
canImportBlocking: "차단 목록 가져오기 허용"
|
||||
|
||||
@@ -5,6 +5,7 @@ introMisskey: "ຍິນດີຕ້ອນຮັບ! Misskey ເປັນຊອ
|
||||
poweredByMisskeyDescription: "{name} ແມ່ນສ່ວນໜຶ່ງຂອງການບໍລິການທີ່ຂັບເຄື່ອນໂດຍແພລດຟອມ open source. <b>Misskey</b> (ເອີ້ນວ່າ \"Misskey instance\")"
|
||||
monthAndDay: "ເດືອນ{month} / ວັນ{day}"
|
||||
search: "ຄົ້ນຫາ"
|
||||
reset: "ຣີເຊັດ"
|
||||
notifications: "ການແຈ້ງເຕືອນ"
|
||||
username: "ຊື່ຜູ້ໃຊ້"
|
||||
password: "ລະຫັດຜ່ານ"
|
||||
|
||||
@@ -1332,7 +1332,9 @@ overrideByAccount: "Переопределить этим аккаунтом"
|
||||
untitled: "Без названия"
|
||||
noName: "Имя не указано"
|
||||
skip: "Пропустить"
|
||||
restore: "Восстановить"
|
||||
syncBetweenDevices: "Синхронизировать между устройствами"
|
||||
paste: "вставить"
|
||||
postForm: "Форма отправки"
|
||||
textCount: "Количество символов"
|
||||
information: "Описание"
|
||||
@@ -2395,6 +2397,8 @@ _imageEffector:
|
||||
opacity: "Непрозрачность"
|
||||
lightness: "Осветление"
|
||||
drafts: "Черновик"
|
||||
_drafts:
|
||||
restore: "Восстановить"
|
||||
_qr:
|
||||
showTabTitle: "Отображение"
|
||||
raw: "Текст"
|
||||
|
||||
@@ -1335,7 +1335,7 @@ markAsSensitiveConfirm: "ต้องการตั้งค่าสื่อ
|
||||
unmarkAsSensitiveConfirm: "ต้องการยกเลิกการระบุว่าสื่อนี้มีเนื้อหาละเอียดอ่อนหรือไม่?"
|
||||
preferences: "การตั้งค่าสภาพแวดล้อม"
|
||||
accessibility: "การช่วยการเข้าถึง"
|
||||
preferencesProfile: "โปรไฟล์การกำหนดค่า"
|
||||
preferencesProfile: "โปรไฟล์ของการตั้งค่า"
|
||||
copyPreferenceId: "คัดลือก ID การตั้งค่า"
|
||||
resetToDefaultValue: "คืนค่าเป็นค่าเริ่มต้น"
|
||||
overrideByAccount: "เขียนทับด้วยบัญชี"
|
||||
@@ -1345,10 +1345,10 @@ skip: "ข้าม"
|
||||
restore: "กู้คืน"
|
||||
syncBetweenDevices: "ซิงค์ระหว่างอุปกรณ์"
|
||||
preferenceSyncConflictTitle: "การตั้งค่ามีอยู่บนเซิร์ฟเวอร์"
|
||||
preferenceSyncConflictText: "การตั้งค่าที่เปิดใช้งานการซิงค์จะบันทึกค่าลงในเซิร์ฟเวอร์ อย่างไรก็ดี พบว่ามีค่าการตั้งค่านี้ที่เคยบันทึกไว้ในเซิร์ฟเวอร์แล้ว ต้องการดำเนินการอย่างไร?"
|
||||
preferenceSyncConflictText: "รายการตั้งค่าที่เปิดการซิงก์จะถูกบันทึกลงเซิร์ฟเวอร์ แต่รายการตั้งค่านี้ได้ถูกบันทึกลงเซิร์ฟเวอร์ไว้อยู่แล้ว ต้องการดำเนินการอย่างไร?"
|
||||
preferenceSyncConflictChoiceMerge: "รวมเข้าด้วยกัน"
|
||||
preferenceSyncConflictChoiceServer: "เขียนทับด้วยค่าการตั้งค่าเซิร์ฟเวอร์"
|
||||
preferenceSyncConflictChoiceDevice: "เขียนทับด้วยค่าการตั้งค่าอุปกรณ์"
|
||||
preferenceSyncConflictChoiceServer: "เขียนทับด้วยค่าการตั้งค่าของเซิร์ฟเวอร์"
|
||||
preferenceSyncConflictChoiceDevice: "เขียนทับด้วยค่าการตั้งค่าของอุปกรณ์"
|
||||
preferenceSyncConflictChoiceCancel: "ยกเลิกการเปิดใช้งานการซิงค์"
|
||||
paste: "วาง"
|
||||
emojiPalette: "จานสีเอโมจิ"
|
||||
@@ -1408,6 +1408,7 @@ frame: "เฟรม"
|
||||
presets: "พรีเซ็ต"
|
||||
zeroPadding: "ห่างเป็น 0"
|
||||
nothingToConfigure: "ไม่มีอะไรให้ต้ังค่า"
|
||||
viewRenotedChannel: "แสดงช่องที่ถูกรีโน้ต"
|
||||
_imageEditing:
|
||||
_vars:
|
||||
caption: "แคปชั่นของไฟล์"
|
||||
@@ -2097,6 +2098,7 @@ _role:
|
||||
canSearchNotes: "การใช้การค้นหาโน้ต"
|
||||
canSearchUsers: "ค้นหาผู้ใช้"
|
||||
canUseTranslator: "การใช้งานแปล"
|
||||
canCreateChannel: "สร้างช่องใหม่"
|
||||
avatarDecorationLimit: "จำนวนของตกแต่งไอคอนสูงสุดที่สามารถติดตั้งได้"
|
||||
canImportAntennas: "อนุญาตให้นำเข้าเสาอากาศ"
|
||||
canImportBlocking: "อนุญาตให้นำเข้าการบล็อก"
|
||||
@@ -2136,7 +2138,7 @@ _sensitiveMediaDetection:
|
||||
sensitivityDescription: "เมื่อความไวต่ำ Misdetection (ผลบวกลวง) จะลดลง, เมื่อความไวสูง Missed detection (ผลลบลวง) จะลดลง"
|
||||
setSensitiveFlagAutomatically: "ทำเครื่องหมายว่ามีเนื้อหาละเอียดอ่อน"
|
||||
setSensitiveFlagAutomaticallyDescription: "ผลลัพธ์ของการตรวจจับภายในนั้นจะยังคงอยู่ ถึงแม้ว่าจะปิดตัวเลือกนี้"
|
||||
analyzeVideos: "เปิดใช้งานวิเคราะห์ของวิดีโอ"
|
||||
analyzeVideos: "เปิดใช้งานวิเคราะห์วิดีโอ"
|
||||
analyzeVideosDescription: "การวิเคราะห์วิดีโอนอกเหนือจากรูปภาพนั้น การทำสิ่งนี้จะทำให้เพิ่มภาระบนเซิร์ฟเวอร์เล็กน้อย"
|
||||
_emailUnavailable:
|
||||
used: "ที่อยู่อีเมลนี้ได้ถูกใช้ไปแล้ว"
|
||||
@@ -2586,7 +2588,7 @@ _widgetOptions:
|
||||
period: "ระยะเวลา"
|
||||
_cw:
|
||||
hide: "ซ่อน"
|
||||
show: "โหลดเพิ่มเติม"
|
||||
show: "ดูเพิ่มเติม"
|
||||
chars: "{count} ตัวอักษร"
|
||||
files: "{count} ไฟล์"
|
||||
_poll:
|
||||
@@ -3029,7 +3031,7 @@ _externalResourceInstaller:
|
||||
description: "เกิดปัญหาระหว่างการติดตั้งธีม กรุณาลองอีกครั้ง. รายละเอียดข้อผิดพลาดสามารถดูได้ในคอนโซล Javascript"
|
||||
_dataSaver:
|
||||
_media:
|
||||
title: "โหลดสื่อ"
|
||||
title: "ปิดใช้งานการโหลดสื่ออัตโนมัติ"
|
||||
description: "กันไม่ให้ภาพและวิดีโอโหลดโดยอัตโนมัติ แตะรูปภาพ/วิดีโอที่ซ่อนอยู่เพื่อโหลด"
|
||||
_avatar:
|
||||
title: "ปิดใช้งานภาพเคลื่อนไหวของไอคอนประจำตัว"
|
||||
@@ -3111,7 +3113,7 @@ _urlPreviewSetting:
|
||||
summaryProxyDescription: "สร้างการแสดงตัวอย่างด้วย summary Proxy แทนที่จะใช้เนื้อหา Misskey"
|
||||
summaryProxyDescription2: "พารามิเตอร์ต่อไปนี้จะถูกใช้เป็นสตริงการสืบค้นเพื่อเชื่อมต่อกับพร็อกซี หากฝั่งพร็อกซีไม่รองรับการตั้งค่าเหล่านี้จะถูกละเว้น"
|
||||
_mediaControls:
|
||||
pip: "รูปภาพในรูปภาม"
|
||||
pip: "ภาพซ้อนภาพ (PiP)"
|
||||
playbackRate: "ความเร็วในการเล่น"
|
||||
loop: "เล่นวนซ้ำ"
|
||||
_contextMenu:
|
||||
@@ -3401,6 +3403,8 @@ _imageEffector:
|
||||
threshold: "เทรชโฮลด์"
|
||||
centerX: "กลาง X"
|
||||
centerY: "กลาง Y"
|
||||
density: "ความหนาทึบ"
|
||||
zoomLinesOutlineThickness: "ความหนาของเงาเส้น"
|
||||
zoomLinesMaskSize: "ขนาดพื้นที่ตรงกลาง"
|
||||
circle: "ทรงกลม"
|
||||
drafts: "ร่าง"
|
||||
|
||||
@@ -1408,6 +1408,7 @@ frame: "Çerçeve"
|
||||
presets: "Ön ayar"
|
||||
zeroPadding: "Sıfır doldurma"
|
||||
nothingToConfigure: "Ayarlar seçeneği bulunmamaktadır."
|
||||
viewRenotedChannel: "Show renoted channel"
|
||||
_imageEditing:
|
||||
_vars:
|
||||
caption: "Dosya başlığı"
|
||||
|
||||
@@ -63,7 +63,7 @@ copyUserId: "复制用户 ID"
|
||||
copyNoteId: "复制帖子 ID"
|
||||
copyFileId: "复制文件ID"
|
||||
copyFolderId: "复制文件夹ID"
|
||||
copyProfileUrl: "复制个人资料URL"
|
||||
copyProfileUrl: "复制个人资料链接"
|
||||
searchUser: "搜索用户"
|
||||
searchThisUsersNotes: "搜索用户帖子"
|
||||
reply: "回复"
|
||||
@@ -101,12 +101,12 @@ somethingHappened: "出错了"
|
||||
retry: "重试"
|
||||
pageLoadError: "页面加载失败。"
|
||||
pageLoadErrorDescription: "这通常是由于网络或浏览器缓存的原因。请清除缓存或等待片刻后重试。"
|
||||
serverIsDead: "没有服务器响应。 请稍后再试。"
|
||||
serverIsDead: "服务器未响应。 请稍后再试。"
|
||||
youShouldUpgradeClient: "请重新加载并使用新版本的客户端查看此页面。"
|
||||
enterListName: "输入列表名称"
|
||||
privacy: "隐私"
|
||||
makeFollowManuallyApprove: "关注请求需要批准"
|
||||
defaultNoteVisibility: "默认可见性"
|
||||
defaultNoteVisibility: "默认可见范围"
|
||||
follow: "关注"
|
||||
followRequest: "申请关注"
|
||||
followRequests: "关注请求"
|
||||
@@ -137,10 +137,10 @@ pinnedEmojisForReactionSettingDescription: "可以设置发表回应时置顶显
|
||||
pinnedEmojisSettingDescription: "可以设置输入表情符号时置顶显示的表情符号"
|
||||
emojiPickerDisplay: "选择器显示设置"
|
||||
overwriteFromPinnedEmojisForReaction: "使用「置顶(回应)」设置覆盖"
|
||||
overwriteFromPinnedEmojis: "从全局设置覆盖"
|
||||
overwriteFromPinnedEmojis: "使用全局设置覆盖"
|
||||
reactionSettingDescription2: "拖动重新排序,单击删除,点击 + 添加。"
|
||||
rememberNoteVisibility: "保存上次设置的可见性"
|
||||
attachCancel: "取消添加附件"
|
||||
attachCancel: "移除附件"
|
||||
deleteFile: "删除文件"
|
||||
markAsSensitive: "标记为敏感内容"
|
||||
unmarkAsSensitive: "取消标记为敏感内容"
|
||||
@@ -149,12 +149,12 @@ mute: "屏蔽"
|
||||
unmute: "取消隐藏"
|
||||
renoteMute: "隐藏转帖"
|
||||
renoteUnmute: "取消隐藏转帖"
|
||||
block: "屏蔽"
|
||||
unblock: "取消屏蔽"
|
||||
block: "禁止对方与我互动"
|
||||
unblock: "允许对方与我互动"
|
||||
suspend: "冻结"
|
||||
unsuspend: "解除冻结"
|
||||
blockConfirm: "确定要屏蔽吗?"
|
||||
unblockConfirm: "确定要取消屏蔽吗?"
|
||||
blockConfirm: "确定要禁止对方与我互动吗?"
|
||||
unblockConfirm: "确定要允许对方与我互动吗?"
|
||||
suspendConfirm: "要冻结吗?"
|
||||
unsuspendConfirm: "要解除冻结吗?"
|
||||
selectList: "选择列表"
|
||||
@@ -184,7 +184,7 @@ flagAsCat: "喵!!!!!!!!!!!!"
|
||||
flagAsCatDescription: "喵喵喵??"
|
||||
flagShowTimelineReplies: "在时间线上显示帖子的回复"
|
||||
flagShowTimelineRepliesDescription: "启用时,时间线除了显示用户的帖子外,还会显示其他用户对帖子的回复。"
|
||||
autoAcceptFollowed: "自动允许回关请求"
|
||||
autoAcceptFollowed: "自动允许我关注的人的关注请求"
|
||||
addAccount: "添加账户"
|
||||
reloadAccountsList: "更新账户列表"
|
||||
loginFailed: "登录失败"
|
||||
@@ -207,7 +207,7 @@ selectSelf: "选择自己"
|
||||
selectUser: "选择用户"
|
||||
recipient: "收件人"
|
||||
annotation: "注解"
|
||||
federation: "联合"
|
||||
federation: "联邦"
|
||||
instances: "服务器"
|
||||
registeredAt: "初次观测"
|
||||
latestRequestReceivedAt: "上次收到的请求"
|
||||
@@ -244,11 +244,11 @@ silencedInstances: "被静音的服务器"
|
||||
silencedInstancesDescription: "设置要静音的服务器,以换行分隔。被静音的服务器内所有的账户都被视为「静音」状态,且关注操作均需要被批准。已被屏蔽的实例不受影响。"
|
||||
mediaSilencedInstances: "已隐藏媒体文件的服务器"
|
||||
mediaSilencedInstancesDescription: "设置要隐藏媒体文件的服务器,以换行分隔。被设置的服务器内所有账号的文件均按照「敏感内容」处理,且将无法使用自定义表情符号。已被屏蔽的实例不受影响。"
|
||||
federationAllowedHosts: "允许联合的服务器"
|
||||
federationAllowedHostsDescription: "设定允许联合的服务器,以换行分隔。"
|
||||
muteAndBlock: "隐藏/屏蔽"
|
||||
mutedUsers: "已隐藏的用户"
|
||||
blockedUsers: "已屏蔽的用户"
|
||||
federationAllowedHosts: "允许联邦交互的服务器"
|
||||
federationAllowedHostsDescription: "设定允许联邦通信的服务器,以换行分隔。"
|
||||
muteAndBlock: "屏蔽用户/禁止用户与我互动"
|
||||
mutedUsers: "已屏蔽的用户"
|
||||
blockedUsers: "禁止与我互动的用户"
|
||||
noUsers: "无用户"
|
||||
editProfile: "编辑资料"
|
||||
noteDeleteConfirm: "确定要删除该帖子吗?"
|
||||
@@ -261,7 +261,7 @@ default: "默认"
|
||||
defaultValueIs: "默认值: {value}"
|
||||
noCustomEmojis: "没有自定义表情符号"
|
||||
noJobs: "没有任务"
|
||||
federating: "联合中"
|
||||
federating: "联邦通信中"
|
||||
blocked: "已屏蔽"
|
||||
suspended: "停止投递"
|
||||
all: "全部"
|
||||
@@ -277,7 +277,7 @@ retypedNotMatch: "两次输入不一致!"
|
||||
currentPassword: "现在的密码"
|
||||
newPassword: "新密码"
|
||||
newPasswordRetype: "重新输入密码:"
|
||||
attachFile: "插入附件"
|
||||
attachFile: "添加附件"
|
||||
more: "更多!"
|
||||
featured: "热门"
|
||||
usernameOrUserId: "用户名或用户 ID"
|
||||
@@ -342,7 +342,7 @@ selectFolders: "选择多个文件夹"
|
||||
fileNotSelected: "未选择文件"
|
||||
renameFile: "重命名文件"
|
||||
folderName: "文件夹名称"
|
||||
createFolder: "创建文件夹"
|
||||
createFolder: "新建文件夹"
|
||||
renameFolder: "重命名文件夹"
|
||||
deleteFolder: "删除文件夹"
|
||||
folder: "文件夹"
|
||||
@@ -353,7 +353,7 @@ emptyFolder: "此文件夹中无文件"
|
||||
dropHereToUpload: "将文件拖动到这里来上传"
|
||||
unableToDelete: "无法删除"
|
||||
inputNewFileName: "请输入新文件名"
|
||||
inputNewDescription: "请输入新标题"
|
||||
inputNewDescription: "请输入新的描述文本"
|
||||
inputNewFolderName: "请输入新文件夹名"
|
||||
circularReferenceFolder: "目标文件夹是要移动的文件夹的子文件夹。"
|
||||
hasChildFilesOrFolders: "此文件夹中有文件,无法删除。"
|
||||
@@ -396,10 +396,10 @@ driveCapacityPerLocalAccount: "每个用户的网盘容量"
|
||||
driveCapacityPerRemoteAccount: "每个远程用户的网盘容量"
|
||||
inMb: "以兆字节(MegaByte)为单位"
|
||||
bannerUrl: "横幅 URL"
|
||||
backgroundImageUrl: "背景图 URL"
|
||||
backgroundImageUrl: "背景图片的链接"
|
||||
basicInfo: "基本信息"
|
||||
pinnedUsers: "置顶用户"
|
||||
pinnedUsersDescription: "输入您想要固定到“发现”页面的用户,一行一个。"
|
||||
pinnedUsersDescription: "在「发现」页面中使用换行标记想要置顶的用户。"
|
||||
pinnedPages: "固定页面"
|
||||
pinnedPagesDescription: "输入您要固定到服务器首页的页面路径,以换行符分隔。"
|
||||
pinnedClipId: "置顶的便签 ID"
|
||||
@@ -432,7 +432,7 @@ antennaExcludeBots: "排除机器人账户"
|
||||
antennaKeywordsDescription: "AND 条件用空格分隔,OR 条件用换行符分隔。"
|
||||
notifyAntenna: "开启通知"
|
||||
withFileAntenna: "仅带有附件的帖子"
|
||||
excludeNotesInSensitiveChannel: "排除敏感频道内的帖子"
|
||||
excludeNotesInSensitiveChannel: "排除敏感频道的帖子"
|
||||
enableServiceworker: "启用 ServiceWorker"
|
||||
antennaUsersDescription: "指定用户名,用换行符进行分隔"
|
||||
caseSensitive: "区分大小写"
|
||||
@@ -476,7 +476,7 @@ passwordLessLogin: "无密码登录"
|
||||
passwordLessLoginDescription: "不使用密码,仅使用安全密钥或 Passkey 登录"
|
||||
resetPassword: "重置密码"
|
||||
newPasswordIs: "新的密码是「{password}」"
|
||||
reduceUiAnimation: "减少 UI 动画"
|
||||
reduceUiAnimation: "减少 UI 动效"
|
||||
share: "分享"
|
||||
notFound: "未找到"
|
||||
notFoundDescription: "没有与指定 URL 对应的页面。"
|
||||
@@ -543,7 +543,7 @@ regenerate: "重新生成"
|
||||
fontSize: "字体大小"
|
||||
mediaListWithOneImageAppearance: "仅一张图片的媒体列表高度"
|
||||
limitTo: "上限为 {x}"
|
||||
showMediaListByGridInWideArea: "在大屏幕上并排显示媒体列表"
|
||||
showMediaListByGridInWideArea: "在宽屏上并排显示媒体列表"
|
||||
noFollowRequests: "没有关注请求"
|
||||
openImageInNewTab: "在新标签页中打开图片"
|
||||
dashboard: "管理面板"
|
||||
@@ -581,7 +581,7 @@ s3ForcePathStyleDesc: "启用 s3ForcePathStyle 会强制将存储桶名称指定
|
||||
serverLogs: "服务器日志"
|
||||
deleteAll: "全部删除"
|
||||
showFixedPostForm: "在时间线顶部显示发帖框"
|
||||
showFixedPostFormInChannel: "在时间线顶部显示发帖对话框(频道)"
|
||||
showFixedPostFormInChannel: "在时间线顶部显示发帖框(频道)"
|
||||
withRepliesByDefaultForNewlyFollowed: "在时间线中默认包含新关注用户的回复"
|
||||
newNoteRecived: "有新的帖子"
|
||||
newNote: "新帖子"
|
||||
@@ -656,7 +656,7 @@ expandTweet: "展开帖子"
|
||||
themeEditor: "主题编辑器"
|
||||
description: "描述"
|
||||
describeFile: "添加描述"
|
||||
enterFileDescription: "输入标题"
|
||||
enterFileDescription: "输入描述文本"
|
||||
author: "作者"
|
||||
leaveConfirm: "存在未保存的更改。要放弃更改吗?"
|
||||
manage: "管理"
|
||||
@@ -664,7 +664,7 @@ plugins: "插件"
|
||||
preferencesBackups: "备份设置"
|
||||
deck: "Deck"
|
||||
undeck: "取消 Deck"
|
||||
useBlurEffectForModal: "对话框使用模糊效果"
|
||||
useBlurEffectForModal: "发帖背景使用模糊效果"
|
||||
useFullReactionPicker: "使用全功能的回应工具栏"
|
||||
width: "宽度"
|
||||
height: "高度"
|
||||
@@ -773,13 +773,13 @@ yes: "是"
|
||||
no: "否"
|
||||
driveFilesCount: "网盘的文件数"
|
||||
driveUsage: "网盘的空间用量"
|
||||
noCrawle: "要求搜索引擎不索引该用户"
|
||||
noCrawleDescription: "要求搜索引擎不要收录(索引)您的用户页面,帖子,页面等。"
|
||||
noCrawle: "拒绝搜索引擎的索引"
|
||||
noCrawleDescription: "拒绝搜索引擎收录(索引)您的个人资料,帖子,页面等。"
|
||||
lockedAccountInfo: "即使启用该功能,只要帖子可见范围不是「仅关注者」,任何人都可以看到您的帖子。"
|
||||
alwaysMarkSensitive: "默认将媒体文件标记为敏感内容"
|
||||
loadRawImages: "添加附件图像的缩略图时使用原始图像质量"
|
||||
disableShowingAnimatedImages: "不播放动画"
|
||||
disableShowingAnimatedImages_caption: "如果即使关闭了此设置但动画仍无法播放,则可能是浏览器或操作系统的辅助功能设置,又或者是省电设置等产生了干扰。"
|
||||
disableShowingAnimatedImages: "不播放动态图像"
|
||||
disableShowingAnimatedImages_caption: "如果即使禁用了此设置,动态图像仍无法播放,可能是由于浏览器或操作系统的辅助功能设置、省电设置或其他因素所致。"
|
||||
highlightSensitiveMedia: "高亮显示敏感媒体"
|
||||
verificationEmailSent: "已发送确认电子邮件。请访问电子邮件中的链接以完成设置。"
|
||||
notSet: "未设置"
|
||||
@@ -851,7 +851,7 @@ fullView: "全屏"
|
||||
quitFullView: "退出全屏"
|
||||
addDescription: "添加描述"
|
||||
userPagePinTip: "在帖子的菜单中选择“置顶”,即可显示该条帖子。"
|
||||
notSpecifiedMentionWarning: "有未指定的提及"
|
||||
notSpecifiedMentionWarning: "有未添加到收件人的提及"
|
||||
info: "关于"
|
||||
userInfo: "用户信息"
|
||||
unknown: "未知"
|
||||
@@ -877,9 +877,9 @@ noMaintainerInformationWarning: "尚未设置管理员信息。"
|
||||
noInquiryUrlWarning: "尚未设置联络地址。"
|
||||
noBotProtectionWarning: "尚未设置 Bot 防御。"
|
||||
configure: "设置"
|
||||
postToGallery: "创建新图集"
|
||||
postToGallery: "发表相册"
|
||||
postToHashtag: "发布至该话题"
|
||||
gallery: "图集"
|
||||
gallery: "相册"
|
||||
recentPosts: "最新发布"
|
||||
popularPosts: "热门投稿"
|
||||
shareWithNote: "分享到帖文"
|
||||
@@ -1032,7 +1032,7 @@ browserPushNotificationDisabledDescription: "{serverName}无权限发送通知
|
||||
windowMaximize: "最大化"
|
||||
windowMinimize: "最小化"
|
||||
windowRestore: "还原"
|
||||
caption: "标题"
|
||||
caption: "描述文本"
|
||||
loggedInAsBot: "以 Bot 账户登录"
|
||||
tools: "工具"
|
||||
cannotLoad: "无法加载"
|
||||
@@ -1073,7 +1073,7 @@ thisPostMayBeAnnoying: "这个帖子可能会让其他人感到困扰。"
|
||||
thisPostMayBeAnnoyingHome: "发到首页"
|
||||
thisPostMayBeAnnoyingCancel: "取消"
|
||||
thisPostMayBeAnnoyingIgnore: "就这样发布"
|
||||
collapseRenotes: "省略显示已经看过的转发内容"
|
||||
collapseRenotes: "折叠已经看过的转贴"
|
||||
collapseRenotesDescription: "折叠显示回应或转发过的帖文。"
|
||||
internalServerError: "内部服务器错误"
|
||||
internalServerErrorDescription: "内部服务器发生了预期外的错误"
|
||||
@@ -1081,9 +1081,9 @@ copyErrorInfo: "复制错误信息"
|
||||
joinThisServer: "在本服务器上注册"
|
||||
exploreOtherServers: "探索其他服务器"
|
||||
letsLookAtTimeline: "看看时间线"
|
||||
disableFederationConfirm: "确定要禁用联合?"
|
||||
disableFederationConfirmWarn: "禁用联合不会将帖子设为私有。在大多数情况下,不需要禁用联合。"
|
||||
disableFederationOk: "联合禁用"
|
||||
disableFederationConfirm: "确定要禁用联邦交互?"
|
||||
disableFederationConfirmWarn: "即使禁用联邦交互,也不会将帖子设为私有。在大多数情况下,没有必要禁用联邦交互。"
|
||||
disableFederationOk: "禁用联邦"
|
||||
invitationRequiredToRegister: "此服务器目前只允许拥有邀请码的人注册。"
|
||||
emailNotSupported: "此服务器不支持发送邮件"
|
||||
postToTheChannel: "发布到频道"
|
||||
@@ -1093,7 +1093,7 @@ likeOnly: "仅点赞"
|
||||
likeOnlyForRemote: "全部(远程仅点赞)"
|
||||
nonSensitiveOnly: "仅限非敏感内容"
|
||||
nonSensitiveOnlyForLocalLikeOnlyForRemote: "仅限非敏感内容(远程仅点赞)"
|
||||
rolesAssignedToMe: "指派给自己的角色"
|
||||
rolesAssignedToMe: "我的角色"
|
||||
resetPasswordConfirm: "确定重置密码?"
|
||||
sensitiveWords: "敏感词"
|
||||
sensitiveWordsDescription: "包含这些词的帖子将只在首页可见。可用换行来设定多个词。"
|
||||
@@ -1148,7 +1148,7 @@ pleaseAgreeAllToContinue: "必须全部勾选「同意」才能够继续。"
|
||||
continue: "继续"
|
||||
preservedUsernames: "保留的用户名"
|
||||
preservedUsernamesDescription: "列出需要保留的用户名,使用换行来作为分割。被指定的用户名在建立账户时无法使用,但由管理员所创建的账户不受该限制。此外,现有的账户也不会受到影响。"
|
||||
createNoteFromTheFile: "从文件创建帖子"
|
||||
createNoteFromTheFile: "使用该文件发帖"
|
||||
archive: "归档"
|
||||
archived: "已归档"
|
||||
unarchive: "取消归档"
|
||||
@@ -1158,7 +1158,7 @@ thisChannelArchived: "该频道已被归档。"
|
||||
displayOfNote: "显示帖子"
|
||||
initialAccountSetting: "初始设定"
|
||||
youFollowing: "正在关注"
|
||||
preventAiLearning: "拒绝接受生成式 AI 的学习"
|
||||
preventAiLearning: "拒绝用于训练生成式 AI"
|
||||
preventAiLearningDescription: "要求文章生成 AI 或图像生成 AI 不能够以发布的帖子和图像等内容作为学习对象。这是通过在 HTML 响应中包含 noai 标志来实现的,这不能完全阻止 AI 学习你的发布内容,并不是所有 AI 都会遵守这类请求。"
|
||||
options: "选项"
|
||||
specifyUser: "指定用户"
|
||||
@@ -1226,8 +1226,8 @@ notificationRecieveConfig: "通知接收设置"
|
||||
mutualFollow: "互相关注"
|
||||
followingOrFollower: "关注中或关注者"
|
||||
fileAttachedOnly: "仅限媒体"
|
||||
showRepliesToOthersInTimeline: "在时间线中包含给别人的回复"
|
||||
hideRepliesToOthersInTimeline: "在时间线中隐藏给别人的回复"
|
||||
showRepliesToOthersInTimeline: "在时间线中显示对他人的回复"
|
||||
hideRepliesToOthersInTimeline: "在时间线中隐藏对他人的回复"
|
||||
showRepliesToOthersInTimelineAll: "在时间线中显示所有现在关注的人的回复"
|
||||
hideRepliesToOthersInTimelineAll: "在时间线中隐藏所有现在关注的人的回复"
|
||||
confirmShowRepliesAll: "此操作不可撤销。确认要在时间线中显示所有现在关注的人的回复吗?"
|
||||
@@ -1325,22 +1325,22 @@ lockdown: "锁定"
|
||||
pleaseSelectAccount: "请选择帐户"
|
||||
availableRoles: "可用角色"
|
||||
acknowledgeNotesAndEnable: "理解注意事项后再开启。"
|
||||
federationSpecified: "此服务器已开启联合白名单。只能与管理员指定的服务器通信。"
|
||||
federationDisabled: "此服务器已禁用联合。无法与其它服务器上的用户通信。"
|
||||
federationSpecified: "此服务器已开启联邦白名单模式。只能与管理员指定的服务器通信。"
|
||||
federationDisabled: "此服务器已禁用联邦功能。无法与其它服务器上的用户通信。"
|
||||
draft: "草稿"
|
||||
draftsAndScheduledNotes: "草稿和定时发送"
|
||||
confirmOnReact: "发送回应前需要确认"
|
||||
reactAreYouSure: "要用「{emoji}」进行回应吗?"
|
||||
markAsSensitiveConfirm: "要将此媒体标记为敏感吗?"
|
||||
unmarkAsSensitiveConfirm: "要将此媒体解除敏感标记吗?"
|
||||
markAsSensitiveConfirm: "确定标记此媒体为敏感内容吗?"
|
||||
unmarkAsSensitiveConfirm: "确定取消标记为敏感内容吗?"
|
||||
preferences: "偏好设置"
|
||||
accessibility: "辅助功能"
|
||||
preferencesProfile: "设置的配置文件"
|
||||
copyPreferenceId: "复制设置 ID"
|
||||
resetToDefaultValue: "重置为默认值"
|
||||
overrideByAccount: "覆盖账号"
|
||||
overrideByAccount: "使用账户设置"
|
||||
untitled: "未命名"
|
||||
noName: "没有名字"
|
||||
noName: "未命名"
|
||||
skip: "跳过"
|
||||
restore: "恢复"
|
||||
syncBetweenDevices: "设备间同步"
|
||||
@@ -1351,7 +1351,7 @@ preferenceSyncConflictChoiceServer: "服务器上的设定值"
|
||||
preferenceSyncConflictChoiceDevice: "设备上的设定值"
|
||||
preferenceSyncConflictChoiceCancel: "取消同步"
|
||||
paste: "粘贴"
|
||||
emojiPalette: "表情符号调色板"
|
||||
emojiPalette: "表情符号选择器"
|
||||
postForm: "发帖窗口"
|
||||
textCount: "字数"
|
||||
information: "关于"
|
||||
@@ -1368,7 +1368,7 @@ embed: "嵌入"
|
||||
settingsMigrating: "正在迁移设置,请稍候。(之后也可以在设置 → 其它 → 迁移旧设置来手动迁移)"
|
||||
readonly: "只读"
|
||||
goToDeck: "返回至 Deck"
|
||||
federationJobs: "联合作业"
|
||||
federationJobs: "联邦作业"
|
||||
driveAboutTip: "网盘可以显示以前上传的文件。<br>\n也可以在发布帖子时重复使用文件,或在发布帖子前预先上传文件。<br>\n<b>删除文件时,其将从至今为止所有用到该文件的地方(如帖子、页面、头像、横幅)消失。</b><br>\n也可以新建文件夹来整理文件。"
|
||||
scrollToClose: "滑动并关闭"
|
||||
advice: "建议"
|
||||
@@ -1382,7 +1382,7 @@ unmuteX: "取消对{x}的隐藏"
|
||||
abort: "中止"
|
||||
tip: "提示和技巧"
|
||||
redisplayAllTips: "重新显示所有的提示和技巧"
|
||||
hideAllTips: "隐藏所有的提示和技巧"
|
||||
hideAllTips: "隐藏所有的 “提示与技巧”"
|
||||
defaultImageCompressionLevel: "默认图像压缩等级"
|
||||
defaultImageCompressionLevel_description: "较低的等级可以保持画质,但会增加文件大小。<br>较高的等级可以减少文件大小,但相对应的画质将会降低。"
|
||||
defaultCompressionLevel: "默认压缩等级"
|
||||
@@ -1394,7 +1394,7 @@ pluginsAreDisabledBecauseSafeMode: "因启用了安全模式,所有插件均
|
||||
customCssIsDisabledBecauseSafeMode: "因启用了安全模式,无法应用自定义 CSS。"
|
||||
themeIsDefaultBecauseSafeMode: "启用安全模式时将使用默认主题。关闭安全模式后将还原。"
|
||||
thankYouForTestingBeta: "感谢您协助测试 beta 版!"
|
||||
createUserSpecifiedNote: "创建指定用户的帖子"
|
||||
createUserSpecifiedNote: "提及该用户并发帖"
|
||||
schedulePost: "定时发布"
|
||||
scheduleToPostOnX: "预定在 {x} 发出"
|
||||
scheduledToPostOnX: "已预定在 {x} 发出"
|
||||
@@ -1408,6 +1408,7 @@ frame: "边框"
|
||||
presets: "预设值"
|
||||
zeroPadding: "填充 0"
|
||||
nothingToConfigure: "没有项目"
|
||||
viewRenotedChannel: "查看转帖所属频道"
|
||||
_imageEditing:
|
||||
_vars:
|
||||
caption: "文件标题"
|
||||
@@ -1510,10 +1511,10 @@ _chat:
|
||||
mutual: "仅相互关注"
|
||||
none: "没有人"
|
||||
_emojiPalette:
|
||||
palettes: "调色板"
|
||||
enableSyncBetweenDevicesForPalettes: "启用调色板的设备间同步"
|
||||
paletteForMain: "主调色板"
|
||||
paletteForReaction: "回应用调色板"
|
||||
palettes: "表情符号托盘"
|
||||
enableSyncBetweenDevicesForPalettes: "在设备间同步表情符号托盘"
|
||||
paletteForMain: "主表情符号托盘"
|
||||
paletteForReaction: "回应时的表情符号托盘"
|
||||
_settings:
|
||||
driveBanner: "可在此管理和设置网盘、确认使用量及配置上传文件的设置。"
|
||||
pluginBanner: "使用插件可以扩展客户端的功能。可以在此安装、单独管理插件。"
|
||||
@@ -1534,9 +1535,9 @@ _settings:
|
||||
timelineAndNote: "时间线和帖子"
|
||||
makeEveryTextElementsSelectable: "使所有的文字均可选择"
|
||||
makeEveryTextElementsSelectable_description: "若开启,在某些情况下可能降低用户体验。"
|
||||
useStickyIcons: "使图标跟随滚动"
|
||||
useStickyIcons: "用户头像跟随页面滚动"
|
||||
enableHighQualityImagePlaceholders: "显示高质量图像的占位符"
|
||||
uiAnimations: "UI 动画"
|
||||
uiAnimations: "UI 动效"
|
||||
showNavbarSubButtons: "在导航栏中显示副按钮"
|
||||
ifOn: "启用时"
|
||||
ifOff: "关闭时"
|
||||
@@ -1551,7 +1552,7 @@ _settings:
|
||||
showAvailableReactionsFirstInNote: "在顶部显示可用的回应"
|
||||
showPageTabBarBottom: "在下方显示页面标签栏"
|
||||
emojiPaletteBanner: "可以将固定显示在表情符号选择器中的预设注册为调色板,也可以自定义表情符号选择器的显示方式。"
|
||||
enableAnimatedImages: "启用动画图像"
|
||||
enableAnimatedImages: "启用动态图像"
|
||||
settingsPersistence_title: "设置持久化"
|
||||
settingsPersistence_description1: "启用设置持久化可防止设置信息丢失。"
|
||||
settingsPersistence_description2: "根据环境不同,有可能无法开启。"
|
||||
@@ -1579,12 +1580,12 @@ _accountSettings:
|
||||
requireSigninToViewContents: "需要登录才能显示内容"
|
||||
requireSigninToViewContentsDescription1: "您发布的所有帖子将变成需要登入后才会显示。有望防止爬虫收集各种信息。"
|
||||
requireSigninToViewContentsDescription2: "没有 URL 预览(OGP)、内嵌网页、引用帖子的功能的服务器也将无法显示。"
|
||||
requireSigninToViewContentsDescription3: "这些限制可能不适用于联合到远程服务器的内容。"
|
||||
requireSigninToViewContentsDescription3: "对于已通过联邦分发到远程服务器的内容,这些限制可能不适用。"
|
||||
makeNotesFollowersOnlyBefore: "可将过去的帖子设为仅关注者可见"
|
||||
makeNotesFollowersOnlyBeforeDescription: "开启此设定时,超过设定的时间或日期后,帖子将变为仅关注者可见。关闭后帖子的公开状态将恢复成原本的设定。"
|
||||
makeNotesHiddenBefore: "将过去的帖子设为私密"
|
||||
makeNotesHiddenBeforeDescription: "开启此设定时,超过设定的时间或日期后,帖子将变为仅自己可见。关闭后帖子的公开状态将恢复成原本的设定。"
|
||||
mayNotEffectForFederatedNotes: "与远程服务器联合的帖子在远端可能会没有效果。"
|
||||
mayNotEffectForFederatedNotes: "对于已通过联邦投递到远程服务器的帖子,此操作在远端可能无法生效。"
|
||||
mayNotEffectSomeSituations: "此限制功能非常简单,在与远程服务器联合等情形时可能不适用。"
|
||||
notesHavePassedSpecifiedPeriod: "超过指定时间的帖子"
|
||||
notesOlderThanSpecifiedDateAndTime: "指定日期前的帖子"
|
||||
@@ -1636,7 +1637,7 @@ _announcement:
|
||||
_initialAccountSetting:
|
||||
accountCreated: "账户创建完成了!"
|
||||
letsStartAccountSetup: "马上来进行账户的初始设定吧。"
|
||||
letsFillYourProfile: "首先,来设定你的个人档案吧!"
|
||||
letsFillYourProfile: "首先,设置一下您的个人资料吧!"
|
||||
profileSetting: "个人资料设置"
|
||||
privacySetting: "隐私设置"
|
||||
theseSettingsCanEditLater: "也可以在稍后修改这里的设置。"
|
||||
@@ -1688,10 +1689,10 @@ _initialTutorial:
|
||||
public: "向所有用户公开。\n"
|
||||
home: "仅在首页时间线上发布。 关注者、从个人资料页查看过来的用户、以及通过转帖也能被别的用户看见。"
|
||||
followers: "仅对关注者可见。 除了您自己之外,没有人可以转贴,并且只有您的关注者可以查看它。\n"
|
||||
direct: "它将仅向指定用户公开,并且他们也会收到通知。 您可以使用它来代替私信。\n"
|
||||
direct: "仅对指定用户公开,且收件人将收到通知。"
|
||||
doNotSendConfidencialOnDirect1: "发送敏感信息时请注意。\n"
|
||||
doNotSendConfidencialOnDirect2: "目标服务器的管理员可以看到发布的内容,因此如果您向不受信任的服务器上的用户发送私信,则在处理敏感信息时需要小心。"
|
||||
localOnly: "不将帖子推送到其它服务器。 无论上述公开范围如何,其它服务器的用户将无法看到附加了此设定的帖子。\n"
|
||||
localOnly: "不将帖子通过联邦推送到其它服务器。 无论上述公开范围如何,其它服务器的用户将无法看到附加了此设定的帖子。\n"
|
||||
_cw:
|
||||
title: "隐藏内容 (CW)\n"
|
||||
description: "显示「注解」里的内容而不是正文。点击「查看更多」将会把正文显示出来。"
|
||||
@@ -1700,7 +1701,7 @@ _initialTutorial:
|
||||
note: "茨了带巧克力的甜甜圈🍩😋"
|
||||
useCases: "用于服务器条款所规定的帖子,或对剧透内容和敏感内容进行自主规制。"
|
||||
_howToMakeAttachmentsSensitive:
|
||||
title: "如何将附件标注为敏感内容?"
|
||||
title: "如何标记附件为敏感内容?"
|
||||
description: "对于服务器方针所要求要求的,又或者不适合直接展示的附件,请添加「敏感」标记。\n"
|
||||
tryThisFile: "试试看,将附加到此窗口的图像标注为敏感!"
|
||||
_exampleNote:
|
||||
@@ -1745,7 +1746,7 @@ _serverSettings:
|
||||
singleUserMode: "单用户模式"
|
||||
singleUserMode_description: "若此服务器只有自己使用,开启此模式将最佳化性能。"
|
||||
signToActivityPubGet: "对 GET 请求签名"
|
||||
signToActivityPubGet_description: "通常情况下请保持启用。若遇到联合通信方面的问题,将其关闭可能会有所改善,但另一方面有可能会造成无法通信。"
|
||||
signToActivityPubGet_description: "通常情况下请保持启用。若遇到联邦通信方面的问题,将其关闭可能会有所改善,但另一方面有可能会造成无法通信。"
|
||||
proxyRemoteFiles: "代理远程文件"
|
||||
proxyRemoteFiles_description: "如果启用,远程服务器的文件将由代理提供。可有效保护图像预览缩略图的生成与用户隐私。"
|
||||
allowExternalApRedirect: "允许通过 ActivityPub 重定向查询"
|
||||
@@ -1770,7 +1771,7 @@ _accountMigration:
|
||||
moveTo: "把这个账户迁移到新的账户"
|
||||
moveToLabel: "迁移后的账户"
|
||||
moveCannotBeUndone: "一旦迁移账户,就无法撤销。"
|
||||
moveAccountDescription: "\n迁移到新帐户。\n ・现有的关注者自动关注新帐户\n ・此帐户的所有关注者都将被删除\n ・您将无法再使用此帐户发帖。\n关注者迁移是自动的,但关注中迁移必须手动完成。请在迁移前在此帐户上导出关注列表,并在迁移后立即在目标帐户上执行导入。\n列表、隐藏、屏蔽也是如此,因此您必须手动迁移它。\n(此描述适用于该服务器(Misskey v13.12.0 或更高版本)。其他 ActivityPub 软件(例如 Mastodon)的行为可能有所不同。)"
|
||||
moveAccountDescription: "\n迁移到新帐户。\n ・现有的关注者自动关注新帐户\n ・此帐户的所有关注者都将被删除\n ・您将无法再使用此帐户发帖。\n关注者迁移是自动的,但关注中迁移必须手动完成。请在迁移前在此帐户上导出关注列表,并在迁移后立即在目标帐户上执行导入。\n列表、屏蔽列表、禁止与我互动的列表也是如此,因此您必须手动迁移它。\n(此描述适用于该服务器(Misskey v13.12.0 或更高版本)。其他 ActivityPub 软件(例如 Mastodon)的行为可能有所不同。)"
|
||||
moveAccountHowTo: "要进行账户迁移,请现在目标账户中为此账户建立一个别名。\n建立别名后,请像这样输入目标账户:@username@server.example.com"
|
||||
startMigration: "迁移"
|
||||
migrationConfirm: "确定要把此账户迁移到 {account} 吗?一旦确定后,此操作无法取消,此账户也无法以原来的状态使用。\n同时,请确认迁移后的账户,已创造别名。"
|
||||
@@ -1888,7 +1889,7 @@ _achievements:
|
||||
description: "第一次将帖子加入收藏"
|
||||
_myNoteFavorited1:
|
||||
title: "想要星星"
|
||||
description: "自己的帖子被其他人加入收藏了"
|
||||
description: "自己的帖子被其他人收藏了"
|
||||
_profileFilled:
|
||||
title: "整装待发"
|
||||
description: "设置了个人资料"
|
||||
@@ -2082,7 +2083,7 @@ _role:
|
||||
maxFileSize: "可上传的最大文件大小"
|
||||
maxFileSize_caption: "可能在反向代理或 CDN 等前端存在其它设定值。"
|
||||
alwaysMarkNsfw: "总是将文件标记为 NSFW"
|
||||
canUpdateBioMedia: "可以更新头像和横幅"
|
||||
canUpdateBioMedia: "允许更新头像和横幅"
|
||||
pinMax: "帖子置顶数量限制"
|
||||
antennaMax: "可创建的最大天线数量"
|
||||
wordMuteMax: "折叠词的字数限制"
|
||||
@@ -2097,9 +2098,10 @@ _role:
|
||||
canSearchNotes: "是否可以搜索帖子"
|
||||
canSearchUsers: "使用用户检索"
|
||||
canUseTranslator: "使用翻译功能"
|
||||
canCreateChannel: "创建频道"
|
||||
avatarDecorationLimit: "可添加头像挂件的最大个数"
|
||||
canImportAntennas: "允许导入天线"
|
||||
canImportBlocking: "允许导入屏蔽列表"
|
||||
canImportBlocking: "允许导入禁止与我互动的列表"
|
||||
canImportFollowing: "允许导入关注列表"
|
||||
canImportMuting: "允许导入隐藏列表"
|
||||
canImportUserLists: "允许导入用户列表"
|
||||
@@ -2107,7 +2109,7 @@ _role:
|
||||
uploadableFileTypes: "可上传的文件类型"
|
||||
uploadableFileTypes_caption: "指定 MIME 类型。可用换行指定多个类型,也可以用星号(*)作为通配符。(如 image/*)"
|
||||
uploadableFileTypes_caption2: "文件根据文件的不同,可能无法判断其类型。若要允许此类文件,请在指定中添加 {x}。"
|
||||
noteDraftLimit: "可在服务器上创建多少草稿"
|
||||
noteDraftLimit: "可在服务器上创建的草稿数量"
|
||||
scheduledNoteLimit: "可同时创建的定时帖子数量"
|
||||
watermarkAvailable: "能否使用水印功能"
|
||||
_condition:
|
||||
@@ -2164,7 +2166,7 @@ _ad:
|
||||
back: "返回"
|
||||
reduceFrequencyOfThisAd: "减少此广告的频率"
|
||||
hide: "不显示"
|
||||
timezoneinfo: "星期几是由服务器的时区所指定的。"
|
||||
timezoneinfo: "星期几是根据服务器的时区确定的。"
|
||||
adsSettings: "广告设置"
|
||||
notesPerOneAd: "在实时更新时间线中插入广告的间隔(帖子个数)"
|
||||
setZeroToDisable: "设为 0 将不在实时更新时间线中投放广告"
|
||||
@@ -2228,7 +2230,7 @@ _aboutMisskey:
|
||||
_displayOfSensitiveMedia:
|
||||
respect: "隐藏敏感媒体"
|
||||
ignore: "显示敏感媒体"
|
||||
force: "隐藏所有内容"
|
||||
force: "隐藏所有媒体"
|
||||
_instanceTicker:
|
||||
none: "不显示"
|
||||
remote: "仅远程用户"
|
||||
@@ -2252,7 +2254,7 @@ _channel:
|
||||
allowRenoteToExternal: "允许转发到频道外和引用"
|
||||
_menuDisplay:
|
||||
sideFull: "横向"
|
||||
sideIcon: "横向(图标)"
|
||||
sideIcon: "横向(图标)"
|
||||
top: "顶部"
|
||||
hide: "隐藏"
|
||||
_wordMute:
|
||||
@@ -2314,7 +2316,7 @@ _theme:
|
||||
mention: "提及"
|
||||
mentionMe: "提及"
|
||||
renote: "转发"
|
||||
modalBg: "对话框背景"
|
||||
modalBg: "发帖背景"
|
||||
divider: "分割线"
|
||||
scrollbarHandle: "滚动条"
|
||||
scrollbarHandleHover: "滚动条(悬停)"
|
||||
@@ -2333,7 +2335,7 @@ _theme:
|
||||
fgHighlighted: "高亮显示文本"
|
||||
_sfx:
|
||||
note: "帖子"
|
||||
noteMy: "我的帖子"
|
||||
noteMy: "发帖"
|
||||
notification: "通知"
|
||||
reaction: "选择回应时"
|
||||
chatMessage: "私信"
|
||||
@@ -2402,8 +2404,8 @@ _2fa:
|
||||
_permissions:
|
||||
"read:account": "查看账户信息"
|
||||
"write:account": "更改帐户信息"
|
||||
"read:blocks": "查看屏蔽列表"
|
||||
"write:blocks": "编辑屏蔽列表"
|
||||
"read:blocks": "查看禁止与我互动的列表"
|
||||
"write:blocks": "编辑禁止与我互动的列表"
|
||||
"read:drive": "查看网盘"
|
||||
"write:drive": "管理网盘文件"
|
||||
"read:favorites": "查看收藏夹"
|
||||
@@ -2428,10 +2430,10 @@ _permissions:
|
||||
"write:user-groups": "编辑用户组"
|
||||
"read:channels": "查看频道"
|
||||
"write:channels": "管理频道"
|
||||
"read:gallery": "浏览图集"
|
||||
"write:gallery": "编辑图集"
|
||||
"read:gallery-likes": "浏览喜欢的图集"
|
||||
"write:gallery-likes": "管理喜欢的图集"
|
||||
"read:gallery": "浏览相册"
|
||||
"write:gallery": "管理相册"
|
||||
"read:gallery-likes": "浏览喜欢的相册"
|
||||
"write:gallery-likes": "管理喜欢的相册"
|
||||
"read:flash": "查看 Play"
|
||||
"write:flash": "编辑 Play"
|
||||
"read:flash-likes": "查看 Play 的点赞"
|
||||
@@ -2455,7 +2457,7 @@ _permissions:
|
||||
"write:admin:unsuspend-user": "解除用户冻结"
|
||||
"write:admin:meta": "编辑实例元数据"
|
||||
"write:admin:user-note": "编辑管理笔记"
|
||||
"write:admin:roles": "编辑角色"
|
||||
"write:admin:roles": "管理角色"
|
||||
"read:admin:roles": "查看角色"
|
||||
"write:admin:relays": "编辑中继"
|
||||
"read:admin:relays": "查看中继"
|
||||
@@ -2465,8 +2467,8 @@ _permissions:
|
||||
"read:admin:announcements": "查看公告"
|
||||
"write:admin:avatar-decorations": "编辑头像挂件"
|
||||
"read:admin:avatar-decorations": "查看头像挂件"
|
||||
"write:admin:federation": "编辑联合相关信息"
|
||||
"write:admin:account": "编辑用户账户"
|
||||
"write:admin:federation": "编辑联邦相关信息"
|
||||
"write:admin:account": "管理用户账户"
|
||||
"read:admin:account": "查看用户相关情报"
|
||||
"write:admin:emoji": "编辑表情符号"
|
||||
"read:admin:emoji": "查看表情符号"
|
||||
@@ -2482,7 +2484,7 @@ _permissions:
|
||||
"read:invite-codes": "获取已发行的邀请码"
|
||||
"write:clip-favorite": "管理喜欢的便签"
|
||||
"read:clip-favorite": "查看便签的点赞"
|
||||
"read:federation": "查看联合相关信息"
|
||||
"read:federation": "查看联邦相关信息"
|
||||
"write:report-abuse": "举报用户"
|
||||
"write:chat": "撰写或删除消息"
|
||||
"read:chat": "查看私信"
|
||||
@@ -2529,7 +2531,7 @@ _widgets:
|
||||
photos: "照片"
|
||||
digitalClock: "数字时钟"
|
||||
unixClock: "UNIX 时钟"
|
||||
federation: "联合"
|
||||
federation: "联邦"
|
||||
instanceCloud: "服务器球状列表"
|
||||
postForm: "发帖窗口"
|
||||
slideshow: "幻灯片展示"
|
||||
@@ -2562,7 +2564,7 @@ _widgetOptions:
|
||||
graduationDots: "点"
|
||||
graduationArabic: "阿拉伯数字"
|
||||
fadeGraduations: "淡化表盘"
|
||||
sAnimation: "秒针动画"
|
||||
sAnimation: "秒针动效"
|
||||
sAnimationElastic: "跳动"
|
||||
sAnimationEaseOut: "平滑"
|
||||
twentyFour: "24 小时制"
|
||||
@@ -2620,11 +2622,11 @@ _visibility:
|
||||
followersDescription: "仅发送至关注者"
|
||||
specified: "指定用户"
|
||||
specifiedDescription: "仅发送至指定用户"
|
||||
disableFederation: "不参与联合"
|
||||
disableFederation: "仅限本地"
|
||||
disableFederationDescription: "不发送到其他服务器"
|
||||
_postForm:
|
||||
quitInspiteOfThereAreUnuploadedFilesConfirm: "还有未上传的文件,要丢弃并关闭窗口吗?"
|
||||
uploaderTip: "文件还未上传。可以在文件菜单中进行重命名、裁剪、添加水印、设置是否压缩等操作。文件将在发帖时自动上传。"
|
||||
uploaderTip: "文件尚未上传。您可以在文件菜单中设置重命名、裁剪图片、添加水印以及是否压缩等功能。文件将在帖子发布时自动上传。"
|
||||
replyPlaceholder: "回复这个帖子..."
|
||||
quotePlaceholder: "引用这个帖子..."
|
||||
channelPlaceholder: "发布到频道…"
|
||||
@@ -2659,12 +2661,12 @@ _profile:
|
||||
metadataDescription: "最多可以在个人资料中以表格形式显示四条其他信息。"
|
||||
metadataLabel: "标签"
|
||||
metadataContent: "内容"
|
||||
changeAvatar: "修改头像"
|
||||
changeBanner: "修改横幅"
|
||||
changeAvatar: "更换头像"
|
||||
changeBanner: "更换横幅"
|
||||
verifiedLinkDescription: "如果将内容设置为 URL,当链接所指向的网页内包含自己的个人资料链接时,可以显示一个已验证图标。"
|
||||
avatarDecorationMax: "最多可添加 {max} 个挂件"
|
||||
followedMessage: "被关注时显示的消息"
|
||||
followedMessageDescription: "可以设置被关注时向对方显示的短消息。"
|
||||
followedMessage: "被关注时的信息"
|
||||
followedMessageDescription: "被关注时,可设置向关注者显示的信息。"
|
||||
followedMessageDescriptionForLockedAccount: "需要批准才能关注的情况下,消息会在请求被批准后显示。"
|
||||
_exportOrImport:
|
||||
allNotes: "所有帖子"
|
||||
@@ -2672,13 +2674,13 @@ _exportOrImport:
|
||||
clips: "便签"
|
||||
followingList: "关注中"
|
||||
muteList: "隐藏"
|
||||
blockingList: "屏蔽"
|
||||
blockingList: "禁止与我互动的列表"
|
||||
userLists: "列表"
|
||||
excludeMutingUsers: "排除已隐藏用户"
|
||||
excludeInactiveUsers: "排除不活跃用户"
|
||||
withReplies: "在时间线中包含导入用户的回复"
|
||||
_charts:
|
||||
federation: "联合"
|
||||
federation: "联邦"
|
||||
apRequest: "请求"
|
||||
usersIncDec: "用户数量:增加/减少"
|
||||
usersTotal: "用户总数"
|
||||
@@ -2976,7 +2978,7 @@ _moderationLogTypes:
|
||||
deleteAccount: "删除帐户"
|
||||
deletePage: "删除页面"
|
||||
deleteFlash: "删除 Play"
|
||||
deleteGalleryPost: "删除图集内容"
|
||||
deleteGalleryPost: "删除相册内容"
|
||||
deleteChatRoom: "删除群聊"
|
||||
updateProxyAccountDescription: "更新代理账户的简介"
|
||||
_fileViewer:
|
||||
@@ -3033,7 +3035,7 @@ _dataSaver:
|
||||
description: "防止自动加载图像和视频。 点击隐藏的图像/视频即可加载它们。\n"
|
||||
_avatar:
|
||||
title: "头像"
|
||||
description: "停止播放头像的动画。 由于动画图片的文件大小可能比普通图像大,这可以进一步减少数据流量。"
|
||||
description: "不播放头像的动画。 由于动态图像的文件大小远大于一般图像,停止播放能够进一步节省数据流量。"
|
||||
_urlPreviewThumbnail:
|
||||
title: "不显示 URL预览缩略图"
|
||||
description: "将不再加载 URL 预览缩略图。"
|
||||
@@ -3052,7 +3054,7 @@ _reversi:
|
||||
gameSettings: "对局设置"
|
||||
chooseBoard: "选择棋盘"
|
||||
blackOrWhite: "先手/后手"
|
||||
blackIs: "{name}执黑(先手)"
|
||||
blackIs: "{name}执黑(先手)"
|
||||
rules: "规则"
|
||||
thisGameIsStartedSoon: "对局即将开始"
|
||||
waitingForOther: "等待对手准备"
|
||||
@@ -3068,7 +3070,7 @@ _reversi:
|
||||
surrendered: "已认输"
|
||||
timeout: "超时"
|
||||
drawn: "平局"
|
||||
won: "{name}获胜"
|
||||
won: "{name} 获胜"
|
||||
black: "黑"
|
||||
white: "白"
|
||||
total: "总计"
|
||||
@@ -3077,7 +3079,7 @@ _reversi:
|
||||
allGames: "所有对局"
|
||||
ended: "结束"
|
||||
playing: "对局中"
|
||||
isLlotheo: "落子少的一方获胜(又名奥赛罗)"
|
||||
isLlotheo: "落子少的一方获胜(黑白棋规则)"
|
||||
loopedMap: "循环棋盘"
|
||||
canPutEverywhere: "无限制放置模式"
|
||||
timeLimitForEachTurn: "1回合的时间限制"
|
||||
@@ -3087,8 +3089,8 @@ _reversi:
|
||||
shareToTlTheGameWhenStart: "开始时在时间线发布对局"
|
||||
iStartedAGame: "对局开始!#MisskeyReversi"
|
||||
opponentHasSettingsChanged: "对手更改了设定"
|
||||
allowIrregularRules: "允许非常规规则(完全自由)"
|
||||
disallowIrregularRules: "禁止非常规规则"
|
||||
allowIrregularRules: "允许特殊规则(完全自由)"
|
||||
disallowIrregularRules: "禁止特殊规则"
|
||||
showBoardLabels: "显示行号和列号"
|
||||
useAvatarAsStone: "用头像作为棋子"
|
||||
_offlineScreen:
|
||||
@@ -3245,7 +3247,7 @@ _search:
|
||||
searchScopeLocal: "本地"
|
||||
searchScopeServer: "指定服务器"
|
||||
searchScopeUser: "指定用户"
|
||||
pleaseEnterServerHost: "请填写服务器主机名"
|
||||
pleaseEnterServerHost: "请填写服务器的主机名称"
|
||||
pleaseSelectUser: "请选择用户"
|
||||
serverHostPlaceholder: "如:misskey.example.com"
|
||||
_serverSetupWizard:
|
||||
@@ -3274,13 +3276,13 @@ _serverSetupWizard:
|
||||
largeScaleServerAdvice: "运营大规模服务器可能需要高级基础设施知识,如负载均衡和数据库复制。"
|
||||
doYouConnectToFediverse: "要加入 Fediverse 吗?"
|
||||
doYouConnectToFediverse_description1: "若加入由分散性服务器所构成的网络(Fediverse),将能与其它服务器交换内容。"
|
||||
doYouConnectToFediverse_description2: "加入 Fediverse 在这里被称为「联合」。"
|
||||
youCanConfigureMoreFederationSettingsLater: "可在之后进行如哪些服务器可以进行联合等高级设置。"
|
||||
doYouConnectToFediverse_description2: "加入 Fediverse 在这里被称为「联邦」。"
|
||||
youCanConfigureMoreFederationSettingsLater: "可在之后进行如哪些服务器允许进行联邦交互等高级设置。"
|
||||
remoteContentsCleaning: "自动清理传入内容"
|
||||
remoteContentsCleaning_description: "加入联合后,服务器将持续接收大量内容。打开自动清理后,将自动删除无法找到的旧内容,可节省存储空间。"
|
||||
remoteContentsCleaning_description: "开启联邦互通后,服务器将持续接收大量内容。打开自动清理后,将自动删除无法找到的旧内容,可节省存储空间。"
|
||||
adminInfo: "管理员信息"
|
||||
adminInfo_description: "设置用于接受询问的管理员信息。"
|
||||
adminInfo_mustBeFilled: "开放服务器或开启了联合的情况下必须输入。"
|
||||
adminInfo_mustBeFilled: "开放服务器或启用了联邦的情况下必须输入。"
|
||||
followingSettingsAreRecommended: "推荐以下设置"
|
||||
applyTheseSettings: "使用此设置"
|
||||
skipSettings: "跳过设置"
|
||||
@@ -3300,7 +3302,7 @@ _uploader:
|
||||
doneConfirm: "部分文件尚未上传,是否继续?"
|
||||
maxFileSizeIsX: "可上传最大 {x} 的文件。"
|
||||
allowedTypes: "可上传的文件类型"
|
||||
tip: "文件还没有被上传。可在此对话框中进行上传前确认、重命名、压缩、裁剪等操作。准备完成后,点击「上传」即可开始上传。"
|
||||
tip: "文件尚未上传。在此对话框中,您可以进行上传前的确认、重命名、压缩和裁剪等操作。准备就绪后,点击“上传”按钮即可开始上传。"
|
||||
_clientPerformanceIssueTip:
|
||||
title: "如果觉得电池耗电过高"
|
||||
makeSureDisabledAdBlocker: "请关闭广告拦截器"
|
||||
|
||||
@@ -10,7 +10,7 @@ notifications: "通知"
|
||||
username: "使用者名稱"
|
||||
password: "密碼"
|
||||
initialPasswordForSetup: "啟動初始設定的密碼"
|
||||
initialPasswordIsIncorrect: "啟動初始設定的密碼錯誤。"
|
||||
initialPasswordIsIncorrect: "啟動初始設定密碼錯誤。"
|
||||
initialPasswordForSetupDescription: "如果您自己安裝了 Misskey,請使用您在設定檔中輸入的密碼。\n如果您使用 Misskey 的託管服務之類的服務,請使用提供的密碼。\n如果您尚未設定密碼,請將其留空並繼續。"
|
||||
forgotPassword: "忘記密碼"
|
||||
fetchingAsApObject: "從聯邦宇宙取得中..."
|
||||
@@ -1408,6 +1408,7 @@ frame: "邊框"
|
||||
presets: "預設值"
|
||||
zeroPadding: "補零"
|
||||
nothingToConfigure: "無可設定的項目"
|
||||
viewRenotedChannel: "顯示轉發貼文者的頻道"
|
||||
_imageEditing:
|
||||
_vars:
|
||||
caption: "檔案標題"
|
||||
|
||||
42
package.json
42
package.json
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"version": "2026.3.2-beta.0",
|
||||
"version": "2026.5.1",
|
||||
"codename": "nasubi",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/misskey-dev/misskey.git"
|
||||
},
|
||||
"packageManager": "pnpm@10.32.1",
|
||||
"packageManager": "pnpm@10.33.2",
|
||||
"workspaces": [
|
||||
"packages/misskey-js",
|
||||
"packages/i18n",
|
||||
@@ -28,9 +28,9 @@
|
||||
"build": "pnpm build-pre && pnpm -r build && pnpm build-assets",
|
||||
"build-storybook": "pnpm --filter frontend build-storybook",
|
||||
"build-misskey-js-with-types": "pnpm build-pre && pnpm --filter backend... --filter=!misskey-js build && pnpm --filter backend generate-api-json --no-build && ncp packages/backend/built/api.json packages/misskey-js/generator/api.json && pnpm --filter misskey-js update-autogen-code && pnpm --filter misskey-js build && pnpm --filter misskey-js api",
|
||||
"start": "cd packages/backend && pnpm compile-config && node ./built/boot/entry.js",
|
||||
"start:inspect": "cd packages/backend && pnpm compile-config && node --inspect ./built/boot/entry.js",
|
||||
"start:test": "ncp ./.github/misskey/test.yml ./.config/test.yml && cd packages/backend && cross-env NODE_ENV=test pnpm compile-config && cross-env NODE_ENV=test node ./built/boot/entry.js",
|
||||
"start": "cd packages/backend && pnpm compile-config && node ./built/entry.js",
|
||||
"start:inspect": "cd packages/backend && pnpm compile-config && node --inspect ./built/entry.js",
|
||||
"start:test": "ncp ./.github/misskey/test.yml ./.config/test.yml && cd packages/backend && cross-env NODE_ENV=test pnpm compile-config && cross-env NODE_ENV=test node ./built/entry.js",
|
||||
"cli": "cd packages/backend && pnpm cli",
|
||||
"init": "pnpm migrate",
|
||||
"migrate": "cd packages/backend && pnpm migrate",
|
||||
@@ -44,8 +44,8 @@
|
||||
"cy:run": "pnpm cypress run",
|
||||
"e2e": "pnpm start-server-and-test start:test http://localhost:61812 cy:run",
|
||||
"e2e-dev-container": "ncp ./.config/cypress-devcontainer.yml ./.config/test.yml && pnpm start-server-and-test start:test http://localhost:61812 cy:run",
|
||||
"jest": "cd packages/backend && pnpm jest",
|
||||
"jest-and-coverage": "cd packages/backend && pnpm jest-and-coverage",
|
||||
"backend-unit-test": "cd packages/backend && pnpm test",
|
||||
"backend-unit-test-and-coverage": "cd packages/backend && pnpm test-and-coverage",
|
||||
"test": "pnpm -r test",
|
||||
"test-and-coverage": "pnpm -r test-and-coverage",
|
||||
"clean": "node scripts/clean.mjs",
|
||||
@@ -53,30 +53,30 @@
|
||||
"cleanall": "pnpm clean-all"
|
||||
},
|
||||
"dependencies": {
|
||||
"cssnano": "7.1.3",
|
||||
"esbuild": "0.27.4",
|
||||
"cssnano": "7.1.7",
|
||||
"esbuild": "0.28.0",
|
||||
"execa": "9.6.1",
|
||||
"ignore-walk": "8.0.0",
|
||||
"js-yaml": "4.1.1",
|
||||
"postcss": "8.5.8",
|
||||
"tar": "7.5.11",
|
||||
"terser": "5.46.0"
|
||||
"postcss": "8.5.10",
|
||||
"tar": "7.5.13",
|
||||
"terser": "5.46.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "9.39.4",
|
||||
"@misskey-dev/eslint-plugin": "2.1.0",
|
||||
"@types/js-yaml": "4.0.9",
|
||||
"@types/node": "24.12.0",
|
||||
"@typescript-eslint/eslint-plugin": "8.57.0",
|
||||
"@typescript-eslint/parser": "8.57.0",
|
||||
"@typescript/native-preview": "7.0.0-dev.20260116.1",
|
||||
"@types/node": "24.12.2",
|
||||
"@typescript-eslint/eslint-plugin": "8.59.0",
|
||||
"@typescript-eslint/parser": "8.59.0",
|
||||
"@typescript/native-preview": "7.0.0-dev.20260426.1",
|
||||
"cross-env": "10.1.0",
|
||||
"cypress": "15.11.0",
|
||||
"cypress": "15.14.1",
|
||||
"eslint": "9.39.4",
|
||||
"globals": "17.4.0",
|
||||
"globals": "17.5.0",
|
||||
"ncp": "2.0.0",
|
||||
"pnpm": "10.32.1",
|
||||
"start-server-and-test": "2.1.5",
|
||||
"pnpm": "10.33.2",
|
||||
"start-server-and-test": "3.0.2",
|
||||
"typescript": "5.9.3"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
@@ -86,7 +86,7 @@
|
||||
"overrides": {
|
||||
"@aiscript-dev/aiscript-languageserver": "-",
|
||||
"chokidar": "5.0.0",
|
||||
"lodash": "4.17.23"
|
||||
"lodash": "4.18.1"
|
||||
},
|
||||
"ignoredBuiltDependencies": [
|
||||
"@sentry-internal/node-cpu-profiler",
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
{
|
||||
"$schema": "https://swc.rs/schema.json",
|
||||
"jsc": {
|
||||
"parser": {
|
||||
"syntax": "typescript",
|
||||
"jsx": true,
|
||||
"dynamicImport": true,
|
||||
"decorators": true
|
||||
},
|
||||
"transform": {
|
||||
"legacyDecorator": true,
|
||||
"decoratorMetadata": true,
|
||||
"react": {
|
||||
"runtime": "automatic",
|
||||
"importSource": "@kitajs/html"
|
||||
}
|
||||
},
|
||||
"experimental": {
|
||||
"keepImportAssertions": true
|
||||
},
|
||||
"baseUrl": "src",
|
||||
"paths": {
|
||||
"@/*": ["*"]
|
||||
},
|
||||
"target": "es2022"
|
||||
},
|
||||
"minify": false,
|
||||
"sourceMaps": "inline"
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Misskey API</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<script
|
||||
id="api-reference"
|
||||
data-url="/api.json"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 5.4 KiB |
@@ -1,4 +0,0 @@
|
||||
user-agent: *
|
||||
allow: /
|
||||
|
||||
# todo: sitemap
|
||||
@@ -1,121 +0,0 @@
|
||||
import fs from 'node:fs';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { dirname, join } from 'node:path';
|
||||
import { build } from 'esbuild';
|
||||
import { swcPlugin } from 'esbuild-plugin-swc';
|
||||
|
||||
const _filename = fileURLToPath(import.meta.url);
|
||||
const _dirname = dirname(_filename);
|
||||
const _package = JSON.parse(fs.readFileSync(_dirname + '/package.json', 'utf-8'));
|
||||
|
||||
const resolveTsPathsPlugin = {
|
||||
name: 'resolve-ts-paths',
|
||||
setup(build) {
|
||||
build.onResolve({ filter: /^\.{1,2}\/.*\.js$/ }, (args) => {
|
||||
if (args.importer) {
|
||||
const absPath = join(args.resolveDir, args.path);
|
||||
const tsPath = absPath.slice(0, -3) + '.ts';
|
||||
if (fs.existsSync(tsPath)) return { path: tsPath };
|
||||
const tsxPath = absPath.slice(0, -3) + '.tsx';
|
||||
if (fs.existsSync(tsxPath)) return { path: tsxPath };
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
const externalIpaddrPlugin = {
|
||||
name: 'external-ipaddr',
|
||||
setup(build) {
|
||||
build.onResolve({ filter: /^ipaddr\.js$/ }, (args) => {
|
||||
return { path: args.path, external: true };
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
/** @type {import('esbuild').BuildOptions} */
|
||||
const options = {
|
||||
entryPoints: ['./src/boot/entry.ts'],
|
||||
minify: true,
|
||||
keepNames: true,
|
||||
bundle: true,
|
||||
outdir: './built/boot',
|
||||
target: 'node22',
|
||||
platform: 'node',
|
||||
format: 'esm',
|
||||
sourcemap: 'linked',
|
||||
packages: 'external',
|
||||
banner: {
|
||||
js: 'import { createRequire as topLevelCreateRequire } from "module";' +
|
||||
'import ___url___ from "url";' +
|
||||
'const require = topLevelCreateRequire(import.meta.url);' +
|
||||
'const __filename = ___url___.fileURLToPath(import.meta.url);' +
|
||||
'const __dirname = ___url___.fileURLToPath(new URL(".", import.meta.url));',
|
||||
},
|
||||
plugins: [
|
||||
externalIpaddrPlugin,
|
||||
resolveTsPathsPlugin,
|
||||
swcPlugin({
|
||||
jsc: {
|
||||
parser: {
|
||||
syntax: 'typescript',
|
||||
decorators: true,
|
||||
dynamicImport: true,
|
||||
},
|
||||
transform: {
|
||||
legacyDecorator: true,
|
||||
decoratorMetadata: true,
|
||||
},
|
||||
experimental: {
|
||||
keepImportAssertions: true,
|
||||
},
|
||||
baseUrl: join(_dirname, 'src'),
|
||||
paths: {
|
||||
'@/*': ['*'],
|
||||
},
|
||||
target: 'esnext',
|
||||
keepClassNames: true,
|
||||
},
|
||||
}),
|
||||
externalIpaddrPlugin,
|
||||
],
|
||||
// external: [
|
||||
// 'slacc-*',
|
||||
// 'class-transformer',
|
||||
// 'class-validator',
|
||||
// '@sentry/*',
|
||||
// '@nestjs/websockets/socket-module',
|
||||
// '@nestjs/microservices/microservices-module',
|
||||
// '@nestjs/microservices',
|
||||
// '@napi-rs/canvas-win32-x64-msvc',
|
||||
// 'mock-aws-s3',
|
||||
// 'aws-sdk',
|
||||
// 'nock',
|
||||
// 'sharp',
|
||||
// 'jsdom',
|
||||
// 're2',
|
||||
// '@napi-rs/canvas',
|
||||
// ],
|
||||
};
|
||||
|
||||
const args = process.argv.slice(2).map(arg => arg.toLowerCase());
|
||||
|
||||
if (!args.includes('--no-clean')) {
|
||||
fs.rmSync('./built', { recursive: true, force: true });
|
||||
}
|
||||
|
||||
await buildSrc();
|
||||
|
||||
async function buildSrc() {
|
||||
console.log(`[${_package.name}] start building...`);
|
||||
|
||||
await build(options)
|
||||
.then(() => {
|
||||
console.log(`[${_package.name}] build succeeded.`);
|
||||
})
|
||||
.catch((err) => {
|
||||
process.stderr.write(err.stderr || err.message || err);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
console.log(`[${_package.name}] finish building.`);
|
||||
}
|
||||
@@ -1,220 +0,0 @@
|
||||
/*
|
||||
* For a detailed explanation regarding each configuration property and type check, visit:
|
||||
* https://jestjs.io/docs/en/configuration.html
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
// All imported modules in your tests should be mocked automatically
|
||||
// automock: false,
|
||||
|
||||
// Stop running tests after `n` failures
|
||||
// bail: 0,
|
||||
|
||||
// The directory where Jest should store its cached dependency information
|
||||
// cacheDirectory: "C:\\Users\\ai\\AppData\\Local\\Temp\\jest",
|
||||
|
||||
// Automatically clear mock calls and instances between every test
|
||||
// clearMocks: false,
|
||||
|
||||
// Indicates whether the coverage information should be collected while executing the test
|
||||
// collectCoverage: false,
|
||||
|
||||
// An array of glob patterns indicating a set of files for which coverage information should be collected
|
||||
collectCoverageFrom: ['src/**/*.ts', '!src/**/*.test.ts'],
|
||||
|
||||
// The directory where Jest should output its coverage files
|
||||
coverageDirectory: "coverage",
|
||||
|
||||
// An array of regexp pattern strings used to skip coverage collection
|
||||
// coveragePathIgnorePatterns: [
|
||||
// "\\\\node_modules\\\\"
|
||||
// ],
|
||||
|
||||
// Indicates which provider should be used to instrument code for coverage
|
||||
coverageProvider: "v8",
|
||||
|
||||
// A list of reporter names that Jest uses when writing coverage reports
|
||||
// coverageReporters: [
|
||||
// "json",
|
||||
// "text",
|
||||
// "lcov",
|
||||
// "clover"
|
||||
// ],
|
||||
|
||||
// An object that configures minimum threshold enforcement for coverage results
|
||||
// coverageThreshold: undefined,
|
||||
|
||||
// A path to a custom dependency extractor
|
||||
// dependencyExtractor: undefined,
|
||||
|
||||
// Make calling deprecated APIs throw helpful error messages
|
||||
// errorOnDeprecated: false,
|
||||
|
||||
// Force coverage collection from ignored files using an array of glob patterns
|
||||
// forceCoverageMatch: [],
|
||||
|
||||
// A path to a module which exports an async function that is triggered once before all test suites
|
||||
// globalSetup: undefined,
|
||||
|
||||
// A path to a module which exports an async function that is triggered once after all test suites
|
||||
// globalTeardown: undefined,
|
||||
|
||||
// A set of global variables that need to be available in all test environments
|
||||
globals: {
|
||||
},
|
||||
|
||||
// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
|
||||
// maxWorkers: "50%",
|
||||
|
||||
// An array of directory names to be searched recursively up from the requiring module's location
|
||||
// moduleDirectories: [
|
||||
// "node_modules"
|
||||
// ],
|
||||
|
||||
// An array of file extensions your modules use
|
||||
// moduleFileExtensions: [
|
||||
// "js",
|
||||
// "json",
|
||||
// "jsx",
|
||||
// "ts",
|
||||
// "tsx",
|
||||
// "node"
|
||||
// ],
|
||||
|
||||
// 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: {
|
||||
// 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}|[A-Z:])*/.*)\\.js$': '$1',
|
||||
},
|
||||
|
||||
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
|
||||
// modulePathIgnorePatterns: [],
|
||||
|
||||
// Activates notifications for test results
|
||||
// notify: false,
|
||||
|
||||
// An enum that specifies notification mode. Requires { notify: true }
|
||||
// notifyMode: "failure-change",
|
||||
|
||||
// A preset that is used as a base for Jest's configuration
|
||||
//preset: "ts-jest/presets/js-with-ts-esm",
|
||||
|
||||
// Run tests from one or more projects
|
||||
// projects: undefined,
|
||||
|
||||
// Use this configuration option to add custom reporters to Jest
|
||||
// reporters: undefined,
|
||||
|
||||
// Automatically reset mock state between every test
|
||||
// resetMocks: false,
|
||||
|
||||
// Reset the module registry before running each individual test
|
||||
// resetModules: false,
|
||||
|
||||
// A path to a custom resolver
|
||||
// resolver: './jest-resolver.cjs',
|
||||
|
||||
// Automatically restore mock state between every test
|
||||
restoreMocks: true,
|
||||
|
||||
// The root directory that Jest should scan for tests and modules within
|
||||
// rootDir: undefined,
|
||||
|
||||
// A list of paths to directories that Jest should use to search for files in
|
||||
roots: [
|
||||
"<rootDir>"
|
||||
],
|
||||
|
||||
// Allows you to use a custom runner instead of Jest's default test runner
|
||||
// runner: "jest-runner",
|
||||
|
||||
// The paths to modules that run some code to configure or set up the testing environment before each test
|
||||
// setupFiles: [],
|
||||
|
||||
// A list of paths to modules that run some code to configure or set up the testing framework before each test
|
||||
// setupFilesAfterEnv: [],
|
||||
|
||||
// The number of seconds after which a test is considered as slow and reported as such in the results.
|
||||
// slowTestThreshold: 5,
|
||||
|
||||
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
|
||||
// snapshotSerializers: [],
|
||||
|
||||
// The test environment that will be used for testing
|
||||
testEnvironment: "node",
|
||||
|
||||
// Options that will be passed to the testEnvironment
|
||||
// testEnvironmentOptions: {},
|
||||
|
||||
// Adds a location field to test results
|
||||
// testLocationInResults: false,
|
||||
|
||||
// The glob patterns Jest uses to detect test files
|
||||
testMatch: [
|
||||
"<rootDir>/test/unit/**/*.ts",
|
||||
"<rootDir>/src/**/*.test.ts",
|
||||
],
|
||||
|
||||
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
|
||||
// testPathIgnorePatterns: [
|
||||
// "\\\\node_modules\\\\"
|
||||
// ],
|
||||
|
||||
// The regexp pattern or array of patterns that Jest uses to detect test files
|
||||
// testRegex: [],
|
||||
|
||||
// This option allows the use of a custom results processor
|
||||
// testResultsProcessor: undefined,
|
||||
|
||||
// This option allows use of a custom test runner
|
||||
// testRunner: "jasmine2",
|
||||
|
||||
// This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
|
||||
// testURL: "http://localhost",
|
||||
|
||||
// Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"
|
||||
// timers: "real",
|
||||
|
||||
// A map from regular expressions to paths to transformers
|
||||
transform: {
|
||||
"^.+\\.(t|j)sx?$": ["@swc/jest"],
|
||||
},
|
||||
|
||||
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
|
||||
// transformIgnorePatterns: [
|
||||
// "\\\\node_modules\\\\",
|
||||
// "\\.pnp\\.[^\\\\]+$"
|
||||
// ],
|
||||
|
||||
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
|
||||
// unmockedModulePathPatterns: undefined,
|
||||
|
||||
// Indicates whether each individual test should be reported during the run
|
||||
// verbose: undefined,
|
||||
|
||||
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
|
||||
// watchPathIgnorePatterns: [],
|
||||
|
||||
// Whether to use watchman for file crawling
|
||||
// watchman: true,
|
||||
|
||||
extensionsToTreatAsEsm: ['.ts', '.tsx'],
|
||||
|
||||
testTimeout: 60000,
|
||||
|
||||
// Let Jest kill the test worker whenever it grows too much
|
||||
// (It seems there's a known memory leak issue in Node.js' vm.Script used by Jest)
|
||||
// https://github.com/facebook/jest/issues/11956
|
||||
maxWorkers: 1, // Make it use worker (that can be killed and restarted)
|
||||
logHeapUsage: true, // To debug when out-of-memory happens on CI
|
||||
workerIdleMemoryLimit: '1GiB', // Limit the worker to 1GB (GitHub Workflows dies at 2GB)
|
||||
|
||||
maxConcurrency: 32,
|
||||
};
|
||||
@@ -1,15 +0,0 @@
|
||||
/*
|
||||
* For a detailed explanation regarding each configuration property and type check, visit:
|
||||
* https://jestjs.io/docs/en/configuration.html
|
||||
*/
|
||||
|
||||
const base = require('./jest.config.cjs')
|
||||
|
||||
module.exports = {
|
||||
...base,
|
||||
globalSetup: "<rootDir>/built-test/entry.js",
|
||||
setupFilesAfterEnv: ["<rootDir>/test/jest.setup.ts"],
|
||||
testMatch: [
|
||||
"<rootDir>/test/e2e/**/*.ts",
|
||||
],
|
||||
};
|
||||
@@ -1,13 +0,0 @@
|
||||
/*
|
||||
* For a detailed explanation regarding each configuration property and type check, visit:
|
||||
* https://jestjs.io/docs/en/configuration.html
|
||||
*/
|
||||
|
||||
const base = require('./jest.config.cjs');
|
||||
|
||||
module.exports = {
|
||||
...base,
|
||||
testMatch: [
|
||||
'<rootDir>/test-federation/test/**/*.test.ts',
|
||||
],
|
||||
};
|
||||
@@ -1,15 +0,0 @@
|
||||
/*
|
||||
* For a detailed explanation regarding each configuration property and type check, visit:
|
||||
* https://jestjs.io/docs/en/configuration.html
|
||||
*/
|
||||
|
||||
const base = require('./jest.config.cjs')
|
||||
|
||||
module.exports = {
|
||||
...base,
|
||||
globalSetup: "<rootDir>/test/jest.setup.unit.cjs",
|
||||
testMatch: [
|
||||
"<rootDir>/test/unit/**/*.ts",
|
||||
"<rootDir>/src/**/*.test.ts",
|
||||
],
|
||||
};
|
||||
@@ -1,31 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
import child_process from 'node:child_process';
|
||||
import path from 'node:path';
|
||||
import url from 'node:url';
|
||||
|
||||
import semver from 'semver';
|
||||
|
||||
const __filename = url.fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
const args = [];
|
||||
args.push(...[
|
||||
...semver.satisfies(process.version, '^20.17.0 || ^22.0.0 || ^24.10.0') ? ['--no-experimental-require-module'] : [],
|
||||
'--experimental-vm-modules',
|
||||
'--experimental-import-meta-resolve',
|
||||
path.join(__dirname, 'node_modules/jest/bin/jest.js'),
|
||||
...process.argv.slice(2),
|
||||
]);
|
||||
|
||||
const child = child_process.spawn(process.execPath, args, { stdio: 'inherit' });
|
||||
child.on('error', (err) => {
|
||||
console.error('Failed to start Jest:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
child.on('exit', (code, signal) => {
|
||||
if (code === null) {
|
||||
process.exit(128 + signal);
|
||||
} else {
|
||||
process.exit(code);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export class AddCategoryToAvatarDecorations1766652173085 {
|
||||
name = 'AddCategoryToAvatarDecorations1766652173085';
|
||||
|
||||
/**
|
||||
* @param {QueryRunner} queryRunner
|
||||
*/
|
||||
async up(queryRunner) {
|
||||
await queryRunner.query('ALTER TABLE "avatar_decoration" ADD "category" character varying(128)');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {QueryRunner} queryRunner
|
||||
*/
|
||||
async down(queryRunner) {
|
||||
await queryRunner.query('ALTER TABLE "avatar_decoration" DROP COLUMN "category"');
|
||||
}
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import { DataSource } from 'typeorm';
|
||||
import { loadConfig } from './src-js/config.js';
|
||||
import { entities } from './src-js/postgres.js';
|
||||
import { loadConfig } from './built/config.js';
|
||||
import { entities } from './built/postgres.js';
|
||||
|
||||
const isConcurrentIndexMigrationEnabled = process.env.MISSKEY_MIGRATION_CREATE_INDEX_CONCURRENTLY === '1';
|
||||
|
||||
|
||||
@@ -7,51 +7,33 @@
|
||||
"node": "^22.15.0 || ^24.10.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "pnpm compile-config && node ./built/boot/entry.js",
|
||||
"start:inspect": "pnpm compile-config && node --inspect ./built/boot/entry.js",
|
||||
"start:test": "cross-env NODE_ENV=test pnpm compile-config && cross-env NODE_ENV=test node ./built/boot/entry.js",
|
||||
"start": "pnpm compile-config && node ./built/entry.js",
|
||||
"start:inspect": "pnpm compile-config && node --inspect ./built/entry.js",
|
||||
"start:test": "cross-env NODE_ENV=test pnpm compile-config && cross-env NODE_ENV=test node ./built/entry.js",
|
||||
"migrate": "pnpm compile-config && pnpm typeorm migration:run -d ormconfig.js",
|
||||
"revert": "pnpm compile-config && pnpm typeorm migration:revert -d ormconfig.js",
|
||||
"cli": "pnpm compile-config && node ./src-js/boot/cli.js",
|
||||
"cli": "pnpm compile-config && node ./built/cli.js",
|
||||
"check:connect": "pnpm compile-config && node ./scripts/check_connect.js",
|
||||
"compile-config": "node ./scripts/compile_config.js",
|
||||
"build": "swc src -d src-js -D --strip-leading-paths && node ./build.js",
|
||||
"build:test": "swc test-server -d built-test -D --config-file test-server/.swcrc --strip-leading-paths",
|
||||
"watch:swc": "swc src -d built -D -w --strip-leading-paths",
|
||||
"build": "rolldown -c",
|
||||
"build:unit": "rolldown -c --sourcemap",
|
||||
"build:e2e": "rolldown -c --e2e",
|
||||
"build:tsc": "tsgo -p tsconfig.json && tsc-alias -p tsconfig.json",
|
||||
"watch": "pnpm compile-config && node ./scripts/watch.mjs",
|
||||
"restart": "pnpm build && pnpm start",
|
||||
"dev": "pnpm compile-config && node ./scripts/dev.mjs",
|
||||
"dev": "pnpm compile-config && rolldown -c --watch",
|
||||
"typecheck": "tsgo --noEmit && tsgo -p test --noEmit && tsgo -p test-federation --noEmit",
|
||||
"eslint": "eslint --quiet \"{src,test-federation}/**/*.ts\"",
|
||||
"lint": "pnpm typecheck && pnpm eslint",
|
||||
"jest": "cross-env NODE_ENV=test pnpm compile-config && cross-env NODE_ENV=test node ./jest.js --forceExit --config jest.config.unit.cjs",
|
||||
"jest:e2e": "cross-env NODE_ENV=test pnpm compile-config && cross-env NODE_ENV=test node ./jest.js --forceExit --config jest.config.e2e.cjs",
|
||||
"jest:fed": "pnpm compile-config && node ./jest.js --forceExit --config jest.config.fed.cjs",
|
||||
"jest-and-coverage": "cross-env NODE_ENV=test pnpm compile-config && cross-env NODE_ENV=test node ./jest.js --coverage --forceExit --config jest.config.unit.cjs",
|
||||
"jest-and-coverage:e2e": "cross-env NODE_ENV=test pnpm compile-config && cross-env NODE_ENV=test node ./jest.js --coverage --forceExit --config jest.config.e2e.cjs",
|
||||
"jest-clear": "cross-env NODE_ENV=test pnpm compile-config && cross-env NODE_ENV=test node ./jest.js --clearCache",
|
||||
"test": "pnpm jest",
|
||||
"test:e2e": "pnpm build && pnpm build:test && pnpm jest:e2e",
|
||||
"test:fed": "pnpm jest:fed",
|
||||
"test-and-coverage": "pnpm jest-and-coverage",
|
||||
"test-and-coverage:e2e": "pnpm build && pnpm build:test && pnpm jest-and-coverage:e2e",
|
||||
"test": "pnpm build:unit && cross-env NODE_ENV=test pnpm compile-config && vitest --config vitest.config.unit.ts",
|
||||
"test:e2e": "pnpm build:e2e && cross-env NODE_ENV=test pnpm compile-config && vitest --config vitest.config.e2e.ts",
|
||||
"test:fed": "cross-env NODE_ENV=test pnpm compile-config && vitest --config vitest.config.fed.ts",
|
||||
"test-and-coverage": "pnpm build:unit && cross-env NODE_ENV=test pnpm compile-config && vitest --coverage --config vitest.config.unit.ts",
|
||||
"test-and-coverage:e2e": "pnpm build:e2e && cross-env NODE_ENV=test pnpm compile-config && vitest --coverage --config vitest.config.e2e.ts",
|
||||
"check-migrations": "node scripts/check_migrations_clean.js",
|
||||
"generate-api-json": "pnpm compile-config && node ./scripts/generate_api_json.js"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@swc/core-android-arm64": "1.3.11",
|
||||
"@swc/core-darwin-arm64": "1.15.18",
|
||||
"@swc/core-darwin-x64": "1.15.18",
|
||||
"@swc/core-freebsd-x64": "1.3.11",
|
||||
"@swc/core-linux-arm-gnueabihf": "1.15.18",
|
||||
"@swc/core-linux-arm64-gnu": "1.15.18",
|
||||
"@swc/core-linux-arm64-musl": "1.15.18",
|
||||
"@swc/core-linux-x64-gnu": "1.15.18",
|
||||
"@swc/core-linux-x64-musl": "1.15.18",
|
||||
"@swc/core-win32-arm64-msvc": "1.15.18",
|
||||
"@swc/core-win32-ia32-msvc": "1.15.18",
|
||||
"@swc/core-win32-x64-msvc": "1.15.18",
|
||||
"@tensorflow/tfjs": "4.22.0",
|
||||
"@tensorflow/tfjs-node": "4.22.0",
|
||||
"bufferutil": "4.1.0",
|
||||
@@ -71,80 +53,79 @@
|
||||
"utf-8-validate": "6.0.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "3.1008.0",
|
||||
"@aws-sdk/lib-storage": "3.1008.0",
|
||||
"@aws-sdk/client-s3": "3.1037.0",
|
||||
"@aws-sdk/lib-storage": "3.1037.0",
|
||||
"@discordapp/twemoji": "16.0.1",
|
||||
"@fastify/accepts": "5.0.4",
|
||||
"@fastify/cors": "11.2.0",
|
||||
"@fastify/express": "4.0.4",
|
||||
"@fastify/http-proxy": "11.4.1",
|
||||
"@fastify/multipart": "9.4.0",
|
||||
"@fastify/static": "9.0.0",
|
||||
"@fastify/express": "4.0.5",
|
||||
"@fastify/http-proxy": "11.4.4",
|
||||
"@fastify/multipart": "10.0.0",
|
||||
"@fastify/static": "9.1.3",
|
||||
"@kitajs/html": "4.2.13",
|
||||
"@misskey-dev/sharp-read-bmp": "1.2.0",
|
||||
"@misskey-dev/summaly": "5.2.5",
|
||||
"@napi-rs/canvas": "0.1.96",
|
||||
"@nestjs/common": "11.1.16",
|
||||
"@nestjs/core": "11.1.16",
|
||||
"@nestjs/testing": "11.1.16",
|
||||
"@misskey-dev/summaly": "5.3.0",
|
||||
"@napi-rs/canvas": "0.1.100",
|
||||
"@nestjs/common": "11.1.19",
|
||||
"@nestjs/core": "11.1.19",
|
||||
"@nestjs/testing": "11.1.19",
|
||||
"@oxc-project/runtime": "0.127.0",
|
||||
"@peertube/http-signature": "1.7.0",
|
||||
"@sentry/node": "10.43.0",
|
||||
"@sentry/profiling-node": "10.43.0",
|
||||
"@sentry/node": "10.50.0",
|
||||
"@sentry/profiling-node": "10.50.0",
|
||||
"@simplewebauthn/server": "13.3.0",
|
||||
"@sinonjs/fake-timers": "15.1.1",
|
||||
"@smithy/node-http-handler": "4.4.16",
|
||||
"@swc/cli": "0.8.0",
|
||||
"@swc/core": "1.15.18",
|
||||
"@sinonjs/fake-timers": "15.3.2",
|
||||
"@smithy/node-http-handler": "4.6.1",
|
||||
"@twemoji/parser": "16.0.0",
|
||||
"accepts": "1.3.8",
|
||||
"ajv": "8.18.0",
|
||||
"ajv": "8.20.0",
|
||||
"archiver": "7.0.1",
|
||||
"async-mutex": "0.5.0",
|
||||
"bcryptjs": "3.0.3",
|
||||
"blurhash": "2.0.5",
|
||||
"body-parser": "2.2.2",
|
||||
"bullmq": "5.71.0",
|
||||
"bullmq": "5.76.2",
|
||||
"cacheable-lookup": "7.0.0",
|
||||
"chalk": "5.6.2",
|
||||
"chalk-template": "1.1.2",
|
||||
"chokidar": "5.0.0",
|
||||
"color-convert": "3.1.3",
|
||||
"content-disposition": "1.0.1",
|
||||
"content-disposition": "1.1.0",
|
||||
"date-fns": "4.1.0",
|
||||
"deep-email-validator": "0.1.21",
|
||||
"fastify": "5.8.2",
|
||||
"deep-email-validator": "0.1.27",
|
||||
"fastify": "5.8.5",
|
||||
"fastify-raw-body": "5.0.0",
|
||||
"feed": "5.2.0",
|
||||
"file-type": "21.3.2",
|
||||
"feed": "5.2.1",
|
||||
"file-type": "22.0.1",
|
||||
"fluent-ffmpeg": "2.1.3",
|
||||
"form-data": "4.0.5",
|
||||
"got": "14.6.6",
|
||||
"got": "15.0.3",
|
||||
"hpagent": "1.2.0",
|
||||
"http-link-header": "1.1.3",
|
||||
"i18n": "workspace:*",
|
||||
"ioredis": "5.10.0",
|
||||
"ioredis": "5.10.1",
|
||||
"ip-cidr": "4.0.2",
|
||||
"ipaddr.js": "2.3.0",
|
||||
"is-svg": "6.1.0",
|
||||
"json5": "2.2.3",
|
||||
"jsonld": "9.0.0",
|
||||
"juice": "11.1.1",
|
||||
"meilisearch": "0.55.0",
|
||||
"meilisearch": "0.57.0",
|
||||
"mfm-js": "0.25.0",
|
||||
"mime-types": "3.0.2",
|
||||
"misskey-js": "workspace:*",
|
||||
"misskey-reversi": "workspace:*",
|
||||
"ms": "3.0.0-canary.202508261828",
|
||||
"nanoid": "5.1.6",
|
||||
"nanoid": "5.1.9",
|
||||
"nested-property": "4.0.0",
|
||||
"node-fetch": "3.3.2",
|
||||
"node-html-parser": "7.1.0",
|
||||
"nodemailer": "8.0.2",
|
||||
"nsfwjs": "4.2.0",
|
||||
"nodemailer": "8.0.6",
|
||||
"nsfwjs": "4.3.0",
|
||||
"oauth2orize": "1.12.0",
|
||||
"oauth2orize-pkce": "0.1.2",
|
||||
"os-utils": "0.0.14",
|
||||
"otpauth": "9.5.0",
|
||||
"otpauth": "9.5.1",
|
||||
"pg": "8.20.0",
|
||||
"pkce-challenge": "6.0.0",
|
||||
"probe-image-size": "7.2.3",
|
||||
@@ -152,19 +133,19 @@
|
||||
"qrcode": "1.5.4",
|
||||
"random-seed": "0.3.0",
|
||||
"ratelimiter": "3.4.1",
|
||||
"re2": "1.23.3",
|
||||
"re2": "1.24.0",
|
||||
"reflect-metadata": "0.2.2",
|
||||
"rename": "1.0.4",
|
||||
"rss-parser": "3.13.0",
|
||||
"rxjs": "7.8.2",
|
||||
"sanitize-html": "2.17.1",
|
||||
"sanitize-html": "2.17.3",
|
||||
"secure-json-parse": "4.1.0",
|
||||
"semver": "7.7.4",
|
||||
"sharp": "0.33.5",
|
||||
"slacc": "0.0.10",
|
||||
"strict-event-emitter-types": "2.0.0",
|
||||
"stringz": "2.1.0",
|
||||
"systeminformation": "5.31.4",
|
||||
"systeminformation": "5.31.5",
|
||||
"tinycolor2": "1.6.0",
|
||||
"tmp": "0.2.5",
|
||||
"tsc-alias": "1.8.16",
|
||||
@@ -172,32 +153,30 @@
|
||||
"ulid": "3.0.2",
|
||||
"vary": "1.1.2",
|
||||
"web-push": "3.6.7",
|
||||
"ws": "8.19.0",
|
||||
"ws": "8.20.0",
|
||||
"xev": "3.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@jest/globals": "29.7.0",
|
||||
"@kitajs/ts-html-plugin": "4.1.4",
|
||||
"@nestjs/platform-express": "11.1.16",
|
||||
"@sentry/vue": "10.43.0",
|
||||
"@simplewebauthn/types": "12.0.0",
|
||||
"@swc/jest": "0.2.39",
|
||||
"@nestjs/platform-express": "11.1.19",
|
||||
"@rollup/plugin-esm-shim": "0.1.8",
|
||||
"@sentry/vue": "10.50.0",
|
||||
"@types/accepts": "1.3.7",
|
||||
"@types/archiver": "7.0.0",
|
||||
"@types/body-parser": "1.19.6",
|
||||
"@types/color-convert": "2.0.4",
|
||||
"@types/color-convert": "3.0.1",
|
||||
"@types/content-disposition": "0.5.9",
|
||||
"@types/fluent-ffmpeg": "2.1.28",
|
||||
"@types/http-link-header": "1.0.7",
|
||||
"@types/jest": "29.5.14",
|
||||
"@types/js-yaml": "4.0.9",
|
||||
"@types/jsonld": "1.5.15",
|
||||
"@types/mime-types": "3.0.1",
|
||||
"@types/ms": "2.1.0",
|
||||
"@types/node": "24.12.0",
|
||||
"@types/nodemailer": "7.0.11",
|
||||
"@types/node": "24.12.2",
|
||||
"@types/nodemailer": "8.0.0",
|
||||
"@types/oauth2orize": "1.11.5",
|
||||
"@types/oauth2orize-pkce": "0.1.2",
|
||||
"@types/pg": "8.18.0",
|
||||
"@types/pg": "8.20.0",
|
||||
"@types/qrcode": "1.5.6",
|
||||
"@types/random-seed": "0.3.5",
|
||||
"@types/ratelimiter": "3.4.6",
|
||||
@@ -206,28 +185,28 @@
|
||||
"@types/semver": "7.7.1",
|
||||
"@types/simple-oauth2": "5.0.8",
|
||||
"@types/sinonjs__fake-timers": "15.0.1",
|
||||
"@types/supertest": "6.0.3",
|
||||
"@types/supertest": "7.2.0",
|
||||
"@types/tinycolor2": "1.4.6",
|
||||
"@types/tmp": "0.2.6",
|
||||
"@types/vary": "1.1.3",
|
||||
"@types/web-push": "3.6.4",
|
||||
"@types/ws": "8.18.1",
|
||||
"@typescript-eslint/eslint-plugin": "8.57.0",
|
||||
"@typescript-eslint/parser": "8.57.0",
|
||||
"@typescript-eslint/eslint-plugin": "8.59.0",
|
||||
"@typescript-eslint/parser": "8.59.0",
|
||||
"@vitest/coverage-v8": "4.1.5",
|
||||
"aws-sdk-client-mock": "4.1.0",
|
||||
"cbor": "10.0.12",
|
||||
"cross-env": "10.1.0",
|
||||
"esbuild-plugin-swc": "1.0.1",
|
||||
"eslint-plugin-import": "2.32.0",
|
||||
"execa": "9.6.1",
|
||||
"fkill": "10.0.3",
|
||||
"jest": "29.7.0",
|
||||
"jest-mock": "29.7.0",
|
||||
"js-yaml": "4.1.1",
|
||||
"nodemon": "3.1.14",
|
||||
"pid-port": "2.0.1",
|
||||
"pid-port": "2.1.1",
|
||||
"rolldown": "1.0.0-rc.15",
|
||||
"simple-oauth2": "5.1.0",
|
||||
"supertest": "7.2.2",
|
||||
"vite": "7.3.1"
|
||||
"vite": "8.0.10",
|
||||
"vitest": "4.1.5",
|
||||
"vitest-mock-extended": "4.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
128
packages/backend/rolldown.config.ts
Normal file
128
packages/backend/rolldown.config.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
import { defineConfig } from 'rolldown';
|
||||
import type { Plugin, ExternalOption } from 'rolldown';
|
||||
import { execa, execaNode } from 'execa';
|
||||
import type { ResultPromise } from 'execa';
|
||||
import esmShim from '@rollup/plugin-esm-shim';
|
||||
|
||||
/**
|
||||
* Watchモード時にバックエンドの起動・停止制御を行うプラグイン
|
||||
*/
|
||||
function backendDevServerPlugin(): Plugin {
|
||||
let backendProcess: ResultPromise | null = null;
|
||||
|
||||
async function runBuildAssets() {
|
||||
await execa('pnpm', ['run', 'build-assets'], {
|
||||
cwd: '../../',
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
});
|
||||
}
|
||||
|
||||
async function killBackendProcess() {
|
||||
if (backendProcess) {
|
||||
backendProcess.catch(() => {}); // backendProcess.kill()によって発生する例外を無視するためにcatch()を呼び出す
|
||||
backendProcess.kill();
|
||||
await new Promise(resolve => backendProcess!.on('exit', resolve));
|
||||
backendProcess = null;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
name: 'backend-dev-server',
|
||||
async closeBundle() {
|
||||
await runBuildAssets();
|
||||
if (backendProcess) {
|
||||
await killBackendProcess();
|
||||
}
|
||||
backendProcess = execaNode('./built/entry.js', [], {
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
env: {
|
||||
NODE_ENV: 'development',
|
||||
},
|
||||
});
|
||||
},
|
||||
async watchChange() {
|
||||
if (backendProcess) {
|
||||
await killBackendProcess();
|
||||
await runBuildAssets();
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default defineConfig((args) => {
|
||||
const isWatchMode = args.watch != null && args.watch !== 'false';
|
||||
const isE2E = args.e2e != null && args.e2e !== 'false';
|
||||
|
||||
// 通常のビルド時にexternalとするモジュール
|
||||
const externalModules: ExternalOption = [
|
||||
/^slacc-.*/,
|
||||
'class-transformer',
|
||||
'class-validator',
|
||||
/^@sentry\/.*/,
|
||||
/^@sentry-internal\/.*/,
|
||||
'@nestjs/websockets/socket-module',
|
||||
'@nestjs/microservices/microservices-module',
|
||||
'@nestjs/microservices',
|
||||
/^@napi-rs\/.*/,
|
||||
'mock-aws-s3',
|
||||
'aws-sdk',
|
||||
'nock',
|
||||
'sharp',
|
||||
'jsdom',
|
||||
're2',
|
||||
'ipaddr.js',
|
||||
'oauth2orize',
|
||||
'file-type',
|
||||
];
|
||||
|
||||
if (isE2E) {
|
||||
return {
|
||||
input: './test-server/entry.ts',
|
||||
platform: 'node',
|
||||
tsconfig: './test-server/tsconfig.json',
|
||||
plugins: [
|
||||
esmShim(),
|
||||
],
|
||||
output: {
|
||||
keepNames: true,
|
||||
sourcemap: true,
|
||||
dir: './built-test',
|
||||
cleanDir: true,
|
||||
format: 'esm',
|
||||
},
|
||||
external: externalModules,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
input: [
|
||||
'./src/boot/entry.ts',
|
||||
'./src/boot/cli.ts',
|
||||
'./src/config.ts',
|
||||
'./src/postgres.ts',
|
||||
'./src/server/api/openapi/gen-spec.ts',
|
||||
],
|
||||
platform: 'node',
|
||||
tsconfig: true,
|
||||
plugins: [
|
||||
esmShim(),
|
||||
(isWatchMode ? backendDevServerPlugin() : undefined),
|
||||
],
|
||||
output: {
|
||||
keepNames: true,
|
||||
minify: !isWatchMode,
|
||||
sourcemap: isWatchMode,
|
||||
dir: './built',
|
||||
cleanDir: !isWatchMode,
|
||||
format: 'esm',
|
||||
},
|
||||
watch: {
|
||||
include: ['src/**/*.{ts,js,mjs,cjs,tsx,json}'],
|
||||
clearScreen: false,
|
||||
},
|
||||
// ビルドの高速化のために、watchモードのときは外部モジュールは全てバンドルしないようにする
|
||||
external: isWatchMode ? /^(?!@\/)[^.\/](?!:[\/\\])/ : externalModules,
|
||||
};
|
||||
}
|
||||
});
|
||||
@@ -4,8 +4,8 @@
|
||||
*/
|
||||
|
||||
import Redis from 'ioredis';
|
||||
import { loadConfig } from '../src-js/config.js';
|
||||
import { createPostgresDataSource } from '../src-js/postgres.js';
|
||||
import { loadConfig } from '../built/config.js';
|
||||
import { createPostgresDataSource } from '../built/postgres.js';
|
||||
|
||||
const config = loadConfig();
|
||||
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { execa, execaNode } from 'execa';
|
||||
|
||||
/** @type {import('execa').ExecaChildProcess | undefined} */
|
||||
let backendProcess;
|
||||
|
||||
async function execBuildAssets() {
|
||||
await execa('pnpm', ['run', 'build-assets'], {
|
||||
cwd: '../../',
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
})
|
||||
}
|
||||
|
||||
function execStart() {
|
||||
// pnpm run start を呼び出したいが、windowsだとプロセスグループ単位でのkillが出来ずゾンビプロセス化するので
|
||||
// 上記と同等の動きをするコマンドで子・孫プロセスを作らないようにしたい
|
||||
backendProcess = execaNode('./built/boot/entry.js', [], {
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
env: {
|
||||
'NODE_ENV': 'development',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function killProc() {
|
||||
if (backendProcess) {
|
||||
backendProcess.catch(() => {}); // backendProcess.kill()によって発生する例外を無視するためにcatch()を呼び出す
|
||||
backendProcess.kill();
|
||||
await new Promise(resolve => backendProcess.on('exit', resolve));
|
||||
backendProcess = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
(async () => {
|
||||
execaNode(
|
||||
'./node_modules/nodemon/bin/nodemon.js',
|
||||
[
|
||||
'-w', 'src',
|
||||
'-e', 'ts,js,mjs,cjs,tsx,json,pug',
|
||||
'--exec', 'pnpm', 'run', 'build',
|
||||
],
|
||||
{
|
||||
stdio: [process.stdin, process.stdout, process.stderr, 'ipc'],
|
||||
serialization: "json",
|
||||
})
|
||||
.on('message', async (message) => {
|
||||
if (message.type === 'exit') {
|
||||
// かならずbuild->build-assetsの順番で呼び出したいので、
|
||||
// 少々トリッキーだがnodemonからのexitイベントを利用してbuild-assets->startを行う。
|
||||
// pnpm restartをbuildが終わる前にbuild-assetsが動いてしまうので、バラバラに呼び出す必要がある
|
||||
|
||||
await killProc();
|
||||
await execBuildAssets();
|
||||
execStart();
|
||||
}
|
||||
})
|
||||
})();
|
||||
@@ -19,10 +19,10 @@ async function main() {
|
||||
}
|
||||
|
||||
/** @type {import('../src/config.js')} */
|
||||
const { loadConfig } = await import('../src-js/config.js');
|
||||
const { loadConfig } = await import('../built/config.js');
|
||||
|
||||
/** @type {import('../src/server/api/openapi/gen-spec.js')} */
|
||||
const { genOpenapiSpec } = await import('../src-js/server/api/openapi/gen-spec.js');
|
||||
const { genOpenapiSpec } = await import('../built/gen-spec.js');
|
||||
|
||||
const config = loadConfig();
|
||||
const spec = genOpenapiSpec(config, true);
|
||||
|
||||
@@ -55,7 +55,7 @@ async function getMemoryUsage(pid) {
|
||||
|
||||
async function measureMemory() {
|
||||
// Start the Misskey backend server using fork to enable IPC
|
||||
const serverProcess = fork(join(__dirname, '../built/boot/entry.js'), ['expose-gc'], {
|
||||
const serverProcess = fork(join(__dirname, '../built/entry.js'), ['expose-gc'], {
|
||||
cwd: join(__dirname, '..'),
|
||||
env: {
|
||||
...process.env,
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { Global, Inject, Module } from '@nestjs/common';
|
||||
import * as Redis from 'ioredis';
|
||||
import { DataSource } from 'typeorm';
|
||||
import { MeiliSearch } from 'meilisearch';
|
||||
import { Meilisearch } from 'meilisearch';
|
||||
import { MiMeta } from '@/models/Meta.js';
|
||||
import { DI } from './di-symbols.js';
|
||||
import { Config, loadConfig } from './config.js';
|
||||
@@ -40,10 +40,10 @@ const $meilisearch: Provider = {
|
||||
useFactory: (config: Config) => {
|
||||
if (config.fulltextSearch?.provider === 'meilisearch') {
|
||||
if (!config.meilisearch) {
|
||||
throw new Error('MeiliSearch is enabled but no configuration is provided');
|
||||
throw new Error('Meilisearch is enabled but no configuration is provided');
|
||||
}
|
||||
|
||||
return new MeiliSearch({
|
||||
return new Meilisearch({
|
||||
host: `${config.meilisearch.ssl ? 'https' : 'http'}://${config.meilisearch.host}:${config.meilisearch.port}`,
|
||||
apiKey: config.meilisearch.apiKey,
|
||||
});
|
||||
|
||||
@@ -4,16 +4,12 @@
|
||||
*/
|
||||
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { ChartManagementService } from '@/core/chart/ChartManagementService.js';
|
||||
import { QueueProcessorService } from '@/queue/QueueProcessorService.js';
|
||||
import { NestLogger } from '@/NestLogger.js';
|
||||
import { QueueProcessorModule } from '@/queue/QueueProcessorModule.js';
|
||||
import { QueueStatsService } from '@/daemons/QueueStatsService.js';
|
||||
import { ServerStatsService } from '@/daemons/ServerStatsService.js';
|
||||
import { ServerService } from '@/server/ServerService.js';
|
||||
import { MainModule } from '@/MainModule.js';
|
||||
|
||||
export async function server() {
|
||||
const { MainModule } = await import('../MainModule.js');
|
||||
const { ServerService } = await import('../server/ServerService.js');
|
||||
|
||||
const app = await NestFactory.createApplicationContext(MainModule, {
|
||||
logger: new NestLogger(),
|
||||
});
|
||||
@@ -22,6 +18,10 @@ export async function server() {
|
||||
await serverService.launch();
|
||||
|
||||
if (process.env.NODE_ENV !== 'test') {
|
||||
const { ChartManagementService } = await import('../core/chart/ChartManagementService.js');
|
||||
const { QueueStatsService } = await import('../daemons/QueueStatsService.js');
|
||||
const { ServerStatsService } = await import('../daemons/ServerStatsService.js');
|
||||
|
||||
app.get(ChartManagementService).start();
|
||||
app.get(QueueStatsService).start();
|
||||
app.get(ServerStatsService).start();
|
||||
@@ -31,6 +31,10 @@ export async function server() {
|
||||
}
|
||||
|
||||
export async function jobQueue() {
|
||||
const { QueueProcessorModule } = await import('../queue/QueueProcessorModule.js');
|
||||
const { QueueProcessorService } = await import('../queue/QueueProcessorService.js');
|
||||
const { ChartManagementService } = await import('../core/chart/ChartManagementService.js');
|
||||
|
||||
const jobQueue = await NestFactory.createApplicationContext(QueueProcessorModule, {
|
||||
logger: new NestLogger(),
|
||||
});
|
||||
|
||||
@@ -13,8 +13,6 @@ import chalk from 'chalk';
|
||||
import Xev from 'xev';
|
||||
import Logger from '@/logger.js';
|
||||
import { envOption } from '../env.js';
|
||||
import { masterMain } from './master.js';
|
||||
import { workerMain } from './worker.js';
|
||||
import { readyRef } from './ready.js';
|
||||
|
||||
import 'reflect-metadata';
|
||||
@@ -71,10 +69,12 @@ process.on('exit', code => {
|
||||
if (!envOption.disableClustering) {
|
||||
if (cluster.isPrimary) {
|
||||
logger.info(`Start main process... pid: ${process.pid}`);
|
||||
const { masterMain } = await import('./master.js');
|
||||
await masterMain();
|
||||
ev.mount();
|
||||
} else if (cluster.isWorker) {
|
||||
logger.info(`Start worker process... pid: ${process.pid}`);
|
||||
const { workerMain } = await import('./worker.js');
|
||||
await workerMain();
|
||||
} else {
|
||||
throw new Error('Unknown process type');
|
||||
@@ -82,6 +82,7 @@ if (!envOption.disableClustering) {
|
||||
} else {
|
||||
// 非clusterの場合はMasterのみが起動するため、Workerの処理は行わない(cluster.isWorker === trueの状態でこのブロックに来ることはない)
|
||||
logger.info(`Start main process... pid: ${process.pid}`);
|
||||
const { masterMain } = await import('./master.js');
|
||||
await masterMain();
|
||||
ev.mount();
|
||||
}
|
||||
|
||||
@@ -190,6 +190,7 @@ export type Config = {
|
||||
userAgent: string;
|
||||
frontendManifestExists: boolean;
|
||||
frontendEmbedManifestExists: boolean;
|
||||
rootDir: string;
|
||||
mediaProxy: string;
|
||||
externalMediaProxyEnabled: boolean;
|
||||
videoThumbnailGenerator: string | null;
|
||||
@@ -330,6 +331,7 @@ export function loadConfig(): Config {
|
||||
userAgent: `Misskey/${version} (${config.url})`,
|
||||
frontendManifestExists: frontendManifestExists,
|
||||
frontendEmbedManifestExists: frontendEmbedManifestExists,
|
||||
rootDir,
|
||||
perChannelMaxNoteCacheCount: config.perChannelMaxNoteCacheCount ?? 1000,
|
||||
perUserNotificationsMaxCount: config.perUserNotificationsMaxCount ?? 500,
|
||||
deactivateAntennaThreshold: config.deactivateAntennaThreshold ?? (1000 * 60 * 60 * 24 * 7),
|
||||
|
||||
@@ -4,27 +4,31 @@
|
||||
*/
|
||||
|
||||
import * as fs from 'node:fs';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { dirname } from 'node:path';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
import { resolve } from 'node:path';
|
||||
import { Injectable, Inject } from '@nestjs/common';
|
||||
import { Mutex } from 'async-mutex';
|
||||
import fetch from 'node-fetch';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import type { NSFWJS, PredictionType } from 'nsfwjs';
|
||||
|
||||
const _filename = fileURLToPath(import.meta.url);
|
||||
const _dirname = dirname(_filename);
|
||||
import type { Config } from '@/config.js';
|
||||
import type { NSFWJS, PredictionType } from 'nsfwjs/core';
|
||||
|
||||
const REQUIRED_CPU_FLAGS_X64 = ['avx2', 'fma'];
|
||||
let isSupportedCpu: undefined | boolean = undefined;
|
||||
|
||||
@Injectable()
|
||||
export class AiService {
|
||||
private readonly modelDir: string;
|
||||
private model: NSFWJS;
|
||||
private modelLoadMutex: Mutex = new Mutex();
|
||||
|
||||
constructor(
|
||||
@Inject(DI.config)
|
||||
private config: Config,
|
||||
) {
|
||||
const md = resolve(this.config.rootDir, 'packages/backend/nsfw-model');
|
||||
this.modelDir = md.endsWith('/') ? md : md + '/';
|
||||
}
|
||||
|
||||
@bindThis
|
||||
@@ -43,10 +47,10 @@ export class AiService {
|
||||
tf.env().global.fetch = fetch;
|
||||
|
||||
if (this.model == null) {
|
||||
const nsfw = await import('nsfwjs');
|
||||
const nsfw = await import('nsfwjs/core');
|
||||
await this.modelLoadMutex.runExclusive(async () => {
|
||||
if (this.model == null) {
|
||||
this.model = await nsfw.load(`file://${_dirname}/../../nsfw-model/`, { size: 299 });
|
||||
this.model = await nsfw.load(pathToFileURL(this.modelDir).toString(), { size: 299 });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,29 +5,25 @@
|
||||
|
||||
import * as fs from 'node:fs';
|
||||
import * as Path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { dirname } from 'node:path';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type { Config } from '@/config.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
|
||||
const _filename = fileURLToPath(import.meta.url);
|
||||
const _dirname = dirname(_filename);
|
||||
|
||||
const path = Path.resolve(_dirname, '../../../../files');
|
||||
|
||||
@Injectable()
|
||||
export class InternalStorageService {
|
||||
private readonly path: string;
|
||||
|
||||
constructor(
|
||||
@Inject(DI.config)
|
||||
private config: Config,
|
||||
) {
|
||||
this.path = Path.resolve(this.config.rootDir, 'files');
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public resolvePath(key: string) {
|
||||
return Path.resolve(path, key);
|
||||
return Path.resolve(this.path, key);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
@@ -37,14 +33,14 @@ export class InternalStorageService {
|
||||
|
||||
@bindThis
|
||||
public saveFromPath(key: string, srcPath: string) {
|
||||
fs.mkdirSync(path, { recursive: true });
|
||||
fs.mkdirSync(this.path, { recursive: true });
|
||||
fs.copyFileSync(srcPath, this.resolvePath(key));
|
||||
return `${this.config.url}/files/${key}`;
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public saveFromBuffer(key: string, data: Buffer) {
|
||||
fs.mkdirSync(path, { recursive: true });
|
||||
fs.mkdirSync(this.path, { recursive: true });
|
||||
fs.writeFileSync(this.resolvePath(key), data);
|
||||
return `${this.config.url}/files/${key}`;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import Logger from '@/logger.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import type { KEYWORD } from 'color-convert/conversions.js';
|
||||
import type { Keyword } from 'color-convert';
|
||||
|
||||
@Injectable()
|
||||
export class LoggerService {
|
||||
@@ -15,7 +15,7 @@ export class LoggerService {
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public getLogger(domain: string, color?: KEYWORD | undefined) {
|
||||
public getLogger(domain: string, color?: Keyword | undefined) {
|
||||
return new Logger(domain, color);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,20 +63,21 @@ type NotificationType = 'reply' | 'renote' | 'quote' | 'mention';
|
||||
class NotificationManager {
|
||||
private notifier: { id: MiUser['id']; };
|
||||
private note: MiNote;
|
||||
private queue: {
|
||||
private queue: Map<MiLocalUser['id'], {
|
||||
target: MiLocalUser['id'];
|
||||
reason: NotificationType;
|
||||
}[];
|
||||
}>;
|
||||
|
||||
constructor(
|
||||
private mutingsRepository: MutingsRepository,
|
||||
private notificationService: NotificationService,
|
||||
private followingsRepository: FollowingsRepository,
|
||||
notifier: { id: MiUser['id']; },
|
||||
note: MiNote,
|
||||
) {
|
||||
this.notifier = notifier;
|
||||
this.note = note;
|
||||
this.queue = [];
|
||||
this.queue = new Map();
|
||||
}
|
||||
|
||||
@bindThis
|
||||
@@ -84,7 +85,7 @@ class NotificationManager {
|
||||
// 自分自身へは通知しない
|
||||
if (this.notifier.id === notifiee) return;
|
||||
|
||||
const exist = this.queue.find(x => x.target === notifiee);
|
||||
const exist = this.queue.get(notifiee);
|
||||
|
||||
if (exist) {
|
||||
// 「メンションされているかつ返信されている」場合は、メンションとしての通知ではなく返信としての通知にする
|
||||
@@ -92,7 +93,7 @@ class NotificationManager {
|
||||
exist.reason = reason;
|
||||
}
|
||||
} else {
|
||||
this.queue.push({
|
||||
this.queue.set(notifiee, {
|
||||
reason: reason,
|
||||
target: notifiee,
|
||||
});
|
||||
@@ -101,7 +102,50 @@ class NotificationManager {
|
||||
|
||||
@bindThis
|
||||
public async notify() {
|
||||
for (const x of this.queue) {
|
||||
if (this.queue.size === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let visibleUserIds: Set<MiUser['id']> | null;
|
||||
|
||||
switch (this.note.visibility) {
|
||||
case 'public':
|
||||
case 'home':
|
||||
visibleUserIds = null;
|
||||
break;
|
||||
|
||||
case 'specified':
|
||||
visibleUserIds = new Set(this.note.visibleUserIds);
|
||||
break;
|
||||
|
||||
case 'followers': {
|
||||
// TODO: フォロワー限定ノートにフォロワーではない人がメンションされた場合通知されるのが正しい挙動なのか確認(一部に挙動の不一致がありそう)。現状は通知されるためフィルタしない
|
||||
// const targetUserIds = this.queue.map(x => x.target);
|
||||
// const followers = await this.followingsRepository.find({
|
||||
// where: {
|
||||
// followeeId: this.note.userId,
|
||||
// followerId: In(targetUserIds),
|
||||
// isFollowerHibernated: false,
|
||||
// },
|
||||
// select: ['followerId'],
|
||||
// });
|
||||
// visibleUserIds = new Set(followers.map(f => f.followerId));
|
||||
visibleUserIds = null;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
visibleUserIds = new Set();
|
||||
break;
|
||||
}
|
||||
|
||||
for (const x of this.queue.values()) {
|
||||
const isVisibleToTarget = visibleUserIds === null || visibleUserIds.has(x.target);
|
||||
|
||||
if (!isVisibleToTarget) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (x.reason === 'renote') {
|
||||
this.notificationService.createNotification(x.target, 'renote', {
|
||||
noteId: this.note.id,
|
||||
@@ -772,7 +816,7 @@ export class NoteCreateService implements OnApplicationShutdown {
|
||||
|
||||
this.webhookService.enqueueUserWebhook(user.id, 'note', { note: noteObj });
|
||||
|
||||
const nm = new NotificationManager(this.mutingsRepository, this.notificationService, user, note);
|
||||
const nm = new NotificationManager(this.mutingsRepository, this.notificationService, this.followingsRepository, user, note);
|
||||
|
||||
await this.createMentionedEvents(mentionedUsers, note, nm);
|
||||
|
||||
|
||||
@@ -246,7 +246,8 @@ export class NotificationService implements OnApplicationShutdown {
|
||||
|
||||
private toXListId(id: string): string {
|
||||
const { date, additional } = this.idService.parseFull(id);
|
||||
return date.toString() + '-' + additional.toString();
|
||||
// Redis Stream sequenceはunit64制約があるため、収まらない場合は下位64bitを取る
|
||||
return date.toString() + '-' + BigInt.asUintN(64, additional).toString();
|
||||
}
|
||||
|
||||
@bindThis
|
||||
|
||||
@@ -91,13 +91,27 @@ export class RelayService {
|
||||
return JSON.stringify(result);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
private getAcceptedRelays(): Promise<MiRelay[]> {
|
||||
return this.relaysCache.fetch(() => this.relaysRepository.findBy({
|
||||
status: 'accepted',
|
||||
}));
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async isRelayActor(actor: { inbox: string | null; sharedInbox: string | null }): Promise<boolean> {
|
||||
const relays = await this.getAcceptedRelays();
|
||||
return relays.some(relay =>
|
||||
(actor.inbox != null && relay.inbox === actor.inbox)
|
||||
|| (actor.sharedInbox != null && relay.inbox === actor.sharedInbox),
|
||||
);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async deliverToRelays(user: { id: MiUser['id']; host: null; }, activity: any): Promise<void> {
|
||||
if (activity == null) return;
|
||||
|
||||
const relays = await this.relaysCache.fetch(() => this.relaysRepository.findBy({
|
||||
status: 'accepted',
|
||||
}));
|
||||
const relays = await this.getAcceptedRelays();
|
||||
if (relays.length === 0) return;
|
||||
|
||||
const copy = deepClone(activity);
|
||||
|
||||
@@ -47,6 +47,7 @@ export type RolePolicies = {
|
||||
canSearchUsers: boolean;
|
||||
canUseTranslator: boolean;
|
||||
canHideAds: boolean;
|
||||
canCreateChannel: boolean;
|
||||
driveCapacityMb: number;
|
||||
maxFileSizeMb: number;
|
||||
alwaysMarkNsfw: boolean;
|
||||
@@ -88,6 +89,7 @@ export const DEFAULT_POLICIES: RolePolicies = {
|
||||
canSearchUsers: true,
|
||||
canUseTranslator: true,
|
||||
canHideAds: false,
|
||||
canCreateChannel: true,
|
||||
driveCapacityMb: 100,
|
||||
maxFileSizeMb: 30,
|
||||
alwaysMarkNsfw: false,
|
||||
@@ -410,6 +412,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
|
||||
canSearchUsers: calc('canSearchUsers', vs => vs.some(v => v === true)),
|
||||
canUseTranslator: calc('canUseTranslator', vs => vs.some(v => v === true)),
|
||||
canHideAds: calc('canHideAds', vs => vs.some(v => v === true)),
|
||||
canCreateChannel: calc('canCreateChannel', vs => vs.some(v => v === true)),
|
||||
driveCapacityMb: calc('driveCapacityMb', vs => Math.max(...vs)),
|
||||
maxFileSizeMb: calc('maxFileSizeMb', vs => Math.max(...vs)),
|
||||
alwaysMarkNsfw: calc('alwaysMarkNsfw', vs => vs.some(v => v === true)),
|
||||
@@ -533,7 +536,8 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
|
||||
roleId: In(administratorRoles.map(r => r.id)),
|
||||
}) : [];
|
||||
// TODO: isRootなアカウントも含める
|
||||
return assigns.map(a => a.userId);
|
||||
// Setを経由して重複を除去(ユーザIDは重複する可能性があるので)
|
||||
return [...new Set(assigns.map(a => a.userId))].sort((x, y) => x.localeCompare(y));
|
||||
}
|
||||
|
||||
@bindThis
|
||||
|
||||
@@ -17,7 +17,7 @@ import { CacheService } from '@/core/CacheService.js';
|
||||
import { QueryService } from '@/core/QueryService.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { LoggerService } from '@/core/LoggerService.js';
|
||||
import type { Index, MeiliSearch } from 'meilisearch';
|
||||
import type { Index, Meilisearch } from 'meilisearch';
|
||||
|
||||
type K = string;
|
||||
type V = string | number | boolean;
|
||||
@@ -85,7 +85,7 @@ export class SearchService {
|
||||
private config: Config,
|
||||
|
||||
@Inject(DI.meilisearch)
|
||||
private meilisearch: MeiliSearch | null,
|
||||
private meilisearch: Meilisearch | null,
|
||||
|
||||
@Inject(DI.notesRepository)
|
||||
private notesRepository: NotesRepository,
|
||||
@@ -187,7 +187,7 @@ export class SearchService {
|
||||
return this.searchNoteByLike(q, me, opts, pagination);
|
||||
}
|
||||
case 'meilisearch': {
|
||||
return this.searchNoteByMeiliSearch(q, me, opts, pagination);
|
||||
return this.searchNoteByMeilisearch(q, me, opts, pagination);
|
||||
}
|
||||
default: {
|
||||
const _: never = this.provider;
|
||||
@@ -239,14 +239,14 @@ export class SearchService {
|
||||
}
|
||||
|
||||
@bindThis
|
||||
private async searchNoteByMeiliSearch(
|
||||
private async searchNoteByMeilisearch(
|
||||
q: string,
|
||||
me: MiUser | null,
|
||||
opts: SearchOpts,
|
||||
pagination: SearchPagination,
|
||||
): Promise<MiNote[]> {
|
||||
if (!this.meilisearch || !this.meilisearchNoteIndex) {
|
||||
throw new Error('MeiliSearch is not available');
|
||||
throw new Error('Meilisearch is not available');
|
||||
}
|
||||
|
||||
const filter: Q = {
|
||||
|
||||
@@ -164,4 +164,3 @@ export class SignupService {
|
||||
return { account, secret };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import type {
|
||||
PublicKeyCredentialCreationOptionsJSON,
|
||||
PublicKeyCredentialRequestOptionsJSON,
|
||||
RegistrationResponseJSON,
|
||||
} from '@simplewebauthn/types';
|
||||
} from '@simplewebauthn/server';
|
||||
|
||||
@Injectable()
|
||||
export class WebAuthnService {
|
||||
|
||||
@@ -259,7 +259,7 @@ export class ApInboxService {
|
||||
|
||||
@bindThis
|
||||
private async add(actor: MiRemoteUser, activity: IAdd, resolver?: Resolver): Promise<string | void> {
|
||||
if (actor.uri !== activity.actor) {
|
||||
if (actor.uri !== getApId(activity.actor)) {
|
||||
return 'invalid actor';
|
||||
}
|
||||
|
||||
@@ -302,16 +302,19 @@ export class ApInboxService {
|
||||
|
||||
@bindThis
|
||||
private async announceNote(actor: MiRemoteUser, activity: IAnnounce, target: IPost, resolver?: Resolver): Promise<string | void> {
|
||||
const uri = getApId(activity);
|
||||
|
||||
if (actor.isSuspended) {
|
||||
return;
|
||||
}
|
||||
|
||||
// リレーからのAnnounceかチェック
|
||||
const fromRelay = await this.relayService.isRelayActor(actor);
|
||||
const uri = getApId(fromRelay ? target : activity);
|
||||
|
||||
// アナウンス先が許可されているかチェック
|
||||
if (!this.utilityService.isFederationAllowedUri(uri)) return;
|
||||
|
||||
const unlock = await acquireApObjectLock(this.redisClient, uri);
|
||||
const activityUri = getApId(activity);
|
||||
const unlock = await acquireApObjectLock(this.redisClient, activityUri);
|
||||
|
||||
try {
|
||||
// 既に同じURIを持つものが登録されていないかチェック
|
||||
@@ -336,6 +339,14 @@ export class ApInboxService {
|
||||
throw err;
|
||||
}
|
||||
|
||||
// リレーからのAnnounceはリノートを作成せず、ノートを直接公開する
|
||||
if (fromRelay) {
|
||||
this.logger.info(`Publishing relay-delivered note: ${uri}`);
|
||||
const noteObj = await this.noteEntityService.pack(renote, null, { skipHide: true, withReactionAndUserPairCache: true });
|
||||
this.globalEventService.publishNotesStream(noteObj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!await this.noteEntityService.isVisibleForMe(renote, actor.id)) {
|
||||
return 'skip: invalid actor for this activity';
|
||||
}
|
||||
@@ -459,7 +470,7 @@ export class ApInboxService {
|
||||
|
||||
@bindThis
|
||||
private async delete(actor: MiRemoteUser, activity: IDelete): Promise<string> {
|
||||
if (actor.uri !== activity.actor) {
|
||||
if (actor.uri !== getApId(activity.actor)) {
|
||||
return 'invalid actor';
|
||||
}
|
||||
|
||||
@@ -613,7 +624,7 @@ export class ApInboxService {
|
||||
|
||||
@bindThis
|
||||
private async remove(actor: MiRemoteUser, activity: IRemove, resolver?: Resolver): Promise<string | void> {
|
||||
if (actor.uri !== activity.actor) {
|
||||
if (actor.uri !== getApId(activity.actor)) {
|
||||
return 'invalid actor';
|
||||
}
|
||||
|
||||
@@ -633,7 +644,7 @@ export class ApInboxService {
|
||||
|
||||
@bindThis
|
||||
private async undo(actor: MiRemoteUser, activity: IUndo, resolver?: Resolver): Promise<string> {
|
||||
if (actor.uri !== activity.actor) {
|
||||
if (actor.uri !== getApId(activity.actor)) {
|
||||
return 'invalid actor';
|
||||
}
|
||||
|
||||
@@ -767,7 +778,7 @@ export class ApInboxService {
|
||||
|
||||
@bindThis
|
||||
private async update(actor: MiRemoteUser, activity: IUpdate, resolver?: Resolver): Promise<string> {
|
||||
if (actor.uri !== activity.actor) {
|
||||
if (actor.uri !== getApId(activity.actor)) {
|
||||
return 'skip: invalid actor';
|
||||
}
|
||||
|
||||
|
||||
@@ -376,7 +376,7 @@ export class ApPersonService implements OnModuleInit {
|
||||
isLocked: person.manuallyApprovesFollowers,
|
||||
movedToUri: person.movedTo,
|
||||
movedAt: person.movedTo ? new Date() : null,
|
||||
alsoKnownAs: person.alsoKnownAs,
|
||||
alsoKnownAs: toArray(person.alsoKnownAs),
|
||||
isExplorable: person.discoverable,
|
||||
username: person.preferredUsername,
|
||||
usernameLower: person.preferredUsername?.toLowerCase(),
|
||||
@@ -568,7 +568,7 @@ export class ApPersonService implements OnModuleInit {
|
||||
isCat: (person as any).isCat === true,
|
||||
isLocked: person.manuallyApprovesFollowers,
|
||||
movedToUri: person.movedTo ?? null,
|
||||
alsoKnownAs: person.alsoKnownAs ?? null,
|
||||
alsoKnownAs: person.alsoKnownAs ? toArray(person.alsoKnownAs) : null,
|
||||
isExplorable: person.discoverable,
|
||||
...(await this.resolveAvatarAndBanner(exist, person.icon, person.image).catch(() => ({}))),
|
||||
} as Partial<MiRemoteUser> & Pick<MiRemoteUser, 'isBot' | 'isCat' | 'isLocked' | 'movedToUri' | 'alsoKnownAs' | 'isExplorable'>;
|
||||
|
||||
@@ -132,7 +132,7 @@ export class MetaEntityService {
|
||||
sentryForFrontend: this.config.sentryForFrontend ?? null,
|
||||
mediaProxy: this.config.mediaProxy,
|
||||
enableUrlPreview: instance.urlPreviewEnabled,
|
||||
noteSearchableScope: (this.config.meilisearch == null || this.config.meilisearch.scope !== 'local') ? 'global' : 'local',
|
||||
noteSearchableScope: (this.config.fulltextSearch?.provider === 'meilisearch' && this.config.meilisearch?.scope === 'local') ? 'local' : 'global',
|
||||
maxFileSize: this.config.maxFileSize,
|
||||
federation: this.meta.federation,
|
||||
};
|
||||
|
||||
@@ -51,6 +51,7 @@ import { ChatService } from '@/core/ChatService.js';
|
||||
import type { OnModuleInit } from '@nestjs/common';
|
||||
import type { NoteEntityService } from './NoteEntityService.js';
|
||||
import type { PageEntityService } from './PageEntityService.js';
|
||||
import { toArray } from '@/misc/prelude/array.js';
|
||||
|
||||
const Ajv = _Ajv.default;
|
||||
const ajv = new Ajv();
|
||||
@@ -527,10 +528,10 @@ export class UserEntityService implements OnModuleInit {
|
||||
url: profile!.url,
|
||||
uri: user.uri,
|
||||
movedTo: user.movedToUri ? this.apPersonService.resolvePerson(user.movedToUri).then(user => user.id).catch(() => null) : null,
|
||||
alsoKnownAs: user.alsoKnownAs
|
||||
? Promise.all(user.alsoKnownAs.map(uri => this.apPersonService.fetchPerson(uri).then(user => user?.id).catch(() => null)))
|
||||
.then(xs => xs.length === 0 ? null : xs.filter(x => x != null))
|
||||
: null,
|
||||
alsoKnownAs: user.alsoKnownAs ?
|
||||
Promise.all(toArray(user.alsoKnownAs).map(uri => this.apPersonService.fetchPerson(uri).then(user => user?.id).catch(() => null)))
|
||||
.then(xs => xs.length === 0 ? null : xs.filter(x => x != null))
|
||||
: null,
|
||||
createdAt: this.idService.parse(user.id).date.toISOString(),
|
||||
updatedAt: user.updatedAt ? user.updatedAt.toISOString() : null,
|
||||
lastFetchedAt: user.lastFetchedAt ? user.lastFetchedAt.toISOString() : null,
|
||||
|
||||
@@ -9,11 +9,11 @@ import { default as convertColor } from 'color-convert';
|
||||
import { format as dateFormat } from 'date-fns';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { envOption } from './env.js';
|
||||
import type { KEYWORD } from 'color-convert/conversions.js';
|
||||
import type { Keyword } from 'color-convert';
|
||||
|
||||
type Context = {
|
||||
name: string;
|
||||
color?: KEYWORD;
|
||||
color?: Keyword;
|
||||
};
|
||||
|
||||
type Level = 'error' | 'success' | 'warning' | 'debug' | 'info';
|
||||
@@ -23,7 +23,7 @@ export default class Logger {
|
||||
private context: Context;
|
||||
private parentLogger: Logger | null = null;
|
||||
|
||||
constructor(context: string, color?: KEYWORD) {
|
||||
constructor(context: string, color?: Keyword) {
|
||||
this.context = {
|
||||
name: context,
|
||||
color: color,
|
||||
@@ -31,7 +31,7 @@ export default class Logger {
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public createSubLogger(context: string, color?: KEYWORD): Logger {
|
||||
public createSubLogger(context: string, color?: Keyword): Logger {
|
||||
const logger = new Logger(context, color);
|
||||
logger.parentLogger = this;
|
||||
return logger;
|
||||
|
||||
@@ -5,12 +5,19 @@
|
||||
|
||||
// Crockford's Base32
|
||||
// https://github.com/ulid/spec#encoding
|
||||
import { parseBigInt32 } from '@/misc/bigint.js';
|
||||
|
||||
const CHARS = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
|
||||
|
||||
export const ulidRegExp = /^[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26}$/;
|
||||
|
||||
function parseBigIntCrockford(str: string): bigint {
|
||||
let result = 0n;
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
result = result * 32n + BigInt(CHARS.indexOf(str[i]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function parseBase32(timestamp: string) {
|
||||
let time = 0;
|
||||
for (let i = 0; i < timestamp.length; i++) {
|
||||
@@ -26,6 +33,6 @@ export function parseUlid(id: string): { date: Date; } {
|
||||
export function parseUlidFull(id: string): { date: number; additional: bigint; } {
|
||||
return {
|
||||
date: parseBase32(id.slice(0, 10)),
|
||||
additional: parseBigInt32(id.slice(10, 26)),
|
||||
additional: parseBigIntCrockford(id.slice(10, 26)),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -36,4 +36,9 @@ export class MiAvatarDecoration {
|
||||
array: true, length: 128, default: '{}',
|
||||
})
|
||||
public roleIdsThatCanBeUsedThisDecoration: string[];
|
||||
|
||||
@Column('varchar', {
|
||||
length: 128, nullable: true,
|
||||
})
|
||||
public category: string | null;
|
||||
}
|
||||
|
||||
@@ -224,6 +224,10 @@ export const packedRolePoliciesSchema = {
|
||||
type: 'boolean',
|
||||
optional: false, nullable: false,
|
||||
},
|
||||
canCreateChannel: {
|
||||
type: 'boolean',
|
||||
optional: false, nullable: false,
|
||||
},
|
||||
driveCapacityMb: {
|
||||
type: 'integer',
|
||||
optional: false, nullable: false,
|
||||
|
||||
@@ -13,7 +13,7 @@ import { FetchInstanceMetadataService } from '@/core/FetchInstanceMetadataServic
|
||||
import InstanceChart from '@/core/chart/charts/instance.js';
|
||||
import ApRequestChart from '@/core/chart/charts/ap-request.js';
|
||||
import FederationChart from '@/core/chart/charts/federation.js';
|
||||
import { getApId } from '@/core/activitypub/type.js';
|
||||
import { getApId, isActor, isDelete } from '@/core/activitypub/type.js';
|
||||
import type { IActivity } from '@/core/activitypub/type.js';
|
||||
import type { MiRemoteUser } from '@/models/User.js';
|
||||
import type { MiUserPublickey } from '@/models/UserPublickey.js';
|
||||
@@ -84,6 +84,23 @@ export class InboxProcessorService implements OnApplicationShutdown {
|
||||
return `Old keyId is no longer supported. ${keyIdLower}`;
|
||||
}
|
||||
|
||||
{
|
||||
let userExistenceCheckApId: string | null = null;
|
||||
|
||||
// 存在しないActorに対するActorのDeleteアクティビティは無視する。
|
||||
// actorとobjectが同じならばそれはActorに違いない
|
||||
if (isDelete(activity) && typeof activity.object === 'object' && (isActor(activity.object) || getApId(activity.actor) === getApId(activity.object))) {
|
||||
userExistenceCheckApId = getApId(activity.object);
|
||||
}
|
||||
|
||||
if (userExistenceCheckApId != null) {
|
||||
const user = await this.apDbResolverService.getUserFromApId(userExistenceCheckApId);
|
||||
if (user == null) {
|
||||
return `skip: user not found for delete activity. ${getApId(userExistenceCheckApId)}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HTTP-Signature keyIdを元にDBから取得
|
||||
let authUser: {
|
||||
user: MiRemoteUser;
|
||||
@@ -98,9 +115,9 @@ export class InboxProcessorService implements OnApplicationShutdown {
|
||||
// 対象が4xxならスキップ
|
||||
if (err instanceof StatusError) {
|
||||
if (!err.isRetryable) {
|
||||
throw new Bull.UnrecoverableError(`skip: Ignored deleted actors on both ends ${activity.actor} - ${err.statusCode}`);
|
||||
throw new Bull.UnrecoverableError(`skip: Ignored deleted actors on both ends ${getApId(activity.actor)} - ${err.statusCode}`);
|
||||
}
|
||||
throw new Error(`Error in actor ${activity.actor} - ${err.statusCode}`);
|
||||
throw new Error(`Error in actor ${getApId(activity.actor)} - ${err.statusCode}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -119,7 +136,7 @@ export class InboxProcessorService implements OnApplicationShutdown {
|
||||
const httpSignatureValidated = httpSignature.verifySignature(signature, authUser.key.keyPem);
|
||||
|
||||
// また、signatureのsignerは、activity.actorと一致する必要がある
|
||||
if (!httpSignatureValidated || authUser.user.uri !== activity.actor) {
|
||||
if (!httpSignatureValidated || authUser.user.uri !== getApId(activity.actor)) {
|
||||
// 一致しなくても、でもLD-Signatureがありそうならそっちも見る
|
||||
const ldSignature = activity.signature;
|
||||
if (ldSignature) {
|
||||
@@ -170,8 +187,8 @@ export class InboxProcessorService implements OnApplicationShutdown {
|
||||
//#endregion
|
||||
|
||||
// もう一度actorチェック
|
||||
if (authUser.user.uri !== activity.actor) {
|
||||
throw new Bull.UnrecoverableError(`skip: LD-Signature user(${authUser.user.uri}) !== activity.actor(${activity.actor})`);
|
||||
if (authUser.user.uri !== getApId(activity.actor)) {
|
||||
throw new Bull.UnrecoverableError(`skip: LD-Signature user(${authUser.user.uri}) !== activity.actor(${getApId(activity.actor)})`);
|
||||
}
|
||||
|
||||
const ldHost = this.utilityService.extractDbHost(authUser.user.uri);
|
||||
@@ -226,14 +243,17 @@ export class InboxProcessorService implements OnApplicationShutdown {
|
||||
}
|
||||
} catch (e) {
|
||||
if (e instanceof IdentifiableError) {
|
||||
if (e.id === '689ee33f-f97c-479a-ac49-1b9f8140af99') {
|
||||
return 'blocked notes with prohibited words';
|
||||
}
|
||||
if (e.id === '85ab9bd7-3a41-4530-959d-f07073900109') {
|
||||
return 'actor has been suspended';
|
||||
}
|
||||
if (e.id === 'd450b8a9-48e4-4dab-ae36-f4db763fda7c') { // invalid Note
|
||||
return e.message;
|
||||
switch (e.id) {
|
||||
case '689ee33f-f97c-479a-ac49-1b9f8140af99':
|
||||
return 'blocked notes with prohibited words';
|
||||
case '85ab9bd7-3a41-4530-959d-f07073900109':
|
||||
return 'actor has been suspended';
|
||||
case 'd450b8a9-48e4-4dab-ae36-f4db763fda7c': // invalid Note
|
||||
return e.message;
|
||||
case '9f466dab-c856-48cd-9e65-ff90ff750580':
|
||||
return 'note contains too many mentions';
|
||||
case '09d79f9e-64f1-4316-9cfa-e75c4d091574': // Instance is blocked
|
||||
return 'skip: blocked instance';
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
*/
|
||||
|
||||
import * as fs from 'node:fs';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { dirname } from 'node:path';
|
||||
import { resolve } from 'node:path';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import type { Config } from '@/config.js';
|
||||
import type { DriveFilesRepository } from '@/models/_.js';
|
||||
@@ -25,11 +24,6 @@ import { FileServerFileResolver } from './file/FileServerFileResolver.js';
|
||||
import { FileServerProxyHandler } from './file/FileServerProxyHandler.js';
|
||||
import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions } from 'fastify';
|
||||
|
||||
const _filename = fileURLToPath(import.meta.url);
|
||||
const _dirname = dirname(_filename);
|
||||
|
||||
const assets = `${_dirname}/../../server/file/assets/`;
|
||||
|
||||
@Injectable()
|
||||
export class FileServerService {
|
||||
private logger: Logger;
|
||||
@@ -37,6 +31,8 @@ export class FileServerService {
|
||||
private proxyHandler: FileServerProxyHandler;
|
||||
private fileResolver: FileServerFileResolver;
|
||||
|
||||
private readonly assets: string;
|
||||
|
||||
constructor(
|
||||
@Inject(DI.config)
|
||||
private config: Config,
|
||||
@@ -52,6 +48,7 @@ export class FileServerService {
|
||||
private loggerService: LoggerService,
|
||||
) {
|
||||
this.logger = this.loggerService.getLogger('server', 'gray');
|
||||
this.assets = resolve(this.config.rootDir, 'packages/backend/src/server/file/assets');
|
||||
this.fileResolver = new FileServerFileResolver(
|
||||
this.driveFilesRepository,
|
||||
this.fileInfoService,
|
||||
@@ -61,13 +58,13 @@ export class FileServerService {
|
||||
this.driveHandler = new FileServerDriveHandler(
|
||||
this.config,
|
||||
this.fileResolver,
|
||||
assets,
|
||||
this.assets,
|
||||
this.videoProcessingService,
|
||||
);
|
||||
this.proxyHandler = new FileServerProxyHandler(
|
||||
this.config,
|
||||
this.fileResolver,
|
||||
assets,
|
||||
this.assets,
|
||||
this.imageProcessingService,
|
||||
);
|
||||
|
||||
@@ -87,7 +84,7 @@ export class FileServerService {
|
||||
fastify.register((fastify, options, done) => {
|
||||
fastify.addHook('onRequest', handleRequestRedirectToOmitSearch);
|
||||
fastify.get('/files/app-default.jpg', (request, reply) => {
|
||||
const file = fs.createReadStream(`${_dirname}/assets/dummy.png`);
|
||||
const file = fs.createReadStream(`${this.assets}/dummy.png`);
|
||||
reply.header('Content-Type', 'image/jpeg');
|
||||
reply.header('Cache-Control', 'max-age=31536000, immutable');
|
||||
return reply.send(file);
|
||||
@@ -121,7 +118,7 @@ export class FileServerService {
|
||||
reply.header('Cache-Control', 'max-age=300');
|
||||
|
||||
if (request.query && 'fallback' in request.query) {
|
||||
return reply.sendFile('/dummy.png', assets);
|
||||
return reply.sendFile('/dummy.png', this.assets);
|
||||
}
|
||||
|
||||
if (err instanceof StatusError && (err.statusCode === 302 || err.isClientError)) {
|
||||
|
||||
@@ -10,7 +10,7 @@ import { bindThis } from '@/decorators.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { readyRef } from '@/boot/ready.js';
|
||||
import type { FastifyInstance, FastifyPluginOptions } from 'fastify';
|
||||
import type { MeiliSearch } from 'meilisearch';
|
||||
import type { Meilisearch } from 'meilisearch';
|
||||
|
||||
@Injectable()
|
||||
export class HealthServerService {
|
||||
@@ -34,7 +34,7 @@ export class HealthServerService {
|
||||
private db: DataSource,
|
||||
|
||||
@Inject(DI.meilisearch)
|
||||
private meilisearch: MeiliSearch | null,
|
||||
private meilisearch: Meilisearch | null,
|
||||
) {}
|
||||
|
||||
@bindThis
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Inject, Injectable } from '@nestjs/common';
|
||||
import cors from '@fastify/cors';
|
||||
import multipart from '@fastify/multipart';
|
||||
import { ModuleRef } from '@nestjs/core';
|
||||
import { AuthenticationResponseJSON } from '@simplewebauthn/types';
|
||||
import type { AuthenticationResponseJSON } from '@simplewebauthn/server';
|
||||
import type { Config } from '@/config.js';
|
||||
import type { InstancesRepository, AccessTokensRepository } from '@/models/_.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
|
||||
@@ -28,7 +28,7 @@ import { LoggerService } from '@/core/LoggerService.js';
|
||||
import { FastifyReplyError } from '@/misc/fastify-reply-error.js';
|
||||
import { RateLimiterService } from './RateLimiterService.js';
|
||||
import { SigninService } from './SigninService.js';
|
||||
import type { AuthenticationResponseJSON } from '@simplewebauthn/types';
|
||||
import type { AuthenticationResponseJSON } from '@simplewebauthn/server';
|
||||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
|
||||
@Injectable()
|
||||
|
||||
@@ -23,7 +23,7 @@ import { LoggerService } from '@/core/LoggerService.js';
|
||||
import type { IdentifiableError } from '@/misc/identifiable-error.js';
|
||||
import { RateLimiterService } from './RateLimiterService.js';
|
||||
import { SigninService } from './SigninService.js';
|
||||
import type { AuthenticationResponseJSON } from '@simplewebauthn/types';
|
||||
import type { AuthenticationResponseJSON } from '@simplewebauthn/server';
|
||||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
|
||||
@Injectable()
|
||||
|
||||
@@ -55,6 +55,10 @@ export const meta = {
|
||||
format: 'id',
|
||||
},
|
||||
},
|
||||
category: {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
@@ -68,6 +72,7 @@ export const paramDef = {
|
||||
roleIdsThatCanBeUsedThisDecoration: { type: 'array', items: {
|
||||
type: 'string',
|
||||
} },
|
||||
category: { type: 'string', nullable: true },
|
||||
},
|
||||
required: ['name', 'description', 'url'],
|
||||
} as const;
|
||||
@@ -84,6 +89,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
description: ps.description,
|
||||
url: ps.url,
|
||||
roleIdsThatCanBeUsedThisDecoration: ps.roleIdsThatCanBeUsedThisDecoration,
|
||||
category: ps.category,
|
||||
}, me);
|
||||
|
||||
return {
|
||||
@@ -94,6 +100,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
description: created.description,
|
||||
url: created.url,
|
||||
roleIdsThatCanBeUsedThisDecoration: created.roleIdsThatCanBeUsedThisDecoration,
|
||||
category: created.category,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -60,6 +60,10 @@ export const meta = {
|
||||
format: 'id',
|
||||
},
|
||||
},
|
||||
category: {
|
||||
type: 'string',
|
||||
optional: true, nullable: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -95,6 +99,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
description: avatarDecoration.description,
|
||||
url: avatarDecoration.url,
|
||||
roleIdsThatCanBeUsedThisDecoration: avatarDecoration.roleIdsThatCanBeUsedThisDecoration,
|
||||
category: avatarDecoration.category,
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ export const paramDef = {
|
||||
roleIdsThatCanBeUsedThisDecoration: { type: 'array', items: {
|
||||
type: 'string',
|
||||
} },
|
||||
category: { type: 'string', nullable: true },
|
||||
},
|
||||
required: ['id'],
|
||||
} as const;
|
||||
@@ -45,6 +46,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
description: ps.description,
|
||||
url: ps.url,
|
||||
roleIdsThatCanBeUsedThisDecoration: ps.roleIdsThatCanBeUsedThisDecoration,
|
||||
category: ps.category,
|
||||
}, me);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ export const meta = {
|
||||
|
||||
kind: 'write:channels',
|
||||
|
||||
requiredRolePolicy: 'canCreateChannel',
|
||||
|
||||
limit: {
|
||||
duration: ms('1hour'),
|
||||
max: 10,
|
||||
|
||||
@@ -119,7 +119,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
}
|
||||
|
||||
// Update
|
||||
this.driveFoldersRepository.update(folder.id, {
|
||||
await this.driveFoldersRepository.update(folder.id, {
|
||||
name: folder.name,
|
||||
parentId: folder.parentId,
|
||||
});
|
||||
|
||||
@@ -5,7 +5,13 @@
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import endpoints from '../endpoints.js';
|
||||
|
||||
// 循環参照を回避
|
||||
let endpointsPromise: Promise<typeof import('../endpoints.js').default> | undefined;
|
||||
|
||||
function getEndpoints() {
|
||||
return endpointsPromise ??= import('../endpoints.js').then(module => module.default);
|
||||
}
|
||||
|
||||
export const meta = {
|
||||
requireCredential: false,
|
||||
@@ -43,6 +49,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
constructor(
|
||||
) {
|
||||
super(meta, paramDef, async (ps) => {
|
||||
const endpoints = await getEndpoints();
|
||||
const ep = endpoints.find(x => x.name === ps.endpoint);
|
||||
if (ep == null) return null;
|
||||
return {
|
||||
|
||||
@@ -5,7 +5,13 @@
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import endpoints from '../endpoints.js';
|
||||
|
||||
// 循環参照を回避
|
||||
let endpointsPromise: Promise<typeof import('../endpoints.js').default> | undefined;
|
||||
|
||||
function getEndpoints() {
|
||||
return endpointsPromise ??= import('../endpoints.js').then(module => module.default);
|
||||
}
|
||||
|
||||
export const meta = {
|
||||
requireCredential: false,
|
||||
@@ -39,6 +45,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
constructor(
|
||||
) {
|
||||
super(meta, paramDef, async () => {
|
||||
const endpoints = await getEndpoints();
|
||||
return endpoints.map(x => x.name);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -49,6 +49,10 @@ export const meta = {
|
||||
format: 'id',
|
||||
},
|
||||
},
|
||||
category: {
|
||||
type: 'string',
|
||||
optional: true, nullable: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -76,6 +80,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
description: decoration.description,
|
||||
url: decoration.url,
|
||||
roleIdsThatCanBeUsedThisDecoration: decoration.roleIdsThatCanBeUsedThisDecoration.filter(roleId => allRoles.some(role => role.id === roleId)),
|
||||
category: decoration.category,
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -39,137 +39,6 @@ export const meta = {
|
||||
|
||||
res: {
|
||||
type: 'object',
|
||||
nullable: false,
|
||||
optional: false,
|
||||
properties: {
|
||||
rp: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
user: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
},
|
||||
displayName: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
challenge: {
|
||||
type: 'string',
|
||||
},
|
||||
pubKeyCredParams: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
type: {
|
||||
type: 'string',
|
||||
},
|
||||
alg: {
|
||||
type: 'number',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
timeout: {
|
||||
type: 'number',
|
||||
nullable: true,
|
||||
},
|
||||
excludeCredentials: {
|
||||
type: 'array',
|
||||
nullable: true,
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
},
|
||||
type: {
|
||||
type: 'string',
|
||||
},
|
||||
transports: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string',
|
||||
enum: [
|
||||
'ble',
|
||||
'cable',
|
||||
'hybrid',
|
||||
'internal',
|
||||
'nfc',
|
||||
'smart-card',
|
||||
'usb',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
authenticatorSelection: {
|
||||
type: 'object',
|
||||
nullable: true,
|
||||
properties: {
|
||||
authenticatorAttachment: {
|
||||
type: 'string',
|
||||
enum: [
|
||||
'cross-platform',
|
||||
'platform',
|
||||
],
|
||||
},
|
||||
requireResidentKey: {
|
||||
type: 'boolean',
|
||||
},
|
||||
userVerification: {
|
||||
type: 'string',
|
||||
enum: [
|
||||
'discouraged',
|
||||
'preferred',
|
||||
'required',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
attestation: {
|
||||
type: 'string',
|
||||
nullable: true,
|
||||
enum: [
|
||||
'direct',
|
||||
'enterprise',
|
||||
'indirect',
|
||||
'none',
|
||||
null,
|
||||
],
|
||||
},
|
||||
extensions: {
|
||||
type: 'object',
|
||||
nullable: true,
|
||||
properties: {
|
||||
appid: {
|
||||
type: 'string',
|
||||
nullable: true,
|
||||
},
|
||||
credProps: {
|
||||
type: 'boolean',
|
||||
nullable: true,
|
||||
},
|
||||
hmacCreateSecret: {
|
||||
type: 'boolean',
|
||||
nullable: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ process.env.NODE_ENV = 'test';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { dirname } from 'node:path';
|
||||
import { describe, test, expect } from '@jest/globals';
|
||||
import { describe, test, expect } from 'vitest';
|
||||
import { getValidator } from '../../../../../test/prelude/get-api-validator.js';
|
||||
import { paramDef } from './create.js';
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
process.env.NODE_ENV = 'test';
|
||||
|
||||
import { describe, test, expect } from 'vitest';
|
||||
import { getValidator } from '../../../../../test/prelude/get-api-validator.js';
|
||||
import { paramDef } from './show.js';
|
||||
|
||||
|
||||
@@ -3,16 +3,14 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import type { Config } from '@/config.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { genOpenapiSpec } from './gen-spec.js';
|
||||
import { ApiDocPage } from './api-doc.js';
|
||||
import type { FastifyInstance, FastifyPluginOptions } from 'fastify';
|
||||
|
||||
const staticAssets = fileURLToPath(new URL('../../../../assets/', import.meta.url));
|
||||
|
||||
@Injectable()
|
||||
export class OpenApiServerService {
|
||||
constructor(
|
||||
@@ -25,7 +23,8 @@ export class OpenApiServerService {
|
||||
public createServer(fastify: FastifyInstance, _options: FastifyPluginOptions, done: (err?: Error) => void) {
|
||||
fastify.get('/api-doc', async (_request, reply) => {
|
||||
reply.header('Cache-Control', 'public, max-age=86400');
|
||||
return await reply.sendFile('/api-doc.html', staticAssets);
|
||||
reply.type('text/html; charset=utf-8');
|
||||
reply.send(await ApiDocPage());
|
||||
});
|
||||
fastify.get('/api.json', (_request, reply) => {
|
||||
reply.header('Cache-Control', 'public, max-age=600');
|
||||
|
||||
26
packages/backend/src/server/api/openapi/api-doc.tsx
Normal file
26
packages/backend/src/server/api/openapi/api-doc.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export function ApiDocPage() {
|
||||
return (
|
||||
<>
|
||||
{'<!DOCTYPE html>'}
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Misskey API</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<style>
|
||||
{`body { margin: 0; padding: 0; }`}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<script id="api-reference" data-url="/api.json"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
|
||||
</body>
|
||||
</html>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -4,9 +4,7 @@
|
||||
*/
|
||||
|
||||
import { randomUUID } from 'node:crypto';
|
||||
import { dirname, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import * as fs from 'node:fs';
|
||||
import { resolve } from 'node:path';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import ms from 'ms';
|
||||
import sharp from 'sharp';
|
||||
@@ -67,35 +65,17 @@ import { ErrorPage } from './views/error.js';
|
||||
|
||||
import type { FastifyError, FastifyInstance, FastifyPluginOptions, FastifyReply } from 'fastify';
|
||||
|
||||
const _filename = fileURLToPath(import.meta.url);
|
||||
const _dirname = dirname(_filename);
|
||||
|
||||
let rootDir = _dirname;
|
||||
// 見つかるまで上に遡る
|
||||
while (!fs.existsSync(resolve(rootDir, 'packages'))) {
|
||||
const parentDir = dirname(rootDir);
|
||||
if (parentDir === rootDir) {
|
||||
throw new Error('Cannot find root directory');
|
||||
}
|
||||
rootDir = parentDir;
|
||||
}
|
||||
|
||||
const backendRootDir = resolve(rootDir, 'packages/backend');
|
||||
const frontendRootDir = resolve(rootDir, 'packages/frontend');
|
||||
|
||||
const staticAssets = resolve(backendRootDir, 'assets');
|
||||
const clientAssets = resolve(frontendRootDir, 'assets');
|
||||
const assets = resolve(rootDir, 'built/_frontend_dist_');
|
||||
const swAssets = resolve(rootDir, 'built/_sw_dist_');
|
||||
const fluentEmojisDir = resolve(rootDir, 'fluent-emojis/dist');
|
||||
const twemojiDir = resolve(backendRootDir, 'node_modules/@discordapp/twemoji/dist/svg');
|
||||
const frontendViteOut = resolve(rootDir, 'built/_frontend_vite_');
|
||||
const frontendEmbedViteOut = resolve(rootDir, 'built/_frontend_embed_vite_');
|
||||
const tarball = resolve(rootDir, 'built/tarball');
|
||||
|
||||
@Injectable()
|
||||
export class ClientServerService {
|
||||
private logger: Logger;
|
||||
private readonly staticAssets: string;
|
||||
private readonly clientAssets: string;
|
||||
private readonly assets: string;
|
||||
private readonly swAssets: string;
|
||||
private readonly fluentEmojisDir: string;
|
||||
private readonly twemojiDir: string;
|
||||
private readonly frontendViteOut: string;
|
||||
private readonly frontendEmbedViteOut: string;
|
||||
private readonly tarball: string;
|
||||
|
||||
constructor(
|
||||
@Inject(DI.config)
|
||||
@@ -149,6 +129,17 @@ export class ClientServerService {
|
||||
private clientLoggerService: ClientLoggerService,
|
||||
) {
|
||||
//this.createServer = this.createServer.bind(this);
|
||||
const backendRootdir = resolve(this.config.rootDir, 'packages/backend');
|
||||
const frontendRootdir = resolve(this.config.rootDir, 'packages/frontend');
|
||||
this.staticAssets = resolve(backendRootdir, 'assets');
|
||||
this.clientAssets = resolve(frontendRootdir, 'assets');
|
||||
this.assets = resolve(this.config.rootDir, 'built/_frontend_dist_');
|
||||
this.swAssets = resolve(this.config.rootDir, 'built/_sw_dist_');
|
||||
this.fluentEmojisDir = resolve(this.config.rootDir, 'fluent-emojis/dist');
|
||||
this.twemojiDir = resolve(backendRootdir, 'node_modules/@discordapp/twemoji/dist/svg');
|
||||
this.frontendViteOut = resolve(this.config.rootDir, 'built/_frontend_vite_');
|
||||
this.frontendEmbedViteOut = resolve(this.config.rootDir, 'built/_frontend_embed_vite_');
|
||||
this.tarball = resolve(this.config.rootDir, 'built/tarball');
|
||||
}
|
||||
|
||||
@bindThis
|
||||
@@ -223,17 +214,17 @@ export class ClientServerService {
|
||||
|
||||
//#region vite assets
|
||||
if (this.config.frontendEmbedManifestExists) {
|
||||
console.log(`[ClientServerService] Using built frontend vite assets. ${frontendViteOut}`);
|
||||
this.clientLoggerService.logger.info(`[ClientServerService] Using built frontend vite assets. ${this.frontendViteOut}`);
|
||||
fastify.register((fastify, options, done) => {
|
||||
fastify.register(fastifyStatic, {
|
||||
root: frontendViteOut,
|
||||
root: this.frontendViteOut,
|
||||
prefix: '/vite/',
|
||||
maxAge: ms('30 days'),
|
||||
immutable: true,
|
||||
decorateReply: false,
|
||||
});
|
||||
fastify.register(fastifyStatic, {
|
||||
root: frontendEmbedViteOut,
|
||||
root: this.frontendEmbedViteOut,
|
||||
prefix: '/embed_vite/',
|
||||
maxAge: ms('30 days'),
|
||||
immutable: true,
|
||||
@@ -265,21 +256,21 @@ export class ClientServerService {
|
||||
//#region static assets
|
||||
|
||||
fastify.register(fastifyStatic, {
|
||||
root: staticAssets,
|
||||
root: this.staticAssets,
|
||||
prefix: '/static-assets/',
|
||||
maxAge: ms('7 days'),
|
||||
decorateReply: false,
|
||||
});
|
||||
|
||||
fastify.register(fastifyStatic, {
|
||||
root: clientAssets,
|
||||
root: this.clientAssets,
|
||||
prefix: '/client-assets/',
|
||||
maxAge: ms('7 days'),
|
||||
decorateReply: false,
|
||||
});
|
||||
|
||||
fastify.register(fastifyStatic, {
|
||||
root: assets,
|
||||
root: this.assets,
|
||||
prefix: '/assets/',
|
||||
maxAge: ms('7 days'),
|
||||
decorateReply: false,
|
||||
@@ -287,7 +278,7 @@ export class ClientServerService {
|
||||
|
||||
fastify.register((fastify, options, done) => {
|
||||
fastify.register(fastifyStatic, {
|
||||
root: tarball,
|
||||
root: this.tarball,
|
||||
prefix: '/tarball/',
|
||||
maxAge: ms('30 days'),
|
||||
immutable: true,
|
||||
@@ -298,11 +289,11 @@ export class ClientServerService {
|
||||
});
|
||||
|
||||
fastify.get('/favicon.ico', async (request, reply) => {
|
||||
return reply.sendFile('/favicon.ico', staticAssets);
|
||||
return reply.sendFile('/favicon.ico', this.staticAssets);
|
||||
});
|
||||
|
||||
fastify.get('/apple-touch-icon.png', async (request, reply) => {
|
||||
return reply.sendFile('/apple-touch-icon.png', staticAssets);
|
||||
return reply.sendFile('/apple-touch-icon.png', this.staticAssets);
|
||||
});
|
||||
|
||||
fastify.get<{ Params: { path: string } }>('/fluent-emoji/:path(.*)', async (request, reply) => {
|
||||
@@ -315,7 +306,7 @@ export class ClientServerService {
|
||||
|
||||
reply.header('Content-Security-Policy', 'default-src \'none\'; style-src \'unsafe-inline\'');
|
||||
|
||||
return reply.sendFile(path, fluentEmojisDir, {
|
||||
return reply.sendFile(path, this.fluentEmojisDir, {
|
||||
maxAge: ms('30 days'),
|
||||
});
|
||||
});
|
||||
@@ -330,7 +321,7 @@ export class ClientServerService {
|
||||
|
||||
reply.header('Content-Security-Policy', 'default-src \'none\'; style-src \'unsafe-inline\'');
|
||||
|
||||
return reply.sendFile(path, twemojiDir, {
|
||||
return reply.sendFile(path, this.twemojiDir, {
|
||||
maxAge: ms('30 days'),
|
||||
});
|
||||
});
|
||||
@@ -344,7 +335,7 @@ export class ClientServerService {
|
||||
}
|
||||
|
||||
const mask = await sharp(
|
||||
`${twemojiDir}/${path.replace('.png', '')}.svg`,
|
||||
`${this.twemojiDir}/${path.replace('.png', '')}.svg`,
|
||||
{ density: 1000 },
|
||||
)
|
||||
.resize(488, 488)
|
||||
@@ -380,7 +371,7 @@ export class ClientServerService {
|
||||
|
||||
// ServiceWorker
|
||||
fastify.get('/sw.js', async (request, reply) => {
|
||||
return await reply.sendFile('/sw.js', swAssets, {
|
||||
return await reply.sendFile('/sw.js', this.swAssets, {
|
||||
maxAge: ms('10 minutes'),
|
||||
});
|
||||
});
|
||||
@@ -390,13 +381,40 @@ export class ClientServerService {
|
||||
|
||||
// Embed Javascript
|
||||
fastify.get('/embed.js', async (request, reply) => {
|
||||
return await reply.sendFile('/embed.js', staticAssets, {
|
||||
return await reply.sendFile('/embed.js', this.staticAssets, {
|
||||
maxAge: ms('1 day'),
|
||||
});
|
||||
});
|
||||
|
||||
fastify.get('/robots.txt', async (request, reply) => {
|
||||
return await reply.sendFile('/robots.txt', staticAssets);
|
||||
const disallowedPaths = [
|
||||
'/settings',
|
||||
'/admin',
|
||||
'/custom-emojis-manager',
|
||||
'/avatar-decorations',
|
||||
'/share',
|
||||
'/my',
|
||||
'/api',
|
||||
'/inbox',
|
||||
'/oauth',
|
||||
'/proxy',
|
||||
'/url',
|
||||
];
|
||||
|
||||
if (this.meta.ugcVisibilityForVisitor === 'none') {
|
||||
disallowedPaths.push(
|
||||
'/@',
|
||||
'/notes',
|
||||
);
|
||||
}
|
||||
|
||||
let content = `User-agent: *\n`;
|
||||
content += disallowedPaths.map((path) => `Disallow: ${path}`).join('\n') + '\n';
|
||||
content += 'Allow: /\n';
|
||||
content += '\n# todo: sitemap\n';
|
||||
|
||||
reply.header('Content-Type', 'text/plain; charset=utf-8');
|
||||
return await reply.send(content);
|
||||
});
|
||||
|
||||
// OpenSearch XML
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { dirname, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { promises as fsp, existsSync } from 'node:fs';
|
||||
import { resolve } from 'node:path';
|
||||
import { promises as fsp } from 'node:fs';
|
||||
import { languages } from 'i18n/const';
|
||||
import { Injectable, Inject } from '@nestjs/common';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
@@ -18,25 +17,11 @@ import type { Config } from '@/config.js';
|
||||
import type { MiMeta } from '@/models/Meta.js';
|
||||
import type { CommonData, ViteFiles } from './views/_.js';
|
||||
|
||||
const _filename = fileURLToPath(import.meta.url);
|
||||
const _dirname = dirname(_filename);
|
||||
|
||||
let rootDir = _dirname;
|
||||
// 見つかるまで上に遡る
|
||||
while (!existsSync(resolve(rootDir, 'packages'))) {
|
||||
const parentDir = dirname(rootDir);
|
||||
if (parentDir === rootDir) {
|
||||
throw new Error('Cannot find root directory');
|
||||
}
|
||||
rootDir = parentDir;
|
||||
}
|
||||
|
||||
const frontendViteBuilt = resolve(rootDir, 'built/_frontend_vite_');
|
||||
const frontendEmbedViteBuilt = resolve(rootDir, 'built/_frontend_embed_vite_');
|
||||
|
||||
@Injectable()
|
||||
export class HtmlTemplateService {
|
||||
private frontendAssetsFetched = false;
|
||||
private readonly frontendViteBuilt: string;
|
||||
private readonly frontendEmbedViteBuilt: string;
|
||||
public frontendViteFiles: ViteFiles | null = null;
|
||||
public frontendBootloaderJs: string | null = null;
|
||||
public frontendBootloaderCss: string | null = null;
|
||||
@@ -53,6 +38,8 @@ export class HtmlTemplateService {
|
||||
|
||||
private metaEntityService: MetaEntityService,
|
||||
) {
|
||||
this.frontendViteBuilt = resolve(this.config.rootDir, 'built/_frontend_vite_');
|
||||
this.frontendEmbedViteBuilt = resolve(this.config.rootDir, 'built/_frontend_embed_vite_');
|
||||
}
|
||||
|
||||
// 初期ロードで読み込むべきファイルのパスを収集する。
|
||||
@@ -118,22 +105,22 @@ export class HtmlTemplateService {
|
||||
embedBootJs,
|
||||
embedBootCss,
|
||||
] = await Promise.all([
|
||||
fsp.readFile(resolve(frontendViteBuilt, 'loader/boot.js'), 'utf-8').catch(() => null),
|
||||
fsp.readFile(resolve(frontendViteBuilt, 'loader/style.css'), 'utf-8').catch(() => null),
|
||||
fsp.readFile(resolve(frontendEmbedViteBuilt, 'loader/boot.js'), 'utf-8').catch(() => null),
|
||||
fsp.readFile(resolve(frontendEmbedViteBuilt, 'loader/style.css'), 'utf-8').catch(() => null),
|
||||
fsp.readFile(resolve(this.frontendViteBuilt, 'loader/boot.js'), 'utf-8').catch(() => null),
|
||||
fsp.readFile(resolve(this.frontendViteBuilt, 'loader/style.css'), 'utf-8').catch(() => null),
|
||||
fsp.readFile(resolve(this.frontendEmbedViteBuilt, 'loader/boot.js'), 'utf-8').catch(() => null),
|
||||
fsp.readFile(resolve(this.frontendEmbedViteBuilt, 'loader/style.css'), 'utf-8').catch(() => null),
|
||||
]);
|
||||
|
||||
let feViteManifest: Manifest | null = null;
|
||||
let embedFeViteManifest: Manifest | null = null;
|
||||
|
||||
if (this.config.frontendManifestExists) {
|
||||
const manifestContent = await fsp.readFile(resolve(frontendViteBuilt, 'manifest.json'), 'utf-8').catch(() => null);
|
||||
const manifestContent = await fsp.readFile(resolve(this.frontendViteBuilt, 'manifest.json'), 'utf-8').catch(() => null);
|
||||
feViteManifest = manifestContent ? JSON.parse(manifestContent) : null;
|
||||
}
|
||||
|
||||
if (this.config.frontendEmbedManifestExists) {
|
||||
const manifestContent = await fsp.readFile(resolve(frontendEmbedViteBuilt, 'manifest.json'), 'utf-8').catch(() => null);
|
||||
const manifestContent = await fsp.readFile(resolve(this.frontendEmbedViteBuilt, 'manifest.json'), 'utf-8').catch(() => null);
|
||||
embedFeViteManifest = manifestContent ? JSON.parse(manifestContent) : null;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user