Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb7edfee04 | ||
|
|
caa14c70ef | ||
|
|
3a0f72867f | ||
|
|
10d72742f5 | ||
|
|
1b9f8a87d3 | ||
|
|
d4a630902d | ||
|
|
fef5ec874b | ||
|
|
f2e347fec1 | ||
|
|
cd3c2484ee | ||
|
|
6a396ef5e3 | ||
|
|
eec1af1f52 | ||
|
|
99fc77b678 | ||
|
|
8bb311df51 | ||
|
|
a77df249c2 | ||
|
|
2883bca257 | ||
|
|
dd3af6886b | ||
|
|
33bcf2d1ea | ||
|
|
795fb0eb60 | ||
|
|
9e9d378bf1 | ||
|
|
4a6b0edce6 | ||
|
|
356225af14 | ||
|
|
331305e6c7 | ||
|
|
917b9475a5 | ||
|
|
e895fc954b | ||
|
|
6d3e18a6a1 | ||
|
|
79354f4faf | ||
|
|
28f8933c3c | ||
|
|
10356b4041 | ||
|
|
6a732ab1cd | ||
|
|
47322b35ff | ||
|
|
4c6d0386b9 | ||
|
|
a448172952 | ||
|
|
244ef0cb8f | ||
|
|
4bf1c23b3c |
10
CHANGELOG.md
10
CHANGELOG.md
@@ -1,6 +1,15 @@
|
|||||||
ChangeLog
|
ChangeLog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
12.28.0 (2020/3/29)
|
||||||
|
-------------------
|
||||||
|
### ✨Improvements
|
||||||
|
* インストールされたアプリのページでアプリの権限を確認できるように
|
||||||
|
* API: api/meta.features.miauthを追加
|
||||||
|
MiAuthに対応しているかどうかを確認するために利用できます。
|
||||||
|
値はつねにtrueを取ります。
|
||||||
|
* インスタンス一覧でソートできるように
|
||||||
|
|
||||||
12.27.1 (2020/03/28)
|
12.27.1 (2020/03/28)
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
@@ -14,6 +23,7 @@ ChangeLog
|
|||||||
* サードパーティーアプリケーションの認証方法にMiAuthを追加 ([Misskey API ドキュメント](https://github.com/syuilo/misskey/blob/b8088dc01a0c53b264c0697082ff5b16b06c4cda/src/docs/api.ja-JP.md#%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%A8%E3%81%97%E3%81%A6%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%83%88%E3%83%BC%E3%82%AF%E3%83%B3%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B))
|
* サードパーティーアプリケーションの認証方法にMiAuthを追加 ([Misskey API ドキュメント](https://github.com/syuilo/misskey/blob/b8088dc01a0c53b264c0697082ff5b16b06c4cda/src/docs/api.ja-JP.md#%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%A8%E3%81%97%E3%81%A6%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%83%88%E3%83%BC%E3%82%AF%E3%83%B3%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B))
|
||||||
従来の、API `app/create` => `auth/session/generate` => `auth/session/userkey` を使用する方法は依然として使用可能です。
|
従来の、API `app/create` => `auth/session/generate` => `auth/session/userkey` を使用する方法は依然として使用可能です。
|
||||||
UIからアプリを作成する画面 (`/dev/apps`) は廃止されました、同等の操作を行いたい場合は API `app/create` で可能です。
|
UIからアプリを作成する画面 (`/dev/apps`) は廃止されました、同等の操作を行いたい場合は API `app/create` で可能です。
|
||||||
|
MiAuthに対応しているかどうかは`api/meta.features.miauth`で確認できます(12.28.0~)。
|
||||||
* テーマをインポートする前にプレビューできるように
|
* テーマをインポートする前にプレビューできるように
|
||||||
* アプリから通知を作成できるように
|
* アプリから通知を作成できるように
|
||||||
* インストールしたアプリを見たり削除したりできるように
|
* インストールしたアプリを見たり削除したりできるように
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
|
|||||||
<td><img src="https://c8.patreon.com/2/200/557245" alt="mkatze " width="100"></td>
|
<td><img src="https://c8.patreon.com/2/200/557245" alt="mkatze " width="100"></td>
|
||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/23915207/25428766ecd745478e600b3d7f871eb2/1.png?token-time=2145916800&token-hash=urCLLA4KjJZX92Y1CxcBP4d8bVTHGkiaPnQZp-Tqz68%3D" alt="kabo2468y " width="100"></td>
|
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/23915207/25428766ecd745478e600b3d7f871eb2/1.png?token-time=2145916800&token-hash=urCLLA4KjJZX92Y1CxcBP4d8bVTHGkiaPnQZp-Tqz68%3D" alt="kabo2468y " width="100"></td>
|
||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/8249688/4aacf36b6b244ab1bc6653591b6640df/2.png?token-time=2145916800&token-hash=1ZEf2w6L34253cZXS_HlVevLEENWS9QqrnxGUAYblPo%3D" alt="AureoleArk " width="100"></td>
|
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/8249688/4aacf36b6b244ab1bc6653591b6640df/2.png?token-time=2145916800&token-hash=1ZEf2w6L34253cZXS_HlVevLEENWS9QqrnxGUAYblPo%3D" alt="AureoleArk " width="100"></td>
|
||||||
|
<td><img src="https://c8.patreon.com/2/200/21285325" alt="Nie(sha) " width="100"></td>
|
||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5670915/ee175f0bfb6347ffa4ea101a8c097bff/1.jpg?token-time=2145916800&token-hash=mPLM9CA-riFHx-myr3bLZJuH2xBRHA9se5VbHhLIOuA%3D" alt="osapon " width="100"></td>
|
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5670915/ee175f0bfb6347ffa4ea101a8c097bff/1.jpg?token-time=2145916800&token-hash=mPLM9CA-riFHx-myr3bLZJuH2xBRHA9se5VbHhLIOuA%3D" alt="osapon " width="100"></td>
|
||||||
<td><img src="https://c8.patreon.com/2/200/16869916" alt="見当かなみ " width="100"></td>
|
<td><img src="https://c8.patreon.com/2/200/16869916" alt="見当かなみ " width="100"></td>
|
||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/18899730/6a22797f68254034a854d69ea2445fc8/1.png?token-time=2145916800&token-hash=b_uj57yxo5VzkSOUS7oXE_762dyOTB_oxzbO6lFNG3k%3D" alt="YuzuRyo61 " width="100"></td>
|
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/18899730/6a22797f68254034a854d69ea2445fc8/1.png?token-time=2145916800&token-hash=b_uj57yxo5VzkSOUS7oXE_762dyOTB_oxzbO6lFNG3k%3D" alt="YuzuRyo61 " width="100"></td>
|
||||||
@@ -141,6 +142,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
|
|||||||
<td><a href="https://www.patreon.com/user?u=557245">mkatze </a></td>
|
<td><a href="https://www.patreon.com/user?u=557245">mkatze </a></td>
|
||||||
<td><a href="https://www.patreon.com/user?u=23915207">kabo2468y </a></td>
|
<td><a href="https://www.patreon.com/user?u=23915207">kabo2468y </a></td>
|
||||||
<td><a href="https://www.patreon.com/AureoleArk">AureoleArk </a></td>
|
<td><a href="https://www.patreon.com/AureoleArk">AureoleArk </a></td>
|
||||||
|
<td><a href="https://www.patreon.com/user?u=21285325">Nie(sha) </a></td>
|
||||||
<td><a href="https://www.patreon.com/osapon">osapon </a></td>
|
<td><a href="https://www.patreon.com/osapon">osapon </a></td>
|
||||||
<td><a href="https://www.patreon.com/user?u=16869916">見当かなみ </a></td>
|
<td><a href="https://www.patreon.com/user?u=16869916">見当かなみ </a></td>
|
||||||
<td><a href="https://www.patreon.com/Yuzulia">YuzuRyo61 </a></td>
|
<td><a href="https://www.patreon.com/Yuzulia">YuzuRyo61 </a></td>
|
||||||
@@ -156,7 +158,6 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
|
|||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5827393/59893c191dda408f9cabd0f20a3a5627/1.jpeg?token-time=2145916800&token-hash=i9N05vOph-eP1LTLb9_npATjYOpntL0ZsHNaZFSsPmE%3D" alt="motcha " width="100"></td>
|
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5827393/59893c191dda408f9cabd0f20a3a5627/1.jpeg?token-time=2145916800&token-hash=i9N05vOph-eP1LTLb9_npATjYOpntL0ZsHNaZFSsPmE%3D" alt="motcha " width="100"></td>
|
||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/20494440/540beaf2445f408ea6597bc61e077bb3/1.png?token-time=2145916800&token-hash=UJ0JQge64Bx9XmN_qYA1inMQhrWf4U91fqz7VAKJeSg%3D" alt="axtuki1 " width="100"></td>
|
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/20494440/540beaf2445f408ea6597bc61e077bb3/1.png?token-time=2145916800&token-hash=UJ0JQge64Bx9XmN_qYA1inMQhrWf4U91fqz7VAKJeSg%3D" alt="axtuki1 " width="100"></td>
|
||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13737140/1adf7835017d479280d90fe8d30aade2/1.png?token-time=2145916800&token-hash=0pdle8h5pDZrww0BDOjdz6zO-HudeGTh36a3qi1biVU%3D" alt="Satsuki Yanagi" width="100"></td>
|
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13737140/1adf7835017d479280d90fe8d30aade2/1.png?token-time=2145916800&token-hash=0pdle8h5pDZrww0BDOjdz6zO-HudeGTh36a3qi1biVU%3D" alt="Satsuki Yanagi" width="100"></td>
|
||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/17880724/311738c8a48f4a6b9443c2445a75adde/1.jpg?token-time=2145916800&token-hash=nVAntpybQrznE0rg05keLrSE6ogPKJXB13rmrJng42c%3D" alt="takimura" width="100"></td>
|
|
||||||
</tr><tr>
|
</tr><tr>
|
||||||
<td><a href="https://www.patreon.com/user?u=28779508">S Y</a></td>
|
<td><a href="https://www.patreon.com/user?u=28779508">S Y</a></td>
|
||||||
<td><a href="https://www.patreon.com/user?u=16542964">Takumi Sugita</a></td>
|
<td><a href="https://www.patreon.com/user?u=16542964">Takumi Sugita</a></td>
|
||||||
@@ -167,9 +168,9 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
|
|||||||
<td><a href="https://www.patreon.com/user?u=5827393">motcha </a></td>
|
<td><a href="https://www.patreon.com/user?u=5827393">motcha </a></td>
|
||||||
<td><a href="https://www.patreon.com/user?u=20494440">axtuki1 </a></td>
|
<td><a href="https://www.patreon.com/user?u=20494440">axtuki1 </a></td>
|
||||||
<td><a href="https://www.patreon.com/user?u=13737140">Satsuki Yanagi</a></td>
|
<td><a href="https://www.patreon.com/user?u=13737140">Satsuki Yanagi</a></td>
|
||||||
<td><a href="https://www.patreon.com/takimura">takimura</a></td>
|
|
||||||
</tr></table>
|
</tr></table>
|
||||||
<table><tr>
|
<table><tr>
|
||||||
|
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/17880724/311738c8a48f4a6b9443c2445a75adde/1.jpg?token-time=2145916800&token-hash=nVAntpybQrznE0rg05keLrSE6ogPKJXB13rmrJng42c%3D" alt="takimura " width="100"></td>
|
||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13100201/fc5be4fa90444f09a9c8a06f72385272/1.png?token-time=2145916800&token-hash=i8PjlgfOB2LPEdbtWyx8ZPsBKhGcNZqcw_FQmH71UGU%3D" alt="aqz tamaina" width="100"></td>
|
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13100201/fc5be4fa90444f09a9c8a06f72385272/1.png?token-time=2145916800&token-hash=i8PjlgfOB2LPEdbtWyx8ZPsBKhGcNZqcw_FQmH71UGU%3D" alt="aqz tamaina" width="100"></td>
|
||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/28295158/cd2451bfb94a449dbf705ef4718cd355/2.jpeg?token-time=2145916800&token-hash=MRv3BxufHPuCyiBSxU5UYmLGvD6YZlhtSFRfMWg2k4U%3D" alt="012 " width="100"></td>
|
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/28295158/cd2451bfb94a449dbf705ef4718cd355/2.jpeg?token-time=2145916800&token-hash=MRv3BxufHPuCyiBSxU5UYmLGvD6YZlhtSFRfMWg2k4U%3D" alt="012 " width="100"></td>
|
||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/9109588/e3cffc48d20a4e43afe04123e696781d/3.png?token-time=2145916800&token-hash=T_VIUA0IFIbleZv4pIjiszZGnQonwn34sLCYFIhakBo%3D" alt="nafuchoco " width="100"></td>
|
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/9109588/e3cffc48d20a4e43afe04123e696781d/3.png?token-time=2145916800&token-hash=T_VIUA0IFIbleZv4pIjiszZGnQonwn34sLCYFIhakBo%3D" alt="nafuchoco " width="100"></td>
|
||||||
@@ -180,6 +181,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
|
|||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/18072312/98e894d960314fa7bc236a72a39488fe/1.jpg?token-time=2145916800&token-hash=7bkMqTwHPRsJPGAq42PYdDXDZBVGLqdgr1ZmBxX8GFQ%3D" alt="Hekovic " width="100"></td>
|
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/18072312/98e894d960314fa7bc236a72a39488fe/1.jpg?token-time=2145916800&token-hash=7bkMqTwHPRsJPGAq42PYdDXDZBVGLqdgr1ZmBxX8GFQ%3D" alt="Hekovic " width="100"></td>
|
||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/24641572/b4fd175424814f15b0ca9178d2d2d2e4/1.png?token-time=2145916800&token-hash=e2fyqdbuJbpCckHcwux7rbuW6OPkKdERcus0u2wIEWU%3D" alt="uroco @99" width="100"></td>
|
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/24641572/b4fd175424814f15b0ca9178d2d2d2e4/1.png?token-time=2145916800&token-hash=e2fyqdbuJbpCckHcwux7rbuW6OPkKdERcus0u2wIEWU%3D" alt="uroco @99" width="100"></td>
|
||||||
</tr><tr>
|
</tr><tr>
|
||||||
|
<td><a href="https://www.patreon.com/takimura">takimura </a></td>
|
||||||
<td><a href="https://www.patreon.com/aqz">aqz tamaina</a></td>
|
<td><a href="https://www.patreon.com/aqz">aqz tamaina</a></td>
|
||||||
<td><a href="https://www.patreon.com/user?u=28295158">012 </a></td>
|
<td><a href="https://www.patreon.com/user?u=28295158">012 </a></td>
|
||||||
<td><a href="https://www.patreon.com/nijimiss">nafuchoco </a></td>
|
<td><a href="https://www.patreon.com/nijimiss">nafuchoco </a></td>
|
||||||
@@ -202,7 +204,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, 17 Mar 2020 18:57:08 UTC
|
**Last updated:** Fri, 03 Apr 2020 11:52:08 UTC
|
||||||
<!-- PATREON_END -->
|
<!-- PATREON_END -->
|
||||||
|
|
||||||
[backer-url]: #backers
|
[backer-url]: #backers
|
||||||
|
|||||||
@@ -34,14 +34,16 @@ unpin: "Lösen"
|
|||||||
copyContent: "Inhalt kopieren"
|
copyContent: "Inhalt kopieren"
|
||||||
copyLink: "Link kopieren"
|
copyLink: "Link kopieren"
|
||||||
delete: "Löschen"
|
delete: "Löschen"
|
||||||
|
deleteAndEdit: "Löschen und Bearbeiten"
|
||||||
|
deleteAndEditConfirm: "Möchtest du diese Notiz wirklich löschen und bearbeiten? Alle Reaktionen, Renotes und Antworten dieser Notiz werden verloren gehen."
|
||||||
addToList: "Zur Liste hinzufügen"
|
addToList: "Zur Liste hinzufügen"
|
||||||
sendMessage: "Nachricht senden"
|
sendMessage: "Nachricht senden"
|
||||||
copyUsername: "Benutzernamen kopieren"
|
copyUsername: "Benutzernamen kopieren"
|
||||||
reply: "Antworten"
|
reply: "Antworten"
|
||||||
loadMore: "Zeige mehr"
|
loadMore: "Zeige mehr"
|
||||||
youGotNewFollower: "Sie haben einen neuen Follower"
|
youGotNewFollower: "Sie haben einen neuen Follower"
|
||||||
receiveFollowRequest: "Follow Request erhalten."
|
receiveFollowRequest: "Follow-Anfrage erhalten."
|
||||||
followRequestAccepted: "FollowRequestAkzeptiert"
|
followRequestAccepted: "Follow-Anfrage akzeptiert"
|
||||||
mentions: "Erwähnungen"
|
mentions: "Erwähnungen"
|
||||||
directNotes: "Direktnachrichten"
|
directNotes: "Direktnachrichten"
|
||||||
importAndExport: "Importieren und Exportieren"
|
importAndExport: "Importieren und Exportieren"
|
||||||
@@ -51,6 +53,8 @@ files: "Dateien"
|
|||||||
download: "Download"
|
download: "Download"
|
||||||
driveFileDeleteConfirm: "Möchtest du die Datei \"{name}\" löschen? Die zugehörige Notiz wird ebenso verschwinden."
|
driveFileDeleteConfirm: "Möchtest du die Datei \"{name}\" löschen? Die zugehörige Notiz wird ebenso verschwinden."
|
||||||
unfollowConfirm: "Möchtest du {name} nicht mehr folgen?"
|
unfollowConfirm: "Möchtest du {name} nicht mehr folgen?"
|
||||||
|
exportRequested: "Du hast einen Export angefragt. Dies kann etwas Zeit in Anspruch nehmen. Sobald der Export abgeschlossen ist, wird er deiner Drive hinzugefügt."
|
||||||
|
importRequested: "Du hast einen Import angefragt. Dies kann etwas Zeit in Anspruch nehmen."
|
||||||
lists: "Listen"
|
lists: "Listen"
|
||||||
noLists: "Keine Liste!"
|
noLists: "Keine Liste!"
|
||||||
note: "Notiz"
|
note: "Notiz"
|
||||||
@@ -109,24 +113,33 @@ addAcount: "Benutzerkonto hinzufügen"
|
|||||||
loginFailed: "Login fehlgeschlagen"
|
loginFailed: "Login fehlgeschlagen"
|
||||||
general: "Allgemein"
|
general: "Allgemein"
|
||||||
wallpaper: "Hintergrund"
|
wallpaper: "Hintergrund"
|
||||||
|
setWallpaper: "Hintergrund festlegen"
|
||||||
removeWallpaper: "Hintergrund entfernen"
|
removeWallpaper: "Hintergrund entfernen"
|
||||||
searchWith: "Suche: {q}"
|
searchWith: "Suche: {q}"
|
||||||
youHaveNoLists: "Du hast keine Listen"
|
youHaveNoLists: "Du hast keine Listen"
|
||||||
followConfirm: "Möchtest du {name} wirklich folgen?"
|
followConfirm: "Möchtest du {name} wirklich folgen?"
|
||||||
|
proxyAccount: "Proxy-Benutzerkonto"
|
||||||
|
host: "Host"
|
||||||
selectUser: "Benutzer wählen"
|
selectUser: "Benutzer wählen"
|
||||||
recipient: "Empfänger"
|
recipient: "Empfänger"
|
||||||
annotation: "Anmerkung"
|
annotation: "Anmerkung"
|
||||||
federation: "Föderation"
|
federation: "Föderation"
|
||||||
instances: "Instanz"
|
instances: "Instanz"
|
||||||
|
registeredAt: "Registriert am"
|
||||||
|
latestRequestSentAt: "Letzte Anfrage gesendet am"
|
||||||
|
latestRequestReceivedAt: "Letzte Anfrage erhalten am"
|
||||||
latestStatus: "Neuester Status"
|
latestStatus: "Neuester Status"
|
||||||
storageUsage: "Speicherplatzverbrauch"
|
storageUsage: "Speicherplatzverbrauch"
|
||||||
charts: "Charts"
|
charts: "Charts"
|
||||||
perHour: "Pro Stunde"
|
perHour: "Pro Stunde"
|
||||||
perDay: "Pro Tag"
|
perDay: "Pro Tag"
|
||||||
|
stopActivityDelivery: "Senden von Aktivitäten einstellen"
|
||||||
blockThisInstance: "Diese Instanz blockieren"
|
blockThisInstance: "Diese Instanz blockieren"
|
||||||
|
software: "Software"
|
||||||
version: "Version"
|
version: "Version"
|
||||||
metadata: "Metadaten"
|
metadata: "Metadaten"
|
||||||
withNFiles: "{n} Datei(en)"
|
withNFiles: "{n} Datei(en)"
|
||||||
|
monitor: "Beobachten"
|
||||||
jobQueue: "Job-Warteschlange"
|
jobQueue: "Job-Warteschlange"
|
||||||
cpuAndMemory: "CPU und Arbeitsspeicher"
|
cpuAndMemory: "CPU und Arbeitsspeicher"
|
||||||
network: "Netzwerk"
|
network: "Netzwerk"
|
||||||
@@ -157,6 +170,7 @@ federating: "Föderiert"
|
|||||||
blocked: "Blockiert"
|
blocked: "Blockiert"
|
||||||
suspended: "Gesperrt"
|
suspended: "Gesperrt"
|
||||||
all: "Alles"
|
all: "Alles"
|
||||||
|
notResponding: "Antwortet nicht"
|
||||||
changePassword: "Passwort ändern"
|
changePassword: "Passwort ändern"
|
||||||
security: "Sicherheit"
|
security: "Sicherheit"
|
||||||
retypedNotMatch: "Eingaben stimmen nicht überein."
|
retypedNotMatch: "Eingaben stimmen nicht überein."
|
||||||
@@ -178,6 +192,10 @@ messaging: "Nachrichten"
|
|||||||
upload: "Hochladen"
|
upload: "Hochladen"
|
||||||
fromDrive: "Aus Drive"
|
fromDrive: "Aus Drive"
|
||||||
fromUrl: "Von einer URL"
|
fromUrl: "Von einer URL"
|
||||||
|
uploadFromUrl: "Von einer URL hochladen"
|
||||||
|
uploadFromUrlDescription: "URL der hochzuladenden Datei"
|
||||||
|
uploadFromUrlRequested: "Upload angefordert"
|
||||||
|
uploadFromUrlMayTakeTime: "Es kann eine Weile dauern, bis der Upload abgeschlossen ist."
|
||||||
explore: "Erkunden"
|
explore: "Erkunden"
|
||||||
games: "Misskey Spiele"
|
games: "Misskey Spiele"
|
||||||
messageRead: "Gelesen"
|
messageRead: "Gelesen"
|
||||||
@@ -191,21 +209,32 @@ activity: "Aktivität"
|
|||||||
images: "Bilder"
|
images: "Bilder"
|
||||||
birthday: "Geburtstag"
|
birthday: "Geburtstag"
|
||||||
yearsOld: "{age} Jahre alt"
|
yearsOld: "{age} Jahre alt"
|
||||||
registeredDate: "Registierdatum"
|
registeredDate: "Registrationsdatum"
|
||||||
location: "Ort"
|
location: "Ort"
|
||||||
theme: "Farbthemen"
|
theme: "Farbthemen"
|
||||||
|
themeForLightMode: "Farbthema, das im Hellmodus genutzt wird"
|
||||||
|
themeForDarkMode: "Farbthema, das im Dunkelmodus genutzt wird"
|
||||||
|
light: "Hell"
|
||||||
|
dark: "Dunkel"
|
||||||
lightThemes: "Helle Farbthemen"
|
lightThemes: "Helle Farbthemen"
|
||||||
darkThemes: "Dunkle Farbthemen"
|
darkThemes: "Dunkle Farbthemen"
|
||||||
drive: "Drive"
|
drive: "Drive"
|
||||||
|
fileName: "Dateiname"
|
||||||
selectFile: "Datei auswählen"
|
selectFile: "Datei auswählen"
|
||||||
selectFiles: "Dateien auswählen"
|
selectFiles: "Dateien auswählen"
|
||||||
renameFile: "Datei umbenennen"
|
renameFile: "Datei umbenennen"
|
||||||
|
folderName: "Ordnername"
|
||||||
createFolder: "Ordner erstellen"
|
createFolder: "Ordner erstellen"
|
||||||
renameFolder: "Ordner umbenennen"
|
renameFolder: "Ordner umbenennen"
|
||||||
deleteFolder: "Ordner löschen"
|
deleteFolder: "Ordner löschen"
|
||||||
addFile: "Datei hinzufügen"
|
addFile: "Datei hinzufügen"
|
||||||
emptyDrive: "Drive ist leer"
|
emptyDrive: "Drive ist leer"
|
||||||
emptyFolder: "Der Ordner ist leer"
|
emptyFolder: "Der Ordner ist leer"
|
||||||
|
unableToDelete: "Nicht löschbar"
|
||||||
|
inputNewFileName: "Gib einen neuen Dateinamen ein"
|
||||||
|
inputNewFolderName: "Gib einen neuen Ordnernamen ein"
|
||||||
|
circularReferenceFolder: "Der Zielordner ist ein Unterorder des Ordners, den du verschieben möchtest."
|
||||||
|
hasChildFilesOrFolders: "Dieser Ordner kann nicht gelöscht werden, da er nicht leer ist."
|
||||||
copyUrl: "URL kopieren"
|
copyUrl: "URL kopieren"
|
||||||
rename: "Umbenennen"
|
rename: "Umbenennen"
|
||||||
avatar: "Profilbild"
|
avatar: "Profilbild"
|
||||||
@@ -226,6 +255,9 @@ tosUrl: "URL der Nutzungsbedingungen"
|
|||||||
thisYear: "Dieses Jahr"
|
thisYear: "Dieses Jahr"
|
||||||
thisMonth: "Dieser Monat"
|
thisMonth: "Dieser Monat"
|
||||||
today: "Heute"
|
today: "Heute"
|
||||||
|
dayX: "{day}"
|
||||||
|
monthX: "{month}"
|
||||||
|
yearX: "{year}"
|
||||||
pages: "Seiten"
|
pages: "Seiten"
|
||||||
integration: "Integration"
|
integration: "Integration"
|
||||||
connectSerice: "Verbinden"
|
connectSerice: "Verbinden"
|
||||||
@@ -242,6 +274,7 @@ pinnedUsers: "Angepinnte Benutzer"
|
|||||||
pinnedUsersDescription: "Gib einen Benutzernamen pro Zeile ein. Diese werden im \"Erkunden\" Tab angezeigt."
|
pinnedUsersDescription: "Gib einen Benutzernamen pro Zeile ein. Diese werden im \"Erkunden\" Tab angezeigt."
|
||||||
recaptcha: "reCAPTCHA"
|
recaptcha: "reCAPTCHA"
|
||||||
enableRecaptcha: "reCAPTCHA aktivieren"
|
enableRecaptcha: "reCAPTCHA aktivieren"
|
||||||
|
recaptchaSecretKey: "Secret key"
|
||||||
antennas: "Antennen"
|
antennas: "Antennen"
|
||||||
manageAntennas: "Antennen verwalten"
|
manageAntennas: "Antennen verwalten"
|
||||||
name: "Name"
|
name: "Name"
|
||||||
@@ -249,6 +282,8 @@ antennaSource: "Antennenquelle"
|
|||||||
antennaKeywords: "Schlüsselwörter, die beobachtet werden sollen"
|
antennaKeywords: "Schlüsselwörter, die beobachtet werden sollen"
|
||||||
antennaExcludeKeywords: "Schlüsselwörter, die ignoriert werden sollen"
|
antennaExcludeKeywords: "Schlüsselwörter, die ignoriert werden sollen"
|
||||||
antennaKeywordsDescription: "Mit Leerzeichen für eine \"UND\"-Verknüpfung trennen, durch Zeilenumbrüche für eine \"ODER\"-Verknüpfung trennen."
|
antennaKeywordsDescription: "Mit Leerzeichen für eine \"UND\"-Verknüpfung trennen, durch Zeilenumbrüche für eine \"ODER\"-Verknüpfung trennen."
|
||||||
|
notifyAntenna: "Über neue Notizen benachrichtigen"
|
||||||
|
withFileAntenna: "Nur Notizen mit Dateien"
|
||||||
serviceworker: "ServiceWorker"
|
serviceworker: "ServiceWorker"
|
||||||
enableServiceworker: "ServiceWorker aktivieren"
|
enableServiceworker: "ServiceWorker aktivieren"
|
||||||
antennaUsersDescription: "Benutzernamen getrennt durch Zeilenumbrüche angeben"
|
antennaUsersDescription: "Benutzernamen getrennt durch Zeilenumbrüche angeben"
|
||||||
@@ -263,8 +298,24 @@ recentlyRegisteredUsers: "Vor kurzem registrierte Benutzer"
|
|||||||
recentlyDiscoveredUsers: "Vor kurzem gefundene Benutzer"
|
recentlyDiscoveredUsers: "Vor kurzem gefundene Benutzer"
|
||||||
exploreUsersCount: "Es gibt {count} Benutzer"
|
exploreUsersCount: "Es gibt {count} Benutzer"
|
||||||
exploreFediverse: "Das Fediverse erkunden"
|
exploreFediverse: "Das Fediverse erkunden"
|
||||||
|
popularTags: "Beliebte Schlagwörter"
|
||||||
userList: "Listen"
|
userList: "Listen"
|
||||||
|
about: "Über"
|
||||||
aboutMisskey: "Über Misskey"
|
aboutMisskey: "Über Misskey"
|
||||||
|
misskeySource: "Der Quelltext ist hier verfügbar:"
|
||||||
|
misskeyTranslation: "Hilf dabei, Misskey zu übersetzen:"
|
||||||
|
misskeyDonate: "Spende an Misskey, um die Weiterentwicklung zu unterstützen:"
|
||||||
|
morePatrons: "Wir schätzen ebenso die Unterstützung vieler anderer hier nicht gelisteter Personen sehr. Danke! 🥰"
|
||||||
|
patrons: "UnterstützerInnen"
|
||||||
|
administrator: "Administrator"
|
||||||
|
twoStepAuthentication: "Zwei-Faktor-Authentifizierung"
|
||||||
|
moderator: "Moderator"
|
||||||
|
nUsersMentioned: "{n} Benutzer erwähnt"
|
||||||
|
securityKey: "Sicherheitsschlüssel"
|
||||||
|
securityKeyName: "Schlüsselname"
|
||||||
|
registerSecurityKey: "Sicherheitsschlüssel registrieren"
|
||||||
|
lastUsed: "Zuletzt benutzt"
|
||||||
|
passwordLessLogin: "Passwortloses Anmelden einrichten"
|
||||||
resetPassword: "Passwort zurücksetzen"
|
resetPassword: "Passwort zurücksetzen"
|
||||||
newPasswordIs: "Das neue Passwort ist \"{password}\""
|
newPasswordIs: "Das neue Passwort ist \"{password}\""
|
||||||
posted: "Gesendet"
|
posted: "Gesendet"
|
||||||
@@ -279,7 +330,18 @@ uploadFolder: "Standardordner für Uploads"
|
|||||||
cacheClear: "Cache leeren"
|
cacheClear: "Cache leeren"
|
||||||
markAsReadAllNotifications: "Alle Benachrichtigungen als gelesen markieren"
|
markAsReadAllNotifications: "Alle Benachrichtigungen als gelesen markieren"
|
||||||
markAsReadAllUnreadNotes: "Alle Notizen als gelesen markieren"
|
markAsReadAllUnreadNotes: "Alle Notizen als gelesen markieren"
|
||||||
|
help: "Hilfe"
|
||||||
|
inputMessageHere: "Hier Nachricht eingeben"
|
||||||
|
close: "Schließen"
|
||||||
|
group: "Gruppe"
|
||||||
|
groups: "Gruppen"
|
||||||
|
createGroup: "Gruppe erstellen"
|
||||||
|
ownedGroups: "Eigene Gruppen"
|
||||||
|
joinedGroups: "Beigetretene Gruppen"
|
||||||
invites: "Einladen"
|
invites: "Einladen"
|
||||||
|
groupName: "Gruppenname"
|
||||||
|
members: "Mitglieder"
|
||||||
|
transfer: "Übertragen"
|
||||||
title: "Betreff"
|
title: "Betreff"
|
||||||
text: "Text"
|
text: "Text"
|
||||||
enable: "Aktivieren"
|
enable: "Aktivieren"
|
||||||
@@ -288,12 +350,40 @@ retype: "Erneut eingeben"
|
|||||||
noteOf: "Notiz von {user}"
|
noteOf: "Notiz von {user}"
|
||||||
inviteToGroup: "Zu Gruppe einladen"
|
inviteToGroup: "Zu Gruppe einladen"
|
||||||
maxNoteTextLength: "Maximale Länge von Notizen"
|
maxNoteTextLength: "Maximale Länge von Notizen"
|
||||||
|
quoteAttached: "Zitiert"
|
||||||
|
quoteQuestion: "Als Zitat anfügen?"
|
||||||
|
noMessagesYet: "Noch keine Nachrichten"
|
||||||
|
newMessageExists: "Du hast eine neue Nachricht"
|
||||||
|
onlyOneFileCanBeAttached: "Es kann pro Nachricht nur eine Datei angehängt werden"
|
||||||
|
signinRequired: "Anmeldung erforderlich"
|
||||||
|
invitationCode: "Einladungscode"
|
||||||
|
checking: "Wird überprüft..."
|
||||||
|
available: "Verfügbar"
|
||||||
|
unavailable: "Unverfügbar"
|
||||||
|
usernameInvalidFormat: "Buchstaben, Zahlen und Unterstriche sind verwendbar."
|
||||||
|
tooShort: "Zu kurz"
|
||||||
|
tooLong: "Zu lang"
|
||||||
|
weakPassword: "Schwaches Passwort"
|
||||||
|
normalPassword: "Standardpasswort"
|
||||||
|
strongPassword: "Starkes Passwort"
|
||||||
|
passwordMatched: "Stimmt überein"
|
||||||
|
passwordNotMatched: "Stimmt nicht überein"
|
||||||
|
signinWith: "Mit {x} anmelden"
|
||||||
|
signinFailed: "Anmeldung fehlgeschlagen. Überprüfe Benutzername und Passswort."
|
||||||
|
tapSecurityKey: "Tippe deinen Sicherheitsschlüssel an"
|
||||||
|
or: "Oder"
|
||||||
|
uiLanguage: "Sprache der Benutzeroberfläche"
|
||||||
|
groupInvited: "Du wurdest in eine Gruppe eingeladen"
|
||||||
|
aboutX: "Über {x}"
|
||||||
useOsNativeEmojis: "Eingebaute Emojis des Betriebssystems benutzen"
|
useOsNativeEmojis: "Eingebaute Emojis des Betriebssystems benutzen"
|
||||||
|
youHaveNoGroups: "Keine Gruppen vorhanden"
|
||||||
joinOrCreateGroup: "Lass dich zu einer Gruppe einladen oder erstelle deine eigene."
|
joinOrCreateGroup: "Lass dich zu einer Gruppe einladen oder erstelle deine eigene."
|
||||||
noHistory: "Kein Verlauf"
|
noHistory: "Kein Verlauf"
|
||||||
disableAnimatedMfm: "MFM, die Animationen enthalten, deaktivieren"
|
disableAnimatedMfm: "MFM, die Animationen enthalten, deaktivieren"
|
||||||
|
doing: "In Bearbeitung"
|
||||||
category: "Kategorie"
|
category: "Kategorie"
|
||||||
tags: "Schlagwörter"
|
tags: "Schlagwörter"
|
||||||
|
docSource: "Quelle dieses Dokuments"
|
||||||
createAccount: "Benutzerkonto erstellen"
|
createAccount: "Benutzerkonto erstellen"
|
||||||
existingAcount: "Bestehendes Benutzerkonto"
|
existingAcount: "Bestehendes Benutzerkonto"
|
||||||
regenerate: "Regenerieren"
|
regenerate: "Regenerieren"
|
||||||
@@ -312,14 +402,43 @@ promotion: "Hervorgehoben"
|
|||||||
promote: "Hervorheben"
|
promote: "Hervorheben"
|
||||||
numberOfDays: "Anzahl der Tage"
|
numberOfDays: "Anzahl der Tage"
|
||||||
hideThisNote: "Diese Notiz verstecken"
|
hideThisNote: "Diese Notiz verstecken"
|
||||||
|
objectStorage: "Objektspeicher"
|
||||||
|
useObjectStorage: "Objektspeicher verwenden"
|
||||||
|
objectStorageBaseUrl: "Basis-URL"
|
||||||
|
objectStorageBucket: "Bucket"
|
||||||
|
objectStoragePrefix: "Prefix"
|
||||||
|
objectStorageEndpoint: "Endpoint"
|
||||||
|
objectStorageRegion: "Region"
|
||||||
|
objectStorageUseSSL: "SSL verwenden"
|
||||||
serverLogs: "Serverprotokolle"
|
serverLogs: "Serverprotokolle"
|
||||||
deleteAll: "Alle löschen"
|
deleteAll: "Alle löschen"
|
||||||
|
newNoteRecived: "Du hast eine neue Notiz empfangen"
|
||||||
sounds: "Töne"
|
sounds: "Töne"
|
||||||
listen: "Anhören"
|
listen: "Anhören"
|
||||||
none: "Keine"
|
none: "Keine"
|
||||||
volume: "Lautstärke"
|
volume: "Lautstärke"
|
||||||
details: "Details"
|
details: "Details"
|
||||||
chooseEmoji: "Wähle ein Emoji"
|
chooseEmoji: "Wähle ein Emoji"
|
||||||
|
unableToProcess: "Der Vorgang konnte nicht abgeschlossen werden."
|
||||||
|
recentUsed: "Vor kurzem verwendet"
|
||||||
|
install: "Installieren"
|
||||||
|
uninstall: "Uninstallieren"
|
||||||
|
installedApps: "Authorisierte Anwendungen"
|
||||||
|
nothing: "Hier gibt es nichts zu sehen"
|
||||||
|
installedDate: "Authorisiert"
|
||||||
|
lastUsedDate: "Zuletzt verwendet"
|
||||||
|
state: "Status"
|
||||||
|
sort: "Sortieren"
|
||||||
|
ascendingOrder: "Aufsteigende Reihenfolge"
|
||||||
|
descendingOrder: "Absteigende Reihenfolge"
|
||||||
|
_theme:
|
||||||
|
explore: "Themen erforschen"
|
||||||
|
install: "Thema installieren"
|
||||||
|
manage: "Themaverwaltung"
|
||||||
|
code: "Themencode"
|
||||||
|
installed: "{name} wurde installiert"
|
||||||
|
alreadyInstalled: "Dieses Thema ist bereits installiert"
|
||||||
|
invalid: "Themenformat ist ungültig"
|
||||||
_sfx:
|
_sfx:
|
||||||
note: "Notizen"
|
note: "Notizen"
|
||||||
noteMy: "Meine Notizen"
|
noteMy: "Meine Notizen"
|
||||||
@@ -342,11 +461,47 @@ _time:
|
|||||||
second: "Sekunde"
|
second: "Sekunde"
|
||||||
minute: "Minute"
|
minute: "Minute"
|
||||||
hour: "Stunde"
|
hour: "Stunde"
|
||||||
|
day: "t"
|
||||||
|
_2fa:
|
||||||
|
alreadyRegistered: "Du hast bereits ein Gerät für Zwei-Faktor-Authentifizierung registriert"
|
||||||
|
registerDevice: "Neues Gerät registrieren"
|
||||||
|
registerKey: "Neuen Sicherheitsschlüssel registrieren"
|
||||||
_permissions:
|
_permissions:
|
||||||
|
"read:account": "Deine Benutzerkontoinformationen lesen"
|
||||||
|
"write:account": "Deine Benutzerkontoinformationen bearbeiten"
|
||||||
|
"read:blocks": "Die Liste deiner blockierten Benutzer lesen"
|
||||||
|
"write:blocks": "Die Liste deiner blockierten Benutzer bearbeiten"
|
||||||
|
"read:drive": "Deine Drive-Dateien und Ordner lesen"
|
||||||
|
"write:drive": "Deine Drive-Dateien und Ordner bearbeiten oder löschen"
|
||||||
|
"read:favorites": "Deine Favoriten-Liste lesen"
|
||||||
|
"write:favorites": "Deine Favoriten-Liste bearbeiten"
|
||||||
|
"read:following": "Deine Follower-Liste lesen"
|
||||||
|
"write:following": "Anderen Benutzern folgen oder entfolgen"
|
||||||
"read:messaging": "Nachrichten lesen"
|
"read:messaging": "Nachrichten lesen"
|
||||||
"write:messaging": "Nachrichten schicken oder löschen"
|
"write:messaging": "Nachrichten schicken oder löschen"
|
||||||
|
"read:mutes": "Stummschaltungen sehen"
|
||||||
|
"write:mutes": "Stummschaltungen bearbeiten"
|
||||||
|
"write:notes": "Notizen schreiben oder löschen"
|
||||||
|
"read:notifications": "Benachrichtigungen lesen"
|
||||||
|
"write:notifications": "Mit Benachrichtigungen arbeiten"
|
||||||
"read:reactions": "Reaktionen sehen"
|
"read:reactions": "Reaktionen sehen"
|
||||||
"write:reactions": "Reaktionen hinzufügen und bearbeiten"
|
"write:reactions": "Reaktionen hinzufügen und bearbeiten"
|
||||||
|
"write:votes": "In Umfragen abstimmen"
|
||||||
|
"read:pages": "Deine Seiten lesen"
|
||||||
|
"write:pages": "Deine Seiten bearbeiten oder löschen"
|
||||||
|
"read:page-likes": "Seiten-Likes lesen"
|
||||||
|
"write:page-likes": "Seiten-Likes bearbeiten"
|
||||||
|
"read:user-groups": "Deine Benutzergruppen lesen"
|
||||||
|
"write:user-groups": "Benutzergruppen bearbeiten oder löschen"
|
||||||
|
_auth:
|
||||||
|
shareAccess: "Möchtest du \"{name}\" authorisieren, auf dieses Benuzerkonto zugreifen zu können?"
|
||||||
|
shareAccessAsk: "Bist du dir sicher, dass du diese Anwendung authorisieren möchtest, auf dein Benutzerkonto zugreifen zu können?"
|
||||||
|
permissionAsk: "Diese Anwendung erfordert folgende Berechtigungen:"
|
||||||
|
pleaseGoBack: "Bitte gehe zurück zur Anwendung"
|
||||||
|
callback: "Rückkehr zur Anwendung"
|
||||||
|
denied: "Zugriff verweigert"
|
||||||
|
_antennaSources:
|
||||||
|
all: "Alle Notizen"
|
||||||
_weekday:
|
_weekday:
|
||||||
sunday: "Sonntag"
|
sunday: "Sonntag"
|
||||||
monday: "Montag"
|
monday: "Montag"
|
||||||
@@ -449,19 +604,28 @@ _instanceCharts:
|
|||||||
_timelines:
|
_timelines:
|
||||||
home: "Startseite"
|
home: "Startseite"
|
||||||
local: "Lokal"
|
local: "Lokal"
|
||||||
|
social: "Sozial"
|
||||||
global: "Global"
|
global: "Global"
|
||||||
_pages:
|
_pages:
|
||||||
|
viewPage: "Deine Seiten lesen"
|
||||||
content: "Inhalt"
|
content: "Inhalt"
|
||||||
title: "Titel"
|
title: "Titel"
|
||||||
url: "Seiten-URL"
|
url: "Seiten-URL"
|
||||||
summary: "Zusammenfassung"
|
summary: "Zusammenfassung"
|
||||||
alignCenter: "Mittig ausrichten"
|
alignCenter: "Mittig ausrichten"
|
||||||
|
hideTitleWhenPinned: "Seitentitel ausblenden, wenn an dein Profil angepinnt "
|
||||||
font: "Schriftart"
|
font: "Schriftart"
|
||||||
fontSerif: "Serif"
|
fontSerif: "Serif"
|
||||||
fontSansSerif: "Sans Serif"
|
fontSansSerif: "Sans Serif"
|
||||||
|
eyeCatchingImageSet: "Vorschaubild festlegen"
|
||||||
|
eyeCatchingImageRemove: "Vorschaubild entfernen"
|
||||||
chooseBlock: "Block hinzufügen"
|
chooseBlock: "Block hinzufügen"
|
||||||
selectType: "Wähle einen Typ"
|
selectType: "Wähle einen Typ"
|
||||||
enterVariableName: "Gib einen Namen für deine Variable ein"
|
enterVariableName: "Gib einen Namen für deine Variable ein"
|
||||||
|
variableNameIsAlreadyUsed: "Dieser Name wird bereits von einer anderen Variable verwendet"
|
||||||
|
contentBlocks: "Inhalt"
|
||||||
|
inputBlocks: "Eingabe"
|
||||||
|
specialBlocks: "Spezial"
|
||||||
blocks:
|
blocks:
|
||||||
text: "Text"
|
text: "Text"
|
||||||
textarea: "Textfeld"
|
textarea: "Textfeld"
|
||||||
@@ -476,22 +640,27 @@ _pages:
|
|||||||
text: "Inhalt"
|
text: "Inhalt"
|
||||||
textInput: "Texteingabe"
|
textInput: "Texteingabe"
|
||||||
_textInput:
|
_textInput:
|
||||||
|
name: "Variablenname"
|
||||||
text: "Titel"
|
text: "Titel"
|
||||||
default: "Standardwert"
|
default: "Standardwert"
|
||||||
textareaInput: "Eingabe des mehrzeiligen Textfelds"
|
textareaInput: "Eingabe des mehrzeiligen Textfelds"
|
||||||
_textareaInput:
|
_textareaInput:
|
||||||
|
name: "Variablenname"
|
||||||
text: "Titel"
|
text: "Titel"
|
||||||
default: "Standardwert"
|
default: "Standardwert"
|
||||||
numberInput: "Nummereingabe"
|
numberInput: "Nummereingabe"
|
||||||
_numberInput:
|
_numberInput:
|
||||||
|
name: "Variablenname"
|
||||||
text: "Titel"
|
text: "Titel"
|
||||||
default: "Standardwert"
|
default: "Standardwert"
|
||||||
switch: "Fallunterscheidung"
|
switch: "Fallunterscheidung"
|
||||||
_switch:
|
_switch:
|
||||||
|
name: "Variablenname"
|
||||||
text: "Titel"
|
text: "Titel"
|
||||||
default: "Standardwert"
|
default: "Standardwert"
|
||||||
counter: "Zähler"
|
counter: "Zähler"
|
||||||
_counter:
|
_counter:
|
||||||
|
name: "Variablenname"
|
||||||
text: "Titel"
|
text: "Titel"
|
||||||
inc: "Erhöhen um"
|
inc: "Erhöhen um"
|
||||||
_button:
|
_button:
|
||||||
@@ -511,6 +680,7 @@ _pages:
|
|||||||
no-variable: "Keine"
|
no-variable: "Keine"
|
||||||
radioButton: "Optionsfeld"
|
radioButton: "Optionsfeld"
|
||||||
_radioButton:
|
_radioButton:
|
||||||
|
name: "Variablenname"
|
||||||
title: "Titel"
|
title: "Titel"
|
||||||
values: "Auswahlmöglichkeiten (getrennt durch Zeilenumbrüche)"
|
values: "Auswahlmöglichkeiten (getrennt durch Zeilenumbrüche)"
|
||||||
default: "Standardwert"
|
default: "Standardwert"
|
||||||
@@ -666,6 +836,7 @@ _pages:
|
|||||||
splitStrByLine: "Text nach Zeilenumbrüchen aufteilen"
|
splitStrByLine: "Text nach Zeilenumbrüchen aufteilen"
|
||||||
_splitStrByLine:
|
_splitStrByLine:
|
||||||
arg1: "Text"
|
arg1: "Text"
|
||||||
|
ref: "Variablen"
|
||||||
fn: "Funktionen"
|
fn: "Funktionen"
|
||||||
_fn:
|
_fn:
|
||||||
arg1: "Ausgabe"
|
arg1: "Ausgabe"
|
||||||
|
|||||||
@@ -676,15 +676,15 @@ _charts:
|
|||||||
_instanceCharts:
|
_instanceCharts:
|
||||||
requests: "Requests"
|
requests: "Requests"
|
||||||
users: "Difference in # of users"
|
users: "Difference in # of users"
|
||||||
usersTotal: "Total # of users"
|
usersTotal: "Cumulative total # of users"
|
||||||
notes: "Difference in # of notes"
|
notes: "Difference in # of notes"
|
||||||
notesTotal: "Total # of notes"
|
notesTotal: "Cumulative total # of notes"
|
||||||
ff: "Difference in # of followers"
|
ff: "Difference in # of followers"
|
||||||
ffTotal: "Total # of followers"
|
ffTotal: "Cumulative total # of followers"
|
||||||
cacheSize: "Difference in cache size"
|
cacheSize: "Difference in cache size"
|
||||||
cacheSizeTotal: "Total cache size"
|
cacheSizeTotal: "Cumulative total cache size"
|
||||||
files: "Difference in # of files"
|
files: "Difference in # of files"
|
||||||
filesTotal: "Total # of files"
|
filesTotal: "Cumulative total # of files"
|
||||||
_timelines:
|
_timelines:
|
||||||
home: "Home"
|
home: "Home"
|
||||||
local: "Local"
|
local: "Local"
|
||||||
|
|||||||
@@ -472,6 +472,10 @@ installedApps: "Aplicaciones Autorizadas"
|
|||||||
nothing: "No hay nada que ver aqui"
|
nothing: "No hay nada que ver aqui"
|
||||||
installedDate: "Autorizado"
|
installedDate: "Autorizado"
|
||||||
lastUsedDate: "Utilizado el"
|
lastUsedDate: "Utilizado el"
|
||||||
|
state: "Estado"
|
||||||
|
sort: "Ordenar"
|
||||||
|
ascendingOrder: "Ascendente"
|
||||||
|
descendingOrder: "Descendente"
|
||||||
_theme:
|
_theme:
|
||||||
explore: "Explorar temas"
|
explore: "Explorar temas"
|
||||||
install: "Instalar tema"
|
install: "Instalar tema"
|
||||||
@@ -672,10 +676,15 @@ _charts:
|
|||||||
_instanceCharts:
|
_instanceCharts:
|
||||||
requests: "Pedidos"
|
requests: "Pedidos"
|
||||||
users: "Variación de usuarios"
|
users: "Variación de usuarios"
|
||||||
|
usersTotal: "Total acumulado de usuarios"
|
||||||
notes: "Variación de la cantidad de notas"
|
notes: "Variación de la cantidad de notas"
|
||||||
|
notesTotal: "Total acumulado de la cantidad de notas"
|
||||||
ff: "Variación de cantidad de seguidos/seguidores"
|
ff: "Variación de cantidad de seguidos/seguidores"
|
||||||
|
ffTotal: "Total acumulado de cantidad de seguidos/seguidores"
|
||||||
cacheSize: "Variación del tamaño de la caché"
|
cacheSize: "Variación del tamaño de la caché"
|
||||||
|
cacheSizeTotal: "Total acumulado del tamaño de la caché"
|
||||||
files: "Variación de cantidad de archivos"
|
files: "Variación de cantidad de archivos"
|
||||||
|
filesTotal: "Total acumulado de cantidad de archivos"
|
||||||
_timelines:
|
_timelines:
|
||||||
home: "Inicio"
|
home: "Inicio"
|
||||||
local: "Local"
|
local: "Local"
|
||||||
|
|||||||
@@ -472,6 +472,10 @@ installedApps: "Applications Autorisées"
|
|||||||
nothing: "Il n'y a rien à voir ici"
|
nothing: "Il n'y a rien à voir ici"
|
||||||
installedDate: "Autorisé"
|
installedDate: "Autorisé"
|
||||||
lastUsedDate: "Dernière utilisation"
|
lastUsedDate: "Dernière utilisation"
|
||||||
|
state: "État"
|
||||||
|
sort: "Trier"
|
||||||
|
ascendingOrder: "Ascendant"
|
||||||
|
descendingOrder: "Descendant"
|
||||||
_theme:
|
_theme:
|
||||||
explore: "Explorer les thèmes"
|
explore: "Explorer les thèmes"
|
||||||
install: "Installer un thème"
|
install: "Installer un thème"
|
||||||
@@ -659,7 +663,7 @@ _charts:
|
|||||||
federationInstancesIncDec: "Variation du nombre d'instances"
|
federationInstancesIncDec: "Variation du nombre d'instances"
|
||||||
federationInstancesTotal: "Nombre d'instances au total"
|
federationInstancesTotal: "Nombre d'instances au total"
|
||||||
usersIncDec: "Variation du nombre d'utilisateur·rice·s"
|
usersIncDec: "Variation du nombre d'utilisateur·rice·s"
|
||||||
usersTotal: "Nombre d'utilsateur·rice·s au total"
|
usersTotal: "Nombre d'utilisateur·rice·s au total"
|
||||||
activeUsers: "Utilisateur·rice·s actif·ve·s"
|
activeUsers: "Utilisateur·rice·s actif·ve·s"
|
||||||
notesIncDec: "Variation du nombre d'notes"
|
notesIncDec: "Variation du nombre d'notes"
|
||||||
localNotesIncDec: "Variation du nombre de notes local"
|
localNotesIncDec: "Variation du nombre de notes local"
|
||||||
@@ -672,10 +676,15 @@ _charts:
|
|||||||
_instanceCharts:
|
_instanceCharts:
|
||||||
requests: "Requêtes"
|
requests: "Requêtes"
|
||||||
users: "Variation du nombre d'utilisateur·rice·s"
|
users: "Variation du nombre d'utilisateur·rice·s"
|
||||||
|
usersTotal: "Nombre d'utilisateur·rice·s au total cumulé"
|
||||||
notes: "Variation du nombre d'notes"
|
notes: "Variation du nombre d'notes"
|
||||||
|
notesTotal: "Nombre d'notes au total cumulé"
|
||||||
ff: "Variation des abonné·e·s"
|
ff: "Variation des abonné·e·s"
|
||||||
|
ffTotal: "Nombre d'abonné·e·s au total cumulé"
|
||||||
cacheSize: "Variation de la taille du cache"
|
cacheSize: "Variation de la taille du cache"
|
||||||
|
cacheSizeTotal: "La taille du cache au total cumulé"
|
||||||
files: "Variation du nombre de fichiers"
|
files: "Variation du nombre de fichiers"
|
||||||
|
filesTotal: "Nombre de fichiers au total cumulé"
|
||||||
_timelines:
|
_timelines:
|
||||||
home: "Principal"
|
home: "Principal"
|
||||||
local: "Local"
|
local: "Local"
|
||||||
|
|||||||
@@ -661,25 +661,30 @@ _exportOrImport:
|
|||||||
userLists: "리스트"
|
userLists: "리스트"
|
||||||
_charts:
|
_charts:
|
||||||
federationInstancesIncDec: "연합 인스턴스 수 증감"
|
federationInstancesIncDec: "연합 인스턴스 수 증감"
|
||||||
federationInstancesTotal: "총 연합 인스턴스 수"
|
federationInstancesTotal: "연합 인스턴스 수 합계"
|
||||||
usersIncDec: "유저 수 증감"
|
usersIncDec: "유저 수 증감"
|
||||||
usersTotal: "유저 수 합계"
|
usersTotal: "유저 수 합계"
|
||||||
activeUsers: "활성 유저 수"
|
activeUsers: "활성 유저 수"
|
||||||
notesIncDec: "노트 수 증감"
|
notesIncDec: "노트 수 증감"
|
||||||
localNotesIncDec: "로컬 노트 수 증감"
|
localNotesIncDec: "로컬 노트 수 증감"
|
||||||
remoteNotesIncDec: "리모트 노트 수 증감"
|
remoteNotesIncDec: "리모트 노트 수 증감"
|
||||||
notesTotal: "총 노트 수"
|
notesTotal: "노트 수 합계"
|
||||||
filesIncDec: "파일 수 증감"
|
filesIncDec: "파일 수 증감"
|
||||||
filesTotal: "총 파일 수"
|
filesTotal: "파일 수 합계"
|
||||||
storageUsageIncDec: "스토리지 사용량 증감"
|
storageUsageIncDec: "스토리지 사용량 증감"
|
||||||
storageUsageTotal: "총 스토리지 사용량"
|
storageUsageTotal: "스토리지 사용량 합계"
|
||||||
_instanceCharts:
|
_instanceCharts:
|
||||||
requests: "요청"
|
requests: "요청"
|
||||||
users: "유저 수 증감"
|
users: "유저 수 증감"
|
||||||
|
usersTotal: "누적 유저 수"
|
||||||
notes: "노트 수 증감"
|
notes: "노트 수 증감"
|
||||||
|
notesTotal: "누적 노트 수"
|
||||||
ff: "팔로잉/팔로워 증감"
|
ff: "팔로잉/팔로워 증감"
|
||||||
|
ffTotal: "누적 팔로잉/팔로워 수"
|
||||||
cacheSize: "캐시 용량 증감"
|
cacheSize: "캐시 용량 증감"
|
||||||
|
cacheSizeTotal: "누적 캐시 용량"
|
||||||
files: "파일 수 증감"
|
files: "파일 수 증감"
|
||||||
|
filesTotal: "누적 파일 수"
|
||||||
_timelines:
|
_timelines:
|
||||||
home: "홈"
|
home: "홈"
|
||||||
local: "로컬"
|
local: "로컬"
|
||||||
|
|||||||
@@ -230,8 +230,8 @@ location: "位置"
|
|||||||
theme: "主题"
|
theme: "主题"
|
||||||
themeForLightMode: "在轻便模式下使用的主题"
|
themeForLightMode: "在轻便模式下使用的主题"
|
||||||
themeForDarkMode: "在黑暗模式下使用的主题"
|
themeForDarkMode: "在黑暗模式下使用的主题"
|
||||||
light: "轻便"
|
light: "浅色"
|
||||||
dark: "黑暗"
|
dark: "深色"
|
||||||
lightThemes: "亮色主题"
|
lightThemes: "亮色主题"
|
||||||
darkThemes: "暗色主题"
|
darkThemes: "暗色主题"
|
||||||
syncDeviceDarkMode: "将黑暗模式与设备设置同步"
|
syncDeviceDarkMode: "将黑暗模式与设备设置同步"
|
||||||
@@ -486,7 +486,7 @@ _theme:
|
|||||||
invalid: "主题格式错误"
|
invalid: "主题格式错误"
|
||||||
_sfx:
|
_sfx:
|
||||||
note: "帖子"
|
note: "帖子"
|
||||||
noteMy: "我的笔记"
|
noteMy: "我的帖子"
|
||||||
notification: "通知"
|
notification: "通知"
|
||||||
chat: "聊天"
|
chat: "聊天"
|
||||||
chatBg: "聊天背景"
|
chatBg: "聊天背景"
|
||||||
|
|||||||
15
migration/1585772678853-ap-url.ts
Normal file
15
migration/1585772678853-ap-url.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/* tslint:disable:quotemark class-name indent */
|
||||||
|
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||||
|
|
||||||
|
export class apUrl1585772678853 implements MigrationInterface {
|
||||||
|
name = 'apUrl1585772678853'
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<any> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "note" ADD "url" character varying(512)`, undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<any> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "note" DROP COLUMN "url"`, undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
123
package.json
123
package.json
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"author": "syuilo <syuilotan@yahoo.co.jp>",
|
"author": "syuilo <syuilotan@yahoo.co.jp>",
|
||||||
"version": "12.28.0",
|
"version": "12.29.0",
|
||||||
"codename": "indigo",
|
"codename": "indigo",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -11,11 +11,13 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node ./index.js",
|
"start": "node ./index.js",
|
||||||
|
"start-product": "cross-env NODE_ENV=production node ./index.js",
|
||||||
"init": "npm run migrate",
|
"init": "npm run migrate",
|
||||||
"ormconfig": "node ./built/ormconfig.js",
|
"ormconfig": "node ./built/ormconfig.js",
|
||||||
"migrate": "ts-node ./node_modules/typeorm/cli.js migration:run",
|
"migrate": "ts-node ./node_modules/typeorm/cli.js migration:run",
|
||||||
"migrateandstart": "npm run migrate && npm run start",
|
"migrateandstart": "npm run migrate && npm run start",
|
||||||
"build": "webpack && gulp build",
|
"build": "webpack && gulp build",
|
||||||
|
"build-product": "cross-env NODE_ENV=production webpack && gulp build",
|
||||||
"webpack": "webpack",
|
"webpack": "webpack",
|
||||||
"watch": "webpack --watch",
|
"watch": "webpack --watch",
|
||||||
"gulp": "gulp build",
|
"gulp": "gulp build",
|
||||||
@@ -30,18 +32,18 @@
|
|||||||
"lodash": "^4.17.13"
|
"lodash": "^4.17.13"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@elastic/elasticsearch": "7.6.0",
|
"@elastic/elasticsearch": "7.6.1",
|
||||||
"@fortawesome/fontawesome-svg-core": "1.2.27",
|
"@fortawesome/fontawesome-svg-core": "1.2.28",
|
||||||
"@fortawesome/free-brands-svg-icons": "5.12.1",
|
"@fortawesome/free-brands-svg-icons": "5.13.0",
|
||||||
"@fortawesome/free-regular-svg-icons": "5.12.1",
|
"@fortawesome/free-regular-svg-icons": "5.13.0",
|
||||||
"@fortawesome/free-solid-svg-icons": "5.12.1",
|
"@fortawesome/free-solid-svg-icons": "5.13.0",
|
||||||
"@fortawesome/vue-fontawesome": "0.1.9",
|
"@fortawesome/vue-fontawesome": "0.1.9",
|
||||||
"@juggle/resize-observer": "3.0.2",
|
"@juggle/resize-observer": "3.1.3",
|
||||||
"@koa/cors": "3.0.0",
|
"@koa/cors": "3.0.0",
|
||||||
"@koa/multer": "2.0.2",
|
"@koa/multer": "2.0.2",
|
||||||
"@koa/router": "8.0.8",
|
"@koa/router": "8.0.8",
|
||||||
"@types/bcryptjs": "2.4.2",
|
"@types/bcryptjs": "2.4.2",
|
||||||
"@types/bull": "3.12.0",
|
"@types/bull": "3.12.1",
|
||||||
"@types/cbor": "5.0.0",
|
"@types/cbor": "5.0.0",
|
||||||
"@types/dateformat": "3.0.1",
|
"@types/dateformat": "3.0.1",
|
||||||
"@types/double-ended-queue": "2.1.1",
|
"@types/double-ended-queue": "2.1.1",
|
||||||
@@ -51,10 +53,10 @@
|
|||||||
"@types/gulp-rename": "0.0.33",
|
"@types/gulp-rename": "0.0.33",
|
||||||
"@types/gulp-replace": "0.0.31",
|
"@types/gulp-replace": "0.0.31",
|
||||||
"@types/is-url": "1.2.28",
|
"@types/is-url": "1.2.28",
|
||||||
"@types/js-yaml": "3.12.2",
|
"@types/js-yaml": "3.12.3",
|
||||||
"@types/jsdom": "12.2.4",
|
"@types/jsdom": "16.2.0",
|
||||||
"@types/katex": "0.11.0",
|
"@types/katex": "0.11.0",
|
||||||
"@types/koa": "2.11.1",
|
"@types/koa": "2.11.3",
|
||||||
"@types/koa-bodyparser": "4.3.0",
|
"@types/koa-bodyparser": "4.3.0",
|
||||||
"@types/koa-compress": "2.0.9",
|
"@types/koa-compress": "2.0.9",
|
||||||
"@types/koa-cors": "0.0.0",
|
"@types/koa-cors": "0.0.0",
|
||||||
@@ -68,8 +70,8 @@
|
|||||||
"@types/koa__router": "8.0.2",
|
"@types/koa__router": "8.0.2",
|
||||||
"@types/lolex": "5.1.0",
|
"@types/lolex": "5.1.0",
|
||||||
"@types/markdown-it": "0.0.9",
|
"@types/markdown-it": "0.0.9",
|
||||||
"@types/mocha": "7.0.1",
|
"@types/mocha": "7.0.2",
|
||||||
"@types/node": "13.7.1",
|
"@types/node": "13.11.0",
|
||||||
"@types/nodemailer": "6.4.0",
|
"@types/nodemailer": "6.4.0",
|
||||||
"@types/nprogress": "0.2.0",
|
"@types/nprogress": "0.2.0",
|
||||||
"@types/oauth": "0.9.1",
|
"@types/oauth": "0.9.1",
|
||||||
@@ -80,7 +82,7 @@
|
|||||||
"@types/qrcode": "1.3.4",
|
"@types/qrcode": "1.3.4",
|
||||||
"@types/random-seed": "0.3.3",
|
"@types/random-seed": "0.3.3",
|
||||||
"@types/ratelimiter": "2.1.28",
|
"@types/ratelimiter": "2.1.28",
|
||||||
"@types/redis": "2.8.15",
|
"@types/redis": "2.8.17",
|
||||||
"@types/rename": "1.0.1",
|
"@types/rename": "1.0.1",
|
||||||
"@types/request": "2.48.4",
|
"@types/request": "2.48.4",
|
||||||
"@types/request-promise-native": "1.0.17",
|
"@types/request-promise-native": "1.0.17",
|
||||||
@@ -93,26 +95,26 @@
|
|||||||
"@types/systeminformation": "3.54.1",
|
"@types/systeminformation": "3.54.1",
|
||||||
"@types/tinycolor2": "1.4.2",
|
"@types/tinycolor2": "1.4.2",
|
||||||
"@types/tmp": "0.1.0",
|
"@types/tmp": "0.1.0",
|
||||||
"@types/uuid": "3.4.7",
|
"@types/uuid": "7.0.2",
|
||||||
"@types/web-push": "3.3.0",
|
"@types/web-push": "3.3.0",
|
||||||
"@types/webpack": "4.41.6",
|
"@types/webpack": "4.41.10",
|
||||||
"@types/webpack-stream": "3.2.10",
|
"@types/webpack-stream": "3.2.10",
|
||||||
"@types/websocket": "1.0.0",
|
"@types/websocket": "1.0.0",
|
||||||
"@types/ws": "7.2.1",
|
"@types/ws": "7.2.3",
|
||||||
"@typescript-eslint/parser": "2.19.2",
|
"@typescript-eslint/parser": "2.26.0",
|
||||||
"agentkeepalive": "4.1.0",
|
"agentkeepalive": "4.1.0",
|
||||||
"animejs": "3.1.0",
|
"animejs": "3.1.0",
|
||||||
"apexcharts": "3.15.6",
|
"apexcharts": "3.17.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",
|
||||||
"aws-sdk": "2.617.0",
|
"aws-sdk": "2.653.0",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"bull": "3.12.1",
|
"bull": "3.13.0",
|
||||||
"cafy": "15.2.1",
|
"cafy": "15.2.1",
|
||||||
"cbor": "5.0.1",
|
"cbor": "5.0.1",
|
||||||
"chai": "4.2.0",
|
"chai": "4.2.0",
|
||||||
"chalk": "3.0.0",
|
"chalk": "4.0.0",
|
||||||
"chart.js": "2.9.3",
|
"chart.js": "2.9.3",
|
||||||
"cli-highlight": "2.1.4",
|
"cli-highlight": "2.1.4",
|
||||||
"commander": "4.1.1",
|
"commander": "4.1.1",
|
||||||
@@ -124,16 +126,16 @@
|
|||||||
"diskusage": "1.1.3",
|
"diskusage": "1.1.3",
|
||||||
"double-ended-queue": "2.1.0-0",
|
"double-ended-queue": "2.1.0-0",
|
||||||
"eslint": "6.8.0",
|
"eslint": "6.8.0",
|
||||||
"eslint-plugin-vue": "6.1.2",
|
"eslint-plugin-vue": "6.2.2",
|
||||||
"eventemitter3": "4.0.0",
|
"eventemitter3": "4.0.0",
|
||||||
"feed": "4.1.0",
|
"feed": "4.1.0",
|
||||||
"fibers": "4.0.2",
|
"fibers": "4.0.2",
|
||||||
"file-type": "14.1.2",
|
"file-type": "14.1.4",
|
||||||
"fluent-ffmpeg": "2.1.2",
|
"fluent-ffmpeg": "2.1.2",
|
||||||
"glob": "7.1.6",
|
"glob": "7.1.6",
|
||||||
"gulp": "4.0.2",
|
"gulp": "4.0.2",
|
||||||
"gulp-clean-css": "4.2.0",
|
"gulp-clean-css": "4.3.0",
|
||||||
"gulp-dart-sass": "0.9.1",
|
"gulp-dart-sass": "1.0.0",
|
||||||
"gulp-mocha": "7.0.2",
|
"gulp-mocha": "7.0.2",
|
||||||
"gulp-rename": "2.0.0",
|
"gulp-rename": "2.0.0",
|
||||||
"gulp-replace": "1.0.0",
|
"gulp-replace": "1.0.0",
|
||||||
@@ -143,21 +145,21 @@
|
|||||||
"gulp-typescript": "5.0.1",
|
"gulp-typescript": "5.0.1",
|
||||||
"hard-source-webpack-plugin": "0.13.1",
|
"hard-source-webpack-plugin": "0.13.1",
|
||||||
"html-minifier": "4.0.0",
|
"html-minifier": "4.0.0",
|
||||||
"http-signature": "1.3.1",
|
"http-signature": "1.3.4",
|
||||||
"https-proxy-agent": "5.0.0",
|
"https-proxy-agent": "5.0.0",
|
||||||
"insert-text-at-cursor": "0.3.0",
|
"insert-text-at-cursor": "0.3.0",
|
||||||
"is-root": "2.1.0",
|
"is-root": "2.1.0",
|
||||||
"is-svg": "4.2.1",
|
"is-svg": "4.2.1",
|
||||||
"js-yaml": "3.13.1",
|
"js-yaml": "3.13.1",
|
||||||
"jsdom": "16.1.0",
|
"jsdom": "16.2.2",
|
||||||
"json5": "2.1.1",
|
"json5": "2.1.2",
|
||||||
"json5-loader": "3.0.0",
|
"json5-loader": "3.0.0",
|
||||||
"jsrsasign": "8.0.12",
|
"jsrsasign": "8.0.13",
|
||||||
"katex": "0.11.1",
|
"katex": "0.11.1",
|
||||||
"koa": "2.11.0",
|
"koa": "2.11.0",
|
||||||
"koa-bodyparser": "4.2.1",
|
"koa-bodyparser": "4.3.0",
|
||||||
"koa-compress": "3.0.0",
|
"koa-compress": "3.0.0",
|
||||||
"koa-favicon": "2.0.1",
|
"koa-favicon": "2.1.0",
|
||||||
"koa-json-body": "5.3.0",
|
"koa-json-body": "5.3.0",
|
||||||
"koa-logger": "3.2.1",
|
"koa-logger": "3.2.1",
|
||||||
"koa-mount": "4.0.0",
|
"koa-mount": "4.0.0",
|
||||||
@@ -165,24 +167,23 @@
|
|||||||
"koa-slow": "2.1.0",
|
"koa-slow": "2.1.0",
|
||||||
"koa-views": "6.2.1",
|
"koa-views": "6.2.1",
|
||||||
"langmap": "0.0.16",
|
"langmap": "0.0.16",
|
||||||
"loader-utils": "1.2.3",
|
|
||||||
"lolex": "5.1.2",
|
"lolex": "5.1.2",
|
||||||
"lookup-dns-cache": "2.1.0",
|
"lookup-dns-cache": "2.1.0",
|
||||||
"markdown-it": "10.0.0",
|
"markdown-it": "10.0.0",
|
||||||
"markdown-it-anchor": "5.2.5",
|
"markdown-it-anchor": "5.2.7",
|
||||||
"mocha": "7.0.1",
|
"mocha": "7.1.1",
|
||||||
"moji": "0.5.1",
|
"moji": "0.5.1",
|
||||||
"ms": "2.1.2",
|
"ms": "2.1.2",
|
||||||
"multer": "1.4.2",
|
"multer": "1.4.2",
|
||||||
"nested-property": "1.0.4",
|
"nested-property": "1.0.4",
|
||||||
"node-fetch": "2.6.0",
|
"node-fetch": "2.6.0",
|
||||||
"nodemailer": "6.4.2",
|
"nodemailer": "6.4.6",
|
||||||
"nprogress": "0.2.0",
|
"nprogress": "0.2.0",
|
||||||
"object-assign-deep": "0.4.0",
|
"object-assign-deep": "0.4.0",
|
||||||
"os-utils": "0.0.14",
|
"os-utils": "0.0.14",
|
||||||
"parse5": "5.1.1",
|
"parse5": "5.1.1",
|
||||||
"parsimmon": "1.13.0",
|
"parsimmon": "1.13.0",
|
||||||
"pg": "7.18.1",
|
"pg": "8.0.0",
|
||||||
"portal-vue": "2.1.7",
|
"portal-vue": "2.1.7",
|
||||||
"portscanner": "2.2.0",
|
"portscanner": "2.2.0",
|
||||||
"postcss-loader": "3.0.0",
|
"postcss-loader": "3.0.0",
|
||||||
@@ -193,11 +194,11 @@
|
|||||||
"promise-sequential": "1.1.1",
|
"promise-sequential": "1.1.1",
|
||||||
"pug": "2.0.4",
|
"pug": "2.0.4",
|
||||||
"punycode": "2.1.1",
|
"punycode": "2.1.1",
|
||||||
"pureimage": "0.1.6",
|
"pureimage": "0.2.1",
|
||||||
"qrcode": "1.4.4",
|
"qrcode": "1.4.4",
|
||||||
"random-seed": "0.3.0",
|
"random-seed": "0.3.0",
|
||||||
"randomcolor": "0.5.4",
|
"randomcolor": "0.5.4",
|
||||||
"ratelimiter": "3.4.0",
|
"ratelimiter": "3.4.1",
|
||||||
"recaptcha-promise": "0.1.3",
|
"recaptcha-promise": "0.1.3",
|
||||||
"reconnecting-websocket": "4.4.0",
|
"reconnecting-websocket": "4.4.0",
|
||||||
"redis": "3.0.2",
|
"redis": "3.0.2",
|
||||||
@@ -211,57 +212,57 @@
|
|||||||
"rimraf": "3.0.2",
|
"rimraf": "3.0.2",
|
||||||
"rndstr": "1.0.0",
|
"rndstr": "1.0.0",
|
||||||
"s-age": "1.1.2",
|
"s-age": "1.1.2",
|
||||||
"sass": "1.25.0",
|
"sass": "1.26.3",
|
||||||
"sass-loader": "8.0.2",
|
"sass-loader": "8.0.2",
|
||||||
"seedrandom": "3.0.5",
|
"seedrandom": "3.0.5",
|
||||||
"sharp": "0.24.0",
|
"sharp": "0.25.2",
|
||||||
"showdown": "1.9.1",
|
"showdown": "1.9.1",
|
||||||
"showdown-highlightjs-extension": "0.1.2",
|
"showdown-highlightjs-extension": "0.1.2",
|
||||||
"speakeasy": "2.0.0",
|
"speakeasy": "2.0.0",
|
||||||
"stringz": "2.0.0",
|
"stringz": "2.1.0",
|
||||||
"style-loader": "1.1.3",
|
"style-loader": "1.1.3",
|
||||||
"summaly": "2.3.1",
|
"summaly": "2.3.1",
|
||||||
"syslog-pro": "1.0.0",
|
"syslog-pro": "1.0.0",
|
||||||
"systeminformation": "4.21.2",
|
"systeminformation": "4.23.1",
|
||||||
"syuilo-password-strength": "0.0.1",
|
"syuilo-password-strength": "0.0.1",
|
||||||
"terser-webpack-plugin": "2.3.4",
|
"terser-webpack-plugin": "2.3.5",
|
||||||
"textarea-caret": "3.1.0",
|
"textarea-caret": "3.1.0",
|
||||||
"three": "0.113.2",
|
"three": "0.115.0",
|
||||||
"tinycolor2": "1.4.1",
|
"tinycolor2": "1.4.1",
|
||||||
"tmp": "0.1.0",
|
"tmp": "0.1.0",
|
||||||
"ts-loader": "6.2.1",
|
"ts-loader": "6.2.2",
|
||||||
"ts-node": "8.6.2",
|
"ts-node": "8.8.1",
|
||||||
"tslint": "6.0.0",
|
"tslint": "6.1.1",
|
||||||
"tslint-sonarts": "1.9.0",
|
"tslint-sonarts": "1.9.0",
|
||||||
"typeorm": "0.2.22",
|
"typeorm": "0.2.24",
|
||||||
"typescript": "3.7.5",
|
"typescript": "3.8.3",
|
||||||
"ulid": "2.3.0",
|
"ulid": "2.3.0",
|
||||||
"url-loader": "3.0.0",
|
"url-loader": "3.0.0",
|
||||||
"uuid": "3.4.0",
|
"uuid": "7.0.3",
|
||||||
"v-animate-css": "0.0.3",
|
"v-animate-css": "0.0.3",
|
||||||
"v-debounce": "0.1.2",
|
"v-debounce": "0.1.2",
|
||||||
"vue": "2.6.11",
|
"vue": "2.6.11",
|
||||||
"vue-color": "2.7.0",
|
"vue-color": "2.7.1",
|
||||||
"vue-content-loading": "1.6.0",
|
"vue-content-loading": "1.6.0",
|
||||||
"vue-cropperjs": "4.0.1",
|
"vue-cropperjs": "4.0.1",
|
||||||
"vue-i18n": "8.15.3",
|
"vue-i18n": "8.16.0",
|
||||||
"vue-json-pretty": "1.6.3",
|
"vue-json-pretty": "1.6.3",
|
||||||
"vue-loader": "15.9.0",
|
"vue-loader": "15.9.1",
|
||||||
"vue-marquee-text-component": "1.1.1",
|
"vue-marquee-text-component": "1.1.1",
|
||||||
"vue-meta": "2.3.2",
|
"vue-meta": "2.3.3",
|
||||||
"vue-prism-component": "1.1.1",
|
"vue-prism-component": "1.1.1",
|
||||||
"vue-router": "3.1.5",
|
"vue-router": "3.1.6",
|
||||||
"vue-style-loader": "4.1.2",
|
"vue-style-loader": "4.1.2",
|
||||||
"vue-svg-inline-loader": "1.4.5",
|
"vue-svg-inline-loader": "1.5.0",
|
||||||
"vue-template-compiler": "2.6.11",
|
"vue-template-compiler": "2.6.11",
|
||||||
"vuedraggable": "2.23.2",
|
"vuedraggable": "2.23.2",
|
||||||
"vuex": "3.1.2",
|
"vuex": "3.1.3",
|
||||||
"vuex-persistedstate": "2.7.1",
|
"vuex-persistedstate": "3.0.1",
|
||||||
"web-push": "3.4.3",
|
"web-push": "3.4.3",
|
||||||
"webpack": "4.41.6",
|
"webpack": "4.42.1",
|
||||||
"webpack-cli": "3.3.11",
|
"webpack-cli": "3.3.11",
|
||||||
"websocket": "1.0.31",
|
"websocket": "1.0.31",
|
||||||
"ws": "7.2.1",
|
"ws": "7.2.3",
|
||||||
"xev": "2.0.1"
|
"xev": "2.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ async function isPortAvailable(port: number): Promise<boolean> {
|
|||||||
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');
|
||||||
logger.info(typeof env == 'undefined' ? 'NODE_ENV is not set' : `NODE_ENV: ${env}`);
|
logger.info(typeof env === 'undefined' ? 'NODE_ENV is not set' : `NODE_ENV: ${env}`);
|
||||||
|
|
||||||
if (env !== 'production') {
|
if (env !== 'production') {
|
||||||
logger.warn('The environment is not in production mode.');
|
logger.warn('The environment is not in production mode.');
|
||||||
|
|||||||
@@ -310,7 +310,7 @@ export default Vue.extend({
|
|||||||
title: this.$t('search'),
|
title: this.$t('search'),
|
||||||
input: true
|
input: true
|
||||||
}).then(async ({ canceled, result: query }) => {
|
}).then(async ({ canceled, result: query }) => {
|
||||||
if (canceled || query == null || query == '') return;
|
if (canceled || query == null || query === '') return;
|
||||||
|
|
||||||
this.searching = true;
|
this.searching = true;
|
||||||
search(this, query).finally(() => {
|
search(this, query).finally(() => {
|
||||||
@@ -320,7 +320,7 @@ export default Vue.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
searchKeypress(e) {
|
searchKeypress(e) {
|
||||||
if (e.keyCode == 13) {
|
if (e.keyCode === 13) {
|
||||||
this.searchWait = true;
|
this.searchWait = true;
|
||||||
search(this, this.searchQuery).finally(() => {
|
search(this, this.searchQuery).finally(() => {
|
||||||
this.searchWait = false;
|
this.searchWait = false;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<redoc spec-url='/api.json'></redoc>
|
<redoc spec-url="/api.json" expand-responses="200" expand-single-schema-field="true"></redoc>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"> </script>
|
<script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"> </script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -517,11 +517,11 @@ export default Vue.extend({
|
|||||||
icon: faLink,
|
icon: faLink,
|
||||||
text: this.$t('copyLink'),
|
text: this.$t('copyLink'),
|
||||||
action: this.copyLink
|
action: this.copyLink
|
||||||
}, this.appearNote.uri ? {
|
}, (this.appearNote.url || this.appearNote.uri) ? {
|
||||||
icon: faExternalLinkSquareAlt,
|
icon: faExternalLinkSquareAlt,
|
||||||
text: this.$t('showOnRemote'),
|
text: this.$t('showOnRemote'),
|
||||||
action: () => {
|
action: () => {
|
||||||
window.open(this.appearNote.uri, '_blank');
|
window.open(this.appearNote.url || this.appearNote.uri, '_blank');
|
||||||
}
|
}
|
||||||
} : undefined,
|
} : undefined,
|
||||||
null,
|
null,
|
||||||
@@ -585,11 +585,11 @@ export default Vue.extend({
|
|||||||
icon: faLink,
|
icon: faLink,
|
||||||
text: this.$t('copyLink'),
|
text: this.$t('copyLink'),
|
||||||
action: this.copyLink
|
action: this.copyLink
|
||||||
}, this.appearNote.uri ? {
|
}, (this.appearNote.url || this.appearNote.uri) ? {
|
||||||
icon: faExternalLinkSquareAlt,
|
icon: faExternalLinkSquareAlt,
|
||||||
text: this.$t('showOnRemote'),
|
text: this.$t('showOnRemote'),
|
||||||
action: () => {
|
action: () => {
|
||||||
window.open(this.appearNote.uri, '_blank');
|
window.open(this.appearNote.url || this.appearNote.uri, '_blank');
|
||||||
}
|
}
|
||||||
} : undefined]
|
} : undefined]
|
||||||
.filter(x => x !== undefined);
|
.filter(x => x !== undefined);
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ export default Vue.extend({
|
|||||||
border: solid var(--lineWidth) var(--urlPreviewBorder);
|
border: solid var(--lineWidth) var(--urlPreviewBorder);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
border: 1px solid var(--divider);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
@@ -42,9 +43,8 @@ export default Vue.extend({
|
|||||||
}
|
}
|
||||||
|
|
||||||
> .thumbnail {
|
> .thumbnail {
|
||||||
position: absolute;
|
width: 100%;
|
||||||
width: 100px;
|
height: 200px;
|
||||||
height: 100%;
|
|
||||||
background-position: center;
|
background-position: center;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<div class="efvhhmdq">
|
<div class="efvhhmdq">
|
||||||
<div class="no-users" v-if="empty">
|
<div class="no-users" v-if="empty">
|
||||||
<p>{{ $t('no-users') }}</p>
|
<p>{{ $t('noUsers') }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="user" v-for="user in users" :key="user.id">
|
<div class="user" v-for="user in users" :key="user.id">
|
||||||
<mk-avatar class="avatar" :user="user"/>
|
<mk-avatar class="avatar" :user="user"/>
|
||||||
|
|||||||
@@ -18,8 +18,9 @@ import PostFormDialog from './components/post-form-dialog.vue';
|
|||||||
import Dialog from './components/dialog.vue';
|
import Dialog from './components/dialog.vue';
|
||||||
import Menu from './components/menu.vue';
|
import Menu from './components/menu.vue';
|
||||||
import { router } from './router';
|
import { router } from './router';
|
||||||
import { applyTheme, lightTheme, builtinThemes } from './theme';
|
import { applyTheme, lightTheme } from './theme';
|
||||||
import { isDeviceDarkmode } from './scripts/is-device-darkmode';
|
import { isDeviceDarkmode } from './scripts/is-device-darkmode';
|
||||||
|
import createStore from './store';
|
||||||
|
|
||||||
Vue.use(Vuex);
|
Vue.use(Vuex);
|
||||||
Vue.use(VueHotkey);
|
Vue.use(VueHotkey);
|
||||||
@@ -134,36 +135,39 @@ document.body.setAttribute('ontouchstart', '');
|
|||||||
// アプリ基底要素マウント
|
// アプリ基底要素マウント
|
||||||
document.body.innerHTML = '<div id="app"></div>';
|
document.body.innerHTML = '<div id="app"></div>';
|
||||||
|
|
||||||
const os = new MiOS();
|
const store = createStore();
|
||||||
|
|
||||||
|
const os = new MiOS(store);
|
||||||
|
|
||||||
os.init(async () => {
|
os.init(async () => {
|
||||||
window.addEventListener('storage', e => {
|
window.addEventListener('storage', e => {
|
||||||
if (e.key === 'vuex') {
|
if (e.key === 'vuex') {
|
||||||
os.store.replaceState(JSON.parse(localStorage['vuex']));
|
store.replaceState(JSON.parse(localStorage['vuex']));
|
||||||
} else if (e.key === 'i') {
|
} else if (e.key === 'i') {
|
||||||
location.reload();
|
location.reload();
|
||||||
}
|
}
|
||||||
}, false)
|
}, false)
|
||||||
|
|
||||||
os.store.watch(state => state.device.darkMode, darkMode => {
|
store.watch(state => state.device.darkMode, darkMode => {
|
||||||
// TODO: このファイルでbuiltinThemesを参照するとcode splittingが効かず、初回読み込み時に全てのテーマコードを読み込むことになってしまい無駄なので何とかする
|
import('./theme').then(({ builtinThemes }) => {
|
||||||
const themes = builtinThemes.concat(os.store.state.device.themes);
|
const themes = builtinThemes.concat(store.state.device.themes);
|
||||||
applyTheme(themes.find(x => x.id === (darkMode ? os.store.state.device.darkTheme : os.store.state.device.lightTheme)));
|
applyTheme(themes.find(x => x.id === (darkMode ? store.state.device.darkTheme : store.state.device.lightTheme)));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
//#region Sync dark mode
|
//#region Sync dark mode
|
||||||
if (os.store.state.device.syncDeviceDarkMode) {
|
if (store.state.device.syncDeviceDarkMode) {
|
||||||
os.store.commit('device/set', { key: 'darkMode', value: isDeviceDarkmode() });
|
store.commit('device/set', { key: 'darkMode', value: isDeviceDarkmode() });
|
||||||
}
|
}
|
||||||
|
|
||||||
window.matchMedia('(prefers-color-scheme: dark)').addListener(mql => {
|
window.matchMedia('(prefers-color-scheme: dark)').addListener(mql => {
|
||||||
if (os.store.state.device.syncDeviceDarkMode) {
|
if (store.state.device.syncDeviceDarkMode) {
|
||||||
os.store.commit('device/set', { key: 'darkMode', value: mql.matches });
|
store.commit('device/set', { key: 'darkMode', value: mql.matches });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
if ('Notification' in window && os.store.getters.isSignedIn) {
|
if ('Notification' in window && store.getters.isSignedIn) {
|
||||||
// 許可を得ていなかったらリクエスト
|
// 許可を得ていなかったらリクエスト
|
||||||
if (Notification.permission === 'default') {
|
if (Notification.permission === 'default') {
|
||||||
Notification.requestPermission();
|
Notification.requestPermission();
|
||||||
@@ -171,7 +175,7 @@ os.init(async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const app = new Vue({
|
const app = new Vue({
|
||||||
store: os.store,
|
store: store,
|
||||||
metaInfo: {
|
metaInfo: {
|
||||||
title: null,
|
title: null,
|
||||||
titleTemplate: title => title ? `${title} | ${(instanceName || 'Misskey')}` : (instanceName || 'Misskey')
|
titleTemplate: title => title ? `${title} | ${(instanceName || 'Misskey')}` : (instanceName || 'Misskey')
|
||||||
@@ -183,7 +187,7 @@ os.init(async () => {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
api: os.api,
|
api: (endpoint: string, data: { [x: string]: any } = {}, token?) => store.dispatch('api', { endpoint, data, token }),
|
||||||
signout: os.signout,
|
signout: os.signout,
|
||||||
new(vm, props) {
|
new(vm, props) {
|
||||||
const x = new vm({
|
const x = new vm({
|
||||||
@@ -234,58 +238,63 @@ os.init(async () => {
|
|||||||
// マウント
|
// マウント
|
||||||
app.$mount('#app');
|
app.$mount('#app');
|
||||||
|
|
||||||
if (app.$store.getters.isSignedIn) {
|
os.stream.on('emojiAdded', data => {
|
||||||
|
// TODO
|
||||||
|
//store.commit('instance/set', );
|
||||||
|
});
|
||||||
|
|
||||||
|
if (store.getters.isSignedIn) {
|
||||||
const main = os.stream.useSharedConnection('main');
|
const main = os.stream.useSharedConnection('main');
|
||||||
|
|
||||||
// 自分の情報が更新されたとき
|
// 自分の情報が更新されたとき
|
||||||
main.on('meUpdated', i => {
|
main.on('meUpdated', i => {
|
||||||
app.$store.dispatch('mergeMe', i);
|
store.dispatch('mergeMe', i);
|
||||||
});
|
});
|
||||||
|
|
||||||
main.on('readAllNotifications', () => {
|
main.on('readAllNotifications', () => {
|
||||||
app.$store.dispatch('mergeMe', {
|
store.dispatch('mergeMe', {
|
||||||
hasUnreadNotification: false
|
hasUnreadNotification: false
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
main.on('unreadNotification', () => {
|
main.on('unreadNotification', () => {
|
||||||
app.$store.dispatch('mergeMe', {
|
store.dispatch('mergeMe', {
|
||||||
hasUnreadNotification: true
|
hasUnreadNotification: true
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
main.on('unreadMention', () => {
|
main.on('unreadMention', () => {
|
||||||
app.$store.dispatch('mergeMe', {
|
store.dispatch('mergeMe', {
|
||||||
hasUnreadMentions: true
|
hasUnreadMentions: true
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
main.on('readAllUnreadMentions', () => {
|
main.on('readAllUnreadMentions', () => {
|
||||||
app.$store.dispatch('mergeMe', {
|
store.dispatch('mergeMe', {
|
||||||
hasUnreadMentions: false
|
hasUnreadMentions: false
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
main.on('unreadSpecifiedNote', () => {
|
main.on('unreadSpecifiedNote', () => {
|
||||||
app.$store.dispatch('mergeMe', {
|
store.dispatch('mergeMe', {
|
||||||
hasUnreadSpecifiedNotes: true
|
hasUnreadSpecifiedNotes: true
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
main.on('readAllUnreadSpecifiedNotes', () => {
|
main.on('readAllUnreadSpecifiedNotes', () => {
|
||||||
app.$store.dispatch('mergeMe', {
|
store.dispatch('mergeMe', {
|
||||||
hasUnreadSpecifiedNotes: false
|
hasUnreadSpecifiedNotes: false
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
main.on('readAllMessagingMessages', () => {
|
main.on('readAllMessagingMessages', () => {
|
||||||
app.$store.dispatch('mergeMe', {
|
store.dispatch('mergeMe', {
|
||||||
hasUnreadMessagingMessage: false
|
hasUnreadMessagingMessage: false
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
main.on('unreadMessagingMessage', () => {
|
main.on('unreadMessagingMessage', () => {
|
||||||
app.$store.dispatch('mergeMe', {
|
store.dispatch('mergeMe', {
|
||||||
hasUnreadMessagingMessage: true
|
hasUnreadMessagingMessage: true
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -293,13 +302,13 @@ os.init(async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
main.on('readAllAntennas', () => {
|
main.on('readAllAntennas', () => {
|
||||||
app.$store.dispatch('mergeMe', {
|
store.dispatch('mergeMe', {
|
||||||
hasUnreadAntenna: false
|
hasUnreadAntenna: false
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
main.on('unreadAntenna', () => {
|
main.on('unreadAntenna', () => {
|
||||||
app.$store.dispatch('mergeMe', {
|
store.dispatch('mergeMe', {
|
||||||
hasUnreadAntenna: true
|
hasUnreadAntenna: true
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -307,13 +316,13 @@ os.init(async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
main.on('readAllAnnouncements', () => {
|
main.on('readAllAnnouncements', () => {
|
||||||
app.$store.dispatch('mergeMe', {
|
store.dispatch('mergeMe', {
|
||||||
hasUnreadAnnouncement: false
|
hasUnreadAnnouncement: false
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
main.on('clientSettingUpdated', x => {
|
main.on('clientSettingUpdated', x => {
|
||||||
app.$store.commit('settings/set', {
|
store.commit('settings/set', {
|
||||||
key: x.key,
|
key: x.key,
|
||||||
value: x.value
|
value: x.value
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,16 +2,11 @@ import autobind from 'autobind-decorator';
|
|||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import { EventEmitter } from 'eventemitter3';
|
import { EventEmitter } from 'eventemitter3';
|
||||||
|
|
||||||
import initStore from './store';
|
|
||||||
import { apiUrl, version } from './config';
|
import { apiUrl, version } from './config';
|
||||||
import Progress from './scripts/loading';
|
import Progress from './scripts/loading';
|
||||||
|
|
||||||
import Stream from './scripts/stream';
|
import Stream from './scripts/stream';
|
||||||
|
import store from './store';
|
||||||
//#region api requests
|
|
||||||
let spinner = null;
|
|
||||||
let pending = 0;
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Misskey Operating System
|
* Misskey Operating System
|
||||||
@@ -19,7 +14,7 @@ let pending = 0;
|
|||||||
export default class MiOS extends EventEmitter {
|
export default class MiOS extends EventEmitter {
|
||||||
public app: Vue;
|
public app: Vue;
|
||||||
|
|
||||||
public store: ReturnType<typeof initStore>;
|
public store: ReturnType<typeof store>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A connection manager of home stream
|
* A connection manager of home stream
|
||||||
@@ -31,6 +26,11 @@ export default class MiOS extends EventEmitter {
|
|||||||
*/
|
*/
|
||||||
private swRegistration: ServiceWorkerRegistration = null;
|
private swRegistration: ServiceWorkerRegistration = null;
|
||||||
|
|
||||||
|
constructor(vuex: MiOS['store']) {
|
||||||
|
super();
|
||||||
|
this.store = vuex;
|
||||||
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
public signout() {
|
public signout() {
|
||||||
this.store.dispatch('logout');
|
this.store.dispatch('logout');
|
||||||
@@ -52,8 +52,6 @@ export default class MiOS extends EventEmitter {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
this.store = initStore(this);
|
|
||||||
|
|
||||||
// ユーザーをフェッチしてコールバックする
|
// ユーザーをフェッチしてコールバックする
|
||||||
const fetchme = (token, cb) => {
|
const fetchme = (token, cb) => {
|
||||||
let me = null;
|
let me = null;
|
||||||
@@ -187,16 +185,19 @@ export default class MiOS extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Register
|
// Register
|
||||||
this.api('sw/register', {
|
this.store.dispatch('api', {
|
||||||
|
endpoint: 'sw/register',
|
||||||
|
data: {
|
||||||
endpoint: subscription.endpoint,
|
endpoint: subscription.endpoint,
|
||||||
auth: encode(subscription.getKey('auth')),
|
auth: encode(subscription.getKey('auth')),
|
||||||
publickey: encode(subscription.getKey('p256dh'))
|
publickey: encode(subscription.getKey('p256dh'))
|
||||||
|
}
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
// When subscribe failed
|
// When subscribe failed
|
||||||
.catch(async (err: Error) => {
|
.catch(async (err: Error) => {
|
||||||
// 通知が許可されていなかったとき
|
// 通知が許可されていなかったとき
|
||||||
if (err.name == 'NotAllowedError') {
|
if (err.name === 'NotAllowedError') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,52 +215,6 @@ export default class MiOS extends EventEmitter {
|
|||||||
// Register service worker
|
// Register service worker
|
||||||
navigator.serviceWorker.register(sw);
|
navigator.serviceWorker.register(sw);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Misskey APIにリクエストします
|
|
||||||
* @param endpoint エンドポイント名
|
|
||||||
* @param data パラメータ
|
|
||||||
*/
|
|
||||||
@autobind
|
|
||||||
public api(endpoint: string, data: { [x: string]: any } = {}, token?): Promise<{ [x: string]: any }> {
|
|
||||||
if (++pending === 1) {
|
|
||||||
spinner = document.createElement('div');
|
|
||||||
spinner.setAttribute('id', 'wait');
|
|
||||||
document.body.appendChild(spinner);
|
|
||||||
}
|
|
||||||
|
|
||||||
const onFinally = () => {
|
|
||||||
if (--pending === 0) spinner.parentNode.removeChild(spinner);
|
|
||||||
};
|
|
||||||
|
|
||||||
const promise = new Promise((resolve, reject) => {
|
|
||||||
// Append a credential
|
|
||||||
if (this.store.getters.isSignedIn) (data as any).i = this.store.state.i.token;
|
|
||||||
if (token) (data as any).i = token;
|
|
||||||
|
|
||||||
// Send request
|
|
||||||
fetch(endpoint.indexOf('://') > -1 ? endpoint : `${apiUrl}/${endpoint}`, {
|
|
||||||
method: 'POST',
|
|
||||||
body: JSON.stringify(data),
|
|
||||||
credentials: 'omit',
|
|
||||||
cache: 'no-cache'
|
|
||||||
}).then(async (res) => {
|
|
||||||
const body = res.status === 204 ? null : await res.json();
|
|
||||||
|
|
||||||
if (res.status === 200) {
|
|
||||||
resolve(body);
|
|
||||||
} else if (res.status === 204) {
|
|
||||||
resolve();
|
|
||||||
} else {
|
|
||||||
reject(body.error);
|
|
||||||
}
|
|
||||||
}).catch(reject);
|
|
||||||
});
|
|
||||||
|
|
||||||
promise.then(onFinally, onFinally);
|
|
||||||
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<x-notes v-if="showNext" ref="next" :pagination="next"/>
|
<x-notes v-if="showNext" ref="next" :pagination="next"/>
|
||||||
<hr v-if="showNext"/>
|
<hr v-if="showNext"/>
|
||||||
|
|
||||||
<mk-remote-caution v-if="note.user.host != null" :href="note.uri" style="margin-bottom: var(--margin)"/>
|
<mk-remote-caution v-if="note.user.host != null" :href="note.url || note.uri" style="margin-bottom: var(--margin)"/>
|
||||||
<x-note :note="note" :key="note.id" :detail="true"/>
|
<x-note :note="note" :key="note.id" :detail="true"/>
|
||||||
<div v-if="error">
|
<div v-if="error">
|
||||||
<mk-error @retry="fetch()"/>
|
<mk-error @retry="fetch()"/>
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ import { blockDefs } from '../../scripts/aiscript/index';
|
|||||||
import { ASTypeChecker } from '../../scripts/aiscript/type-checker';
|
import { ASTypeChecker } from '../../scripts/aiscript/type-checker';
|
||||||
import { url } from '../../config';
|
import { url } from '../../config';
|
||||||
import { collectPageVars } from '../../scripts/collect-page-vars';
|
import { collectPageVars } from '../../scripts/collect-page-vars';
|
||||||
|
import { selectDriveFile } from '../../scripts/select-drive-file';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
i18n,
|
i18n,
|
||||||
@@ -405,9 +406,7 @@ export default Vue.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
setEyeCatchingImage() {
|
setEyeCatchingImage() {
|
||||||
this.$chooseDriveFile({
|
selectDriveFile(this.$root, false).then(file => {
|
||||||
multiple: false
|
|
||||||
}).then(file => {
|
|
||||||
this.eyeCatchingImageId = file.id;
|
this.eyeCatchingImageId = file.id;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
<div class="_card" v-if="page" :key="page.id">
|
<div class="_card" v-if="page" :key="page.id">
|
||||||
<div class="_title">{{ page.title }}</div>
|
<div class="_title">{{ page.title }}</div>
|
||||||
|
<img class="header" :src="page.eyeCatchingImage.url" v-if="page.eyeCatchingImageId" />
|
||||||
<div class="_content">
|
<div class="_content">
|
||||||
<x-page :page="page"/>
|
<x-page :page="page"/>
|
||||||
</div>
|
</div>
|
||||||
@@ -115,6 +116,8 @@ export default Vue.extend({
|
|||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.xcukqgmh {
|
.xcukqgmh {
|
||||||
|
> ._card > .header {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -57,9 +57,9 @@ const ignoreElemens = ['input', 'textarea'];
|
|||||||
function match(e: KeyboardEvent, patterns: action['patterns']): boolean {
|
function match(e: KeyboardEvent, patterns: action['patterns']): boolean {
|
||||||
const key = e.code.toLowerCase();
|
const key = e.code.toLowerCase();
|
||||||
return patterns.some(pattern => pattern.which.includes(key) &&
|
return patterns.some(pattern => pattern.which.includes(key) &&
|
||||||
pattern.ctrl == e.ctrlKey &&
|
pattern.ctrl === e.ctrlKey &&
|
||||||
pattern.shift == e.shiftKey &&
|
pattern.shift === e.shiftKey &&
|
||||||
pattern.alt == e.altKey &&
|
pattern.alt === e.altKey &&
|
||||||
!e.metaKey
|
!e.metaKey
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export default (input: string): string[] => {
|
export default (input: string): string[] => {
|
||||||
if (Object.keys(aliases).some(a => a.toLowerCase() == input.toLowerCase())) {
|
if (Object.keys(aliases).some(a => a.toLowerCase() === input.toLowerCase())) {
|
||||||
const codes = aliases[input];
|
const codes = aliases[input];
|
||||||
return Array.isArray(codes) ? codes : [codes];
|
return Array.isArray(codes) ? codes : [codes];
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export default (opts) => ({
|
|||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
empty(): boolean {
|
empty(): boolean {
|
||||||
return this.items.length == 0 && !this.fetching && this.inited;
|
return this.items.length === 0 && !this.fetching && this.inited;
|
||||||
},
|
},
|
||||||
|
|
||||||
error(): boolean {
|
error(): boolean {
|
||||||
|
|||||||
@@ -47,9 +47,9 @@ export async function search(v: any, q: string) {
|
|||||||
uri: q
|
uri: q
|
||||||
});
|
});
|
||||||
dialog.close();
|
dialog.close();
|
||||||
if (res.type == 'User') {
|
if (res.type === 'User') {
|
||||||
v.$router.push(`/@${res.object.username}@${res.object.host}`);
|
v.$router.push(`/@${res.object.username}@${res.object.host}`);
|
||||||
} else if (res.type == 'Note') {
|
} else if (res.type === 'Note') {
|
||||||
v.$router.push(`/notes/${res.object.id}`);
|
v.$router.push(`/notes/${res.object.id}`);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export default class Stream extends EventEmitter {
|
|||||||
*/
|
*/
|
||||||
@autobind
|
@autobind
|
||||||
private onOpen() {
|
private onOpen() {
|
||||||
const isReconnect = this.state == 'reconnecting';
|
const isReconnect = this.state === 'reconnecting';
|
||||||
|
|
||||||
this.state = 'connected';
|
this.state = 'connected';
|
||||||
this.emit('_connected_');
|
this.emit('_connected_');
|
||||||
@@ -87,7 +87,7 @@ export default class Stream extends EventEmitter {
|
|||||||
*/
|
*/
|
||||||
@autobind
|
@autobind
|
||||||
private onClose() {
|
private onClose() {
|
||||||
if (this.state == 'connected') {
|
if (this.state === 'connected') {
|
||||||
this.state = 'reconnecting';
|
this.state = 'reconnecting';
|
||||||
this.emit('_disconnected_');
|
this.emit('_disconnected_');
|
||||||
}
|
}
|
||||||
@@ -100,7 +100,7 @@ export default class Stream extends EventEmitter {
|
|||||||
private onMessage(message) {
|
private onMessage(message) {
|
||||||
const { type, body } = JSON.parse(message.data);
|
const { type, body } = JSON.parse(message.data);
|
||||||
|
|
||||||
if (type == 'channel') {
|
if (type === 'channel') {
|
||||||
const id = body.id;
|
const id = body.id;
|
||||||
|
|
||||||
let connections: Connection[];
|
let connections: Connection[];
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import Vuex from 'vuex';
|
import Vuex from 'vuex';
|
||||||
import createPersistedState from 'vuex-persistedstate';
|
import createPersistedState from 'vuex-persistedstate';
|
||||||
import * as nestedProperty from 'nested-property';
|
import * as nestedProperty from 'nested-property';
|
||||||
|
import { apiUrl } from './config';
|
||||||
import MiOS from './mios';
|
|
||||||
|
|
||||||
const defaultSettings = {
|
const defaultSettings = {
|
||||||
tutorial: 0,
|
tutorial: 0,
|
||||||
@@ -57,13 +56,15 @@ function copy<T>(data: T): T {
|
|||||||
return JSON.parse(JSON.stringify(data));
|
return JSON.parse(JSON.stringify(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
export default (os: MiOS) => new Vuex.Store({
|
export default () => new Vuex.Store({
|
||||||
plugins: [createPersistedState({
|
plugins: [createPersistedState({
|
||||||
paths: ['i', 'device', 'deviceUser', 'settings', 'instance']
|
paths: ['i', 'device', 'deviceUser', 'settings', 'instance']
|
||||||
})],
|
})],
|
||||||
|
|
||||||
state: {
|
state: {
|
||||||
i: null,
|
i: null,
|
||||||
|
pendingApiRequestsCount: 0,
|
||||||
|
spinner: null
|
||||||
},
|
},
|
||||||
|
|
||||||
getters: {
|
getters: {
|
||||||
@@ -121,6 +122,47 @@ export default (os: MiOS) => new Vuex.Store({
|
|||||||
ctx.commit('settings/init', me.clientData);
|
ctx.commit('settings/init', me.clientData);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
api(ctx, { endpoint, data, token }) {
|
||||||
|
if (++ctx.state.pendingApiRequestsCount === 1) {
|
||||||
|
// TODO: spinnerの表示はstoreでやらない
|
||||||
|
ctx.state.spinner = document.createElement('div');
|
||||||
|
ctx.state.spinner.setAttribute('id', 'wait');
|
||||||
|
document.body.appendChild(ctx.state.spinner);
|
||||||
|
}
|
||||||
|
|
||||||
|
const onFinally = () => {
|
||||||
|
if (--ctx.state.pendingApiRequestsCount === 0) ctx.state.spinner.parentNode.removeChild(ctx.state.spinner);
|
||||||
|
};
|
||||||
|
|
||||||
|
const promise = new Promise((resolve, reject) => {
|
||||||
|
// Append a credential
|
||||||
|
if (ctx.getters.isSignedIn) (data as any).i = ctx.state.i.token;
|
||||||
|
if (token) (data as any).i = token;
|
||||||
|
|
||||||
|
// Send request
|
||||||
|
fetch(endpoint.indexOf('://') > -1 ? endpoint : `${apiUrl}/${endpoint}`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
credentials: 'omit',
|
||||||
|
cache: 'no-cache'
|
||||||
|
}).then(async (res) => {
|
||||||
|
const body = res.status === 204 ? null : await res.json();
|
||||||
|
|
||||||
|
if (res.status === 200) {
|
||||||
|
resolve(body);
|
||||||
|
} else if (res.status === 204) {
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
|
reject(body.error);
|
||||||
|
}
|
||||||
|
}).catch(reject);
|
||||||
|
});
|
||||||
|
|
||||||
|
promise.then(onFinally, onFinally);
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
modules: {
|
modules: {
|
||||||
@@ -139,9 +181,12 @@ export default (os: MiOS) => new Vuex.Store({
|
|||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
async fetch(ctx) {
|
async fetch(ctx) {
|
||||||
const meta = await os.api('meta', {
|
const meta = await ctx.dispatch('api', {
|
||||||
|
endpoint: 'meta',
|
||||||
|
data: {
|
||||||
detail: false
|
detail: false
|
||||||
});
|
}
|
||||||
|
}, { root: true });
|
||||||
|
|
||||||
ctx.commit('set', meta);
|
ctx.commit('set', meta);
|
||||||
}
|
}
|
||||||
@@ -212,7 +257,7 @@ export default (os: MiOS) => new Vuex.Store({
|
|||||||
},
|
},
|
||||||
|
|
||||||
updateWidget(state, x) {
|
updateWidget(state, x) {
|
||||||
const w = state.widgets.find(w => w.id == x.id);
|
const w = state.widgets.find(w => w.id === x.id);
|
||||||
if (w) {
|
if (w) {
|
||||||
w.data = x.data;
|
w.data = x.data;
|
||||||
}
|
}
|
||||||
@@ -246,10 +291,13 @@ export default (os: MiOS) => new Vuex.Store({
|
|||||||
ctx.commit('set', x);
|
ctx.commit('set', x);
|
||||||
|
|
||||||
if (ctx.rootGetters.isSignedIn) {
|
if (ctx.rootGetters.isSignedIn) {
|
||||||
os.api('i/update-client-setting', {
|
ctx.dispatch('api', {
|
||||||
|
endpoint: 'i/update-client-setting',
|
||||||
|
data: {
|
||||||
name: x.key,
|
name: x.key,
|
||||||
value: x.value
|
value: x.value
|
||||||
});
|
}
|
||||||
|
}, { root: true });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,14 +51,14 @@ export default Vue.extend({
|
|||||||
weekday: date.getDay()
|
weekday: date.getDay()
|
||||||
};
|
};
|
||||||
|
|
||||||
d.v = peak == 0 ? 0 : d.total / (peak / 2);
|
d.v = peak === 0 ? 0 : d.total / (peak / 2);
|
||||||
if (d.v > 1) d.v = 1;
|
if (d.v > 1) d.v = 1;
|
||||||
const ch = d.date.weekday == 0 || d.date.weekday == 6 ? 275 : 170;
|
const ch = d.date.weekday === 0 || d.date.weekday === 6 ? 275 : 170;
|
||||||
const cs = d.v * 100;
|
const cs = d.v * 100;
|
||||||
const cl = 15 + ((1 - d.v) * 80);
|
const cl = 15 + ((1 - d.v) * 80);
|
||||||
d.color = `hsl(${ch}, ${cs}%, ${cl}%)`;
|
d.color = `hsl(${ch}, ${cs}%, ${cl}%)`;
|
||||||
|
|
||||||
if (d.date.weekday == 0) x--;
|
if (d.date.weekday === 0) x--;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ export default define({
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
func() {
|
func() {
|
||||||
if (this.props.design == 2) {
|
if (this.props.design === 2) {
|
||||||
this.props.design = 0;
|
this.props.design = 0;
|
||||||
} else {
|
} else {
|
||||||
this.props.design++;
|
this.props.design++;
|
||||||
@@ -68,7 +68,7 @@ export default define({
|
|||||||
this.save();
|
this.save();
|
||||||
},
|
},
|
||||||
toggleView() {
|
toggleView() {
|
||||||
if (this.props.view == 1) {
|
if (this.props.view === 1) {
|
||||||
this.props.view = 0;
|
this.props.view = 0;
|
||||||
} else {
|
} else {
|
||||||
this.props.view++;
|
this.props.view++;
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export default define({
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
func() {
|
func() {
|
||||||
if (this.props.design == 2) {
|
if (this.props.design === 2) {
|
||||||
this.props.design = 0;
|
this.props.design = 0;
|
||||||
} else {
|
} else {
|
||||||
this.props.design++;
|
this.props.design++;
|
||||||
@@ -102,7 +102,7 @@ export default define({
|
|||||||
this.monthP = monthNumer / monthDenom * 100;
|
this.monthP = monthNumer / monthDenom * 100;
|
||||||
this.yearP = yearNumer / yearDenom * 100;
|
this.yearP = yearNumer / yearDenom * 100;
|
||||||
|
|
||||||
this.isHoliday = now.getDay() == 0 || now.getDay() == 6;
|
this.isHoliday = now.getDay() === 0 || now.getDay() === 6;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<mk-container :show-header="props.design === 0" :naked="props.design === 2" :class="$style.root" :data-melt="props.design == 2">
|
<mk-container :show-header="props.design === 0" :naked="props.design === 2" :class="$style.root" :data-melt="props.design === 2">
|
||||||
<template #header><fa :icon="faCamera"/>{{ $t('_widgets.photos') }}</template>
|
<template #header><fa :icon="faCamera"/>{{ $t('_widgets.photos') }}</template>
|
||||||
|
|
||||||
<div class="">
|
<div class="">
|
||||||
@@ -66,7 +66,7 @@ export default define({
|
|||||||
},
|
},
|
||||||
|
|
||||||
func() {
|
func() {
|
||||||
if (this.props.design == 2) {
|
if (this.props.design === 2) {
|
||||||
this.props.design = 0;
|
this.props.design = 0;
|
||||||
} else {
|
} else {
|
||||||
this.props.design++;
|
this.props.design++;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const dir = `${__dirname}/../../.config`;
|
|||||||
/**
|
/**
|
||||||
* Path of configuration file
|
* Path of configuration file
|
||||||
*/
|
*/
|
||||||
const path = process.env.NODE_ENV == 'test'
|
const path = process.env.NODE_ENV === 'test'
|
||||||
? `${dir}/test.yml`
|
? `${dir}/test.yml`
|
||||||
: `${dir}/default.yml`;
|
: `${dir}/default.yml`;
|
||||||
|
|
||||||
|
|||||||
4
src/docs/aiscript.ja-JP.md
Normal file
4
src/docs/aiscript.ja-JP.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# AiScript
|
||||||
|
|
||||||
|
## 関数
|
||||||
|
デフォルトで値渡しです。
|
||||||
@@ -27,6 +27,8 @@ APIを使い始めるには、まずアクセストークンを取得する必
|
|||||||
|
|
||||||
UUIDを生成する。以後これをセッションIDと呼びます。
|
UUIDを生成する。以後これをセッションIDと呼びます。
|
||||||
|
|
||||||
|
> このセッションIDは毎回生成し、使いまわさないようにしてください。
|
||||||
|
|
||||||
#### Step 2
|
#### Step 2
|
||||||
|
|
||||||
`{_URL_}/miauth/{session}`をユーザーのブラウザで表示させる。`{session}`の部分は、セッションIDに置き換えてください。
|
`{_URL_}/miauth/{session}`をユーザーのブラウザで表示させる。`{session}`の部分は、セッションIDに置き換えてください。
|
||||||
|
|||||||
@@ -226,10 +226,10 @@ export default class Reversi {
|
|||||||
// 座標が指し示す位置がボード外に出たとき
|
// 座標が指し示す位置がボード外に出たとき
|
||||||
if (this.opts.loopedBoard && this.transformXyToPos(
|
if (this.opts.loopedBoard && this.transformXyToPos(
|
||||||
(x = ((x % this.mapWidth) + this.mapWidth) % this.mapWidth),
|
(x = ((x % this.mapWidth) + this.mapWidth) % this.mapWidth),
|
||||||
(y = ((y % this.mapHeight) + this.mapHeight) % this.mapHeight)) == initPos)
|
(y = ((y % this.mapHeight) + this.mapHeight) % this.mapHeight)) === initPos)
|
||||||
// 盤面の境界でループし、自分が石を置く位置に戻ってきたとき、挟めるようにしている (ref: Test4のマップ)
|
// 盤面の境界でループし、自分が石を置く位置に戻ってきたとき、挟めるようにしている (ref: Test4のマップ)
|
||||||
return found;
|
return found;
|
||||||
else if (x == -1 || y == -1 || x == this.mapWidth || y == this.mapHeight)
|
else if (x === -1 || y === -1 || x === this.mapWidth || y === this.mapHeight)
|
||||||
return []; // 挟めないことが確定 (盤面外に到達)
|
return []; // 挟めないことが確定 (盤面外に到達)
|
||||||
|
|
||||||
const pos = this.transformXyToPos(x, y);
|
const pos = this.transformXyToPos(x, y);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { parseFragment, DefaultTreeDocumentFragment } from 'parse5';
|
import { parseFragment, DefaultTreeDocumentFragment } from 'parse5';
|
||||||
import { urlRegex } from './prelude';
|
import { urlRegex } from './prelude';
|
||||||
|
|
||||||
export function fromHtml(html: string): string {
|
export function fromHtml(html: string, hashtagNames?: string[]): string {
|
||||||
const dom = parseFragment(html) as DefaultTreeDocumentFragment;
|
const dom = parseFragment(html) as DefaultTreeDocumentFragment;
|
||||||
|
|
||||||
let text = '';
|
let text = '';
|
||||||
@@ -13,7 +13,7 @@ export function fromHtml(html: string): string {
|
|||||||
return text.trim();
|
return text.trim();
|
||||||
|
|
||||||
function getText(node: any): string {
|
function getText(node: any): string {
|
||||||
if (node.nodeName == '#text') return node.value;
|
if (node.nodeName === '#text') return node.value;
|
||||||
|
|
||||||
if (node.childNodes) {
|
if (node.childNodes) {
|
||||||
return node.childNodes.map((n: any) => getText(n)).join('');
|
return node.childNodes.map((n: any) => getText(n)).join('');
|
||||||
@@ -34,29 +34,27 @@ export function fromHtml(html: string): string {
|
|||||||
|
|
||||||
case 'a':
|
case 'a':
|
||||||
const txt = getText(node);
|
const txt = getText(node);
|
||||||
const rel = node.attrs.find((x: any) => x.name == 'rel');
|
const rel = node.attrs.find((x: any) => x.name === 'rel');
|
||||||
const href = node.attrs.find((x: any) => x.name == 'href');
|
const href = node.attrs.find((x: any) => x.name === 'href');
|
||||||
const _class = node.attrs.find((x: any) => x.name == 'class');
|
|
||||||
const isHashtag = rel?.value?.match('tag') || _class?.value?.match('hashtag');
|
|
||||||
|
|
||||||
// ハッシュタグ / hrefがない / txtがURL
|
// ハッシュタグ
|
||||||
if (isHashtag || !href || href.value == txt) {
|
if (hashtagNames && href && hashtagNames.map(x => x.toLowerCase()).includes(txt.toLowerCase())) {
|
||||||
text += isHashtag || txt.match(urlRegex) ? txt : `<${txt}>`;
|
text += txt;
|
||||||
// メンション
|
// メンション
|
||||||
} else if (txt.startsWith('@') && !(rel && rel.value.match(/^me /))) {
|
} else if (txt.startsWith('@') && !(rel && rel.value.match(/^me /))) {
|
||||||
const part = txt.split('@');
|
const part = txt.split('@');
|
||||||
|
|
||||||
if (part.length == 2) {
|
if (part.length === 2) {
|
||||||
//#region ホスト名部分が省略されているので復元する
|
//#region ホスト名部分が省略されているので復元する
|
||||||
const acct = `${txt}@${(new URL(href.value)).hostname}`;
|
const acct = `${txt}@${(new URL(href.value)).hostname}`;
|
||||||
text += acct;
|
text += acct;
|
||||||
//#endregion
|
//#endregion
|
||||||
} else if (part.length == 3) {
|
} else if (part.length === 3) {
|
||||||
text += txt;
|
text += txt;
|
||||||
}
|
}
|
||||||
// その他
|
// その他
|
||||||
} else {
|
} else {
|
||||||
text += `[${txt}](${href.value})`;
|
text += (!href || (txt === href.value && txt.match(urlRegex))) ? txt : `[${txt}](${href.value})`;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export const mfmLanguage = P.createLanguage({
|
|||||||
r.center,
|
r.center,
|
||||||
),
|
),
|
||||||
startOfLine: () => P((input, i) => {
|
startOfLine: () => P((input, i) => {
|
||||||
if (i == 0 || input[i] == '\n' || input[i - 1] == '\n') {
|
if (i === 0 || input[i] === '\n' || input[i - 1] === '\n') {
|
||||||
return P.makeSuccess(i, null);
|
return P.makeSuccess(i, null);
|
||||||
} else {
|
} else {
|
||||||
return P.makeFailure(i, 'not newline');
|
return P.makeFailure(i, 'not newline');
|
||||||
@@ -50,7 +50,7 @@ export const mfmLanguage = P.createLanguage({
|
|||||||
if (!text.match(/^>[\s\S]+?/)) return P.makeFailure(i, 'not a quote');
|
if (!text.match(/^>[\s\S]+?/)) return P.makeFailure(i, 'not a quote');
|
||||||
const quote = takeWhile(line => line.startsWith('>'), text.split('\n'));
|
const quote = takeWhile(line => line.startsWith('>'), text.split('\n'));
|
||||||
const qInner = quote.join('\n').replace(/^>/gm, '').replace(/^ /gm, '');
|
const qInner = quote.join('\n').replace(/^>/gm, '').replace(/^ /gm, '');
|
||||||
if (qInner == '') return P.makeFailure(i, 'not a quote');
|
if (qInner === '') return P.makeFailure(i, 'not a quote');
|
||||||
const contents = r.root.tryParse(qInner);
|
const contents = r.root.tryParse(qInner);
|
||||||
return P.makeSuccess(i + quote.join('\n').length + 1, createTree('quote', contents, {}));
|
return P.makeSuccess(i + quote.join('\n').length + 1, createTree('quote', contents, {}));
|
||||||
})),
|
})),
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { MfmForest, MfmTree } from './prelude';
|
|||||||
import { createTree, createLeaf } from '../prelude/tree';
|
import { createTree, createLeaf } from '../prelude/tree';
|
||||||
|
|
||||||
function isEmptyTextTree(t: MfmTree): boolean {
|
function isEmptyTextTree(t: MfmTree): boolean {
|
||||||
return t.node.type == 'text' && t.node.props.text === '';
|
return t.node.type === 'text' && t.node.props.text === '';
|
||||||
}
|
}
|
||||||
|
|
||||||
function concatTextTrees(ts: MfmForest): MfmTree {
|
function concatTextTrees(ts: MfmForest): MfmTree {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { MfmForest } from './prelude';
|
|||||||
import { normalize } from './normalize';
|
import { normalize } from './normalize';
|
||||||
|
|
||||||
export function parse(source: string | null): MfmForest | null {
|
export function parse(source: string | null): MfmForest | null {
|
||||||
if (source == null || source == '') {
|
if (source == null || source === '') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11,7 +11,7 @@ export function parse(source: string | null): MfmForest | null {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function parsePlain(source: string | null): MfmForest | null {
|
export function parsePlain(source: string | null): MfmForest | null {
|
||||||
if (source == null || source == '') {
|
if (source == null || source === '') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
21
src/misc/secure-rndstr.ts
Normal file
21
src/misc/secure-rndstr.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import * as crypto from 'crypto';
|
||||||
|
|
||||||
|
const L_CHARS = '0123456789abcdefghijklmnopqrstuvwxyz';
|
||||||
|
const LU_CHARS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||||
|
|
||||||
|
export function secureRndstr(length = 32, useLU = true): string {
|
||||||
|
const chars = useLU ? LU_CHARS : L_CHARS;
|
||||||
|
const chars_len = chars.length;
|
||||||
|
|
||||||
|
let str = '';
|
||||||
|
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
let rand = Math.floor((crypto.randomBytes(1).readUInt8(0) / 0xFF) * chars_len);
|
||||||
|
if (rand === chars_len) {
|
||||||
|
rand = chars_len - 1;
|
||||||
|
}
|
||||||
|
str += chars.charAt(rand);
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
@@ -112,6 +112,12 @@ export class Note {
|
|||||||
})
|
})
|
||||||
public uri: string | null;
|
public uri: string | null;
|
||||||
|
|
||||||
|
@Column('varchar', {
|
||||||
|
length: 512, nullable: true,
|
||||||
|
comment: 'The human readable url of a note. it will be null when the note is local.'
|
||||||
|
})
|
||||||
|
public url: string | null;
|
||||||
|
|
||||||
@Column('integer', {
|
@Column('integer', {
|
||||||
default: 0, select: false
|
default: 0, select: false
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { getRepository, getCustomRepository } from 'typeorm';
|
|||||||
import { Announcement } from './entities/announcement';
|
import { Announcement } from './entities/announcement';
|
||||||
import { AnnouncementRead } from './entities/announcement-read';
|
import { AnnouncementRead } from './entities/announcement-read';
|
||||||
import { Instance } from './entities/instance';
|
import { Instance } from './entities/instance';
|
||||||
import { Emoji } from './entities/emoji';
|
|
||||||
import { Poll } from './entities/poll';
|
import { Poll } from './entities/poll';
|
||||||
import { PollVote } from './entities/poll-vote';
|
import { PollVote } from './entities/poll-vote';
|
||||||
import { Meta } from './entities/meta';
|
import { Meta } from './entities/meta';
|
||||||
@@ -52,6 +51,7 @@ import { AntennaRepository } from './repositories/antenna';
|
|||||||
import { AntennaNote } from './entities/antenna-note';
|
import { AntennaNote } from './entities/antenna-note';
|
||||||
import { PromoNote } from './entities/promo-note';
|
import { PromoNote } from './entities/promo-note';
|
||||||
import { PromoRead } from './entities/promo-read';
|
import { PromoRead } from './entities/promo-read';
|
||||||
|
import { EmojiRepository } from './repositories/emoji';
|
||||||
|
|
||||||
export const Announcements = getRepository(Announcement);
|
export const Announcements = getRepository(Announcement);
|
||||||
export const AnnouncementReads = getRepository(AnnouncementRead);
|
export const AnnouncementReads = getRepository(AnnouncementRead);
|
||||||
@@ -79,7 +79,7 @@ export const UsedUsernames = getRepository(UsedUsername);
|
|||||||
export const Followings = getCustomRepository(FollowingRepository);
|
export const Followings = getCustomRepository(FollowingRepository);
|
||||||
export const FollowRequests = getCustomRepository(FollowRequestRepository);
|
export const FollowRequests = getCustomRepository(FollowRequestRepository);
|
||||||
export const Instances = getRepository(Instance);
|
export const Instances = getRepository(Instance);
|
||||||
export const Emojis = getRepository(Emoji);
|
export const Emojis = getCustomRepository(EmojiRepository);
|
||||||
export const DriveFiles = getCustomRepository(DriveFileRepository);
|
export const DriveFiles = getCustomRepository(DriveFileRepository);
|
||||||
export const DriveFolders = getCustomRepository(DriveFolderRepository);
|
export const DriveFolders = getCustomRepository(DriveFolderRepository);
|
||||||
export const Notifications = getCustomRepository(NotificationRepository);
|
export const Notifications = getCustomRepository(NotificationRepository);
|
||||||
|
|||||||
27
src/models/repositories/emoji.ts
Normal file
27
src/models/repositories/emoji.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { EntityRepository, Repository } from 'typeorm';
|
||||||
|
import { Emoji } from '../entities/emoji';
|
||||||
|
import { ensure } from '../../prelude/ensure';
|
||||||
|
|
||||||
|
@EntityRepository(Emoji)
|
||||||
|
export class EmojiRepository extends Repository<Emoji> {
|
||||||
|
public async pack(
|
||||||
|
src: Emoji['id'] | Emoji,
|
||||||
|
) {
|
||||||
|
const emoji = typeof src === 'object' ? src : await this.findOne(src).then(ensure);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: emoji.id,
|
||||||
|
aliases: emoji.aliases,
|
||||||
|
name: emoji.name,
|
||||||
|
category: emoji.category,
|
||||||
|
host: emoji.host,
|
||||||
|
url: emoji.url,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public packMany(
|
||||||
|
emojis: any[],
|
||||||
|
) {
|
||||||
|
return Promise.all(emojis.map(x => this.pack(x)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,7 +39,7 @@ export class NoteRepository extends Repository<Note> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// visibility が followers かつ自分が投稿者のフォロワーでなかったら非表示
|
// visibility が followers かつ自分が投稿者のフォロワーでなかったら非表示
|
||||||
if (packedNote.visibility == 'followers') {
|
if (packedNote.visibility === 'followers') {
|
||||||
if (meId == null) {
|
if (meId == null) {
|
||||||
hide = true;
|
hide = true;
|
||||||
} else if (meId === packedNote.userId) {
|
} else if (meId === packedNote.userId) {
|
||||||
@@ -171,8 +171,8 @@ export class NoteRepository extends Repository<Note> {
|
|||||||
|
|
||||||
let text = note.text;
|
let text = note.text;
|
||||||
|
|
||||||
if (note.name && note.uri) {
|
if (note.name && (note.url || note.uri)) {
|
||||||
text = `【${note.name}】\n${(note.text || '').trim()}\n${note.uri}`;
|
text = `【${note.name}】\n${(note.text || '').trim()}\n\n${note.url || note.uri}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const packed = await awaitAll({
|
const packed = await awaitAll({
|
||||||
@@ -197,6 +197,7 @@ export class NoteRepository extends Repository<Note> {
|
|||||||
renoteId: note.renoteId,
|
renoteId: note.renoteId,
|
||||||
mentions: note.mentions.length > 0 ? note.mentions : undefined,
|
mentions: note.mentions.length > 0 ? note.mentions : undefined,
|
||||||
uri: note.uri || undefined,
|
uri: note.uri || undefined,
|
||||||
|
url: note.url || undefined,
|
||||||
_featuredId_: (note as any)._featuredId_ || undefined,
|
_featuredId_: (note as any)._featuredId_ || undefined,
|
||||||
_prId_: (note as any)._prId_ || undefined,
|
_prId_: (note as any)._prId_ || undefined,
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { IFollow } from '../../type';
|
|||||||
import { Users } from '../../../../models';
|
import { Users } from '../../../../models';
|
||||||
|
|
||||||
export default async (actor: IRemoteUser, activity: IFollow): Promise<void> => {
|
export default async (actor: IRemoteUser, activity: IFollow): Promise<void> => {
|
||||||
const id = typeof activity.actor == 'string' ? activity.actor : activity.actor.id;
|
const id = typeof activity.actor === 'string' ? activity.actor : activity.actor.id;
|
||||||
if (id == null) throw new Error('missing id');
|
if (id == null) throw new Error('missing id');
|
||||||
|
|
||||||
if (!id.startsWith(config.url + '/')) {
|
if (!id.startsWith(config.url + '/')) {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { IFollow } from '../type';
|
|||||||
import { Users } from '../../../models';
|
import { Users } from '../../../models';
|
||||||
|
|
||||||
export default async (actor: IRemoteUser, activity: IFollow): Promise<void> => {
|
export default async (actor: IRemoteUser, activity: IFollow): Promise<void> => {
|
||||||
const id = typeof activity.object == 'string' ? activity.object : activity.object.id;
|
const id = typeof activity.object === 'string' ? activity.object : activity.object.id;
|
||||||
if (id == null) throw new Error('missing id');
|
if (id == null) throw new Error('missing id');
|
||||||
|
|
||||||
if (!id.startsWith(config.url + '/')) {
|
if (!id.startsWith(config.url + '/')) {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { IFollow } from '../../type';
|
|||||||
import { Users } from '../../../../models';
|
import { Users } from '../../../../models';
|
||||||
|
|
||||||
export default async (actor: IRemoteUser, activity: IFollow): Promise<void> => {
|
export default async (actor: IRemoteUser, activity: IFollow): Promise<void> => {
|
||||||
const id = typeof activity.actor == 'string' ? activity.actor : activity.actor.id;
|
const id = typeof activity.actor === 'string' ? activity.actor : activity.actor.id;
|
||||||
if (id == null) throw new Error('missing id');
|
if (id == null) throw new Error('missing id');
|
||||||
|
|
||||||
if (!id.startsWith(config.url + '/')) {
|
if (!id.startsWith(config.url + '/')) {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { Users } from '../../../../models';
|
|||||||
const logger = apLogger;
|
const logger = apLogger;
|
||||||
|
|
||||||
export default async (actor: IRemoteUser, activity: IBlock): Promise<void> => {
|
export default async (actor: IRemoteUser, activity: IBlock): Promise<void> => {
|
||||||
const id = typeof activity.object == 'string' ? activity.object : activity.object.id;
|
const id = typeof activity.object === 'string' ? activity.object : activity.object.id;
|
||||||
if (id == null) throw new Error('missing id');
|
if (id == null) throw new Error('missing id');
|
||||||
|
|
||||||
const uri = activity.id || activity;
|
const uri = activity.id || activity;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { IRemoteUser } from '../../../../models/entities/user';
|
|||||||
import { Users, FollowRequests, Followings } from '../../../../models';
|
import { Users, FollowRequests, Followings } from '../../../../models';
|
||||||
|
|
||||||
export default async (actor: IRemoteUser, activity: IFollow): Promise<void> => {
|
export default async (actor: IRemoteUser, activity: IFollow): Promise<void> => {
|
||||||
const id = typeof activity.object == 'string' ? activity.object : activity.object.id;
|
const id = typeof activity.object === 'string' ? activity.object : activity.object.id;
|
||||||
if (id == null) throw new Error('missing id');
|
if (id == null) throw new Error('missing id');
|
||||||
|
|
||||||
if (!id.startsWith(config.url + '/')) {
|
if (!id.startsWith(config.url + '/')) {
|
||||||
|
|||||||
9
src/remote/activitypub/misc/html-to-mfm.ts
Normal file
9
src/remote/activitypub/misc/html-to-mfm.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { IObject } from '../type';
|
||||||
|
import { extractApHashtagObjects } from '../models/tag';
|
||||||
|
import { fromHtml } from '../../../mfm/fromHtml';
|
||||||
|
|
||||||
|
export function htmlToMfm(html: string, tag?: IObject | IObject[]) {
|
||||||
|
const hashtagNames = extractApHashtagObjects(tag).map(x => x.name).filter((x): x is string => x != null);
|
||||||
|
|
||||||
|
return fromHtml(html, hashtagNames);
|
||||||
|
}
|
||||||
24
src/remote/activitypub/models/mention.ts
Normal file
24
src/remote/activitypub/models/mention.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { toArray, unique } from '../../../prelude/array';
|
||||||
|
import { IObject, isMention, IApMention } from '../type';
|
||||||
|
import { resolvePerson } from './person';
|
||||||
|
import * as promiseLimit from 'promise-limit';
|
||||||
|
import Resolver from '../resolver';
|
||||||
|
import { User } from '../../../models/entities/user';
|
||||||
|
|
||||||
|
export async function extractApMentions(tags: IObject | IObject[] | null | undefined) {
|
||||||
|
const hrefs = unique(extractApMentionObjects(tags).map(x => x.href as string));
|
||||||
|
|
||||||
|
const resolver = new Resolver();
|
||||||
|
|
||||||
|
const limit = promiseLimit<User | null>(2);
|
||||||
|
const mentionedUsers = (await Promise.all(
|
||||||
|
hrefs.map(x => limit(() => resolvePerson(x, resolver).catch(() => null)))
|
||||||
|
)).filter((x): x is User => x != null);
|
||||||
|
|
||||||
|
return mentionedUsers;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function extractApMentionObjects(tags: IObject | IObject[] | null | undefined): IApMention[] {
|
||||||
|
if (tags == null) return [];
|
||||||
|
return toArray(tags).filter(isMention);
|
||||||
|
}
|
||||||
@@ -6,9 +6,9 @@ import post from '../../../services/note/create';
|
|||||||
import { resolvePerson, updatePerson } from './person';
|
import { resolvePerson, updatePerson } from './person';
|
||||||
import { resolveImage } from './image';
|
import { resolveImage } from './image';
|
||||||
import { IRemoteUser } from '../../../models/entities/user';
|
import { IRemoteUser } from '../../../models/entities/user';
|
||||||
import { fromHtml } from '../../../mfm/fromHtml';
|
import { htmlToMfm } from '../misc/html-to-mfm';
|
||||||
import { ITag, extractHashtags } from './tag';
|
import { extractApHashtags } from './tag';
|
||||||
import { unique } from '../../../prelude/array';
|
import { unique, toArray, toSingle } from '../../../prelude/array';
|
||||||
import { extractPollFromQuestion } from './question';
|
import { extractPollFromQuestion } from './question';
|
||||||
import vote from '../../../services/note/polls/vote';
|
import vote from '../../../services/note/polls/vote';
|
||||||
import { apLogger } from '../logger';
|
import { apLogger } from '../logger';
|
||||||
@@ -17,7 +17,7 @@ import { deliverQuestionUpdate } from '../../../services/note/polls/update';
|
|||||||
import { extractDbHost, toPuny } from '../../../misc/convert-host';
|
import { extractDbHost, toPuny } from '../../../misc/convert-host';
|
||||||
import { Notes, Emojis, Polls, MessagingMessages } from '../../../models';
|
import { Notes, Emojis, Polls, MessagingMessages } from '../../../models';
|
||||||
import { Note } from '../../../models/entities/note';
|
import { Note } from '../../../models/entities/note';
|
||||||
import { IObject, getOneApId, getApId, validPost, IPost } from '../type';
|
import { IObject, getOneApId, getApId, validPost, IPost, isEmoji } from '../type';
|
||||||
import { Emoji } from '../../../models/entities/emoji';
|
import { Emoji } from '../../../models/entities/emoji';
|
||||||
import { genId } from '../../../misc/gen-id';
|
import { genId } from '../../../misc/gen-id';
|
||||||
import { fetchMeta } from '../../../misc/fetch-meta';
|
import { fetchMeta } from '../../../misc/fetch-meta';
|
||||||
@@ -25,6 +25,7 @@ import { ensure } from '../../../prelude/ensure';
|
|||||||
import { getApLock } from '../../../misc/app-lock';
|
import { getApLock } from '../../../misc/app-lock';
|
||||||
import { createMessage } from '../../../services/messages/create';
|
import { createMessage } from '../../../services/messages/create';
|
||||||
import { parseAudience } from '../audience';
|
import { parseAudience } from '../audience';
|
||||||
|
import { extractApMentions } from './mention';
|
||||||
|
|
||||||
const logger = apLogger;
|
const logger = apLogger;
|
||||||
|
|
||||||
@@ -113,7 +114,6 @@ export async function createNote(value: string | IObject, resolver?: Resolver, s
|
|||||||
const noteAudience = await parseAudience(actor, note.to, note.cc);
|
const noteAudience = await parseAudience(actor, note.to, note.cc);
|
||||||
let visibility = noteAudience.visibility;
|
let visibility = noteAudience.visibility;
|
||||||
const visibleUsers = noteAudience.visibleUsers;
|
const visibleUsers = noteAudience.visibleUsers;
|
||||||
const apMentions = noteAudience.mentionedUsers;
|
|
||||||
|
|
||||||
// Audience (to, cc) が指定されてなかった場合
|
// Audience (to, cc) が指定されてなかった場合
|
||||||
if (visibility === 'specified' && visibleUsers.length === 0) {
|
if (visibility === 'specified' && visibleUsers.length === 0) {
|
||||||
@@ -125,7 +125,8 @@ export async function createNote(value: string | IObject, resolver?: Resolver, s
|
|||||||
|
|
||||||
let isTalk = note._misskey_talk && visibility === 'specified';
|
let isTalk = note._misskey_talk && visibility === 'specified';
|
||||||
|
|
||||||
const apHashtags = await extractHashtags(note.tag);
|
const apMentions = await extractApMentions(note.tag);
|
||||||
|
const apHashtags = await extractApHashtags(note.tag);
|
||||||
|
|
||||||
// 添付ファイル
|
// 添付ファイル
|
||||||
// TODO: attachmentは必ずしもImageではない
|
// TODO: attachmentは必ずしもImageではない
|
||||||
@@ -210,7 +211,7 @@ export async function createNote(value: string | IObject, resolver?: Resolver, s
|
|||||||
const cw = note.summary === '' ? null : note.summary;
|
const cw = note.summary === '' ? null : note.summary;
|
||||||
|
|
||||||
// テキストのパース
|
// テキストのパース
|
||||||
const text = note._misskey_content || (note.content ? fromHtml(note.content) : null);
|
const text = note._misskey_content || (note.content ? htmlToMfm(note.content, note.tag) : null);
|
||||||
|
|
||||||
// vote
|
// vote
|
||||||
if (reply && reply.hasPoll) {
|
if (reply && reply.hasPoll) {
|
||||||
@@ -280,7 +281,8 @@ export async function createNote(value: string | IObject, resolver?: Resolver, s
|
|||||||
apHashtags,
|
apHashtags,
|
||||||
apEmojis,
|
apEmojis,
|
||||||
poll,
|
poll,
|
||||||
uri: note.id
|
uri: note.id,
|
||||||
|
url: note.url,
|
||||||
}, silent);
|
}, silent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,7 +293,7 @@ export async function createNote(value: string | IObject, resolver?: Resolver, s
|
|||||||
* リモートサーバーからフェッチしてMisskeyに登録しそれを返します。
|
* リモートサーバーからフェッチしてMisskeyに登録しそれを返します。
|
||||||
*/
|
*/
|
||||||
export async function resolveNote(value: string | IObject, resolver?: Resolver): Promise<Note | null> {
|
export async function resolveNote(value: string | IObject, resolver?: Resolver): Promise<Note | null> {
|
||||||
const uri = typeof value == 'string' ? value : value.id;
|
const uri = typeof value === 'string' ? value : value.id;
|
||||||
if (uri == null) throw new Error('missing uri');
|
if (uri == null) throw new Error('missing uri');
|
||||||
|
|
||||||
// ブロックしてたら中断
|
// ブロックしてたら中断
|
||||||
@@ -318,15 +320,16 @@ export async function resolveNote(value: string | IObject, resolver?: Resolver):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function extractEmojis(tags: ITag[], host: string): Promise<Emoji[]> {
|
export async function extractEmojis(tags: IObject | IObject[], host: string): Promise<Emoji[]> {
|
||||||
host = toPuny(host);
|
host = toPuny(host);
|
||||||
|
|
||||||
if (!tags) return [];
|
if (!tags) return [];
|
||||||
|
|
||||||
const eomjiTags = tags.filter(tag => tag.type === 'Emoji' && tag.icon && tag.icon.url && tag.name);
|
const eomjiTags = toArray(tags).filter(isEmoji);
|
||||||
|
|
||||||
return await Promise.all(eomjiTags.map(async tag => {
|
return await Promise.all(eomjiTags.map(async tag => {
|
||||||
const name = tag.name!.replace(/^:/, '').replace(/:$/, '');
|
const name = tag.name!.replace(/^:/, '').replace(/:$/, '');
|
||||||
|
tag.icon = toSingle(tag.icon);
|
||||||
|
|
||||||
const exists = await Emojis.findOne({
|
const exists = await Emojis.findOne({
|
||||||
host,
|
host,
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ import * as promiseLimit from 'promise-limit';
|
|||||||
import config from '../../../config';
|
import config from '../../../config';
|
||||||
import Resolver from '../resolver';
|
import Resolver from '../resolver';
|
||||||
import { resolveImage } from './image';
|
import { resolveImage } from './image';
|
||||||
import { isCollectionOrOrderedCollection, isCollection, IPerson, getApId } from '../type';
|
import { isCollectionOrOrderedCollection, isCollection, IPerson, getApId, IObject, isPropertyValue, IApPropertyValue } from '../type';
|
||||||
import { fromHtml } from '../../../mfm/fromHtml';
|
import { fromHtml } from '../../../mfm/fromHtml';
|
||||||
|
import { htmlToMfm } from '../misc/html-to-mfm';
|
||||||
import { resolveNote, extractEmojis } from './note';
|
import { resolveNote, extractEmojis } from './note';
|
||||||
import { registerOrFetchInstanceDoc } from '../../../services/register-or-fetch-instance-doc';
|
import { registerOrFetchInstanceDoc } from '../../../services/register-or-fetch-instance-doc';
|
||||||
import { ITag, extractHashtags } from './tag';
|
import { extractApHashtags } from './tag';
|
||||||
import { IIdentifier } from './identifier';
|
|
||||||
import { apLogger } from '../logger';
|
import { apLogger } from '../logger';
|
||||||
import { Note } from '../../../models/entities/note';
|
import { Note } from '../../../models/entities/note';
|
||||||
import { updateUsertags } from '../../../services/update-hashtag';
|
import { updateUsertags } from '../../../services/update-hashtag';
|
||||||
@@ -134,9 +134,9 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us
|
|||||||
|
|
||||||
const { fields } = analyzeAttachments(person.attachment || []);
|
const { fields } = analyzeAttachments(person.attachment || []);
|
||||||
|
|
||||||
const tags = extractHashtags(person.tag).map(tag => tag.toLowerCase()).splice(0, 32);
|
const tags = extractApHashtags(person.tag).map(tag => tag.toLowerCase()).splice(0, 32);
|
||||||
|
|
||||||
const isBot = object.type == 'Service';
|
const isBot = object.type === 'Service';
|
||||||
|
|
||||||
// Create user
|
// Create user
|
||||||
let user: IRemoteUser;
|
let user: IRemoteUser;
|
||||||
@@ -165,7 +165,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us
|
|||||||
|
|
||||||
await transactionalEntityManager.save(new UserProfile({
|
await transactionalEntityManager.save(new UserProfile({
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
description: person.summary ? fromHtml(person.summary) : null,
|
description: person.summary ? htmlToMfm(person.summary, person.tag) : null,
|
||||||
url: person.url,
|
url: person.url,
|
||||||
fields,
|
fields,
|
||||||
userHost: host
|
userHost: host
|
||||||
@@ -180,12 +180,21 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
// duplicate key error
|
// duplicate key error
|
||||||
if (isDuplicateKeyValueError(e)) {
|
if (isDuplicateKeyValueError(e)) {
|
||||||
|
// /users/@a => /users/:id のように入力がaliasなときにエラーになることがあるのを対応
|
||||||
|
const u = await Users.findOne({
|
||||||
|
uri: person.id
|
||||||
|
});
|
||||||
|
|
||||||
|
if (u) {
|
||||||
|
user = u as IRemoteUser;
|
||||||
|
} else {
|
||||||
throw new Error('already registered');
|
throw new Error('already registered');
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
logger.error(e);
|
logger.error(e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Register host
|
// Register host
|
||||||
registerOrFetchInstanceDoc(host).then(i => {
|
registerOrFetchInstanceDoc(host).then(i => {
|
||||||
@@ -308,7 +317,7 @@ export async function updatePerson(uri: string, resolver?: Resolver | null, hint
|
|||||||
|
|
||||||
const { fields } = analyzeAttachments(person.attachment || []);
|
const { fields } = analyzeAttachments(person.attachment || []);
|
||||||
|
|
||||||
const tags = extractHashtags(person.tag).map(tag => tag.toLowerCase()).splice(0, 32);
|
const tags = extractApHashtags(person.tag).map(tag => tag.toLowerCase()).splice(0, 32);
|
||||||
|
|
||||||
const updates = {
|
const updates = {
|
||||||
lastFetchedAt: new Date(),
|
lastFetchedAt: new Date(),
|
||||||
@@ -318,7 +327,7 @@ export async function updatePerson(uri: string, resolver?: Resolver | null, hint
|
|||||||
emojis: emojiNames,
|
emojis: emojiNames,
|
||||||
name: person.name,
|
name: person.name,
|
||||||
tags,
|
tags,
|
||||||
isBot: object.type == 'Service',
|
isBot: object.type === 'Service',
|
||||||
isCat: (person as any).isCat === true,
|
isCat: (person as any).isCat === true,
|
||||||
isLocked: !!person.manuallyApprovesFollowers,
|
isLocked: !!person.manuallyApprovesFollowers,
|
||||||
} as Partial<User>;
|
} as Partial<User>;
|
||||||
@@ -346,7 +355,7 @@ export async function updatePerson(uri: string, resolver?: Resolver | null, hint
|
|||||||
await UserProfiles.update({ userId: exist.id }, {
|
await UserProfiles.update({ userId: exist.id }, {
|
||||||
url: person.url,
|
url: person.url,
|
||||||
fields,
|
fields,
|
||||||
description: person.summary ? fromHtml(person.summary) : null,
|
description: person.summary ? htmlToMfm(person.summary, person.tag) : null,
|
||||||
});
|
});
|
||||||
|
|
||||||
// ハッシュタグ更新
|
// ハッシュタグ更新
|
||||||
@@ -384,16 +393,6 @@ export async function resolvePerson(uri: string, resolver?: Resolver): Promise<U
|
|||||||
return await createPerson(uri, resolver);
|
return await createPerson(uri, resolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
const isPropertyValue = (x: {
|
|
||||||
type: string,
|
|
||||||
name?: string,
|
|
||||||
value?: string
|
|
||||||
}) =>
|
|
||||||
x &&
|
|
||||||
x.type === 'PropertyValue' &&
|
|
||||||
typeof x.name === 'string' &&
|
|
||||||
typeof x.value === 'string';
|
|
||||||
|
|
||||||
const services: {
|
const services: {
|
||||||
[x: string]: (id: string, username: string) => any
|
[x: string]: (id: string, username: string) => any
|
||||||
} = {
|
} = {
|
||||||
@@ -409,7 +408,7 @@ const $discord = (id: string, name: string) => {
|
|||||||
return { id, username, discriminator };
|
return { id, username, discriminator };
|
||||||
};
|
};
|
||||||
|
|
||||||
function addService(target: { [x: string]: any }, source: IIdentifier) {
|
function addService(target: { [x: string]: any }, source: IApPropertyValue) {
|
||||||
const service = services[source.name];
|
const service = services[source.name];
|
||||||
|
|
||||||
if (typeof source.value !== 'string')
|
if (typeof source.value !== 'string')
|
||||||
@@ -421,7 +420,7 @@ function addService(target: { [x: string]: any }, source: IIdentifier) {
|
|||||||
target[source.name.split(':')[2]] = service(id, username);
|
target[source.name.split(':')[2]] = service(id, username);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function analyzeAttachments(attachments: ITag[]) {
|
export function analyzeAttachments(attachments: IObject | IObject[] | undefined) {
|
||||||
const fields: {
|
const fields: {
|
||||||
name: string,
|
name: string,
|
||||||
value: string
|
value: string
|
||||||
@@ -430,12 +429,12 @@ export function analyzeAttachments(attachments: ITag[]) {
|
|||||||
|
|
||||||
if (Array.isArray(attachments)) {
|
if (Array.isArray(attachments)) {
|
||||||
for (const attachment of attachments.filter(isPropertyValue)) {
|
for (const attachment of attachments.filter(isPropertyValue)) {
|
||||||
if (isPropertyValue(attachment.identifier!)) {
|
if (isPropertyValue(attachment.identifier)) {
|
||||||
addService(services, attachment.identifier!);
|
addService(services, attachment.identifier);
|
||||||
} else {
|
} else {
|
||||||
fields.push({
|
fields.push({
|
||||||
name: attachment.name!,
|
name: attachment.name,
|
||||||
value: fromHtml(attachment.value!)
|
value: fromHtml(attachment.value)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export async function extractPollFromQuestion(source: string | IObject, resolver
|
|||||||
* @returns true if updated
|
* @returns true if updated
|
||||||
*/
|
*/
|
||||||
export async function updateQuestion(value: any) {
|
export async function updateQuestion(value: any) {
|
||||||
const uri = typeof value == 'string' ? value : value.id;
|
const uri = typeof value === 'string' ? value : value.id;
|
||||||
|
|
||||||
// URIがこのサーバーを指しているならスキップ
|
// URIがこのサーバーを指しているならスキップ
|
||||||
if (uri.startsWith(config.url + '/')) throw new Error('uri points local');
|
if (uri.startsWith(config.url + '/')) throw new Error('uri points local');
|
||||||
|
|||||||
@@ -1,26 +1,18 @@
|
|||||||
import { IIcon } from './icon';
|
import { toArray } from '../../../prelude/array';
|
||||||
import { IIdentifier } from './identifier';
|
import { IObject, isHashtag, IApHashtag } from '../type';
|
||||||
|
|
||||||
/***
|
export function extractApHashtags(tags: IObject | IObject[] | null | undefined) {
|
||||||
* tag (ActivityPub)
|
|
||||||
*/
|
|
||||||
export type ITag = {
|
|
||||||
id: string;
|
|
||||||
type: string;
|
|
||||||
name?: string;
|
|
||||||
value?: string;
|
|
||||||
updated?: Date;
|
|
||||||
icon?: IIcon;
|
|
||||||
identifier?: IIdentifier;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function extractHashtags(tags: ITag[] | null | undefined): string[] {
|
|
||||||
if (tags == null) return [];
|
if (tags == null) return [];
|
||||||
|
|
||||||
const hashtags = tags.filter(tag => tag.type === 'Hashtag' && typeof tag.name == 'string');
|
const hashtags = extractApHashtagObjects(tags);
|
||||||
|
|
||||||
return hashtags.map(tag => {
|
return hashtags.map(tag => {
|
||||||
const m = tag.name ? tag.name.match(/^#(.+)/) : null;
|
const m = tag.name.match(/^#(.+)/);
|
||||||
return m ? m[1] : null;
|
return m ? m[1] : null;
|
||||||
}).filter(x => x != null) as string[];
|
}).filter((x): x is string => x != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function extractApHashtagObjects(tags: IObject | IObject[] | null | undefined): IApHashtag[] {
|
||||||
|
if (tags == null) return [];
|
||||||
|
return toArray(tags).filter(isHashtag);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ export default (object: any, note: Note) => {
|
|||||||
let to: string[] = [];
|
let to: string[] = [];
|
||||||
let cc: string[] = [];
|
let cc: string[] = [];
|
||||||
|
|
||||||
if (note.visibility == 'public') {
|
if (note.visibility === 'public') {
|
||||||
to = ['https://www.w3.org/ns/activitystreams#Public'];
|
to = ['https://www.w3.org/ns/activitystreams#Public'];
|
||||||
cc = [`${attributedTo}/followers`];
|
cc = [`${attributedTo}/followers`];
|
||||||
} else if (note.visibility == 'home') {
|
} else if (note.visibility === 'home') {
|
||||||
to = [`${attributedTo}/followers`];
|
to = [`${attributedTo}/followers`];
|
||||||
cc = ['https://www.w3.org/ns/activitystreams#Public'];
|
cc = ['https://www.w3.org/ns/activitystreams#Public'];
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -4,6 +4,6 @@ import { Users } from '../../../models';
|
|||||||
|
|
||||||
export default (mention: User) => ({
|
export default (mention: User) => ({
|
||||||
type: 'Mention',
|
type: 'Mention',
|
||||||
href: Users.isRemoteUser(mention) ? mention.uri : `${config.url}/@${(mention as ILocalUser).username}`,
|
href: Users.isRemoteUser(mention) ? mention.uri : `${config.url}/users/${(mention as ILocalUser).id}`,
|
||||||
name: Users.isRemoteUser(mention) ? `@${mention.username}@${mention.host}` : `@${(mention as ILocalUser).username}`,
|
name: Users.isRemoteUser(mention) ? `@${mention.username}@${mention.host}` : `@${(mention as ILocalUser).username}`,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -63,13 +63,13 @@ export default async function renderNote(note: Note, dive = true, isTalk = false
|
|||||||
let to: string[] = [];
|
let to: string[] = [];
|
||||||
let cc: string[] = [];
|
let cc: string[] = [];
|
||||||
|
|
||||||
if (note.visibility == 'public') {
|
if (note.visibility === 'public') {
|
||||||
to = ['https://www.w3.org/ns/activitystreams#Public'];
|
to = ['https://www.w3.org/ns/activitystreams#Public'];
|
||||||
cc = [`${attributedTo}/followers`].concat(mentions);
|
cc = [`${attributedTo}/followers`].concat(mentions);
|
||||||
} else if (note.visibility == 'home') {
|
} else if (note.visibility === 'home') {
|
||||||
to = [`${attributedTo}/followers`];
|
to = [`${attributedTo}/followers`];
|
||||||
cc = ['https://www.w3.org/ns/activitystreams#Public'].concat(mentions);
|
cc = ['https://www.w3.org/ns/activitystreams#Public'].concat(mentions);
|
||||||
} else if (note.visibility == 'followers') {
|
} else if (note.visibility === 'followers') {
|
||||||
to = [`${attributedTo}/followers`];
|
to = [`${attributedTo}/followers`];
|
||||||
cc = mentions;
|
cc = mentions;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ export interface IObject {
|
|||||||
icon?: any;
|
icon?: any;
|
||||||
image?: any;
|
image?: any;
|
||||||
url?: string;
|
url?: string;
|
||||||
tag?: any[];
|
href?: string;
|
||||||
|
tag?: IObject | IObject[];
|
||||||
sensitive?: boolean;
|
sensitive?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,6 +131,45 @@ export const isOrderedCollection = (object: IObject): object is IOrderedCollecti
|
|||||||
export const isCollectionOrOrderedCollection = (object: IObject): object is ICollection | IOrderedCollection =>
|
export const isCollectionOrOrderedCollection = (object: IObject): object is ICollection | IOrderedCollection =>
|
||||||
isCollection(object) || isOrderedCollection(object);
|
isCollection(object) || isOrderedCollection(object);
|
||||||
|
|
||||||
|
export interface IApPropertyValue extends IObject {
|
||||||
|
type: 'PropertyValue';
|
||||||
|
identifier: IApPropertyValue;
|
||||||
|
name: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isPropertyValue = (object: IObject): object is IApPropertyValue =>
|
||||||
|
object &&
|
||||||
|
object.type === 'PropertyValue' &&
|
||||||
|
typeof object.name === 'string' &&
|
||||||
|
typeof (object as any).value === 'string';
|
||||||
|
|
||||||
|
export interface IApMention extends IObject {
|
||||||
|
type: 'Mention';
|
||||||
|
href: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isMention = (object: IObject): object is IApMention=>
|
||||||
|
object.type === 'Mention' &&
|
||||||
|
typeof object.href === 'string';
|
||||||
|
|
||||||
|
export interface IApHashtag extends IObject {
|
||||||
|
type: 'Hashtag';
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isHashtag = (object: IObject): object is IApHashtag =>
|
||||||
|
object.type === 'Hashtag' &&
|
||||||
|
typeof object.name === 'string';
|
||||||
|
|
||||||
|
export interface IApEmoji extends IObject {
|
||||||
|
type: 'Emoji';
|
||||||
|
updated: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isEmoji = (object: IObject): object is IApEmoji =>
|
||||||
|
object.type === 'Emoji' && !Array.isArray(object.icon) && object.icon.url != null;
|
||||||
|
|
||||||
export interface ICreate extends IActivity {
|
export interface ICreate extends IActivity {
|
||||||
type: 'Create';
|
type: 'Create';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ export default async (ctx: Router.RouterContext) => {
|
|||||||
* @param note Note
|
* @param note Note
|
||||||
*/
|
*/
|
||||||
export async function packActivity(note: Note): Promise<any> {
|
export async function packActivity(note: Note): Promise<any> {
|
||||||
if (note.renoteId && note.text == null && !note.hasPoll && (note.fileIds == null || note.fileIds.length == 0)) {
|
if (note.renoteId && note.text == null && !note.hasPoll && (note.fileIds == null || note.fileIds.length === 0)) {
|
||||||
const renote = await Notes.findOne(note.renoteId).then(ensure);
|
const renote = await Notes.findOne(note.renoteId).then(ensure);
|
||||||
return renderAnnounce(renote.uri ? renote.uri : `${config.url}/notes/${renote.id}`, note);
|
return renderAnnounce(renote.uri ? renote.uri : `${config.url}/notes/${renote.id}`, note);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ function verifyCertificateChain(certificates: string[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function PEMString(pemBuffer: Buffer, type = 'CERTIFICATE') {
|
function PEMString(pemBuffer: Buffer, type = 'CERTIFICATE') {
|
||||||
if (pemBuffer.length == 65 && pemBuffer[0] == 0x04) {
|
if (pemBuffer.length === 65 && pemBuffer[0] === 0x04) {
|
||||||
pemBuffer = Buffer.concat([PEM_PRELUDE, pemBuffer], 91);
|
pemBuffer = Buffer.concat([PEM_PRELUDE, pemBuffer], 91);
|
||||||
type = 'PUBLIC KEY';
|
type = 'PUBLIC KEY';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export default (endpoint: IEndpoint, ctx: Koa.Context) => new Promise((res) => {
|
|||||||
call(endpoint.name, user, app, body, (ctx as any).file).then((res: any) => {
|
call(endpoint.name, user, app, body, (ctx as any).file).then((res: any) => {
|
||||||
reply(res);
|
reply(res);
|
||||||
}).catch((e: ApiError) => {
|
}).catch((e: ApiError) => {
|
||||||
reply(e.httpStatusCode ? e.httpStatusCode : e.kind == 'client' ? 400 : 500, e);
|
reply(e.httpStatusCode ? e.httpStatusCode : e.kind === 'client' ? 400 : 500, e);
|
||||||
});
|
});
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
reply(403, new ApiError({
|
reply(403, new ApiError({
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ export default async (endpoint: string, user: User | null | undefined, token: Ac
|
|||||||
if (e instanceof ApiError) {
|
if (e instanceof ApiError) {
|
||||||
throw e;
|
throw e;
|
||||||
} else {
|
} else {
|
||||||
apiLogger.error(`Internal error occurred in ${ep.name}`, {
|
apiLogger.error(`Internal error occurred in ${ep.name}: ${e?.message}`, {
|
||||||
ep: ep.name,
|
ep: ep.name,
|
||||||
ps: data,
|
ps: data,
|
||||||
e: {
|
e: {
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
import rndstr from 'rndstr';
|
import { secureRndstr } from '../../../misc/secure-rndstr';
|
||||||
|
|
||||||
export default () => rndstr('a-zA-Z0-9', 16);
|
export default () => secureRndstr(16, true);
|
||||||
|
|||||||
@@ -22,8 +22,6 @@ export async function signup(username: User['username'], password: UserProfile['
|
|||||||
throw new Error('INVALID_PASSWORD');
|
throw new Error('INVALID_PASSWORD');
|
||||||
}
|
}
|
||||||
|
|
||||||
const usersCount = await Users.count({});
|
|
||||||
|
|
||||||
// Generate hash of password
|
// Generate hash of password
|
||||||
const salt = await bcrypt.genSalt(8);
|
const salt = await bcrypt.genSalt(8);
|
||||||
const hash = await bcrypt.hash(password, salt);
|
const hash = await bcrypt.hash(password, salt);
|
||||||
@@ -76,7 +74,9 @@ export async function signup(username: User['username'], password: UserProfile['
|
|||||||
usernameLower: username.toLowerCase(),
|
usernameLower: username.toLowerCase(),
|
||||||
host: toPunyNullable(host),
|
host: toPunyNullable(host),
|
||||||
token: secret,
|
token: secret,
|
||||||
isAdmin: usersCount === 0,
|
isAdmin: (await Users.count({
|
||||||
|
host: null,
|
||||||
|
})) === 0,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
await transactionalEntityManager.save(new UserKeypair({
|
await transactionalEntityManager.save(new UserKeypair({
|
||||||
|
|||||||
@@ -17,8 +17,10 @@ export const meta = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default define(meta, async (ps, me) => {
|
export default define(meta, async (ps, me) => {
|
||||||
const noUsers = (await Users.count({})) === 0;
|
const noUsers = (await Users.count({
|
||||||
if (!noUsers && me == null) throw new Error('access denied');
|
host: null,
|
||||||
|
})) === 0;
|
||||||
|
if (!noUsers && !me?.isAdmin) throw new Error('access denied');
|
||||||
|
|
||||||
const { account, secret } = await signup(ps.username, ps.password);
|
const { account, secret } = await signup(ps.username, ps.password);
|
||||||
|
|
||||||
|
|||||||
@@ -51,8 +51,8 @@ const sort: any = { // < https://github.com/Microsoft/TypeScript/issues/1863
|
|||||||
export default define(meta, async (ps, me) => {
|
export default define(meta, async (ps, me) => {
|
||||||
const q = {} as any;
|
const q = {} as any;
|
||||||
|
|
||||||
if (ps.origin == 'local') q['userHost'] = null;
|
if (ps.origin === 'local') q['userHost'] = null;
|
||||||
if (ps.origin == 'remote') q['userHost'] = { $ne: null };
|
if (ps.origin === 'remote') q['userHost'] = { $ne: null };
|
||||||
|
|
||||||
const files = await DriveFiles.find({
|
const files = await DriveFiles.find({
|
||||||
where: q,
|
where: q,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { insertModerationLog } from '../../../../../services/insert-moderation-l
|
|||||||
import { ApiError } from '../../../error';
|
import { ApiError } from '../../../error';
|
||||||
import { ID } from '../../../../../misc/cafy-id';
|
import { ID } from '../../../../../misc/cafy-id';
|
||||||
import rndstr from 'rndstr';
|
import rndstr from 'rndstr';
|
||||||
|
import { publishBroadcastStream } from '../../../../../services/stream';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
desc: {
|
desc: {
|
||||||
@@ -53,6 +54,10 @@ export default define(meta, async (ps, me) => {
|
|||||||
|
|
||||||
await getConnection().queryResultCache!.remove(['meta_emojis']);
|
await getConnection().queryResultCache!.remove(['meta_emojis']);
|
||||||
|
|
||||||
|
publishBroadcastStream('emojiAdded', {
|
||||||
|
emoji: await Emojis.pack(emoji.id)
|
||||||
|
});
|
||||||
|
|
||||||
insertModerationLog(me, 'addEmoji', {
|
insertModerationLog(me, 'addEmoji', {
|
||||||
emojiId: emoji.id
|
emojiId: emoji.id
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -51,12 +51,5 @@ export default define(meta, async (ps) => {
|
|||||||
.take(ps.limit!)
|
.take(ps.limit!)
|
||||||
.getMany();
|
.getMany();
|
||||||
|
|
||||||
return emojis.map(e => ({
|
return Emojis.packMany(emojis);
|
||||||
id: e.id,
|
|
||||||
name: e.name,
|
|
||||||
category: e.category,
|
|
||||||
aliases: e.aliases,
|
|
||||||
host: e.host,
|
|
||||||
url: e.url
|
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -36,12 +36,5 @@ export default define(meta, async (ps) => {
|
|||||||
.take(ps.limit!)
|
.take(ps.limit!)
|
||||||
.getMany();
|
.getMany();
|
||||||
|
|
||||||
return emojis.map(e => ({
|
return Emojis.packMany(emojis);
|
||||||
id: e.id,
|
|
||||||
name: e.name,
|
|
||||||
category: e.category,
|
|
||||||
aliases: e.aliases,
|
|
||||||
host: e.host,
|
|
||||||
url: e.url
|
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,13 +2,14 @@ import define from '../../define';
|
|||||||
import { getConnection } from 'typeorm';
|
import { getConnection } from 'typeorm';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
requireCredential: false as const,
|
requireCredential: true as const,
|
||||||
|
requireAdmin: true,
|
||||||
|
|
||||||
desc: {
|
desc: {
|
||||||
'en-US': 'Get table stats'
|
'en-US': 'Get table stats'
|
||||||
},
|
},
|
||||||
|
|
||||||
tags: ['meta'],
|
tags: ['admin'],
|
||||||
|
|
||||||
params: {
|
params: {
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import { getNote } from '../../../common/getters';
|
|||||||
import { PromoNotes } from '../../../../../models';
|
import { PromoNotes } from '../../../../../models';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
|
tags: ['admin'],
|
||||||
|
|
||||||
requireCredential: true as const,
|
requireCredential: true as const,
|
||||||
requireModerator: true,
|
requireModerator: true,
|
||||||
|
|
||||||
|
|||||||
@@ -5,12 +5,13 @@ import define from '../../define';
|
|||||||
import redis from '../../../../db/redis';
|
import redis from '../../../../db/redis';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
requireCredential: false as const,
|
requireCredential: true as const,
|
||||||
|
requireAdmin: true,
|
||||||
|
|
||||||
desc: {
|
desc: {
|
||||||
},
|
},
|
||||||
|
|
||||||
tags: ['meta'],
|
tags: ['admin', 'meta'],
|
||||||
|
|
||||||
params: {
|
params: {
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import { Announcements, AnnouncementReads } from '../../../models';
|
|||||||
import { makePaginationQuery } from '../common/make-pagination-query';
|
import { makePaginationQuery } from '../common/make-pagination-query';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
|
tags: ['meta'],
|
||||||
|
|
||||||
requireCredential: false as const,
|
requireCredential: false as const,
|
||||||
|
|
||||||
params: {
|
params: {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { generateMuteQuery } from '../../common/generate-mute-query';
|
|||||||
import { ApiError } from '../../error';
|
import { ApiError } from '../../error';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ['account', 'notes', 'antennas'],
|
tags: ['antennas', 'account', 'notes'],
|
||||||
|
|
||||||
requireCredential: true as const,
|
requireCredential: true as const,
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import rndstr from 'rndstr';
|
|
||||||
import $ from 'cafy';
|
import $ from 'cafy';
|
||||||
import define from '../../define';
|
import define from '../../define';
|
||||||
import { Apps } from '../../../../models';
|
import { Apps } from '../../../../models';
|
||||||
import { genId } from '../../../../misc/gen-id';
|
import { genId } from '../../../../misc/gen-id';
|
||||||
import { unique } from '../../../../prelude/array';
|
import { unique } from '../../../../prelude/array';
|
||||||
|
import { secureRndstr } from '../../../../misc/secure-rndstr';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ['app'],
|
tags: ['app'],
|
||||||
@@ -60,7 +60,7 @@ export const meta = {
|
|||||||
|
|
||||||
export default define(meta, async (ps, user) => {
|
export default define(meta, async (ps, user) => {
|
||||||
// Generate secret
|
// Generate secret
|
||||||
const secret = rndstr('a-zA-Z0-9', 32);
|
const secret = secureRndstr(32, true);
|
||||||
|
|
||||||
// for backward compatibility
|
// for backward compatibility
|
||||||
const permission = unique(ps.permission.map(v => v.replace(/^(.+)(\/|-)(read|write)$/, '$3:$1')));
|
const permission = unique(ps.permission.map(v => v.replace(/^(.+)(\/|-)(read|write)$/, '$3:$1')));
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import rndstr from 'rndstr';
|
|
||||||
import * as crypto from 'crypto';
|
import * as crypto from 'crypto';
|
||||||
import $ from 'cafy';
|
import $ from 'cafy';
|
||||||
import define from '../../define';
|
import define from '../../define';
|
||||||
@@ -6,6 +5,7 @@ import { ApiError } from '../../error';
|
|||||||
import { AuthSessions, AccessTokens, Apps } from '../../../../models';
|
import { AuthSessions, AccessTokens, Apps } from '../../../../models';
|
||||||
import { genId } from '../../../../misc/gen-id';
|
import { genId } from '../../../../misc/gen-id';
|
||||||
import { ensure } from '../../../../prelude/ensure';
|
import { ensure } from '../../../../prelude/ensure';
|
||||||
|
import { secureRndstr } from '../../../../misc/secure-rndstr';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ['auth'],
|
tags: ['auth'],
|
||||||
@@ -39,7 +39,7 @@ export default define(meta, async (ps, user) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate access token
|
// Generate access token
|
||||||
const accessToken = rndstr('a-zA-Z0-9', 32);
|
const accessToken = secureRndstr(32, true);
|
||||||
|
|
||||||
// Fetch exist access token
|
// Fetch exist access token
|
||||||
const exist = await AccessTokens.findOne({
|
const exist = await AccessTokens.findOne({
|
||||||
@@ -60,6 +60,7 @@ export default define(meta, async (ps, user) => {
|
|||||||
await AccessTokens.save({
|
await AccessTokens.save({
|
||||||
id: genId(),
|
id: genId(),
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
|
lastUsedAt: new Date(),
|
||||||
appId: session.appId,
|
appId: session.appId,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
token: accessToken,
|
token: accessToken,
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export const meta = {
|
|||||||
'en-US': 'Block a user.'
|
'en-US': 'Block a user.'
|
||||||
},
|
},
|
||||||
|
|
||||||
tags: ['blocking', 'users'],
|
tags: ['account'],
|
||||||
|
|
||||||
limit: {
|
limit: {
|
||||||
duration: ms('1hour'),
|
duration: ms('1hour'),
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export const meta = {
|
|||||||
'en-US': 'Unblock a user.'
|
'en-US': 'Unblock a user.'
|
||||||
},
|
},
|
||||||
|
|
||||||
tags: ['blocking', 'users'],
|
tags: ['account'],
|
||||||
|
|
||||||
limit: {
|
limit: {
|
||||||
duration: ms('1hour'),
|
duration: ms('1hour'),
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export const meta = {
|
|||||||
'en-US': 'Get blocking users.'
|
'en-US': 'Get blocking users.'
|
||||||
},
|
},
|
||||||
|
|
||||||
tags: ['blocking', 'account'],
|
tags: ['account'],
|
||||||
|
|
||||||
requireCredential: true as const,
|
requireCredential: true as const,
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { Followings } from '../../../../models';
|
|||||||
import { makePaginationQuery } from '../../common/make-pagination-query';
|
import { makePaginationQuery } from '../../common/make-pagination-query';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ['users'],
|
tags: ['federation'],
|
||||||
|
|
||||||
requireCredential: false as const,
|
requireCredential: false as const,
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { Followings } from '../../../../models';
|
|||||||
import { makePaginationQuery } from '../../common/make-pagination-query';
|
import { makePaginationQuery } from '../../common/make-pagination-query';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ['users'],
|
tags: ['federation'],
|
||||||
|
|
||||||
requireCredential: false as const,
|
requireCredential: false as const,
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { Users } from '../../../../models';
|
|||||||
import { makePaginationQuery } from '../../common/make-pagination-query';
|
import { makePaginationQuery } from '../../common/make-pagination-query';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ['users'],
|
tags: ['federation'],
|
||||||
|
|
||||||
requireCredential: false as const,
|
requireCredential: false as const,
|
||||||
|
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ export default define(meta, async (ps, user) => {
|
|||||||
switch (ps.sort) {
|
switch (ps.sort) {
|
||||||
case '+createdAt': query.orderBy('token.createdAt', 'DESC'); break;
|
case '+createdAt': query.orderBy('token.createdAt', 'DESC'); break;
|
||||||
case '-createdAt': query.orderBy('token.createdAt', 'ASC'); break;
|
case '-createdAt': query.orderBy('token.createdAt', 'ASC'); break;
|
||||||
case '+lastUsedAt': query.andWhere('token.lastUsedAt IS NOT NULL').orderBy('token.lastUsedAt', 'DESC'); break;
|
case '+lastUsedAt': query.orderBy('token.lastUsedAt', 'DESC'); break;
|
||||||
case '-lastUsedAt': query.andWhere('token.lastUsedAt IS NOT NULL').orderBy('token.lastUsedAt', 'ASC'); break;
|
case '-lastUsedAt': query.orderBy('token.lastUsedAt', 'ASC'); break;
|
||||||
default: query.orderBy('token.id', 'ASC'); break;
|
default: query.orderBy('token.id', 'ASC'); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -193,14 +193,14 @@ export default define(meta, async (ps, user, token) => {
|
|||||||
if (ps.birthday !== undefined) profileUpdates.birthday = ps.birthday;
|
if (ps.birthday !== undefined) profileUpdates.birthday = ps.birthday;
|
||||||
if (ps.avatarId !== undefined) updates.avatarId = ps.avatarId;
|
if (ps.avatarId !== undefined) updates.avatarId = ps.avatarId;
|
||||||
if (ps.bannerId !== undefined) updates.bannerId = ps.bannerId;
|
if (ps.bannerId !== undefined) updates.bannerId = ps.bannerId;
|
||||||
if (typeof ps.isLocked == 'boolean') updates.isLocked = ps.isLocked;
|
if (typeof ps.isLocked === 'boolean') updates.isLocked = ps.isLocked;
|
||||||
if (typeof ps.isBot == 'boolean') updates.isBot = ps.isBot;
|
if (typeof ps.isBot === 'boolean') updates.isBot = ps.isBot;
|
||||||
if (typeof ps.carefulBot == 'boolean') profileUpdates.carefulBot = ps.carefulBot;
|
if (typeof ps.carefulBot === 'boolean') profileUpdates.carefulBot = ps.carefulBot;
|
||||||
if (typeof ps.autoAcceptFollowed == 'boolean') profileUpdates.autoAcceptFollowed = ps.autoAcceptFollowed;
|
if (typeof ps.autoAcceptFollowed === 'boolean') profileUpdates.autoAcceptFollowed = ps.autoAcceptFollowed;
|
||||||
if (typeof ps.isCat == 'boolean') updates.isCat = ps.isCat;
|
if (typeof ps.isCat === 'boolean') updates.isCat = ps.isCat;
|
||||||
if (typeof ps.autoWatch == 'boolean') profileUpdates.autoWatch = ps.autoWatch;
|
if (typeof ps.autoWatch === 'boolean') profileUpdates.autoWatch = ps.autoWatch;
|
||||||
if (typeof ps.injectFeaturedNote == 'boolean') profileUpdates.injectFeaturedNote = ps.injectFeaturedNote;
|
if (typeof ps.injectFeaturedNote === 'boolean') profileUpdates.injectFeaturedNote = ps.injectFeaturedNote;
|
||||||
if (typeof ps.alwaysMarkNsfw == 'boolean') profileUpdates.alwaysMarkNsfw = ps.alwaysMarkNsfw;
|
if (typeof ps.alwaysMarkNsfw === 'boolean') profileUpdates.alwaysMarkNsfw = ps.alwaysMarkNsfw;
|
||||||
|
|
||||||
if (ps.avatarId) {
|
if (ps.avatarId) {
|
||||||
const avatar = await DriveFiles.findOne(ps.avatarId);
|
const avatar = await DriveFiles.findOne(ps.avatarId);
|
||||||
|
|||||||
@@ -130,14 +130,10 @@ export default define(meta, async (ps, me) => {
|
|||||||
errorImageUrl: instance.errorImageUrl,
|
errorImageUrl: instance.errorImageUrl,
|
||||||
iconUrl: instance.iconUrl,
|
iconUrl: instance.iconUrl,
|
||||||
maxNoteTextLength: Math.min(instance.maxNoteTextLength, DB_MAX_NOTE_TEXT_LENGTH),
|
maxNoteTextLength: Math.min(instance.maxNoteTextLength, DB_MAX_NOTE_TEXT_LENGTH),
|
||||||
emojis: emojis.map(e => ({
|
emojis: await Emojis.packMany(emojis),
|
||||||
id: e.id,
|
requireSetup: (await Users.count({
|
||||||
aliases: e.aliases,
|
host: null,
|
||||||
name: e.name,
|
})) === 0,
|
||||||
category: e.category,
|
|
||||||
url: e.url,
|
|
||||||
})),
|
|
||||||
requireSetup: (await Users.count({})) === 0,
|
|
||||||
enableEmail: instance.enableEmail,
|
enableEmail: instance.enableEmail,
|
||||||
|
|
||||||
enableTwitterIntegration: instance.enableTwitterIntegration,
|
enableTwitterIntegration: instance.enableTwitterIntegration,
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import rndstr from 'rndstr';
|
|
||||||
import $ from 'cafy';
|
import $ from 'cafy';
|
||||||
import define from '../../define';
|
import define from '../../define';
|
||||||
import { AccessTokens } from '../../../../models';
|
import { AccessTokens } from '../../../../models';
|
||||||
import { genId } from '../../../../misc/gen-id';
|
import { genId } from '../../../../misc/gen-id';
|
||||||
|
import { secureRndstr } from '../../../../misc/secure-rndstr';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ['auth'],
|
tags: ['auth'],
|
||||||
@@ -36,7 +36,7 @@ export const meta = {
|
|||||||
|
|
||||||
export default define(meta, async (ps, user) => {
|
export default define(meta, async (ps, user) => {
|
||||||
// Generate access token
|
// Generate access token
|
||||||
const accessToken = rndstr('a-zA-Z0-9', 32);
|
const accessToken = secureRndstr(32, true);
|
||||||
|
|
||||||
// Insert access token doc
|
// Insert access token doc
|
||||||
await AccessTokens.save({
|
await AccessTokens.save({
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export const meta = {
|
|||||||
'en-US': 'Mute a user'
|
'en-US': 'Mute a user'
|
||||||
},
|
},
|
||||||
|
|
||||||
tags: ['mute', 'users'],
|
tags: ['account'],
|
||||||
|
|
||||||
requireCredential: true as const,
|
requireCredential: true as const,
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export const meta = {
|
|||||||
'en-US': 'Unmute a user'
|
'en-US': 'Unmute a user'
|
||||||
},
|
},
|
||||||
|
|
||||||
tags: ['mute', 'users'],
|
tags: ['account'],
|
||||||
|
|
||||||
requireCredential: true as const,
|
requireCredential: true as const,
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export const meta = {
|
|||||||
'en-US': 'Get muted users.'
|
'en-US': 'Get muted users.'
|
||||||
},
|
},
|
||||||
|
|
||||||
tags: ['mute', 'account'],
|
tags: ['account'],
|
||||||
|
|
||||||
requireCredential: true as const,
|
requireCredential: true as const,
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import define from '../../define';
|
|||||||
import { Users, UserProfiles } from '../../../../models';
|
import { Users, UserProfiles } from '../../../../models';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
|
tags: ['room'],
|
||||||
|
|
||||||
requireCredential: true as const,
|
requireCredential: true as const,
|
||||||
|
|
||||||
params: {
|
params: {
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ export function genOpenapiSpec(lang = 'ja-JP') {
|
|||||||
url: `https://github.com/syuilo/misskey/blob/develop/src/server/api/endpoints/${endpoint.name}.ts`
|
url: `https://github.com/syuilo/misskey/blob/develop/src/server/api/endpoints/${endpoint.name}.ts`
|
||||||
},
|
},
|
||||||
...(endpoint.meta.tags ? {
|
...(endpoint.meta.tags ? {
|
||||||
tags: endpoint.meta.tags
|
tags: [endpoint.meta.tags[0]]
|
||||||
} : {}),
|
} : {}),
|
||||||
...(endpoint.meta.requireCredential ? {
|
...(endpoint.meta.requireCredential ? {
|
||||||
security: [{
|
security: [{
|
||||||
|
|||||||
@@ -39,6 +39,10 @@ export default class Connection {
|
|||||||
|
|
||||||
this.wsConnection.on('message', this.onWsConnectionMessage);
|
this.wsConnection.on('message', this.onWsConnectionMessage);
|
||||||
|
|
||||||
|
this.subscriber.on('broadcast', async ({ type, body }) => {
|
||||||
|
this.onBroadcastMessage(type, body);
|
||||||
|
});
|
||||||
|
|
||||||
if (this.user) {
|
if (this.user) {
|
||||||
this.updateFollowing();
|
this.updateFollowing();
|
||||||
this.followingClock = setInterval(this.updateFollowing, 5000);
|
this.followingClock = setInterval(this.updateFollowing, 5000);
|
||||||
@@ -72,6 +76,11 @@ export default class Connection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
private onBroadcastMessage(type: string, body: any) {
|
||||||
|
this.sendMessageToWs(type, body);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* APIリクエスト要求時
|
* APIリクエスト要求時
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ module.exports = (server: http.Server) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
connection.on('message', async (data) => {
|
connection.on('message', async (data) => {
|
||||||
if (data.utf8Data == 'ping') {
|
if (data.utf8Data === 'ping') {
|
||||||
connection.send('pong');
|
connection.send('pong');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ export default () => new Promise(resolve => {
|
|||||||
|
|
||||||
// Bulk write
|
// Bulk write
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
if (queue.length == 0) return;
|
if (queue.length === 0) return;
|
||||||
|
|
||||||
const requests = queue.length;
|
const requests = queue.length;
|
||||||
const time = sum(queue.map(x => x.time));
|
const time = sum(queue.map(x => x.time));
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ type Option = {
|
|||||||
apHashtags?: string[] | null;
|
apHashtags?: string[] | null;
|
||||||
apEmojis?: string[] | null;
|
apEmojis?: string[] | null;
|
||||||
uri?: string | null;
|
uri?: string | null;
|
||||||
|
url?: string | null;
|
||||||
app?: App | null;
|
app?: App | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -407,6 +408,7 @@ async function insertNote(user: User, data: Option, tags: string[], emojis: stri
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (data.uri != null) insert.uri = data.uri;
|
if (data.uri != null) insert.uri = data.uri;
|
||||||
|
if (data.url != null) insert.url = data.url;
|
||||||
|
|
||||||
// Append mentions data
|
// Append mentions data
|
||||||
if (mentionedUsers.length > 0) {
|
if (mentionedUsers.length > 0) {
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export default async function(userId: string, type: string, body?: any) {
|
|||||||
//swLogger.info(err.headers);
|
//swLogger.info(err.headers);
|
||||||
//swLogger.info(err.body);
|
//swLogger.info(err.body);
|
||||||
|
|
||||||
if (err.statusCode == 410) {
|
if (err.statusCode === 410) {
|
||||||
SwSubscriptions.delete({
|
SwSubscriptions.delete({
|
||||||
userId: userId,
|
userId: userId,
|
||||||
endpoint: subscription.endpoint,
|
endpoint: subscription.endpoint,
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ class Publisher {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public publishBroadcastStream = (type: string, value?: any): void => {
|
||||||
|
this.publish('broadcast', type, typeof value === 'undefined' ? null : value);
|
||||||
|
}
|
||||||
|
|
||||||
public publishMainStream = (userId: User['id'], type: string, value?: any): void => {
|
public publishMainStream = (userId: User['id'], type: string, value?: any): void => {
|
||||||
this.publish(`mainStream:${userId}`, type, typeof value === 'undefined' ? null : value);
|
this.publish(`mainStream:${userId}`, type, typeof value === 'undefined' ? null : value);
|
||||||
}
|
}
|
||||||
@@ -75,6 +79,7 @@ const publisher = new Publisher();
|
|||||||
|
|
||||||
export default publisher;
|
export default publisher;
|
||||||
|
|
||||||
|
export const publishBroadcastStream = publisher.publishBroadcastStream;
|
||||||
export const publishMainStream = publisher.publishMainStream;
|
export const publishMainStream = publisher.publishMainStream;
|
||||||
export const publishDriveStream = publisher.publishDriveStream;
|
export const publishDriveStream = publisher.publishDriveStream;
|
||||||
export const publishNoteStream = publisher.publishNoteStream;
|
export const publishNoteStream = publisher.publishNoteStream;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ class WebpackOnBuildPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const isProduction = process.env.NODE_ENV == 'production';
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
|
|
||||||
const locales = require('./locales');
|
const locales = require('./locales');
|
||||||
const meta = require('./package.json');
|
const meta = require('./package.json');
|
||||||
|
|||||||
Reference in New Issue
Block a user