Merge branch 'develop' of https://github.com/syuilo/misskey into develop

This commit is contained in:
Acid Chicken (硫酸鶏) 2019-07-20 02:02:48 +09:00
commit 42af8c7695
No known key found for this signature in database
GPG key ID: 5388F56C75B677A1
52 changed files with 12992 additions and 107 deletions

2
.gitattributes vendored
View file

@ -1,5 +1,3 @@
*.svg -diff -text *.svg -diff -text
*.psd -diff -text *.psd -diff -text
*.ai -diff -text *.ai -diff -text
yarn.lock -diff -text
package-lock.json -diff -text

3
.gitignore vendored
View file

@ -7,9 +7,6 @@
# Node.js # Node.js
/node_modules /node_modules
# yarn
yarn.lock
# config # config
/.config/* /.config/*
!/.config/example.yml !/.config/example.yml

View file

@ -1,6 +1,38 @@
ChangeLog ChangeLog
========= =========
11.26.0 (2019/07/19)
--------------------
### ✨Improvements
* モデレーターログを記録して確認できるように
* プロフィールに追加情報を設定できるように
* Mastodonのリンクの所有者認証に対応
* AP: Delete Person アクティビティを配信するように
* AP: Delete アクティビティの後にフォロー解除するように
* AP: アカウント削除でもDelete activityを配信するように
* Pages: ラジオボタンを追加
* AdminページのUsers Viewでユーザーのレコードをクリックすることですぐユーザーを照会できるように
* AdminページのUsers Viewでユーザー一覧からユーザー名とホスト情報で検索できるように
* 特定ホストへのメンションの特別処理をクライアントに追加
* 設定画面でデスクトップ・モバイルモード変更時はすぐにrefreshするか伺うように
* ペーストされたファイル名のテンプレート変更時すぐどのようになるか見れるように
* (コ`・ヘ・´ケ)を追加
### 🐛Fixes
* ログインのログが正しく保存されない問題を修正
* 同じユーザー名のユーザーが作成できてしまうことがある問題を修正
* 報告されたレポート内容が表示されない問題を修正
* リモートのプロフィールの追加情報が表示されない問題を修正
* 「見つける」のタグが大文字小文字区別されている問題を修正
* 管理画面のインスタンス一覧でソートが正しく機能していない問題を修正
* プロフィール設定でバナーに動画を設定すると以降編集ができない問題を修正
* ウェブ検索エンジンの設定でグリッチが発生する問題を修正
* MFMの引用がインライン表示になっている問題を修正
* アンケートの期限入力部分のタイトル表示がおかしい問題を修正
* 画面上の項目がすべていなくなると実際はロードされてないだけのファイルやフォルダーがあるにも関わらず「もっと読み込む」ボタンがなくなり「このフォルダーは空です」っていうplaceholderが表示されてしまう問題を修正
* proxy-media後のContent-Typeが違う問題を修正
* ビルド時にエラーが出るのを修正
11.25.1 (2019/07/09) 11.25.1 (2019/07/09)
-------------------- --------------------
### 🐛Fixes ### 🐛Fixes
@ -637,9 +669,9 @@ mongodb:
db: misskey db: misskey
``` ```
3. migration ブランチに切り替え 3. migration ブランチに切り替え
4. `npm i` 4. `yarn install`
5. `npm run build` 5. `yarn build`
6. `npm run migrate` 6. `yarn migrate`
7. master ブランチに戻す 7. master ブランチに戻す
8. enjoy 8. enjoy

View file

@ -7,18 +7,18 @@ Feature suggestions and bug reports are filed in https://github.com/syuilo/missk
* Please search existing issues to avoid duplication. If your issue is already filed, please add your reaction or comment to the existing one. * Please search existing issues to avoid duplication. If your issue is already filed, please add your reaction or comment to the existing one.
* If you have multiple independent issues, please submit them separately. * If you have multiple independent issues, please submit them separately.
## Localization (l10n) ## Localization (l10n)
Misskey uses [Crowdin](https://crowdin.com/project/misskey) for localization management. Misskey uses [Crowdin](https://crowdin.com/project/misskey) for localization management.
You can improve our translations with your Crowdin account. You can improve our translations with your Crowdin account.
Changes you make in Crowdin will be merged into develop branch. Changes you make in Crowdin will be merged into the develop branch by @syuilo.
If you can't find the language you want to contribute with, please open an issue. If you cannot find the language you want to contribute with, please open an issue.
![Crowdin](https://d322cqt584bo4o.cloudfront.net/misskey/localized.svg) ![Crowdin](https://d322cqt584bo4o.cloudfront.net/misskey/localized.svg)
## Internationalization (i18n) ## Internationalization (i18n)
Misskey uses [vue-i18n](https://github.com/kazupon/vue-i18n). Misskey uses the Vue.js plugin [Vue I18n](https://github.com/kazupon/vue-i18n).
Documentation of Vue I18n is available at http://kazupon.github.io/vue-i18n/introduction.html .
## Documentation ## Documentation
* Documents for contributors are located in [`/docs`](/docs). * Documents for contributors are located in [`/docs`](/docs).
@ -29,9 +29,15 @@ Misskey uses [vue-i18n](https://github.com/kazupon/vue-i18n).
* Test codes are located in [`/test`](/test). * Test codes are located in [`/test`](/test).
## Continuous integration ## Continuous integration
Misskey uses CircleCI for automated test. Misskey uses CircleCI for executing automated tests.
Configuration files are located in [`/.circleci`](/.circleci). Configuration files are located in [`/.circleci`](/.circleci).
## FAQ
### How to resolve conflictions occurred at yarn.lock?
Just execute `yarn` to fix it.
## Glossary ## Glossary
### AP ### AP
Stands for _**A**ctivity**P**ub_. Stands for _**A**ctivity**P**ub_.
@ -51,11 +57,15 @@ Convert な(na) to にゃ(nya)
#### Denyaize #### Denyaize
Revert Nyaize Revert Nyaize
## Code style ## TypeScript Coding Style
### セミコロンを省略しない ### Do not omit semicolons
ASI Hazardを避けるためでもある This is to avoid Automatic Semicolon Insertion (ASI) hazard.
### 中括弧を省略しない Ref:
* https://www.ecma-international.org/ecma-262/#sec-automatic-semicolon-insertion
* https://github.com/tc39/ecma262/pull/1062
### Do not omit curly brackets
Bad: Bad:
``` ts ``` ts
if (foo) if (foo)
@ -73,16 +83,20 @@ if (foo) {
} }
``` ```
ただし**`if`が一行**の時だけは省略しても良い As a special case, you can omit the curly brackets if
* the body of the `if`-statement have only one statement and,
* the `if`-statement does not have `else`-clause.
Good: Good:
``` ts ``` ts
if (foo) bar; if (foo) bar;
``` ```
### 特別な理由がない限り`===`を使う ### Do not use `==` when it can simply be replaced with `===`.
🥰 🥰
### null系を除いて、bool以外の値をifに渡さない ### Use only boolean (or null related) values in the condition of an `if`-statement.
Bad: Bad:
``` ts ``` ts
if (foo.length) if (foo.length)
@ -93,12 +107,12 @@ Good:
if (foo.length > 0) if (foo.length > 0)
``` ```
### `export default`を使わない ### Do not use `export default`
インテリセンスと相性が悪かったりするため This is because the current language support does not work well with `export default`.
参考: Ref:
* https://gfx.hatenablog.com/entry/2017/11/24/135343
* https://basarat.gitbooks.io/typescript/docs/tips/defaultIsBad.html * https://basarat.gitbooks.io/typescript/docs/tips/defaultIsBad.html
* https://gfx.hatenablog.com/entry/2017/11/24/135343
Bad: Bad:
``` ts ``` ts

View file

@ -23,9 +23,9 @@ RUN apk add --no-cache \
zlib-dev zlib-dev
COPY package.json ./ COPY package.json ./
RUN npm i RUN yarn install
COPY . ./ COPY . ./
RUN npm run build RUN yarn build
FROM base AS runner FROM base AS runner

View file

@ -104,38 +104,38 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<!-- PATREON_START --> <!-- PATREON_START -->
<table><tr> <table><tr>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5888816/36da0f7c15954df0ab13f9abdf227f66/1.jpeg?token-time=2145916800&token-hash=at8QpJXJ8C0zINY_NmoMKv-MhXVoUK-YzTgaJPJzJYU%3D" alt="Hiroshi Seki" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5888816/36da0f7c15954df0ab13f9abdf227f66/1.jpeg?token-time=2145916800&token-hash=at8QpJXJ8C0zINY_NmoMKv-MhXVoUK-YzTgaJPJzJYU%3D" alt="Hiroshi Seki" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/20010324/b8af4bd31ae34fbf8806cc0e6228e400/1.png?token-time=2145916800&token-hash=iyiocfousNIUwASmatsIDq8EOsmLUdrQNkWyktHlmJg%3D" alt="Nemo" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12190916/fb7fa7983c14425f890369535b1506a4/3.png?token-time=2145916800&token-hash=oH_i7gJjNT7Ot6j9JiVwy7ZJIBqACVnzLqlz4YrDAZA%3D" alt="weepjp" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12190916/fb7fa7983c14425f890369535b1506a4/3.png?token-time=2145916800&token-hash=oH_i7gJjNT7Ot6j9JiVwy7ZJIBqACVnzLqlz4YrDAZA%3D" alt="weepjp" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/19045173/cb91c0f345c24d4ebfd05f19906d5e26/1.png?token-time=2145916800&token-hash=o_zKBytJs_AxHwSYw_5R8eD0eSJe3RoTR3kR3Q0syN0%3D" alt="kiritan" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/19045173/cb91c0f345c24d4ebfd05f19906d5e26/1.png?token-time=2145916800&token-hash=o_zKBytJs_AxHwSYw_5R8eD0eSJe3RoTR3kR3Q0syN0%3D" alt="kiritan" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/776209" alt="Denshi" width="100"></td> <td><img src="https://c8.patreon.com/2/200/776209" alt="Denshi" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/557245" alt="mkatze" width="100"></td> <td><img src="https://c8.patreon.com/2/200/557245" alt="mkatze" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13099460/43cecdbaa63a40d79bf50a96b9910b9d/1.jpe?token-time=2145916800&token-hash=bqwLTk0Wo0hUJJ8J5y7ii05bLzz-_CDA7Bo0Mp4RFU0%3D" alt="ne_moni" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13099460/43cecdbaa63a40d79bf50a96b9910b9d/1.jpe?token-time=2145916800&token-hash=bqwLTk0Wo0hUJJ8J5y7ii05bLzz-_CDA7Bo0Mp4RFU0%3D" alt="ne_moni" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12913507/f7181eacafe8469a93033d85f5969c29/4.jpe?token-time=2145916800&token-hash=zEyJqVM7u9d8Ri-65fJYSJcWF1jBH1nJ5a3taRzrTmw%3D" alt="Melilot" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12913507/f7181eacafe8469a93033d85f5969c29/4.jpe?token-time=2145916800&token-hash=zEyJqVM7u9d8Ri-65fJYSJcWF1jBH1nJ5a3taRzrTmw%3D" alt="Melilot" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5670915/ee175f0bfb6347ffa4ea101a8c097bff/1.jpg?token-time=2145916800&token-hash=mPLM9CA-riFHx-myr3bLZJuH2xBRHA9se5VbHhLIOuA%3D" alt="osapon" width="100"></td>
</tr><tr> </tr><tr>
<td><a href="https://www.patreon.com/rane_hs">Hiroshi Seki</a></td> <td><a href="https://www.patreon.com/rane_hs">Hiroshi Seki</a></td>
<td><a href="https://www.patreon.com/user?u=20010324">Nemo</a></td>
<td><a href="https://www.patreon.com/weepjp">weepjp</a></td> <td><a href="https://www.patreon.com/weepjp">weepjp</a></td>
<td><a href="https://www.patreon.com/user?u=19045173">kiritan</a></td> <td><a href="https://www.patreon.com/user?u=19045173">kiritan</a></td>
<td><a href="https://www.patreon.com/user?u=776209">Denshi</a></td> <td><a href="https://www.patreon.com/user?u=776209">Denshi</a></td>
<td><a href="https://www.patreon.com/user?u=557245">mkatze</a></td> <td><a href="https://www.patreon.com/user?u=557245">mkatze</a></td>
<td><a href="https://www.patreon.com/user?u=13099460">ne_moni</a></td> <td><a href="https://www.patreon.com/user?u=13099460">ne_moni</a></td>
<td><a href="https://www.patreon.com/user?u=12913507">Melilot</a></td> <td><a href="https://www.patreon.com/user?u=12913507">Melilot</a></td>
<td><a href="https://www.patreon.com/osapon">osapon</a></td>
</tr></table> </tr></table>
<table><tr> <table><tr>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5670915/ee175f0bfb6347ffa4ea101a8c097bff/1.jpg?token-time=2145916800&token-hash=mPLM9CA-riFHx-myr3bLZJuH2xBRHA9se5VbHhLIOuA%3D" alt="osapon" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/16869916" alt="見当かなみ" width="100"></td> <td><img src="https://c8.patreon.com/2/200/16869916" alt="見当かなみ" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/18899730/6a22797f68254034a854d69ea2445fc8/1.png?token-time=2145916800&token-hash=b_uj57yxo5VzkSOUS7oXE_762dyOTB_oxzbO6lFNG3k%3D" alt="YuzuRyo61" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/18899730/6a22797f68254034a854d69ea2445fc8/1.png?token-time=2145916800&token-hash=b_uj57yxo5VzkSOUS7oXE_762dyOTB_oxzbO6lFNG3k%3D" alt="YuzuRyo61" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/11357794/923ce94cd8c44ba788ee931907881839/1.png?token-time=2145916800&token-hash=9nEQje_eMvUjq9a7L3uBqW-MQbS-rRMaMgd7UYVoFNM%3D" alt="mydarkstar" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/11357794/923ce94cd8c44ba788ee931907881839/1.png?token-time=2145916800&token-hash=9nEQje_eMvUjq9a7L3uBqW-MQbS-rRMaMgd7UYVoFNM%3D" alt="mydarkstar" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/12718187" alt="Peter G." width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13039004/509d0c412eb14ae08d6a812a3054f7d6/1.jpe?token-time=2145916800&token-hash=UQRWf01TwHDV4Cls1K0YAOAjM29ssif7hLVq0ESQ0hs%3D" alt="nemu" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13039004/509d0c412eb14ae08d6a812a3054f7d6/1.jpe?token-time=2145916800&token-hash=UQRWf01TwHDV4Cls1K0YAOAjM29ssif7hLVq0ESQ0hs%3D" alt="nemu" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/17866454" alt="sikyosyounin" width="100"></td> <td><img src="https://c8.patreon.com/2/200/17866454" alt="sikyosyounin" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5881381/6235ca5d3fb04c8e95ef5b4ff2abcc18/3.png?token-time=2145916800&token-hash=KjfQL8nf3AIf6WqzLshBYAyX44piAqOAZiYXgZS_H6A%3D" alt="YUKIMOCHI" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5881381/6235ca5d3fb04c8e95ef5b4ff2abcc18/3.png?token-time=2145916800&token-hash=KjfQL8nf3AIf6WqzLshBYAyX44piAqOAZiYXgZS_H6A%3D" alt="YUKIMOCHI" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/17463605" alt="Sampot" width="100"></td> <td><img src="https://c8.patreon.com/2/200/17463605" alt="Sampot" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/19356899/496b4681d33b4520bd7688e0fd19c04d/2.jpeg?token-time=2145916800&token-hash=_sTj3dUBOhn9qwiJ7F19Qd-yWWfUqJC_0jG1h0agEqQ%3D" alt="sheeta.s" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/19356899/496b4681d33b4520bd7688e0fd19c04d/2.jpeg?token-time=2145916800&token-hash=_sTj3dUBOhn9qwiJ7F19Qd-yWWfUqJC_0jG1h0agEqQ%3D" alt="sheeta.s" width="100"></td>
</tr><tr> </tr><tr>
<td><a href="https://www.patreon.com/osapon">osapon</a></td>
<td><a href="https://www.patreon.com/user?u=16869916">見当かなみ</a></td> <td><a href="https://www.patreon.com/user?u=16869916">見当かなみ</a></td>
<td><a href="https://www.patreon.com/Yuzulia">YuzuRyo61</a></td> <td><a href="https://www.patreon.com/Yuzulia">YuzuRyo61</a></td>
<td><a href="https://www.patreon.com/mydarkstar">mydarkstar</a></td> <td><a href="https://www.patreon.com/mydarkstar">mydarkstar</a></td>
<td><a href="https://www.patreon.com/user?u=12718187">Peter G.</a></td>
<td><a href="https://www.patreon.com/user?u=13039004">nemu</a></td> <td><a href="https://www.patreon.com/user?u=13039004">nemu</a></td>
<td><a href="https://www.patreon.com/user?u=17866454">sikyosyounin</a></td> <td><a href="https://www.patreon.com/user?u=17866454">sikyosyounin</a></td>
<td><a href="https://www.patreon.com/yukimochi">YUKIMOCHI</a></td> <td><a href="https://www.patreon.com/yukimochi">YUKIMOCHI</a></td>
@ -175,7 +175,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td> <td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td>
</tr></table> </tr></table>
**Last updated:** Mon, 01 Jul 2019 21:44:06 UTC **Last updated:** Fri, 19 Jul 2019 15:41:09 UTC
<!-- PATREON_END --> <!-- PATREON_END -->
:four_leaf_clover: Copyright :four_leaf_clover: Copyright

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

After

Width:  |  Height:  |  Size: 148 KiB

View file

@ -68,7 +68,7 @@ Build misskey with the following:
*5.* Init DB *5.* Init DB
---------------------------------------------------------------- ----------------------------------------------------------------
``` shell ``` shell
docker-compose run --rm web npm run init docker-compose run --rm web yarn run init
``` ```
*6.* That is it. *6.* That is it.

View file

@ -68,7 +68,7 @@ cp docker_example.env docker.env
*5.* データベースを初期化 *5.* データベースを初期化
---------------------------------------------------------------- ----------------------------------------------------------------
``` shell ``` shell
docker-compose run --rm web npm run init docker-compose run --rm web yarn run init
``` ```
*6.* 以上です! *6.* 以上です!

View file

@ -27,6 +27,7 @@ Please install and setup these softwares:
* **[Redis](https://redis.io/)** * **[Redis](https://redis.io/)**
##### Optional ##### Optional
* [Yarn](https://yarnpkg.com/) *Optional but recommended for security reason. If you won't install it, use `npx yarn` instead of `yarn`.*
* [Elasticsearch](https://www.elastic.co/) - required to enable the search feature * [Elasticsearch](https://www.elastic.co/) - required to enable the search feature
* [FFmpeg](https://www.ffmpeg.org/) * [FFmpeg](https://www.ffmpeg.org/)
@ -50,7 +51,7 @@ Please install and setup these softwares:
5. Install misskey dependencies. 5. Install misskey dependencies.
`npm install` `yarn`
*4.* Configure Misskey *4.* Configure Misskey
---------------------------------------------------------------- ----------------------------------------------------------------
@ -65,21 +66,20 @@ Please install and setup these softwares:
Build misskey with the following: Build misskey with the following:
`NODE_ENV=production npm run build` `NODE_ENV=production yarn build`
If you're on Debian, you will need to install the `build-essential`, `python` package. If you're on Debian, you will need to install the `build-essential`, `python` package.
If you're still encountering errors about some modules, use node-gyp: If you're still encountering errors about some modules, use node-gyp:
1. `npm install -g node-gyp` 1. `npx node-gyp configure`
2. `node-gyp configure` 2. `npx node-gyp build`
3. `node-gyp build` 3. `NODE_ENV=production yarn build`
4. `NODE_ENV=production npm run build`
*6.* Init DB *6.* Init DB
---------------------------------------------------------------- ----------------------------------------------------------------
``` shell ``` shell
npm run init yarn run init
``` ```
*7.* That is it. *7.* That is it.
@ -130,15 +130,15 @@ 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 checkout master` 1. `git checkout master`
2. `git pull` 2. `git pull`
3. `npm install` 3. `yarn install`
4. `NODE_ENV=production npm run build` 4. `NODE_ENV=production yarn build`
5. `npm run migrate` 5. `yarn migrate`
6. Restart your Misskey process to apply changes 6. Restart your Misskey process to apply changes
7. Enjoy 7. Enjoy
If you encounter any problems with updating, please try the following: If you encounter any problems with updating, please try the following:
1. `npm run clean` or `npm run cleanall` 1. `yarn clean` or `yarn cleanall`
2. Retry update (Don't forget `npm i`) 2. Retry update (Don't forget `yarn install`
---------------------------------------------------------------- ----------------------------------------------------------------

View file

@ -27,7 +27,8 @@ Installez les paquets suivants :
* **[Redis](https://redis.io/)** * **[Redis](https://redis.io/)**
##### Optionnels ##### Optionnels
* [Elasticsearch](https://www.elastic.co/) - requis pour pouvoir activer la fonctionnalité de recherche * [Yarn](https://yarnpkg.com/) - *recommander pour des raisons de sécurité. Si vous ne l'installez pas, utilisez `npx yarn` au lieu de` yarn`.*
* [Elasticsearch](https://www.elastic.co/) - *requis pour pouvoir activer la fonctionnalité de recherche.*
* [FFmpeg](https://www.ffmpeg.org/) * [FFmpeg](https://www.ffmpeg.org/)
*3.* Installation de Misskey *3.* Installation de Misskey
@ -50,7 +51,7 @@ Installez les paquets suivants :
5. Installez les dépendances de misskey. 5. Installez les dépendances de misskey.
`npm install` `yarn install`
*4.* Création du fichier de configuration *4.* Création du fichier de configuration
---------------------------------------------------------------- ----------------------------------------------------------------
@ -65,23 +66,22 @@ Installez les paquets suivants :
Construisez Misskey comme ceci : Construisez Misskey comme ceci :
`NODE_ENV=production npm run build` `NODE_ENV=production yarn build`
Si vous êtes sous Debian, vous serez amené à installer les paquets `build-essential` et `python`. Si vous êtes sous Debian, vous serez amené à installer les paquets `build-essential` et `python`.
Si vous rencontrez des erreurs concernant certains modules, utilisez node-gyp: Si vous rencontrez des erreurs concernant certains modules, utilisez node-gyp:
1. `npm install -g node-gyp` 1. `npx node-gyp configure`
2. `node-gyp configure` 2. `npx node-gyp build`
3. `node-gyp build` 3. `NODE_ENV=production yarn build`
4. `NODE_ENV=production npm run build`
*6.* C'est tout. *6.* C'est tout.
---------------------------------------------------------------- ----------------------------------------------------------------
Excellent ! Maintenant, vous avez un environnement prêt pour lancer Misskey Excellent ! Maintenant, vous avez un environnement prêt pour lancer Misskey
### Lancement conventionnel ### Lancement conventionnel
Lancez tout simplement `NODE_ENV=production npm start`. Bonne chance et amusez-vous bien ! Lancez tout simplement `NODE_ENV=production yarn start`. Bonne chance et amusez-vous bien !
### Démarrage avec systemd ### Démarrage avec systemd
@ -124,9 +124,9 @@ Vous pouvez vérifier si le service a démarré en utilisant la commande `system
### 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 checkout master` 1. `git checkout master`
2. `git pull` 2. `git pull`
3. `npm install` 3. `yarn install`
4. `NODE_ENV=production npm run build` 4. `NODE_ENV=production yarn build`
5. `npm run migrate` 5. `yarn migrate`
---------------------------------------------------------------- ----------------------------------------------------------------

View file

@ -27,6 +27,8 @@ adduser --disabled-password --disabled-login misskey
* **[Redis](https://redis.io/)** * **[Redis](https://redis.io/)**
##### オプション ##### オプション
* [Yarn](https://yarnpkg.com/)
* セキュリティの観点から推奨されます。 yarn をインストールしない方針の場合は、文章中の `yarn` を適宜 `npx yarn` と読み替えてください。
* [Elasticsearch](https://www.elastic.co/) * [Elasticsearch](https://www.elastic.co/)
* 検索機能を有効にするためにはインストールが必要です。 * 検索機能を有効にするためにはインストールが必要です。
* [FFmpeg](https://www.ffmpeg.org/) * [FFmpeg](https://www.ffmpeg.org/)
@ -51,7 +53,7 @@ adduser --disabled-password --disabled-login misskey
5. Misskeyの依存パッケージをインストール 5. Misskeyの依存パッケージをインストール
`npm install` `yarn install`
*4.* 設定ファイルを作成する *4.* 設定ファイルを作成する
---------------------------------------------------------------- ----------------------------------------------------------------
@ -66,20 +68,19 @@ adduser --disabled-password --disabled-login misskey
次のコマンドでMisskeyをビルドしてください: 次のコマンドでMisskeyをビルドしてください:
`NODE_ENV=production npm run build` `NODE_ENV=production yarn build`
Debianをお使いであれば、`build-essential`パッケージをインストールする必要があります。 Debianをお使いであれば、`build-essential`パッケージをインストールする必要があります。
何らかのモジュールでエラーが発生する場合はnode-gypを使ってください: 何らかのモジュールでエラーが発生する場合はnode-gypを使ってください:
1. `npm install -g node-gyp` 1. `npx node-gyp configure`
2. `node-gyp configure` 2. `npx node-gyp build`
3. `node-gyp build` 3. `NODE_ENV=production yarn build`
4. `NODE_ENV=production npm run build`
*6.* データベースを初期化 *6.* データベースを初期化
---------------------------------------------------------------- ----------------------------------------------------------------
``` shell ``` shell
npm run init yarn run init
``` ```
*7.* 以上です! *7.* 以上です!
@ -87,7 +88,7 @@ npm run init
お疲れ様でした。これでMisskeyを動かす準備は整いました。 お疲れ様でした。これでMisskeyを動かす準備は整いました。
### 通常起動 ### 通常起動
`NODE_ENV=production npm start`するだけです。GLHF! `NODE_ENV=production yarn start`するだけです。GLHF!
### systemdを用いた起動 ### systemdを用いた起動
1. systemdサービスのファイルを作成 1. systemdサービスのファイルを作成
@ -120,7 +121,7 @@ npm run init
3. systemdを再読み込みしmisskeyサービスを有効化 3. systemdを再読み込みしmisskeyサービスを有効化
`systemctl daemon-reload ; systemctl enable misskey` `systemctl daemon-reload; systemctl enable misskey`
4. misskeyサービスの起動 4. misskeyサービスの起動
@ -131,11 +132,11 @@ npm run init
### Misskeyを最新バージョンにアップデートする方法: ### Misskeyを最新バージョンにアップデートする方法:
1. `git checkout master` 1. `git checkout master`
2. `git pull` 2. `git pull`
3. `npm install` 3. `yarn install`
4. `NODE_ENV=production npm run build` 4. `NODE_ENV=production yarn build`
5. `npm run migrate` 5. `yarn migrate`
なにか問題が発生した場合は、`npm run clean`または`npm run cleanall`すると直る場合があります。 なにか問題が発生した場合は、`yarn clean`または`yarn cleanall`すると直る場合があります。
---------------------------------------------------------------- ----------------------------------------------------------------

View file

@ -454,9 +454,12 @@ common/views/components/messaging.vue:
no-history: "Žádná historie" no-history: "Žádná historie"
user: "Uživatel" user: "Uživatel"
group: "Skupina" group: "Skupina"
start-with-user: "Zahájit konverzaci s uživatelem"
start-with-group: "Zahájit skupinovou konverzaci"
common/views/components/messaging-room.vue: common/views/components/messaging-room.vue:
new-message: "Máte novou zprávu" new-message: "Máte novou zprávu"
common/views/components/messaging-room.form.vue: common/views/components/messaging-room.form.vue:
input-message-here: "Sem zadejte zprávu"
send: "Odeslat" send: "Odeslat"
attach-from-local: "Přiložit soubory z Vašeho zařízení" attach-from-local: "Přiložit soubory z Vašeho zařízení"
common/views/components/messaging-room.message.vue: common/views/components/messaging-room.message.vue:
@ -644,6 +647,7 @@ common/views/components/profile-editor.vue:
saved: "Profil byl úspěšně aktualizován" saved: "Profil byl úspěšně aktualizován"
uploading: "Nahrávám" uploading: "Nahrávám"
upload-failed: "Nahrávání selhalo" upload-failed: "Nahrávání selhalo"
unable-to-process: "Operace nemohla být dokončena."
email: "Nastavení e-mailů" email: "Nastavení e-mailů"
email-address: "Emailová adresa" email-address: "Emailová adresa"
email-verified: "Váš e-mail byl ověřen" email-verified: "Váš e-mail byl ověřen"
@ -660,6 +664,7 @@ common/views/components/profile-editor.vue:
danger-zone: "Nebezpečná zóna" danger-zone: "Nebezpečná zóna"
delete-account: "Smazat účet" delete-account: "Smazat účet"
account-deleted: "Váš účet byl smazán. Může chvilku trvat než zmizí všechna data." account-deleted: "Váš účet byl smazán. Může chvilku trvat než zmizí všechna data."
metadata-content: "Obsah"
common/views/components/user-list-editor.vue: common/views/components/user-list-editor.vue:
users: "Uživatel" users: "Uživatel"
rename: "Přejmenovat seznam" rename: "Přejmenovat seznam"
@ -733,6 +738,7 @@ desktop:
avatar: "Avatar" avatar: "Avatar"
uploading-avatar: "Nahrál nový avatar" uploading-avatar: "Nahrál nový avatar"
avatar-updated: "Vaše avatar byl aktualizován" avatar-updated: "Vaše avatar byl aktualizován"
unable-to-process: "Operace nemohla být dokončena."
invalid-filetype: "Tento formát souboru není podporován" invalid-filetype: "Tento formát souboru není podporován"
desktop/views/components/activity.chart.vue: desktop/views/components/activity.chart.vue:
total: "Černá ... Celkem" total: "Černá ... Celkem"
@ -1041,6 +1047,8 @@ admin/views/users.vue:
reset-password-confirm: "Opravdu chcete resetovat Vaše heslo?" reset-password-confirm: "Opravdu chcete resetovat Vaše heslo?"
password-updated: "Heslo je nyní \"{password}\"" password-updated: "Heslo je nyní \"{password}\""
update-remote-user: "Aktualizovat informace o vzdáleném účtu" update-remote-user: "Aktualizovat informace o vzdáleném účtu"
username: "Přezdívka"
host: "Hostitel"
users: users:
title: "Uživatel" title: "Uživatel"
state: state:

View file

@ -686,6 +686,7 @@ common/views/components/profile-editor.vue:
saved: "Profil er opdateret med succes" saved: "Profil er opdateret med succes"
uploading: "Overfører" uploading: "Overfører"
upload-failed: "Fejl ved overførsel" upload-failed: "Fejl ved overførsel"
unable-to-process: "Handlingen kunne ikke gennemføres."
email: "Email indstillinger" email: "Email indstillinger"
email-address: "Email adresse" email-address: "Email adresse"
email-verified: "Din email er blevet bekræftet" email-verified: "Din email er blevet bekræftet"
@ -705,6 +706,7 @@ common/views/components/profile-editor.vue:
danger-zone: "Risici" danger-zone: "Risici"
delete-account: "Slet kontoen" delete-account: "Slet kontoen"
account-deleted: "Kontoen er slettet. Det kan vare lidt, inden alle data forsvinder." account-deleted: "Kontoen er slettet. Det kan vare lidt, inden alle data forsvinder."
metadata-content: "Indhold"
common/views/components/user-list-editor.vue: common/views/components/user-list-editor.vue:
users: "Bruger" users: "Bruger"
rename: "Omdøb listen" rename: "Omdøb listen"
@ -811,6 +813,7 @@ desktop:
uploading-avatar: "Overfør en ny avatar" uploading-avatar: "Overfør en ny avatar"
avatar-updated: "Avatar er overført med succes" avatar-updated: "Avatar er overført med succes"
choose-avatar: "Vælg et billede til din avatar" choose-avatar: "Vælg et billede til din avatar"
unable-to-process: "Handlingen kunne ikke gennemføres."
invalid-filetype: "Denne filtype kan ikke benyttes her" invalid-filetype: "Denne filtype kan ikke benyttes her"
desktop/views/components/activity.chart.vue: desktop/views/components/activity.chart.vue:
total: "Sort ... Total" total: "Sort ... Total"
@ -1278,6 +1281,8 @@ admin/views/users.vue:
remote-user-updated: "Informationen om den eksterne bruger er nu blevet opdateret." remote-user-updated: "Informationen om den eksterne bruger er nu blevet opdateret."
delete-all-files: "Slet alle filer" delete-all-files: "Slet alle filer"
delete-all-files-confirm: "Er du sikker på, at alle filerne skal slettes?" delete-all-files-confirm: "Er du sikker på, at alle filerne skal slettes?"
username: "Brugernavn"
host: "Vært"
users: users:
title: "Bruger" title: "Bruger"
sort: sort:

View file

@ -567,6 +567,7 @@ common/views/components/profile-editor.vue:
avatar: "Avatar" avatar: "Avatar"
banner: "Banner" banner: "Banner"
save: "Speichern" save: "Speichern"
unable-to-process: "Der Vorgang konnte nicht abgeschlossen werden"
export: "Exportieren" export: "Exportieren"
import: "Importieren" import: "Importieren"
export-targets: export-targets:
@ -600,6 +601,7 @@ common/views/widgets/memo.vue:
save: "Speichern" save: "Speichern"
desktop: desktop:
banner: "Banner" banner: "Banner"
unable-to-process: "Der Vorgang konnte nicht abgeschlossen werden"
desktop/views/components/activity.chart.vue: desktop/views/components/activity.chart.vue:
total: "Schwarz ... komplett" total: "Schwarz ... komplett"
notes: "Blau ... Hinweise" notes: "Blau ... Hinweise"
@ -784,6 +786,7 @@ admin/views/drive.vue:
local: "Lokal" local: "Lokal"
delete: "Löschen" delete: "Löschen"
admin/views/users.vue: admin/views/users.vue:
username: "Benutzername"
users: users:
origin: origin:
local: "Lokal" local: "Lokal"

View file

@ -712,7 +712,7 @@ common/views/components/profile-editor.vue:
you-can-include-hashtags: "You can also include hashtags in your profile description." you-can-include-hashtags: "You can also include hashtags in your profile description."
language: "Language" language: "Language"
birthday: "Birthday" birthday: "Birthday"
avatar: "Icon" avatar: "Avatar"
banner: "Banner" banner: "Banner"
is-cat: "This account is a Cat" is-cat: "This account is a Cat"
is-bot: "This account is a Bot" is-bot: "This account is a Bot"
@ -725,6 +725,9 @@ common/views/components/profile-editor.vue:
saved: "Profile updated successfully" saved: "Profile updated successfully"
uploading: "Uploading" uploading: "Uploading"
upload-failed: "Failed to upload" upload-failed: "Failed to upload"
unable-to-process: "The operation could not be completed."
avatar-not-an-image: "The file specified as an avatar is not an image"
banner-not-an-image: "The file specified as a banner is not an image"
email: "Email settings" email: "Email settings"
email-address: "Email Address" email-address: "Email Address"
email-verified: "Your email has been verified." email-verified: "Your email has been verified."
@ -744,6 +747,9 @@ common/views/components/profile-editor.vue:
danger-zone: "Cautious options" danger-zone: "Cautious options"
delete-account: "Remove the account" delete-account: "Remove the account"
account-deleted: "The account has been deleted. It may take some time until all of the data disappears." account-deleted: "The account has been deleted. It may take some time until all of the data disappears."
profile-metadata: "Profile metadata"
metadata-label: "Label"
metadata-content: "Content"
common/views/components/user-list-editor.vue: common/views/components/user-list-editor.vue:
users: "User" users: "User"
rename: "Rename list" rename: "Rename list"
@ -853,6 +859,7 @@ desktop:
uploading-avatar: "Uploading a new avatar" uploading-avatar: "Uploading a new avatar"
avatar-updated: "Successfully updated the avatar" avatar-updated: "Successfully updated the avatar"
choose-avatar: "Select an image for the avatar" choose-avatar: "Select an image for the avatar"
unable-to-process: "The operation could not be completed."
invalid-filetype: "This filetype is not acceptable here" invalid-filetype: "This filetype is not acceptable here"
desktop/views/components/activity.chart.vue: desktop/views/components/activity.chart.vue:
total: "Black ... Total" total: "Black ... Total"
@ -1365,6 +1372,8 @@ admin/views/users.vue:
remote-user-updated: "The information regarding the remote user has been updated." remote-user-updated: "The information regarding the remote user has been updated."
delete-all-files: "Delete all files" delete-all-files: "Delete all files"
delete-all-files-confirm: "Are you sure that you want to delete all files?" delete-all-files-confirm: "Are you sure that you want to delete all files?"
username: "Username"
host: "Host"
users: users:
title: "Users" title: "Users"
sort: sort:

View file

@ -569,6 +569,7 @@ common/views/components/profile-editor.vue:
saved: "Perfil actualizado con exito" saved: "Perfil actualizado con exito"
uploading: "Subiendo" uploading: "Subiendo"
upload-failed: "Error al subir" upload-failed: "Error al subir"
unable-to-process: "La operación no se puede llevar a cabo"
email: "Preferencias de correo" email: "Preferencias de correo"
email-address: "Correo electrónico" email-address: "Correo electrónico"
email-verified: "Tu cuenta de correo ha sido verificada." email-verified: "Tu cuenta de correo ha sido verificada."
@ -671,6 +672,7 @@ desktop:
uploading-avatar: "Cargando un nuevo avatar" uploading-avatar: "Cargando un nuevo avatar"
avatar-updated: "Avatar actualizado" avatar-updated: "Avatar actualizado"
choose-avatar: "Escoge una imagen de avatar" choose-avatar: "Escoge una imagen de avatar"
unable-to-process: "La operación no se puede llevar a cabo"
invalid-filetype: "Este tipo de archivo no es compatible aquí" invalid-filetype: "Este tipo de archivo no es compatible aquí"
desktop/views/components/activity.chart.vue: desktop/views/components/activity.chart.vue:
total: "Negro ... Total" total: "Negro ... Total"
@ -964,6 +966,8 @@ admin/views/drive.vue:
mark-as-sensitive: "Marcar como 'sensible'" mark-as-sensitive: "Marcar como 'sensible'"
unmark-as-sensitive: "Desmarcar como 'sensible'" unmark-as-sensitive: "Desmarcar como 'sensible'"
admin/views/users.vue: admin/views/users.vue:
username: "Usuario"
host: "Host"
users: users:
state: state:
all: "Todo" all: "Todo"

View file

@ -583,6 +583,11 @@ common/views/components/emoji-picker.vue:
symbols: "Symboles" symbols: "Symboles"
flags: "Drapeaux" flags: "Drapeaux"
common/views/components/settings/app-type.vue: common/views/components/settings/app-type.vue:
title: "Mode"
choices:
auto: "Choisir la disposition automatiquement"
desktop: "Toujours utiliser la disposition de bureau"
mobile: "Toujours utiliser la disposition mobile"
info: "Le rechargement de la page est requis afin d'appliquer les modifications." info: "Le rechargement de la page est requis afin d'appliquer les modifications."
common/views/components/signin.vue: common/views/components/signin.vue:
username: "Nom d'utilisateur·rice" username: "Nom d'utilisateur·rice"
@ -700,6 +705,7 @@ common/views/components/profile-editor.vue:
saved: "Profil mis à jour avec succès" saved: "Profil mis à jour avec succès"
uploading: "En cours denvoi …" uploading: "En cours denvoi …"
upload-failed: "Échec de l'envoi" upload-failed: "Échec de l'envoi"
unable-to-process: "L'opération n'a pas pu être complétée"
email: "Paramètres de messagerie" email: "Paramètres de messagerie"
email-address: "Adresse de courrier électronique" email-address: "Adresse de courrier électronique"
email-verified: "Ladresse du courrier électronique a été vérifiée." email-verified: "Ladresse du courrier électronique a été vérifiée."
@ -719,6 +725,7 @@ common/views/components/profile-editor.vue:
danger-zone: "Zone de danger" danger-zone: "Zone de danger"
delete-account: "Supprimer le compte" delete-account: "Supprimer le compte"
account-deleted: "Le compte a été supprimé. Cela peut prendre un certain temps avant que toutes les données disparaissent." account-deleted: "Le compte a été supprimé. Cela peut prendre un certain temps avant que toutes les données disparaissent."
metadata-content: "Contenu"
common/views/components/user-list-editor.vue: common/views/components/user-list-editor.vue:
users: "Utilisateur·rice" users: "Utilisateur·rice"
rename: "Renommer la liste" rename: "Renommer la liste"
@ -827,6 +834,7 @@ desktop:
uploading-avatar: "Téléversement du nouvel avatar" uploading-avatar: "Téléversement du nouvel avatar"
avatar-updated: "Mise à jour de lavatar avec succès" avatar-updated: "Mise à jour de lavatar avec succès"
choose-avatar: "Choisir un avatar" choose-avatar: "Choisir un avatar"
unable-to-process: "L'opération n'a pas pu être complétée"
invalid-filetype: "Ce format de fichier nest pas pris en charge" invalid-filetype: "Ce format de fichier nest pas pris en charge"
desktop/views/components/activity.chart.vue: desktop/views/components/activity.chart.vue:
total: "Noirs ... Total" total: "Noirs ... Total"
@ -887,6 +895,7 @@ desktop/views/components/drive.folder.vue:
rename-folder: "Renommer le dossier" rename-folder: "Renommer le dossier"
input-new-folder-name: "Entrer un nouveau nom" input-new-folder-name: "Entrer un nouveau nom"
else-folders: "Avancé" else-folders: "Avancé"
set-as-upload-folder: "Spécifier en tant que dossier de téléversement par défaut"
desktop/views/components/drive.vue: desktop/views/components/drive.vue:
search: "Rechercher" search: "Rechercher"
empty-draghover: "Drop Welcome!" empty-draghover: "Drop Welcome!"
@ -996,6 +1005,7 @@ desktop/views/components/settings.2fa.vue:
last-used: "Dernière utilisation :" last-used: "Dernière utilisation :"
activate-key: "Cliquez pour activer la clé de sécurité" activate-key: "Cliquez pour activer la clé de sécurité"
security-key-name: "Nom de la clé" security-key-name: "Nom de la clé"
something-went-wrong: "Oula ! Il y a eu un problème lors de lenregistrement de la clé."
key-unregistered: "La clé a été supprimée" key-unregistered: "La clé a été supprimée"
use-password-less-login: "Utiliser une connexion sans mot de passe" use-password-less-login: "Utiliser une connexion sans mot de passe"
common/views/components/media-image.vue: common/views/components/media-image.vue:
@ -1023,6 +1033,7 @@ common/views/components/drive-settings.vue:
in-use: "utilisé" in-use: "utilisé"
stats: "Statistiques" stats: "Statistiques"
default-upload-folder-name: "Dossier·s" default-upload-folder-name: "Dossier·s"
change-default-upload-folder: "Changer de dossier"
common/views/components/mute-and-block.vue: common/views/components/mute-and-block.vue:
mute-and-block: "Silencés / Bloqués" mute-and-block: "Silencés / Bloqués"
mute: "Mettre en sourdine" mute: "Mettre en sourdine"
@ -1313,6 +1324,8 @@ admin/views/users.vue:
remote-user-updated: "Les informations de lutilisateur·rice distant·e ont étés mis à jour" remote-user-updated: "Les informations de lutilisateur·rice distant·e ont étés mis à jour"
delete-all-files: "Supprimer tous les fichiers" delete-all-files: "Supprimer tous les fichiers"
delete-all-files-confirm: "Êtes vous surs de vouloir supprimer tous les fichiers ?" delete-all-files-confirm: "Êtes vous surs de vouloir supprimer tous les fichiers ?"
username: "Nom d'utilisateur·rice"
host: "Hôte"
users: users:
title: "Utilisateur·rice·s" title: "Utilisateur·rice·s"
sort: sort:
@ -1682,6 +1695,7 @@ deck/deck.user-column.vue:
activity: "Activité" activity: "Activité"
timeline: "Fil dactualité" timeline: "Fil dactualité"
pinned-notes: "Notes épinglées" pinned-notes: "Notes épinglées"
pinned-page: "Page épinglée"
docs: docs:
edit-this-page-on-github: "Vous avez trouvé une erreur ou vous voulez contribuer à la documentation ?" edit-this-page-on-github: "Vous avez trouvé une erreur ou vous voulez contribuer à la documentation ?"
edit-this-page-on-github-link: "Éditez cette page sur GitHub !" edit-this-page-on-github-link: "Éditez cette page sur GitHub !"
@ -1797,6 +1811,7 @@ pages:
message: "Message à afficher lorsque appuyé" message: "Message à afficher lorsque appuyé"
variable: "Variable à envoyer" variable: "Variable à envoyer"
no-variable: "Aucune" no-variable: "Aucune"
radioButton: "Choix"
_radioButton: _radioButton:
name: "Nom de la variable" name: "Nom de la variable"
title: "Titre" title: "Titre"

View file

@ -782,6 +782,9 @@ common/views/components/profile-editor.vue:
saved: "プロフィールを保存しました" saved: "プロフィールを保存しました"
uploading: "アップロード中" uploading: "アップロード中"
upload-failed: "アップロードに失敗しました" upload-failed: "アップロードに失敗しました"
unable-to-process: "操作を完了できません"
avatar-not-an-image: "アイコンとして指定したファイルは画像ではありません"
banner-not-an-image: "バナーとして指定したファイルは画像ではありません"
email: "メール設定" email: "メール設定"
email-address: "メールアドレス" email-address: "メールアドレス"
email-verified: "メールアドレスが確認されました" email-verified: "メールアドレスが確認されました"
@ -801,6 +804,9 @@ common/views/components/profile-editor.vue:
danger-zone: "危険な設定" danger-zone: "危険な設定"
delete-account: "アカウントを削除" delete-account: "アカウントを削除"
account-deleted: "アカウントが削除されました。データが消えるまで時間がかかる場合があります。" account-deleted: "アカウントが削除されました。データが消えるまで時間がかかる場合があります。"
profile-metadata: "プロフィール補足情報"
metadata-label: "ラベル"
metadata-content: "内容"
common/views/components/user-list-editor.vue: common/views/components/user-list-editor.vue:
users: "ユーザー" users: "ユーザー"
@ -927,6 +933,7 @@ desktop:
uploading-avatar: "新しいアバターをアップロードしています" uploading-avatar: "新しいアバターをアップロードしています"
avatar-updated: "アバターを更新しました" avatar-updated: "アバターを更新しました"
choose-avatar: "アバターにする画像を選択" choose-avatar: "アバターにする画像を選択"
unable-to-process: "操作を完了できません"
invalid-filetype: "この形式のファイルはサポートされていません" invalid-filetype: "この形式のファイルはサポートされていません"
desktop/views/components/activity.chart.vue: desktop/views/components/activity.chart.vue:
@ -1498,6 +1505,8 @@ admin/views/users.vue:
remote-user-updated: "リモートユーザー情報を更新しました" remote-user-updated: "リモートユーザー情報を更新しました"
delete-all-files: "すべてのファイルを削除" delete-all-files: "すべてのファイルを削除"
delete-all-files-confirm: "すべてのファイルを削除しますか?" delete-all-files-confirm: "すべてのファイルを削除しますか?"
username: "ユーザー名"
host: "ホスト"
users: users:
title: "ユーザー" title: "ユーザー"
sort: sort:

View file

@ -472,6 +472,7 @@ common/views/components/profile-editor.vue:
saved: "プロフィールを保存したで" saved: "プロフィールを保存したで"
uploading: "アップロードしとります" uploading: "アップロードしとります"
upload-failed: "これアップロードでけへんわ" upload-failed: "これアップロードでけへんわ"
unable-to-process: "あかん、無理やわ"
email: "メール設定" email: "メール設定"
email-address: "メールアドレス" email-address: "メールアドレス"
email-verified: "このメールアドレスOKや" email-verified: "このメールアドレスOKや"
@ -565,6 +566,7 @@ desktop:
uploading-avatar: "新しいアバターをアップロードしとるで" uploading-avatar: "新しいアバターをアップロードしとるで"
avatar-updated: "アバターを更新したで" avatar-updated: "アバターを更新したで"
choose-avatar: "アバターにする画像選んでや" choose-avatar: "アバターにする画像選んでや"
unable-to-process: "あかん、無理やわ"
invalid-filetype: "この形式のファイル無理やねん" invalid-filetype: "この形式のファイル無理やねん"
desktop/views/components/activity.chart.vue: desktop/views/components/activity.chart.vue:
total: "黒いの ... 全部" total: "黒いの ... 全部"
@ -944,6 +946,8 @@ admin/views/users.vue:
reset-password: "パスワードをリセット" reset-password: "パスワードをリセット"
password-updated: "パスワードは現在「{password} 」やで" password-updated: "パスワードは現在「{password} 」やで"
suspend: "凍結" suspend: "凍結"
username: "ユーザー名"
host: "ホスト"
users: users:
title: "ユーザー" title: "ユーザー"
state: state:

View file

@ -725,6 +725,9 @@ common/views/components/profile-editor.vue:
saved: "프로필을 저장하였습니다" saved: "프로필을 저장하였습니다"
uploading: "업로드 중" uploading: "업로드 중"
upload-failed: "업로드에 실패하였습니다" upload-failed: "업로드에 실패하였습니다"
unable-to-process: "작업을 완료할 수 없습니다"
avatar-not-an-image: "아바타로 지정한 파일이 이미지 형식이 아닙니다"
banner-not-an-image: "배너로 지정한 파일이 이미지 형식이 아닙니다"
email: "메일 설정" email: "메일 설정"
email-address: "메일 주소" email-address: "메일 주소"
email-verified: "매일 주소가 확인되었습니다" email-verified: "매일 주소가 확인되었습니다"
@ -744,6 +747,9 @@ common/views/components/profile-editor.vue:
danger-zone: "위험한 설정" danger-zone: "위험한 설정"
delete-account: "계정 삭제" delete-account: "계정 삭제"
account-deleted: "계정이 삭제되었습니다. 데이터가 사라질 때까지 시간이 걸릴 수 있습니다." account-deleted: "계정이 삭제되었습니다. 데이터가 사라질 때까지 시간이 걸릴 수 있습니다."
profile-metadata: "프로필 추가 정보"
metadata-label: "라벨"
metadata-content: "내용"
common/views/components/user-list-editor.vue: common/views/components/user-list-editor.vue:
users: "사용자" users: "사용자"
rename: "리스트 이름 바꾸기" rename: "리스트 이름 바꾸기"
@ -853,6 +859,7 @@ desktop:
uploading-avatar: "새로운 아바타를 업로드하고 있습니다" uploading-avatar: "새로운 아바타를 업로드하고 있습니다"
avatar-updated: "아바타가 변경되었습니다" avatar-updated: "아바타가 변경되었습니다"
choose-avatar: "아바타 이미지를 선택" choose-avatar: "아바타 이미지를 선택"
unable-to-process: "작업을 완료할 수 없습니다"
invalid-filetype: "이 형식의 파일은 지원되지 않습니다" invalid-filetype: "이 형식의 파일은 지원되지 않습니다"
desktop/views/components/activity.chart.vue: desktop/views/components/activity.chart.vue:
total: "검은색 ... 전체" total: "검은색 ... 전체"
@ -894,7 +901,7 @@ desktop/views/components/drive.file.vue:
copy-url: "URL 복사" copy-url: "URL 복사"
download: "다운로드" download: "다운로드"
else-files: "기타" else-files: "기타"
set-as-avatar: "아이콘으로 설정" set-as-avatar: "아바타로 설정"
set-as-banner: "배너로 설정" set-as-banner: "배너로 설정"
open-in-app: "앱에서 열기" open-in-app: "앱에서 열기"
add-app: "앱 추가" add-app: "앱 추가"
@ -1365,6 +1372,8 @@ admin/views/users.vue:
remote-user-updated: "원격 사용자 정보를 갱신하였습니다" remote-user-updated: "원격 사용자 정보를 갱신하였습니다"
delete-all-files: "모든 파일 삭제" delete-all-files: "모든 파일 삭제"
delete-all-files-confirm: "모든 파일을 삭제하시겠습니까?" delete-all-files-confirm: "모든 파일을 삭제하시겠습니까?"
username: "사용자명"
host: "관리자"
users: users:
title: "사용자" title: "사용자"
sort: sort:

View file

@ -199,6 +199,7 @@ common/views/components/profile-editor.vue:
name: "Naam" name: "Naam"
avatar: "Gebruikersafbeelding" avatar: "Gebruikersafbeelding"
banner: "Omslagfoto" banner: "Omslagfoto"
unable-to-process: "De operatie kan niet worden voltooid."
export-targets: export-targets:
following-list: "Volgend" following-list: "Volgend"
user-lists: "Lijsten" user-lists: "Lijsten"
@ -226,6 +227,7 @@ common/views/pages/follow.vue:
follow: "Volgend" follow: "Volgend"
desktop: desktop:
banner: "Omslagfoto" banner: "Omslagfoto"
unable-to-process: "De operatie kan niet worden voltooid."
desktop/views/components/activity.chart.vue: desktop/views/components/activity.chart.vue:
total: "Zwart ... totaal" total: "Zwart ... totaal"
notes: "Blauw ... notities" notes: "Blauw ... notities"
@ -425,6 +427,7 @@ admin/views/drive.vue:
local: "Lokaal" local: "Lokaal"
delete: "Verwijderen" delete: "Verwijderen"
admin/views/users.vue: admin/views/users.vue:
username: "Gebruikersnaam"
users: users:
title: "Gebruiker" title: "Gebruiker"
state: state:

View file

@ -346,6 +346,7 @@ admin/views/drive.vue:
local: "Lokalt" local: "Lokalt"
delete: "Slett" delete: "Slett"
admin/views/users.vue: admin/views/users.vue:
username: "Brukernavn"
users: users:
title: "Bruker" title: "Bruker"
state: state:

View file

@ -518,6 +518,7 @@ common/views/components/profile-editor.vue:
saved: "Pomyślnie zaktualizowano profil" saved: "Pomyślnie zaktualizowano profil"
uploading: "Wysyłanie" uploading: "Wysyłanie"
upload-failed: "Wysyłanie nie powiodło się" upload-failed: "Wysyłanie nie powiodło się"
unable-to-process: "Nie udało się ukończyć działania."
email: "Ustawienia e-mail" email: "Ustawienia e-mail"
email-address: "Adres e-mail" email-address: "Adres e-mail"
email-verified: "Twój adres e-mail został zweryfikowany." email-verified: "Twój adres e-mail został zweryfikowany."
@ -612,6 +613,7 @@ desktop:
uploading-avatar: "Wysyłanie awatara" uploading-avatar: "Wysyłanie awatara"
avatar-updated: "Wysłano awatar" avatar-updated: "Wysłano awatar"
choose-avatar: "Wybierz awatar" choose-avatar: "Wybierz awatar"
unable-to-process: "Nie udało się ukończyć działania."
desktop/views/components/activity.chart.vue: desktop/views/components/activity.chart.vue:
total: "Czarny … Łącznie" total: "Czarny … Łącznie"
notes: "Niebieski … Wpisy" notes: "Niebieski … Wpisy"
@ -910,6 +912,7 @@ admin/views/drive.vue:
unmark-as-sensitive: "Cofnij oznaczenie jako zawartość wrażliwą" unmark-as-sensitive: "Cofnij oznaczenie jako zawartość wrażliwą"
admin/views/users.vue: admin/views/users.vue:
user-not-found: "Nie znaleziono użytkownika" user-not-found: "Nie znaleziono użytkownika"
username: "Nazwa użytkownika"
users: users:
title: "Użytkownicy" title: "Użytkownicy"
sort: sort:

View file

@ -725,6 +725,7 @@ common/views/components/profile-editor.vue:
saved: "您的个人资料已保存" saved: "您的个人资料已保存"
uploading: "正在上传" uploading: "正在上传"
upload-failed: "上传失败" upload-failed: "上传失败"
unable-to-process: "无法完成操作"
email: "邮件设置" email: "邮件设置"
email-address: "电子邮件地址" email-address: "电子邮件地址"
email-verified: "电子邮件地址已验证" email-verified: "电子邮件地址已验证"
@ -744,6 +745,7 @@ common/views/components/profile-editor.vue:
danger-zone: "危险选项" danger-zone: "危险选项"
delete-account: "删除帐户" delete-account: "删除帐户"
account-deleted: "帐户已被删除。 数据会在一段时间之后清除。" account-deleted: "帐户已被删除。 数据会在一段时间之后清除。"
metadata-content: "内容"
common/views/components/user-list-editor.vue: common/views/components/user-list-editor.vue:
users: "用户" users: "用户"
rename: "重命名列表" rename: "重命名列表"
@ -853,6 +855,7 @@ desktop:
uploading-avatar: "上传一个新的头像" uploading-avatar: "上传一个新的头像"
avatar-updated: "成功上传头像" avatar-updated: "成功上传头像"
choose-avatar: "选择作为头像的图片" choose-avatar: "选择作为头像的图片"
unable-to-process: "无法完成操作"
invalid-filetype: "不接受此文件类型" invalid-filetype: "不接受此文件类型"
desktop/views/components/activity.chart.vue: desktop/views/components/activity.chart.vue:
total: "黑 ... 总计" total: "黑 ... 总计"
@ -1365,6 +1368,8 @@ admin/views/users.vue:
remote-user-updated: "远程用户信息已更新" remote-user-updated: "远程用户信息已更新"
delete-all-files: "删除所有文件" delete-all-files: "删除所有文件"
delete-all-files-confirm: "删除所有文件吗?" delete-all-files-confirm: "删除所有文件吗?"
username: "用户名"
host: "主机名"
users: users:
title: "用户" title: "用户"
sort: sort:
@ -1399,6 +1404,7 @@ admin/views/moderators.vue:
title: "登录" title: "登录"
moderator: "版主" moderator: "版主"
type: "操作" type: "操作"
at: "日期和时间"
info: "信息" info: "信息"
admin/views/emoji.vue: admin/views/emoji.vue:
add-emoji: add-emoji:

View file

@ -1,7 +1,7 @@
{ {
"name": "misskey", "name": "misskey",
"author": "syuilo <i@syuilo.com>", "author": "syuilo <i@syuilo.com>",
"version": "11.25.1", "version": "11.26.0",
"codename": "daybreak", "codename": "daybreak",
"repository": { "repository": {
"type": "git", "type": "git",
@ -37,7 +37,6 @@
"@fortawesome/free-solid-svg-icons": "5.9.0", "@fortawesome/free-solid-svg-icons": "5.9.0",
"@fortawesome/vue-fontawesome": "0.1.6", "@fortawesome/vue-fontawesome": "0.1.6",
"@koa/cors": "3.0.0", "@koa/cors": "3.0.0",
"@typescript-eslint/parser": "1.11.0",
"@types/bcryptjs": "2.4.2", "@types/bcryptjs": "2.4.2",
"@types/bull": "3.5.15", "@types/bull": "3.5.15",
"@types/cbor": "2.0.0", "@types/cbor": "2.0.0",
@ -82,7 +81,7 @@
"@types/ratelimiter": "2.1.28", "@types/ratelimiter": "2.1.28",
"@types/redis": "2.8.13", "@types/redis": "2.8.13",
"@types/rename": "1.0.1", "@types/rename": "1.0.1",
"@types/request": "2.48.1", "@types/request": "2.48.2",
"@types/request-promise-native": "1.0.16", "@types/request-promise-native": "1.0.16",
"@types/request-stats": "3.0.0", "@types/request-stats": "3.0.0",
"@types/rimraf": "2.0.2", "@types/rimraf": "2.0.2",
@ -99,6 +98,7 @@
"@types/webpack-stream": "3.2.10", "@types/webpack-stream": "3.2.10",
"@types/websocket": "0.0.40", "@types/websocket": "0.0.40",
"@types/ws": "6.0.1", "@types/ws": "6.0.1",
"@typescript-eslint/parser": "1.11.0",
"animejs": "3.0.1", "animejs": "3.0.1",
"apexcharts": "3.8.2", "apexcharts": "3.8.2",
"autobind-decorator": "2.4.0", "autobind-decorator": "2.4.0",

View file

@ -5,7 +5,7 @@
<mk-avatar class="avatar" :user="user" :disable-link="true"/> <mk-avatar class="avatar" :user="user" :disable-link="true"/>
</a> </a>
</div> </div>
<div> <div @click="click(user.id)">
<header> <header>
<b><mk-user-name :user="user"/></b> <b><mk-user-name :user="user"/></b>
<span class="username">@{{ user | acct }}</span> <span class="username">@{{ user | acct }}</span>
@ -32,7 +32,7 @@ import { faSnowflake } from '@fortawesome/free-regular-svg-icons';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('admin/views/users.vue'), i18n: i18n('admin/views/users.vue'),
props: ['user'], props: ['user', 'click'],
data() { data() {
return { return {
faSnowflake, faMicrophoneSlash faSnowflake, faMicrophoneSlash
@ -44,7 +44,7 @@ export default Vue.extend({
<style lang="stylus" scoped> <style lang="stylus" scoped>
.kofvwchc .kofvwchc
display flex display flex
padding 16px 0 padding 16px
border-top solid 1px var(--faceDivider) border-top solid 1px var(--faceDivider)
> div:first-child > div:first-child
@ -55,6 +55,7 @@ export default Vue.extend({
> div:last-child > div:last-child
flex 1 flex 1
cursor pointer
padding-left 16px padding-left 16px
@media (max-width 500px) @media (max-width 500px)
@ -80,4 +81,15 @@ export default Vue.extend({
> .is-suspended > .is-suspended
margin 0 0 0 .5em margin 0 0 0 .5em
color #4dabf7 color #4dabf7
&:hover
color var(--primaryForeground)
background var(--primary)
text-decoration none
border-radius 3px
&:active
color var(--primaryForeground)
background var(--primaryDarken10)
border-radius 3px
</style> </style>

View file

@ -8,7 +8,7 @@
</ui-input> </ui-input>
<ui-button @click="showUser"><fa :icon="faSearch"/> {{ $t('lookup') }}</ui-button> <ui-button @click="showUser"><fa :icon="faSearch"/> {{ $t('lookup') }}</ui-button>
<div class="user" v-if="user"> <div ref="user" class="user" v-if="user" :key="user.id">
<x-user :user="user"/> <x-user :user="user"/>
<div class="actions"> <div class="actions">
<ui-button v-if="user.host != null" @click="updateRemoteUser"><fa :icon="faSync"/> {{ $t('update-remote-user') }}</ui-button> <ui-button v-if="user.host != null" @click="updateRemoteUser"><fa :icon="faSync"/> {{ $t('update-remote-user') }}</ui-button>
@ -54,8 +54,16 @@
<option value="remote">{{ $t('users.origin.remote') }}</option> <option value="remote">{{ $t('users.origin.remote') }}</option>
</ui-select> </ui-select>
</ui-horizon-group> </ui-horizon-group>
<ui-horizon-group searchboxes>
<ui-input v-model="searchUsername" type="text" spellcheck="false" @input="fetchUsers(true)">
<span>{{ $t('username') }}</span>
</ui-input>
<ui-input v-model="searchHost" type="text" spellcheck="false" @input="fetchUsers(true)" :disabled="origin === 'local'">
<span>{{ $t('host') }}</span>
</ui-input>
</ui-horizon-group>
<sequential-entrance animation="entranceFromTop" delay="25"> <sequential-entrance animation="entranceFromTop" delay="25">
<x-user v-for="user in users" :user='user' :key="user.id"/> <x-user v-for="user in users" :key="user.id" :user='user' :click="showUserOnClick"/>
</sequential-entrance> </sequential-entrance>
<ui-button v-if="existMore" @click="fetchUsers">{{ $t('@.load-more') }}</ui-button> <ui-button v-if="existMore" @click="fetchUsers">{{ $t('@.load-more') }}</ui-button>
</section> </section>
@ -85,6 +93,8 @@ export default Vue.extend({
sort: '+createdAt', sort: '+createdAt',
state: 'all', state: 'all',
origin: 'local', origin: 'local',
searchUsername: '',
searchHost: '',
limit: 10, limit: 10,
offset: 0, offset: 0,
users: [], users: [],
@ -107,6 +117,7 @@ export default Vue.extend({
}, },
origin() { origin() {
if (this.origin === 'local') this.searchHost = '';
this.users = []; this.users = [];
this.offset = 0; this.offset = 0;
this.fetchUsers(); this.fetchUsers();
@ -157,6 +168,15 @@ export default Vue.extend({
this.target = ''; this.target = '';
}, },
async showUserOnClick(userId: string) {
this.$root.api('admin/show-user', { userId: userId }).then(info => {
this.user = info;
this.$nextTick(() => {
this.$refs.user.scrollIntoView();
});
});
},
/** 処理対象ユーザーの情報を更新する */ /** 処理対象ユーザーの情報を更新する */
async refreshUser() { async refreshUser() {
this.$root.api('admin/show-user', { userId: this.user.id }).then(info => { this.$root.api('admin/show-user', { userId: this.user.id }).then(info => {
@ -308,13 +328,16 @@ export default Vue.extend({
return !confirm.canceled; return !confirm.canceled;
}, },
fetchUsers() { fetchUsers(truncate?: boolean) {
if (truncate) this.offset = 0;
this.$root.api('admin/show-users', { this.$root.api('admin/show-users', {
state: this.state, state: this.state,
origin: this.origin, origin: this.origin,
sort: this.sort, sort: this.sort,
offset: this.offset, offset: this.offset,
limit: this.limit + 1 limit: this.limit + 1,
username: this.searchUsername,
hostname: this.searchHost
}).then(users => { }).then(users => {
if (users.length == this.limit + 1) { if (users.length == this.limit + 1) {
users.pop(); users.pop();
@ -322,7 +345,7 @@ export default Vue.extend({
} else { } else {
this.existMore = false; this.existMore = false;
} }
this.users = this.users.concat(users); this.users = truncate ? users : this.users.concat(users);
this.offset += this.limit; this.offset += this.limit;
}); });
} }

View file

@ -4,7 +4,8 @@ const faces = [
'🐡( \'-\' 🐡 )フグパンチ!!!!', '🐡( \'-\' 🐡 )フグパンチ!!!!',
'✌️(´・_・`)✌️', '✌️(´・_・`)✌️',
'(。><。)', '(。><。)',
'(Δ・x・Δ)' '(Δ・x・Δ)',
'(コ`・ヘ・´ケ)'
]; ];
export default () => faces[Math.floor(Math.random() * faces.length)]; export default () => faces[Math.floor(Math.random() * faces.length)];

View file

@ -1,11 +1,17 @@
<template> <template>
<router-link class="ldlomzub" :to="`/${ canonical }`" v-user-preview="canonical"> <router-link class="ldlomzub" :to="url" v-user-preview="canonical" v-if="url.startsWith('/')">
<span class="me" v-if="isMe">{{ $t('@.you') }}</span> <span class="me" v-if="isMe">{{ $t('@.you') }}</span>
<span class="main"> <span class="main">
<span class="username">@{{ username }}</span> <span class="username">@{{ username }}</span>
<span class="host" :class="{ fade: $store.state.settings.contrastedAcct }" v-if="(host != localHost) || $store.state.settings.showFullAcct">@{{ toUnicode(host) }}</span> <span class="host" :class="{ fade: $store.state.settings.contrastedAcct }" v-if="(host != localHost) || $store.state.settings.showFullAcct">@{{ toUnicode(host) }}</span>
</span> </span>
</router-link> </router-link>
<a class="ldlomzub" :href="url" target="_blank" rel="noopener" v-else>
<span class="main">
<span class="username">@{{ username }}</span>
<span class="host" :class="{ fade: $store.state.settings.contrastedAcct }">@{{ toUnicode(host) }}</span>
</span>
</a>
</template> </template>
<script lang="ts"> <script lang="ts">
@ -32,6 +38,15 @@ export default Vue.extend({
}; };
}, },
computed: { computed: {
url(): string {
switch (this.host) {
case 'twitter.com':
case 'github.com':
return `https://${this.host}/${this.username}`;
default:
return `/${this.canonical}`;
}
},
canonical(): string { canonical(): string {
return this.host === localHost ? `@${this.username}` : `@${this.username}@${toUnicode(this.host)}`; return this.host === localHost ? `@${this.username}` : `@${this.username}@${toUnicode(this.host)}`;
}, },

View file

@ -30,6 +30,7 @@ export default Vue.extend({
border-radius 4px border-radius 4px
>>> .quote >>> .quote
display block
margin 8px margin 8px
padding 6px 0 6px 12px padding 6px 0 6px 12px
color var(--mfmQuote) color var(--mfmQuote)

View file

@ -26,13 +26,19 @@
<option value="after">{{ $t('after') }}</option> <option value="after">{{ $t('after') }}</option>
</ui-select> </ui-select>
<section v-if="expiration === 'at'"> <section v-if="expiration === 'at'">
<ui-input v-model="atDate" type="date">{{ $t('deadline-date') }}</ui-input> <ui-input v-model="atDate" type="date">
<ui-input v-model="atTime" type="time">{{ $t('deadline-time') }}</ui-input> <template #title>{{ $t('deadline-date') }}</template>
</ui-input>
<ui-input v-model="atTime" type="time">
<template #title>{{ $t('deadline-time') }}</template>
</ui-input>
</section> </section>
<section v-if="expiration === 'after'"> <section v-if="expiration === 'after'">
<ui-input v-model="after" type="number">{{ $t('interval') }}</ui-input> <ui-input v-model="after" type="number">
<template #title>{{ $t('interval') }}</template>
</ui-input>
<ui-select v-model="unit"> <ui-select v-model="unit">
<template #label>{{ $t('unit') }}</template> <template #title>{{ $t('unit') }}</template>
<option value="second">{{ $t('second') }}</option> <option value="second">{{ $t('second') }}</option>
<option value="minute">{{ $t('minute') }}</option> <option value="minute">{{ $t('minute') }}</option>
<option value="hour">{{ $t('hour') }}</option> <option value="hour">{{ $t('hour') }}</option>

View file

@ -51,6 +51,26 @@
<template #desc v-if="bannerUploading">{{ $t('uploading') }}<mk-ellipsis/></template> <template #desc v-if="bannerUploading">{{ $t('uploading') }}<mk-ellipsis/></template>
</ui-input> </ui-input>
<div class="fields">
<header>{{ $t('profile-metadata') }}</header>
<ui-horizon-group>
<ui-input v-model="fieldName0">{{ $t('metadata-label') }}</ui-input>
<ui-input v-model="fieldValue0">{{ $t('metadata-content') }}</ui-input>
</ui-horizon-group>
<ui-horizon-group>
<ui-input v-model="fieldName1">{{ $t('metadata-label') }}</ui-input>
<ui-input v-model="fieldValue1">{{ $t('metadata-content') }}</ui-input>
</ui-horizon-group>
<ui-horizon-group>
<ui-input v-model="fieldName2">{{ $t('metadata-label') }}</ui-input>
<ui-input v-model="fieldValue2">{{ $t('metadata-content') }}</ui-input>
</ui-horizon-group>
<ui-horizon-group>
<ui-input v-model="fieldName3">{{ $t('metadata-label') }}</ui-input>
<ui-input v-model="fieldValue3">{{ $t('metadata-content') }}</ui-input>
</ui-horizon-group>
</div>
<ui-button @click="save(true)"><fa :icon="faSave"/> {{ $t('save') }}</ui-button> <ui-button @click="save(true)"><fa :icon="faSave"/> {{ $t('save') }}</ui-button>
</ui-form> </ui-form>
</section> </section>
@ -189,6 +209,17 @@ export default Vue.extend({
this.isLocked = this.$store.state.i.isLocked; this.isLocked = this.$store.state.i.isLocked;
this.carefulBot = this.$store.state.i.carefulBot; this.carefulBot = this.$store.state.i.carefulBot;
this.autoAcceptFollowed = this.$store.state.i.autoAcceptFollowed; this.autoAcceptFollowed = this.$store.state.i.autoAcceptFollowed;
if (this.$store.state.i.fields) {
this.fieldName0 = this.$store.state.i.fields[0].name;
this.fieldValue0 = this.$store.state.i.fields[0].value;
this.fieldName1 = this.$store.state.i.fields[1].name;
this.fieldValue1 = this.$store.state.i.fields[1].value;
this.fieldName2 = this.$store.state.i.fields[2].name;
this.fieldValue2 = this.$store.state.i.fields[2].value;
this.fieldName3 = this.$store.state.i.fields[3].name;
this.fieldValue3 = this.$store.state.i.fields[3].value;
}
}, },
methods: { methods: {
@ -237,6 +268,13 @@ export default Vue.extend({
}, },
save(notify) { save(notify) {
const fields = [
{ name: this.fieldName0, value: this.fieldValue0 },
{ name: this.fieldName1, value: this.fieldValue1 },
{ name: this.fieldName2, value: this.fieldValue2 },
{ name: this.fieldName3, value: this.fieldValue3 },
];
this.saving = true; this.saving = true;
this.$root.api('i/update', { this.$root.api('i/update', {
@ -247,6 +285,7 @@ export default Vue.extend({
birthday: this.birthday || null, birthday: this.birthday || null,
avatarId: this.avatarId || undefined, avatarId: this.avatarId || undefined,
bannerId: this.bannerId || undefined, bannerId: this.bannerId || undefined,
fields,
isCat: !!this.isCat, isCat: !!this.isCat,
isBot: !!this.isBot, isBot: !!this.isBot,
isLocked: !!this.isLocked, isLocked: !!this.isLocked,
@ -265,6 +304,29 @@ export default Vue.extend({
text: this.$t('saved') text: this.$t('saved')
}); });
} }
}).catch(err => {
this.saving = false;
switch(err.id) {
case 'f419f9f8-2f4d-46b1-9fb4-49d3a2fd7191':
this.$root.dialog({
type: 'error',
title: this.$t('unable-to-process'),
text: this.$t('avatar-not-an-image')
});
break;
case '75aedb19-2afd-4e6d-87fc-67941256fa60':
this.$root.dialog({
type: 'error',
title: this.$t('unable-to-process'),
text: this.$t('banner-not-an-image')
});
break;
default:
this.$root.dialog({
type: 'error',
text: this.$t('unable-to-process')
});
}
}); });
}, },
@ -366,4 +428,9 @@ export default Vue.extend({
height 72px height 72px
margin auto margin auto
.fields
> header
padding 8px 0px
font-weight bold
</style> </style>

View file

@ -83,6 +83,21 @@ export default ($root: any) => {
}); });
return i; return i;
}).catch(err => {
switch (err.id) {
case 'f419f9f8-2f4d-46b1-9fb4-49d3a2fd7191':
$root.dialog({
type: 'error',
title: locale['desktop']['unable-to-process'],
text: locale['desktop']['invalid-filetype']
});
break;
default:
$root.dialog({
type: 'error',
text: locale['desktop']['unable-to-process']
});
}
}); });
}; };

View file

@ -83,6 +83,21 @@ export default ($root: any) => {
}); });
return i; return i;
}).catch(err => {
switch (err.id) {
case '75aedb19-2afd-4e6d-87fc-67941256fa60':
$root.dialog({
type: 'error',
title: locale['desktop']['unable-to-process'],
text: locale['desktop']['invalid-filetype']
});
break;
default:
$root.dialog({
type: 'error',
text: locale['desktop']['unable-to-process']
});
}
}); });
}; };

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

