Compare commits

...

75 Commits

Author SHA1 Message Date
532ef744f4 Merge branch 'develop' 2019-04-17 02:58:09 +09:00
27a961814b 11.1.4 2019-04-17 02:57:48 +09:00
5a5b65e9bf Clean up 2019-04-17 02:57:36 +09:00
96673ad610 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-04-17 02:51:19 +09:00
a9025aea0d Better response 2019-04-17 02:51:12 +09:00
f38ab0b973 Update delete-file.ts 2019-04-17 02:37:37 +09:00
150dac00cf Update stream.ja-JP.md (#4725) 2019-04-17 02:21:22 +09:00
bcb1a9c5d3 Refactor 2019-04-17 02:19:49 +09:00
2e55aea584 Doc: Update setup documents (#4680)
* Doc: Update setup documents

Use GitHub api to checkout latest release instead of "git tag" command
which cannot accurately determine prerelease tag.
Also, Changed numbered list format because
the shell command is too long to fit on one line.

* Doc: Use tab instead of spaces

* Doc: Use master branch as latest release tag

master branch and latest release is the same.
master branch merges develop branch with each latest release.
2019-04-17 02:15:27 +09:00
4f5a3f0df5 smallint 2019-04-17 02:12:15 +09:00
e265b538cc Fix #4724 2019-04-17 02:11:22 +09:00
b186504718 Metaのアクセスでトランザクションを張るように (#4720)
* admin/instanceでmetaをキャッシュしないように

* Metaのアクセスにトランザクションをかける
2019-04-17 00:45:33 +09:00
fc27890f13 Refactor 2019-04-17 00:34:49 +09:00
a583939767 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-04-17 00:33:10 +09:00
52e3bcfd29 Meta を二重にデータベースに保存できないように 2019-04-17 00:33:02 +09:00
85d29a3f9d Correct syntax error (#4718) 2019-04-17 00:30:34 +09:00
18944d389d Fix space indent in misskey.nginx (#4717)
Tabインデントになっていたものをスペースインデントに変更
2019-04-16 19:41:58 +09:00
e90ac5d6a4 チャートの数値カラムの型を拡張 2019-04-16 17:52:09 +09:00
cb9a6ae774 Update README.md [AUTOGEN] (#4697) 2019-04-16 13:07:00 +09:00
3ef09aa6b2 Docker: Uncomment redis (#4707)
Redis is required since Misskey 11.0.0.
2019-04-16 13:06:36 +09:00
4db22e45a2 fix syuilo#4711 (#4715) 2019-04-16 13:05:47 +09:00
f966d0b32c 色々な修正; Fix #4709 (#4714)
* Fix a la carte 1

* use dialog instead of alert() etc
2019-04-16 13:05:10 +09:00
ba3879a95a Fix: admin/driveのアイコンがずれてる (#4710) 2019-04-16 02:46:47 +09:00
c6cef0162d Merge branch 'develop' 2019-04-16 01:24:00 +09:00
6d09aa86e9 11.1.3 2019-04-16 01:23:26 +09:00
b711f0f9c6 Clean up 2019-04-16 01:22:48 +09:00
8fefb3a4c9 Update authenticate.ts 2019-04-16 01:20:28 +09:00
400cdf0f26 Fix #4705 2019-04-16 01:11:20 +09:00
bce8c5a315 Refactor 2019-04-16 01:05:21 +09:00
f44dc2dd05 Doc: Add "Init DB" section (#4695)
Misskey 11.x requires database initialization.
2019-04-15 23:39:09 +09:00
df950d2fc5 Clean up 2019-04-15 23:37:35 +09:00
5e1f804dd1 Merge branch 'develop' 2019-04-15 23:32:45 +09:00
15de89f2f9 11.1.2 2019-04-15 23:32:26 +09:00
df647a415c Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-04-15 23:29:44 +09:00
fc66231f8e Fix #4701 2019-04-15 23:29:26 +09:00
71df3e1566 APIドキュメントの説明とか一部追記 (#4702)
* Define description in generate.ts

* Add description in create.ts
2019-04-15 23:26:20 +09:00
168c22fc98 Fix bug 2019-04-15 23:00:39 +09:00
792ec23d7a Use pureimage instead of canvas 2019-04-15 22:58:04 +09:00
ff625253ce Merge branch 'develop' 2019-04-15 22:09:10 +09:00
8c872c6b22 11.1.1 2019-04-15 22:08:53 +09:00
541f5bc0a6 Fix bug 2019-04-15 21:59:14 +09:00
77ff7b9df0 デフォルトでHTLを表示するように 2019-04-15 21:30:49 +09:00
203cc5075e Fix application-name 2019-04-15 21:17:52 +09:00
7abfcd06da Merge branch 'develop' 2019-04-15 21:02:04 +09:00
724f81c7f3 11.1.0 2019-04-15 21:01:46 +09:00
4d2e98af7b 管理者やモデレーターはレートリミット無効に 2019-04-15 20:52:08 +09:00
08221fdda7 Fix bug 2019-04-15 20:41:56 +09:00
c0f72970b9 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-04-15 20:37:29 +09:00
18bc4a49e8 ランダムにアバターを生成するように 2019-04-15 20:37:21 +09:00
f90b6dbed4 Follow lint 2019-04-15 19:05:35 +09:00
d2d991ff34 Fix error 2019-04-15 18:08:57 +09:00
190a5e175b Fix bug 2019-04-15 16:37:54 +09:00
a05f5a91b8 Merge branch 'develop' 2019-04-15 13:14:58 +09:00
4d02ff27be 11.0.3 2019-04-15 13:14:36 +09:00
df92b41d25 Fix bug 2019-04-15 13:11:17 +09:00
075af96338 Fix error 2019-04-15 12:54:42 +09:00
64bbc55336 Fix bug 2019-04-15 12:52:02 +09:00
06c621acc1 Fix bug 2019-04-15 12:49:38 +09:00
d040dc19bc Merge branch 'develop' 2019-04-15 12:23:20 +09:00
a38ae4a402 11.0.2 2019-04-15 12:23:03 +09:00
772063aade Refactor 2019-04-15 12:20:48 +09:00
31c26354c5 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-04-15 12:11:58 +09:00
64b331c5b2 Update read.ts 2019-04-15 12:11:32 +09:00
94f8a145ec Better permisson Fix #2341 (#4611)
* Better permisson Fix #2341

* add kinds.ts

* test

* fix

* v11

* fix
2019-04-15 12:10:40 +09:00
b357afa30a Clean up 2019-04-15 12:04:23 +09:00
fee0437493 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-04-15 12:03:04 +09:00
663b8864c1 Fix bug 2019-04-15 12:03:00 +09:00
c6eafdde30 Fix bug 2019-04-15 11:59:25 +09:00
f32ff95256 New Crowdin translations (#4685)
* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Czech)
2019-04-15 11:33:08 +09:00
0452b75c3e Fix #4688 (#4689) 2019-04-15 11:32:53 +09:00
97efd23ec8 Update README.md [AUTOGEN] (#4691) 2019-04-15 11:31:32 +09:00
ea3e311528 簡易的なパーミッションの後方互換 (#4687) 2019-04-15 03:48:54 +09:00
0565454419 callbackUrlをappのレスポンスに追加 (#4686) 2019-04-15 03:39:31 +09:00
35c79c2f29 Fix bug 2019-04-15 03:20:14 +09:00
e6089aec48 Update README.md 2019-04-15 02:45:16 +09:00
100 changed files with 915 additions and 321 deletions

View File

@ -5,12 +5,6 @@ executors:
working_directory: /tmp/workspace working_directory: /tmp/workspace
docker: docker:
- image: misskey/ci:latest - image: misskey/ci:latest
- image: circleci/mongo:latest
with-redis:
working_directory: /tmp/workspace
docker:
- image: misskey/ci:latest
- image: circleci/mongo:latest
- image: circleci/redis:latest - image: circleci/redis:latest
docker: docker:
working_directory: /tmp/workspace working_directory: /tmp/workspace

View File

@ -5,17 +5,79 @@ If you encounter any problems with updating, please try the following:
1. `npm run clean` or `npm run cleanall` 1. `npm run clean` or `npm run cleanall`
2. Retry update (Don't forget `npm i`) 2. Retry update (Don't forget `npm i`)
11.1.4 (2019/04/17)
-------------------
### Fixes
* タイムライン取得時に削除されたファイルを添付している投稿が含まれているとサーバーでエラーになる問題を修正
* 管理画面のインスタンスメニューで変更前の設定が読み込まれないことがある問題を修正
* 猫ではないのに猫のままで表示される問題を修正
* admin/driveのアイコンがずれてる問題を修正
* チャートで大きな数値を扱えない問題を修正
* UIの修正
11.1.3 (2019/04/16)
-------------------
### Fixes
* アプリからAPIにリクエストするときにランダムなユーザーがリクエストしたことになる問題を修正
11.1.2 (2019/04/15)
-------------------
### Fixes
* 画像描画の依存関係を変更
* リモートユーザーのファイルを削除するときに古い方からではなく新しい方から削除されるのを修正
* リアクションしてないのにリアクションしたことになる問題を修正
* APIドキュメントの修正
11.1.1 (2019/04/15)
-------------------
### Fixes
* Metaタグの application-name を Misskey で固定するように修正
* トークメッセージが既読にならない問題を修正
* デフォルトでHTLを表示するように
11.1.0 (2019/04/15)
-------------------
### Improvements
* アイコン未設定時にランダムな画像を表示するように
* 管理者やモデレーターはレートリミット無効に
### Fixes
* メンションの「あなた」インジケーターが表示されない問題を修正
* ブロックAPIでエラーが発生する問題を修正
* プッシュ通知の購読に失敗する問題を修正
11.0.3 (2019/04/15)
-------------------
### Fixes
* ハッシュタグ検索APIが動作しない問題を修正
* モデレーターなのにアカウントメニューに「管理」が表示されない問題を修正
* プッシュ通知の購読に失敗する問題を修正
* ユーザー取得APIでユーザーを指定しない場合エラーになる問題を修正
11.0.2 (2019/04/15)
-------------------
### Fixes
* アプリが作成できない問題を修正
* 「ハイライト」が表示されない問題を修正
* リモートの投稿に添付されている画像が小さい問題を修正
* モバイル版でリストの名前が表示されない問題を修正
* APIドキュメントにパーミッション一覧を追加
11.0.1 (2019/04/15) 11.0.1 (2019/04/15)
------------------- -------------------
### Improvements
* 不要な依存関係を削除 * 不要な依存関係を削除
11.0.0 daybreak (2019/04/14) 11.0.0 daybreak (2019/04/14)
---------------------------- ----------------------------
### Improvements
* **データベースがMongoDBからPostgreSQLに変更されました** * **データベースがMongoDBからPostgreSQLに変更されました**
* **Redisが必須に** * **Redisが必須に**
* アカウントを完全に削除できるように * アカウントを完全に削除できるように
* 投稿フォームで添付ファイルの閲覧注意を確認/設定できるように * 投稿フォームで添付ファイルの閲覧注意を確認/設定できるように
* ミュート/ブロック時にそのユーザーの投稿のウォッチをすべて解除するように * ミュート/ブロック時にそのユーザーの投稿のウォッチをすべて解除するように
### Fixes
* フォロー申請数が実際より1すくなくなる問題を修正 * フォロー申請数が実際より1すくなくなる問題を修正
* リストからアカウント削除したユーザーを削除できない問題を修正 * リストからアカウント削除したユーザーを削除できない問題を修正
* リストTLでフォローしていないユーザーの非公開投稿が流れる問題を修正 * リストTLでフォローしていないユーザーの非公開投稿が流れる問題を修正

View File

@ -59,7 +59,7 @@ Organize and store your files! Want to post a picture you have already uploaded?
--- ---
...and more! Experience Misskey with your own eyes at [misskey.xyz](https://misskey.xyz) or join one of the [other instances](https://joinmisskey.github.io/) that are available. ...and more! Experience Misskey with your own eyes at [misskey.io](https://misskey.io/) or join one of the [other instances](https://joinmisskey.github.io/) that are available.
Screen shots Screen shots
---------------------------------------------------------------- ----------------------------------------------------------------
@ -103,7 +103,6 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<table><tr> <table><tr>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5888816/36da0f7c15954df0ab13f9abdf227f66/1.jpeg?token-time=2145916800&token-hash=at8QpJXJ8C0zINY_NmoMKv-MhXVoUK-YzTgaJPJzJYU%3D" alt="Hiroshi Seki" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5888816/36da0f7c15954df0ab13f9abdf227f66/1.jpeg?token-time=2145916800&token-hash=at8QpJXJ8C0zINY_NmoMKv-MhXVoUK-YzTgaJPJzJYU%3D" alt="Hiroshi Seki" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12190916/fb7fa7983c14425f890369535b1506a4/3.png?token-time=2145916800&token-hash=oH_i7gJjNT7Ot6j9JiVwy7ZJIBqACVnzLqlz4YrDAZA%3D" alt="weep" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12190916/fb7fa7983c14425f890369535b1506a4/3.png?token-time=2145916800&token-hash=oH_i7gJjNT7Ot6j9JiVwy7ZJIBqACVnzLqlz4YrDAZA%3D" alt="weep" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/12059069" alt="naga_rus" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13099460/43cecdbaa63a40d79bf50a96b9910b9d/1.jpe?token-time=2145916800&token-hash=bqwLTk0Wo0hUJJ8J5y7ii05bLzz-_CDA7Bo0Mp4RFU0%3D" alt="ne_moni" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13099460/43cecdbaa63a40d79bf50a96b9910b9d/1.jpe?token-time=2145916800&token-hash=bqwLTk0Wo0hUJJ8J5y7ii05bLzz-_CDA7Bo0Mp4RFU0%3D" alt="ne_moni" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12913507/f7181eacafe8469a93033d85f5969c29/4.jpe?token-time=2145916800&token-hash=zEyJqVM7u9d8Ri-65fJYSJcWF1jBH1nJ5a3taRzrTmw%3D" alt="Melilot" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12913507/f7181eacafe8469a93033d85f5969c29/4.jpe?token-time=2145916800&token-hash=zEyJqVM7u9d8Ri-65fJYSJcWF1jBH1nJ5a3taRzrTmw%3D" alt="Melilot" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5670915/ee175f0bfb6347ffa4ea101a8c097bff/1.jpg?token-time=2145916800&token-hash=mPLM9CA-riFHx-myr3bLZJuH2xBRHA9se5VbHhLIOuA%3D" alt="osapon" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5670915/ee175f0bfb6347ffa4ea101a8c097bff/1.jpg?token-time=2145916800&token-hash=mPLM9CA-riFHx-myr3bLZJuH2xBRHA9se5VbHhLIOuA%3D" alt="osapon" width="100"></td>
@ -111,7 +110,6 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
</tr><tr> </tr><tr>
<td><a href="https://www.patreon.com/rane_hs">Hiroshi Seki</a></td> <td><a href="https://www.patreon.com/rane_hs">Hiroshi Seki</a></td>
<td><a href="https://www.patreon.com/weepjp">weep</a></td> <td><a href="https://www.patreon.com/weepjp">weep</a></td>
<td><a href="https://www.patreon.com/user?u=12059069">naga_rus</a></td>
<td><a href="https://www.patreon.com/user?u=13099460">ne_moni</a></td> <td><a href="https://www.patreon.com/user?u=13099460">ne_moni</a></td>
<td><a href="https://www.patreon.com/user?u=12913507">Melilot</a></td> <td><a href="https://www.patreon.com/user?u=12913507">Melilot</a></td>
<td><a href="https://www.patreon.com/osapon">osapon</a></td> <td><a href="https://www.patreon.com/osapon">osapon</a></td>
@ -142,7 +140,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
</tr></table> </tr></table>
<table><tr> <table><tr>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/17195955/be45e5e14c3e48b2bee0456c84e19df4/4.jpe?token-time=2145916800&token-hash=UslrPVM-8TXOe8AapuNiaFYjcIJgPNcU-fKpGbfGJNI%3D" alt="Damillora" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/17195955/be45e5e14c3e48b2bee0456c84e19df4/4.jpe?token-time=2145916800&token-hash=UslrPVM-8TXOe8AapuNiaFYjcIJgPNcU-fKpGbfGJNI%3D" alt="Damillora" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/16900731/935a10339daa4ede8e555903a0707060/1.png?token-time=2145916800&token-hash=c1XAS1qGBPxVdCvnICxtAUmx41mVkMG87h7cIRF9YYE%3D" alt="Atsuko Tominaga" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/16900731/83884b38afc74d4cbe83c30a13b10edd/1.png?token-time=2145916800&token-hash=R5Tog8RWg0rguRoCIoir3lThokrdPvs8Utfikhc0nhY%3D" alt="Atsuko Tominaga" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/4389829/9f709180ac714651a70f74a82f3ffdb9/3.png?token-time=2145916800&token-hash=FTm3WVom4dJ9NwWMU4OpCL_8Yc13WiwEbKrDPyTZTPs%3D" alt="natalie" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/4389829/9f709180ac714651a70f74a82f3ffdb9/3.png?token-time=2145916800&token-hash=FTm3WVom4dJ9NwWMU4OpCL_8Yc13WiwEbKrDPyTZTPs%3D" alt="natalie" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13034746/c711c7f58e204ecfbc2fd646bc8a4eee/1.jpe?token-time=2145916800&token-hash=EWxXhVbZYH7KB4IDT3joc8TbIg8zPO40x1r5IDn3R7c%3D" alt="Hiratake" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13034746/c711c7f58e204ecfbc2fd646bc8a4eee/1.jpe?token-time=2145916800&token-hash=EWxXhVbZYH7KB4IDT3joc8TbIg8zPO40x1r5IDn3R7c%3D" alt="Hiratake" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/18072312/98e894d960314fa7bc236a72a39488fe/1.jpe?token-time=2145916800&token-hash=qA8j97lIZNc-74AuZ0p4F3ms6sKPeKjtNt2vEuwpsyo%3D" alt="Hekovic" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/18072312/98e894d960314fa7bc236a72a39488fe/1.jpe?token-time=2145916800&token-hash=qA8j97lIZNc-74AuZ0p4F3ms6sKPeKjtNt2vEuwpsyo%3D" alt="Hekovic" width="100"></td>
@ -165,7 +163,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
</tr><tr> </tr><tr>
</tr></table> </tr></table>
**Last updated:** Sun, 14 Apr 2019 08:13:12 UTC **Last updated:** Mon, 15 Apr 2019 12:07:08 UTC
<!-- PATREON_END --> <!-- PATREON_END -->
:four_leaf_clover: Copyright :four_leaf_clover: Copyright

View File

@ -9,9 +9,17 @@ This guide describes how to install and setup Misskey with Docker.
*1.* Download Misskey *1.* Download Misskey
---------------------------------------------------------------- ----------------------------------------------------------------
1. `git clone -b master git://github.com/syuilo/misskey.git` Clone Misskey repository's master branch. 1. Clone Misskey repository's master branch.
2. `cd misskey` Move to misskey directory.
3. `git checkout $(git tag -l | grep -Ev -- '-(rc|alpha)\.[0-9]+$' | sort -V | tail -n 1)` Checkout to the [latest release](https://github.com/syuilo/misskey/releases/latest) tag. `git clone -b master git://github.com/syuilo/misskey.git`
2. Move to misskey directory.
`cd misskey`
3. Checkout to the [latest release](https://github.com/syuilo/misskey/releases/latest) tag.
`git checkout master`
*2.* Configure Misskey *2.* Configure Misskey
---------------------------------------------------------------- ----------------------------------------------------------------
@ -57,7 +65,13 @@ Build misskey with the following:
`docker-compose build` `docker-compose build`
*5.* That is it. *5.* Init DB
----------------------------------------------------------------
``` shell
docker-compose run --rm web npm run init
```
*6.* That is it.
---------------------------------------------------------------- ----------------------------------------------------------------
Well done! Now you have an environment to run Misskey. Well done! Now you have an environment to run Misskey.
@ -65,9 +79,9 @@ Well done! Now you have an environment to run Misskey.
Just `docker-compose up -d`. GLHF! Just `docker-compose up -d`. GLHF!
### How to update your Misskey server to the latest version ### How to update your Misskey server to the latest version
1. `git fetch` 1. `git stash`
2. `git stash` 2. `git checkout master`
3. `git checkout $(git tag -l | grep -Ev -- '-(rc|alpha)\.[0-9]+$' | sort -V | tail -n 1)` 3. `git pull`
4. `git stash pop` 4. `git stash pop`
5. `docker-compose build` 5. `docker-compose build`
6. Check [ChangeLog](../CHANGELOG.md) for migration information 6. Check [ChangeLog](../CHANGELOG.md) for migration information

View File

@ -10,9 +10,17 @@ Ce guide explique comment installer et configurer Misskey avec Docker.
*1.* Télécharger Misskey *1.* Télécharger Misskey
---------------------------------------------------------------- ----------------------------------------------------------------
1. `git clone -b master git://github.com/syuilo/misskey.git` Clone le dépôt de Misskey sur la branche master. 1. Clone le dépôt de Misskey sur la branche master.
2. `cd misskey` Naviguez dans le dossier du dépôt.
3. `git checkout $(git tag -l | grep -Ev -- '-(rc|alpha)\.[0-9]+$' | sort -V | tail -n 1)` Checkout sur le tag de la [dernière version](https://github.com/syuilo/misskey/releases/latest). `git clone -b master git://github.com/syuilo/misskey.git`
2. Naviguez dans le dossier du dépôt.
`cd misskey`
3. Checkout sur le tag de la [dernière version](https://github.com/syuilo/misskey/releases/latest).
`git checkout master`
*2.* Configuration de Misskey *2.* Configuration de Misskey
---------------------------------------------------------------- ----------------------------------------------------------------
@ -38,9 +46,9 @@ Parfait, Vous avez un environnement prêt pour démarrer Misskey.
Utilisez la commande `docker-compose up -d`. GLHF! Utilisez la commande `docker-compose up -d`. GLHF!
### How to update your Misskey server to the latest version ### How to update your Misskey server to the latest version
1. `git fetch` 1. `git stash`
2. `git stash` 2. `git checkout master`
3. `git checkout $(git tag -l | grep -Ev -- '-(rc|alpha)\.[0-9]+$' | sort -V | tail -n 1)` 3. `git pull`
4. `git stash pop` 4. `git stash pop`
5. `docker-compose build` 5. `docker-compose build`
6. Consultez le [ChangeLog](../CHANGELOG.md) pour avoir les éventuelles informations de migration 6. Consultez le [ChangeLog](../CHANGELOG.md) pour avoir les éventuelles informations de migration
@ -52,14 +60,28 @@ Utilisez la commande `docker-compose up -d`. GLHF!
### Configuration d'ElasticSearch (pour la fonction de recherche) ### Configuration d'ElasticSearch (pour la fonction de recherche)
*1.* Préparation de l'environnement *1.* Préparation de l'environnement
---------------------------------------------------------------- ----------------------------------------------------------------
1. `mkdir elasticsearch && chown 1000:1000 elasticsearch` Permet de créer le dossier d'accueil de la base ElasticSearch aves les bons droits 1. Permet de créer le dossier d'accueil de la base ElasticSearch aves les bons droits
2. `sysctl -w vm.max_map_count=262144` Augmente la valeur max du paramètre map_count du système (valeur minimum pour pouvoir lancer ES)
`mkdir elasticsearch && chown 1000:1000 elasticsearch`
2. Augmente la valeur max du paramètre map_count du système (valeur minimum pour pouvoir lancer ES)
`sysctl -w vm.max_map_count=262144`
*2.* Après lancement du docker-compose, initialisation de la base ElasticSearch *2.* Après lancement du docker-compose, initialisation de la base ElasticSearch
---------------------------------------------------------------- ----------------------------------------------------------------
1. `docker-compose -it web /bin/sh` Connexion dans le conteneur web 1. Connexion dans le conteneur web
2. `apk add curl` Ajout du paquet curl
3. `curl -X PUT "es:9200/misskey" -H 'Content-Type: application/json' -d'{ "settings" : { "index" : { } }}'` Création de la base ES `docker-compose -it web /bin/sh`
2. Ajout du paquet curl
`apk add curl`
3. Création de la base ES
`curl -X PUT "es:9200/misskey" -H 'Content-Type: application/json' -d'{ "settings" : { "index" : { } }}'`
4. `exit` 4. `exit`
---------------------------------------------------------------- ----------------------------------------------------------------

View File

@ -9,9 +9,17 @@ Dockerを使ったMisskey構築方法
*1.* Misskeyのダウンロード *1.* Misskeyのダウンロード
---------------------------------------------------------------- ----------------------------------------------------------------
1. `git clone -b master git://github.com/syuilo/misskey.git` masterブランチからMisskeyレポジトリをクローン 1. masterブランチからMisskeyレポジトリをクローン
2. `cd misskey` misskeyディレクトリに移動
3. `git checkout $(git tag -l | grep -Ev -- '-(rc|alpha)\.[0-9]+$' | sort -V | tail -n 1)` [最新のリリース](https://github.com/syuilo/misskey/releases/latest)を確認 `git clone -b master git://github.com/syuilo/misskey.git`
2. misskeyディレクトリに移動
`cd misskey`
3. [最新のリリース](https://github.com/syuilo/misskey/releases/latest)を確認
`git checkout master`
*2.* 設定ファイルの作成と編集 *2.* 設定ファイルの作成と編集
---------------------------------------------------------------- ----------------------------------------------------------------
@ -57,7 +65,13 @@ cp docker_example.env docker.env
`docker-compose build` `docker-compose build`
*5.* 以上です! *5.* データベースを初期化
----------------------------------------------------------------
``` shell
docker-compose run --rm web npm run init
```
*6.* 以上です!
---------------------------------------------------------------- ----------------------------------------------------------------
お疲れ様でした。これでMisskeyを動かす準備は整いました。 お疲れ様でした。これでMisskeyを動かす準備は整いました。
@ -65,9 +79,9 @@ cp docker_example.env docker.env
`docker-compose up -d`するだけです。GLHF! `docker-compose up -d`するだけです。GLHF!
### Misskeyを最新バージョンにアップデートする方法: ### Misskeyを最新バージョンにアップデートする方法:
1. `git fetch` 1. `git stash`
2. `git stash` 2. `git checkout master`
3. `git checkout $(git tag -l | grep -Ev -- '-(rc|alpha)\.[0-9]+$' | sort -V | tail -n 1)` 3. `git pull`
4. `git stash pop` 4. `git stash pop`
5. `docker-compose build` 5. `docker-compose build`
6. [ChangeLog](../CHANGELOG.md)でマイグレーション情報を確認する 6. [ChangeLog](../CHANGELOG.md)でマイグレーション情報を確認する

View File

@ -29,7 +29,7 @@ server {
listen [::]:443 http2; listen [::]:443 http2;
server_name example.tld; server_name example.tld;
ssl on; ssl on;
ssl_session_cache shared:ssl_session_cache:10m; ssl_session_cache shared:ssl_session_cache:10m;
# To use Let's Encrypt certificate # To use Let's Encrypt certificate
ssl_certificate /etc/letsencrypt/live/example.tld/fullchain.pem; ssl_certificate /etc/letsencrypt/live/example.tld/fullchain.pem;

View File

@ -32,15 +32,32 @@ Please install and setup these softwares:
*3.* Install Misskey *3.* Install Misskey
---------------------------------------------------------------- ----------------------------------------------------------------
1. `su - misskey` Connect to misskey user. 1. Connect to misskey user.
2. `git clone -b master git://github.com/syuilo/misskey.git` Clone the misskey repo from master branch.
3. `cd misskey` Navigate to misskey directory `su - misskey`
4. `git checkout $(git tag -l | grep -Ev -- '-(rc|alpha)\.[0-9]+$' | sort -V | tail -n 1)` Checkout to the [latest release](https://github.com/syuilo/misskey/releases/latest)
5. `npm install` Install misskey dependencies. 2. Clone the misskey repo from master branch.
`git clone -b master git://github.com/syuilo/misskey.git`
3. Navigate to misskey directory
`cd misskey`
4. Checkout to the [latest release](https://github.com/syuilo/misskey/releases/latest)
`git checkout master`
5. Install misskey dependencies.
`npm install`
*4.* Configure Misskey *4.* Configure Misskey
---------------------------------------------------------------- ----------------------------------------------------------------
1. `cp .config/example.yml .config/default.yml` Copy the `.config/example.yml` and rename it to `default.yml`. 1. Copy the `.config/example.yml` and rename it to `default.yml`.
`cp .config/example.yml .config/default.yml`
2. Edit `default.yml` 2. Edit `default.yml`
*5.* Build Misskey *5.* Build Misskey
@ -74,37 +91,45 @@ Just `NODE_ENV=production npm start`. GLHF!
### Launch with systemd ### Launch with systemd
1. Create a systemd service here: `/etc/systemd/system/misskey.service` 1. Create a systemd service here
`/etc/systemd/system/misskey.service`
2. Edit it, and paste this and save: 2. Edit it, and paste this and save:
``` ```
[Unit] [Unit]
Description=Misskey daemon Description=Misskey daemon
[Service] [Service]
Type=simple Type=simple
User=misskey User=misskey
ExecStart=/usr/bin/npm start ExecStart=/usr/bin/npm start
WorkingDirectory=/home/misskey/misskey WorkingDirectory=/home/misskey/misskey
Environment="NODE_ENV=production" Environment="NODE_ENV=production"
TimeoutSec=60 TimeoutSec=60
StandardOutput=syslog StandardOutput=syslog
StandardError=syslog StandardError=syslog
SyslogIdentifier=misskey SyslogIdentifier=misskey
Restart=always Restart=always
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target
``` ```
3. `systemctl daemon-reload ; systemctl enable misskey` Reload systemd and enable the misskey service. 3. Reload systemd and enable the misskey service.
4. `systemctl start misskey` Start the misskey service.
`systemctl daemon-reload ; systemctl enable misskey`
4. Start the misskey service.
`systemctl start misskey`
You can check if the service is running with `systemctl status misskey`. You can check if the service is running with `systemctl status misskey`.
### How to update your Misskey server to the latest version ### How to update your Misskey server to the latest version
1. `git fetch` 1. `git checkout master`
2. `git checkout $(git tag -l | grep -Ev -- '-(rc|alpha)\.[0-9]+$' | sort -V | tail -n 1)` 2. `git pull`
3. `npm install` 3. `npm install`
4. `NODE_ENV=production npm run build` 4. `NODE_ENV=production npm run build`
5. Check [ChangeLog](../CHANGELOG.md) for migration information 5. Check [ChangeLog](../CHANGELOG.md) for migration information

View File

@ -32,15 +32,32 @@ Installez les paquets suivants :
*3.* Installation de Misskey *3.* Installation de Misskey
---------------------------------------------------------------- ----------------------------------------------------------------
1. `su - misskey` Basculez vers l'utilisateur misskey. 1. Basculez vers l'utilisateur misskey.
2. `git clone -b master git://github.com/syuilo/misskey.git` Clonez la branche master du dépôt misskey.
3. `cd misskey` Accédez au dossier misskey. `su - misskey`
4. `git checkout $(git tag -l | grep -Ev -- '-(rc|alpha)\.[0-9]+$' | sort -V | tail -n 1)` Checkout sur le tag de la [version la plus récente](https://github.com/syuilo/misskey/releases/latest)
5. `npm install` Installez les dépendances de misskey. 2. Clonez la branche master du dépôt misskey.
`git clone -b master git://github.com/syuilo/misskey.git`
3. Accédez au dossier misskey.
`cd misskey`
4. Checkout sur le tag de la [version la plus récente](https://github.com/syuilo/misskey/releases/latest)
`git checkout master`
5. Installez les dépendances de misskey.
`npm install`
*4.* Création du fichier de configuration *4.* Création du fichier de configuration
---------------------------------------------------------------- ----------------------------------------------------------------
1. `cp .config/example.yml .config/default.yml` Copiez le fichier `.config/example.yml` et renommez-le`default.yml`. 1. Copiez le fichier `.config/example.yml` et renommez-le`default.yml`.
`cp .config/example.yml .config/default.yml`
2. Editez le fichier `default.yml` 2. Editez le fichier `default.yml`
*5.* Construction de Misskey *5.* Construction de Misskey
@ -68,37 +85,45 @@ Lancez tout simplement `NODE_ENV=production npm start`. Bonne chance et amusez-v
### Démarrage avec systemd ### Démarrage avec systemd
1. Créez un service systemd sur : `/etc/systemd/system/misskey.service` 1. Créez un service systemd sur
`/etc/systemd/system/misskey.service`
2. Editez-le puis copiez et coller ceci dans le fichier : 2. Editez-le puis copiez et coller ceci dans le fichier :
``` ```
[Unit] [Unit]
Description=Misskey daemon Description=Misskey daemon
[Service] [Service]
Type=simple Type=simple
User=misskey User=misskey
ExecStart=/usr/bin/npm start ExecStart=/usr/bin/npm start
WorkingDirectory=/home/misskey/misskey WorkingDirectory=/home/misskey/misskey
Environment="NODE_ENV=production" Environment="NODE_ENV=production"
TimeoutSec=60 TimeoutSec=60
StandardOutput=syslog StandardOutput=syslog
StandardError=syslog StandardError=syslog
SyslogIdentifier=misskey SyslogIdentifier=misskey
Restart=always Restart=always
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target
``` ```
3. `systemctl daemon-reload ; systemctl enable misskey` Redémarre systemd et active le service misskey. 3. Redémarre systemd et active le service misskey.
4. `systemctl start misskey` Démarre le service misskey.
`systemctl daemon-reload ; systemctl enable misskey`
4. Démarre le service misskey.
`systemctl start misskey`
Vous pouvez vérifier si le service a démarré en utilisant la commande `systemctl status misskey`. Vous pouvez vérifier si le service a démarré en utilisant la commande `systemctl status misskey`.
### Méthode de mise à jour vers la plus récente version de Misskey ### Méthode de mise à jour vers la plus récente version de Misskey
1. `git fetch` 1. `git checkout master`
2. `git checkout $(git tag -l | grep -Ev -- '-(rc|alpha)\.[0-9]+$' | sort -V | tail -n 1)` 2. `git pull`
3. `npm install` 3. `npm install`
4. `NODE_ENV=production npm run build` 4. `NODE_ENV=production npm run build`
5. Consultez [ChangeLog](../CHANGELOG.md) pour les information de migration. 5. Consultez [ChangeLog](../CHANGELOG.md) pour les information de migration.

View File

@ -33,15 +33,32 @@ adduser --disabled-password --disabled-login misskey
*3.* Misskeyのインストール *3.* Misskeyのインストール
---------------------------------------------------------------- ----------------------------------------------------------------
1. `su - misskey` misskeyユーザーを使用 1. misskeyユーザーを使用
2. `git clone -b master git://github.com/syuilo/misskey.git` masterブランチからMisskeyレポジトリをクローン
3. `cd misskey` misskeyディレクトリに移動 `su - misskey`
4. `git checkout $(git tag -l | grep -Ev -- '-(rc|alpha)\.[0-9]+$' | sort -V | tail -n 1)` [最新のリリース](https://github.com/syuilo/misskey/releases/latest)を確認
5. `npm install` Misskeyの依存パッケージをインストール 2. masterブランチからMisskeyレポジトリをクローン
`git clone -b master git://github.com/syuilo/misskey.git`
3. misskeyディレクトリに移動
`cd misskey`
4. [最新のリリース](https://github.com/syuilo/misskey/releases/latest)を確認
`git checkout master`
5. Misskeyの依存パッケージをインストール
`npm install`
*4.* 設定ファイルを作成する *4.* 設定ファイルを作成する
---------------------------------------------------------------- ----------------------------------------------------------------
1. `cp .config/example.yml .config/default.yml` `.config/example.yml`をコピーし名前を`default.yml`にする。 1. `.config/example.yml`をコピーし名前を`default.yml`にする。
`cp .config/example.yml .config/default.yml`
2. `default.yml` を編集する。 2. `default.yml` を編集する。
*5.* Misskeyのビルド *5.* Misskeyのビルド
@ -73,38 +90,47 @@ npm run init
`NODE_ENV=production npm start`するだけです。GLHF! `NODE_ENV=production npm start`するだけです。GLHF!
### systemdを用いた起動 ### systemdを用いた起動
1. systemdサービスのファイルを作成: `/etc/systemd/system/misskey.service` 1. systemdサービスのファイルを作成
`/etc/systemd/system/misskey.service`
2. エディタで開き、以下のコードを貼り付けて保存: 2. エディタで開き、以下のコードを貼り付けて保存:
``` ```
[Unit] [Unit]
Description=Misskey daemon Description=Misskey daemon
[Service] [Service]
Type=simple Type=simple
User=misskey User=misskey
ExecStart=/usr/bin/npm start ExecStart=/usr/bin/npm start
WorkingDirectory=/home/misskey/misskey WorkingDirectory=/home/misskey/misskey
Environment="NODE_ENV=production" Environment="NODE_ENV=production"
TimeoutSec=60 TimeoutSec=60
StandardOutput=syslog StandardOutput=syslog
StandardError=syslog StandardError=syslog
SyslogIdentifier=misskey SyslogIdentifier=misskey
Restart=always Restart=always
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target
``` ```
CentOSで1024以下のポートを使用してMisskeyを使用する場合は`ExecStart=/usr/bin/sudo /usr/bin/npm start`に変更する必要があります。
3. `systemctl daemon-reload ; systemctl enable misskey` systemdを再読み込みしmisskeyサービスを有効化 CentOSで1024以下のポートを使用してMisskeyを使用する場合は`ExecStart=/usr/bin/sudo /usr/bin/npm start`に変更する必要があります。
4. `systemctl start misskey` misskeyサービスの起動
3. systemdを再読み込みしmisskeyサービスを有効化
`systemctl daemon-reload ; systemctl enable misskey`
4. misskeyサービスの起動
`systemctl start misskey`
`systemctl status misskey`と入力すると、サービスの状態を調べることができます。 `systemctl status misskey`と入力すると、サービスの状態を調べることができます。
### Misskeyを最新バージョンにアップデートする方法: ### Misskeyを最新バージョンにアップデートする方法:
1. `git fetch` 1. `git checkout master`
2. `git checkout $(git tag -l | grep -Ev -- '-(rc|alpha)\.[0-9]+$' | sort -V | tail -n 1)` 2. `git pull`
3. `npm install` 3. `npm install`
4. `NODE_ENV=production npm run build` 4. `NODE_ENV=production npm run build`
5. [ChangeLog](../CHANGELOG.md)でマイグレーション情報を確認する 5. [ChangeLog](../CHANGELOG.md)でマイグレーション情報を確認する

View File

@ -69,6 +69,9 @@ common:
following: "Sledovaní" following: "Sledovaní"
followers: "Sledující" followers: "Sledující"
favorites: "Oblíbené" favorites: "Oblíbené"
permissions:
'read:drive': "Prohlížet Disk"
'write:drive': "Pracovat s Diskem"
empty-timeline-info: empty-timeline-info:
follow-users-to-make-your-timeline: "Poznámky sledujících se zobrazí ve vaší časové ose" follow-users-to-make-your-timeline: "Poznámky sledujících se zobrazí ve vaší časové ose"
explore: "Najít uživatele" explore: "Najít uživatele"

View File

@ -20,6 +20,13 @@ common:
load-more: "Mehr laden" load-more: "Mehr laden"
enter-password: "Bitte Passwort eingeben" enter-password: "Bitte Passwort eingeben"
2fa: "Zwei-Faktor-Authentifizierung" 2fa: "Zwei-Faktor-Authentifizierung"
customize-home: "Layout Anpassen"
featured-notes: "Hervorgehobene Beiträge"
dark-mode: "Dunkler Modus"
signin: "Einloggen"
signup: "Registrieren"
signout: "Ausloggen"
reload-to-apply-the-setting: "Die Seite muss zum Übernehmen dieser Einstellung aktualisiert werden. Soll die Seite jetzt neu geladen werden?"
got-it: "Verstanden!" got-it: "Verstanden!"
customization-tips: customization-tips:
title: "Anpassung-Tipps" title: "Anpassung-Tipps"
@ -27,6 +34,7 @@ common:
notification: notification:
file-uploaded: "Datei hochgeladen!" file-uploaded: "Datei hochgeladen!"
message-from: "Nachricht von {}:" message-from: "Nachricht von {}:"
reversi-invited: "Zu einem Spiel eingeladen"
reversi-invited-by: "Eingeladen von {}:" reversi-invited-by: "Eingeladen von {}:"
notified-by: "Benachrichtigt von {}:" notified-by: "Benachrichtigt von {}:"
reply-from: "Antwort von {}:" reply-from: "Antwort von {}:"
@ -47,7 +55,20 @@ common:
drive: "Drive" drive: "Drive"
messaging: "Unterhaltungen" messaging: "Unterhaltungen"
home: "Home" home: "Home"
favorites: "Diese Notiz favorisieren" deck: "Stapel"
timeline: "Zeitleiste"
explore: "Entdecken"
following: "Folgt"
followers: "Folgende"
favorites: "Diesen Beitrag favorisieren"
permissions:
'read:account': "Accountinformationen anzeigen."
'write:account': "Accountinformationen bearbeiten."
'read:drive': "Dateien anzeigen"
'write:drive': "Dateien bearbeiten"
empty-timeline-info:
follow-users-to-make-your-timeline: "Beiträge von Benutzern, denen du folgst, werden in der Zeitleiste angezeigt."
explore: "Benutzer finden"
weekday-short: weekday-short:
sunday: "So" sunday: "So"
monday: "Mo" monday: "Mo"
@ -78,11 +99,11 @@ common:
note-visibility: note-visibility:
public: "Öffentlich" public: "Öffentlich"
home: "Startseite" home: "Startseite"
home-desc: "Nur auf die Startseite posten" home-desc: "Auf die Startseite posten"
followers: "Abonnenten" followers: "Abonnenten"
followers-desc: "Nur für diejenigen sichtbar, die dir folgen" followers-desc: "Nur für diejenigen sichtbar, die dir folgen"
specified: "Direkt" specified: "Direkt"
specified-desc: "Nur für bestimmte Benutzer posten" specified-desc: "Nur für bestimmte Benutzer sichtbar"
local-public: "Öffentlich (nur lokal)" local-public: "Öffentlich (nur lokal)"
local-home: "Home (nur lokal)" local-home: "Home (nur lokal)"
local-followers: "Follower (nur lokal)" local-followers: "Follower (nur lokal)"
@ -92,11 +113,34 @@ common:
c: "Was geht dir durch den Kopf?" c: "Was geht dir durch den Kopf?"
d: "Willst du etwas sagen?" d: "Willst du etwas sagen?"
e: "Schreib hier etwas!" e: "Schreib hier etwas!"
f: "Warte darauf, das du schreibst." f: "Warte darauf, das du schreibst..."
settings: "Einstellungen"
_settings: _settings:
profile: "Dein Profil" profile: "Dein Profil"
notification: "Benachrichtigungen" notification: "Benachrichtigungen"
apps: "Anwendungen"
tags: "Hashtags"
mute-and-block: "Stummschalten/Blocken"
blocking: "Blocken"
security: "Sicherheit"
signin: "Login-Verlauf"
password: "Passwort" password: "Passwort"
other: "Mehr"
appearance: "Designs"
behavior: "Verhalten"
fetch-on-scroll: "Unendliches laden beim scrollen"
fetch-on-scroll-desc: "Wenn beim scrollen das Ende erreicht wird, lädt die Anwendung automatisch neue Inhalte nach."
note-visibility: "Sichtbarkeit von Beiträgen"
default-note-visibility: "Die Standardsichtbarkeit"
remember-note-visibility: "Erinnerung an Sichtbarkeit von Beiträgen"
web-search-engine: "Web-Suchmaschine"
web-search-engine-desc: "Beispiel: https://www.google.de/search?q={{query}}"
keep-cw: "Inhaltswarnung beibehalten"
keep-cw-desc: "Wenn auf einen Beitrag geantwortet wird, wird die Inhaltswarnung des Originalbeitrags übernommen."
i-like-sushi: "Ich bevorzuge Sushi anstelle von Pudding"
show-reversi-board-labels: "Zeige Reihen- und Spaltenbeschreibungen in Reversi an"
use-avatar-reversi-stones: "Avatar als Stein in Reversi anzeigen"
disable-animated-mfm: "Animierten Text in Beiträgen deaktivieren"
search: "Suche" search: "Suche"
delete: "Löschen" delete: "Löschen"
loading: "Laden" loading: "Laden"
@ -104,7 +148,7 @@ common:
update-available: "Eine neue Version von Misskey ist verfügbar ({newer}, aktuell ist {current}). Lade die Seite neu um die aktuelle Version zu laden" update-available: "Eine neue Version von Misskey ist verfügbar ({newer}, aktuell ist {current}). Lade die Seite neu um die aktuelle Version zu laden"
my-token-regenerated: "Dein Token wurde generiert. Du wirst jetzt abgemeldet." my-token-regenerated: "Dein Token wurde generiert. Du wirst jetzt abgemeldet."
verified-user: "Verifizierter Benutzer" verified-user: "Verifizierter Benutzer"
do-not-use-in-production: "Dies ist eine Entwicklungsversion. Nicht in einer Produktionsumgebung verwenden." do-not-use-in-production: "Dies ist eine Entwicklungsversion. Nicht in einer Produktivumgebung verwenden."
error: error:
retry: "Erneut versuchen" retry: "Erneut versuchen"
reversi: reversi:
@ -118,7 +162,7 @@ common:
analog-clock: "Analoge Uhr" analog-clock: "Analoge Uhr"
profile: "Profil" profile: "Profil"
calendar: "Kalender" calendar: "Kalender"
timemachine: "Kalender (Zeitmaschiene)" timemachine: "Kalender (Zeitmaschine)"
activity: "Aktivitäten" activity: "Aktivitäten"
rss: "RSS Leser" rss: "RSS Leser"
memo: "Notizen" memo: "Notizen"
@ -129,7 +173,7 @@ common:
notifications: "Benachrichtigungen" notifications: "Benachrichtigungen"
users: "Empfohlene Benutzer" users: "Empfohlene Benutzer"
polls: "Umfrage" polls: "Umfrage"
post-form: "Beitragsform" post-form: "\"Neuer Beitrag\"-Formular"
server: "Server-Info" server: "Server-Info"
nav: "Navigation" nav: "Navigation"
tips: "Tipps" tips: "Tipps"
@ -179,7 +223,7 @@ common/views/components/games/reversi/reversi.room.vue:
ready: "Bereit" ready: "Bereit"
common/views/components/connect-failed.vue: common/views/components/connect-failed.vue:
title: "Verbindung zum Server ist fehlgeschlagen" title: "Verbindung zum Server ist fehlgeschlagen"
description: "Es gibt entweder ein Problem mit deiner Internetverbindung, der Server ist nicht erreichbar oder wird gerade gewartet. Bitte versuche es später noch einmal." description: "Entweder gibt es ein Problem mit deiner Internetverbindung oder der Server ist zur Zeit nicht erreichbar oder wird gewartet. Bitte versuche es später noch einmal."
thanks: "Vielen Dank für das nutzen von Misskey." thanks: "Vielen Dank für das nutzen von Misskey."
troubleshoot: "Problembehandlung" troubleshoot: "Problembehandlung"
common/views/components/connect-failed.troubleshooter.vue: common/views/components/connect-failed.troubleshooter.vue:
@ -196,8 +240,8 @@ common/views/components/connect-failed.troubleshooter.vue:
no-internet: "Keine Internetverbindung" no-internet: "Keine Internetverbindung"
no-internet-desc: "Bitte vergewissere dich, dass du mit dem Internet verbunden bist." no-internet-desc: "Bitte vergewissere dich, dass du mit dem Internet verbunden bist."
no-server: "Verbindung mit dem Server nicht möglich" no-server: "Verbindung mit dem Server nicht möglich"
no-server-desc: "Die Internetverbindung scheint in Ordnung zu sein, aber eine Verbindung mit dem Misskey Server konnte nicht hergestellt werden. Möglicherweise ist dieser zur Zeit offline oder in der Wartung, bitte versuche es später noch einmal." no-server-desc: "Die Internetverbindung scheint in Ordnung zu sein, aber eine Verbindung mit dem Misskey-Server konnte nicht hergestellt werden. Möglicherweise ist dieser zur Zeit offline oder wird gewartet. Bitte versuche es später noch einmal."
success: "Erfolgreich mit dem Misskey Server verbunden" success: "Erfolgreich mit dem Misskey-Server verbunden"
success-desc: "Die Verbindung scheint zu funktionieren. Bitte lade die Seite neu." success-desc: "Die Verbindung scheint zu funktionieren. Bitte lade die Seite neu."
flush: "Cache leeren" flush: "Cache leeren"
set-version: "Version angeben" set-version: "Version angeben"
@ -224,6 +268,7 @@ common/views/components/theme.vue:
invalid-theme: "Thema ist ungültig" invalid-theme: "Thema ist ungültig"
already-installed: "Thema ist bereits installiert" already-installed: "Thema ist bereits installiert"
author: "Autor" author: "Autor"
desc: "Beschreibung"
export: "Exportieren" export: "Exportieren"
import: "Importieren" import: "Importieren"
common/views/components/cw-button.vue: common/views/components/cw-button.vue:
@ -251,15 +296,15 @@ common/views/components/nav.vue:
status: "Status" status: "Status"
wiki: "Wiki" wiki: "Wiki"
donors: "Spender" donors: "Spender"
repository: "Projektarchiv" repository: "Quellcode"
develop: "Entwickler" develop: "Entwickler"
feedback: "Feedback" feedback: "Feedback"
common/views/components/note-menu.vue: common/views/components/note-menu.vue:
favorite: "Diese Notiz favorisieren" favorite: "Diesen Beitrag favorisieren"
unfavorite: "Aus Favoriten entfernen" unfavorite: "Aus Favoriten entfernen"
pin: "An die Profilseite pinnen" pin: "An die Profilseite pinnen"
delete: "Löschen" delete: "Löschen"
delete-confirm: "Diesen Post löschen?" delete-confirm: "Diesen Beitrag löschen?"
remote: "Auf Quelle anzeigen" remote: "Auf Quelle anzeigen"
common/views/components/poll.vue: common/views/components/poll.vue:
vote-to: "Stimme für '{}'" vote-to: "Stimme für '{}'"
@ -296,8 +341,8 @@ common/views/components/signup.vue:
password: "Passwort" password: "Passwort"
password-placeholder: "Wir empfehlen mindestens 8 Zeichen" password-placeholder: "Wir empfehlen mindestens 8 Zeichen"
weak-password: "Schwaches Passwort" weak-password: "Schwaches Passwort"
normal-password: "Faires Passwort" normal-password: "Normales Passwort"
strong-password: "Schwaches Passwort" strong-password: "Starkes Passwort"
retype: "Wiederholen" retype: "Wiederholen"
retype-placeholder: "Bitte das Passwort erneut eingeben" retype-placeholder: "Bitte das Passwort erneut eingeben"
password-matched: "OK" password-matched: "OK"
@ -319,16 +364,17 @@ common/views/components/uploader.vue:
common/views/components/visibility-chooser.vue: common/views/components/visibility-chooser.vue:
public: "Öffentlich" public: "Öffentlich"
home: "Home" home: "Home"
home-desc: "Nur auf die Startseite posten" home-desc: "Auf die Startseite posten"
followers: "Folgende" followers: "Folgende"
followers-desc: "Nur für diejenigen sichtbar, die dir folgen" followers-desc: "Nur für diejenigen sichtbar, die dir folgen"
specified: "Direkt" specified: "Direkt"
specified-desc: "Poste nur für bestimmte Benutzer" specified-desc: "Nur für bestimmte Benutzer sichtbar"
local-public: "Öffentlich (nur lokal)" local-public: "Öffentlich (nur lokal)"
local-home: "Home (nur lokal)" local-home: "Home (nur lokal)"
local-followers: "Follower (nur lokal)" local-followers: "Follower (nur lokal)"
common/views/components/profile-editor.vue: common/views/components/profile-editor.vue:
title: "Dein Profil" title: "Dein Profil"
name: "Name"
avatar: "Avatar" avatar: "Avatar"
banner: "Banner" banner: "Banner"
save: "Speichern" save: "Speichern"
@ -400,8 +446,8 @@ desktop/views/components/drive.file.vue:
copied: "Kopieren erfolgreich" copied: "Kopieren erfolgreich"
copied-url-to-clipboard: "URL wurde in die Zwischenablage kopiert" copied-url-to-clipboard: "URL wurde in die Zwischenablage kopiert"
desktop/views/components/drive.folder.vue: desktop/views/components/drive.folder.vue:
unable-to-process: "Der Vorgang konnte nicht beendet werden" unable-to-process: "Der Vorgang konnte nicht abgeschlossen werden"
circular-reference-detected: "Das Zielverzeichnis ist ein Unterverzeichnis des Verzeichnisses welches du verschieben möchtest" circular-reference-detected: "Das Zielverzeichnis ist innerhalb des Verzeichnisses, dass du verschieben möchtest"
unhandled-error: "Unbekannter Fehler" unhandled-error: "Unbekannter Fehler"
contextmenu: contextmenu:
move-to-this-folder: "Verschiebe in diesen Ordner" move-to-this-folder: "Verschiebe in diesen Ordner"
@ -416,7 +462,7 @@ desktop/views/components/drive.vue:
empty-drive-description: "Du kannst rechts klicken und \"Datei hochladen\" auswählen oder eine Datei per Drag and Drop auf das Fenster ziehen." empty-drive-description: "Du kannst rechts klicken und \"Datei hochladen\" auswählen oder eine Datei per Drag and Drop auf das Fenster ziehen."
empty-folder: "Dieser Ordner ist leer" empty-folder: "Dieser Ordner ist leer"
unable-to-process: "Der Vorgang konnte nicht beendet werden" unable-to-process: "Der Vorgang konnte nicht beendet werden"
circular-reference-detected: "Das Zielverzeichnis ist ein Unterverzeichnis des Verzeichnisses welches du verschieben möchtest" circular-reference-detected: "Das Zielverzeichnis ist innerhalb des Verzeichnisses, dass du verschieben möchtest"
unhandled-error: "Unbekannter Fehler" unhandled-error: "Unbekannter Fehler"
url-upload: "Von einer URL hochladen" url-upload: "Von einer URL hochladen"
url-of-file: "URL der Datei, welche du hochladen möchtest" url-of-file: "URL der Datei, welche du hochladen möchtest"
@ -440,15 +486,15 @@ desktop/views/input-dialog.vue:
cancel: "Abbrechen" cancel: "Abbrechen"
ok: "OK" ok: "OK"
desktop/views/components/note-detail.vue: desktop/views/components/note-detail.vue:
private: "Dieser Post ist privat" private: "Dieser Beitrag ist privat"
deleted: "Dieser Beitrag wurde entfernt" deleted: "Dieser Beitrag wurde entfernt"
location: "Ort" location: "Ort"
renote: "Anmerkung" renote: "Anmerkung"
add-reaction: "Reaktion hinzufügen" add-reaction: "Reaktion hinzufügen"
desktop/views/components/note.vue: desktop/views/components/note.vue:
reply: "Antworten" reply: "Antworten"
renote: "Anmerkung" renote: "Anmerken"
private: "Dieser Post ist privat" private: "Dieser Beitrag ist privat"
deleted: "Dieser Beitrag wurde entfernt" deleted: "Dieser Beitrag wurde entfernt"
desktop/views/components/notes.vue: desktop/views/components/notes.vue:
error: "Laden fehlgeschlagen." error: "Laden fehlgeschlagen."
@ -460,9 +506,9 @@ desktop/views/components/post-form.vue:
hide-contents: "Inhalt verstecken" hide-contents: "Inhalt verstecken"
reply-placeholder: "Antworte auf diese Anmerkung..." reply-placeholder: "Antworte auf diese Anmerkung..."
quote-placeholder: "Zitiere diese Anmerkung..." quote-placeholder: "Zitiere diese Anmerkung..."
submit: "Beitragsform" submit: "Abschicken"
reply: "Antworten" reply: "Antworten"
renote: "Anmerkung" renote: "Anmerken"
posted: "Gepostet!" posted: "Gepostet!"
replied: "Geantwortet!" replied: "Geantwortet!"
reposted: "Weitergesagt!" reposted: "Weitergesagt!"
@ -478,7 +524,7 @@ desktop/views/components/post-form.vue:
error: "Fehler" error: "Fehler"
enter-username: "Bitte gib einen Benutzernamen ein..." enter-username: "Bitte gib einen Benutzernamen ein..."
desktop/views/components/post-form-window.vue: desktop/views/components/post-form-window.vue:
note: "Neue Notiz" note: "Neuer Beitrag"
reply: "Antworten" reply: "Antworten"
attaches: "{} Medien hinzugefügt" attaches: "{} Medien hinzugefügt"
uploading-media: "Lade {} Medien hoch" uploading-media: "Lade {} Medien hoch"
@ -492,7 +538,7 @@ desktop/views/components/renote-form.vue:
success: "Weitergesagt!" success: "Weitergesagt!"
failure: "Weitersagen fehlgeschlagen" failure: "Weitersagen fehlgeschlagen"
desktop/views/components/renote-form-window.vue: desktop/views/components/renote-form-window.vue:
title: "Bist du dir sicher, dass du das reposten willst?" title: "Bist du dir sicher, dass du das weitersagen willst?"
desktop/views/components/settings.2fa.vue: desktop/views/components/settings.2fa.vue:
url: "https://www.google.de/intl/de/landing/2step/" url: "https://www.google.de/intl/de/landing/2step/"
register: "Ein Gerät registrieren" register: "Ein Gerät registrieren"
@ -504,6 +550,7 @@ desktop/views/components/settings.2fa.vue:
common/views/components/api-settings.vue: common/views/components/api-settings.vue:
enter-password: "Bitte Passwort eingeben" enter-password: "Bitte Passwort eingeben"
console: console:
parameter: "Parameter"
send: "Senden" send: "Senden"
common/views/components/drive-settings.vue: common/views/components/drive-settings.vue:
in-use: "benutzt" in-use: "benutzt"
@ -511,7 +558,7 @@ common/views/components/drive-settings.vue:
common/views/components/mute-and-block.vue: common/views/components/mute-and-block.vue:
save: "Speichern" save: "Speichern"
desktop/views/components/sub-note-content.vue: desktop/views/components/sub-note-content.vue:
private: "Dieser Post ist privat" private: "Dieser Beitrag ist privat"
deleted: "Dieser Beitrag wurde entfernt" deleted: "Dieser Beitrag wurde entfernt"
poll: "Umfrage" poll: "Umfrage"
desktop/views/components/settings.tags.vue: desktop/views/components/settings.tags.vue:
@ -601,22 +648,22 @@ mobile/views/components/drive.file-detail.vue:
download: "Download" download: "Download"
rename: "Umbenennen" rename: "Umbenennen"
mobile/views/components/note.vue: mobile/views/components/note.vue:
private: "Dieser Post ist privat" private: "Dieser Beitrag ist privat"
deleted: "Dieser Beitrag wurde entfernt" deleted: "Dieser Beitrag wurde entfernt"
location: "Ort" location: "Ort"
mobile/views/components/note-detail.vue: mobile/views/components/note-detail.vue:
reply: "Antworten" reply: "Antworten"
private: "Dieser Post ist privat" private: "Dieser Beitrag ist privat"
deleted: "Dieser Beitrag wurde entfernt" deleted: "Dieser Beitrag wurde entfernt"
location: "Ort" location: "Ort"
mobile/views/components/notifications.vue: mobile/views/components/notifications.vue:
empty: "Keine Benachrichtigungen" empty: "Keine Benachrichtigungen"
mobile/views/components/post-form.vue: mobile/views/components/post-form.vue:
reply: "Antworten" reply: "Antworten"
renote: "Anmerkung" renote: "Anmerken"
reply-placeholder: "Antworte auf diese Anmerkung..." reply-placeholder: "Antworte auf diese Anmerkung..."
mobile/views/components/sub-note-content.vue: mobile/views/components/sub-note-content.vue:
private: "Dieser Post ist privat" private: "Dieser Beitrag ist privat"
deleted: "Dieser Beitrag wurde entfernt" deleted: "Dieser Beitrag wurde entfernt"
poll: "Umfrage" poll: "Umfrage"
mobile/views/components/ui.nav.vue: mobile/views/components/ui.nav.vue:
@ -633,7 +680,7 @@ mobile/views/pages/home.vue:
global: "Global" global: "Global"
mobile/views/pages/widgets.vue: mobile/views/pages/widgets.vue:
add-widget: "Hinzufügen" add-widget: "Hinzufügen"
customization-tips: "Anpassung-Tipps" customization-tips: "Anpassungs-Tipps"
mobile/views/pages/widgets/activity.vue: mobile/views/pages/widgets/activity.vue:
activity: "Aktivität" activity: "Aktivität"
mobile/views/pages/note.vue: mobile/views/pages/note.vue:
@ -654,4 +701,50 @@ deck:
list: "Listen" list: "Listen"
rename: "Umbenennen" rename: "Umbenennen"
deck/deck.user-column.vue: deck/deck.user-column.vue:
following: "Folgen"
followers: "Folgende"
images: "Bilder"
activity: "Aktivität" activity: "Aktivität"
timeline: "Zeitleiste"
pinned-notes: "Angeheftete Beiträge"
docs:
edit-this-page-on-github: "Hast Du einen Fehler gefunden oder Lust, diese Dokumentation zu verbessern?"
edit-this-page-on-github-link: "Seite auf GitHub bearbeiten!"
api:
entities:
properties: "Eigenschaften"
endpoints:
params: "Parameter"
no-params: "Keine Parameter."
res: "Antwort"
require-credential: "Dieser Endpunkt erfordert eine Authentifizierung."
require-permission: "Dieser Endpunkt erfordert die {permission} Berechtigung."
has-limit: "Es gibt eine Ratenbegrenzung."
duration-limit: "Es sind maximal {max} Anfragen pro {duration} Millisekunden möglich."
min-interval-limit: "Es ist nur eine Anfrage alle {interval} Millisekunden möglich."
show-src: "Quellcode anzeigen."
show-src-link: "Quellcode auf GitHub anzeigen"
generated: "Dieses Dokument wird automatisch anhand der API-Definition generiert."
props:
name: "Name"
type: "Typ"
description: "Beschreibung"
dev/views/index.vue:
manage-apps: "Anwendungen verwalten"
dev/views/apps.vue:
manage-apps: "Anwendungen verwalten"
create-app: "Anwendung erstellen"
app-missing: "Keine Anwendungen"
dev/views/new-app.vue:
create-app: "Erstelle Anwendung"
app-name: "Name der Anwendung"
app-name-desc: "Der Name der Anwendung"
app-name-ex: "z.B. Misskey für iOS"
app-overview: "Beschreibung der Anwendung"
app-desc: "Eine kurze Beschreibung oder Einführung der Anwendung."
app-desc-ex: "z.B. Ein iOS-Client für Misskey."
callback-url: "Callback-URL (optional)"
callback-url-desc: "Die URL, auf die nach erfolgreicher Authentifizierung umgeleitet werden soll."
authority: "Berechtigungen"
authority-desc: "Nur die hier eingetragenen Berechtigungen, werden per API zur Verfügung stehen."
authority-warning: "Dies kann auch nach dem erstellen der Anwendung geändert werden, allerdings werden dann alle bisher generierten Token ungültig."

View File

@ -74,10 +74,26 @@ common:
favorites: "お気に入り" favorites: "お気に入り"
permissions: permissions:
'read:account': "アカウントの情報を見る" "read:account": "アカウントの情報を見る"
'write:account': "アカウントの情報を変更する" "write:account": "アカウントの情報を変更する"
'read:drive': "ドライブを見る" "read:blocks": "ブロックを見る"
'write:drive': "ドライブを操作する" "write:blocks": "ブロックを操作する"
"read:drive": "ドライブを見る"
"write:drive": "ドライブを操作する"
"read:favorites": "お気に入りを見る"
"write:favorites": "お気に入りを操作する"
"read:following": "フォローの情報を見る"
"write:following": "フォロー・フォロー解除する"
"read:messaging": "トークを見る"
"write:messaging": "トークを操作する"
"read:mutes": "ミュートを見る"
"write:mutes": "ミュートを操作する"
"write:notes": "投稿を作成・削除する"
"read:notifications": "通知を見る"
"write:notifications": "通知を操作する"
"read:reactions": "リアクションを見る"
"write:reactions": "リアクションを操作する"
"write:votes": "投票する"
empty-timeline-info: empty-timeline-info:
follow-users-to-make-your-timeline: "ユーザーをフォローすると投稿がタイムラインに表示されます。" follow-users-to-make-your-timeline: "ユーザーをフォローすると投稿がタイムラインに表示されます。"
@ -297,6 +313,7 @@ common:
nav: "ナビゲーション" nav: "ナビゲーション"
tips: "ヒント" tips: "ヒント"
hashtags: "ハッシュタグ" hashtags: "ハッシュタグ"
queue: "キュー"
dev: "アプリの作成に失敗しました。再度お試しください。" dev: "アプリの作成に失敗しました。再度お試しください。"
ai-chan-kawaii: "藍ちゃかわいい" ai-chan-kawaii: "藍ちゃかわいい"
@ -1533,6 +1550,7 @@ desktop/views/widgets/polls.vue:
desktop/views/widgets/post-form.vue: desktop/views/widgets/post-form.vue:
title: "投稿" title: "投稿"
note: "投稿" note: "投稿"
something-happened: "何らかの事情で投稿できませんでした。"
desktop/views/widgets/profile.vue: desktop/views/widgets/profile.vue:
update-banner: "クリックでバナー編集" update-banner: "クリックでバナー編集"
@ -1625,7 +1643,7 @@ mobile/views/components/post-form.vue:
quote-placeholder: "この投稿を引用... (オプション)" quote-placeholder: "この投稿を引用... (オプション)"
reply-placeholder: "この投稿への返信..." reply-placeholder: "この投稿への返信..."
cw-placeholder: "内容への注釈 (オプション)" cw-placeholder: "内容への注釈 (オプション)"
location-alert: "お使いの端末は位置情報に対応していません" geolocation-alert: "お使いの端末は位置情報に対応していません"
error: "エラー" error: "エラー"
username-prompt: "ユーザー名を入力してください" username-prompt: "ユーザー名を入力してください"
@ -1804,14 +1822,17 @@ dev/views/apps.vue:
app-missing: "アプリなし" app-missing: "アプリなし"
dev/views/new-app.vue: dev/views/new-app.vue:
new-app: "新しいアプリケーション"
new-app-info: "アプリケーションはAPIからでも作成できます。 (app/create)"
create-app: "アプリケーションの作成" create-app: "アプリケーションの作成"
app-name: "アプリケーション名" app-name: "アプリケーション名"
app-name-placeholder: "ex) Misskey for iOS"
app-name-desc: "あなたのアプリの名称。" app-name-desc: "あなたのアプリの名称。"
app-name-ex: "ex) Misskey for iOS"
app-overview: "アプリの概要" app-overview: "アプリの概要"
app-desc: "あなたのアプリの簡単な説明や紹介。" app-overview-placeholder: " ex) Misskey iOSクライアント。"
app-desc-ex: "ex) Misskey iOSクライアント。" app-overview-desc: "あなたのアプリの簡単な説明や紹介。"
callback-url: "コールバックURL (オプション)" callback-url: "コールバックURL (オプション)"
callback-url-placeholder: "ex) https://your.app.example.com/callback.php"
callback-url-desc: "ユーザーが認証フォームで認証した際にリダイレクトするURLを設定できます。" callback-url-desc: "ユーザーが認証フォームで認証した際にリダイレクトするURLを設定できます。"
authority: "権限" authority: "権限"
authority-desc: "ここで要求した機能だけがAPIからアクセスできます。" authority-desc: "ここで要求した機能だけがAPIからアクセスできます。"

View File

@ -966,6 +966,7 @@ common/views/components/password-settings.vue:
changed: "비밀번호를 변경하였습니다" changed: "비밀번호를 변경하였습니다"
failed: "비밀번호 변경을 실패하였습니다." failed: "비밀번호 변경을 실패하였습니다."
common/views/components/post-form-attaches.vue: common/views/components/post-form-attaches.vue:
attach-cancel: "첨부 취소"
mark-as-sensitive: "열람주의로 설정" mark-as-sensitive: "열람주의로 설정"
unmark-as-sensitive: "열람주의 해제" unmark-as-sensitive: "열람주의 해제"
desktop/views/components/sub-note-content.vue: desktop/views/components/sub-note-content.vue:

View File

@ -1,7 +1,7 @@
{ {
"name": "misskey", "name": "misskey",
"author": "syuilo <i@syuilo.com>", "author": "syuilo <i@syuilo.com>",
"version": "11.0.1", "version": "11.1.4",
"codename": "daybreak", "codename": "daybreak",
"repository": { "repository": {
"type": "git", "type": "git",
@ -64,8 +64,6 @@
"@types/lolex": "3.1.1", "@types/lolex": "3.1.1",
"@types/minio": "7.0.1", "@types/minio": "7.0.1",
"@types/mocha": "5.2.6", "@types/mocha": "5.2.6",
"@types/mongodb": "3.1.22",
"@types/monk": "6.0.0",
"@types/node": "11.13.4", "@types/node": "11.13.4",
"@types/nodemailer": "4.6.7", "@types/nodemailer": "4.6.7",
"@types/nprogress": "0.0.29", "@types/nprogress": "0.0.29",
@ -75,6 +73,7 @@
"@types/portscanner": "2.1.0", "@types/portscanner": "2.1.0",
"@types/pug": "2.0.4", "@types/pug": "2.0.4",
"@types/qrcode": "1.3.2", "@types/qrcode": "1.3.2",
"@types/random-seed": "0.3.3",
"@types/ratelimiter": "2.1.28", "@types/ratelimiter": "2.1.28",
"@types/redis": "2.8.12", "@types/redis": "2.8.12",
"@types/rename": "1.0.1", "@types/rename": "1.0.1",
@ -166,8 +165,6 @@
"mocha": "6.1.3", "mocha": "6.1.3",
"moji": "0.5.1", "moji": "0.5.1",
"moment": "2.24.0", "moment": "2.24.0",
"mongodb": "3.2.3",
"monk": "6.0.6",
"ms": "2.1.1", "ms": "2.1.1",
"nested-property": "0.0.7", "nested-property": "0.0.7",
"node-fetch": "2.3.0", "node-fetch": "2.3.0",
@ -187,7 +184,9 @@
"promise-sequential": "1.1.1", "promise-sequential": "1.1.1",
"pug": "2.0.3", "pug": "2.0.3",
"punycode": "2.1.1", "punycode": "2.1.1",
"pureimage": "0.1.6",
"qrcode": "1.3.3", "qrcode": "1.3.3",
"random-seed": "0.3.0",
"randomcolor": "0.5.4", "randomcolor": "0.5.4",
"ratelimiter": "3.3.0", "ratelimiter": "3.3.0",
"recaptcha-promise": "0.1.3", "recaptcha-promise": "0.1.3",

View File

@ -242,7 +242,7 @@ export default Vue.extend({
> div:nth-child(1) > div:nth-child(1)
> .thumbnail > .thumbnail
display block display flex
width 64px width 64px
height 64px height 64px
background-size cover background-size cover

View File

@ -195,7 +195,7 @@ export default Vue.extend({
}, },
created() { created() {
this.$root.getMeta().then(meta => { this.$root.getMeta(true).then(meta => {
this.maintainerName = meta.maintainerName; this.maintainerName = meta.maintainerName;
this.maintainerEmail = meta.maintainerEmail; this.maintainerEmail = meta.maintainerEmail;
this.disableRegistration = meta.disableRegistration; this.disableRegistration = meta.disableRegistration;

View File

@ -134,7 +134,7 @@ export default (opts: Opts = {}) => ({
}, },
reactDirectly(reaction) { reactDirectly(reaction) {
(this.$root.api('notes/reactions/create', { this.$root.api('notes/reactions/create', {
noteId: this.appearNote.id, noteId: this.appearNote.id,
reaction: reaction reaction: reaction
}); });

View File

@ -36,7 +36,9 @@ export default Vue.extend({
return this.host === localHost ? `@${this.username}` : `@${this.username}@${toUnicode(this.host)}`; return this.host === localHost ? `@${this.username}` : `@${this.username}@${toUnicode(this.host)}`;
}, },
isMe(): boolean { isMe(): boolean {
return this.$store.getters.isSignedIn && this.canonical.toLowerCase() === `@${this.$store.state.i.username}@${toUnicode(localHost)}`.toLowerCase(); return this.$store.getters.isSignedIn && (
`@${this.username}@${toUnicode(this.host)}` === `@${this.$store.state.i.username}@${toUnicode(localHost)}`.toLowerCase()
);
} }
}, },
methods: { methods: {

View File

@ -85,7 +85,10 @@ export default Vue.extend({
} }
} else { } else {
if (items[0].kind == 'file') { if (items[0].kind == 'file') {
alert(this.$t('only-one-file-attached')); this.$root.dialog({
type: 'error',
text: this.$t('only-one-file-attached')
});
} }
} }
}, },
@ -107,7 +110,10 @@ export default Vue.extend({
return; return;
} else if (e.dataTransfer.files.length > 1) { } else if (e.dataTransfer.files.length > 1) {
e.preventDefault(); e.preventDefault();
alert(this.$t('only-one-file-attached')); this.$root.dialog({
type: 'error',
text: this.$t('only-one-file-attached')
});
return; return;
} }

View File

@ -125,7 +125,10 @@ export default Vue.extend({
this.form.upload(e.dataTransfer.files[0]); this.form.upload(e.dataTransfer.files[0]);
return; return;
} else if (e.dataTransfer.files.length > 1) { } else if (e.dataTransfer.files.length > 1) {
alert(this.$t('only-one-file-attached')); this.$root.dialog({
type: 'error',
text: this.$t('only-one-file-attached')
});
return; return;
} }

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="skeikyzd" v-show="files.length != 0"> <div class="skeikyzd" v-show="files.length != 0">
<x-draggable class="files" :list="files" :options="{ animation: 150 }"> <x-draggable class="files" :list="files" animation="150">
<div v-for="file in files" :key="file.id" @click="showFileMenu(file, $event)" @contextmenu.prevent="showFileMenu(file, $event)"> <div v-for="file in files" :key="file.id" @click="showFileMenu(file, $event)" @contextmenu.prevent="showFileMenu(file, $event)">
<x-file-thumbnail :data-id="file.id" class="thumbnail" :file="file" fit="cover"/> <x-file-thumbnail :data-id="file.id" class="thumbnail" :file="file" fit="cover"/>
<img class="remove" @click.stop="detachMedia(file.id)" src="/assets/desktop/remove.png" :title="$t('attach-cancel')" alt=""/> <img class="remove" @click.stop="detachMedia(file.id)" src="/assets/desktop/remove.png" :title="$t('attach-cancel')" alt=""/>

View File

@ -84,7 +84,7 @@
<ui-info v-else warn>{{ $t('email-not-verified') }}</ui-info> <ui-info v-else warn>{{ $t('email-not-verified') }}</ui-info>
</template> </template>
<ui-input v-model="email" type="email"><span>{{ $t('email-address') }}</span></ui-input> <ui-input v-model="email" type="email"><span>{{ $t('email-address') }}</span></ui-input>
<ui-button @click="updateEmail()"><fa :icon="faSave"/> {{ $t('save') }}</ui-button> <ui-button @click="updateEmail()" :disabled="email === $store.state.i.email"><fa :icon="faSave"/> {{ $t('save') }}</ui-button>
</div> </div>
</section> </section>

View File

@ -542,8 +542,8 @@ export default Vue.extend({
this.latestVersion = newer; this.latestVersion = newer;
if (newer == null) { if (newer == null) {
this.$root.dialog({ this.$root.dialog({
title: this.$t('no-updates'), title: this.$t('@._settings.no-updates'),
text: this.$t('no-updates-desc') text: this.$t('@._settings.no-updates-desc')
}); });
} else { } else {
this.$root.dialog({ this.$root.dialog({

View File

@ -79,7 +79,10 @@ export default Vue.extend({
localStorage.setItem('i', res.i); localStorage.setItem('i', res.i);
location.reload(); location.reload();
}).catch(() => { }).catch(() => {
alert(this.$t('login-failed')); this.$root.dialog({
type: 'error',
text: this.$t('login-failed')
});
this.signing = false; this.signing = false;
}); });
} }

View File

@ -153,7 +153,10 @@ export default Vue.extend({
location.href = '/'; location.href = '/';
}); });
}).catch(() => { }).catch(() => {
alert(this.$t('some-error')); this.$root.dialog({
type: 'error',
text: this.$t('some-error')
});
if (this.meta.enableRecaptcha) { if (this.meta.enableRecaptcha) {
(window as any).grecaptcha.reset(); (window as any).grecaptcha.reset();

View File

@ -118,7 +118,6 @@ export default Vue.extend({
fileType: image, fileType: image,
excludeNsfw: !this.$store.state.device.alwaysShowNsfw, excludeNsfw: !this.$store.state.device.alwaysShowNsfw,
limit: 9, limit: 9,
untilDate: new Date().getTime() + 1000 * 86400 * 365
}).then(notes => { }).then(notes => {
for (const note of notes) { for (const note of notes) {
for (const file of note.files) { for (const file of note.files) {

View File

@ -21,7 +21,7 @@
<option value="users">{{ $t('@.widgets.users') }}</option> <option value="users">{{ $t('@.widgets.users') }}</option>
<option value="polls">{{ $t('@.widgets.polls') }}</option> <option value="polls">{{ $t('@.widgets.polls') }}</option>
<option value="post-form">{{ $t('@.widgets.post-form') }}</option> <option value="post-form">{{ $t('@.widgets.post-form') }}</option>
<option value="messaging">{{ $t('@.widgets.messaging') }}</option> <option value="messaging">{{ $t('@.messaging') }}</option>
<option value="memo">{{ $t('@.widgets.memo') }}</option> <option value="memo">{{ $t('@.widgets.memo') }}</option>
<option value="hashtags">{{ $t('@.widgets.hashtags') }}</option> <option value="hashtags">{{ $t('@.widgets.hashtags') }}</option>
<option value="posts-monitor">{{ $t('@.widgets.posts-monitor') }}</option> <option value="posts-monitor">{{ $t('@.widgets.posts-monitor') }}</option>
@ -33,7 +33,7 @@
</header> </header>
<x-draggable <x-draggable
:list="column.widgets" :list="column.widgets"
:options="{ animation: 150 }" animation="150"
@sort="onWidgetSort" @sort="onWidgetSort"
> >
<div v-for="widget in column.widgets" class="customize-container" :key="widget.id" @contextmenu.stop.prevent="widgetFunc(widget.id)"> <div v-for="widget in column.widgets" class="customize-container" :key="widget.id" @contextmenu.stop.prevent="widgetFunc(widget.id)">

View File

@ -188,7 +188,10 @@ export default define({
}).then(data => { }).then(data => {
this.clear(); this.clear();
}).catch(err => { }).catch(err => {
alert('Something happened'); this.$root.dialog({
type: 'error',
text: this.$t('something-happened')
});
}).then(() => { }).then(() => {
this.posting = false; this.posting = false;
this.$nextTick(() => { this.$nextTick(() => {

View File

@ -161,7 +161,10 @@ export default Vue.extend({
}); });
break; break;
default: default:
alert(this.$t('unhandled-error')); this.$root.dialog({
type: 'error',
text: this.$t('unhandled-error')
});
} }
}); });
} }

View File

@ -320,7 +320,10 @@ export default Vue.extend({
}); });
break; break;
default: default:
alert(this.$t('unhandled-error')); this.$root.dialog({
type: 'error',
text: this.$t('unhandled-error')
});
} }
}); });
} }

View File

@ -364,7 +364,10 @@ export default Vue.extend({
setGeo() { setGeo() {
if (navigator.geolocation == null) { if (navigator.geolocation == null) {
alert(this.$t('geolocation-alert')); this.$root.dialog({
type: 'warning',
text: this.$t('geolocation-alert')
});
return; return;
} }
@ -372,7 +375,11 @@ export default Vue.extend({
this.geo = pos.coords; this.geo = pos.coords;
this.$emit('geo-attached', this.geo); this.$emit('geo-attached', this.geo);
}, err => { }, err => {
alert(`%i18n:@error%: ${err.message}`); this.$root.dialog({
type: 'error',
title: this.$t('error')
text: err.message
});
}, { }, {
enableHighAccuracy: true enableHighAccuracy: true
}); });

View File

@ -34,7 +34,7 @@
<button @click="addWidget">{{ $t('add') }}</button> <button @click="addWidget">{{ $t('add') }}</button>
</div> </div>
<div class="trash"> <div class="trash">
<x-draggable v-model="trash" :options="{ group: 'x' }" @add="onTrash"></x-draggable> <x-draggable v-model="trash" group="x" @add="onTrash"></x-draggable>
<p>{{ $t('@.trash') }}</p> <p>{{ $t('@.trash') }}</p>
</div> </div>
</div> </div>
@ -45,7 +45,8 @@
:list="widgets[place]" :list="widgets[place]"
:class="place" :class="place"
:data-place="place" :data-place="place"
:options="{ group: 'x', animation: 150 }" group="x"
animation="150"
@sort="onWidgetSort" @sort="onWidgetSort"
:key="place" :key="place"
> >

View File

@ -88,8 +88,6 @@ export default Vue.extend({
} else if (this.src == 'tag') { } else if (this.src == 'tag') {
this.tagTl = this.$store.state.device.tl.arg; this.tagTl = this.$store.state.device.tl.arg;
} }
} else if (this.$store.state.i.followingCount == 0) {
this.src = 'hybrid';
} }
}, },

View File

@ -46,7 +46,6 @@ export default Vue.extend({
fileType: image, fileType: image,
excludeNsfw: !this.$store.state.device.alwaysShowNsfw, excludeNsfw: !this.$store.state.device.alwaysShowNsfw,
limit: 9, limit: 9,
untilDate: new Date().getTime() + 1000 * 86400 * 365
}).then(notes => { }).then(notes => {
for (const note of notes) { for (const note of notes) {
for (const file of note.files) { for (const file of note.files) {

View File

@ -34,7 +34,7 @@ export default Vue.extend({
document.title = title; document.title = title;
}, },
onOpenFolder(folder) { onOpenFolder(folder) {
const title = folder.name + ' | %i18n:@title%'; const title = `${folder.name} | ${this.$t('title')}`;
// Rewrite URL // Rewrite URL
history.pushState(null, title, `/i/drive/folder/${folder.id}`); history.pushState(null, title, `/i/drive/folder/${folder.id}`);

View File

@ -1,6 +1,6 @@
<template> <template>
<mk-ui> <mk-ui>
<b-card :header="$t('header')"> <b-card :header="$t('manage-apps')">
<b-button to="/app/new" variant="primary">{{ $t('create-app') }}</b-button> <b-button to="/app/new" variant="primary">{{ $t('create-app') }}</b-button>
<hr> <hr>
<div class="apps"> <div class="apps">

View File

@ -1,35 +1,22 @@
<template> <template>
<mk-ui> <mk-ui>
<b-card :header="$t('header')"> <b-card :header="$t('new-app')">
<b-alert show variant="info"><fa icon="info-circle"/> {{ $t('new-app-info') }}</b-alert>
<b-form @submit.prevent="onSubmit" autocomplete="off"> <b-form @submit.prevent="onSubmit" autocomplete="off">
<b-form-group :label="$t('app-name')" :description="$t('description')"> <b-form-group :label="$t('app-name')" :description="$t('app-name-desc')">
<b-form-input v-model="name" type="text" :placeholder="$t('placeholder')" autocomplete="off" required/> <b-form-input v-model="name" type="text" :placeholder="$t('app-name-placeholder')" autocomplete="off" required/>
</b-form-group> </b-form-group>
<b-form-group :label="$t('app-overview')" :description="$t('description')"> <b-form-group :label="$t('app-overview')" :description="$t('app-overview-desc')">
<b-textarea v-model="description" :placeholder="$t('placeholder')" autocomplete="off" required></b-textarea> <b-textarea v-model="description" :placeholder="$t('app-overview-placeholder')" autocomplete="off" required></b-textarea>
</b-form-group> </b-form-group>
<b-form-group :label="$t('callback-url')" :description="$t('description')"> <b-form-group :label="$t('callback-url')" :description="$t('callback-url-desc')">
<b-input v-model="cb" type="url" placeholder="ex) https://your.app.example.com/callback.php" autocomplete="off"/> <b-input v-model="cb" type="url" :placeholder="$t('callback-url-placeholder')" autocomplete="off"/>
</b-form-group> </b-form-group>
<b-card :header="$t('header')"> <b-card :header="$t('authority')">
<b-form-group :description="$t('description')"> <b-form-group :description="$t('authority-desc')">
<b-alert show variant="warning"><fa icon="exclamation-triangle"/> {{ $t('authority-warning') }}</b-alert> <b-alert show variant="warning"><fa icon="exclamation-triangle"/> {{ $t('authority-warning') }}</b-alert>
<b-form-checkbox-group v-model="permission" stacked> <b-form-checkbox-group v-model="permission" stacked>
<b-form-checkbox value="read:account">{{ $t('read:account') }}</b-form-checkbox> <b-form-checkbox v-for="v in permissionsList" :value="v" :key="v">{{ $t(`@.permissions.${v}`) }} ({{ v }})</b-form-checkbox>
<b-form-checkbox value="write:account">{{ $t('write:account') }}</b-form-checkbox>
<b-form-checkbox value="write:notes">{{ $t('write:notes') }}</b-form-checkbox>
<b-form-checkbox value="read:reactions">{{ $t('read:reactions') }}</b-form-checkbox>
<b-form-checkbox value="write:reactions">{{ $t('write:reactions') }}</b-form-checkbox>
<b-form-checkbox value="read:following">{{ $t('read:following') }}</b-form-checkbox>
<b-form-checkbox value="write:following">{{ $t('write:following') }}</b-form-checkbox>
<b-form-checkbox value="read:mutes">{{ $t('read:mutes') }}</b-form-checkbox>
<b-form-checkbox value="write:mutes">{{ $t('write:mutes') }}</b-form-checkbox>
<b-form-checkbox value="read:blocks">{{ $t('read:blocks') }}</b-form-checkbox>
<b-form-checkbox value="write:blocks">{{ $t('write:blocks') }}</b-form-checkbox>
<b-form-checkbox value="read:drive">{{ $t('read:drive') }}</b-form-checkbox>
<b-form-checkbox value="write:drive">{{ $t('write:drive') }}</b-form-checkbox>
<b-form-checkbox value="read:notifications">{{ $t('read:notifications') }}</b-form-checkbox>
<b-form-checkbox value="write:notifications">{{ $t('write:notifications') }}</b-form-checkbox>
</b-form-checkbox-group> </b-form-checkbox-group>
</b-form-group> </b-form-group>
</b-card> </b-card>
@ -43,6 +30,8 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../i18n'; import i18n from '../../i18n';
import { kinds } from '../../../../server/api/kinds';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('dev/views/new-app.vue'), i18n: i18n('dev/views/new-app.vue'),
data() { data() {
@ -51,7 +40,8 @@ export default Vue.extend({
description: '', description: '',
cb: '', cb: '',
nidState: null, nidState: null,
permission: [] permission: [],
permissionsList: kinds
}; };
}, },
methods: { methods: {

View File

@ -4,7 +4,7 @@ import { EventEmitter } from 'eventemitter3';
import * as uuid from 'uuid'; import * as uuid from 'uuid';
import initStore from './store'; import initStore from './store';
import { apiUrl, version } from './config'; import { apiUrl, version, locale } from './config';
import Progress from './common/scripts/loading'; import Progress from './common/scripts/loading';
import Err from './common/views/components/connect-failed.vue'; import Err from './common/views/components/connect-failed.vue';
@ -281,7 +281,7 @@ export default class MiOS extends EventEmitter {
// トークンが再生成されたとき // トークンが再生成されたとき
// このままではMisskeyが利用できないので強制的にサインアウトさせる // このままではMisskeyが利用できないので強制的にサインアウトさせる
main.on('myTokenRegenerated', () => { main.on('myTokenRegenerated', () => {
alert('%i18n:common.my-token-regenerated%'); alert(locale['common']['my-token-regenerated'])
this.signout(); this.signout();
}); });
} }

View File

@ -136,7 +136,10 @@ export default Vue.extend({
}, },
showCreatedAt() { showCreatedAt() {
alert(new Date(this.file.createdAt).toLocaleString()); this.$root.dialog({
type: 'info',
text: (new Date(this.file.createdAt)).toLocaleString()
});
} }
} }
}); });
@ -150,11 +153,13 @@ export default Vue.extend({
> .preview > .preview
width fit-content width fit-content
width -moz-fit-content
max-width 100% max-width 100%
margin 0 auto margin 0 auto
box-shadow 1px 1px 4px rgba(#000, 0.2) box-shadow 1px 1px 4px rgba(#000, 0.2)
overflow hidden overflow hidden
color var(--driveFileIcon) color var(--driveFileIcon)
justify-content center
> footer > footer
padding 8px 8px 0 8px padding 8px 8px 0 8px

View File

@ -283,14 +283,21 @@ export default Vue.extend({
setGeo() { setGeo() {
if (navigator.geolocation == null) { if (navigator.geolocation == null) {
alert(this.$t('location-alert')); this.$root.dialog({
type: 'warning',
text: this.$t('geolocation-alert')
});
return; return;
} }
navigator.geolocation.getCurrentPosition(pos => { navigator.geolocation.getCurrentPosition(pos => {
this.geo = pos.coords; this.geo = pos.coords;
}, err => { }, err => {
alert(`%i18n:@error%: ${err.message}`); this.$root.dialog({
type: 'error',
title: this.$t('error')
text: err.message
});
}, { }, {
enableHighAccuracy: true enableHighAccuracy: true
}); });

View File

@ -32,7 +32,7 @@ export default Vue.extend({
this.fetch(); this.fetch();
}, },
mounted() { mounted() {
document.title = `${this.$root.instanceName} | %i18n:@notifications%`; document.title = `${this.$root.instanceName} | ${this.$t('@.favorites')}`;
}, },
methods: { methods: {
fetch() { fetch() {

View File

@ -15,7 +15,7 @@ export default Vue.extend({
XReversi: () => import('../../../../common/views/components/games/reversi/reversi.vue').then(m => m.default) XReversi: () => import('../../../../common/views/components/games/reversi/reversi.vue').then(m => m.default)
}, },
mounted() { mounted() {
document.title = `${this.$root.instanceName} %i18n:@reversi%`; document.title = `${this.$root.instanceName} | ${this.$t('reversi')}`;
}, },
methods: { methods: {
nav(game, actualNav) { nav(game, actualNav) {

View File

@ -39,7 +39,7 @@
<span :data-active="src == 'messages'" @click="src = 'messages'"><fa :icon="['far', 'envelope']"/> {{ $t('messages') }}<i class="badge" v-if="$store.state.i.hasUnreadSpecifiedNotes"><fa icon="circle"/></i></span> <span :data-active="src == 'messages'" @click="src = 'messages'"><fa :icon="['far', 'envelope']"/> {{ $t('messages') }}<i class="badge" v-if="$store.state.i.hasUnreadSpecifiedNotes"><fa icon="circle"/></i></span>
<template v-if="lists"> <template v-if="lists">
<div class="hr" v-if="lists.length > 0"></div> <div class="hr" v-if="lists.length > 0"></div>
<span v-for="l in lists" :data-active="src == 'list' && list == l" @click="src = 'list'; list = l" :key="l.id"><fa icon="list"/> {{ l.title }}</span> <span v-for="l in lists" :data-active="src == 'list' && list == l" @click="src = 'list'; list = l" :key="l.id"><fa icon="list"/> {{ l.name }}</span>
</template> </template>
<div class="hr" v-if="$store.state.settings.tagTimelines && $store.state.settings.tagTimelines.length > 0"></div> <div class="hr" v-if="$store.state.settings.tagTimelines && $store.state.settings.tagTimelines.length > 0"></div>
<span v-for="tl in $store.state.settings.tagTimelines" :data-active="src == 'tag' && tagTl == tl" @click="src = 'tag'; tagTl = tl" :key="tl.id"><fa icon="hashtag"/> {{ tl.title }}</span> <span v-for="tl in $store.state.settings.tagTimelines" :data-active="src == 'tag' && tagTl == tl" @click="src = 'tag'; tagTl = tl" :key="tl.id"><fa icon="hashtag"/> {{ tl.title }}</span>
@ -130,8 +130,6 @@ export default Vue.extend({
} else if (this.src == 'tag') { } else if (this.src == 'tag') {
this.tagTl = this.$store.state.device.tl.arg; this.tagTl = this.$store.state.device.tl.arg;
} }
} else if (this.$store.state.i.followingCount == 0) {
this.src = 'hybrid';
} }
}, },

View File

@ -50,7 +50,7 @@ export default Vue.extend({
} }
}, },
mounted() { mounted() {
document.title = `%i18n:@search%: ${this.q} | ${this.$root.instanceName}`; document.title = `${this.$t('search')}: ${this.q} | ${this.$root.instanceName}`;
}, },
methods: { methods: {
inited() { inited() {

View File

@ -23,7 +23,6 @@ export default Vue.extend({
mounted() { mounted() {
this.$root.api('users/notes', { this.$root.api('users/notes', {
userId: this.user.id, userId: this.user.id,
untilDate: new Date().getTime() + 1000 * 86400 * 365
}).then(notes => { }).then(notes => {
this.notes = notes; this.notes = notes;
this.fetching = false; this.fetching = false;

View File

@ -37,7 +37,6 @@ export default Vue.extend({
fileType: image, fileType: image,
excludeNsfw: !this.$store.state.device.alwaysShowNsfw, excludeNsfw: !this.$store.state.device.alwaysShowNsfw,
limit: 9, limit: 9,
untilDate: new Date().getTime() + 1000 * 86400 * 365
}).then(notes => { }).then(notes => {
for (const note of notes) { for (const note of notes) {
for (const file of note.files) { for (const file of note.files) {

View File

@ -29,7 +29,8 @@
</header> </header>
<x-draggable <x-draggable
:list="widgets" :list="widgets"
:options="{ handle: '.handle', animation: 150 }" handle=".handle"
animation="150"
@sort="onWidgetSort" @sort="onWidgetSort"
> >
<div v-for="widget in widgets" class="customize-container" :key="widget.id"> <div v-for="widget in widgets" class="customize-container" :key="widget.id">
@ -106,7 +107,10 @@ export default Vue.extend({
methods: { methods: {
hint() { hint() {
alert(this.$t('widgets-hints')); this.$root.dialog({
type: 'info',
text: this.$t('widgets-hints')
});
}, },
widgetFunc(id) { widgetFunc(id) {

View File

@ -1,4 +1,4 @@
@charset 'utf-8' @charset "utf-8"
/* /*
::selection ::selection

View File

@ -130,10 +130,12 @@ Misskeyのストリームに接続しただけでは、まだリアルタイム
```json ```json
{ {
type: 'api', type: 'api',
id: 'xxxxxxxxxxxxxxxx', body: {
endpoint: 'notes/create', id: 'xxxxxxxxxxxxxxxx',
data: { endpoint: 'notes/create',
text: 'yee haw!' data: {
text: 'yee haw!'
}
} }
} }
``` ```

View File

@ -1,14 +1,21 @@
import { Meta } from '../models/entities/meta'; import { Meta } from '../models/entities/meta';
import { Metas } from '../models'; import { getConnection } from 'typeorm';
import { genId } from './gen-id';
export default async function(): Promise<Meta> { export default async function(): Promise<Meta> {
const meta = await Metas.findOne(); return await getConnection().transaction(async transactionalEntityManager => {
if (meta) { // バグでレコードが複数出来てしまっている可能性があるので新しいIDを優先する
return meta; const meta = await transactionalEntityManager.findOne(Meta, {
} else { order: {
return Metas.save({ id: 'DESC'
id: genId(), }
} as Meta); });
}
if (meta) {
return meta;
} else {
return await transactionalEntityManager.save(Meta, {
id: 'x'
}) as Meta;
}
});
} }

90
src/misc/gen-avatar.ts Normal file
View File

@ -0,0 +1,90 @@
/**
* Random avatar generator
*/
const p = require('pureimage');
import * as gen from 'random-seed';
import { WriteStream } from 'fs';
const size = 256; // px
const n = 5; // resolution
const margin = (size / n) / 1.5;
const colors = [
'#e57373',
'#F06292',
'#BA68C8',
'#9575CD',
'#7986CB',
'#64B5F6',
'#4FC3F7',
'#4DD0E1',
'#4DB6AC',
'#81C784',
'#8BC34A',
'#AFB42B',
'#F57F17',
'#FF5722',
'#795548',
'#455A64',
];
const bg = '#e9e9e9';
const actualSize = size - (margin * 2);
const cellSize = actualSize / n;
const sideN = Math.floor(n / 2);
/**
* Generate buffer of random avatar by seed
*/
export function genAvatar(seed: string, stream: WriteStream): Promise<void> {
const rand = gen.create(seed);
const canvas = p.make(size, size);
const ctx = canvas.getContext('2d');
ctx.fillStyle = bg;
ctx.beginPath();
ctx.fillRect(0, 0, size, size);
ctx.fillStyle = colors[rand(colors.length)];
// side bitmap (filled by false)
const side: boolean[][] = new Array(sideN);
for (let i = 0; i < side.length; i++) {
side[i] = new Array(n).fill(false);
}
// 1*n (filled by false)
const center: boolean[] = new Array(n).fill(false);
// tslint:disable-next-line:prefer-for-of
for (let x = 0; x < side.length; x++) {
for (let y = 0; y < side[x].length; y++) {
side[x][y] = rand(3) === 0;
}
}
for (let i = 0; i < center.length; i++) {
center[i] = rand(3) === 0;
}
// Draw
for (let x = 0; x < n; x++) {
for (let y = 0; y < n; y++) {
const isXCenter = x === ((n - 1) / 2);
if (isXCenter && !center[y]) continue;
const isLeftSide = x < ((n - 1) / 2);
if (isLeftSide && !side[x][y]) continue;
const isRightSide = x > ((n - 1) / 2);
if (isRightSide && !side[sideN - (x - sideN)][y]) continue;
const actualX = margin + (cellSize * x);
const actualY = margin + (cellSize * y);
ctx.beginPath();
ctx.fillRect(actualX, actualY, cellSize, cellSize);
}
}
return p.encodePNGToStream(canvas, stream);
}

View File

@ -1,4 +1,4 @@
import { Entity, PrimaryColumn, Index, Column, ManyToOne, JoinColumn, RelationId } from 'typeorm'; import { Entity, PrimaryColumn, Index, Column, ManyToOne, JoinColumn } from 'typeorm';
import { User } from './user'; import { User } from './user';
import { App } from './app'; import { App } from './app';
import { id } from '../id'; import { id } from '../id';
@ -25,7 +25,8 @@ export class AccessToken {
}) })
public hash: string; public hash: string;
@RelationId((self: AccessToken) => self.user) @Index()
@Column(id())
public userId: User['id']; public userId: User['id'];
@ManyToOne(type => User, { @ManyToOne(type => User, {

View File

@ -1,9 +1,11 @@
import { Entity, Column, PrimaryColumn } from 'typeorm'; import { Entity, Column, PrimaryColumn } from 'typeorm';
import { id } from '../id';
@Entity() @Entity()
export class Meta { export class Meta {
@PrimaryColumn(id()) @PrimaryColumn({
type: 'varchar',
length: 32
})
public id: string; public id: string;
@Column('varchar', { @Column('varchar', {

View File

@ -93,12 +93,12 @@ export class Note {
}) })
public localOnly: boolean; public localOnly: boolean;
@Column('integer', { @Column('smallint', {
default: 0 default: 0
}) })
public renoteCount: number; public renoteCount: number;
@Column('integer', { @Column('smallint', {
default: 0 default: 0
}) })
public repliesCount: number; public repliesCount: number;
@ -129,12 +129,14 @@ export class Note {
}) })
public score: number; public score: number;
@Index()
@Column({ @Column({
...id(), ...id(),
array: true, default: '{}' array: true, default: '{}'
}) })
public fileIds: DriveFile['id'][]; public fileIds: DriveFile['id'][];
@Index()
@Column('varchar', { @Column('varchar', {
length: 256, array: true, default: '{}' length: 256, array: true, default: '{}'
}) })

View File

@ -25,6 +25,7 @@ export class AppRepository extends Repository<App> {
return { return {
id: app.id, id: app.id,
name: app.name, name: app.name,
callbackUrl: app.callbackUrl,
...(opts.includeSecret ? { secret: app.secret } : {}), ...(opts.includeSecret ? { secret: app.secret } : {}),
...(me ? { ...(me ? {
isAuthorized: await AccessTokens.count({ isAuthorized: await AccessTokens.count({

View File

@ -14,7 +14,8 @@ export class AuthSessionRepository extends Repository<AuthSession> {
return await rap({ return await rap({
id: session.id, id: session.id,
app: Apps.pack(session.appId, me) app: Apps.pack(session.appId, me),
token: session.token
}); });
} }
} }

View File

@ -19,7 +19,7 @@ export class DriveFileRepository extends Repository<DriveFile> {
} }
public getPublicUrl(file: DriveFile, thumbnail = false): string | null { public getPublicUrl(file: DriveFile, thumbnail = false): string | null {
return thumbnail ? (file.thumbnailUrl || file.webpublicUrl || null) : (file.webpublicUrl || file.thumbnailUrl || file.url); return thumbnail ? (file.thumbnailUrl || file.webpublicUrl || null) : (file.webpublicUrl || file.url);
} }
public async clacDriveUsageOf(user: User['id'] | User): Promise<number> { public async clacDriveUsageOf(user: User['id'] | User): Promise<number> {

View File

@ -103,7 +103,7 @@ export class NoteRepository extends Repository<Note> {
const host = note.userHost; const host = note.userHost;
async function populatePoll() { async function populatePoll() {
const poll = await Polls.findOne({ noteId: note.id }).then(ensure); const poll = await Polls.findOne(note.id).then(ensure);
const choices = poll.choices.map(c => ({ const choices = poll.choices.map(c => ({
text: c, text: c,
votes: poll.votes[poll.choices.indexOf(c)], votes: poll.votes[poll.choices.indexOf(c)],

View File

@ -3,6 +3,7 @@ import { User, ILocalUser, IRemoteUser } from '../entities/user';
import { Emojis, Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles } from '..'; import { Emojis, Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles } from '..';
import rap from '@prezzemolo/rap'; import rap from '@prezzemolo/rap';
import { ensure } from '../../prelude/ensure'; import { ensure } from '../../prelude/ensure';
import config from '../../config';
@EntityRepository(User) @EntityRepository(User)
export class UserRepository extends Repository<User> { export class UserRepository extends Repository<User> {
@ -83,17 +84,19 @@ export class UserRepository extends Repository<User> {
const pins = opts.detail ? await UserNotePinings.find({ userId: user.id }) : []; const pins = opts.detail ? await UserNotePinings.find({ userId: user.id }) : [];
const profile = opts.detail ? await UserProfiles.findOne({ userId: user.id }).then(ensure) : null; const profile = opts.detail ? await UserProfiles.findOne({ userId: user.id }).then(ensure) : null;
const falsy = opts.detail ? false : undefined;
return await rap({ return await rap({
id: user.id, id: user.id,
name: user.name, name: user.name,
username: user.username, username: user.username,
host: user.host, host: user.host,
avatarUrl: user.avatarUrl, avatarUrl: user.avatarUrl ? user.avatarUrl : config.url + '/avatar/' + user.id,
avatarColor: user.avatarColor, avatarColor: user.avatarColor,
isAdmin: user.isAdmin || undefined, isAdmin: user.isAdmin || falsy,
isBot: user.isBot || undefined, isBot: user.isBot || falsy,
isCat: user.isCat || undefined, isCat: user.isCat || falsy,
isVerified: user.isVerified || undefined, isVerified: user.isVerified || falsy,
// カスタム絵文字添付 // カスタム絵文字添付
emojis: user.emojis.length > 0 ? Emojis.find({ emojis: user.emojis.length > 0 ? Emojis.find({
@ -122,6 +125,7 @@ export class UserRepository extends Repository<User> {
bannerUrl: user.bannerUrl, bannerUrl: user.bannerUrl,
bannerColor: user.bannerColor, bannerColor: user.bannerColor,
isLocked: user.isLocked, isLocked: user.isLocked,
isModerator: user.isModerator || falsy,
description: profile!.description, description: profile!.description,
location: profile!.location, location: profile!.location,
birthday: profile!.birthday, birthday: profile!.birthday,

View File

@ -115,3 +115,8 @@ export function cumulativeSum(xs: number[]): number[] {
for (let i = 1; i < ys.length; i++) ys[i] += ys[i - 1]; for (let i = 1; i < ys.length; i++) ys[i] += ys[i - 1];
return ys; return ys;
} }
// Object.fromEntries()
export function fromEntries(xs: [string, any][]): { [x: string]: any; } {
return xs.reduce((obj, [k, v]) => Object.assign(obj, { [k]: v }), {} as { [x: string]: any; });
}

View File

@ -31,7 +31,9 @@ export default async (token: string): Promise<[User | null | undefined, App | nu
.findOne(accessToken.appId); .findOne(accessToken.appId);
const user = await Users const user = await Users
.findOne(accessToken.userId); .findOne({
id: accessToken.userId // findOne(accessToken.userId) のように書かないのは後方互換性のため
});
return [user, app]; return [user, app];
} }

View File

@ -59,7 +59,7 @@ export default async (endpoint: string, user: User | null | undefined, app: App
}); });
} }
if (ep.meta.requireCredential && ep.meta.limit) { if (ep.meta.requireCredential && ep.meta.limit && !user!.isAdmin && !user!.isModerator) {
// Rate limit // Rate limit
await limiter(ep, user!).catch(e => { await limiter(ep, user!).catch(e => {
throw new ApiError({ throw new ApiError({

View File

@ -1,3 +1,3 @@
import rndstr from 'rndstr'; import rndstr from 'rndstr';
export default () => `0${rndstr('a-zA-Z0-9', 15)}`; export default () => rndstr('a-zA-Z0-9', 16);

View File

@ -1 +1 @@
export default (token: string) => token.startsWith('0'); export default (token: string) => token.length === 16;

View File

@ -1,6 +1,6 @@
import $ from 'cafy'; import $ from 'cafy';
import define from '../../define'; import define from '../../define';
import { Metas } from '../../../../models'; import { getConnection } from 'typeorm';
import { Meta } from '../../../../models/entities/meta'; import { Meta } from '../../../../models/entities/meta';
export const meta = { export const meta = {
@ -505,11 +505,17 @@ export default define(meta, async (ps) => {
set.swPrivateKey = ps.swPrivateKey; set.swPrivateKey = ps.swPrivateKey;
} }
const meta = await Metas.findOne(); await getConnection().transaction(async transactionalEntityManager => {
const meta = await transactionalEntityManager.findOne(Meta, {
order: {
id: 'DESC'
}
});
if (meta) { if (meta) {
await Metas.update(meta.id, set); await transactionalEntityManager.update(Meta, meta.id, set);
} else { } else {
await Metas.save(set); await transactionalEntityManager.save(Meta, set);
} }
});
}); });

View File

@ -3,30 +3,75 @@ import $ from 'cafy';
import define from '../../define'; import define from '../../define';
import { Apps } from '../../../../models'; import { Apps } from '../../../../models';
import { genId } from '../../../../misc/gen-id'; import { genId } from '../../../../misc/gen-id';
import { unique } from '../../../../prelude/array';
export const meta = { export const meta = {
tags: ['app'], tags: ['app'],
requireCredential: false, requireCredential: false,
desc: {
'ja-JP': 'アプリを作成します。',
'en-US': 'Create a application.'
},
params: { params: {
name: { name: {
validator: $.str validator: $.str,
desc: {
'ja-JP': 'アプリの名前',
'en-US': 'Name of application'
}
}, },
description: { description: {
validator: $.str validator: $.str,
desc: {
'ja-JP': 'アプリの説明',
'en-US': 'Description of application'
}
}, },
permission: { permission: {
validator: $.arr($.str).unique() validator: $.arr($.str).unique(),
desc: {
'ja-JP': 'このアプリに割り当てる権限(権限については"Permissions"を参照)',
'en-US': 'Permissions assigned to this app (see "Permissions" for the permissions)'
}
}, },
// TODO: Check it is valid url // TODO: Check it is valid url
callbackUrl: { callbackUrl: {
validator: $.optional.nullable.str, validator: $.optional.nullable.str,
default: null as any default: null as any,
desc: {
'ja-JP': 'アプリ認証時にコールバックするURL',
'en-US': 'URL to call back at app authentication'
}
}, },
},
res: {
type: 'object',
properties: {
id: {
type: 'string',
description: 'アプリケーションのID'
},
name: {
type: 'string',
description: 'アプリケーションの名前'
},
callbackUrl: {
type: 'string',
nullable: true,
description: 'コールバックするURL'
},
secret: {
type: 'string',
description: 'アプリケーションのシークレットキー'
}
}
} }
}; };
@ -34,6 +79,9 @@ export default define(meta, async (ps, user) => {
// Generate secret // Generate secret
const secret = rndstr('a-zA-Z0-9', 32); const secret = rndstr('a-zA-Z0-9', 32);
// for backward compatibility
const permission = unique(ps.permission.map(v => v.replace(/^(.+)(\/|-)(read|write)$/, '$3:$1')));
// Create account // Create account
const app = await Apps.save({ const app = await Apps.save({
id: genId(), id: genId(),
@ -41,7 +89,7 @@ export default define(meta, async (ps, user) => {
userId: user ? user.id : null, userId: user ? user.id : null,
name: ps.name, name: ps.name,
description: ps.description, description: ps.description,
permission: ps.permission, permission,
callbackUrl: ps.callbackUrl, callbackUrl: ps.callbackUrl,
secret: secret secret: secret
}); });

View File

@ -39,7 +39,7 @@ export default define(meta, async (ps, user) => {
} }
// Generate access token // Generate access token
const accessToken = '1' + rndstr('a-zA-Z0-9', 15); const accessToken = rndstr('a-zA-Z0-9', 32);
// Fetch exist access token // Fetch exist access token
const exist = await AccessTokens.findOne({ const exist = await AccessTokens.findOne({

View File

@ -10,6 +10,11 @@ export const meta = {
tags: ['auth'], tags: ['auth'],
requireCredential: false, requireCredential: false,
desc: {
'ja-JP': 'アプリを認証するためのトークンを作成します。',
'en-US': 'Generate a token for authorize application.'
},
params: { params: {
appSecret: { appSecret: {

View File

@ -5,7 +5,7 @@ import create from '../../../../services/blocking/create';
import define from '../../define'; import define from '../../define';
import { ApiError } from '../../error'; import { ApiError } from '../../error';
import { getUser } from '../../common/getters'; import { getUser } from '../../common/getters';
import { Blockings, NoteWatchings } from '../../../../models'; import { Blockings, NoteWatchings, Users } from '../../../../models';
export const meta = { export const meta = {
stability: 'stable', stability: 'stable',
@ -89,5 +89,5 @@ export default define(meta, async (ps, user) => {
noteUserId: blockee.id noteUserId: blockee.id
}); });
return await Blockings.pack(blockee.id, user); return await Users.pack(blockee.id, user);
}); });

View File

@ -5,7 +5,7 @@ import deleteBlocking from '../../../../services/blocking/delete';
import define from '../../define'; import define from '../../define';
import { ApiError } from '../../error'; import { ApiError } from '../../error';
import { getUser } from '../../common/getters'; import { getUser } from '../../common/getters';
import { Blockings } from '../../../../models'; import { Blockings, Users } from '../../../../models';
export const meta = { export const meta = {
stability: 'stable', stability: 'stable',
@ -84,5 +84,5 @@ export default define(meta, async (ps, user) => {
// Delete blocking // Delete blocking
await deleteBlocking(blocker, blockee); await deleteBlocking(blocker, blockee);
return await Blockings.pack(blockee.id, user); return await Users.pack(blockee.id, user);
}); });

View File

@ -48,6 +48,7 @@ export default define(meta, async (ps) => {
const hashtags = await Hashtags.createQueryBuilder('tag') const hashtags = await Hashtags.createQueryBuilder('tag')
.where('tag.name like :q', { q: ps.query.toLowerCase() + '%' }) .where('tag.name like :q', { q: ps.query.toLowerCase() + '%' })
.orderBy('tag.count', 'DESC') .orderBy('tag.count', 'DESC')
.groupBy('tag.id')
.take(ps.limit!) .take(ps.limit!)
.skip(ps.offset) .skip(ps.offset)
.getMany(); .getMany();

View File

@ -14,7 +14,7 @@ export const meta = {
requireCredential: true, requireCredential: true,
kind: 'favorites-read', kind: 'read:favorites',
params: { params: {
limit: { limit: {

View File

@ -14,7 +14,7 @@ export const meta = {
requireCredential: true, requireCredential: true,
kind: 'messaging-read', kind: 'read:messaging',
params: { params: {
limit: { limit: {

View File

@ -17,7 +17,7 @@ export const meta = {
requireCredential: true, requireCredential: true,
kind: 'messaging-read', kind: 'read:messaging',
params: { params: {
userId: { userId: {

View File

@ -20,7 +20,7 @@ export const meta = {
requireCredential: true, requireCredential: true,
kind: 'messaging-write', kind: 'write:messaging',
params: { params: {
userId: { userId: {

View File

@ -18,7 +18,7 @@ export const meta = {
requireCredential: true, requireCredential: true,
kind: 'messaging-write', kind: 'write:messaging',
limit: { limit: {
duration: ms('1hour'), duration: ms('1hour'),

View File

@ -15,7 +15,7 @@ export const meta = {
requireCredential: true, requireCredential: true,
kind: 'messaging-write', kind: 'write:messaging',
params: { params: {
messageId: { messageId: {

View File

@ -238,8 +238,6 @@ export default define(meta, async (ps, user, app) => {
userId: user.id userId: user.id
}) })
))).filter(file => file != null) as DriveFile[]; ))).filter(file => file != null) as DriveFile[];
files = files;
} }
let renote: Note | undefined; let renote: Note | undefined;

View File

@ -18,7 +18,7 @@ export const meta = {
requireCredential: true, requireCredential: true,
kind: 'favorite-write', kind: 'write:favorites',
params: { params: {
noteId: { noteId: {

View File

@ -17,7 +17,7 @@ export const meta = {
requireCredential: true, requireCredential: true,
kind: 'favorite-write', kind: 'write:favorites',
params: { params: {
noteId: { noteId: {

View File

@ -35,6 +35,7 @@ export default define(meta, async (ps, user) => {
const day = 1000 * 60 * 60 * 24 * 3; // 3日前まで const day = 1000 * 60 * 60 * 24 * 3; // 3日前まで
const query = Notes.createQueryBuilder('note') const query = Notes.createQueryBuilder('note')
.addSelect('note.score')
.where('note.userHost IS NULL') .where('note.userHost IS NULL')
.andWhere(`note.createdAt > :date`, { date: new Date(Date.now() - day) }) .andWhere(`note.createdAt > :date`, { date: new Date(Date.now() - day) })
.andWhere(`note.visibility = 'public'`) .andWhere(`note.visibility = 'public'`)

View File

@ -26,7 +26,7 @@ export const meta = {
requireCredential: true, requireCredential: true,
kind: 'vote-write', kind: 'write:votes',
params: { params: {
noteId: { noteId: {

View File

@ -44,6 +44,7 @@ export default define(meta, async (ps, user) => {
await SwSubscriptions.save({ await SwSubscriptions.save({
id: genId(), id: genId(),
createdAt: new Date(),
userId: user.id, userId: user.id,
endpoint: ps.endpoint, endpoint: ps.endpoint,
auth: ps.auth, auth: ps.auth,

View File

@ -196,5 +196,5 @@ export default define(meta, async (ps, me) => {
const timeline = await query.take(ps.limit!).getMany(); const timeline = await query.take(ps.limit!).getMany();
return await Notes.packMany(timeline, user); return await Notes.packMany(timeline, me);
}); });

View File

@ -65,6 +65,10 @@ export default define(meta, async (ps, me) => {
let user; let user;
if (ps.userIds) { if (ps.userIds) {
if (ps.userIds.length === 0) {
return [];
}
const users = await Users.find({ const users = await Users.find({
id: In(ps.userIds) id: In(ps.userIds)
}); });

22
src/server/api/kinds.ts Normal file
View File

@ -0,0 +1,22 @@
export const kinds = [
'read:account',
'write:account',
'read:blocks',
'write:blocks',
'read:drive',
'write:drive',
'read:favorites',
'write:favorites',
'read:following',
'write:following',
'read:messaging',
'write:messaging',
'read:mutes',
'write:mutes',
'write:notes',
'read:notifications',
'write:notifications',
'read:reactions',
'write:reactions',
'write:votes'
];

View File

@ -1,6 +1,49 @@
import config from '../../../config'; import config from '../../../config';
import endpoints from '../endpoints';
import * as locale from '../../../../locales/';
import { fromEntries } from '../../../prelude/array';
import { kinds as kindsList } from '../kinds';
export interface IKindInfo {
endpoints: string[];
descs: { [x: string]: string; };
}
export function kinds() {
const kinds = fromEntries(
kindsList
.map(k => [k, {
endpoints: [],
descs: fromEntries(
Object.keys(locale)
.map(l => [l, locale[l].common.permissions[k] as string] as [string, string])
) as { [x: string]: string; }
}] as [ string, IKindInfo ])
) as { [x: string]: IKindInfo; };
const errors = [] as string[][];
for (const endpoint of endpoints.filter(ep => !ep.meta.secure)) {
if (endpoint.meta.kind) {
const kind = endpoint.meta.kind;
if (kind in kinds) kinds[kind].endpoints.push(endpoint.name);
else errors.push([kind, endpoint.name]);
}
}
if (errors.length > 0) throw Error('\n ' + errors.map((e) => `Unknown kind (permission) "${e[0]}" found at ${e[1]}.`).join('\n '));
return kinds;
}
export function getDescription(lang = 'ja-JP'): string {
const permissionTable = (Object.entries(kinds()) as [string, IKindInfo][])
.map(e => `|${e[0]}|${e[1].descs[lang]}|${e[1].endpoints.map(f => `[${f}](#operation/${f})`).join(', ')}|`)
.join('\n');
const descriptions = {
'ja-JP': `**Misskey is a decentralized microblogging platform.**
export const description = `
## Usage ## Usage
**APIはすべてPOSTでリクエスト/レスポンスともにJSON形式です。** **APIはすべてPOSTでリクエスト/レスポンスともにJSON形式です。**
一部のAPIはリクエストに認証情報(APIキー)が必要です。リクエストの際に\`i\`というパラメータでAPIキーを添付してください。 一部のAPIはリクエストに認証情報(APIキー)が必要です。リクエストの際に\`i\`というパラメータでAPIキーを添付してください。
@ -44,4 +87,12 @@ APIキーの生成方法を擬似コードで表すと次のようになりま
\`\`\` js \`\`\` js
const i = sha256(userToken + secretKey); const i = sha256(userToken + secretKey);
\`\`\` \`\`\`
`;
## Permissions
|Permisson (kind)|Description|Endpoints|
|:--|:--|:--|
${permissionTable}
`
} as { [x: string]: string };
return lang in descriptions ? descriptions[lang] : descriptions['ja-JP'];
}

View File

@ -3,7 +3,7 @@ import { Context } from 'cafy';
import config from '../../../config'; import config from '../../../config';
import { errors as basicErrors } from './errors'; import { errors as basicErrors } from './errors';
import { schemas } from './schemas'; import { schemas } from './schemas';
import { description } from './description'; import { getDescription } from './description';
import { convertOpenApiSchema } from '../../../misc/schema'; import { convertOpenApiSchema } from '../../../misc/schema';
export function genOpenapiSpec(lang = 'ja-JP') { export function genOpenapiSpec(lang = 'ja-JP') {
@ -13,7 +13,7 @@ export function genOpenapiSpec(lang = 'ja-JP') {
info: { info: {
version: 'v1', version: 'v1',
title: 'Misskey API', title: 'Misskey API',
description: '**Misskey is a decentralized microblogging platform.**\n\n' + description, description: getDescription(lang),
'x-logo': { url: '/assets/api-doc.png' } 'x-logo': { url: '/assets/api-doc.png' }
}, },
@ -110,7 +110,10 @@ export function genOpenapiSpec(lang = 'ja-JP') {
let desc = (endpoint.meta.desc ? endpoint.meta.desc[lang] : 'No description provided.') + '\n\n'; let desc = (endpoint.meta.desc ? endpoint.meta.desc[lang] : 'No description provided.') + '\n\n';
desc += `**Credential required**: *${endpoint.meta.requireCredential ? 'Yes' : 'No'}*`; desc += `**Credential required**: *${endpoint.meta.requireCredential ? 'Yes' : 'No'}*`;
if (endpoint.meta.kind) desc += ` / **Permission**: *${endpoint.meta.kind}*`; if (endpoint.meta.kind) {
const kind = endpoint.meta.kind;
desc += ` / **Permission**: *${kind}*`;
}
const info = { const info = {
operationId: endpoint.name, operationId: endpoint.name,

View File

@ -23,7 +23,7 @@ export default class extends Channel {
public onMessage(type: string, body: any) { public onMessage(type: string, body: any) {
switch (type) { switch (type) {
case 'read': case 'read':
read(this.user!.id, this.otherpartyId, body.id); read(this.user!.id, this.otherpartyId, [body.id]);
break; break;
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -21,12 +21,6 @@ app.use(async (ctx, next) => {
// Init router // Init router
const router = new Router(); const router = new Router();
router.get('/default-avatar.jpg', ctx => {
const file = fs.createReadStream(`${__dirname}/assets/avatar.jpg`);
ctx.set('Content-Type', 'image/jpeg');
ctx.body = file;
});
router.get('/app-default.jpg', ctx => { router.get('/app-default.jpg', ctx => {
const file = fs.createReadStream(`${__dirname}/assets/dummy.png`); const file = fs.createReadStream(`${__dirname}/assets/dummy.png`);
ctx.set('Content-Type', 'image/jpeg'); ctx.set('Content-Type', 'image/jpeg');

View File

@ -25,6 +25,8 @@ import Logger from '../services/logger';
import { program } from '../argv'; import { program } from '../argv';
import { UserProfiles } from '../models'; import { UserProfiles } from '../models';
import { networkChart } from '../services/chart'; import { networkChart } from '../services/chart';
import { genAvatar } from '../misc/gen-avatar';
import { createTemp } from '../misc/create-temp';
export const serverLogger = new Logger('server', 'gray', false); export const serverLogger = new Logger('server', 'gray', false);
@ -72,6 +74,13 @@ router.use(activityPub.routes());
router.use(nodeinfo.routes()); router.use(nodeinfo.routes());
router.use(wellKnown.routes()); router.use(wellKnown.routes());
router.get('/avatar/:x', async ctx => {
const [temp] = await createTemp();
await genAvatar(ctx.params.x, fs.createWriteStream(temp));
ctx.set('Content-Type', 'image/png');
ctx.body = fs.createReadStream(temp);
});
router.get('/verify-email/:code', async ctx => { router.get('/verify-email/:code', async ctx => {
const profile = await UserProfiles.findOne({ const profile = await UserProfiles.findOne({
emailVerifyCode: ctx.params.code emailVerifyCode: ctx.params.code

View File

@ -8,7 +8,7 @@ html
head head
meta(charset='utf-8') meta(charset='utf-8')
meta(name='application-name' content= title || 'Misskey') meta(name='application-name' content='Misskey')
meta(name='referrer' content='origin') meta(name='referrer' content='origin')
meta(name='theme-color' content='#105779') meta(name='theme-color' content='#105779')
meta(property='og:site_name' content= title || 'Misskey') meta(property='og:site_name' content= title || 'Misskey')

View File

@ -79,7 +79,7 @@ export default abstract class Chart<T extends Record<string, any>> {
flatColumns(v.properties, p); flatColumns(v.properties, p);
} else { } else {
columns[this.columnPrefix + p] = { columns[this.columnPrefix + p] = {
type: 'integer', type: 'bigint',
}; };
} }
} }

View File

@ -209,10 +209,10 @@ async function deleteOldFile(user: IRemoteUser) {
} }
if (user.bannerId) { if (user.bannerId) {
q.andWhere('file.id != :bannerId', { bannerId: user.bannerId }) q.andWhere('file.id != :bannerId', { bannerId: user.bannerId });
} }
q.orderBy('file.id', 'DESC'); q.orderBy('file.id', 'ASC');
const oldFile = await q.getOne(); const oldFile = await q.getOne();

View File

@ -2,7 +2,7 @@ import * as Minio from 'minio';
import config from '../../config'; import config from '../../config';
import { DriveFile } from '../../models/entities/drive-file'; import { DriveFile } from '../../models/entities/drive-file';
import { InternalStorage } from './internal-storage'; import { InternalStorage } from './internal-storage';
import { DriveFiles, Instances } from '../../models'; import { DriveFiles, Instances, Notes } from '../../models';
import { driveChart, perUserDriveChart, instanceChart } from '../chart'; import { driveChart, perUserDriveChart, instanceChart } from '../chart';
export default async function(file: DriveFile, isExpired = false) { export default async function(file: DriveFile, isExpired = false) {
@ -40,6 +40,11 @@ export default async function(file: DriveFile, isExpired = false) {
}); });
} else { } else {
DriveFiles.delete(file.id); DriveFiles.delete(file.id);
// TODO: トランザクション
Notes.createQueryBuilder().delete()
.where(':id = ANY(fileIds)', { id: file.id })
.execute();
} }
// 統計を更新 // 統計を更新

View File

@ -11,15 +11,15 @@ export default (
noteId: Note['id'] noteId: Note['id']
) => new Promise<any>(async (resolve, reject) => { ) => new Promise<any>(async (resolve, reject) => {
// Remove document // Remove document
const res = await NoteUnreads.delete({ /*const res = */await NoteUnreads.delete({
userId: userId, userId: userId,
noteId: noteId noteId: noteId
}); });
// v11 TODO: https://github.com/typeorm/typeorm/issues/2415 // v11 TODO: https://github.com/typeorm/typeorm/issues/2415
if (res.affected == 0) { //if (res.affected == 0) {
return; // return;
} //}
const count1 = await NoteUnreads.count({ const count1 = await NoteUnreads.count({
userId: userId, userId: userId,

View File

@ -792,7 +792,7 @@ describe('API', () => {
parentId: folderA.id parentId: folderA.id
}, arisugawa); }, arisugawa);
expect(res).have.status(400); assert.strictEqual(res.status, 400);
})); }));
it('存在しない親フォルダを設定できない', async(async () => { it('存在しない親フォルダを設定できない', async(async () => {

View File

@ -1141,7 +1141,7 @@ describe('MFM', () => {
it('exlude emotes', () => { it('exlude emotes', () => {
const tokens = parse('*.*'); const tokens = parse('*.*');
assert.deepStrictEqual(tokens, [ assert.deepStrictEqual(tokens, [
text("*.*"), text('*.*'),
]); ]);
}); });