Compare commits
138 Commits
Author | SHA1 | Date | |
---|---|---|---|
52c3f9e98c | |||
6c8b4184fe | |||
a0979f8435 | |||
faba21d003 | |||
d82c5dff71 | |||
59fbc5b054 | |||
2c1a7f4392 | |||
769e6182d8 | |||
88176a17a3 | |||
fc660e869f | |||
dc04869650 | |||
93c3f34813 | |||
1282eed192 | |||
962b3ca78e | |||
62d17c9266 | |||
f5b928a537 | |||
c8811894b5 | |||
e579b49228 | |||
9561908ad3 | |||
fac7ebf4f6 | |||
a0769d65e3 | |||
d17aa4b24e | |||
310371658b | |||
7ca073aafd | |||
7216d0fb1f | |||
22a9e950c7 | |||
6683d50bae | |||
8f26176273 | |||
9ea7d446e8 | |||
757312ba52 | |||
1675c473d4 | |||
3a3a5d4bfb | |||
4a41499c95 | |||
a1d1cb58e0 | |||
acb82fe7b6 | |||
b25df24cea | |||
39284eb9b2 | |||
31b0e552a2 | |||
c4a2a31cf3 | |||
4497ddb3f3 | |||
5e0eda9526 | |||
72b85fc09f | |||
6c27412c9c | |||
46bddfc9c2 | |||
56275bcfcb | |||
f35688bab8 | |||
93541f83c8 | |||
ea0d114833 | |||
7f6a3ec828 | |||
732804b6fa | |||
aba85b977d | |||
e6612f610c | |||
5a28632af7 | |||
4099db0d42 | |||
9d50a06d9c | |||
dd7bf9b2a3 | |||
c463284c2f | |||
c1d728a616 | |||
e43c9c0e21 | |||
15cac10d7b | |||
49958ca03f | |||
280dbe9853 | |||
bf964ee969 | |||
61dcd51888 | |||
5448c22031 | |||
27768081e2 | |||
c3140f57b9 | |||
7275bc6d3b | |||
485f2f460e | |||
336912e442 | |||
dd9c94e47e | |||
055863144d | |||
0bf602bae6 | |||
6fc28d1df7 | |||
2ef795aba8 | |||
1d2c50fc26 | |||
cef8aa5e7a | |||
edf3e75344 | |||
62835c6011 | |||
60fb22cb3c | |||
20dea3a793 | |||
aba37ae701 | |||
2c6e6275aa | |||
20ef362854 | |||
4692aa8d9b | |||
f7b6dc08f7 | |||
7dfe7005e0 | |||
b91de4ac12 | |||
d5205d7328 | |||
f44ce535fa | |||
7177fd27c8 | |||
cf304f88d4 | |||
dff1d84031 | |||
96bc17aa10 | |||
41ba06a5e6 | |||
d7ac0418d7 | |||
f4319a9c01 | |||
f4c4d53bbb | |||
0ed43e1bdf | |||
d25bd876cb | |||
b9782397c2 | |||
ea0abc9f71 | |||
27d16c6a12 | |||
ede70d354e | |||
66fa583f6e | |||
77bcb58f12 | |||
61036e3a70 | |||
bcd886c4f5 | |||
4d868aaf1f | |||
80ea747db6 | |||
960f29ce81 | |||
20ee57931f | |||
71ba72e796 | |||
9835945ee1 | |||
4f2d52697d | |||
46c258d77a | |||
3b5b3cf521 | |||
5e0bdd8a78 | |||
b299988bb5 | |||
e26bec6ab4 | |||
e9955e01d6 | |||
1974d8f58b | |||
08c0be11b2 | |||
87c7058494 | |||
b92addffa9 | |||
e8b49df842 | |||
18fd39b335 | |||
8a11322802 | |||
31929dad61 | |||
4a41d2fddc | |||
4c65b0cd6f | |||
3e89dc603d | |||
9595a56346 | |||
c7e8c27ce6 | |||
67792fcb5e | |||
353fc18f19 | |||
cf9e8ed39e | |||
8b71006fbe |
@ -2,6 +2,11 @@ version: 2.1
|
|||||||
|
|
||||||
executors:
|
executors:
|
||||||
default:
|
default:
|
||||||
|
working_directory: /tmp/workspace
|
||||||
|
docker:
|
||||||
|
- image: misskey/ci:latest
|
||||||
|
- image: circleci/mongo:latest
|
||||||
|
with-redis:
|
||||||
working_directory: /tmp/workspace
|
working_directory: /tmp/workspace
|
||||||
docker:
|
docker:
|
||||||
- image: misskey/ci:latest
|
- image: misskey/ci:latest
|
||||||
@ -24,7 +29,6 @@ jobs:
|
|||||||
name: OK
|
name: OK
|
||||||
command: |
|
command: |
|
||||||
echo -e '\033[0;32mOK\033[0;39m'
|
echo -e '\033[0;32mOK\033[0;39m'
|
||||||
|
|
||||||
build:
|
build:
|
||||||
executor: default
|
executor: default
|
||||||
steps:
|
steps:
|
||||||
@ -68,10 +72,13 @@ jobs:
|
|||||||
- .
|
- .
|
||||||
test:
|
test:
|
||||||
parameters:
|
parameters:
|
||||||
without_redis:
|
executor:
|
||||||
type: string
|
type: string
|
||||||
default: ""
|
default: "default"
|
||||||
executor: default
|
without_redis:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
executor: <<parameters.executor>>
|
||||||
steps:
|
steps:
|
||||||
- attach_workspace:
|
- attach_workspace:
|
||||||
at: /tmp/workspace
|
at: /tmp/workspace
|
||||||
@ -94,12 +101,11 @@ jobs:
|
|||||||
key: yarn-v1-arch-{{ arch }}-env-{{ .Environment.variableName }}-package-{{ checksum "package.json" }}-lock-{{ checksum "yarn.lock" }}
|
key: yarn-v1-arch-{{ arch }}-env-{{ .Environment.variableName }}-package-{{ checksum "package.json" }}-lock-{{ checksum "yarn.lock" }}
|
||||||
paths:
|
paths:
|
||||||
- node_modules
|
- node_modules
|
||||||
|
|
||||||
docker:
|
docker:
|
||||||
parameters:
|
parameters:
|
||||||
with_deploy:
|
with_deploy:
|
||||||
type: string
|
type: boolean
|
||||||
default: ""
|
default: false
|
||||||
executor: docker
|
executor: docker
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
@ -126,7 +132,7 @@ jobs:
|
|||||||
|
|
||||||
workflows:
|
workflows:
|
||||||
version: 2
|
version: 2
|
||||||
build-and-test:
|
nodejs:
|
||||||
jobs:
|
jobs:
|
||||||
- ok:
|
- ok:
|
||||||
filters:
|
filters:
|
||||||
@ -143,6 +149,7 @@ workflows:
|
|||||||
- imgbot
|
- imgbot
|
||||||
- patch-autogen
|
- patch-autogen
|
||||||
- test:
|
- test:
|
||||||
|
executor: with-redis
|
||||||
requires:
|
requires:
|
||||||
- build
|
- build
|
||||||
filters:
|
filters:
|
||||||
@ -153,7 +160,7 @@ workflows:
|
|||||||
- imgbot
|
- imgbot
|
||||||
- patch-autogen
|
- patch-autogen
|
||||||
- test:
|
- test:
|
||||||
without_redis: "true"
|
without_redis: true
|
||||||
requires:
|
requires:
|
||||||
- build
|
- build
|
||||||
filters:
|
filters:
|
||||||
@ -165,12 +172,21 @@ workflows:
|
|||||||
- l10n_develop
|
- l10n_develop
|
||||||
- imgbot
|
- imgbot
|
||||||
- patch-autogen
|
- patch-autogen
|
||||||
# - docker:
|
docker:
|
||||||
# filters:
|
jobs:
|
||||||
# branches:
|
- hold:
|
||||||
# ignore: master
|
type: approval
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
ignore: master
|
||||||
- docker:
|
- docker:
|
||||||
with_deploy: "true"
|
requires:
|
||||||
|
- hold
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
ignore: master
|
||||||
|
- docker:
|
||||||
|
with_deploy: true
|
||||||
filters:
|
filters:
|
||||||
branches:
|
branches:
|
||||||
only: master
|
only: master
|
||||||
|
@ -6,6 +6,8 @@ mongodb:
|
|||||||
db: misskey
|
db: misskey
|
||||||
user: syuilo
|
user: syuilo
|
||||||
pass: ''
|
pass: ''
|
||||||
|
drive:
|
||||||
|
storage: 'db'
|
||||||
redis:
|
redis:
|
||||||
host: localhost
|
host: localhost
|
||||||
port: 6379
|
port: 6379
|
||||||
|
@ -6,6 +6,8 @@ mongodb:
|
|||||||
db: test-misskey
|
db: test-misskey
|
||||||
user: admin
|
user: admin
|
||||||
pass: ''
|
pass: ''
|
||||||
|
drive:
|
||||||
|
storage: 'db'
|
||||||
# __REDIS__
|
# __REDIS__
|
||||||
redis:
|
redis:
|
||||||
host: localhost
|
host: localhost
|
||||||
|
@ -108,5 +108,8 @@ autoAdmin: true
|
|||||||
# port: 9200
|
# port: 9200
|
||||||
# pass: null
|
# pass: null
|
||||||
|
|
||||||
|
# Whether disable HSTS
|
||||||
|
#disableHsts: true
|
||||||
|
|
||||||
# Clustering
|
# Clustering
|
||||||
#clusterLimit: 1
|
#clusterLimit: 1
|
||||||
|
10
.github/ISSUE_TEMPLATE/bug_report.md
vendored
10
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -1,22 +1,30 @@
|
|||||||
---
|
---
|
||||||
name: Bug Report
|
name: Bug Report
|
||||||
about: Create a report to help us improve
|
about: Create a report to help us improve
|
||||||
|
title: ''
|
||||||
|
labels: bug
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Summary
|
# Summary
|
||||||
|
|
||||||
<!-- Tell us what the bug is -->
|
<!-- Tell us what the bug is -->
|
||||||
|
|
||||||
# Expected Behavior
|
# Expected Behavior
|
||||||
|
|
||||||
<!--- Tell us what should happen -->
|
<!--- Tell us what should happen -->
|
||||||
|
|
||||||
# Actual Behavior
|
# Actual Behavior
|
||||||
|
|
||||||
<!--- Tell us what happens instead of the expected behavior -->
|
<!--- Tell us what happens instead of the expected behavior -->
|
||||||
|
|
||||||
# Steps to Reproduce
|
# Steps to Reproduce
|
||||||
|
|
||||||
1.
|
1.
|
||||||
2.
|
2.
|
||||||
3.
|
3.
|
||||||
|
|
||||||
# Environment
|
# Environment
|
||||||
|
|
||||||
<!-- Tell us where on the platform it happens -->
|
<!-- Tell us where on the platform it happens -->
|
||||||
<!-- e.g. desktop or mobile version, your browser, your OS -->
|
|
||||||
|
31
.github/ISSUE_TEMPLATE/client-side-bug-report.md
vendored
Normal file
31
.github/ISSUE_TEMPLATE/client-side-bug-report.md
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
---
|
||||||
|
name: Client-side Bug Report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: ''
|
||||||
|
labels: bug, client-side
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
|
||||||
|
<!-- Tell us what the bug is -->
|
||||||
|
|
||||||
|
# Expected Behavior
|
||||||
|
|
||||||
|
<!--- Tell us what should happen -->
|
||||||
|
|
||||||
|
# Actual Behavior
|
||||||
|
|
||||||
|
<!--- Tell us what happens instead of the expected behavior -->
|
||||||
|
|
||||||
|
# Steps to Reproduce
|
||||||
|
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3.
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
|
||||||
|
<!-- Tell us where on the platform it happens -->
|
||||||
|
<!-- e.g. desktop or mobile version, your browser, your OS -->
|
12
.github/ISSUE_TEMPLATE/client-side-feature-request.md
vendored
Normal file
12
.github/ISSUE_TEMPLATE/client-side-feature-request.md
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
name: Client-side Feature Request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: ''
|
||||||
|
labels: client-side, feature
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
|
||||||
|
<!-- Tell us what the suggestion is -->
|
9
.github/ISSUE_TEMPLATE/feature_request.md
vendored
9
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -1,11 +1,12 @@
|
|||||||
---
|
---
|
||||||
name: Feature Request
|
name: Feature Request
|
||||||
about: Suggest an idea for this project
|
about: Suggest an idea for this project
|
||||||
|
title: ''
|
||||||
|
labels: feature
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Summary
|
# Summary
|
||||||
<!-- Tell us what the suggestion is -->
|
|
||||||
|
|
||||||
# Environment
|
<!-- Tell us what the suggestion is -->
|
||||||
<!-- Tell us where on the platform it related -->
|
|
||||||
<!-- e.g. desktop or mobile version, your browser, your OS -->
|
|
||||||
|
31
.github/ISSUE_TEMPLATE/server-side-bug-report.md
vendored
Normal file
31
.github/ISSUE_TEMPLATE/server-side-bug-report.md
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
---
|
||||||
|
name: Server-side Bug Report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: ''
|
||||||
|
labels: bug, server-side
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
|
||||||
|
<!-- Tell us what the bug is -->
|
||||||
|
|
||||||
|
# Expected Behavior
|
||||||
|
|
||||||
|
<!--- Tell us what should happen -->
|
||||||
|
|
||||||
|
# Actual Behavior
|
||||||
|
|
||||||
|
<!--- Tell us what happens instead of the expected behavior -->
|
||||||
|
|
||||||
|
# Steps to Reproduce
|
||||||
|
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3.
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
|
||||||
|
<!-- Tell us where on the platform it happens -->
|
||||||
|
<!-- e.g. your Node.js version, your OS -->
|
12
.github/ISSUE_TEMPLATE/server-side-feature-request.md
vendored
Normal file
12
.github/ISSUE_TEMPLATE/server-side-feature-request.md
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
name: Server-side Feature Request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: ''
|
||||||
|
labels: feature, server-side
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
|
||||||
|
<!-- Tell us what the suggestion is -->
|
41
CHANGELOG.md
41
CHANGELOG.md
@ -1,6 +1,47 @@
|
|||||||
ChangeLog
|
ChangeLog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
10.84.1
|
||||||
|
----------
|
||||||
|
* deckにフォローされていますマークを追加
|
||||||
|
* URLプレビューのサムネイルの調整
|
||||||
|
* 管理画面でサイレンスされているユーザーを一覧できるように
|
||||||
|
* ドキュメントにアクセスできない問題を修正
|
||||||
|
* ジョブキューを無効化
|
||||||
|
* 軽微なバグ修正
|
||||||
|
|
||||||
|
10.84.0
|
||||||
|
----------
|
||||||
|
* インスタンス管理の強化
|
||||||
|
* パフォーマンスの問題の修正
|
||||||
|
* バグ修正
|
||||||
|
|
||||||
|
10.83.0
|
||||||
|
----------
|
||||||
|
* 特定のインスタンスをブロックをできるように
|
||||||
|
* 特定のインスタンスからのフォローを全解除できるように
|
||||||
|
* インスタンスごとのチャートを追加
|
||||||
|
|
||||||
|
10.82.4
|
||||||
|
----------
|
||||||
|
* 10.82.3でオブジェクトストレージの設定をしていると起動しなくなるバグを修正
|
||||||
|
|
||||||
|
10.82.3
|
||||||
|
----------
|
||||||
|
* フォロー/ミュート/ブロックデータをエクスポート可能に
|
||||||
|
* バグ修正
|
||||||
|
* デザインの調整
|
||||||
|
* ジョブキューの動作を修正
|
||||||
|
|
||||||
|
10.82.2
|
||||||
|
----------
|
||||||
|
* ジョブキューの動作を修正
|
||||||
|
|
||||||
|
10.82.1
|
||||||
|
----------
|
||||||
|
* クラスタリング環境でのジョブキューの動作を修正
|
||||||
|
* その他の軽微な改善
|
||||||
|
|
||||||
10.82.0
|
10.82.0
|
||||||
----------
|
----------
|
||||||
* 自分の投稿情報をエクスポートできるように
|
* 自分の投稿情報をエクスポートできるように
|
||||||
|
@ -44,3 +44,31 @@ Stands for _**S**ervice**W**orker_.
|
|||||||
|
|
||||||
#### Denyaize
|
#### Denyaize
|
||||||
Nyaizeを解除すること
|
Nyaizeを解除すること
|
||||||
|
|
||||||
|
## Code style
|
||||||
|
### Don't use `export default`
|
||||||
|
Bad:
|
||||||
|
``` ts
|
||||||
|
export default function(foo: string): string {
|
||||||
|
```
|
||||||
|
|
||||||
|
Good:
|
||||||
|
``` ts
|
||||||
|
export function something(foo: string): string {
|
||||||
|
```
|
||||||
|
|
||||||
|
## Directory structure
|
||||||
|
```
|
||||||
|
src ... ソースコード
|
||||||
|
@types ... 外部ライブラリなどの型定義
|
||||||
|
prelude ... Misskeyに関係ないかつ副作用なし
|
||||||
|
misc ... 副作用なしのユーティリティ処理
|
||||||
|
service ... 副作用ありの共通処理
|
||||||
|
queue ... ジョブキューとジョブ
|
||||||
|
server ... Webサーバー
|
||||||
|
client ... クライアント
|
||||||
|
mfm ... MFM
|
||||||
|
|
||||||
|
test ... テスト
|
||||||
|
|
||||||
|
```
|
||||||
|
10
Dockerfile
10
Dockerfile
@ -23,15 +23,11 @@ RUN apk add --no-cache \
|
|||||||
procps \
|
procps \
|
||||||
python \
|
python \
|
||||||
zlib-dev
|
zlib-dev
|
||||||
RUN npm i -g node-gyp
|
RUN npm i -g yarn
|
||||||
|
|
||||||
COPY ./package.json ./
|
|
||||||
RUN npm i
|
|
||||||
|
|
||||||
COPY . ./
|
COPY . ./
|
||||||
RUN node-gyp configure \
|
RUN yarn install
|
||||||
&& node-gyp build \
|
RUN yarn build
|
||||||
&& npm run build
|
|
||||||
|
|
||||||
FROM base AS runner
|
FROM base AS runner
|
||||||
|
|
||||||
|
14
README.md
14
README.md
@ -3,9 +3,9 @@
|
|||||||
[](https://misskey.xyz/)
|
[](https://misskey.xyz/)
|
||||||
================================================================
|
================================================================
|
||||||
|
|
||||||
[](https://circleci.com/gh/syuilo/misskey)
|
[](https://circleci.com/gh/syuilo/misskey)
|
||||||
[](https://david-dm.org/syuilo/misskey)
|
[](https://david-dm.org/syuilo/misskey)
|
||||||
[](http://makeapullrequest.com)
|
[](http://makeapullrequest.com)
|
||||||
|
|
||||||
**A forever evolving, sophisticated microblogging platform.**
|
**A forever evolving, sophisticated microblogging platform.**
|
||||||
|
|
||||||
@ -61,6 +61,10 @@ Organize and store your files! Want to post a picture you have already uploaded?
|
|||||||
|
|
||||||
...and more! Experience Misskey with your own eyes at [misskey.xyz](https://misskey.xyz) or join one of the [other instances](https://joinmisskey.github.io/) that are available.
|
...and more! Experience Misskey with your own eyes at [misskey.xyz](https://misskey.xyz) or join one of the [other instances](https://joinmisskey.github.io/) that are available.
|
||||||
|
|
||||||
|
:new: What's new
|
||||||
|
----------------------------------------------------------------
|
||||||
|
Please see the [Release notes](./CHANGELOG.md).
|
||||||
|
|
||||||
:package: Create your own instance
|
:package: Create your own instance
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
Please see the [Setup and Installation Guide](./docs/setup.en.md).
|
Please see the [Setup and Installation Guide](./docs/setup.en.md).
|
||||||
@ -111,6 +115,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
|
|||||||
<td><img src="https://c8.patreon.com/2/200/16542964" alt="Takumi Sugita" width="100"></td>
|
<td><img src="https://c8.patreon.com/2/200/16542964" alt="Takumi Sugita" width="100"></td>
|
||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13039004/509d0c412eb14ae08d6a812a3054f7d6/1?token-time=2145916800&token-hash=2PsbFNw0tnubZzgSXD01R6hIgncfiElG7H7HX2Y3dyo%3D" alt="nemu" width="100"></td>
|
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13039004/509d0c412eb14ae08d6a812a3054f7d6/1?token-time=2145916800&token-hash=2PsbFNw0tnubZzgSXD01R6hIgncfiElG7H7HX2Y3dyo%3D" alt="nemu" width="100"></td>
|
||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5881381/6235ca5d3fb04c8e95ef5b4ff2abcc18/3?token-time=2145916800&token-hash=9JtETp0X8gI280Ne1E8bxn6j4Lw5o2k4mJkICx97V_k%3D" alt="YUKIMOCHI" width="100"></td>
|
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5881381/6235ca5d3fb04c8e95ef5b4ff2abcc18/3?token-time=2145916800&token-hash=9JtETp0X8gI280Ne1E8bxn6j4Lw5o2k4mJkICx97V_k%3D" alt="YUKIMOCHI" width="100"></td>
|
||||||
|
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/17195955/be45e5e14c3e48b2bee0456c84e19df4/4?token-time=2145916800&token-hash=SbdZeN5SmsuT9stD6v0jN1z0hftg0FmRiCTxysU0Ihw%3D" alt="Damillora" width="100"></td>
|
||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/8241184/39e18850e87a449e9c9a71acb3310ebd/3?token-time=2145916800&token-hash=gMq30aylxu5v3G8pRhWR5jeRBbYWEoRKjGbNeiCQz5g%3D" alt="Acid Chicken" width="100"></td>
|
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/8241184/39e18850e87a449e9c9a71acb3310ebd/3?token-time=2145916800&token-hash=gMq30aylxu5v3G8pRhWR5jeRBbYWEoRKjGbNeiCQz5g%3D" alt="Acid Chicken" width="100"></td>
|
||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/4389829/9f709180ac714651a70f74a82f3ffdb9/2?token-time=2145916800&token-hash=zcwFxb2zopzWwksKVU1YpfAEjsl4yKT02aQ6yiAFRiQ%3D" alt="natalie" width="100"></td>
|
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/4389829/9f709180ac714651a70f74a82f3ffdb9/2?token-time=2145916800&token-hash=zcwFxb2zopzWwksKVU1YpfAEjsl4yKT02aQ6yiAFRiQ%3D" alt="natalie" width="100"></td>
|
||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13034746/c711c7f58e204ecfbc2fd646bc8a4eee/1?token-time=2145916800&token-hash=5T8XcaAf9Zyzfg3QubR06s_kJZkArVEM2dwObrBVAU4%3D" alt="Hiratake" width="100"></td>
|
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13034746/c711c7f58e204ecfbc2fd646bc8a4eee/1?token-time=2145916800&token-hash=5T8XcaAf9Zyzfg3QubR06s_kJZkArVEM2dwObrBVAU4%3D" alt="Hiratake" width="100"></td>
|
||||||
@ -120,6 +125,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
|
|||||||
<td><a href="https://www.patreon.com/user?u=16542964">Takumi Sugita</a></td>
|
<td><a href="https://www.patreon.com/user?u=16542964">Takumi Sugita</a></td>
|
||||||
<td><a href="https://www.patreon.com/user?u=13039004">nemu</a></td>
|
<td><a href="https://www.patreon.com/user?u=13039004">nemu</a></td>
|
||||||
<td><a href="https://www.patreon.com/yukimochi">YUKIMOCHI</a></td>
|
<td><a href="https://www.patreon.com/yukimochi">YUKIMOCHI</a></td>
|
||||||
|
<td><a href="https://www.patreon.com/damillora">Damillora</a></td>
|
||||||
<td><a href="https://www.patreon.com/acid_chicken">Acid Chicken</a></td>
|
<td><a href="https://www.patreon.com/acid_chicken">Acid Chicken</a></td>
|
||||||
<td><a href="https://www.patreon.com/user?u=4389829">natalie</a></td>
|
<td><a href="https://www.patreon.com/user?u=4389829">natalie</a></td>
|
||||||
<td><a href="https://www.patreon.com/hiratake">Hiratake</a></td>
|
<td><a href="https://www.patreon.com/hiratake">Hiratake</a></td>
|
||||||
@ -136,7 +142,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
|
|||||||
<td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td>
|
<td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td>
|
||||||
</tr></table>
|
</tr></table>
|
||||||
|
|
||||||
**Last updated:** Sun, 03 Feb 2019 10:13:06 UTC
|
**Last updated:** Wed, 06 Feb 2019 18:18:05 UTC
|
||||||
<!-- PATREON_END -->
|
<!-- PATREON_END -->
|
||||||
|
|
||||||
:four_leaf_clover: Copyright
|
:four_leaf_clover: Copyright
|
||||||
|
@ -509,6 +509,13 @@ common/views/components/profile-editor.vue:
|
|||||||
email-address: "メールアドレス"
|
email-address: "メールアドレス"
|
||||||
email-verified: "メールアドレスが確認されました"
|
email-verified: "メールアドレスが確認されました"
|
||||||
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
||||||
|
export: "エクスポート"
|
||||||
|
export-targets:
|
||||||
|
all-notes: "すべての投稿データ"
|
||||||
|
following-list: "フォロー"
|
||||||
|
mute-list: "ミュート"
|
||||||
|
blocking-list: "ブロック"
|
||||||
|
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||||
common/views/components/user-list-editor.vue:
|
common/views/components/user-list-editor.vue:
|
||||||
users: "ユーザー"
|
users: "ユーザー"
|
||||||
rename: "リスト名を変更"
|
rename: "リスト名を変更"
|
||||||
@ -1000,6 +1007,7 @@ admin/views/index.vue:
|
|||||||
announcements: "お知らせ"
|
announcements: "お知らせ"
|
||||||
hashtags: "ハッシュタグ"
|
hashtags: "ハッシュタグ"
|
||||||
abuse: "スパム報告"
|
abuse: "スパム報告"
|
||||||
|
queue: "ジョブキュー"
|
||||||
back-to-misskey: "Misskeyに戻る"
|
back-to-misskey: "Misskeyに戻る"
|
||||||
admin/views/dashboard.vue:
|
admin/views/dashboard.vue:
|
||||||
dashboard: "ダッシュボード"
|
dashboard: "ダッシュボード"
|
||||||
@ -1009,6 +1017,9 @@ admin/views/dashboard.vue:
|
|||||||
instances: "インスタンス"
|
instances: "インスタンス"
|
||||||
this-instance: "このインスタンス"
|
this-instance: "このインスタンス"
|
||||||
federated: "連合"
|
federated: "連合"
|
||||||
|
admin/views/queue.vue:
|
||||||
|
operation: "操作"
|
||||||
|
remove-all-jobs: "すべてのジョブをクリア"
|
||||||
admin/views/abuse.vue:
|
admin/views/abuse.vue:
|
||||||
title: "スパム報告"
|
title: "スパム報告"
|
||||||
target: "対象"
|
target: "対象"
|
||||||
@ -1175,6 +1186,7 @@ admin/views/users.vue:
|
|||||||
moderator: "モデレーター"
|
moderator: "モデレーター"
|
||||||
adminOrModerator: "管理者+モデレーター"
|
adminOrModerator: "管理者+モデレーター"
|
||||||
verified: "公式アカウント"
|
verified: "公式アカウント"
|
||||||
|
silenced: "サイレンス済み"
|
||||||
suspended: "凍結済み"
|
suspended: "凍結済み"
|
||||||
origin:
|
origin:
|
||||||
title: "オリジン"
|
title: "オリジン"
|
||||||
@ -1222,6 +1234,64 @@ admin/views/announcements.vue:
|
|||||||
removed: "削除しました"
|
removed: "削除しました"
|
||||||
admin/views/hashtags.vue:
|
admin/views/hashtags.vue:
|
||||||
hided-tags: "Hidden Tags"
|
hided-tags: "Hidden Tags"
|
||||||
|
admin/views/federation.vue:
|
||||||
|
federation: "連合"
|
||||||
|
host: "ホスト"
|
||||||
|
notes: "投稿"
|
||||||
|
users: "ユーザー"
|
||||||
|
following: "フォロー中"
|
||||||
|
followers: "フォロワー"
|
||||||
|
status: "ステータス"
|
||||||
|
latest-request-sent-at: "直近のリクエスト送信"
|
||||||
|
latest-request-received-at: "直近のリクエスト受信"
|
||||||
|
remove-all-following: "フォローを全解除"
|
||||||
|
remove-all-following-info: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。"
|
||||||
|
block: "ブロック"
|
||||||
|
marked-as-closed: "閉鎖されているとマーク"
|
||||||
|
lookup: "照会"
|
||||||
|
instances: "インスタンス"
|
||||||
|
instance-not-registered: "そのインスタンスは登録されていません"
|
||||||
|
sort: "ソート"
|
||||||
|
sorts:
|
||||||
|
caughtAtAsc: "登録日時が古い順"
|
||||||
|
caughtAtDesc: "登録日時が新しい順"
|
||||||
|
lastCommunicatedAtAsc: "最後にやり取りした日時が古い順"
|
||||||
|
lastCommunicatedAtDesc: "最後にやり取りした日時が新しい順"
|
||||||
|
notesAsc: "投稿が少ない順"
|
||||||
|
notesDesc: "投稿が多い順"
|
||||||
|
usersAsc: "ユーザーが少ない順"
|
||||||
|
usersDesc: "ユーザーが多い順"
|
||||||
|
followingAsc: "フォローが少ない順"
|
||||||
|
followingDesc: "フォローが多い順"
|
||||||
|
followersAsc: "フォロワーが少ない順"
|
||||||
|
followersDesc: "フォロワーが多い順"
|
||||||
|
driveUsageAsc: "ドライブ使用量が少ない順"
|
||||||
|
driveUsageDesc: "ドライブ使用量が多い順"
|
||||||
|
driveFilesAsc: "ドライブのファイル数が少ない順"
|
||||||
|
driveFilesDesc: "ドライブのファイル数が多い順"
|
||||||
|
state: "状態"
|
||||||
|
states:
|
||||||
|
all: "すべて"
|
||||||
|
blocked: "ブロック"
|
||||||
|
not-responding: "応答なし"
|
||||||
|
marked-as-closed: "閉鎖とマーク済み"
|
||||||
|
result-is-truncated: "上位{n}件を表示しています。"
|
||||||
|
charts: "チャート"
|
||||||
|
chart-srcs:
|
||||||
|
requests: "リクエスト"
|
||||||
|
users: "ユーザーの増減"
|
||||||
|
users-total: "ユーザーの積算"
|
||||||
|
notes: "投稿の増減"
|
||||||
|
notes-total: "投稿の積算"
|
||||||
|
ff: "フォロー/フォロワーの増減"
|
||||||
|
ff-total: "フォロー/フォロワーの積算"
|
||||||
|
drive-usage: "ドライブ使用量の増減"
|
||||||
|
drive-usage-total: "ドライブ使用量の積算"
|
||||||
|
drive-files: "ドライブファイル数の増減"
|
||||||
|
drive-files-total: "ドライブファイル数の積算"
|
||||||
|
chart-spans:
|
||||||
|
hour: "1時間ごと"
|
||||||
|
day: "1日ごと"
|
||||||
desktop/views/pages/welcome.vue:
|
desktop/views/pages/welcome.vue:
|
||||||
about: "詳しく..."
|
about: "詳しく..."
|
||||||
gotit: "わかった"
|
gotit: "わかった"
|
||||||
@ -1557,6 +1627,7 @@ deck/deck.tl-column.vue:
|
|||||||
is-media-view: "メディアビュー"
|
is-media-view: "メディアビュー"
|
||||||
edit: "オプション"
|
edit: "オプション"
|
||||||
deck/deck.user-column.vue:
|
deck/deck.user-column.vue:
|
||||||
|
follows-you: "フォローされています"
|
||||||
posts: "投稿"
|
posts: "投稿"
|
||||||
following: "フォロー"
|
following: "フォロー"
|
||||||
followers: "フォロワー"
|
followers: "フォロワー"
|
||||||
|
@ -5,7 +5,7 @@ meta:
|
|||||||
common:
|
common:
|
||||||
misskey: "Ein ⭐ des Fediversums"
|
misskey: "Ein ⭐ des Fediversums"
|
||||||
about-title: "Ein ⭐ des Fediversums."
|
about-title: "Ein ⭐ des Fediversums."
|
||||||
about: "Misskeyを見つけていただき、ありがとうございます。Misskeyは、地球で生まれた<b>分散マイクロブログSNS</b>です。Fediverse(様々なSNSで構成される宇宙)の中に存在するため、他のSNSと相互に繋がっています。暫し都会の喧騒から離れて、新しいインターネットにダイブしてみませんか。"
|
about: "Danke, dass Du Misskey gefunden hast. Misskey ist eine <b>dezentralisierte Microblogging-Plattform</b>, welche auf der ganzen Welt verteilt ist. Da es innerhalb es Fediversums existiert (ein Universum, in dem verschiedene Soziale Netzwerke organisiert sind), ist es unmittelbar mit anderen sozialen Netzwerken verbunden. Warum nimmst du dir nicht einmal eine Auszeit von dem Trubel der Stadt und tauchst in das neue Internet hinein?"
|
||||||
intro:
|
intro:
|
||||||
title: "Was ist Misskey?"
|
title: "Was ist Misskey?"
|
||||||
about: "Misskeyはオープンソースの<b>分散型マイクロブログSNS</b>です。リッチで高度にカスタマイズできるUI、投稿へのリアクション、ファイルを一元管理できるドライブなど、先進的な機能を揃えています。また、Fediverseと呼ばれるネットワークに接続できるため、他のSNSともやり取りできます。例えば、あなたが何か投稿すると、その投稿はMisskeyだけでなく他のSNSにも伝わります。ちょうどある惑星から他の惑星に電波を発信している様子をイメージしてください。"
|
about: "Misskeyはオープンソースの<b>分散型マイクロブログSNS</b>です。リッチで高度にカスタマイズできるUI、投稿へのリアクション、ファイルを一元管理できるドライブなど、先進的な機能を揃えています。また、Fediverseと呼ばれるネットワークに接続できるため、他のSNSともやり取りできます。例えば、あなたが何か投稿すると、その投稿はMisskeyだけでなく他のSNSにも伝わります。ちょうどある惑星から他の惑星に電波を発信している様子をイメージしてください。"
|
||||||
@ -25,9 +25,9 @@ common:
|
|||||||
application-authorization: "Autorisierte Anwendungen"
|
application-authorization: "Autorisierte Anwendungen"
|
||||||
close: "Schließen"
|
close: "Schließen"
|
||||||
do-not-copy-paste: "ここにコードを入力したり張り付けたりしないでください。アカウントが不正利用される可能性があります。"
|
do-not-copy-paste: "ここにコードを入力したり張り付けたりしないでください。アカウントが不正利用される可能性があります。"
|
||||||
load-more: "もっと読み込む"
|
load-more: "Mehr laden"
|
||||||
enter-password: "パスワードを入力してください"
|
enter-password: "Bitte Passwort eingeben"
|
||||||
2fa: "二段階認証"
|
2fa: "Zwei-Faktor-Authentifizierung"
|
||||||
got-it: "Verstanden!"
|
got-it: "Verstanden!"
|
||||||
customization-tips:
|
customization-tips:
|
||||||
title: "Anpassung-Tipps"
|
title: "Anpassung-Tipps"
|
||||||
@ -54,8 +54,8 @@ common:
|
|||||||
years_ago: "vor {} Jahr(en)"
|
years_ago: "vor {} Jahr(en)"
|
||||||
month-and-day: "{day}/{month}"
|
month-and-day: "{day}/{month}"
|
||||||
trash: "Papierkorb"
|
trash: "Papierkorb"
|
||||||
drive: "ドライブ"
|
drive: "Drive"
|
||||||
messaging: "トーク"
|
messaging: "Unterhaltungen"
|
||||||
weekday-short:
|
weekday-short:
|
||||||
sunday: "So"
|
sunday: "So"
|
||||||
monday: "Mo"
|
monday: "Mo"
|
||||||
@ -91,9 +91,9 @@ common:
|
|||||||
followers-desc: "Nur für diejenigen sichtbar, die dir folgen"
|
followers-desc: "Nur für diejenigen sichtbar, die dir folgen"
|
||||||
specified: "Direkt"
|
specified: "Direkt"
|
||||||
specified-desc: "Nur für bestimmte Benutzer posten"
|
specified-desc: "Nur für bestimmte Benutzer posten"
|
||||||
local-public: "公開 (ローカルのみ)"
|
local-public: "Öffentlich (nur lokal)"
|
||||||
local-home: "ホーム (ローカルのみ)"
|
local-home: "Home (nur lokal)"
|
||||||
local-followers: "フォロワー (ローカルのみ)"
|
local-followers: "Follower (nur lokal)"
|
||||||
note-placeholders:
|
note-placeholders:
|
||||||
a: "Was machst du gerade?"
|
a: "Was machst du gerade?"
|
||||||
b: "Was ist so passiert?"
|
b: "Was ist so passiert?"
|
||||||
@ -172,18 +172,18 @@ common:
|
|||||||
hashtags: "Hashtags"
|
hashtags: "Hashtags"
|
||||||
dev: "Fehler beim Erstellen der Applikation. Bitte versuche es erneut."
|
dev: "Fehler beim Erstellen der Applikation. Bitte versuche es erneut."
|
||||||
ai-chan-kawaii: "藍ちゃかわいい"
|
ai-chan-kawaii: "藍ちゃかわいい"
|
||||||
you: "あなた"
|
you: "Du"
|
||||||
auth/views/form.vue:
|
auth/views/form.vue:
|
||||||
share-access: "<i>{name}</i>があなたのアカウントにアクセスすることを許可しますか?"
|
share-access: "Erlaubst Du <i>{name}</i> auf deinen Account zuzugreifen?"
|
||||||
permission-ask: "このアプリは次の権限を要求しています:"
|
permission-ask: "Diese Applikation benötigt folgende Berechtigungen:"
|
||||||
account-read: "アカウントの情報を見る。"
|
account-read: "Accountinformationen anzeigen."
|
||||||
account-write: "アカウントの情報を操作する。"
|
account-write: "Accountinformationen bearbeiten."
|
||||||
note-write: "Senden."
|
note-write: "Senden."
|
||||||
like-write: "いいねしたりいいね解除する。"
|
like-write: "Auf Beiträge reagieren."
|
||||||
following-write: "フォローしたりフォロー解除する。"
|
following-write: "Folgen oder entfolgen."
|
||||||
drive-read: "ドライブを見る。"
|
drive-read: "ドライブを見る。"
|
||||||
drive-write: "ドライブを操作する。"
|
drive-write: "ドライブを操作する。"
|
||||||
notification-read: "通知を見る。"
|
notification-read: "Siehe deine Benachrichtigungen."
|
||||||
notification-write: "Benachrichtigungen verwalten."
|
notification-write: "Benachrichtigungen verwalten."
|
||||||
cancel: "Abbrechen"
|
cancel: "Abbrechen"
|
||||||
accept: "Zugriff erlauben."
|
accept: "Zugriff erlauben."
|
||||||
@ -509,6 +509,13 @@ common/views/components/profile-editor.vue:
|
|||||||
email-address: "メールアドレス"
|
email-address: "メールアドレス"
|
||||||
email-verified: "メールアドレスが確認されました"
|
email-verified: "メールアドレスが確認されました"
|
||||||
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
||||||
|
export: "エクスポート"
|
||||||
|
export-targets:
|
||||||
|
all-notes: "すべての投稿データ"
|
||||||
|
following-list: "フォロー"
|
||||||
|
mute-list: "ミュート"
|
||||||
|
blocking-list: "ブロック"
|
||||||
|
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||||
common/views/components/user-list-editor.vue:
|
common/views/components/user-list-editor.vue:
|
||||||
users: "ユーザー"
|
users: "ユーザー"
|
||||||
rename: "リスト名を変更"
|
rename: "リスト名を変更"
|
||||||
@ -1000,6 +1007,7 @@ admin/views/index.vue:
|
|||||||
announcements: "お知らせ"
|
announcements: "お知らせ"
|
||||||
hashtags: "ハッシュタグ"
|
hashtags: "ハッシュタグ"
|
||||||
abuse: "スパム報告"
|
abuse: "スパム報告"
|
||||||
|
queue: "ジョブキュー"
|
||||||
back-to-misskey: "Misskeyに戻る"
|
back-to-misskey: "Misskeyに戻る"
|
||||||
admin/views/dashboard.vue:
|
admin/views/dashboard.vue:
|
||||||
dashboard: "ダッシュボード"
|
dashboard: "ダッシュボード"
|
||||||
@ -1009,6 +1017,9 @@ admin/views/dashboard.vue:
|
|||||||
instances: "インスタンス"
|
instances: "インスタンス"
|
||||||
this-instance: "このインスタンス"
|
this-instance: "このインスタンス"
|
||||||
federated: "連合"
|
federated: "連合"
|
||||||
|
admin/views/queue.vue:
|
||||||
|
operation: "操作"
|
||||||
|
remove-all-jobs: "すべてのジョブをクリア"
|
||||||
admin/views/abuse.vue:
|
admin/views/abuse.vue:
|
||||||
title: "スパム報告"
|
title: "スパム報告"
|
||||||
target: "対象"
|
target: "対象"
|
||||||
@ -1175,6 +1186,7 @@ admin/views/users.vue:
|
|||||||
moderator: "モデレーター"
|
moderator: "モデレーター"
|
||||||
adminOrModerator: "管理者+モデレーター"
|
adminOrModerator: "管理者+モデレーター"
|
||||||
verified: "公式アカウント"
|
verified: "公式アカウント"
|
||||||
|
silenced: "サイレンス済み"
|
||||||
suspended: "凍結済み"
|
suspended: "凍結済み"
|
||||||
origin:
|
origin:
|
||||||
title: "オリジン"
|
title: "オリジン"
|
||||||
@ -1222,6 +1234,64 @@ admin/views/announcements.vue:
|
|||||||
removed: "削除しました"
|
removed: "削除しました"
|
||||||
admin/views/hashtags.vue:
|
admin/views/hashtags.vue:
|
||||||
hided-tags: "Hidden Tags"
|
hided-tags: "Hidden Tags"
|
||||||
|
admin/views/federation.vue:
|
||||||
|
federation: "連合"
|
||||||
|
host: "ホスト"
|
||||||
|
notes: "投稿"
|
||||||
|
users: "ユーザー"
|
||||||
|
following: "フォロー中"
|
||||||
|
followers: "フォロワー"
|
||||||
|
status: "ステータス"
|
||||||
|
latest-request-sent-at: "直近のリクエスト送信"
|
||||||
|
latest-request-received-at: "直近のリクエスト受信"
|
||||||
|
remove-all-following: "フォローを全解除"
|
||||||
|
remove-all-following-info: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。"
|
||||||
|
block: "ブロック"
|
||||||
|
marked-as-closed: "閉鎖されているとマーク"
|
||||||
|
lookup: "照会"
|
||||||
|
instances: "インスタンス"
|
||||||
|
instance-not-registered: "そのインスタンスは登録されていません"
|
||||||
|
sort: "ソート"
|
||||||
|
sorts:
|
||||||
|
caughtAtAsc: "登録日時が古い順"
|
||||||
|
caughtAtDesc: "登録日時が新しい順"
|
||||||
|
lastCommunicatedAtAsc: "最後にやり取りした日時が古い順"
|
||||||
|
lastCommunicatedAtDesc: "最後にやり取りした日時が新しい順"
|
||||||
|
notesAsc: "投稿が少ない順"
|
||||||
|
notesDesc: "投稿が多い順"
|
||||||
|
usersAsc: "ユーザーが少ない順"
|
||||||
|
usersDesc: "ユーザーが多い順"
|
||||||
|
followingAsc: "フォローが少ない順"
|
||||||
|
followingDesc: "フォローが多い順"
|
||||||
|
followersAsc: "フォロワーが少ない順"
|
||||||
|
followersDesc: "フォロワーが多い順"
|
||||||
|
driveUsageAsc: "ドライブ使用量が少ない順"
|
||||||
|
driveUsageDesc: "ドライブ使用量が多い順"
|
||||||
|
driveFilesAsc: "ドライブのファイル数が少ない順"
|
||||||
|
driveFilesDesc: "ドライブのファイル数が多い順"
|
||||||
|
state: "状態"
|
||||||
|
states:
|
||||||
|
all: "すべて"
|
||||||
|
blocked: "ブロック"
|
||||||
|
not-responding: "応答なし"
|
||||||
|
marked-as-closed: "閉鎖とマーク済み"
|
||||||
|
result-is-truncated: "上位{n}件を表示しています。"
|
||||||
|
charts: "チャート"
|
||||||
|
chart-srcs:
|
||||||
|
requests: "リクエスト"
|
||||||
|
users: "ユーザーの増減"
|
||||||
|
users-total: "ユーザーの積算"
|
||||||
|
notes: "投稿の増減"
|
||||||
|
notes-total: "投稿の積算"
|
||||||
|
ff: "フォロー/フォロワーの増減"
|
||||||
|
ff-total: "フォロー/フォロワーの積算"
|
||||||
|
drive-usage: "ドライブ使用量の増減"
|
||||||
|
drive-usage-total: "ドライブ使用量の積算"
|
||||||
|
drive-files: "ドライブファイル数の増減"
|
||||||
|
drive-files-total: "ドライブファイル数の積算"
|
||||||
|
chart-spans:
|
||||||
|
hour: "1時間ごと"
|
||||||
|
day: "1日ごと"
|
||||||
desktop/views/pages/welcome.vue:
|
desktop/views/pages/welcome.vue:
|
||||||
about: "詳しく..."
|
about: "詳しく..."
|
||||||
gotit: "わかった"
|
gotit: "わかった"
|
||||||
@ -1557,6 +1627,7 @@ deck/deck.tl-column.vue:
|
|||||||
is-media-view: "メディアビュー"
|
is-media-view: "メディアビュー"
|
||||||
edit: "オプション"
|
edit: "オプション"
|
||||||
deck/deck.user-column.vue:
|
deck/deck.user-column.vue:
|
||||||
|
follows-you: "フォローされています"
|
||||||
posts: "投稿"
|
posts: "投稿"
|
||||||
following: "フォロー"
|
following: "フォロー"
|
||||||
followers: "フォロワー"
|
followers: "フォロワー"
|
||||||
|
@ -509,6 +509,13 @@ common/views/components/profile-editor.vue:
|
|||||||
email-address: "Email Address"
|
email-address: "Email Address"
|
||||||
email-verified: "Your email has been verified."
|
email-verified: "Your email has been verified."
|
||||||
email-not-verified: "Email address is not confirmed. Please check your inbox."
|
email-not-verified: "Email address is not confirmed. Please check your inbox."
|
||||||
|
export: "Export"
|
||||||
|
export-targets:
|
||||||
|
all-notes: "All posted Notes"
|
||||||
|
following-list: "List of followers"
|
||||||
|
mute-list: "List of muted accounts"
|
||||||
|
blocking-list: "List of blocked accounts"
|
||||||
|
export-requested: "You have requested an export. This may take a while. After the export is complete, the resulting file will be added to the drive."
|
||||||
common/views/components/user-list-editor.vue:
|
common/views/components/user-list-editor.vue:
|
||||||
users: "User"
|
users: "User"
|
||||||
rename: "Rename list"
|
rename: "Rename list"
|
||||||
@ -1000,6 +1007,7 @@ admin/views/index.vue:
|
|||||||
announcements: "Announcements"
|
announcements: "Announcements"
|
||||||
hashtags: "Hashtags"
|
hashtags: "Hashtags"
|
||||||
abuse: "Abuse"
|
abuse: "Abuse"
|
||||||
|
queue: "Job Queue"
|
||||||
back-to-misskey: "Back to Misskey"
|
back-to-misskey: "Back to Misskey"
|
||||||
admin/views/dashboard.vue:
|
admin/views/dashboard.vue:
|
||||||
dashboard: "Dashboard"
|
dashboard: "Dashboard"
|
||||||
@ -1009,6 +1017,9 @@ admin/views/dashboard.vue:
|
|||||||
instances: "Instances"
|
instances: "Instances"
|
||||||
this-instance: "This instance"
|
this-instance: "This instance"
|
||||||
federated: "Federated"
|
federated: "Federated"
|
||||||
|
admin/views/queue.vue:
|
||||||
|
operation: "Action(s)"
|
||||||
|
remove-all-jobs: "Clear all queued jobs"
|
||||||
admin/views/abuse.vue:
|
admin/views/abuse.vue:
|
||||||
title: "Abuse"
|
title: "Abuse"
|
||||||
target: "Target"
|
target: "Target"
|
||||||
@ -1107,7 +1118,7 @@ admin/views/charts.vue:
|
|||||||
users: "The number of users: increase/decrease"
|
users: "The number of users: increase/decrease"
|
||||||
users-total: "Total users"
|
users-total: "Total users"
|
||||||
active-users: "Active users"
|
active-users: "Active users"
|
||||||
drive: "Capacity used as the storage: increase/decrease"
|
drive: "Increase and decrease in storage capacity use"
|
||||||
drive-total: "Total usage of Drive"
|
drive-total: "Total usage of Drive"
|
||||||
drive-files: "The number of files on the storage: increase/decrease"
|
drive-files: "The number of files on the storage: increase/decrease"
|
||||||
drive-files-total: "Total number of files on Drive"
|
drive-files-total: "Total number of files on Drive"
|
||||||
@ -1175,6 +1186,7 @@ admin/views/users.vue:
|
|||||||
moderator: "Moderator"
|
moderator: "Moderator"
|
||||||
adminOrModerator: "Admin/Moderator"
|
adminOrModerator: "Admin/Moderator"
|
||||||
verified: "Verified account"
|
verified: "Verified account"
|
||||||
|
silenced: "Already silenced"
|
||||||
suspended: "Suspended"
|
suspended: "Suspended"
|
||||||
origin:
|
origin:
|
||||||
title: "Origin"
|
title: "Origin"
|
||||||
@ -1222,6 +1234,64 @@ admin/views/announcements.vue:
|
|||||||
removed: "Deleted"
|
removed: "Deleted"
|
||||||
admin/views/hashtags.vue:
|
admin/views/hashtags.vue:
|
||||||
hided-tags: "Hidden Tags"
|
hided-tags: "Hidden Tags"
|
||||||
|
admin/views/federation.vue:
|
||||||
|
federation: "Federation"
|
||||||
|
host: "Host"
|
||||||
|
notes: "Notes"
|
||||||
|
users: "Users"
|
||||||
|
following: "Following"
|
||||||
|
followers: "Followers"
|
||||||
|
status: "Status"
|
||||||
|
latest-request-sent-at: "Time of last request sent"
|
||||||
|
latest-request-received-at: "Last request received at"
|
||||||
|
remove-all-following: "Withold all followers"
|
||||||
|
remove-all-following-info: "Unfollow all accounts from {host}. Please run this if the instance no longer exists."
|
||||||
|
block: "Block"
|
||||||
|
marked-as-closed: "Marked as closed"
|
||||||
|
lookup: "Look up"
|
||||||
|
instances: "Instances"
|
||||||
|
instance-not-registered: "The instance has not been discovered"
|
||||||
|
sort: "Sort by"
|
||||||
|
sorts:
|
||||||
|
caughtAtAsc: "Date of discovery (Ascending)"
|
||||||
|
caughtAtDesc: "Date of discovery (Descending)"
|
||||||
|
lastCommunicatedAtAsc: "The date and time of the older interactions"
|
||||||
|
lastCommunicatedAtDesc: "The date and time of the newer interactions"
|
||||||
|
notesAsc: "Order by least Notes posted"
|
||||||
|
notesDesc: "Order by most Notes posted"
|
||||||
|
usersAsc: "Less followers"
|
||||||
|
usersDesc: "More followers"
|
||||||
|
followingAsc: "Least followed"
|
||||||
|
followingDesc: "Has more followers"
|
||||||
|
followersAsc: "Sort by having less followers"
|
||||||
|
followersDesc: "Sort by the larger number of followers"
|
||||||
|
driveUsageAsc: "Least storage used"
|
||||||
|
driveUsageDesc: "Most storage used"
|
||||||
|
driveFilesAsc: "By the smallest number of files stored on Drive"
|
||||||
|
driveFilesDesc: "By the largest number of files stored on Drive"
|
||||||
|
state: "Status"
|
||||||
|
states:
|
||||||
|
all: "All"
|
||||||
|
blocked: "Blocked"
|
||||||
|
not-responding: "Without response"
|
||||||
|
marked-as-closed: "Marked as closed"
|
||||||
|
result-is-truncated: "Displaying the top {n} items."
|
||||||
|
charts: "Charts"
|
||||||
|
chart-srcs:
|
||||||
|
requests: "Requests"
|
||||||
|
users: "Increase, or decrease in the number of users"
|
||||||
|
users-total: "Total number of users"
|
||||||
|
notes: "Increase, or decrease in the number of notes"
|
||||||
|
notes-total: "Total number of notes"
|
||||||
|
ff: "Increase of followers"
|
||||||
|
ff-total: "Total number of follows accumulated"
|
||||||
|
drive-usage: "Increase and decrease in storage use"
|
||||||
|
drive-usage-total: "Total usage of the Drive"
|
||||||
|
drive-files: "Increase, or decrease in the number of files stored on Drive"
|
||||||
|
drive-files-total: "The number of files accumulated on Drive"
|
||||||
|
chart-spans:
|
||||||
|
hour: "Hourly"
|
||||||
|
day: "Daily"
|
||||||
desktop/views/pages/welcome.vue:
|
desktop/views/pages/welcome.vue:
|
||||||
about: "More details..."
|
about: "More details..."
|
||||||
gotit: "Got it!"
|
gotit: "Got it!"
|
||||||
@ -1557,6 +1627,7 @@ deck/deck.tl-column.vue:
|
|||||||
is-media-view: "Media view"
|
is-media-view: "Media view"
|
||||||
edit: "Options"
|
edit: "Options"
|
||||||
deck/deck.user-column.vue:
|
deck/deck.user-column.vue:
|
||||||
|
follows-you: "Follows you"
|
||||||
posts: "Posts"
|
posts: "Posts"
|
||||||
following: "Following"
|
following: "Following"
|
||||||
followers: "Followers"
|
followers: "Followers"
|
||||||
|
@ -509,6 +509,13 @@ common/views/components/profile-editor.vue:
|
|||||||
email-address: "Correo electrónico"
|
email-address: "Correo electrónico"
|
||||||
email-verified: "メールアドレスが確認されました"
|
email-verified: "メールアドレスが確認されました"
|
||||||
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
||||||
|
export: "エクスポート"
|
||||||
|
export-targets:
|
||||||
|
all-notes: "すべての投稿データ"
|
||||||
|
following-list: "フォロー"
|
||||||
|
mute-list: "ミュート"
|
||||||
|
blocking-list: "ブロック"
|
||||||
|
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||||
common/views/components/user-list-editor.vue:
|
common/views/components/user-list-editor.vue:
|
||||||
users: "Usuarios"
|
users: "Usuarios"
|
||||||
rename: "リスト名を変更"
|
rename: "リスト名を変更"
|
||||||
@ -1000,6 +1007,7 @@ admin/views/index.vue:
|
|||||||
announcements: "お知らせ"
|
announcements: "お知らせ"
|
||||||
hashtags: "Hashtags"
|
hashtags: "Hashtags"
|
||||||
abuse: "スパム報告"
|
abuse: "スパム報告"
|
||||||
|
queue: "ジョブキュー"
|
||||||
back-to-misskey: "Volver a Misskey"
|
back-to-misskey: "Volver a Misskey"
|
||||||
admin/views/dashboard.vue:
|
admin/views/dashboard.vue:
|
||||||
dashboard: "Panel de Control"
|
dashboard: "Panel de Control"
|
||||||
@ -1009,6 +1017,9 @@ admin/views/dashboard.vue:
|
|||||||
instances: "Instancias"
|
instances: "Instancias"
|
||||||
this-instance: "Esta instancia"
|
this-instance: "Esta instancia"
|
||||||
federated: "連合"
|
federated: "連合"
|
||||||
|
admin/views/queue.vue:
|
||||||
|
operation: "操作"
|
||||||
|
remove-all-jobs: "すべてのジョブをクリア"
|
||||||
admin/views/abuse.vue:
|
admin/views/abuse.vue:
|
||||||
title: "スパム報告"
|
title: "スパム報告"
|
||||||
target: "対象"
|
target: "対象"
|
||||||
@ -1175,6 +1186,7 @@ admin/views/users.vue:
|
|||||||
moderator: "モデレーター"
|
moderator: "モデレーター"
|
||||||
adminOrModerator: "管理者+モデレーター"
|
adminOrModerator: "管理者+モデレーター"
|
||||||
verified: "公式アカウント"
|
verified: "公式アカウント"
|
||||||
|
silenced: "サイレンス済み"
|
||||||
suspended: "凍結済み"
|
suspended: "凍結済み"
|
||||||
origin:
|
origin:
|
||||||
title: "オリジン"
|
title: "オリジン"
|
||||||
@ -1222,6 +1234,64 @@ admin/views/announcements.vue:
|
|||||||
removed: "削除しました"
|
removed: "削除しました"
|
||||||
admin/views/hashtags.vue:
|
admin/views/hashtags.vue:
|
||||||
hided-tags: "Hidden Tags"
|
hided-tags: "Hidden Tags"
|
||||||
|
admin/views/federation.vue:
|
||||||
|
federation: "連合"
|
||||||
|
host: "ホスト"
|
||||||
|
notes: "投稿"
|
||||||
|
users: "ユーザー"
|
||||||
|
following: "フォロー中"
|
||||||
|
followers: "フォロワー"
|
||||||
|
status: "ステータス"
|
||||||
|
latest-request-sent-at: "直近のリクエスト送信"
|
||||||
|
latest-request-received-at: "直近のリクエスト受信"
|
||||||
|
remove-all-following: "フォローを全解除"
|
||||||
|
remove-all-following-info: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。"
|
||||||
|
block: "ブロック"
|
||||||
|
marked-as-closed: "閉鎖されているとマーク"
|
||||||
|
lookup: "照会"
|
||||||
|
instances: "インスタンス"
|
||||||
|
instance-not-registered: "そのインスタンスは登録されていません"
|
||||||
|
sort: "ソート"
|
||||||
|
sorts:
|
||||||
|
caughtAtAsc: "登録日時が古い順"
|
||||||
|
caughtAtDesc: "登録日時が新しい順"
|
||||||
|
lastCommunicatedAtAsc: "最後にやり取りした日時が古い順"
|
||||||
|
lastCommunicatedAtDesc: "最後にやり取りした日時が新しい順"
|
||||||
|
notesAsc: "投稿が少ない順"
|
||||||
|
notesDesc: "投稿が多い順"
|
||||||
|
usersAsc: "ユーザーが少ない順"
|
||||||
|
usersDesc: "ユーザーが多い順"
|
||||||
|
followingAsc: "フォローが少ない順"
|
||||||
|
followingDesc: "フォローが多い順"
|
||||||
|
followersAsc: "フォロワーが少ない順"
|
||||||
|
followersDesc: "フォロワーが多い順"
|
||||||
|
driveUsageAsc: "ドライブ使用量が少ない順"
|
||||||
|
driveUsageDesc: "ドライブ使用量が多い順"
|
||||||
|
driveFilesAsc: "ドライブのファイル数が少ない順"
|
||||||
|
driveFilesDesc: "ドライブのファイル数が多い順"
|
||||||
|
state: "状態"
|
||||||
|
states:
|
||||||
|
all: "すべて"
|
||||||
|
blocked: "ブロック"
|
||||||
|
not-responding: "応答なし"
|
||||||
|
marked-as-closed: "閉鎖とマーク済み"
|
||||||
|
result-is-truncated: "上位{n}件を表示しています。"
|
||||||
|
charts: "チャート"
|
||||||
|
chart-srcs:
|
||||||
|
requests: "リクエスト"
|
||||||
|
users: "ユーザーの増減"
|
||||||
|
users-total: "ユーザーの積算"
|
||||||
|
notes: "投稿の増減"
|
||||||
|
notes-total: "投稿の積算"
|
||||||
|
ff: "フォロー/フォロワーの増減"
|
||||||
|
ff-total: "フォロー/フォロワーの積算"
|
||||||
|
drive-usage: "ドライブ使用量の増減"
|
||||||
|
drive-usage-total: "ドライブ使用量の積算"
|
||||||
|
drive-files: "ドライブファイル数の増減"
|
||||||
|
drive-files-total: "ドライブファイル数の積算"
|
||||||
|
chart-spans:
|
||||||
|
hour: "1時間ごと"
|
||||||
|
day: "1日ごと"
|
||||||
desktop/views/pages/welcome.vue:
|
desktop/views/pages/welcome.vue:
|
||||||
about: "詳しく..."
|
about: "詳しく..."
|
||||||
gotit: "わかった"
|
gotit: "わかった"
|
||||||
@ -1557,6 +1627,7 @@ deck/deck.tl-column.vue:
|
|||||||
is-media-view: "メディアビュー"
|
is-media-view: "メディアビュー"
|
||||||
edit: "オプション"
|
edit: "オプション"
|
||||||
deck/deck.user-column.vue:
|
deck/deck.user-column.vue:
|
||||||
|
follows-you: "フォローされています"
|
||||||
posts: "投稿"
|
posts: "投稿"
|
||||||
following: "フォロー"
|
following: "フォロー"
|
||||||
followers: "フォロワー"
|
followers: "フォロワー"
|
||||||
|
@ -345,8 +345,8 @@ common/views/components/note-menu.vue:
|
|||||||
copy-link: "Copier le lien"
|
copy-link: "Copier le lien"
|
||||||
favorite: "Mettre cette note en favoris"
|
favorite: "Mettre cette note en favoris"
|
||||||
unfavorite: "Retirer des favoris"
|
unfavorite: "Retirer des favoris"
|
||||||
watch: "ウォッチ"
|
watch: "Surveiller"
|
||||||
unwatch: "ウォッチ解除"
|
unwatch: "Ne plus surveiller"
|
||||||
pin: "Épingler sur votre profil"
|
pin: "Épingler sur votre profil"
|
||||||
unpin: "Désépingler"
|
unpin: "Désépingler"
|
||||||
delete: "Supprimer"
|
delete: "Supprimer"
|
||||||
@ -363,10 +363,10 @@ common/views/components/user-menu.vue:
|
|||||||
report-abuse: "Signaler un abus"
|
report-abuse: "Signaler un abus"
|
||||||
report-abuse-detail: "Détail du signalement"
|
report-abuse-detail: "Détail du signalement"
|
||||||
report-abuse-reported: "Transmit à l’administrateur. Merci de votre collaboration."
|
report-abuse-reported: "Transmit à l’administrateur. Merci de votre collaboration."
|
||||||
silence: "サイレンス"
|
silence: "Mettre en sourdine"
|
||||||
unsilence: "サイレンス解除"
|
unsilence: "Enlever la sourdine"
|
||||||
suspend: "Suspendre"
|
suspend: "Suspendre"
|
||||||
unsuspend: "凍結解除"
|
unsuspend: "Ne plus suspendre"
|
||||||
common/views/components/poll.vue:
|
common/views/components/poll.vue:
|
||||||
vote-to: "Voter pour '{}'"
|
vote-to: "Voter pour '{}'"
|
||||||
vote-count: "{} votes"
|
vote-count: "{} votes"
|
||||||
@ -509,6 +509,13 @@ common/views/components/profile-editor.vue:
|
|||||||
email-address: "Adresse de courrier électronique"
|
email-address: "Adresse de courrier électronique"
|
||||||
email-verified: "L’adresse du courrier électronique a été vérifiée."
|
email-verified: "L’adresse du courrier électronique a été vérifiée."
|
||||||
email-not-verified: "Adresse de courriel n’est pas confirmée. Veuillez vérifier votre boite de réception."
|
email-not-verified: "Adresse de courriel n’est pas confirmée. Veuillez vérifier votre boite de réception."
|
||||||
|
export: "Exporter"
|
||||||
|
export-targets:
|
||||||
|
all-notes: "Toutes les notes publiées"
|
||||||
|
following-list: "Liste des abonnements"
|
||||||
|
mute-list: "Liste des comptes mis en sourdine"
|
||||||
|
blocking-list: "Liste des comptes bloqués"
|
||||||
|
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||||
common/views/components/user-list-editor.vue:
|
common/views/components/user-list-editor.vue:
|
||||||
users: "Utilisateur·rice"
|
users: "Utilisateur·rice"
|
||||||
rename: "Renommer la liste"
|
rename: "Renommer la liste"
|
||||||
@ -1000,6 +1007,7 @@ admin/views/index.vue:
|
|||||||
announcements: "Annonces"
|
announcements: "Annonces"
|
||||||
hashtags: "Hashtags"
|
hashtags: "Hashtags"
|
||||||
abuse: "Abus"
|
abuse: "Abus"
|
||||||
|
queue: "File d’attente"
|
||||||
back-to-misskey: "Retour vers Misskey"
|
back-to-misskey: "Retour vers Misskey"
|
||||||
admin/views/dashboard.vue:
|
admin/views/dashboard.vue:
|
||||||
dashboard: "Tableau de bord"
|
dashboard: "Tableau de bord"
|
||||||
@ -1009,6 +1017,9 @@ admin/views/dashboard.vue:
|
|||||||
instances: "Instances"
|
instances: "Instances"
|
||||||
this-instance: "Cette instance"
|
this-instance: "Cette instance"
|
||||||
federated: "Fédérées"
|
federated: "Fédérées"
|
||||||
|
admin/views/queue.vue:
|
||||||
|
operation: "Action(s)"
|
||||||
|
remove-all-jobs: "すべてのジョブをクリア"
|
||||||
admin/views/abuse.vue:
|
admin/views/abuse.vue:
|
||||||
title: "Abus"
|
title: "Abus"
|
||||||
target: "Cible"
|
target: "Cible"
|
||||||
@ -1150,8 +1161,8 @@ admin/views/users.vue:
|
|||||||
unsuspend: "Suspension levée"
|
unsuspend: "Suspension levée"
|
||||||
unsuspend-confirm: "Souhaiteriez-vous ne plus suspendre ce compte ?"
|
unsuspend-confirm: "Souhaiteriez-vous ne plus suspendre ce compte ?"
|
||||||
unsuspended: "La suspension de l’utilisateur a été levée avec succès"
|
unsuspended: "La suspension de l’utilisateur a été levée avec succès"
|
||||||
make-silence: "サイレンス"
|
make-silence: "Mettre en sourdine"
|
||||||
unmake-silence: "サイレンスの解除"
|
unmake-silence: "Enlever la sourdine"
|
||||||
verify: "Vérification du compte"
|
verify: "Vérification du compte"
|
||||||
verify-confirm: "Souhaiteriez-vous rendre votre compte comme étant un compte vérifié ?"
|
verify-confirm: "Souhaiteriez-vous rendre votre compte comme étant un compte vérifié ?"
|
||||||
verified: "Le compte a été vérifié"
|
verified: "Le compte a été vérifié"
|
||||||
@ -1175,6 +1186,7 @@ admin/views/users.vue:
|
|||||||
moderator: "Modérateur"
|
moderator: "Modérateur"
|
||||||
adminOrModerator: "Administrateur/Modérateur"
|
adminOrModerator: "Administrateur/Modérateur"
|
||||||
verified: "Compte vérifié"
|
verified: "Compte vérifié"
|
||||||
|
silenced: "サイレンス済み"
|
||||||
suspended: "Suspendu"
|
suspended: "Suspendu"
|
||||||
origin:
|
origin:
|
||||||
title: "Origine"
|
title: "Origine"
|
||||||
@ -1222,6 +1234,64 @@ admin/views/announcements.vue:
|
|||||||
removed: "Supprimé"
|
removed: "Supprimé"
|
||||||
admin/views/hashtags.vue:
|
admin/views/hashtags.vue:
|
||||||
hided-tags: "Tags cachés"
|
hided-tags: "Tags cachés"
|
||||||
|
admin/views/federation.vue:
|
||||||
|
federation: "Fédération"
|
||||||
|
host: "Hôte"
|
||||||
|
notes: "Notes"
|
||||||
|
users: "Utilisateur·rice·s"
|
||||||
|
following: "Abonnements"
|
||||||
|
followers: "Abonné·e·s"
|
||||||
|
status: "Statuts"
|
||||||
|
latest-request-sent-at: "Dernière requête envoyée"
|
||||||
|
latest-request-received-at: "Dernière requête reçue"
|
||||||
|
remove-all-following: "フォローを全解除"
|
||||||
|
remove-all-following-info: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。"
|
||||||
|
block: "ブロック"
|
||||||
|
marked-as-closed: "閉鎖されているとマーク"
|
||||||
|
lookup: "照会"
|
||||||
|
instances: "Instances"
|
||||||
|
instance-not-registered: "そのインスタンスは登録されていません"
|
||||||
|
sort: "Trier par"
|
||||||
|
sorts:
|
||||||
|
caughtAtAsc: "Date d’inscription (Ascendant)"
|
||||||
|
caughtAtDesc: "Date d’inscription (Descendant)"
|
||||||
|
lastCommunicatedAtAsc: "最後にやり取りした日時が古い順"
|
||||||
|
lastCommunicatedAtDesc: "最後にやり取りした日時が新しい順"
|
||||||
|
notesAsc: "投稿が少ない順"
|
||||||
|
notesDesc: "Description des notes"
|
||||||
|
usersAsc: "ユーザーが少ない順"
|
||||||
|
usersDesc: "ユーザーが多い順"
|
||||||
|
followingAsc: "Les moins suivies"
|
||||||
|
followingDesc: "フォローが多い順"
|
||||||
|
followersAsc: "Ayant le moins d'abonné·e·s"
|
||||||
|
followersDesc: "Ayant le plus d'abonné·e·s"
|
||||||
|
driveUsageAsc: "ドライブ使用量が少ない順"
|
||||||
|
driveUsageDesc: "ドライブ使用量が多い順"
|
||||||
|
driveFilesAsc: "ドライブのファイル数が少ない順"
|
||||||
|
driveFilesDesc: "ドライブのファイル数が多い順"
|
||||||
|
state: "状態"
|
||||||
|
states:
|
||||||
|
all: "すべて"
|
||||||
|
blocked: "ブロック"
|
||||||
|
not-responding: "応答なし"
|
||||||
|
marked-as-closed: "閉鎖とマーク済み"
|
||||||
|
result-is-truncated: "上位{n}件を表示しています。"
|
||||||
|
charts: "チャート"
|
||||||
|
chart-srcs:
|
||||||
|
requests: "リクエスト"
|
||||||
|
users: "ユーザーの増減"
|
||||||
|
users-total: "ユーザーの積算"
|
||||||
|
notes: "投稿の増減"
|
||||||
|
notes-total: "投稿の積算"
|
||||||
|
ff: "フォロー/フォロワーの増減"
|
||||||
|
ff-total: "フォロー/フォロワーの積算"
|
||||||
|
drive-usage: "ドライブ使用量の増減"
|
||||||
|
drive-usage-total: "ドライブ使用量の積算"
|
||||||
|
drive-files: "ドライブファイル数の増減"
|
||||||
|
drive-files-total: "ドライブファイル数の積算"
|
||||||
|
chart-spans:
|
||||||
|
hour: "1時間ごと"
|
||||||
|
day: "1日ごと"
|
||||||
desktop/views/pages/welcome.vue:
|
desktop/views/pages/welcome.vue:
|
||||||
about: "à propos"
|
about: "à propos"
|
||||||
gotit: "J'ai compris !"
|
gotit: "J'ai compris !"
|
||||||
@ -1557,6 +1627,7 @@ deck/deck.tl-column.vue:
|
|||||||
is-media-view: "Vue média"
|
is-media-view: "Vue média"
|
||||||
edit: "Option"
|
edit: "Option"
|
||||||
deck/deck.user-column.vue:
|
deck/deck.user-column.vue:
|
||||||
|
follows-you: "フォローされています"
|
||||||
posts: "Notes"
|
posts: "Notes"
|
||||||
following: "Suit"
|
following: "Suit"
|
||||||
followers: "Abonnés"
|
followers: "Abonnés"
|
||||||
|
6
locales/index.d.ts
vendored
6
locales/index.d.ts
vendored
@ -1,5 +1,3 @@
|
|||||||
type Locale = { [key: string]: string };
|
declare const locales: { [lang: string]: any };
|
||||||
|
|
||||||
declare const locales: { [lang: string]: Locale };
|
export = locales;
|
||||||
|
|
||||||
export default locales;
|
|
||||||
|
@ -509,6 +509,13 @@ common/views/components/profile-editor.vue:
|
|||||||
email-address: "メールアドレス"
|
email-address: "メールアドレス"
|
||||||
email-verified: "メールアドレスが確認されました"
|
email-verified: "メールアドレスが確認されました"
|
||||||
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
||||||
|
export: "エクスポート"
|
||||||
|
export-targets:
|
||||||
|
all-notes: "すべての投稿データ"
|
||||||
|
following-list: "フォロー"
|
||||||
|
mute-list: "ミュート"
|
||||||
|
blocking-list: "ブロック"
|
||||||
|
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||||
common/views/components/user-list-editor.vue:
|
common/views/components/user-list-editor.vue:
|
||||||
users: "ユーザー"
|
users: "ユーザー"
|
||||||
rename: "リスト名を変更"
|
rename: "リスト名を変更"
|
||||||
@ -1000,6 +1007,7 @@ admin/views/index.vue:
|
|||||||
announcements: "お知らせ"
|
announcements: "お知らせ"
|
||||||
hashtags: "ハッシュタグ"
|
hashtags: "ハッシュタグ"
|
||||||
abuse: "スパム報告"
|
abuse: "スパム報告"
|
||||||
|
queue: "ジョブキュー"
|
||||||
back-to-misskey: "Misskeyに戻る"
|
back-to-misskey: "Misskeyに戻る"
|
||||||
admin/views/dashboard.vue:
|
admin/views/dashboard.vue:
|
||||||
dashboard: "ダッシュボード"
|
dashboard: "ダッシュボード"
|
||||||
@ -1009,6 +1017,9 @@ admin/views/dashboard.vue:
|
|||||||
instances: "インスタンス"
|
instances: "インスタンス"
|
||||||
this-instance: "このインスタンス"
|
this-instance: "このインスタンス"
|
||||||
federated: "連合"
|
federated: "連合"
|
||||||
|
admin/views/queue.vue:
|
||||||
|
operation: "操作"
|
||||||
|
remove-all-jobs: "すべてのジョブをクリア"
|
||||||
admin/views/abuse.vue:
|
admin/views/abuse.vue:
|
||||||
title: "スパム報告"
|
title: "スパム報告"
|
||||||
target: "対象"
|
target: "対象"
|
||||||
@ -1175,6 +1186,7 @@ admin/views/users.vue:
|
|||||||
moderator: "モデレーター"
|
moderator: "モデレーター"
|
||||||
adminOrModerator: "管理者+モデレーター"
|
adminOrModerator: "管理者+モデレーター"
|
||||||
verified: "公式アカウント"
|
verified: "公式アカウント"
|
||||||
|
silenced: "サイレンス済み"
|
||||||
suspended: "凍結済み"
|
suspended: "凍結済み"
|
||||||
origin:
|
origin:
|
||||||
title: "オリジン"
|
title: "オリジン"
|
||||||
@ -1222,6 +1234,64 @@ admin/views/announcements.vue:
|
|||||||
removed: "削除しました"
|
removed: "削除しました"
|
||||||
admin/views/hashtags.vue:
|
admin/views/hashtags.vue:
|
||||||
hided-tags: "Hidden Tags"
|
hided-tags: "Hidden Tags"
|
||||||
|
admin/views/federation.vue:
|
||||||
|
federation: "連合"
|
||||||
|
host: "ホスト"
|
||||||
|
notes: "投稿"
|
||||||
|
users: "ユーザー"
|
||||||
|
following: "フォロー中"
|
||||||
|
followers: "フォロワー"
|
||||||
|
status: "ステータス"
|
||||||
|
latest-request-sent-at: "直近のリクエスト送信"
|
||||||
|
latest-request-received-at: "直近のリクエスト受信"
|
||||||
|
remove-all-following: "フォローを全解除"
|
||||||
|
remove-all-following-info: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。"
|
||||||
|
block: "ブロック"
|
||||||
|
marked-as-closed: "閉鎖されているとマーク"
|
||||||
|
lookup: "照会"
|
||||||
|
instances: "インスタンス"
|
||||||
|
instance-not-registered: "そのインスタンスは登録されていません"
|
||||||
|
sort: "ソート"
|
||||||
|
sorts:
|
||||||
|
caughtAtAsc: "登録日時が古い順"
|
||||||
|
caughtAtDesc: "登録日時が新しい順"
|
||||||
|
lastCommunicatedAtAsc: "最後にやり取りした日時が古い順"
|
||||||
|
lastCommunicatedAtDesc: "最後にやり取りした日時が新しい順"
|
||||||
|
notesAsc: "投稿が少ない順"
|
||||||
|
notesDesc: "投稿が多い順"
|
||||||
|
usersAsc: "ユーザーが少ない順"
|
||||||
|
usersDesc: "ユーザーが多い順"
|
||||||
|
followingAsc: "フォローが少ない順"
|
||||||
|
followingDesc: "フォローが多い順"
|
||||||
|
followersAsc: "フォロワーが少ない順"
|
||||||
|
followersDesc: "フォロワーが多い順"
|
||||||
|
driveUsageAsc: "ドライブ使用量が少ない順"
|
||||||
|
driveUsageDesc: "ドライブ使用量が多い順"
|
||||||
|
driveFilesAsc: "ドライブのファイル数が少ない順"
|
||||||
|
driveFilesDesc: "ドライブのファイル数が多い順"
|
||||||
|
state: "状態"
|
||||||
|
states:
|
||||||
|
all: "すべて"
|
||||||
|
blocked: "ブロック"
|
||||||
|
not-responding: "応答なし"
|
||||||
|
marked-as-closed: "閉鎖とマーク済み"
|
||||||
|
result-is-truncated: "上位{n}件を表示しています。"
|
||||||
|
charts: "チャート"
|
||||||
|
chart-srcs:
|
||||||
|
requests: "リクエスト"
|
||||||
|
users: "ユーザーの増減"
|
||||||
|
users-total: "ユーザーの積算"
|
||||||
|
notes: "投稿の増減"
|
||||||
|
notes-total: "投稿の積算"
|
||||||
|
ff: "フォロー/フォロワーの増減"
|
||||||
|
ff-total: "フォロー/フォロワーの積算"
|
||||||
|
drive-usage: "ドライブ使用量の増減"
|
||||||
|
drive-usage-total: "ドライブ使用量の積算"
|
||||||
|
drive-files: "ドライブファイル数の増減"
|
||||||
|
drive-files-total: "ドライブファイル数の積算"
|
||||||
|
chart-spans:
|
||||||
|
hour: "1時間ごと"
|
||||||
|
day: "1日ごと"
|
||||||
desktop/views/pages/welcome.vue:
|
desktop/views/pages/welcome.vue:
|
||||||
about: "詳しく..."
|
about: "詳しく..."
|
||||||
gotit: "わかった"
|
gotit: "わかった"
|
||||||
@ -1557,6 +1627,7 @@ deck/deck.tl-column.vue:
|
|||||||
is-media-view: "メディアビュー"
|
is-media-view: "メディアビュー"
|
||||||
edit: "オプション"
|
edit: "オプション"
|
||||||
deck/deck.user-column.vue:
|
deck/deck.user-column.vue:
|
||||||
|
follows-you: "フォローされています"
|
||||||
posts: "投稿"
|
posts: "投稿"
|
||||||
following: "フォロー"
|
following: "フォロー"
|
||||||
followers: "フォロワー"
|
followers: "フォロワー"
|
||||||
|
@ -558,7 +558,11 @@ common/views/components/profile-editor.vue:
|
|||||||
email-verified: "メールアドレスが確認されました"
|
email-verified: "メールアドレスが確認されました"
|
||||||
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
||||||
export: "エクスポート"
|
export: "エクスポート"
|
||||||
export-notes: "すべての投稿のエクスポート"
|
export-targets:
|
||||||
|
all-notes: "すべての投稿データ"
|
||||||
|
following-list: "フォロー"
|
||||||
|
mute-list: "ミュート"
|
||||||
|
blocking-list: "ブロック"
|
||||||
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||||
|
|
||||||
common/views/components/user-list-editor.vue:
|
common/views/components/user-list-editor.vue:
|
||||||
@ -1129,6 +1133,7 @@ admin/views/index.vue:
|
|||||||
announcements: "お知らせ"
|
announcements: "お知らせ"
|
||||||
hashtags: "ハッシュタグ"
|
hashtags: "ハッシュタグ"
|
||||||
abuse: "スパム報告"
|
abuse: "スパム報告"
|
||||||
|
queue: "ジョブキュー"
|
||||||
back-to-misskey: "Misskeyに戻る"
|
back-to-misskey: "Misskeyに戻る"
|
||||||
|
|
||||||
admin/views/dashboard.vue:
|
admin/views/dashboard.vue:
|
||||||
@ -1140,6 +1145,10 @@ admin/views/dashboard.vue:
|
|||||||
this-instance: "このインスタンス"
|
this-instance: "このインスタンス"
|
||||||
federated: "連合"
|
federated: "連合"
|
||||||
|
|
||||||
|
admin/views/queue.vue:
|
||||||
|
operation: "操作"
|
||||||
|
remove-all-jobs: "すべてのジョブをクリア"
|
||||||
|
|
||||||
admin/views/abuse.vue:
|
admin/views/abuse.vue:
|
||||||
title: "スパム報告"
|
title: "スパム報告"
|
||||||
target: "対象"
|
target: "対象"
|
||||||
@ -1310,6 +1319,7 @@ admin/views/users.vue:
|
|||||||
moderator: "モデレーター"
|
moderator: "モデレーター"
|
||||||
adminOrModerator: "管理者+モデレーター"
|
adminOrModerator: "管理者+モデレーター"
|
||||||
verified: "公式アカウント"
|
verified: "公式アカウント"
|
||||||
|
silenced: "サイレンス済み"
|
||||||
suspended: "凍結済み"
|
suspended: "凍結済み"
|
||||||
origin:
|
origin:
|
||||||
title: "オリジン"
|
title: "オリジン"
|
||||||
@ -1362,6 +1372,65 @@ admin/views/announcements.vue:
|
|||||||
admin/views/hashtags.vue:
|
admin/views/hashtags.vue:
|
||||||
hided-tags: "Hidden Tags"
|
hided-tags: "Hidden Tags"
|
||||||
|
|
||||||
|
admin/views/federation.vue:
|
||||||
|
federation: "連合"
|
||||||
|
host: "ホスト"
|
||||||
|
notes: "投稿"
|
||||||
|
users: "ユーザー"
|
||||||
|
following: "フォロー中"
|
||||||
|
followers: "フォロワー"
|
||||||
|
status: "ステータス"
|
||||||
|
latest-request-sent-at: "直近のリクエスト送信"
|
||||||
|
latest-request-received-at: "直近のリクエスト受信"
|
||||||
|
remove-all-following: "フォローを全解除"
|
||||||
|
remove-all-following-info: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。"
|
||||||
|
block: "ブロック"
|
||||||
|
marked-as-closed: "閉鎖されているとマーク"
|
||||||
|
lookup: "照会"
|
||||||
|
instances: "インスタンス"
|
||||||
|
instance-not-registered: "そのインスタンスは登録されていません"
|
||||||
|
sort: "ソート"
|
||||||
|
sorts:
|
||||||
|
caughtAtAsc: "登録日時が古い順"
|
||||||
|
caughtAtDesc: "登録日時が新しい順"
|
||||||
|
lastCommunicatedAtAsc: "最後にやり取りした日時が古い順"
|
||||||
|
lastCommunicatedAtDesc: "最後にやり取りした日時が新しい順"
|
||||||
|
notesAsc: "投稿が少ない順"
|
||||||
|
notesDesc: "投稿が多い順"
|
||||||
|
usersAsc: "ユーザーが少ない順"
|
||||||
|
usersDesc: "ユーザーが多い順"
|
||||||
|
followingAsc: "フォローが少ない順"
|
||||||
|
followingDesc: "フォローが多い順"
|
||||||
|
followersAsc: "フォロワーが少ない順"
|
||||||
|
followersDesc: "フォロワーが多い順"
|
||||||
|
driveUsageAsc: "ドライブ使用量が少ない順"
|
||||||
|
driveUsageDesc: "ドライブ使用量が多い順"
|
||||||
|
driveFilesAsc: "ドライブのファイル数が少ない順"
|
||||||
|
driveFilesDesc: "ドライブのファイル数が多い順"
|
||||||
|
state: "状態"
|
||||||
|
states:
|
||||||
|
all: "すべて"
|
||||||
|
blocked: "ブロック"
|
||||||
|
not-responding: "応答なし"
|
||||||
|
marked-as-closed: "閉鎖とマーク済み"
|
||||||
|
result-is-truncated: "上位{n}件を表示しています。"
|
||||||
|
charts: "チャート"
|
||||||
|
chart-srcs:
|
||||||
|
requests: "リクエスト"
|
||||||
|
users: "ユーザーの増減"
|
||||||
|
users-total: "ユーザーの積算"
|
||||||
|
notes: "投稿の増減"
|
||||||
|
notes-total: "投稿の積算"
|
||||||
|
ff: "フォロー/フォロワーの増減"
|
||||||
|
ff-total: "フォロー/フォロワーの積算"
|
||||||
|
drive-usage: "ドライブ使用量の増減"
|
||||||
|
drive-usage-total: "ドライブ使用量の積算"
|
||||||
|
drive-files: "ドライブファイル数の増減"
|
||||||
|
drive-files-total: "ドライブファイル数の積算"
|
||||||
|
chart-spans:
|
||||||
|
hour: "1時間ごと"
|
||||||
|
day: "1日ごと"
|
||||||
|
|
||||||
desktop/views/pages/welcome.vue:
|
desktop/views/pages/welcome.vue:
|
||||||
about: "詳しく..."
|
about: "詳しく..."
|
||||||
gotit: "わかった"
|
gotit: "わかった"
|
||||||
@ -1765,6 +1834,7 @@ deck/deck.tl-column.vue:
|
|||||||
edit: "オプション"
|
edit: "オプション"
|
||||||
|
|
||||||
deck/deck.user-column.vue:
|
deck/deck.user-column.vue:
|
||||||
|
follows-you: "フォローされています"
|
||||||
posts: "投稿"
|
posts: "投稿"
|
||||||
following: "フォロー"
|
following: "フォロー"
|
||||||
followers: "フォロワー"
|
followers: "フォロワー"
|
||||||
|
@ -509,6 +509,13 @@ common/views/components/profile-editor.vue:
|
|||||||
email-address: "メールアドレス"
|
email-address: "メールアドレス"
|
||||||
email-verified: "このメールアドレスOKや!"
|
email-verified: "このメールアドレスOKや!"
|
||||||
email-not-verified: "メールアドレスが確認されとらん。メールボックスもっぺん見てくれへん?"
|
email-not-verified: "メールアドレスが確認されとらん。メールボックスもっぺん見てくれへん?"
|
||||||
|
export: "エクスポート"
|
||||||
|
export-targets:
|
||||||
|
all-notes: "すべての投稿データ"
|
||||||
|
following-list: "フォロー"
|
||||||
|
mute-list: "ミュート"
|
||||||
|
blocking-list: "ブロック"
|
||||||
|
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||||
common/views/components/user-list-editor.vue:
|
common/views/components/user-list-editor.vue:
|
||||||
users: "ユーザー"
|
users: "ユーザー"
|
||||||
rename: "リスト名を変更"
|
rename: "リスト名を変更"
|
||||||
@ -1000,6 +1007,7 @@ admin/views/index.vue:
|
|||||||
announcements: "知っといてや"
|
announcements: "知っといてや"
|
||||||
hashtags: "ハッシュタグ"
|
hashtags: "ハッシュタグ"
|
||||||
abuse: "スパム報告"
|
abuse: "スパム報告"
|
||||||
|
queue: "ジョブキュー"
|
||||||
back-to-misskey: "Misskeyに戻る"
|
back-to-misskey: "Misskeyに戻る"
|
||||||
admin/views/dashboard.vue:
|
admin/views/dashboard.vue:
|
||||||
dashboard: "ダッシュボード"
|
dashboard: "ダッシュボード"
|
||||||
@ -1009,6 +1017,9 @@ admin/views/dashboard.vue:
|
|||||||
instances: "インスタンス"
|
instances: "インスタンス"
|
||||||
this-instance: "ワイのインスタンス"
|
this-instance: "ワイのインスタンス"
|
||||||
federated: "連合"
|
federated: "連合"
|
||||||
|
admin/views/queue.vue:
|
||||||
|
operation: "操作"
|
||||||
|
remove-all-jobs: "すべてのジョブをクリア"
|
||||||
admin/views/abuse.vue:
|
admin/views/abuse.vue:
|
||||||
title: "スパム報告"
|
title: "スパム報告"
|
||||||
target: "対象"
|
target: "対象"
|
||||||
@ -1175,6 +1186,7 @@ admin/views/users.vue:
|
|||||||
moderator: "モデレーター"
|
moderator: "モデレーター"
|
||||||
adminOrModerator: "管理者+モデレーター"
|
adminOrModerator: "管理者+モデレーター"
|
||||||
verified: "公式アカウント"
|
verified: "公式アカウント"
|
||||||
|
silenced: "サイレンス済み"
|
||||||
suspended: "凍結済み"
|
suspended: "凍結済み"
|
||||||
origin:
|
origin:
|
||||||
title: "オリジン"
|
title: "オリジン"
|
||||||
@ -1222,6 +1234,64 @@ admin/views/announcements.vue:
|
|||||||
removed: "削除しました"
|
removed: "削除しました"
|
||||||
admin/views/hashtags.vue:
|
admin/views/hashtags.vue:
|
||||||
hided-tags: "Hidden Tags"
|
hided-tags: "Hidden Tags"
|
||||||
|
admin/views/federation.vue:
|
||||||
|
federation: "連合"
|
||||||
|
host: "ホスト"
|
||||||
|
notes: "投稿"
|
||||||
|
users: "ユーザー"
|
||||||
|
following: "フォロー中"
|
||||||
|
followers: "フォロワー"
|
||||||
|
status: "ステータス"
|
||||||
|
latest-request-sent-at: "直近のリクエスト送信"
|
||||||
|
latest-request-received-at: "直近のリクエスト受信"
|
||||||
|
remove-all-following: "フォローを全解除"
|
||||||
|
remove-all-following-info: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。"
|
||||||
|
block: "ブロック"
|
||||||
|
marked-as-closed: "閉鎖されているとマーク"
|
||||||
|
lookup: "照会"
|
||||||
|
instances: "インスタンス"
|
||||||
|
instance-not-registered: "そのインスタンスは登録されていません"
|
||||||
|
sort: "ソート"
|
||||||
|
sorts:
|
||||||
|
caughtAtAsc: "登録日時が古い順"
|
||||||
|
caughtAtDesc: "登録日時が新しい順"
|
||||||
|
lastCommunicatedAtAsc: "最後にやり取りした日時が古い順"
|
||||||
|
lastCommunicatedAtDesc: "最後にやり取りした日時が新しい順"
|
||||||
|
notesAsc: "投稿が少ない順"
|
||||||
|
notesDesc: "投稿が多い順"
|
||||||
|
usersAsc: "ユーザーが少ない順"
|
||||||
|
usersDesc: "ユーザーが多い順"
|
||||||
|
followingAsc: "フォローが少ない順"
|
||||||
|
followingDesc: "フォローが多い順"
|
||||||
|
followersAsc: "フォロワーが少ない順"
|
||||||
|
followersDesc: "フォロワーが多い順"
|
||||||
|
driveUsageAsc: "ドライブ使用量が少ない順"
|
||||||
|
driveUsageDesc: "ドライブ使用量が多い順"
|
||||||
|
driveFilesAsc: "ドライブのファイル数が少ない順"
|
||||||
|
driveFilesDesc: "ドライブのファイル数が多い順"
|
||||||
|
state: "状態"
|
||||||
|
states:
|
||||||
|
all: "すべて"
|
||||||
|
blocked: "ブロック"
|
||||||
|
not-responding: "応答なし"
|
||||||
|
marked-as-closed: "閉鎖とマーク済み"
|
||||||
|
result-is-truncated: "上位{n}件を表示しています。"
|
||||||
|
charts: "チャート"
|
||||||
|
chart-srcs:
|
||||||
|
requests: "リクエスト"
|
||||||
|
users: "ユーザーの増減"
|
||||||
|
users-total: "ユーザーの積算"
|
||||||
|
notes: "投稿の増減"
|
||||||
|
notes-total: "投稿の積算"
|
||||||
|
ff: "フォロー/フォロワーの増減"
|
||||||
|
ff-total: "フォロー/フォロワーの積算"
|
||||||
|
drive-usage: "ドライブ使用量の増減"
|
||||||
|
drive-usage-total: "ドライブ使用量の積算"
|
||||||
|
drive-files: "ドライブファイル数の増減"
|
||||||
|
drive-files-total: "ドライブファイル数の積算"
|
||||||
|
chart-spans:
|
||||||
|
hour: "1時間ごと"
|
||||||
|
day: "1日ごと"
|
||||||
desktop/views/pages/welcome.vue:
|
desktop/views/pages/welcome.vue:
|
||||||
about: "もうちょい……"
|
about: "もうちょい……"
|
||||||
gotit: "ほい"
|
gotit: "ほい"
|
||||||
@ -1557,6 +1627,7 @@ deck/deck.tl-column.vue:
|
|||||||
is-media-view: "メディアビュー"
|
is-media-view: "メディアビュー"
|
||||||
edit: "オプション"
|
edit: "オプション"
|
||||||
deck/deck.user-column.vue:
|
deck/deck.user-column.vue:
|
||||||
|
follows-you: "フォローされています"
|
||||||
posts: "投稿"
|
posts: "投稿"
|
||||||
following: "フォロー"
|
following: "フォロー"
|
||||||
followers: "フォロワー"
|
followers: "フォロワー"
|
||||||
|
@ -113,7 +113,7 @@ common:
|
|||||||
use-avatar-reversi-stones: "리버시의 돌로 아바타를 사용"
|
use-avatar-reversi-stones: "리버시의 돌로 아바타를 사용"
|
||||||
verified-user: "공식 계정"
|
verified-user: "공식 계정"
|
||||||
disable-animated-mfm: "글의 문자 애니메이션을 비활성화"
|
disable-animated-mfm: "글의 문자 애니메이션을 비활성화"
|
||||||
disable-showing-animated-images: "アニメーション画像を再生しない"
|
disable-showing-animated-images: "움직이는 이미지를 자동으로 재생하지 않음"
|
||||||
suggest-recent-hashtags: "최근 해시태그를 글 작성란에 표시"
|
suggest-recent-hashtags: "최근 해시태그를 글 작성란에 표시"
|
||||||
always-show-nsfw: "항상 열람주의 미디어를 표시"
|
always-show-nsfw: "항상 열람주의 미디어를 표시"
|
||||||
always-mark-nsfw: "항상 미디어를 열람주의로 설정하여 게시"
|
always-mark-nsfw: "항상 미디어를 열람주의로 설정하여 게시"
|
||||||
@ -345,8 +345,8 @@ common/views/components/note-menu.vue:
|
|||||||
copy-link: "링크 복사"
|
copy-link: "링크 복사"
|
||||||
favorite: "이 노트 즐겨찾기"
|
favorite: "이 노트 즐겨찾기"
|
||||||
unfavorite: "즐겨찾기에서 제거"
|
unfavorite: "즐겨찾기에서 제거"
|
||||||
watch: "ウォッチ"
|
watch: "지켜보기"
|
||||||
unwatch: "ウォッチ解除"
|
unwatch: "지켜보기 해제"
|
||||||
pin: "프로필에 고정"
|
pin: "프로필에 고정"
|
||||||
unpin: "프로필에서 고정 해제"
|
unpin: "프로필에서 고정 해제"
|
||||||
delete: "삭제"
|
delete: "삭제"
|
||||||
@ -509,6 +509,13 @@ common/views/components/profile-editor.vue:
|
|||||||
email-address: "메일 주소"
|
email-address: "메일 주소"
|
||||||
email-verified: "매일 주소가 확인되었습니다"
|
email-verified: "매일 주소가 확인되었습니다"
|
||||||
email-not-verified: "메일 주소가 확인되지 않았습니다. 받은 편지함을 확인하여 주시기 바랍니다."
|
email-not-verified: "메일 주소가 확인되지 않았습니다. 받은 편지함을 확인하여 주시기 바랍니다."
|
||||||
|
export: "내보내기"
|
||||||
|
export-targets:
|
||||||
|
all-notes: "모든 글 데이터"
|
||||||
|
following-list: "팔로잉"
|
||||||
|
mute-list: "뮤트"
|
||||||
|
blocking-list: "차단"
|
||||||
|
export-requested: "내보내기를 요청하였습니다. 이 작업은 시간이 걸릴 수 있습니다. 내보내기가 완료되면 드라이브에 파일이 추가됩니다."
|
||||||
common/views/components/user-list-editor.vue:
|
common/views/components/user-list-editor.vue:
|
||||||
users: "사용자"
|
users: "사용자"
|
||||||
rename: "리스트 이름 바꾸기"
|
rename: "리스트 이름 바꾸기"
|
||||||
@ -1000,6 +1007,7 @@ admin/views/index.vue:
|
|||||||
announcements: "공지사항"
|
announcements: "공지사항"
|
||||||
hashtags: "해시태그"
|
hashtags: "해시태그"
|
||||||
abuse: "스팸 신고"
|
abuse: "스팸 신고"
|
||||||
|
queue: "작업 대기열"
|
||||||
back-to-misskey: "Misskey로 돌아가기"
|
back-to-misskey: "Misskey로 돌아가기"
|
||||||
admin/views/dashboard.vue:
|
admin/views/dashboard.vue:
|
||||||
dashboard: "대시보드"
|
dashboard: "대시보드"
|
||||||
@ -1009,6 +1017,9 @@ admin/views/dashboard.vue:
|
|||||||
instances: "인스턴스"
|
instances: "인스턴스"
|
||||||
this-instance: "이 인스턴스"
|
this-instance: "이 인스턴스"
|
||||||
federated: "연합"
|
federated: "연합"
|
||||||
|
admin/views/queue.vue:
|
||||||
|
operation: "동작"
|
||||||
|
remove-all-jobs: "모든 작업 제거"
|
||||||
admin/views/abuse.vue:
|
admin/views/abuse.vue:
|
||||||
title: "스팸 신고"
|
title: "스팸 신고"
|
||||||
target: "대상"
|
target: "대상"
|
||||||
@ -1103,7 +1114,7 @@ admin/views/charts.vue:
|
|||||||
notes: "글 증감 (통합)"
|
notes: "글 증감 (통합)"
|
||||||
local-notes: "글 증감 (로컬)"
|
local-notes: "글 증감 (로컬)"
|
||||||
remote-notes: "글 증감 (원격)"
|
remote-notes: "글 증감 (원격)"
|
||||||
notes-total: "글 누적 수"
|
notes-total: "글 누적"
|
||||||
users: "사용자 증감"
|
users: "사용자 증감"
|
||||||
users-total: "사용자 누적"
|
users-total: "사용자 누적"
|
||||||
active-users: "활성 사용자 수"
|
active-users: "활성 사용자 수"
|
||||||
@ -1175,6 +1186,7 @@ admin/views/users.vue:
|
|||||||
moderator: "모더레이터"
|
moderator: "모더레이터"
|
||||||
adminOrModerator: "관리자+모더레이터"
|
adminOrModerator: "관리자+모더레이터"
|
||||||
verified: "공식 계정"
|
verified: "공식 계정"
|
||||||
|
silenced: "침묵됨"
|
||||||
suspended: "정지됨"
|
suspended: "정지됨"
|
||||||
origin:
|
origin:
|
||||||
title: "위치 (오리진)"
|
title: "위치 (오리진)"
|
||||||
@ -1222,6 +1234,64 @@ admin/views/announcements.vue:
|
|||||||
removed: "삭제하였습니다"
|
removed: "삭제하였습니다"
|
||||||
admin/views/hashtags.vue:
|
admin/views/hashtags.vue:
|
||||||
hided-tags: "Hidden Tags"
|
hided-tags: "Hidden Tags"
|
||||||
|
admin/views/federation.vue:
|
||||||
|
federation: "연합"
|
||||||
|
host: "호스트"
|
||||||
|
notes: "글"
|
||||||
|
users: "사용자"
|
||||||
|
following: "팔로우 중"
|
||||||
|
followers: "팔로워"
|
||||||
|
status: "상태"
|
||||||
|
latest-request-sent-at: "마지막으로 요청을 전송한 시간"
|
||||||
|
latest-request-received-at: "마지막으로 요청을 받은 시간"
|
||||||
|
remove-all-following: "모든 팔로잉 해제"
|
||||||
|
remove-all-following-info: "{host}(으)로부터 모든 팔로잉을 해제합니다. 해당 인스턴스가 더 이상 존재하지 않게 된 경우 등에 실행하십시오."
|
||||||
|
block: "차단"
|
||||||
|
marked-as-closed: "폐쇄된 것으로 표시"
|
||||||
|
lookup: "조회"
|
||||||
|
instances: "인스턴스"
|
||||||
|
instance-not-registered: "해당 인스턴스가 등록되어 있지 않습니다"
|
||||||
|
sort: "정렬"
|
||||||
|
sorts:
|
||||||
|
caughtAtAsc: "등록일이 오래된 순"
|
||||||
|
caughtAtDesc: "등록일이 최신인 순"
|
||||||
|
lastCommunicatedAtAsc: "마지막으로 요청을 주고받은 일시가 오래된 순"
|
||||||
|
lastCommunicatedAtDesc: "마지막으로 요청을 주고받은 일시가 빠른 순"
|
||||||
|
notesAsc: "글이 적은 순"
|
||||||
|
notesDesc: "글이 많은 순"
|
||||||
|
usersAsc: "사용자가 적은 순"
|
||||||
|
usersDesc: "사용자가 많은 순"
|
||||||
|
followingAsc: "팔로잉이 적은 순"
|
||||||
|
followingDesc: "팔로잉이 많은 순"
|
||||||
|
followersAsc: "팔로워가 적은 순"
|
||||||
|
followersDesc: "팔로워가 많은 순"
|
||||||
|
driveUsageAsc: "드라이브 사용량이 적은 순"
|
||||||
|
driveUsageDesc: "드라이브 사용량이 많은 순"
|
||||||
|
driveFilesAsc: "드라이브 파일 수가 적은 순"
|
||||||
|
driveFilesDesc: "드라이브 파일 수가 많은 순"
|
||||||
|
state: "상태"
|
||||||
|
states:
|
||||||
|
all: "모두"
|
||||||
|
blocked: "차단됨"
|
||||||
|
not-responding: "응답 없음"
|
||||||
|
marked-as-closed: "폐쇄된 것으로 표시됨"
|
||||||
|
result-is-truncated: "상위 {n}개를 표시하고 있습니다."
|
||||||
|
charts: "차트"
|
||||||
|
chart-srcs:
|
||||||
|
requests: "요청"
|
||||||
|
users: "사용자 증감"
|
||||||
|
users-total: "사용자 누적"
|
||||||
|
notes: "글 증감"
|
||||||
|
notes-total: "글 누적"
|
||||||
|
ff: "팔로잉/팔로워 증감"
|
||||||
|
ff-total: "팔로잉/팔로워 누적"
|
||||||
|
drive-usage: "드라이브 사용량 증감"
|
||||||
|
drive-usage-total: "드라이브 사용량 누적"
|
||||||
|
drive-files: "드라이브 파일 수 증감"
|
||||||
|
drive-files-total: "드라이브 파일 수 누적"
|
||||||
|
chart-spans:
|
||||||
|
hour: "1시간마다"
|
||||||
|
day: "1일마다"
|
||||||
desktop/views/pages/welcome.vue:
|
desktop/views/pages/welcome.vue:
|
||||||
about: "자세히..."
|
about: "자세히..."
|
||||||
gotit: "알겠습니다"
|
gotit: "알겠습니다"
|
||||||
@ -1557,6 +1627,7 @@ deck/deck.tl-column.vue:
|
|||||||
is-media-view: "미디어 보기"
|
is-media-view: "미디어 보기"
|
||||||
edit: "옵션"
|
edit: "옵션"
|
||||||
deck/deck.user-column.vue:
|
deck/deck.user-column.vue:
|
||||||
|
follows-you: "당신을 팔로우합니다"
|
||||||
posts: "글"
|
posts: "글"
|
||||||
following: "팔로잉"
|
following: "팔로잉"
|
||||||
followers: "팔로워"
|
followers: "팔로워"
|
||||||
|
@ -509,6 +509,13 @@ common/views/components/profile-editor.vue:
|
|||||||
email-address: "メールアドレス"
|
email-address: "メールアドレス"
|
||||||
email-verified: "メールアドレスが確認されました"
|
email-verified: "メールアドレスが確認されました"
|
||||||
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
||||||
|
export: "エクスポート"
|
||||||
|
export-targets:
|
||||||
|
all-notes: "すべての投稿データ"
|
||||||
|
following-list: "フォロー"
|
||||||
|
mute-list: "ミュート"
|
||||||
|
blocking-list: "ブロック"
|
||||||
|
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||||
common/views/components/user-list-editor.vue:
|
common/views/components/user-list-editor.vue:
|
||||||
users: "ユーザー"
|
users: "ユーザー"
|
||||||
rename: "リスト名を変更"
|
rename: "リスト名を変更"
|
||||||
@ -1000,6 +1007,7 @@ admin/views/index.vue:
|
|||||||
announcements: "お知らせ"
|
announcements: "お知らせ"
|
||||||
hashtags: "ハッシュタグ"
|
hashtags: "ハッシュタグ"
|
||||||
abuse: "スパム報告"
|
abuse: "スパム報告"
|
||||||
|
queue: "ジョブキュー"
|
||||||
back-to-misskey: "Misskeyに戻る"
|
back-to-misskey: "Misskeyに戻る"
|
||||||
admin/views/dashboard.vue:
|
admin/views/dashboard.vue:
|
||||||
dashboard: "ダッシュボード"
|
dashboard: "ダッシュボード"
|
||||||
@ -1009,6 +1017,9 @@ admin/views/dashboard.vue:
|
|||||||
instances: "インスタンス"
|
instances: "インスタンス"
|
||||||
this-instance: "このインスタンス"
|
this-instance: "このインスタンス"
|
||||||
federated: "連合"
|
federated: "連合"
|
||||||
|
admin/views/queue.vue:
|
||||||
|
operation: "操作"
|
||||||
|
remove-all-jobs: "すべてのジョブをクリア"
|
||||||
admin/views/abuse.vue:
|
admin/views/abuse.vue:
|
||||||
title: "スパム報告"
|
title: "スパム報告"
|
||||||
target: "対象"
|
target: "対象"
|
||||||
@ -1175,6 +1186,7 @@ admin/views/users.vue:
|
|||||||
moderator: "モデレーター"
|
moderator: "モデレーター"
|
||||||
adminOrModerator: "管理者+モデレーター"
|
adminOrModerator: "管理者+モデレーター"
|
||||||
verified: "公式アカウント"
|
verified: "公式アカウント"
|
||||||
|
silenced: "サイレンス済み"
|
||||||
suspended: "凍結済み"
|
suspended: "凍結済み"
|
||||||
origin:
|
origin:
|
||||||
title: "オリジン"
|
title: "オリジン"
|
||||||
@ -1222,6 +1234,64 @@ admin/views/announcements.vue:
|
|||||||
removed: "削除しました"
|
removed: "削除しました"
|
||||||
admin/views/hashtags.vue:
|
admin/views/hashtags.vue:
|
||||||
hided-tags: "Hidden Tags"
|
hided-tags: "Hidden Tags"
|
||||||
|
admin/views/federation.vue:
|
||||||
|
federation: "連合"
|
||||||
|
host: "ホスト"
|
||||||
|
notes: "投稿"
|
||||||
|
users: "ユーザー"
|
||||||
|
following: "フォロー中"
|
||||||
|
followers: "フォロワー"
|
||||||
|
status: "ステータス"
|
||||||
|
latest-request-sent-at: "直近のリクエスト送信"
|
||||||
|
latest-request-received-at: "直近のリクエスト受信"
|
||||||
|
remove-all-following: "フォローを全解除"
|
||||||
|
remove-all-following-info: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。"
|
||||||
|
block: "ブロック"
|
||||||
|
marked-as-closed: "閉鎖されているとマーク"
|
||||||
|
lookup: "照会"
|
||||||
|
instances: "インスタンス"
|
||||||
|
instance-not-registered: "そのインスタンスは登録されていません"
|
||||||
|
sort: "ソート"
|
||||||
|
sorts:
|
||||||
|
caughtAtAsc: "登録日時が古い順"
|
||||||
|
caughtAtDesc: "登録日時が新しい順"
|
||||||
|
lastCommunicatedAtAsc: "最後にやり取りした日時が古い順"
|
||||||
|
lastCommunicatedAtDesc: "最後にやり取りした日時が新しい順"
|
||||||
|
notesAsc: "投稿が少ない順"
|
||||||
|
notesDesc: "投稿が多い順"
|
||||||
|
usersAsc: "ユーザーが少ない順"
|
||||||
|
usersDesc: "ユーザーが多い順"
|
||||||
|
followingAsc: "フォローが少ない順"
|
||||||
|
followingDesc: "フォローが多い順"
|
||||||
|
followersAsc: "フォロワーが少ない順"
|
||||||
|
followersDesc: "フォロワーが多い順"
|
||||||
|
driveUsageAsc: "ドライブ使用量が少ない順"
|
||||||
|
driveUsageDesc: "ドライブ使用量が多い順"
|
||||||
|
driveFilesAsc: "ドライブのファイル数が少ない順"
|
||||||
|
driveFilesDesc: "ドライブのファイル数が多い順"
|
||||||
|
state: "状態"
|
||||||
|
states:
|
||||||
|
all: "すべて"
|
||||||
|
blocked: "ブロック"
|
||||||
|
not-responding: "応答なし"
|
||||||
|
marked-as-closed: "閉鎖とマーク済み"
|
||||||
|
result-is-truncated: "上位{n}件を表示しています。"
|
||||||
|
charts: "チャート"
|
||||||
|
chart-srcs:
|
||||||
|
requests: "リクエスト"
|
||||||
|
users: "ユーザーの増減"
|
||||||
|
users-total: "ユーザーの積算"
|
||||||
|
notes: "投稿の増減"
|
||||||
|
notes-total: "投稿の積算"
|
||||||
|
ff: "フォロー/フォロワーの増減"
|
||||||
|
ff-total: "フォロー/フォロワーの積算"
|
||||||
|
drive-usage: "ドライブ使用量の増減"
|
||||||
|
drive-usage-total: "ドライブ使用量の積算"
|
||||||
|
drive-files: "ドライブファイル数の増減"
|
||||||
|
drive-files-total: "ドライブファイル数の積算"
|
||||||
|
chart-spans:
|
||||||
|
hour: "1時間ごと"
|
||||||
|
day: "1日ごと"
|
||||||
desktop/views/pages/welcome.vue:
|
desktop/views/pages/welcome.vue:
|
||||||
about: "詳しく..."
|
about: "詳しく..."
|
||||||
gotit: "わかった"
|
gotit: "わかった"
|
||||||
@ -1557,6 +1627,7 @@ deck/deck.tl-column.vue:
|
|||||||
is-media-view: "メディアビュー"
|
is-media-view: "メディアビュー"
|
||||||
edit: "オプション"
|
edit: "オプション"
|
||||||
deck/deck.user-column.vue:
|
deck/deck.user-column.vue:
|
||||||
|
follows-you: "フォローされています"
|
||||||
posts: "投稿"
|
posts: "投稿"
|
||||||
following: "フォロー"
|
following: "フォロー"
|
||||||
followers: "フォロワー"
|
followers: "フォロワー"
|
||||||
|
@ -509,6 +509,13 @@ common/views/components/profile-editor.vue:
|
|||||||
email-address: "メールアドレス"
|
email-address: "メールアドレス"
|
||||||
email-verified: "メールアドレスが確認されました"
|
email-verified: "メールアドレスが確認されました"
|
||||||
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
||||||
|
export: "エクスポート"
|
||||||
|
export-targets:
|
||||||
|
all-notes: "すべての投稿データ"
|
||||||
|
following-list: "フォロー"
|
||||||
|
mute-list: "ミュート"
|
||||||
|
blocking-list: "ブロック"
|
||||||
|
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||||
common/views/components/user-list-editor.vue:
|
common/views/components/user-list-editor.vue:
|
||||||
users: "ユーザー"
|
users: "ユーザー"
|
||||||
rename: "リスト名を変更"
|
rename: "リスト名を変更"
|
||||||
@ -1000,6 +1007,7 @@ admin/views/index.vue:
|
|||||||
announcements: "お知らせ"
|
announcements: "お知らせ"
|
||||||
hashtags: "ハッシュタグ"
|
hashtags: "ハッシュタグ"
|
||||||
abuse: "スパム報告"
|
abuse: "スパム報告"
|
||||||
|
queue: "ジョブキュー"
|
||||||
back-to-misskey: "Misskeyに戻る"
|
back-to-misskey: "Misskeyに戻る"
|
||||||
admin/views/dashboard.vue:
|
admin/views/dashboard.vue:
|
||||||
dashboard: "ダッシュボード"
|
dashboard: "ダッシュボード"
|
||||||
@ -1009,6 +1017,9 @@ admin/views/dashboard.vue:
|
|||||||
instances: "インスタンス"
|
instances: "インスタンス"
|
||||||
this-instance: "このインスタンス"
|
this-instance: "このインスタンス"
|
||||||
federated: "連合"
|
federated: "連合"
|
||||||
|
admin/views/queue.vue:
|
||||||
|
operation: "操作"
|
||||||
|
remove-all-jobs: "すべてのジョブをクリア"
|
||||||
admin/views/abuse.vue:
|
admin/views/abuse.vue:
|
||||||
title: "スパム報告"
|
title: "スパム報告"
|
||||||
target: "対象"
|
target: "対象"
|
||||||
@ -1175,6 +1186,7 @@ admin/views/users.vue:
|
|||||||
moderator: "モデレーター"
|
moderator: "モデレーター"
|
||||||
adminOrModerator: "管理者+モデレーター"
|
adminOrModerator: "管理者+モデレーター"
|
||||||
verified: "公式アカウント"
|
verified: "公式アカウント"
|
||||||
|
silenced: "サイレンス済み"
|
||||||
suspended: "凍結済み"
|
suspended: "凍結済み"
|
||||||
origin:
|
origin:
|
||||||
title: "オリジン"
|
title: "オリジン"
|
||||||
@ -1222,6 +1234,64 @@ admin/views/announcements.vue:
|
|||||||
removed: "削除しました"
|
removed: "削除しました"
|
||||||
admin/views/hashtags.vue:
|
admin/views/hashtags.vue:
|
||||||
hided-tags: "Hidden Tags"
|
hided-tags: "Hidden Tags"
|
||||||
|
admin/views/federation.vue:
|
||||||
|
federation: "連合"
|
||||||
|
host: "ホスト"
|
||||||
|
notes: "投稿"
|
||||||
|
users: "ユーザー"
|
||||||
|
following: "フォロー中"
|
||||||
|
followers: "フォロワー"
|
||||||
|
status: "ステータス"
|
||||||
|
latest-request-sent-at: "直近のリクエスト送信"
|
||||||
|
latest-request-received-at: "直近のリクエスト受信"
|
||||||
|
remove-all-following: "フォローを全解除"
|
||||||
|
remove-all-following-info: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。"
|
||||||
|
block: "ブロック"
|
||||||
|
marked-as-closed: "閉鎖されているとマーク"
|
||||||
|
lookup: "照会"
|
||||||
|
instances: "インスタンス"
|
||||||
|
instance-not-registered: "そのインスタンスは登録されていません"
|
||||||
|
sort: "ソート"
|
||||||
|
sorts:
|
||||||
|
caughtAtAsc: "登録日時が古い順"
|
||||||
|
caughtAtDesc: "登録日時が新しい順"
|
||||||
|
lastCommunicatedAtAsc: "最後にやり取りした日時が古い順"
|
||||||
|
lastCommunicatedAtDesc: "最後にやり取りした日時が新しい順"
|
||||||
|
notesAsc: "投稿が少ない順"
|
||||||
|
notesDesc: "投稿が多い順"
|
||||||
|
usersAsc: "ユーザーが少ない順"
|
||||||
|
usersDesc: "ユーザーが多い順"
|
||||||
|
followingAsc: "フォローが少ない順"
|
||||||
|
followingDesc: "フォローが多い順"
|
||||||
|
followersAsc: "フォロワーが少ない順"
|
||||||
|
followersDesc: "フォロワーが多い順"
|
||||||
|
driveUsageAsc: "ドライブ使用量が少ない順"
|
||||||
|
driveUsageDesc: "ドライブ使用量が多い順"
|
||||||
|
driveFilesAsc: "ドライブのファイル数が少ない順"
|
||||||
|
driveFilesDesc: "ドライブのファイル数が多い順"
|
||||||
|
state: "状態"
|
||||||
|
states:
|
||||||
|
all: "すべて"
|
||||||
|
blocked: "ブロック"
|
||||||
|
not-responding: "応答なし"
|
||||||
|
marked-as-closed: "閉鎖とマーク済み"
|
||||||
|
result-is-truncated: "上位{n}件を表示しています。"
|
||||||
|
charts: "チャート"
|
||||||
|
chart-srcs:
|
||||||
|
requests: "リクエスト"
|
||||||
|
users: "ユーザーの増減"
|
||||||
|
users-total: "ユーザーの積算"
|
||||||
|
notes: "投稿の増減"
|
||||||
|
notes-total: "投稿の積算"
|
||||||
|
ff: "フォロー/フォロワーの増減"
|
||||||
|
ff-total: "フォロー/フォロワーの積算"
|
||||||
|
drive-usage: "ドライブ使用量の増減"
|
||||||
|
drive-usage-total: "ドライブ使用量の積算"
|
||||||
|
drive-files: "ドライブファイル数の増減"
|
||||||
|
drive-files-total: "ドライブファイル数の積算"
|
||||||
|
chart-spans:
|
||||||
|
hour: "1時間ごと"
|
||||||
|
day: "1日ごと"
|
||||||
desktop/views/pages/welcome.vue:
|
desktop/views/pages/welcome.vue:
|
||||||
about: "詳しく..."
|
about: "詳しく..."
|
||||||
gotit: "Skjønner!"
|
gotit: "Skjønner!"
|
||||||
@ -1557,6 +1627,7 @@ deck/deck.tl-column.vue:
|
|||||||
is-media-view: "メディアビュー"
|
is-media-view: "メディアビュー"
|
||||||
edit: "オプション"
|
edit: "オプション"
|
||||||
deck/deck.user-column.vue:
|
deck/deck.user-column.vue:
|
||||||
|
follows-you: "フォローされています"
|
||||||
posts: "投稿"
|
posts: "投稿"
|
||||||
following: "フォロー"
|
following: "フォロー"
|
||||||
followers: "フォロワー"
|
followers: "フォロワー"
|
||||||
|
@ -509,6 +509,13 @@ common/views/components/profile-editor.vue:
|
|||||||
email-address: "Adres e-mail"
|
email-address: "Adres e-mail"
|
||||||
email-verified: "Twój adres e-mail został zweryfikowany."
|
email-verified: "Twój adres e-mail został zweryfikowany."
|
||||||
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
||||||
|
export: "エクスポート"
|
||||||
|
export-targets:
|
||||||
|
all-notes: "すべての投稿データ"
|
||||||
|
following-list: "フォロー"
|
||||||
|
mute-list: "ミュート"
|
||||||
|
blocking-list: "ブロック"
|
||||||
|
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||||
common/views/components/user-list-editor.vue:
|
common/views/components/user-list-editor.vue:
|
||||||
users: "Użytkownicy"
|
users: "Użytkownicy"
|
||||||
rename: "Zmień nazwę listy"
|
rename: "Zmień nazwę listy"
|
||||||
@ -1000,6 +1007,7 @@ admin/views/index.vue:
|
|||||||
announcements: "Ogłoszenia"
|
announcements: "Ogłoszenia"
|
||||||
hashtags: "Hashtagi"
|
hashtags: "Hashtagi"
|
||||||
abuse: "スパム報告"
|
abuse: "スパム報告"
|
||||||
|
queue: "ジョブキュー"
|
||||||
back-to-misskey: "Misskeyに戻る"
|
back-to-misskey: "Misskeyに戻る"
|
||||||
admin/views/dashboard.vue:
|
admin/views/dashboard.vue:
|
||||||
dashboard: "ダッシュボード"
|
dashboard: "ダッシュボード"
|
||||||
@ -1009,6 +1017,9 @@ admin/views/dashboard.vue:
|
|||||||
instances: "インスタンス"
|
instances: "インスタンス"
|
||||||
this-instance: "このインスタンス"
|
this-instance: "このインスタンス"
|
||||||
federated: "連合"
|
federated: "連合"
|
||||||
|
admin/views/queue.vue:
|
||||||
|
operation: "操作"
|
||||||
|
remove-all-jobs: "すべてのジョブをクリア"
|
||||||
admin/views/abuse.vue:
|
admin/views/abuse.vue:
|
||||||
title: "スパム報告"
|
title: "スパム報告"
|
||||||
target: "対象"
|
target: "対象"
|
||||||
@ -1175,6 +1186,7 @@ admin/views/users.vue:
|
|||||||
moderator: "モデレーター"
|
moderator: "モデレーター"
|
||||||
adminOrModerator: "管理者+モデレーター"
|
adminOrModerator: "管理者+モデレーター"
|
||||||
verified: "公式アカウント"
|
verified: "公式アカウント"
|
||||||
|
silenced: "サイレンス済み"
|
||||||
suspended: "凍結済み"
|
suspended: "凍結済み"
|
||||||
origin:
|
origin:
|
||||||
title: "Źródło"
|
title: "Źródło"
|
||||||
@ -1222,6 +1234,64 @@ admin/views/announcements.vue:
|
|||||||
removed: "Usunięto"
|
removed: "Usunięto"
|
||||||
admin/views/hashtags.vue:
|
admin/views/hashtags.vue:
|
||||||
hided-tags: "Hidden Tags"
|
hided-tags: "Hidden Tags"
|
||||||
|
admin/views/federation.vue:
|
||||||
|
federation: "連合"
|
||||||
|
host: "ホスト"
|
||||||
|
notes: "投稿"
|
||||||
|
users: "ユーザー"
|
||||||
|
following: "フォロー中"
|
||||||
|
followers: "フォロワー"
|
||||||
|
status: "ステータス"
|
||||||
|
latest-request-sent-at: "直近のリクエスト送信"
|
||||||
|
latest-request-received-at: "直近のリクエスト受信"
|
||||||
|
remove-all-following: "フォローを全解除"
|
||||||
|
remove-all-following-info: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。"
|
||||||
|
block: "ブロック"
|
||||||
|
marked-as-closed: "閉鎖されているとマーク"
|
||||||
|
lookup: "照会"
|
||||||
|
instances: "インスタンス"
|
||||||
|
instance-not-registered: "そのインスタンスは登録されていません"
|
||||||
|
sort: "ソート"
|
||||||
|
sorts:
|
||||||
|
caughtAtAsc: "登録日時が古い順"
|
||||||
|
caughtAtDesc: "登録日時が新しい順"
|
||||||
|
lastCommunicatedAtAsc: "最後にやり取りした日時が古い順"
|
||||||
|
lastCommunicatedAtDesc: "最後にやり取りした日時が新しい順"
|
||||||
|
notesAsc: "投稿が少ない順"
|
||||||
|
notesDesc: "投稿が多い順"
|
||||||
|
usersAsc: "ユーザーが少ない順"
|
||||||
|
usersDesc: "ユーザーが多い順"
|
||||||
|
followingAsc: "フォローが少ない順"
|
||||||
|
followingDesc: "フォローが多い順"
|
||||||
|
followersAsc: "フォロワーが少ない順"
|
||||||
|
followersDesc: "フォロワーが多い順"
|
||||||
|
driveUsageAsc: "ドライブ使用量が少ない順"
|
||||||
|
driveUsageDesc: "ドライブ使用量が多い順"
|
||||||
|
driveFilesAsc: "ドライブのファイル数が少ない順"
|
||||||
|
driveFilesDesc: "ドライブのファイル数が多い順"
|
||||||
|
state: "状態"
|
||||||
|
states:
|
||||||
|
all: "すべて"
|
||||||
|
blocked: "ブロック"
|
||||||
|
not-responding: "応答なし"
|
||||||
|
marked-as-closed: "閉鎖とマーク済み"
|
||||||
|
result-is-truncated: "上位{n}件を表示しています。"
|
||||||
|
charts: "チャート"
|
||||||
|
chart-srcs:
|
||||||
|
requests: "リクエスト"
|
||||||
|
users: "ユーザーの増減"
|
||||||
|
users-total: "ユーザーの積算"
|
||||||
|
notes: "投稿の増減"
|
||||||
|
notes-total: "投稿の積算"
|
||||||
|
ff: "フォロー/フォロワーの増減"
|
||||||
|
ff-total: "フォロー/フォロワーの積算"
|
||||||
|
drive-usage: "ドライブ使用量の増減"
|
||||||
|
drive-usage-total: "ドライブ使用量の積算"
|
||||||
|
drive-files: "ドライブファイル数の増減"
|
||||||
|
drive-files-total: "ドライブファイル数の積算"
|
||||||
|
chart-spans:
|
||||||
|
hour: "1時間ごと"
|
||||||
|
day: "1日ごと"
|
||||||
desktop/views/pages/welcome.vue:
|
desktop/views/pages/welcome.vue:
|
||||||
about: "O Misskey"
|
about: "O Misskey"
|
||||||
gotit: "Rozumiem!"
|
gotit: "Rozumiem!"
|
||||||
@ -1557,6 +1627,7 @@ deck/deck.tl-column.vue:
|
|||||||
is-media-view: "Widok multimediów"
|
is-media-view: "Widok multimediów"
|
||||||
edit: "Opcje"
|
edit: "Opcje"
|
||||||
deck/deck.user-column.vue:
|
deck/deck.user-column.vue:
|
||||||
|
follows-you: "フォローされています"
|
||||||
posts: "Wpisy"
|
posts: "Wpisy"
|
||||||
following: "Śledzeni"
|
following: "Śledzeni"
|
||||||
followers: "Śledzący"
|
followers: "Śledzący"
|
||||||
|
@ -509,6 +509,13 @@ common/views/components/profile-editor.vue:
|
|||||||
email-address: "メールアドレス"
|
email-address: "メールアドレス"
|
||||||
email-verified: "メールアドレスが確認されました"
|
email-verified: "メールアドレスが確認されました"
|
||||||
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
||||||
|
export: "エクスポート"
|
||||||
|
export-targets:
|
||||||
|
all-notes: "すべての投稿データ"
|
||||||
|
following-list: "フォロー"
|
||||||
|
mute-list: "ミュート"
|
||||||
|
blocking-list: "ブロック"
|
||||||
|
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||||
common/views/components/user-list-editor.vue:
|
common/views/components/user-list-editor.vue:
|
||||||
users: "ユーザー"
|
users: "ユーザー"
|
||||||
rename: "リスト名を変更"
|
rename: "リスト名を変更"
|
||||||
@ -1000,6 +1007,7 @@ admin/views/index.vue:
|
|||||||
announcements: "お知らせ"
|
announcements: "お知らせ"
|
||||||
hashtags: "ハッシュタグ"
|
hashtags: "ハッシュタグ"
|
||||||
abuse: "スパム報告"
|
abuse: "スパム報告"
|
||||||
|
queue: "ジョブキュー"
|
||||||
back-to-misskey: "Misskeyに戻る"
|
back-to-misskey: "Misskeyに戻る"
|
||||||
admin/views/dashboard.vue:
|
admin/views/dashboard.vue:
|
||||||
dashboard: "ダッシュボード"
|
dashboard: "ダッシュボード"
|
||||||
@ -1009,6 +1017,9 @@ admin/views/dashboard.vue:
|
|||||||
instances: "インスタンス"
|
instances: "インスタンス"
|
||||||
this-instance: "このインスタンス"
|
this-instance: "このインスタンス"
|
||||||
federated: "連合"
|
federated: "連合"
|
||||||
|
admin/views/queue.vue:
|
||||||
|
operation: "操作"
|
||||||
|
remove-all-jobs: "すべてのジョブをクリア"
|
||||||
admin/views/abuse.vue:
|
admin/views/abuse.vue:
|
||||||
title: "スパム報告"
|
title: "スパム報告"
|
||||||
target: "対象"
|
target: "対象"
|
||||||
@ -1175,6 +1186,7 @@ admin/views/users.vue:
|
|||||||
moderator: "モデレーター"
|
moderator: "モデレーター"
|
||||||
adminOrModerator: "管理者+モデレーター"
|
adminOrModerator: "管理者+モデレーター"
|
||||||
verified: "公式アカウント"
|
verified: "公式アカウント"
|
||||||
|
silenced: "サイレンス済み"
|
||||||
suspended: "凍結済み"
|
suspended: "凍結済み"
|
||||||
origin:
|
origin:
|
||||||
title: "オリジン"
|
title: "オリジン"
|
||||||
@ -1222,6 +1234,64 @@ admin/views/announcements.vue:
|
|||||||
removed: "削除しました"
|
removed: "削除しました"
|
||||||
admin/views/hashtags.vue:
|
admin/views/hashtags.vue:
|
||||||
hided-tags: "Hidden Tags"
|
hided-tags: "Hidden Tags"
|
||||||
|
admin/views/federation.vue:
|
||||||
|
federation: "連合"
|
||||||
|
host: "ホスト"
|
||||||
|
notes: "投稿"
|
||||||
|
users: "ユーザー"
|
||||||
|
following: "フォロー中"
|
||||||
|
followers: "フォロワー"
|
||||||
|
status: "ステータス"
|
||||||
|
latest-request-sent-at: "直近のリクエスト送信"
|
||||||
|
latest-request-received-at: "直近のリクエスト受信"
|
||||||
|
remove-all-following: "フォローを全解除"
|
||||||
|
remove-all-following-info: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。"
|
||||||
|
block: "ブロック"
|
||||||
|
marked-as-closed: "閉鎖されているとマーク"
|
||||||
|
lookup: "照会"
|
||||||
|
instances: "インスタンス"
|
||||||
|
instance-not-registered: "そのインスタンスは登録されていません"
|
||||||
|
sort: "ソート"
|
||||||
|
sorts:
|
||||||
|
caughtAtAsc: "登録日時が古い順"
|
||||||
|
caughtAtDesc: "登録日時が新しい順"
|
||||||
|
lastCommunicatedAtAsc: "最後にやり取りした日時が古い順"
|
||||||
|
lastCommunicatedAtDesc: "最後にやり取りした日時が新しい順"
|
||||||
|
notesAsc: "投稿が少ない順"
|
||||||
|
notesDesc: "投稿が多い順"
|
||||||
|
usersAsc: "ユーザーが少ない順"
|
||||||
|
usersDesc: "ユーザーが多い順"
|
||||||
|
followingAsc: "フォローが少ない順"
|
||||||
|
followingDesc: "フォローが多い順"
|
||||||
|
followersAsc: "フォロワーが少ない順"
|
||||||
|
followersDesc: "フォロワーが多い順"
|
||||||
|
driveUsageAsc: "ドライブ使用量が少ない順"
|
||||||
|
driveUsageDesc: "ドライブ使用量が多い順"
|
||||||
|
driveFilesAsc: "ドライブのファイル数が少ない順"
|
||||||
|
driveFilesDesc: "ドライブのファイル数が多い順"
|
||||||
|
state: "状態"
|
||||||
|
states:
|
||||||
|
all: "すべて"
|
||||||
|
blocked: "ブロック"
|
||||||
|
not-responding: "応答なし"
|
||||||
|
marked-as-closed: "閉鎖とマーク済み"
|
||||||
|
result-is-truncated: "上位{n}件を表示しています。"
|
||||||
|
charts: "チャート"
|
||||||
|
chart-srcs:
|
||||||
|
requests: "リクエスト"
|
||||||
|
users: "ユーザーの増減"
|
||||||
|
users-total: "ユーザーの積算"
|
||||||
|
notes: "投稿の増減"
|
||||||
|
notes-total: "投稿の積算"
|
||||||
|
ff: "フォロー/フォロワーの増減"
|
||||||
|
ff-total: "フォロー/フォロワーの積算"
|
||||||
|
drive-usage: "ドライブ使用量の増減"
|
||||||
|
drive-usage-total: "ドライブ使用量の積算"
|
||||||
|
drive-files: "ドライブファイル数の増減"
|
||||||
|
drive-files-total: "ドライブファイル数の積算"
|
||||||
|
chart-spans:
|
||||||
|
hour: "1時間ごと"
|
||||||
|
day: "1日ごと"
|
||||||
desktop/views/pages/welcome.vue:
|
desktop/views/pages/welcome.vue:
|
||||||
about: "詳しく..."
|
about: "詳しく..."
|
||||||
gotit: "わかった"
|
gotit: "わかった"
|
||||||
@ -1557,6 +1627,7 @@ deck/deck.tl-column.vue:
|
|||||||
is-media-view: "メディアビュー"
|
is-media-view: "メディアビュー"
|
||||||
edit: "オプション"
|
edit: "オプション"
|
||||||
deck/deck.user-column.vue:
|
deck/deck.user-column.vue:
|
||||||
|
follows-you: "フォローされています"
|
||||||
posts: "投稿"
|
posts: "投稿"
|
||||||
following: "フォロー"
|
following: "フォロー"
|
||||||
followers: "フォロワー"
|
followers: "フォロワー"
|
||||||
|
@ -509,6 +509,13 @@ common/views/components/profile-editor.vue:
|
|||||||
email-address: "メールアドレス"
|
email-address: "メールアドレス"
|
||||||
email-verified: "メールアドレスが確認されました"
|
email-verified: "メールアドレスが確認されました"
|
||||||
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
||||||
|
export: "エクスポート"
|
||||||
|
export-targets:
|
||||||
|
all-notes: "すべての投稿データ"
|
||||||
|
following-list: "フォロー"
|
||||||
|
mute-list: "ミュート"
|
||||||
|
blocking-list: "ブロック"
|
||||||
|
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||||
common/views/components/user-list-editor.vue:
|
common/views/components/user-list-editor.vue:
|
||||||
users: "ユーザー"
|
users: "ユーザー"
|
||||||
rename: "リスト名を変更"
|
rename: "リスト名を変更"
|
||||||
@ -1000,6 +1007,7 @@ admin/views/index.vue:
|
|||||||
announcements: "お知らせ"
|
announcements: "お知らせ"
|
||||||
hashtags: "ハッシュタグ"
|
hashtags: "ハッシュタグ"
|
||||||
abuse: "スパム報告"
|
abuse: "スパム報告"
|
||||||
|
queue: "ジョブキュー"
|
||||||
back-to-misskey: "Misskeyに戻る"
|
back-to-misskey: "Misskeyに戻る"
|
||||||
admin/views/dashboard.vue:
|
admin/views/dashboard.vue:
|
||||||
dashboard: "ダッシュボード"
|
dashboard: "ダッシュボード"
|
||||||
@ -1009,6 +1017,9 @@ admin/views/dashboard.vue:
|
|||||||
instances: "インスタンス"
|
instances: "インスタンス"
|
||||||
this-instance: "このインスタンス"
|
this-instance: "このインスタンス"
|
||||||
federated: "連合"
|
federated: "連合"
|
||||||
|
admin/views/queue.vue:
|
||||||
|
operation: "操作"
|
||||||
|
remove-all-jobs: "すべてのジョブをクリア"
|
||||||
admin/views/abuse.vue:
|
admin/views/abuse.vue:
|
||||||
title: "スパム報告"
|
title: "スパム報告"
|
||||||
target: "対象"
|
target: "対象"
|
||||||
@ -1175,6 +1186,7 @@ admin/views/users.vue:
|
|||||||
moderator: "モデレーター"
|
moderator: "モデレーター"
|
||||||
adminOrModerator: "管理者+モデレーター"
|
adminOrModerator: "管理者+モデレーター"
|
||||||
verified: "公式アカウント"
|
verified: "公式アカウント"
|
||||||
|
silenced: "サイレンス済み"
|
||||||
suspended: "凍結済み"
|
suspended: "凍結済み"
|
||||||
origin:
|
origin:
|
||||||
title: "オリジン"
|
title: "オリジン"
|
||||||
@ -1222,6 +1234,64 @@ admin/views/announcements.vue:
|
|||||||
removed: "削除しました"
|
removed: "削除しました"
|
||||||
admin/views/hashtags.vue:
|
admin/views/hashtags.vue:
|
||||||
hided-tags: "Hidden Tags"
|
hided-tags: "Hidden Tags"
|
||||||
|
admin/views/federation.vue:
|
||||||
|
federation: "連合"
|
||||||
|
host: "ホスト"
|
||||||
|
notes: "投稿"
|
||||||
|
users: "ユーザー"
|
||||||
|
following: "フォロー中"
|
||||||
|
followers: "フォロワー"
|
||||||
|
status: "ステータス"
|
||||||
|
latest-request-sent-at: "直近のリクエスト送信"
|
||||||
|
latest-request-received-at: "直近のリクエスト受信"
|
||||||
|
remove-all-following: "フォローを全解除"
|
||||||
|
remove-all-following-info: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。"
|
||||||
|
block: "ブロック"
|
||||||
|
marked-as-closed: "閉鎖されているとマーク"
|
||||||
|
lookup: "照会"
|
||||||
|
instances: "インスタンス"
|
||||||
|
instance-not-registered: "そのインスタンスは登録されていません"
|
||||||
|
sort: "ソート"
|
||||||
|
sorts:
|
||||||
|
caughtAtAsc: "登録日時が古い順"
|
||||||
|
caughtAtDesc: "登録日時が新しい順"
|
||||||
|
lastCommunicatedAtAsc: "最後にやり取りした日時が古い順"
|
||||||
|
lastCommunicatedAtDesc: "最後にやり取りした日時が新しい順"
|
||||||
|
notesAsc: "投稿が少ない順"
|
||||||
|
notesDesc: "投稿が多い順"
|
||||||
|
usersAsc: "ユーザーが少ない順"
|
||||||
|
usersDesc: "ユーザーが多い順"
|
||||||
|
followingAsc: "フォローが少ない順"
|
||||||
|
followingDesc: "フォローが多い順"
|
||||||
|
followersAsc: "フォロワーが少ない順"
|
||||||
|
followersDesc: "フォロワーが多い順"
|
||||||
|
driveUsageAsc: "ドライブ使用量が少ない順"
|
||||||
|
driveUsageDesc: "ドライブ使用量が多い順"
|
||||||
|
driveFilesAsc: "ドライブのファイル数が少ない順"
|
||||||
|
driveFilesDesc: "ドライブのファイル数が多い順"
|
||||||
|
state: "状態"
|
||||||
|
states:
|
||||||
|
all: "すべて"
|
||||||
|
blocked: "ブロック"
|
||||||
|
not-responding: "応答なし"
|
||||||
|
marked-as-closed: "閉鎖とマーク済み"
|
||||||
|
result-is-truncated: "上位{n}件を表示しています。"
|
||||||
|
charts: "チャート"
|
||||||
|
chart-srcs:
|
||||||
|
requests: "リクエスト"
|
||||||
|
users: "ユーザーの増減"
|
||||||
|
users-total: "ユーザーの積算"
|
||||||
|
notes: "投稿の増減"
|
||||||
|
notes-total: "投稿の積算"
|
||||||
|
ff: "フォロー/フォロワーの増減"
|
||||||
|
ff-total: "フォロー/フォロワーの積算"
|
||||||
|
drive-usage: "ドライブ使用量の増減"
|
||||||
|
drive-usage-total: "ドライブ使用量の積算"
|
||||||
|
drive-files: "ドライブファイル数の増減"
|
||||||
|
drive-files-total: "ドライブファイル数の積算"
|
||||||
|
chart-spans:
|
||||||
|
hour: "1時間ごと"
|
||||||
|
day: "1日ごと"
|
||||||
desktop/views/pages/welcome.vue:
|
desktop/views/pages/welcome.vue:
|
||||||
about: "詳しく..."
|
about: "詳しく..."
|
||||||
gotit: "わかった"
|
gotit: "わかった"
|
||||||
@ -1557,6 +1627,7 @@ deck/deck.tl-column.vue:
|
|||||||
is-media-view: "メディアビュー"
|
is-media-view: "メディアビュー"
|
||||||
edit: "オプション"
|
edit: "オプション"
|
||||||
deck/deck.user-column.vue:
|
deck/deck.user-column.vue:
|
||||||
|
follows-you: "フォローされています"
|
||||||
posts: "投稿"
|
posts: "投稿"
|
||||||
following: "フォロー"
|
following: "フォロー"
|
||||||
followers: "フォロワー"
|
followers: "フォロワー"
|
||||||
|
@ -16,8 +16,8 @@ common:
|
|||||||
reaction-desc: "这是表达情绪的最简单方法。 Misskey允许您向其他帖子添加各种类型的回应。 一旦体验过Misskey的回应功能,就再也不会想回到那些只有点赞功能的其他SNS上了。"
|
reaction-desc: "这是表达情绪的最简单方法。 Misskey允许您向其他帖子添加各种类型的回应。 一旦体验过Misskey的回应功能,就再也不会想回到那些只有点赞功能的其他SNS上了。"
|
||||||
ui: "交互界面"
|
ui: "交互界面"
|
||||||
ui-desc: "世界上没有一个UI可以适合每一个人. 所以, Misskey 提供一个可以高度定制的UI交互界面. 您可以通过编辑, 调整布局, 放置可选择的小部件来轻松定制您的专属UI界面。"
|
ui-desc: "世界上没有一个UI可以适合每一个人. 所以, Misskey 提供一个可以高度定制的UI交互界面. 您可以通过编辑, 调整布局, 放置可选择的小部件来轻松定制您的专属UI界面。"
|
||||||
drive: "Misskey 云盘"
|
drive: "网盘"
|
||||||
drive-desc: "想要发布一张您已经上传过的照片吗? 想要组织,命名和为上传的文件创建文件夹吗? Misskey 云盘是一个最好的解决方案. "
|
drive-desc: "想要发布一张您已经上传过的照片吗?想要管理文件或为上传的文件创建文件夹吗?Misskey的网盘是一个最好的解决方案。"
|
||||||
outro: "Misskey还有其他更多功能,请亲身体验一下吧。因为 Misskey 是一个分布式的 SNS,如果您感觉某个功能不适合自己,试试其他的吧。祝您玩得开心!"
|
outro: "Misskey还有其他更多功能,请亲身体验一下吧。因为 Misskey 是一个分布式的 SNS,如果您感觉某个功能不适合自己,试试其他的吧。祝您玩得开心!"
|
||||||
adblock:
|
adblock:
|
||||||
detected: "请关闭广告拦截器"
|
detected: "请关闭广告拦截器"
|
||||||
@ -181,8 +181,8 @@ auth/views/form.vue:
|
|||||||
note-write: "投稿。"
|
note-write: "投稿。"
|
||||||
like-write: "点赞或取消赞。"
|
like-write: "点赞或取消赞。"
|
||||||
following-write: "关注或取消关注。"
|
following-write: "关注或取消关注。"
|
||||||
drive-read: "查看您的云盘"
|
drive-read: "查看您的网盘"
|
||||||
drive-write: "上传/删除您云盘中的文件。"
|
drive-write: "管理网盘文件。"
|
||||||
notification-read: "查看通知。"
|
notification-read: "查看通知。"
|
||||||
notification-write: "管理通知。"
|
notification-write: "管理通知。"
|
||||||
cancel: "取消"
|
cancel: "取消"
|
||||||
@ -324,7 +324,7 @@ common/views/components/messaging-room.form.vue:
|
|||||||
input-message-here: "在此键入信息"
|
input-message-here: "在此键入信息"
|
||||||
send: "发送"
|
send: "发送"
|
||||||
attach-from-local: "从电脑中添加文件"
|
attach-from-local: "从电脑中添加文件"
|
||||||
attach-from-drive: "从云盘中添加文件"
|
attach-from-drive: "从网盘中添加文件"
|
||||||
only-one-file-attached: "在信息中只允许添加一个附件"
|
only-one-file-attached: "在信息中只允许添加一个附件"
|
||||||
common/views/components/messaging-room.message.vue:
|
common/views/components/messaging-room.message.vue:
|
||||||
is-read: "已读"
|
is-read: "已读"
|
||||||
@ -354,10 +354,10 @@ common/views/components/note-menu.vue:
|
|||||||
remote: "显示原始投稿"
|
remote: "显示原始投稿"
|
||||||
common/views/components/user-menu.vue:
|
common/views/components/user-menu.vue:
|
||||||
mention: "提到"
|
mention: "提到"
|
||||||
mute: "免打扰"
|
mute: "屏蔽"
|
||||||
unmute: "解除免打扰"
|
unmute: "解除屏蔽"
|
||||||
block: "屏蔽"
|
block: "拉黑"
|
||||||
unblock: "取消屏蔽"
|
unblock: "取消拉黑"
|
||||||
push-to-list: "添加至列表"
|
push-to-list: "添加至列表"
|
||||||
select-list: "请选择一个列表"
|
select-list: "请选择一个列表"
|
||||||
report-abuse: "举报骚扰"
|
report-abuse: "举报骚扰"
|
||||||
@ -509,6 +509,13 @@ common/views/components/profile-editor.vue:
|
|||||||
email-address: "电子邮件地址"
|
email-address: "电子邮件地址"
|
||||||
email-verified: "电子邮件地址已验证"
|
email-verified: "电子邮件地址已验证"
|
||||||
email-not-verified: "邮件地址尚未验证。 请检查您的邮箱。"
|
email-not-verified: "邮件地址尚未验证。 请检查您的邮箱。"
|
||||||
|
export: "导出"
|
||||||
|
export-targets:
|
||||||
|
all-notes: "所有发帖"
|
||||||
|
following-list: "关注列表"
|
||||||
|
mute-list: "屏蔽列表"
|
||||||
|
blocking-list: "黑名单"
|
||||||
|
export-requested: "导出请求已提交。可能需要花一些时间。导出的文件将保存到网盘中。"
|
||||||
common/views/components/user-list-editor.vue:
|
common/views/components/user-list-editor.vue:
|
||||||
users: "用户"
|
users: "用户"
|
||||||
rename: "重命名列表"
|
rename: "重命名列表"
|
||||||
@ -552,14 +559,14 @@ common/views/widgets/tips.vue:
|
|||||||
tips-line2: "从 <kbd>p</kbd> 或者 <kbd>n</kbd>打开投稿表单"
|
tips-line2: "从 <kbd>p</kbd> 或者 <kbd>n</kbd>打开投稿表单"
|
||||||
tips-line3: "您可以在投稿表单上拖放文件。"
|
tips-line3: "您可以在投稿表单上拖放文件。"
|
||||||
tips-line4: "您可以将剪贴板中的图像粘贴到提交表单中。"
|
tips-line4: "您可以将剪贴板中的图像粘贴到提交表单中。"
|
||||||
tips-line5: "您可以通过将文件拖放到云盘来上传文件。"
|
tips-line5: "您可以通过将文件拖放到网盘来上传文件。"
|
||||||
tips-line6: "您可以通过在云盘中拖动文件夹来移动文件夹。"
|
tips-line6: "您可以通过在网盘中通过拖动操作来移动文件夹"
|
||||||
tips-line7: "您可以通过在文件夹中拖动文件夹来移动文件夹。"
|
tips-line7: "您可以通过在网盘中通过拖动操作来移动文件夹。"
|
||||||
tips-line8: "可以从设置中定制主页。"
|
tips-line8: "可以从设置中定制主页。"
|
||||||
tips-line9: "Misskey 根据 AGPLv3 获得许可。"
|
tips-line9: "Misskey 根据 AGPLv3 获得许可。"
|
||||||
tips-line10: "使用Time Machine(时光机)小部件可以轻松追溯到过去的时间轴。"
|
tips-line10: "使用Time Machine(时光机)小部件可以轻松追溯到过去的时间轴。"
|
||||||
tips-line11: "您可以点击“...”将帖子固定到用户页面"
|
tips-line11: "您可以点击“...”将帖子固定到用户页面"
|
||||||
tips-line13: "附在帖子上的所有文件都会保存到云盘中。"
|
tips-line13: "附在帖子上的所有文件都会保存到网盘中。"
|
||||||
tips-line14: "在自定义首页布局时,您可以右键单击窗口小部件以更改其设计。"
|
tips-line14: "在自定义首页布局时,您可以右键单击窗口小部件以更改其设计。"
|
||||||
tips-line17: "用“**”围绕文本将突出显示它。"
|
tips-line17: "用“**”围绕文本将突出显示它。"
|
||||||
tips-line19: "可以在浏览器外部分离多个窗口。"
|
tips-line19: "可以在浏览器外部分离多个窗口。"
|
||||||
@ -733,7 +740,7 @@ desktop/views/components/post-form.vue:
|
|||||||
renote-failed: "转发失败"
|
renote-failed: "转发失败"
|
||||||
posting: "发送中"
|
posting: "发送中"
|
||||||
attach-media-from-local: "从设备中添加媒体文件"
|
attach-media-from-local: "从设备中添加媒体文件"
|
||||||
attach-media-from-drive: "从云盘中添加媒体文件"
|
attach-media-from-drive: "从网盘中添加媒体文件"
|
||||||
attach-cancel: "删除附件"
|
attach-cancel: "删除附件"
|
||||||
insert-a-kao: "v('ω')v"
|
insert-a-kao: "v('ω')v"
|
||||||
create-poll: "创建一个投票"
|
create-poll: "创建一个投票"
|
||||||
@ -773,8 +780,8 @@ desktop/views/components/settings.vue:
|
|||||||
notification: "通知"
|
notification: "通知"
|
||||||
apps: "应用程序"
|
apps: "应用程序"
|
||||||
tags: "标签"
|
tags: "标签"
|
||||||
mute-and-block: "静音/屏蔽"
|
mute-and-block: "屏蔽/拉黑"
|
||||||
blocking: "屏蔽中"
|
blocking: "已拉黑"
|
||||||
security: "安全性"
|
security: "安全性"
|
||||||
signin: "登录历史"
|
signin: "登录历史"
|
||||||
password: "密码"
|
password: "密码"
|
||||||
@ -905,13 +912,13 @@ common/views/components/drive-settings.vue:
|
|||||||
in-use: "正在使用"
|
in-use: "正在使用"
|
||||||
stats: "统计"
|
stats: "统计"
|
||||||
common/views/components/mute-and-block.vue:
|
common/views/components/mute-and-block.vue:
|
||||||
mute-and-block: "静音/封锁"
|
mute-and-block: "屏蔽/拉黑"
|
||||||
mute: "静音"
|
mute: "屏蔽"
|
||||||
block: "封锁中"
|
block: "拉黑中"
|
||||||
no-muted-users: "没有静音的用户"
|
no-muted-users: "无屏蔽用户"
|
||||||
no-blocked-users: "没有封锁的用户"
|
no-blocked-users: "无拉黑的用户"
|
||||||
word-mute: "文字静音"
|
word-mute: "文字屏蔽"
|
||||||
muted-words: "静音的关键字"
|
muted-words: "屏蔽关键字"
|
||||||
muted-words-description: "使用空格分隔会产生AND规范,并且使用换行符分隔会产生OR规范"
|
muted-words-description: "使用空格分隔会产生AND规范,并且使用换行符分隔会产生OR规范"
|
||||||
save: "保存"
|
save: "保存"
|
||||||
common/views/components/password-settings.vue:
|
common/views/components/password-settings.vue:
|
||||||
@ -1000,15 +1007,19 @@ admin/views/index.vue:
|
|||||||
announcements: "公告"
|
announcements: "公告"
|
||||||
hashtags: "标签"
|
hashtags: "标签"
|
||||||
abuse: "举报垃圾信息"
|
abuse: "举报垃圾信息"
|
||||||
|
queue: "作业队列"
|
||||||
back-to-misskey: "返回 Misskey"
|
back-to-misskey: "返回 Misskey"
|
||||||
admin/views/dashboard.vue:
|
admin/views/dashboard.vue:
|
||||||
dashboard: "Dashboard"
|
dashboard: "Dashboard"
|
||||||
accounts: "账户"
|
accounts: "账户"
|
||||||
notes: "帖子"
|
notes: "帖子"
|
||||||
drive: "Misskey 云盘"
|
drive: "网盘"
|
||||||
instances: "例子"
|
instances: "例子"
|
||||||
this-instance: "此实例"
|
this-instance: "此实例"
|
||||||
federated: "联合"
|
federated: "联合"
|
||||||
|
admin/views/queue.vue:
|
||||||
|
operation: "操作"
|
||||||
|
remove-all-jobs: "清除所有作业"
|
||||||
admin/views/abuse.vue:
|
admin/views/abuse.vue:
|
||||||
title: "举报垃圾信息"
|
title: "举报垃圾信息"
|
||||||
target: "目标"
|
target: "目标"
|
||||||
@ -1027,11 +1038,11 @@ admin/views/instance.vue:
|
|||||||
maintainer-config: "管理员信息"
|
maintainer-config: "管理员信息"
|
||||||
maintainer-name: "管理员名称"
|
maintainer-name: "管理员名称"
|
||||||
maintainer-email: "联系管理员"
|
maintainer-email: "联系管理员"
|
||||||
drive-config: "云盘设置"
|
drive-config: "网盘设置"
|
||||||
cache-remote-files: "远程文件缓存"
|
cache-remote-files: "远程文件缓存"
|
||||||
cache-remote-files-desc: "如果没有此参数,则所有远程文件都将直接链接到其主机服务器。 这将是保存服务器存储的有效解决方案,但是对于设置禁用直接链接的用户而言,远程文件不可见,因为不会生成缩略图,从而增加流量。 建议启用此参数集。"
|
cache-remote-files-desc: "如果没有此参数,则所有远程文件都将直接链接到其主机服务器。 这将是保存服务器存储的有效解决方案,但是对于设置禁用直接链接的用户而言,远程文件不可见,因为不会生成缩略图,从而增加流量。 建议启用此参数集。"
|
||||||
local-drive-capacity-mb: "每个用户的云盘空间"
|
local-drive-capacity-mb: "每个用户的网盘空间"
|
||||||
remote-drive-capacity-mb: "每个远程用户的云盘容量"
|
remote-drive-capacity-mb: "每个远程用户的网盘容量"
|
||||||
mb: "以兆字节(Mbps)为单位"
|
mb: "以兆字节(Mbps)为单位"
|
||||||
recaptcha-config: "reCAPTCHA设置"
|
recaptcha-config: "reCAPTCHA设置"
|
||||||
recaptcha-info: "reCAPTCHA token是必要的. 请从 https://www.google.com/recaptcha/intro/ 获取。\n请注意, 该功能在中国大陆不可用。"
|
recaptcha-info: "reCAPTCHA token是必要的. 请从 https://www.google.com/recaptcha/intro/ 获取。\n请注意, 该功能在中国大陆不可用。"
|
||||||
@ -1095,7 +1106,7 @@ admin/views/charts.vue:
|
|||||||
federation: "联合"
|
federation: "联合"
|
||||||
notes: "投稿"
|
notes: "投稿"
|
||||||
users: "用户"
|
users: "用户"
|
||||||
drive: "Misskey 云盘"
|
drive: "网盘"
|
||||||
network: "网络"
|
network: "网络"
|
||||||
charts:
|
charts:
|
||||||
federation-instances: "实例数:增加/减少"
|
federation-instances: "实例数:增加/减少"
|
||||||
@ -1108,9 +1119,9 @@ admin/views/charts.vue:
|
|||||||
users-total: "用户总数"
|
users-total: "用户总数"
|
||||||
active-users: "活跃用户数"
|
active-users: "活跃用户数"
|
||||||
drive: "存储容量:增加/减少"
|
drive: "存储容量:增加/减少"
|
||||||
drive-total: "云盘总容量"
|
drive-total: "网盘总使用量"
|
||||||
drive-files: "云盘上的文件数:增加/减少"
|
drive-files: "网盘文件数量变化"
|
||||||
drive-files-total: "云盘上文件总数"
|
drive-files-total: "网盘文件总数"
|
||||||
network-requests: "请求"
|
network-requests: "请求"
|
||||||
network-time: "响应时间"
|
network-time: "响应时间"
|
||||||
network-usage: "网络流量"
|
network-usage: "网络流量"
|
||||||
@ -1175,6 +1186,7 @@ admin/views/users.vue:
|
|||||||
moderator: "版主"
|
moderator: "版主"
|
||||||
adminOrModerator: "管理员+版主"
|
adminOrModerator: "管理员+版主"
|
||||||
verified: "官方认证账户"
|
verified: "官方认证账户"
|
||||||
|
silenced: "已禁言"
|
||||||
suspended: "已冻结"
|
suspended: "已冻结"
|
||||||
origin:
|
origin:
|
||||||
title: "源自"
|
title: "源自"
|
||||||
@ -1222,6 +1234,64 @@ admin/views/announcements.vue:
|
|||||||
removed: "已删除"
|
removed: "已删除"
|
||||||
admin/views/hashtags.vue:
|
admin/views/hashtags.vue:
|
||||||
hided-tags: "隐藏标签"
|
hided-tags: "隐藏标签"
|
||||||
|
admin/views/federation.vue:
|
||||||
|
federation: "联合"
|
||||||
|
host: "主机名"
|
||||||
|
notes: "帖子"
|
||||||
|
users: "用户"
|
||||||
|
following: "正在关注"
|
||||||
|
followers: "关注者"
|
||||||
|
status: "状态"
|
||||||
|
latest-request-sent-at: "上次发送的请求"
|
||||||
|
latest-request-received-at: "上次收到的请求"
|
||||||
|
remove-all-following: "取消所有关注"
|
||||||
|
remove-all-following-info: "取消{host}的所有关注者。当实例不存在时执行。"
|
||||||
|
block: "拉黑"
|
||||||
|
marked-as-closed: "标记为已关闭"
|
||||||
|
lookup: "查询"
|
||||||
|
instances: "实例"
|
||||||
|
instance-not-registered: "实例未注册"
|
||||||
|
sort: "排序"
|
||||||
|
sorts:
|
||||||
|
caughtAtAsc: "注册时间从旧到新"
|
||||||
|
caughtAtDesc: "注册时间从新到旧"
|
||||||
|
lastCommunicatedAtAsc: "上次互动时间从旧到新"
|
||||||
|
lastCommunicatedAtDesc: "上次互动时间从新到旧"
|
||||||
|
notesAsc: "发帖数量从少到多"
|
||||||
|
notesDesc: "发帖数量从多到少"
|
||||||
|
usersAsc: "用户数从少到多"
|
||||||
|
usersDesc: "用户数从多到少"
|
||||||
|
followingAsc: "关注数从少到多"
|
||||||
|
followingDesc: "关注数从多到少"
|
||||||
|
followersAsc: "粉丝数从少到多"
|
||||||
|
followersDesc: "粉丝数从多到少"
|
||||||
|
driveUsageAsc: "网盘使用量从少到多"
|
||||||
|
driveUsageDesc: "网盘使用量从多到少"
|
||||||
|
driveFilesAsc: "网盘文件数从少到多"
|
||||||
|
driveFilesDesc: "网盘文件数从多到少"
|
||||||
|
state: "状态"
|
||||||
|
states:
|
||||||
|
all: "所有"
|
||||||
|
blocked: "已拉黑"
|
||||||
|
not-responding: "没有响应"
|
||||||
|
marked-as-closed: "已标记为已关闭"
|
||||||
|
result-is-truncated: "显示最前面的{n}项。"
|
||||||
|
charts: "图表"
|
||||||
|
chart-srcs:
|
||||||
|
requests: "请求"
|
||||||
|
users: "用户数量变化"
|
||||||
|
users-total: "用户总数"
|
||||||
|
notes: "发帖数变化"
|
||||||
|
notes-total: "帖子总数"
|
||||||
|
ff: "关注/被关注数量变化"
|
||||||
|
ff-total: "关注/被关注总数"
|
||||||
|
drive-usage: "网盘使用量变化"
|
||||||
|
drive-usage-total: "网盘总使用量"
|
||||||
|
drive-files: "网盘文件数量变化"
|
||||||
|
drive-files-total: "网盘文件总数"
|
||||||
|
chart-spans:
|
||||||
|
hour: "每小时"
|
||||||
|
day: "每天"
|
||||||
desktop/views/pages/welcome.vue:
|
desktop/views/pages/welcome.vue:
|
||||||
about: "更多信息..."
|
about: "更多信息..."
|
||||||
gotit: "没问题! "
|
gotit: "没问题! "
|
||||||
@ -1235,7 +1305,7 @@ desktop/views/pages/welcome.vue:
|
|||||||
powered-by-misskey: "Powered by <b>Misskey</b>."
|
powered-by-misskey: "Powered by <b>Misskey</b>."
|
||||||
info: "信息"
|
info: "信息"
|
||||||
desktop/views/pages/drive.vue:
|
desktop/views/pages/drive.vue:
|
||||||
title: "Misskey 云盘"
|
title: "Misskey 网盘"
|
||||||
desktop/views/pages/home-customize.vue:
|
desktop/views/pages/home-customize.vue:
|
||||||
title: "自定义首页布局"
|
title: "自定义首页布局"
|
||||||
desktop/views/pages/note.vue:
|
desktop/views/pages/note.vue:
|
||||||
@ -1315,7 +1385,7 @@ mobile/views/components/drive.vue:
|
|||||||
folder-count: "文件夹"
|
folder-count: "文件夹"
|
||||||
count-separator: ","
|
count-separator: ","
|
||||||
file-count: "文件"
|
file-count: "文件"
|
||||||
nothing-in-drive: "云盘上没有任何东西"
|
nothing-in-drive: "网盘为空"
|
||||||
folder-is-empty: "这文件夹是空的"
|
folder-is-empty: "这文件夹是空的"
|
||||||
prompt: "您想要干什么呢?(请输入数字):<1 → 上传文件 | 2 → 从URL上传文件 | 3 → 创建新文件夹 | 4 → 更改这个文件夹的名称 | 5 → 移动这个文件夹 | 6 → 删除这个文件夹>"
|
prompt: "您想要干什么呢?(请输入数字):<1 → 上传文件 | 2 → 从URL上传文件 | 3 → 创建新文件夹 | 4 → 更改这个文件夹的名称 | 5 → 移动这个文件夹 | 6 → 删除这个文件夹>"
|
||||||
deletion-alert: "抱歉! 删除文件夹功能尚未实现。"
|
deletion-alert: "抱歉! 删除文件夹功能尚未实现。"
|
||||||
@ -1557,6 +1627,7 @@ deck/deck.tl-column.vue:
|
|||||||
is-media-view: "媒体视图"
|
is-media-view: "媒体视图"
|
||||||
edit: "选项"
|
edit: "选项"
|
||||||
deck/deck.user-column.vue:
|
deck/deck.user-column.vue:
|
||||||
|
follows-you: "关注您"
|
||||||
posts: "帖子"
|
posts: "帖子"
|
||||||
following: "关注中"
|
following: "关注中"
|
||||||
followers: "关注者"
|
followers: "关注者"
|
||||||
@ -1610,7 +1681,7 @@ dev/views/new-app.vue:
|
|||||||
note-write: "投稿。"
|
note-write: "投稿。"
|
||||||
reaction-write: "添加或删除反应。"
|
reaction-write: "添加或删除反应。"
|
||||||
following-write: "关注和不关注"
|
following-write: "关注和不关注"
|
||||||
drive-read: "查看云盘"
|
drive-read: "查看网盘"
|
||||||
drive-write: "上传/删除云盘里的文件"
|
drive-write: "管理网盘文件。"
|
||||||
notification-read: "阅读您的通知"
|
notification-read: "阅读您的通知"
|
||||||
notification-write: "管理通知"
|
notification-write: "管理通知"
|
||||||
|
26
package.json
26
package.json
@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"author": "syuilo <i@syuilo.com>",
|
"author": "syuilo <i@syuilo.com>",
|
||||||
"version": "10.82.0",
|
"version": "10.84.1",
|
||||||
"clientVersion": "2.0.14114",
|
"clientVersion": "2.0.14252",
|
||||||
"codename": "nighthike",
|
"codename": "nighthike",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -23,14 +23,11 @@
|
|||||||
"test": "gulp test",
|
"test": "gulp test",
|
||||||
"format": "gulp format"
|
"format": "gulp format"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
|
||||||
"terser": "3.14.1"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-svg-core": "1.2.14",
|
"@fortawesome/fontawesome-svg-core": "1.2.14",
|
||||||
"@fortawesome/free-brands-svg-icons": "5.6.3",
|
"@fortawesome/free-brands-svg-icons": "5.7.1",
|
||||||
"@fortawesome/free-regular-svg-icons": "5.7.0",
|
"@fortawesome/free-regular-svg-icons": "5.7.0",
|
||||||
"@fortawesome/free-solid-svg-icons": "5.6.3",
|
"@fortawesome/free-solid-svg-icons": "5.7.1",
|
||||||
"@fortawesome/vue-fontawesome": "0.1.5",
|
"@fortawesome/vue-fontawesome": "0.1.5",
|
||||||
"@koa/cors": "2.2.3",
|
"@koa/cors": "2.2.3",
|
||||||
"@prezzemolo/rap": "0.1.2",
|
"@prezzemolo/rap": "0.1.2",
|
||||||
@ -70,7 +67,7 @@
|
|||||||
"@types/mkdirp": "0.5.2",
|
"@types/mkdirp": "0.5.2",
|
||||||
"@types/mocha": "5.2.5",
|
"@types/mocha": "5.2.5",
|
||||||
"@types/mongodb": "3.1.19",
|
"@types/mongodb": "3.1.19",
|
||||||
"@types/node": "10.12.18",
|
"@types/node": "10.12.21",
|
||||||
"@types/nodemailer": "4.6.5",
|
"@types/nodemailer": "4.6.5",
|
||||||
"@types/nprogress": "0.0.29",
|
"@types/nprogress": "0.0.29",
|
||||||
"@types/oauth": "0.9.1",
|
"@types/oauth": "0.9.1",
|
||||||
@ -99,7 +96,7 @@
|
|||||||
"@types/websocket": "0.0.40",
|
"@types/websocket": "0.0.40",
|
||||||
"@types/ws": "6.0.1",
|
"@types/ws": "6.0.1",
|
||||||
"animejs": "3.0.1",
|
"animejs": "3.0.1",
|
||||||
"apexcharts": "3.2.1",
|
"apexcharts": "3.3.0",
|
||||||
"autobind-decorator": "2.4.0",
|
"autobind-decorator": "2.4.0",
|
||||||
"autosize": "4.0.2",
|
"autosize": "4.0.2",
|
||||||
"autwh": "0.1.0",
|
"autwh": "0.1.0",
|
||||||
@ -144,12 +141,11 @@
|
|||||||
"hard-source-webpack-plugin": "0.13.1",
|
"hard-source-webpack-plugin": "0.13.1",
|
||||||
"html-minifier": "3.5.21",
|
"html-minifier": "3.5.21",
|
||||||
"http-signature": "1.2.0",
|
"http-signature": "1.2.0",
|
||||||
"insert-text-at-cursor": "0.1.1",
|
"insert-text-at-cursor": "0.1.2",
|
||||||
"is-root": "2.0.0",
|
"is-root": "2.0.0",
|
||||||
"is-svg": "3.0.0",
|
"is-svg": "3.0.0",
|
||||||
"is-url": "1.2.4",
|
|
||||||
"js-yaml": "3.12.1",
|
"js-yaml": "3.12.1",
|
||||||
"jsdom": "13.1.0",
|
"jsdom": "13.2.0",
|
||||||
"json5": "2.1.0",
|
"json5": "2.1.0",
|
||||||
"json5-loader": "1.0.1",
|
"json5-loader": "1.0.1",
|
||||||
"katex": "0.10.0",
|
"katex": "0.10.0",
|
||||||
@ -224,7 +220,7 @@
|
|||||||
"tmp": "0.0.33",
|
"tmp": "0.0.33",
|
||||||
"ts-loader": "5.3.3",
|
"ts-loader": "5.3.3",
|
||||||
"ts-node": "7.0.1",
|
"ts-node": "7.0.1",
|
||||||
"tslint": "5.12.0",
|
"tslint": "5.12.1",
|
||||||
"tslint-sonarts": "1.9.0",
|
"tslint-sonarts": "1.9.0",
|
||||||
"typescript": "3.2.4",
|
"typescript": "3.2.4",
|
||||||
"typescript-eslint-parser": "21.0.2",
|
"typescript-eslint-parser": "21.0.2",
|
||||||
@ -233,7 +229,7 @@
|
|||||||
"uuid": "3.3.2",
|
"uuid": "3.3.2",
|
||||||
"v-animate-css": "0.0.3",
|
"v-animate-css": "0.0.3",
|
||||||
"video-thumbnail-generator": "1.1.3",
|
"video-thumbnail-generator": "1.1.3",
|
||||||
"vue": "2.6.2",
|
"vue": "2.6.5",
|
||||||
"vue-color": "2.7.0",
|
"vue-color": "2.7.0",
|
||||||
"vue-content-loading": "1.5.3",
|
"vue-content-loading": "1.5.3",
|
||||||
"vue-cropperjs": "3.0.0",
|
"vue-cropperjs": "3.0.0",
|
||||||
@ -246,7 +242,7 @@
|
|||||||
"vue-sequential-entrance": "1.1.3",
|
"vue-sequential-entrance": "1.1.3",
|
||||||
"vue-style-loader": "4.1.2",
|
"vue-style-loader": "4.1.2",
|
||||||
"vue-svg-inline-loader": "1.2.10",
|
"vue-svg-inline-loader": "1.2.10",
|
||||||
"vue-template-compiler": "2.6.2",
|
"vue-template-compiler": "2.6.5",
|
||||||
"vuedraggable": "2.17.0",
|
"vuedraggable": "2.17.0",
|
||||||
"vuewordcloud": "18.7.11",
|
"vuewordcloud": "18.7.11",
|
||||||
"vuex": "3.1.0",
|
"vuex": "3.1.0",
|
||||||
|
@ -5,11 +5,17 @@ program
|
|||||||
.version(pkg.version)
|
.version(pkg.version)
|
||||||
.option('--no-daemons', 'Disable daemon processes (for debbuging)')
|
.option('--no-daemons', 'Disable daemon processes (for debbuging)')
|
||||||
.option('--disable-clustering', 'Disable clustering')
|
.option('--disable-clustering', 'Disable clustering')
|
||||||
.option('--disable-queue', 'Disable job queue')
|
.option('--disable-queue', 'Disable job queue processing')
|
||||||
|
.option('--only-server', 'Run server only (without job queue)')
|
||||||
|
.option('--only-queue', 'Pocessing job queue only (without server)')
|
||||||
.option('--quiet', 'Suppress all logs')
|
.option('--quiet', 'Suppress all logs')
|
||||||
.option('--verbose', 'Enable all logs')
|
.option('--verbose', 'Enable all logs')
|
||||||
|
.option('--with-log-time', 'Include timestamp for each logs')
|
||||||
.option('--slow', 'Delay all requests (for debbuging)')
|
.option('--slow', 'Delay all requests (for debbuging)')
|
||||||
.option('--color', 'This option is a dummy for some external program\'s (e.g. forever) issue.')
|
.option('--color', 'This option is a dummy for some external program\'s (e.g. forever) issue.')
|
||||||
.parse(process.argv);
|
.parse(process.argv);
|
||||||
|
|
||||||
|
/*if (process.env.MK_DISABLE_QUEUE)*/ program.disableQueue = true;
|
||||||
|
if (process.env.MK_ONLY_QUEUE) program.onlyQueue = true;
|
||||||
|
|
||||||
export { program };
|
export { program };
|
||||||
|
@ -124,7 +124,7 @@ export default Vue.extend({
|
|||||||
this.meta = meta;
|
this.meta = meta;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$root.api('instances', {
|
this.$root.api('federation/instances', {
|
||||||
sort: '+notes'
|
sort: '+notes'
|
||||||
}).then(instances => {
|
}).then(instances => {
|
||||||
for (const i of instances) {
|
for (const i of instances) {
|
||||||
|
488
src/client/app/admin/views/federation.vue
Normal file
488
src/client/app/admin/views/federation.vue
Normal file
@ -0,0 +1,488 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<ui-card>
|
||||||
|
<div slot="title"><fa :icon="faTerminal"/> {{ $t('federation') }}</div>
|
||||||
|
<section class="fit-top">
|
||||||
|
<ui-input class="target" v-model="target" type="text" @enter="showInstance()">
|
||||||
|
<span>{{ $t('host') }}</span>
|
||||||
|
</ui-input>
|
||||||
|
<ui-button @click="showInstance()"><fa :icon="faSearch"/> {{ $t('lookup') }}</ui-button>
|
||||||
|
|
||||||
|
<div class="instance" v-if="instance">
|
||||||
|
<ui-input :value="instance.host" type="text" readonly>
|
||||||
|
<span>{{ $t('host') }}</span>
|
||||||
|
</ui-input>
|
||||||
|
<ui-horizon-group inputs>
|
||||||
|
<ui-input :value="instance.notesCount | number" type="text" readonly>
|
||||||
|
<span>{{ $t('notes') }}</span>
|
||||||
|
</ui-input>
|
||||||
|
<ui-input :value="instance.usersCount | number" type="text" readonly>
|
||||||
|
<span>{{ $t('users') }}</span>
|
||||||
|
</ui-input>
|
||||||
|
</ui-horizon-group>
|
||||||
|
<ui-horizon-group inputs>
|
||||||
|
<ui-input :value="instance.followingCount | number" type="text" readonly>
|
||||||
|
<span>{{ $t('following') }}</span>
|
||||||
|
</ui-input>
|
||||||
|
<ui-input :value="instance.followersCount | number" type="text" readonly>
|
||||||
|
<span>{{ $t('followers') }}</span>
|
||||||
|
</ui-input>
|
||||||
|
</ui-horizon-group>
|
||||||
|
<ui-horizon-group inputs>
|
||||||
|
<ui-input :value="instance.latestRequestSentAt" type="text" readonly>
|
||||||
|
<span>{{ $t('latest-request-sent-at') }}</span>
|
||||||
|
</ui-input>
|
||||||
|
<ui-input :value="instance.latestStatus" type="text" readonly>
|
||||||
|
<span>{{ $t('status') }}</span>
|
||||||
|
</ui-input>
|
||||||
|
</ui-horizon-group>
|
||||||
|
<ui-input :value="instance.latestRequestReceivedAt" type="text" readonly>
|
||||||
|
<span>{{ $t('latest-request-received-at') }}</span>
|
||||||
|
</ui-input>
|
||||||
|
<ui-switch v-model="instance.isBlocked" @change="updateInstance()">{{ $t('block') }}</ui-switch>
|
||||||
|
<ui-switch v-model="instance.isMarkedAsClosed" @change="updateInstance()">{{ $t('marked-as-closed') }}</ui-switch>
|
||||||
|
<details>
|
||||||
|
<summary>{{ $t('charts') }}</summary>
|
||||||
|
<ui-horizon-group inputs>
|
||||||
|
<ui-select v-model="chartSrc">
|
||||||
|
<option value="requests">{{ $t('chart-srcs.requests') }}</option>
|
||||||
|
<option value="users">{{ $t('chart-srcs.users') }}</option>
|
||||||
|
<option value="users-total">{{ $t('chart-srcs.users-total') }}</option>
|
||||||
|
<option value="notes">{{ $t('chart-srcs.notes') }}</option>
|
||||||
|
<option value="notes-total">{{ $t('chart-srcs.notes-total') }}</option>
|
||||||
|
<option value="ff">{{ $t('chart-srcs.ff') }}</option>
|
||||||
|
<option value="ff-total">{{ $t('chart-srcs.ff-total') }}</option>
|
||||||
|
<option value="drive-usage">{{ $t('chart-srcs.drive-usage') }}</option>
|
||||||
|
<option value="drive-usage-total">{{ $t('chart-srcs.drive-usage-total') }}</option>
|
||||||
|
<option value="drive-files">{{ $t('chart-srcs.drive-files') }}</option>
|
||||||
|
<option value="drive-files-total">{{ $t('chart-srcs.drive-files-total') }}</option>
|
||||||
|
</ui-select>
|
||||||
|
<ui-select v-model="chartSpan">
|
||||||
|
<option value="hour">{{ $t('chart-spans.hour') }}</option>
|
||||||
|
<option value="day">{{ $t('chart-spans.day') }}</option>
|
||||||
|
</ui-select>
|
||||||
|
</ui-horizon-group>
|
||||||
|
<div ref="chart"></div>
|
||||||
|
</details>
|
||||||
|
<details>
|
||||||
|
<summary>{{ $t('remove-all-following') }}</summary>
|
||||||
|
<ui-button @click="removeAllFollowing()" style="margin-top: 16px;"><fa :icon="faMinusCircle"/> {{ $t('remove-all-following') }}</ui-button>
|
||||||
|
<ui-info warn>{{ $t('remove-all-following-info', { host: instance.host }) }}</ui-info>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</ui-card>
|
||||||
|
|
||||||
|
<ui-card>
|
||||||
|
<div slot="title"><fa :icon="faServer"/> {{ $t('instances') }}</div>
|
||||||
|
<section class="fit-top">
|
||||||
|
<ui-horizon-group inputs>
|
||||||
|
<ui-select v-model="sort">
|
||||||
|
<span slot="label">{{ $t('sort') }}</span>
|
||||||
|
<option value="-caughtAt">{{ $t('sorts.caughtAtAsc') }}</option>
|
||||||
|
<option value="+caughtAt">{{ $t('sorts.caughtAtDesc') }}</option>
|
||||||
|
<option value="-lastCommunicatedAt">{{ $t('sorts.lastCommunicatedAtAsc') }}</option>
|
||||||
|
<option value="+lastCommunicatedAt">{{ $t('sorts.lastCommunicatedAtDesc') }}</option>
|
||||||
|
<option value="-notes">{{ $t('sorts.notesAsc') }}</option>
|
||||||
|
<option value="+notes">{{ $t('sorts.notesDesc') }}</option>
|
||||||
|
<option value="-users">{{ $t('sorts.usersAsc') }}</option>
|
||||||
|
<option value="+users">{{ $t('sorts.usersDesc') }}</option>
|
||||||
|
<option value="-following">{{ $t('sorts.followingAsc') }}</option>
|
||||||
|
<option value="+following">{{ $t('sorts.followingDesc') }}</option>
|
||||||
|
<option value="-followers">{{ $t('sorts.followersAsc') }}</option>
|
||||||
|
<option value="+followers">{{ $t('sorts.followersDesc') }}</option>
|
||||||
|
<option value="-driveUsage">{{ $t('sorts.driveUsageAsc') }}</option>
|
||||||
|
<option value="+driveUsage">{{ $t('sorts.driveUsageDesc') }}</option>
|
||||||
|
<option value="-driveFiles">{{ $t('sorts.driveFilesAsc') }}</option>
|
||||||
|
<option value="+driveFiles">{{ $t('sorts.driveFilesDesc') }}</option>
|
||||||
|
</ui-select>
|
||||||
|
<ui-select v-model="state">
|
||||||
|
<span slot="label">{{ $t('state') }}</span>
|
||||||
|
<option value="all">{{ $t('states.all') }}</option>
|
||||||
|
<option value="blocked">{{ $t('states.blocked') }}</option>
|
||||||
|
<option value="notResponding">{{ $t('states.not-responding') }}</option>
|
||||||
|
<option value="markedAsClosed">{{ $t('states.marked-as-closed') }}</option>
|
||||||
|
</ui-select>
|
||||||
|
</ui-horizon-group>
|
||||||
|
|
||||||
|
<div class="instances">
|
||||||
|
<header>
|
||||||
|
<span>{{ $t('host') }}</span>
|
||||||
|
<span>{{ $t('notes') }}</span>
|
||||||
|
<span>{{ $t('users') }}</span>
|
||||||
|
<span>{{ $t('following') }}</span>
|
||||||
|
<span>{{ $t('followers') }}</span>
|
||||||
|
<span>{{ $t('status') }}</span>
|
||||||
|
</header>
|
||||||
|
<div v-for="instance in instances" :style="{ opacity: instance.isNotResponding ? 0.5 : 1 }">
|
||||||
|
<a @click.prevent="showInstance(instance.host)" target="_blank" :href="`https://${instance.host}`" :style="{ textDecoration: instance.isMarkedAsClosed ? 'line-through' : 'none' }">{{ instance.host }}</a>
|
||||||
|
<span>{{ instance.notesCount | number }}</span>
|
||||||
|
<span>{{ instance.usersCount | number }}</span>
|
||||||
|
<span>{{ instance.followingCount | number }}</span>
|
||||||
|
<span>{{ instance.followersCount | number }}</span>
|
||||||
|
<span>{{ instance.latestStatus }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ui-info v-if="instances.length == limit">{{ $t('result-is-truncated', { n: limit }) }}</ui-info>
|
||||||
|
</section>
|
||||||
|
</ui-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import i18n from '../../i18n';
|
||||||
|
import { faGlobe, faTerminal, faSearch, faMinusCircle, faServer } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import ApexCharts from 'apexcharts';
|
||||||
|
import * as tinycolor from 'tinycolor2';
|
||||||
|
|
||||||
|
const chartLimit = 90;
|
||||||
|
const sum = (...arr) => arr.reduce((r, a) => r.map((b, i) => a[i] + b));
|
||||||
|
const negate = arr => arr.map(x => -x);
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
i18n: i18n('admin/views/federation.vue'),
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
instance: null,
|
||||||
|
target: null,
|
||||||
|
sort: '+lastCommunicatedAt',
|
||||||
|
state: 'all',
|
||||||
|
limit: 50,
|
||||||
|
instances: [],
|
||||||
|
chart: null,
|
||||||
|
chartSrc: 'requests',
|
||||||
|
chartSpan: 'hour',
|
||||||
|
chartInstance: null,
|
||||||
|
faGlobe, faTerminal, faSearch, faMinusCircle, faServer
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
data(): any {
|
||||||
|
if (this.chart == null) return null;
|
||||||
|
switch (this.chartSrc) {
|
||||||
|
case 'requests': return this.requestsChart();
|
||||||
|
case 'users': return this.usersChart(false);
|
||||||
|
case 'users-total': return this.usersChart(true);
|
||||||
|
case 'notes': return this.notesChart(false);
|
||||||
|
case 'notes-total': return this.notesChart(true);
|
||||||
|
case 'ff': return this.ffChart(false);
|
||||||
|
case 'ff-total': return this.ffChart(true);
|
||||||
|
case 'drive-usage': return this.driveUsageChart(false);
|
||||||
|
case 'drive-usage-total': return this.driveUsageChart(true);
|
||||||
|
case 'drive-files': return this.driveFilesChart(false);
|
||||||
|
case 'drive-files-total': return this.driveFilesChart(true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
stats(): any[] {
|
||||||
|
const stats =
|
||||||
|
this.chartSpan == 'day' ? this.chart.perDay :
|
||||||
|
this.chartSpan == 'hour' ? this.chart.perHour :
|
||||||
|
null;
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
sort() {
|
||||||
|
this.fetchInstances();
|
||||||
|
},
|
||||||
|
|
||||||
|
state() {
|
||||||
|
this.fetchInstances();
|
||||||
|
},
|
||||||
|
|
||||||
|
async instance() {
|
||||||
|
this.now = new Date();
|
||||||
|
|
||||||
|
const [perHour, perDay] = await Promise.all([
|
||||||
|
this.$root.api('charts/instance', { host: this.instance.host, limit: chartLimit, span: 'hour' }),
|
||||||
|
this.$root.api('charts/instance', { host: this.instance.host, limit: chartLimit, span: 'day' }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const chart = {
|
||||||
|
perHour: perHour,
|
||||||
|
perDay: perDay
|
||||||
|
};
|
||||||
|
|
||||||
|
this.chart = chart;
|
||||||
|
|
||||||
|
this.renderChart();
|
||||||
|
},
|
||||||
|
|
||||||
|
chartSrc() {
|
||||||
|
this.renderChart();
|
||||||
|
},
|
||||||
|
|
||||||
|
chartSpan() {
|
||||||
|
this.renderChart();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.fetchInstances();
|
||||||
|
},
|
||||||
|
|
||||||
|
beforeDestroy() {
|
||||||
|
this.chartInstance.destroy();
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
showInstance(target?: string) {
|
||||||
|
this.$root.api('federation/show-instance', {
|
||||||
|
host: target || this.target
|
||||||
|
}).then(instance => {
|
||||||
|
if (instance == null) {
|
||||||
|
this.$root.dialog({
|
||||||
|
type: 'error',
|
||||||
|
text: this.$t('instance-not-registered')
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.instance = instance;
|
||||||
|
this.target = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
fetchInstances() {
|
||||||
|
this.instances = [];
|
||||||
|
this.$root.api('federation/instances', {
|
||||||
|
blocked: this.state === 'blocked' ? true : null,
|
||||||
|
notResponding: this.state === 'notResponding' ? true : null,
|
||||||
|
markedAsClosed: this.state === 'markedAsClosed' ? true : null,
|
||||||
|
sort: this.sort,
|
||||||
|
limit: this.limit
|
||||||
|
}).then(instances => {
|
||||||
|
this.instances = instances;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
removeAllFollowing() {
|
||||||
|
this.$root.api('admin/federation/remove-all-following', {
|
||||||
|
host: this.instance.host
|
||||||
|
}).then(() => {
|
||||||
|
this.$root.dialog({
|
||||||
|
type: 'success',
|
||||||
|
splash: true
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
updateInstance() {
|
||||||
|
this.$root.api('admin/federation/update-instance', {
|
||||||
|
host: this.instance.host,
|
||||||
|
isBlocked: this.instance.isBlocked || false,
|
||||||
|
isClosed: this.instance.isMarkedAsClosed || false
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setSrc(src) {
|
||||||
|
this.chartSrc = src;
|
||||||
|
},
|
||||||
|
|
||||||
|
renderChart() {
|
||||||
|
if (this.chartInstance) {
|
||||||
|
this.chartInstance.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.chartInstance = new ApexCharts(this.$refs.chart, {
|
||||||
|
chart: {
|
||||||
|
type: 'area',
|
||||||
|
height: 300,
|
||||||
|
animations: {
|
||||||
|
dynamicAnimation: {
|
||||||
|
enabled: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
toolbar: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
zoom: {
|
||||||
|
enabled: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dataLabels: {
|
||||||
|
enabled: false
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
clipMarkers: false,
|
||||||
|
borderColor: 'rgba(0, 0, 0, 0.1)'
|
||||||
|
},
|
||||||
|
stroke: {
|
||||||
|
curve: 'straight',
|
||||||
|
width: 2
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
theme: this.$store.state.device.darkmode ? 'dark' : 'light'
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
labels: {
|
||||||
|
colors: tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--text')).toRgbString()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
xaxis: {
|
||||||
|
type: 'datetime',
|
||||||
|
labels: {
|
||||||
|
style: {
|
||||||
|
colors: tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--text')).toRgbString()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axisBorder: {
|
||||||
|
color: 'rgba(0, 0, 0, 0.1)'
|
||||||
|
},
|
||||||
|
axisTicks: {
|
||||||
|
color: 'rgba(0, 0, 0, 0.1)'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
yaxis: {
|
||||||
|
labels: {
|
||||||
|
formatter: this.data.bytes ? v => Vue.filter('bytes')(v, 0) : v => Vue.filter('number')(v),
|
||||||
|
style: {
|
||||||
|
color: tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--text')).toRgbString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: this.data.series
|
||||||
|
});
|
||||||
|
|
||||||
|
this.chartInstance.render();
|
||||||
|
},
|
||||||
|
|
||||||
|
getDate(i: number) {
|
||||||
|
const y = this.now.getFullYear();
|
||||||
|
const m = this.now.getMonth();
|
||||||
|
const d = this.now.getDate();
|
||||||
|
const h = this.now.getHours();
|
||||||
|
|
||||||
|
return (
|
||||||
|
this.chartSpan == 'day' ? new Date(y, m, d - i) :
|
||||||
|
this.chartSpan == 'hour' ? new Date(y, m, d, h - i) :
|
||||||
|
null
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
format(arr) {
|
||||||
|
return arr.map((v, i) => ({ x: this.getDate(i).getTime(), y: v }));
|
||||||
|
},
|
||||||
|
|
||||||
|
requestsChart(): any {
|
||||||
|
return {
|
||||||
|
series: [{
|
||||||
|
name: 'Incoming',
|
||||||
|
data: this.format(this.stats.requests.received)
|
||||||
|
}, {
|
||||||
|
name: 'Outgoing (succeeded)',
|
||||||
|
data: this.format(this.stats.requests.succeeded)
|
||||||
|
}, {
|
||||||
|
name: 'Outgoing (failed)',
|
||||||
|
data: this.format(this.stats.requests.failed)
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
usersChart(total: boolean): any {
|
||||||
|
return {
|
||||||
|
series: [{
|
||||||
|
name: 'Users',
|
||||||
|
type: 'area',
|
||||||
|
data: this.format(total
|
||||||
|
? this.stats.users.total
|
||||||
|
: sum(this.stats.users.inc, negate(this.stats.users.dec))
|
||||||
|
)
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
notesChart(total: boolean): any {
|
||||||
|
return {
|
||||||
|
series: [{
|
||||||
|
name: 'Notes',
|
||||||
|
type: 'area',
|
||||||
|
data: this.format(total
|
||||||
|
? this.stats.notes.total
|
||||||
|
: sum(this.stats.notes.inc, negate(this.stats.notes.dec))
|
||||||
|
)
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
ffChart(total: boolean): any {
|
||||||
|
return {
|
||||||
|
series: [{
|
||||||
|
name: 'Following',
|
||||||
|
type: 'area',
|
||||||
|
data: this.format(total
|
||||||
|
? this.stats.following.total
|
||||||
|
: sum(this.stats.following.inc, negate(this.stats.following.dec))
|
||||||
|
)
|
||||||
|
}, {
|
||||||
|
name: 'Followers',
|
||||||
|
type: 'area',
|
||||||
|
data: this.format(total
|
||||||
|
? this.stats.followers.total
|
||||||
|
: sum(this.stats.followers.inc, negate(this.stats.followers.dec))
|
||||||
|
)
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
driveUsageChart(total: boolean): any {
|
||||||
|
return {
|
||||||
|
bytes: true,
|
||||||
|
series: [{
|
||||||
|
name: 'Drive usage',
|
||||||
|
type: 'area',
|
||||||
|
data: this.format(total
|
||||||
|
? this.stats.drive.totalUsage
|
||||||
|
: sum(this.stats.drive.incUsage, negate(this.stats.drive.decUsage))
|
||||||
|
)
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
driveFilesChart(total: boolean): any {
|
||||||
|
return {
|
||||||
|
series: [{
|
||||||
|
name: 'Drive files',
|
||||||
|
type: 'area',
|
||||||
|
data: this.format(total
|
||||||
|
? this.stats.drive.totalFiles
|
||||||
|
: sum(this.stats.drive.incFiles, negate(this.stats.drive.decFiles))
|
||||||
|
)
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
.target
|
||||||
|
margin-bottom 16px !important
|
||||||
|
|
||||||
|
.instances
|
||||||
|
width 100%
|
||||||
|
|
||||||
|
> header
|
||||||
|
display flex
|
||||||
|
|
||||||
|
> *
|
||||||
|
color var(--text)
|
||||||
|
font-weight bold
|
||||||
|
|
||||||
|
> div
|
||||||
|
display flex
|
||||||
|
|
||||||
|
> * > *
|
||||||
|
flex 1
|
||||||
|
overflow auto
|
||||||
|
|
||||||
|
&:first-child
|
||||||
|
min-width 200px
|
||||||
|
|
||||||
|
</style>
|
@ -20,10 +20,11 @@
|
|||||||
<ul>
|
<ul>
|
||||||
<li @click="nav('dashboard')" :class="{ active: page == 'dashboard' }"><fa icon="home" fixed-width/>{{ $t('dashboard') }}</li>
|
<li @click="nav('dashboard')" :class="{ active: page == 'dashboard' }"><fa icon="home" fixed-width/>{{ $t('dashboard') }}</li>
|
||||||
<li @click="nav('instance')" :class="{ active: page == 'instance' }"><fa icon="cog" fixed-width/>{{ $t('instance') }}</li>
|
<li @click="nav('instance')" :class="{ active: page == 'instance' }"><fa icon="cog" fixed-width/>{{ $t('instance') }}</li>
|
||||||
|
<li @click="nav('queue')" :class="{ active: page == 'queue' }"><fa :icon="faTasks" fixed-width/>{{ $t('queue') }}</li>
|
||||||
<li @click="nav('moderators')" :class="{ active: page == 'moderators' }"><fa :icon="faHeadset" fixed-width/>{{ $t('moderators') }}</li>
|
<li @click="nav('moderators')" :class="{ active: page == 'moderators' }"><fa :icon="faHeadset" fixed-width/>{{ $t('moderators') }}</li>
|
||||||
<li @click="nav('users')" :class="{ active: page == 'users' }"><fa icon="users" fixed-width/>{{ $t('users') }}</li>
|
<li @click="nav('users')" :class="{ active: page == 'users' }"><fa icon="users" fixed-width/>{{ $t('users') }}</li>
|
||||||
<li @click="nav('drive')" :class="{ active: page == 'drive' }"><fa icon="cloud" fixed-width/>{{ $t('@.drive') }}</li>
|
<li @click="nav('drive')" :class="{ active: page == 'drive' }"><fa icon="cloud" fixed-width/>{{ $t('@.drive') }}</li>
|
||||||
<!-- <li @click="nav('federation')" :class="{ active: page == 'federation' }"><fa :icon="faShareAlt" fixed-width/>{{ $t('federation') }}</li> -->
|
<li @click="nav('federation')" :class="{ active: page == 'federation' }"><fa :icon="faGlobe" fixed-width/>{{ $t('federation') }}</li>
|
||||||
<li @click="nav('emoji')" :class="{ active: page == 'emoji' }"><fa :icon="faGrin" fixed-width/>{{ $t('emoji') }}</li>
|
<li @click="nav('emoji')" :class="{ active: page == 'emoji' }"><fa :icon="faGrin" fixed-width/>{{ $t('emoji') }}</li>
|
||||||
<li @click="nav('announcements')" :class="{ active: page == 'announcements' }"><fa icon="broadcast-tower" fixed-width/>{{ $t('announcements') }}</li>
|
<li @click="nav('announcements')" :class="{ active: page == 'announcements' }"><fa icon="broadcast-tower" fixed-width/>{{ $t('announcements') }}</li>
|
||||||
<li @click="nav('hashtags')" :class="{ active: page == 'hashtags' }"><fa icon="hashtag" fixed-width/>{{ $t('hashtags') }}</li>
|
<li @click="nav('hashtags')" :class="{ active: page == 'hashtags' }"><fa icon="hashtag" fixed-width/>{{ $t('hashtags') }}</li>
|
||||||
@ -40,12 +41,14 @@
|
|||||||
<div class="page">
|
<div class="page">
|
||||||
<div v-if="page == 'dashboard'"><x-dashboard/></div>
|
<div v-if="page == 'dashboard'"><x-dashboard/></div>
|
||||||
<div v-if="page == 'instance'"><x-instance/></div>
|
<div v-if="page == 'instance'"><x-instance/></div>
|
||||||
|
<div v-if="page == 'queue'"><x-queue/></div>
|
||||||
<div v-if="page == 'moderators'"><x-moderators/></div>
|
<div v-if="page == 'moderators'"><x-moderators/></div>
|
||||||
<div v-if="page == 'users'"><x-users/></div>
|
<div v-if="page == 'users'"><x-users/></div>
|
||||||
<div v-if="page == 'emoji'"><x-emoji/></div>
|
<div v-if="page == 'emoji'"><x-emoji/></div>
|
||||||
<div v-if="page == 'announcements'"><x-announcements/></div>
|
<div v-if="page == 'announcements'"><x-announcements/></div>
|
||||||
<div v-if="page == 'hashtags'"><x-hashtags/></div>
|
<div v-if="page == 'hashtags'"><x-hashtags/></div>
|
||||||
<div v-if="page == 'drive'"><x-drive/></div>
|
<div v-if="page == 'drive'"><x-drive/></div>
|
||||||
|
<div v-if="page == 'federation'"><x-federation/></div>
|
||||||
<div v-if="page == 'abuse'"><x-abuse/></div>
|
<div v-if="page == 'abuse'"><x-abuse/></div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
@ -58,6 +61,7 @@ import i18n from '../../i18n';
|
|||||||
import { version } from '../../config';
|
import { version } from '../../config';
|
||||||
import XDashboard from "./dashboard.vue";
|
import XDashboard from "./dashboard.vue";
|
||||||
import XInstance from "./instance.vue";
|
import XInstance from "./instance.vue";
|
||||||
|
import XQueue from "./queue.vue";
|
||||||
import XModerators from "./moderators.vue";
|
import XModerators from "./moderators.vue";
|
||||||
import XEmoji from "./emoji.vue";
|
import XEmoji from "./emoji.vue";
|
||||||
import XAnnouncements from "./announcements.vue";
|
import XAnnouncements from "./announcements.vue";
|
||||||
@ -65,7 +69,9 @@ import XHashtags from "./hashtags.vue";
|
|||||||
import XUsers from "./users.vue";
|
import XUsers from "./users.vue";
|
||||||
import XDrive from "./drive.vue";
|
import XDrive from "./drive.vue";
|
||||||
import XAbuse from "./abuse.vue";
|
import XAbuse from "./abuse.vue";
|
||||||
import { faHeadset, faArrowLeft, faShareAlt, faExclamationCircle } from '@fortawesome/free-solid-svg-icons';
|
import XFederation from "./federation.vue";
|
||||||
|
|
||||||
|
import { faHeadset, faArrowLeft, faGlobe, faExclamationCircle, faTasks } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { faGrin } from '@fortawesome/free-regular-svg-icons';
|
import { faGrin } from '@fortawesome/free-regular-svg-icons';
|
||||||
|
|
||||||
// Detect the user agent
|
// Detect the user agent
|
||||||
@ -77,6 +83,7 @@ export default Vue.extend({
|
|||||||
components: {
|
components: {
|
||||||
XDashboard,
|
XDashboard,
|
||||||
XInstance,
|
XInstance,
|
||||||
|
XQueue,
|
||||||
XModerators,
|
XModerators,
|
||||||
XEmoji,
|
XEmoji,
|
||||||
XAnnouncements,
|
XAnnouncements,
|
||||||
@ -84,6 +91,7 @@ export default Vue.extend({
|
|||||||
XUsers,
|
XUsers,
|
||||||
XDrive,
|
XDrive,
|
||||||
XAbuse,
|
XAbuse,
|
||||||
|
XFederation,
|
||||||
},
|
},
|
||||||
provide: {
|
provide: {
|
||||||
isMobile
|
isMobile
|
||||||
@ -97,8 +105,9 @@ export default Vue.extend({
|
|||||||
faGrin,
|
faGrin,
|
||||||
faArrowLeft,
|
faArrowLeft,
|
||||||
faHeadset,
|
faHeadset,
|
||||||
faShareAlt,
|
faGlobe,
|
||||||
faExclamationCircle
|
faExclamationCircle,
|
||||||
|
faTasks
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -6,8 +6,10 @@
|
|||||||
<ui-input v-model="username" type="text">
|
<ui-input v-model="username" type="text">
|
||||||
<span slot="prefix">@</span>
|
<span slot="prefix">@</span>
|
||||||
</ui-input>
|
</ui-input>
|
||||||
|
<ui-horizon-group>
|
||||||
<ui-button @click="add" :disabled="changing">{{ $t('add-moderator.add') }}</ui-button>
|
<ui-button @click="add" :disabled="changing">{{ $t('add-moderator.add') }}</ui-button>
|
||||||
<ui-button @click="remove" :disabled="changing">{{ $t('add-moderator.remove') }}</ui-button>
|
<ui-button @click="remove" :disabled="changing">{{ $t('add-moderator.remove') }}</ui-button>
|
||||||
|
</ui-horizon-group>
|
||||||
</section>
|
</section>
|
||||||
</ui-card>
|
</ui-card>
|
||||||
</div>
|
</div>
|
||||||
|
43
src/client/app/admin/views/queue.vue
Normal file
43
src/client/app/admin/views/queue.vue
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<ui-card>
|
||||||
|
<div slot="title">{{ $t('operation') }}</div>
|
||||||
|
<section>
|
||||||
|
<ui-button @click="removeAllJobs">{{ $t('remove-all-jobs') }}</ui-button>
|
||||||
|
</section>
|
||||||
|
</ui-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import i18n from '../../i18n';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
i18n: i18n('admin/views/queue.vue'),
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
async removeAllJobs() {
|
||||||
|
const process = async () => {
|
||||||
|
await this.$root.api('admin/queue/clear');
|
||||||
|
this.$root.dialog({
|
||||||
|
type: 'success',
|
||||||
|
splash: true
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
await process().catch(e => {
|
||||||
|
this.$root.dialog({
|
||||||
|
type: 'error',
|
||||||
|
text: e.toString()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
@ -48,6 +48,7 @@
|
|||||||
<option value="admin">{{ $t('users.state.admin') }}</option>
|
<option value="admin">{{ $t('users.state.admin') }}</option>
|
||||||
<option value="moderator">{{ $t('users.state.moderator') }}</option>
|
<option value="moderator">{{ $t('users.state.moderator') }}</option>
|
||||||
<option value="verified">{{ $t('users.state.verified') }}</option>
|
<option value="verified">{{ $t('users.state.verified') }}</option>
|
||||||
|
<option value="silenced">{{ $t('users.state.silenced') }}</option>
|
||||||
<option value="suspended">{{ $t('users.state.suspended') }}</option>
|
<option value="suspended">{{ $t('users.state.suspended') }}</option>
|
||||||
</ui-select>
|
</ui-select>
|
||||||
<ui-select v-model="origin">
|
<ui-select v-model="origin">
|
||||||
@ -89,7 +90,7 @@ export default Vue.extend({
|
|||||||
unsuspending: false,
|
unsuspending: false,
|
||||||
sort: '+createdAt',
|
sort: '+createdAt',
|
||||||
state: 'all',
|
state: 'all',
|
||||||
origin: 'combined',
|
origin: 'local',
|
||||||
limit: 10,
|
limit: 10,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
users: [],
|
users: [],
|
||||||
@ -129,16 +130,25 @@ export default Vue.extend({
|
|||||||
const usernamePromise = this.$root.api('users/show', parseAcct(this.target));
|
const usernamePromise = this.$root.api('users/show', parseAcct(this.target));
|
||||||
const idPromise = this.$root.api('users/show', { userId: this.target });
|
const idPromise = this.$root.api('users/show', { userId: this.target });
|
||||||
|
|
||||||
usernamePromise.then(res);
|
let _notFound = false;
|
||||||
idPromise.then(res);
|
const notFound = () => {
|
||||||
|
if (_notFound) {
|
||||||
idPromise.catch(e => {
|
|
||||||
if (e == 'user not found') {
|
|
||||||
this.$root.dialog({
|
this.$root.dialog({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
text: this.$t('user-not-found')
|
text: this.$t('user-not-found')
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
_notFound = true;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
usernamePromise.then(res).catch(e => {
|
||||||
|
if (e == 'user not found') {
|
||||||
|
notFound();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
idPromise.then(res).catch(e => {
|
||||||
|
notFound();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -420,7 +420,7 @@ export default Vue.extend({
|
|||||||
margin 0
|
margin 0
|
||||||
padding 16px
|
padding 16px
|
||||||
text-align center
|
text-align center
|
||||||
color #aaa
|
color var(--text)
|
||||||
|
|
||||||
> [data-icon]
|
> [data-icon]
|
||||||
margin-right 4px
|
margin-right 4px
|
||||||
|
@ -92,7 +92,13 @@
|
|||||||
<header>{{ $t('export') }}</header>
|
<header>{{ $t('export') }}</header>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<ui-button @click="exportNotes()">{{ $t('export-notes') }}</ui-button>
|
<ui-select v-model="exportTarget">
|
||||||
|
<option value="notes">{{ $t('export-targets.all-notes') }}</option>
|
||||||
|
<option value="following">{{ $t('export-targets.following-list') }}</option>
|
||||||
|
<option value="mute">{{ $t('export-targets.mute-list') }}</option>
|
||||||
|
<option value="blocking">{{ $t('export-targets.blocking-list') }}</option>
|
||||||
|
</ui-select>
|
||||||
|
<ui-button @click="doExport()"><fa :icon="faDownload"/> {{ $t('export') }}</ui-button>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</ui-card>
|
</ui-card>
|
||||||
@ -105,6 +111,7 @@ import { apiUrl, host } from '../../../config';
|
|||||||
import { toUnicode } from 'punycode';
|
import { toUnicode } from 'punycode';
|
||||||
import langmap from 'langmap';
|
import langmap from 'langmap';
|
||||||
import { unique } from '../../../../../prelude/array';
|
import { unique } from '../../../../../prelude/array';
|
||||||
|
import { faDownload } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
i18n: i18n('common/views/components/profile-editor.vue'),
|
i18n: i18n('common/views/components/profile-editor.vue'),
|
||||||
@ -131,7 +138,9 @@ export default Vue.extend({
|
|||||||
autoAcceptFollowed: false,
|
autoAcceptFollowed: false,
|
||||||
saving: false,
|
saving: false,
|
||||||
avatarUploading: false,
|
avatarUploading: false,
|
||||||
bannerUploading: false
|
bannerUploading: false,
|
||||||
|
exportTarget: 'notes',
|
||||||
|
faDownload
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -262,8 +271,13 @@ export default Vue.extend({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
exportNotes() {
|
doExport() {
|
||||||
this.$root.api('i/export-notes', {});
|
this.$root.api(
|
||||||
|
this.exportTarget == 'notes' ? 'i/export-notes' :
|
||||||
|
this.exportTarget == 'following' ? 'i/export-following' :
|
||||||
|
this.exportTarget == 'mute' ? 'i/export-mute' :
|
||||||
|
this.exportTarget == 'blocking' ? 'i/export-blocking' :
|
||||||
|
null, {});
|
||||||
|
|
||||||
this.$root.dialog({
|
this.$root.dialog({
|
||||||
type: 'info',
|
type: 'info',
|
||||||
|
@ -0,0 +1,147 @@
|
|||||||
|
<template>
|
||||||
|
<span
|
||||||
|
class="reaction"
|
||||||
|
:class="{ reacted: note.myReaction == reaction }"
|
||||||
|
@click="toggleReaction(reaction)"
|
||||||
|
v-if="count > 0"
|
||||||
|
v-particle="!isMe"
|
||||||
|
>
|
||||||
|
<mk-reaction-icon :reaction="reaction" ref="icon"/>
|
||||||
|
<span>{{ count }}</span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import Icon from './reaction-icon.vue';
|
||||||
|
import anime from 'animejs';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
props: {
|
||||||
|
reaction: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
count: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
canToggle: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isMe(): boolean {
|
||||||
|
return this.$store.getters.isSignedIn && this.$store.state.i.id === this.note.userId;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
count() {
|
||||||
|
this.anime();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toggleReaction() {
|
||||||
|
if (this.isMe) return;
|
||||||
|
if (!this.canToggle) return;
|
||||||
|
|
||||||
|
const oldReaction = this.note.myReaction;
|
||||||
|
if (oldReaction) {
|
||||||
|
this.$root.api('notes/reactions/delete', {
|
||||||
|
noteId: this.note.id
|
||||||
|
}).then(() => {
|
||||||
|
if (oldReaction !== this.reaction) {
|
||||||
|
this.$root.api('notes/reactions/create', {
|
||||||
|
noteId: this.note.id,
|
||||||
|
reaction: this.reaction
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.$root.api('notes/reactions/create', {
|
||||||
|
noteId: this.note.id,
|
||||||
|
reaction: this.reaction
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
anime() {
|
||||||
|
if (this.$store.state.device.reduceMotion) return;
|
||||||
|
if (document.hidden) return;
|
||||||
|
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const rect = this.$refs.icon.$el.getBoundingClientRect();
|
||||||
|
|
||||||
|
const x = rect.left;
|
||||||
|
const y = rect.top;
|
||||||
|
|
||||||
|
const icon = new Icon({
|
||||||
|
parent: this,
|
||||||
|
propsData: {
|
||||||
|
reaction: this.reaction
|
||||||
|
}
|
||||||
|
}).$mount();
|
||||||
|
|
||||||
|
icon.$el.style.position = 'absolute';
|
||||||
|
icon.$el.style.zIndex = 100;
|
||||||
|
icon.$el.style.top = (y + window.scrollY) + 'px';
|
||||||
|
icon.$el.style.left = (x + window.scrollX) + 'px';
|
||||||
|
icon.$el.style.fontSize = window.getComputedStyle(this.$refs.icon.$el).fontSize;
|
||||||
|
|
||||||
|
document.body.appendChild(icon.$el);
|
||||||
|
|
||||||
|
anime({
|
||||||
|
targets: icon.$el,
|
||||||
|
opacity: [1, 0],
|
||||||
|
translateY: [0, -64],
|
||||||
|
duration: 1000,
|
||||||
|
easing: 'linear',
|
||||||
|
complete: () => {
|
||||||
|
icon.destroyDom();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
.reaction
|
||||||
|
display inline-block
|
||||||
|
height 32px
|
||||||
|
margin 2px
|
||||||
|
padding 0 6px
|
||||||
|
border-radius 4px
|
||||||
|
cursor pointer
|
||||||
|
|
||||||
|
*
|
||||||
|
user-select none
|
||||||
|
pointer-events none
|
||||||
|
|
||||||
|
&.reacted
|
||||||
|
background var(--primary)
|
||||||
|
|
||||||
|
> span
|
||||||
|
color var(--primaryForeground)
|
||||||
|
|
||||||
|
&:not(.reacted)
|
||||||
|
background var(--reactionViewerButtonBg)
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
background var(--reactionViewerButtonHoverBg)
|
||||||
|
|
||||||
|
> .mk-reaction-icon
|
||||||
|
font-size 1.4em
|
||||||
|
|
||||||
|
> span
|
||||||
|
font-size 1.1em
|
||||||
|
line-height 32px
|
||||||
|
vertical-align middle
|
||||||
|
color var(--text)
|
||||||
|
</style>
|
@ -1,139 +1,37 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="mk-reactions-viewer" :class="{ isMe }">
|
<div class="mk-reactions-viewer" :class="{ isMe }">
|
||||||
<template v-if="reactions">
|
<x-reaction v-for="(count, reaction) in reactions" :reaction="reaction" :count="count" :note="note" :key="reaction"/>
|
||||||
<span :class="{ reacted: note.myReaction == 'like' }" @click="toggleReaction('like')" v-if="reactions.like" v-particle="!isMe"><mk-reaction-icon reaction="like" ref="like"/><span>{{ reactions.like }}</span></span>
|
|
||||||
<span :class="{ reacted: note.myReaction == 'love' }" @click="toggleReaction('love')" v-if="reactions.love" v-particle="!isMe"><mk-reaction-icon reaction="love" ref="love"/><span>{{ reactions.love }}</span></span>
|
|
||||||
<span :class="{ reacted: note.myReaction == 'laugh' }" @click="toggleReaction('laugh')" v-if="reactions.laugh" v-particle="!isMe"><mk-reaction-icon reaction="laugh" ref="laugh"/><span>{{ reactions.laugh }}</span></span>
|
|
||||||
<span :class="{ reacted: note.myReaction == 'hmm' }" @click="toggleReaction('hmm')" v-if="reactions.hmm" v-particle="!isMe"><mk-reaction-icon reaction="hmm" ref="hmm"/><span>{{ reactions.hmm }}</span></span>
|
|
||||||
<span :class="{ reacted: note.myReaction == 'surprise' }" @click="toggleReaction('surprise')" v-if="reactions.surprise" v-particle="!isMe"><mk-reaction-icon reaction="surprise" ref="surprise"/><span>{{ reactions.surprise }}</span></span>
|
|
||||||
<span :class="{ reacted: note.myReaction == 'congrats' }" @click="toggleReaction('congrats')" v-if="reactions.congrats" v-particle="!isMe"><mk-reaction-icon reaction="congrats" ref="congrats"/><span>{{ reactions.congrats }}</span></span>
|
|
||||||
<span :class="{ reacted: note.myReaction == 'angry' }" @click="toggleReaction('angry')" v-if="reactions.angry" v-particle="!isMe"><mk-reaction-icon reaction="angry" ref="angry"/><span>{{ reactions.angry }}</span></span>
|
|
||||||
<span :class="{ reacted: note.myReaction == 'confused' }" @click="toggleReaction('confused')" v-if="reactions.confused" v-particle="!isMe"><mk-reaction-icon reaction="confused" ref="confused"/><span>{{ reactions.confused }}</span></span>
|
|
||||||
<span :class="{ reacted: note.myReaction == 'rip' }" @click="toggleReaction('rip')" v-if="reactions.rip" v-particle="!isMe"><mk-reaction-icon reaction="rip" ref="rip"/><span>{{ reactions.rip }}</span></span>
|
|
||||||
<span :class="{ reacted: note.myReaction == 'pudding' }" @click="toggleReaction('pudding')" v-if="reactions.pudding" v-particle="!isMe"><mk-reaction-icon reaction="pudding" ref="pudding"/><span>{{ reactions.pudding }}</span></span>
|
|
||||||
</template>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import Icon from './reaction-icon.vue';
|
import XReaction from './reactions-viewer.reaction.vue';
|
||||||
import anime from 'animejs';
|
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
|
components: {
|
||||||
|
XReaction
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
note: {
|
note: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
reactions(): any {
|
reactions(): any {
|
||||||
return this.note.reactionCounts;
|
return this.note.reactionCounts;
|
||||||
},
|
},
|
||||||
isMe(): boolean {
|
isMe(): boolean {
|
||||||
return this.$store.getters.isSignedIn && (this.$store.state.i.id === this.note.userId);
|
return this.$store.getters.isSignedIn && this.$store.state.i.id === this.note.userId;
|
||||||
}
|
|
||||||
},
|
},
|
||||||
watch: {
|
|
||||||
'reactions.like'() {
|
|
||||||
this.anime('like');
|
|
||||||
},
|
},
|
||||||
'reactions.love'() {
|
|
||||||
this.anime('love');
|
|
||||||
},
|
|
||||||
'reactions.laugh'() {
|
|
||||||
this.anime('laugh');
|
|
||||||
},
|
|
||||||
'reactions.hmm'() {
|
|
||||||
this.anime('hmm');
|
|
||||||
},
|
|
||||||
'reactions.surprise'() {
|
|
||||||
this.anime('surprise');
|
|
||||||
},
|
|
||||||
'reactions.congrats'() {
|
|
||||||
this.anime('congrats');
|
|
||||||
},
|
|
||||||
'reactions.angry'() {
|
|
||||||
this.anime('angry');
|
|
||||||
},
|
|
||||||
'reactions.confused'() {
|
|
||||||
this.anime('confused');
|
|
||||||
},
|
|
||||||
'reactions.rip'() {
|
|
||||||
this.anime('rip');
|
|
||||||
},
|
|
||||||
'reactions.pudding'() {
|
|
||||||
this.anime('pudding');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
toggleReaction(reaction: string) {
|
|
||||||
if (this.isMe) return;
|
|
||||||
|
|
||||||
const oldReaction = this.note.myReaction;
|
|
||||||
if (oldReaction) {
|
|
||||||
this.$root.api('notes/reactions/delete', {
|
|
||||||
noteId: this.note.id
|
|
||||||
}).then(() => {
|
|
||||||
if (oldReaction !== reaction) {
|
|
||||||
this.$root.api('notes/reactions/create', {
|
|
||||||
noteId: this.note.id,
|
|
||||||
reaction: reaction
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.$root.api('notes/reactions/create', {
|
|
||||||
noteId: this.note.id,
|
|
||||||
reaction: reaction
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
anime(reaction: string) {
|
|
||||||
if (this.$store.state.device.reduceMotion) return;
|
|
||||||
if (document.hidden) return;
|
|
||||||
|
|
||||||
this.$nextTick(() => {
|
|
||||||
const rect = this.$refs[reaction].$el.getBoundingClientRect();
|
|
||||||
|
|
||||||
const x = rect.left;
|
|
||||||
const y = rect.top;
|
|
||||||
|
|
||||||
const icon = new Icon({
|
|
||||||
parent: this,
|
|
||||||
propsData: {
|
|
||||||
reaction: reaction
|
|
||||||
}
|
|
||||||
}).$mount();
|
|
||||||
|
|
||||||
icon.$el.style.position = 'absolute';
|
|
||||||
icon.$el.style.zIndex = 100;
|
|
||||||
icon.$el.style.top = (y + window.scrollY) + 'px';
|
|
||||||
icon.$el.style.left = (x + window.scrollX) + 'px';
|
|
||||||
icon.$el.style.fontSize = window.getComputedStyle(this.$refs[reaction].$el).fontSize;
|
|
||||||
|
|
||||||
document.body.appendChild(icon.$el);
|
|
||||||
|
|
||||||
anime({
|
|
||||||
targets: icon.$el,
|
|
||||||
opacity: [1, 0],
|
|
||||||
translateY: [0, -64],
|
|
||||||
duration: 1000,
|
|
||||||
easing: 'linear',
|
|
||||||
complete: () => {
|
|
||||||
icon.destroyDom();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
.mk-reactions-viewer
|
.mk-reactions-viewer
|
||||||
margin 6px 0
|
margin: 4px -2px
|
||||||
|
|
||||||
&:empty
|
&:empty
|
||||||
display none
|
display none
|
||||||
@ -144,38 +42,4 @@ export default Vue.extend({
|
|||||||
|
|
||||||
&:hover
|
&:hover
|
||||||
background var(--reactionViewerButtonBg) !important
|
background var(--reactionViewerButtonBg) !important
|
||||||
|
|
||||||
> span
|
|
||||||
display inline-block
|
|
||||||
height 32px
|
|
||||||
margin-right 6px
|
|
||||||
padding 0 6px
|
|
||||||
border-radius 4px
|
|
||||||
cursor pointer
|
|
||||||
|
|
||||||
*
|
|
||||||
user-select none
|
|
||||||
pointer-events none
|
|
||||||
|
|
||||||
&.reacted
|
|
||||||
background var(--primary)
|
|
||||||
|
|
||||||
> span
|
|
||||||
color var(--primaryForeground)
|
|
||||||
|
|
||||||
&:not(.reacted)
|
|
||||||
background var(--reactionViewerButtonBg)
|
|
||||||
|
|
||||||
&:hover
|
|
||||||
background var(--reactionViewerButtonHoverBg)
|
|
||||||
|
|
||||||
> .mk-reaction-icon
|
|
||||||
font-size 1.4em
|
|
||||||
|
|
||||||
> span
|
|
||||||
font-size 1.1em
|
|
||||||
line-height 32px
|
|
||||||
vertical-align middle
|
|
||||||
color var(--text)
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -74,7 +74,7 @@ export default Vue.extend({
|
|||||||
margin 0
|
margin 0
|
||||||
padding 16px
|
padding 16px
|
||||||
text-align center
|
text-align center
|
||||||
color #aaa
|
color var(--text)
|
||||||
|
|
||||||
> [data-icon]
|
> [data-icon]
|
||||||
margin-right 4px
|
margin-right 4px
|
||||||
|
@ -119,11 +119,11 @@ export default Vue.extend({
|
|||||||
font-size 16px
|
font-size 16px
|
||||||
cursor pointer
|
cursor pointer
|
||||||
transition inherit
|
transition inherit
|
||||||
|
color var(--text)
|
||||||
|
|
||||||
> span
|
> span
|
||||||
display block
|
display block
|
||||||
line-height 20px
|
line-height 20px
|
||||||
color var(--text)
|
|
||||||
transition inherit
|
transition inherit
|
||||||
|
|
||||||
> p
|
> p
|
||||||
|
@ -117,7 +117,7 @@ export default define({
|
|||||||
margin 0
|
margin 0
|
||||||
padding 16px
|
padding 16px
|
||||||
text-align center
|
text-align center
|
||||||
color #aaa
|
color var(--text)
|
||||||
|
|
||||||
> [data-icon]
|
> [data-icon]
|
||||||
margin-right 4px
|
margin-right 4px
|
||||||
|
@ -86,7 +86,7 @@ export default define({
|
|||||||
margin 0
|
margin 0
|
||||||
padding 16px
|
padding 16px
|
||||||
text-align center
|
text-align center
|
||||||
color #aaa
|
color var(--text)
|
||||||
|
|
||||||
> [data-icon]
|
> [data-icon]
|
||||||
margin-right 4px
|
margin-right 4px
|
||||||
|
@ -88,7 +88,7 @@ export default define({
|
|||||||
margin 0
|
margin 0
|
||||||
padding 16px
|
padding 16px
|
||||||
text-align center
|
text-align center
|
||||||
color #aaa
|
color var(--text)
|
||||||
|
|
||||||
> [data-icon]
|
> [data-icon]
|
||||||
margin-right 4px
|
margin-right 4px
|
||||||
|
@ -12,7 +12,7 @@ export const hostname = address.hostname;
|
|||||||
export const url = address.origin;
|
export const url = address.origin;
|
||||||
export const apiUrl = url + '/api';
|
export const apiUrl = url + '/api';
|
||||||
export const wsUrl = url.replace('http://', 'ws://').replace('https://', 'wss://') + '/streaming';
|
export const wsUrl = url.replace('http://', 'ws://').replace('https://', 'wss://') + '/streaming';
|
||||||
export const lang = localStorage.getItem('lang');
|
export const lang = localStorage.getItem('lang') || window.lang; // windowは後方互換性のため
|
||||||
export const langs = _LANGS_;
|
export const langs = _LANGS_;
|
||||||
export const locale = JSON.parse(localStorage.getItem('locale'));
|
export const locale = JSON.parse(localStorage.getItem('locale'));
|
||||||
export const copyright = _COPYRIGHT_;
|
export const copyright = _COPYRIGHT_;
|
||||||
|
@ -78,7 +78,7 @@ export default Vue.extend({
|
|||||||
margin 0
|
margin 0
|
||||||
padding 16px
|
padding 16px
|
||||||
text-align center
|
text-align center
|
||||||
color #aaa
|
color var(--text)
|
||||||
|
|
||||||
> [data-icon]
|
> [data-icon]
|
||||||
margin-right 4px
|
margin-right 4px
|
||||||
|
@ -120,13 +120,13 @@ export default Vue.extend({
|
|||||||
margin 0
|
margin 0
|
||||||
padding 16px
|
padding 16px
|
||||||
text-align center
|
text-align center
|
||||||
color #aaa
|
color var(--text)
|
||||||
|
|
||||||
> .fetching
|
> .fetching
|
||||||
margin 0
|
margin 0
|
||||||
padding 16px
|
padding 16px
|
||||||
text-align center
|
text-align center
|
||||||
color #aaa
|
color var(--text)
|
||||||
|
|
||||||
> [data-icon]
|
> [data-icon]
|
||||||
margin-right 4px
|
margin-right 4px
|
||||||
|
@ -598,6 +598,7 @@ export default Vue.extend({
|
|||||||
padding 16px 0 0 0
|
padding 16px 0 0 0
|
||||||
overflow auto
|
overflow auto
|
||||||
z-index 1
|
z-index 1
|
||||||
|
font-size 15px
|
||||||
|
|
||||||
&.inWindow
|
&.inWindow
|
||||||
box-shadow var(--shadowRight)
|
box-shadow var(--shadowRight)
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<div class="zvdbznxvfixtmujpsigoccczftvpiwqh">
|
<div class="zvdbznxvfixtmujpsigoccczftvpiwqh">
|
||||||
<div class="banner" :style="bannerStyle"></div>
|
<div class="banner" :style="bannerStyle"></div>
|
||||||
<mk-avatar class="avatar" :user="user" :disable-preview="true"/>
|
<mk-avatar class="avatar" :user="user" :disable-preview="true"/>
|
||||||
<mk-follow-button :user="user" class="follow" mini/>
|
<mk-follow-button v-if="$store.getters.isSignedIn && user.id != $store.state.i.id" :user="user" class="follow" mini/>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<router-link :to="user | userPage" class="name">
|
<router-link :to="user | userPage" class="name">
|
||||||
<mk-user-name :user="user"/>
|
<mk-user-name :user="user"/>
|
||||||
|
@ -218,6 +218,6 @@ export default Vue.extend({
|
|||||||
margin 0
|
margin 0
|
||||||
padding 16px
|
padding 16px
|
||||||
text-align center
|
text-align center
|
||||||
color #aaa
|
color var(--text)
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
<mk-user-name :user="user"/>
|
<mk-user-name :user="user"/>
|
||||||
</span>
|
</span>
|
||||||
<span class="acct">@{{ user | acct }} <fa v-if="user.isLocked == true" class="locked" icon="lock" fixed-width/></span>
|
<span class="acct">@{{ user | acct }} <fa v-if="user.isLocked == true" class="locked" icon="lock" fixed-width/></span>
|
||||||
|
<span class="followed" v-if="user.isFollowed">{{ $t('follows-you') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<div class="info">
|
<div class="info">
|
||||||
@ -376,6 +377,7 @@ export default Vue.extend({
|
|||||||
text-shadow 0 0 8px #000
|
text-shadow 0 0 8px #000
|
||||||
|
|
||||||
> .acct
|
> .acct
|
||||||
|
display block
|
||||||
font-size 14px
|
font-size 14px
|
||||||
opacity 0.7
|
opacity 0.7
|
||||||
text-shadow 0 0 8px #000
|
text-shadow 0 0 8px #000
|
||||||
@ -383,6 +385,15 @@ export default Vue.extend({
|
|||||||
> .locked
|
> .locked
|
||||||
opacity 0.8
|
opacity 0.8
|
||||||
|
|
||||||
|
> .followed
|
||||||
|
display inline-block
|
||||||
|
font-size 12px
|
||||||
|
background rgba(0, 0, 0, 0.5)
|
||||||
|
opacity 0.7
|
||||||
|
margin-top: 2px
|
||||||
|
padding 4px
|
||||||
|
border-radius 4px
|
||||||
|
|
||||||
> .info
|
> .info
|
||||||
padding 16px
|
padding 16px
|
||||||
font-size 12px
|
font-size 12px
|
||||||
|
@ -64,6 +64,8 @@ export default Vue.extend({
|
|||||||
margin 4px
|
margin 4px
|
||||||
|
|
||||||
> img
|
> img
|
||||||
|
display inline-block
|
||||||
|
text-align center
|
||||||
width 48px
|
width 48px
|
||||||
height 48px
|
height 48px
|
||||||
vertical-align bottom
|
vertical-align bottom
|
||||||
@ -74,7 +76,7 @@ export default Vue.extend({
|
|||||||
margin 0
|
margin 0
|
||||||
padding 16px
|
padding 16px
|
||||||
text-align center
|
text-align center
|
||||||
color #aaa
|
color var(--text)
|
||||||
|
|
||||||
> i
|
> i
|
||||||
margin-right 4px
|
margin-right 4px
|
||||||
|
@ -66,7 +66,7 @@ export default Vue.extend({
|
|||||||
margin 0
|
margin 0
|
||||||
padding 16px
|
padding 16px
|
||||||
text-align center
|
text-align center
|
||||||
color #aaa
|
color var(--text)
|
||||||
|
|
||||||
> i
|
> i
|
||||||
margin-right 4px
|
margin-right 4px
|
||||||
|
@ -98,7 +98,7 @@ export default Vue.extend({
|
|||||||
margin 0
|
margin 0
|
||||||
padding 16px
|
padding 16px
|
||||||
text-align center
|
text-align center
|
||||||
color #aaa
|
color var(--text)
|
||||||
|
|
||||||
> i
|
> i
|
||||||
margin-right 4px
|
margin-right 4px
|
||||||
|
@ -132,7 +132,7 @@ export default Vue.extend({
|
|||||||
padding 0 12px
|
padding 0 12px
|
||||||
text-align center
|
text-align center
|
||||||
font-size 0.8em
|
font-size 0.8em
|
||||||
color #aaa
|
color var(--text)
|
||||||
|
|
||||||
> .instance
|
> .instance
|
||||||
box-shadow var(--shadow)
|
box-shadow var(--shadow)
|
||||||
|
@ -92,13 +92,13 @@ export default define({
|
|||||||
margin 0
|
margin 0
|
||||||
padding 16px
|
padding 16px
|
||||||
text-align center
|
text-align center
|
||||||
color #aaa
|
color var(--text)
|
||||||
|
|
||||||
> .fetching
|
> .fetching
|
||||||
margin 0
|
margin 0
|
||||||
padding 16px
|
padding 16px
|
||||||
text-align center
|
text-align center
|
||||||
color #aaa
|
color var(--text)
|
||||||
|
|
||||||
> [data-icon]
|
> [data-icon]
|
||||||
margin-right 4px
|
margin-right 4px
|
||||||
|
@ -89,13 +89,13 @@ export default define({
|
|||||||
margin 0
|
margin 0
|
||||||
padding 16px
|
padding 16px
|
||||||
text-align center
|
text-align center
|
||||||
color #aaa
|
color var(--text)
|
||||||
|
|
||||||
> .fetching
|
> .fetching
|
||||||
margin 0
|
margin 0
|
||||||
padding 16px
|
padding 16px
|
||||||
text-align center
|
text-align center
|
||||||
color #aaa
|
color var(--text)
|
||||||
|
|
||||||
> [data-icon]
|
> [data-icon]
|
||||||
margin-right 4px
|
margin-right 4px
|
||||||
|
@ -129,13 +129,13 @@ export default define({
|
|||||||
margin 0
|
margin 0
|
||||||
padding 16px
|
padding 16px
|
||||||
text-align center
|
text-align center
|
||||||
color #aaa
|
color var(--text)
|
||||||
|
|
||||||
> .fetching
|
> .fetching
|
||||||
margin 0
|
margin 0
|
||||||
padding 16px
|
padding 16px
|
||||||
text-align center
|
text-align center
|
||||||
color #aaa
|
color var(--text)
|
||||||
|
|
||||||
> [data-icon]
|
> [data-icon]
|
||||||
margin-right 4px
|
margin-right 4px
|
||||||
|
@ -83,13 +83,13 @@ export default Vue.extend({
|
|||||||
margin 0
|
margin 0
|
||||||
padding 16px
|
padding 16px
|
||||||
text-align center
|
text-align center
|
||||||
color #aaa
|
color var(--text)
|
||||||
|
|
||||||
> .fetching
|
> .fetching
|
||||||
margin 0
|
margin 0
|
||||||
padding 16px
|
padding 16px
|
||||||
text-align center
|
text-align center
|
||||||
color #aaa
|
color var(--text)
|
||||||
|
|
||||||
> [data-icon]
|
> [data-icon]
|
||||||
margin-right 4px
|
margin-right 4px
|
||||||
|
@ -88,6 +88,6 @@ export default Vue.extend({
|
|||||||
> .mk-time
|
> .mk-time
|
||||||
display inline-block
|
display inline-block
|
||||||
padding 8px
|
padding 8px
|
||||||
color #aaa
|
color var(--text)
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -184,7 +184,7 @@ export default Vue.extend({
|
|||||||
margin 0
|
margin 0
|
||||||
padding 16px
|
padding 16px
|
||||||
text-align center
|
text-align center
|
||||||
color #aaa
|
color var(--text)
|
||||||
|
|
||||||
> .placeholder
|
> .placeholder
|
||||||
padding 16px
|
padding 16px
|
||||||
|
@ -171,6 +171,7 @@ export default Vue.extend({
|
|||||||
overflow auto
|
overflow auto
|
||||||
-webkit-overflow-scrolling touch
|
-webkit-overflow-scrolling touch
|
||||||
background var(--secondary)
|
background var(--secondary)
|
||||||
|
font-size 15px
|
||||||
|
|
||||||
.me
|
.me
|
||||||
display block
|
display block
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<mk-user-name :user="user"/>
|
<mk-user-name :user="user"/>
|
||||||
</a>
|
</a>
|
||||||
<p class="username"><mk-acct :user="user"/></p>
|
<p class="username"><mk-acct :user="user"/></p>
|
||||||
<mk-follow-button class="follow-button" :user="user"/>
|
<mk-follow-button v-if="$store.getters.isSignedIn && user.id != $store.state.i.id" class="follow-button" :user="user"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -121,13 +121,13 @@ export default Vue.extend({
|
|||||||
margin 0
|
margin 0
|
||||||
padding 16px
|
padding 16px
|
||||||
text-align center
|
text-align center
|
||||||
color #aaa
|
color var(--text)
|
||||||
|
|
||||||
> .fetching
|
> .fetching
|
||||||
margin 0
|
margin 0
|
||||||
padding 16px
|
padding 16px
|
||||||
text-align center
|
text-align center
|
||||||
color #aaa
|
color var(--text)
|
||||||
|
|
||||||
> [data-icon]
|
> [data-icon]
|
||||||
margin-right 4px
|
margin-right 4px
|
||||||
|
@ -57,7 +57,7 @@ export default Vue.extend({
|
|||||||
margin 0
|
margin 0
|
||||||
padding 16px
|
padding 16px
|
||||||
text-align center
|
text-align center
|
||||||
color #aaa
|
color var(--text)
|
||||||
|
|
||||||
> i
|
> i
|
||||||
margin-right 4px
|
margin-right 4px
|
||||||
|
@ -48,7 +48,7 @@ export default Vue.extend({
|
|||||||
margin 0
|
margin 0
|
||||||
padding 16px
|
padding 16px
|
||||||
text-align center
|
text-align center
|
||||||
color #aaa
|
color var(--text)
|
||||||
|
|
||||||
> i
|
> i
|
||||||
margin-right 4px
|
margin-right 4px
|
||||||
|
@ -52,7 +52,7 @@ export default Vue.extend({
|
|||||||
margin 0
|
margin 0
|
||||||
padding 16px
|
padding 16px
|
||||||
text-align center
|
text-align center
|
||||||
color #aaa
|
color var(--text)
|
||||||
|
|
||||||
> i
|
> i
|
||||||
margin-right 4px
|
margin-right 4px
|
||||||
|
@ -89,7 +89,7 @@ export default Vue.extend({
|
|||||||
margin 0
|
margin 0
|
||||||
padding 16px
|
padding 16px
|
||||||
text-align center
|
text-align center
|
||||||
color #aaa
|
color var(--text)
|
||||||
|
|
||||||
> i
|
> i
|
||||||
margin-right 4px
|
margin-right 4px
|
||||||
|
@ -23,6 +23,7 @@ export const colorfulTheme: Theme = require('../theme/colorful.json5');
|
|||||||
export const rainyTheme: Theme = require('../theme/rainy.json5');
|
export const rainyTheme: Theme = require('../theme/rainy.json5');
|
||||||
export const mauveTheme: Theme = require('../theme/mauve.json5');
|
export const mauveTheme: Theme = require('../theme/mauve.json5');
|
||||||
export const grayTheme: Theme = require('../theme/gray.json5');
|
export const grayTheme: Theme = require('../theme/gray.json5');
|
||||||
|
export const tweetDeckTheme: Theme = require('../theme/tweet-deck.json5');
|
||||||
|
|
||||||
export const builtinThemes = [
|
export const builtinThemes = [
|
||||||
lightTheme,
|
lightTheme,
|
||||||
@ -38,6 +39,7 @@ export const builtinThemes = [
|
|||||||
rainyTheme,
|
rainyTheme,
|
||||||
mauveTheme,
|
mauveTheme,
|
||||||
grayTheme,
|
grayTheme,
|
||||||
|
tweetDeckTheme,
|
||||||
];
|
];
|
||||||
|
|
||||||
export function applyTheme(theme: Theme, persisted = true) {
|
export function applyTheme(theme: Theme, persisted = true) {
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
id: '2b0a0654-cdb4-4c9a-8244-736b647d3c2a',
|
id: '2b0a0654-cdb4-4c9a-8244-736b647d3c2a',
|
||||||
|
|
||||||
name: 'Japanese Sushi Set',
|
name: 'Japanese Sushi Set',
|
||||||
author: 'noizenecio & syuilo',
|
author: 'Noizenecio',
|
||||||
|
|
||||||
base: 'dark',
|
base: 'dark',
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
id: '252b2caf-86c2-4c3f-a73f-e1fc1cfa5298',
|
id: '252b2caf-86c2-4c3f-a73f-e1fc1cfa5298',
|
||||||
|
|
||||||
name: 'Mauve',
|
name: 'Mauve',
|
||||||
author: 'とわこ & syuilo',
|
author: 'とわこ',
|
||||||
|
|
||||||
base: 'dark',
|
base: 'dark',
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
id: 'e9c8c01d-9c15-48d0-9b5c-3d00843b5b36',
|
id: 'e9c8c01d-9c15-48d0-9b5c-3d00843b5b36',
|
||||||
|
|
||||||
name: 'Lavender',
|
name: 'Lavender',
|
||||||
author: 'sokuyuku & syuilo',
|
author: 'sokuyuku',
|
||||||
|
|
||||||
base: 'light',
|
base: 'light',
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
name: 'Rainy',
|
name: 'Rainy',
|
||||||
author: 'syuilo',
|
author: 'syuilo',
|
||||||
|
desc: 'It\'s a rainy day.',
|
||||||
|
|
||||||
base: 'light',
|
base: 'light',
|
||||||
|
|
||||||
|
44
src/client/theme/tweet-deck.json5
Normal file
44
src/client/theme/tweet-deck.json5
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
name: 'Tweet Deck',
|
||||||
|
id: '06f82fb4-0dad-4d70-8a3f-56cae91e1163',
|
||||||
|
author: 'simirall',
|
||||||
|
desc: 'Tweet like a pro.',
|
||||||
|
base: 'dark',
|
||||||
|
vars: {
|
||||||
|
primary: '#1da1f2',
|
||||||
|
secondary: '#15202b',
|
||||||
|
text: '#fdfdfd',
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
bg: '#10171e',
|
||||||
|
faceHeader: '$secondary',
|
||||||
|
faceTextButton: '$primary',
|
||||||
|
renoteGradient: '$secondary',
|
||||||
|
renoteText: '#17bf63',
|
||||||
|
quoteBorder: '#38444d',
|
||||||
|
noteHeaderAdminFg: '$primary',
|
||||||
|
noteHeaderAdminBg: '$secondary',
|
||||||
|
noteActionsReplyHover: '$primary',
|
||||||
|
noteActionsRenoteHover: '#17bf63',
|
||||||
|
noteActionsReactionHover: '#e0245e',
|
||||||
|
calendarWeek: '$primary',
|
||||||
|
calendarSaturdayOrSunday: '#e0245e',
|
||||||
|
announcementsBg: '$secondary',
|
||||||
|
announcementsTitle: '$primary',
|
||||||
|
suspendedInfoBg: '$secondary',
|
||||||
|
suspendedInfoFg: '$primary',
|
||||||
|
remoteInfoBg: '$secondary',
|
||||||
|
remoteInfoFg: '$primary',
|
||||||
|
desktopHeaderBg: '#1c2938',
|
||||||
|
desktopHeaderFg: '#a9adae',
|
||||||
|
desktopHeaderHoverFg: '#fff',
|
||||||
|
desktopPostFormTransparentButtonFg: '#a9adae',
|
||||||
|
desktopTimelineSrc: '$primary',
|
||||||
|
desktopTimelineSrcHover: '#fff',
|
||||||
|
deckAcrylicColumnBg: 'rgba(0, 0, 0, 0.0)',
|
||||||
|
reversiBannerGradientStart: '$primary',
|
||||||
|
reversiBannerGradientEnd: '$primary',
|
||||||
|
reversiGameEmptyCellMyTurn: ':lighten<5<$primary',
|
||||||
|
reversiGameEmptyCellCanPut: ':lighten<4<$primary',
|
||||||
|
},
|
||||||
|
}
|
@ -6,7 +6,6 @@ import * as fs from 'fs';
|
|||||||
import { URL } from 'url';
|
import { URL } from 'url';
|
||||||
import * as yaml from 'js-yaml';
|
import * as yaml from 'js-yaml';
|
||||||
import { Source, Mixin } from './types';
|
import { Source, Mixin } from './types';
|
||||||
import isUrl = require('is-url');
|
|
||||||
import * as pkg from '../../package.json';
|
import * as pkg from '../../package.json';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -26,10 +25,8 @@ export default function load() {
|
|||||||
|
|
||||||
const mixin = {} as Mixin;
|
const mixin = {} as Mixin;
|
||||||
|
|
||||||
// Validate URLs
|
const url = validateUrl(config.url);
|
||||||
if (!isUrl(config.url)) throw `url="${config.url}" is not a valid URL`;
|
|
||||||
|
|
||||||
const url = new URL(config.url);
|
|
||||||
config.url = normalizeUrl(config.url);
|
config.url = normalizeUrl(config.url);
|
||||||
|
|
||||||
mixin.host = url.host;
|
mixin.host = url.host;
|
||||||
@ -51,6 +48,21 @@ export default function load() {
|
|||||||
return Object.assign(config, mixin);
|
return Object.assign(config, mixin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function tryCreateUrl(url: string) {
|
||||||
|
try {
|
||||||
|
return new URL(url);
|
||||||
|
} catch (e) {
|
||||||
|
throw `url="${url}" is not a valid URL.`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateUrl(url: string) {
|
||||||
|
const result = tryCreateUrl(url);
|
||||||
|
if (result.pathname.replace('/', '').length) throw `url="${url}" is not a valid URL, has a pathname.`;
|
||||||
|
if (!url.includes(result.host)) throw `url="${url}" is not a valid URL, has an invalid hostname.`;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeUrl(url: string) {
|
function normalizeUrl(url: string) {
|
||||||
return url.endsWith('/') ? url.substr(0, url.length - 1) : url;
|
return url.endsWith('/') ? url.substr(0, url.length - 1) : url;
|
||||||
}
|
}
|
||||||
|
88
src/index.ts
88
src/index.ts
@ -26,7 +26,7 @@ import { showMachineInfo } from './misc/show-machine-info';
|
|||||||
|
|
||||||
const logger = new Logger('core', 'cyan');
|
const logger = new Logger('core', 'cyan');
|
||||||
const bootLogger = logger.createSubLogger('boot', 'magenta');
|
const bootLogger = logger.createSubLogger('boot', 'magenta');
|
||||||
const clusterLog = logger.createSubLogger('cluster', 'orange');
|
const clusterLogger = logger.createSubLogger('cluster', 'orange');
|
||||||
const ev = new Xev();
|
const ev = new Xev();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -35,6 +35,11 @@ const ev = new Xev();
|
|||||||
function main() {
|
function main() {
|
||||||
process.title = `Misskey (${cluster.isMaster ? 'master' : 'worker'})`;
|
process.title = `Misskey (${cluster.isMaster ? 'master' : 'worker'})`;
|
||||||
|
|
||||||
|
if (program.onlyQueue) {
|
||||||
|
queueMain();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (cluster.isMaster || program.disableClustering) {
|
if (cluster.isMaster || program.disableClustering) {
|
||||||
masterMain();
|
masterMain();
|
||||||
|
|
||||||
@ -53,12 +58,7 @@ function main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function greet() {
|
||||||
* Init master process
|
|
||||||
*/
|
|
||||||
async function masterMain() {
|
|
||||||
let config: Config;
|
|
||||||
|
|
||||||
if (!program.quiet) {
|
if (!program.quiet) {
|
||||||
//#region Misskey logo
|
//#region Misskey logo
|
||||||
const v = `v${pkg.version}`;
|
const v = `v${pkg.version}`;
|
||||||
@ -75,10 +75,34 @@ async function masterMain() {
|
|||||||
bootLogger.info('Welcome to Misskey!');
|
bootLogger.info('Welcome to Misskey!');
|
||||||
bootLogger.info(`Misskey v${pkg.version}`, true);
|
bootLogger.info(`Misskey v${pkg.version}`, true);
|
||||||
bootLogger.info('Misskey is maintained by @syuilo, @AyaMorisawa, @mei23, and @acid-chicken.');
|
bootLogger.info('Misskey is maintained by @syuilo, @AyaMorisawa, @mei23, and @acid-chicken.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init master process
|
||||||
|
*/
|
||||||
|
async function masterMain() {
|
||||||
|
greet();
|
||||||
|
|
||||||
|
let config: Config;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// initialize app
|
// initialize app
|
||||||
config = await init();
|
config = await init();
|
||||||
|
|
||||||
|
if (config.port == null) {
|
||||||
|
bootLogger.error('The port is not configured. Please configure port.', true);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.platform === 'linux' && isWellKnownPort(config.port) && !isRoot()) {
|
||||||
|
bootLogger.error('You need root privileges to listen on well-known port on Linux', true);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!await isPortAvailable(config.port)) {
|
||||||
|
bootLogger.error(`Port ${config.port} is already in use`, true);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
bootLogger.error('Fatal error occurred during initialization', true);
|
bootLogger.error('Fatal error occurred during initialization', true);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
@ -90,6 +114,9 @@ async function masterMain() {
|
|||||||
await spawnWorkers(config.clusterLimit);
|
await spawnWorkers(config.clusterLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// start queue
|
||||||
|
require('./queue').default();
|
||||||
|
|
||||||
bootLogger.succ(`Now listening on port ${config.port} on ${config.url}`, true);
|
bootLogger.succ(`Now listening on port ${config.port} on ${config.url}`, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,15 +127,35 @@ async function workerMain() {
|
|||||||
// start server
|
// start server
|
||||||
await require('./server').default();
|
await require('./server').default();
|
||||||
|
|
||||||
// start processor
|
|
||||||
require('./queue').default();
|
|
||||||
|
|
||||||
if (cluster.isWorker) {
|
if (cluster.isWorker) {
|
||||||
// Send a 'ready' message to parent process
|
// Send a 'ready' message to parent process
|
||||||
process.send('ready');
|
process.send('ready');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function queueMain() {
|
||||||
|
greet();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// initialize app
|
||||||
|
await init();
|
||||||
|
} catch (e) {
|
||||||
|
bootLogger.error('Fatal error occurred during initialization', true);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bootLogger.succ('Misskey initialized');
|
||||||
|
|
||||||
|
// start processor
|
||||||
|
const queue = require('./queue').default();
|
||||||
|
|
||||||
|
if (queue) {
|
||||||
|
bootLogger.succ('Queue started', true);
|
||||||
|
} else {
|
||||||
|
bootLogger.error('Queue not available');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const runningNodejsVersion = process.version.slice(1).split('.').map(x => parseInt(x, 10));
|
const runningNodejsVersion = process.version.slice(1).split('.').map(x => parseInt(x, 10));
|
||||||
const requiredNodejsVersion = [10, 0, 0];
|
const requiredNodejsVersion = [10, 0, 0];
|
||||||
const satisfyNodejsVersion = !lessThan(runningNodejsVersion, requiredNodejsVersion);
|
const satisfyNodejsVersion = !lessThan(runningNodejsVersion, requiredNodejsVersion);
|
||||||
@ -170,21 +217,6 @@ async function init(): Promise<Config> {
|
|||||||
|
|
||||||
configLogger.succ('Loaded');
|
configLogger.succ('Loaded');
|
||||||
|
|
||||||
if (config.port == null) {
|
|
||||||
bootLogger.error('The port is not configured. Please configure port.', true);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (process.platform === 'linux' && isWellKnownPort(config.port) && !isRoot()) {
|
|
||||||
bootLogger.error('You need root privileges to listen on well-known port on Linux', true);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!await isPortAvailable(config.port)) {
|
|
||||||
bootLogger.error(`Port ${config.port} is already in use`, true);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to connect to MongoDB
|
// Try to connect to MongoDB
|
||||||
try {
|
try {
|
||||||
await checkMongoDB(config, bootLogger);
|
await checkMongoDB(config, bootLogger);
|
||||||
@ -217,19 +249,19 @@ function spawnWorker(): Promise<void> {
|
|||||||
|
|
||||||
// Listen new workers
|
// Listen new workers
|
||||||
cluster.on('fork', worker => {
|
cluster.on('fork', worker => {
|
||||||
clusterLog.debug(`Process forked: [${worker.id}]`);
|
clusterLogger.debug(`Process forked: [${worker.id}]`);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Listen online workers
|
// Listen online workers
|
||||||
cluster.on('online', worker => {
|
cluster.on('online', worker => {
|
||||||
clusterLog.debug(`Process is now online: [${worker.id}]`);
|
clusterLogger.debug(`Process is now online: [${worker.id}]`);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Listen for dying workers
|
// Listen for dying workers
|
||||||
cluster.on('exit', worker => {
|
cluster.on('exit', worker => {
|
||||||
// Replace the dead worker,
|
// Replace the dead worker,
|
||||||
// we're not sentimental
|
// we're not sentimental
|
||||||
clusterLog.error(chalk.red(`[${worker.id}] died :(`));
|
clusterLogger.error(chalk.red(`[${worker.id}] died :(`));
|
||||||
cluster.fork();
|
cluster.fork();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -142,7 +142,7 @@ export const mfmLanguage = P.createLanguage({
|
|||||||
},
|
},
|
||||||
hashtag: () => P((input, i) => {
|
hashtag: () => P((input, i) => {
|
||||||
const text = input.substr(i);
|
const text = input.substr(i);
|
||||||
const match = text.match(/^#([^\s\.,!\?'"#:]+)/i);
|
const match = text.match(/^#([^\s\.,!\?'"#:\/]+)/i);
|
||||||
if (!match) return P.makeFailure(i, 'not a hashtag');
|
if (!match) return P.makeFailure(i, 'not a hashtag');
|
||||||
let hashtag = match[1];
|
let hashtag = match[1];
|
||||||
hashtag = removeOrphanedBrackets(hashtag);
|
hashtag = removeOrphanedBrackets(hashtag);
|
||||||
|
@ -21,6 +21,7 @@ export default class Logger {
|
|||||||
|
|
||||||
private log(level: string, message: string, important = false, subDomains: string[] = []): void {
|
private log(level: string, message: string, important = false, subDomains: string[] = []): void {
|
||||||
if (program.quiet) return;
|
if (program.quiet) return;
|
||||||
|
if (process.env.NODE_ENV === 'test') return;
|
||||||
const domain = this.color ? chalk.keyword(this.color)(this.domain) : chalk.white(this.domain);
|
const domain = this.color ? chalk.keyword(this.color)(this.domain) : chalk.white(this.domain);
|
||||||
const domains = [domain].concat(subDomains);
|
const domains = [domain].concat(subDomains);
|
||||||
if (this.parentLogger) {
|
if (this.parentLogger) {
|
||||||
@ -28,7 +29,8 @@ export default class Logger {
|
|||||||
} else {
|
} else {
|
||||||
const time = dateformat(new Date(), 'HH:MM:ss');
|
const time = dateformat(new Date(), 'HH:MM:ss');
|
||||||
const process = cluster.isMaster ? '*' : cluster.worker.id;
|
const process = cluster.isMaster ? '*' : cluster.worker.id;
|
||||||
const log = `${chalk.gray(time)} ${level} ${process}\t[${domains.join(' ')}]\t${message}`;
|
let log = `${level} ${process}\t[${domains.join(' ')}]\t${message}`;
|
||||||
|
if (program.withLogTime) log = chalk.gray(time) + ' ' + log;
|
||||||
console.log(important ? chalk.bold(log) : log);
|
console.log(important ? chalk.bold(log) : log);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -45,7 +47,7 @@ export default class Logger {
|
|||||||
this.log(important ? chalk.bgGreen.white('DONE') : chalk.green('DONE'), chalk.green(message), important);
|
this.log(important ? chalk.bgGreen.white('DONE') : chalk.green('DONE'), chalk.green(message), important);
|
||||||
}
|
}
|
||||||
|
|
||||||
public debug(message: string, important = false): void { // デバッグ用に使う(開発者にとっては必要だが利用者にとっては不要な情報)
|
public debug(message: string, important = false): void { // デバッグ用に使う(開発者に必要だが利用者に不要な情報)
|
||||||
if (process.env.NODE_ENV != 'production' || program.verbose) {
|
if (process.env.NODE_ENV != 'production' || program.verbose) {
|
||||||
this.log(chalk.gray('VERB'), chalk.gray(message), important);
|
this.log(chalk.gray('VERB'), chalk.gray(message), important);
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ DriveFile.createIndex('md5');
|
|||||||
DriveFile.createIndex('metadata.uri');
|
DriveFile.createIndex('metadata.uri');
|
||||||
DriveFile.createIndex('metadata.userId');
|
DriveFile.createIndex('metadata.userId');
|
||||||
DriveFile.createIndex('metadata.folderId');
|
DriveFile.createIndex('metadata.folderId');
|
||||||
|
DriveFile.createIndex('metadata._user.host');
|
||||||
export default DriveFile;
|
export default DriveFile;
|
||||||
|
|
||||||
export const DriveFileChunk = monkDb.get('driveFiles.chunks');
|
export const DriveFileChunk = monkDb.get('driveFiles.chunks');
|
||||||
|
@ -32,4 +32,59 @@ export interface IInstance {
|
|||||||
* このインスタンスから受け取った投稿数
|
* このインスタンスから受け取った投稿数
|
||||||
*/
|
*/
|
||||||
notesCount: number;
|
notesCount: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* このインスタンスのユーザーからフォローされている、自インスタンスのユーザーの数
|
||||||
|
*/
|
||||||
|
followingCount: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* このインスタンスのユーザーをフォローしている、自インスタンスのユーザーの数
|
||||||
|
*/
|
||||||
|
followersCount: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ドライブ使用量
|
||||||
|
*/
|
||||||
|
driveUsage: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ドライブのファイル数
|
||||||
|
*/
|
||||||
|
driveFiles: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 直近のリクエスト送信日時
|
||||||
|
*/
|
||||||
|
latestRequestSentAt?: Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 直近のリクエスト送信時のHTTPステータスコード
|
||||||
|
*/
|
||||||
|
latestStatus?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 直近のリクエスト受信日時
|
||||||
|
*/
|
||||||
|
latestRequestReceivedAt?: Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* このインスタンスと不通かどうか
|
||||||
|
*/
|
||||||
|
isNotResponding: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* このインスタンスと最後にやり取りした日時
|
||||||
|
*/
|
||||||
|
lastCommunicatedAt: Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* このインスタンスをブロックしているか
|
||||||
|
*/
|
||||||
|
isBlocked: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* このインスタンスが閉鎖済みとしてマークされているか
|
||||||
|
*/
|
||||||
|
isMarkedAsClosed: boolean;
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ const User = db.get<IUser>('users');
|
|||||||
|
|
||||||
User.createIndex('username');
|
User.createIndex('username');
|
||||||
User.createIndex('usernameLower');
|
User.createIndex('usernameLower');
|
||||||
|
User.createIndex('host');
|
||||||
User.createIndex(['username', 'host'], { unique: true });
|
User.createIndex(['username', 'host'], { unique: true });
|
||||||
User.createIndex(['usernameLower', 'host'], { unique: true });
|
User.createIndex(['usernameLower', 'host'], { unique: true });
|
||||||
User.createIndex('token', { sparse: true, unique: true });
|
User.createIndex('token', { sparse: true, unique: true });
|
||||||
|
20
src/prelude/maybe.ts
Normal file
20
src/prelude/maybe.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
export interface Maybe<T> {
|
||||||
|
isJust(): this is Just<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Just<T> = Maybe<T> & {
|
||||||
|
get(): T
|
||||||
|
};
|
||||||
|
|
||||||
|
export function just<T>(value: T): Just<T> {
|
||||||
|
return {
|
||||||
|
isJust: () => true,
|
||||||
|
get: () => value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function nothing<T>(): Maybe<T> {
|
||||||
|
return {
|
||||||
|
isJust: () => false,
|
||||||
|
};
|
||||||
|
}
|
@ -1,16 +1,20 @@
|
|||||||
import * as Queue from 'bee-queue';
|
import * as Queue from 'bee-queue';
|
||||||
import config from '../config';
|
import * as httpSignature from 'http-signature';
|
||||||
|
|
||||||
|
import config from '../config';
|
||||||
import { ILocalUser } from '../models/user';
|
import { ILocalUser } from '../models/user';
|
||||||
import { program } from '../argv';
|
import { program } from '../argv';
|
||||||
import handler from './processors';
|
import handler from './processors';
|
||||||
|
import { queueLogger } from './logger';
|
||||||
|
|
||||||
const enableQueue = config.redis != null && !program.disableQueue;
|
const enableQueue = !program.disableQueue;
|
||||||
|
const enableQueueProcessing = !program.onlyServer && enableQueue;
|
||||||
|
const queueAvailable = config.redis != null;
|
||||||
|
|
||||||
const queue = initializeQueue();
|
const queue = initializeQueue();
|
||||||
|
|
||||||
function initializeQueue() {
|
function initializeQueue() {
|
||||||
if (enableQueue) {
|
if (queueAvailable && enableQueue) {
|
||||||
return new Queue('misskey', {
|
return new Queue('misskey', {
|
||||||
redis: {
|
redis: {
|
||||||
port: config.redis.port,
|
port: config.redis.port,
|
||||||
@ -29,40 +33,106 @@ function initializeQueue() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createHttpJob(data: any) {
|
export function deliver(user: ILocalUser, content: any, to: any) {
|
||||||
if (enableQueue) {
|
if (content == null) return;
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
type: 'deliver',
|
||||||
|
user,
|
||||||
|
content,
|
||||||
|
to
|
||||||
|
};
|
||||||
|
|
||||||
|
if (queueAvailable && enableQueueProcessing) {
|
||||||
return queue.createJob(data)
|
return queue.createJob(data)
|
||||||
.retries(4)
|
.retries(8)
|
||||||
.backoff('exponential', 16384) // 16s
|
.backoff('exponential', 1000)
|
||||||
.save();
|
.save();
|
||||||
} else {
|
} else {
|
||||||
return handler({ data }, () => {});
|
return handler({ data }, () => {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deliver(user: ILocalUser, content: any, to: any) {
|
export function processInbox(activity: any, signature: httpSignature.IParsedSignature) {
|
||||||
if (content == null) return;
|
const data = {
|
||||||
|
type: 'processInbox',
|
||||||
|
activity: activity,
|
||||||
|
signature
|
||||||
|
};
|
||||||
|
|
||||||
createHttpJob({
|
if (queueAvailable && enableQueueProcessing) {
|
||||||
type: 'deliver',
|
return queue.createJob(data)
|
||||||
user,
|
.retries(3)
|
||||||
content,
|
.backoff('exponential', 500)
|
||||||
to
|
.save();
|
||||||
});
|
} else {
|
||||||
|
return handler({ data }, () => {});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createExportNotesJob(user: ILocalUser) {
|
export function createExportNotesJob(user: ILocalUser) {
|
||||||
if (!enableQueue) throw 'queue disabled';
|
const data = {
|
||||||
|
|
||||||
return queue.createJob({
|
|
||||||
type: 'exportNotes',
|
type: 'exportNotes',
|
||||||
user: user
|
user: user
|
||||||
})
|
};
|
||||||
.save();
|
|
||||||
|
if (queueAvailable && enableQueueProcessing) {
|
||||||
|
return queue.createJob(data).save();
|
||||||
|
} else {
|
||||||
|
return handler({ data }, () => {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createExportFollowingJob(user: ILocalUser) {
|
||||||
|
const data = {
|
||||||
|
type: 'exportFollowing',
|
||||||
|
user: user
|
||||||
|
};
|
||||||
|
|
||||||
|
if (queueAvailable && enableQueueProcessing) {
|
||||||
|
return queue.createJob(data).save();
|
||||||
|
} else {
|
||||||
|
return handler({ data }, () => {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createExportMuteJob(user: ILocalUser) {
|
||||||
|
const data = {
|
||||||
|
type: 'exportMute',
|
||||||
|
user: user
|
||||||
|
};
|
||||||
|
|
||||||
|
if (queueAvailable && enableQueueProcessing) {
|
||||||
|
return queue.createJob(data).save();
|
||||||
|
} else {
|
||||||
|
return handler({ data }, () => {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createExportBlockingJob(user: ILocalUser) {
|
||||||
|
const data = {
|
||||||
|
type: 'exportBlocking',
|
||||||
|
user: user
|
||||||
|
};
|
||||||
|
|
||||||
|
if (queueAvailable && enableQueueProcessing) {
|
||||||
|
return queue.createJob(data).save();
|
||||||
|
} else {
|
||||||
|
return handler({ data }, () => {});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function() {
|
export default function() {
|
||||||
if (enableQueue) {
|
if (queueAvailable && enableQueueProcessing) {
|
||||||
queue.process(128, handler);
|
queue.process(128, handler);
|
||||||
|
queueLogger.succ('Processing started');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function destroy() {
|
||||||
|
queue.destroy().then(n => {
|
||||||
|
queueLogger.succ(`All job removed (${n} jobs)`);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
89
src/queue/processors/export-blocking.ts
Normal file
89
src/queue/processors/export-blocking.ts
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import * as bq from 'bee-queue';
|
||||||
|
import * as tmp from 'tmp';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as mongo from 'mongodb';
|
||||||
|
|
||||||
|
import { queueLogger } from '../logger';
|
||||||
|
import addFile from '../../services/drive/add-file';
|
||||||
|
import User from '../../models/user';
|
||||||
|
import dateFormat = require('dateformat');
|
||||||
|
import Blocking from '../../models/blocking';
|
||||||
|
import config from '../../config';
|
||||||
|
|
||||||
|
const logger = queueLogger.createSubLogger('export-blocking');
|
||||||
|
|
||||||
|
export async function exportBlocking(job: bq.Job, done: any): Promise<void> {
|
||||||
|
logger.info(`Exporting blocking of ${job.data.user._id} ...`);
|
||||||
|
|
||||||
|
const user = await User.findOne({
|
||||||
|
_id: new mongo.ObjectID(job.data.user._id.toString())
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create temp file
|
||||||
|
const [path, cleanup] = await new Promise<[string, any]>((res, rej) => {
|
||||||
|
tmp.file((e, path, fd, cleanup) => {
|
||||||
|
if (e) return rej(e);
|
||||||
|
res([path, cleanup]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.info(`Temp file is ${path}`);
|
||||||
|
|
||||||
|
const stream = fs.createWriteStream(path, { flags: 'a' });
|
||||||
|
|
||||||
|
let exportedCount = 0;
|
||||||
|
let ended = false;
|
||||||
|
let cursor: any = null;
|
||||||
|
|
||||||
|
while (!ended) {
|
||||||
|
const blockings = await Blocking.find({
|
||||||
|
blockerId: user._id,
|
||||||
|
...(cursor ? { _id: { $gt: cursor } } : {})
|
||||||
|
}, {
|
||||||
|
limit: 100,
|
||||||
|
sort: {
|
||||||
|
_id: 1
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (blockings.length === 0) {
|
||||||
|
ended = true;
|
||||||
|
if (job.reportProgress) job.reportProgress(100);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor = blockings[blockings.length - 1]._id;
|
||||||
|
|
||||||
|
for (const block of blockings) {
|
||||||
|
const u = await User.findOne({ _id: block.blockeeId }, { fields: { username: true, host: true } });
|
||||||
|
const content = u.host ? `${u.username}@${u.host}` : `${u.username}@${config.host}`;
|
||||||
|
await new Promise((res, rej) => {
|
||||||
|
stream.write(content + '\n', err => {
|
||||||
|
if (err) {
|
||||||
|
logger.error(err);
|
||||||
|
rej(err);
|
||||||
|
} else {
|
||||||
|
res();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
exportedCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const total = await Blocking.count({
|
||||||
|
blockerId: user._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (job.reportProgress) job.reportProgress(exportedCount / total);
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.end();
|
||||||
|
logger.succ(`Exported to: ${path}`);
|
||||||
|
|
||||||
|
const fileName = 'blocking-' + dateFormat(new Date(), 'yyyy-mm-dd-HH-MM-ss') + '.csv';
|
||||||
|
const driveFile = await addFile(user, path, fileName);
|
||||||
|
|
||||||
|
logger.succ(`Exported to: ${driveFile._id}`);
|
||||||
|
cleanup();
|
||||||
|
done();
|
||||||
|
}
|
89
src/queue/processors/export-following.ts
Normal file
89
src/queue/processors/export-following.ts
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import * as bq from 'bee-queue';
|
||||||
|
import * as tmp from 'tmp';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as mongo from 'mongodb';
|
||||||
|
|
||||||
|
import { queueLogger } from '../logger';
|
||||||
|
import addFile from '../../services/drive/add-file';
|
||||||
|
import User from '../../models/user';
|
||||||
|
import dateFormat = require('dateformat');
|
||||||
|
import Following from '../../models/following';
|
||||||
|
import config from '../../config';
|
||||||
|
|
||||||
|
const logger = queueLogger.createSubLogger('export-following');
|
||||||
|
|
||||||
|
export async function exportFollowing(job: bq.Job, done: any): Promise<void> {
|
||||||
|
logger.info(`Exporting following of ${job.data.user._id} ...`);
|
||||||
|
|
||||||
|
const user = await User.findOne({
|
||||||
|
_id: new mongo.ObjectID(job.data.user._id.toString())
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create temp file
|
||||||
|
const [path, cleanup] = await new Promise<[string, any]>((res, rej) => {
|
||||||
|
tmp.file((e, path, fd, cleanup) => {
|
||||||
|
if (e) return rej(e);
|
||||||
|
res([path, cleanup]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.info(`Temp file is ${path}`);
|
||||||
|
|
||||||
|
const stream = fs.createWriteStream(path, { flags: 'a' });
|
||||||
|
|
||||||
|
let exportedCount = 0;
|
||||||
|
let ended = false;
|
||||||
|
let cursor: any = null;
|
||||||
|
|
||||||
|
while (!ended) {
|
||||||
|
const followings = await Following.find({
|
||||||
|
followerId: user._id,
|
||||||
|
...(cursor ? { _id: { $gt: cursor } } : {})
|
||||||
|
}, {
|
||||||
|
limit: 100,
|
||||||
|
sort: {
|
||||||
|
_id: 1
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (followings.length === 0) {
|
||||||
|
ended = true;
|
||||||
|
if (job.reportProgress) job.reportProgress(100);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor = followings[followings.length - 1]._id;
|
||||||
|
|
||||||
|
for (const following of followings) {
|
||||||
|
const u = await User.findOne({ _id: following.followeeId }, { fields: { username: true, host: true } });
|
||||||
|
const content = u.host ? `${u.username}@${u.host}` : `${u.username}@${config.host}`;
|
||||||
|
await new Promise((res, rej) => {
|
||||||
|
stream.write(content + '\n', err => {
|
||||||
|
if (err) {
|
||||||
|
logger.error(err);
|
||||||
|
rej(err);
|
||||||
|
} else {
|
||||||
|
res();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
exportedCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const total = await Following.count({
|
||||||
|
followerId: user._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (job.reportProgress) job.reportProgress(exportedCount / total);
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.end();
|
||||||
|
logger.succ(`Exported to: ${path}`);
|
||||||
|
|
||||||
|
const fileName = 'following-' + dateFormat(new Date(), 'yyyy-mm-dd-HH-MM-ss') + '.csv';
|
||||||
|
const driveFile = await addFile(user, path, fileName);
|
||||||
|
|
||||||
|
logger.succ(`Exported to: ${driveFile._id}`);
|
||||||
|
cleanup();
|
||||||
|
done();
|
||||||
|
}
|
89
src/queue/processors/export-mute.ts
Normal file
89
src/queue/processors/export-mute.ts
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import * as bq from 'bee-queue';
|
||||||
|
import * as tmp from 'tmp';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as mongo from 'mongodb';
|
||||||
|
|
||||||
|
import { queueLogger } from '../logger';
|
||||||
|
import addFile from '../../services/drive/add-file';
|
||||||
|
import User from '../../models/user';
|
||||||
|
import dateFormat = require('dateformat');
|
||||||
|
import Mute from '../../models/mute';
|
||||||
|
import config from '../../config';
|
||||||
|
|
||||||
|
const logger = queueLogger.createSubLogger('export-mute');
|
||||||
|
|
||||||
|
export async function exportMute(job: bq.Job, done: any): Promise<void> {
|
||||||
|
logger.info(`Exporting mute of ${job.data.user._id} ...`);
|
||||||
|
|
||||||
|
const user = await User.findOne({
|
||||||
|
_id: new mongo.ObjectID(job.data.user._id.toString())
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create temp file
|
||||||
|
const [path, cleanup] = await new Promise<[string, any]>((res, rej) => {
|
||||||
|
tmp.file((e, path, fd, cleanup) => {
|
||||||
|
if (e) return rej(e);
|
||||||
|
res([path, cleanup]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.info(`Temp file is ${path}`);
|
||||||
|
|
||||||
|
const stream = fs.createWriteStream(path, { flags: 'a' });
|
||||||
|
|
||||||
|
let exportedCount = 0;
|
||||||
|
let ended = false;
|
||||||
|
let cursor: any = null;
|
||||||
|
|
||||||
|
while (!ended) {
|
||||||
|
const mutes = await Mute.find({
|
||||||
|
muterId: user._id,
|
||||||
|
...(cursor ? { _id: { $gt: cursor } } : {})
|
||||||
|
}, {
|
||||||
|
limit: 100,
|
||||||
|
sort: {
|
||||||
|
_id: 1
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (mutes.length === 0) {
|
||||||
|
ended = true;
|
||||||
|
if (job.reportProgress) job.reportProgress(100);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor = mutes[mutes.length - 1]._id;
|
||||||
|
|
||||||
|
for (const mute of mutes) {
|
||||||
|
const u = await User.findOne({ _id: mute.muteeId }, { fields: { username: true, host: true } });
|
||||||
|
const content = u.host ? `${u.username}@${u.host}` : `${u.username}@${config.host}`;
|
||||||
|
await new Promise((res, rej) => {
|
||||||
|
stream.write(content + '\n', err => {
|
||||||
|
if (err) {
|
||||||
|
logger.error(err);
|
||||||
|
rej(err);
|
||||||
|
} else {
|
||||||
|
res();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
exportedCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const total = await Mute.count({
|
||||||
|
muterId: user._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (job.reportProgress) job.reportProgress(exportedCount / total);
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.end();
|
||||||
|
logger.succ(`Exported to: ${path}`);
|
||||||
|
|
||||||
|
const fileName = 'mute-' + dateFormat(new Date(), 'yyyy-mm-dd-HH-MM-ss') + '.csv';
|
||||||
|
const driveFile = await addFile(user, path, fileName);
|
||||||
|
|
||||||
|
logger.succ(`Exported to: ${driveFile._id}`);
|
||||||
|
cleanup();
|
||||||
|
done();
|
||||||
|
}
|
@ -58,7 +58,7 @@ export async function exportNotes(job: bq.Job, done: any): Promise<void> {
|
|||||||
|
|
||||||
if (notes.length === 0) {
|
if (notes.length === 0) {
|
||||||
ended = true;
|
ended = true;
|
||||||
job.reportProgress(100);
|
if (job.reportProgress) job.reportProgress(100);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,7 +83,7 @@ export async function exportNotes(job: bq.Job, done: any): Promise<void> {
|
|||||||
userId: user._id,
|
userId: user._id,
|
||||||
});
|
});
|
||||||
|
|
||||||
job.reportProgress(exportedNotesCount / total);
|
if (job.reportProgress) job.reportProgress(exportedNotesCount / total);
|
||||||
}
|
}
|
||||||
|
|
||||||
await new Promise((res, rej) => {
|
await new Promise((res, rej) => {
|
||||||
@ -100,7 +100,7 @@ export async function exportNotes(job: bq.Job, done: any): Promise<void> {
|
|||||||
stream.end();
|
stream.end();
|
||||||
logger.succ(`Exported to: ${path}`);
|
logger.succ(`Exported to: ${path}`);
|
||||||
|
|
||||||
const fileName = dateFormat(new Date(), 'yyyy-mm-dd-HH-MM-ss') + '.json';
|
const fileName = 'notes-' + dateFormat(new Date(), 'yyyy-mm-dd-HH-MM-ss') + '.json';
|
||||||
const driveFile = await addFile(user, path, fileName);
|
const driveFile = await addFile(user, path, fileName);
|
||||||
|
|
||||||
logger.succ(`Exported to: ${driveFile._id}`);
|
logger.succ(`Exported to: ${driveFile._id}`);
|
||||||
|
@ -2,19 +2,53 @@ import * as bq from 'bee-queue';
|
|||||||
|
|
||||||
import request from '../../../remote/activitypub/request';
|
import request from '../../../remote/activitypub/request';
|
||||||
import { queueLogger } from '../../logger';
|
import { queueLogger } from '../../logger';
|
||||||
|
import { registerOrFetchInstanceDoc } from '../../../services/register-or-fetch-instance-doc';
|
||||||
|
import Instance from '../../../models/instance';
|
||||||
|
import instanceChart from '../../../services/chart/instance';
|
||||||
|
|
||||||
export default async (job: bq.Job, done: any): Promise<void> => {
|
export default async (job: bq.Job, done: any): Promise<void> => {
|
||||||
|
const { host } = new URL(job.data.to);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await request(job.data.user, job.data.to, job.data.content);
|
await request(job.data.user, job.data.to, job.data.content);
|
||||||
|
|
||||||
|
// Update stats
|
||||||
|
registerOrFetchInstanceDoc(host).then(i => {
|
||||||
|
Instance.update({ _id: i._id }, {
|
||||||
|
$set: {
|
||||||
|
latestRequestSentAt: new Date(),
|
||||||
|
latestStatus: 200,
|
||||||
|
lastCommunicatedAt: new Date(),
|
||||||
|
isNotResponding: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
instanceChart.requestSent(i.host, true);
|
||||||
|
});
|
||||||
|
|
||||||
done();
|
done();
|
||||||
} catch (res) {
|
} catch (res) {
|
||||||
|
// Update stats
|
||||||
|
registerOrFetchInstanceDoc(host).then(i => {
|
||||||
|
Instance.update({ _id: i._id }, {
|
||||||
|
$set: {
|
||||||
|
latestRequestSentAt: new Date(),
|
||||||
|
latestStatus: res != null && res.hasOwnProperty('statusCode') ? res.statusCode : null,
|
||||||
|
isNotResponding: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
instanceChart.requestSent(i.host, false);
|
||||||
|
});
|
||||||
|
|
||||||
if (res != null && res.hasOwnProperty('statusCode')) {
|
if (res != null && res.hasOwnProperty('statusCode')) {
|
||||||
|
queueLogger.warn(`deliver failed: ${res.statusCode} ${res.statusMessage} to=${job.data.to}`);
|
||||||
|
|
||||||
if (res.statusCode >= 400 && res.statusCode < 500) {
|
if (res.statusCode >= 400 && res.statusCode < 500) {
|
||||||
// HTTPステータスコード4xxはクライアントエラーであり、それはつまり
|
// HTTPステータスコード4xxはクライアントエラーであり、それはつまり
|
||||||
// 何回再送しても成功することはないということなのでエラーにはしないでおく
|
// 何回再送しても成功することはないということなのでエラーにはしないでおく
|
||||||
done();
|
done();
|
||||||
} else {
|
} else {
|
||||||
queueLogger.warn(`deliver failed: ${res.statusCode} ${res.statusMessage} to=${job.data.to}`);
|
|
||||||
done(res.statusMessage);
|
done(res.statusMessage);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -8,6 +8,9 @@ import { toUnicode } from 'punycode';
|
|||||||
import { URL } from 'url';
|
import { URL } from 'url';
|
||||||
import { publishApLogStream } from '../../../services/stream';
|
import { publishApLogStream } from '../../../services/stream';
|
||||||
import Logger from '../../../misc/logger';
|
import Logger from '../../../misc/logger';
|
||||||
|
import { registerOrFetchInstanceDoc } from '../../../services/register-or-fetch-instance-doc';
|
||||||
|
import Instance from '../../../models/instance';
|
||||||
|
import instanceChart from '../../../services/chart/instance';
|
||||||
|
|
||||||
const logger = new Logger('inbox');
|
const logger = new Logger('inbox');
|
||||||
|
|
||||||
@ -43,6 +46,15 @@ export default async (job: bq.Job, done: any): Promise<void> => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ブロックしてたら中断
|
||||||
|
// TODO: いちいちデータベースにアクセスするのはコスト高そうなのでどっかにキャッシュしておく
|
||||||
|
const instance = await Instance.findOne({ host: host.toLowerCase() });
|
||||||
|
if (instance && instance.isBlocked) {
|
||||||
|
logger.warn(`Blocked request: ${host}`);
|
||||||
|
done();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
user = await User.findOne({ usernameLower: username, host: host.toLowerCase() }) as IRemoteUser;
|
user = await User.findOne({ usernameLower: username, host: host.toLowerCase() }) as IRemoteUser;
|
||||||
} else {
|
} else {
|
||||||
// アクティビティ内のホストの検証
|
// アクティビティ内のホストの検証
|
||||||
@ -55,6 +67,15 @@ export default async (job: bq.Job, done: any): Promise<void> => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ブロックしてたら中断
|
||||||
|
// TODO: いちいちデータベースにアクセスするのはコスト高そうなのでどっかにキャッシュしておく
|
||||||
|
const instance = await Instance.findOne({ host: host.toLowerCase() });
|
||||||
|
if (instance && instance.isBlocked) {
|
||||||
|
logger.warn(`Blocked request: ${host}`);
|
||||||
|
done();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
user = await User.findOne({
|
user = await User.findOne({
|
||||||
host: { $ne: null },
|
host: { $ne: null },
|
||||||
'publicKey.id': signature.keyId
|
'publicKey.id': signature.keyId
|
||||||
@ -101,6 +122,19 @@ export default async (job: bq.Job, done: any): Promise<void> => {
|
|||||||
});
|
});
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
// Update stats
|
||||||
|
registerOrFetchInstanceDoc(user.host).then(i => {
|
||||||
|
Instance.update({ _id: i._id }, {
|
||||||
|
$set: {
|
||||||
|
latestRequestReceivedAt: new Date(),
|
||||||
|
lastCommunicatedAt: new Date(),
|
||||||
|
isNotResponding: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
instanceChart.requestReceived(i.host);
|
||||||
|
});
|
||||||
|
|
||||||
// アクティビティを処理
|
// アクティビティを処理
|
||||||
try {
|
try {
|
||||||
await perform(user, activity);
|
await perform(user, activity);
|
||||||
|
@ -1,12 +1,18 @@
|
|||||||
import deliver from './http/deliver';
|
import deliver from './http/deliver';
|
||||||
import processInbox from './http/process-inbox';
|
import processInbox from './http/process-inbox';
|
||||||
import { exportNotes } from './export-notes';
|
import { exportNotes } from './export-notes';
|
||||||
|
import { exportFollowing } from './export-following';
|
||||||
|
import { exportMute } from './export-mute';
|
||||||
|
import { exportBlocking } from './export-blocking';
|
||||||
import { queueLogger } from '../logger';
|
import { queueLogger } from '../logger';
|
||||||
|
|
||||||
const handlers: any = {
|
const handlers: any = {
|
||||||
deliver,
|
deliver,
|
||||||
processInbox,
|
processInbox,
|
||||||
exportNotes,
|
exportNotes,
|
||||||
|
exportFollowing,
|
||||||
|
exportMute,
|
||||||
|
exportBlocking,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default (job: any, done: any) => {
|
export default (job: any, done: any) => {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import * as mongo from 'mongodb';
|
import * as mongo from 'mongodb';
|
||||||
|
import * as promiseLimit from 'promise-limit';
|
||||||
|
|
||||||
import config from '../../../config';
|
import config from '../../../config';
|
||||||
import Resolver from '../resolver';
|
import Resolver from '../resolver';
|
||||||
@ -16,6 +17,7 @@ import { unique, concat, difference } from '../../../prelude/array';
|
|||||||
import { extractPollFromQuestion } from './question';
|
import { extractPollFromQuestion } from './question';
|
||||||
import vote from '../../../services/note/polls/vote';
|
import vote from '../../../services/note/polls/vote';
|
||||||
import { apLogger } from '../logger';
|
import { apLogger } from '../logger';
|
||||||
|
import { IDriveFile } from '../../../models/drive-file';
|
||||||
|
|
||||||
const logger = apLogger;
|
const logger = apLogger;
|
||||||
|
|
||||||
@ -92,9 +94,10 @@ export async function createNote(value: any, resolver?: Resolver, silent = false
|
|||||||
// TODO: attachmentは必ずしもImageではない
|
// TODO: attachmentは必ずしもImageではない
|
||||||
// TODO: attachmentは必ずしも配列ではない
|
// TODO: attachmentは必ずしも配列ではない
|
||||||
// Noteがsensitiveなら添付もsensitiveにする
|
// Noteがsensitiveなら添付もsensitiveにする
|
||||||
|
const limit = promiseLimit(2);
|
||||||
const files = note.attachment
|
const files = note.attachment
|
||||||
.map(attach => attach.sensitive = note.sensitive)
|
.map(attach => attach.sensitive = note.sensitive)
|
||||||
? await Promise.all(note.attachment.map(x => resolveImage(actor, x)))
|
? await Promise.all(note.attachment.map(x => limit(() => resolveImage(actor, x)) as Promise<IDriveFile>))
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
// リプライ
|
// リプライ
|
||||||
@ -233,8 +236,9 @@ async function extractMentionedUsers(actor: IRemoteUser, to: string[], cc: strin
|
|||||||
const ignoreUris = ['https://www.w3.org/ns/activitystreams#Public', `${actor.uri}/followers`];
|
const ignoreUris = ['https://www.w3.org/ns/activitystreams#Public', `${actor.uri}/followers`];
|
||||||
const uris = difference(unique(concat([to || [], cc || []])), ignoreUris);
|
const uris = difference(unique(concat([to || [], cc || []])), ignoreUris);
|
||||||
|
|
||||||
|
const limit = promiseLimit(2);
|
||||||
const users = await Promise.all(
|
const users = await Promise.all(
|
||||||
uris.map(async uri => await resolvePerson(uri, null, resolver).catch(() => null))
|
uris.map(uri => limit(() => resolvePerson(uri, null, resolver).catch(() => null)) as Promise<IUser>)
|
||||||
);
|
);
|
||||||
|
|
||||||
return users.filter(x => x != null);
|
return users.filter(x => x != null);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import * as mongo from 'mongodb';
|
import * as mongo from 'mongodb';
|
||||||
|
import * as promiseLimit from 'promise-limit';
|
||||||
import { toUnicode } from 'punycode';
|
import { toUnicode } from 'punycode';
|
||||||
|
|
||||||
import config from '../../../config';
|
import config from '../../../config';
|
||||||
@ -9,10 +10,11 @@ import { isCollectionOrOrderedCollection, isCollection, IPerson } from '../type'
|
|||||||
import { IDriveFile } from '../../../models/drive-file';
|
import { IDriveFile } from '../../../models/drive-file';
|
||||||
import Meta from '../../../models/meta';
|
import Meta from '../../../models/meta';
|
||||||
import { fromHtml } from '../../../mfm/fromHtml';
|
import { fromHtml } from '../../../mfm/fromHtml';
|
||||||
import usersChart from '../../../chart/users';
|
import usersChart from '../../../services/chart/users';
|
||||||
|
import instanceChart from '../../../services/chart/instance';
|
||||||
import { URL } from 'url';
|
import { URL } from 'url';
|
||||||
import { resolveNote, extractEmojis } from './note';
|
import { resolveNote, extractEmojis } from './note';
|
||||||
import registerInstance from '../../../services/register-instance';
|
import { registerOrFetchInstanceDoc } from '../../../services/register-or-fetch-instance-doc';
|
||||||
import Instance from '../../../models/instance';
|
import Instance from '../../../models/instance';
|
||||||
import getDriveFileUrl from '../../../misc/get-drive-file-url';
|
import getDriveFileUrl from '../../../misc/get-drive-file-url';
|
||||||
import { IEmoji } from '../../../models/emoji';
|
import { IEmoji } from '../../../models/emoji';
|
||||||
@ -20,7 +22,7 @@ import { ITag, extractHashtags } from './tag';
|
|||||||
import Following from '../../../models/following';
|
import Following from '../../../models/following';
|
||||||
import { IIdentifier } from './identifier';
|
import { IIdentifier } from './identifier';
|
||||||
import { apLogger } from '../logger';
|
import { apLogger } from '../logger';
|
||||||
|
import { INote } from '../../../models/note';
|
||||||
const logger = apLogger;
|
const logger = apLogger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -188,15 +190,14 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<IU
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Register host
|
// Register host
|
||||||
registerInstance(host).then(i => {
|
registerOrFetchInstanceDoc(host).then(i => {
|
||||||
Instance.update({ _id: i._id }, {
|
Instance.update({ _id: i._id }, {
|
||||||
$inc: {
|
$inc: {
|
||||||
usersCount: 1
|
usersCount: 1
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO
|
instanceChart.newUser(i.host);
|
||||||
//perInstanceChart.newUser();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//#region Increment users count
|
//#region Increment users count
|
||||||
@ -494,10 +495,11 @@ export async function updateFeatured(userId: mongo.ObjectID) {
|
|||||||
if (!Array.isArray(items)) throw new Error(`Collection items is not an array`);
|
if (!Array.isArray(items)) throw new Error(`Collection items is not an array`);
|
||||||
|
|
||||||
// Resolve and regist Notes
|
// Resolve and regist Notes
|
||||||
|
const limit = promiseLimit(2);
|
||||||
const featuredNotes = await Promise.all(items
|
const featuredNotes = await Promise.all(items
|
||||||
.filter(item => item.type === 'Note')
|
.filter(item => item.type === 'Note')
|
||||||
.slice(0, 5)
|
.slice(0, 5)
|
||||||
.map(item => resolveNote(item, resolver)));
|
.map(item => limit(() => resolveNote(item, resolver)) as Promise<INote>));
|
||||||
|
|
||||||
await User.update({ _id: user._id }, {
|
await User.update({ _id: user._id }, {
|
||||||
$set: {
|
$set: {
|
||||||
|
@ -4,11 +4,13 @@ import { URL } from 'url';
|
|||||||
import * as crypto from 'crypto';
|
import * as crypto from 'crypto';
|
||||||
import { lookup, IRunOptions } from 'lookup-dns-cache';
|
import { lookup, IRunOptions } from 'lookup-dns-cache';
|
||||||
import * as promiseAny from 'promise-any';
|
import * as promiseAny from 'promise-any';
|
||||||
|
import { toUnicode } from 'punycode';
|
||||||
|
|
||||||
import config from '../../config';
|
import config from '../../config';
|
||||||
import { ILocalUser } from '../../models/user';
|
import { ILocalUser } from '../../models/user';
|
||||||
import { publishApLogStream } from '../../services/stream';
|
import { publishApLogStream } from '../../services/stream';
|
||||||
import { apLogger } from './logger';
|
import { apLogger } from './logger';
|
||||||
|
import Instance from '../../models/instance';
|
||||||
|
|
||||||
export const logger = apLogger.createSubLogger('deliver');
|
export const logger = apLogger.createSubLogger('deliver');
|
||||||
|
|
||||||
@ -19,6 +21,11 @@ export default (user: ILocalUser, url: string, object: any) => new Promise(async
|
|||||||
|
|
||||||
const { protocol, host, hostname, port, pathname, search } = new URL(url);
|
const { protocol, host, hostname, port, pathname, search } = new URL(url);
|
||||||
|
|
||||||
|
// ブロックしてたら中断
|
||||||
|
// TODO: いちいちデータベースにアクセスするのはコスト高そうなのでどっかにキャッシュしておく
|
||||||
|
const instance = await Instance.findOne({ host: toUnicode(host) });
|
||||||
|
if (instance && instance.isBlocked) return;
|
||||||
|
|
||||||
const data = JSON.stringify(object);
|
const data = JSON.stringify(object);
|
||||||
|
|
||||||
const sha256 = crypto.createHash('sha256');
|
const sha256 = crypto.createHash('sha256');
|
||||||
@ -43,11 +50,11 @@ export default (user: ILocalUser, url: string, object: any) => new Promise(async
|
|||||||
'Digest': `SHA-256=${hash}`
|
'Digest': `SHA-256=${hash}`
|
||||||
}
|
}
|
||||||
}, res => {
|
}, res => {
|
||||||
logger.info(`${url} --> ${res.statusCode}`);
|
|
||||||
|
|
||||||
if (res.statusCode >= 400) {
|
if (res.statusCode >= 400) {
|
||||||
|
logger.warn(`${url} --> ${res.statusCode}`);
|
||||||
reject(res);
|
reject(res);
|
||||||
} else {
|
} else {
|
||||||
|
logger.succ(`${url} --> ${res.statusCode}`);
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -19,23 +19,28 @@ export default class Resolver {
|
|||||||
: value;
|
: value;
|
||||||
|
|
||||||
switch (collection.type) {
|
switch (collection.type) {
|
||||||
case 'Collection':
|
case 'Collection': {
|
||||||
collection.objects = collection.items;
|
collection.objects = collection.items;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'OrderedCollection':
|
case 'OrderedCollection': {
|
||||||
collection.objects = collection.orderedItems;
|
collection.objects = collection.orderedItems;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default: {
|
||||||
|
logger.error(`unknown collection type: ${collection.type}`);
|
||||||
throw new Error(`unknown collection type: ${collection.type}`);
|
throw new Error(`unknown collection type: ${collection.type}`);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return collection;
|
return collection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async resolve(value: any): Promise<IObject> {
|
public async resolve(value: any): Promise<IObject> {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
|
logger.error('resolvee is null (or undefined)');
|
||||||
throw new Error('resolvee is null (or undefined)');
|
throw new Error('resolvee is null (or undefined)');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,6 +49,7 @@ export default class Resolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.history.has(value)) {
|
if (this.history.has(value)) {
|
||||||
|
logger.error(`cannot resolve already resolved one`);
|
||||||
throw new Error('cannot resolve already resolved one');
|
throw new Error('cannot resolve already resolved one');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,6 +65,7 @@ export default class Resolver {
|
|||||||
},
|
},
|
||||||
json: true
|
json: true
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
|
logger.error(`request error: ${e.message}`);
|
||||||
throw new Error(`request error: ${e.message}`);
|
throw new Error(`request error: ${e.message}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@ import * as Router from 'koa-router';
|
|||||||
import * as json from 'koa-json-body';
|
import * as json from 'koa-json-body';
|
||||||
import * as httpSignature from 'http-signature';
|
import * as httpSignature from 'http-signature';
|
||||||
|
|
||||||
import { createHttpJob } from '../queue';
|
|
||||||
import { renderActivity } from '../remote/activitypub/renderer';
|
import { renderActivity } from '../remote/activitypub/renderer';
|
||||||
import Note from '../models/note';
|
import Note from '../models/note';
|
||||||
import User, { isLocalUser, ILocalUser, IUser } from '../models/user';
|
import User, { isLocalUser, ILocalUser, IUser } from '../models/user';
|
||||||
@ -17,6 +16,7 @@ import Followers from './activitypub/followers';
|
|||||||
import Following from './activitypub/following';
|
import Following from './activitypub/following';
|
||||||
import Featured from './activitypub/featured';
|
import Featured from './activitypub/featured';
|
||||||
import renderQuestion from '../remote/activitypub/renderer/question';
|
import renderQuestion from '../remote/activitypub/renderer/question';
|
||||||
|
import { processInbox } from '../queue';
|
||||||
|
|
||||||
// Init router
|
// Init router
|
||||||
const router = new Router();
|
const router = new Router();
|
||||||
@ -35,11 +35,7 @@ function inbox(ctx: Router.IRouterContext) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
createHttpJob({
|
processInbox(ctx.request.body, signature);
|
||||||
type: 'processInbox',
|
|
||||||
activity: ctx.request.body,
|
|
||||||
signature
|
|
||||||
});
|
|
||||||
|
|
||||||
ctx.status = 202;
|
ctx.status = 202;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
import $ from 'cafy';
|
||||||
|
import define from '../../../define';
|
||||||
|
import Following from '../../../../../models/following';
|
||||||
|
import User from '../../../../../models/user';
|
||||||
|
import deleteFollowing from '../../../../../services/following/delete';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
requireCredential: true,
|
||||||
|
requireModerator: true,
|
||||||
|
|
||||||
|
params: {
|
||||||
|
host: {
|
||||||
|
validator: $.str
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default define(meta, (ps, me) => new Promise(async (res, rej) => {
|
||||||
|
const followings = await Following.find({
|
||||||
|
'_follower.host': ps.host
|
||||||
|
});
|
||||||
|
|
||||||
|
const pairs = await Promise.all(followings.map(f => Promise.all([
|
||||||
|
User.findOne({ _id: f.followerId }),
|
||||||
|
User.findOne({ _id: f.followeeId })
|
||||||
|
])));
|
||||||
|
|
||||||
|
for (const pair of pairs) {
|
||||||
|
deleteFollowing(pair[0], pair[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
res();
|
||||||
|
}));
|
39
src/server/api/endpoints/admin/federation/update-instance.ts
Normal file
39
src/server/api/endpoints/admin/federation/update-instance.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import $ from 'cafy';
|
||||||
|
import define from '../../../define';
|
||||||
|
import Instance from '../../../../../models/instance';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
requireCredential: true,
|
||||||
|
requireModerator: true,
|
||||||
|
|
||||||
|
params: {
|
||||||
|
host: {
|
||||||
|
validator: $.str
|
||||||
|
},
|
||||||
|
|
||||||
|
isBlocked: {
|
||||||
|
validator: $.bool
|
||||||
|
},
|
||||||
|
|
||||||
|
isClosed: {
|
||||||
|
validator: $.bool
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default define(meta, (ps, me) => new Promise(async (res, rej) => {
|
||||||
|
const instance = await Instance.findOne({ host: ps.host });
|
||||||
|
|
||||||
|
if (instance == null) {
|
||||||
|
return rej('instance not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
Instance.update({ host: ps.host }, {
|
||||||
|
$set: {
|
||||||
|
isBlocked: ps.isBlocked,
|
||||||
|
isMarkedAsClosed: ps.isClosed
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
res();
|
||||||
|
}));
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user