Compare commits
81 Commits
Author | SHA1 | Date | |
---|---|---|---|
929982117f | |||
56a530d769 | |||
7ef75fb06b | |||
112a72abdf | |||
71813e03ee | |||
9b05b6ef28 | |||
54a87b25b3 | |||
55e97864bd | |||
95733c9490 | |||
e19ae644f1 | |||
ac4ea25267 | |||
611e4f34dc | |||
faf017f333 | |||
5eec896615 | |||
17f35174ea | |||
bf71b31123 | |||
9399a44c82 | |||
c96418806f | |||
7945eddef6 | |||
0ede390fef | |||
85959a3b9b | |||
946c3a25b9 | |||
f9d697128a | |||
532ef744f4 | |||
27a961814b | |||
5a5b65e9bf | |||
96673ad610 | |||
a9025aea0d | |||
f38ab0b973 | |||
150dac00cf | |||
bcb1a9c5d3 | |||
2e55aea584 | |||
4f5a3f0df5 | |||
e265b538cc | |||
b186504718 | |||
fc27890f13 | |||
a583939767 | |||
52e3bcfd29 | |||
85d29a3f9d | |||
18944d389d | |||
e90ac5d6a4 | |||
cb9a6ae774 | |||
3ef09aa6b2 | |||
4db22e45a2 | |||
f966d0b32c | |||
ba3879a95a | |||
c6cef0162d | |||
6d09aa86e9 | |||
b711f0f9c6 | |||
8fefb3a4c9 | |||
400cdf0f26 | |||
bce8c5a315 | |||
f44dc2dd05 | |||
df950d2fc5 | |||
5e1f804dd1 | |||
15de89f2f9 | |||
df647a415c | |||
fc66231f8e | |||
71df3e1566 | |||
168c22fc98 | |||
792ec23d7a | |||
ff625253ce | |||
8c872c6b22 | |||
541f5bc0a6 | |||
77ff7b9df0 | |||
203cc5075e | |||
7abfcd06da | |||
724f81c7f3 | |||
4d2e98af7b | |||
08221fdda7 | |||
c0f72970b9 | |||
18bc4a49e8 | |||
f90b6dbed4 | |||
d2d991ff34 | |||
190a5e175b | |||
a05f5a91b8 | |||
4d02ff27be | |||
df92b41d25 | |||
075af96338 | |||
64bbc55336 | |||
06c621acc1 |
@ -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:
|
||||||
|
69
CHANGELOG.md
69
CHANGELOG.md
@ -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でフォローしていないユーザーの非公開投稿が流れる問題を修正
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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`
|
||||||
|
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
|
@ -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)でマイグレーション情報を確認する
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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)でマイグレーション情報を確認する
|
||||||
|
@ -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."
|
|
||||||
|
@ -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"
|
||||||
|
@ -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"
|
||||||
|
@ -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"
|
||||||
|
@ -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 l’application. Veuillez réessayer."
|
dev: "Échec lors de la création de l’application. 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 d’une application"
|
create-app: "Création d’une application"
|
||||||
app-name: "Nom de l’application"
|
app-name: "Nom de l’application"
|
||||||
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 l’application"
|
app-overview: "Description courte de l’application"
|
||||||
app-desc: "Brève description introductive à votre application."
|
|
||||||
app-desc-ex: "p. ex) Misskey pour iOS"
|
|
||||||
callback-url: "L’Url de callback (facultatif)"
|
callback-url: "L’Url de callback (facultatif)"
|
||||||
callback-url-desc: "Vous pouvez définir l’URL de redirection lorsque l’utilisateur s’est authentifié via formulaire d’authentification."
|
callback-url-desc: "Vous pouvez définir l’URL de redirection lorsque l’utilisateur s’est authentifié via formulaire d’authentification."
|
||||||
authority: "Autorisations "
|
authority: "Autorisations "
|
||||||
|
@ -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: "ユーザー名を入力してください"
|
||||||
|
|
||||||
|
@ -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: "権限"
|
||||||
|
@ -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: "권한"
|
||||||
|
@ -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"
|
||||||
|
@ -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"
|
||||||
|
@ -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:
|
||||||
|
@ -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访问此处请求的功能。"
|
||||||
|
@ -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",
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
});
|
});
|
||||||
|
@ -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;
|
||||||
|
@ -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'
|
||||||
});
|
});
|
||||||
|
@ -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: {
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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: {
|
||||||
|
@ -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>
|
||||||
|
|
||||||
|
@ -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({
|
||||||
|
@ -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;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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)">
|
||||||
|
@ -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(() => {
|
||||||
|
@ -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'
|
||||||
});
|
});
|
||||||
|
@ -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')
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -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')
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
});
|
});
|
||||||
|
@ -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>
|
||||||
|
@ -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
|
||||||
|
@ -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"
|
||||||
>
|
>
|
||||||
|
@ -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';
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -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}`);
|
||||||
|
@ -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();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
});
|
});
|
||||||
|
@ -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() {
|
||||||
|
@ -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) {
|
||||||
|
@ -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';
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -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() {
|
||||||
|
@ -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) {
|
||||||
|
@ -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: {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@charset 'utf-8'
|
@charset "utf-8"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
::selection
|
::selection
|
||||||
|
@ -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,
|
||||||
|
@ -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!'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -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);
|
||||||
|
@ -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
90
src/misc/gen-avatar.ts
Normal 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);
|
||||||
|
}
|
@ -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, {
|
||||||
|
@ -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', {
|
||||||
|
@ -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: '{}'
|
||||||
})
|
})
|
||||||
|
@ -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, {
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -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);
|
||||||
|
@ -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: {
|
||||||
|
@ -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];
|
||||||
}
|
}
|
||||||
|
@ -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({
|
||||||
|
@ -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);
|
||||||
|
@ -1 +1 @@
|
|||||||
export default (token: string) => token.startsWith('0');
|
export default (token: string) => token.length === 16;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -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: 'アプリケーションのシークレットキー'
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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({
|
||||||
|
@ -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: {
|
||||||
|
@ -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);
|
||||||
});
|
});
|
||||||
|
@ -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);
|
||||||
});
|
});
|
||||||
|
@ -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();
|
||||||
|
@ -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('二段階認証の設定が開始されていません');
|
||||||
|
@ -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!);
|
||||||
|
@ -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!);
|
||||||
|
@ -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!);
|
||||||
|
@ -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!);
|
||||||
|
@ -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!);
|
||||||
|
@ -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!);
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
@ -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) {
|
||||||
|
@ -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,
|
||||||
|
@ -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);
|
||||||
});
|
});
|
||||||
|
@ -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)
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export const schemas = {
|
export const schemas = {
|
||||||
Error: {
|
Error: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
|
@ -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!);
|
||||||
|
@ -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
Reference in New Issue
Block a user