Compare commits

..

88 Commits

Author SHA1 Message Date
2a1def3cce 10.82.0 2019-02-05 19:53:31 +09:00
938fe05fef Update CHANGELOG.md 2019-02-05 19:52:22 +09:00
5db5bbd1cd 自分の投稿情報をエクスポートできるように (#4144)
* wip

* 正しいJSONを生成するように

* データを整形
2019-02-05 19:50:14 +09:00
ba7e05837c Add ffmpeg package for the runner container (#4145)
* Revert "Update Dockerfile (#4090)"

This reverts commit 6758b0f133.

* Docker: Add ffmpeg package for the runner container

Misskey 10.81.0 added thumbnail generation support.
However it did not work with Docker bacause ffmpeg has not been
installed in the runner container.
2019-02-05 19:32:53 +09:00
9dd06a7621 /.well-known 周りをいい感じに (#4141)
* Enhance /.well-known and their friends

* Fix bug
2019-02-05 17:42:55 +09:00
2f4434b0d8 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-02-05 17:30:25 +09:00
350328770b Improve readability 2019-02-05 17:30:17 +09:00
17e1b49bff New translations ja-JP.yml (Chinese Simplified) (#4140) 2019-02-05 15:16:53 +09:00
266c31981d Fix typo 2019-02-05 15:09:37 +09:00
803fb0898a Fix bug 2019-02-05 14:56:33 +09:00
01983da514 [Client] Use localStorage instead of window 2019-02-05 14:42:18 +09:00
6f473aa64a Update setup.ja.md 2019-02-05 14:25:03 +09:00
574747b9d4 Improve doc 2019-02-05 14:24:10 +09:00
dff1122bd5 Enable service worker 2019-02-05 14:22:52 +09:00
43cb12930a Remove unused declare 2019-02-05 14:22:35 +09:00
8129d4dc23 Refactoring 2019-02-05 14:14:23 +09:00
9b780dff04 Fix log 2019-02-05 14:04:40 +09:00
11a0ef485b Resolve #4139 2019-02-05 13:56:50 +09:00
83b2aa72b1 [Client] Clip note content 2019-02-05 13:54:40 +09:00
c71b24987d Make index.js for more convinience 2019-02-05 13:46:06 +09:00
78d22dbd22 Update vue to 2.6.2 🚀 2019-02-05 13:35:47 +09:00
8961dab137 Fix comment 2019-02-05 11:49:03 +09:00
bcc549fd8e Fix style 2019-02-05 11:48:08 +09:00
5a6c3fc11c Update css-loader to 2.1.0 🚀
Close #3961
2019-02-05 07:06:57 +09:00
7d730f676d Refactoring: Extract showMachineInfo function 2019-02-05 06:49:00 +09:00
6bda571660 Make some error logs important 2019-02-05 06:46:01 +09:00
d3c7129e1f Refactoring: Extract checkMongoDB function 2019-02-05 06:43:36 +09:00
3709ba95cd Improve doc 2019-02-05 06:29:56 +09:00
4162981081 アニメーション停止箇所の追加 (#4138) 2019-02-05 06:22:39 +09:00
7b7359fbdc [Client] Fix bug 2019-02-05 06:12:41 +09:00
70c01c52a8 Update Vue to 2.6 🚀 2019-02-05 06:07:23 +09:00
443006c868 Update terser-webpack-plugin requirement from 1.2.1 to 1.2.2 (#4137)
Updates the requirements on [terser-webpack-plugin](https://github.com/webpack-contrib/terser-webpack-plugin) to permit the latest version.
- [Release notes](https://github.com/webpack-contrib/terser-webpack-plugin/releases)
- [Changelog](https://github.com/webpack-contrib/terser-webpack-plugin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/terser-webpack-plugin/commits/v1.2.2)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-05 05:20:10 +09:00
7c1db1fea5 New Crowdin translations (#4130)
* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

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

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

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

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

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

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

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

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (English)
2019-02-05 04:58:07 +09:00
7c2b704bef Fix CHANGELOG 2019-02-05 04:23:28 +09:00
368c3f1e29 Update CHANGELOG.md 2019-02-05 04:22:24 +09:00
dd39d6ea37 Better key name 2019-02-05 04:09:44 +09:00
ef618b2431 [Client] Increase font size a little in the Deck view
Resolve #1821
2019-02-05 04:01:50 +09:00
861302f0fd アニメーション画像を無効にする際、サーバーサイドではなくクライアントサイドでURLを変更するように 2019-02-05 03:51:54 +09:00
f014b7ae0e アニメーションを自動再生しないオプション (#4131)
* Refactor

* settings

* Media Proxy

* Replace API response
2019-02-05 03:01:36 +09:00
00b2d89f1a Refactor: Better type definition 2019-02-05 01:52:09 +09:00
5410efe9ca Refactoring 2019-02-05 01:31:02 +09:00
1d814ba0e1 個別に投稿のウォッチ/ウォッチ解除をできるように
Resolve #161
2019-02-05 01:24:44 +09:00
c107333f56 [API] お気に入り状態は投稿情報に含めないように統一 2019-02-05 01:11:06 +09:00
06707705bf Add dummy option for some libraries 2019-02-04 19:41:31 +09:00
68ee9a008e Resolve #4121 (#4122)
* Resolve #4121

* Update tslint.json
2019-02-04 18:27:45 +09:00
3a035c481e Fix fusion 2019-02-04 16:50:14 +09:00
23a0aead9f Update config.yml
Redis なしが帰ってきたぞっ!
2019-02-04 16:41:57 +09:00
6cd41f9860 Initialize queue only if queue is enabled (#4125) 2019-02-04 16:41:53 +09:00
baf861ac79 Update __MISSKEY_HEAD 2019-02-04 16:41:11 +09:00
0ae1190c08 Add --disable-queue option 2019-02-04 13:37:50 +09:00
d3b3426ebe Enable job queue
Resolve #3216
2019-02-04 13:35:58 +09:00
4982ea8f14 Add --verbose option 2019-02-04 12:14:07 +09:00
3be89e9702 Better logging 2019-02-04 12:09:59 +09:00
4275af2324 Make Logger#log method private 2019-02-04 11:51:23 +09:00
84d42be090 Fix MongoDB connectivity checking 2019-02-04 11:48:59 +09:00
c4c7783691 [Server] Enable slow option 2019-02-04 10:03:49 +09:00
d6dba7fd71 Update dependencies 🚀 2019-02-04 09:56:48 +09:00
30b1b1a5ed Fix imports 2019-02-04 09:37:15 +09:00
90b6688057 Update apexcharts requirement from 2.5.1 to 3.2.1 (#4124)
Updates the requirements on [apexcharts](https://github.com/apexcharts/apexcharts.js) to permit the latest version.
- [Release notes](https://github.com/apexcharts/apexcharts.js/releases)
- [Changelog](https://github.com/apexcharts/apexcharts.js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/apexcharts/apexcharts.js/commits/v3.2.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-04 09:29:47 +09:00
b536ee4dcd Add collaborators credit 2019-02-04 09:09:45 +09:00
11101a6aca Better error log of WebFinger 2019-02-04 09:06:13 +09:00
b4a3e5aa4f Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-02-04 09:02:27 +09:00
874c0eae6a Better error log of WebFinger 2019-02-04 09:02:23 +09:00
9950b6fbc6 Update tslint-sonarts requirement from 1.8.0 to 1.9.0 (#3978)
Updates the requirements on [tslint-sonarts](https://github.com/SonarSource/SonarTS) to permit the latest version.
- [Release notes](https://github.com/SonarSource/SonarTS/releases)
- [Commits](https://github.com/SonarSource/SonarTS/commits/v1.9.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-04 07:16:01 +09:00
42d6ed62f6 New translations ja-JP.yml (English) (#4120) 2019-02-04 07:01:19 +09:00
beb1b570d4 New translations ja-JP.yml (Polish) (#4119) 2019-02-04 01:52:39 +09:00
ba1b5a8ede Update README.md 2019-02-04 01:51:01 +09:00
99d8d0a484 Create type definition for '*/const.json' (#4118) 2019-02-04 01:47:12 +09:00
5891135ac1 Create type definition for ./locales (#4117) 2019-02-04 01:46:59 +09:00
c4f7491322 Revert "Create new type definition for 'is-url' (#4013)"
This reverts commit 014b58cb40.
2019-02-04 01:30:33 +09:00
206b57b962 New translations ja-JP.yml (Polish) (#4116) 2019-02-04 01:22:37 +09:00
1b0e03704e New translations ja-JP.yml (Polish) (#4115) 2019-02-04 01:13:39 +09:00
8f2f4b6d2d Add missing semicolons 2019-02-03 23:52:46 +09:00
6e0c055faf Module 'jsdom' as import syntax (#4016) 2019-02-03 23:49:00 +09:00
893a3b527d Create type definition for 'os-utils' (#4050) 2019-02-03 23:41:27 +09:00
fe13c17fcb Create type definition for 'langmap' (#4059)
* Create type definition for 'langmap'

* Follow lint
2019-02-03 23:41:05 +09:00
5049870b6e Create type definition for 'koa-slow' (#4072) 2019-02-03 23:01:03 +09:00
ce576dea8f Create type definition for 'recaptcha-promise' (#4068) 2019-02-03 22:21:55 +09:00
ceda3dd72a Create new type definition for 'escape-regexp' (#4058) 2019-02-03 22:21:47 +09:00
014b58cb40 Create new type definition for 'is-url' (#4013)
* Create new type definition for 'is-url'

* [Client] Resolve #3638

* 10.79.1

* New translations ja-JP.yml (Korean)

* Add missing semicolon

* Remove file-loader from dependencies (#4025)

* Update README.md [AUTOGEN] (#4028)

* Update README.md [AUTOGEN] (#4030)

* Add visibility test (#4029)

* Update ws requirement from 6.1.2 to 6.1.3 (#4027)

Updates the requirements on [ws](https://github.com/websockets/ws) to permit the latest version.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/commits/6.1.3)

Signed-off-by: dependabot[bot] <support@dependabot.com>

* Module 'web-push' as import syntax (#4017)

* Fix visibility test (#4031)

* Upgrade gulp version to 4.0.0

* Prevent typescript errors from crashing

* Remove duplicated dependencies

* Use parallel and task to specify dependencies

* Sort tasks by topological ordering

* リプライ/メンションされていれば非フォロワーへのフォロワー限定でも参照可能に (#4033)

* 非メンション/リプライ投稿がmentionsにあるかどうかはvisibilityと関係ないので削除

* リプライ/メンションされていれば非フォロワーでも参照可能に

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

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

* Fix #4034 (#4037)

* Fix #4034

* improve

* Module 'crypto' as import syntax (#4011)

* Extract MFM normalize function

* Extract MFM types

* Rename html to toHtml

* Rename html-to-mfm to fromHtml

* Merge plainParser into mfm

* Extract parsePlain function

* Rename analyze to parse in MFM tests

* Update @types/mongodb requirement from 3.1.18 to 3.1.19 (#4041)

Updates the requirements on [@types/mongodb](https://github.com/DefinitelyTyped/DefinitelyTyped) to permit the latest version.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits)

Signed-off-by: dependabot[bot] <support@dependabot.com>

* Update vue-svg-inline-loader requirement from 1.2.7 to 1.2.10 (#4040)

Updates the requirements on [vue-svg-inline-loader](https://github.com/oliverfindl/vue-svg-inline-loader) to permit the latest version.
- [Release notes](https://github.com/oliverfindl/vue-svg-inline-loader/releases)
- [Commits](https://github.com/oliverfindl/vue-svg-inline-loader/commits/v1.2.10)

Signed-off-by: dependabot[bot] <support@dependabot.com>

* Avoid export default

* Rename parser to language

* Fix import

* Introduce silence (#4043)

* Introduce silence

* Fix icon

* delete unnecessary key (#4045)

* delete unnecessary key

* Add note

* Update CHANGELOG.md

* [ActivityPub] Use microformats on mentions

To avoid pointless link previews.
see: https://misskey.xyz/notes/5c51ab5c2d85f2003248eddc

* Create type definition for '*/package.json' (#4014)

* Create type definition for '*/package.json'

* Update tsconfig.json

* Update index.ts

* Create type definition for 'is-root' (#4001)

* Update @types/sharp requirement from 0.21.0 to 0.21.1

Updates the requirements on [@types/sharp](https://github.com/DefinitelyTyped/DefinitelyTyped) to permit the latest version.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits)

Signed-off-by: dependabot[bot] <support@dependabot.com>

* Add multiline math syntax

Co-authored-by: syuilo <syuilotan@yahoo.co.jp>

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

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

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (English)

* Create type definition for 'is-root'

* [MFM] Add spin syntax

Resolve #4003

* [MFM] Add flip syntax

Resolve #4002

* Fix test

* Update CHANGELOG.md

* 10.79.0

* Update @fortawesome/free-regular-svg-icons requirement (#3963)

Updates the requirements on [@fortawesome/free-regular-svg-icons](https://github.com/FortAwesome/Font-Awesome) to permit the latest version.
- [Release notes](https://github.com/FortAwesome/Font-Awesome/releases)
- [Changelog](https://github.com/FortAwesome/Font-Awesome/blob/master/CHANGELOG.md)
- [Commits](https://github.com/FortAwesome/Font-Awesome/commits/5.6.3)

Signed-off-by: dependabot[bot] <support@dependabot.com>

* Update @types/webpack requirement from 4.4.21 to 4.4.24 (#3976)

Updates the requirements on [@types/webpack](https://github.com/DefinitelyTyped/DefinitelyTyped) to permit the latest version.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits)

Signed-off-by: dependabot[bot] <support@dependabot.com>

* Update @types/js-yaml requirement from 3.11.4 to 3.12.0 (#3977)

Updates the requirements on [@types/js-yaml](https://github.com/DefinitelyTyped/DefinitelyTyped) to permit the latest version.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits)

Signed-off-by: dependabot[bot] <support@dependabot.com>

* Update debug requirement from 4.1.0 to 4.1.1 (#3964)

Updates the requirements on [debug](https://github.com/visionmedia/debug) to permit the latest version.
- [Release notes](https://github.com/visionmedia/debug/releases)
- [Commits](https://github.com/visionmedia/debug/commits/4.1.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (French)

* [MFM] spinの中でflipを使えるように

* Add jump syntax (#4007)

* Add jump syntax

* Fix typo: spin -> jump

* Fix typo

* [MFM] Resolve #4009

* Module 'nprogress' as import syntax (#4012)

* 🎨

* [Client] Fix #4008

* Use yarn instead of npm on CircleCI

* touch yarn.lock

* [Client] Resolve #3638

* 10.79.1

* New translations ja-JP.yml (Korean)

* Add missing semicolon

* Remove file-loader from dependencies (#4025)

* Update README.md [AUTOGEN] (#4028)

* Update README.md [AUTOGEN] (#4030)

* Add visibility test (#4029)

* Update ws requirement from 6.1.2 to 6.1.3 (#4027)

Updates the requirements on [ws](https://github.com/websockets/ws) to permit the latest version.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/commits/6.1.3)

Signed-off-by: dependabot[bot] <support@dependabot.com>

* Module 'web-push' as import syntax (#4017)

* Fix visibility test (#4031)

* Upgrade gulp version to 4.0.0

* Prevent typescript errors from crashing

* Remove duplicated dependencies

* Use parallel and task to specify dependencies

* Sort tasks by topological ordering

* リプライ/メンションされていれば非フォロワーへのフォロワー限定でも参照可能に (#4033)

* 非メンション/リプライ投稿がmentionsにあるかどうかはvisibilityと関係ないので削除

* リプライ/メンションされていれば非フォロワーでも参照可能に

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

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

* Fix #4034 (#4037)

* Fix #4034

* improve

* Module 'crypto' as import syntax (#4011)

* Extract MFM normalize function

* Extract MFM types

* Rename html to toHtml

* Rename html-to-mfm to fromHtml

* Merge plainParser into mfm

* Extract parsePlain function

* Rename analyze to parse in MFM tests

* Update @types/mongodb requirement from 3.1.18 to 3.1.19 (#4041)

Updates the requirements on [@types/mongodb](https://github.com/DefinitelyTyped/DefinitelyTyped) to permit the latest version.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits)

Signed-off-by: dependabot[bot] <support@dependabot.com>

* Update vue-svg-inline-loader requirement from 1.2.7 to 1.2.10 (#4040)

Updates the requirements on [vue-svg-inline-loader](https://github.com/oliverfindl/vue-svg-inline-loader) to permit the latest version.
- [Release notes](https://github.com/oliverfindl/vue-svg-inline-loader/releases)
- [Commits](https://github.com/oliverfindl/vue-svg-inline-loader/commits/v1.2.10)

Signed-off-by: dependabot[bot] <support@dependabot.com>

* Avoid export default

* Rename parser to language

* Fix import

* Introduce silence (#4043)

* Introduce silence

* Fix icon

* Update is-root.d.ts

* Update index.ts

* Create type definition for 'is-root'

* Update is-root.d.ts

* Update index.ts

* Fix path

refs: 7e3a8d56e6

* Re-fix path

refs: 4bb4903ee5, 7e3a8d56e6

*  Do not import as pack from AP renderer  (#4048)

* Do not import as pack from AP renderer

* rename

* Simplify MFM (#4046)

* [MFM] Make some syntax block

Resolve #3508

* [MFM] Better syntax parsing

Allow nesting by same tag

* [Client] MFMの制限を緩和

* 🎨

* Update CHANGELOG.md

* 10.80.0

* Update is-url.d.ts
2019-02-03 22:21:36 +09:00
b4859be098 Improve log readability 2019-02-03 21:42:52 +09:00
df54da9510 Fix type declaretion 2019-02-03 20:32:46 +09:00
b97f788d71 Display version in logo 2019-02-03 20:31:35 +09:00
edd1baa9f4 🎨 2019-02-03 20:23:53 +09:00
4a23ebe534 Refactoring 2019-02-03 20:10:20 +09:00
64c1075b06 Remember the media playback volume (#4089)
* Remember the media playback volume

* fix
2019-02-03 19:33:23 +09:00
217e4ee39c New translations ja-JP.yml (Korean) (#4110) 2019-02-03 19:13:56 +09:00
7e2a7cdff8 Update README.md [AUTOGEN] (#4111) 2019-02-03 19:13:41 +09:00
214 changed files with 1618 additions and 692 deletions

View File

@ -2,7 +2,7 @@
# __MISSKEY_BEARER_TOKEN= # __MISSKEY_BEARER_TOKEN=
# __MISSKEY_CAMPAIGN_ID= # __MISSKEY_CAMPAIGN_ID=
# __MISSKEY_GITHUB_TOKEN= # __MISSKEY_GITHUB_TOKEN=
# __MISSKEY_HEAD=acid-chicken:patch-autogen # __MISSKEY_HEAD=syuilo:patch-autogen
# __MISSKEY_REPO=syuilo/misskey # __MISSKEY_REPO=syuilo/misskey
# __MISSKEY_BRANCH=develop # __MISSKEY_BRANCH=develop
test "$(curl -LSs -w '\n' -- "https://api.github.com/repos/$REPO/pulls?access_token=$__MISSKEY_GITHUB_TOKEN" | jq -r -f check_pr.jq | grep $__MISSKEY_HEAD)" && exit 1 test "$(curl -LSs -w '\n' -- "https://api.github.com/repos/$REPO/pulls?access_token=$__MISSKEY_GITHUB_TOKEN" | jq -r -f check_pr.jq | grep $__MISSKEY_HEAD)" && exit 1

View File

@ -134,12 +134,14 @@ workflows:
only: only:
- l10n_develop - l10n_develop
- imgbot - imgbot
- patch-autogen
- build: - build:
filters: filters:
branches: branches:
ignore: ignore:
- l10n_develop - l10n_develop
- imgbot - imgbot
- patch-autogen
- test: - test:
requires: requires:
- build - build
@ -149,13 +151,20 @@ workflows:
# - master # - master
- l10n_develop - l10n_develop
- imgbot - imgbot
- patch-autogen
- test: - test:
without_redis: "true" without_redis: "true"
requires: requires:
- build - build
filters: filters:
# branches:
# only: master
branches: branches:
only: master ignore:
# - master
- l10n_develop
- imgbot
- patch-autogen
# - docker: # - docker:
# filters: # filters:
# branches: # branches:

View File

@ -1,6 +1,12 @@
ChangeLog ChangeLog
========= =========
10.82.0
----------
* 自分の投稿情報をエクスポートできるように
* アニメーションする画像を再生しないで表示するオプションを実装
* 個別に投稿のウォッチ/ウォッチ解除をできるように
10.81.0 10.81.0
---------- ----------
* 動画のサムネイルを作成するように * 動画のサムネイルを作成するように

View File

@ -12,7 +12,6 @@ RUN unlink /usr/bin/free
RUN apk add --no-cache \ RUN apk add --no-cache \
autoconf \ autoconf \
automake \ automake \
ffmpeg \
file \ file \
g++ \ g++ \
gcc \ gcc \
@ -36,7 +35,9 @@ RUN node-gyp configure \
FROM base AS runner FROM base AS runner
RUN apk add --no-cache tini RUN apk add --no-cache \
ffmpeg \
tini
ENTRYPOINT ["/sbin/tini", "--"] ENTRYPOINT ["/sbin/tini", "--"]
COPY --from=builder /misskey/node_modules ./node_modules COPY --from=builder /misskey/node_modules ./node_modules

View File

@ -85,12 +85,11 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
</tr> </tr>
</table> </table>
:heart: Backers & Sponsors :heart: Backers
---------------------------------------------------------------- ----------------------------------------------------------------
<!-- PATREON_START --> <!-- PATREON_START -->
<table><tr> <table><tr>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12190916/fb7fa7983c14425f890369535b1506a4/1?token-time=2145916800&token-hash=WeuDzzz24cRXJogyIkU-mxARqkdyms-rcZKbO-GpGjw%3D" alt="weep" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12190916/fb7fa7983c14425f890369535b1506a4/1?token-time=2145916800&token-hash=WeuDzzz24cRXJogyIkU-mxARqkdyms-rcZKbO-GpGjw%3D" alt="weep" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/12059069" alt="naga_rus" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12731202/0995c46cdcb54153ab5f073f5869b70a/1?token-time=2145916800&token-hash=prtYqPOiSHBulhM7NU0VzMaWx39-9ntdq25b6kafDNA%3D" alt="negao" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12731202/0995c46cdcb54153ab5f073f5869b70a/1?token-time=2145916800&token-hash=prtYqPOiSHBulhM7NU0VzMaWx39-9ntdq25b6kafDNA%3D" alt="negao" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12913507/f7181eacafe8469a93033d85f5969c29/3?token-time=2145916800&token-hash=c8HeVqLtmdgH-gSBJg8i10gmOcwllM87MDHeznl3el0%3D" alt="Melilot" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12913507/f7181eacafe8469a93033d85f5969c29/3?token-time=2145916800&token-hash=c8HeVqLtmdgH-gSBJg8i10gmOcwllM87MDHeznl3el0%3D" alt="Melilot" 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>
@ -99,7 +98,6 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12021162/963128bb8d14476dbd8407943db8f31a/1?token-time=2145916800&token-hash=1FlxS9MEgmNGH_RHUVHbO5hIXB5I1z0lvA33CTvYvjA%3D" alt="gutfuckllc" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12021162/963128bb8d14476dbd8407943db8f31a/1?token-time=2145916800&token-hash=1FlxS9MEgmNGH_RHUVHbO5hIXB5I1z0lvA33CTvYvjA%3D" alt="gutfuckllc" width="100"></td>
</tr><tr> </tr><tr>
<td><a href="https://www.patreon.com/weepjp">weep</a></td> <td><a href="https://www.patreon.com/weepjp">weep</a></td>
<td><a href="https://www.patreon.com/user?u=12059069">naga_rus</a></td>
<td><a href="https://www.patreon.com/negao">negao</a></td> <td><a href="https://www.patreon.com/negao">negao</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/user?u=16869916">見当かなみ</a></td> <td><a href="https://www.patreon.com/user?u=16869916">見当かなみ</a></td>
@ -138,7 +136,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:** Tue, 29 Jan 2019 04:42:06 UTC **Last updated:** Sun, 03 Feb 2019 10:13:06 UTC
<!-- PATREON_END --> <!-- PATREON_END -->
:four_leaf_clover: Copyright :four_leaf_clover: Copyright

View File

@ -29,6 +29,7 @@ Please install and setup these softwares:
* [Redis](https://redis.io/) * [Redis](https://redis.io/)
* Redis is optional, but we strongly recommended to install it * Redis is optional, but we strongly recommended to install it
* [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/)
*3.* Setup MongoDB *3.* Setup MongoDB
---------------------------------------------------------------- ----------------------------------------------------------------
@ -54,6 +55,11 @@ As root:
*6.* Build Misskey *6.* Build Misskey
---------------------------------------------------------------- ----------------------------------------------------------------
Before build, you need to set `NODE_ENV` to `production`. like this:
* Linux: `export NODE_ENV=production`
* Windows (PowerShell): `$env:NODE_ENV="production"`
* Windows (CMD): `set NODE_ENV=production`
Build misskey with the following: Build misskey with the following:
`npm run build` `npm run build`

View File

@ -29,6 +29,7 @@ Installez les paquets suivants :
* [Redis](https://redis.io/) * [Redis](https://redis.io/)
* Redis est optionnel mais nous vous recommandons vivement de l'installer * Redis est optionnel mais nous vous recommandons vivement de l'installer
* [Elasticsearch](https://www.elastic.co/) - requis pour pouvoir activer la fonctionnalité de recherche * [Elasticsearch](https://www.elastic.co/) - requis pour pouvoir activer la fonctionnalité de recherche
* [FFmpeg](https://www.ffmpeg.org/)
*3.* Paramètrage de MongoDB *3.* Paramètrage de MongoDB
---------------------------------------------------------------- ----------------------------------------------------------------

View File

@ -32,9 +32,11 @@ adduser --disabled-password --disabled-login misskey
* 具体的には、Redisをインストールしないと、次の事が出来なくなります: * 具体的には、Redisをインストールしないと、次の事が出来なくなります:
* Misskeyプロセスを複数起動しての負荷分散 * Misskeyプロセスを複数起動しての負荷分散
* レートリミット * レートリミット
* ジョブキュー
* Twitter連携 * Twitter連携
* [Elasticsearch](https://www.elastic.co/) * [Elasticsearch](https://www.elastic.co/)
* 検索機能を有効にするためにはインストールが必要です。 * 検索機能を有効にするためにはインストールが必要です。
* [FFmpeg](https://www.ffmpeg.org/)
*3.* MongoDBの設定 *3.* MongoDBの設定
---------------------------------------------------------------- ----------------------------------------------------------------
@ -60,6 +62,11 @@ adduser --disabled-password --disabled-login misskey
*6.* Misskeyのビルド *6.* Misskeyのビルド
---------------------------------------------------------------- ----------------------------------------------------------------
ビルドする前に、`NODE_ENV``production`にする必要があります。例:
* Linux: `export NODE_ENV=production`
* Windows (PowerShell): `$env:NODE_ENV="production"`
* Windows (CMD): `set NODE_ENV=production`
次のコマンドでMisskeyをビルドしてください: 次のコマンドでMisskeyをビルドしてください:
`npm run build` `npm run build`

1
index.js Normal file
View File

@ -0,0 +1 @@
require('./built');

View File

@ -113,6 +113,7 @@ common:
use-avatar-reversi-stones: "リバーシの石にアバターを使う" use-avatar-reversi-stones: "リバーシの石にアバターを使う"
verified-user: "公式アカウント" verified-user: "公式アカウント"
disable-animated-mfm: "投稿内の動きのあるテキストを無効にする" disable-animated-mfm: "投稿内の動きのあるテキストを無効にする"
disable-showing-animated-images: "アニメーション画像を再生しない"
suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する" suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する"
always-show-nsfw: "常に閲覧注意のメディアを表示する" always-show-nsfw: "常に閲覧注意のメディアを表示する"
always-mark-nsfw: "常にメディアを閲覧注意として投稿" always-mark-nsfw: "常にメディアを閲覧注意として投稿"
@ -344,6 +345,8 @@ common/views/components/note-menu.vue:
copy-link: "リンクをコピー" copy-link: "リンクをコピー"
favorite: "お気に入り" favorite: "お気に入り"
unfavorite: "お気に入り解除" unfavorite: "お気に入り解除"
watch: "ウォッチ"
unwatch: "ウォッチ解除"
pin: "ピン留め" pin: "ピン留め"
unpin: "ピン留め解除" unpin: "ピン留め解除"
delete: "削除" delete: "削除"

View File

@ -113,6 +113,7 @@ common:
use-avatar-reversi-stones: "リバーシの石にアバターを使う" use-avatar-reversi-stones: "リバーシの石にアバターを使う"
verified-user: "Verifizierter Benutzer" verified-user: "Verifizierter Benutzer"
disable-animated-mfm: "投稿内の動きのあるテキストを無効にする" disable-animated-mfm: "投稿内の動きのあるテキストを無効にする"
disable-showing-animated-images: "アニメーション画像を再生しない"
suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する" suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する"
always-show-nsfw: "常に閲覧注意のメディアを表示する" always-show-nsfw: "常に閲覧注意のメディアを表示する"
always-mark-nsfw: "常にメディアを閲覧注意として投稿" always-mark-nsfw: "常にメディアを閲覧注意として投稿"
@ -344,6 +345,8 @@ common/views/components/note-menu.vue:
copy-link: "リンクをコピー" copy-link: "リンクをコピー"
favorite: "Diese Notiz favorisieren" favorite: "Diese Notiz favorisieren"
unfavorite: "Aus Favoriten entfernen" unfavorite: "Aus Favoriten entfernen"
watch: "ウォッチ"
unwatch: "ウォッチ解除"
pin: "An die Profilseite pinnen" pin: "An die Profilseite pinnen"
unpin: "ピン留め解除" unpin: "ピン留め解除"
delete: "Löschen" delete: "Löschen"

View File

@ -8,10 +8,10 @@ common:
about: "Thank you for finding Misskey. Misskey is a <b>decentralized microblogging platform</b> born on Earth. Since it exists within the Fediverse (a universe where various social media platforms are organized), it is mutually linked with other social media platforms. Why don't you take a short break from the hustle and bustle of the city, and dive into a new Internet?" about: "Thank you for finding Misskey. Misskey is a <b>decentralized microblogging platform</b> born on Earth. Since it exists within the Fediverse (a universe where various social media platforms are organized), it is mutually linked with other social media platforms. Why don't you take a short break from the hustle and bustle of the city, and dive into a new Internet?"
intro: intro:
title: "What is Misskey?" title: "What is Misskey?"
about: "Misskey is an open-source <b>decentralized microblogging service</b>. Sophisticated fully customizable UI, varieties of reactions for posts, free file storage providing an integrated management system and other advanced functions are available. In addition, Misskey connects to a network system called the “Fediverse” enables us to communicate with users on other SNSs. For example, when you post something it will be sent not only to Misskey but also Mastodon and Pleroma. Just imagine that the planet is sending a radio transmission to other planet to communicate." about: "Misskey is an open-source, <b>decentralized microblogging service</b>. Sophisticated, fully customizable UI, varieties of reactions for posts, free file storage providing an integrated management system and other advanced features are available. In addition, Misskey connects to a network system called the “Fediverse”, that enables us to communicate with users on other SNSs. For example, when you post something, it will be sent not only to Misskey, but also to Mastodon, Osada and Pleroma. Just imagine that the planet is sending a radio transmission to another planet to communicate."
features: "Features" features: "Features"
rich-contents: "Post" rich-contents: "Post"
rich-contents-desc: "Just post your idea, hot topics and anything you want to share. You may want to decorate your words, attach your favorite pictures, send files including movies and create a poll - those are the things you can do on Misskey!" rich-contents-desc: "Just post your idea, hot topics, and anything you want to share. You may want to decorate your words, attach your favorite pictures, send files, including videos, or create a poll - those are some of the things you can do with Misskey!"
reaction: "Reactions" reaction: "Reactions"
reaction-desc: "Easiest way to tell your emotions. Misskey allows you to add various type of reactions to others post. The emotional experience on Misskey will never be on other SNSs which only able to push “likes”." reaction-desc: "Easiest way to tell your emotions. Misskey allows you to add various type of reactions to others post. The emotional experience on Misskey will never be on other SNSs which only able to push “likes”."
ui: "Interface" ui: "Interface"
@ -113,6 +113,7 @@ common:
use-avatar-reversi-stones: "Use avatar as a stone in reversi" use-avatar-reversi-stones: "Use avatar as a stone in reversi"
verified-user: "Verified account" verified-user: "Verified account"
disable-animated-mfm: "Disable animated texts in a post" disable-animated-mfm: "Disable animated texts in a post"
disable-showing-animated-images: "Do not play animated images"
suggest-recent-hashtags: "Suggest recently used hashtags within the post composition area" suggest-recent-hashtags: "Suggest recently used hashtags within the post composition area"
always-show-nsfw: "Always show NSFW contents" always-show-nsfw: "Always show NSFW contents"
always-mark-nsfw: "Always mark posts with media attachments as NSFW" always-mark-nsfw: "Always mark posts with media attachments as NSFW"
@ -344,6 +345,8 @@ common/views/components/note-menu.vue:
copy-link: "Copy link" copy-link: "Copy link"
favorite: "Favorite this note" favorite: "Favorite this note"
unfavorite: "Unfavorite" unfavorite: "Unfavorite"
watch: "Watch"
unwatch: "Unwatch"
pin: "Pin to your profile" pin: "Pin to your profile"
unpin: "Unpin" unpin: "Unpin"
delete: "Delete" delete: "Delete"
@ -360,8 +363,8 @@ common/views/components/user-menu.vue:
report-abuse: "Report abuse" report-abuse: "Report abuse"
report-abuse-detail: "What kind of nuisance did you encounter?" report-abuse-detail: "What kind of nuisance did you encounter?"
report-abuse-reported: "The issue has been reported to the administrator. Your cooperation is much appreciated." report-abuse-reported: "The issue has been reported to the administrator. Your cooperation is much appreciated."
silence: "Make Silence" silence: "Mute"
unsilence: "Unsilence" unsilence: "Unmute"
suspend: "Suspend" suspend: "Suspend"
unsuspend: "Unsuspend" unsuspend: "Unsuspend"
common/views/components/poll.vue: common/views/components/poll.vue:
@ -1147,8 +1150,8 @@ admin/views/users.vue:
unsuspend: "Unsuspend" unsuspend: "Unsuspend"
unsuspend-confirm: "Are you sure you want to unsuspend this account?" unsuspend-confirm: "Are you sure you want to unsuspend this account?"
unsuspended: "The user has successfully unsuspended." unsuspended: "The user has successfully unsuspended."
make-silence: "Make Silence" make-silence: "Mute"
unmake-silence: "Unmake Silence" unmake-silence: "Unmute"
verify: "Verify account" verify: "Verify account"
verify-confirm: "Do you want this to be a verified account?" verify-confirm: "Do you want this to be a verified account?"
verified: "The account is now being verified" verified: "The account is now being verified"

View File

@ -113,6 +113,7 @@ common:
use-avatar-reversi-stones: "リバーシの石にアバターを使う" use-avatar-reversi-stones: "リバーシの石にアバターを使う"
verified-user: "Cuenta verificada" verified-user: "Cuenta verificada"
disable-animated-mfm: "Desactivar texto animado en una publicación" disable-animated-mfm: "Desactivar texto animado en una publicación"
disable-showing-animated-images: "アニメーション画像を再生しない"
suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する" suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する"
always-show-nsfw: "常に閲覧注意のメディアを表示する" always-show-nsfw: "常に閲覧注意のメディアを表示する"
always-mark-nsfw: "常にメディアを閲覧注意として投稿" always-mark-nsfw: "常にメディアを閲覧注意として投稿"
@ -344,6 +345,8 @@ common/views/components/note-menu.vue:
copy-link: "Copiar enlace" copy-link: "Copiar enlace"
favorite: "Me gusta esta nota" favorite: "Me gusta esta nota"
unfavorite: "お気に入り解除" unfavorite: "お気に入り解除"
watch: "ウォッチ"
unwatch: "ウォッチ解除"
pin: "Fijar en el perfil" pin: "Fijar en el perfil"
unpin: "ピン留め解除" unpin: "ピン留め解除"
delete: "Borrar" delete: "Borrar"

View File

@ -113,6 +113,7 @@ common:
use-avatar-reversi-stones: "Utiliser lavatar comme pion dans Reversi" use-avatar-reversi-stones: "Utiliser lavatar comme pion dans Reversi"
verified-user: "Compte vérifié" verified-user: "Compte vérifié"
disable-animated-mfm: "Désactiver les textes animés dans les publications" disable-animated-mfm: "Désactiver les textes animés dans les publications"
disable-showing-animated-images: "アニメーション画像を再生しない"
suggest-recent-hashtags: "Suggérer les hashtags récemment utilisés dans le champs de saisie" suggest-recent-hashtags: "Suggérer les hashtags récemment utilisés dans le champs de saisie"
always-show-nsfw: "Toujours afficher les contenus sensibles" always-show-nsfw: "Toujours afficher les contenus sensibles"
always-mark-nsfw: "Toujours marquer les notes ayant des attachements comme sensibles" always-mark-nsfw: "Toujours marquer les notes ayant des attachements comme sensibles"
@ -344,6 +345,8 @@ common/views/components/note-menu.vue:
copy-link: "Copier le lien" copy-link: "Copier le lien"
favorite: "Mettre cette note en favoris" favorite: "Mettre cette note en favoris"
unfavorite: "Retirer des favoris" unfavorite: "Retirer des favoris"
watch: "ウォッチ"
unwatch: "ウォッチ解除"
pin: "Épingler sur votre profil" pin: "Épingler sur votre profil"
unpin: "Désépingler" unpin: "Désépingler"
delete: "Supprimer" delete: "Supprimer"

5
locales/index.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
type Locale = { [key: string]: string };
declare const locales: { [lang: string]: Locale };
export default locales;

View File

@ -113,6 +113,7 @@ common:
use-avatar-reversi-stones: "リバーシの石にアバターを使う" use-avatar-reversi-stones: "リバーシの石にアバターを使う"
verified-user: "公式アカウント" verified-user: "公式アカウント"
disable-animated-mfm: "投稿内の動きのあるテキストを無効にする" disable-animated-mfm: "投稿内の動きのあるテキストを無効にする"
disable-showing-animated-images: "アニメーション画像を再生しない"
suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する" suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する"
always-show-nsfw: "常に閲覧注意のメディアを表示する" always-show-nsfw: "常に閲覧注意のメディアを表示する"
always-mark-nsfw: "常にメディアを閲覧注意として投稿" always-mark-nsfw: "常にメディアを閲覧注意として投稿"
@ -344,6 +345,8 @@ common/views/components/note-menu.vue:
copy-link: "リンクをコピー" copy-link: "リンクをコピー"
favorite: "お気に入り" favorite: "お気に入り"
unfavorite: "お気に入り解除" unfavorite: "お気に入り解除"
watch: "ウォッチ"
unwatch: "ウォッチ解除"
pin: "ピン留め" pin: "ピン留め"
unpin: "ピン留め解除" unpin: "ピン留め解除"
delete: "削除" delete: "削除"

View File

@ -121,6 +121,7 @@ common:
use-avatar-reversi-stones: "リバーシの石にアバターを使う" use-avatar-reversi-stones: "リバーシの石にアバターを使う"
verified-user: "公式アカウント" verified-user: "公式アカウント"
disable-animated-mfm: "投稿内の動きのあるテキストを無効にする" disable-animated-mfm: "投稿内の動きのあるテキストを無効にする"
disable-showing-animated-images: "アニメーション画像を再生しない"
suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する" suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する"
always-show-nsfw: "常に閲覧注意のメディアを表示する" always-show-nsfw: "常に閲覧注意のメディアを表示する"
always-mark-nsfw: "常にメディアを閲覧注意として投稿" always-mark-nsfw: "常にメディアを閲覧注意として投稿"
@ -374,6 +375,8 @@ common/views/components/note-menu.vue:
copy-link: "リンクをコピー" copy-link: "リンクをコピー"
favorite: "お気に入り" favorite: "お気に入り"
unfavorite: "お気に入り解除" unfavorite: "お気に入り解除"
watch: "ウォッチ"
unwatch: "ウォッチ解除"
pin: "ピン留め" pin: "ピン留め"
unpin: "ピン留め解除" unpin: "ピン留め解除"
delete: "削除" delete: "削除"
@ -554,6 +557,9 @@ common/views/components/profile-editor.vue:
email-address: "メールアドレス" email-address: "メールアドレス"
email-verified: "メールアドレスが確認されました" email-verified: "メールアドレスが確認されました"
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。" email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
export: "エクスポート"
export-notes: "すべての投稿のエクスポート"
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
common/views/components/user-list-editor.vue: common/views/components/user-list-editor.vue:
users: "ユーザー" users: "ユーザー"

View File

@ -113,6 +113,7 @@ common:
use-avatar-reversi-stones: "リバーシの石にアバターを使う" use-avatar-reversi-stones: "リバーシの石にアバターを使う"
verified-user: "アメちゃん付きアカウント" verified-user: "アメちゃん付きアカウント"
disable-animated-mfm: "投稿内のちょろちょろ動いてんのを止める" disable-animated-mfm: "投稿内のちょろちょろ動いてんのを止める"
disable-showing-animated-images: "アニメーション画像を再生しない"
suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する" suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する"
always-show-nsfw: "閲覧注意?見せたらあかん?そんなん知らんわ、見せろや!" always-show-nsfw: "閲覧注意?見せたらあかん?そんなん知らんわ、見せろや!"
always-mark-nsfw: "わからんからとりあえずメディアは見せたらあかん" always-mark-nsfw: "わからんからとりあえずメディアは見せたらあかん"
@ -344,6 +345,8 @@ common/views/components/note-menu.vue:
copy-link: "リンクをコピー" copy-link: "リンクをコピー"
favorite: "お気に入り" favorite: "お気に入り"
unfavorite: "お気に入りやめる" unfavorite: "お気に入りやめる"
watch: "ウォッチ"
unwatch: "ウォッチ解除"
pin: "ピン留め" pin: "ピン留め"
unpin: "ピン留めやめる" unpin: "ピン留めやめる"
delete: "ほかす" delete: "ほかす"

View File

@ -113,6 +113,7 @@ common:
use-avatar-reversi-stones: "리버시의 돌로 아바타를 사용" use-avatar-reversi-stones: "리버시의 돌로 아바타를 사용"
verified-user: "공식 계정" verified-user: "공식 계정"
disable-animated-mfm: "글의 문자 애니메이션을 비활성화" disable-animated-mfm: "글의 문자 애니메이션을 비활성화"
disable-showing-animated-images: "アニメーション画像を再生しない"
suggest-recent-hashtags: "최근 해시태그를 글 작성란에 표시" suggest-recent-hashtags: "최근 해시태그를 글 작성란에 표시"
always-show-nsfw: "항상 열람주의 미디어를 표시" always-show-nsfw: "항상 열람주의 미디어를 표시"
always-mark-nsfw: "항상 미디어를 열람주의로 설정하여 게시" always-mark-nsfw: "항상 미디어를 열람주의로 설정하여 게시"
@ -129,7 +130,7 @@ common:
show-password: "비밀번호 표시" show-password: "비밀번호 표시"
do-not-use-in-production: "이것은 개발 빌드입니다. 프로덕션 환경에서 사용하지 마십시오." do-not-use-in-production: "이것은 개발 빌드입니다. 프로덕션 환경에서 사용하지 마십시오."
user-suspended: "이 사용자는 정지된 상태입니다." user-suspended: "이 사용자는 정지된 상태입니다."
is-remote-user: "このユーザー情報は不正確な可能性があります。" is-remote-user: "이 사용자 정보는 정확하지 않을 수 있습니다."
is-remote-post: "이 글 정보는 복사본입니다." is-remote-post: "이 글 정보는 복사본입니다."
view-on-remote: "정확한 정보 보기" view-on-remote: "정확한 정보 보기"
renoted-by: "{user}이(가) 리노트" renoted-by: "{user}이(가) 리노트"
@ -344,6 +345,8 @@ common/views/components/note-menu.vue:
copy-link: "링크 복사" copy-link: "링크 복사"
favorite: "이 노트 즐겨찾기" favorite: "이 노트 즐겨찾기"
unfavorite: "즐겨찾기에서 제거" unfavorite: "즐겨찾기에서 제거"
watch: "ウォッチ"
unwatch: "ウォッチ解除"
pin: "프로필에 고정" pin: "프로필에 고정"
unpin: "프로필에서 고정 해제" unpin: "프로필에서 고정 해제"
delete: "삭제" delete: "삭제"
@ -360,10 +363,10 @@ common/views/components/user-menu.vue:
report-abuse: "스팸 신고" report-abuse: "스팸 신고"
report-abuse-detail: "어떤 스팸 행위를 하고 있습니까?" report-abuse-detail: "어떤 스팸 행위를 하고 있습니까?"
report-abuse-reported: "관리자에게 보고되었습니다. 협조해주셔서 감사합니다." report-abuse-reported: "관리자에게 보고되었습니다. 협조해주셔서 감사합니다."
silence: "サイレンス" silence: "침묵"
unsilence: "サイレンス解除" unsilence: "침묵 해제"
suspend: "凍結" suspend: "정지"
unsuspend: "凍結解除" unsuspend: "정지 해제"
common/views/components/poll.vue: common/views/components/poll.vue:
vote-to: "\"{}\"에 투표하기" vote-to: "\"{}\"에 투표하기"
vote-count: "{}표" vote-count: "{}표"

View File

@ -113,6 +113,7 @@ common:
use-avatar-reversi-stones: "リバーシの石にアバターを使う" use-avatar-reversi-stones: "リバーシの石にアバターを使う"
verified-user: "公式アカウント" verified-user: "公式アカウント"
disable-animated-mfm: "投稿内の動きのあるテキストを無効にする" disable-animated-mfm: "投稿内の動きのあるテキストを無効にする"
disable-showing-animated-images: "アニメーション画像を再生しない"
suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する" suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する"
always-show-nsfw: "常に閲覧注意のメディアを表示する" always-show-nsfw: "常に閲覧注意のメディアを表示する"
always-mark-nsfw: "常にメディアを閲覧注意として投稿" always-mark-nsfw: "常にメディアを閲覧注意として投稿"
@ -344,6 +345,8 @@ common/views/components/note-menu.vue:
copy-link: "リンクをコピー" copy-link: "リンクをコピー"
favorite: "Deze notitie toevoegen aan favorieten" favorite: "Deze notitie toevoegen aan favorieten"
unfavorite: "お気に入り解除" unfavorite: "お気に入り解除"
watch: "ウォッチ"
unwatch: "ウォッチ解除"
pin: "Vastmaken aan profielpagina" pin: "Vastmaken aan profielpagina"
unpin: "ピン留め解除" unpin: "ピン留め解除"
delete: "削除" delete: "削除"

View File

@ -113,6 +113,7 @@ common:
use-avatar-reversi-stones: "リバーシの石にアバターを使う" use-avatar-reversi-stones: "リバーシの石にアバターを使う"
verified-user: "公式アカウント" verified-user: "公式アカウント"
disable-animated-mfm: "投稿内の動きのあるテキストを無効にする" disable-animated-mfm: "投稿内の動きのあるテキストを無効にする"
disable-showing-animated-images: "アニメーション画像を再生しない"
suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する" suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する"
always-show-nsfw: "常に閲覧注意のメディアを表示する" always-show-nsfw: "常に閲覧注意のメディアを表示する"
always-mark-nsfw: "常にメディアを閲覧注意として投稿" always-mark-nsfw: "常にメディアを閲覧注意として投稿"
@ -344,6 +345,8 @@ common/views/components/note-menu.vue:
copy-link: "リンクをコピー" copy-link: "リンクをコピー"
favorite: "Merket som favoritt" favorite: "Merket som favoritt"
unfavorite: "お気に入り解除" unfavorite: "お気に入り解除"
watch: "ウォッチ"
unwatch: "ウォッチ解除"
pin: "Fest til profilen din" pin: "Fest til profilen din"
unpin: "ピン留め解除" unpin: "ピン留め解除"
delete: "Slett" delete: "Slett"

View File

@ -113,6 +113,7 @@ common:
use-avatar-reversi-stones: "リバーシの石にアバターを使う" use-avatar-reversi-stones: "リバーシの石にアバターを使う"
verified-user: "Zweryfikowane konto" verified-user: "Zweryfikowane konto"
disable-animated-mfm: "Wyłącz animowany tekst we wpisach" disable-animated-mfm: "Wyłącz animowany tekst we wpisach"
disable-showing-animated-images: "アニメーション画像を再生しない"
suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する" suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する"
always-show-nsfw: "Zawszę pokazuj zawartość NSFW" always-show-nsfw: "Zawszę pokazuj zawartość NSFW"
always-mark-nsfw: "Zawsze oznaczaj posty z multimediami jako NSFW" always-mark-nsfw: "Zawsze oznaczaj posty z multimediami jako NSFW"
@ -344,6 +345,8 @@ common/views/components/note-menu.vue:
copy-link: "Skopiuj adres" copy-link: "Skopiuj adres"
favorite: "Dodaj do ulubionych" favorite: "Dodaj do ulubionych"
unfavorite: "Usuń z ulubionych" unfavorite: "Usuń z ulubionych"
watch: "ウォッチ"
unwatch: "ウォッチ解除"
pin: "Przypnij do profilu" pin: "Przypnij do profilu"
unpin: "Odepnij" unpin: "Odepnij"
delete: "Usuń" delete: "Usuń"
@ -356,7 +359,7 @@ common/views/components/user-menu.vue:
block: "Zablokuj" block: "Zablokuj"
unblock: "Odblokuj" unblock: "Odblokuj"
push-to-list: "Dodaj do listy" push-to-list: "Dodaj do listy"
select-list: "リストを選択してください" select-list: "Wybierz listę"
report-abuse: "Zgłoś nadużycie" report-abuse: "Zgłoś nadużycie"
report-abuse-detail: "どのような迷惑行為を行っていますか?" report-abuse-detail: "どのような迷惑行為を行っていますか?"
report-abuse-reported: "管理者に報告されました。ご協力ありがとうございました。" report-abuse-reported: "管理者に報告されました。ご協力ありがとうございました。"
@ -395,11 +398,11 @@ common/views/components/signin.vue:
token: "Token" token: "Token"
signing-in: "Logowanie…" signing-in: "Logowanie…"
signin: "Zaloguj" signin: "Zaloguj"
or: "または" or: "lub"
signin-with-twitter: "Zaloguj się za pomocą Twittera" signin-with-twitter: "Zaloguj się za pomocą Twittera"
signin-with-github: "Zaloguj się za pomocą GitHuba" signin-with-github: "Zaloguj się za pomocą GitHuba"
signin-with-discord: "Zaloguj się za pomocą Discorda" signin-with-discord: "Zaloguj się za pomocą Discorda"
login-failed: "ログインできませんでした。ユーザー名とパスワードを確認してください。" login-failed: "Logowanie nie powiodło się. Upewnij się, że podałeś prawidłową nazwę użytkownika i hasło."
common/views/components/signup.vue: common/views/components/signup.vue:
invitation-code: "Kod zaproszenia" invitation-code: "Kod zaproszenia"
invitation-info: "招待コードをお持ちでない方は、<a href=\"{}\">管理者</a>までご連絡ください。" invitation-info: "招待コードをお持ちでない方は、<a href=\"{}\">管理者</a>までご連絡ください。"
@ -472,13 +475,13 @@ common/views/components/visibility-chooser.vue:
local-followers: "Dla śledzących (tylko lokalnie)" local-followers: "Dla śledzących (tylko lokalnie)"
common/views/components/trends.vue: common/views/components/trends.vue:
count: "{}人が投稿" count: "{}人が投稿"
empty: "トレンドなし" empty: "Brak popularnych hashtagów"
common/views/components/language-settings.vue: common/views/components/language-settings.vue:
title: "Język" title: "Język"
pick-language: "Wybierz język" pick-language: "Wybierz język"
recommended: "Zalecane" recommended: "Zalecane"
auto: "Automatyczny" auto: "Automatyczny"
specify-language: "言語を指定" specify-language: "Wybierz język"
info: "Musisz odświeżyć stronę, aby zmiany zostały uwzględnione." info: "Musisz odświeżyć stronę, aby zmiany zostały uwzględnione."
common/views/components/profile-editor.vue: common/views/components/profile-editor.vue:
title: "Twój profil" title: "Twój profil"
@ -504,15 +507,15 @@ common/views/components/profile-editor.vue:
upload-failed: "Wysyłanie nie powiodło się" upload-failed: "Wysyłanie nie powiodło się"
email: "Ustawienia e-mail" email: "Ustawienia e-mail"
email-address: "Adres e-mail" email-address: "Adres e-mail"
email-verified: "メールアドレスが確認されました" email-verified: "Twój adres e-mail został zweryfikowany."
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。" email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
common/views/components/user-list-editor.vue: common/views/components/user-list-editor.vue:
users: "Użytkownicy" users: "Użytkownicy"
rename: "Zmień nazwę listy" rename: "Zmień nazwę listy"
delete: "Usuń listę" delete: "Usuń listę"
remove-user: "Usuń z tej listy" remove-user: "Usuń z tej listy"
delete-are-you-sure: "リスト「$1」を削除しますか" delete-are-you-sure: "Usunąć listę \"$1\"?"
deleted: "削除しました" deleted: "Usunięto"
common/views/widgets/broadcast.vue: common/views/widgets/broadcast.vue:
fetching: "Sprawdzanie" fetching: "Sprawdzanie"
no-broadcasts: "Brak transmisji" no-broadcasts: "Brak transmisji"
@ -595,7 +598,7 @@ desktop/views/components/activity.vue:
title: "Aktywność" title: "Aktywność"
toggle: "Przełącz widok" toggle: "Przełącz widok"
desktop/views/components/calendar.vue: desktop/views/components/calendar.vue:
title: "{year} {month}" title: "{year} / {month}"
prev: "Poprzedni miesiąc" prev: "Poprzedni miesiąc"
next: "Następny miesiąc" next: "Następny miesiąc"
go: "Naciśnij, aby przejść" go: "Naciśnij, aby przejść"
@ -706,7 +709,7 @@ desktop/views/components/note.vue:
add-reaction: "Dodaj reakcję" add-reaction: "Dodaj reakcję"
undo-reaction: "リアクション解除" undo-reaction: "リアクション解除"
detail: "Szczegóły" detail: "Szczegóły"
private: "この投稿は非公開です" private: "Ten wpis jest prywatny"
deleted: "この投稿は削除されました" deleted: "この投稿は削除されました"
desktop/views/components/notes.vue: desktop/views/components/notes.vue:
error: "Ładowanie nie powiodło się." error: "Ładowanie nie powiodło się."
@ -1011,7 +1014,7 @@ admin/views/abuse.vue:
target: "対象" target: "対象"
reporter: "報告者" reporter: "報告者"
details: "詳細" details: "詳細"
remove-report: "削除" remove-report: "Usuń"
admin/views/instance.vue: admin/views/instance.vue:
instance: "インスタンス" instance: "インスタンス"
instance-name: "インスタンス名" instance-name: "インスタンス名"

View File

@ -113,6 +113,7 @@ common:
use-avatar-reversi-stones: "リバーシの石にアバターを使う" use-avatar-reversi-stones: "リバーシの石にアバターを使う"
verified-user: "Conta verificada" verified-user: "Conta verificada"
disable-animated-mfm: "Desativar texto animado nas publicações" disable-animated-mfm: "Desativar texto animado nas publicações"
disable-showing-animated-images: "アニメーション画像を再生しない"
suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する" suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する"
always-show-nsfw: "常に閲覧注意のメディアを表示する" always-show-nsfw: "常に閲覧注意のメディアを表示する"
always-mark-nsfw: "常にメディアを閲覧注意として投稿" always-mark-nsfw: "常にメディアを閲覧注意として投稿"
@ -344,6 +345,8 @@ common/views/components/note-menu.vue:
copy-link: "リンクをコピー" copy-link: "リンクをコピー"
favorite: "お気に入り" favorite: "お気に入り"
unfavorite: "お気に入り解除" unfavorite: "お気に入り解除"
watch: "ウォッチ"
unwatch: "ウォッチ解除"
pin: "ピン留め" pin: "ピン留め"
unpin: "ピン留め解除" unpin: "ピン留め解除"
delete: "削除" delete: "削除"

View File

@ -113,6 +113,7 @@ common:
use-avatar-reversi-stones: "リバーシの石にアバターを使う" use-avatar-reversi-stones: "リバーシの石にアバターを使う"
verified-user: "公式アカウント" verified-user: "公式アカウント"
disable-animated-mfm: "Отключить анимированный текст в постах" disable-animated-mfm: "Отключить анимированный текст в постах"
disable-showing-animated-images: "アニメーション画像を再生しない"
suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する" suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する"
always-show-nsfw: "Всегда показывать NSFW контент" always-show-nsfw: "Всегда показывать NSFW контент"
always-mark-nsfw: "Всегда помечать посты с медиафайлами как NSFW" always-mark-nsfw: "Всегда помечать посты с медиафайлами как NSFW"
@ -344,6 +345,8 @@ common/views/components/note-menu.vue:
copy-link: "リンクをコピー" copy-link: "リンクをコピー"
favorite: "お気に入り" favorite: "お気に入り"
unfavorite: "お気に入り解除" unfavorite: "お気に入り解除"
watch: "ウォッチ"
unwatch: "ウォッチ解除"
pin: "ピン留め" pin: "ピン留め"
unpin: "ピン留め解除" unpin: "ピン留め解除"
delete: "削除" delete: "削除"

View File

@ -113,6 +113,7 @@ common:
use-avatar-reversi-stones: "用头像作为黑白棋的棋子" use-avatar-reversi-stones: "用头像作为黑白棋的棋子"
verified-user: "认证用户" verified-user: "认证用户"
disable-animated-mfm: "在帖子中禁用动画文本" disable-animated-mfm: "在帖子中禁用动画文本"
disable-showing-animated-images: "不播放动画"
suggest-recent-hashtags: "在帖子表单上显示最近流行的主题标签" suggest-recent-hashtags: "在帖子表单上显示最近流行的主题标签"
always-show-nsfw: "总是显示 NSFW 的内容" always-show-nsfw: "总是显示 NSFW 的内容"
always-mark-nsfw: "总是用 NSFW 来标记附件" always-mark-nsfw: "总是用 NSFW 来标记附件"
@ -344,6 +345,8 @@ common/views/components/note-menu.vue:
copy-link: "复制链接" copy-link: "复制链接"
favorite: "收藏这个投稿" favorite: "收藏这个投稿"
unfavorite: "取消收藏" unfavorite: "取消收藏"
watch: "关注"
unwatch: "取消关注"
pin: "固定个人资料" pin: "固定个人资料"
unpin: "解除固定" unpin: "解除固定"
delete: "删除" delete: "删除"

View File

@ -1,14 +1,18 @@
{ {
"name": "misskey", "name": "misskey",
"author": "syuilo <i@syuilo.com>", "author": "syuilo <i@syuilo.com>",
"version": "10.81.0", "version": "10.82.0",
"clientVersion": "2.0.14026", "clientVersion": "2.0.14114",
"codename": "nighthike", "codename": "nighthike",
"main": "./built/index.js", "repository": {
"type": "git",
"url": "https://github.com/syuilo/misskey.git"
},
"main": "./index.js",
"private": true, "private": true,
"scripts": { "scripts": {
"start": "node ./built", "start": "node ./index.js",
"debug": "DEBUG=misskey:* node ./built", "debug": "DEBUG=misskey:* node ./index.js",
"build": "webpack && gulp build", "build": "webpack && gulp build",
"webpack": "webpack", "webpack": "webpack",
"watch": "webpack --watch", "watch": "webpack --watch",
@ -48,6 +52,7 @@
"@types/is-svg": "3.0.0", "@types/is-svg": "3.0.0",
"@types/is-url": "1.2.28", "@types/is-url": "1.2.28",
"@types/js-yaml": "3.12.0", "@types/js-yaml": "3.12.0",
"@types/jsdom": "12.2.1",
"@types/katex": "0.5.0", "@types/katex": "0.5.0",
"@types/koa": "2.0.48", "@types/koa": "2.0.48",
"@types/koa-bodyparser": "5.0.2", "@types/koa-bodyparser": "5.0.2",
@ -94,7 +99,7 @@
"@types/websocket": "0.0.40", "@types/websocket": "0.0.40",
"@types/ws": "6.0.1", "@types/ws": "6.0.1",
"animejs": "3.0.1", "animejs": "3.0.1",
"apexcharts": "2.5.1", "apexcharts": "3.2.1",
"autobind-decorator": "2.4.0", "autobind-decorator": "2.4.0",
"autosize": "4.0.2", "autosize": "4.0.2",
"autwh": "0.1.0", "autwh": "0.1.0",
@ -107,7 +112,7 @@
"chalk": "2.4.2", "chalk": "2.4.2",
"commander": "2.19.0", "commander": "2.19.0",
"crc-32": "1.2.0", "crc-32": "1.2.0",
"css-loader": "1.0.1", "css-loader": "2.1.0",
"cssnano": "4.1.8", "cssnano": "4.1.8",
"dateformat": "3.0.3", "dateformat": "3.0.3",
"deep-equal": "1.0.1", "deep-equal": "1.0.1",
@ -121,7 +126,7 @@
"eslint-plugin-vue": "5.1.0", "eslint-plugin-vue": "5.1.0",
"eventemitter3": "3.1.0", "eventemitter3": "3.1.0",
"feed": "2.0.2", "feed": "2.0.2",
"file-type": "10.7.0", "file-type": "10.7.1",
"fuckadblock": "3.2.1", "fuckadblock": "3.2.1",
"gulp": "4.0.0", "gulp": "4.0.0",
"gulp-cssnano": "2.1.3", "gulp-cssnano": "2.1.3",
@ -148,7 +153,7 @@
"json5": "2.1.0", "json5": "2.1.0",
"json5-loader": "1.0.1", "json5-loader": "1.0.1",
"katex": "0.10.0", "katex": "0.10.0",
"koa": "2.6.2", "koa": "2.7.0",
"koa-bodyparser": "4.2.1", "koa-bodyparser": "4.2.1",
"koa-compress": "3.0.0", "koa-compress": "3.0.0",
"koa-favicon": "2.0.1", "koa-favicon": "2.0.1",
@ -163,12 +168,12 @@
"langmap": "0.0.16", "langmap": "0.0.16",
"loader-utils": "1.2.3", "loader-utils": "1.2.3",
"lookup-dns-cache": "2.1.0", "lookup-dns-cache": "2.1.0",
"minio": "7.0.3", "minio": "7.0.5",
"mkdirp": "0.5.1", "mkdirp": "0.5.1",
"mocha": "5.2.0", "mocha": "5.2.0",
"moji": "0.5.1", "moji": "0.5.1",
"moment": "2.23.0", "moment": "2.24.0",
"mongodb": "3.1.10", "mongodb": "3.1.13",
"monk": "6.0.6", "monk": "6.0.6",
"ms": "2.1.1", "ms": "2.1.1",
"nan": "2.12.1", "nan": "2.12.1",
@ -183,7 +188,7 @@
"portscanner": "2.2.0", "portscanner": "2.2.0",
"postcss-loader": "3.0.0", "postcss-loader": "3.0.0",
"prismjs": "1.15.0", "prismjs": "1.15.0",
"progress-bar-webpack-plugin": "1.12.0", "progress-bar-webpack-plugin": "1.12.1",
"promise-any": "0.2.0", "promise-any": "0.2.0",
"promise-limit": "2.7.0", "promise-limit": "2.7.0",
"promise-sequential": "1.1.1", "promise-sequential": "1.1.1",
@ -213,14 +218,14 @@
"summaly": "2.2.0", "summaly": "2.2.0",
"systeminformation": "3.54.0", "systeminformation": "3.54.0",
"syuilo-password-strength": "0.0.1", "syuilo-password-strength": "0.0.1",
"terser-webpack-plugin": "1.2.1", "terser-webpack-plugin": "1.2.2",
"textarea-caret": "3.1.0", "textarea-caret": "3.1.0",
"tinycolor2": "1.4.1", "tinycolor2": "1.4.1",
"tmp": "0.0.33", "tmp": "0.0.33",
"ts-loader": "5.3.3", "ts-loader": "5.3.3",
"ts-node": "7.0.1", "ts-node": "7.0.1",
"tslint": "5.12.0", "tslint": "5.12.0",
"tslint-sonarts": "1.8.0", "tslint-sonarts": "1.9.0",
"typescript": "3.2.4", "typescript": "3.2.4",
"typescript-eslint-parser": "21.0.2", "typescript-eslint-parser": "21.0.2",
"uglify-es": "3.3.9", "uglify-es": "3.3.9",
@ -228,23 +233,23 @@
"uuid": "3.3.2", "uuid": "3.3.2",
"v-animate-css": "0.0.3", "v-animate-css": "0.0.3",
"video-thumbnail-generator": "1.1.3", "video-thumbnail-generator": "1.1.3",
"vue": "2.5.17", "vue": "2.6.2",
"vue-color": "2.7.0", "vue-color": "2.7.0",
"vue-content-loading": "1.5.3", "vue-content-loading": "1.5.3",
"vue-cropperjs": "3.0.0", "vue-cropperjs": "3.0.0",
"vue-i18n": "8.8.0", "vue-i18n": "8.8.0",
"vue-js-modal": "1.3.28", "vue-js-modal": "1.3.28",
"vue-loader": "15.5.1", "vue-loader": "15.6.2",
"vue-marquee-text-component": "1.1.1", "vue-marquee-text-component": "1.1.1",
"vue-prism-component": "1.1.1", "vue-prism-component": "1.1.1",
"vue-router": "3.0.2", "vue-router": "3.0.2",
"vue-sequential-entrance": "1.1.3", "vue-sequential-entrance": "1.1.3",
"vue-style-loader": "4.1.2", "vue-style-loader": "4.1.2",
"vue-svg-inline-loader": "1.2.10", "vue-svg-inline-loader": "1.2.10",
"vue-template-compiler": "2.5.17", "vue-template-compiler": "2.6.2",
"vuedraggable": "2.17.0", "vuedraggable": "2.17.0",
"vuewordcloud": "18.7.11", "vuewordcloud": "18.7.11",
"vuex": "3.0.1", "vuex": "3.1.0",
"vuex-persistedstate": "2.5.4", "vuex-persistedstate": "2.5.4",
"web-push": "3.3.3", "web-push": "3.3.3",
"webfinger.js": "2.7.0", "webfinger.js": "2.7.0",

3
src/@types/const.json.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
declare module '*/const.json' {
const copyright: string;
}

View File

@ -8,7 +8,7 @@ declare namespace deepcopy {
valueType: DeepcopyCustomizerValueType) => T; valueType: DeepcopyCustomizerValueType) => T;
interface DeepcopyOptions<T> { interface DeepcopyOptions<T> {
customizer: DeepcopyCustomizer<T> customizer: DeepcopyCustomizer<T>;
} }
export function deepcopy<T>( export function deepcopy<T>(

7
src/@types/escape-regexp.d.ts vendored Normal file
View File

@ -0,0 +1,7 @@
declare module 'escape-regexp' {
function escapeRegExp(str: string): string;
namespace escapeRegExp {} // Hack
export = escapeRegExp;
}

14
src/@types/koa-slow.d.ts vendored Normal file
View File

@ -0,0 +1,14 @@
declare module 'koa-slow' {
import { Middleware } from 'koa';
interface ISlowOptions {
url?: RegExp;
delay?: number;
}
function slow(options?: ISlowOptions): Middleware;
namespace slow { } // Hack
export = slow;
}

10
src/@types/langmap.d.ts vendored Normal file
View File

@ -0,0 +1,10 @@
declare module 'langmap' {
type Lang = {
nativeName: string;
englishName: string;
};
const langmap: { [lang: string]: Lang };
export = langmap;
}

View File

@ -1,3 +1,5 @@
type Obj = { [key: string]: any };
declare module 'nested-property' { declare module 'nested-property' {
interface IHasNestedPropertyOptions { interface IHasNestedPropertyOptions {
own?: boolean; own?: boolean;
@ -9,11 +11,11 @@ declare module 'nested-property' {
export function set<T>(object: T, property: string, value: any): T; export function set<T>(object: T, property: string, value: any): T;
export function get(object: object, property: string): any; export function get(object: Obj, property: string): any;
export function has(object: object, property: string, options?: IHasNestedPropertyOptions): boolean; export function has(object: Obj, property: string, options?: IHasNestedPropertyOptions): boolean;
export function hasOwn(object: object, property: string, options?: IHasNestedPropertyOptions): boolean; export function hasOwn(object: Obj, property: string, options?: IHasNestedPropertyOptions): boolean;
export function isIn(object: object, property: string, objectInPath: object, options?: IIsInNestedPropertyOptions): boolean; export function isIn(object: Obj, property: string, objectInPath: Obj, options?: IIsInNestedPropertyOptions): boolean;
} }

30
src/@types/os-utils.d.ts vendored Normal file
View File

@ -0,0 +1,30 @@
declare module 'os-utils' {
type FreeCommandCallback = (usedmem: number) => void;
type HarddriveCallback = (total: number, free: number, used: number) => void;
type GetProcessesCallback = (result: string) => void;
type CPUCallback = (perc: number) => void;
export function platform(): NodeJS.Platform;
export function cpuCount(): number;
export function sysUptime(): number;
export function processUptime(): number;
export function freemem(): number;
export function totalmem(): number;
export function freememPercentage(): number;
export function freeCommand(callback: FreeCommandCallback): void;
export function harddrive(callback: HarddriveCallback): void;
export function getProcesses(callback: GetProcessesCallback): void;
export function getProcesses(nProcess: number, callback: GetProcessesCallback): void;
export function allLoadavg(): string;
export function loadavg(_time?: number): number;
export function cpuFree(callback: CPUCallback): void;
export function cpuUsage(callback: CPUCallback): void;
}

View File

@ -1,3 +1,10 @@
declare module '*/package.json' { declare module '*/package.json' {
const version: string; interface IRepository {
type: string;
url: string;
}
export const name: string;
export const version: string;
export const repository: IRepository;
} }

16
src/@types/recaptcha-promise.d.ts vendored Normal file
View File

@ -0,0 +1,16 @@
declare module 'recaptcha-promise' {
interface IVerifyOptions {
secret_key?: string;
}
interface IVerify {
(response: string, remoteAddress?: string): Promise<boolean>;
init(options: IVerifyOptions): IVerify;
}
namespace recaptchaPromise {} // Hack
const verify: IVerify;
export = verify;
}

15
src/argv.ts Normal file
View File

@ -0,0 +1,15 @@
import * as program from 'commander';
import * as pkg from '../package.json';
program
.version(pkg.version)
.option('--no-daemons', 'Disable daemon processes (for debbuging)')
.option('--disable-clustering', 'Disable clustering')
.option('--disable-queue', 'Disable job queue')
.option('--quiet', 'Suppress all logs')
.option('--verbose', 'Enable all logs')
.option('--slow', 'Delay all requests (for debbuging)')
.option('--color', 'This option is a dummy for some external program\'s (e.g. forever) issue.')
.parse(process.argv);
export { program };

View File

@ -21,7 +21,7 @@ export type Partial<T> = {
}; };
type ArrayValue<T> = { type ArrayValue<T> = {
[P in keyof T]: T[P] extends number ? Array<T[P]> : ArrayValue<T[P]>; [P in keyof T]: T[P] extends number ? T[P][] : ArrayValue<T[P]>;
}; };
type Span = 'day' | 'hour'; type Span = 'day' | 'hour';
@ -58,7 +58,7 @@ type Log<T extends Obj> = {
/** /**
* 様々なチャートの管理を司るクラス * 様々なチャートの管理を司るクラス
*/ */
export default abstract class Chart<T> { export default abstract class Chart<T extends Obj> {
protected collection: ICollection<Log<T>>; protected collection: ICollection<Log<T>>;
protected abstract async getTemplate(init: boolean, latest?: T, group?: any): Promise<T>; protected abstract async getTemplate(init: boolean, latest?: T, group?: any): Promise<T>;
private name: string; private name: string;

View File

@ -42,7 +42,7 @@
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../i18n'; import i18n from '../../i18n';
import * as tinycolor from 'tinycolor2'; import * as tinycolor from 'tinycolor2';
import * as ApexCharts from 'apexcharts'; import ApexCharts from 'apexcharts';
const limit = 90; const limit = 90;

View File

@ -19,7 +19,7 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import * as ApexCharts from 'apexcharts'; import ApexCharts from 'apexcharts';
export default Vue.extend({ export default Vue.extend({
props: ['connection'], props: ['connection'],

View File

@ -62,11 +62,12 @@
} }
if (settings && settings.device.lang && if (settings && settings.device.lang &&
langs.includes(settings.device.lang)) { langs.includes(settings.device.lang))
{
lang = settings.device.lang; lang = settings.device.lang;
} }
window.lang = lang; localStorage.setItem('lang', lang);
//#endregion //#endregion
//#region Fetch locale data //#region Fetch locale data

View File

@ -0,0 +1,9 @@
import { url as instanceUrl } from '../../config';
export function getStaticImageUrl(url: string): string {
const u = new URL(url);
const dummy = `${u.host}${u.pathname}`; // 拡張子がないとキャッシュしてくれないCDNがあるので
let result = `${instanceUrl}/proxy/${dummy}?url=${encodeURIComponent(u.href)}`;
result += '&static=1';
return result;
}

View File

@ -15,6 +15,7 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import { getStaticImageUrl } from '../../../common/scripts/get-static-image-url';
export default Vue.extend({ export default Vue.extend({
props: { props: {
@ -47,6 +48,11 @@ export default Vue.extend({
borderRadius: this.$store.state.settings.circleIcons ? '100%' : null borderRadius: this.$store.state.settings.circleIcons ? '100%' : null
}; };
}, },
url(): string {
return this.$store.state.device.disableShowingAnimatedImages
? getStaticImageUrl(this.user.avatarUrl)
: this.user.avatarUrl;
},
icon(): any { icon(): any {
return { return {
backgroundColor: this.lightmode backgroundColor: this.lightmode
@ -54,7 +60,7 @@ export default Vue.extend({
: this.user.avatarColor && this.user.avatarColor.length == 3 : this.user.avatarColor && this.user.avatarColor.length == 3
? `rgb(${this.user.avatarColor.join(',')})` ? `rgb(${this.user.avatarColor.join(',')})`
: null, : null,
backgroundImage: this.lightmode ? null : `url(${this.user.avatarUrl})`, backgroundImage: this.lightmode ? null : `url(${this.url})`,
borderRadius: this.$store.state.settings.circleIcons ? '100%' : null borderRadius: this.$store.state.settings.circleIcons ? '100%' : null
}; };
} }

View File

@ -18,7 +18,7 @@
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n'; import i18n from '../../../i18n';
import * as tinycolor from 'tinycolor2'; import * as tinycolor from 'tinycolor2';
import * as ApexCharts from 'apexcharts'; import ApexCharts from 'apexcharts';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/drive-settings.vue'), i18n: i18n('common/views/components/drive-settings.vue'),

View File

@ -9,6 +9,7 @@
import Vue from 'vue'; import Vue from 'vue';
// スクリプトサイズがデカい // スクリプトサイズがデカい
//import { lib } from 'emojilib'; //import { lib } from 'emojilib';
import { getStaticImageUrl } from '../../../common/scripts/get-static-image-url';
export default Vue.extend({ export default Vue.extend({
props: { props: {
@ -54,7 +55,9 @@ export default Vue.extend({
const customEmoji = this.customEmojis.find(x => x.name == this.name); const customEmoji = this.customEmojis.find(x => x.name == this.name);
if (customEmoji) { if (customEmoji) {
this.customEmoji = customEmoji; this.customEmoji = customEmoji;
this.url = customEmoji.url; this.url = this.$store.state.device.disableShowingAnimatedImages
? getStaticImageUrl(customEmoji.url)
: customEmoji.url;
} else { } else {
//const emoji = lib[this.name]; //const emoji = lib[this.name];
//if (emoji) { //if (emoji) {

View File

@ -11,6 +11,7 @@
:title="media.name" :title="media.name"
controls controls
ref="audio" ref="audio"
@volumechange="volumechange"
preload="metadata" /> preload="metadata" />
</div> </div>
<a class="download" v-else <a class="download" v-else
@ -40,7 +41,17 @@ export default Vue.extend({
return { return {
hide: true hide: true
}; };
} },
mounted() {
const audioTag = this.$refs.audio as HTMLAudioElement;
audioTag.volume = this.$store.state.device.mediaVolume;
},
methods: {
volumechange() {
const audioTag = this.$refs.audio as HTMLAudioElement;
this.$store.commit('device/set', { key: 'mediaVolume', value: audioTag.volume });
},
},
}) })
</script> </script>

View File

@ -17,6 +17,7 @@
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n'; import i18n from '../../../i18n';
import ImageViewer from './image-viewer.vue'; import ImageViewer from './image-viewer.vue';
import { getStaticImageUrl } from '../../../common/scripts/get-static-image-url';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/media-image.vue'), i18n: i18n('common/views/components/media-image.vue'),
@ -36,7 +37,11 @@ export default Vue.extend({
} }
computed: { computed: {
style(): any { style(): any {
let url = `url(${this.image.thumbnailUrl})`; let url = `url(${
this.$store.state.device.disableShowingAnimatedImages
? getStaticImageUrl(this.image.thumbnailUrl)
: this.image.thumbnailUrl
})`;
if (this.$store.state.device.loadRemoteMedia || this.$store.state.device.lightmode) { if (this.$store.state.device.loadRemoteMedia || this.$store.state.device.lightmode) {
url = null; url = null;

View File

@ -10,73 +10,91 @@ import i18n from '../../../i18n';
import { url } from '../../../config'; import { url } from '../../../config';
import copyToClipboard from '../../../common/scripts/copy-to-clipboard'; import copyToClipboard from '../../../common/scripts/copy-to-clipboard';
import { concat, intersperse } from '../../../../../prelude/array'; import { concat, intersperse } from '../../../../../prelude/array';
import { faCopy } from '@fortawesome/free-regular-svg-icons'; import { faCopy, faEye, faEyeSlash } from '@fortawesome/free-regular-svg-icons';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/note-menu.vue'), i18n: i18n('common/views/components/note-menu.vue'),
props: ['note', 'source'], props: ['note', 'source'],
data() {
return {
isFavorited: false,
isWatching: false
};
},
computed: { computed: {
items(): any[] { items(): any[] {
return concat(intersperse([null], [ return [{
[
[{
icon: 'at', icon: 'at',
text: this.$t('mention'), text: this.$t('mention'),
action: this.mention action: this.mention
}] }, null, {
],
[
[{
icon: 'info-circle', icon: 'info-circle',
text: this.$t('detail'), text: this.$t('detail'),
action: this.detail action: this.detail
}], [{ }, {
icon: faCopy, icon: faCopy,
text: this.$t('copy-content'), text: this.$t('copy-content'),
action: this.copyContent action: this.copyContent
}], [{ }, {
icon: 'link', icon: 'link',
text: this.$t('copy-link'), text: this.$t('copy-link'),
action: this.copyLink action: this.copyLink
}], this.note.uri ? [{ }, this.note.uri ? {
icon: 'external-link-square-alt', icon: 'external-link-square-alt',
text: this.$t('remote'), text: this.$t('remote'),
action: () => { action: () => {
window.open(this.note.uri, '_blank'); window.open(this.note.uri, '_blank');
} }
}] : [] } : undefined,
], null,
[ this.isFavorited ? {
this.note.isFavorited ? [{
icon: 'star', icon: 'star',
text: this.$t('unfavorite'), text: this.$t('unfavorite'),
action: this.unfavorite action: () => this.toggleFavorite(false)
}] : [{ } : {
icon: 'star', icon: 'star',
text: this.$t('favorite'), text: this.$t('favorite'),
action: this.favorite action: () => this.toggleFavorite(true)
}], this.note.userId == this.$store.state.i.id ? [ },
(this.$store.state.i.pinnedNoteIds || []).includes(this.note.id) ? { this.note.userId != this.$store.state.i.id ? this.isWatching ? {
icon: faEyeSlash,
text: this.$t('unwatch'),
action: () => this.toggleWatch(false)
} : {
icon: faEye,
text: this.$t('watch'),
action: () => this.toggleWatch(true)
} : undefined,
this.note.userId == this.$store.state.i.id ? (this.$store.state.i.pinnedNoteIds || []).includes(this.note.id) ? {
icon: 'thumbtack', icon: 'thumbtack',
text: this.$t('unpin'), text: this.$t('unpin'),
action: this.unpin action: () => this.togglePin(false)
} : { } : {
icon: 'thumbtack', icon: 'thumbtack',
text: this.$t('pin'), text: this.$t('pin'),
action: this.pin action: () => this.togglePin(true)
} } : undefined,
] : [] ...(this.note.userId == this.$store.state.i.id || this.$store.state.i.isAdmin || this.$store.state.i.isModerator ? [
], [ null, {
this.note.userId == this.$store.state.i.id || this.$store.state.i.isAdmin || this.$store.state.i.isModerator ? [{
icon: ['far', 'trash-alt'], icon: ['far', 'trash-alt'],
text: this.$t('delete'), text: this.$t('delete'),
action: this.del action: this.del
}] : [] }]
] : []
].map(concat).filter(x => x.length > 0))); )]
.filter(x => x !== undefined)
} }
}, },
created() {
this.$root.api('notes/state', {
noteId: this.note.id
}).then(state => {
this.isFavorited = state.isFavorited;
this.isWatching = state.isWatching;
});
},
methods: { methods: {
mention() { mention() {
this.$post({ mention: this.note.user }); this.$post({ mention: this.note.user });
@ -102,8 +120,8 @@ export default Vue.extend({
}); });
}, },
pin() { togglePin(pin: boolean) {
this.$root.api('i/pin', { this.$root.api(pin ? 'i/pin' : 'i/unpin', {
noteId: this.note.id noteId: this.note.id
}).then(() => { }).then(() => {
this.$root.dialog({ this.$root.dialog({
@ -114,14 +132,6 @@ export default Vue.extend({
}); });
}, },
unpin() {
this.$root.api('i/unpin', {
noteId: this.note.id
}).then(() => {
this.destroyDom();
});
},
del() { del() {
this.$root.dialog({ this.$root.dialog({
type: 'warning', type: 'warning',
@ -138,8 +148,8 @@ export default Vue.extend({
}); });
}, },
favorite() { toggleFavorite(favorite: boolean) {
this.$root.api('notes/favorites/create', { this.$root.api(favorite ? 'notes/favorites/create' : 'notes/favorites/delete', {
noteId: this.note.id noteId: this.note.id
}).then(() => { }).then(() => {
this.$root.dialog({ this.$root.dialog({
@ -150,8 +160,8 @@ export default Vue.extend({
}); });
}, },
unfavorite() { toggleWatch(watch: boolean) {
this.$root.api('notes/favorites/delete', { this.$root.api(watch ? 'notes/watching/create' : 'notes/watching/delete', {
noteId: this.note.id noteId: this.note.id
}).then(() => { }).then(() => {
this.$root.dialog({ this.$root.dialog({

View File

@ -87,6 +87,14 @@
<ui-button @click="updateEmail()">{{ $t('save') }}</ui-button> <ui-button @click="updateEmail()">{{ $t('save') }}</ui-button>
</div> </div>
</section> </section>
<section>
<header>{{ $t('export') }}</header>
<div>
<ui-button @click="exportNotes()">{{ $t('export-notes') }}</ui-button>
</div>
</section>
</ui-card> </ui-card>
</template> </template>
@ -252,6 +260,15 @@ export default Vue.extend({
email: this.email == '' ? null : this.email email: this.email == '' ? null : this.email
}); });
}); });
},
exportNotes() {
this.$root.api('i/export-notes', {});
this.$root.dialog({
type: 'info',
text: this.$t('export-requested')
});
} }
} }
}); });

View File

@ -5,9 +5,9 @@
<p :class="$style.fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p> <p :class="$style.fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<div :class="$style.stream" v-if="!fetching && images.length > 0"> <div :class="$style.stream" v-if="!fetching && images.length > 0">
<div v-for="image in images" <div v-for="(image, i) in images" :key="i"
:class="$style.img" :class="$style.img"
:style="`background-image: url(${image.thumbnailUrl || image.url})`" :style="`background-image: url(${thumbnail(image)})`"
draggable="true" draggable="true"
@dragstart="onDragstart(image, $event)" @dragstart="onDragstart(image, $event)"
></div> ></div>
@ -20,6 +20,7 @@
<script lang="ts"> <script lang="ts">
import define from '../../../common/define-widget'; import define from '../../../common/define-widget';
import i18n from '../../../i18n'; import i18n from '../../../i18n';
import { getStaticImageUrl } from '../../scripts/get-static-image-url';
export default define({ export default define({
name: 'photo-stream', name: 'photo-stream',
@ -77,6 +78,12 @@ export default define({
e.dataTransfer.effectAllowed = 'move'; e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('mk_drive_file', JSON.stringify(file)); e.dataTransfer.setData('mk_drive_file', JSON.stringify(file));
}, },
thumbnail(image: any): string {
return this.$store.state.device.disableShowingAnimatedImages
? getStaticImageUrl(image.thumbnailUrl)
: image.thumbnailUrl;
},
} }
}); });
</script> </script>

View File

@ -1,5 +1,4 @@
declare const _LANGS_: string[]; declare const _LANGS_: string[];
declare const _THEME_COLOR_: string;
declare const _COPYRIGHT_: string; declare const _COPYRIGHT_: string;
declare const _VERSION_: string; declare const _VERSION_: string;
declare const _CLIENT_VERSION_: string; declare const _CLIENT_VERSION_: string;
@ -13,7 +12,7 @@ export const hostname = address.hostname;
export const url = address.origin; export const url = address.origin;
export const apiUrl = url + '/api'; export const apiUrl = url + '/api';
export const wsUrl = url.replace('http://', 'ws://').replace('https://', 'wss://') + '/streaming'; export const wsUrl = url.replace('http://', 'ws://').replace('https://', 'wss://') + '/streaming';
export const lang = window.lang; export const lang = localStorage.getItem('lang');
export const langs = _LANGS_; export const langs = _LANGS_;
export const locale = JSON.parse(localStorage.getItem('locale')); export const locale = JSON.parse(localStorage.getItem('locale'));
export const copyright = _COPYRIGHT_; export const copyright = _COPYRIGHT_;

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="mk-media-video-dialog"> <div class="mk-media-video-dialog">
<div class="bg" @click="close"></div> <div class="bg" @click="close"></div>
<video :src="video.url" :title="video.name" controls autoplay ref="video"/> <video :src="video.url" :title="video.name" controls autoplay ref="video" @volumechange="volumechange"/>
</div> </div>
</template> </template>
@ -18,8 +18,9 @@ export default Vue.extend({
duration: 100, duration: 100,
easing: 'linear' easing: 'linear'
}); });
const videoTag = this.$refs.video as HTMLVideoElement const videoTag = this.$refs.video as HTMLVideoElement;
if (this.start) videoTag.currentTime = this.start if (this.start) videoTag.currentTime = this.start
videoTag.volume = this.$store.state.device.mediaVolume;
}, },
methods: { methods: {
close() { close() {
@ -30,7 +31,11 @@ export default Vue.extend({
easing: 'linear', easing: 'linear',
complete: () => this.destroyDom() complete: () => this.destroyDom()
}); });
} },
volumechange() {
const videoTag = this.$refs.video as HTMLVideoElement;
this.$store.commit('device/set', { key: 'mediaVolume', value: videoTag.volume });
},
} }
}); });
</script> </script>

View File

@ -49,6 +49,7 @@ export default Vue.extend({
<style lang="stylus" scoped> <style lang="stylus" scoped>
.qiziqtywpuaucsgarwajitwaakggnisj .qiziqtywpuaucsgarwajitwaakggnisj
display flex display flex
overflow hidden
font-size 0.9em font-size 0.9em
> .avatar > .avatar

View File

@ -144,11 +144,12 @@ export default Vue.extend({
.note .note
margin 0 margin 0
padding 0 padding 0
overflow hidden
background var(--face) background var(--face)
border-bottom solid var(--lineWidth) var(--faceDivider) border-bottom solid var(--lineWidth) var(--faceDivider)
&.mini &.mini
font-size 13px font-size 14px
> .renote > .renote
padding 8px 16px 0 16px padding 8px 16px 0 16px

View File

@ -117,6 +117,7 @@
<ui-switch v-model="showReplyTarget">{{ $t('show-reply-target') }}</ui-switch> <ui-switch v-model="showReplyTarget">{{ $t('show-reply-target') }}</ui-switch>
<ui-switch v-model="showMaps">{{ $t('show-maps') }}</ui-switch> <ui-switch v-model="showMaps">{{ $t('show-maps') }}</ui-switch>
<ui-switch v-model="disableAnimatedMfm">{{ $t('@.disable-animated-mfm') }}</ui-switch> <ui-switch v-model="disableAnimatedMfm">{{ $t('@.disable-animated-mfm') }}</ui-switch>
<ui-switch v-model="disableShowingAnimatedImages">{{ $t('@.disable-showing-animated-images') }}</ui-switch>
<ui-switch v-model="remainDeletedNote">{{ $t('remain-deleted-note') }}</ui-switch> <ui-switch v-model="remainDeletedNote">{{ $t('remain-deleted-note') }}</ui-switch>
</section> </section>
<section> <section>
@ -516,6 +517,11 @@ export default Vue.extend({
set(value) { this.$store.dispatch('settings/set', { key: 'disableAnimatedMfm', value }); } set(value) { this.$store.dispatch('settings/set', { key: 'disableAnimatedMfm', value }); }
}, },
disableShowingAnimatedImages: {
get() { return this.$store.state.device.disableShowingAnimatedImages; },
set(value) { this.$store.commit('device/set', { key: 'disableShowingAnimatedImages', value }); }
},
remainDeletedNote: { remainDeletedNote: {
get() { return this.$store.state.settings.remainDeletedNote; }, get() { return this.$store.state.settings.remainDeletedNote; },
set(value) { this.$store.dispatch('settings/set', { key: 'remainDeletedNote', value }); } set(value) { this.$store.dispatch('settings/set', { key: 'remainDeletedNote', value }); }

View File

@ -15,7 +15,7 @@
import Vue from 'vue'; import Vue from 'vue';
import XColumn from './deck.column.vue'; import XColumn from './deck.column.vue';
import XHashtagTl from './deck.hashtag-tl.vue'; import XHashtagTl from './deck.hashtag-tl.vue';
import * as ApexCharts from 'apexcharts'; import ApexCharts from 'apexcharts';
export default Vue.extend({ export default Vue.extend({
components: { components: {

View File

@ -99,7 +99,7 @@ import XNotes from './deck.notes.vue';
import XNote from '../../components/note.vue'; import XNote from '../../components/note.vue';
import XUserMenu from '../../../../common/views/components/user-menu.vue'; import XUserMenu from '../../../../common/views/components/user-menu.vue';
import { concat } from '../../../../../../prelude/array'; import { concat } from '../../../../../../prelude/array';
import * as ApexCharts from 'apexcharts'; import ApexCharts from 'apexcharts';
const fetchLimit = 10; const fetchLimit = 10;

View File

@ -3,8 +3,8 @@
<p class="title"><fa icon="camera"/>{{ $t('title') }}</p> <p class="title"><fa icon="camera"/>{{ $t('title') }}</p>
<p class="initializing" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('loading') }}<mk-ellipsis/></p> <p class="initializing" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('loading') }}<mk-ellipsis/></p>
<div class="stream" v-if="!fetching && images.length > 0"> <div class="stream" v-if="!fetching && images.length > 0">
<div v-for="image in images" class="img" <div v-for="(image, i) in images" :key="i" class="img"
:style="`background-image: url(${image.thumbnailUrl})`" :style="`background-image: url(${thumbnail(image)})`"
></div> ></div>
</div> </div>
<p class="empty" v-if="!fetching && images.length == 0">{{ $t('no-photos') }}</p> <p class="empty" v-if="!fetching && images.length == 0">{{ $t('no-photos') }}</p>
@ -14,6 +14,8 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../../i18n'; import i18n from '../../../../i18n';
import { getStaticImageUrl } from '../../../../common/scripts/get-static-image-url';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('desktop/views/pages/user/user.photos.vue'), i18n: i18n('desktop/views/pages/user/user.photos.vue'),
props: ['user'], props: ['user'],
@ -44,7 +46,14 @@ export default Vue.extend({
} }
this.fetching = false; this.fetching = false;
}); });
} },
methods: {
thumbnail(image: any): string {
return this.$store.state.device.disableShowingAnimatedImages
? getStaticImageUrl(image.thumbnailUrl)
: image.thumbnailUrl;
},
},
}); });
</script> </script>

View File

@ -4,7 +4,7 @@ import { EventEmitter } from 'eventemitter3';
import * as uuid from 'uuid'; import * as uuid from 'uuid';
import initStore from './store'; import initStore from './store';
import { apiUrl, clientVersion as version, lang } from './config'; import { apiUrl, clientVersion as version } 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';
@ -172,7 +172,7 @@ export default class MiOS extends EventEmitter {
callback(); callback();
// Init service worker // Init service worker
//if (this.shouldRegisterSw) this.registerSw(); if (this.shouldRegisterSw) this.registerSw();
}; };
// キャッシュがあったとき // キャッシュがあったとき

View File

@ -6,7 +6,7 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import * as ApexCharts from 'apexcharts'; import ApexCharts from 'apexcharts';
export default Vue.extend({ export default Vue.extend({
props: ['user'], props: ['user'],

View File

@ -2,7 +2,7 @@
<div class="mk-note-card"> <div class="mk-note-card">
<a :href="note | notePage"> <a :href="note | notePage">
<header> <header>
<img :src="note.user.avatarUrl" alt="avatar"/> <img :src="avator" alt="avatar"/>
<h3><mk-user-name :user="note.user"/></h3> <h3><mk-user-name :user="note.user"/></h3>
</header> </header>
<div> <div>
@ -16,13 +16,19 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import summary from '../../../../../misc/get-note-summary'; import summary from '../../../../../misc/get-note-summary';
import { getStaticImageUrl } from '../../../common/scripts/get-static-image-url';
export default Vue.extend({ export default Vue.extend({
props: ['note'], props: ['note'],
computed: { computed: {
text(): string { text(): string {
return summary(this.note); return summary(this.note);
} },
avator(): string {
return this.$store.state.device.disableShowingAnimatedImages
? getStaticImageUrl(this.note.user.avatarUrl)
: this.note.user.avatarUrl;
},
} }
}); });
</script> </script>

View File

@ -40,6 +40,7 @@ export default Vue.extend({
display flex display flex
margin 0 margin 0
padding 0 padding 0
overflow hidden
font-size 10px font-size 10px
@media (min-width 350px) @media (min-width 350px)

View File

@ -102,6 +102,7 @@ export default Vue.extend({
<style lang="stylus" scoped> <style lang="stylus" scoped>
.note .note
overflow hidden
font-size 12px font-size 12px
border-bottom solid var(--lineWidth) var(--faceDivider) border-bottom solid var(--lineWidth) var(--faceDivider)

View File

@ -29,6 +29,7 @@
<ui-switch v-model="useOsDefaultEmojis">{{ $t('@.use-os-default-emojis') }}</ui-switch> <ui-switch v-model="useOsDefaultEmojis">{{ $t('@.use-os-default-emojis') }}</ui-switch>
<ui-switch v-model="iLikeSushi">{{ $t('@.i-like-sushi') }}</ui-switch> <ui-switch v-model="iLikeSushi">{{ $t('@.i-like-sushi') }}</ui-switch>
<ui-switch v-model="disableAnimatedMfm">{{ $t('@.disable-animated-mfm') }}</ui-switch> <ui-switch v-model="disableAnimatedMfm">{{ $t('@.disable-animated-mfm') }}</ui-switch>
<ui-switch v-model="disableShowingAnimatedImages">{{ $t('@.disable-showing-animated-images') }}</ui-switch>
<ui-switch v-model="suggestRecentHashtags">{{ $t('@.suggest-recent-hashtags') }}</ui-switch> <ui-switch v-model="suggestRecentHashtags">{{ $t('@.suggest-recent-hashtags') }}</ui-switch>
<ui-switch v-model="alwaysShowNsfw">{{ $t('@.always-show-nsfw') }} ({{ $t('@.this-setting-is-this-device-only') }})</ui-switch> <ui-switch v-model="alwaysShowNsfw">{{ $t('@.always-show-nsfw') }} ({{ $t('@.this-setting-is-this-device-only') }})</ui-switch>
</section> </section>
@ -313,6 +314,11 @@ export default Vue.extend({
set(value) { this.$store.dispatch('settings/set', { key: 'disableAnimatedMfm', value }); } set(value) { this.$store.dispatch('settings/set', { key: 'disableAnimatedMfm', value }); }
}, },
disableShowingAnimatedImages: {
get() { return this.$store.state.device.disableShowingAnimatedImages; },
set(value) { this.$store.commit('device/set', { key: 'disableShowingAnimatedImages', value }); }
},
showReplyTarget: { showReplyTarget: {
get() { return this.$store.state.settings.showReplyTarget; }, get() { return this.$store.state.settings.showReplyTarget; },
set(value) { this.$store.dispatch('settings/set', { key: 'showReplyTarget', value }); } set(value) { this.$store.dispatch('settings/set', { key: 'showReplyTarget', value }); }

View File

@ -1,6 +1,6 @@
<template> <template>
<mk-ui> <mk-ui>
<template slot="header" v-if="!fetching"><img :src="user.avatarUrl" alt=""> <template slot="header" v-if="!fetching"><img :src="avator" alt="">
<mk-user-name :user="user"/> <mk-user-name :user="user"/>
</template> </template>
<main v-if="!fetching"> <main v-if="!fetching">
@ -11,7 +11,7 @@
<div class="body"> <div class="body">
<div class="top"> <div class="top">
<a class="avatar"> <a class="avatar">
<img :src="user.avatarUrl" alt="avatar"/> <img :src="avator" alt="avatar"/>
</a> </a>
<button class="menu" ref="menu" @click="menu"><fa icon="ellipsis-h"/></button> <button class="menu" ref="menu" @click="menu"><fa icon="ellipsis-h"/></button>
<mk-follow-button v-if="$store.getters.isSignedIn && $store.state.i.id != user.id" :user="user"/> <mk-follow-button v-if="$store.getters.isSignedIn && $store.state.i.id != user.id" :user="user"/>
@ -82,6 +82,7 @@ import parseAcct from '../../../../../misc/acct/parse';
import Progress from '../../../common/scripts/loading'; import Progress from '../../../common/scripts/loading';
import XUserMenu from '../../../common/views/components/user-menu.vue'; import XUserMenu from '../../../common/views/components/user-menu.vue';
import XHome from './user/home.vue'; import XHome from './user/home.vue';
import { getStaticImageUrl } from '../../../common/scripts/get-static-image-url';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('mobile/views/pages/user.vue'), i18n: i18n('mobile/views/pages/user.vue'),
@ -99,6 +100,11 @@ export default Vue.extend({
age(): number { age(): number {
return age(this.user.profile.birthday); return age(this.user.profile.birthday);
}, },
avator(): string {
return this.$store.state.device.disableShowingAnimatedImages
? getStaticImageUrl(this.user.avatarUrl)
: this.user.avatarUrl;
},
style(): any { style(): any {
if (this.user.bannerUrl == null) return {}; if (this.user.bannerUrl == null) return {};
return { return {

View File

@ -2,9 +2,9 @@
<div class="root photos"> <div class="root photos">
<p class="initializing" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p> <p class="initializing" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<div class="stream" v-if="!fetching && images.length > 0"> <div class="stream" v-if="!fetching && images.length > 0">
<a v-for="image in images" <a v-for="(image, i) in images" :key="i"
class="img" class="img"
:style="`background-image: url(${image.media.thumbnailUrl})`" :style="`background-image: url(${thumbnail(image.media)})`"
:href="image.note | notePage" :href="image.note | notePage"
></a> ></a>
</div> </div>
@ -15,6 +15,7 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../../i18n'; import i18n from '../../../../i18n';
import { getStaticImageUrl } from '../../../../common/scripts/get-static-image-url';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('mobile/views/pages/user/home.photos.vue'), i18n: i18n('mobile/views/pages/user/home.photos.vue'),
@ -50,7 +51,14 @@ export default Vue.extend({
} }
this.fetching = false; this.fetching = false;
}); });
} },
methods: {
thumbnail(image: any): string {
return this.$store.state.device.disableShowingAnimatedImages
? getStaticImageUrl(image.thumbnailUrl)
: image.thumbnailUrl;
},
},
}); });
</script> </script>

View File

@ -3,7 +3,6 @@ import createPersistedState from 'vuex-persistedstate';
import * as nestedProperty from 'nested-property'; import * as nestedProperty from 'nested-property';
import MiOS from './mios'; import MiOS from './mios';
import { hostname } from './config';
import { erase } from '../../prelude/array'; import { erase } from '../../prelude/array';
import getNoteSummary from '../../misc/get-note-summary'; import getNoteSummary from '../../misc/get-note-summary';
@ -56,6 +55,7 @@ const defaultDeviceSettings = {
themes: [], themes: [],
enableSounds: true, enableSounds: true,
soundVolume: 0.5, soundVolume: 0.5,
mediaVolume: 0.5,
lang: null, lang: null,
preventUpdate: false, preventUpdate: false,
debug: false, debug: false,
@ -69,7 +69,8 @@ const defaultDeviceSettings = {
mobileNotificationPosition: 'bottom', mobileNotificationPosition: 'bottom',
deckTemporaryColumn: null, deckTemporaryColumn: null,
deckDefault: false, deckDefault: false,
useOsDefaultEmojis: false useOsDefaultEmojis: false,
disableShowingAnimatedImages: false
}; };
export default (os: MiOS) => new Vuex.Store({ export default (os: MiOS) => new Vuex.Store({

View File

@ -3,7 +3,7 @@ import * as sysUtils from 'systeminformation';
import * as diskusage from 'diskusage'; import * as diskusage from 'diskusage';
import * as Deque from 'double-ended-queue'; import * as Deque from 'double-ended-queue';
import Xev from 'xev'; import Xev from 'xev';
const osUtils = require('os-utils'); import * as osUtils from 'os-utils';
const ev = new Xev(); const ev = new Xev();

View File

@ -12,9 +12,6 @@ import chalk from 'chalk';
import * as portscanner from 'portscanner'; import * as portscanner from 'portscanner';
import * as isRoot from 'is-root'; import * as isRoot from 'is-root';
import Xev from 'xev'; import Xev from 'xev';
import * as program from 'commander';
import * as sysUtils from 'systeminformation';
import mongo, { nativeDbConn } from './db/mongodb';
import Logger from './misc/logger'; import Logger from './misc/logger';
import serverStats from './daemons/server-stats'; import serverStats from './daemons/server-stats';
@ -23,21 +20,15 @@ import loadConfig from './config/load';
import { Config } from './config/types'; import { Config } from './config/types';
import { lessThan } from './prelude/array'; import { lessThan } from './prelude/array';
import * as pkg from '../package.json'; import * as pkg from '../package.json';
import { program } from './argv';
import { checkMongoDB } from './misc/check-mongodb';
import { showMachineInfo } from './misc/show-machine-info';
const logger = new Logger('core', 'cyan'); const logger = new Logger('core', 'cyan');
const bootLogger = logger.createSubLogger('boot', 'magenta'); const bootLogger = logger.createSubLogger('boot', 'magenta');
const clusterLog = logger.createSubLogger('cluster', 'orange'); const clusterLog = logger.createSubLogger('cluster', 'orange');
const ev = new Xev(); const ev = new Xev();
//#region Command line argument definitions
program
.version(pkg.version)
.option('--no-daemons', 'Disable daemon processes (for debbuging)')
.option('--disable-clustering', 'Disable clustering')
.option('--quiet', 'Suppress all logs')
.parse(process.argv);
//#endregion
/** /**
* Init process * Init process
*/ */
@ -70,16 +61,20 @@ async function masterMain() {
if (!program.quiet) { if (!program.quiet) {
//#region Misskey logo //#region Misskey logo
const v = `v${pkg.version}`;
console.log(' _____ _ _ '); console.log(' _____ _ _ ');
console.log('| |_|___ ___| |_ ___ _ _ '); console.log(' | |_|___ ___| |_ ___ _ _ ');
console.log('| | | | |_ -|_ -| \'_| -_| | |'); console.log(' | | | | |_ -|_ -| \'_| -_| | |');
console.log('|_|_|_|_|___|___|_,_|___|_ |'); console.log(' |_|_|_|_|___|___|_,_|___|_ |');
console.log(' |___|\n'); console.log(' ' + chalk.gray(v) + (' |___|\n'.substr(v.length)));
//#endregion //#endregion
} }
console.log(chalk`${os.hostname()} {gray (PID: ${process.pid.toString()})}`);
bootLogger.info('Welcome to Misskey!'); bootLogger.info('Welcome to Misskey!');
bootLogger.info(`Misskey v${pkg.version}`, true); bootLogger.info(`Misskey v${pkg.version}`, true);
bootLogger.info('Misskey is maintained by @syuilo, @AyaMorisawa, @mei23, and @acid-chicken.');
try { try {
// initialize app // initialize app
@ -105,6 +100,9 @@ async function workerMain() {
// start server // start server
await require('./server').default(); await require('./server').default();
// start processor
require('./queue').default();
if (cluster.isWorker) { if (cluster.isWorker) {
// Send a 'ready' message to parent process // Send a 'ready' message to parent process
process.send('ready'); process.send('ready');
@ -123,18 +121,6 @@ async function isPortAvailable(port: number): Promise<boolean> {
return await portscanner.checkPortStatus(port, '127.0.0.1') === 'closed'; return await portscanner.checkPortStatus(port, '127.0.0.1') === 'closed';
} }
async function showMachine() {
const logger = bootLogger.createSubLogger('machine');
logger.info(`Hostname: ${os.hostname()}`);
logger.info(`Platform: ${process.platform}`);
logger.info(`Architecture: ${process.arch}`);
logger.info(`CPU: ${os.cpus().length} core`);
const mem = await sysUtils.mem();
const totalmem = (mem.total / 1024 / 1024 / 1024).toFixed(1);
const availmem = (mem.available / 1024 / 1024 / 1024).toFixed(1);
logger.info(`MEM: ${totalmem}GB (available: ${availmem}GB)`);
}
function showEnvironment(): void { function showEnvironment(): void {
const env = process.env.NODE_ENV; const env = process.env.NODE_ENV;
const logger = bootLogger.createSubLogger('env'); const logger = bootLogger.createSubLogger('env');
@ -159,11 +145,11 @@ async function init(): Promise<Config> {
nodejsLogger.info(`Version ${runningNodejsVersion.join('.')}`); nodejsLogger.info(`Version ${runningNodejsVersion.join('.')}`);
if (!satisfyNodejsVersion) { if (!satisfyNodejsVersion) {
nodejsLogger.error(`Node.js version is less than ${requiredNodejsVersion.join('.')}. Please upgrade it.`); nodejsLogger.error(`Node.js version is less than ${requiredNodejsVersion.join('.')}. Please upgrade it.`, true);
process.exit(1); process.exit(1);
} }
await showMachine(); await showMachineInfo(bootLogger);
const configLogger = bootLogger.createSubLogger('config'); const configLogger = bootLogger.createSubLogger('config');
let config; let config;
@ -176,7 +162,7 @@ async function init(): Promise<Config> {
process.exit(1); process.exit(1);
} }
if (exception.code === 'ENOENT') { if (exception.code === 'ENOENT') {
configLogger.error('Configuration file not found'); configLogger.error('Configuration file not found', true);
process.exit(1); process.exit(1);
} }
throw exception; throw exception;
@ -185,12 +171,12 @@ async function init(): Promise<Config> {
configLogger.succ('Loaded'); configLogger.succ('Loaded');
if (config.port == null) { if (config.port == null) {
bootLogger.error('The port is not configured. Please configure port.'); bootLogger.error('The port is not configured. Please configure port.', true);
process.exit(1); process.exit(1);
} }
if (process.platform === 'linux' && isWellKnownPort(config.port) && !isRoot()) { if (process.platform === 'linux' && isWellKnownPort(config.port) && !isRoot()) {
bootLogger.error('You need root privileges to listen on well-known port on Linux'); bootLogger.error('You need root privileges to listen on well-known port on Linux', true);
process.exit(1); process.exit(1);
} }
@ -200,33 +186,14 @@ async function init(): Promise<Config> {
} }
// Try to connect to MongoDB // Try to connect to MongoDB
await checkMongoDB(config); try {
await checkMongoDB(config, bootLogger);
return config; } catch (e) {
} bootLogger.error('Cannot connect to database', true);
const requiredMongoDBVersion = [3, 6];
function checkMongoDB(config: Config) {
const mongoDBLogger = bootLogger.createSubLogger('db');
const u = config.mongodb.user ? encodeURIComponent(config.mongodb.user) : null;
const p = config.mongodb.pass ? encodeURIComponent(config.mongodb.pass) : null;
const uri = `mongodb://${u && p ? `${u}:****@` : ''}${config.mongodb.host}:${config.mongodb.port}/${config.mongodb.db}`;
mongoDBLogger.info(`Connecting to ${uri}`);
mongo.then(() => {
mongoDBLogger.succ('Connectivity confirmed');
nativeDbConn().then(db => db.admin().serverInfo()).then(x => x.version).then((version: string) => {
mongoDBLogger.info(`Version: ${version}`);
if (lessThan(version.split('.').map(x => parseInt(x, 10)), requiredMongoDBVersion)) {
mongoDBLogger.error(`MongoDB version is less than ${requiredMongoDBVersion.join('.')}. Please upgrade it.`);
process.exit(1); process.exit(1);
} }
});
}).catch(err => { return config;
mongoDBLogger.error(err.message);
});
} }
async function spawnWorkers(limit: number = Infinity) { async function spawnWorkers(limit: number = Infinity) {
@ -250,12 +217,12 @@ function spawnWorker(): Promise<void> {
// Listen new workers // Listen new workers
cluster.on('fork', worker => { cluster.on('fork', worker => {
clusterLog.info(`Process forked: [${worker.id}]`); clusterLog.debug(`Process forked: [${worker.id}]`);
}); });
// Listen online workers // Listen online workers
cluster.on('online', worker => { cluster.on('online', worker => {
clusterLog.succ(`Process is now online: [${worker.id}]`); clusterLog.debug(`Process is now online: [${worker.id}]`);
}); });
// Listen for dying workers // Listen for dying workers

View File

@ -1,5 +1,4 @@
const jsdom = require('jsdom'); import { JSDOM } from 'jsdom';
const { JSDOM } = jsdom;
import config from '../config'; import config from '../config';
import { INote } from '../models/note'; import { INote } from '../models/note';
import { intersperse } from '../prelude/array'; import { intersperse } from '../prelude/array';
@ -158,9 +157,9 @@ export function toHtml(tokens: MfmForest, mentionedRemoteUsers: INote['mentioned
text(token) { text(token) {
const el = doc.createElement('span'); const el = doc.createElement('span');
const nodes = (token.node.props.text as string).split(/\r\n|\r|\n/).map(x => doc.createTextNode(x)); const nodes = (token.node.props.text as string).split(/\r\n|\r|\n/).map(x => doc.createTextNode(x) as Node);
for (const x of intersperse('br', nodes)) { for (const x of intersperse<Node | 'br'>('br', nodes)) {
el.appendChild(x === 'br' ? doc.createElement('br') : x); el.appendChild(x === 'br' ? doc.createElement('br') : x);
} }

View File

@ -1,4 +1,6 @@
export default (acct: string) => { import Acct from './type';
export default (acct: string): Acct => {
if (acct.startsWith('@')) acct = acct.substr(1); if (acct.startsWith('@')) acct = acct.substr(1);
const split = acct.split('@', 2); const split = acct.split('@', 2);
return { username: split[0], host: split[1] || null }; return { username: split[0], host: split[1] || null };

View File

@ -1,8 +1,5 @@
type UserLike = { import Acct from './type';
host: string;
username: string;
};
export default (user: UserLike) => { export default (user: Acct) => {
return user.host === null ? user.username : `${user.username}@${user.host}`; return user.host === null ? user.username : `${user.username}@${user.host}`;
}; };

6
src/misc/acct/type.ts Normal file
View File

@ -0,0 +1,6 @@
type Acct = {
username: string;
host: string;
};
export default Acct;

37
src/misc/check-mongodb.ts Normal file
View File

@ -0,0 +1,37 @@
import { nativeDbConn } from '../db/mongodb';
import { Config } from '../config/types';
import Logger from './logger';
import { lessThan } from '../prelude/array';
const requiredMongoDBVersion = [3, 6];
export function checkMongoDB(config: Config, logger: Logger) {
return new Promise((res, rej) => {
const mongoDBLogger = logger.createSubLogger('db');
const u = config.mongodb.user ? encodeURIComponent(config.mongodb.user) : null;
const p = config.mongodb.pass ? encodeURIComponent(config.mongodb.pass) : null;
const uri = `mongodb://${u && p ? `${u}:****@` : ''}${config.mongodb.host}:${config.mongodb.port}/${config.mongodb.db}`;
mongoDBLogger.info(`Connecting to ${uri} ...`);
nativeDbConn().then(db => {
mongoDBLogger.succ('Connectivity confirmed');
db.admin().serverInfo().then(x => {
const version = x.version as string;
mongoDBLogger.info(`Version: ${version}`);
if (lessThan(version.split('.').map(x => parseInt(x, 10)), requiredMongoDBVersion)) {
mongoDBLogger.error(`MongoDB version is less than ${requiredMongoDBVersion.join('.')}. Please upgrade it.`);
rej('outdated version');
} else {
res();
}
}).catch(err => {
mongoDBLogger.error(`Failed to fetch server info: ${err.message}`);
rej(err);
});
}).catch(err => {
mongoDBLogger.error(err.message);
rej(err);
});
});
}

View File

@ -1,8 +1,7 @@
import * as cluster from 'cluster'; import * as cluster from 'cluster';
import chalk from 'chalk'; import chalk from 'chalk';
import * as dateformat from 'dateformat'; import * as dateformat from 'dateformat';
import { program } from '../argv';
const quiet = process.argv.find(x => x == '--quiet');
export default class Logger { export default class Logger {
private domain: string; private domain: string;
@ -20,21 +19,22 @@ export default class Logger {
return logger; return logger;
} }
public log(level: string, message: string, important = false): void { private log(level: string, message: string, important = false, subDomains: string[] = []): void {
if (quiet) return; if (program.quiet) return;
const domain = this.color ? chalk.keyword(this.color)(this.domain) : chalk.white(this.domain); const domain = this.color ? chalk.keyword(this.color)(this.domain) : chalk.white(this.domain);
const domains = [domain].concat(subDomains);
if (this.parentLogger) { if (this.parentLogger) {
this.parentLogger.log(level, `[${domain}]\t${message}`, important); this.parentLogger.log(level, message, important, domains);
} else { } else {
const time = dateformat(new Date(), 'HH:MM:ss'); const time = dateformat(new Date(), 'HH:MM:ss');
const process = cluster.isMaster ? '*' : cluster.worker.id; const process = cluster.isMaster ? '*' : cluster.worker.id;
const log = `${chalk.gray(time)} ${level} ${process}\t[${domain}]\t${message}`; const log = `${chalk.gray(time)} ${level} ${process}\t[${domains.join(' ')}]\t${message}`;
console.log(important ? chalk.bold(log) : log); console.log(important ? chalk.bold(log) : log);
} }
} }
public error(message: string | Error, important = false): void { // 実行を継続できない状況で使う public error(message: string | Error, important = false): void { // 実行を継続できない状況で使う
this.log(chalk.red('ERR '), chalk.red(message.toString()), important); this.log(important ? chalk.bgRed.white('ERR ') : chalk.red('ERR '), chalk.red(message.toString()), important);
} }
public warn(message: string, important = false): void { // 実行を継続できるが改善すべき状況で使う public warn(message: string, important = false): void { // 実行を継続できるが改善すべき状況で使う
@ -42,16 +42,16 @@ export default class Logger {
} }
public succ(message: string, important = false): void { // 何かに成功した状況で使う public succ(message: string, important = false): void { // 何かに成功した状況で使う
this.log(chalk.green('DONE'), chalk.green(message), important); this.log(important ? chalk.bgGreen.white('DONE') : chalk.green('DONE'), chalk.green(message), important);
}
public debug(message: string, important = false): void { // デバッグ用に使う(開発者にとっては必要だが利用者にとっては不要な情報)
if (process.env.NODE_ENV != 'production' || program.verbose) {
this.log(chalk.gray('VERB'), chalk.gray(message), important);
}
} }
public info(message: string, important = false): void { // それ以外 public info(message: string, important = false): void { // それ以外
this.log(chalk.blue('INFO'), message, important); this.log(chalk.blue('INFO'), message, important);
} }
public debug(message: string, important = false): void { // デバッグ用に使う
if (process.env.NODE_ENV != 'production') {
this.log(chalk.gray('VERB'), chalk.gray(message), important);
}
}
} }

View File

@ -0,0 +1,15 @@
import * as os from 'os';
import * as sysUtils from 'systeminformation';
import Logger from "./logger";
export async function showMachineInfo(parentLogger: Logger) {
const logger = parentLogger.createSubLogger('machine');
logger.debug(`Hostname: ${os.hostname()}`);
logger.debug(`Platform: ${process.platform}`);
logger.debug(`Architecture: ${process.arch}`);
logger.debug(`CPU: ${os.cpus().length} core`);
const mem = await sysUtils.mem();
const totalmem = (mem.total / 1024 / 1024 / 1024).toFixed(1);
const availmem = (mem.available / 1024 / 1024 / 1024).toFixed(1);
logger.debug(`MEM: ${totalmem}GB (available: ${availmem}GB)`);
}

View File

@ -27,11 +27,11 @@ export interface IReversiGame {
isEnded: boolean; isEnded: boolean;
winnerId: mongo.ObjectID; winnerId: mongo.ObjectID;
surrendered: mongo.ObjectID; surrendered: mongo.ObjectID;
logs: Array<{ logs: {
at: Date; at: Date;
color: boolean; color: boolean;
pos: number; pos: number;
}>; }[];
settings: { settings: {
map: string[]; map: string[];
bw: string | number; bw: string | number;

View File

@ -209,7 +209,7 @@ export type IMeta = {
remoteDriveCapacityMb?: number; remoteDriveCapacityMb?: number;
/** /**
* Max allowed note text length in charactors * Max allowed note text length in characters
*/ */
maxNoteTextLength?: number; maxNoteTextLength?: number;

View File

@ -9,7 +9,6 @@ import { pack as packApp } from './app';
import PollVote from './poll-vote'; import PollVote from './poll-vote';
import Reaction from './note-reaction'; import Reaction from './note-reaction';
import { packMany as packFileMany, IDriveFile } from './drive-file'; import { packMany as packFileMany, IDriveFile } from './drive-file';
import Favorite from './favorite';
import Following from './following'; import Following from './following';
import Emoji from './emoji'; import Emoji from './emoji';
@ -52,11 +51,11 @@ export type INote = {
repliesCount: number; repliesCount: number;
reactionCounts: any; reactionCounts: any;
mentions: mongo.ObjectID[]; mentions: mongo.ObjectID[];
mentionedRemoteUsers: Array<{ mentionedRemoteUsers: {
uri: string; uri: string;
username: string; username: string;
host: string; host: string;
}>; }[];
/** /**
* public ... 公開 * public ... 公開
@ -346,19 +345,6 @@ export const pack = async (
return null; return null;
})(); })();
// isFavorited
_note.isFavorited = (async () => {
const favorite = await Favorite
.count({
userId: meId,
noteId: id
}, {
limit: 1
});
return favorite === 1;
})();
} }
} }

View File

@ -1,9 +1,43 @@
import http from './processors/http'; import * as Queue from 'bee-queue';
import config from '../config';
import { ILocalUser } from '../models/user'; import { ILocalUser } from '../models/user';
import Logger from '../misc/logger'; import { program } from '../argv';
import handler from './processors';
const enableQueue = config.redis != null && !program.disableQueue;
const queue = initializeQueue();
function initializeQueue() {
if (enableQueue) {
return new Queue('misskey', {
redis: {
port: config.redis.port,
host: config.redis.host,
password: config.redis.pass
},
removeOnSuccess: true,
removeOnFailure: true,
getEvents: false,
sendEvents: false,
storeJobs: false
});
} else {
return null;
}
}
export function createHttpJob(data: any) { export function createHttpJob(data: any) {
return http({ data }, () => {}); if (enableQueue) {
return queue.createJob(data)
.retries(4)
.backoff('exponential', 16384) // 16s
.save();
} else {
return handler({ data }, () => {});
}
} }
export function deliver(user: ILocalUser, content: any, to: any) { export function deliver(user: ILocalUser, content: any, to: any) {
@ -17,4 +51,18 @@ export function deliver(user: ILocalUser, content: any, to: any) {
}); });
} }
export const queueLogger = new Logger('queue'); export function createExportNotesJob(user: ILocalUser) {
if (!enableQueue) throw 'queue disabled';
return queue.createJob({
type: 'exportNotes',
user: user
})
.save();
}
export default function() {
if (enableQueue) {
queue.process(128, handler);
}
}

3
src/queue/logger.ts Normal file
View File

@ -0,0 +1,3 @@
import Logger from '../misc/logger';
export const queueLogger = new Logger('queue', 'orange');

View File

@ -0,0 +1,128 @@
import * as bq from 'bee-queue';
import * as tmp from 'tmp';
import * as fs from 'fs';
import * as mongo from 'mongodb';
import { queueLogger } from '../logger';
import Note, { INote } from '../../models/note';
import addFile from '../../services/drive/add-file';
import User from '../../models/user';
import dateFormat = require('dateformat');
const logger = queueLogger.createSubLogger('export-notes');
export async function exportNotes(job: bq.Job, done: any): Promise<void> {
logger.info(`Exporting notes of ${job.data.user._id} ...`);
const user = await User.findOne({
_id: new mongo.ObjectID(job.data.user._id.toString())
});
// Create temp file
const [path, cleanup] = await new Promise<[string, any]>((res, rej) => {
tmp.file((e, path, fd, cleanup) => {
if (e) return rej(e);
res([path, cleanup]);
});
});
logger.info(`Temp file is ${path}`);
const stream = fs.createWriteStream(path, { flags: 'a' });
await new Promise((res, rej) => {
stream.write('[', err => {
if (err) {
logger.error(err);
rej(err);
} else {
res();
}
});
});
let exportedNotesCount = 0;
let ended = false;
let cursor: any = null;
while (!ended) {
const notes = await Note.find({
userId: user._id,
...(cursor ? { _id: { $gt: cursor } } : {})
}, {
limit: 100,
sort: {
_id: 1
}
});
if (notes.length === 0) {
ended = true;
job.reportProgress(100);
break;
}
cursor = notes[notes.length - 1]._id;
for (const note of notes) {
const content = JSON.stringify(serialize(note));
await new Promise((res, rej) => {
stream.write(exportedNotesCount === 0 ? content : ',\n' + content, err => {
if (err) {
logger.error(err);
rej(err);
} else {
res();
}
});
});
exportedNotesCount++;
}
const total = await Note.count({
userId: user._id,
});
job.reportProgress(exportedNotesCount / total);
}
await new Promise((res, rej) => {
stream.write(']', err => {
if (err) {
logger.error(err);
rej(err);
} else {
res();
}
});
});
stream.end();
logger.succ(`Exported to: ${path}`);
const fileName = dateFormat(new Date(), 'yyyy-mm-dd-HH-MM-ss') + '.json';
const driveFile = await addFile(user, path, fileName);
logger.succ(`Exported to: ${driveFile._id}`);
cleanup();
done();
}
function serialize(note: INote): any {
return {
id: note._id,
text: note.text,
createdAt: note.createdAt,
fileIds: note.fileIds,
replyId: note.replyId,
renoteId: note.renoteId,
poll: note.poll,
cw: note.cw,
viaMobile: note.viaMobile,
visibility: note.visibility,
visibleUserIds: note.visibleUserIds,
appId: note.appId,
geo: note.geo,
localOnly: note.localOnly
};
}

View File

@ -1,7 +1,7 @@
import * as bq from 'bee-queue'; import * as bq from 'bee-queue';
import request from '../../../remote/activitypub/request'; import request from '../../../remote/activitypub/request';
import { queueLogger } from '../..'; import { queueLogger } from '../../logger';
export default async (job: bq.Job, done: any): Promise<void> => { export default async (job: bq.Job, done: any): Promise<void> => {
try { try {

View File

@ -6,7 +6,7 @@ import perform from '../../../remote/activitypub/perform';
import { resolvePerson, updatePerson } from '../../../remote/activitypub/models/person'; import { resolvePerson, updatePerson } from '../../../remote/activitypub/models/person';
import { toUnicode } from 'punycode'; import { toUnicode } from 'punycode';
import { URL } from 'url'; import { URL } from 'url';
import { publishApLogStream } from '../../../stream'; import { publishApLogStream } from '../../../services/stream';
import Logger from '../../../misc/logger'; import Logger from '../../../misc/logger';
const logger = new Logger('inbox'); const logger = new Logger('inbox');
@ -20,7 +20,7 @@ export default async (job: bq.Job, done: any): Promise<void> => {
const info = Object.assign({}, activity); const info = Object.assign({}, activity);
delete info['@context']; delete info['@context'];
delete info['signature']; delete info['signature'];
logger.info(info); logger.debug(JSON.stringify(info, null, 2));
//#endregion //#endregion
const keyIdLower = signature.keyId.toLowerCase(); const keyIdLower = signature.keyId.toLowerCase();

View File

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

View File

@ -7,7 +7,7 @@ import * as promiseAny from 'promise-any';
import config from '../../config'; import config from '../../config';
import { ILocalUser } from '../../models/user'; import { ILocalUser } from '../../models/user';
import { publishApLogStream } from '../../stream'; import { publishApLogStream } from '../../services/stream';
import { apLogger } from './logger'; import { apLogger } from './logger';
export const logger = apLogger.createSubLogger('deliver'); export const logger = apLogger.createSubLogger('deliver');

View File

@ -78,9 +78,13 @@ export default async (username: string, _host: string, option?: any, resync?: bo
async function resolveSelf(acctLower: string) { async function resolveSelf(acctLower: string) {
logger.info(`WebFinger for ${chalk.yellow(acctLower)}`); logger.info(`WebFinger for ${chalk.yellow(acctLower)}`);
const finger = await webFinger(acctLower); const finger = await webFinger(acctLower).catch(e => {
logger.error(`Failed to WebFinger for ${chalk.yellow(acctLower)}: ${e.message} (${e.status})`);
throw e;
});
const self = finger.links.find(link => link.rel && link.rel.toLowerCase() === 'self'); const self = finger.links.find(link => link.rel && link.rel.toLowerCase() === 'self');
if (!self) { if (!self) {
logger.error(`Failed to WebFinger for ${chalk.yellow(acctLower)}: self link not found`);
throw new Error('self link not found'); throw new Error('self link not found');
} }
return self; return self;

View File

@ -1,7 +1,8 @@
import { ObjectID } from 'mongodb'; import { ObjectID } from 'mongodb';
import * as Router from 'koa-router'; import * as Router from 'koa-router';
import config from '../../config'; import config from '../../config';
import $ from 'cafy'; import ID, { transform } from '../../misc/cafy-id'; import $ from 'cafy';
import ID, { transform } from '../../misc/cafy-id';
import User from '../../models/user'; import User from '../../models/user';
import Following from '../../models/following'; import Following from '../../models/following';
import { renderActivity } from '../../remote/activitypub/renderer'; import { renderActivity } from '../../remote/activitypub/renderer';

View File

@ -2,9 +2,9 @@ import * as mongo from 'mongodb';
import isObjectId from '../../../misc/is-objectid'; import isObjectId from '../../../misc/is-objectid';
import Message from '../../../models/messaging-message'; import Message from '../../../models/messaging-message';
import { IMessagingMessage as IMessage } from '../../../models/messaging-message'; import { IMessagingMessage as IMessage } from '../../../models/messaging-message';
import { publishMainStream } from '../../../stream'; import { publishMainStream } from '../../../services/stream';
import { publishMessagingStream } from '../../../stream'; import { publishMessagingStream } from '../../../services/stream';
import { publishMessagingIndexStream } from '../../../stream'; import { publishMessagingIndexStream } from '../../../services/stream';
import User from '../../../models/user'; import User from '../../../models/user';
/** /**

View File

@ -1,7 +1,7 @@
import * as mongo from 'mongodb'; import * as mongo from 'mongodb';
import isObjectId from '../../../misc/is-objectid'; import isObjectId from '../../../misc/is-objectid';
import { default as Notification, INotification } from '../../../models/notification'; import { default as Notification, INotification } from '../../../models/notification';
import { publishMainStream } from '../../../stream'; import { publishMainStream } from '../../../services/stream';
import Mute from '../../../models/mute'; import Mute from '../../../models/mute';
import User from '../../../models/user'; import User from '../../../models/user';

View File

@ -1,4 +1,5 @@
import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id'; import $ from 'cafy';
import ID, { transform } from '../../../../misc/cafy-id';
import Report, { packMany } from '../../../../models/abuse-user-report'; import Report, { packMany } from '../../../../models/abuse-user-report';
import define from '../../define'; import define from '../../define';

View File

@ -29,22 +29,22 @@ export default define(meta, (ps) => new Promise(async (res, rej) => {
$group: { $group: {
_id: { tag: '$tagsLower', userId: '$userId' } _id: { tag: '$tagsLower', userId: '$userId' }
} }
}]) as Array<{ }]) as {
_id: { _id: {
tag: string; tag: string;
userId: any; userId: any;
} }
}>; }[];
//#endregion //#endregion
if (data.length == 0) { if (data.length == 0) {
return res([]); return res([]);
} }
let tags: Array<{ let tags: {
name: string; name: string;
count: number; count: number;
}> = []; }[] = [];
// カウント // カウント
for (const x of data.map(x => x._id).filter(x => !hidedTags.includes(x.tag))) { for (const x of data.map(x => x._id).filter(x => !hidedTags.includes(x.tag))) {

View File

@ -1,4 +1,5 @@
import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id'; import $ from 'cafy';
import ID, { transform } from '../../../../misc/cafy-id';
import App, { pack } from '../../../../models/app'; import App, { pack } from '../../../../models/app';
import define from '../../define'; import define from '../../define';

View File

@ -1,4 +1,5 @@
import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id'; import $ from 'cafy';
import ID, { transform } from '../../../../misc/cafy-id';
import * as ms from 'ms'; import * as ms from 'ms';
import User, { pack } from '../../../../models/user'; import User, { pack } from '../../../../models/user';
import Blocking from '../../../../models/blocking'; import Blocking from '../../../../models/blocking';

View File

@ -1,4 +1,5 @@
import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id'; import $ from 'cafy';
import ID, { transform } from '../../../../misc/cafy-id';
import * as ms from 'ms'; import * as ms from 'ms';
import User, { pack } from '../../../../models/user'; import User, { pack } from '../../../../models/user';
import Blocking from '../../../../models/blocking'; import Blocking from '../../../../models/blocking';

View File

@ -1,4 +1,5 @@
import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id'; import $ from 'cafy';
import ID, { transform } from '../../../../misc/cafy-id';
import Blocking, { packMany } from '../../../../models/blocking'; import Blocking, { packMany } from '../../../../models/blocking';
import define from '../../define'; import define from '../../define';

View File

@ -1,4 +1,5 @@
import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id'; import $ from 'cafy';
import ID, { transform } from '../../../../misc/cafy-id';
import DriveFile, { packMany } from '../../../../models/drive-file'; import DriveFile, { packMany } from '../../../../models/drive-file';
import define from '../../define'; import define from '../../define';

View File

@ -1,4 +1,5 @@
import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id'; import $ from 'cafy';
import ID, { transform } from '../../../../../misc/cafy-id';
import DriveFile from '../../../../../models/drive-file'; import DriveFile from '../../../../../models/drive-file';
import define from '../../../define'; import define from '../../../define';
import { packMany } from '../../../../../models/note'; import { packMany } from '../../../../../models/note';

View File

@ -1,5 +1,6 @@
import * as ms from 'ms'; import * as ms from 'ms';
import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id'; import $ from 'cafy';
import ID, { transform } from '../../../../../misc/cafy-id';
import { validateFileName, pack } from '../../../../../models/drive-file'; import { validateFileName, pack } from '../../../../../models/drive-file';
import create from '../../../../../services/drive/add-file'; import create from '../../../../../services/drive/add-file';
import define from '../../../define'; import define from '../../../define';

View File

@ -1,7 +1,8 @@
import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id'; import $ from 'cafy';
import ID, { transform } from '../../../../../misc/cafy-id';
import DriveFile from '../../../../../models/drive-file'; import DriveFile from '../../../../../models/drive-file';
import del from '../../../../../services/drive/delete-file'; import del from '../../../../../services/drive/delete-file';
import { publishDriveStream } from '../../../../../stream'; import { publishDriveStream } from '../../../../../services/stream';
import define from '../../../define'; import define from '../../../define';
export const meta = { export const meta = {

View File

@ -1,4 +1,5 @@
import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id'; import $ from 'cafy';
import ID, { transform } from '../../../../../misc/cafy-id';
import DriveFile, { pack } from '../../../../../models/drive-file'; import DriveFile, { pack } from '../../../../../models/drive-file';
import define from '../../../define'; import define from '../../../define';

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