Compare commits

..

87 Commits

Author SHA1 Message Date
syuilo
9ea7d446e8 10.84.0 2019-02-09 13:04:58 +09:00
MeiMei
757312ba52 Limit the parallelism of AP object processing (#4193) 2019-02-09 13:01:21 +09:00
syuilo
1675c473d4 🎨 2019-02-09 12:43:26 +09:00
syuilo
3a3a5d4bfb Update vue 2019-02-09 12:35:52 +09:00
syuilo
4a41499c95 🎨 2019-02-09 12:34:42 +09:00
syuilo
a1d1cb58e0 New Crowdin translations (#4184)
* New translations ja-JP.yml (Catalan)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Catalan)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Catalan)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)
2019-02-09 12:29:44 +09:00
syuilo
acb82fe7b6 フォロー処理のリファクタリング (#4196)
* Fix #4185

* Fix bug
2019-02-09 12:04:03 +09:00
dependabot[bot]
b25df24cea Merge pull request #4197 from syuilo/dependabot/npm_and_yarn/@fortawesome/free-solid-svg-icons-5.7.1 2019-02-08 20:37:00 +00:00
dependabot[bot]
39284eb9b2 Update @fortawesome/free-solid-svg-icons requirement from 5.6.3 to 5.7.1
Updates the requirements on [@fortawesome/free-solid-svg-icons](https://github.com/FortAwesome/Font-Awesome) to permit the latest version.
- [Release notes](https://github.com/FortAwesome/Font-Awesome/releases)
- [Changelog](https://github.com/FortAwesome/Font-Awesome/blob/master/CHANGELOG.md)
- [Commits](https://github.com/FortAwesome/Font-Awesome/commits/5.7.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-08 20:27:31 +00:00
syuilo
31b0e552a2 Improve usability 2019-02-09 01:53:46 +09:00
fangzheng
c4a2a31cf3 Update ja-JP.yml (#4195)
Fix incorrect strings
2019-02-08 23:10:42 +09:00
かひわし4(バージョン1)
4497ddb3f3 Doc: Add bug details to CHANGELOG (#4191)
Bug in Misskey 10.82.3 (#4179) is critical to server administrators,
and they need more detail about it.
2019-02-08 21:04:04 +09:00
syuilo
5e0eda9526 Improve instances manegement
Resolve #4187
2019-02-08 20:56:16 +09:00
syuilo
72b85fc09f 10.83.0 2019-02-08 17:06:07 +09:00
syuilo
6c27412c9c Fix theme 2019-02-08 17:05:50 +09:00
syuilo
46bddfc9c2 New Crowdin translations (#4178)
* New translations ja-JP.yml (Catalan)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Catalan)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Catalan)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Catalan)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)
2019-02-08 17:01:43 +09:00
syuilo
56275bcfcb Introduce per-instance chart (#4183)
* Introduce per-instance chart

* Implement chart view in client

* Handle note deleting

* More chart srcs

* Add drive stats

* Improve drive stats

* Fix bug

* Add icon
2019-02-08 16:58:57 +09:00
syuilo
f35688bab8 Supress logs during test 2019-02-08 16:56:23 +09:00
syuilo
93541f83c8 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-02-08 04:40:55 +09:00
syuilo
ea0d114833 🎨 2019-02-08 04:40:47 +09:00
syuilo
7f6a3ec828 🎨 2019-02-08 04:36:23 +09:00
syuilo
732804b6fa Update CONTRIBUTING.md 2019-02-08 04:33:15 +09:00
syuilo
aba85b977d Refactoring: Move chart dir into services dir 2019-02-08 04:31:33 +09:00
syuilo
e6612f610c Implement instance blocking (#4182)
* Implement instance blocking

* Add missing text

* Delete unnecessary file

* Covert Punycode to Unicode
2019-02-08 04:26:43 +09:00
syuilo
5a28632af7 Update CONTRIBUTING.md 2019-02-08 04:08:25 +09:00
syuilo
4099db0d42 [Client] Add icon 🎨 2019-02-07 23:42:56 +09:00
syuilo
9d50a06d9c Fix bug 2019-02-07 23:37:39 +09:00
syuilo
dd7bf9b2a3 Remove unused import 2019-02-07 23:32:39 +09:00
syuilo
c463284c2f Fix bug 2019-02-07 23:27:42 +09:00
syuilo
c1d728a616 インスタンス一覧の表示数を増やした 2019-02-07 22:00:55 +09:00
syuilo
e43c9c0e21 特定インスタンスからのフォローを全解除できるように 2019-02-07 21:59:18 +09:00
syuilo
15cac10d7b 10.82.4 2019-02-07 21:30:38 +09:00
syuilo
49958ca03f Make instance information more detail 2019-02-07 21:23:12 +09:00
syuilo
280dbe9853 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-02-07 21:07:58 +09:00
Acid Chicken (硫酸鶏)
bf964ee969 Update load.ts 2019-02-07 21:03:24 +09:00
syuilo
61dcd51888 Revert "Fix bug"
This reverts commit 2ef795aba8.
2019-02-07 21:02:57 +09:00
syuilo
5448c22031 Revert 96bc17aa10 2019-02-07 21:02:33 +09:00
Acid Chicken (硫酸鶏)
27768081e2 Fix #4179 2019-02-07 20:14:15 +09:00
syuilo
c3140f57b9 連合しているインスタンスを一覧できるように 2019-02-07 18:11:20 +09:00
syuilo
7275bc6d3b Improve instance stats 2019-02-07 16:05:29 +09:00
syuilo
485f2f460e Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-02-07 15:00:52 +09:00
syuilo
336912e442 Improve instance stats 2019-02-07 15:00:44 +09:00
syuilo
dd9c94e47e Update CONTRIBUTING.md 2019-02-07 14:54:14 +09:00
Acid Chicken (硫酸鶏)
055863144d Update issue templates (#4038)
* WIP: Update issue templates

* Update client-side-feature-request.md

* Update bug_report.md

* Update feature_request.md

* Update server-side-bug-report.md

* Update server-side-feature-request.md

* Update bug_report.md
2019-02-07 14:46:17 +09:00
syuilo
0bf602bae6 10.82.3 2019-02-07 10:55:52 +09:00
syuilo
6fc28d1df7 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-02-07 10:52:07 +09:00
syuilo
2ef795aba8 Fix bug 2019-02-07 10:51:55 +09:00
syuilo
1d2c50fc26 デフォルトでログのタイムスタンプ非表示 2019-02-07 10:51:50 +09:00
syuilo
cef8aa5e7a APのジョブキュー無効化 2019-02-07 10:51:24 +09:00
syuilo
edf3e75344 New Crowdin translations (#4166)
* New translations ja-JP.yml (Catalan)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Catalan)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (English)
2019-02-07 10:37:36 +09:00
syuilo
62835c6011 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-02-07 10:36:09 +09:00
syuilo
60fb22cb3c Update dependencies 🚀 2019-02-07 10:35:59 +09:00
dependabot[bot]
20dea3a793 Merge pull request #4174 from syuilo/dependabot/npm_and_yarn/@fortawesome/free-brands-svg-icons-5.7.1 2019-02-06 20:35:37 +00:00
dependabot[bot]
aba37ae701 Merge pull request #4173 from syuilo/dependabot/npm_and_yarn/tslint-5.12.1 2019-02-06 20:26:06 +00:00
dependabot[bot]
2c6e6275aa Update @fortawesome/free-brands-svg-icons requirement
Updates the requirements on [@fortawesome/free-brands-svg-icons](https://github.com/FortAwesome/Font-Awesome) to permit the latest version.
- [Release notes](https://github.com/FortAwesome/Font-Awesome/releases)
- [Changelog](https://github.com/FortAwesome/Font-Awesome/blob/master/CHANGELOG.md)
- [Commits](https://github.com/FortAwesome/Font-Awesome/commits/5.7.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-06 20:24:43 +00:00
dependabot[bot]
20ef362854 Update tslint requirement from 5.12.0 to 5.12.1
Updates the requirements on [tslint](https://github.com/palantir/tslint) to permit the latest version.
- [Release notes](https://github.com/palantir/tslint/releases)
- [Changelog](https://github.com/palantir/tslint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/palantir/tslint/commits/5.12.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-06 20:15:49 +00:00
Acid Chicken (硫酸鶏)
4692aa8d9b Update README.md [AUTOGEN] (#4172) 2019-02-07 03:29:10 +09:00
syuilo
f7b6dc08f7 😢 2019-02-07 02:50:03 +09:00
syuilo
7dfe7005e0 Update builtin themes 2019-02-07 02:36:02 +09:00
syuilo
b91de4ac12 🎨 2019-02-07 02:28:08 +09:00
MeiMei
d5205d7328 Refactor reaction-viewer (#4171)
* Refactor reaction-viewer

* code style

* fix
2019-02-07 02:05:49 +09:00
MeiMei
f44ce535fa リアクションマージン再調整 (#4169)
* リアクションマージン再調整

* fix size
2019-02-06 22:57:08 +09:00
syuilo
7177fd27c8 Fix bug 2019-02-06 22:56:20 +09:00
syuilo
cf304f88d4 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-02-06 22:49:42 +09:00
syuilo
dff1d84031 Fix cofig for ci 2019-02-06 22:46:21 +09:00
Aya Morisawa
96bc17aa10 Check config on load (#4170)
Co-authored-by: syuilo <syuilotan@yahoo.co.jp>
2019-02-06 22:44:55 +09:00
syuilo
41ba06a5e6 Fix bug 2019-02-06 22:27:23 +09:00
syuilo
d7ac0418d7 Revert "余計なマージンを削除 (#4168)"
This reverts commit 77bcb58f12.
2019-02-06 21:51:01 +09:00
syuilo
f4319a9c01 Revert "[Client] リアクション一覧のマージンを調整"
This reverts commit 80ea747db6.
2019-02-06 21:50:37 +09:00
syuilo
f4c4d53bbb Fix bug 2019-02-06 21:21:49 +09:00
syuilo
0ed43e1bdf Fix file name 2019-02-06 21:10:37 +09:00
syuilo
d25bd876cb Better file names 2019-02-06 21:10:12 +09:00
syuilo
b9782397c2 Fix file ext 2019-02-06 21:07:36 +09:00
syuilo
ea0abc9f71 Clean up 2019-02-06 20:57:15 +09:00
syuilo
27d16c6a12 Resolve #4151 2019-02-06 20:56:48 +09:00
syuilo
ede70d354e Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-02-06 19:36:52 +09:00
syuilo
66fa583f6e Update example.yml 2019-02-06 19:36:44 +09:00
MeiMei
77bcb58f12 余計なマージンを削除 (#4168) 2019-02-06 18:29:39 +09:00
Aya Morisawa
61036e3a70 Rename clusterLog to clusterLogger (#4167) 2019-02-06 18:01:35 +09:00
syuilo
bcd886c4f5 🎨 2019-02-06 17:51:33 +09:00
syuilo
4d868aaf1f 🎨 2019-02-06 17:10:40 +09:00
syuilo
80ea747db6 [Client] リアクション一覧のマージンを調整
Close #4160
2019-02-06 17:03:43 +09:00
syuilo
960f29ce81 10.82.2 2019-02-06 15:25:47 +09:00
syuilo
20ee57931f Resolve #4165 2019-02-06 15:24:59 +09:00
syuilo
71ba72e796 Better logs 2019-02-06 15:06:23 +09:00
syuilo
9835945ee1 Improve queue option 2019-02-06 15:01:43 +09:00
syuilo
4f2d52697d Update queue setting 2019-02-06 14:53:02 +09:00
141 changed files with 3414 additions and 538 deletions

View File

@@ -6,6 +6,8 @@ mongodb:
db: misskey
user: syuilo
pass: ''
drive:
storage: 'db'
redis:
host: localhost
port: 6379

View File

@@ -6,6 +6,8 @@ mongodb:
db: test-misskey
user: admin
pass: ''
drive:
storage: 'db'
# __REDIS__
redis:
host: localhost

View File

@@ -108,5 +108,8 @@ autoAdmin: true
# port: 9200
# pass: null
# Whether disable HSTS
#disableHsts: true
# Clustering
#clusterLimit: 1

View File

@@ -1,22 +1,30 @@
---
name: Bug Report
about: Create a report to help us improve
title: ''
labels: bug
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 -->

View 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 -->

View 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 -->

View File

@@ -1,11 +1,12 @@
---
name: Feature Request
about: Suggest an idea for this project
title: ''
labels: feature
assignees: ''
---
# Summary
<!-- Tell us what the suggestion is -->
# Environment
<!-- Tell us where on the platform it related -->
<!-- e.g. desktop or mobile version, your browser, your OS -->
<!-- Tell us what the suggestion is -->

View 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 -->

View 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 -->

View File

@@ -1,6 +1,33 @@
ChangeLog
=========
10.84.0
----------
* インスタンス管理の強化
* パフォーマンスの問題の修正
* バグ修正
10.83.0
----------
* 特定のインスタンスをブロックをできるように
* 特定のインスタンスからのフォローを全解除できるように
* インスタンスごとのチャートを追加
10.82.4
----------
* 10.82.3でオブジェクトストレージの設定をしていると起動しなくなるバグを修正
10.82.3
----------
* フォロー/ミュート/ブロックデータをエクスポート可能に
* バグ修正
* デザインの調整
* ジョブキューの動作を修正
10.82.2
----------
* ジョブキューの動作を修正
10.82.1
----------
* クラスタリング環境でのジョブキューの動作を修正

View File

@@ -44,3 +44,31 @@ Stands for _**S**ervice**W**orker_.
#### Denyaize
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 ... テスト
```

View File

@@ -115,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://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/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/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>
@@ -124,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=13039004">nemu</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/user?u=4389829">natalie</a></td>
<td><a href="https://www.patreon.com/hiratake">Hiratake</a></td>
@@ -140,7 +142,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td>
</tr></table>
**Last updated:** Sun, 03 Feb 2019 10:13:06 UTC
**Last updated:** Wed, 06 Feb 2019 18:18:05 UTC
<!-- PATREON_END -->
:four_leaf_clover: Copyright

View File

@@ -510,7 +510,11 @@ common/views/components/profile-editor.vue:
email-verified: "メールアドレスが確認されました"
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
export: "エクスポート"
export-notes: "すべての投稿のエクスポート"
export-targets:
all-notes: "すべての投稿データ"
following-list: "フォロー"
mute-list: "ミュート"
blocking-list: "ブロック"
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
common/views/components/user-list-editor.vue:
users: "ユーザー"
@@ -1003,6 +1007,7 @@ admin/views/index.vue:
announcements: "お知らせ"
hashtags: "ハッシュタグ"
abuse: "スパム報告"
queue: "ジョブキュー"
back-to-misskey: "Misskeyに戻る"
admin/views/dashboard.vue:
dashboard: "ダッシュボード"
@@ -1012,6 +1017,9 @@ admin/views/dashboard.vue:
instances: "インスタンス"
this-instance: "このインスタンス"
federated: "連合"
admin/views/queue.vue:
operation: "操作"
remove-all-jobs: "すべてのジョブをクリア"
admin/views/abuse.vue:
title: "スパム報告"
target: "対象"
@@ -1225,6 +1233,64 @@ admin/views/announcements.vue:
removed: "削除しました"
admin/views/hashtags.vue:
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:
about: "詳しく..."
gotit: "わかった"

View File

@@ -5,7 +5,7 @@ meta:
common:
misskey: "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:
title: "Was ist Misskey?"
about: "Misskeyはオープンソースの<b>分散型マイクロブログSNS</b>です。リッチで高度にカスタマイズできるUI、投稿へのリアクション、ファイルを一元管理できるドライブなど、先進的な機能を揃えています。また、Fediverseと呼ばれるネットワークに接続できるため、他のSNSともやり取りできます。例えば、あなたが何か投稿すると、その投稿はMisskeyだけでなく他のSNSにも伝わります。ちょうどある惑星から他の惑星に電波を発信している様子をイメージしてください。"
@@ -25,9 +25,9 @@ common:
application-authorization: "Autorisierte Anwendungen"
close: "Schließen"
do-not-copy-paste: "ここにコードを入力したり張り付けたりしないでください。アカウントが不正利用される可能性があります。"
load-more: "もっと読み込む"
enter-password: "パスワードを入力してください"
2fa: "二段階認証"
load-more: "Mehr laden"
enter-password: "Bitte Passwort eingeben"
2fa: "Zwei-Faktor-Authentifizierung"
got-it: "Verstanden!"
customization-tips:
title: "Anpassung-Tipps"
@@ -54,8 +54,8 @@ common:
years_ago: "vor {} Jahr(en)"
month-and-day: "{day}/{month}"
trash: "Papierkorb"
drive: "ドライブ"
messaging: "トーク"
drive: "Drive"
messaging: "Unterhaltungen"
weekday-short:
sunday: "So"
monday: "Mo"
@@ -91,9 +91,9 @@ common:
followers-desc: "Nur für diejenigen sichtbar, die dir folgen"
specified: "Direkt"
specified-desc: "Nur für bestimmte Benutzer posten"
local-public: "公開 (ローカルのみ)"
local-home: "ホーム (ローカルのみ)"
local-followers: "フォロワー (ローカルのみ)"
local-public: "Öffentlich (nur lokal)"
local-home: "Home (nur lokal)"
local-followers: "Follower (nur lokal)"
note-placeholders:
a: "Was machst du gerade?"
b: "Was ist so passiert?"
@@ -172,18 +172,18 @@ common:
hashtags: "Hashtags"
dev: "Fehler beim Erstellen der Applikation. Bitte versuche es erneut."
ai-chan-kawaii: "藍ちゃかわいい"
you: "あなた"
you: "Du"
auth/views/form.vue:
share-access: "<i>{name}</i>があなたのアカウントにアクセスすることを許可しますか?"
permission-ask: "このアプリは次の権限を要求しています:"
account-read: "アカウントの情報を見る。"
account-write: "アカウントの情報を操作する。"
share-access: "Erlaubst Du <i>{name}</i> auf deinen Account zuzugreifen?"
permission-ask: "Diese Applikation benötigt folgende Berechtigungen:"
account-read: "Accountinformationen anzeigen."
account-write: "Accountinformationen bearbeiten."
note-write: "Senden."
like-write: "いいねしたりいいね解除する。"
following-write: "フォローしたりフォロー解除する。"
like-write: "Auf Beiträge reagieren."
following-write: "Folgen oder entfolgen."
drive-read: "ドライブを見る。"
drive-write: "ドライブを操作する。"
notification-read: "通知を見る。"
notification-read: "Siehe deine Benachrichtigungen."
notification-write: "Benachrichtigungen verwalten."
cancel: "Abbrechen"
accept: "Zugriff erlauben."
@@ -510,7 +510,11 @@ common/views/components/profile-editor.vue:
email-verified: "メールアドレスが確認されました"
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
export: "エクスポート"
export-notes: "すべての投稿のエクスポート"
export-targets:
all-notes: "すべての投稿データ"
following-list: "フォロー"
mute-list: "ミュート"
blocking-list: "ブロック"
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
common/views/components/user-list-editor.vue:
users: "ユーザー"
@@ -1003,6 +1007,7 @@ admin/views/index.vue:
announcements: "お知らせ"
hashtags: "ハッシュタグ"
abuse: "スパム報告"
queue: "ジョブキュー"
back-to-misskey: "Misskeyに戻る"
admin/views/dashboard.vue:
dashboard: "ダッシュボード"
@@ -1012,6 +1017,9 @@ admin/views/dashboard.vue:
instances: "インスタンス"
this-instance: "このインスタンス"
federated: "連合"
admin/views/queue.vue:
operation: "操作"
remove-all-jobs: "すべてのジョブをクリア"
admin/views/abuse.vue:
title: "スパム報告"
target: "対象"
@@ -1225,6 +1233,64 @@ admin/views/announcements.vue:
removed: "削除しました"
admin/views/hashtags.vue:
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:
about: "詳しく..."
gotit: "わかった"

View File

@@ -510,7 +510,11 @@ common/views/components/profile-editor.vue:
email-verified: "Your email has been verified."
email-not-verified: "Email address is not confirmed. Please check your inbox."
export: "Export"
export-notes: "Export all of your Notes"
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:
users: "User"
@@ -1003,6 +1007,7 @@ admin/views/index.vue:
announcements: "Announcements"
hashtags: "Hashtags"
abuse: "Abuse"
queue: "Job Queue"
back-to-misskey: "Back to Misskey"
admin/views/dashboard.vue:
dashboard: "Dashboard"
@@ -1012,6 +1017,9 @@ admin/views/dashboard.vue:
instances: "Instances"
this-instance: "This instance"
federated: "Federated"
admin/views/queue.vue:
operation: "Action(s)"
remove-all-jobs: "Clear all queued jobs"
admin/views/abuse.vue:
title: "Abuse"
target: "Target"
@@ -1225,6 +1233,64 @@ admin/views/announcements.vue:
removed: "Deleted"
admin/views/hashtags.vue:
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: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。"
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: "最後にやり取りした日時が古い順"
lastCommunicatedAtDesc: "最後にやり取りした日時が新しい順"
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: "ドライブ使用量が少ない順"
driveUsageDesc: "ドライブ使用量が多い順"
driveFilesAsc: "ドライブのファイル数が少ない順"
driveFilesDesc: "ドライブのファイル数が多い順"
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: "フォロー/フォロワーの増減"
ff-total: "フォロー/フォロワーの積算"
drive-usage: "ドライブ使用量の増減"
drive-usage-total: "ドライブ使用量の積算"
drive-files: "ドライブファイル数の増減"
drive-files-total: "ドライブファイル数の積算"
chart-spans:
hour: "Hourly"
day: "Daily"
desktop/views/pages/welcome.vue:
about: "More details..."
gotit: "Got it!"

View File

@@ -510,7 +510,11 @@ common/views/components/profile-editor.vue:
email-verified: "メールアドレスが確認されました"
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
export: "エクスポート"
export-notes: "すべての投稿のエクスポート"
export-targets:
all-notes: "すべての投稿データ"
following-list: "フォロー"
mute-list: "ミュート"
blocking-list: "ブロック"
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
common/views/components/user-list-editor.vue:
users: "Usuarios"
@@ -1003,6 +1007,7 @@ admin/views/index.vue:
announcements: "お知らせ"
hashtags: "Hashtags"
abuse: "スパム報告"
queue: "ジョブキュー"
back-to-misskey: "Volver a Misskey"
admin/views/dashboard.vue:
dashboard: "Panel de Control"
@@ -1012,6 +1017,9 @@ admin/views/dashboard.vue:
instances: "Instancias"
this-instance: "Esta instancia"
federated: "連合"
admin/views/queue.vue:
operation: "操作"
remove-all-jobs: "すべてのジョブをクリア"
admin/views/abuse.vue:
title: "スパム報告"
target: "対象"
@@ -1225,6 +1233,64 @@ admin/views/announcements.vue:
removed: "削除しました"
admin/views/hashtags.vue:
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:
about: "詳しく..."
gotit: "わかった"

View File

@@ -345,8 +345,8 @@ common/views/components/note-menu.vue:
copy-link: "Copier le lien"
favorite: "Mettre cette note en favoris"
unfavorite: "Retirer des favoris"
watch: "ウォッチ"
unwatch: "ウォッチ解除"
watch: "Surveiller"
unwatch: "Ne plus surveiller"
pin: "Épingler sur votre profil"
unpin: "Désépingler"
delete: "Supprimer"
@@ -363,10 +363,10 @@ common/views/components/user-menu.vue:
report-abuse: "Signaler un abus"
report-abuse-detail: "Détail du signalement"
report-abuse-reported: "Transmit à ladministrateur. Merci de votre collaboration."
silence: "サイレンス"
unsilence: "サイレンス解除"
silence: "Mettre en sourdine"
unsilence: "Enlever la sourdine"
suspend: "Suspendre"
unsuspend: "凍結解除"
unsuspend: "Ne plus suspendre"
common/views/components/poll.vue:
vote-to: "Voter pour '{}'"
vote-count: "{} votes"
@@ -509,8 +509,12 @@ common/views/components/profile-editor.vue:
email-address: "Adresse de courrier électronique"
email-verified: "Ladresse du courrier électronique a été vérifiée."
email-not-verified: "Adresse de courriel nest pas confirmée. Veuillez vérifier votre boite de réception."
export: "エクスポート"
export-notes: "すべての投稿のエクスポート"
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:
users: "Utilisateur·rice"
@@ -1003,6 +1007,7 @@ admin/views/index.vue:
announcements: "Annonces"
hashtags: "Hashtags"
abuse: "Abus"
queue: "File dattente"
back-to-misskey: "Retour vers Misskey"
admin/views/dashboard.vue:
dashboard: "Tableau de bord"
@@ -1012,6 +1017,9 @@ admin/views/dashboard.vue:
instances: "Instances"
this-instance: "Cette instance"
federated: "Fédérées"
admin/views/queue.vue:
operation: "Action(s)"
remove-all-jobs: "すべてのジョブをクリア"
admin/views/abuse.vue:
title: "Abus"
target: "Cible"
@@ -1153,8 +1161,8 @@ admin/views/users.vue:
unsuspend: "Suspension levée"
unsuspend-confirm: "Souhaiteriez-vous ne plus suspendre ce compte ?"
unsuspended: "La suspension de lutilisateur a été levée avec succès"
make-silence: "サイレンス"
unmake-silence: "サイレンスの解除"
make-silence: "Mettre en sourdine"
unmake-silence: "Enlever la sourdine"
verify: "Vérification du compte"
verify-confirm: "Souhaiteriez-vous rendre votre compte comme étant un compte vérifié ?"
verified: "Le compte a été vérifié"
@@ -1225,6 +1233,64 @@ admin/views/announcements.vue:
removed: "Supprimé"
admin/views/hashtags.vue:
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 dinscription (Ascendant)"
caughtAtDesc: "Date dinscription (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:
about: "à propos"
gotit: "J'ai compris !"

View File

@@ -510,7 +510,11 @@ common/views/components/profile-editor.vue:
email-verified: "メールアドレスが確認されました"
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
export: "エクスポート"
export-notes: "すべての投稿のエクスポート"
export-targets:
all-notes: "すべての投稿データ"
following-list: "フォロー"
mute-list: "ミュート"
blocking-list: "ブロック"
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
common/views/components/user-list-editor.vue:
users: "ユーザー"
@@ -1003,6 +1007,7 @@ admin/views/index.vue:
announcements: "お知らせ"
hashtags: "ハッシュタグ"
abuse: "スパム報告"
queue: "ジョブキュー"
back-to-misskey: "Misskeyに戻る"
admin/views/dashboard.vue:
dashboard: "ダッシュボード"
@@ -1012,6 +1017,9 @@ admin/views/dashboard.vue:
instances: "インスタンス"
this-instance: "このインスタンス"
federated: "連合"
admin/views/queue.vue:
operation: "操作"
remove-all-jobs: "すべてのジョブをクリア"
admin/views/abuse.vue:
title: "スパム報告"
target: "対象"
@@ -1225,6 +1233,64 @@ admin/views/announcements.vue:
removed: "削除しました"
admin/views/hashtags.vue:
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:
about: "詳しく..."
gotit: "わかった"

View File

@@ -558,7 +558,11 @@ common/views/components/profile-editor.vue:
email-verified: "メールアドレスが確認されました"
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
export: "エクスポート"
export-notes: "すべての投稿のエクスポート"
export-targets:
all-notes: "すべての投稿データ"
following-list: "フォロー"
mute-list: "ミュート"
blocking-list: "ブロック"
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
common/views/components/user-list-editor.vue:
@@ -1129,6 +1133,7 @@ admin/views/index.vue:
announcements: "お知らせ"
hashtags: "ハッシュタグ"
abuse: "スパム報告"
queue: "ジョブキュー"
back-to-misskey: "Misskeyに戻る"
admin/views/dashboard.vue:
@@ -1140,6 +1145,10 @@ admin/views/dashboard.vue:
this-instance: "このインスタンス"
federated: "連合"
admin/views/queue.vue:
operation: "操作"
remove-all-jobs: "すべてのジョブをクリア"
admin/views/abuse.vue:
title: "スパム報告"
target: "対象"
@@ -1362,6 +1371,65 @@ admin/views/announcements.vue:
admin/views/hashtags.vue:
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:
about: "詳しく..."
gotit: "わかった"

View File

@@ -510,7 +510,11 @@ common/views/components/profile-editor.vue:
email-verified: "このメールアドレスOKや"
email-not-verified: "メールアドレスが確認されとらん。メールボックスもっぺん見てくれへん?"
export: "エクスポート"
export-notes: "すべての投稿のエクスポート"
export-targets:
all-notes: "すべての投稿データ"
following-list: "フォロー"
mute-list: "ミュート"
blocking-list: "ブロック"
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
common/views/components/user-list-editor.vue:
users: "ユーザー"
@@ -1003,6 +1007,7 @@ admin/views/index.vue:
announcements: "知っといてや"
hashtags: "ハッシュタグ"
abuse: "スパム報告"
queue: "ジョブキュー"
back-to-misskey: "Misskeyに戻る"
admin/views/dashboard.vue:
dashboard: "ダッシュボード"
@@ -1012,6 +1017,9 @@ admin/views/dashboard.vue:
instances: "インスタンス"
this-instance: "ワイのインスタンス"
federated: "連合"
admin/views/queue.vue:
operation: "操作"
remove-all-jobs: "すべてのジョブをクリア"
admin/views/abuse.vue:
title: "スパム報告"
target: "対象"
@@ -1225,6 +1233,64 @@ admin/views/announcements.vue:
removed: "削除しました"
admin/views/hashtags.vue:
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:
about: "もうちょい……"
gotit: "ほい"

View File

@@ -510,7 +510,11 @@ common/views/components/profile-editor.vue:
email-verified: "매일 주소가 확인되었습니다"
email-not-verified: "메일 주소가 확인되지 않았습니다. 받은 편지함을 확인하여 주시기 바랍니다."
export: "エクスポート"
export-notes: "すべての投稿のエクスポート"
export-targets:
all-notes: "すべての投稿データ"
following-list: "フォロー"
mute-list: "ミュート"
blocking-list: "ブロック"
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
common/views/components/user-list-editor.vue:
users: "사용자"
@@ -1003,6 +1007,7 @@ admin/views/index.vue:
announcements: "공지사항"
hashtags: "해시태그"
abuse: "스팸 신고"
queue: "ジョブキュー"
back-to-misskey: "Misskey로 돌아가기"
admin/views/dashboard.vue:
dashboard: "대시보드"
@@ -1012,6 +1017,9 @@ admin/views/dashboard.vue:
instances: "인스턴스"
this-instance: "이 인스턴스"
federated: "연합"
admin/views/queue.vue:
operation: "操作"
remove-all-jobs: "すべてのジョブをクリア"
admin/views/abuse.vue:
title: "스팸 신고"
target: "대상"
@@ -1225,6 +1233,64 @@ admin/views/announcements.vue:
removed: "삭제하였습니다"
admin/views/hashtags.vue:
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:
about: "자세히..."
gotit: "알겠습니다"

View File

@@ -510,7 +510,11 @@ common/views/components/profile-editor.vue:
email-verified: "メールアドレスが確認されました"
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
export: "エクスポート"
export-notes: "すべての投稿のエクスポート"
export-targets:
all-notes: "すべての投稿データ"
following-list: "フォロー"
mute-list: "ミュート"
blocking-list: "ブロック"
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
common/views/components/user-list-editor.vue:
users: "ユーザー"
@@ -1003,6 +1007,7 @@ admin/views/index.vue:
announcements: "お知らせ"
hashtags: "ハッシュタグ"
abuse: "スパム報告"
queue: "ジョブキュー"
back-to-misskey: "Misskeyに戻る"
admin/views/dashboard.vue:
dashboard: "ダッシュボード"
@@ -1012,6 +1017,9 @@ admin/views/dashboard.vue:
instances: "インスタンス"
this-instance: "このインスタンス"
federated: "連合"
admin/views/queue.vue:
operation: "操作"
remove-all-jobs: "すべてのジョブをクリア"
admin/views/abuse.vue:
title: "スパム報告"
target: "対象"
@@ -1225,6 +1233,64 @@ admin/views/announcements.vue:
removed: "削除しました"
admin/views/hashtags.vue:
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:
about: "詳しく..."
gotit: "わかった"

View File

@@ -510,7 +510,11 @@ common/views/components/profile-editor.vue:
email-verified: "メールアドレスが確認されました"
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
export: "エクスポート"
export-notes: "すべての投稿のエクスポート"
export-targets:
all-notes: "すべての投稿データ"
following-list: "フォロー"
mute-list: "ミュート"
blocking-list: "ブロック"
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
common/views/components/user-list-editor.vue:
users: "ユーザー"
@@ -1003,6 +1007,7 @@ admin/views/index.vue:
announcements: "お知らせ"
hashtags: "ハッシュタグ"
abuse: "スパム報告"
queue: "ジョブキュー"
back-to-misskey: "Misskeyに戻る"
admin/views/dashboard.vue:
dashboard: "ダッシュボード"
@@ -1012,6 +1017,9 @@ admin/views/dashboard.vue:
instances: "インスタンス"
this-instance: "このインスタンス"
federated: "連合"
admin/views/queue.vue:
operation: "操作"
remove-all-jobs: "すべてのジョブをクリア"
admin/views/abuse.vue:
title: "スパム報告"
target: "対象"
@@ -1225,6 +1233,64 @@ admin/views/announcements.vue:
removed: "削除しました"
admin/views/hashtags.vue:
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:
about: "詳しく..."
gotit: "Skjønner!"

View File

@@ -510,7 +510,11 @@ common/views/components/profile-editor.vue:
email-verified: "Twój adres e-mail został zweryfikowany."
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
export: "エクスポート"
export-notes: "すべての投稿のエクスポート"
export-targets:
all-notes: "すべての投稿データ"
following-list: "フォロー"
mute-list: "ミュート"
blocking-list: "ブロック"
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
common/views/components/user-list-editor.vue:
users: "Użytkownicy"
@@ -1003,6 +1007,7 @@ admin/views/index.vue:
announcements: "Ogłoszenia"
hashtags: "Hashtagi"
abuse: "スパム報告"
queue: "ジョブキュー"
back-to-misskey: "Misskeyに戻る"
admin/views/dashboard.vue:
dashboard: "ダッシュボード"
@@ -1012,6 +1017,9 @@ admin/views/dashboard.vue:
instances: "インスタンス"
this-instance: "このインスタンス"
federated: "連合"
admin/views/queue.vue:
operation: "操作"
remove-all-jobs: "すべてのジョブをクリア"
admin/views/abuse.vue:
title: "スパム報告"
target: "対象"
@@ -1225,6 +1233,64 @@ admin/views/announcements.vue:
removed: "Usunięto"
admin/views/hashtags.vue:
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:
about: "O Misskey"
gotit: "Rozumiem!"

View File

@@ -510,7 +510,11 @@ common/views/components/profile-editor.vue:
email-verified: "メールアドレスが確認されました"
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
export: "エクスポート"
export-notes: "すべての投稿のエクスポート"
export-targets:
all-notes: "すべての投稿データ"
following-list: "フォロー"
mute-list: "ミュート"
blocking-list: "ブロック"
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
common/views/components/user-list-editor.vue:
users: "ユーザー"
@@ -1003,6 +1007,7 @@ admin/views/index.vue:
announcements: "お知らせ"
hashtags: "ハッシュタグ"
abuse: "スパム報告"
queue: "ジョブキュー"
back-to-misskey: "Misskeyに戻る"
admin/views/dashboard.vue:
dashboard: "ダッシュボード"
@@ -1012,6 +1017,9 @@ admin/views/dashboard.vue:
instances: "インスタンス"
this-instance: "このインスタンス"
federated: "連合"
admin/views/queue.vue:
operation: "操作"
remove-all-jobs: "すべてのジョブをクリア"
admin/views/abuse.vue:
title: "スパム報告"
target: "対象"
@@ -1225,6 +1233,64 @@ admin/views/announcements.vue:
removed: "削除しました"
admin/views/hashtags.vue:
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:
about: "詳しく..."
gotit: "わかった"

View File

@@ -510,7 +510,11 @@ common/views/components/profile-editor.vue:
email-verified: "メールアドレスが確認されました"
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
export: "エクスポート"
export-notes: "すべての投稿のエクスポート"
export-targets:
all-notes: "すべての投稿データ"
following-list: "フォロー"
mute-list: "ミュート"
blocking-list: "ブロック"
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
common/views/components/user-list-editor.vue:
users: "ユーザー"
@@ -1003,6 +1007,7 @@ admin/views/index.vue:
announcements: "お知らせ"
hashtags: "ハッシュタグ"
abuse: "スパム報告"
queue: "ジョブキュー"
back-to-misskey: "Misskeyに戻る"
admin/views/dashboard.vue:
dashboard: "ダッシュボード"
@@ -1012,6 +1017,9 @@ admin/views/dashboard.vue:
instances: "インスタンス"
this-instance: "このインスタンス"
federated: "連合"
admin/views/queue.vue:
operation: "操作"
remove-all-jobs: "すべてのジョブをクリア"
admin/views/abuse.vue:
title: "スパム報告"
target: "対象"
@@ -1225,6 +1233,64 @@ admin/views/announcements.vue:
removed: "削除しました"
admin/views/hashtags.vue:
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:
about: "詳しく..."
gotit: "わかった"

View File

@@ -16,8 +16,8 @@ common:
reaction-desc: "这是表达情绪的最简单方法。 Misskey允许您向其他帖子添加各种类型的回应。 一旦体验过Misskey的回应功能就再也不会想回到那些只有点赞功能的其他SNS上了。"
ui: "交互界面"
ui-desc: "世界上没有一个UI可以适合每一个人. 所以, Misskey 提供一个可以高度定制的UI交互界面. 您可以通过编辑, 调整布局, 放置可选择的小部件来轻松定制您的专属UI界面。"
drive: "Misskey 云盘"
drive-desc: "想要发布一张您已经上传过的照片吗? 想要组织,命名和为上传的文件创建文件夹吗? Misskey盘是一个最好的解决方案. "
drive: "盘"
drive-desc: "想要发布一张您已经上传过的照片吗?想要管理文件或为上传的文件创建文件夹吗Misskey的网盘是一个最好的解决方案"
outro: "Misskey还有其他更多功能请亲身体验一下吧。因为 Misskey 是一个分布式的 SNS如果您感觉某个功能不适合自己试试其他的吧。祝您玩得开心"
adblock:
detected: "请关闭广告拦截器"
@@ -181,8 +181,8 @@ auth/views/form.vue:
note-write: "投稿。"
like-write: "点赞或取消赞。"
following-write: "关注或取消关注。"
drive-read: "查看您的盘"
drive-write: "上传/删除您云盘中的文件。"
drive-read: "查看您的盘"
drive-write: "管理网盘文件。"
notification-read: "查看通知。"
notification-write: "管理通知。"
cancel: "取消"
@@ -324,7 +324,7 @@ common/views/components/messaging-room.form.vue:
input-message-here: "在此键入信息"
send: "发送"
attach-from-local: "从电脑中添加文件"
attach-from-drive: "从盘中添加文件"
attach-from-drive: "从盘中添加文件"
only-one-file-attached: "在信息中只允许添加一个附件"
common/views/components/messaging-room.message.vue:
is-read: "已读"
@@ -354,10 +354,10 @@ common/views/components/note-menu.vue:
remote: "显示原始投稿"
common/views/components/user-menu.vue:
mention: "提到"
mute: "免打扰"
unmute: "解除免打扰"
block: "屏蔽"
unblock: "取消屏蔽"
mute: "屏蔽"
unmute: "解除屏蔽"
block: "拉黑"
unblock: "取消拉黑"
push-to-list: "添加至列表"
select-list: "请选择一个列表"
report-abuse: "举报骚扰"
@@ -510,7 +510,11 @@ common/views/components/profile-editor.vue:
email-verified: "电子邮件地址已验证"
email-not-verified: "邮件地址尚未验证。 请检查您的邮箱。"
export: "导出"
export-notes: "导出所有帖子"
export-targets:
all-notes: "所有发帖"
following-list: "关注列表"
mute-list: "屏蔽列表"
blocking-list: "黑名单"
export-requested: "导出请求已提交。可能需要花一些时间。导出的文件将保存到网盘中。"
common/views/components/user-list-editor.vue:
users: "用户"
@@ -555,14 +559,14 @@ common/views/widgets/tips.vue:
tips-line2: "从 <kbd>p</kbd> 或者 <kbd>n</kbd>打开投稿表单"
tips-line3: "您可以在投稿表单上拖放文件。"
tips-line4: "您可以将剪贴板中的图像粘贴到提交表单中。"
tips-line5: "您可以通过将文件拖放到盘来上传文件。"
tips-line6: "您可以通过在盘中拖动文件夹来移动文件夹"
tips-line7: "您可以通过在文件夹中拖动文件夹来移动文件夹。"
tips-line5: "您可以通过将文件拖放到盘来上传文件。"
tips-line6: "您可以通过在盘中通过拖动操作来移动文件夹"
tips-line7: "您可以通过在网盘中通过拖动操作来移动文件夹。"
tips-line8: "可以从设置中定制主页。"
tips-line9: "Misskey 根据 AGPLv3 获得许可。"
tips-line10: "使用Time Machine(时光机)小部件可以轻松追溯到过去的时间轴。"
tips-line11: "您可以点击“...”将帖子固定到用户页面"
tips-line13: "附在帖子上的所有文件都会保存到盘中。"
tips-line13: "附在帖子上的所有文件都会保存到盘中。"
tips-line14: "在自定义首页布局时,您可以右键单击窗口小部件以更改其设计。"
tips-line17: "用“**”围绕文本将突出显示它。"
tips-line19: "可以在浏览器外部分离多个窗口。"
@@ -736,7 +740,7 @@ desktop/views/components/post-form.vue:
renote-failed: "转发失败"
posting: "发送中"
attach-media-from-local: "从设备中添加媒体文件"
attach-media-from-drive: "从盘中添加媒体文件"
attach-media-from-drive: "从盘中添加媒体文件"
attach-cancel: "删除附件"
insert-a-kao: "v('ω')v"
create-poll: "创建一个投票"
@@ -776,8 +780,8 @@ desktop/views/components/settings.vue:
notification: "通知"
apps: "应用程序"
tags: "标签"
mute-and-block: "静音/屏蔽"
blocking: "屏蔽中"
mute-and-block: "屏蔽/拉黑"
blocking: "已拉黑"
security: "安全性"
signin: "登录历史"
password: "密码"
@@ -908,13 +912,13 @@ common/views/components/drive-settings.vue:
in-use: "正在使用"
stats: "统计"
common/views/components/mute-and-block.vue:
mute-and-block: "静音/封锁"
mute: "静音"
block: "封锁中"
no-muted-users: "没有静音的用户"
no-blocked-users: "没有封锁的用户"
word-mute: "文字静音"
muted-words: "静音的关键字"
mute-and-block: "屏蔽/拉黑"
mute: "屏蔽"
block: "拉黑中"
no-muted-users: "无屏蔽用户"
no-blocked-users: "无拉黑的用户"
word-mute: "文字屏蔽"
muted-words: "屏蔽关键字"
muted-words-description: "使用空格分隔会产生AND规范并且使用换行符分隔会产生OR规范"
save: "保存"
common/views/components/password-settings.vue:
@@ -1003,15 +1007,19 @@ admin/views/index.vue:
announcements: "公告"
hashtags: "标签"
abuse: "举报垃圾信息"
queue: "作业队列"
back-to-misskey: "返回 Misskey"
admin/views/dashboard.vue:
dashboard: "Dashboard"
accounts: "账户"
notes: "帖子"
drive: "Misskey 云盘"
drive: "盘"
instances: "例子"
this-instance: "此实例"
federated: "联合"
admin/views/queue.vue:
operation: "操作"
remove-all-jobs: "清除所有作业"
admin/views/abuse.vue:
title: "举报垃圾信息"
target: "目标"
@@ -1030,11 +1038,11 @@ admin/views/instance.vue:
maintainer-config: "管理员信息"
maintainer-name: "管理员名称"
maintainer-email: "联系管理员"
drive-config: "盘设置"
drive-config: "盘设置"
cache-remote-files: "远程文件缓存"
cache-remote-files-desc: "如果没有此参数,则所有远程文件都将直接链接到其主机服务器。 这将是保存服务器存储的有效解决方案,但是对于设置禁用直接链接的用户而言,远程文件不可见,因为不会生成缩略图,从而增加流量。 建议启用此参数集。"
local-drive-capacity-mb: "每个用户的盘空间"
remote-drive-capacity-mb: "每个远程用户的盘容量"
local-drive-capacity-mb: "每个用户的盘空间"
remote-drive-capacity-mb: "每个远程用户的盘容量"
mb: "以兆字节(Mbps)为单位"
recaptcha-config: "reCAPTCHA设置"
recaptcha-info: "reCAPTCHA token是必要的. 请从 https://www.google.com/recaptcha/intro/ 获取。\n请注意, 该功能在中国大陆不可用。"
@@ -1098,7 +1106,7 @@ admin/views/charts.vue:
federation: "联合"
notes: "投稿"
users: "用户"
drive: "Misskey 云盘"
drive: "盘"
network: "网络"
charts:
federation-instances: "实例数:增加/减少"
@@ -1111,9 +1119,9 @@ admin/views/charts.vue:
users-total: "用户总数"
active-users: "活跃用户数"
drive: "存储容量:增加/减少"
drive-total: "盘总量"
drive-files: "云盘上的文件数:增加/减少"
drive-files-total: "云盘上文件总数"
drive-total: "盘总使用量"
drive-files: "网盘文件数量变化"
drive-files-total: "网盘文件总数"
network-requests: "请求"
network-time: "响应时间"
network-usage: "网络流量"
@@ -1225,6 +1233,64 @@ admin/views/announcements.vue:
removed: "已删除"
admin/views/hashtags.vue:
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:
about: "更多信息..."
gotit: "没问题! "
@@ -1238,7 +1304,7 @@ desktop/views/pages/welcome.vue:
powered-by-misskey: "Powered by <b>Misskey</b>."
info: "信息"
desktop/views/pages/drive.vue:
title: "Misskey 盘"
title: "Misskey 盘"
desktop/views/pages/home-customize.vue:
title: "自定义首页布局"
desktop/views/pages/note.vue:
@@ -1318,7 +1384,7 @@ mobile/views/components/drive.vue:
folder-count: "文件夹"
count-separator: ""
file-count: "文件"
nothing-in-drive: "云盘上没有任何东西"
nothing-in-drive: "网盘为空"
folder-is-empty: "这文件夹是空的"
prompt: "您想要干什么呢?(请输入数字):<1 → 上传文件 | 2 → 从URL上传文件 | 3 → 创建新文件夹 | 4 → 更改这个文件夹的名称 | 5 → 移动这个文件夹 | 6 → 删除这个文件夹>"
deletion-alert: "抱歉! 删除文件夹功能尚未实现。"
@@ -1613,7 +1679,7 @@ dev/views/new-app.vue:
note-write: "投稿。"
reaction-write: "添加或删除反应。"
following-write: "关注和不关注"
drive-read: "查看盘"
drive-write: "上传/删除云盘里的文件"
drive-read: "查看盘"
drive-write: "管理网盘文件"
notification-read: "阅读您的通知"
notification-write: "管理通知"

View File

@@ -1,8 +1,8 @@
{
"name": "misskey",
"author": "syuilo <i@syuilo.com>",
"version": "10.82.1",
"clientVersion": "2.0.14137",
"version": "10.84.0",
"clientVersion": "2.0.14224",
"codename": "nighthike",
"repository": {
"type": "git",
@@ -28,9 +28,9 @@
},
"dependencies": {
"@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-solid-svg-icons": "5.6.3",
"@fortawesome/free-solid-svg-icons": "5.7.1",
"@fortawesome/vue-fontawesome": "0.1.5",
"@koa/cors": "2.2.3",
"@prezzemolo/rap": "0.1.2",
@@ -99,7 +99,7 @@
"@types/websocket": "0.0.40",
"@types/ws": "6.0.1",
"animejs": "3.0.1",
"apexcharts": "3.2.1",
"apexcharts": "3.2.2",
"autobind-decorator": "2.4.0",
"autosize": "4.0.2",
"autwh": "0.1.0",
@@ -223,7 +223,7 @@
"tmp": "0.0.33",
"ts-loader": "5.3.3",
"ts-node": "7.0.1",
"tslint": "5.12.0",
"tslint": "5.12.1",
"tslint-sonarts": "1.9.0",
"typescript": "3.2.4",
"typescript-eslint-parser": "21.0.2",
@@ -232,7 +232,7 @@
"uuid": "3.3.2",
"v-animate-css": "0.0.3",
"video-thumbnail-generator": "1.1.3",
"vue": "2.6.2",
"vue": "2.6.4",
"vue-color": "2.7.0",
"vue-content-loading": "1.5.3",
"vue-cropperjs": "3.0.0",
@@ -245,7 +245,7 @@
"vue-sequential-entrance": "1.1.3",
"vue-style-loader": "4.1.2",
"vue-svg-inline-loader": "1.2.10",
"vue-template-compiler": "2.6.2",
"vue-template-compiler": "2.6.4",
"vuedraggable": "2.17.0",
"vuewordcloud": "18.7.11",
"vuex": "3.1.0",

View File

@@ -5,14 +5,17 @@ program
.version(pkg.version)
.option('--no-daemons', 'Disable daemon processes (for debbuging)')
.option('--disable-clustering', 'Disable clustering')
.option('--disable-ap-queue', 'Disable creating job queue related to ap')
.option('--disable-queue', 'Disable job queue processing')
.option('--only-queue', 'Pocessing job queue only')
.option('--quiet', 'Suppress all logs')
.option('--verbose', 'Enable all logs')
.option('--with-log-time', 'Include timestamp for each logs')
.option('--slow', 'Delay all requests (for debbuging)')
.option('--color', 'This option is a dummy for some external program\'s (e.g. forever) issue.')
.parse(process.argv);
/*if (process.env.MK_DISABLE_AP_QUEUE)*/ program.disableApQueue = true;
if (process.env.MK_DISABLE_QUEUE) program.disableQueue = true;
if (process.env.MK_ONLY_QUEUE) program.onlyQueue = true;

View File

@@ -124,7 +124,7 @@ export default Vue.extend({
this.meta = meta;
});
this.$root.api('instances', {
this.$root.api('federation/instances', {
sort: '+notes'
}).then(instances => {
for (const i of instances) {

View 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: '+caughtAt',
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>

View File

@@ -20,10 +20,11 @@
<ul>
<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('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('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('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('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>
@@ -40,12 +41,14 @@
<div class="page">
<div v-if="page == 'dashboard'"><x-dashboard/></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 == 'users'"><x-users/></div>
<div v-if="page == 'emoji'"><x-emoji/></div>
<div v-if="page == 'announcements'"><x-announcements/></div>
<div v-if="page == 'hashtags'"><x-hashtags/></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>
</main>
@@ -58,6 +61,7 @@ import i18n from '../../i18n';
import { version } from '../../config';
import XDashboard from "./dashboard.vue";
import XInstance from "./instance.vue";
import XQueue from "./queue.vue";
import XModerators from "./moderators.vue";
import XEmoji from "./emoji.vue";
import XAnnouncements from "./announcements.vue";
@@ -65,7 +69,9 @@ import XHashtags from "./hashtags.vue";
import XUsers from "./users.vue";
import XDrive from "./drive.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';
// Detect the user agent
@@ -77,6 +83,7 @@ export default Vue.extend({
components: {
XDashboard,
XInstance,
XQueue,
XModerators,
XEmoji,
XAnnouncements,
@@ -84,6 +91,7 @@ export default Vue.extend({
XUsers,
XDrive,
XAbuse,
XFederation,
},
provide: {
isMobile
@@ -97,8 +105,9 @@ export default Vue.extend({
faGrin,
faArrowLeft,
faHeadset,
faShareAlt,
faExclamationCircle
faGlobe,
faExclamationCircle,
faTasks
};
},
methods: {

View File

@@ -6,8 +6,10 @@
<ui-input v-model="username" type="text">
<span slot="prefix">@</span>
</ui-input>
<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-horizon-group>
<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-horizon-group>
</section>
</ui-card>
</div>

View 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>

View File

@@ -420,7 +420,7 @@ export default Vue.extend({
margin 0
padding 16px
text-align center
color #aaa
color var(--text)
> [data-icon]
margin-right 4px

View File

@@ -92,7 +92,13 @@
<header>{{ $t('export') }}</header>
<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>
</section>
</ui-card>
@@ -105,6 +111,7 @@ import { apiUrl, host } from '../../../config';
import { toUnicode } from 'punycode';
import langmap from 'langmap';
import { unique } from '../../../../../prelude/array';
import { faDownload } from '@fortawesome/free-solid-svg-icons';
export default Vue.extend({
i18n: i18n('common/views/components/profile-editor.vue'),
@@ -131,7 +138,9 @@ export default Vue.extend({
autoAcceptFollowed: false,
saving: false,
avatarUploading: false,
bannerUploading: false
bannerUploading: false,
exportTarget: 'notes',
faDownload
};
},
@@ -262,8 +271,13 @@ export default Vue.extend({
});
},
exportNotes() {
this.$root.api('i/export-notes', {});
doExport() {
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({
type: 'info',

View File

@@ -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>

View File

@@ -1,139 +1,37 @@
<template>
<div class="mk-reactions-viewer" :class="{ isMe }">
<template v-if="reactions">
<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>
<x-reaction v-for="(count, reaction) in reactions" :reaction="reaction" :count="count" :note="note" :key="reaction"/>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import Icon from './reaction-icon.vue';
import anime from 'animejs';
import XReaction from './reactions-viewer.reaction.vue';
export default Vue.extend({
components: {
XReaction
},
props: {
note: {
type: Object,
required: true
}
},
},
computed: {
reactions(): any {
return this.note.reactionCounts;
},
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>
<style lang="stylus" scoped>
.mk-reactions-viewer
margin 6px 0
margin: 4px -2px
&:empty
display none
@@ -144,38 +42,4 @@ export default Vue.extend({
&:hover
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>

View File

@@ -74,7 +74,7 @@ export default Vue.extend({
margin 0
padding 16px
text-align center
color #aaa
color var(--text)
> [data-icon]
margin-right 4px

View File

@@ -117,7 +117,7 @@ export default define({
margin 0
padding 16px
text-align center
color #aaa
color var(--text)
> [data-icon]
margin-right 4px

View File

@@ -86,7 +86,7 @@ export default define({
margin 0
padding 16px
text-align center
color #aaa
color var(--text)
> [data-icon]
margin-right 4px

View File

@@ -88,7 +88,7 @@ export default define({
margin 0
padding 16px
text-align center
color #aaa
color var(--text)
> [data-icon]
margin-right 4px

View File

@@ -12,7 +12,7 @@ export const hostname = address.hostname;
export const url = address.origin;
export const apiUrl = url + '/api';
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 locale = JSON.parse(localStorage.getItem('locale'));
export const copyright = _COPYRIGHT_;

View File

@@ -78,7 +78,7 @@ export default Vue.extend({
margin 0
padding 16px
text-align center
color #aaa
color var(--text)
> [data-icon]
margin-right 4px

View File

@@ -120,13 +120,13 @@ export default Vue.extend({
margin 0
padding 16px
text-align center
color #aaa
color var(--text)
> .fetching
margin 0
padding 16px
text-align center
color #aaa
color var(--text)
> [data-icon]
margin-right 4px

View File

@@ -598,6 +598,7 @@ export default Vue.extend({
padding 16px 0 0 0
overflow auto
z-index 1
font-size 15px
&.inWindow
box-shadow var(--shadowRight)

View File

@@ -218,6 +218,6 @@ export default Vue.extend({
margin 0
padding 16px
text-align center
color #aaa
color var(--text)
</style>

View File

@@ -74,7 +74,7 @@ export default Vue.extend({
margin 0
padding 16px
text-align center
color #aaa
color var(--text)
> i
margin-right 4px

View File

@@ -66,7 +66,7 @@ export default Vue.extend({
margin 0
padding 16px
text-align center
color #aaa
color var(--text)
> i
margin-right 4px

View File

@@ -98,7 +98,7 @@ export default Vue.extend({
margin 0
padding 16px
text-align center
color #aaa
color var(--text)
> i
margin-right 4px

View File

@@ -132,7 +132,7 @@ export default Vue.extend({
padding 0 12px
text-align center
font-size 0.8em
color #aaa
color var(--text)
> .instance
box-shadow var(--shadow)

View File

@@ -5,7 +5,7 @@
<button slot="func" :title="$t('title')" @click="fetch">
<fa v-if="!fetching && more" icon="arrow-right"/>
<fa v-if="!fetching && !more" icon="sync"/>
</button>
</button>
<div class="mkw-polls--body">
<div class="poll" v-if="!fetching && poll != null">
@@ -92,13 +92,13 @@ export default define({
margin 0
padding 16px
text-align center
color #aaa
color var(--text)
> .fetching
margin 0
padding 16px
text-align center
color #aaa
color var(--text)
> [data-icon]
margin-right 4px

View File

@@ -89,13 +89,13 @@ export default define({
margin 0
padding 16px
text-align center
color #aaa
color var(--text)
> .fetching
margin 0
padding 16px
text-align center
color #aaa
color var(--text)
> [data-icon]
margin-right 4px

View File

@@ -129,13 +129,13 @@ export default define({
margin 0
padding 16px
text-align center
color #aaa
color var(--text)
> .fetching
margin 0
padding 16px
text-align center
color #aaa
color var(--text)
> [data-icon]
margin-right 4px

View File

@@ -83,13 +83,13 @@ export default Vue.extend({
margin 0
padding 16px
text-align center
color #aaa
color var(--text)
> .fetching
margin 0
padding 16px
text-align center
color #aaa
color var(--text)
> [data-icon]
margin-right 4px

View File

@@ -88,6 +88,6 @@ export default Vue.extend({
> .mk-time
display inline-block
padding 8px
color #aaa
color var(--text)
</style>

View File

@@ -184,7 +184,7 @@ export default Vue.extend({
margin 0
padding 16px
text-align center
color #aaa
color var(--text)
> .placeholder
padding 16px

View File

@@ -171,6 +171,7 @@ export default Vue.extend({
overflow auto
-webkit-overflow-scrolling touch
background var(--secondary)
font-size 15px
.me
display block

View File

@@ -121,13 +121,13 @@ export default Vue.extend({
margin 0
padding 16px
text-align center
color #aaa
color var(--text)
> .fetching
margin 0
padding 16px
text-align center
color #aaa
color var(--text)
> [data-icon]
margin-right 4px

View File

@@ -57,7 +57,7 @@ export default Vue.extend({
margin 0
padding 16px
text-align center
color #aaa
color var(--text)
> i
margin-right 4px

View File

@@ -48,7 +48,7 @@ export default Vue.extend({
margin 0
padding 16px
text-align center
color #aaa
color var(--text)
> i
margin-right 4px

View File

@@ -52,7 +52,7 @@ export default Vue.extend({
margin 0
padding 16px
text-align center
color #aaa
color var(--text)
> i
margin-right 4px

View File

@@ -89,7 +89,7 @@ export default Vue.extend({
margin 0
padding 16px
text-align center
color #aaa
color var(--text)
> i
margin-right 4px

View File

@@ -23,6 +23,7 @@ export const colorfulTheme: Theme = require('../theme/colorful.json5');
export const rainyTheme: Theme = require('../theme/rainy.json5');
export const mauveTheme: Theme = require('../theme/mauve.json5');
export const grayTheme: Theme = require('../theme/gray.json5');
export const tweetDeckTheme: Theme = require('../theme/tweet-deck.json5');
export const builtinThemes = [
lightTheme,
@@ -38,6 +39,7 @@ export const builtinThemes = [
rainyTheme,
mauveTheme,
grayTheme,
tweetDeckTheme,
];
export function applyTheme(theme: Theme, persisted = true) {

View File

@@ -2,7 +2,7 @@
id: '2b0a0654-cdb4-4c9a-8244-736b647d3c2a',
name: 'Japanese Sushi Set',
author: 'noizenecio & syuilo',
author: 'Noizenecio',
base: 'dark',

View File

@@ -2,7 +2,7 @@
id: '252b2caf-86c2-4c3f-a73f-e1fc1cfa5298',
name: 'Mauve',
author: 'とわこ & syuilo',
author: 'とわこ',
base: 'dark',

View File

@@ -2,7 +2,7 @@
id: 'e9c8c01d-9c15-48d0-9b5c-3d00843b5b36',
name: 'Lavender',
author: 'sokuyuku & syuilo',
author: 'sokuyuku',
base: 'light',

View File

@@ -3,6 +3,7 @@
name: 'Rainy',
author: 'syuilo',
desc: 'It\'s a rainy day.',
base: 'light',

View 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',
},
}

View File

@@ -26,6 +26,7 @@ export default function load() {
const mixin = {} as Mixin;
const url = validateUrl(config.url);
config.url = normalizeUrl(config.url);
mixin.host = url.host;

View File

@@ -26,7 +26,7 @@ import { showMachineInfo } from './misc/show-machine-info';
const logger = new Logger('core', 'cyan');
const bootLogger = logger.createSubLogger('boot', 'magenta');
const clusterLog = logger.createSubLogger('cluster', 'orange');
const clusterLogger = logger.createSubLogger('cluster', 'orange');
const ev = new Xev();
/**
@@ -249,19 +249,19 @@ function spawnWorker(): Promise<void> {
// Listen new workers
cluster.on('fork', worker => {
clusterLog.debug(`Process forked: [${worker.id}]`);
clusterLogger.debug(`Process forked: [${worker.id}]`);
});
// Listen online workers
cluster.on('online', worker => {
clusterLog.debug(`Process is now online: [${worker.id}]`);
clusterLogger.debug(`Process is now online: [${worker.id}]`);
});
// Listen for dying workers
cluster.on('exit', worker => {
// Replace the dead worker,
// we're not sentimental
clusterLog.error(chalk.red(`[${worker.id}] died :(`));
clusterLogger.error(chalk.red(`[${worker.id}] died :(`));
cluster.fork();
});

View File

@@ -21,6 +21,7 @@ export default class Logger {
private log(level: string, message: string, important = false, subDomains: string[] = []): void {
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 domains = [domain].concat(subDomains);
if (this.parentLogger) {
@@ -28,7 +29,8 @@ export default class Logger {
} else {
const time = dateformat(new Date(), 'HH:MM:ss');
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);
}
}

View File

@@ -11,6 +11,7 @@ DriveFile.createIndex('md5');
DriveFile.createIndex('metadata.uri');
DriveFile.createIndex('metadata.userId');
DriveFile.createIndex('metadata.folderId');
DriveFile.createIndex('metadata._user.host');
export default DriveFile;
export const DriveFileChunk = monkDb.get('driveFiles.chunks');

View File

@@ -32,4 +32,59 @@ export interface IInstance {
* このインスタンスから受け取った投稿数
*/
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;
}

View File

@@ -17,6 +17,7 @@ const User = db.get<IUser>('users');
User.createIndex('username');
User.createIndex('usernameLower');
User.createIndex('host');
User.createIndex(['username', 'host'], { unique: true });
User.createIndex(['usernameLower', 'host'], { unique: true });
User.createIndex('token', { sparse: true, unique: true });

View File

@@ -1,6 +1,7 @@
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 { program } from '../argv';
import handler from './processors';
@@ -31,26 +32,41 @@ function initializeQueue() {
}
}
export function createHttpJob(data: any) {
if (queueAvailable) {
export function deliver(user: ILocalUser, content: any, to: any) {
if (content == null) return;
const data = {
type: 'deliver',
user,
content,
to
};
if (queueAvailable && !program.disableApQueue) {
return queue.createJob(data)
.retries(4)
.backoff('exponential', 16384) // 16s
.retries(8)
.backoff('exponential', 1000)
.save();
} else {
return handler({ data }, () => {});
}
}
export function deliver(user: ILocalUser, content: any, to: any) {
if (content == null) return;
export function processInbox(activity: any, signature: httpSignature.IParsedSignature) {
const data = {
type: 'processInbox',
activity: activity,
signature
};
createHttpJob({
type: 'deliver',
user,
content,
to
});
if (queueAvailable && !program.disableApQueue) {
return queue.createJob(data)
.retries(3)
.backoff('exponential', 500)
.save();
} else {
return handler({ data }, () => {});
}
}
export function createExportNotesJob(user: ILocalUser) {
@@ -63,6 +79,36 @@ export function createExportNotesJob(user: ILocalUser) {
.save();
}
export function createExportFollowingJob(user: ILocalUser) {
if (!queueAvailable) throw 'queue unavailable';
return queue.createJob({
type: 'exportFollowing',
user: user
})
.save();
}
export function createExportMuteJob(user: ILocalUser) {
if (!queueAvailable) throw 'queue unavailable';
return queue.createJob({
type: 'exportMute',
user: user
})
.save();
}
export function createExportBlockingJob(user: ILocalUser) {
if (!queueAvailable) throw 'queue unavailable';
return queue.createJob({
type: 'exportBlocking',
user: user
})
.save();
}
export default function() {
if (queueAvailable && enableQueue) {
queue.process(128, handler);
@@ -71,3 +117,9 @@ export default function() {
return queue;
}
export function destroy() {
queue.destroy().then(n => {
queueLogger.succ(`All job removed (${n} jobs)`);
});
}

View 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;
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,
});
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();
}

View 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;
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,
});
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();
}

View 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;
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,
});
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();
}

View File

@@ -100,7 +100,7 @@ export async function exportNotes(job: bq.Job, done: any): Promise<void> {
stream.end();
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);
logger.succ(`Exported to: ${driveFile._id}`);

View File

@@ -2,19 +2,53 @@ import * as bq from 'bee-queue';
import request from '../../../remote/activitypub/request';
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> => {
const { host } = new URL(job.data.to);
try {
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();
} 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')) {
queueLogger.warn(`deliver failed: ${res.statusCode} ${res.statusMessage} to=${job.data.to}`);
if (res.statusCode >= 400 && res.statusCode < 500) {
// HTTPステータスコード4xxはクライアントエラーであり、それはつまり
// 何回再送しても成功することはないということなのでエラーにはしないでおく
done();
} else {
queueLogger.warn(`deliver failed: ${res.statusCode} ${res.statusMessage} to=${job.data.to}`);
done(res.statusMessage);
}
} else {

View File

@@ -8,6 +8,9 @@ import { toUnicode } from 'punycode';
import { URL } from 'url';
import { publishApLogStream } from '../../../services/stream';
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');
@@ -43,6 +46,15 @@ export default async (job: bq.Job, done: any): Promise<void> => {
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;
} else {
// アクティビティ内のホストの検証
@@ -55,6 +67,15 @@ export default async (job: bq.Job, done: any): Promise<void> => {
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({
host: { $ne: null },
'publicKey.id': signature.keyId
@@ -101,6 +122,19 @@ export default async (job: bq.Job, done: any): Promise<void> => {
});
//#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 {
await perform(user, activity);

View File

@@ -1,12 +1,18 @@
import deliver from './http/deliver';
import processInbox from './http/process-inbox';
import { exportNotes } from './export-notes';
import { exportFollowing } from './export-following';
import { exportMute } from './export-mute';
import { exportBlocking } from './export-blocking';
import { queueLogger } from '../logger';
const handlers: any = {
deliver,
processInbox,
exportNotes,
exportFollowing,
exportMute,
exportBlocking,
};
export default (job: any, done: any) => {

View File

@@ -1,4 +1,5 @@
import * as mongo from 'mongodb';
import * as promiseLimit from 'promise-limit';
import config from '../../../config';
import Resolver from '../resolver';
@@ -16,6 +17,7 @@ import { unique, concat, difference } from '../../../prelude/array';
import { extractPollFromQuestion } from './question';
import vote from '../../../services/note/polls/vote';
import { apLogger } from '../logger';
import { IDriveFile } from '../../../models/drive-file';
const logger = apLogger;
@@ -92,9 +94,10 @@ export async function createNote(value: any, resolver?: Resolver, silent = false
// TODO: attachmentは必ずしもImageではない
// TODO: attachmentは必ずしも配列ではない
// Noteがsensitiveなら添付もsensitiveにする
const limit = promiseLimit(2);
const files = note.attachment
.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 uris = difference(unique(concat([to || [], cc || []])), ignoreUris);
const limit = promiseLimit(2);
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);

View File

@@ -1,4 +1,5 @@
import * as mongo from 'mongodb';
import * as promiseLimit from 'promise-limit';
import { toUnicode } from 'punycode';
import config from '../../../config';
@@ -9,10 +10,11 @@ import { isCollectionOrOrderedCollection, isCollection, IPerson } from '../type'
import { IDriveFile } from '../../../models/drive-file';
import Meta from '../../../models/meta';
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 { 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 getDriveFileUrl from '../../../misc/get-drive-file-url';
import { IEmoji } from '../../../models/emoji';
@@ -20,7 +22,7 @@ import { ITag, extractHashtags } from './tag';
import Following from '../../../models/following';
import { IIdentifier } from './identifier';
import { apLogger } from '../logger';
import { INote } from '../../../models/note';
const logger = apLogger;
/**
@@ -188,15 +190,14 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<IU
}
// Register host
registerInstance(host).then(i => {
registerOrFetchInstanceDoc(host).then(i => {
Instance.update({ _id: i._id }, {
$inc: {
usersCount: 1
}
});
// TODO
//perInstanceChart.newUser();
instanceChart.newUser(i.host);
});
//#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`);
// Resolve and regist Notes
const limit = promiseLimit(2);
const featuredNotes = await Promise.all(items
.filter(item => item.type === 'Note')
.slice(0, 5)
.map(item => resolveNote(item, resolver)));
.map(item => limit(() => resolveNote(item, resolver)) as Promise<INote>));
await User.update({ _id: user._id }, {
$set: {

View File

@@ -4,11 +4,13 @@ import { URL } from 'url';
import * as crypto from 'crypto';
import { lookup, IRunOptions } from 'lookup-dns-cache';
import * as promiseAny from 'promise-any';
import { toUnicode } from 'punycode';
import config from '../../config';
import { ILocalUser } from '../../models/user';
import { publishApLogStream } from '../../services/stream';
import { apLogger } from './logger';
import Instance from '../../models/instance';
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);
// ブロックしてたら中断
// TODO: いちいちデータベースにアクセスするのはコスト高そうなのでどっかにキャッシュしておく
const instance = await Instance.findOne({ host: toUnicode(host) });
if (instance && instance.isBlocked) return;
const data = JSON.stringify(object);
const sha256 = crypto.createHash('sha256');
@@ -43,11 +50,11 @@ export default (user: ILocalUser, url: string, object: any) => new Promise(async
'Digest': `SHA-256=${hash}`
}
}, res => {
logger.info(`${url} --> ${res.statusCode}`);
if (res.statusCode >= 400) {
logger.warn(`${url} --> ${res.statusCode}`);
reject(res);
} else {
logger.succ(`${url} --> ${res.statusCode}`);
resolve();
}
});

View File

@@ -3,7 +3,6 @@ import * as Router from 'koa-router';
import * as json from 'koa-json-body';
import * as httpSignature from 'http-signature';
import { createHttpJob } from '../queue';
import { renderActivity } from '../remote/activitypub/renderer';
import Note from '../models/note';
import User, { isLocalUser, ILocalUser, IUser } from '../models/user';
@@ -17,6 +16,7 @@ import Followers from './activitypub/followers';
import Following from './activitypub/following';
import Featured from './activitypub/featured';
import renderQuestion from '../remote/activitypub/renderer/question';
import { processInbox } from '../queue';
// Init router
const router = new Router();
@@ -35,11 +35,7 @@ function inbox(ctx: Router.IRouterContext) {
return;
}
createHttpJob({
type: 'processInbox',
activity: ctx.request.body,
signature
});
processInbox(ctx.request.body, signature);
ctx.status = 202;
}

View File

@@ -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();
}));

View 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();
}));

View File

@@ -0,0 +1,15 @@
import define from '../../../define';
import { destroy } from '../../../../../queue';
export const meta = {
requireCredential: true,
requireModerator: true,
params: {}
};
export default define(meta, (ps) => new Promise(async (res, rej) => {
destroy();
res();
}));

View File

@@ -1,6 +1,6 @@
import $ from 'cafy';
import define from '../../define';
import activeUsersChart from '../../../../chart/active-users';
import activeUsersChart from '../../../../services/chart/active-users';
export const meta = {
stability: 'stable',

View File

@@ -1,6 +1,6 @@
import $ from 'cafy';
import define from '../../define';
import driveChart from '../../../../chart/drive';
import driveChart from '../../../../services/chart/drive';
export const meta = {
stability: 'stable',

View File

@@ -1,6 +1,6 @@
import $ from 'cafy';
import define from '../../define';
import federationChart from '../../../../chart/federation';
import federationChart from '../../../../services/chart/federation';
export const meta = {
stability: 'stable',

View File

@@ -1,6 +1,6 @@
import $ from 'cafy';
import define from '../../define';
import hashtagChart from '../../../../chart/hashtag';
import hashtagChart from '../../../../services/chart/hashtag';
export const meta = {
stability: 'stable',

View File

@@ -0,0 +1,42 @@
import $ from 'cafy';
import define from '../../define';
import instanceChart from '../../../../services/chart/instance';
export const meta = {
stability: 'stable',
desc: {
'ja-JP': 'インスタンスごとのチャートを取得します。'
},
params: {
span: {
validator: $.str.or(['day', 'hour']),
desc: {
'ja-JP': '集計のスパン (day または hour)'
}
},
limit: {
validator: $.num.optional.range(1, 500),
default: 30,
desc: {
'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。'
}
},
host: {
validator: $.str,
desc: {
'ja-JP': '対象のインスタンスのホスト',
'en-US': 'Target instance host'
}
}
}
};
export default define(meta, (ps) => new Promise(async (res, rej) => {
const stats = await instanceChart.getChart(ps.span as any, ps.limit, ps.host);
res(stats);
}));

View File

@@ -1,6 +1,6 @@
import $ from 'cafy';
import define from '../../define';
import networkChart from '../../../../chart/network';
import networkChart from '../../../../services/chart/network';
export const meta = {
stability: 'stable',

View File

@@ -1,6 +1,6 @@
import $ from 'cafy';
import define from '../../define';
import notesChart from '../../../../chart/notes';
import notesChart from '../../../../services/chart/notes';
export const meta = {
stability: 'stable',

View File

@@ -1,6 +1,6 @@
import $ from 'cafy';
import define from '../../../define';
import perUserDriveChart from '../../../../../chart/per-user-drive';
import perUserDriveChart from '../../../../../services/chart/per-user-drive';
import ID, { transform } from '../../../../../misc/cafy-id';
export const meta = {

View File

@@ -1,6 +1,6 @@
import $ from 'cafy';
import define from '../../../define';
import perUserFollowingChart from '../../../../../chart/per-user-following';
import perUserFollowingChart from '../../../../../services/chart/per-user-following';
import ID, { transform } from '../../../../../misc/cafy-id';
export const meta = {

View File

@@ -1,6 +1,6 @@
import $ from 'cafy';
import define from '../../../define';
import perUserNotesChart from '../../../../../chart/per-user-notes';
import perUserNotesChart from '../../../../../services/chart/per-user-notes';
import ID, { transform } from '../../../../../misc/cafy-id';
export const meta = {

Some files were not shown because too many files have changed in this diff Show More