Compare commits

...

81 Commits

Author SHA1 Message Date
929982117f Merge branch 'develop' 2019-04-18 01:12:21 +09:00
56a530d769 11.1.6 2019-04-18 01:12:03 +09:00
7ef75fb06b New Crowdin translations (#4692)
* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (French)

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

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Spanish)

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

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (French)

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

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

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

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

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

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

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

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)
2019-04-18 01:11:06 +09:00
112a72abdf Docker: Back to npm from yarn (#4730)
This commit reverts "Fix Dockerfile #4214" which uses yarn instead of npm.
The cause of the build error is that binding.gyp and src/crypto_key.cc
are missing when installing dependencies.
In other words, yarn did not fix build error.

There is no reason to use yarn, so go back to npm.
2019-04-18 01:09:31 +09:00
71813e03ee Fix bug 2019-04-18 01:05:40 +09:00
9b05b6ef28 Improve readability 2019-04-18 00:57:06 +09:00
54a87b25b3 Remove unused imports 2019-04-18 00:56:10 +09:00
55e97864bd Fix: v11で未認知ユーザーからActivityが飛んできた場合に処理できない (#4733)
* Fix: inboxに未知のユーザーが来ると処理できない

* こうかな
2019-04-18 00:53:00 +09:00
95733c9490 [MFM] Better hashtag parsing 2019-04-18 00:40:56 +09:00
e19ae644f1 Fix indent 2019-04-18 00:13:31 +09:00
ac4ea25267 Better error handling 2019-04-18 00:09:08 +09:00
611e4f34dc Merge branch 'develop' 2019-04-17 19:37:32 +09:00
faf017f333 11.1.5 2019-04-17 19:36:58 +09:00
5eec896615 Better avgColor 2019-04-17 17:13:49 +09:00
17f35174ea 🎨 2019-04-17 17:01:57 +09:00
bf71b31123 Update CONTRIBUTING.md 2019-04-17 16:59:39 +09:00
9399a44c82 Fix error 2019-04-17 16:50:50 +09:00
c96418806f Disable sql log 2019-04-17 16:45:31 +09:00
7945eddef6 Clean up 2019-04-17 14:39:45 +09:00
0ede390fef Refactor 2019-04-17 14:32:59 +09:00
85959a3b9b Fix #4721 Fix #4722 2019-04-17 14:30:31 +09:00
946c3a25b9 Clean up 2019-04-17 07:25:34 +09:00
f9d697128a Update schemas.ts 2019-04-17 04:32:04 +09:00
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
112 changed files with 922 additions and 448 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
@ -62,22 +56,10 @@ jobs:
executor: executor:
type: string type: string
default: "default" default: "default"
without_redis:
type: boolean
default: false
executor: <<parameters.executor>> executor: <<parameters.executor>>
steps: steps:
- attach_workspace: - attach_workspace:
at: /tmp/workspace at: /tmp/workspace
- when:
condition: <<parameters.without_redis>>
steps:
- run:
name: Configure
command: |
mv .config/test.yml .config/test_redis.yml
touch .config/test.yml
cat .config/test_redis.yml | while IFS= read line; do if [[ "$line" = '# __REDIS__' ]]; then break; else echo "$line" >> .config/test.yml; fi; done
- run: - run:
name: Test name: Test
command: | command: |
@ -140,32 +122,14 @@ workflows:
branches: branches:
only: master only: master
- test: - test:
name: manual-test-with-redis name: manual-test
executor: with-redis
requires: requires:
- manual-build - manual-build
filters: filters:
branches: branches:
ignore: master ignore: master
- test: - test:
name: auto-test-without-redis name: auto-test
executor: with-redis
requires:
- auto-build
filters:
branches:
only: master
- test:
name: manual-test-with-redis
without_redis: true
requires:
- manual-build
filters:
branches:
ignore: master
- test:
name: auto-test-without-redis
without_redis: true
requires: requires:
- auto-build - auto-build
filters: filters:

View File

@ -5,8 +5,73 @@ 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.6 (2019/04/18)
-------------------
### Fixes
* 未認知ユーザーからActivityが飛んできた場合に処理できない問題を修正
* その投稿を見たのにも関わらずメンションインジケーターが点灯し続ける問題を修正
* ハッシュタグの判定を改善
* サーバーのエラーハンドリングを改善
11.1.5 (2019/04/17)
-------------------
### Fixes
* ユーザー名に含まれているカスタム絵文字が表示されないことがある問題を修正
* 壁紙の設定ができない問題を修正
* デザインの調整
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) 11.0.2 (2019/04/15)
------------------- -------------------
### Fixes
* アプリが作成できない問題を修正 * アプリが作成できない問題を修正
* 「ハイライト」が表示されない問題を修正 * 「ハイライト」が表示されない問題を修正
* リモートの投稿に添付されている画像が小さい問題を修正 * リモートの投稿に添付されている画像が小さい問題を修正
@ -15,15 +80,19 @@ If you encounter any problems with updating, please try the following:
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

@ -46,10 +46,40 @@ Convert な(na) to にゃ(nya)
Revert Nyaize Revert Nyaize
## Code style ## Code style
### Use semicolon ### セミコロンを省略しない
To avoid ASI Hazard ASI Hazardを避けるためでもある
### 中括弧を省略しない
Bad:
``` ts
if (foo)
bar;
else
baz;
```
Good:
``` ts
if (foo) {
bar;
} else {
baz;
}
```
ただし**`if`が一行**の時だけは省略しても良い
Good:
``` ts
if (foo) bar;
```
### `export default`を使わない
インテリセンスと相性が悪かったりするため
参考:
* https://gfx.hatenablog.com/entry/2017/11/24/135343
* https://basarat.gitbooks.io/typescript/docs/tips/defaultIsBad.html
### Don't use `export default`
Bad: Bad:
``` ts ``` ts
export default function(foo: string): string { export default function(foo: string): string {

View File

@ -21,12 +21,11 @@ RUN apk add --no-cache \
pkgconfig \ pkgconfig \
python \ python \
zlib-dev zlib-dev
RUN npm i -g yarn
COPY package.json ./ COPY package.json ./
RUN yarn install RUN npm i
COPY . ./ COPY . ./
RUN yarn build RUN npm run build
FROM base AS runner FROM base AS runner

View File

@ -140,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>
@ -163,7 +163,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
</tr><tr> </tr><tr>
</tr></table> </tr></table>
**Last updated:** Mon, 15 Apr 2019 01:59:07 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

@ -70,8 +70,14 @@ common:
followers: "Sledující" followers: "Sledující"
favorites: "Oblíbené" favorites: "Oblíbené"
permissions: permissions:
'read:drive': "Prohlížet Disk" "read:account": "Zobrazit informace o účtu"
'write:drive': "Pracovat s Diskem" "read:drive": "Prohlížet Disk"
"write:drive": "Pracovat s Diskem"
"read:favorites": "Prohlížet oblíbené"
"read:messaging": "Prohlížet konverzaci"
"write:messaging": "Pracovat s konverzaci"
"read:mutes": "Prohlížet ztlumené"
"write:votes": "Hlasovat"
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"
@ -1114,7 +1120,7 @@ mobile/views/components/post-form.vue:
reply: "Odpovědět" reply: "Odpovědět"
renote: "Renotovat" renote: "Renotovat"
reply-placeholder: "Odpovědět na tento příspěvěk" reply-placeholder: "Odpovědět na tento příspěvěk"
location-alert: "Vaše zařízení nepodporuje lokační službu" geolocation-alert: "Vaše zařízení nepodporuje lokační službu"
error: "Chyba" error: "Chyba"
username-prompt: "Zadejte uživatelské jméno" username-prompt: "Zadejte uživatelské jméno"
mobile/views/components/sub-note-content.vue: mobile/views/components/sub-note-content.vue:
@ -1185,4 +1191,3 @@ deck/deck.user-column.vue:
activity: "Aktivita" activity: "Aktivita"
dev/views/new-app.vue: dev/views/new-app.vue:
app-name-desc: "Jméno vaší aplikace" app-name-desc: "Jméno vaší aplikace"
app-desc: "Stručný popis nebo představení vaší aplikace."

View File

@ -62,10 +62,14 @@ common:
followers: "Folgende" followers: "Folgende"
favorites: "Diesen Beitrag favorisieren" favorites: "Diesen Beitrag favorisieren"
permissions: permissions:
'read:account': "Accountinformationen anzeigen." "read:account": "Accountinformationen anzeigen."
'write:account': "Accountinformationen bearbeiten." "write:account": "Accountinformationen bearbeiten."
'read:drive': "Dateien anzeigen" "read:drive": "Dateien anzeigen"
'write:drive': "Dateien bearbeiten" "write:drive": "Dateien bearbeiten"
"read:favorites": "Favoriten anzeigen"
"read:messaging": "Unterhaltung anzeigen"
"write:messaging": "Unterhaltung bearbeiten"
"write:votes": "Abstimmen"
empty-timeline-info: empty-timeline-info:
follow-users-to-make-your-timeline: "Beiträge von Benutzern, denen du folgst, werden in der Zeitleiste angezeigt." follow-users-to-make-your-timeline: "Beiträge von Benutzern, denen du folgst, werden in der Zeitleiste angezeigt."
explore: "Benutzer finden" explore: "Benutzer finden"
@ -739,10 +743,7 @@ dev/views/new-app.vue:
create-app: "Erstelle Anwendung" create-app: "Erstelle Anwendung"
app-name: "Name der Anwendung" app-name: "Name der Anwendung"
app-name-desc: "Der 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-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: "Callback-URL (optional)"
callback-url-desc: "Die URL, auf die nach erfolgreicher Authentifizierung umgeleitet werden soll." callback-url-desc: "Die URL, auf die nach erfolgreicher Authentifizierung umgeleitet werden soll."
authority: "Berechtigungen" authority: "Berechtigungen"

View File

@ -70,10 +70,21 @@ common:
followers: "Followers" followers: "Followers"
favorites: "Favorites" favorites: "Favorites"
permissions: permissions:
'read:account': "View account information" "read:account": "View account information"
'write:account': "Update your account information" "write:account": "Update your account information"
'read:drive': "Browse the Drive" "read:blocks": "View Blocks"
'write:drive': "Work with the Drive" "write:blocks": "Work with Blocks"
"read:drive": "Browse the Drive"
"write:drive": "Work with the Drive"
"read:favorites": "View Favorites"
"write:favorites": "Work with Favorites"
"read:following": "View Follower info"
"write:following": "Work with Follow info"
"read:messaging": "View Messaging"
"write:messaging": "Work with Messaging"
"read:mutes": "View Muted"
"write:mutes": "Work with Muted"
"write:votes": "Vote"
empty-timeline-info: empty-timeline-info:
follow-users-to-make-your-timeline: "Following users will show their posts in your timeline." follow-users-to-make-your-timeline: "Following users will show their posts in your timeline."
explore: "Find users" explore: "Find users"
@ -281,6 +292,7 @@ common:
nav: "Navigation" nav: "Navigation"
tips: "Tips" tips: "Tips"
hashtags: "Hashtags" hashtags: "Hashtags"
queue: "Queue"
dev: "Failed to create the application. Please try again." dev: "Failed to create the application. Please try again."
ai-chan-kawaii: "Ai-chan kawaii!" ai-chan-kawaii: "Ai-chan kawaii!"
you: "You" you: "You"
@ -1462,7 +1474,7 @@ mobile/views/components/post-form.vue:
quote-placeholder: "Quote this post... (optional)" quote-placeholder: "Quote this post... (optional)"
reply-placeholder: "Reply to this note..." reply-placeholder: "Reply to this note..."
cw-placeholder: "Comments for the post (optional)" cw-placeholder: "Comments for the post (optional)"
location-alert: "Your device does not provide location services" geolocation-alert: "Your device does not provide location services."
error: "Error" error: "Error"
username-prompt: "Enter user name" username-prompt: "Enter user name"
mobile/views/components/sub-note-content.vue: mobile/views/components/sub-note-content.vue:
@ -1615,10 +1627,7 @@ dev/views/new-app.vue:
create-app: "Creating application" create-app: "Creating application"
app-name: "Application name" app-name: "Application name"
app-name-desc: "The name of your app" app-name-desc: "The name of your app"
app-name-ex: "ex) Misskey for iOS"
app-overview: "Application summary" app-overview: "Application summary"
app-desc: "A brief description or introduction of your app."
app-desc-ex: "ex) Misskey iOS client."
callback-url: "The callback URL (optional)" callback-url: "The callback URL (optional)"
callback-url-desc: "The URL to redirect to after the user is authenticated via the authentication form." callback-url-desc: "The URL to redirect to after the user is authenticated via the authentication form."
authority: "Permissions" authority: "Permissions"

View File

@ -20,8 +20,15 @@ common:
application-authorization: "Autorizaciones de la aplicación." application-authorization: "Autorizaciones de la aplicación."
close: "Cerrar" close: "Cerrar"
do-not-copy-paste: "Por favor no copies código aquí. Tu cuenta puede resultar comprometida." do-not-copy-paste: "Por favor no copies código aquí. Tu cuenta puede resultar comprometida."
load-more: "Leer más"
enter-password: "Escribe una contraseña" enter-password: "Escribe una contraseña"
2fa: "Autenticación de dos factores" 2fa: "Autenticación de dos factores"
customize-home: "Personaliza la página principal"
featured-notes: "Destacados"
dark-mode: "Modo oscuro"
signin: "Iniciar sesión"
signup: "¡Regístrate!"
signout: "Cerrar sesión"
got-it: "¡Listo!" got-it: "¡Listo!"
customization-tips: customization-tips:
title: "Consejos de personalización" title: "Consejos de personalización"
@ -50,8 +57,22 @@ common:
drive: "Drive" drive: "Drive"
messaging: "Conversación" messaging: "Conversación"
home: "Inicio" home: "Inicio"
deck: "Deck"
timeline: "Timeline"
explore: "Explorar"
following: "Siguiendo" following: "Siguiendo"
followers: "Seguidores"
favorites: "Me gusta esta nota" favorites: "Me gusta esta nota"
permissions:
"read:account": "Ver información de la cuenta"
"write:account": "Editar información de la cuenta"
"read:blocks": "Ver bloques"
"write:blocks": "Editar bloques"
"read:favorites": "Ver favoritos"
"write:favorites": "Editar favoritos"
"read:messaging": "Ver conversación"
"read:notifications": "Ver notificaciones"
"write:votes": "Vota"
weekday-short: weekday-short:
sunday: "domingo" sunday: "domingo"
monday: "lunes" monday: "lunes"
@ -97,12 +118,24 @@ common:
d: "¿Quieres decir algo?" d: "¿Quieres decir algo?"
e: "¡Escribe aquí!" e: "¡Escribe aquí!"
f: "Esperando a que escribas algo..." f: "Esperando a que escribas algo..."
settings: "Configuración"
_settings: _settings:
profile: "Tu perfil" profile: "Tu perfil"
notification: "Notificaciones" notification: "Notificaciones"
apps: "Aplicaciones"
tags: "Etiquetas" tags: "Etiquetas"
mute-and-block: "Silenciar/Bloquear"
blocking: "Bloquear" blocking: "Bloquear"
security: "Seguridad"
password: "Contraseña" password: "Contraseña"
other: "Otros"
appearance: "Diseño"
behavior: "Comportamiento"
fetch-on-scroll-desc: "Cuando te deslizas al final de la página nuevo contenido se carga automáticamente."
note-visibility: "Visibilidad de la publicación"
default-note-visibility: "Rango de publicación predeterminado"
web-search-engine: "Buscador web"
web-search-engine-desc: "Ejemplo: https://www.google.com/?#q={{query}}"
use-os-default-emojis: "Usar los emoticonos estándar del sistema operativo" use-os-default-emojis: "Usar los emoticonos estándar del sistema operativo"
line-width: "Grosor de línea" line-width: "Grosor de línea"
line-width-thick: "Grosor" line-width-thick: "Grosor"
@ -128,6 +161,19 @@ common:
contrasted-acct: "Añadir contraste al nombre de usuario" contrasted-acct: "Añadir contraste al nombre de usuario"
wallpaper: "Fondo de pantalla" wallpaper: "Fondo de pantalla"
choose-wallpaper: "Escoge un fondo de pantalla" choose-wallpaper: "Escoge un fondo de pantalla"
delete-wallpaper: "Quitar fondo de pantalla"
show-clock-on-header: "Muestra el reloj en la parte superior derecha"
timeline: "Timeline"
sound: "Sonido"
enable-sounds: "Habilitar sonido"
volume: "Volúmen"
test: "Prueba"
version: "Versión"
no-updates: "No hay actualizaciones disponibles"
no-updates-desc: "Tu Misskey está actualizado"
update-available: "¡Una nueva versión está disponible!"
update-available-desc: "Las actualizaciones se aplicarán cuando la página se vuelva a cargar."
advanced-settings: "Configuraciones avanzadas"
navbar-position-left: "Izquierda" navbar-position-left: "Izquierda"
search: "Buscar" search: "Buscar"
delete: "eliminar" delete: "eliminar"
@ -869,6 +915,7 @@ mobile/views/components/post-form.vue:
reply: "Responder" reply: "Responder"
renote: "Republicar" renote: "Republicar"
reply-placeholder: "Responder a esta nota..." reply-placeholder: "Responder a esta nota..."
geolocation-alert: "Tu dispositivo no tiene soporte de geolocalización."
mobile/views/components/sub-note-content.vue: mobile/views/components/sub-note-content.vue:
private: "Esta publicación es privada" private: "Esta publicación es privada"
deleted: "Esta publicación ha sido removida" deleted: "Esta publicación ha sido removida"

View File

@ -68,10 +68,11 @@ common:
followers: "Abonné·e·s" followers: "Abonné·e·s"
favorites: "Mettre cette note en favoris" favorites: "Mettre cette note en favoris"
permissions: permissions:
'read:account': "Afficher les informations du compte" "read:account": "Afficher les informations du compte"
'write:account': "Mettre à jour les informations de votre compte" "write:account": "Mettre à jour les informations de votre compte"
'read:drive': "Parcourir le Drive" "read:drive": "Parcourir le Drive"
'write:drive': "Écrire sur le Drive" "write:drive": "Écrire sur le Drive"
"write:votes": "Vote"
empty-timeline-info: empty-timeline-info:
follow-users-to-make-your-timeline: "Les utilisateurs suivants afficheront leurs publications sur votre fil." follow-users-to-make-your-timeline: "Les utilisateurs suivants afficheront leurs publications sur votre fil."
explore: "Trouver des utilisateurs" explore: "Trouver des utilisateurs"
@ -274,6 +275,7 @@ common:
nav: "Navigation" nav: "Navigation"
tips: "Conseils" tips: "Conseils"
hashtags: "Hashtags" hashtags: "Hashtags"
queue: "File d'attente"
dev: "Échec lors de la création de lapplication. Veuillez réessayer." dev: "Échec lors de la création de lapplication. Veuillez réessayer."
ai-chan-kawaii: "Ai-Chan est mignonne !" ai-chan-kawaii: "Ai-Chan est mignonne !"
you: "Vous" you: "Vous"
@ -1434,7 +1436,7 @@ mobile/views/components/post-form.vue:
quote-placeholder: "Citer ce billet ... (Facultatif)" quote-placeholder: "Citer ce billet ... (Facultatif)"
reply-placeholder: "Répondre à cette note" reply-placeholder: "Répondre à cette note"
cw-placeholder: "Commenter le contenu (optionnel)" cw-placeholder: "Commenter le contenu (optionnel)"
location-alert: "Votre appareil ne prend pas en charge les services de localisation" geolocation-alert: "Votre appareil ne prend pas en charge les services de localisation"
error: "Erreur" error: "Erreur"
username-prompt: "Saisir un nom d'utilisateur" username-prompt: "Saisir un nom d'utilisateur"
mobile/views/components/sub-note-content.vue: mobile/views/components/sub-note-content.vue:
@ -1586,10 +1588,7 @@ dev/views/new-app.vue:
create-app: "Création dune application" create-app: "Création dune application"
app-name: "Nom de lapplication" app-name: "Nom de lapplication"
app-name-desc: "Le nom de votre application" app-name-desc: "Le nom de votre application"
app-name-ex: "p. ex. Misskey pour iOS"
app-overview: "Description courte de lapplication" app-overview: "Description courte de lapplication"
app-desc: "Brève description introductive à votre application."
app-desc-ex: "p. ex) Misskey pour iOS"
callback-url: "LUrl de callback (facultatif)" callback-url: "LUrl de callback (facultatif)"
callback-url-desc: "Vous pouvez définir lURL de redirection lorsque lutilisateur sest authentifié via formulaire dauthentification." callback-url-desc: "Vous pouvez définir lURL de redirection lorsque lutilisateur sest authentifié via formulaire dauthentification."
authority: "Autorisations " authority: "Autorisations "

View File

@ -313,6 +313,7 @@ common:
nav: "ナビゲーション" nav: "ナビゲーション"
tips: "ヒント" tips: "ヒント"
hashtags: "ハッシュタグ" hashtags: "ハッシュタグ"
queue: "キュー"
dev: "アプリの作成に失敗しました。再度お試しください。" dev: "アプリの作成に失敗しました。再度お試しください。"
ai-chan-kawaii: "藍ちゃかわいい" ai-chan-kawaii: "藍ちゃかわいい"
@ -1549,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: "クリックでバナー編集"
@ -1641,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: "ユーザー名を入力してください"

View File

@ -60,6 +60,8 @@ common:
following: "フォローしとる" following: "フォローしとる"
followers: "フォロワー" followers: "フォロワー"
favorites: "お気に入り" favorites: "お気に入り"
permissions:
"write:votes": "投票するで"
weekday-short: weekday-short:
sunday: "日" sunday: "日"
monday: "月" monday: "月"
@ -1126,7 +1128,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: "ユーザー名を入力してや"
mobile/views/components/sub-note-content.vue: mobile/views/components/sub-note-content.vue:
@ -1272,10 +1274,7 @@ dev/views/new-app.vue:
create-app: "アプリケーション作る" create-app: "アプリケーション作る"
app-name: "アプリケーションの名前" app-name: "アプリケーションの名前"
app-name-desc: "あんたのアプリの名前。" app-name-desc: "あんたのアプリの名前。"
app-name-ex: "ex) 関西ミスキー保安協会"
app-overview: "このアプリどんなん?" app-overview: "このアプリどんなん?"
app-desc: "あんたのアプリどんなんか教えて"
app-desc-ex: "ex) 関西人なら誰でも口ずさめるこのCMがついにMisskeyへ。"
callback-url: "コールバックURL (無くてもええで)" callback-url: "コールバックURL (無くてもええで)"
callback-url-desc: "ユーザーが認証フォームで認証した後どこに連れてくかを設定できるで" callback-url-desc: "ユーザーが認証フォームで認証した後どこに連れてくかを設定できるで"
authority: "権限" authority: "権限"

View File

@ -70,10 +70,11 @@ common:
followers: "팔로워" followers: "팔로워"
favorites: "즐겨찾기" favorites: "즐겨찾기"
permissions: permissions:
'read:account': "계정 정보 보기" "read:account": "계정 정보 보기"
'write:account': "계정 정보 변경" "write:account": "계정 정보 변경"
'read:drive': "드라이브 보기" "read:drive": "드라이브 보기"
'write:drive': "드라이브 수정" "write:drive": "드라이브 수정"
"write:votes": "투표하기"
empty-timeline-info: empty-timeline-info:
follow-users-to-make-your-timeline: "사용자를 팔로우하면 글이 타임라인에 표시됩니다." follow-users-to-make-your-timeline: "사용자를 팔로우하면 글이 타임라인에 표시됩니다."
explore: "사용자 탐색" explore: "사용자 탐색"
@ -281,6 +282,7 @@ common:
nav: "내비게이션" nav: "내비게이션"
tips: "팁" tips: "팁"
hashtags: "해시태그" hashtags: "해시태그"
queue: "큐"
dev: "앱을 만드는 데 실패했습니다. 다시 시도하시기 바랍니다." dev: "앱을 만드는 데 실패했습니다. 다시 시도하시기 바랍니다."
ai-chan-kawaii: "아이쨩 귀여워" ai-chan-kawaii: "아이쨩 귀여워"
you: "당신" you: "당신"
@ -1462,7 +1464,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: "사용자명을 입력하여 주십시오"
mobile/views/components/sub-note-content.vue: mobile/views/components/sub-note-content.vue:
@ -1615,10 +1617,7 @@ dev/views/new-app.vue:
create-app: "어플리케이션 생성" create-app: "어플리케이션 생성"
app-name: "어플리케이션 이름" app-name: "어플리케이션 이름"
app-name-desc: "앱의 이름." app-name-desc: "앱의 이름."
app-name-ex: "ex) Misskey for iOS"
app-overview: "앱 개요" app-overview: "앱 개요"
app-desc: "어플리케이션에 대한 간단한 설명."
app-desc-ex: "ex) Misskey iOS 클라이언트."
callback-url: "콜백 URL (옵션)" callback-url: "콜백 URL (옵션)"
callback-url-desc: "사용자가 인증 폼에서 인증한 뒤 리다이렉트할 URL을 설정합니다." callback-url-desc: "사용자가 인증 폼에서 인증한 뒤 리다이렉트할 URL을 설정합니다."
authority: "권한" authority: "권한"

View File

@ -23,6 +23,8 @@ common:
timeline: "Tijdlijn" timeline: "Tijdlijn"
followers: "Volgers" followers: "Volgers"
favorites: "Deze notitie toevoegen aan favorieten" favorites: "Deze notitie toevoegen aan favorieten"
permissions:
"write:votes": "Stemmen"
weekday-short: weekday-short:
sunday: "Z" sunday: "Z"
monday: "M" monday: "M"

View File

@ -37,6 +37,8 @@ common:
home: "Hjem" home: "Hjem"
followers: "Følgere" followers: "Følgere"
favorites: "Merket som favoritt" favorites: "Merket som favoritt"
permissions:
"write:votes": "Stem"
weekday-short: weekday-short:
sunday: "S" sunday: "S"
monday: "M" monday: "M"

View File

@ -63,7 +63,8 @@ common:
followers: "Śledzący" followers: "Śledzący"
favorites: "Moje ulubione" favorites: "Moje ulubione"
permissions: permissions:
'read:drive': "Wyświetl dysk" "read:drive": "Wyświetl dysk"
"write:votes": "Zagłosuj"
empty-timeline-info: empty-timeline-info:
explore: "Poznaj" explore: "Poznaj"
weekday-short: weekday-short:
@ -1082,7 +1083,7 @@ mobile/views/components/post-form.vue:
quote-placeholder: "Zacytuj ten wpis… (nieobowiązkowe)" quote-placeholder: "Zacytuj ten wpis… (nieobowiązkowe)"
reply-placeholder: "Odpowiedź na ten wpis…" reply-placeholder: "Odpowiedź na ten wpis…"
cw-placeholder: "Treść ostrzeżenia (opcjonalnie)" cw-placeholder: "Treść ostrzeżenia (opcjonalnie)"
location-alert: "Twoje urządzenie nie pozwala na przekazywanie informacji o lokalizacji" geolocation-alert: "Twoje urządzenie nie obsługuje geolokalizacji."
error: "Błąd" error: "Błąd"
username-prompt: "Wprowadź nazwę użytkownika" username-prompt: "Wprowadź nazwę użytkownika"
mobile/views/components/sub-note-content.vue: mobile/views/components/sub-note-content.vue:

View File

@ -70,10 +70,26 @@ common:
followers: "关注者" followers: "关注者"
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: "关注其他用户时,帖子将显示在时间线中。"
explore: "查找用户" explore: "查找用户"
@ -129,7 +145,7 @@ common:
apps: "应用程序" apps: "应用程序"
tags: "标签" tags: "标签"
mute-and-block: "屏蔽/拉黑" mute-and-block: "屏蔽/拉黑"
blocking: "屏蔽" blocking: "拉黑"
security: "安全性" security: "安全性"
signin: "登录历史" signin: "登录历史"
password: "密码" password: "密码"
@ -281,6 +297,7 @@ common:
nav: "导航" nav: "导航"
tips: "提示" tips: "提示"
hashtags: "标签" hashtags: "标签"
queue: "队列"
dev: "构建应用程序失败,请再试一次。" dev: "构建应用程序失败,请再试一次。"
ai-chan-kawaii: "小蓝真可爱" ai-chan-kawaii: "小蓝真可爱"
you: "您" you: "您"
@ -966,6 +983,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:
@ -1385,6 +1403,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: "点击来剪辑背景"
update-avatar: "点击来剪辑头像" update-avatar: "点击来剪辑头像"
@ -1461,7 +1480,7 @@ mobile/views/components/post-form.vue:
quote-placeholder: "引用这个帖子t... (可选)" quote-placeholder: "引用这个帖子t... (可选)"
reply-placeholder: "回复这个帖子" reply-placeholder: "回复这个帖子"
cw-placeholder: "评论帖子(可选)" cw-placeholder: "评论帖子(可选)"
location-alert: "您的设备不提供位服务" geolocation-alert: "您的设备不提供位服务"
error: "错误" error: "错误"
username-prompt: "请输入用户名" username-prompt: "请输入用户名"
mobile/views/components/sub-note-content.vue: mobile/views/components/sub-note-content.vue:
@ -1611,14 +1630,17 @@ dev/views/apps.vue:
create-app: "创建应用" create-app: "创建应用"
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) iOS版Misskey"
app-name-desc: "您应用的名称" app-name-desc: "您应用的名称"
app-name-ex: "ex) iOS版本的Misskey"
app-overview: "应用摘要" app-overview: "应用摘要"
app-desc: "您的应用的简要说明或介绍。" app-overview-placeholder: " ex) iOS版Misskey客户端."
app-desc-ex: "ex) iOS版Misskey客户端." app-overview-desc: "您的应用的简要说明或介绍。"
callback-url: "回应URL (optional)" callback-url: "回应URL (optional)"
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

@ -1,7 +1,7 @@
{ {
"name": "misskey", "name": "misskey",
"author": "syuilo <i@syuilo.com>", "author": "syuilo <i@syuilo.com>",
"version": "11.0.2", "version": "11.1.6",
"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

@ -137,7 +137,6 @@ export default prop => ({
Vue.set(this.$_ns_target, 'deletedAt', body.deletedAt); Vue.set(this.$_ns_target, 'deletedAt', body.deletedAt);
Vue.set(this.$_ns_target, 'renote', null); Vue.set(this.$_ns_target, 'renote', null);
this.$_ns_target.text = null; this.$_ns_target.text = null;
this.$_ns_target.tags = [];
this.$_ns_target.fileIds = []; this.$_ns_target.fileIds = [];
this.$_ns_target.poll = null; this.$_ns_target.poll = null;
this.$_ns_target.geo = null; this.$_ns_target.geo = null;

View File

@ -121,7 +121,7 @@ export default Vue.extend({
if (this.file.properties.avgColor) { if (this.file.properties.avgColor) {
anime({ anime({
targets: this.$refs.thumbnail, targets: this.$refs.thumbnail,
backgroundColor: this.file.properties.avgColor.replace('255)', '0)'), backgroundColor: 'transparent', // TODO fade
duration: 100, duration: 100,
easing: 'linear' easing: 'linear'
}); });

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=""/>
@ -32,7 +32,7 @@ export default Vue.extend({
props: { props: {
files: { files: {
type: Object, type: Array,
required: true required: true
}, },
detachMediaFn: { detachMediaFn: {

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

@ -525,15 +525,11 @@ export default Vue.extend({
this.$chooseDriveFile({ this.$chooseDriveFile({
multiple: false multiple: false
}).then(file => { }).then(file => {
this.$root.api('i/update', { this.$store.dispatch('settings/set', { key: 'wallpaper', value: file.url });
wallpaperId: file.id
});
}); });
}, },
deleteWallpaper() { deleteWallpaper() {
this.$root.api('i/update', { this.$store.dispatch('settings/set', { key: 'wallpaper', value: null });
wallpaperId: null
});
}, },
checkForUpdate() { checkForUpdate() {
this.checkingForUpdate = true; this.checkingForUpdate = true;
@ -542,8 +538,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

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

@ -21,7 +21,7 @@
<fa :icon="['far', 'laugh']"/> <fa :icon="['far', 'laugh']"/>
</button> </button>
</div> </div>
<x-post-form-attaches class="files" :files="files" :detachMediaFn="detachMedia"/> <x-post-form-attaches class="files" :files="files" :detach-media-fn="detachMedia"/>
<input ref="file" type="file" multiple="multiple" tabindex="-1" @change="onChangeFile"/> <input ref="file" type="file" multiple="multiple" tabindex="-1" @change="onChangeFile"/>
<mk-uploader ref="uploader" @uploaded="attachMedia"/> <mk-uploader ref="uploader" @uploaded="attachMedia"/>
<footer> <footer>
@ -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

@ -142,7 +142,7 @@ export default Vue.extend({
if (this.file.properties.avgColor) { if (this.file.properties.avgColor) {
anime({ anime({
targets: this.$refs.thumbnail, targets: this.$refs.thumbnail,
backgroundColor: this.file.properties.avgColor.replace('255)', '0)'), backgroundColor: 'transparent', // TODO fade
duration: 100, duration: 100,
easing: 'linear' easing: 'linear'
}); });

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

@ -87,6 +87,5 @@ export default Vue.extend({
height 100% height 100%
flex auto flex auto
overflow auto overflow auto
background var(--bg)
</style> </style>

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="mk-ui" v-hotkey.global="keymap"> <div class="mk-ui" v-hotkey.global="keymap">
<div class="bg" v-if="$store.getters.isSignedIn && $store.state.i.wallpaperUrl" :style="style"></div> <div class="bg" v-if="$store.getters.isSignedIn && $store.state.settings.wallpaper" :style="style"></div>
<x-header class="header" v-if="navbar == 'top'" v-show="!zenMode" ref="header"/> <x-header class="header" v-if="navbar == 'top'" v-show="!zenMode" ref="header"/>
<x-sidebar class="sidebar" v-if="navbar != 'top'" v-show="!zenMode" ref="sidebar"/> <x-sidebar class="sidebar" v-if="navbar != 'top'" v-show="!zenMode" ref="sidebar"/>
<div class="content" :class="[{ sidebar: navbar != 'top', zen: zenMode }, navbar]"> <div class="content" :class="[{ sidebar: navbar != 'top', zen: zenMode }, navbar]">
@ -33,10 +33,9 @@ export default Vue.extend({
}, },
style(): any { style(): any {
if (!this.$store.getters.isSignedIn || this.$store.state.i.wallpaperUrl == null) return {}; if (!this.$store.getters.isSignedIn || this.$store.state.settings.wallpaper == null) return {};
return { return {
backgroundColor: this.$store.state.i.wallpaperColor && this.$store.state.i.wallpaperColor.length == 3 ? `rgb(${ this.$store.state.i.wallpaperColor.join(',') })` : null, backgroundImage: `url(${ this.$store.state.settings.wallpaper })`
backgroundImage: `url(${ this.$store.state.i.wallpaperUrl })`
}; };
}, },
@ -96,7 +95,6 @@ export default Vue.extend({
background-size cover background-size cover
background-position center background-position center
background-attachment fixed background-attachment fixed
opacity 0.3
> .content.sidebar.left > .content.sidebar.left
padding-left 68px padding-left 68px

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

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

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

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

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

@ -28,6 +28,7 @@ const defaultSettings = {
iLikeSushi: false, iLikeSushi: false,
rememberNoteVisibility: false, rememberNoteVisibility: false,
defaultNoteVisibility: 'public', defaultNoteVisibility: 'public',
wallpaper: null,
webSearchEngine: 'https://www.google.com/?#q={{query}}', webSearchEngine: 'https://www.google.com/?#q={{query}}',
mutedWords: [], mutedWords: [],
games: { games: {

View File

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

View File

@ -76,8 +76,6 @@ class MyCustomLogger implements Logger {
} }
export function initDb(justBorrow = false, sync = false, log = false) { export function initDb(justBorrow = false, sync = false, log = false) {
const enableLogging = log || !['production', 'test'].includes(process.env.NODE_ENV || '');
try { try {
const conn = getConnection(); const conn = getConnection();
return Promise.resolve(conn); return Promise.resolve(conn);
@ -92,8 +90,8 @@ export function initDb(justBorrow = false, sync = false, log = false) {
database: config.db.db, database: config.db.db,
synchronize: process.env.NODE_ENV === 'test' || sync, synchronize: process.env.NODE_ENV === 'test' || sync,
dropSchema: process.env.NODE_ENV === 'test' && !justBorrow, dropSchema: process.env.NODE_ENV === 'test' && !justBorrow,
logging: enableLogging, logging: log,
logger: enableLogging ? new MyCustomLogger() : undefined, logger: log ? new MyCustomLogger() : undefined,
entities: [ entities: [
Meta, Meta,
Instance, Instance,

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

@ -141,7 +141,7 @@ export const mfmLanguage = P.createLanguage({
}, },
hashtag: () => P((input, i) => { hashtag: () => P((input, i) => {
const text = input.substr(i); const text = input.substr(i);
const match = text.match(/^#([^\s\.,!\?'"#:\/\[\]]+)/i); const match = text.match(/^#([^\s\.,!\?'"#:\/\[\]【】]+)/i);
if (!match) return P.makeFailure(i, 'not a hashtag'); if (!match) return P.makeFailure(i, 'not a hashtag');
let hashtag = match[1]; let hashtag = match[1];
hashtag = removeOrphanedBrackets(hashtag); hashtag = removeOrphanedBrackets(hashtag);

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

@ -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)],
@ -178,12 +178,12 @@ export class NoteRepository extends Repository<Note> {
name: In(reactionEmojis), name: In(reactionEmojis),
host: host host: host
}) : [], }) : [],
tags: note.tags,
fileIds: note.fileIds, fileIds: note.fileIds,
files: DriveFiles.packMany(note.fileIds), files: DriveFiles.packMany(note.fileIds),
replyId: note.replyId, replyId: note.replyId,
renoteId: note.renoteId, renoteId: note.renoteId,
uri: note.uri, mentions: note.mentions.length > 0 ? note.mentions : undefined,
uri: note.uri || undefined,
...(opts.detail ? { ...(opts.detail ? {
reply: note.replyId ? this.pack(note.replyId, meId, { reply: note.replyId ? this.pack(note.replyId, meId, {

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> {
@ -81,19 +82,21 @@ export class UserRepository extends Repository<User> {
const relation = meId && (meId !== user.id) && opts.detail ? await this.getRelation(meId, user.id) : null; const relation = meId && (meId !== user.id) && opts.detail ? await this.getRelation(meId, user.id) : null;
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(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

@ -1,6 +1,5 @@
import * as Bull from 'bull'; import * as Bull from 'bull';
import * as httpSignature from 'http-signature'; import * as httpSignature from 'http-signature';
import parseAcct from '../../misc/acct/parse';
import { IRemoteUser } from '../../models/entities/user'; import { IRemoteUser } from '../../models/entities/user';
import perform from '../../remote/activitypub/perform'; import perform from '../../remote/activitypub/perform';
import { resolvePerson, updatePerson } from '../../remote/activitypub/models/person'; import { resolvePerson, updatePerson } from '../../remote/activitypub/models/person';
@ -12,7 +11,7 @@ import { Instances, Users, UserPublickeys } from '../../models';
import { instanceChart } from '../../services/chart'; import { instanceChart } from '../../services/chart';
import { UserPublickey } from '../../models/entities/user-publickey'; import { UserPublickey } from '../../models/entities/user-publickey';
import fetchMeta from '../../misc/fetch-meta'; import fetchMeta from '../../misc/fetch-meta';
import { toPuny, toPunyNullable } from '../../misc/convert-host'; import { toPuny } from '../../misc/convert-host';
import { validActor } from '../../remote/activitypub/type'; import { validActor } from '../../remote/activitypub/type';
import { ensure } from '../../prelude/ensure'; import { ensure } from '../../prelude/ensure';
@ -35,68 +34,49 @@ export default async (job: Bull.Job): Promise<void> => {
let key: UserPublickey; let key: UserPublickey;
if (keyIdLower.startsWith('acct:')) { if (keyIdLower.startsWith('acct:')) {
const acct = parseAcct(keyIdLower.slice('acct:'.length)); logger.warn(`Old keyId is no longer supported. ${keyIdLower}`);
const host = toPunyNullable(acct.host); return;
const username = toPuny(acct.username); }
if (host === null) { // アクティビティ内のホストの検証
logger.warn(`request was made by local user: @${username}`); const host = toPuny(new URL(signature.keyId).hostname);
return; try {
ValidateActivity(activity, host);
} catch (e) {
logger.warn(e.message);
return;
}
// ブロックしてたら中断
// TODO: いちいちデータベースにアクセスするのはコスト高そうなのでどっかにキャッシュしておく
const meta = await fetchMeta();
if (meta.blockedHosts.includes(host)) {
logger.info(`Blocked request: ${host}`);
return;
}
const _key = await UserPublickeys.findOne({
keyId: signature.keyId
});
if (_key) {
// 登録済みユーザー
user = await Users.findOne(_key.userId) as IRemoteUser;
key = _key;
} else {
// 未登録ユーザーの場合はリモート解決
user = await resolvePerson(activity.actor) as IRemoteUser;
if (user == null) {
throw new Error('failed to resolve user');
} }
// アクティビティ内のホストの検証
try {
ValidateActivity(activity, host);
} catch (e) {
logger.warn(e.message);
return;
}
// ブロックしてたら中断
// TODO: いちいちデータベースにアクセスするのはコスト高そうなのでどっかにキャッシュしておく
const meta = await fetchMeta();
if (meta.blockedHosts.includes(host)) {
logger.info(`Blocked request: ${host}`);
return;
}
user = await Users.findOne({
usernameLower: username.toLowerCase(),
host: host
}) as IRemoteUser;
key = await UserPublickeys.findOne(user.id).then(ensure); key = await UserPublickeys.findOne(user.id).then(ensure);
} else {
// アクティビティ内のホストの検証
const host = toPuny(new URL(signature.keyId).hostname);
try {
ValidateActivity(activity, host);
} catch (e) {
logger.warn(e.message);
return;
}
// ブロックしてたら中断
// TODO: いちいちデータベースにアクセスするのはコスト高そうなのでどっかにキャッシュしておく
const meta = await fetchMeta();
if (meta.blockedHosts.includes(host)) {
logger.info(`Blocked request: ${host}`);
return;
}
key = await UserPublickeys.findOne({
keyId: signature.keyId
}).then(ensure);
user = await Users.findOne(key.userId) as IRemoteUser;
} }
// Update Person activityの場合は、ここで署名検証/更新処理まで実施して終了 // Update Person activityの場合は、ここで署名検証/更新処理まで実施して終了
if (activity.type === 'Update') { if (activity.type === 'Update') {
if (activity.object && validActor.includes(activity.object.type)) { if (activity.object && validActor.includes(activity.object.type)) {
if (user == null) { if (!httpSignature.verifySignature(signature, key.keyPem)) {
logger.warn('Update activity received, but user not registed.');
} else if (!httpSignature.verifySignature(signature, key.keyPem)) {
logger.warn('Update activity received, but signature verification failed.'); logger.warn('Update activity received, but signature verification failed.');
} else { } else {
updatePerson(activity.actor, null, activity.object); updatePerson(activity.actor, null, activity.object);
@ -105,15 +85,6 @@ export default async (job: Bull.Job): Promise<void> => {
} }
} }
// アクティビティを送信してきたユーザーがまだMisskeyサーバーに登録されていなかったら登録する
if (user == null) {
user = await resolvePerson(activity.actor) as IRemoteUser;
}
if (user == null) {
throw new Error('failed to resolve user');
}
if (!httpSignature.verifySignature(signature, key.keyPem)) { if (!httpSignature.verifySignature(signature, key.keyPem)) {
logger.error('signature verification failed'); logger.error('signature verification failed');
return; return;

View File

@ -20,21 +20,21 @@ export default async (actor: IRemoteUser, activity: IDelete): Promise<void> => {
const uri = (object as any).id; const uri = (object as any).id;
switch (object.type) { switch (object.type) {
case 'Note': case 'Note':
case 'Question': case 'Question':
case 'Article': case 'Article':
deleteNote(actor, uri);
break;
case 'Tombstone':
const note = await Notes.findOne({ uri });
if (note != null) {
deleteNote(actor, uri); deleteNote(actor, uri);
} break;
break;
default: case 'Tombstone':
apLogger.warn(`Unknown type: ${object.type}`); const note = await Notes.findOne({ uri });
break; if (note != null) {
deleteNote(actor, uri);
}
break;
default:
apLogger.warn(`Unknown type: ${object.type}`);
break;
} }
}; };

View File

@ -2,6 +2,7 @@ import { IRemoteUser } from '../../../models/entities/user';
import { ILike } from '../type'; import { ILike } from '../type';
import create from '../../../services/note/reaction/create'; import create from '../../../services/note/reaction/create';
import { Notes } from '../../../models'; import { Notes } from '../../../models';
import { apLogger } from '../logger';
export default async (actor: IRemoteUser, activity: ILike) => { export default async (actor: IRemoteUser, activity: ILike) => {
const id = typeof activity.object == 'string' ? activity.object : activity.object.id; const id = typeof activity.object == 'string' ? activity.object : activity.object.id;
@ -14,7 +15,8 @@ export default async (actor: IRemoteUser, activity: ILike) => {
const note = await Notes.findOne(noteId); const note = await Notes.findOne(noteId);
if (note == null) { if (note == null) {
throw new Error(); apLogger.warn(`Like activity recivied, but no such note: ${id}`, { id });
return;
} }
await create(actor, note, activity._misskey_reaction); await create(actor, note, activity._misskey_reaction);

View File

@ -17,7 +17,7 @@ export async function renderPerson(user: ILocalUser) {
const [avatar, banner, profile] = await Promise.all([ const [avatar, banner, profile] = await Promise.all([
user.avatarId ? DriveFiles.findOne(user.avatarId) : Promise.resolve(undefined), user.avatarId ? DriveFiles.findOne(user.avatarId) : Promise.resolve(undefined),
user.bannerId ? DriveFiles.findOne(user.bannerId) : Promise.resolve(undefined), user.bannerId ? DriveFiles.findOne(user.bannerId) : Promise.resolve(undefined),
UserProfiles.findOne({ userId: user.id }).then(ensure) UserProfiles.findOne(user.id).then(ensure)
]); ]);
const attachment: { const attachment: {

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

@ -9,25 +9,69 @@ 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: 'アプリケーションのシークレットキー'
}
}
} }
}; };

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

@ -19,7 +19,7 @@ export const meta = {
export default define(meta, async (ps, user) => { export default define(meta, async (ps, user) => {
const token = ps.token.replace(/\s/g, ''); const token = ps.token.replace(/\s/g, '');
const profile = await UserProfiles.findOne({ userId: user.id }).then(ensure); const profile = await UserProfiles.findOne(user.id).then(ensure);
if (profile.twoFactorTempSecret == null) { if (profile.twoFactorTempSecret == null) {
throw new Error('二段階認証の設定が開始されていません'); throw new Error('二段階認証の設定が開始されていません');

View File

@ -20,7 +20,7 @@ export const meta = {
}; };
export default define(meta, async (ps, user) => { export default define(meta, async (ps, user) => {
const profile = await UserProfiles.findOne({ userId: user.id }).then(ensure); const profile = await UserProfiles.findOne(user.id).then(ensure);
// Compare password // Compare password
const same = await bcrypt.compare(ps.password, profile.password!); const same = await bcrypt.compare(ps.password, profile.password!);

View File

@ -17,7 +17,7 @@ export const meta = {
}; };
export default define(meta, async (ps, user) => { export default define(meta, async (ps, user) => {
const profile = await UserProfiles.findOne({ userId: user.id }).then(ensure); const profile = await UserProfiles.findOne(user.id).then(ensure);
// Compare password // Compare password
const same = await bcrypt.compare(ps.password, profile.password!); const same = await bcrypt.compare(ps.password, profile.password!);

View File

@ -21,7 +21,7 @@ export const meta = {
}; };
export default define(meta, async (ps, user) => { export default define(meta, async (ps, user) => {
const profile = await UserProfiles.findOne({ userId: user.id }).then(ensure); const profile = await UserProfiles.findOne(user.id).then(ensure);
// Compare password // Compare password
const same = await bcrypt.compare(ps.currentPassword, profile.password!); const same = await bcrypt.compare(ps.currentPassword, profile.password!);

View File

@ -17,7 +17,7 @@ export const meta = {
}; };
export default define(meta, async (ps, user) => { export default define(meta, async (ps, user) => {
const profile = await UserProfiles.findOne({ userId: user.id }).then(ensure); const profile = await UserProfiles.findOne(user.id).then(ensure);
// Compare password // Compare password
const same = await bcrypt.compare(ps.password, profile.password!); const same = await bcrypt.compare(ps.password, profile.password!);

View File

@ -19,7 +19,7 @@ export const meta = {
}; };
export default define(meta, async (ps, user) => { export default define(meta, async (ps, user) => {
const profile = await UserProfiles.findOne({ userId: user.id }).then(ensure); const profile = await UserProfiles.findOne(user.id).then(ensure);
// Compare password // Compare password
const same = await bcrypt.compare(ps.password, profile.password!); const same = await bcrypt.compare(ps.password, profile.password!);

View File

@ -33,7 +33,7 @@ export const meta = {
}; };
export default define(meta, async (ps, user) => { export default define(meta, async (ps, user) => {
const profile = await UserProfiles.findOne({ userId: user.id }).then(ensure); const profile = await UserProfiles.findOne(user.id).then(ensure);
// Compare password // Compare password
const same = await bcrypt.compare(ps.password, profile.password!); const same = await bcrypt.compare(ps.password, profile.password!);

View File

@ -13,6 +13,7 @@ import { ApiError } from '../../error';
import { Users, DriveFiles, UserProfiles } from '../../../../models'; import { Users, DriveFiles, UserProfiles } from '../../../../models';
import { User } from '../../../../models/entities/user'; import { User } from '../../../../models/entities/user';
import { UserProfile } from '../../../../models/entities/user-profile'; import { UserProfile } from '../../../../models/entities/user-profile';
import { ensure } from '../../../../prelude/ensure';
export const meta = { export const meta = {
desc: { desc: {
@ -157,22 +158,24 @@ export default define(meta, async (ps, user, app) => {
const isSecure = user != null && app == null; const isSecure = user != null && app == null;
const updates = {} as Partial<User>; const updates = {} as Partial<User>;
const profile = {} as Partial<UserProfile>; const profileUpdates = {} as Partial<UserProfile>;
const profile = await UserProfiles.findOne(user.id).then(ensure);
if (ps.name !== undefined) updates.name = ps.name; if (ps.name !== undefined) updates.name = ps.name;
if (ps.description !== undefined) profile.description = ps.description; if (ps.description !== undefined) profileUpdates.description = ps.description;
//if (ps.lang !== undefined) updates.lang = ps.lang; //if (ps.lang !== undefined) updates.lang = ps.lang;
if (ps.location !== undefined) profile.location = ps.location; if (ps.location !== undefined) profileUpdates.location = ps.location;
if (ps.birthday !== undefined) profile.birthday = ps.birthday; if (ps.birthday !== undefined) profileUpdates.birthday = ps.birthday;
if (ps.avatarId !== undefined) updates.avatarId = ps.avatarId; if (ps.avatarId !== undefined) updates.avatarId = ps.avatarId;
if (ps.bannerId !== undefined) updates.bannerId = ps.bannerId; if (ps.bannerId !== undefined) updates.bannerId = ps.bannerId;
if (typeof ps.isLocked == 'boolean') updates.isLocked = ps.isLocked; if (typeof ps.isLocked == 'boolean') updates.isLocked = ps.isLocked;
if (typeof ps.isBot == 'boolean') updates.isBot = ps.isBot; if (typeof ps.isBot == 'boolean') updates.isBot = ps.isBot;
if (typeof ps.carefulBot == 'boolean') profile.carefulBot = ps.carefulBot; if (typeof ps.carefulBot == 'boolean') profileUpdates.carefulBot = ps.carefulBot;
if (typeof ps.autoAcceptFollowed == 'boolean') profile.autoAcceptFollowed = ps.autoAcceptFollowed; if (typeof ps.autoAcceptFollowed == 'boolean') profileUpdates.autoAcceptFollowed = ps.autoAcceptFollowed;
if (typeof ps.isCat == 'boolean') updates.isCat = ps.isCat; if (typeof ps.isCat == 'boolean') updates.isCat = ps.isCat;
if (typeof ps.autoWatch == 'boolean') profile.autoWatch = ps.autoWatch; if (typeof ps.autoWatch == 'boolean') profileUpdates.autoWatch = ps.autoWatch;
if (typeof ps.alwaysMarkNsfw == 'boolean') profile.alwaysMarkNsfw = ps.alwaysMarkNsfw; if (typeof ps.alwaysMarkNsfw == 'boolean') profileUpdates.alwaysMarkNsfw = ps.alwaysMarkNsfw;
if (ps.avatarId) { if (ps.avatarId) {
const avatar = await DriveFiles.findOne(ps.avatarId); const avatar = await DriveFiles.findOne(ps.avatarId);
@ -201,16 +204,20 @@ export default define(meta, async (ps, user, app) => {
} }
//#region emojis/tags //#region emojis/tags
let emojis = [] as string[]; let emojis = [] as string[];
let tags = [] as string[]; let tags = [] as string[];
if (updates.name != null) { const newName = updates.name === undefined ? user.name : updates.name;
const tokens = parsePlain(updates.name); const newDescription = profileUpdates.description === undefined ? profile.description : profileUpdates.description;
if (newName != null) {
const tokens = parsePlain(newName);
emojis = emojis.concat(extractEmojis(tokens!)); emojis = emojis.concat(extractEmojis(tokens!));
} }
if (profile.description != null) { if (newDescription != null) {
const tokens = parse(profile.description); const tokens = parse(newDescription);
emojis = emojis.concat(extractEmojis(tokens!)); emojis = emojis.concat(extractEmojis(tokens!));
tags = extractHashtags(tokens!).map(tag => tag.toLowerCase()); tags = extractHashtags(tokens!).map(tag => tag.toLowerCase());
} }
@ -224,7 +231,7 @@ export default define(meta, async (ps, user, app) => {
//#endregion //#endregion
if (Object.keys(updates).length > 0) await Users.update(user.id, updates); if (Object.keys(updates).length > 0) await Users.update(user.id, updates);
if (Object.keys(profile).length > 0) await UserProfiles.update({ userId: user.id }, profile); if (Object.keys(profileUpdates).length > 0) await UserProfiles.update({ userId: user.id }, profileUpdates);
const iObj = await Users.pack(user.id, user, { const iObj = await Users.pack(user.id, user, {
detail: true, detail: true,

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

@ -150,7 +150,7 @@ export default define(meta, async (ps, user) => {
} }
}); });
const profile = await UserProfiles.findOne({ userId: user.id }).then(ensure); const profile = await UserProfiles.findOne(user.id).then(ensure);
// この投稿をWatchする // この投稿をWatchする
if (profile.autoWatch !== false) { if (profile.autoWatch !== false) {

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

View File

@ -1,4 +1,3 @@
export const schemas = { export const schemas = {
Error: { Error: {
type: 'object', type: 'object',

View File

@ -46,7 +46,7 @@ export default async (ctx: Koa.BaseContext) => {
return; return;
} }
const profile = await UserProfiles.findOne({ userId: user.id }).then(ensure); const profile = await UserProfiles.findOne(user.id).then(ensure);
// Compare password // Compare password
const same = await bcrypt.compare(password, profile.password!); const same = await bcrypt.compare(password, profile.password!);

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;
} }
} }

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