commit
a5f28c21e4
@ -220,5 +220,10 @@ allowedPrivateNetworks: [
|
||||
'127.0.0.1/32'
|
||||
]
|
||||
|
||||
# Disable automatic redirect for ActivityPub object lookup. (default: false)
|
||||
# This is a strong defense against potential impersonation attacks if the viewer instance has inadequate validation.
|
||||
# However it will make it impossible for other instances to lookup third-party user and notes through your URL.
|
||||
#disallowExternalApRedirect: true
|
||||
|
||||
# Upload or download file size limits (bytes)
|
||||
#maxFileSize: 262144000
|
||||
|
@ -235,6 +235,11 @@ signToActivityPubGet: true
|
||||
# '127.0.0.1/32'
|
||||
#]
|
||||
|
||||
# Disable automatic redirect for ActivityPub object lookup. (default: false)
|
||||
# This is a strong defense against potential impersonation attacks if the viewer instance has inadequate validation.
|
||||
# However it will make it impossible for other instances to lookup third-party user and notes through your URL.
|
||||
#disallowExternalApRedirect: true
|
||||
|
||||
# Upload or download file size limits (bytes)
|
||||
#maxFileSize: 262144000
|
||||
|
||||
|
@ -334,6 +334,11 @@ signToActivityPubGet: true
|
||||
# '127.0.0.1/32'
|
||||
#]
|
||||
|
||||
# Disable automatic redirect for ActivityPub object lookup. (default: false)
|
||||
# This is a strong defense against potential impersonation attacks if the viewer instance has inadequate validation.
|
||||
# However it will make it impossible for other instances to lookup third-party user and notes through your URL.
|
||||
#disallowExternalApRedirect: true
|
||||
|
||||
# Upload or download file size limits (bytes)
|
||||
#maxFileSize: 262144000
|
||||
|
||||
|
@ -7,7 +7,9 @@
|
||||
"ghcr.io/devcontainers/features/node:1": {
|
||||
"version": "22.11.0"
|
||||
},
|
||||
"ghcr.io/devcontainers-contrib/features/corepack:1": {}
|
||||
"ghcr.io/devcontainers-extra/features/corepack:1": {
|
||||
"version": "0.31.0"
|
||||
}
|
||||
},
|
||||
"forwardPorts": [3000],
|
||||
"postCreateCommand": "/bin/bash .devcontainer/init.sh",
|
||||
|
4
.github/dependabot.yml
vendored
4
.github/dependabot.yml
vendored
@ -9,7 +9,7 @@ updates:
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 100
|
||||
open-pull-requests-limit: 0
|
||||
|
||||
# Add only the root, not each workspace item
|
||||
# https://github.com/dependabot/dependabot-core/issues/4993#issuecomment-1289133027
|
||||
@ -17,7 +17,7 @@ updates:
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 10
|
||||
open-pull-requests-limit: 0
|
||||
# List dependencies required to be updated together, sharing the same version numbers.
|
||||
# Those who simply have the common owner (e.g. @fastify) don't need to be listed.
|
||||
groups:
|
||||
|
4
.github/workflows/api-misskey-js.yml
vendored
4
.github/workflows/api-misskey-js.yml
vendored
@ -20,12 +20,12 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4.1.1
|
||||
uses: actions/checkout@v4.2.2
|
||||
|
||||
- run: corepack enable
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4.1.0
|
||||
uses: actions/setup-node@v4.2.0
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
cache: 'pnpm'
|
||||
|
4
.github/workflows/changelog-check.yml
vendored
4
.github/workflows/changelog-check.yml
vendored
@ -12,9 +12,9 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout head
|
||||
uses: actions/checkout@v4.1.1
|
||||
uses: actions/checkout@v4.2.2
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4.1.0
|
||||
uses: actions/setup-node@v4.2.0
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
|
||||
|
@ -18,7 +18,7 @@ jobs:
|
||||
if: ${{ github.event.pull_request.mergeable == null || github.event.pull_request.mergeable == true }}
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4.1.1
|
||||
uses: actions/checkout@v4.2.2
|
||||
with:
|
||||
submodules: true
|
||||
persist-credentials: false
|
||||
@ -29,7 +29,7 @@ jobs:
|
||||
|
||||
- name: setup node
|
||||
id: setup-node
|
||||
uses: actions/setup-node@v4.1.0
|
||||
uses: actions/setup-node@v4.2.0
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
cache: pnpm
|
||||
@ -66,7 +66,7 @@ jobs:
|
||||
if: ${{ github.event.pull_request.mergeable == null || github.event.pull_request.mergeable == true }}
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4.1.1
|
||||
uses: actions/checkout@v4.2.2
|
||||
with:
|
||||
submodules: true
|
||||
persist-credentials: false
|
||||
|
@ -20,7 +20,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4.1.1
|
||||
uses: actions/checkout@v4.2.2
|
||||
- name: Check version
|
||||
run: |
|
||||
if [ "$(jq -r '.version' package.json)" != "$(jq -r '.version' packages/misskey-js/package.json)" ]; then
|
||||
|
2
.github/workflows/check-spdx-license-id.yml
vendored
2
.github/workflows/check-spdx-license-id.yml
vendored
@ -12,7 +12,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4.1.1
|
||||
uses: actions/checkout@v4.2.2
|
||||
- name: Check
|
||||
run: |
|
||||
counter=0
|
||||
|
2
.github/workflows/check_copyright_year.yml
vendored
2
.github/workflows/check_copyright_year.yml
vendored
@ -10,7 +10,7 @@ jobs:
|
||||
check_copyright_year:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.1
|
||||
- uses: actions/checkout@v4.2.2
|
||||
- run: |
|
||||
if [ "$(grep Copyright COPYING | sed -e 's/.*2014-\([0-9]*\) .*/\1/g')" -ne "$(date +%Y)" ]; then
|
||||
echo "Please change copyright year!"
|
||||
|
@ -28,7 +28,7 @@ jobs:
|
||||
wait_time: ${{ steps.get-wait-time.outputs.wait_time }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4.1.1
|
||||
uses: actions/checkout@v4.2.2
|
||||
|
||||
- name: Check allowed users
|
||||
id: check-allowed-users
|
||||
|
2
.github/workflows/docker-develop.yml
vendored
2
.github/workflows/docker-develop.yml
vendored
@ -27,7 +27,7 @@ jobs:
|
||||
platform=${{ matrix.platform }}
|
||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v4.1.1
|
||||
uses: actions/checkout@v4.2.2
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Log in to Docker Hub
|
||||
|
2
.github/workflows/docker.yml
vendored
2
.github/workflows/docker.yml
vendored
@ -32,7 +32,7 @@ jobs:
|
||||
platform=${{ matrix.platform }}
|
||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v4.1.1
|
||||
uses: actions/checkout@v4.2.2
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Docker meta
|
||||
|
2
.github/workflows/dockle.yml
vendored
2
.github/workflows/dockle.yml
vendored
@ -15,7 +15,7 @@ jobs:
|
||||
DOCKER_CONTENT_TRUST: 1
|
||||
DOCKLE_VERSION: 0.4.14
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.1
|
||||
- uses: actions/checkout@v4.2.2
|
||||
- name: Download and install dockle v${{ env.DOCKLE_VERSION }}
|
||||
run: |
|
||||
curl -L -o dockle.deb "https://github.com/goodwithtech/dockle/releases/download/v${DOCKLE_VERSION}/dockle_${DOCKLE_VERSION}_Linux-64bit.deb"
|
||||
|
4
.github/workflows/get-api-diff.yml
vendored
4
.github/workflows/get-api-diff.yml
vendored
@ -30,14 +30,14 @@ jobs:
|
||||
ref: refs/pull/${{ github.event.number }}/merge
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.1
|
||||
- uses: actions/checkout@v4.2.2
|
||||
with:
|
||||
ref: ${{ matrix.ref }}
|
||||
submodules: true
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4.1.0
|
||||
uses: actions/setup-node@v4.2.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'pnpm'
|
||||
|
14
.github/workflows/lint.yml
vendored
14
.github/workflows/lint.yml
vendored
@ -36,12 +36,12 @@ jobs:
|
||||
pnpm_install:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.1
|
||||
- uses: actions/checkout@v4.2.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: true
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4.1.0
|
||||
- uses: actions/setup-node@v4.2.0
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
cache: 'pnpm'
|
||||
@ -67,19 +67,19 @@ jobs:
|
||||
eslint-cache-version: v1
|
||||
eslint-cache-path: ${{ github.workspace }}/node_modules/.cache/eslint-${{ matrix.workspace }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.1
|
||||
- uses: actions/checkout@v4.2.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: true
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4.1.0
|
||||
- uses: actions/setup-node@v4.2.0
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
cache: 'pnpm'
|
||||
- run: corepack enable
|
||||
- run: pnpm i --frozen-lockfile
|
||||
- name: Restore eslint cache
|
||||
uses: actions/cache@v4.2.0
|
||||
uses: actions/cache@v4.2.1
|
||||
with:
|
||||
path: ${{ env.eslint-cache-path }}
|
||||
key: eslint-${{ env.eslint-cache-version }}-${{ matrix.workspace }}-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ github.ref_name }}-${{ github.sha }}
|
||||
@ -97,12 +97,12 @@ jobs:
|
||||
- sw
|
||||
- misskey-js
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.1
|
||||
- uses: actions/checkout@v4.2.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: true
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4.1.0
|
||||
- uses: actions/setup-node@v4.2.0
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
cache: 'pnpm'
|
||||
|
4
.github/workflows/locale.yml
vendored
4
.github/workflows/locale.yml
vendored
@ -18,12 +18,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.1
|
||||
- uses: actions/checkout@v4.2.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: true
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4.1.0
|
||||
- uses: actions/setup-node@v4.2.0
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
cache: 'pnpm'
|
||||
|
36
.github/workflows/ok-to-test.yml
vendored
36
.github/workflows/ok-to-test.yml
vendored
@ -1,36 +0,0 @@
|
||||
# If someone with write access comments "/ok-to-test" on a pull request, emit a repository_dispatch event
|
||||
name: Ok To Test
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
ok-to-test:
|
||||
runs-on: ubuntu-latest
|
||||
# Only run for PRs, not issue comments
|
||||
if: ${{ github.event.issue.pull_request }}
|
||||
steps:
|
||||
# Generate a GitHub App installation access token from an App ID and private key
|
||||
# To create a new GitHub App:
|
||||
# https://developer.github.com/apps/building-github-apps/creating-a-github-app/
|
||||
# See app.yml for an example app manifest
|
||||
- name: Generate token
|
||||
id: generate_token
|
||||
uses: tibdex/github-app-token@v2
|
||||
with:
|
||||
app_id: ${{ secrets.DEPLOYBOT_APP_ID }}
|
||||
private_key: ${{ secrets.DEPLOYBOT_PRIVATE_KEY }}
|
||||
|
||||
- name: Slash Command Dispatch
|
||||
uses: peter-evans/slash-command-dispatch@v4
|
||||
env:
|
||||
TOKEN: ${{ steps.generate_token.outputs.token }}
|
||||
with:
|
||||
token: ${{ env.TOKEN }} # GitHub App installation access token
|
||||
# token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} # PAT or OAuth token will also work
|
||||
reaction-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-type: pull-request
|
||||
commands: deploy
|
||||
named-args: true
|
||||
permission: write
|
4
.github/workflows/on-release-created.yml
vendored
4
.github/workflows/on-release-created.yml
vendored
@ -23,13 +23,13 @@ jobs:
|
||||
node-version: [22.11.0]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.1
|
||||
- uses: actions/checkout@v4.2.2
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4.1.0
|
||||
uses: actions/setup-node@v4.2.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'pnpm'
|
||||
|
92
.github/workflows/pr-preview-deploy.yml
vendored
92
.github/workflows/pr-preview-deploy.yml
vendored
@ -1,92 +0,0 @@
|
||||
# Run secret-dependent integration tests only after /deploy approval
|
||||
on:
|
||||
repository_dispatch:
|
||||
types: [deploy-command]
|
||||
|
||||
name: Deploy preview environment
|
||||
|
||||
jobs:
|
||||
# Repo owner has commented /deploy on a (fork-based) pull request
|
||||
deploy-preview-environment:
|
||||
runs-on: ubuntu-latest
|
||||
if:
|
||||
github.event.client_payload.slash_command.sha != '' &&
|
||||
contains(github.event.client_payload.pull_request.head.sha, github.event.client_payload.slash_command.sha)
|
||||
steps:
|
||||
- uses: actions/github-script@v7.0.1
|
||||
id: check-id
|
||||
env:
|
||||
number: ${{ github.event.client_payload.pull_request.number }}
|
||||
job: ${{ github.job }}
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
result-encoding: string
|
||||
script: |
|
||||
const { data: pull } = await github.rest.pulls.get({
|
||||
...context.repo,
|
||||
pull_number: process.env.number
|
||||
});
|
||||
const ref = pull.head.sha;
|
||||
|
||||
const { data: checks } = await github.rest.checks.listForRef({
|
||||
...context.repo,
|
||||
ref
|
||||
});
|
||||
|
||||
const check = checks.check_runs.filter(c => c.name === process.env.job);
|
||||
|
||||
return check[0].id;
|
||||
|
||||
- uses: actions/github-script@v7.0.1
|
||||
env:
|
||||
check_id: ${{ steps.check-id.outputs.result }}
|
||||
details_url: ${{ github.server_url }}/${{ github.repository }}/runs/${{ github.run_id }}
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
await github.rest.checks.update({
|
||||
...context.repo,
|
||||
check_run_id: process.env.check_id,
|
||||
status: 'in_progress',
|
||||
details_url: process.env.details_url
|
||||
});
|
||||
|
||||
# Check out merge commit
|
||||
- name: Fork based /deploy checkout
|
||||
uses: actions/checkout@v4.1.1
|
||||
with:
|
||||
ref: 'refs/pull/${{ github.event.client_payload.pull_request.number }}/merge'
|
||||
|
||||
# <insert integration tests needing secrets>
|
||||
- name: Context
|
||||
uses: okteto/context@latest
|
||||
with:
|
||||
token: ${{ secrets.OKTETO_TOKEN }}
|
||||
|
||||
- name: Deploy preview environment
|
||||
uses: ikuradon/deploy-preview@latest
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
name: pr-${{ github.event.client_payload.pull_request.number }}-syuilo
|
||||
timeout: 15m
|
||||
|
||||
# Update check run called "integration-fork"
|
||||
- uses: actions/github-script@v7.0.1
|
||||
id: update-check-run
|
||||
if: ${{ always() }}
|
||||
env:
|
||||
# Conveniently, job.status maps to https://developer.github.com/v3/checks/runs/#update-a-check-run
|
||||
conclusion: ${{ job.status }}
|
||||
check_id: ${{ steps.check-id.outputs.result }}
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const { data: result } = await github.rest.checks.update({
|
||||
...context.repo,
|
||||
check_run_id: process.env.check_id,
|
||||
status: 'completed',
|
||||
conclusion: process.env.conclusion
|
||||
});
|
||||
|
||||
return result;
|
54
.github/workflows/pr-preview-destroy.yml
vendored
54
.github/workflows/pr-preview-destroy.yml
vendored
@ -1,54 +0,0 @@
|
||||
# file: .github/workflows/preview-closed.yaml
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- closed
|
||||
|
||||
name: Destroy preview environment
|
||||
|
||||
jobs:
|
||||
destroy-preview-environment:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/github-script@v7.0.1
|
||||
id: check-conclusion
|
||||
env:
|
||||
number: ${{ github.event.number }}
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
result-encoding: string
|
||||
script: |
|
||||
const { data: pull } = await github.rest.pulls.get({
|
||||
...context.repo,
|
||||
pull_number: process.env.number
|
||||
});
|
||||
const ref = pull.head.sha;
|
||||
|
||||
const { data: checks } = await github.rest.checks.listForRef({
|
||||
...context.repo,
|
||||
ref
|
||||
});
|
||||
|
||||
const check = checks.check_runs.filter(c => c.name === 'deploy-preview-environment');
|
||||
|
||||
if (check.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { data: result } = await github.rest.checks.get({
|
||||
...context.repo,
|
||||
check_run_id: check[0].id,
|
||||
});
|
||||
|
||||
return result.conclusion;
|
||||
- name: Context
|
||||
if: steps.check-conclusion.outputs.result == 'success'
|
||||
uses: okteto/context@latest
|
||||
with:
|
||||
token: ${{ secrets.OKTETO_TOKEN }}
|
||||
|
||||
- name: Destroy preview environment
|
||||
if: steps.check-conclusion.outputs.result == 'success'
|
||||
uses: okteto/destroy-preview@latest
|
||||
with:
|
||||
name: pr-${{ github.event.number }}-syuilo
|
6
.github/workflows/storybook.yml
vendored
6
.github/workflows/storybook.yml
vendored
@ -26,12 +26,12 @@ jobs:
|
||||
NODE_OPTIONS: "--max_old_space_size=7168"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.1
|
||||
- uses: actions/checkout@v4.2.2
|
||||
if: github.event_name != 'pull_request_target'
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: true
|
||||
- uses: actions/checkout@v4.1.1
|
||||
- uses: actions/checkout@v4.2.2
|
||||
if: github.event_name == 'pull_request_target'
|
||||
with:
|
||||
fetch-depth: 0
|
||||
@ -46,7 +46,7 @@ jobs:
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
- name: Use Node.js 20.x
|
||||
uses: actions/setup-node@v4.1.0
|
||||
uses: actions/setup-node@v4.2.0
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
cache: 'pnpm'
|
||||
|
8
.github/workflows/test-backend.yml
vendored
8
.github/workflows/test-backend.yml
vendored
@ -45,7 +45,7 @@ jobs:
|
||||
- 56312:6379
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.1
|
||||
- uses: actions/checkout@v4.2.2
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install pnpm
|
||||
@ -66,7 +66,7 @@ jobs:
|
||||
fi
|
||||
done
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4.1.0
|
||||
uses: actions/setup-node@v4.2.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'pnpm'
|
||||
@ -108,13 +108,13 @@ jobs:
|
||||
- 56312:6379
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.1
|
||||
- uses: actions/checkout@v4.2.2
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4.1.0
|
||||
uses: actions/setup-node@v4.2.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'pnpm'
|
||||
|
2
.github/workflows/test-federation.yml
vendored
2
.github/workflows/test-federation.yml
vendored
@ -47,7 +47,7 @@ jobs:
|
||||
fi
|
||||
done
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4.1.0
|
||||
uses: actions/setup-node@v4.2.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'pnpm'
|
||||
|
8
.github/workflows/test-frontend.yml
vendored
8
.github/workflows/test-frontend.yml
vendored
@ -36,13 +36,13 @@ jobs:
|
||||
node-version: [22.11.0]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.1
|
||||
- uses: actions/checkout@v4.2.2
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4.1.0
|
||||
uses: actions/setup-node@v4.2.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'pnpm'
|
||||
@ -86,7 +86,7 @@ jobs:
|
||||
- 56312:6379
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.1
|
||||
- uses: actions/checkout@v4.2.2
|
||||
with:
|
||||
submodules: true
|
||||
# https://github.com/cypress-io/cypress-docker-images/issues/150
|
||||
@ -98,7 +98,7 @@ jobs:
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4.1.0
|
||||
uses: actions/setup-node@v4.2.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'pnpm'
|
||||
|
4
.github/workflows/test-misskey-js.yml
vendored
4
.github/workflows/test-misskey-js.yml
vendored
@ -31,12 +31,12 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4.1.1
|
||||
uses: actions/checkout@v4.2.2
|
||||
|
||||
- run: corepack enable
|
||||
|
||||
- name: Setup Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4.1.0
|
||||
uses: actions/setup-node@v4.2.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'pnpm'
|
||||
|
4
.github/workflows/test-production.yml
vendored
4
.github/workflows/test-production.yml
vendored
@ -21,13 +21,13 @@ jobs:
|
||||
node-version: [22.11.0]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.1
|
||||
- uses: actions/checkout@v4.2.2
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4.1.0
|
||||
uses: actions/setup-node@v4.2.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'pnpm'
|
||||
|
4
.github/workflows/validate-api-json.yml
vendored
4
.github/workflows/validate-api-json.yml
vendored
@ -25,13 +25,13 @@ jobs:
|
||||
node-version: [22.11.0]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.1
|
||||
- uses: actions/checkout@v4.2.2
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4.1.0
|
||||
uses: actions/setup-node@v4.2.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'pnpm'
|
||||
|
41
CHANGELOG.md
41
CHANGELOG.md
@ -1,3 +1,44 @@
|
||||
## 2025.2.1
|
||||
|
||||
### General
|
||||
- Feat: アクセストークン発行時に通知するように
|
||||
- Feat: 実験的なGoogleAnalyticsサポートを追加
|
||||
- 依存関係の更新
|
||||
|
||||
### Client
|
||||
- Feat: 投稿フォームで画像をプレビュー可能に
|
||||
- Enhance: 投稿フォームの「迷惑になる可能性があります」のダイアログを表示する条件においてCWを考慮するように
|
||||
- Enhance: アンテナ、リスト等の名前をカラム名のデフォルト値にするように `#13992`
|
||||
- Enhance: クライアントエラー画面の多言語対応
|
||||
- Enhance: 開発者モードでメニューからファイルIDをコピー出来るように `#15441'
|
||||
- Enhance: ノートに埋め込まれたメディアのコンテキストメニューから管理者用のファイル管理画面を開けるように ( #15440 )
|
||||
- Enhance: リアクションする際に確認ダイアログを表示できるように
|
||||
- Enhance: コントロールパネルのユーザ検索で入力された情報をページ遷移で損なわないように `#15437`
|
||||
- Enhance: CWの注釈で入力済みの文字数を表示
|
||||
- Enhance: ノート検索ページのデザイン調整
|
||||
(Cherry-picked from https://github.com/taiyme/misskey/pull/273)
|
||||
- Fix: ノートページで、クリップ一覧が表示されないことがある問題を修正
|
||||
- Fix: コンディショナルロールを手動で割り当てできる導線を削除 `#13529`
|
||||
- Fix: 埋め込みプレイヤーから外部ページに移動できない問題を修正
|
||||
- Fix: Play の再読込時に UI が以前の状態を引き継いでしまう問題を修正 `#14378`
|
||||
- Fix: カスタム絵文字管理画面(beta)にてisSensitive/localOnlyの絞り込みが上手くいかない問題の修正 ( #15445 )
|
||||
- Fix: ユーザのサジェスト中に@を入力してもサジェスト結果が消えないように `#14385`
|
||||
- Fix: CWの注釈が100文字を超えている場合、ノート投稿ボタンを非アクティブに
|
||||
- Fix: テーマ選択で現在のテーマが初期表示されていない問題を修正
|
||||
- 翻訳の更新
|
||||
|
||||
### Server
|
||||
- Enhance: 成り済まし対策として、ActivityPub照会された時にリモートのリダイレクトを拒否できるように (config.disallowExternalApRedirect)
|
||||
- Fix: `following/invalidate`でフォロワーを解除しようとしているユーザーの情報を返すように
|
||||
- Fix: オブジェクトストレージの設定でPrefixを設定していなかった場合nullまたは空文字になる問題を修正
|
||||
- Fix: HTTPプロキシとその除外設定を行った状態でカスタム絵文字の一括インポートをしたとき、除外設定が効かないのを修正( #8766 )
|
||||
- Fix: pgroongaでの検索時にはじめのキーワードのみが検索に使用される問題を修正
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/886)
|
||||
- Fix: メールアドレスの形式が正しくなければ以降の処理を行わないように
|
||||
- Fix: `update-meta`でobjectStoragePrefixにS3_SAFEかつURL-safeでない文字列を使えないように
|
||||
- Fix: クリップの説明欄を更新する際に空にできない問題を修正
|
||||
- Fix: フォロワーではないユーザーにリノートもしくは返信された場合にノートのDeleteアクティビティが送られていない問題を修正
|
||||
|
||||
## 2025.2.0
|
||||
|
||||
### General
|
||||
|
@ -1584,3 +1584,7 @@ _offlineScreen:
|
||||
_remoteLookupErrors:
|
||||
_noSuchObject:
|
||||
title: "غير موجود"
|
||||
_search:
|
||||
searchScopeAll: "الكل"
|
||||
searchScopeLocal: "المحلي"
|
||||
searchScopeUser: "مستخدم محدد"
|
||||
|
@ -1348,3 +1348,6 @@ _reversi:
|
||||
_remoteLookupErrors:
|
||||
_noSuchObject:
|
||||
title: "পাওয়া যায়নি"
|
||||
_search:
|
||||
searchScopeAll: "সবগুলো"
|
||||
searchScopeLocal: "স্থানীয়"
|
||||
|
@ -66,7 +66,7 @@ copyFolderId: "Copiar ID de la carpeta"
|
||||
copyProfileUrl: "Copiar adreça URL del perfil"
|
||||
searchUser: "Cercar un usuari"
|
||||
searchThisUsersNotes: "Cercar les publicacions de l'usuari"
|
||||
reply: "Respon"
|
||||
reply: "Respostes"
|
||||
loadMore: "Carregar més"
|
||||
showMore: "Veure més"
|
||||
showLess: "Mostrar menys"
|
||||
@ -111,7 +111,7 @@ followRequests: "Peticions de seguiment"
|
||||
unfollow: "Deixar de seguir"
|
||||
followRequestPending: "Sol·licituds de seguiment pendents"
|
||||
enterEmoji: "Introduir un emoji"
|
||||
renote: "Impulsar "
|
||||
renote: "Impulsos"
|
||||
unrenote: "Anul·la l'impuls"
|
||||
renoted: "S'ha impulsat"
|
||||
renotedToX: "Impulsat per {name}."
|
||||
@ -646,7 +646,7 @@ disablePlayer: "Tanca el reproductor de vídeo"
|
||||
expandTweet: "Expandir post"
|
||||
themeEditor: "Editor de temes"
|
||||
description: "Descripció"
|
||||
describeFile: "Afegir subtitulació"
|
||||
describeFile: "Afegeix una descripció "
|
||||
enterFileDescription: "Escriu un peu de foto"
|
||||
author: "Autor"
|
||||
leaveConfirm: "Hi ha canvis sense guardar. Els vols descartar?"
|
||||
@ -1189,8 +1189,8 @@ currentAnnouncements: "Informes actuals"
|
||||
pastAnnouncements: "Informes passats"
|
||||
youHaveUnreadAnnouncements: "Tens informes per llegir."
|
||||
useSecurityKey: "Segueix les instruccions del teu navegador O dispositiu per fer servir el teu passkey."
|
||||
replies: "Respon"
|
||||
renotes: "Impulsar "
|
||||
replies: "Respostes"
|
||||
renotes: "Impulsos"
|
||||
loadReplies: "Mostrar les respostes"
|
||||
loadConversation: "Mostrar la conversació "
|
||||
pinnedList: "Llista fixada"
|
||||
@ -1309,6 +1309,8 @@ availableRoles: "Roles disponibles "
|
||||
acknowledgeNotesAndEnable: "Activa'l després de comprendre els possibles perills."
|
||||
federationSpecified: "Aquest servidor treballa amb una federació de llistes blanques. No pot interactuar amb altres servidors que no siguin els especificats per l'administrador."
|
||||
federationDisabled: "La unió es troba deshabilitada en aquest servidor. No es pot interactuar amb usuaris d'altres servidors."
|
||||
confirmOnReact: "Confirmar en reaccionar"
|
||||
reactAreYouSure: "Vols reaccionar amb \"{emoji}\"?"
|
||||
_accountSettings:
|
||||
requireSigninToViewContents: "És obligatori l'inici de sessió per poder veure el contingut"
|
||||
requireSigninToViewContentsDescription1: "Es requereix l'inici de sessió per poder veure totes les notes i el contingut que has creat. Amb això esperem evitar que els rastrejadors recopilin informació."
|
||||
@ -2440,6 +2442,8 @@ _notification:
|
||||
flushNotification: "Netejar notificacions"
|
||||
exportOfXCompleted: "Completada l'exportació de {x}"
|
||||
login: "Algú ha iniciat sessió "
|
||||
createToken: "Token d'accés generat"
|
||||
createTokenDescription: "Si no saps què és, esborra el token des de {text}."
|
||||
_types:
|
||||
all: "Tots"
|
||||
note: "Notes noves"
|
||||
@ -2822,8 +2826,6 @@ _remoteLookupErrors:
|
||||
_responseInvalid:
|
||||
title: "La resposta no és correcta "
|
||||
description: "Hem pogut comunicar-nos amb aquest servidor, però les dades rebudes no són correctes."
|
||||
_responseInvalidIdHostNotMatch:
|
||||
description: "El domini de l'adreça introduïda no és el mateix que el domini de l'adreça final obtinguda. Si estàs consultant continguts remots mitjançant servidors tercers, torna a fer la consulta fent servir l'adreça que es pot obtenir en el servidor origen."
|
||||
_noSuchObject:
|
||||
title: "No s'ha trobat"
|
||||
description: "No es pot trobar el recurs sol·licitat, si us plau comprova l'adreça una altra vegada."
|
||||
@ -2840,3 +2842,23 @@ _captcha:
|
||||
_unknown:
|
||||
title: "Error CAPTCHA"
|
||||
text: "S'ha produït un error inesperat."
|
||||
_bootErrors:
|
||||
title: "Hi ha hagut en error en carregar"
|
||||
serverError: "Si el problema persisteix després d'esperar una mica i recarregar, posa't en contacte amb l'administrador del servidor amb el següent codi d'error."
|
||||
solution: "Per intentar resoldre el problema pots fer el següent."
|
||||
solution1: "Actualitza el navegador i el sistema operatiu a l'última versió "
|
||||
solution2: "Desactiva els adblockers"
|
||||
solution3: "Esborra la memòria cau del navegador"
|
||||
solution4: "(Navegador Tor) configura dom.webaudio.enabled a true"
|
||||
otherOption: "Altres opcions"
|
||||
otherOption1: "Esborrar la configuració i la memòria cau del client"
|
||||
otherOption2: "Iniciar client senzill"
|
||||
otherOption3: "Iniciar l'eina de reparació "
|
||||
_search:
|
||||
searchScopeAll: "Tot"
|
||||
searchScopeLocal: "Local"
|
||||
searchScopeServer: "Instància "
|
||||
searchScopeUser: "Especificar usuari"
|
||||
pleaseEnterServerHost: "Introdueix l'adreça de la instància "
|
||||
pleaseSelectUser: "Selecciona un usuari"
|
||||
serverHostPlaceholder: "Ex: misskey.example.com"
|
||||
|
@ -2024,3 +2024,7 @@ _reversi:
|
||||
_remoteLookupErrors:
|
||||
_noSuchObject:
|
||||
title: "Nenalezeno"
|
||||
_search:
|
||||
searchScopeAll: "Vše"
|
||||
searchScopeLocal: "Místní"
|
||||
searchScopeUser: "Upřesnit uživatele"
|
||||
|
@ -1383,6 +1383,9 @@ _initialTutorial:
|
||||
title: "Was sind Notizen?"
|
||||
description: "Beiträge auf Misskey heißen \"Notizen\". Notizen werden chronologisch in der Chronik angeordnet und in Echtzeit aktualisiert."
|
||||
reply: "Klicke auf diesen Button, um auf eine Nachricht zu antworten. Es ist auch möglich, auf Antworten zu antworten und die Unterhaltung wie einen Thread fortzusetzen."
|
||||
renote: "Du kannst diese Notiz in deiner eigenen Chronik teilen. Du kannst sie auch mit deinen Kommentaren zitieren."
|
||||
reaction: "Du kannst der Notiz Reaktionen hinzufügen. Weitere Einzelheiten werden auf der nächsten Seite erläutert."
|
||||
menu: "Du kannst Details zu Notizen anzeigen, Links kopieren und verschiedene andere Aktionen durchführen."
|
||||
_reaction:
|
||||
title: "Was sind Reaktionen?"
|
||||
description: "Auf Notizen kann mit verschiedenen Emojis reagiert werden. Reaktionen ermöglichen es dir, Nuancen auszudrücken, die mit einem einfachen „Gefällt mir“ vielleicht nicht ausgedrückt werden können."
|
||||
@ -1401,13 +1404,21 @@ _initialTutorial:
|
||||
_visibility:
|
||||
description: "Du kannst einschränken, wer deine Notiz sehen kann."
|
||||
public: "Deine Notiz wird für alle Nutzer sichtbar sein."
|
||||
direct: "Die Notiz wird nur für den angegebenen Benutzer veröffentlicht und der Empfänger wird benachrichtigt. Kann anstelle von Direktnachrichten verwendet werden."
|
||||
doNotSendConfidencialOnDirect1: "Sei vorsichtig, wenn du sensible Informationen verschickst!"
|
||||
doNotSendConfidencialOnDirect2: "Die Administratoren des Servers können den Inhalt der Notiz sehen. Sei vorsichtig mit sensiblen Informationen, wenn du Direktnachrichten an Benutzer auf nicht vertrauenswürdigen Servern sendest."
|
||||
localOnly: "Wenn du eine Notiz mit dieser Einstellung veröffentlichst, wird sie nicht an andere Server weitergeleitet. Benutzer auf anderen Servern können diese Notizen nicht direkt sehen, unabhängig von den obigen Anzeigeeinstellungen."
|
||||
_cw:
|
||||
title: "Inhaltswarnung"
|
||||
description: "Anstelle des Textes wird das angezeigt, was du im Abschnitt „Anmerkungen“ angibst. Drücke auf „Inhalt anzeigen“, um den vollständigen Text zu sehen."
|
||||
_exampleNote:
|
||||
cw: "Das wird dich bestimmt hungrig machen!"
|
||||
note: "Ich hatte gerade einen Donut mit Schokoladenüberzug 🍩😋"
|
||||
_howToMakeAttachmentsSensitive:
|
||||
title: "Wie markiert man Anhänge als sensibel?"
|
||||
tryThisFile: "Versuche, das angehängte Bild als sensibel zu markieren!"
|
||||
_exampleNote:
|
||||
note: "Ups, ich habe es vergeigt, den Natto-Deckel zu öffnen..."
|
||||
method: "Um einen Anhang als sensibel zu kennzeichnen, klicke auf das Vorschaubild der Datei, um das Menü zu öffnen, und klicke auf „Als sensibel markieren“."
|
||||
sensitiveSucceeded: "Wenn du Dateien anhängst, stelle bitte die Sensibilität entsprechend der Serverrichtlinien ein."
|
||||
doItToContinue: "Markiere die angehängte Datei als sensibel, um fortzufahren."
|
||||
@ -1431,6 +1442,7 @@ _serverSettings:
|
||||
fanoutTimelineDescription: "Ist diese Option aktiviert, kann eine erhebliche Verbesserung im Abrufen von Chroniken und eine Reduzierung der Datenbankbelastung erzielt werden, im Gegenzug zu einer Steigerung in der Speichernutzung von Redis. Bei geringem Serverspeicher oder Serverinstabilität kann diese Option deaktiviert werden."
|
||||
fanoutTimelineDbFallback: "Auf die Datenbank zurückfallen"
|
||||
fanoutTimelineDbFallbackDescription: "Ist diese Option aktiviert, wird die Chronik auf zusätzliche Abfragen in der Datenbank zurückgreifen, wenn sich die Chronik nicht im Cache befindet. Eine Deaktivierung führt zu geringerer Serverlast, aber schränkt den Zeitraum der abrufbaren Chronik ein. "
|
||||
reactionsBufferingDescription: "Wenn diese Option aktiviert ist, kann sie die Leistung beim Erstellen von Reaktionen erheblich verbessern und die Belastung der Datenbank verringern. Allerdings steigt die Speichernutzung von Redis."
|
||||
openRegistrationWarning: "Das Aktivieren von Registrierungen ist riskant. Es wird empfohlen, sie nur dann zu aktivieren, wenn der Server ständig überwacht wird und im Falle eines Problems sofort reagiert werden kann."
|
||||
thisSettingWillAutomaticallyOffWhenModeratorsInactive: "Wenn über einen bestimmten Zeitraum keine Moderatorenaktivität festgestellt wird, wird diese Einstellung automatisch deaktiviert, um Spam zu verhindern."
|
||||
_accountMigration:
|
||||
@ -1835,6 +1847,7 @@ _plugin:
|
||||
installWarn: "Installiere bitte nur vertrauenswürdige Plugins."
|
||||
manage: "Plugins verwalten"
|
||||
viewSource: "Quelltext anzeigen"
|
||||
viewLog: "Protokoll anzeigen"
|
||||
_preferencesBackups:
|
||||
list: "Erstellte Backups"
|
||||
saveNew: "Neu erstellen"
|
||||
@ -1864,6 +1877,8 @@ _aboutMisskey:
|
||||
contributors: "Hauptmitwirkende"
|
||||
allContributors: "Alle Mitwirkenden"
|
||||
source: "Quellcode"
|
||||
original: "Original"
|
||||
thisIsModifiedVersion: "{name} verwendet eine modifizierte Version des ursprünglichen Misskey."
|
||||
translation: "Misskey übersetzen"
|
||||
donate: "An Misskey spenden"
|
||||
morePatrons: "Wir schätzen ebenso die Unterstützung vieler anderer hier nicht gelisteter Personen sehr. Danke! 🥰"
|
||||
@ -1989,6 +2004,8 @@ _soundSettings:
|
||||
driveFileTypeWarn: "Diese Datei wird nicht unterstützt"
|
||||
driveFileTypeWarnDescription: "Bitte wähle eine Audiodatei"
|
||||
driveFileDurationWarn: "Audio zu lang."
|
||||
driveFileDurationWarnDescription: "Lange Töne kann die Verwendung von Misskey stören. Trotzdem fortfahren?"
|
||||
driveFileError: "Audio konnte nicht geladen werden. Bitte ändere die Einstellung."
|
||||
_ago:
|
||||
future: "Zukunft"
|
||||
justNow: "Gerade eben"
|
||||
@ -2000,6 +2017,10 @@ _ago:
|
||||
monthsAgo: "vor {n} Monat(en)"
|
||||
yearsAgo: "vor {n} Jahr(en)"
|
||||
invalid: "Ungültig"
|
||||
_timeIn:
|
||||
seconds: "In {n}s"
|
||||
minutes: "In {n} Min."
|
||||
hours: "In {n} Std."
|
||||
_time:
|
||||
second: "Sekunde(n)"
|
||||
minute: "Minute(n)"
|
||||
@ -2105,6 +2126,7 @@ _auth:
|
||||
permissionAsk: "Diese Anwendung fordert folgende Berechtigungen"
|
||||
pleaseGoBack: "Bitte kehre zur Anwendung zurück"
|
||||
callback: "Es wird zur Anwendung zurückgekehrt"
|
||||
accepted: "Zugriff gewährt"
|
||||
denied: "Zugriff verweigert"
|
||||
pleaseLogin: "Bitte logge dich ein, um Apps zu authorisieren."
|
||||
_antennaSources:
|
||||
@ -2215,6 +2237,7 @@ _profile:
|
||||
changeBanner: "Banner ändern"
|
||||
verifiedLinkDescription: "Gibst du hier eine URL ein, die einen Link zu deinem Profile enthält, wird neben diesem Feld ein Icon zur Besitzbestätigung angezeigt."
|
||||
avatarDecorationMax: "Du kannst bis zu {max} Dekorationen hinzufügen."
|
||||
followedMessage: "Nachricht, wenn dir jemand folgt"
|
||||
followedMessageDescription: "Du kannst eine kurze Nachricht festlegen, die dem Empfänger angezeigt wird, wenn er dir folgt."
|
||||
_exportOrImport:
|
||||
allNotes: "Alle Notizen"
|
||||
@ -2343,8 +2366,11 @@ _notification:
|
||||
sendTestNotification: "Testbenachrichtigung senden"
|
||||
notificationWillBeDisplayedLikeThis: "Benachrichtigungen sehen so aus"
|
||||
reactedBySomeUsers: "{n} Benutzer haben eine Reaktion geschickt"
|
||||
likedBySomeUsers: "{n} Benutzer mochten deine Notiz"
|
||||
renotedBySomeUsers: "Renote von {n} Benutzern"
|
||||
followedBySomeUsers: "Von {n} Benutzern gefolgt"
|
||||
flushNotification: "Benachrichtigungen löschen"
|
||||
exportOfXCompleted: "Der Export von {x} ist abgeschlossen"
|
||||
login: "Neue Anmeldung erfolgt"
|
||||
_types:
|
||||
all: "Alle"
|
||||
@ -2360,7 +2386,9 @@ _notification:
|
||||
followRequestAccepted: "Akzeptierte Follow-Anfragen"
|
||||
roleAssigned: "Rolle zugewiesen"
|
||||
achievementEarned: "Errungenschaft freigeschaltet"
|
||||
login: "Anmelden"
|
||||
exportCompleted: "Der Export ist abgeschlossen"
|
||||
login: "Anmeldung"
|
||||
test: "Test-Benachrichtigungen"
|
||||
app: "Benachrichtigungen von Apps"
|
||||
_actions:
|
||||
followBack: "folgt dir nun auch"
|
||||
@ -2370,6 +2398,7 @@ _deck:
|
||||
alwaysShowMainColumn: "Hauptspalte immer zeigen"
|
||||
columnAlign: "Spaltenausrichtung"
|
||||
addColumn: "Spalte hinzufügen"
|
||||
newNoteNotificationSettings: "Benachrichtigungseinstellungen für neue Notizen"
|
||||
configureColumn: "Spalteneinstellungen"
|
||||
swapLeft: "Mit linker Spalte tauschen"
|
||||
swapRight: "Mit rechter Spalte tauschen"
|
||||
@ -2408,6 +2437,7 @@ _drivecleaner:
|
||||
orderByCreatedAtAsc: "Aufsteigendes Erstelldatum"
|
||||
_webhookSettings:
|
||||
createWebhook: "Webhook erstellen"
|
||||
modifyWebhook: "Webhook bearbeiten"
|
||||
name: "Name"
|
||||
secret: "Secret"
|
||||
trigger: "Auslöser"
|
||||
@ -2420,12 +2450,22 @@ _webhookSettings:
|
||||
renote: "Wenn du ein Renote erhältst"
|
||||
reaction: "Wenn du eine Reaktion erhältst"
|
||||
mention: "Wenn du erwähnt wirst"
|
||||
deleteConfirm: "Bist du sicher, dass du den Webhook löschen willst?"
|
||||
_abuseReport:
|
||||
_notificationRecipient:
|
||||
createRecipient: "Meldungsempfänger hinzufügen"
|
||||
modifyRecipient: "Bearbeite einen Empfänger für Meldungen"
|
||||
recipientType: "Art der Benachrichtigung"
|
||||
_recipientType:
|
||||
mail: "Email"
|
||||
webhook: "Webhook"
|
||||
_captions:
|
||||
mail: "Die Benachrichtigung wird bei Eingang einer Meldung an die E-Mail-Adressen der Moderatoren gesendet"
|
||||
webhook: "Sendet eine Benachrichtigung an den System Webhook, wenn eine Meldung eingegangen ist oder gelöst wurde"
|
||||
keywords: "Schlüsselwort"
|
||||
notifiedUser: "Zu benachrichtigender Benutzer"
|
||||
notifiedWebhook: "Zu verwendender Webhook"
|
||||
deleteConfirm: "Bist du sicher, dass du den Empfänger der Benachrichtigung entfernen möchtest?"
|
||||
_moderationLogTypes:
|
||||
createRole: "Rolle erstellt"
|
||||
deleteRole: "Rolle gelöscht"
|
||||
@ -2465,6 +2505,7 @@ _moderationLogTypes:
|
||||
createSystemWebhook: "System-Webhook erstellt"
|
||||
updateSystemWebhook: "System-Webhook aktualisiert"
|
||||
deleteSystemWebhook: "System-Webhook gelöscht"
|
||||
deleteAccount: "Benutzerkonto gelöscht"
|
||||
deletePage: "Seite gelöscht"
|
||||
deleteGalleryPost: "Galeriebeitrag gelöscht"
|
||||
_fileViewer:
|
||||
@ -2573,3 +2614,7 @@ _remoteLookupErrors:
|
||||
_noSuchObject:
|
||||
title: "Nicht gefunden"
|
||||
description: "Die angeforderte Ressource konnte nicht gefunden werden, bitte überprüfe die URI erneut."
|
||||
_search:
|
||||
searchScopeAll: "Alle"
|
||||
searchScopeLocal: "Lokal"
|
||||
searchScopeUser: "Spezifischer Benutzer"
|
||||
|
@ -397,3 +397,5 @@ _moderationLogTypes:
|
||||
suspend: "Αποβολή"
|
||||
_reversi:
|
||||
total: "Σύνολο"
|
||||
_search:
|
||||
searchScopeLocal: "Τοπικό"
|
||||
|
@ -132,7 +132,7 @@ reaction: "Reactions"
|
||||
reactions: "Reactions"
|
||||
emojiPicker: "Emoji picker"
|
||||
pinnedEmojisForReactionSettingDescription: "Set the emojis to be pinned and displayed when reacting."
|
||||
pinnedEmojisSettingDescription: "Set the emojis to be pinned and displayed when viewing emoji picker."
|
||||
pinnedEmojisSettingDescription: "Set the emojis to be pinned and displayed when viewing emoji picker"
|
||||
emojiPickerDisplay: "Emoji picker display"
|
||||
overwriteFromPinnedEmojisForReaction: "Override from reaction settings"
|
||||
overwriteFromPinnedEmojis: "Override from general settings"
|
||||
@ -586,7 +586,7 @@ popout: "Pop-out"
|
||||
volume: "Volume"
|
||||
masterVolume: "Master volume"
|
||||
notUseSound: "Disable sound"
|
||||
useSoundOnlyWhenActive: "Output sounds only if Misskey is active."
|
||||
useSoundOnlyWhenActive: "Output sounds only if Misskey is active"
|
||||
details: "Details"
|
||||
renoteDetails: "Renote details"
|
||||
chooseEmoji: "Select an emoji"
|
||||
@ -1261,7 +1261,7 @@ copyReplayData: "Copy replay data"
|
||||
ranking: "Ranking"
|
||||
lastNDays: "Last {n} days"
|
||||
backToTitle: "Go back to title"
|
||||
hemisphere: "Where are you located"
|
||||
hemisphere: "Where you live"
|
||||
withSensitive: "Include notes with sensitive files"
|
||||
userSaysSomethingSensitive: "Post by {name} contains sensitive content"
|
||||
enableHorizontalSwipe: "Swipe to switch tabs"
|
||||
@ -1309,6 +1309,8 @@ availableRoles: "Available roles"
|
||||
acknowledgeNotesAndEnable: "Turn on after understanding the precautions."
|
||||
federationSpecified: "This server is operated in a whitelist federation. Interacting with servers other than those designated by the administrator is not allowed."
|
||||
federationDisabled: "Federation is disabled on this server. You cannot interact with users on other servers."
|
||||
confirmOnReact: "Confirm when reacting"
|
||||
reactAreYouSure: "Would you like to add a \"{emoji}\" reaction?"
|
||||
_accountSettings:
|
||||
requireSigninToViewContents: "Require sign-in to view contents"
|
||||
requireSigninToViewContentsDescription1: "Require login to view all notes and other content you have created. This will have the effect of preventing crawlers from collecting your information."
|
||||
@ -2440,6 +2442,8 @@ _notification:
|
||||
flushNotification: "Clear notifications"
|
||||
exportOfXCompleted: "Export of {x} has been completed"
|
||||
login: "Someone logged in"
|
||||
createToken: "An access token has been created"
|
||||
createTokenDescription: "If you have no idea, delete the access token through \"{text}\"."
|
||||
_types:
|
||||
all: "All"
|
||||
note: "New notes"
|
||||
@ -2822,8 +2826,6 @@ _remoteLookupErrors:
|
||||
_responseInvalid:
|
||||
title: "Response is invalid"
|
||||
description: "It could communicate with this server, but the data obtained was incorrect."
|
||||
_responseInvalidIdHostNotMatch:
|
||||
description: "The domain of the entered URI differs from the domain of the final obtained URI. If you are looking up remote content through a third-party server, please look up again using a URI that can be obtained from the origin server."
|
||||
_noSuchObject:
|
||||
title: "Not found"
|
||||
description: "The requested resource was not found, please recheck the URI."
|
||||
@ -2840,3 +2842,19 @@ _captcha:
|
||||
_unknown:
|
||||
title: "CAPTCHA error"
|
||||
text: "An unexpected error occurred."
|
||||
_bootErrors:
|
||||
title: "Failed to load"
|
||||
serverError: "If the problem persists after waiting a moment and reloading, please contact the server administrator with the following Error ID."
|
||||
solution: "The following may solve the problem."
|
||||
solution1: "Update your browser and OS to the latest version"
|
||||
solution2: "Disable ad blocker"
|
||||
solution3: "Clear the browser cache"
|
||||
solution4: "Set the dom.webaudio.enabled to true for Tor Browser"
|
||||
otherOption: "Other options"
|
||||
otherOption1: "Delete client settings and cache"
|
||||
otherOption2: "Start the simple client"
|
||||
otherOption3: "Launch the repair tool"
|
||||
_search:
|
||||
searchScopeAll: "All"
|
||||
searchScopeLocal: "Local"
|
||||
searchScopeUser: "Specific user"
|
||||
|
@ -605,6 +605,7 @@ descendingOrder: "Descendente"
|
||||
scratchpad: "Scratch pad"
|
||||
scratchpadDescription: "Scratchpad proporciona un entorno experimental para AiScript. Puede escribir, ejecutar y verificar los resultados que interactúan con Misskey."
|
||||
uiInspector: "Inspector de UI"
|
||||
uiInspectorDescription: "Puedes visualizar una lista de elementos UI presentes en la memoria. Los componentes de la interfaz de usuario son generados por las funciones UI:C:"
|
||||
output: "Salida"
|
||||
script: "Script"
|
||||
disablePagesScript: "Deshabilitar AiScript en Páginas"
|
||||
@ -693,6 +694,7 @@ regexpError: "Error de la expresión regular"
|
||||
regexpErrorDescription: "Ocurrió un error en la expresión regular en la linea {line} de las palabras muteadas {tab}"
|
||||
instanceMute: "Instancias silenciadas"
|
||||
userSaysSomething: "{name} dijo algo"
|
||||
userSaysSomethingAbout: "{name} dijo algo sobre {word}"
|
||||
makeActive: "Activar"
|
||||
display: "Apariencia"
|
||||
copy: "Copiar"
|
||||
@ -861,6 +863,7 @@ administration: "Administrar"
|
||||
accounts: "Cuentas"
|
||||
switch: "Cambiar"
|
||||
noMaintainerInformationWarning: "No se ha establecido la información del administrador"
|
||||
noInquiryUrlWarning: "No se ha guardado la URL de consulta."
|
||||
noBotProtectionWarning: "La protección contra los bots no está configurada"
|
||||
configure: "Configurar"
|
||||
postToGallery: "Crear una nueva publicación en la galería"
|
||||
@ -925,6 +928,7 @@ followersVisibility: "Visibilidad de seguidores"
|
||||
continueThread: "Ver la continuación del hilo"
|
||||
deleteAccountConfirm: "La cuenta será borrada. ¿Está seguro?"
|
||||
incorrectPassword: "La contraseña es incorrecta"
|
||||
incorrectTotp: "La contraseña de un solo uso es incorrecta o ha caducado."
|
||||
voteConfirm: "¿Confirma su voto a {choice}?"
|
||||
hide: "Ocultar"
|
||||
useDrawerReactionPickerForMobile: "Mostrar panel de reacciones en móviles"
|
||||
@ -1053,6 +1057,7 @@ thisPostMayBeAnnoyingHome: "Publicar en línea de tiempo 'Inicio'"
|
||||
thisPostMayBeAnnoyingCancel: "detener"
|
||||
thisPostMayBeAnnoyingIgnore: "Publicar de todos modos"
|
||||
collapseRenotes: "Colapsar renotas que ya hayas visto"
|
||||
collapseRenotesDescription: "Contrae notas a las que ya has reaccionado o renotado "
|
||||
internalServerError: "Error interno del servidor"
|
||||
internalServerErrorDescription: "El servidor tuvo un error inesperado."
|
||||
copyErrorInfo: "Copiar detalles del error"
|
||||
@ -1091,6 +1096,7 @@ retryAllQueuesConfirmTitle: "Desea ¿reintentar inmediatamente todas las colas?"
|
||||
retryAllQueuesConfirmText: "La carga del servidor está incrementándose temporalmente "
|
||||
enableChartsForRemoteUser: "Generar gráficas de usuarios remotos."
|
||||
enableChartsForFederatedInstances: "Generar gráficos de servidores remotos"
|
||||
enableStatsForFederatedInstances: "Activar las estadísticas de las instancias remotas federadas"
|
||||
showClipButtonInNoteFooter: "Añadir \"Clip\" al menú de notas"
|
||||
reactionsDisplaySize: "Tamaño de las reacciones"
|
||||
limitWidthOfReaction: "Limitar ancho de las reacciones"
|
||||
@ -1139,6 +1145,7 @@ preventAiLearningDescription: "Pedirle a las arañas (crawlers) no usar los text
|
||||
options: "Opción"
|
||||
specifyUser: "Especificar usuario"
|
||||
lookupConfirm: "¿Quiere informarse?"
|
||||
openTagPageConfirm: "¿Quieres abrir la página de etiquetas?"
|
||||
specifyHost: "Especificar Host"
|
||||
failedToPreviewUrl: "No se pudo generar la vista previa"
|
||||
update: "Actualizar"
|
||||
@ -1267,13 +1274,29 @@ useBackupCode: "Usar códigos de respaldo"
|
||||
launchApp: "Ejecutar la app"
|
||||
useNativeUIForVideoAudioPlayer: "Usar la interfaz del navegador cuando se reproduce audio y vídeo"
|
||||
keepOriginalFilename: "Mantener el nombre original del archivo"
|
||||
keepOriginalFilenameDescription: "Si desactivas esta opción, los nombres de los archivos serán remplazados por una cadena de caracteres aleatoria cuando subas los archivos."
|
||||
noDescription: "No hay descripción"
|
||||
alwaysConfirmFollow: "Confirmar siempre cuando se sigue a alguien"
|
||||
inquiry: "Contacto"
|
||||
tryAgain: "Por favor , inténtalo de nuevo"
|
||||
confirmWhenRevealingSensitiveMedia: "Confirmación cuando se revele contenido sensible"
|
||||
sensitiveMediaRevealConfirm: "Esto puede contener contenido sensible. ¿Estás seguro/a de querer mostrarlo?"
|
||||
createdLists: "Listas creadas"
|
||||
createdAntennas: "Antenas creadas"
|
||||
fromX: "De {x}"
|
||||
genEmbedCode: "Obtener el código para incrustar"
|
||||
noteOfThisUser: "Notas de este usuario"
|
||||
clipNoteLimitExceeded: "No se pueden añadir más notas a este clip."
|
||||
performance: "Rendimiento"
|
||||
modified: "Modificado"
|
||||
discard: "Descartar"
|
||||
thereAreNChanges: "Hay {n} cambio(s)"
|
||||
signinWithPasskey: "Iniciar sesión con clave de acceso"
|
||||
unknownWebAuthnKey: "Esto no se ha registrado llave maestra."
|
||||
passkeyVerificationFailed: "La verificación de la clave de acceso ha fallado."
|
||||
passkeyVerificationSucceededButPasswordlessLoginDisabled: "La verificación de la clave de acceso ha sido satisfactoria pero se ha deshabilitado el inicio de sesión sin contraseña."
|
||||
messageToFollower: "Mensaje a seguidores"
|
||||
target: "Para"
|
||||
federationSpecified: "Este servidor opera en una federación de listas blancas. No puede interactuar con otros servidores que no sean los especificados por el administrador."
|
||||
federationDisabled: "La federación está desactivada en este servidor. No puede interactuar con usuarios de otros servidores"
|
||||
_accountSettings:
|
||||
@ -2560,6 +2583,13 @@ _mediaControls:
|
||||
pip: "Picture in Picture"
|
||||
playbackRate: "Velocidad de reproducción"
|
||||
loop: "Reproducción en bucle"
|
||||
_followRequest:
|
||||
recieved: "Petición de seguimiento recibida"
|
||||
sent: "Petición de seguimiento enviada"
|
||||
_remoteLookupErrors:
|
||||
_noSuchObject:
|
||||
title: "No se encuentra"
|
||||
_search:
|
||||
searchScopeAll: "Todo"
|
||||
searchScopeLocal: "Local"
|
||||
searchScopeUser: "Especificar usuario"
|
||||
|
@ -2364,3 +2364,7 @@ _embedCodeGen:
|
||||
_remoteLookupErrors:
|
||||
_noSuchObject:
|
||||
title: "Non trouvé"
|
||||
_search:
|
||||
searchScopeAll: "Tous"
|
||||
searchScopeLocal: "Local"
|
||||
searchScopeUser: "Spécifier l'utilisateur·rice"
|
||||
|
@ -2610,3 +2610,7 @@ _mediaControls:
|
||||
_remoteLookupErrors:
|
||||
_noSuchObject:
|
||||
title: "Tidak dapat ditemukan"
|
||||
_search:
|
||||
searchScopeAll: "Semua"
|
||||
searchScopeLocal: "Lokal"
|
||||
searchScopeUser: "Pengguna spesifik"
|
||||
|
100
locales/index.d.ts
vendored
100
locales/index.d.ts
vendored
@ -5254,6 +5254,14 @@ export interface Locale extends ILocale {
|
||||
* このサーバーは連合が無効化されています。他のサーバーのユーザーとやり取りすることはできません。
|
||||
*/
|
||||
"federationDisabled": string;
|
||||
/**
|
||||
* リアクションする際に確認する
|
||||
*/
|
||||
"confirmOnReact": string;
|
||||
/**
|
||||
* " {emoji} " をリアクションしますか?
|
||||
*/
|
||||
"reactAreYouSure": ParameterizedString<"emoji">;
|
||||
"_accountSettings": {
|
||||
/**
|
||||
* コンテンツの表示にログインを必須にする
|
||||
@ -9472,6 +9480,14 @@ export interface Locale extends ILocale {
|
||||
* ログインがありました
|
||||
*/
|
||||
"login": string;
|
||||
/**
|
||||
* アクセストークンが作成されました
|
||||
*/
|
||||
"createToken": string;
|
||||
/**
|
||||
* 心当たりがない場合は「{text}」を通じてアクセストークンを削除してください。
|
||||
*/
|
||||
"createTokenDescription": ParameterizedString<"text">;
|
||||
"_types": {
|
||||
/**
|
||||
* すべて
|
||||
@ -10880,13 +10896,7 @@ export interface Locale extends ILocale {
|
||||
*/
|
||||
"title": string;
|
||||
/**
|
||||
* このサーバーと通信することはできましたが、得られたデータが不正なものでした。
|
||||
*/
|
||||
"description": string;
|
||||
};
|
||||
"_responseInvalidIdHostNotMatch": {
|
||||
/**
|
||||
* 入力されたURIのドメインと最終的に得られたURIのドメインとが異なります。第三者のサーバーを介してリモートのコンテンツを照会している場合は、発信元のサーバーで取得できるURIを使用して照会し直してください。
|
||||
* このサーバーと通信することはできましたが、得られたデータが不正なものでした。第三者のサーバーを介してリモートのコンテンツを照会している場合は、発信元のサーバーで取得できるURIを使用して照会し直してください。
|
||||
*/
|
||||
"description": string;
|
||||
};
|
||||
@ -10944,6 +10954,82 @@ export interface Locale extends ILocale {
|
||||
};
|
||||
};
|
||||
};
|
||||
"_bootErrors": {
|
||||
/**
|
||||
* 読み込みに失敗しました
|
||||
*/
|
||||
"title": string;
|
||||
/**
|
||||
* 少し待ってからリロードしてもまだ問題が解決されない場合、以下のError IDを添えてサーバー管理者に連絡してください。
|
||||
*/
|
||||
"serverError": string;
|
||||
/**
|
||||
* 以下を行うと解決する可能性があります。
|
||||
*/
|
||||
"solution": string;
|
||||
/**
|
||||
* ブラウザおよびOSを最新バージョンに更新する
|
||||
*/
|
||||
"solution1": string;
|
||||
/**
|
||||
* アドブロッカーを無効にする
|
||||
*/
|
||||
"solution2": string;
|
||||
/**
|
||||
* ブラウザのキャッシュをクリアする
|
||||
*/
|
||||
"solution3": string;
|
||||
/**
|
||||
* (Tor Browser) dom.webaudio.enabledをtrueに設定する
|
||||
*/
|
||||
"solution4": string;
|
||||
/**
|
||||
* その他のオプション
|
||||
*/
|
||||
"otherOption": string;
|
||||
/**
|
||||
* クライアント設定とキャッシュを削除
|
||||
*/
|
||||
"otherOption1": string;
|
||||
/**
|
||||
* 簡易クライアントを起動
|
||||
*/
|
||||
"otherOption2": string;
|
||||
/**
|
||||
* 修復ツールを起動
|
||||
*/
|
||||
"otherOption3": string;
|
||||
};
|
||||
"_search": {
|
||||
/**
|
||||
* 全て
|
||||
*/
|
||||
"searchScopeAll": string;
|
||||
/**
|
||||
* ローカル
|
||||
*/
|
||||
"searchScopeLocal": string;
|
||||
/**
|
||||
* サーバー指定
|
||||
*/
|
||||
"searchScopeServer": string;
|
||||
/**
|
||||
* ユーザー指定
|
||||
*/
|
||||
"searchScopeUser": string;
|
||||
/**
|
||||
* サーバーのホストを入力してください
|
||||
*/
|
||||
"pleaseEnterServerHost": string;
|
||||
/**
|
||||
* ユーザーを選択してください
|
||||
*/
|
||||
"pleaseSelectUser": string;
|
||||
/**
|
||||
* 例: misskey.example.com
|
||||
*/
|
||||
"serverHostPlaceholder": string;
|
||||
};
|
||||
}
|
||||
declare const locales: {
|
||||
[lang: string]: Locale;
|
||||
|
@ -1309,6 +1309,8 @@ availableRoles: "Ruoli disponibili"
|
||||
acknowledgeNotesAndEnable: "Attivare dopo averne compreso il comportamento."
|
||||
federationSpecified: "Questo server è federato solo con istanze specifiche del Fediverso. Puoi interagire solo con quelle scelte dall'amministrazione."
|
||||
federationDisabled: "Questo server ha la federazione disabilitata. Non puoi interagire con profili provenienti da altri server."
|
||||
confirmOnReact: "Confermare le reazioni"
|
||||
reactAreYouSure: "Vuoi davvero reagire con {emoji} ?"
|
||||
_accountSettings:
|
||||
requireSigninToViewContents: "Per vedere il contenuto, è necessaria l'iscrizione"
|
||||
requireSigninToViewContentsDescription1: "Richiedere l'iscrizione per visualizzare tutte le Note e gli altri contenuti che hai creato. Probabilmente l'effetto è impedire la raccolta di informazioni da parte dei bot crawler."
|
||||
@ -2440,6 +2442,8 @@ _notification:
|
||||
flushNotification: "Azzera le notifiche"
|
||||
exportOfXCompleted: "Abbiamo completato l'esportazione di {x}"
|
||||
login: "Autenticazione avvenuta"
|
||||
createToken: "È stato creato un token di accesso"
|
||||
createTokenDescription: "In caso contrario, eliminare il token di accesso tramite ({text})."
|
||||
_types:
|
||||
all: "Tutto"
|
||||
note: "Nuove Note"
|
||||
@ -2822,8 +2826,6 @@ _remoteLookupErrors:
|
||||
_responseInvalid:
|
||||
title: "Risposta non valida"
|
||||
description: "La comunicazione col server è andata a buon fine, ma abbiamo ricevuto dati non validi."
|
||||
_responseInvalidIdHostNotMatch:
|
||||
description: "L'indirizzo immesso non coincide con la URL finale. Interrogando i server per un contenuto remoto, assicurarsi di utilizzare la URL finale e non quella di un server intermedio."
|
||||
_noSuchObject:
|
||||
title: "Non trovato"
|
||||
description: "La risorsa richiesta non è stata trovata. Verificare nuovamente la URL."
|
||||
@ -2840,3 +2842,19 @@ _captcha:
|
||||
_unknown:
|
||||
title: "Errore CAPTCHA"
|
||||
text: "Si è verificato un errore imprevisto."
|
||||
_bootErrors:
|
||||
title: "Caricamento non riuscito"
|
||||
serverError: "Dopo una breve attesa, e dopo aver ricaricato la pagina, se il problema persiste, contatta l'amministrazione comunicando il seguente ID di errore."
|
||||
solution: "Di seguito, alcune probabili soluzioni al problema."
|
||||
solution1: "Aggiornare browser e il sistema operativo all'ultima versione"
|
||||
solution2: "Disattivare gli adblocker"
|
||||
solution3: "Cancellare la cache del browser"
|
||||
solution4: "(Per chi utilizza il Browser Tor) Impostare dom.webaudio.enabled = vero"
|
||||
otherOption: "Altre opzioni"
|
||||
otherOption1: "Nelle impostazioni, cancellare le impostazioni del client e svuotare la cache"
|
||||
otherOption2: "Avviare il client predefinito"
|
||||
otherOption3: "Avviare lo strumento di riparazione"
|
||||
_search:
|
||||
searchScopeAll: "Tutte"
|
||||
searchScopeLocal: "Locale"
|
||||
searchScopeUser: "Profilo specifico"
|
||||
|
@ -1309,6 +1309,8 @@ availableRoles: "利用可能なロール"
|
||||
acknowledgeNotesAndEnable: "注意事項を理解した上でオンにします。"
|
||||
federationSpecified: "このサーバーはホワイトリスト連合で運用されています。管理者が指定したサーバー以外とやり取りすることはできません。"
|
||||
federationDisabled: "このサーバーは連合が無効化されています。他のサーバーのユーザーとやり取りすることはできません。"
|
||||
confirmOnReact: "リアクションする際に確認する"
|
||||
reactAreYouSure: "\" {emoji} \" をリアクションしますか?"
|
||||
|
||||
_accountSettings:
|
||||
requireSigninToViewContents: "コンテンツの表示にログインを必須にする"
|
||||
@ -2500,6 +2502,8 @@ _notification:
|
||||
flushNotification: "通知の履歴をリセットする"
|
||||
exportOfXCompleted: "{x}のエクスポートが完了しました"
|
||||
login: "ログインがありました"
|
||||
createToken: "アクセストークンが作成されました"
|
||||
createTokenDescription: "心当たりがない場合は「{text}」を通じてアクセストークンを削除してください。"
|
||||
|
||||
_types:
|
||||
all: "すべて"
|
||||
@ -2907,9 +2911,7 @@ _remoteLookupErrors:
|
||||
description: "このサーバーとの通信に失敗しました。相手サーバーがダウンしている可能性があります。また、不正なURIや存在しないURIを入力していないか確認してください。"
|
||||
_responseInvalid:
|
||||
title: "レスポンスが不正です"
|
||||
description: "このサーバーと通信することはできましたが、得られたデータが不正なものでした。"
|
||||
_responseInvalidIdHostNotMatch:
|
||||
description: "入力されたURIのドメインと最終的に得られたURIのドメインとが異なります。第三者のサーバーを介してリモートのコンテンツを照会している場合は、発信元のサーバーで取得できるURIを使用して照会し直してください。"
|
||||
description: "このサーバーと通信することはできましたが、得られたデータが不正なものでした。第三者のサーバーを介してリモートのコンテンツを照会している場合は、発信元のサーバーで取得できるURIを使用して照会し直してください。"
|
||||
_noSuchObject:
|
||||
title: "見つかりません"
|
||||
description: "要求されたリソースは見つかりませんでした。URIをもう一度お確かめください。"
|
||||
@ -2927,3 +2929,25 @@ _captcha:
|
||||
_unknown:
|
||||
title: "CAPTCHAエラー"
|
||||
text: "想定外のエラーが発生しました。"
|
||||
|
||||
_bootErrors:
|
||||
title: "読み込みに失敗しました"
|
||||
serverError: "少し待ってからリロードしてもまだ問題が解決されない場合、以下のError IDを添えてサーバー管理者に連絡してください。"
|
||||
solution: "以下を行うと解決する可能性があります。"
|
||||
solution1: "ブラウザおよびOSを最新バージョンに更新する"
|
||||
solution2: "アドブロッカーを無効にする"
|
||||
solution3: "ブラウザのキャッシュをクリアする"
|
||||
solution4: "(Tor Browser) dom.webaudio.enabledをtrueに設定する"
|
||||
otherOption: "その他のオプション"
|
||||
otherOption1: "クライアント設定とキャッシュを削除"
|
||||
otherOption2: "簡易クライアントを起動"
|
||||
otherOption3: "修復ツールを起動"
|
||||
|
||||
_search:
|
||||
searchScopeAll: "全て"
|
||||
searchScopeLocal: "ローカル"
|
||||
searchScopeServer: "サーバー指定"
|
||||
searchScopeUser: "ユーザー指定"
|
||||
pleaseEnterServerHost: "サーバーのホストを入力してください"
|
||||
pleaseSelectUser: "ユーザーを選択してください"
|
||||
serverHostPlaceholder: "例: misskey.example.com"
|
||||
|
@ -5,6 +5,7 @@ introMisskey: "ようお越し!Misskeyは、オープンソースの分散型
|
||||
poweredByMisskeyDescription: "{name}は、オープンソースのプラットフォーム<b>Misskey</b>のサーバーのひとつなんやで。"
|
||||
monthAndDay: "{month}月 {day}日"
|
||||
search: "探す"
|
||||
reset: "リセット"
|
||||
notifications: "通知"
|
||||
username: "ユーザー名"
|
||||
password: "パスワード"
|
||||
@ -48,6 +49,7 @@ pin: "ピン留めしとく"
|
||||
unpin: "ピン留めやめる"
|
||||
copyContent: "内容をコピー"
|
||||
copyLink: "リンクをコピー"
|
||||
copyRemoteLink: "リモートのリンクをコピーするで?"
|
||||
copyLinkRenote: "リノートのリンクをコピーするで?"
|
||||
delete: "ほかす"
|
||||
deleteAndEdit: "ほかして直す"
|
||||
@ -684,11 +686,15 @@ smtpSecure: "SMTP 接続に暗黙的なSSL/TLSを使用する"
|
||||
smtpSecureInfo: "STARTTLS使っとる時はオフにしてや。"
|
||||
testEmail: "配信テスト"
|
||||
wordMute: "ワードミュート"
|
||||
wordMuteDescription: "指定した語句が入ってるノートを最小化するで。最小化されたノートをクリックしたら、表示できるようになるで。"
|
||||
hardWordMute: "ハードワードミュート"
|
||||
showMutedWord: "ミュートされたワードを表示するで"
|
||||
hardWordMuteDescription: "指定した語句が入ってるノートを隠すで。ワードミュートとちゃうて、ノートは完全に表示されんようになるで。"
|
||||
regexpError: "正規表現エラー"
|
||||
regexpErrorDescription: "{tab}ワードミュートの{line}行目の正規表現にエラーが出てきたで:"
|
||||
instanceMute: "サーバーミュート"
|
||||
userSaysSomething: "{name}が何か言うとるわ"
|
||||
userSaysSomethingAbout: "{name}が「{word}」についてなんか言うてたで"
|
||||
makeActive: "使うで"
|
||||
display: "表示"
|
||||
copy: "コピー"
|
||||
@ -1301,6 +1307,10 @@ lockdown: "ロックダウン"
|
||||
pleaseSelectAccount: "アカウント選んでや"
|
||||
availableRoles: "使えるロール"
|
||||
acknowledgeNotesAndEnable: "注意事項をわかった上でオンにする。"
|
||||
federationSpecified: "このサーバーはホワイトリスト連合で運用されてるで。管理者が指定したサーバー以外とはやり取りできひんで。"
|
||||
federationDisabled: "このサーバーは連合が無効化されてるで。他のサーバーのユーザーとやり取りすることはできひんで。"
|
||||
confirmOnReact: "ツッコむときに確認とる"
|
||||
reactAreYouSure: "\" {emoji} \" でツッコむ?"
|
||||
_accountSettings:
|
||||
requireSigninToViewContents: "ログインしてもらってからコンテンツ見てもらう"
|
||||
requireSigninToViewContentsDescription1: "あなたが作成した全部のノートとかのコンテンツを見れるようにするのにログインがいるようにするで。クローラーにいろいろ収集されるんを防げるかもしれん。"
|
||||
@ -2432,6 +2442,8 @@ _notification:
|
||||
flushNotification: "通知の履歴をリセットする"
|
||||
exportOfXCompleted: "{x}のエクスポートが終わったわ"
|
||||
login: "ログインしとったで"
|
||||
createToken: "アクセストークンが作成されたで"
|
||||
createTokenDescription: "心当たりないんやったら「{text}」でアクセストークンを削除してやって。"
|
||||
_types:
|
||||
all: "すべて"
|
||||
note: "あんたらの新規投稿"
|
||||
@ -2718,6 +2730,66 @@ _contextMenu:
|
||||
app: "アプリ"
|
||||
appWithShift: "Shiftキーでアプリ"
|
||||
native: "ブラウザのUI"
|
||||
_gridComponent:
|
||||
_error:
|
||||
requiredValue: "この値は必須項目やで"
|
||||
columnTypeNotSupport: "正規表現によるバリデーションはtype:textのカラムだけサポートしてるで"
|
||||
patternNotMatch: "この値は{pattern}のパターンに一致しいひんで"
|
||||
notUnique: "この値は一意でなあかんで"
|
||||
_roleSelectDialog:
|
||||
notSelected: "選択されとらんで"
|
||||
_customEmojisManager:
|
||||
_gridCommon:
|
||||
copySelectionRows: "選択行をコピーするで"
|
||||
copySelectionRanges: "選択範囲をコピーするで"
|
||||
deleteSelectionRows: "選択行を削除するで"
|
||||
deleteSelectionRanges: "選択範囲の値をクリアするで"
|
||||
searchSettings: "検索設定"
|
||||
searchSettingCaption: "検索条件を詳しく設定するで。"
|
||||
searchLimit: "表示件数"
|
||||
sortOrder: "並び順"
|
||||
registrationLogs: "登録ログ"
|
||||
registrationLogsCaption: "絵文字更新・削除時のログが表示されるで。更新・削除操作をしたり、ページを遷移・リロードしたら消えるから気ぃつけてな。"
|
||||
alertEmojisRegisterFailedDescription: "絵文字の更新・削除に失敗したで。詳細は登録ログを確認してな。"
|
||||
_logs:
|
||||
showSuccessLogSwitch: "成功ログを表示するで"
|
||||
failureLogNothing: "失敗ログはあらへん。"
|
||||
logNothing: "失敗ログはあらへん。"
|
||||
_remote:
|
||||
selectionRowDetail: "選択行の詳細やで"
|
||||
importSelectionRows: "選択行をインポートするで"
|
||||
importSelectionRangesRows: "選択範囲の行をインポートするで"
|
||||
importEmojisButton: "チェックされた絵文字をインポートするで"
|
||||
confirmImportEmojisTitle: "絵文字のインポートするで"
|
||||
confirmImportEmojisDescription: "リモートから受信した{count}個の絵文字をインポートするで。絵文字のライセンスには十分気ぃつけてな。実行してもええか?"
|
||||
_local:
|
||||
tabTitleList: "登録済み絵文字一覧"
|
||||
tabTitleRegister: "絵文字の登録"
|
||||
_list:
|
||||
emojisNothing: "登録された絵文字はないで。"
|
||||
markAsDeleteTargetRows: "選択行を削除対象にするで"
|
||||
markAsDeleteTargetRanges: "選択範囲の行を削除対象にするで"
|
||||
alertUpdateEmojisNothingDescription: "変更された絵文字はないで。"
|
||||
alertDeleteEmojisNothingDescription: "削除対象の絵文字はないで。"
|
||||
confirmMovePage: "ページを移動してもええんか?"
|
||||
confirmChangeView: "表示を変更してもええんか?"
|
||||
confirmUpdateEmojisDescription: "{count}個の絵文字を更新するで。実行してもええか?"
|
||||
confirmDeleteEmojisDescription: "チェックがつけられた{count}個の絵文字を削除するで。ほんまにええか?"
|
||||
confirmResetDescription: "今までやった変更が全部リセットされるで。"
|
||||
confirmMovePageDesciption: "このページの絵文字に変更が加えられてるで。\n保存せずページを移動してまうと、このページで加えた変更が全てパーになるで。"
|
||||
dialogSelectRoleTitle: "絵文字に設定されたロールで検索"
|
||||
_register:
|
||||
uploadSettingTitle: "アップロード設定"
|
||||
uploadSettingDescription: "この画面で絵文字アップロードするときの動きを設定できるで。"
|
||||
directoryToCategoryLabel: "ディレクトリ名を\"category\"に入力する"
|
||||
directoryToCategoryCaption: "ディレクトリをドラッグ・ドロップした時に、ディレクトリ名を\"category\"に入力します。"
|
||||
emojiInputAreaCaption: "どれかの方法で登録する絵文字を選択して。"
|
||||
emojiInputAreaList1: "この枠に画像ファイルかディレクトリをドラッグ&ドロップ"
|
||||
emojiInputAreaList2: "このリンクをクリックしてPCから選択する"
|
||||
emojiInputAreaList3: "このリンクをクリックしてドライブから選択する"
|
||||
confirmRegisterEmojisDescription: "リストに表示されてる絵文字を新たなカスタム絵文字として登録するで。ほんまにええか? (サーバーがしんどくなるから、一回で登録できる絵文字は{count}件までやで)"
|
||||
confirmClearEmojisDescription: "編集内容をほかして、リストに表示されている絵文字をクリアするで。ほんまにええか?"
|
||||
confirmUploadEmojisDescription: "ドラッグ&ドロップされた{count}個のファイルをドライブにアップロードするで。ほんまにええか?"
|
||||
_embedCodeGen:
|
||||
title: "埋め込みコードをカスタム"
|
||||
header: "ヘッダー出す"
|
||||
@ -2754,8 +2826,35 @@ _remoteLookupErrors:
|
||||
_responseInvalid:
|
||||
title: "レスポンスがおかしいで"
|
||||
description: "このサーバーと通信することはできたけど、もらったデータがおかしかったで。"
|
||||
_responseInvalidIdHostNotMatch:
|
||||
description: "入力されたURIのドメインと最終的に得られたURIのドメインとが違うで。第三者のサーバーを介してリモートのコンテンツを照会してるんやったら、発信元のサーバーで取得できるURIを使って照会し直して。"
|
||||
_noSuchObject:
|
||||
title: "見つからへんね"
|
||||
description: "求められたリソースが見つからんかったで。URIをもっかい確かめてや。"
|
||||
_captcha:
|
||||
verify: "CAPTCHAしばいたって"
|
||||
testSiteKeyMessage: "サイトキーとシークレットキーにテスト用の値を入力することでプレビューを確認できるで。\n詳細は下記ページを確認してな。"
|
||||
_error:
|
||||
_requestFailed:
|
||||
title: "CAPTCHAのリクエストに失敗してもうた"
|
||||
text: "しばらく後で実行するか、設定をもっかい確認してや。"
|
||||
_verificationFailed:
|
||||
title: "CAPTCHAのリクエストに失敗してもうた"
|
||||
text: "設定がほんまに合ってるかもっかい確認してや。"
|
||||
_unknown:
|
||||
title: "CAPTCHAエラー"
|
||||
text: "思いもせんかったエラーが起きたわ。"
|
||||
_bootErrors:
|
||||
title: "読み込みに失敗したで"
|
||||
serverError: "少し待ってからリロードしてもまだ問題が解決されんのやったら、以下のError IDを添えてサーバー管理者に連絡して。"
|
||||
solution: "以下のことやったら解決するかもやで。"
|
||||
solution1: "ブラウザとかOSを最新バージョンに更新する"
|
||||
solution2: "アドブロッカーを無効にする"
|
||||
solution3: "ブラウザのキャッシュをクリアする"
|
||||
solution4: "(Tor Browser) dom.webaudio.enabledをtrueに設定する"
|
||||
otherOption: "ほかのオプション"
|
||||
otherOption1: "クライアント設定とキャッシュをほかす"
|
||||
otherOption2: "簡易クライアントを起動"
|
||||
otherOption3: "修復ツールを起動"
|
||||
_search:
|
||||
searchScopeAll: "みんな"
|
||||
searchScopeLocal: "ローカル"
|
||||
searchScopeUser: "ユーザー指定"
|
||||
|
@ -843,3 +843,6 @@ _reversi:
|
||||
_remoteLookupErrors:
|
||||
_noSuchObject:
|
||||
title: "몬 찾앗십니다"
|
||||
_search:
|
||||
searchScopeAll: "말캉"
|
||||
searchScopeUser: "사용자 지정"
|
||||
|
@ -2822,8 +2822,6 @@ _remoteLookupErrors:
|
||||
_responseInvalid:
|
||||
title: "유효하지 않은 반응입니다."
|
||||
description: "이 서버와 통신할 수 있지만, 데이터가 올바르지 않습니다."
|
||||
_responseInvalidIdHostNotMatch:
|
||||
description: "입력된 URI과 실제 URI가 다릅니다. 제 3자 서버를 통한 리모트 컨텐츠를 조회하는 경우, 원래 서버 측에서 받아올 수 있는 URI를 사용하여 조회하시길 바랍니다."
|
||||
_noSuchObject:
|
||||
title: "찾을 수 없습니다"
|
||||
description: "요구된 리소스를 찾을 수 없습니다. URI를 다시 한 번 확인해보세요."
|
||||
@ -2840,3 +2838,10 @@ _captcha:
|
||||
_unknown:
|
||||
title: "CAPTCHA 에러"
|
||||
text: "알 수 없는 에러가 발생했습니다."
|
||||
_bootErrors:
|
||||
title: "로딩이 실패함"
|
||||
solution4: "(Tor Browser) dom.webaudio.enabled를 true로 설정하세요"
|
||||
_search:
|
||||
searchScopeAll: "전체"
|
||||
searchScopeLocal: "로컬"
|
||||
searchScopeUser: "사용자 지정"
|
||||
|
@ -477,3 +477,5 @@ _moderationLogTypes:
|
||||
_remoteLookupErrors:
|
||||
_noSuchObject:
|
||||
title: "ບໍ່ພົບ"
|
||||
_search:
|
||||
searchScopeAll: "ທັງໝົດ"
|
||||
|
@ -540,3 +540,5 @@ _reversi:
|
||||
_remoteLookupErrors:
|
||||
_noSuchObject:
|
||||
title: "Niet gevonden"
|
||||
_search:
|
||||
searchScopeAll: "Alle"
|
||||
|
@ -730,3 +730,5 @@ _moderationLogTypes:
|
||||
_remoteLookupErrors:
|
||||
_noSuchObject:
|
||||
title: "Ikke funnet"
|
||||
_search:
|
||||
searchScopeAll: "Alle"
|
||||
|
@ -1583,3 +1583,6 @@ _reversi:
|
||||
_remoteLookupErrors:
|
||||
_noSuchObject:
|
||||
title: "Nie znaleziono"
|
||||
_search:
|
||||
searchScopeAll: "Wszystkie"
|
||||
searchScopeLocal: "Lokalne"
|
||||
|
@ -2754,8 +2754,10 @@ _remoteLookupErrors:
|
||||
_responseInvalid:
|
||||
title: "Resposta inválida"
|
||||
description: "Foi possível comunicar com o servidor, porém os dados obtidos foram incorretos."
|
||||
_responseInvalidIdHostNotMatch:
|
||||
description: "O domínio do endereço inserido difere do domínio do endereço final. Se você estiver pesquisando por um servidor de terceiros, tente buscar novamente com um endereço que pode ser obtido através do servidor original."
|
||||
_noSuchObject:
|
||||
title: "Não encontrado"
|
||||
description: "O recurso solicitado não foi encontrado, confira o endereço."
|
||||
_search:
|
||||
searchScopeAll: "Todos"
|
||||
searchScopeLocal: "Local"
|
||||
searchScopeUser: "Usuário específico"
|
||||
|
@ -736,3 +736,5 @@ _reversi:
|
||||
_remoteLookupErrors:
|
||||
_noSuchObject:
|
||||
title: "Nu a fost găsit"
|
||||
_search:
|
||||
searchScopeAll: "Tot"
|
||||
|
@ -2147,3 +2147,7 @@ _reversi:
|
||||
_remoteLookupErrors:
|
||||
_noSuchObject:
|
||||
title: "Не найдено"
|
||||
_search:
|
||||
searchScopeAll: "Все"
|
||||
searchScopeLocal: "Местная"
|
||||
searchScopeUser: "Указанный пользователь"
|
||||
|
@ -1449,3 +1449,6 @@ _reversi:
|
||||
_remoteLookupErrors:
|
||||
_noSuchObject:
|
||||
title: "Nenájdené"
|
||||
_search:
|
||||
searchScopeAll: "Všetko"
|
||||
searchScopeLocal: "Lokálne"
|
||||
|
@ -707,3 +707,5 @@ _reversi:
|
||||
white: "Vit"
|
||||
_selfXssPrevention:
|
||||
warning: "VARNING"
|
||||
_search:
|
||||
searchScopeAll: "Allt"
|
||||
|
@ -2709,3 +2709,7 @@ _embedCodeGen:
|
||||
_remoteLookupErrors:
|
||||
_noSuchObject:
|
||||
title: "ไม่พบหน้าที่ต้องการ"
|
||||
_search:
|
||||
searchScopeAll: "ทั้งหมด"
|
||||
searchScopeLocal: "ท้องถิ่น"
|
||||
searchScopeUser: "ผู้ใช้เฉพาะ"
|
||||
|
@ -460,3 +460,5 @@ _deck:
|
||||
_moderationLogTypes:
|
||||
suspend: "askıya al"
|
||||
resetPassword: "Şifre sıfırlama"
|
||||
_search:
|
||||
searchScopeAll: "Tümü"
|
||||
|
@ -1624,3 +1624,6 @@ _reversi:
|
||||
_remoteLookupErrors:
|
||||
_noSuchObject:
|
||||
title: "Не знайдено"
|
||||
_search:
|
||||
searchScopeAll: "Всі"
|
||||
searchScopeLocal: "Локальна"
|
||||
|
@ -1094,3 +1094,6 @@ _reversi:
|
||||
_remoteLookupErrors:
|
||||
_noSuchObject:
|
||||
title: "Topilmadi"
|
||||
_search:
|
||||
searchScopeAll: "Barcha"
|
||||
searchScopeLocal: "Mahalliy"
|
||||
|
@ -1930,3 +1930,7 @@ _reversi:
|
||||
_remoteLookupErrors:
|
||||
_noSuchObject:
|
||||
title: "Không tìm thấy"
|
||||
_search:
|
||||
searchScopeAll: "Tất cả"
|
||||
searchScopeLocal: "Máy chủ này"
|
||||
searchScopeUser: "Người dùng chỉ định"
|
||||
|
@ -1309,6 +1309,8 @@ availableRoles: "可用角色"
|
||||
acknowledgeNotesAndEnable: "理解注意事项后再开启。"
|
||||
federationSpecified: "此服务器已开启联合白名单。只能与管理员指定的服务器通信。"
|
||||
federationDisabled: "此服务器已禁用联合。无法与其它服务器上的用户通信。"
|
||||
confirmOnReact: "发送回应前需要确认"
|
||||
reactAreYouSure: "要用「{emoji}」进行回应吗?"
|
||||
_accountSettings:
|
||||
requireSigninToViewContents: "需要登录才能显示内容"
|
||||
requireSigninToViewContentsDescription1: "您发布的所有帖子将变成需要登入后才会显示。有望防止爬虫收集各种信息。"
|
||||
@ -2440,6 +2442,8 @@ _notification:
|
||||
flushNotification: "重置通知历史"
|
||||
exportOfXCompleted: "已完成 {x} 的导出"
|
||||
login: "有新的登录"
|
||||
createToken: "访问令牌已创建"
|
||||
createTokenDescription: "如果不明白其用途,请遵循「{text}」的指示删除访问令牌。"
|
||||
_types:
|
||||
all: "全部"
|
||||
note: "用户的新帖子"
|
||||
@ -2777,8 +2781,8 @@ _customEmojisManager:
|
||||
_register:
|
||||
uploadSettingTitle: "上传设置"
|
||||
uploadSettingDescription: "可以在此页面设置上传表情符号时的行为。"
|
||||
directoryToCategoryLabel: "目录名请输入「category」"
|
||||
directoryToCategoryCaption: "拖放目录时,目录名请输入「category」"
|
||||
directoryToCategoryLabel: "将目录名设为「category」"
|
||||
directoryToCategoryCaption: "拖放目录时,将目录名设置为「category」"
|
||||
emojiInputAreaCaption: "请使用其中一种方法选择要注册的表情符号。"
|
||||
emojiInputAreaList1: "在此区域内拖放图像文件或者目录"
|
||||
emojiInputAreaList2: "单击此链接以从电脑中选择"
|
||||
@ -2822,8 +2826,6 @@ _remoteLookupErrors:
|
||||
_responseInvalid:
|
||||
title: "响应无效"
|
||||
description: "成功与此服务器通信,但返回的数据无效。"
|
||||
_responseInvalidIdHostNotMatch:
|
||||
description: "输入 URI 的域名和最终取得的 URI 的域名不同。如果是通过第三方服务器获取远程内容,请使用可以从原始服务器获取内容的 URI 再试一次。"
|
||||
_noSuchObject:
|
||||
title: "未找到"
|
||||
description: "未找到请求的资源。请再次检查 URI。"
|
||||
@ -2840,3 +2842,19 @@ _captcha:
|
||||
_unknown:
|
||||
title: "CAPTCHA 错误"
|
||||
text: "发生意外错误。"
|
||||
_bootErrors:
|
||||
title: "加载失败"
|
||||
serverError: "请稍等片刻再重试。若问题仍无法解决,请将以下 Error ID 一起发送给管理员。"
|
||||
solution: "以下方法或许可以解决问题:"
|
||||
solution1: "将浏览器及操作系统更新到最新版本"
|
||||
solution2: "禁用广告屏蔽插件"
|
||||
solution3: "清除浏览器缓存"
|
||||
solution4: "(Tor Browser)将 dom.webaudio.enabled 设定为 true"
|
||||
otherOption: "其它选项"
|
||||
otherOption1: "清除客户端设定与缓存"
|
||||
otherOption2: "使用简易客户端"
|
||||
otherOption3: "启动修复工具"
|
||||
_search:
|
||||
searchScopeAll: "全部"
|
||||
searchScopeLocal: "本地"
|
||||
searchScopeUser: "用户指定"
|
||||
|
@ -368,7 +368,7 @@ normal: "正常"
|
||||
instanceName: "伺服器名稱"
|
||||
instanceDescription: "伺服器介紹"
|
||||
maintainerName: "管理員名稱"
|
||||
maintainerEmail: "管理員郵箱"
|
||||
maintainerEmail: "管理員信箱"
|
||||
tosUrl: "服務條款 URL"
|
||||
thisYear: "本年"
|
||||
thisMonth: "本月"
|
||||
@ -464,7 +464,7 @@ securityKey: "安全金鑰"
|
||||
lastUsed: "上次使用"
|
||||
lastUsedAt: "上次使用:{t}"
|
||||
unregister: "註銷"
|
||||
passwordLessLogin: "設置無密碼登入"
|
||||
passwordLessLogin: "無密碼登入"
|
||||
passwordLessLoginDescription: "不使用密碼,以安全金鑰或 Passkey 登入"
|
||||
resetPassword: "重設密碼"
|
||||
newPasswordIs: "新密碼為「{password}」"
|
||||
@ -521,7 +521,7 @@ menuStyle: "選單風格"
|
||||
style: "風格"
|
||||
drawer: "側邊欄"
|
||||
popup: "彈出式視窗"
|
||||
showNoteActionsOnlyHover: "僅在游標停留時顯示貼文的"
|
||||
showNoteActionsOnlyHover: "僅於游標懸停時顯示貼文選項"
|
||||
showReactionsCount: "顯示貼文的反應數目"
|
||||
noHistory: "沒有歷史紀錄"
|
||||
signinHistory: "登入歷史"
|
||||
@ -558,12 +558,12 @@ useObjectStorage: "使用物件儲存"
|
||||
objectStorageBaseUrl: "Base URL"
|
||||
objectStorageBaseUrlDesc: "用於引用的 URL。如果您使用的是 CDN 或反向代理,請指定其 URL,例如 S3(https://<bucket>.s3.amazonaws.com)、GCS(https://storage.googleapis.com/<bucket>)。"
|
||||
objectStorageBucket: "儲存空間(Bucket)"
|
||||
objectStorageBucketDesc: "請填寫所用服務的儲存空間(Bucket)名稱。 "
|
||||
objectStorageBucketDesc: "請填寫所用服務的儲存桶(Bucket)名稱。 "
|
||||
objectStoragePrefix: "前綴"
|
||||
objectStoragePrefixDesc: "它儲存在此前綴目錄下。"
|
||||
objectStorageEndpoint: "端點(Endpoint)"
|
||||
objectStorageEndpointDesc: "如使用 AWS S3,請留空。如使用其他服務,請按照其說明文件以「<host>」或「<host>:<port>」的形式設定端點(Endpoint)。"
|
||||
objectStorageRegion: "地域(Region)"
|
||||
objectStorageRegion: "區域(Region)"
|
||||
objectStorageRegionDesc: "請填寫一個分區,例如「xx-east-1」。 如果您使用的服務不設分區,請留空或填寫「us-east-1」。"
|
||||
objectStorageUseSSL: "使用 SSL"
|
||||
objectStorageUseSSLDesc: "請在不使用 https 連接 API 時關閉"
|
||||
@ -586,7 +586,7 @@ popout: "彈出式視窗"
|
||||
volume: "音量"
|
||||
masterVolume: "主音量"
|
||||
notUseSound: "關閉音效"
|
||||
useSoundOnlyWhenActive: "瀏覽器在前景運作時,Misskey 才會發出音效"
|
||||
useSoundOnlyWhenActive: "僅在 Misskey 於前景運作時發出音效"
|
||||
details: "詳細資訊"
|
||||
renoteDetails: "轉發貼文的細節"
|
||||
chooseEmoji: "選擇您的表情符號"
|
||||
@ -681,7 +681,7 @@ smtpHost: "主機"
|
||||
smtpPort: "埠"
|
||||
smtpUser: "使用者名稱"
|
||||
smtpPass: "密碼"
|
||||
emptyToDisableSmtpAuth: "留空使用者名稱和密碼以關閉SMTP驗證。"
|
||||
emptyToDisableSmtpAuth: "將使用者名稱和密碼留空以關閉 SMTP 驗證。"
|
||||
smtpSecure: "在 SMTP 連接中使用隱式 SSL/TLS"
|
||||
smtpSecureInfo: "使用 STARTTLS 時關閉。"
|
||||
testEmail: "測試郵件發送"
|
||||
@ -711,7 +711,7 @@ useGlobalSetting: "使用全域設定"
|
||||
useGlobalSettingDesc: "啟用時,將使用帳戶通知設定。停用時,則可以單獨設定。"
|
||||
other: "其他"
|
||||
regenerateLoginToken: "重新產生登入權杖"
|
||||
regenerateLoginTokenDescription: "重新產生用於登入的內部權杖。一般情況下是不需要這樣做的。重新產生後,所有裝置將會被登出。"
|
||||
regenerateLoginTokenDescription: "重新產生用於登入的內部權杖。通常不需要使用此功能。重新產生後,所有裝置都將被登出。"
|
||||
theKeywordWhenSearchingForCustomEmoji: "這是搜尋自訂表情符號時的關鍵字"
|
||||
setMultipleBySeparatingWithSpace: "您可以使用空格分隔多個項目。"
|
||||
fileIdOrUrl: "檔案 ID 或 URL"
|
||||
@ -745,7 +745,7 @@ unclip: "解除摘錄"
|
||||
confirmToUnclipAlreadyClippedNote: "此貼文已包含在摘錄「{name}」中。 你想將貼文從這個摘錄中排除嗎?"
|
||||
public: "公開"
|
||||
private: "私密"
|
||||
i18nInfo: "Misskey 已被志願者們翻譯成各種語言版本。您可以瀏覽 {link} 幫助翻譯。"
|
||||
i18nInfo: "Misskey 已被志願者們翻譯成各種語言版本。您可以前往 {link} 以協助翻譯。"
|
||||
manageAccessTokens: "管理存取權杖"
|
||||
accountInfo: "帳戶資訊"
|
||||
notesCount: "貼文數量"
|
||||
@ -781,7 +781,7 @@ useSystemFont: "使用系統預設的字型"
|
||||
clips: "摘錄"
|
||||
experimentalFeatures: "實驗中的功能"
|
||||
experimental: "實驗性"
|
||||
thisIsExperimentalFeature: "這是實驗性的功能。可能會有變更規格和不能正常動作的可能性。"
|
||||
thisIsExperimentalFeature: "這是一項實驗性功能,其行為會隨需要進行調整,也可能無法正常運作。"
|
||||
developer: "開發者"
|
||||
makeExplorable: "使自己的帳戶更容易被找到"
|
||||
makeExplorableDescription: "如果關閉,帳戶將不會被顯示在「探索」頁面中。"
|
||||
@ -1207,7 +1207,7 @@ notificationRecieveConfig: "接受通知的設定"
|
||||
mutualFollow: "互相追隨"
|
||||
followingOrFollower: "追隨中或者追隨者"
|
||||
fileAttachedOnly: "只顯示包含附件的貼文"
|
||||
showRepliesToOthersInTimeline: "顯示給其他人的回覆"
|
||||
showRepliesToOthersInTimeline: "在時間軸上顯示給其他人的回覆"
|
||||
hideRepliesToOthersInTimeline: "在時間軸上隱藏給其他人的回覆"
|
||||
showRepliesToOthersInTimelineAll: "在時間軸包含追隨中所有人的回覆"
|
||||
hideRepliesToOthersInTimelineAll: "在時間軸不包含追隨中所有人的回覆"
|
||||
@ -1247,7 +1247,7 @@ reloadRequiredToApplySettings: "需要重新載入頁面設定才能生效。"
|
||||
remainingN: "剩餘:{n}"
|
||||
overwriteContentConfirm: "確定要覆蓋目前的內容嗎?"
|
||||
seasonalScreenEffect: "隨季節變換畫面的呈現"
|
||||
decorate: "設置頭像裝飾"
|
||||
decorate: "裝飾"
|
||||
addMfmFunction: "插入 MFM 功能語法"
|
||||
enableQuickAddMfmFunction: "顯示進階 MFM 選擇器"
|
||||
bubbleGame: "氣泡遊戲"
|
||||
@ -1274,7 +1274,7 @@ useBackupCode: "使用備用驗證碼"
|
||||
launchApp: "啟動 APP"
|
||||
useNativeUIForVideoAudioPlayer: "使用瀏覽器的 UI 播放影片與音訊"
|
||||
keepOriginalFilename: "保留原始檔名"
|
||||
keepOriginalFilenameDescription: "如果關閉此設置,上傳時檔案名稱會自動替換為隨機字串。"
|
||||
keepOriginalFilenameDescription: "如果關閉此設定,上傳時檔案名稱會自動替換為隨機字串。"
|
||||
noDescription: "沒有說明文字"
|
||||
alwaysConfirmFollow: "追隨時總是確認"
|
||||
inquiry: "聯絡我們"
|
||||
@ -1309,6 +1309,8 @@ availableRoles: "可用角色"
|
||||
acknowledgeNotesAndEnable: "了解注意事項後再開啟。"
|
||||
federationSpecified: "此伺服器以白名單聯邦的方式運作。除了管理員指定的伺服器外,它無法與其他伺服器互動。"
|
||||
federationDisabled: "此伺服器未開啟站台聯邦。無法與其他伺服器上的使用者互動。"
|
||||
confirmOnReact: "反應時確認"
|
||||
reactAreYouSure: "用「 {emoji} 」反應嗎?"
|
||||
_accountSettings:
|
||||
requireSigninToViewContents: "須登入以顯示內容"
|
||||
requireSigninToViewContentsDescription1: "必須登入才會顯示您建立的貼文等內容。可望有效防止資訊被爬蟲蒐集。"
|
||||
@ -1433,7 +1435,7 @@ _initialTutorial:
|
||||
useCases: "伺服器的服務條款可能會規範特定的貼文需要使用隱藏內容,除此之外也會用在隱藏劇情洩漏與敏感內容的貼文。"
|
||||
_howToMakeAttachmentsSensitive:
|
||||
title: "如何標記上傳附件為敏感內容?"
|
||||
description: "如果伺服器服務條款有規範,又或者不希望上傳附件直接被看見,可以設置為「敏感內容」"
|
||||
description: "如果伺服器的服務條款有規範,又或者不適合直接展示的附件,請記得加上「敏感」標記。"
|
||||
tryThisFile: "試試看!把附加在發文表單的圖像檔案標記為敏感內容。"
|
||||
_exampleNote:
|
||||
note: "打開納豆的包裝失敗了…"
|
||||
@ -1467,7 +1469,7 @@ _serverSettings:
|
||||
inquiryUrlDescription: "指定伺服器運營者的聯絡表單網址,或包含運營者聯絡資訊網頁的網址。"
|
||||
openRegistration: "允許建立帳戶"
|
||||
openRegistrationWarning: "開放註冊伴隨著風險。 建議只有在伺服器受到持續監控,並準備好在出現問題時能立即處理的情況下才開放註冊。"
|
||||
thisSettingWillAutomaticallyOffWhenModeratorsInactive: "為了防止 spam,如果一段期間內沒有偵測到審查員的活動,此設定將自動關閉。"
|
||||
thisSettingWillAutomaticallyOffWhenModeratorsInactive: "如果在一段期間內沒有偵測到任何審查員活動,此設定將自動關閉,以防止垃圾內容。"
|
||||
_accountMigration:
|
||||
moveFrom: "從其他帳戶遷移到這個帳戶"
|
||||
moveFromSub: "為另一個帳戶建立別名"
|
||||
@ -1481,7 +1483,7 @@ _accountMigration:
|
||||
startMigration: "遷移"
|
||||
migrationConfirm: "確定要將這個帳戶遷移至 {account} 嗎?一旦遷移就無法撤銷,也就無法以原來的狀態使用這個帳戶。\n另外,請確認在要遷移到的帳戶已經建立了一個別名。"
|
||||
movedAndCannotBeUndone: "帳戶已遷移。\n遷移無法撤消。"
|
||||
postMigrationNote: "將在完成遷移後的 24 小時取消追隨所有帳號。\n此帳戶的追隨中/追隨者人數將歸零。由於不會解除粉絲對您的追隨,因此他們仍然可以繼續閱覽此帳戶僅對追隨者公開的貼文。"
|
||||
postMigrationNote: "將在完成遷移的 24 小時後取消追隨所有帳號。\n此帳戶的追隨中/追隨者人數將歸零。由於不會解除粉絲對您的追隨,因此他們仍然可以繼續閱覽此帳戶內僅對追隨者公開的貼文。"
|
||||
movedTo: "要遷移到的帳戶:"
|
||||
_achievements:
|
||||
earnedAt: "獲得日期"
|
||||
@ -1798,7 +1800,7 @@ _role:
|
||||
canHideAds: "不顯示廣告"
|
||||
canSearchNotes: "可否搜尋貼文"
|
||||
canUseTranslator: "使用翻譯功能"
|
||||
avatarDecorationLimit: "頭像裝飾的最大設置量"
|
||||
avatarDecorationLimit: "頭像可掛上的最大裝飾數量"
|
||||
canImportAntennas: "允許匯入天線"
|
||||
canImportBlocking: "允許匯入封鎖名單"
|
||||
canImportFollowing: "允許匯入追隨名單"
|
||||
@ -1957,7 +1959,7 @@ _instanceMute:
|
||||
instanceMuteDescription: "包括對被靜音伺服器上的使用者的回覆,被設定的伺服器上所有貼文及轉發都會被靜音。"
|
||||
instanceMuteDescription2: "設定時以換行進行分隔"
|
||||
title: "將隱藏被設定的伺服器貼文。"
|
||||
heading: "將伺服器靜音"
|
||||
heading: "要靜音的伺服器"
|
||||
_theme:
|
||||
explore: "探索佈景主題"
|
||||
install: "安裝佈景主題"
|
||||
@ -2408,7 +2410,7 @@ _pages:
|
||||
note: "嵌式貼文"
|
||||
_note:
|
||||
id: "貼文ID"
|
||||
idDescription: "您也可以粘貼筆記 URL 並進行設置。 "
|
||||
idDescription: "您也可以貼上貼文 URL 來進行設定。 "
|
||||
detailed: "顯示詳細內容"
|
||||
_relayStatus:
|
||||
requesting: "等待核准"
|
||||
@ -2440,6 +2442,8 @@ _notification:
|
||||
flushNotification: "重置通知歷史紀錄"
|
||||
exportOfXCompleted: "{x} 的匯出已完成。"
|
||||
login: "已登入"
|
||||
createToken: "已產生存取權杖"
|
||||
createTokenDescription: "如果您不知道,請透過「{text}」刪除存取權杖。"
|
||||
_types:
|
||||
all: "全部 "
|
||||
note: "使用者的最新貼文"
|
||||
@ -2655,7 +2659,7 @@ _dataSaver:
|
||||
_hemisphere:
|
||||
N: "北半球"
|
||||
S: "南半球"
|
||||
caption: "在某些客戶端的設定中,用於判斷季節。"
|
||||
caption: "某些客戶端的設定會用此來判斷季節。"
|
||||
_reversi:
|
||||
reversi: "黑白棋"
|
||||
gameSettings: "對弈設定"
|
||||
@ -2822,8 +2826,6 @@ _remoteLookupErrors:
|
||||
_responseInvalid:
|
||||
title: "回應不正確"
|
||||
description: "雖然能夠與這個伺服器通訊,但是取得的資料不正確。"
|
||||
_responseInvalidIdHostNotMatch:
|
||||
description: "輸入的 URI 的網域與最終取得的 URI 的網域不同。 如果您是透過第三方伺服器查詢遠端內容,請使用可在原始伺服器上取得的 URI 再次查詢。"
|
||||
_noSuchObject:
|
||||
title: "查無項目"
|
||||
description: "無法找到所要求的資源,請再次檢查 URI。"
|
||||
@ -2840,3 +2842,19 @@ _captcha:
|
||||
_unknown:
|
||||
title: "CAPTCHA 錯誤"
|
||||
text: "發生了意外的錯誤。"
|
||||
_bootErrors:
|
||||
title: "載入失敗"
|
||||
serverError: "如果稍等片刻並重新載入後問題仍然存在,請聯絡您的伺服器管理員並提供以下的錯誤 ID。"
|
||||
solution: "執行以下操作或許可以解決問題。"
|
||||
solution1: "將瀏覽器和作業系統更新至最新版本"
|
||||
solution2: "停用廣告攔截器"
|
||||
solution3: "清除瀏覽器的快取"
|
||||
solution4: "(Tor 瀏覽器)將 dom.webaudio.enabled 設為 true"
|
||||
otherOption: "其他選項"
|
||||
otherOption1: "刪除用戶端設定和快取"
|
||||
otherOption2: "啟動簡易用戶端"
|
||||
otherOption3: "啟動修復工具"
|
||||
_search:
|
||||
searchScopeAll: "全部"
|
||||
searchScopeLocal: "本地"
|
||||
searchScopeUser: "指定使用者"
|
||||
|
38
package.json
38
package.json
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"version": "2025.2.0",
|
||||
"version": "2025.2.1",
|
||||
"codename": "nasubi",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/misskey-dev/misskey.git"
|
||||
},
|
||||
"packageManager": "pnpm@9.6.0",
|
||||
"packageManager": "pnpm@9.15.4",
|
||||
"workspaces": [
|
||||
"packages/frontend-shared",
|
||||
"packages/frontend",
|
||||
@ -47,35 +47,35 @@
|
||||
"cleanall": "pnpm clean-all"
|
||||
},
|
||||
"resolutions": {
|
||||
"chokidar": "3.5.3",
|
||||
"chokidar": "3.6.0",
|
||||
"lodash": "4.17.21"
|
||||
},
|
||||
"dependencies": {
|
||||
"cssnano": "6.1.2",
|
||||
"cssnano": "7.0.6",
|
||||
"execa": "8.0.1",
|
||||
"fast-glob": "3.3.2",
|
||||
"fast-glob": "3.3.3",
|
||||
"ignore-walk": "6.0.5",
|
||||
"js-yaml": "4.1.0",
|
||||
"postcss": "8.4.49",
|
||||
"postcss": "8.5.2",
|
||||
"tar": "6.2.1",
|
||||
"terser": "5.36.0",
|
||||
"typescript": "5.6.3",
|
||||
"esbuild": "0.24.0",
|
||||
"glob": "11.0.0"
|
||||
"terser": "5.39.0",
|
||||
"typescript": "5.7.3",
|
||||
"esbuild": "0.25.0",
|
||||
"glob": "11.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@misskey-dev/eslint-plugin": "2.0.3",
|
||||
"@types/node": "22.9.0",
|
||||
"@typescript-eslint/eslint-plugin": "7.17.0",
|
||||
"@typescript-eslint/parser": "7.17.0",
|
||||
"@misskey-dev/eslint-plugin": "2.1.0",
|
||||
"@types/node": "22.13.4",
|
||||
"@typescript-eslint/eslint-plugin": "8.24.0",
|
||||
"@typescript-eslint/parser": "8.24.0",
|
||||
"cross-env": "7.0.3",
|
||||
"cypress": "13.15.2",
|
||||
"eslint": "9.14.0",
|
||||
"globals": "15.12.0",
|
||||
"cypress": "14.0.3",
|
||||
"eslint": "9.20.1",
|
||||
"globals": "15.15.0",
|
||||
"ncp": "2.0.0",
|
||||
"start-server-and-test": "2.0.8"
|
||||
"start-server-and-test": "2.0.10"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@tensorflow/tfjs-core": "4.4.0"
|
||||
"@tensorflow/tfjs-core": "4.22.0"
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/swcrc",
|
||||
"$schema": "https://swc.rs/schema.json",
|
||||
"jsc": {
|
||||
"parser": {
|
||||
"syntax": "typescript",
|
||||
|
16
packages/backend/migration/1739006797620-GoogleAnalytics.js
Normal file
16
packages/backend/migration/1739006797620-GoogleAnalytics.js
Normal file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export class GoogleAnalytics1739006797620 {
|
||||
name = 'GoogleAnalytics1739006797620'
|
||||
|
||||
async up(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "googleAnalyticsMeasurementId" character varying(64)`);
|
||||
}
|
||||
|
||||
async down(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "googleAnalyticsMeasurementId"`);
|
||||
}
|
||||
}
|
@ -37,20 +37,20 @@
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@swc/core-android-arm64": "1.3.11",
|
||||
"@swc/core-darwin-arm64": "1.3.56",
|
||||
"@swc/core-darwin-x64": "1.3.56",
|
||||
"@swc/core-darwin-arm64": "1.10.16",
|
||||
"@swc/core-darwin-x64": "1.10.16",
|
||||
"@swc/core-freebsd-x64": "1.3.11",
|
||||
"@swc/core-linux-arm-gnueabihf": "1.3.56",
|
||||
"@swc/core-linux-arm64-gnu": "1.3.56",
|
||||
"@swc/core-linux-arm64-musl": "1.3.56",
|
||||
"@swc/core-linux-x64-gnu": "1.3.56",
|
||||
"@swc/core-linux-x64-musl": "1.3.56",
|
||||
"@swc/core-win32-arm64-msvc": "1.3.56",
|
||||
"@swc/core-win32-ia32-msvc": "1.3.56",
|
||||
"@swc/core-win32-x64-msvc": "1.3.56",
|
||||
"@tensorflow/tfjs": "4.4.0",
|
||||
"@tensorflow/tfjs-node": "4.4.0",
|
||||
"bufferutil": "4.0.7",
|
||||
"@swc/core-linux-arm-gnueabihf": "1.10.16",
|
||||
"@swc/core-linux-arm64-gnu": "1.10.16",
|
||||
"@swc/core-linux-arm64-musl": "1.10.16",
|
||||
"@swc/core-linux-x64-gnu": "1.10.16",
|
||||
"@swc/core-linux-x64-musl": "1.10.16",
|
||||
"@swc/core-win32-arm64-msvc": "1.10.16",
|
||||
"@swc/core-win32-ia32-msvc": "1.10.16",
|
||||
"@swc/core-win32-x64-msvc": "1.10.16",
|
||||
"@tensorflow/tfjs": "4.22.0",
|
||||
"@tensorflow/tfjs-node": "4.22.0",
|
||||
"bufferutil": "4.0.9",
|
||||
"slacc-android-arm-eabi": "0.0.10",
|
||||
"slacc-android-arm64": "0.0.10",
|
||||
"slacc-darwin-arm64": "0.0.10",
|
||||
@ -64,37 +64,37 @@
|
||||
"slacc-linux-x64-musl": "0.0.10",
|
||||
"slacc-win32-arm64-msvc": "0.0.10",
|
||||
"slacc-win32-x64-msvc": "0.0.10",
|
||||
"utf-8-validate": "6.0.3"
|
||||
"utf-8-validate": "6.0.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "3.620.0",
|
||||
"@aws-sdk/lib-storage": "3.620.0",
|
||||
"@bull-board/api": "6.5.0",
|
||||
"@bull-board/fastify": "6.5.0",
|
||||
"@bull-board/ui": "6.5.0",
|
||||
"@aws-sdk/client-s3": "3.749.0",
|
||||
"@aws-sdk/lib-storage": "3.749.0",
|
||||
"@bull-board/api": "6.7.7",
|
||||
"@bull-board/fastify": "6.7.7",
|
||||
"@bull-board/ui": "6.7.7",
|
||||
"@discordapp/twemoji": "15.1.0",
|
||||
"@fastify/accepts": "5.0.1",
|
||||
"@fastify/cookie": "11.0.1",
|
||||
"@fastify/cors": "10.0.1",
|
||||
"@fastify/express": "4.0.1",
|
||||
"@fastify/http-proxy": "10.0.1",
|
||||
"@fastify/multipart": "9.0.1",
|
||||
"@fastify/static": "8.0.2",
|
||||
"@fastify/view": "10.0.1",
|
||||
"@fastify/accepts": "5.0.2",
|
||||
"@fastify/cookie": "11.0.2",
|
||||
"@fastify/cors": "10.0.2",
|
||||
"@fastify/express": "4.0.2",
|
||||
"@fastify/http-proxy": "10.0.2",
|
||||
"@fastify/multipart": "9.0.3",
|
||||
"@fastify/static": "8.1.0",
|
||||
"@fastify/view": "10.0.2",
|
||||
"@misskey-dev/sharp-read-bmp": "1.2.0",
|
||||
"@misskey-dev/summaly": "5.1.0",
|
||||
"@napi-rs/canvas": "0.1.56",
|
||||
"@nestjs/common": "10.4.7",
|
||||
"@nestjs/core": "10.4.7",
|
||||
"@nestjs/testing": "10.4.7",
|
||||
"@misskey-dev/summaly": "5.2.0",
|
||||
"@napi-rs/canvas": "0.1.67",
|
||||
"@nestjs/common": "11.0.9",
|
||||
"@nestjs/core": "11.0.9",
|
||||
"@nestjs/testing": "11.0.9",
|
||||
"@peertube/http-signature": "1.7.0",
|
||||
"@sentry/node": "8.38.0",
|
||||
"@sentry/profiling-node": "8.38.0",
|
||||
"@simplewebauthn/server": "10.0.1",
|
||||
"@sinonjs/fake-timers": "11.2.2",
|
||||
"@sentry/node": "8.55.0",
|
||||
"@sentry/profiling-node": "8.55.0",
|
||||
"@simplewebauthn/server": "12.0.0",
|
||||
"@sinonjs/fake-timers": "11.3.1",
|
||||
"@smithy/node-http-handler": "2.5.0",
|
||||
"@swc/cli": "0.3.12",
|
||||
"@swc/core": "1.9.2",
|
||||
"@swc/cli": "0.6.0",
|
||||
"@swc/core": "1.10.16",
|
||||
"@twemoji/parser": "15.1.1",
|
||||
"accepts": "1.3.8",
|
||||
"ajv": "8.17.1",
|
||||
@ -103,10 +103,10 @@
|
||||
"bcryptjs": "2.4.3",
|
||||
"blurhash": "2.0.5",
|
||||
"body-parser": "1.20.3",
|
||||
"bullmq": "5.26.1",
|
||||
"bullmq": "5.41.1",
|
||||
"cacheable-lookup": "7.0.0",
|
||||
"cbor": "9.0.2",
|
||||
"chalk": "5.3.0",
|
||||
"chalk": "5.4.1",
|
||||
"chalk-template": "1.1.0",
|
||||
"chokidar": "3.6.0",
|
||||
"cli-highlight": "2.1.11",
|
||||
@ -114,46 +114,46 @@
|
||||
"content-disposition": "0.5.4",
|
||||
"date-fns": "2.30.0",
|
||||
"deep-email-validator": "0.1.21",
|
||||
"fastify": "5.0.0",
|
||||
"fastify": "5.2.1",
|
||||
"fastify-raw-body": "5.0.0",
|
||||
"feed": "4.2.2",
|
||||
"file-type": "19.6.0",
|
||||
"fluent-ffmpeg": "2.1.3",
|
||||
"form-data": "4.0.1",
|
||||
"got": "14.4.4",
|
||||
"happy-dom": "15.11.4",
|
||||
"form-data": "4.0.2",
|
||||
"got": "14.4.6",
|
||||
"happy-dom": "16.8.1",
|
||||
"hpagent": "1.2.0",
|
||||
"htmlescape": "1.1.1",
|
||||
"http-link-header": "1.1.3",
|
||||
"ioredis": "5.4.1",
|
||||
"ioredis": "5.5.0",
|
||||
"ip-cidr": "4.0.2",
|
||||
"ipaddr.js": "2.2.0",
|
||||
"is-svg": "5.1.0",
|
||||
"js-yaml": "4.1.0",
|
||||
"jsdom": "24.1.1",
|
||||
"jsdom": "26.0.0",
|
||||
"json5": "2.2.3",
|
||||
"jsonld": "8.3.2",
|
||||
"jsonld": "8.3.3",
|
||||
"jsrsasign": "11.1.0",
|
||||
"juice": "11.0.0",
|
||||
"meilisearch": "0.45.0",
|
||||
"meilisearch": "0.48.2",
|
||||
"mfm-js": "0.24.0",
|
||||
"microformats-parser": "2.0.2",
|
||||
"mime-types": "2.1.35",
|
||||
"misskey-js": "workspace:*",
|
||||
"misskey-reversi": "workspace:*",
|
||||
"ms": "3.0.0-canary.1",
|
||||
"nanoid": "5.0.8",
|
||||
"nanoid": "5.1.0",
|
||||
"nested-property": "4.0.0",
|
||||
"node-fetch": "3.3.2",
|
||||
"nodemailer": "6.9.16",
|
||||
"nodemailer": "6.10.0",
|
||||
"nsfwjs": "4.2.0",
|
||||
"oauth": "0.10.0",
|
||||
"oauth2orize": "1.12.0",
|
||||
"oauth2orize-pkce": "0.1.2",
|
||||
"os-utils": "0.0.14",
|
||||
"otpauth": "9.3.4",
|
||||
"otpauth": "9.3.6",
|
||||
"parse5": "7.2.1",
|
||||
"pg": "8.13.1",
|
||||
"pg": "8.13.3",
|
||||
"pkce-challenge": "4.1.0",
|
||||
"probe-image-size": "7.2.3",
|
||||
"promise-limit": "2.7.0",
|
||||
@ -167,19 +167,19 @@
|
||||
"rename": "1.0.4",
|
||||
"rss-parser": "3.13.0",
|
||||
"rxjs": "7.8.1",
|
||||
"sanitize-html": "2.13.1",
|
||||
"secure-json-parse": "2.7.0",
|
||||
"sanitize-html": "2.14.0",
|
||||
"secure-json-parse": "3.0.2",
|
||||
"sharp": "0.33.5",
|
||||
"slacc": "0.0.10",
|
||||
"strict-event-emitter-types": "2.0.0",
|
||||
"stringz": "2.1.0",
|
||||
"systeminformation": "5.23.5",
|
||||
"systeminformation": "5.25.11",
|
||||
"tinycolor2": "1.6.0",
|
||||
"tmp": "0.2.3",
|
||||
"tsc-alias": "1.8.10",
|
||||
"tsconfig-paths": "4.2.0",
|
||||
"typeorm": "0.3.20",
|
||||
"typescript": "5.6.3",
|
||||
"typescript": "5.7.3",
|
||||
"ulid": "2.3.0",
|
||||
"vary": "1.1.2",
|
||||
"web-push": "3.6.7",
|
||||
@ -188,8 +188,8 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@jest/globals": "29.7.0",
|
||||
"@nestjs/platform-express": "10.4.7",
|
||||
"@simplewebauthn/types": "10.0.0",
|
||||
"@nestjs/platform-express": "10.4.15",
|
||||
"@simplewebauthn/types": "12.0.0",
|
||||
"@swc/jest": "0.2.37",
|
||||
"@types/accepts": "1.3.7",
|
||||
"@types/archiver": "6.0.3",
|
||||
@ -204,15 +204,15 @@
|
||||
"@types/js-yaml": "4.0.9",
|
||||
"@types/jsdom": "21.1.7",
|
||||
"@types/jsonld": "1.5.15",
|
||||
"@types/jsrsasign": "10.5.14",
|
||||
"@types/jsrsasign": "10.5.15",
|
||||
"@types/mime-types": "2.1.4",
|
||||
"@types/ms": "0.7.34",
|
||||
"@types/node": "22.9.0",
|
||||
"@types/nodemailer": "6.4.16",
|
||||
"@types/node": "22.13.4",
|
||||
"@types/nodemailer": "6.4.17",
|
||||
"@types/oauth": "0.9.6",
|
||||
"@types/oauth2orize": "1.11.5",
|
||||
"@types/oauth2orize-pkce": "0.1.2",
|
||||
"@types/pg": "8.11.10",
|
||||
"@types/pg": "8.11.11",
|
||||
"@types/pug": "2.0.10",
|
||||
"@types/qrcode": "1.5.5",
|
||||
"@types/random-seed": "0.3.5",
|
||||
@ -226,18 +226,18 @@
|
||||
"@types/tmp": "0.2.6",
|
||||
"@types/vary": "1.1.3",
|
||||
"@types/web-push": "3.6.4",
|
||||
"@types/ws": "8.5.13",
|
||||
"@typescript-eslint/eslint-plugin": "7.17.0",
|
||||
"@typescript-eslint/parser": "7.17.0",
|
||||
"aws-sdk-client-mock": "4.0.1",
|
||||
"@types/ws": "8.5.14",
|
||||
"@typescript-eslint/eslint-plugin": "8.24.0",
|
||||
"@typescript-eslint/parser": "8.24.0",
|
||||
"aws-sdk-client-mock": "4.1.0",
|
||||
"cross-env": "7.0.3",
|
||||
"eslint-plugin-import": "2.30.0",
|
||||
"eslint-plugin-import": "2.31.0",
|
||||
"execa": "8.0.1",
|
||||
"fkill": "9.0.0",
|
||||
"jest": "29.7.0",
|
||||
"jest-mock": "29.7.0",
|
||||
"nodemon": "3.1.7",
|
||||
"pid-port": "1.0.0",
|
||||
"nodemon": "3.1.9",
|
||||
"pid-port": "1.0.2",
|
||||
"simple-oauth2": "5.1.0"
|
||||
}
|
||||
}
|
||||
|
@ -73,6 +73,7 @@ type Source = {
|
||||
proxyBypassHosts?: string[];
|
||||
|
||||
allowedPrivateNetworks?: string[];
|
||||
disallowExternalApRedirect?: boolean;
|
||||
|
||||
maxFileSize?: number;
|
||||
|
||||
@ -105,8 +106,8 @@ type Source = {
|
||||
|
||||
logging?: {
|
||||
sql?: {
|
||||
disableQueryTruncation? : boolean,
|
||||
enableQueryParamLogging? : boolean,
|
||||
disableQueryTruncation?: boolean,
|
||||
enableQueryParamLogging?: boolean,
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -149,6 +150,7 @@ export type Config = {
|
||||
proxySmtp: string | undefined;
|
||||
proxyBypassHosts: string[] | undefined;
|
||||
allowedPrivateNetworks: string[] | undefined;
|
||||
disallowExternalApRedirect: boolean;
|
||||
maxFileSize: number;
|
||||
clusterLimit: number | undefined;
|
||||
id: string;
|
||||
@ -166,8 +168,8 @@ export type Config = {
|
||||
signToActivityPubGet: boolean | undefined;
|
||||
logging?: {
|
||||
sql?: {
|
||||
disableQueryTruncation? : boolean,
|
||||
enableQueryParamLogging? : boolean,
|
||||
disableQueryTruncation?: boolean,
|
||||
enableQueryParamLogging?: boolean,
|
||||
}
|
||||
}
|
||||
|
||||
@ -287,6 +289,7 @@ export function loadConfig(): Config {
|
||||
proxySmtp: config.proxySmtp,
|
||||
proxyBypassHosts: config.proxyBypassHosts,
|
||||
allowedPrivateNetworks: config.allowedPrivateNetworks,
|
||||
disallowExternalApRedirect: config.disallowExternalApRedirect ?? false,
|
||||
maxFileSize: config.maxFileSize ?? 262144000,
|
||||
clusterLimit: config.clusterLimit,
|
||||
outgoingAddress: config.outgoingAddress,
|
||||
|
@ -43,7 +43,7 @@ export type CaptchaSetting = {
|
||||
siteKey: string | null;
|
||||
secretKey: string | null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export class CaptchaError extends Error {
|
||||
public readonly code: CaptchaErrorCode;
|
||||
@ -59,11 +59,11 @@ export class CaptchaError extends Error {
|
||||
|
||||
export type CaptchaSaveSuccess = {
|
||||
success: true;
|
||||
}
|
||||
};
|
||||
export type CaptchaSaveFailure = {
|
||||
success: false;
|
||||
error: CaptchaError;
|
||||
}
|
||||
};
|
||||
export type CaptchaSaveResult = CaptchaSaveSuccess | CaptchaSaveFailure;
|
||||
|
||||
type CaptchaResponse = {
|
||||
|
@ -60,8 +60,8 @@ export class DownloadService {
|
||||
request: operationTimeout, // whole operation timeout
|
||||
},
|
||||
agent: {
|
||||
http: this.httpRequestService.httpAgent,
|
||||
https: this.httpRequestService.httpsAgent,
|
||||
http: this.httpRequestService.getAgentForHttp(urlObj, true),
|
||||
https: this.httpRequestService.getAgentForHttps(urlObj, true),
|
||||
},
|
||||
http2: false, // default
|
||||
retry: {
|
||||
|
@ -173,7 +173,8 @@ export class DriveService {
|
||||
?? `${ this.meta.objectStorageUseSSL ? 'https' : 'http' }://${ this.meta.objectStorageEndpoint }${ this.meta.objectStoragePort ? `:${this.meta.objectStoragePort}` : '' }/${ this.meta.objectStorageBucket }`;
|
||||
|
||||
// for original
|
||||
const key = `${this.meta.objectStoragePrefix}/${randomUUID()}${ext}`;
|
||||
const prefix = this.meta.objectStoragePrefix ? `${this.meta.objectStoragePrefix}/` : '';
|
||||
const key = `${prefix}${randomUUID()}${ext}`;
|
||||
const url = `${ baseUrl }/${ key }`;
|
||||
|
||||
// for alts
|
||||
@ -190,7 +191,7 @@ export class DriveService {
|
||||
];
|
||||
|
||||
if (alts.webpublic) {
|
||||
webpublicKey = `${this.meta.objectStoragePrefix}/webpublic-${randomUUID()}.${alts.webpublic.ext}`;
|
||||
webpublicKey = `${prefix}webpublic-${randomUUID()}.${alts.webpublic.ext}`;
|
||||
webpublicUrl = `${ baseUrl }/${ webpublicKey }`;
|
||||
|
||||
this.registerLogger.info(`uploading webpublic: ${webpublicKey}`);
|
||||
@ -198,7 +199,7 @@ export class DriveService {
|
||||
}
|
||||
|
||||
if (alts.thumbnail) {
|
||||
thumbnailKey = `${this.meta.objectStoragePrefix}/thumbnail-${randomUUID()}.${alts.thumbnail.ext}`;
|
||||
thumbnailKey = `${prefix}thumbnail-${randomUUID()}.${alts.thumbnail.ext}`;
|
||||
thumbnailUrl = `${ baseUrl }/${ thumbnailKey }`;
|
||||
|
||||
this.registerLogger.info(`uploading thumbnail: ${thumbnailKey}`);
|
||||
|
@ -164,6 +164,13 @@ export class EmailService {
|
||||
available: boolean;
|
||||
reason: null | 'used' | 'format' | 'disposable' | 'mx' | 'smtp' | 'banned' | 'network' | 'blacklist';
|
||||
}> {
|
||||
if (!this.utilityService.validateEmailFormat(emailAddress)) {
|
||||
return {
|
||||
available: false,
|
||||
reason: 'format',
|
||||
};
|
||||
}
|
||||
|
||||
const exist = await this.userProfilesRepository.countBy({
|
||||
emailVerified: true,
|
||||
email: emailAddress,
|
||||
|
@ -9,7 +9,7 @@ import { DI } from '@/di-symbols.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
|
||||
export type FanoutTimelineName =
|
||||
export type FanoutTimelineName = (
|
||||
// home timeline
|
||||
| `homeTimeline:${string}`
|
||||
| `homeTimelineWithFiles:${string}` // only notes with files are included
|
||||
@ -37,6 +37,7 @@ export type FanoutTimelineName =
|
||||
|
||||
// role timelines
|
||||
| `roleTimeline:${string}` // any notes are included
|
||||
);
|
||||
|
||||
@Injectable()
|
||||
export class FanoutTimelineService {
|
||||
|
@ -211,7 +211,7 @@ type SerializedAll<T> = {
|
||||
|
||||
type UndefinedAsNullAll<T> = {
|
||||
[K in keyof T]: T[K] extends undefined ? null : T[K];
|
||||
}
|
||||
};
|
||||
|
||||
export interface InternalEventTypes {
|
||||
userChangeSuspendedState: { id: MiUser['id']; isSuspended: MiUser['isSuspended']; };
|
||||
|
@ -16,7 +16,7 @@ import type { Config } from '@/config.js';
|
||||
import { StatusError } from '@/misc/status-error.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { validateContentTypeSetAsActivityPub } from '@/core/activitypub/misc/validator.js';
|
||||
import { assertActivityMatchesUrls } from '@/core/activitypub/misc/check-against-url.js';
|
||||
import { assertActivityMatchesUrls, FetchAllowSoftFailMask } from '@/core/activitypub/misc/check-against-url.js';
|
||||
import type { IObject } from '@/core/activitypub/type.js';
|
||||
import type { Response } from 'node-fetch';
|
||||
import type { URL } from 'node:url';
|
||||
@ -115,32 +115,32 @@ export class HttpRequestService {
|
||||
/**
|
||||
* Get http non-proxy agent (without local address filtering)
|
||||
*/
|
||||
private httpNative: http.Agent;
|
||||
private readonly httpNative: http.Agent;
|
||||
|
||||
/**
|
||||
* Get https non-proxy agent (without local address filtering)
|
||||
*/
|
||||
private httpsNative: https.Agent;
|
||||
private readonly httpsNative: https.Agent;
|
||||
|
||||
/**
|
||||
* Get http non-proxy agent
|
||||
*/
|
||||
private http: http.Agent;
|
||||
private readonly http: http.Agent;
|
||||
|
||||
/**
|
||||
* Get https non-proxy agent
|
||||
*/
|
||||
private https: https.Agent;
|
||||
private readonly https: https.Agent;
|
||||
|
||||
/**
|
||||
* Get http proxy or non-proxy agent
|
||||
*/
|
||||
public httpAgent: http.Agent;
|
||||
public readonly httpAgent: http.Agent;
|
||||
|
||||
/**
|
||||
* Get https proxy or non-proxy agent
|
||||
*/
|
||||
public httpsAgent: https.Agent;
|
||||
public readonly httpsAgent: https.Agent;
|
||||
|
||||
constructor(
|
||||
@Inject(DI.config)
|
||||
@ -197,7 +197,8 @@ export class HttpRequestService {
|
||||
/**
|
||||
* Get agent by URL
|
||||
* @param url URL
|
||||
* @param bypassProxy Allways bypass proxy
|
||||
* @param bypassProxy Always bypass proxy
|
||||
* @param isLocalAddressAllowed
|
||||
*/
|
||||
@bindThis
|
||||
public getAgentByUrl(url: URL, bypassProxy = false, isLocalAddressAllowed = false): http.Agent | https.Agent {
|
||||
@ -214,8 +215,40 @@ export class HttpRequestService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get agent for http by URL
|
||||
* @param url URL
|
||||
* @param isLocalAddressAllowed
|
||||
*/
|
||||
@bindThis
|
||||
public async getActivityJson(url: string, isLocalAddressAllowed = false): Promise<IObject> {
|
||||
public getAgentForHttp(url: URL, isLocalAddressAllowed = false): http.Agent {
|
||||
if ((this.config.proxyBypassHosts ?? []).includes(url.hostname)) {
|
||||
return isLocalAddressAllowed
|
||||
? this.httpNative
|
||||
: this.http;
|
||||
} else {
|
||||
return this.httpAgent;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get agent for https by URL
|
||||
* @param url URL
|
||||
* @param isLocalAddressAllowed
|
||||
*/
|
||||
@bindThis
|
||||
public getAgentForHttps(url: URL, isLocalAddressAllowed = false): https.Agent {
|
||||
if ((this.config.proxyBypassHosts ?? []).includes(url.hostname)) {
|
||||
return isLocalAddressAllowed
|
||||
? this.httpsNative
|
||||
: this.https;
|
||||
} else {
|
||||
return this.httpsAgent;
|
||||
}
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async getActivityJson(url: string, isLocalAddressAllowed = false, allowSoftfail: FetchAllowSoftFailMask = FetchAllowSoftFailMask.Strict): Promise<IObject> {
|
||||
const res = await this.send(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
@ -232,7 +265,7 @@ export class HttpRequestService {
|
||||
const finalUrl = res.url; // redirects may have been involved
|
||||
const activity = await res.json() as IObject;
|
||||
|
||||
assertActivityMatchesUrls(activity, [finalUrl]);
|
||||
assertActivityMatchesUrls(url, activity, [finalUrl], allowSoftfail);
|
||||
|
||||
return activity;
|
||||
}
|
||||
|
@ -492,7 +492,8 @@ export class MfmService {
|
||||
|
||||
appendChildren(nodes, body);
|
||||
|
||||
const serialized = new XMLSerializer().serializeToString(body);
|
||||
// Remove the unnecessary namespace
|
||||
const serialized = new XMLSerializer().serializeToString(body).replace(/^\s*<p xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">/, '<p>');
|
||||
|
||||
happyDOM.close().catch(err => {});
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { Brackets, In } from 'typeorm';
|
||||
import { Brackets, In, IsNull, Not } from 'typeorm';
|
||||
import { Injectable, Inject } from '@nestjs/common';
|
||||
import type { MiUser, MiLocalUser, MiRemoteUser } from '@/models/User.js';
|
||||
import type { MiNote, IMentionedRemoteUsers } from '@/models/Note.js';
|
||||
@ -189,13 +189,27 @@ export class NoteDeleteService {
|
||||
}) as MiRemoteUser[];
|
||||
}
|
||||
|
||||
@bindThis
|
||||
private async getRenotedOrRepliedRemoteUsers(note: MiNote) {
|
||||
const query = this.notesRepository.createQueryBuilder('note')
|
||||
.leftJoinAndSelect('note.user', 'user')
|
||||
.where(new Brackets(qb => {
|
||||
qb.orWhere('note.renoteId = :renoteId', { renoteId: note.id });
|
||||
qb.orWhere('note.replyId = :replyId', { replyId: note.id });
|
||||
}))
|
||||
.andWhere({ userHost: Not(IsNull()) });
|
||||
const notes = await query.getMany() as (MiNote & { user: MiRemoteUser })[];
|
||||
const remoteUsers = notes.map(({ user }) => user);
|
||||
return remoteUsers;
|
||||
}
|
||||
|
||||
@bindThis
|
||||
private async deliverToConcerned(user: { id: MiLocalUser['id']; host: null; }, note: MiNote, content: any) {
|
||||
this.apDeliverManagerService.deliverToFollowers(user, content);
|
||||
this.relayService.deliverToRelays(user, content);
|
||||
const remoteUsers = await this.getMentionedRemoteUsers(note);
|
||||
for (const remoteUser of remoteUsers) {
|
||||
this.apDeliverManagerService.deliverToUser(user, content, remoteUser);
|
||||
}
|
||||
this.apDeliverManagerService.deliverToUsers(user, content, [
|
||||
...await this.getMentionedRemoteUsers(note),
|
||||
...await this.getRenotedOrRepliedRemoteUsers(note),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ export class RemoteUserResolveService {
|
||||
if (user == null) {
|
||||
const self = await this.resolveSelf(acctLower);
|
||||
|
||||
if (self.href.startsWith(this.config.url)) {
|
||||
if (this.utilityService.isUriLocal(self.href)) {
|
||||
const local = this.apDbResolverService.parseUri(self.href);
|
||||
if (local.local && local.type === 'users') {
|
||||
// the LR points to local
|
||||
|
@ -220,7 +220,7 @@ export class SearchService {
|
||||
.leftJoinAndSelect('renote.user', 'renoteUser');
|
||||
|
||||
if (this.config.fulltextSearch?.provider === 'sqlPgroonga') {
|
||||
query.andWhere('note.text &@ :q', { q });
|
||||
query.andWhere('note.text &@~ :q', { q });
|
||||
} else {
|
||||
query.andWhere('LOWER(note.text) LIKE :q', { q: `%${ sqlLikeEscape(q.toLowerCase()) }%` });
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ import { QueueService } from '@/core/QueueService.js';
|
||||
import type { OnApplicationShutdown } from '@nestjs/common';
|
||||
|
||||
export type UserWebhookPayload<T extends WebhookEventTypes> =
|
||||
T extends 'note' | 'reply' | 'renote' |'mention' ? {
|
||||
T extends 'note' | 'reply' | 'renote' | 'mention' ? {
|
||||
note: Packed<'Note'>,
|
||||
} :
|
||||
T extends 'follow' | 'unfollow' ? {
|
||||
|
@ -38,6 +38,14 @@ export class UtilityService {
|
||||
return this.punyHost(uri) === this.toPuny(this.config.host);
|
||||
}
|
||||
|
||||
// メールアドレスのバリデーションを行う
|
||||
// https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address
|
||||
@bindThis
|
||||
public validateEmailFormat(email: string): boolean {
|
||||
const regexp = /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
|
||||
return regexp.test(email);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public isBlockedHost(blockedHosts: string[], host: string | null): boolean {
|
||||
if (host == null) return false;
|
||||
|
@ -127,11 +127,11 @@ export class WebAuthnService {
|
||||
const { registrationInfo } = verification;
|
||||
|
||||
return {
|
||||
credentialID: registrationInfo.credentialID,
|
||||
credentialPublicKey: registrationInfo.credentialPublicKey,
|
||||
credentialID: registrationInfo.credential.id,
|
||||
credentialPublicKey: registrationInfo.credential.publicKey,
|
||||
attestationObject: registrationInfo.attestationObject,
|
||||
fmt: registrationInfo.fmt,
|
||||
counter: registrationInfo.counter,
|
||||
counter: registrationInfo.credential.counter,
|
||||
userVerified: registrationInfo.userVerified,
|
||||
credentialDeviceType: registrationInfo.credentialDeviceType,
|
||||
credentialBackedUp: registrationInfo.credentialBackedUp,
|
||||
@ -212,9 +212,9 @@ export class WebAuthnService {
|
||||
expectedChallenge: challenge,
|
||||
expectedOrigin: relyingParty.origin,
|
||||
expectedRPID: relyingParty.rpId,
|
||||
authenticator: {
|
||||
credentialID: key.id,
|
||||
credentialPublicKey: Buffer.from(key.publicKey, 'base64url'),
|
||||
credential: {
|
||||
id: key.id,
|
||||
publicKey: Buffer.from(key.publicKey, 'base64url'),
|
||||
counter: key.counter,
|
||||
transports: key.transports ? key.transports as AuthenticatorTransportFuture[] : undefined,
|
||||
},
|
||||
@ -292,9 +292,9 @@ export class WebAuthnService {
|
||||
expectedChallenge: challenge,
|
||||
expectedOrigin: relyingParty.origin,
|
||||
expectedRPID: relyingParty.rpId,
|
||||
authenticator: {
|
||||
credentialID: key.id,
|
||||
credentialPublicKey: Buffer.from(key.publicKey, 'base64url'),
|
||||
credential: {
|
||||
id: key.id,
|
||||
publicKey: Buffer.from(key.publicKey, 'base64url'),
|
||||
counter: key.counter,
|
||||
transports: key.transports ? key.transports as AuthenticatorTransportFuture[] : undefined,
|
||||
},
|
||||
|
@ -196,6 +196,25 @@ export class ApDeliverManagerService {
|
||||
await manager.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deliver activity to users
|
||||
* @param actor
|
||||
* @param activity Activity
|
||||
* @param targets Target users
|
||||
*/
|
||||
@bindThis
|
||||
public async deliverToUsers(actor: { id: MiLocalUser['id']; host: null; }, activity: IActivity, targets: MiRemoteUser[]): Promise<void> {
|
||||
const manager = new DeliverManager(
|
||||
this.userEntityService,
|
||||
this.followingsRepository,
|
||||
this.queueService,
|
||||
actor,
|
||||
activity,
|
||||
);
|
||||
for (const to of targets) manager.addDirectRecipe(to);
|
||||
await manager.execute();
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public createDeliverManager(actor: { id: MiUser['id']; host: null; }, activity: IActivity | null): DeliverManager {
|
||||
return new DeliverManager(
|
||||
|
@ -27,6 +27,7 @@ import type { UsersRepository, UserProfilesRepository, NotesRepository, DriveFil
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { CustomEmojiService } from '@/core/CustomEmojiService.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { UtilityService } from '@/core/UtilityService.js';
|
||||
import { JsonLdService } from './JsonLdService.js';
|
||||
import { ApMfmService } from './ApMfmService.js';
|
||||
import { CONTEXT } from './misc/contexts.js';
|
||||
@ -61,6 +62,7 @@ export class ApRendererService {
|
||||
private apMfmService: ApMfmService,
|
||||
private mfmService: MfmService,
|
||||
private idService: IdService,
|
||||
private utilityService: UtilityService,
|
||||
) {
|
||||
}
|
||||
|
||||
@ -577,7 +579,7 @@ export class ApRendererService {
|
||||
|
||||
@bindThis
|
||||
public renderUndo(object: string | IObject, user: { id: MiUser['id'] }): IUndo {
|
||||
const id = typeof object !== 'string' && typeof object.id === 'string' && object.id.startsWith(this.config.url) ? `${object.id}/undo` : undefined;
|
||||
const id = typeof object !== 'string' && typeof object.id === 'string' && this.utilityService.isUriLocal(object.id) ? `${object.id}/undo` : undefined;
|
||||
|
||||
return {
|
||||
type: 'Undo',
|
||||
|
@ -17,7 +17,7 @@ import { LoggerService } from '@/core/LoggerService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import type Logger from '@/logger.js';
|
||||
import { validateContentTypeSetAsActivityPub } from '@/core/activitypub/misc/validator.js';
|
||||
import { assertActivityMatchesUrls } from '@/core/activitypub/misc/check-against-url.js';
|
||||
import { assertActivityMatchesUrls, FetchAllowSoftFailMask as FetchAllowSoftFailMask } from '@/core/activitypub/misc/check-against-url.js';
|
||||
import type { IObject } from './type.js';
|
||||
|
||||
type Request = {
|
||||
@ -185,7 +185,7 @@ export class ApRequestService {
|
||||
* @param url URL to fetch
|
||||
*/
|
||||
@bindThis
|
||||
public async signedGet(url: string, user: { id: MiUser['id'] }, followAlternate?: boolean): Promise<unknown> {
|
||||
public async signedGet(url: string, user: { id: MiUser['id'] }, allowSoftfail: FetchAllowSoftFailMask = FetchAllowSoftFailMask.Strict, followAlternate?: boolean): Promise<unknown> {
|
||||
const _followAlternate = followAlternate ?? true;
|
||||
const keypair = await this.userKeypairService.getUserKeypair(user.id);
|
||||
|
||||
@ -243,7 +243,7 @@ export class ApRequestService {
|
||||
if (alternate) {
|
||||
const href = alternate.getAttribute('href');
|
||||
if (href && this.utilityService.punyHost(url) === this.utilityService.punyHost(href)) {
|
||||
return await this.signedGet(href, user, false);
|
||||
return await this.signedGet(href, user, allowSoftfail, false);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
@ -258,7 +258,7 @@ export class ApRequestService {
|
||||
const finalUrl = res.url; // redirects may have been involved
|
||||
const activity = await res.json() as IObject;
|
||||
|
||||
assertActivityMatchesUrls(activity, [finalUrl]);
|
||||
assertActivityMatchesUrls(url, activity, [finalUrl], allowSoftfail);
|
||||
|
||||
return activity;
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ import { ApRendererService } from './ApRendererService.js';
|
||||
import { ApRequestService } from './ApRequestService.js';
|
||||
import type { IObject, ICollection, IOrderedCollection } from './type.js';
|
||||
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
||||
import { FetchAllowSoftFailMask } from './misc/check-against-url.js';
|
||||
|
||||
export class Resolver {
|
||||
private history: Set<string>;
|
||||
@ -72,7 +73,7 @@ export class Resolver {
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async resolve(value: string | IObject): Promise<IObject> {
|
||||
public async resolve(value: string | IObject, allowSoftfail: FetchAllowSoftFailMask = FetchAllowSoftFailMask.Strict): Promise<IObject> {
|
||||
if (typeof value !== 'string') {
|
||||
return value;
|
||||
}
|
||||
@ -108,8 +109,8 @@ export class Resolver {
|
||||
}
|
||||
|
||||
const object = (this.user
|
||||
? await this.apRequestService.signedGet(value, this.user) as IObject
|
||||
: await this.httpRequestService.getActivityJson(value)) as IObject;
|
||||
? await this.apRequestService.signedGet(value, this.user, allowSoftfail) as IObject
|
||||
: await this.httpRequestService.getActivityJson(value, undefined, allowSoftfail)) as IObject;
|
||||
|
||||
if (
|
||||
Array.isArray(object['@context']) ?
|
||||
@ -118,19 +119,7 @@ export class Resolver {
|
||||
) {
|
||||
throw new IdentifiableError('72180409-793c-4973-868e-5a118eb5519b', 'invalid response');
|
||||
}
|
||||
|
||||
// HttpRequestService / ApRequestService have already checked that
|
||||
// `object.id` or `object.url` matches the URL used to fetch the
|
||||
// object after redirects; here we double-check that no redirects
|
||||
// bounced between hosts
|
||||
if (object.id == null) {
|
||||
throw new IdentifiableError('ad2dc287-75c1-44c4-839d-3d2e64576675', 'invalid AP object: missing id');
|
||||
}
|
||||
|
||||
if (this.utilityService.punyHost(object.id) !== this.utilityService.punyHost(value)) {
|
||||
throw new IdentifiableError('fd93c2fa-69a8-440f-880b-bf178e0ec877', `invalid AP object ${value}: id ${object.id} has different host`);
|
||||
}
|
||||
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
|
@ -4,18 +4,124 @@
|
||||
*/
|
||||
import type { IObject } from '../type.js';
|
||||
|
||||
export function assertActivityMatchesUrls(activity: IObject, urls: string[]) {
|
||||
const hosts = urls.map(it => new URL(it).host);
|
||||
|
||||
const idOk = activity.id !== undefined && hosts.includes(new URL(activity.id).host);
|
||||
|
||||
// technically `activity.url` could be an `ApObject = IObject |
|
||||
// string | (IObject | string)[]`, but if it's a complicated thing
|
||||
// and the `activity.id` doesn't match, I think we're fine
|
||||
// rejecting the activity
|
||||
const urlOk = typeof(activity.url) === 'string' && hosts.includes(new URL(activity.url).host);
|
||||
|
||||
if (!idOk && !urlOk) {
|
||||
throw new Error(`bad Activity: neither id(${activity?.id}) nor url(${activity?.url}) match location(${urls})`);
|
||||
}
|
||||
export enum FetchAllowSoftFailMask {
|
||||
// Allow no softfail flags
|
||||
Strict = 0,
|
||||
// The values in tuple (requestUrl, finalUrl, objectId) are not all identical
|
||||
//
|
||||
// This condition is common for user-initiated lookups but should not be allowed in federation loop
|
||||
//
|
||||
// Allow variations:
|
||||
// good example: https://alice.example.com/@user -> https://alice.example.com/user/:userId
|
||||
// problematic example: https://alice.example.com/redirect?url=https://bad.example.com/ -> https://bad.example.com/ -> https://alice.example.com/somethingElse
|
||||
NonCanonicalId = 1 << 0,
|
||||
// Allow the final object to be at most one subdomain deeper than the request URL, similar to SPF relaxed alignment
|
||||
//
|
||||
// Currently no code path allows this flag to be set, but is kept in case of future use as some niche deployments do this, and we provide a pre-reviewed mechanism to opt-in.
|
||||
//
|
||||
// Allow variations:
|
||||
// good example: https://example.com/@user -> https://activitypub.example.com/@user { id: 'https://activitypub.example.com/@user' }
|
||||
// problematic example: https://example.com/@user -> https://untrusted.example.com/@user { id: 'https://untrusted.example.com/@user' }
|
||||
MisalignedOrigin = 1 << 1,
|
||||
// The requested URL has a different host than the returned object ID, although the final URL is still consistent with the object ID
|
||||
//
|
||||
// This condition is common for user-initiated lookups using an intermediate host but should not be allowed in federation loops
|
||||
//
|
||||
// Allow variations:
|
||||
// good example: https://alice.example.com/@user@bob.example.com -> https://bob.example.com/@user { id: 'https://bob.example.com/@user' }
|
||||
// problematic example: https://alice.example.com/definitelyAlice -> https://bob.example.com/@somebodyElse { id: 'https://bob.example.com/@somebodyElse' }
|
||||
CrossOrigin = 1 << 2 | MisalignedOrigin,
|
||||
// Allow all softfail flags
|
||||
//
|
||||
// do not use this flag on released code
|
||||
Any = ~0,
|
||||
}
|
||||
|
||||
/**
|
||||
* Fuzz match on whether the candidate host has authority over the request host
|
||||
*
|
||||
* @param requestHost The host of the requested resources
|
||||
* @param candidateHost The host of final response
|
||||
* @returns Whether the candidate host has authority over the request host, or if a soft fail is required for a match
|
||||
*/
|
||||
function hostFuzzyMatch(requestHost: string, candidateHost: string): FetchAllowSoftFailMask {
|
||||
const requestFqdn = requestHost.endsWith('.') ? requestHost : `${requestHost}.`;
|
||||
const candidateFqdn = candidateHost.endsWith('.') ? candidateHost : `${candidateHost}.`;
|
||||
|
||||
if (requestFqdn === candidateFqdn) {
|
||||
return FetchAllowSoftFailMask.Strict;
|
||||
}
|
||||
|
||||
// allow only one case where candidateHost is a first-level subdomain of requestHost
|
||||
const requestDnsDepth = requestFqdn.split('.').length;
|
||||
const candidateDnsDepth = candidateFqdn.split('.').length;
|
||||
|
||||
if ((candidateDnsDepth - requestDnsDepth) !== 1) {
|
||||
return FetchAllowSoftFailMask.CrossOrigin;
|
||||
}
|
||||
|
||||
if (`.${candidateHost}`.endsWith(`.${requestHost}`)) {
|
||||
return FetchAllowSoftFailMask.MisalignedOrigin;
|
||||
}
|
||||
|
||||
return FetchAllowSoftFailMask.CrossOrigin;
|
||||
}
|
||||
|
||||
// normalize host names by removing www. prefix
|
||||
function normalizeSynonymousSubdomain(url: URL | string): URL {
|
||||
const urlParsed = url instanceof URL ? url : new URL(url);
|
||||
const host = urlParsed.host;
|
||||
const normalizedHost = host.replace(/^www\./, '');
|
||||
return new URL(urlParsed.toString().replace(host, normalizedHost));
|
||||
}
|
||||
|
||||
export function assertActivityMatchesUrls(requestUrl: string | URL, activity: IObject, candidateUrls: (string | URL)[], allowSoftfail: FetchAllowSoftFailMask): FetchAllowSoftFailMask {
|
||||
// must have a unique identifier to verify authority
|
||||
if (!activity.id) {
|
||||
throw new Error('bad Activity: missing id field');
|
||||
}
|
||||
|
||||
let softfail = 0;
|
||||
|
||||
// if the flag is allowed, set the flag on return otherwise throw
|
||||
const requireSoftfail = (needed: FetchAllowSoftFailMask, message: string) => {
|
||||
if ((allowSoftfail & needed) !== needed) {
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
softfail |= needed;
|
||||
};
|
||||
|
||||
const requestUrlParsed = normalizeSynonymousSubdomain(requestUrl);
|
||||
const idParsed = normalizeSynonymousSubdomain(activity.id);
|
||||
|
||||
const candidateUrlsParsed = candidateUrls.map(it => normalizeSynonymousSubdomain(it));
|
||||
|
||||
const requestUrlSecure = requestUrlParsed.protocol === 'https:';
|
||||
const finalUrlSecure = candidateUrlsParsed.every(it => it.protocol === 'https:');
|
||||
if (requestUrlSecure && !finalUrlSecure) {
|
||||
throw new Error(`bad Activity: id(${activity.id}) is not allowed to have http:// in the url`);
|
||||
}
|
||||
|
||||
// Compare final URL to the ID
|
||||
if (!candidateUrlsParsed.some(it => it.href === idParsed.href)) {
|
||||
requireSoftfail(FetchAllowSoftFailMask.NonCanonicalId, `bad Activity: id(${activity.id}) does not match response url(${candidateUrlsParsed.map(it => it.toString())})`);
|
||||
|
||||
// at lease host need to match exactly (ActivityPub requirement)
|
||||
if (!candidateUrlsParsed.some(it => idParsed.host === it.host)) {
|
||||
throw new Error(`bad Activity: id(${activity.id}) does not match response host(${candidateUrlsParsed.map(it => it.host)})`);
|
||||
}
|
||||
}
|
||||
|
||||
// Compare request URL to the ID
|
||||
if (!requestUrlParsed.href.includes(idParsed.href)) {
|
||||
requireSoftfail(FetchAllowSoftFailMask.NonCanonicalId, `bad Activity: id(${activity.id}) does not match request url(${requestUrlParsed.toString()})`);
|
||||
|
||||
// if cross-origin lookup is allowed, we can accept some variation between the original request URL to the final object ID (but not between the final URL and the object ID)
|
||||
const hostResult = hostFuzzyMatch(requestUrlParsed.host, idParsed.host);
|
||||
|
||||
requireSoftfail(hostResult, `bad Activity: id(${activity.id}) is valid but is not the same origin as request url(${requestUrlParsed.toString()})`);
|
||||
}
|
||||
|
||||
return softfail;
|
||||
}
|
||||
|
@ -97,6 +97,7 @@ export class MetaEntityService {
|
||||
enableTurnstile: instance.enableTurnstile,
|
||||
turnstileSiteKey: instance.turnstileSiteKey,
|
||||
enableTestcaptcha: instance.enableTestcaptcha,
|
||||
googleAnalyticsMeasurementId: instance.googleAnalyticsMeasurementId,
|
||||
swPublickey: instance.swPublicKey,
|
||||
themeColor: instance.themeColor,
|
||||
mascotImageUrl: instance.mascotImageUrl ?? '/assets/ai.png',
|
||||
|
@ -57,12 +57,14 @@ const ajv = new Ajv();
|
||||
|
||||
function isLocalUser(user: MiUser): user is MiLocalUser;
|
||||
function isLocalUser<T extends { host: MiUser['host'] }>(user: T): user is (T & { host: null; });
|
||||
|
||||
function isLocalUser(user: MiUser | { host: MiUser['host'] }): boolean {
|
||||
return user.host == null;
|
||||
}
|
||||
|
||||
function isRemoteUser(user: MiUser): user is MiRemoteUser;
|
||||
function isRemoteUser<T extends { host: MiUser['host'] }>(user: T): user is (T & { host: string; });
|
||||
|
||||
function isRemoteUser(user: MiUser | { host: MiUser['host'] }): boolean {
|
||||
return !isLocalUser(user);
|
||||
}
|
||||
@ -78,7 +80,7 @@ export type UserRelation = {
|
||||
isBlocked: boolean
|
||||
isMuted: boolean
|
||||
isRenoteMuted: boolean
|
||||
}
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class UserEntityService implements OnModuleInit {
|
||||
|
@ -143,7 +143,7 @@ type OfSchema = {
|
||||
readonly anyOf?: ReadonlyArray<Schema>;
|
||||
readonly oneOf?: ReadonlyArray<Schema>;
|
||||
readonly allOf?: ReadonlyArray<Schema>;
|
||||
}
|
||||
};
|
||||
|
||||
export interface Schema extends OfSchema {
|
||||
readonly type?: TypeStringef;
|
||||
@ -217,7 +217,7 @@ type ObjectSchemaTypeDef<p extends Schema> =
|
||||
:
|
||||
p['anyOf'] extends ReadonlyArray<Schema> ? never : // see CONTRIBUTING.md
|
||||
p['allOf'] extends ReadonlyArray<Schema> ? UnionToIntersection<UnionSchemaType<p['allOf']>> :
|
||||
any
|
||||
any;
|
||||
|
||||
type ObjectSchemaType<p extends Schema> = NullOrUndefined<p, ObjectSchemaTypeDef<p>>;
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
export type JsonValue = JsonArray | JsonObject | string | number | boolean | null;
|
||||
export type JsonObject = {[K in string]?: JsonValue};
|
||||
export type JsonObject = { [K in string]?: JsonValue };
|
||||
export type JsonArray = JsonValue[];
|
||||
|
||||
export function isJsonObject(value: JsonValue | undefined): value is JsonObject {
|
||||
|
@ -658,4 +658,10 @@ export class MiMeta {
|
||||
default: '{}',
|
||||
})
|
||||
public federationHosts: string[];
|
||||
|
||||
@Column('varchar', {
|
||||
length: 64,
|
||||
nullable: true,
|
||||
})
|
||||
public googleAnalyticsMeasurementId: string | null;
|
||||
}
|
||||
|
@ -90,6 +90,10 @@ export type MiNotification = {
|
||||
type: 'login';
|
||||
id: string;
|
||||
createdAt: string;
|
||||
} | {
|
||||
type: 'createToken';
|
||||
id: string;
|
||||
createdAt: string;
|
||||
} | {
|
||||
type: 'app';
|
||||
id: string;
|
||||
|
@ -288,24 +288,24 @@ export class MiUser {
|
||||
export type MiLocalUser = MiUser & {
|
||||
host: null;
|
||||
uri: null;
|
||||
}
|
||||
};
|
||||
|
||||
export type MiPartialLocalUser = Partial<MiUser> & {
|
||||
id: MiUser['id'];
|
||||
host: null;
|
||||
uri: null;
|
||||
}
|
||||
};
|
||||
|
||||
export type MiRemoteUser = MiUser & {
|
||||
host: string;
|
||||
uri: string;
|
||||
}
|
||||
};
|
||||
|
||||
export type MiPartialRemoteUser = Partial<MiUser> & {
|
||||
id: MiUser['id'];
|
||||
host: string;
|
||||
uri: string;
|
||||
}
|
||||
};
|
||||
|
||||
export const localUsernameSchema = { type: 'string', pattern: /^\w{1,20}$/.toString().slice(1, -1) } as const;
|
||||
export const passwordSchema = { type: 'string', minLength: 1 } as const;
|
||||
|
@ -119,6 +119,10 @@ export const packedMetaLiteSchema = {
|
||||
type: 'boolean',
|
||||
optional: false, nullable: false,
|
||||
},
|
||||
googleAnalyticsMeasurementId: {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
},
|
||||
swPublickey: {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
|
@ -332,6 +332,16 @@ export const packedNotificationSchema = {
|
||||
enum: ['login'],
|
||||
},
|
||||
},
|
||||
}, {
|
||||
type: 'object',
|
||||
properties: {
|
||||
...baseSchema.properties,
|
||||
type: {
|
||||
type: 'string',
|
||||
optional: false, nullable: false,
|
||||
enum: ['createToken'],
|
||||
},
|
||||
},
|
||||
}, {
|
||||
type: 'object',
|
||||
properties: {
|
||||
|
@ -92,7 +92,7 @@ const sqlLogger = dbLogger.createSubLogger('sql', 'gray');
|
||||
export type LoggerProps = {
|
||||
disableQueryTruncation?: boolean;
|
||||
enableQueryParamLogging?: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
function highlightSql(sql: string) {
|
||||
return highlight.highlight(sql, {
|
||||
|
@ -29,7 +29,7 @@ export type ModeratorInactivityEvaluationResult = {
|
||||
isModeratorsInactive: boolean;
|
||||
inactiveModerators: MiUser[];
|
||||
remainingTime: ModeratorInactivityRemainingTime;
|
||||
}
|
||||
};
|
||||
|
||||
export type ModeratorInactivityRemainingTime = {
|
||||
time: number;
|
||||
|
@ -107,12 +107,12 @@ export class InboxProcessorService implements OnApplicationShutdown {
|
||||
|
||||
// それでもわからなければ終了
|
||||
if (authUser == null) {
|
||||
throw new Bull.UnrecoverableError('skip: failed to resolve user');
|
||||
throw new Bull.UnrecoverableError(`skip: failed to resolve user ${getApId(activity.actor)}`);
|
||||
}
|
||||
|
||||
// publicKey がなくても終了
|
||||
if (authUser.key == null) {
|
||||
throw new Bull.UnrecoverableError('skip: failed to resolve user publicKey');
|
||||
throw new Bull.UnrecoverableError(`skip: failed to resolve user publicKey ${getApId(activity.actor)}`);
|
||||
}
|
||||
|
||||
// HTTP-Signatureの検証
|
||||
|
@ -38,7 +38,7 @@ export type RelationshipJobData = {
|
||||
silent?: boolean;
|
||||
requestId?: string;
|
||||
withReplies?: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
export type DbJobData<T extends keyof DbJobMap> = DbJobMap[T];
|
||||
|
||||
@ -61,11 +61,11 @@ export type DbJobMap = {
|
||||
importUserLists: DbUserImportJobData;
|
||||
importCustomEmojis: DbUserImportJobData;
|
||||
deleteAccount: DbUserDeleteJobData;
|
||||
}
|
||||
};
|
||||
|
||||
export type DbJobDataWithUser = {
|
||||
user: ThinUser;
|
||||
}
|
||||
};
|
||||
|
||||
export type DbExportFollowingData = {
|
||||
user: ThinUser;
|
||||
@ -75,7 +75,7 @@ export type DbExportFollowingData = {
|
||||
|
||||
export type DBExportAntennasData = {
|
||||
user: ThinUser
|
||||
}
|
||||
};
|
||||
|
||||
export type DbUserDeleteJobData = {
|
||||
user: ThinUser;
|
||||
@ -91,7 +91,7 @@ export type DbUserImportJobData = {
|
||||
export type DBAntennaImportJobData = {
|
||||
user: ThinUser,
|
||||
antenna: Antenna
|
||||
}
|
||||
};
|
||||
|
||||
export type DbUserImportToDbJobData = {
|
||||
user: ThinUser;
|
||||
|
@ -103,6 +103,43 @@ export class ServerService implements OnApplicationShutdown {
|
||||
serve: false,
|
||||
});
|
||||
|
||||
// if the requester looks like to be performing an ActivityPub object lookup, reject all external redirects
|
||||
//
|
||||
// this will break lookup that involve copying a URL from a third-party server, like trying to lookup http://charlie.example.com/@alice@alice.com
|
||||
//
|
||||
// this is not required by standard but protect us from peers that did not validate final URL.
|
||||
if (this.config.disallowExternalApRedirect) {
|
||||
const maybeApLookupRegex = /application\/activity\+json|application\/ld\+json.+activitystreams/i;
|
||||
fastify.addHook('onSend', (request, reply, _, done) => {
|
||||
const location = reply.getHeader('location');
|
||||
if (reply.statusCode < 300 || reply.statusCode >= 400 || typeof location !== 'string') {
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!maybeApLookupRegex.test(request.headers.accept ?? '')) {
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
const effectiveLocation = process.env.NODE_ENV === 'production' ? location : location.replace(/^http:\/\//, 'https://');
|
||||
if (effectiveLocation.startsWith(`https://${this.config.host}/`)) {
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
reply.status(406);
|
||||
reply.removeHeader('location');
|
||||
reply.header('content-type', 'text/plain; charset=utf-8');
|
||||
reply.header('link', `<${encodeURI(location)}>; rel="canonical"`);
|
||||
done(null, [
|
||||
"Refusing to relay remote ActivityPub object lookup.",
|
||||
"",
|
||||
`Please remove 'application/activity+json' and 'application/ld+json' from the Accept header or fetch using the authoritative URL at ${location}.`,
|
||||
].join('\n'));
|
||||
});
|
||||
}
|
||||
|
||||
fastify.register(this.apiServerService.createServer, { prefix: '/api' });
|
||||
fastify.register(this.openApiServerService.createServer);
|
||||
fastify.register(this.fileServerService.createServer);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user