View file

@ -148,6 +148,7 @@ export class UserRepository extends Repository<User> {
description: profile!.description, description: profile!.description,
location: profile!.location, location: profile!.location,
birthday: profile!.birthday, birthday: profile!.birthday,
fields: profile!.fields,
followersCount: user.followersCount, followersCount: user.followersCount,
followingCount: user.followingCount, followingCount: user.followingCount,
notesCount: user.notesCount, notesCount: user.notesCount,

View file

@ -21,13 +21,24 @@ export async function renderPerson(user: ILocalUser) {
]); ]);
const attachment: { const attachment: {
type: string, type: 'PropertyValue',
name: string, name: string,
value: string, value: string,
verified_at?: string,
identifier?: IIdentifier identifier?: IIdentifier
}[] = []; }[] = [];
if (profile.fields) {
for (const field of profile.fields) {
attachment.push({
type: 'PropertyValue',
name: field.name,
value: (field.value != null && field.value.match(/^https?:/))
? `<a href="${new URL(field.value).href}" rel="me nofollow noopener" target="_blank">${new URL(field.value).href}</a>`
: field.value
});
}
}
if (profile.twitter) { if (profile.twitter) {
attachment.push({ attachment.push({
type: 'PropertyValue', type: 'PropertyValue',

View file

@ -2,6 +2,9 @@ import * as Koa from 'koa';
import config from '../../../config'; import config from '../../../config';
import { ILocalUser } from '../../../models/entities/user'; import { ILocalUser } from '../../../models/entities/user';
import { Signins } from '../../../models';
import { genId } from '../../../misc/gen-id';
import { publishMainStream } from '../../../services/stream';
export default function(ctx: Koa.BaseContext, user: ILocalUser, redirect = false) { export default function(ctx: Koa.BaseContext, user: ILocalUser, redirect = false) {
if (redirect) { if (redirect) {
@ -24,4 +27,19 @@ export default function(ctx: Koa.BaseContext, user: ILocalUser, redirect = false
ctx.body = { i: user.token }; ctx.body = { i: user.token };
ctx.status = 200; ctx.status = 200;
} }
(async () => {
// Append signin history
const record = await Signins.save({
id: genId(),
createdAt: new Date(),
userId: user.id,
ip: ctx.ip,
headers: ctx.headers,
success: true
});
// Publish signin event
publishMainStream(user.id, 'signin', await Signins.pack(record));
})();
} }

View file

@ -49,6 +49,16 @@ export const meta = {
'remote', 'remote',
]), ]),
default: 'local' default: 'local'
},
username: {
validator: $.optional.str,
default: null
},
hostname: {
validator: $.optional.str,
default: null
} }
} }
}; };
@ -70,6 +80,14 @@ export default define(meta, async (ps, me) => {
case 'remote': query.andWhere('user.host IS NOT NULL'); break; case 'remote': query.andWhere('user.host IS NOT NULL'); break;
} }
if (ps.username) {
query.andWhere('user.usernameLower like :username', { username: ps.username.toLowerCase() + '%' });
}
if (ps.hostname) {
query.andWhere('user.host like :hostname', { hostname: '%' + ps.hostname.toLowerCase() + '%' });
}
switch (ps.sort) { switch (ps.sort) {
case '+follower': query.orderBy('user.followersCount', 'DESC'); break; case '+follower': query.orderBy('user.followersCount', 'DESC'); break;
case '-follower': query.orderBy('user.followersCount', 'ASC'); break; case '-follower': query.orderBy('user.followersCount', 'ASC'); break;

View file

@ -5,6 +5,7 @@ import deleteFollowing from '../../../../services/following/delete';
import { Users, Followings } from '../../../../models'; import { Users, Followings } from '../../../../models';
import { User } from '../../../../models/entities/user'; import { User } from '../../../../models/entities/user';
import { insertModerationLog } from '../../../../services/insert-moderation-log'; import { insertModerationLog } from '../../../../services/insert-moderation-log';
import { doPostSuspend } from '../../../../services/suspend-user';
export const meta = { export const meta = {
desc: { desc: {
@ -51,7 +52,10 @@ export default define(meta, async (ps, me) => {
targetId: user.id, targetId: user.id,
}); });
unFollowAll(user); (async () => {
await doPostSuspend(user).catch(e => {});
await unFollowAll(user).catch(e => {});
})();
}); });
async function unFollowAll(follower: User) { async function unFollowAll(follower: User) {

View file

@ -3,6 +3,7 @@ import { ID } from '../../../../misc/cafy-id';
import define from '../../define'; import define from '../../define';
import { Users } from '../../../../models'; import { Users } from '../../../../models';
import { insertModerationLog } from '../../../../services/insert-moderation-log'; import { insertModerationLog } from '../../../../services/insert-moderation-log';
import { doPostUnsuspend } from '../../../../services/unsuspend-user';
export const meta = { export const meta = {
desc: { desc: {
@ -40,4 +41,6 @@ export default define(meta, async (ps, me) => {
insertModerationLog(me, 'unsuspend', { insertModerationLog(me, 'unsuspend', {
targetId: user.id, targetId: user.id,
}); });
doPostUnsuspend(user);
}); });

View file

@ -3,6 +3,7 @@ import * as bcrypt from 'bcryptjs';
import define from '../../define'; import define from '../../define';
import { Users, UserProfiles } from '../../../../models'; import { Users, UserProfiles } from '../../../../models';
import { ensure } from '../../../../prelude/ensure'; import { ensure } from '../../../../prelude/ensure';
import { doPostSuspend } from '../../../../services/suspend-user';
export const meta = { export const meta = {
requireCredential: true, requireCredential: true,
@ -26,5 +27,8 @@ export default define(meta, async (ps, user) => {
throw new Error('incorrect password'); throw new Error('incorrect password');
} }
// 物理削除する前にDelete activityを送信する
await doPostSuspend(user).catch(e => {});
await Users.delete(user.id); await Users.delete(user.id);
}); });

View file

@ -77,6 +77,13 @@ export const meta = {
} }
}, },
fields: {
validator: $.optional.arr($.object()).range(1, 4),
desc: {
'ja-JP': 'プロフィール補足情報'
}
},
isLocked: { isLocked: {
validator: $.optional.bool, validator: $.optional.bool,
desc: { desc: {
@ -226,6 +233,14 @@ export default define(meta, async (ps, user, app) => {
profileUpdates.pinnedPageId = null; profileUpdates.pinnedPageId = null;
} }
if (ps.fields) {
profileUpdates.fields = ps.fields
.filter(x => typeof x.name === 'string' && x.name !== '' && typeof x.value === 'string' && x.value !== '')
.map(x => {
return { name: x.name, value: x.value };
});
}
//#region emojis/tags //#region emojis/tags
let emojis = [] as string[]; let emojis = [] as string[];

View file

@ -1,7 +1,6 @@
import * as Koa from 'koa'; import * as Koa from 'koa';
import * as bcrypt from 'bcryptjs'; import * as bcrypt from 'bcryptjs';
import * as speakeasy from 'speakeasy'; import * as speakeasy from 'speakeasy';
import { publishMainStream } from '../../../services/stream';
import signin from '../common/signin'; import signin from '../common/signin';
import config from '../../../config'; import config from '../../../config';
import { Users, Signins, UserProfiles, UserSecurityKeys, AttestationChallenges } from '../../../models'; import { Users, Signins, UserProfiles, UserSecurityKeys, AttestationChallenges } from '../../../models';
@ -53,35 +52,31 @@ export default async (ctx: Koa.BaseContext) => {
// Compare password // Compare password
const same = await bcrypt.compare(password, profile.password!); const same = await bcrypt.compare(password, profile.password!);
async function fail(status?: number, failure?: {error: string}) { async function fail(status?: number, failure?: { error: string }) {
// Append signin history // Append signin history
const record = await Signins.save({ await Signins.save({
id: genId(), id: genId(),
createdAt: new Date(), createdAt: new Date(),
userId: user.id, userId: user.id,
ip: ctx.ip, ip: ctx.ip,
headers: ctx.headers, headers: ctx.headers,
success: !!(status || failure) success: false
}); });
// Publish signin event ctx.throw(status || 500, failure || { error: 'someting happened' });
publishMainStream(user.id, 'signin', await Signins.pack(record));
if (status && failure) {
ctx.throw(status, failure);
}
} }
if (!profile.twoFactorEnabled) { if (!profile.twoFactorEnabled) {
if (same) { if (same) {
signin(ctx, user); signin(ctx, user);
return;
} else { } else {
await fail(403, { await fail(403, {
error: 'incorrect password' error: 'incorrect password'
}); });
}
return; return;
} }
}
if (token) { if (token) {
if (!same) { if (!same) {
@ -169,6 +164,7 @@ export default async (ctx: Koa.BaseContext) => {
if (isValid) { if (isValid) {
signin(ctx, user); signin(ctx, user);
return;
} else { } else {
await fail(403, { await fail(403, {
error: 'invalid challenge data' error: 'invalid challenge data'
@ -191,6 +187,7 @@ export default async (ctx: Koa.BaseContext) => {
await fail(403, { await fail(403, {
error: 'no keys found' error: 'no keys found'
}); });
return;
} }
// 32 byte challenge // 32 byte challenge
@ -219,6 +216,5 @@ export default async (ctx: Koa.BaseContext) => {
ctx.status = 200; ctx.status = 200;
return; return;
} }
// never get here
await fail();
}; };

View file

@ -5,7 +5,7 @@ import generateUserToken from '../common/generate-native-user-token';
import config from '../../../config'; import config from '../../../config';
import { fetchMeta } from '../../../misc/fetch-meta'; import { fetchMeta } from '../../../misc/fetch-meta';
import * as recaptcha from 'recaptcha-promise'; import * as recaptcha from 'recaptcha-promise';
import { Users, RegistrationTickets } from '../../../models'; import { Users, Signins, RegistrationTickets } from '../../../models';
import { genId } from '../../../misc/gen-id'; import { genId } from '../../../misc/gen-id';
import { usersChart } from '../../../services/chart'; import { usersChart } from '../../../services/chart';
import { User } from '../../../models/entities/user'; import { User } from '../../../models/entities/user';
@ -137,6 +137,16 @@ export default async (ctx: Koa.BaseContext) => {
usersChart.update(account, true); usersChart.update(account, true);
// Append signin history
await Signins.save({
id: genId(),
createdAt: new Date(),
userId: account.id,
ip: ctx.ip,
headers: ctx.headers,
success: true
});
const res = await Users.pack(account, account, { const res = await Users.pack(account, account, {
detail: true, detail: true,
includeSecrets: true includeSecrets: true

View file

@ -156,11 +156,17 @@ router.get('/@:user', async (ctx, next) => {
if (user != null) { if (user != null) {
const profile = await UserProfiles.findOne(user.id).then(ensure); const profile = await UserProfiles.findOne(user.id).then(ensure);
const meta = await fetchMeta(); const meta = await fetchMeta();
const me = profile.fields
? profile.fields
.filter(filed => filed.value != null && filed.value.match(/^https?:/))
.map(field => field.value)
: [];
await ctx.render('user', { await ctx.render('user', {
user, profile, user, profile, me,
instanceName: meta.name || 'Misskey' instanceName: meta.name || 'Misskey'
}); });
ctx.set('Cache-Control', 'public, max-age=180'); ctx.set('Cache-Control', 'public, max-age=30');
} else { } else {
// リモートユーザーなので // リモートユーザーなので
await next(); await next();

View file

@ -44,3 +44,4 @@ html
<svg viewBox="0 0 50 50"> <svg viewBox="0 0 50 50">
<path fill=#fb4e4e d="M25.251,6.461c-10.318,0-18.683,8.365-18.683,18.683h4.068c0-8.071,6.543-14.615,14.615-14.615V6.461z" /> <path fill=#fb4e4e d="M25.251,6.461c-10.318,0-18.683,8.365-18.683,18.683h4.068c0-8.071,6.543-14.615,14.615-14.615V6.461z" />
</svg> </svg>
block content

View file

@ -36,3 +36,8 @@ block meta
link(rel='alternate' href=user.uri type='application/activity+json') link(rel='alternate' href=user.uri type='application/activity+json')
if profile.url if profile.url
link(rel='alternate' href=profile.url type='text/html') link(rel='alternate' href=profile.url type='text/html')
block content
div#me
each m in me
a(rel='me' href=`${m}`) #{m}

View file

@ -0,0 +1,34 @@
import renderDelete from '../remote/activitypub/renderer/delete';
import { renderActivity } from '../remote/activitypub/renderer';
import { deliver } from '../queue';
import config from '../config';
import { User } from '../models/entities/user';
import { Users, Followings } from '../models';
import { Not, IsNull } from 'typeorm';
export async function doPostSuspend(user: User) {
if (Users.isLocalUser(user)) {
// 知り得る全SharedInboxにDelete配信
const content = renderActivity(renderDelete(`${config.url}/users/${user.id}`, user));
const queue: string[] = [];
const followings = await Followings.find({
where: [
{ followerSharedInbox: Not(IsNull()) },
{ followeeSharedInbox: Not(IsNull()) }
],
select: ['followerSharedInbox', 'followeeSharedInbox']
});
const inboxes = followings.map(x => x.followerSharedInbox || x.followeeSharedInbox);
for (const inbox of inboxes) {
if (inbox != null && !queue.includes(inbox)) queue.push(inbox);
}
for (const inbox of queue) {
deliver(user as any, content, inbox);
}
}
}

View file

@ -0,0 +1,35 @@
import renderDelete from '../remote/activitypub/renderer/delete';
import renderUndo from '../remote/activitypub/renderer/undo';
import { renderActivity } from '../remote/activitypub/renderer';
import { deliver } from '../queue';
import config from '../config';
import { User } from '../models/entities/user';
import { Users, Followings } from '../models';
import { Not, IsNull } from 'typeorm';
export async function doPostUnsuspend(user: User) {
if (Users.isLocalUser(user)) {
// 知り得る全SharedInboxにUndo Delete配信
const content = renderActivity(renderUndo(renderDelete(`${config.url}/users/${user.id}`, user), user));
const queue: string[] = [];
const followings = await Followings.find({
where: [
{ followerSharedInbox: Not(IsNull()) },
{ followeeSharedInbox: Not(IsNull()) }
],
select: ['followerSharedInbox', 'followeeSharedInbox']
});
const inboxes = followings.map(x => x.followerSharedInbox || x.followeeSharedInbox);
for (const inbox of inboxes) {
if (inbox != null && !queue.includes(inbox)) queue.push(inbox);
}
for (const inbox of queue) {
deliver(user as any, content, inbox);
}
}
}

12448
yarn.lock Normal file

File diff suppressed because it is too large Load diff