Compare commits
46 Commits
13.0.0-rc.
...
13.0.0-rc.
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d513848f65 | ||
![]() |
ae6af6aefd | ||
![]() |
a0ae9f7593 | ||
![]() |
dace5b6940 | ||
![]() |
2d8b97287e | ||
![]() |
ec63a50de2 | ||
![]() |
6e2d7e9792 | ||
![]() |
39349dcba5 | ||
![]() |
a5b1fe5d16 | ||
![]() |
91bbb67e4a | ||
![]() |
f368bce9d5 | ||
![]() |
fbfe42d6f0 | ||
![]() |
f3c5ca6cf4 | ||
![]() |
0022267072 | ||
![]() |
30fced38c4 | ||
![]() |
7e5f3dbf11 | ||
![]() |
9f0dfb5517 | ||
![]() |
678c7d9502 | ||
![]() |
91a3c3943d | ||
![]() |
c46b45a467 | ||
![]() |
9385767b12 | ||
![]() |
7795ff0c95 | ||
![]() |
a9acd72eb7 | ||
![]() |
67d366c3ca | ||
![]() |
1f8f051ee2 | ||
![]() |
94004b7a3f | ||
![]() |
3e9f88506e | ||
![]() |
81f11d8f86 | ||
![]() |
518b3e2f73 | ||
![]() |
d0157b3bfd | ||
![]() |
7fc8d2e6d5 | ||
![]() |
fb0f9711ba | ||
![]() |
92136272b0 | ||
![]() |
e1159e9ef2 | ||
![]() |
a2e61c6708 | ||
![]() |
726959911c | ||
![]() |
d59914b959 | ||
![]() |
07025caee9 | ||
![]() |
1c0289e490 | ||
![]() |
275fcd8bbc | ||
![]() |
0c0aa93668 | ||
![]() |
bfcd5ea440 | ||
![]() |
3ff43cca02 | ||
![]() |
6bd536c526 | ||
![]() |
7738a36014 | ||
![]() |
daddec8362 |
151
.config/docker_example.yml
Normal file
151
.config/docker_example.yml
Normal file
@@ -0,0 +1,151 @@
|
||||
#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
# Misskey configuration
|
||||
#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
# ┌─────┐
|
||||
#───┘ URL └─────────────────────────────────────────────────────
|
||||
|
||||
# Final accessible URL seen by a user.
|
||||
url: https://example.tld/
|
||||
|
||||
# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE
|
||||
# URL SETTINGS AFTER THAT!
|
||||
|
||||
# ┌───────────────────────┐
|
||||
#───┘ Port and TLS settings └───────────────────────────────────
|
||||
|
||||
#
|
||||
# Misskey requires a reverse proxy to support HTTPS connections.
|
||||
#
|
||||
# +----- https://example.tld/ ------------+
|
||||
# +------+ |+-------------+ +----------------+|
|
||||
# | User | ---> || Proxy (443) | ---> | Misskey (3000) ||
|
||||
# +------+ |+-------------+ +----------------+|
|
||||
# +---------------------------------------+
|
||||
#
|
||||
# You need to set up a reverse proxy. (e.g. nginx)
|
||||
# An encrypted connection with HTTPS is highly recommended
|
||||
# because tokens may be transferred in GET requests.
|
||||
|
||||
# The port that your Misskey server should listen on.
|
||||
port: 3000
|
||||
|
||||
# ┌──────────────────────────┐
|
||||
#───┘ PostgreSQL configuration └────────────────────────────────
|
||||
|
||||
db:
|
||||
host: db
|
||||
port: 5432
|
||||
|
||||
# Database name
|
||||
db: misskey
|
||||
|
||||
# Auth
|
||||
user: example-misskey-user
|
||||
pass: example-misskey-pass
|
||||
|
||||
# Whether disable Caching queries
|
||||
#disableCache: true
|
||||
|
||||
# Extra Connection options
|
||||
#extra:
|
||||
# ssl: true
|
||||
|
||||
# ┌─────────────────────┐
|
||||
#───┘ Redis configuration └─────────────────────────────────────
|
||||
|
||||
redis:
|
||||
host: redis
|
||||
port: 6379
|
||||
#family: 0 # 0=Both, 4=IPv4, 6=IPv6
|
||||
#pass: example-pass
|
||||
#prefix: example-prefix
|
||||
#db: 1
|
||||
|
||||
# ┌─────────────────────────────┐
|
||||
#───┘ Elasticsearch configuration └─────────────────────────────
|
||||
|
||||
#elasticsearch:
|
||||
# host: localhost
|
||||
# port: 9200
|
||||
# ssl: false
|
||||
# user:
|
||||
# pass:
|
||||
|
||||
# ┌───────────────┐
|
||||
#───┘ ID generation └───────────────────────────────────────────
|
||||
|
||||
# You can select the ID generation method.
|
||||
# You don't usually need to change this setting, but you can
|
||||
# change it according to your preferences.
|
||||
|
||||
# Available methods:
|
||||
# aid ... Short, Millisecond accuracy
|
||||
# meid ... Similar to ObjectID, Millisecond accuracy
|
||||
# ulid ... Millisecond accuracy
|
||||
# objectid ... This is left for backward compatibility
|
||||
|
||||
# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE
|
||||
# ID SETTINGS AFTER THAT!
|
||||
|
||||
id: 'aid'
|
||||
|
||||
# ┌─────────────────────┐
|
||||
#───┘ Other configuration └─────────────────────────────────────
|
||||
|
||||
# Whether disable HSTS
|
||||
#disableHsts: true
|
||||
|
||||
# Number of worker processes
|
||||
#clusterLimit: 1
|
||||
|
||||
# Job concurrency per worker
|
||||
# deliverJobConcurrency: 128
|
||||
# inboxJobConcurrency: 16
|
||||
|
||||
# Job rate limiter
|
||||
# deliverJobPerSec: 128
|
||||
# inboxJobPerSec: 16
|
||||
|
||||
# Job attempts
|
||||
# deliverJobMaxAttempts: 12
|
||||
# inboxJobMaxAttempts: 8
|
||||
|
||||
# IP address family used for outgoing request (ipv4, ipv6 or dual)
|
||||
#outgoingAddressFamily: ipv4
|
||||
|
||||
# Syslog option
|
||||
#syslog:
|
||||
# host: localhost
|
||||
# port: 514
|
||||
|
||||
# Proxy for HTTP/HTTPS
|
||||
#proxy: http://127.0.0.1:3128
|
||||
|
||||
proxyBypassHosts:
|
||||
- api.deepl.com
|
||||
- api-free.deepl.com
|
||||
- www.recaptcha.net
|
||||
- hcaptcha.com
|
||||
- challenges.cloudflare.com
|
||||
|
||||
# Proxy for SMTP/SMTPS
|
||||
#proxySmtp: http://127.0.0.1:3128 # use HTTP/1.1 CONNECT
|
||||
#proxySmtp: socks4://127.0.0.1:1080 # use SOCKS4
|
||||
#proxySmtp: socks5://127.0.0.1:1080 # use SOCKS5
|
||||
|
||||
# Media Proxy
|
||||
#mediaProxy: https://example.com/proxy
|
||||
|
||||
# Proxy remote files (default: false)
|
||||
#proxyRemoteFiles: true
|
||||
|
||||
# Sign to ActivityPub GET request (default: true)
|
||||
signToActivityPubGet: true
|
||||
|
||||
#allowedPrivateNetworks: [
|
||||
# '127.0.0.1/32'
|
||||
#]
|
||||
|
||||
# Upload or download file size limits (bytes)
|
||||
#maxFileSize: 262144000
|
10
.github/dependabot.yml
vendored
10
.github/dependabot.yml
vendored
@@ -5,6 +5,11 @@
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 0
|
||||
- package-ecosystem: npm
|
||||
directory: "/"
|
||||
schedule:
|
||||
@@ -20,3 +25,8 @@ updates:
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 0
|
||||
- package-ecosystem: npm
|
||||
directory: "/packages/sw"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 0
|
||||
|
18
.github/workflows/check_copyright_year.yml
vendored
Normal file
18
.github/workflows/check_copyright_year.yml
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
name: Check copyright year
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
|
||||
jobs:
|
||||
check_copyright_year:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3.2.0
|
||||
- run: |
|
||||
if [ "$(grep Copyright COPYING | sed -e 's/.*2014-\([0-9]*\) .*/\1/g')" -ne "$(date +%Y)" ]; then
|
||||
echo "Please change copyright year!"
|
||||
exit 1
|
||||
fi
|
4
.github/workflows/docker-develop.yml
vendored
4
.github/workflows/docker-develop.yml
vendored
@@ -10,10 +10,10 @@ jobs:
|
||||
push_to_registry:
|
||||
name: Push Docker image to Docker Hub
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
if: github.repository == 'misskey-dev/misskey'
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3.3.0
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v3
|
||||
|
2
.github/workflows/docker.yml
vendored
2
.github/workflows/docker.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3.3.0
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v3
|
||||
|
30
.github/workflows/lint.yml
vendored
30
.github/workflows/lint.yml
vendored
@@ -8,22 +8,26 @@ on:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
yarn_install:
|
||||
pnpm_install:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3.3.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: true
|
||||
- uses: actions/setup-node@v3.2.0
|
||||
- uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 7
|
||||
run_install: false
|
||||
- uses: actions/setup-node@v3.6.0
|
||||
with:
|
||||
node-version: 18.x
|
||||
cache: 'yarn'
|
||||
cache: 'pnpm'
|
||||
- run: corepack enable
|
||||
- run: yarn install --immutable
|
||||
- run: pnpm i --frozen-lockfile
|
||||
|
||||
lint:
|
||||
needs: [yarn_install]
|
||||
needs: [pnpm_install]
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
strategy:
|
||||
@@ -33,14 +37,18 @@ jobs:
|
||||
- frontend
|
||||
- sw
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3.3.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: true
|
||||
- uses: actions/setup-node@v3.2.0
|
||||
- uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 7
|
||||
run_install: false
|
||||
- uses: actions/setup-node@v3.6.0
|
||||
with:
|
||||
node-version: 18.x
|
||||
cache: 'yarn'
|
||||
cache: 'pnpm'
|
||||
- run: corepack enable
|
||||
- run: yarn install --immutable
|
||||
- run: yarn workspace ${{ matrix.workspace }} run lint
|
||||
- run: pnpm i --frozen-lockfile
|
||||
- run: pnpm --filter ${{ matrix.workspace }} run lint
|
||||
|
8
.github/workflows/pr-preview-deploy.yml
vendored
8
.github/workflows/pr-preview-deploy.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
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@v5
|
||||
- uses: actions/github-script@v6.3.3
|
||||
id: check-id
|
||||
env:
|
||||
number: ${{ github.event.client_payload.pull_request.number }}
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
|
||||
return check[0].id;
|
||||
|
||||
- uses: actions/github-script@v5
|
||||
- uses: actions/github-script@v6.3.3
|
||||
env:
|
||||
check_id: ${{ steps.check-id.outputs.result }}
|
||||
details_url: ${{ github.server_url }}/${{ github.repository }}/runs/${{ github.run_id }}
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
|
||||
# Check out merge commit
|
||||
- name: Fork based /deploy checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3.3.0
|
||||
with:
|
||||
ref: 'refs/pull/${{ github.event.client_payload.pull_request.number }}/merge'
|
||||
|
||||
@@ -72,7 +72,7 @@ jobs:
|
||||
timeout: 15m
|
||||
|
||||
# Update check run called "integration-fork"
|
||||
- uses: actions/github-script@v5
|
||||
- uses: actions/github-script@v6.3.3
|
||||
id: update-check-run
|
||||
if: ${{ always() }}
|
||||
env:
|
||||
|
41
.github/workflows/test.yml
vendored
41
.github/workflows/test.yml
vendored
@@ -23,31 +23,35 @@ jobs:
|
||||
env:
|
||||
POSTGRES_DB: test-misskey
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
YARN_CHECKSUM_BEHAVIOR: update
|
||||
redis:
|
||||
image: redis:6
|
||||
ports:
|
||||
- 56312:6379
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3.3.0
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 7
|
||||
run_install: false
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3.2.0
|
||||
uses: actions/setup-node@v3.6.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'yarn'
|
||||
cache: 'pnpm'
|
||||
- run: corepack enable
|
||||
- run: yarn install --immutable
|
||||
- name: Check yarn.lock
|
||||
run: git diff --exit-code yarn.lock
|
||||
- run: pnpm i --frozen-lockfile
|
||||
- name: Check pnpm-lock.yaml
|
||||
run: git diff --exit-code pnpm-lock.yaml
|
||||
- name: Copy Configure
|
||||
run: cp .github/misskey/test.yml .config
|
||||
- name: Build
|
||||
run: yarn build
|
||||
run: pnpm build
|
||||
- name: Test
|
||||
run: yarn jest-and-coverage
|
||||
run: pnpm jest-and-coverage
|
||||
- name: Upload Coverage
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
@@ -77,7 +81,7 @@ jobs:
|
||||
- 56312:6379
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3.3.0
|
||||
with:
|
||||
submodules: true
|
||||
# https://github.com/cypress-io/cypress-docker-images/issues/150
|
||||
@@ -86,19 +90,22 @@ jobs:
|
||||
# if: ${{ matrix.browser == 'firefox' }}
|
||||
#- uses: browser-actions/setup-firefox@latest
|
||||
# if: ${{ matrix.browser == 'firefox' }}
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 7
|
||||
run_install: false
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3.2.0
|
||||
uses: actions/setup-node@v3.6.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'yarn'
|
||||
cache: 'pnpm'
|
||||
- run: corepack enable
|
||||
- run: yarn install --immutable
|
||||
env:
|
||||
YARN_CHECKSUM_BEHAVIOR: update
|
||||
- run: pnpm i --frozen-lockfile
|
||||
- name: Copy Configure
|
||||
run: cp .github/misskey/test.yml .config
|
||||
- name: Build
|
||||
run: yarn build
|
||||
run: pnpm build
|
||||
# https://github.com/cypress-io/cypress/issues/4351#issuecomment-559489091
|
||||
- name: ALSA Env
|
||||
run: echo -e 'pcm.!default {\n type hw\n card 0\n}\n\nctl.!default {\n type hw\n card 0\n}' > ~/.asoundrc
|
||||
@@ -106,7 +113,7 @@ jobs:
|
||||
uses: cypress-io/github-action@v4
|
||||
with:
|
||||
install: false
|
||||
start: yarn start:test
|
||||
start: pnpm start:test
|
||||
wait-on: 'http://localhost:61812'
|
||||
headless: false
|
||||
browser: ${{ matrix.browser }}
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -30,6 +30,7 @@ coverage
|
||||
# config
|
||||
/.config/*
|
||||
!/.config/example.yml
|
||||
!/.config/docker_example.yml
|
||||
!/.config/docker_example.env
|
||||
|
||||
# misskey
|
||||
|
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"search.exclude": {
|
||||
"**/node_modules": true
|
||||
}
|
||||
}
|
546
.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
vendored
546
.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
807
.yarn/releases/yarn-3.3.0.cjs
vendored
807
.yarn/releases/yarn-3.3.0.cjs
vendored
File diff suppressed because one or more lines are too long
42
.yarnrc.yml
42
.yarnrc.yml
@@ -1,42 +0,0 @@
|
||||
httpTimeout: 600000
|
||||
|
||||
nmHoistingLimits: none
|
||||
|
||||
nodeLinker: pnpm
|
||||
|
||||
packageExtensions:
|
||||
"@bull-board/api@*":
|
||||
peerDependencies:
|
||||
"@bull-board/ui": "*"
|
||||
"@tensorflow/tfjs@*":
|
||||
dependencies:
|
||||
long: "*"
|
||||
chartjs-adapter-date-fns@*:
|
||||
peerDependencies:
|
||||
date-fns: "*"
|
||||
consolidate@*:
|
||||
dependencies:
|
||||
ejs: "*"
|
||||
# these are needed to extend fastify types
|
||||
"@fastify/accepts@*":
|
||||
peerDependencies:
|
||||
fastify: "*"
|
||||
"@fastify/cookie@*":
|
||||
peerDependencies:
|
||||
fastify: "*"
|
||||
"@fastify/static@*":
|
||||
peerDependencies:
|
||||
fastify: "*"
|
||||
"@fastify/view@*":
|
||||
peerDependencies:
|
||||
fastify: "*"
|
||||
|
||||
plugins:
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
|
||||
spec: "@yarnpkg/plugin-interactive-tools"
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
|
||||
spec: "@yarnpkg/plugin-workspace-tools"
|
||||
|
||||
progressBarStyle: patrick
|
||||
|
||||
yarnPath: .yarn/releases/yarn-3.3.0.cjs
|
@@ -31,8 +31,7 @@ You should also include the user name that made the change.
|
||||
- Misskey not using 15 specific features at 13.0.0, but may do so in the future.
|
||||
- Elasticsearchのサポートが削除されました
|
||||
- 代わりに今後任意の検索プロバイダを設定できる仕組みを構想しています。その仕組みを使えば今まで通りElasticsearchも利用できます
|
||||
- Migrate to Yarn Berry (v3.2.1) @ThatOneCalculator
|
||||
- You may have to `yarn run clean-all`, `sudo corepack enable` and `yarn set version berry` before running `yarn install` if you're still on yarn classic
|
||||
- Yarnからpnpmに移行されました
|
||||
- インスタンスブロックはサブドメインにも適用されるようになります
|
||||
- ロールの導入に伴い、いくつかの機能がロールと統合されました
|
||||
- モデレーターはロールに統合されました。今までのモデレーター情報は失われるため、予めモデレーター一覧を記録しておき、アップデート後にモデレーターロールを作りアサインし直してください。
|
||||
@@ -64,6 +63,7 @@ You should also include the user name that made the change.
|
||||
- API: `user`および`note`エンティティに`emojis`プロパティが含まれなくなりました
|
||||
- API: `user`エンティティに`avatarColor`および`bannerColor`プロパティが含まれなくなりました
|
||||
- API: `instance`エンティティに`latestStatus`、`lastCommunicatedAt`、`latestRequestSentAt`プロパティが含まれなくなりました
|
||||
- API: `instance`エンティティの`caughtAt`は`firstRetrievedAt`に名前が変わりました
|
||||
|
||||
### Improvements
|
||||
- Role system @syuilo
|
||||
@@ -74,6 +74,7 @@ You should also include the user name that made the change.
|
||||
- Push notification of Antenna note @tamaina
|
||||
- AVIF support @tamaina
|
||||
- Add Cloudflare Turnstile CAPTCHA support @CyberRex0
|
||||
- レートリミットをユーザーごとに調整可能に @syuilo
|
||||
- 非モデレーターでも、権限を持つロールをアサインされたユーザーはインスタンスの招待コードを発行できるように @syuilo
|
||||
- 非モデレーターでも、権限を持つロールをアサインされたユーザーはカスタム絵文字の追加、編集、削除を行えるように @syuilo
|
||||
- クリップおよびクリップ内のノートの作成可能数を設定可能に @syuilo
|
||||
@@ -87,6 +88,7 @@ You should also include the user name that made the change.
|
||||
- Server: Judge instance block by endsWith @tamaina
|
||||
- Server: improve note scoring for featured notes @CyberRex0
|
||||
- Server: アンケート選択肢の文字数制限を緩和 @syuilo
|
||||
- Server: プロフィールの文字数制限を緩和 @syuilo
|
||||
- Server: add rate limits for some endpoints @syuilo
|
||||
- Server: improve stats api performance @syuilo
|
||||
- Server: improve nodeinfo performance @syuilo
|
||||
@@ -99,6 +101,7 @@ You should also include the user name that made the change.
|
||||
- Client: Add link to user RSS feed in profile menu @ssmucny
|
||||
- Client: Compress non-animated PNG files @saschanaz
|
||||
- Client: YouTube window player @sim1222
|
||||
- Client: show readable error when rate limit exceeded @syuilo
|
||||
- Client: enhance dashboard of control panel @syuilo
|
||||
- Client: Vite is upgraded to v4 @syuilo, @tamaina
|
||||
- Client: HMR is available while yarn dev @tamaina
|
||||
|
2
COPYING
2
COPYING
@@ -1,5 +1,5 @@
|
||||
Unless otherwise stated this repository is
|
||||
Copyright © 2014-2022 syuilo and contributers
|
||||
Copyright © 2014-2023 syuilo and contributers
|
||||
|
||||
And is distributed under The GNU Affero General Public License Version 3, you should have received a copy of the license file as LICENSE.
|
||||
|
||||
|
17
Dockerfile
17
Dockerfile
@@ -2,27 +2,27 @@ ARG NODE_VERSION=18.13.0-bullseye
|
||||
|
||||
FROM node:${NODE_VERSION} AS builder
|
||||
|
||||
ARG NODE_ENV=production
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
build-essential
|
||||
|
||||
WORKDIR /misskey
|
||||
|
||||
COPY [".yarnrc.yml", "package.json", "yarn.lock", "./"]
|
||||
COPY [".yarn", "./.yarn"]
|
||||
COPY ["pnpm-lock.yaml", "pnpm-workspace.yaml", "package.json", "./"]
|
||||
COPY ["scripts", "./scripts"]
|
||||
COPY ["packages/backend/package.json", "./packages/backend/"]
|
||||
COPY ["packages/frontend/package.json", "./packages/frontend/"]
|
||||
COPY ["packages/sw/package.json", "./packages/sw/"]
|
||||
|
||||
RUN yarn install --immutable
|
||||
RUN npm i -g pnpm
|
||||
RUN pnpm i --frozen-lockfile
|
||||
|
||||
COPY . ./
|
||||
|
||||
ARG NODE_ENV=production
|
||||
|
||||
RUN git submodule update --init
|
||||
RUN yarn build
|
||||
RUN pnpm build
|
||||
|
||||
FROM node:${NODE_VERSION}-slim AS runner
|
||||
|
||||
@@ -37,17 +37,18 @@ RUN apt-get update \
|
||||
&& groupadd -g "${GID}" misskey \
|
||||
&& useradd -l -u "${UID}" -g "${GID}" -m -d /misskey misskey
|
||||
|
||||
RUN npm i -g pnpm
|
||||
USER misskey
|
||||
WORKDIR /misskey
|
||||
|
||||
COPY --chown=misskey:misskey --from=builder /misskey/.yarn/install-state.gz ./.yarn/install-state.gz
|
||||
COPY --chown=misskey:misskey --from=builder /misskey/node_modules ./node_modules
|
||||
COPY --chown=misskey:misskey --from=builder /misskey/built ./built
|
||||
COPY --chown=misskey:misskey --from=builder /misskey/packages/backend/node_modules ./packages/backend/node_modules
|
||||
COPY --chown=misskey:misskey --from=builder /misskey/packages/backend/built ./packages/backend/built
|
||||
COPY --chown=misskey:misskey --from=builder /misskey/packages/frontend/node_modules ./packages/frontend/node_modules
|
||||
COPY --chown=misskey:misskey --from=builder /misskey/fluent-emojis /misskey/fluent-emojis
|
||||
COPY --chown=misskey:misskey . ./
|
||||
|
||||
ENV NODE_ENV=production
|
||||
ENTRYPOINT ["/usr/bin/tini", "--"]
|
||||
CMD ["yarn", "run", "migrateandstart"]
|
||||
CMD ["pnpm", "run", "migrateandstart"]
|
||||
|
@@ -39,7 +39,7 @@ describe('After user signed in', () => {
|
||||
cy.get('.mk-widget-select select').select(widgetName, { force: true });
|
||||
cy.get('.data-cy-bg._modalBg.data-cy-transparent').click({ multiple: true, force: true });
|
||||
cy.get('.mk-widget-add').click({ force: true });
|
||||
cy.get(`.mkw-${widgetName}`).should('exist');
|
||||
cy.get(`.data-cy-mkw-${widgetName}`).should('exist');
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -44,7 +44,7 @@ services:
|
||||
volumes:
|
||||
- ./db:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: "pg_isready"
|
||||
test: "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"
|
||||
interval: 5s
|
||||
retries: 20
|
||||
|
||||
|
@@ -818,6 +818,12 @@ cannotLoad: "تعذر التحميل"
|
||||
like: "أعجبني"
|
||||
show: "المظهر"
|
||||
color: "اللون"
|
||||
_role:
|
||||
priority: "الأولوية"
|
||||
_priority:
|
||||
low: "منخفضة"
|
||||
middle: "متوسط"
|
||||
high: "عالية"
|
||||
_emailUnavailable:
|
||||
used: "هذا البريد الإلكتروني مستخدم"
|
||||
format: "صيغة البريد الإلكتروني غير صالحة"
|
||||
|
@@ -854,6 +854,12 @@ account: "অ্যাকাউন্টগুলি"
|
||||
like: "পছন্দ করা"
|
||||
show: "প্রদর্শন"
|
||||
color: "রং"
|
||||
_role:
|
||||
priority: "অগ্রাধিকার"
|
||||
_priority:
|
||||
low: "নিম্ন"
|
||||
middle: "মাঝারি"
|
||||
high: "উচ্চ"
|
||||
_emailUnavailable:
|
||||
used: "এই ইমেইল ঠিকানাটি ইতোমধ্যে ব্যবহৃত হয়েছে"
|
||||
format: "এই ইমেল ঠিকানাটি সঠিকভাবে লিখা হয়নি"
|
||||
|
@@ -612,6 +612,12 @@ fast: "Rychlá"
|
||||
account: "Účty"
|
||||
show: "Zobrazit"
|
||||
color: "Barva"
|
||||
_role:
|
||||
priority: "Priorita"
|
||||
_priority:
|
||||
low: "Nízká"
|
||||
middle: "Střední"
|
||||
high: "Vysoká"
|
||||
_ad:
|
||||
back: "Zpět"
|
||||
_gallery:
|
||||
|
@@ -933,6 +933,8 @@ unassign: "Entfernen"
|
||||
color: "Farbe"
|
||||
manageCustomEmojis: "Benutzerdefinierte Emojis verwalten"
|
||||
youCannotCreateAnymore: "Du hast das Erstellungslimit erreicht."
|
||||
cannotPerformTemporary: "Vorübergehend nicht verfügbar"
|
||||
cannotPerformTemporaryDescription: "Diese Aktion ist wegen des Überschreitenes des Ausführungslimits temporär nicht verfügbar. Bitte versuche es nach einiger Zeit erneut."
|
||||
_role:
|
||||
new: "Rolle erstellen"
|
||||
edit: "Rolle bearbeiten"
|
||||
@@ -949,11 +951,17 @@ _role:
|
||||
isPublic: "Öffentliche Rolle"
|
||||
descriptionOfIsPublic: "Ist dies aktiviert, so kann jeder die Liste der Benutzer, die dieser Rolle zugewiesen sind, einsehen. Zusätzlich wird diese Rolle im Profil zugewiesener Benutzer angezeigt."
|
||||
options: "Optionen"
|
||||
policies: "Richtlinien"
|
||||
baseRole: "Rollenvorlage"
|
||||
useBaseValue: "Wert der Rollenvorlage verwenden"
|
||||
chooseRoleToAssign: "Zuzuweisende Rolle auswählen"
|
||||
canEditMembersByModerator: "Moderatoren können Benutzern diese Rolle zuweisen"
|
||||
descriptionOfCanEditMembersByModerator: "Wenn aktiviert, so können Moderatoren und Adminstratoren anderen Benutzern diese Rolle zuweisen bzw. diese Zuweisung aufheben. Wenn deaktiviert, so ist es nur Administratoren möglich, Zuweisungen dieser Rolle zu verwalten."
|
||||
priority: "Priorität"
|
||||
_priority:
|
||||
low: "Niedrig"
|
||||
middle: "Mittel"
|
||||
high: "Hoch"
|
||||
_options:
|
||||
gtlAvailable: "Kann auf die globale Chronik zugreifen"
|
||||
ltlAvailable: "Kann auf die lokale Chronik zugreifen"
|
||||
@@ -969,6 +977,8 @@ _role:
|
||||
noteEachClipsMax: "Maximale Anzahl an Notizen innerhalb eines Clips"
|
||||
userListMax: "Maximale Anzahl an Benutzern in einer Benutzerliste"
|
||||
userEachUserListsMax: "Maximale Anzahl an Benutzerlisten"
|
||||
rateLimitFactor: "Versuchsanzahl"
|
||||
descriptionOfRateLimitFactor: "Je niedriger desto weniger restriktiv, je höher destro restriktiver."
|
||||
_condition:
|
||||
isLocal: "Lokaler Benutzer"
|
||||
isRemote: "Benutzer fremder Instanz"
|
||||
|
@@ -933,6 +933,8 @@ unassign: "Unassign"
|
||||
color: "Color"
|
||||
manageCustomEmojis: "Manage Custom Emojis"
|
||||
youCannotCreateAnymore: "You've hit the creation limit."
|
||||
cannotPerformTemporary: "Temporarily unavailable"
|
||||
cannotPerformTemporaryDescription: "This action cannot be performed temporarily due to exceeding the execution limit. Please wait for a while and then try again."
|
||||
_role:
|
||||
new: "New role"
|
||||
edit: "Edit role"
|
||||
@@ -949,11 +951,17 @@ _role:
|
||||
isPublic: "Public role"
|
||||
descriptionOfIsPublic: "Anyone will be able to view a list of users assigned to this role. In addition, this role will be displayed in the profiles of assigned users."
|
||||
options: "Role options"
|
||||
policies: "Policies"
|
||||
baseRole: "Base role"
|
||||
useBaseValue: "Use base role value"
|
||||
chooseRoleToAssign: "Select the role to assign"
|
||||
canEditMembersByModerator: "Allow moderators to edit the list members of this role"
|
||||
descriptionOfCanEditMembersByModerator: "When turned on, moderators as well as administrators will be able to assign and unassign users to this role. When turned off, only administrators will be able to assign users."
|
||||
priority: "Priority"
|
||||
_priority:
|
||||
low: "Low"
|
||||
middle: "Medium"
|
||||
high: "High"
|
||||
_options:
|
||||
gtlAvailable: "Viewing the global timeline"
|
||||
ltlAvailable: "Viewing the local timeline"
|
||||
@@ -969,6 +977,8 @@ _role:
|
||||
noteEachClipsMax: "Maximum number of notes within a clip"
|
||||
userListMax: "Maximum number of user lists"
|
||||
userEachUserListsMax: "Maximum number of users within a user list"
|
||||
rateLimitFactor: "Rate limit"
|
||||
descriptionOfRateLimitFactor: "Lower rate limits are less restrictive, higher ones more restrictive. "
|
||||
_condition:
|
||||
isLocal: "Local user"
|
||||
isRemote: "Remote user"
|
||||
|
@@ -919,6 +919,12 @@ numberOfProfileView: "Número de vistas de perfil"
|
||||
like: "¡Muy bien!"
|
||||
show: "Apariencia"
|
||||
color: "Color"
|
||||
_role:
|
||||
priority: "Prioridad"
|
||||
_priority:
|
||||
low: "Baja"
|
||||
middle: "Mediano"
|
||||
high: "Alta"
|
||||
_sensitiveMediaDetection:
|
||||
description: "Reduce el esfuerzo de la moderación el el servidor a través del reconocimiento automático de contenido NSFW usando 'Machine Learning'. Esto puede incrementar ligeramente la carga en el servidor."
|
||||
sensitivity: "Sensibilidad de detección"
|
||||
|
@@ -916,6 +916,12 @@ show: "Affichage"
|
||||
neverShow: "Ne plus afficher"
|
||||
remindMeLater: "Peut-être plus tard"
|
||||
color: "Couleur"
|
||||
_role:
|
||||
priority: "Priorité"
|
||||
_priority:
|
||||
low: "Basse"
|
||||
middle: "Moyen"
|
||||
high: "Haute"
|
||||
_sensitiveMediaDetection:
|
||||
description: "L'apprentissage automatique peut être utilisé pour détecter automatiquement les médias sensibles à modérer. La sollicitation des serveurs augmente légèrement."
|
||||
sensitivity: "Sensibilité de la détection"
|
||||
|
@@ -860,6 +860,12 @@ unlike: "Tidak Suka"
|
||||
numberOfLikes: "Jumlah yang disukai"
|
||||
show: "Tampilkan"
|
||||
color: "Warna"
|
||||
_role:
|
||||
priority: "Prioritas"
|
||||
_priority:
|
||||
low: "Rendah"
|
||||
middle: "Sedang"
|
||||
high: "Tinggi"
|
||||
_emailUnavailable:
|
||||
used: "Alamat surel ini telah digunakan"
|
||||
format: "Format tidak valid."
|
||||
|
@@ -8,7 +8,7 @@ search: "Cerca"
|
||||
notifications: "Notifiche"
|
||||
username: "Nome utente"
|
||||
password: "Password"
|
||||
forgotPassword: "Hai dimenticato la tua password?"
|
||||
forgotPassword: "Hai dimenticato la password?"
|
||||
fetchingAsApObject: "Recuperando dal Fediverso..."
|
||||
ok: "OK"
|
||||
gotIt: "Ho capito"
|
||||
@@ -325,7 +325,7 @@ connectService: "Connessione"
|
||||
disconnectService: "Disconnessione "
|
||||
enableLocalTimeline: "Abilita Timeline locale"
|
||||
enableGlobalTimeline: "Abilita Timeline federata"
|
||||
disablingTimelinesInfo: "Anche se disabiliti queste timeline, gli amministratori e i moderatori potranno sempre accederci."
|
||||
disablingTimelinesInfo: "Anche disabilitandole, gli Amministratori e i Moderatori potranno comunque accedervi."
|
||||
registration: "Iscriviti"
|
||||
enableRegistration: "Permettere nuove registrazioni"
|
||||
invite: "Invita"
|
||||
@@ -932,27 +932,35 @@ assign: "Assegna"
|
||||
unassign: "Disassegna"
|
||||
color: "Colore"
|
||||
manageCustomEmojis: "Gestisci le emoji personalizzate"
|
||||
youCannotCreateAnymore: "Non puoi creare, hai raggiunto il limite."
|
||||
cannotPerformTemporary: "Indisponibilità temporanea"
|
||||
cannotPerformTemporaryDescription: "L'attività non può essere svolta, poiché si è raggiunto il limite di esecuzioni possibili. Per favore, riprova più tardi."
|
||||
_role:
|
||||
new: "Nuovo ruolo"
|
||||
edit: "Modifica ruolo"
|
||||
name: "Nome del ruolo"
|
||||
description: "Descrizione del ruolo"
|
||||
permission: "Permessi del ruolo"
|
||||
permission: "Permessi globali del ruolo"
|
||||
descriptionOfPermission: "<b>Moderatori</b> possono svolgere le attività di moderazione basilari.\n<b>Amministratori</b> possono modificare la configurazione dell'istanza."
|
||||
assignTarget: "Assegna il target"
|
||||
descriptionOfAssignTarget: "<b>Manuale</b> per assegnare manualmente questo ruolo ai profili.\n<b>Condizionale</b> per assegnare o rimuovere automaticamente questo ruolo ai profili, secondo determinate condizioni."
|
||||
assignTarget: "Modalità di assegnazione del ruolo"
|
||||
descriptionOfAssignTarget: "<b>Manuale</b>: per assegnare manualmente questo ruolo ai profili.\n<b>Condizionale</b>: per assegnare o rimuovere automaticamente questo ruolo ai profili, a precise condizioni."
|
||||
manual: "Manuale"
|
||||
conditional: "Condizionale"
|
||||
condition: "Condizioni"
|
||||
isConditionalRole: "Questo è un ruolo condizionato"
|
||||
isPublic: "Ruolo pubblico"
|
||||
descriptionOfIsPublic: "La lista di profili assegnati a questo ruolo è visibile a chiunque. Inoltre, il ruolo verrà mostrato nei relativi profili."
|
||||
descriptionOfIsPublic: "La lista di profili assegnati a questo ruolo è visibile a chiunque. Inoltre, il nome del ruolo verrà mostrato pubblicamente nei relativi profili."
|
||||
options: "Opzioni del ruolo"
|
||||
baseRole: "Ruolo di base"
|
||||
useBaseValue: "Eredita dal ruolo base"
|
||||
chooseRoleToAssign: "Seleziona il ruolo da assegnare"
|
||||
canEditMembersByModerator: "Consenti ai Moderatori di modificare i membri di questo ruolo"
|
||||
descriptionOfCanEditMembersByModerator: "Se attivo, anche i Moderatori potranno assegnare o togliere questo ruolo. Altrimenti, se disattivo, potranno solo gli Amministratori."
|
||||
canEditMembersByModerator: "Anche i Moderatori assegnano profili a questo ruolo"
|
||||
descriptionOfCanEditMembersByModerator: "Se disattivo, potranno farlo solamente gli Amministratori."
|
||||
priority: "Priorità"
|
||||
_priority:
|
||||
low: "Bassa"
|
||||
middle: "Medio"
|
||||
high: "Alta"
|
||||
_options:
|
||||
gtlAvailable: "Disponibilità della Timeline Federata"
|
||||
ltlAvailable: "Disponibilità della Timeline Locale"
|
||||
@@ -960,12 +968,25 @@ _role:
|
||||
canInvite: "Genera codici di invito all'istanza"
|
||||
canManageCustomEmojis: "Gestire le emoji personalizzate"
|
||||
driveCapacity: "Capienza del Drive"
|
||||
antennaMax: "Numero massimo di Antenne"
|
||||
pinMax: "Quantità massima di Note in primo piano"
|
||||
antennaMax: "Quantità massima di Antenne"
|
||||
wordMuteMax: "Lunghezza massima del filtro parole"
|
||||
webhookMax: "Quantità massima di Webhook"
|
||||
clipMax: "Quantità massima di Clip"
|
||||
noteEachClipsMax: "Quantità massima di Note nella Clip"
|
||||
userListMax: "Quantità massima di liste"
|
||||
userEachUserListsMax: "Quantità massima di profili per lista"
|
||||
rateLimitFactor: "Limite del rapporto"
|
||||
descriptionOfRateLimitFactor: "I rapporti più bassi sono meno restrittivi, quelli più alti lo sono di più."
|
||||
_condition:
|
||||
isLocal: "Profilo locale"
|
||||
isRemote: "Profilo remoto"
|
||||
createdLessThan: "Creato meno di"
|
||||
createdMoreThan: "Creato più di"
|
||||
followersLessThanOrEq: "Ha meno di N follower"
|
||||
followersMoreThanOrEq: "Ha più di N follower"
|
||||
followingLessThanOrEq: "Segue N profili o meno"
|
||||
followingMoreThanOrEq: "Segue N profili o più"
|
||||
and: "E"
|
||||
or: "O"
|
||||
not: "NON"
|
||||
@@ -1384,7 +1405,7 @@ _cw:
|
||||
_poll:
|
||||
noOnlyOneChoice: "Sono necessarie almeno 2 risposte"
|
||||
choiceN: "Opzione {n}"
|
||||
noMore: "Hai aggiunto il numero massimo di opzioni."
|
||||
noMore: "Hai raggiunto il limite di opzioni."
|
||||
canMultipleVote: "Possibilità di risposte multiple"
|
||||
expiration: "Scadenza"
|
||||
infinite: "Non scade"
|
||||
|
@@ -933,6 +933,8 @@ unassign: "アサインを解除"
|
||||
color: "色"
|
||||
manageCustomEmojis: "カスタム絵文字の管理"
|
||||
youCannotCreateAnymore: "これ以上作成することはできません。"
|
||||
cannotPerformTemporary: "一時的に利用できません"
|
||||
cannotPerformTemporaryDescription: "操作回数が制限を超過するため一時的に利用できません。しばらく時間を置いてから再度お試しください。"
|
||||
|
||||
_role:
|
||||
new: "ロールの作成"
|
||||
@@ -950,11 +952,17 @@ _role:
|
||||
isPublic: "ロールを公開"
|
||||
descriptionOfIsPublic: "ロールにアサインされたユーザーを誰でも見ることができます。また、ユーザーのプロフィールでこのロールが表示されます。"
|
||||
options: "オプション"
|
||||
policies: "ポリシー"
|
||||
baseRole: "ベースロール"
|
||||
useBaseValue: "ベースロールの値を使用"
|
||||
chooseRoleToAssign: "アサインするロールを選択"
|
||||
canEditMembersByModerator: "モデレーターのメンバー編集を許可"
|
||||
descriptionOfCanEditMembersByModerator: "オンにすると、管理者に加えてモデレーターもこのロールへユーザーをアサイン/アサイン解除できるようになります。オフにすると管理者のみが行えます。"
|
||||
priority: "優先度"
|
||||
_priority:
|
||||
low: "低"
|
||||
middle: "中"
|
||||
high: "高"
|
||||
_options:
|
||||
gtlAvailable: "グローバルタイムラインの閲覧"
|
||||
ltlAvailable: "ローカルタイムラインの閲覧"
|
||||
@@ -970,6 +978,8 @@ _role:
|
||||
noteEachClipsMax: "クリップ内のノートの最大数"
|
||||
userListMax: "ユーザーリストの作成可能数"
|
||||
userEachUserListsMax: "ユーザーリスト内のユーザーの最大数"
|
||||
rateLimitFactor: "レートリミット"
|
||||
descriptionOfRateLimitFactor: "小さいほど制限が緩和され、大きいほど制限が強化されます。"
|
||||
_condition:
|
||||
isLocal: "ローカルユーザー"
|
||||
isRemote: "リモートユーザー"
|
||||
|
@@ -926,29 +926,71 @@ didYouLikeMisskey: "Misskeyを気に入っとっただけましたん?"
|
||||
pleaseDonate: "Misskeyは{host}が使用している無料のソフトウェアやで。これからも開発を続けれるように、寄付したってな~。"
|
||||
roles: "ロール"
|
||||
role: "ロール"
|
||||
normalUser: "一般ユーザー"
|
||||
undefined: "未定義"
|
||||
assign: "アサイン"
|
||||
unassign: "アサインを解除"
|
||||
color: "色"
|
||||
manageCustomEmojis: "カスタム絵文字の管理"
|
||||
youCannotCreateAnymore: "これ以上作れなさそうや"
|
||||
cannotPerformTemporary: "一時的に利用できへんで"
|
||||
cannotPerformTemporaryDescription: "操作回数が制限を超えたから一時的に利用できへんくなったで。ちょっと時間置いてからもう一回やってやー。"
|
||||
_role:
|
||||
new: "ロールの作成"
|
||||
edit: "ロールの編集"
|
||||
name: "ロール名"
|
||||
description: "ロールの説明"
|
||||
permission: "ロールの権限"
|
||||
descriptionOfPermission: "<b>モデレーター</b>は基本的なモデレーションに関わる操作を行えるで。\n<b>管理者</b>はインスタンスの全ての設定を変更できるで。"
|
||||
assignTarget: "アサインターゲット"
|
||||
descriptionOfAssignTarget: "<b>マニュアル</b>は誰がこのロールに含まれてるかを手動で管理するで。\n<b>コンディショナル</b>は条件を設定して、それに合うユーザーが自動で含まれるようになるで。"
|
||||
manual: "マニュアル"
|
||||
conditional: "コンディショナル"
|
||||
condition: "条件"
|
||||
isConditionalRole: "これはコンディショナルロールやで"
|
||||
isPublic: "ロールを公開"
|
||||
descriptionOfIsPublic: "ロールにアサインされたユーザーを誰でも見ることができるで。そんで、ユーザーのプロフィールでこのロールが表示されるで。"
|
||||
options: "オプション"
|
||||
policies: "ポリシー"
|
||||
baseRole: "ベースロール"
|
||||
useBaseValue: "ベースロールの値を使用"
|
||||
chooseRoleToAssign: "アサインするロールを選択"
|
||||
canEditMembersByModerator: "モデレーターのメンバー編集を許可"
|
||||
descriptionOfCanEditMembersByModerator: "オンにすると、管理者に加えてモデレーターもこのロールへユーザーをアサイン/アサイン解除できるようになるで。オフにすると管理者のみが行えるで。"
|
||||
priority: "優先度"
|
||||
_priority:
|
||||
low: "低い"
|
||||
middle: "中"
|
||||
high: "高い"
|
||||
_options:
|
||||
gtlAvailable: "グローバルタイムラインの閲覧"
|
||||
ltlAvailable: "ローカルタイムラインの閲覧"
|
||||
canPublicNote: "パブリック投稿の許可"
|
||||
canInvite: "インスタンス招待コードの発行"
|
||||
canManageCustomEmojis: "カスタム絵文字の管理"
|
||||
driveCapacity: "ドライブ容量"
|
||||
pinMax: "ノートのピン留めの最大数"
|
||||
antennaMax: "アンテナの作成可能数"
|
||||
wordMuteMax: "ワードミュートの最大文字数"
|
||||
webhookMax: "Webhockの作成可能数"
|
||||
clipMax: "クリップの作成可能数"
|
||||
noteEachClipsMax: "クリップ内のノートの最大数"
|
||||
userListMax: "ユーザーリストの作成可能数"
|
||||
userEachUserListsMax: "ユーザーリスト内のユーザーの最大数"
|
||||
rateLimitFactor: "レートリミット"
|
||||
descriptionOfRateLimitFactor: "ちっちゃいほど制限が緩くなって、大きいほど制限されるで。"
|
||||
_condition:
|
||||
isLocal: "ローカルユーザー"
|
||||
isRemote: "リモートユーザー"
|
||||
createdLessThan: "アカウント作成から~以内"
|
||||
createdMoreThan: "アカウント作成から~経過"
|
||||
followersLessThanOrEq: "フォロワー数が~以下"
|
||||
followersMoreThanOrEq: "フォロワー数が~以上"
|
||||
followingLessThanOrEq: "フォロー数が~以下"
|
||||
followingMoreThanOrEq: "フォロー数が~以上"
|
||||
and: "~かつ~"
|
||||
or: "~または~"
|
||||
not: "~ではない"
|
||||
_sensitiveMediaDetection:
|
||||
description: "機械学習を使って自動でセンシティブなメディアを検出して、モデレーションに役立てることができるで。サーバーの負荷が少し増えてまうなあ。"
|
||||
sensitivity: "検出感度やで"
|
||||
|
@@ -933,6 +933,8 @@ unassign: "할당 취소"
|
||||
color: "색"
|
||||
manageCustomEmojis: "커스텀 이모지 관리"
|
||||
youCannotCreateAnymore: "더 이상 생성할 수 없습니다."
|
||||
cannotPerformTemporary: "일시적으로 사용할 수 없음"
|
||||
cannotPerformTemporaryDescription: "조작 횟수 제한을 초과하여 일시적으로 사용이 불가합니다. 잠시 후 다시 시도해 주세요."
|
||||
_role:
|
||||
new: "새 역할 생성"
|
||||
edit: "역할 수정"
|
||||
@@ -949,11 +951,17 @@ _role:
|
||||
isPublic: "공개 역할"
|
||||
descriptionOfIsPublic: "역할에 할당된 사용자를 누구나 볼 수 있습니다. 또한 사용자 프로필에 이 역할이 표시됩니다."
|
||||
options: "옵션"
|
||||
policies: "정책"
|
||||
baseRole: "기본 역할"
|
||||
useBaseValue: "기본값 사용"
|
||||
chooseRoleToAssign: "할당할 역할 선택"
|
||||
canEditMembersByModerator: "모더레이터의 역할 수정 허용"
|
||||
descriptionOfCanEditMembersByModerator: "이 옵션을 켜면 모더레이터도 이 역할에 사용자를 추가하거나 삭제할 수 있습니다. 꺼져 있으면 관리자만 가능합니다."
|
||||
priority: "우선순위"
|
||||
_priority:
|
||||
low: "낮음"
|
||||
middle: "보통"
|
||||
high: "높음"
|
||||
_options:
|
||||
gtlAvailable: "글로벌 타임라인 보이기"
|
||||
ltlAvailable: "로컬 타임라인 보이기"
|
||||
@@ -969,6 +977,8 @@ _role:
|
||||
noteEachClipsMax: "각 클립에 추가할 수 있는 노트 수"
|
||||
userListMax: "생성할 수 있는 리스트 수"
|
||||
userEachUserListsMax: "리스트당 최대 사용자 수"
|
||||
rateLimitFactor: "속도 제한"
|
||||
descriptionOfRateLimitFactor: "작을수록 제한이 완화되고, 클수록 제한이 강화됩니다."
|
||||
_condition:
|
||||
isLocal: "로컬 사용자"
|
||||
isRemote: "리모트 사용자"
|
||||
|
@@ -869,6 +869,12 @@ loggedInAsBot: "Jesteś obecnie zalogowany/a jako bot"
|
||||
like: "Polub"
|
||||
show: "Wyświetlanie"
|
||||
color: "Kolor"
|
||||
_role:
|
||||
priority: "Priorytet"
|
||||
_priority:
|
||||
low: "Niski"
|
||||
middle: "Średnie"
|
||||
high: "Wysoki"
|
||||
_sensitiveMediaDetection:
|
||||
description: "Zmniejsza wysiłek związany z moderacją serwera dzięki automatycznemu rozpoznawaniu zawartości NSFW za pomocą uczenia maszynowego. To nieznacznie zwiększy obciążenie serwera."
|
||||
setSensitiveFlagAutomatically: "Oznacz jako NSFW"
|
||||
|
@@ -648,6 +648,9 @@ sent: "Trimite"
|
||||
searchByGoogle: "Caută"
|
||||
file: "Fișiere"
|
||||
show: "Arată"
|
||||
_role:
|
||||
_priority:
|
||||
middle: "Mediu"
|
||||
_email:
|
||||
_follow:
|
||||
title: "te-a urmărit"
|
||||
|
@@ -867,6 +867,12 @@ windowRestore: "Восстановить"
|
||||
like: "Нравится!"
|
||||
show: "Отображение"
|
||||
color: "Цвет"
|
||||
_role:
|
||||
priority: "Приоритет"
|
||||
_priority:
|
||||
low: "Низкий"
|
||||
middle: "Средне"
|
||||
high: "Высокий"
|
||||
_sensitiveMediaDetection:
|
||||
description: "Машинное обучение может быть использовано для автоматического обнаружения чувствительных медиа для модерации. Нагрузка на сервер увеличивается незначительно."
|
||||
setSensitiveFlagAutomatically: "Установить флаг NSFW"
|
||||
|
@@ -918,6 +918,12 @@ remindMeLater: "Pripomenúť neskôr"
|
||||
didYouLikeMisskey: "Páči sa vám Misskey?"
|
||||
pleaseDonate: "Misskey je bezplatný softvér, ktorý používa {host}. Prosím, prispejte, aby sme ho mohli ďalej rozvíjať!"
|
||||
color: "Farba"
|
||||
_role:
|
||||
priority: "Priorita"
|
||||
_priority:
|
||||
low: "Málo"
|
||||
middle: "Stredné"
|
||||
high: "Vysoká"
|
||||
_sensitiveMediaDetection:
|
||||
description: "Strojové učenie sa použije na automatickú detekciu citlivých médií na účely ich moderovania. Mierne sa zvýši zaťaženie servera."
|
||||
sensitivity: "Citlivosť detekcie"
|
||||
|
@@ -953,6 +953,11 @@ _role:
|
||||
chooseRoleToAssign: "เลือกบทบาทที่ต้องการกำหนด"
|
||||
canEditMembersByModerator: "อนุญาตให้ผู้ดูแลแก้ไขสมาชิก"
|
||||
descriptionOfCanEditMembersByModerator: "เมื่อเปิดใช้ ผู้ดูแลนอกเหนือจากผู้ดูแลระบบแล้ว จะสามารถกำหนดและยกเลิกการมอบหมายบทบาทนี้ให้กับผู้ใช้ได้ เมื่อปิด เฉพาะผู้ดูแลระบบเท่านั้นที่จะสามารถกำหนดผู้ใช้ได้นะ"
|
||||
priority: "ลำดับความสำคัญ"
|
||||
_priority:
|
||||
low: "ต่ำ"
|
||||
middle: "ปานกลาง"
|
||||
high: "สูง"
|
||||
_options:
|
||||
gtlAvailable: "การดูไทม์ไลน์ทั่วโลก"
|
||||
ltlAvailable: "การดูไทม์ไลน์ในท้องถิ่น"
|
||||
|
@@ -895,6 +895,12 @@ caption: "Підпис"
|
||||
like: "Вподобати"
|
||||
show: "Відображення"
|
||||
color: "Колір"
|
||||
_role:
|
||||
priority: "Пріоритет"
|
||||
_priority:
|
||||
low: "Низький"
|
||||
middle: "Середній"
|
||||
high: "Високий"
|
||||
_sensitiveMediaDetection:
|
||||
sensitivity: "Чутливість детектування"
|
||||
setSensitiveFlagAutomatically: "Позначити як NSFW"
|
||||
|
@@ -897,6 +897,12 @@ move: "Di chuyển"
|
||||
like: "Thích"
|
||||
show: "Hiển thị"
|
||||
color: "Màu sắc"
|
||||
_role:
|
||||
priority: "Ưu tiên"
|
||||
_priority:
|
||||
low: "Thấp"
|
||||
middle: "Vừa"
|
||||
high: "Cao"
|
||||
_sensitiveMediaDetection:
|
||||
description: "Giảm nỗ lực kiểm duyệt máy chủ thông qua việc tự động nhận dạng media NSFW thông qua học máy. Điều này sẽ làm tăng một chút áp lực trên máy chủ."
|
||||
sensitivity: "Phát hiện nhạy cảm"
|
||||
|
@@ -933,6 +933,8 @@ unassign: "取消分配"
|
||||
color: "颜色"
|
||||
manageCustomEmojis: "管理自定义表情符号"
|
||||
youCannotCreateAnymore: "抱歉,您无法再创建更多了。"
|
||||
cannotPerformTemporary: "暂时不可用"
|
||||
cannotPerformTemporaryDescription: "因操作过于频繁,暂时不可用,请稍后再试。"
|
||||
_role:
|
||||
new: "创建角色"
|
||||
edit: "编辑角色"
|
||||
@@ -949,11 +951,17 @@ _role:
|
||||
isPublic: "角色公开"
|
||||
descriptionOfIsPublic: "任何人都可以看到分配该角色的用户。而用户的个人资料也将显示该角色。"
|
||||
options: "选项"
|
||||
policies: "策略"
|
||||
baseRole: "基本角色"
|
||||
useBaseValue: "使用基本角色的值"
|
||||
chooseRoleToAssign: "选择要分配的角色"
|
||||
canEditMembersByModerator: "允许监察者编辑成员"
|
||||
descriptionOfCanEditMembersByModerator: "如果选中,监察者和管理员都能够为用户分配/取消分配角色。如果未选中,则只有管理员可以执行此操作。"
|
||||
priority: "优先级"
|
||||
_priority:
|
||||
low: "低"
|
||||
middle: "中"
|
||||
high: "高"
|
||||
_options:
|
||||
gtlAvailable: "查看全局时间线"
|
||||
ltlAvailable: "查看本地时间线"
|
||||
@@ -969,6 +977,8 @@ _role:
|
||||
noteEachClipsMax: "单个便签内的贴文数量限制"
|
||||
userListMax: "用户列表创建数量限制"
|
||||
userEachUserListsMax: "单个用户列表内用户数量限制"
|
||||
rateLimitFactor: "速率限制"
|
||||
descriptionOfRateLimitFactor: "值越小限制越少,值越大限制越多。"
|
||||
_condition:
|
||||
isLocal: "是本地用户"
|
||||
isRemote: "是远程用户"
|
||||
|
@@ -932,6 +932,8 @@ assign: "指派"
|
||||
unassign: "取消指派"
|
||||
color: "顏色"
|
||||
manageCustomEmojis: "管理自訂表情符號"
|
||||
cannotPerformTemporary: "暫時無法進行"
|
||||
cannotPerformTemporaryDescription: "由於超過操作次數限制,暫時無法進行。請過一段時間之後再嘗試。"
|
||||
_role:
|
||||
new: "建立角色"
|
||||
edit: "編輯角色"
|
||||
@@ -948,11 +950,17 @@ _role:
|
||||
isPublic: "角色為公開"
|
||||
descriptionOfIsPublic: "任何人都可以看到被指派了角色的使用者。此外,使用者的個人檔案將顯示這個角色。"
|
||||
options: "選項"
|
||||
policies: "政策"
|
||||
baseRole: "基本角色"
|
||||
useBaseValue: "使用基本角色的值"
|
||||
chooseRoleToAssign: "選擇要指派的角色"
|
||||
canEditMembersByModerator: "允許編輯監察員的成員"
|
||||
descriptionOfCanEditMembersByModerator: "如果開啟,管理員與監察員都可以為使用者指派/解除指派該角色。如果關閉,則只有管理員可以執行。"
|
||||
priority: "優先級"
|
||||
_priority:
|
||||
low: "低"
|
||||
middle: "中"
|
||||
high: "高"
|
||||
_options:
|
||||
gtlAvailable: "瀏覽全域時間軸"
|
||||
ltlAvailable: "瀏覽本地時間軸"
|
||||
|
42
package.json
42
package.json
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"version": "13.0.0-rc.7",
|
||||
"version": "13.0.0-rc.10",
|
||||
"codename": "indigo",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/misskey-dev/misskey.git"
|
||||
},
|
||||
"packageManager": "yarn@3.3.0",
|
||||
"packageManager": "pnpm@7.24.3",
|
||||
"workspaces": [
|
||||
"packages/frontend",
|
||||
"packages/backend",
|
||||
@@ -15,27 +15,27 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build-pre": "node ./scripts/build-pre.js",
|
||||
"build": "yarn build-pre && yarn workspaces foreach run build && yarn run gulp",
|
||||
"build": "pnpm build-pre && pnpm -r build && pnpm gulp",
|
||||
"start": "cd packages/backend && node ./built/boot/index.js",
|
||||
"start:test": "cd packages/backend && cross-env NODE_ENV=test node ./built/boot/index.js",
|
||||
"init": "yarn migrate",
|
||||
"migrate": "cd packages/backend && yarn run typeorm migration:run -d ormconfig.js",
|
||||
"migrateandstart": "yarn migrate && yarn start",
|
||||
"gulp": "gulp build",
|
||||
"watch": "yarn dev",
|
||||
"init": "pnpm migrate",
|
||||
"migrate": "cd packages/backend && pnpm typeorm migration:run -d ormconfig.js",
|
||||
"migrateandstart": "pnpm migrate && pnpm start",
|
||||
"gulp": "pnpm exec gulp build",
|
||||
"watch": "pnpm dev",
|
||||
"dev": "node ./scripts/dev.js",
|
||||
"lint": "yarn workspaces foreach run lint",
|
||||
"cy:open": "cypress open --browser --e2e --config-file=cypress.config.ts",
|
||||
"cy:run": "cypress run",
|
||||
"e2e": "start-server-and-test start:test http://localhost:61812 cy:run",
|
||||
"jest": "cd packages/backend && cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --forceExit --runInBand",
|
||||
"jest-and-coverage": "cd packages/backend && cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --coverage --forceExit --runInBand",
|
||||
"test": "yarn jest",
|
||||
"test-and-coverage": "yarn jest-and-coverage",
|
||||
"format": "gulp format",
|
||||
"lint": "pnpm -r lint",
|
||||
"cy:open": "pnpm cypress open --browser --e2e --config-file=cypress.config.ts",
|
||||
"cy:run": "pnpm cypress run",
|
||||
"e2e": "pnpm start-server-and-test start:test http://localhost:61812 cy:run",
|
||||
"jest": "cd packages/backend && pnpm cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --forceExit --runInBand",
|
||||
"jest-and-coverage": "cd packages/backend && pnpm cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --coverage --forceExit --runInBand",
|
||||
"test": "pnpm jest",
|
||||
"test-and-coverage": "pnpm jest-and-coverage",
|
||||
"format": "pnpm exec gulp format",
|
||||
"clean": "node ./scripts/clean.js",
|
||||
"clean-all": "node ./scripts/clean-all.js",
|
||||
"cleanall": "yarn clean-all"
|
||||
"cleanall": "pnpm clean-all"
|
||||
},
|
||||
"resolutions": {
|
||||
"chokidar": "^3.3.1",
|
||||
@@ -48,7 +48,8 @@
|
||||
"gulp-rename": "2.0.0",
|
||||
"gulp-replace": "1.1.4",
|
||||
"gulp-terser": "2.1.0",
|
||||
"js-yaml": "4.1.0"
|
||||
"js-yaml": "4.1.0",
|
||||
"typescript": "4.9.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/gulp": "4.0.10",
|
||||
@@ -58,8 +59,7 @@
|
||||
"cross-env": "7.0.3",
|
||||
"cypress": "12.3.0",
|
||||
"eslint": "^8.31.0",
|
||||
"start-server-and-test": "1.15.2",
|
||||
"typescript": "4.9.4"
|
||||
"start-server-and-test": "1.15.2"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@tensorflow/tfjs-core": "^4.2.0"
|
||||
|
13
packages/backend/migration/1673783015567-Policies.js
Normal file
13
packages/backend/migration/1673783015567-Policies.js
Normal file
@@ -0,0 +1,13 @@
|
||||
export class Policies1673783015567 {
|
||||
name = 'Policies1673783015567'
|
||||
|
||||
async up(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "role" RENAME COLUMN "options" TO "policies"`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" RENAME COLUMN "defaultRoleOverride" TO "policies"`);
|
||||
}
|
||||
|
||||
async down(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "meta" RENAME COLUMN "policies" TO "defaultRoleOverride"`);
|
||||
await queryRunner.query(`ALTER TABLE "role" RENAME COLUMN "policies" TO "options"`);
|
||||
}
|
||||
}
|
11
packages/backend/migration/1673812883772-firstRetrievedAt.js
Normal file
11
packages/backend/migration/1673812883772-firstRetrievedAt.js
Normal file
@@ -0,0 +1,11 @@
|
||||
export class firstRetrievedAt1673812883772 {
|
||||
name = 'firstRetrievedAt1673812883772'
|
||||
|
||||
async up(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "instance" RENAME COLUMN "caughtAt" TO "firstRetrievedAt"`);
|
||||
}
|
||||
|
||||
async down(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "instance" RENAME COLUMN "firstRetrievedAt" TO "caughtAt"`);
|
||||
}
|
||||
}
|
@@ -6,15 +6,15 @@
|
||||
"scripts": {
|
||||
"start": "node ./built/index.js",
|
||||
"start:test": "NODE_ENV=test node ./built/index.js",
|
||||
"migrate": "typeorm migration:run -d ormconfig.js",
|
||||
"migrate": "pnpm typeorm migration:run -d ormconfig.js",
|
||||
"build": "tsc -p tsconfig.json || echo done. && tsc-alias -p tsconfig.json",
|
||||
"watch": "node watch.mjs",
|
||||
"lint": "tsc --noEmit && eslint --quiet \"src/**/*.ts\"",
|
||||
"jest": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --forceExit --runInBand",
|
||||
"jest-and-coverage": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --coverage --forceExit --runInBand",
|
||||
"jest-clear": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --clearCache",
|
||||
"test": "yarn jest",
|
||||
"test-and-coverage": "yarn jest-and-coverage"
|
||||
"test": "pnpm jest",
|
||||
"test-and-coverage": "pnpm jest-and-coverage"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@tensorflow/tfjs": "^4.1.0",
|
||||
@@ -116,6 +116,7 @@
|
||||
"tsconfig-paths": "4.1.2",
|
||||
"twemoji-parser": "14.0.0",
|
||||
"typeorm": "0.3.11",
|
||||
"typescript": "4.9.4",
|
||||
"ulid": "2.3.0",
|
||||
"undici": "^5.15.0",
|
||||
"unzipper": "0.10.11",
|
||||
@@ -180,7 +181,6 @@
|
||||
"execa": "6.1.0",
|
||||
"jest": "29.3.1",
|
||||
"jest-mock": "^29.3.1",
|
||||
"node-fetch": "3.3.0",
|
||||
"typescript": "4.9.4"
|
||||
"node-fetch": "3.3.0"
|
||||
}
|
||||
}
|
||||
|
@@ -479,8 +479,8 @@ export class DriveService {
|
||||
if (user && !isLink) {
|
||||
const usage = await this.driveFileEntityService.calcDriveUsageOf(user);
|
||||
|
||||
const role = await this.roleService.getUserRoleOptions(user.id);
|
||||
const driveCapacity = 1024 * 1024 * role.driveCapacityMb;
|
||||
const policies = await this.roleService.getUserPolicies(user.id);
|
||||
const driveCapacity = 1024 * 1024 * policies.driveCapacityMb;
|
||||
this.registerLogger.debug('drive capacity override applied');
|
||||
this.registerLogger.debug(`overrideCap: ${driveCapacity}bytes, usage: ${usage}bytes, u+s: ${usage + info.size}bytes`);
|
||||
|
||||
|
@@ -34,7 +34,7 @@ export class FederatedInstanceService {
|
||||
const i = await this.instancesRepository.insert({
|
||||
id: this.idService.genId(),
|
||||
host,
|
||||
caughtAt: new Date(),
|
||||
firstRetrievedAt: new Date(),
|
||||
}).then(x => this.instancesRepository.findOneByOrFail(x.identifiers[0]));
|
||||
|
||||
this.cache.set(host, i);
|
||||
|
@@ -226,7 +226,7 @@ export class NoteCreateService {
|
||||
if (data.channel != null) data.localOnly = true;
|
||||
|
||||
if (data.visibility === 'public' && data.channel == null) {
|
||||
if ((await this.roleService.getUserRoleOptions(user.id)).canPublicNote === false) {
|
||||
if ((await this.roleService.getUserPolicies(user.id)).canPublicNote === false) {
|
||||
data.visibility = 'home';
|
||||
}
|
||||
}
|
||||
|
@@ -57,7 +57,7 @@ export class NotePiningService {
|
||||
|
||||
const pinings = await this.userNotePiningsRepository.findBy({ userId: user.id });
|
||||
|
||||
if (pinings.length >= (await this.roleService.getUserRoleOptions(user.id)).pinLimit) {
|
||||
if (pinings.length >= (await this.roleService.getUserPolicies(user.id)).pinLimit) {
|
||||
throw new IdentifiableError('15a018eb-58e5-4da1-93be-330fcc5e4e1a', 'You can not pin notes any more.');
|
||||
}
|
||||
|
||||
|
@@ -13,7 +13,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||
import { StreamMessages } from '@/server/api/stream/types.js';
|
||||
import type { OnApplicationShutdown } from '@nestjs/common';
|
||||
|
||||
export type RoleOptions = {
|
||||
export type RolePolicies = {
|
||||
gtlAvailable: boolean;
|
||||
ltlAvailable: boolean;
|
||||
canPublicNote: boolean;
|
||||
@@ -28,9 +28,10 @@ export type RoleOptions = {
|
||||
noteEachClipsLimit: number;
|
||||
userListLimit: number;
|
||||
userEachUserListsLimit: number;
|
||||
rateLimitFactor: number;
|
||||
};
|
||||
|
||||
export const DEFAULT_ROLE: RoleOptions = {
|
||||
export const DEFAULT_POLICIES: RolePolicies = {
|
||||
gtlAvailable: true,
|
||||
ltlAvailable: true,
|
||||
canPublicNote: true,
|
||||
@@ -45,6 +46,7 @@ export const DEFAULT_ROLE: RoleOptions = {
|
||||
noteEachClipsLimit: 200,
|
||||
userListLimit: 10,
|
||||
userEachUserListsLimit: 50,
|
||||
rateLimitFactor: 1,
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
@@ -193,34 +195,44 @@ export class RoleService implements OnApplicationShutdown {
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async getUserRoleOptions(userId: User['id'] | null): Promise<RoleOptions> {
|
||||
public async getUserPolicies(userId: User['id'] | null): Promise<RolePolicies> {
|
||||
const meta = await this.metaService.fetch();
|
||||
const baseRoleOptions = { ...DEFAULT_ROLE, ...meta.defaultRoleOverride };
|
||||
const basePolicies = { ...DEFAULT_POLICIES, ...meta.policies };
|
||||
|
||||
if (userId == null) return baseRoleOptions;
|
||||
if (userId == null) return basePolicies;
|
||||
|
||||
const roles = await this.getUserRoles(userId);
|
||||
|
||||
function getOptionValues(option: keyof RoleOptions) {
|
||||
if (roles.length === 0) return [baseRoleOptions[option]];
|
||||
return roles.map(role => (role.options[option] && (role.options[option].useDefault !== true)) ? role.options[option].value : baseRoleOptions[option]);
|
||||
function calc<T extends keyof RolePolicies>(name: T, aggregate: (values: RolePolicies[T][]) => RolePolicies[T]) {
|
||||
if (roles.length === 0) return basePolicies[name];
|
||||
|
||||
const policies = roles.map(role => role.policies[name] ?? { priority: 0, useDefault: true });
|
||||
|
||||
const p2 = policies.filter(policy => policy.priority === 2);
|
||||
if (p2.length > 0) return aggregate(p2.map(policy => policy.useDefault ? basePolicies[name] : policy.value));
|
||||
|
||||
const p1 = policies.filter(policy => policy.priority === 1);
|
||||
if (p1.length > 0) return aggregate(p1.map(policy => policy.useDefault ? basePolicies[name] : policy.value));
|
||||
|
||||
return aggregate(policies.map(policy => policy.useDefault ? basePolicies[name] : policy.value));
|
||||
}
|
||||
|
||||
return {
|
||||
gtlAvailable: getOptionValues('gtlAvailable').some(x => x === true),
|
||||
ltlAvailable: getOptionValues('ltlAvailable').some(x => x === true),
|
||||
canPublicNote: getOptionValues('canPublicNote').some(x => x === true),
|
||||
canInvite: getOptionValues('canInvite').some(x => x === true),
|
||||
canManageCustomEmojis: getOptionValues('canManageCustomEmojis').some(x => x === true),
|
||||
driveCapacityMb: Math.max(...getOptionValues('driveCapacityMb')),
|
||||
pinLimit: Math.max(...getOptionValues('pinLimit')),
|
||||
antennaLimit: Math.max(...getOptionValues('antennaLimit')),
|
||||
wordMuteLimit: Math.max(...getOptionValues('wordMuteLimit')),
|
||||
webhookLimit: Math.max(...getOptionValues('webhookLimit')),
|
||||
clipLimit: Math.max(...getOptionValues('clipLimit')),
|
||||
noteEachClipsLimit: Math.max(...getOptionValues('noteEachClipsLimit')),
|
||||
userListLimit: Math.max(...getOptionValues('userListLimit')),
|
||||
userEachUserListsLimit: Math.max(...getOptionValues('userEachUserListsLimit')),
|
||||
gtlAvailable: calc('gtlAvailable', vs => vs.some(v => v === true)),
|
||||
ltlAvailable: calc('ltlAvailable', vs => vs.some(v => v === true)),
|
||||
canPublicNote: calc('canPublicNote', vs => vs.some(v => v === true)),
|
||||
canInvite: calc('canInvite', vs => vs.some(v => v === true)),
|
||||
canManageCustomEmojis: calc('canManageCustomEmojis', vs => vs.some(v => v === true)),
|
||||
driveCapacityMb: calc('driveCapacityMb', vs => Math.max(...vs)),
|
||||
pinLimit: calc('pinLimit', vs => Math.max(...vs)),
|
||||
antennaLimit: calc('antennaLimit', vs => Math.max(...vs)),
|
||||
wordMuteLimit: calc('wordMuteLimit', vs => Math.max(...vs)),
|
||||
webhookLimit: calc('webhookLimit', vs => Math.max(...vs)),
|
||||
clipLimit: calc('clipLimit', vs => Math.max(...vs)),
|
||||
noteEachClipsLimit: calc('noteEachClipsLimit', vs => Math.max(...vs)),
|
||||
userListLimit: calc('userListLimit', vs => Math.max(...vs)),
|
||||
userEachUserListsLimit: calc('userEachUserListsLimit', vs => Math.max(...vs)),
|
||||
rateLimitFactor: calc('rateLimitFactor', vs => Math.max(...vs)),
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -35,7 +35,7 @@ export class UserListService {
|
||||
const currentCount = await this.userListJoiningsRepository.countBy({
|
||||
userListId: list.id,
|
||||
});
|
||||
if (currentCount > (await this.roleService.getUserRoleOptions(me.id)).userEachUserListsLimit) {
|
||||
if (currentCount > (await this.roleService.getUserPolicies(me.id)).userEachUserListsLimit) {
|
||||
throw new Error('Too many users');
|
||||
}
|
||||
|
||||
|
@@ -29,7 +29,7 @@ export class InstanceEntityService {
|
||||
const meta = await this.metaService.fetch();
|
||||
return {
|
||||
id: instance.id,
|
||||
caughtAt: instance.caughtAt.toISOString(),
|
||||
firstRetrievedAt: instance.firstRetrievedAt.toISOString(),
|
||||
host: instance.host,
|
||||
usersCount: instance.usersCount,
|
||||
notesCount: instance.notesCount,
|
||||
|
@@ -6,7 +6,7 @@ import type { Packed } from '@/misc/schema.js';
|
||||
import type { User } from '@/models/entities/User.js';
|
||||
import type { Role } from '@/models/entities/Role.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { DEFAULT_ROLE } from '@/core/RoleService.js';
|
||||
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
|
||||
import { UserEntityService } from './UserEntityService.js';
|
||||
|
||||
@Injectable()
|
||||
@@ -40,10 +40,11 @@ export class RoleEntityService {
|
||||
roleId: role.id,
|
||||
});
|
||||
|
||||
const roleOptions = { ...role.options };
|
||||
for (const [k, v] of Object.entries(DEFAULT_ROLE)) {
|
||||
if (roleOptions[k] == null) roleOptions[k] = {
|
||||
const policies = { ...role.policies };
|
||||
for (const [k, v] of Object.entries(DEFAULT_POLICIES)) {
|
||||
if (policies[k] == null) policies[k] = {
|
||||
useDefault: true,
|
||||
priority: 0,
|
||||
value: v,
|
||||
};
|
||||
}
|
||||
@@ -61,7 +62,7 @@ export class RoleEntityService {
|
||||
isAdministrator: role.isAdministrator,
|
||||
isModerator: role.isModerator,
|
||||
canEditMembersByModerator: role.canEditMembersByModerator,
|
||||
options: roleOptions,
|
||||
policies: policies,
|
||||
usersCount: assigns.length,
|
||||
...(opts.detail ? {
|
||||
users: this.userEntityService.packMany(assigns.map(x => x.userId), me),
|
||||
|
@@ -423,7 +423,7 @@ export class UserEntityService implements OnModuleInit {
|
||||
bannerUrl: user.banner ? this.driveFileEntityService.getPublicUrl(user.banner, false) : null,
|
||||
bannerBlurhash: user.banner?.blurhash ?? null,
|
||||
isLocked: user.isLocked,
|
||||
isSilenced: this.roleService.getUserRoleOptions(user.id).then(r => !r.canPublicNote),
|
||||
isSilenced: this.roleService.getUserPolicies(user.id).then(r => !r.canPublicNote),
|
||||
isSuspended: user.isSuspended ?? falsy,
|
||||
description: profile!.description,
|
||||
location: profile!.location,
|
||||
@@ -496,7 +496,7 @@ export class UserEntityService implements OnModuleInit {
|
||||
} : {}),
|
||||
|
||||
...(opts.includeSecrets ? {
|
||||
role: this.roleService.getUserRoleOptions(user.id),
|
||||
policies: this.roleService.getUserPolicies(user.id),
|
||||
email: profile!.email,
|
||||
emailVerified: profile!.emailVerified,
|
||||
securityKeysList: profile!.twoFactorEnabled
|
||||
|
@@ -13,7 +13,7 @@ export class Instance {
|
||||
@Column('timestamp with time zone', {
|
||||
comment: 'The caught date of the Instance.',
|
||||
})
|
||||
public caughtAt: Date;
|
||||
public firstRetrievedAt: Date;
|
||||
|
||||
/**
|
||||
* ホスト
|
||||
|
@@ -458,5 +458,5 @@ export class Meta {
|
||||
@Column('jsonb', {
|
||||
default: { },
|
||||
})
|
||||
public defaultRoleOverride: Record<string, any>;
|
||||
public policies: Record<string, any>;
|
||||
}
|
||||
|
@@ -136,8 +136,9 @@ export class Role {
|
||||
@Column('jsonb', {
|
||||
default: { },
|
||||
})
|
||||
public options: Record<string, {
|
||||
public policies: Record<string, {
|
||||
useDefault: boolean;
|
||||
priority: number;
|
||||
value: any;
|
||||
}>;
|
||||
}
|
||||
|
@@ -232,6 +232,6 @@ export type CacheableUser = CacheableLocalUser | CacheableRemoteUser;
|
||||
export const localUsernameSchema = { type: 'string', pattern: /^\w{1,20}$/.toString().slice(1, -1) } as const;
|
||||
export const passwordSchema = { type: 'string', minLength: 1 } as const;
|
||||
export const nameSchema = { type: 'string', minLength: 1, maxLength: 50 } as const;
|
||||
export const descriptionSchema = { type: 'string', minLength: 1, maxLength: 500 } as const;
|
||||
export const descriptionSchema = { type: 'string', minLength: 1, maxLength: 1500 } as const;
|
||||
export const locationSchema = { type: 'string', minLength: 1, maxLength: 50 } as const;
|
||||
export const birthdaySchema = { type: 'string', pattern: /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/.toString().slice(1, -1) } as const;
|
||||
|
@@ -6,7 +6,7 @@ export const packedFederationInstanceSchema = {
|
||||
optional: false, nullable: false,
|
||||
format: 'id',
|
||||
},
|
||||
caughtAt: {
|
||||
firstRetrievedAt: {
|
||||
type: 'string',
|
||||
optional: false, nullable: false,
|
||||
format: 'date-time',
|
||||
|
@@ -10,7 +10,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import NotesChart from '@/core/chart/charts/notes.js';
|
||||
import UsersChart from '@/core/chart/charts/users.js';
|
||||
import { DEFAULT_ROLE } from '@/core/RoleService.js';
|
||||
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
|
||||
import type { FastifyInstance, FastifyPluginOptions } from 'fastify';
|
||||
|
||||
const nodeinfo2_1path = '/nodeinfo/2.1';
|
||||
@@ -74,7 +74,7 @@ export class NodeinfoServerService {
|
||||
|
||||
const proxyAccount = meta.proxyAccountId ? await this.userEntityService.pack(meta.proxyAccountId).catch(() => null) : null;
|
||||
|
||||
const baseRoleOptions = { ...DEFAULT_ROLE, ...meta.defaultRoleOverride };
|
||||
const basePolicies = { ...DEFAULT_POLICIES, ...meta.policies };
|
||||
|
||||
return {
|
||||
software: {
|
||||
@@ -105,8 +105,8 @@ export class NodeinfoServerService {
|
||||
repositoryUrl: meta.repositoryUrl,
|
||||
feedbackUrl: meta.feedbackUrl,
|
||||
disableRegistration: meta.disableRegistration,
|
||||
disableLocalTimeline: !baseRoleOptions.ltlAvailable,
|
||||
disableGlobalTimeline: !baseRoleOptions.gtlAvailable,
|
||||
disableLocalTimeline: !basePolicies.ltlAvailable,
|
||||
disableGlobalTimeline: !basePolicies.gtlAvailable,
|
||||
emailRequiredForSignup: meta.emailRequiredForSignup,
|
||||
enableHcaptcha: meta.enableHcaptcha,
|
||||
enableRecaptcha: meta.enableRecaptcha,
|
||||
|
@@ -224,8 +224,11 @@ export class ApiCallService implements OnApplicationShutdown {
|
||||
limit.key = ep.name;
|
||||
}
|
||||
|
||||
// TODO: 毎リクエスト計算するのもあれだしキャッシュしたい
|
||||
const factor = user ? (await this.roleService.getUserPolicies(user.id)).rateLimitFactor : 1;
|
||||
|
||||
// Rate limit
|
||||
await this.rateLimiterService.limit(limit as IEndpointMeta['limit'] & { key: NonNullable<string> }, limitActor).catch(err => {
|
||||
await this.rateLimiterService.limit(limit as IEndpointMeta['limit'] & { key: NonNullable<string> }, limitActor, factor).catch(err => {
|
||||
throw new ApiError({
|
||||
message: 'Rate limit exceeded. Please try again later.',
|
||||
code: 'RATE_LIMIT_EXCEEDED',
|
||||
@@ -271,9 +274,9 @@ export class ApiCallService implements OnApplicationShutdown {
|
||||
}
|
||||
}
|
||||
|
||||
if (ep.meta.requireRoleOption != null && !user!.isRoot) {
|
||||
const myRole = await this.roleService.getUserRoleOptions(user!.id);
|
||||
if (!myRole[ep.meta.requireRoleOption]) {
|
||||
if (ep.meta.requireRolePolicy != null && !user!.isRoot) {
|
||||
const policies = await this.roleService.getUserPolicies(user!.id);
|
||||
if (!policies[ep.meta.requireRolePolicy]) {
|
||||
throw new ApiError({
|
||||
message: 'You are not assigned to a required role.',
|
||||
code: 'ROLE_PERMISSION_DENIED',
|
||||
|
@@ -65,7 +65,7 @@ import * as ep___admin_roles_show from './endpoints/admin/roles/show.js';
|
||||
import * as ep___admin_roles_update from './endpoints/admin/roles/update.js';
|
||||
import * as ep___admin_roles_assign from './endpoints/admin/roles/assign.js';
|
||||
import * as ep___admin_roles_unassign from './endpoints/admin/roles/unassign.js';
|
||||
import * as ep___admin_roles_updateDefaultRoleOverride from './endpoints/admin/roles/update-default-role-override.js';
|
||||
import * as ep___admin_roles_updateDefaultPolicies from './endpoints/admin/roles/update-default-policies.js';
|
||||
import * as ep___announcements from './endpoints/announcements.js';
|
||||
import * as ep___antennas_create from './endpoints/antennas/create.js';
|
||||
import * as ep___antennas_delete from './endpoints/antennas/delete.js';
|
||||
@@ -399,7 +399,7 @@ const $admin_roles_show: Provider = { provide: 'ep:admin/roles/show', useClass:
|
||||
const $admin_roles_update: Provider = { provide: 'ep:admin/roles/update', useClass: ep___admin_roles_update.default };
|
||||
const $admin_roles_assign: Provider = { provide: 'ep:admin/roles/assign', useClass: ep___admin_roles_assign.default };
|
||||
const $admin_roles_unassign: Provider = { provide: 'ep:admin/roles/unassign', useClass: ep___admin_roles_unassign.default };
|
||||
const $admin_roles_updateDefaultRoleOverride: Provider = { provide: 'ep:admin/roles/update-default-role-override', useClass: ep___admin_roles_updateDefaultRoleOverride.default };
|
||||
const $admin_roles_updateDefaultPolicies: Provider = { provide: 'ep:admin/roles/update-default-policies', useClass: ep___admin_roles_updateDefaultPolicies.default };
|
||||
const $announcements: Provider = { provide: 'ep:announcements', useClass: ep___announcements.default };
|
||||
const $antennas_create: Provider = { provide: 'ep:antennas/create', useClass: ep___antennas_create.default };
|
||||
const $antennas_delete: Provider = { provide: 'ep:antennas/delete', useClass: ep___antennas_delete.default };
|
||||
@@ -737,7 +737,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
|
||||
$admin_roles_update,
|
||||
$admin_roles_assign,
|
||||
$admin_roles_unassign,
|
||||
$admin_roles_updateDefaultRoleOverride,
|
||||
$admin_roles_updateDefaultPolicies,
|
||||
$announcements,
|
||||
$antennas_create,
|
||||
$antennas_delete,
|
||||
@@ -1069,7 +1069,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
|
||||
$admin_roles_update,
|
||||
$admin_roles_assign,
|
||||
$admin_roles_unassign,
|
||||
$admin_roles_updateDefaultRoleOverride,
|
||||
$admin_roles_updateDefaultPolicies,
|
||||
$announcements,
|
||||
$antennas_create,
|
||||
$antennas_delete,
|
||||
|
@@ -26,7 +26,7 @@ export class RateLimiterService {
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public limit(limitation: IEndpointMeta['limit'] & { key: NonNullable<string> }, actor: string) {
|
||||
public limit(limitation: IEndpointMeta['limit'] & { key: NonNullable<string> }, actor: string, factor = 1) {
|
||||
return new Promise<void>((ok, reject) => {
|
||||
if (this.disabled) ok();
|
||||
|
||||
@@ -34,7 +34,7 @@ export class RateLimiterService {
|
||||
const min = (): void => {
|
||||
const minIntervalLimiter = new Limiter({
|
||||
id: `${actor}:${limitation.key}:min`,
|
||||
duration: limitation.minInterval,
|
||||
duration: limitation.minInterval * factor,
|
||||
max: 1,
|
||||
db: this.redisClient,
|
||||
});
|
||||
@@ -62,8 +62,8 @@ export class RateLimiterService {
|
||||
const max = (): void => {
|
||||
const limiter = new Limiter({
|
||||
id: `${actor}:${limitation.key}`,
|
||||
duration: limitation.duration,
|
||||
max: limitation.max,
|
||||
duration: limitation.duration * factor,
|
||||
max: limitation.max / factor,
|
||||
db: this.redisClient,
|
||||
});
|
||||
|
||||
|
@@ -64,7 +64,7 @@ import * as ep___admin_roles_show from './endpoints/admin/roles/show.js';
|
||||
import * as ep___admin_roles_update from './endpoints/admin/roles/update.js';
|
||||
import * as ep___admin_roles_assign from './endpoints/admin/roles/assign.js';
|
||||
import * as ep___admin_roles_unassign from './endpoints/admin/roles/unassign.js';
|
||||
import * as ep___admin_roles_updateDefaultRoleOverride from './endpoints/admin/roles/update-default-role-override.js';
|
||||
import * as ep___admin_roles_updateDefaultPolicies from './endpoints/admin/roles/update-default-policies.js';
|
||||
import * as ep___announcements from './endpoints/announcements.js';
|
||||
import * as ep___antennas_create from './endpoints/antennas/create.js';
|
||||
import * as ep___antennas_delete from './endpoints/antennas/delete.js';
|
||||
@@ -396,7 +396,7 @@ const eps = [
|
||||
['admin/roles/update', ep___admin_roles_update],
|
||||
['admin/roles/assign', ep___admin_roles_assign],
|
||||
['admin/roles/unassign', ep___admin_roles_unassign],
|
||||
['admin/roles/update-default-role-override', ep___admin_roles_updateDefaultRoleOverride],
|
||||
['admin/roles/update-default-policies', ep___admin_roles_updateDefaultPolicies],
|
||||
['announcements', ep___announcements],
|
||||
['antennas/create', ep___antennas_create],
|
||||
['antennas/delete', ep___antennas_delete],
|
||||
@@ -695,7 +695,7 @@ export interface IEndpointMeta {
|
||||
*/
|
||||
readonly requireAdmin?: boolean;
|
||||
|
||||
readonly requireRoleOption?: string;
|
||||
readonly requireRolePolicy?: string;
|
||||
|
||||
/**
|
||||
* エンドポイントのリミテーションに関するやつ
|
||||
|
@@ -8,7 +8,7 @@ export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireRoleOption: 'canManageCustomEmojis',
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
|
@@ -14,7 +14,7 @@ export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireRoleOption: 'canManageCustomEmojis',
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
|
||||
errors: {
|
||||
noSuchFile: {
|
||||
|
@@ -14,7 +14,7 @@ export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireRoleOption: 'canManageCustomEmojis',
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
|
||||
errors: {
|
||||
noSuchEmoji: {
|
||||
|
@@ -9,7 +9,7 @@ export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireRoleOption: 'canManageCustomEmojis',
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
|
@@ -10,7 +10,7 @@ export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireRoleOption: 'canManageCustomEmojis',
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
|
||||
errors: {
|
||||
noSuchEmoji: {
|
||||
|
@@ -5,7 +5,7 @@ import { QueueService } from '@/core/QueueService.js';
|
||||
export const meta = {
|
||||
secure: true,
|
||||
requireCredential: true,
|
||||
requireRoleOption: 'canManageCustomEmojis',
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
|
@@ -11,7 +11,7 @@ export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireRoleOption: 'canManageCustomEmojis',
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
|
||||
res: {
|
||||
type: 'array',
|
||||
|
@@ -11,7 +11,7 @@ export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireRoleOption: 'canManageCustomEmojis',
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
|
||||
res: {
|
||||
type: 'array',
|
||||
|
@@ -8,7 +8,7 @@ export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireRoleOption: 'canManageCustomEmojis',
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
|
@@ -8,7 +8,7 @@ export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireRoleOption: 'canManageCustomEmojis',
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
|
@@ -8,7 +8,7 @@ export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireRoleOption: 'canManageCustomEmojis',
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
|
@@ -9,7 +9,7 @@ export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireRoleOption: 'canManageCustomEmojis',
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
|
||||
errors: {
|
||||
noSuchEmoji: {
|
||||
|
@@ -4,7 +4,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import { MetaService } from '@/core/MetaService.js';
|
||||
import type { Config } from '@/config.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { DEFAULT_ROLE } from '@/core/RoleService.js';
|
||||
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['meta'],
|
||||
@@ -440,7 +440,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
deeplIsPro: instance.deeplIsPro,
|
||||
enableIpLogging: instance.enableIpLogging,
|
||||
enableActiveEmailValidation: instance.enableActiveEmailValidation,
|
||||
baseRole: { ...DEFAULT_ROLE, ...instance.defaultRoleOverride },
|
||||
policies: { ...DEFAULT_POLICIES, ...instance.policies },
|
||||
};
|
||||
});
|
||||
}
|
||||
|
@@ -25,7 +25,7 @@ export const paramDef = {
|
||||
isModerator: { type: 'boolean' },
|
||||
isAdministrator: { type: 'boolean' },
|
||||
canEditMembersByModerator: { type: 'boolean' },
|
||||
options: {
|
||||
policies: {
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
@@ -39,7 +39,7 @@ export const paramDef = {
|
||||
'isModerator',
|
||||
'isAdministrator',
|
||||
'canEditMembersByModerator',
|
||||
'options',
|
||||
'policies',
|
||||
],
|
||||
} as const;
|
||||
|
||||
@@ -70,7 +70,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
isAdministrator: ps.isAdministrator,
|
||||
isModerator: ps.isModerator,
|
||||
canEditMembersByModerator: ps.canEditMembersByModerator,
|
||||
options: ps.options,
|
||||
policies: ps.policies,
|
||||
}).then(x => this.rolesRepository.findOneByOrFail(x.identifiers[0]));
|
||||
|
||||
this.globalEventService.publishInternalEvent('roleCreated', created);
|
||||
|
@@ -16,12 +16,12 @@ export const meta = {
|
||||
export const paramDef = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
options: {
|
||||
policies: {
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
required: [
|
||||
'options',
|
||||
'policies',
|
||||
],
|
||||
} as const;
|
||||
|
||||
@@ -34,9 +34,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
) {
|
||||
super(meta, paramDef, async (ps) => {
|
||||
await this.metaService.update({
|
||||
defaultRoleOverride: ps.options,
|
||||
policies: ps.policies,
|
||||
});
|
||||
this.globalEventService.publishInternalEvent('defaultRoleOverrideUpdated', ps.options);
|
||||
this.globalEventService.publishInternalEvent('policiesUpdated', ps.policies);
|
||||
});
|
||||
}
|
||||
}
|
@@ -33,7 +33,7 @@ export const paramDef = {
|
||||
isModerator: { type: 'boolean' },
|
||||
isAdministrator: { type: 'boolean' },
|
||||
canEditMembersByModerator: { type: 'boolean' },
|
||||
options: {
|
||||
policies: {
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
@@ -48,7 +48,7 @@ export const paramDef = {
|
||||
'isModerator',
|
||||
'isAdministrator',
|
||||
'canEditMembersByModerator',
|
||||
'options',
|
||||
'policies',
|
||||
],
|
||||
} as const;
|
||||
|
||||
@@ -79,7 +79,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
isModerator: ps.isModerator,
|
||||
isAdministrator: ps.isAdministrator,
|
||||
canEditMembersByModerator: ps.canEditMembersByModerator,
|
||||
options: ps.options,
|
||||
policies: ps.policies,
|
||||
});
|
||||
const updated = await this.rolesRepository.findOneByOrFail({ id: ps.roleId });
|
||||
this.globalEventService.publishInternalEvent('roleUpdated', updated);
|
||||
|
@@ -52,7 +52,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
}
|
||||
|
||||
const isModerator = await this.roleService.isModerator(user);
|
||||
const isSilenced = !(await this.roleService.getUserRoleOptions(user.id)).canPublicNote;
|
||||
const isSilenced = !(await this.roleService.getUserPolicies(user.id)).canPublicNote;
|
||||
|
||||
const _me = await this.usersRepository.findOneByOrFail({ id: me.id });
|
||||
if (!await this.roleService.isAdministrator(_me) && await this.roleService.isAdministrator(user)) {
|
||||
@@ -94,6 +94,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
lastActiveDate: user.lastActiveDate,
|
||||
moderationNote: profile.moderationNote,
|
||||
signins,
|
||||
policies: await this.roleService.getUserPolicies(user.id),
|
||||
roles: await this.roleEntityService.packMany(roles, me, { detail: false }),
|
||||
};
|
||||
});
|
||||
|
@@ -92,7 +92,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
const currentAntennasCount = await this.antennasRepository.countBy({
|
||||
userId: me.id,
|
||||
});
|
||||
if (currentAntennasCount > (await this.roleService.getUserRoleOptions(me.id)).antennaLimit) {
|
||||
if (currentAntennasCount > (await this.roleService.getUserPolicies(me.id)).antennaLimit) {
|
||||
throw new ApiError(meta.errors.tooManyAntennas);
|
||||
}
|
||||
|
||||
|
@@ -97,7 +97,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
const currentCount = await this.clipNotesRepository.countBy({
|
||||
clipId: clip.id,
|
||||
});
|
||||
if (currentCount > (await this.roleService.getUserRoleOptions(me.id)).noteEachClipsLimit) {
|
||||
if (currentCount > (await this.roleService.getUserPolicies(me.id)).noteEachClipsLimit) {
|
||||
throw new ApiError(meta.errors.tooManyClipNotes);
|
||||
}
|
||||
|
||||
|
@@ -54,7 +54,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
const currentCount = await this.clipsRepository.countBy({
|
||||
userId: me.id,
|
||||
});
|
||||
if (currentCount > (await this.roleService.getUserRoleOptions(me.id)).clipLimit) {
|
||||
if (currentCount > (await this.roleService.getUserPolicies(me.id)).clipLimit) {
|
||||
throw new ApiError(meta.errors.tooManyClips);
|
||||
}
|
||||
|
||||
|
@@ -47,10 +47,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
// Calculate drive usage
|
||||
const usage = await this.driveFileEntityService.calcDriveUsageOf(me.id);
|
||||
|
||||
const myRole = await this.roleService.getUserRoleOptions(me.id);
|
||||
const policies = await this.roleService.getUserPolicies(me.id);
|
||||
|
||||
return {
|
||||
capacity: 1024 * 1024 * myRole.driveCapacityMb,
|
||||
capacity: 1024 * 1024 * policies.driveCapacityMb,
|
||||
usage: usage,
|
||||
};
|
||||
});
|
||||
|
@@ -63,8 +63,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
case '-following': query.orderBy('instance.followingCount', 'ASC'); break;
|
||||
case '+followers': query.orderBy('instance.followersCount', 'DESC'); break;
|
||||
case '-followers': query.orderBy('instance.followersCount', 'ASC'); break;
|
||||
case '+caughtAt': query.orderBy('instance.caughtAt', 'DESC'); break;
|
||||
case '-caughtAt': query.orderBy('instance.caughtAt', 'ASC'); break;
|
||||
case '+firstRetrievedAt': query.orderBy('instance.firstRetrievedAt', 'DESC'); break;
|
||||
case '-firstRetrievedAt': query.orderBy('instance.firstRetrievedAt', 'ASC'); break;
|
||||
case '+latestRequestReceivedAt': query.orderBy('instance.latestRequestReceivedAt', 'DESC', 'NULLS LAST'); break;
|
||||
case '-latestRequestReceivedAt': query.orderBy('instance.latestRequestReceivedAt', 'ASC', 'NULLS FIRST'); break;
|
||||
|
||||
|
@@ -173,7 +173,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
if (ps.mutedWords !== undefined) {
|
||||
// TODO: ちゃんと数える
|
||||
const length = JSON.stringify(ps.mutedWords).length;
|
||||
if (length > (await this.roleService.getUserRoleOptions(user.id)).wordMuteLimit) {
|
||||
if (length > (await this.roleService.getUserPolicies(user.id)).wordMuteLimit) {
|
||||
throw new ApiError(meta.errors.tooManyMutedWords);
|
||||
}
|
||||
|
||||
|
@@ -54,7 +54,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
const currentWebhooksCount = await this.webhooksRepository.countBy({
|
||||
userId: me.id,
|
||||
});
|
||||
if (currentWebhooksCount > (await this.roleService.getUserRoleOptions(me.id)).webhookLimit) {
|
||||
if (currentWebhooksCount > (await this.roleService.getUserPolicies(me.id)).webhookLimit) {
|
||||
throw new ApiError(meta.errors.tooManyWebhooks);
|
||||
}
|
||||
|
||||
|
@@ -9,7 +9,7 @@ export const meta = {
|
||||
tags: ['meta'],
|
||||
|
||||
requireCredential: true,
|
||||
requireRoleOption: 'canInvite',
|
||||
requireRolePolicy: 'canInvite',
|
||||
|
||||
res: {
|
||||
type: 'object',
|
||||
|
@@ -7,7 +7,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||
import { MetaService } from '@/core/MetaService.js';
|
||||
import type { Config } from '@/config.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { DEFAULT_ROLE } from '@/core/RoleService.js';
|
||||
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['meta'],
|
||||
@@ -334,7 +334,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
|
||||
translatorAvailable: instance.deeplAuthKey != null,
|
||||
|
||||
baseRole: { ...DEFAULT_ROLE, ...instance.defaultRoleOverride },
|
||||
policies: { ...DEFAULT_POLICIES, ...instance.policies },
|
||||
|
||||
...(ps.detail ? {
|
||||
pinnedPages: instance.pinnedPages,
|
||||
|
@@ -62,8 +62,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
private activeUsersChart: ActiveUsersChart,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const role = await this.roleService.getUserRoleOptions(me ? me.id : null);
|
||||
if (!role.gtlAvailable) {
|
||||
const policies = await this.roleService.getUserPolicies(me ? me.id : null);
|
||||
if (!policies.gtlAvailable) {
|
||||
throw new ApiError(meta.errors.gtlDisabled);
|
||||
}
|
||||
|
||||
|
@@ -71,8 +71,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
private activeUsersChart: ActiveUsersChart,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const role = await this.roleService.getUserRoleOptions(me.id);
|
||||
if (!role.ltlAvailable) {
|
||||
const policies = await this.roleService.getUserPolicies(me.id);
|
||||
if (!policies.ltlAvailable) {
|
||||
throw new ApiError(meta.errors.stlDisabled);
|
||||
}
|
||||
|
||||
|
@@ -67,8 +67,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
private activeUsersChart: ActiveUsersChart,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const role = await this.roleService.getUserRoleOptions(me ? me.id : null);
|
||||
if (!role.ltlAvailable) {
|
||||
const policies = await this.roleService.getUserPolicies(me ? me.id : null);
|
||||
if (!policies.ltlAvailable) {
|
||||
throw new ApiError(meta.errors.ltlDisabled);
|
||||
}
|
||||
|
||||
|
@@ -55,7 +55,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
const currentCount = await this.userListsRepository.countBy({
|
||||
userId: me.id,
|
||||
});
|
||||
if (currentCount > (await this.roleService.getUserRoleOptions(me.id)).userListLimit) {
|
||||
if (currentCount > (await this.roleService.getUserPolicies(me.id)).userListLimit) {
|
||||
throw new ApiError(meta.errors.tooManyUserLists);
|
||||
}
|
||||
|
||||
|
@@ -29,8 +29,8 @@ class GlobalTimelineChannel extends Channel {
|
||||
|
||||
@bindThis
|
||||
public async init(params: any) {
|
||||
const role = await this.roleService.getUserRoleOptions(this.user ? this.user.id : null);
|
||||
if (!role.gtlAvailable) return;
|
||||
const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null);
|
||||
if (!policies.gtlAvailable) return;
|
||||
|
||||
// Subscribe events
|
||||
this.subscriber.on('notesStream', this.onNote);
|
||||
|
@@ -30,8 +30,8 @@ class HybridTimelineChannel extends Channel {
|
||||
|
||||
@bindThis
|
||||
public async init(params: any): Promise<void> {
|
||||
const role = await this.roleService.getUserRoleOptions(this.user ? this.user.id : null);
|
||||
if (!role.ltlAvailable) return;
|
||||
const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null);
|
||||
if (!policies.ltlAvailable) return;
|
||||
|
||||
// Subscribe events
|
||||
this.subscriber.on('notesStream', this.onNote);
|
||||
|
@@ -28,8 +28,8 @@ class LocalTimelineChannel extends Channel {
|
||||
|
||||
@bindThis
|
||||
public async init(params: any) {
|
||||
const role = await this.roleService.getUserRoleOptions(this.user ? this.user.id : null);
|
||||
if (!role.ltlAvailable) return;
|
||||
const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null);
|
||||
if (!policies.ltlAvailable) return;
|
||||
|
||||
// Subscribe events
|
||||
this.subscriber.on('notesStream', this.onNote);
|
||||
|
@@ -30,7 +30,7 @@ export interface InternalStreamTypes {
|
||||
remoteUserUpdated: Serialized<{ id: User['id']; }>;
|
||||
follow: Serialized<{ followerId: User['id']; followeeId: User['id']; }>;
|
||||
unfollow: Serialized<{ followerId: User['id']; followeeId: User['id']; }>;
|
||||
defaultRoleOverrideUpdated: Serialized<Role['options']>;
|
||||
policiesUpdated: Serialized<Role['options']>;
|
||||
roleCreated: Serialized<Role>;
|
||||
roleDeleted: Serialized<Role>;
|
||||
roleUpdated: Serialized<Role>;
|
||||
|
@@ -73,6 +73,7 @@
|
||||
"@types/gulp": "4.0.10",
|
||||
"@types/gulp-rename": "2.0.1",
|
||||
"@types/matter-js": "0.18.2",
|
||||
"@types/node": "^18.11.18",
|
||||
"@types/punycode": "2.1.0",
|
||||
"@types/sanitize-html": "^2.8.0",
|
||||
"@types/seedrandom": "3.0.4",
|
||||
|
@@ -23,7 +23,7 @@
|
||||
@leave="leave"
|
||||
@after-leave="afterLeave"
|
||||
>
|
||||
<div v-show="showBody" ref="content" :class="[$style.content, { omitted }]">
|
||||
<div v-show="showBody" ref="content" :class="[$style.content, { [$style.omitted]: omitted }]">
|
||||
<slot></slot>
|
||||
<button v-if="omitted" :class="$style.fade" class="_button" @click="() => { ignoreOmit = true; omitted = false; }">
|
||||
<span :class="$style.fadeLabel">{{ $ts.showMore }}</span>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user