Compare commits
32 Commits
Author | SHA1 | Date | |
---|---|---|---|
7abfcd06da | |||
724f81c7f3 | |||
4d2e98af7b | |||
08221fdda7 | |||
c0f72970b9 | |||
18bc4a49e8 | |||
f90b6dbed4 | |||
d2d991ff34 | |||
190a5e175b | |||
a05f5a91b8 | |||
4d02ff27be | |||
df92b41d25 | |||
075af96338 | |||
64bbc55336 | |||
06c621acc1 | |||
d040dc19bc | |||
a38ae4a402 | |||
772063aade | |||
31c26354c5 | |||
64b331c5b2 | |||
94f8a145ec | |||
b357afa30a | |||
fee0437493 | |||
663b8864c1 | |||
c6eafdde30 | |||
f32ff95256 | |||
0452b75c3e | |||
97efd23ec8 | |||
ea3e311528 | |||
0565454419 | |||
35c79c2f29 | |||
e6089aec48 |
32
CHANGELOG.md
32
CHANGELOG.md
@ -5,17 +5,49 @@ If you encounter any problems with updating, please try the following:
|
|||||||
1. `npm run clean` or `npm run cleanall`
|
1. `npm run clean` or `npm run cleanall`
|
||||||
2. Retry update (Don't forget `npm i`)
|
2. Retry update (Don't forget `npm i`)
|
||||||
|
|
||||||
|
11.1.0 (2019/04/15)
|
||||||
|
-------------------
|
||||||
|
### Improvements
|
||||||
|
* アイコン未設定時にランダムな画像を表示するように
|
||||||
|
* 管理者やモデレーターはレートリミット無効に
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
* メンションの「あなた」インジケーターが表示されない問題を修正
|
||||||
|
* ブロックAPIでエラーが発生する問題を修正
|
||||||
|
* プッシュ通知の購読に失敗する問題を修正
|
||||||
|
|
||||||
|
11.0.3 (2019/04/15)
|
||||||
|
-------------------
|
||||||
|
### Fixes
|
||||||
|
* ハッシュタグ検索APIが動作しない問題を修正
|
||||||
|
* モデレーターなのにアカウントメニューに「管理」が表示されない問題を修正
|
||||||
|
* プッシュ通知の購読に失敗する問題を修正
|
||||||
|
* ユーザー取得APIでユーザーを指定しない場合エラーになる問題を修正
|
||||||
|
|
||||||
|
11.0.2 (2019/04/15)
|
||||||
|
-------------------
|
||||||
|
### Fixes
|
||||||
|
* アプリが作成できない問題を修正
|
||||||
|
* 「ハイライト」が表示されない問題を修正
|
||||||
|
* リモートの投稿に添付されている画像が小さい問題を修正
|
||||||
|
* モバイル版でリストの名前が表示されない問題を修正
|
||||||
|
* APIドキュメントにパーミッション一覧を追加
|
||||||
|
|
||||||
11.0.1 (2019/04/15)
|
11.0.1 (2019/04/15)
|
||||||
-------------------
|
-------------------
|
||||||
|
### Improvements
|
||||||
* 不要な依存関係を削除
|
* 不要な依存関係を削除
|
||||||
|
|
||||||
11.0.0 daybreak (2019/04/14)
|
11.0.0 daybreak (2019/04/14)
|
||||||
----------------------------
|
----------------------------
|
||||||
|
### Improvements
|
||||||
* **データベースがMongoDBからPostgreSQLに変更されました**
|
* **データベースがMongoDBからPostgreSQLに変更されました**
|
||||||
* **Redisが必須に**
|
* **Redisが必須に**
|
||||||
* アカウントを完全に削除できるように
|
* アカウントを完全に削除できるように
|
||||||
* 投稿フォームで添付ファイルの閲覧注意を確認/設定できるように
|
* 投稿フォームで添付ファイルの閲覧注意を確認/設定できるように
|
||||||
* ミュート/ブロック時にそのユーザーの投稿のウォッチをすべて解除するように
|
* ミュート/ブロック時にそのユーザーの投稿のウォッチをすべて解除するように
|
||||||
|
|
||||||
|
### Fixes
|
||||||
* フォロー申請数が実際より1すくなくなる問題を修正
|
* フォロー申請数が実際より1すくなくなる問題を修正
|
||||||
* リストからアカウント削除したユーザーを削除できない問題を修正
|
* リストからアカウント削除したユーザーを削除できない問題を修正
|
||||||
* リストTLでフォローしていないユーザーの非公開投稿が流れる問題を修正
|
* リストTLでフォローしていないユーザーの非公開投稿が流れる問題を修正
|
||||||
|
@ -59,7 +59,7 @@ Organize and store your files! Want to post a picture you have already uploaded?
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
...and more! Experience Misskey with your own eyes at [misskey.xyz](https://misskey.xyz) or join one of the [other instances](https://joinmisskey.github.io/) that are available.
|
...and more! Experience Misskey with your own eyes at [misskey.io](https://misskey.io/) or join one of the [other instances](https://joinmisskey.github.io/) that are available.
|
||||||
|
|
||||||
Screen shots
|
Screen shots
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
@ -103,7 +103,6 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
|
|||||||
<table><tr>
|
<table><tr>
|
||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5888816/36da0f7c15954df0ab13f9abdf227f66/1.jpeg?token-time=2145916800&token-hash=at8QpJXJ8C0zINY_NmoMKv-MhXVoUK-YzTgaJPJzJYU%3D" alt="Hiroshi Seki" width="100"></td>
|
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5888816/36da0f7c15954df0ab13f9abdf227f66/1.jpeg?token-time=2145916800&token-hash=at8QpJXJ8C0zINY_NmoMKv-MhXVoUK-YzTgaJPJzJYU%3D" alt="Hiroshi Seki" width="100"></td>
|
||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12190916/fb7fa7983c14425f890369535b1506a4/3.png?token-time=2145916800&token-hash=oH_i7gJjNT7Ot6j9JiVwy7ZJIBqACVnzLqlz4YrDAZA%3D" alt="weep" width="100"></td>
|
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12190916/fb7fa7983c14425f890369535b1506a4/3.png?token-time=2145916800&token-hash=oH_i7gJjNT7Ot6j9JiVwy7ZJIBqACVnzLqlz4YrDAZA%3D" alt="weep" width="100"></td>
|
||||||
<td><img src="https://c8.patreon.com/2/200/12059069" alt="naga_rus" width="100"></td>
|
|
||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13099460/43cecdbaa63a40d79bf50a96b9910b9d/1.jpe?token-time=2145916800&token-hash=bqwLTk0Wo0hUJJ8J5y7ii05bLzz-_CDA7Bo0Mp4RFU0%3D" alt="ne_moni" width="100"></td>
|
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13099460/43cecdbaa63a40d79bf50a96b9910b9d/1.jpe?token-time=2145916800&token-hash=bqwLTk0Wo0hUJJ8J5y7ii05bLzz-_CDA7Bo0Mp4RFU0%3D" alt="ne_moni" width="100"></td>
|
||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12913507/f7181eacafe8469a93033d85f5969c29/4.jpe?token-time=2145916800&token-hash=zEyJqVM7u9d8Ri-65fJYSJcWF1jBH1nJ5a3taRzrTmw%3D" alt="Melilot" width="100"></td>
|
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12913507/f7181eacafe8469a93033d85f5969c29/4.jpe?token-time=2145916800&token-hash=zEyJqVM7u9d8Ri-65fJYSJcWF1jBH1nJ5a3taRzrTmw%3D" alt="Melilot" width="100"></td>
|
||||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5670915/ee175f0bfb6347ffa4ea101a8c097bff/1.jpg?token-time=2145916800&token-hash=mPLM9CA-riFHx-myr3bLZJuH2xBRHA9se5VbHhLIOuA%3D" alt="osapon" width="100"></td>
|
<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>
|
||||||
@ -111,7 +110,6 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
|
|||||||
</tr><tr>
|
</tr><tr>
|
||||||
<td><a href="https://www.patreon.com/rane_hs">Hiroshi Seki</a></td>
|
<td><a href="https://www.patreon.com/rane_hs">Hiroshi Seki</a></td>
|
||||||
<td><a href="https://www.patreon.com/weepjp">weep</a></td>
|
<td><a href="https://www.patreon.com/weepjp">weep</a></td>
|
||||||
<td><a href="https://www.patreon.com/user?u=12059069">naga_rus</a></td>
|
|
||||||
<td><a href="https://www.patreon.com/user?u=13099460">ne_moni</a></td>
|
<td><a href="https://www.patreon.com/user?u=13099460">ne_moni</a></td>
|
||||||
<td><a href="https://www.patreon.com/user?u=12913507">Melilot</a></td>
|
<td><a href="https://www.patreon.com/user?u=12913507">Melilot</a></td>
|
||||||
<td><a href="https://www.patreon.com/osapon">osapon</a></td>
|
<td><a href="https://www.patreon.com/osapon">osapon</a></td>
|
||||||
@ -165,7 +163,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
|
|||||||
</tr><tr>
|
</tr><tr>
|
||||||
</tr></table>
|
</tr></table>
|
||||||
|
|
||||||
**Last updated:** Sun, 14 Apr 2019 08:13:12 UTC
|
**Last updated:** Mon, 15 Apr 2019 01:59:07 UTC
|
||||||
<!-- PATREON_END -->
|
<!-- PATREON_END -->
|
||||||
|
|
||||||
:four_leaf_clover: Copyright
|
:four_leaf_clover: Copyright
|
||||||
|
@ -69,6 +69,9 @@ common:
|
|||||||
following: "Sledovaní"
|
following: "Sledovaní"
|
||||||
followers: "Sledující"
|
followers: "Sledující"
|
||||||
favorites: "Oblíbené"
|
favorites: "Oblíbené"
|
||||||
|
permissions:
|
||||||
|
'read:drive': "Prohlížet Disk"
|
||||||
|
'write:drive': "Pracovat s Diskem"
|
||||||
empty-timeline-info:
|
empty-timeline-info:
|
||||||
follow-users-to-make-your-timeline: "Poznámky sledujících se zobrazí ve vaší časové ose"
|
follow-users-to-make-your-timeline: "Poznámky sledujících se zobrazí ve vaší časové ose"
|
||||||
explore: "Najít uživatele"
|
explore: "Najít uživatele"
|
||||||
|
@ -20,6 +20,13 @@ common:
|
|||||||
load-more: "Mehr laden"
|
load-more: "Mehr laden"
|
||||||
enter-password: "Bitte Passwort eingeben"
|
enter-password: "Bitte Passwort eingeben"
|
||||||
2fa: "Zwei-Faktor-Authentifizierung"
|
2fa: "Zwei-Faktor-Authentifizierung"
|
||||||
|
customize-home: "Layout Anpassen"
|
||||||
|
featured-notes: "Hervorgehobene Beiträge"
|
||||||
|
dark-mode: "Dunkler Modus"
|
||||||
|
signin: "Einloggen"
|
||||||
|
signup: "Registrieren"
|
||||||
|
signout: "Ausloggen"
|
||||||
|
reload-to-apply-the-setting: "Die Seite muss zum Übernehmen dieser Einstellung aktualisiert werden. Soll die Seite jetzt neu geladen werden?"
|
||||||
got-it: "Verstanden!"
|
got-it: "Verstanden!"
|
||||||
customization-tips:
|
customization-tips:
|
||||||
title: "Anpassung-Tipps"
|
title: "Anpassung-Tipps"
|
||||||
@ -27,6 +34,7 @@ common:
|
|||||||
notification:
|
notification:
|
||||||
file-uploaded: "Datei hochgeladen!"
|
file-uploaded: "Datei hochgeladen!"
|
||||||
message-from: "Nachricht von {}:"
|
message-from: "Nachricht von {}:"
|
||||||
|
reversi-invited: "Zu einem Spiel eingeladen"
|
||||||
reversi-invited-by: "Eingeladen von {}:"
|
reversi-invited-by: "Eingeladen von {}:"
|
||||||
notified-by: "Benachrichtigt von {}:"
|
notified-by: "Benachrichtigt von {}:"
|
||||||
reply-from: "Antwort von {}:"
|
reply-from: "Antwort von {}:"
|
||||||
@ -47,7 +55,20 @@ common:
|
|||||||
drive: "Drive"
|
drive: "Drive"
|
||||||
messaging: "Unterhaltungen"
|
messaging: "Unterhaltungen"
|
||||||
home: "Home"
|
home: "Home"
|
||||||
favorites: "Diese Notiz favorisieren"
|
deck: "Stapel"
|
||||||
|
timeline: "Zeitleiste"
|
||||||
|
explore: "Entdecken"
|
||||||
|
following: "Folgt"
|
||||||
|
followers: "Folgende"
|
||||||
|
favorites: "Diesen Beitrag favorisieren"
|
||||||
|
permissions:
|
||||||
|
'read:account': "Accountinformationen anzeigen."
|
||||||
|
'write:account': "Accountinformationen bearbeiten."
|
||||||
|
'read:drive': "Dateien anzeigen"
|
||||||
|
'write:drive': "Dateien bearbeiten"
|
||||||
|
empty-timeline-info:
|
||||||
|
follow-users-to-make-your-timeline: "Beiträge von Benutzern, denen du folgst, werden in der Zeitleiste angezeigt."
|
||||||
|
explore: "Benutzer finden"
|
||||||
weekday-short:
|
weekday-short:
|
||||||
sunday: "So"
|
sunday: "So"
|
||||||
monday: "Mo"
|
monday: "Mo"
|
||||||
@ -78,11 +99,11 @@ common:
|
|||||||
note-visibility:
|
note-visibility:
|
||||||
public: "Öffentlich"
|
public: "Öffentlich"
|
||||||
home: "Startseite"
|
home: "Startseite"
|
||||||
home-desc: "Nur auf die Startseite posten"
|
home-desc: "Auf die Startseite posten"
|
||||||
followers: "Abonnenten"
|
followers: "Abonnenten"
|
||||||
followers-desc: "Nur für diejenigen sichtbar, die dir folgen"
|
followers-desc: "Nur für diejenigen sichtbar, die dir folgen"
|
||||||
specified: "Direkt"
|
specified: "Direkt"
|
||||||
specified-desc: "Nur für bestimmte Benutzer posten"
|
specified-desc: "Nur für bestimmte Benutzer sichtbar"
|
||||||
local-public: "Öffentlich (nur lokal)"
|
local-public: "Öffentlich (nur lokal)"
|
||||||
local-home: "Home (nur lokal)"
|
local-home: "Home (nur lokal)"
|
||||||
local-followers: "Follower (nur lokal)"
|
local-followers: "Follower (nur lokal)"
|
||||||
@ -92,11 +113,34 @@ common:
|
|||||||
c: "Was geht dir durch den Kopf?"
|
c: "Was geht dir durch den Kopf?"
|
||||||
d: "Willst du etwas sagen?"
|
d: "Willst du etwas sagen?"
|
||||||
e: "Schreib hier etwas!"
|
e: "Schreib hier etwas!"
|
||||||
f: "Warte darauf, das du schreibst."
|
f: "Warte darauf, das du schreibst..."
|
||||||
|
settings: "Einstellungen"
|
||||||
_settings:
|
_settings:
|
||||||
profile: "Dein Profil"
|
profile: "Dein Profil"
|
||||||
notification: "Benachrichtigungen"
|
notification: "Benachrichtigungen"
|
||||||
|
apps: "Anwendungen"
|
||||||
|
tags: "Hashtags"
|
||||||
|
mute-and-block: "Stummschalten/Blocken"
|
||||||
|
blocking: "Blocken"
|
||||||
|
security: "Sicherheit"
|
||||||
|
signin: "Login-Verlauf"
|
||||||
password: "Passwort"
|
password: "Passwort"
|
||||||
|
other: "Mehr"
|
||||||
|
appearance: "Designs"
|
||||||
|
behavior: "Verhalten"
|
||||||
|
fetch-on-scroll: "Unendliches laden beim scrollen"
|
||||||
|
fetch-on-scroll-desc: "Wenn beim scrollen das Ende erreicht wird, lädt die Anwendung automatisch neue Inhalte nach."
|
||||||
|
note-visibility: "Sichtbarkeit von Beiträgen"
|
||||||
|
default-note-visibility: "Die Standardsichtbarkeit"
|
||||||
|
remember-note-visibility: "Erinnerung an Sichtbarkeit von Beiträgen"
|
||||||
|
web-search-engine: "Web-Suchmaschine"
|
||||||
|
web-search-engine-desc: "Beispiel: https://www.google.de/search?q={{query}}"
|
||||||
|
keep-cw: "Inhaltswarnung beibehalten"
|
||||||
|
keep-cw-desc: "Wenn auf einen Beitrag geantwortet wird, wird die Inhaltswarnung des Originalbeitrags übernommen."
|
||||||
|
i-like-sushi: "Ich bevorzuge Sushi anstelle von Pudding"
|
||||||
|
show-reversi-board-labels: "Zeige Reihen- und Spaltenbeschreibungen in Reversi an"
|
||||||
|
use-avatar-reversi-stones: "Avatar als Stein in Reversi anzeigen"
|
||||||
|
disable-animated-mfm: "Animierten Text in Beiträgen deaktivieren"
|
||||||
search: "Suche"
|
search: "Suche"
|
||||||
delete: "Löschen"
|
delete: "Löschen"
|
||||||
loading: "Laden"
|
loading: "Laden"
|
||||||
@ -104,7 +148,7 @@ common:
|
|||||||
update-available: "Eine neue Version von Misskey ist verfügbar ({newer}, aktuell ist {current}). Lade die Seite neu um die aktuelle Version zu laden"
|
update-available: "Eine neue Version von Misskey ist verfügbar ({newer}, aktuell ist {current}). Lade die Seite neu um die aktuelle Version zu laden"
|
||||||
my-token-regenerated: "Dein Token wurde generiert. Du wirst jetzt abgemeldet."
|
my-token-regenerated: "Dein Token wurde generiert. Du wirst jetzt abgemeldet."
|
||||||
verified-user: "Verifizierter Benutzer"
|
verified-user: "Verifizierter Benutzer"
|
||||||
do-not-use-in-production: "Dies ist eine Entwicklungsversion. Nicht in einer Produktionsumgebung verwenden."
|
do-not-use-in-production: "Dies ist eine Entwicklungsversion. Nicht in einer Produktivumgebung verwenden."
|
||||||
error:
|
error:
|
||||||
retry: "Erneut versuchen"
|
retry: "Erneut versuchen"
|
||||||
reversi:
|
reversi:
|
||||||
@ -118,7 +162,7 @@ common:
|
|||||||
analog-clock: "Analoge Uhr"
|
analog-clock: "Analoge Uhr"
|
||||||
profile: "Profil"
|
profile: "Profil"
|
||||||
calendar: "Kalender"
|
calendar: "Kalender"
|
||||||
timemachine: "Kalender (Zeitmaschiene)"
|
timemachine: "Kalender (Zeitmaschine)"
|
||||||
activity: "Aktivitäten"
|
activity: "Aktivitäten"
|
||||||
rss: "RSS Leser"
|
rss: "RSS Leser"
|
||||||
memo: "Notizen"
|
memo: "Notizen"
|
||||||
@ -129,7 +173,7 @@ common:
|
|||||||
notifications: "Benachrichtigungen"
|
notifications: "Benachrichtigungen"
|
||||||
users: "Empfohlene Benutzer"
|
users: "Empfohlene Benutzer"
|
||||||
polls: "Umfrage"
|
polls: "Umfrage"
|
||||||
post-form: "Beitragsform"
|
post-form: "\"Neuer Beitrag\"-Formular"
|
||||||
server: "Server-Info"
|
server: "Server-Info"
|
||||||
nav: "Navigation"
|
nav: "Navigation"
|
||||||
tips: "Tipps"
|
tips: "Tipps"
|
||||||
@ -179,7 +223,7 @@ common/views/components/games/reversi/reversi.room.vue:
|
|||||||
ready: "Bereit"
|
ready: "Bereit"
|
||||||
common/views/components/connect-failed.vue:
|
common/views/components/connect-failed.vue:
|
||||||
title: "Verbindung zum Server ist fehlgeschlagen"
|
title: "Verbindung zum Server ist fehlgeschlagen"
|
||||||
description: "Es gibt entweder ein Problem mit deiner Internetverbindung, der Server ist nicht erreichbar oder wird gerade gewartet. Bitte versuche es später noch einmal."
|
description: "Entweder gibt es ein Problem mit deiner Internetverbindung oder der Server ist zur Zeit nicht erreichbar oder wird gewartet. Bitte versuche es später noch einmal."
|
||||||
thanks: "Vielen Dank für das nutzen von Misskey."
|
thanks: "Vielen Dank für das nutzen von Misskey."
|
||||||
troubleshoot: "Problembehandlung"
|
troubleshoot: "Problembehandlung"
|
||||||
common/views/components/connect-failed.troubleshooter.vue:
|
common/views/components/connect-failed.troubleshooter.vue:
|
||||||
@ -196,8 +240,8 @@ common/views/components/connect-failed.troubleshooter.vue:
|
|||||||
no-internet: "Keine Internetverbindung"
|
no-internet: "Keine Internetverbindung"
|
||||||
no-internet-desc: "Bitte vergewissere dich, dass du mit dem Internet verbunden bist."
|
no-internet-desc: "Bitte vergewissere dich, dass du mit dem Internet verbunden bist."
|
||||||
no-server: "Verbindung mit dem Server nicht möglich"
|
no-server: "Verbindung mit dem Server nicht möglich"
|
||||||
no-server-desc: "Die Internetverbindung scheint in Ordnung zu sein, aber eine Verbindung mit dem Misskey Server konnte nicht hergestellt werden. Möglicherweise ist dieser zur Zeit offline oder in der Wartung, bitte versuche es später noch einmal."
|
no-server-desc: "Die Internetverbindung scheint in Ordnung zu sein, aber eine Verbindung mit dem Misskey-Server konnte nicht hergestellt werden. Möglicherweise ist dieser zur Zeit offline oder wird gewartet. Bitte versuche es später noch einmal."
|
||||||
success: "Erfolgreich mit dem Misskey Server verbunden"
|
success: "Erfolgreich mit dem Misskey-Server verbunden"
|
||||||
success-desc: "Die Verbindung scheint zu funktionieren. Bitte lade die Seite neu."
|
success-desc: "Die Verbindung scheint zu funktionieren. Bitte lade die Seite neu."
|
||||||
flush: "Cache leeren"
|
flush: "Cache leeren"
|
||||||
set-version: "Version angeben"
|
set-version: "Version angeben"
|
||||||
@ -224,6 +268,7 @@ common/views/components/theme.vue:
|
|||||||
invalid-theme: "Thema ist ungültig"
|
invalid-theme: "Thema ist ungültig"
|
||||||
already-installed: "Thema ist bereits installiert"
|
already-installed: "Thema ist bereits installiert"
|
||||||
author: "Autor"
|
author: "Autor"
|
||||||
|
desc: "Beschreibung"
|
||||||
export: "Exportieren"
|
export: "Exportieren"
|
||||||
import: "Importieren"
|
import: "Importieren"
|
||||||
common/views/components/cw-button.vue:
|
common/views/components/cw-button.vue:
|
||||||
@ -251,15 +296,15 @@ common/views/components/nav.vue:
|
|||||||
status: "Status"
|
status: "Status"
|
||||||
wiki: "Wiki"
|
wiki: "Wiki"
|
||||||
donors: "Spender"
|
donors: "Spender"
|
||||||
repository: "Projektarchiv"
|
repository: "Quellcode"
|
||||||
develop: "Entwickler"
|
develop: "Entwickler"
|
||||||
feedback: "Feedback"
|
feedback: "Feedback"
|
||||||
common/views/components/note-menu.vue:
|
common/views/components/note-menu.vue:
|
||||||
favorite: "Diese Notiz favorisieren"
|
favorite: "Diesen Beitrag favorisieren"
|
||||||
unfavorite: "Aus Favoriten entfernen"
|
unfavorite: "Aus Favoriten entfernen"
|
||||||
pin: "An die Profilseite pinnen"
|
pin: "An die Profilseite pinnen"
|
||||||
delete: "Löschen"
|
delete: "Löschen"
|
||||||
delete-confirm: "Diesen Post löschen?"
|
delete-confirm: "Diesen Beitrag löschen?"
|
||||||
remote: "Auf Quelle anzeigen"
|
remote: "Auf Quelle anzeigen"
|
||||||
common/views/components/poll.vue:
|
common/views/components/poll.vue:
|
||||||
vote-to: "Stimme für '{}'"
|
vote-to: "Stimme für '{}'"
|
||||||
@ -296,8 +341,8 @@ common/views/components/signup.vue:
|
|||||||
password: "Passwort"
|
password: "Passwort"
|
||||||
password-placeholder: "Wir empfehlen mindestens 8 Zeichen"
|
password-placeholder: "Wir empfehlen mindestens 8 Zeichen"
|
||||||
weak-password: "Schwaches Passwort"
|
weak-password: "Schwaches Passwort"
|
||||||
normal-password: "Faires Passwort"
|
normal-password: "Normales Passwort"
|
||||||
strong-password: "Schwaches Passwort"
|
strong-password: "Starkes Passwort"
|
||||||
retype: "Wiederholen"
|
retype: "Wiederholen"
|
||||||
retype-placeholder: "Bitte das Passwort erneut eingeben"
|
retype-placeholder: "Bitte das Passwort erneut eingeben"
|
||||||
password-matched: "OK"
|
password-matched: "OK"
|
||||||
@ -319,16 +364,17 @@ common/views/components/uploader.vue:
|
|||||||
common/views/components/visibility-chooser.vue:
|
common/views/components/visibility-chooser.vue:
|
||||||
public: "Öffentlich"
|
public: "Öffentlich"
|
||||||
home: "Home"
|
home: "Home"
|
||||||
home-desc: "Nur auf die Startseite posten"
|
home-desc: "Auf die Startseite posten"
|
||||||
followers: "Folgende"
|
followers: "Folgende"
|
||||||
followers-desc: "Nur für diejenigen sichtbar, die dir folgen"
|
followers-desc: "Nur für diejenigen sichtbar, die dir folgen"
|
||||||
specified: "Direkt"
|
specified: "Direkt"
|
||||||
specified-desc: "Poste nur für bestimmte Benutzer"
|
specified-desc: "Nur für bestimmte Benutzer sichtbar"
|
||||||
local-public: "Öffentlich (nur lokal)"
|
local-public: "Öffentlich (nur lokal)"
|
||||||
local-home: "Home (nur lokal)"
|
local-home: "Home (nur lokal)"
|
||||||
local-followers: "Follower (nur lokal)"
|
local-followers: "Follower (nur lokal)"
|
||||||
common/views/components/profile-editor.vue:
|
common/views/components/profile-editor.vue:
|
||||||
title: "Dein Profil"
|
title: "Dein Profil"
|
||||||
|
name: "Name"
|
||||||
avatar: "Avatar"
|
avatar: "Avatar"
|
||||||
banner: "Banner"
|
banner: "Banner"
|
||||||
save: "Speichern"
|
save: "Speichern"
|
||||||
@ -400,8 +446,8 @@ desktop/views/components/drive.file.vue:
|
|||||||
copied: "Kopieren erfolgreich"
|
copied: "Kopieren erfolgreich"
|
||||||
copied-url-to-clipboard: "URL wurde in die Zwischenablage kopiert"
|
copied-url-to-clipboard: "URL wurde in die Zwischenablage kopiert"
|
||||||
desktop/views/components/drive.folder.vue:
|
desktop/views/components/drive.folder.vue:
|
||||||
unable-to-process: "Der Vorgang konnte nicht beendet werden"
|
unable-to-process: "Der Vorgang konnte nicht abgeschlossen werden"
|
||||||
circular-reference-detected: "Das Zielverzeichnis ist ein Unterverzeichnis des Verzeichnisses welches du verschieben möchtest"
|
circular-reference-detected: "Das Zielverzeichnis ist innerhalb des Verzeichnisses, dass du verschieben möchtest"
|
||||||
unhandled-error: "Unbekannter Fehler"
|
unhandled-error: "Unbekannter Fehler"
|
||||||
contextmenu:
|
contextmenu:
|
||||||
move-to-this-folder: "Verschiebe in diesen Ordner"
|
move-to-this-folder: "Verschiebe in diesen Ordner"
|
||||||
@ -416,7 +462,7 @@ desktop/views/components/drive.vue:
|
|||||||
empty-drive-description: "Du kannst rechts klicken und \"Datei hochladen\" auswählen oder eine Datei per Drag and Drop auf das Fenster ziehen."
|
empty-drive-description: "Du kannst rechts klicken und \"Datei hochladen\" auswählen oder eine Datei per Drag and Drop auf das Fenster ziehen."
|
||||||
empty-folder: "Dieser Ordner ist leer"
|
empty-folder: "Dieser Ordner ist leer"
|
||||||
unable-to-process: "Der Vorgang konnte nicht beendet werden"
|
unable-to-process: "Der Vorgang konnte nicht beendet werden"
|
||||||
circular-reference-detected: "Das Zielverzeichnis ist ein Unterverzeichnis des Verzeichnisses welches du verschieben möchtest"
|
circular-reference-detected: "Das Zielverzeichnis ist innerhalb des Verzeichnisses, dass du verschieben möchtest"
|
||||||
unhandled-error: "Unbekannter Fehler"
|
unhandled-error: "Unbekannter Fehler"
|
||||||
url-upload: "Von einer URL hochladen"
|
url-upload: "Von einer URL hochladen"
|
||||||
url-of-file: "URL der Datei, welche du hochladen möchtest"
|
url-of-file: "URL der Datei, welche du hochladen möchtest"
|
||||||
@ -440,15 +486,15 @@ desktop/views/input-dialog.vue:
|
|||||||
cancel: "Abbrechen"
|
cancel: "Abbrechen"
|
||||||
ok: "OK"
|
ok: "OK"
|
||||||
desktop/views/components/note-detail.vue:
|
desktop/views/components/note-detail.vue:
|
||||||
private: "Dieser Post ist privat"
|
private: "Dieser Beitrag ist privat"
|
||||||
deleted: "Dieser Beitrag wurde entfernt"
|
deleted: "Dieser Beitrag wurde entfernt"
|
||||||
location: "Ort"
|
location: "Ort"
|
||||||
renote: "Anmerkung"
|
renote: "Anmerkung"
|
||||||
add-reaction: "Reaktion hinzufügen"
|
add-reaction: "Reaktion hinzufügen"
|
||||||
desktop/views/components/note.vue:
|
desktop/views/components/note.vue:
|
||||||
reply: "Antworten"
|
reply: "Antworten"
|
||||||
renote: "Anmerkung"
|
renote: "Anmerken"
|
||||||
private: "Dieser Post ist privat"
|
private: "Dieser Beitrag ist privat"
|
||||||
deleted: "Dieser Beitrag wurde entfernt"
|
deleted: "Dieser Beitrag wurde entfernt"
|
||||||
desktop/views/components/notes.vue:
|
desktop/views/components/notes.vue:
|
||||||
error: "Laden fehlgeschlagen."
|
error: "Laden fehlgeschlagen."
|
||||||
@ -460,9 +506,9 @@ desktop/views/components/post-form.vue:
|
|||||||
hide-contents: "Inhalt verstecken"
|
hide-contents: "Inhalt verstecken"
|
||||||
reply-placeholder: "Antworte auf diese Anmerkung..."
|
reply-placeholder: "Antworte auf diese Anmerkung..."
|
||||||
quote-placeholder: "Zitiere diese Anmerkung..."
|
quote-placeholder: "Zitiere diese Anmerkung..."
|
||||||
submit: "Beitragsform"
|
submit: "Abschicken"
|
||||||
reply: "Antworten"
|
reply: "Antworten"
|
||||||
renote: "Anmerkung"
|
renote: "Anmerken"
|
||||||
posted: "Gepostet!"
|
posted: "Gepostet!"
|
||||||
replied: "Geantwortet!"
|
replied: "Geantwortet!"
|
||||||
reposted: "Weitergesagt!"
|
reposted: "Weitergesagt!"
|
||||||
@ -478,7 +524,7 @@ desktop/views/components/post-form.vue:
|
|||||||
error: "Fehler"
|
error: "Fehler"
|
||||||
enter-username: "Bitte gib einen Benutzernamen ein..."
|
enter-username: "Bitte gib einen Benutzernamen ein..."
|
||||||
desktop/views/components/post-form-window.vue:
|
desktop/views/components/post-form-window.vue:
|
||||||
note: "Neue Notiz"
|
note: "Neuer Beitrag"
|
||||||
reply: "Antworten"
|
reply: "Antworten"
|
||||||
attaches: "{} Medien hinzugefügt"
|
attaches: "{} Medien hinzugefügt"
|
||||||
uploading-media: "Lade {} Medien hoch"
|
uploading-media: "Lade {} Medien hoch"
|
||||||
@ -492,7 +538,7 @@ desktop/views/components/renote-form.vue:
|
|||||||
success: "Weitergesagt!"
|
success: "Weitergesagt!"
|
||||||
failure: "Weitersagen fehlgeschlagen"
|
failure: "Weitersagen fehlgeschlagen"
|
||||||
desktop/views/components/renote-form-window.vue:
|
desktop/views/components/renote-form-window.vue:
|
||||||
title: "Bist du dir sicher, dass du das reposten willst?"
|
title: "Bist du dir sicher, dass du das weitersagen willst?"
|
||||||
desktop/views/components/settings.2fa.vue:
|
desktop/views/components/settings.2fa.vue:
|
||||||
url: "https://www.google.de/intl/de/landing/2step/"
|
url: "https://www.google.de/intl/de/landing/2step/"
|
||||||
register: "Ein Gerät registrieren"
|
register: "Ein Gerät registrieren"
|
||||||
@ -504,6 +550,7 @@ desktop/views/components/settings.2fa.vue:
|
|||||||
common/views/components/api-settings.vue:
|
common/views/components/api-settings.vue:
|
||||||
enter-password: "Bitte Passwort eingeben"
|
enter-password: "Bitte Passwort eingeben"
|
||||||
console:
|
console:
|
||||||
|
parameter: "Parameter"
|
||||||
send: "Senden"
|
send: "Senden"
|
||||||
common/views/components/drive-settings.vue:
|
common/views/components/drive-settings.vue:
|
||||||
in-use: "benutzt"
|
in-use: "benutzt"
|
||||||
@ -511,7 +558,7 @@ common/views/components/drive-settings.vue:
|
|||||||
common/views/components/mute-and-block.vue:
|
common/views/components/mute-and-block.vue:
|
||||||
save: "Speichern"
|
save: "Speichern"
|
||||||
desktop/views/components/sub-note-content.vue:
|
desktop/views/components/sub-note-content.vue:
|
||||||
private: "Dieser Post ist privat"
|
private: "Dieser Beitrag ist privat"
|
||||||
deleted: "Dieser Beitrag wurde entfernt"
|
deleted: "Dieser Beitrag wurde entfernt"
|
||||||
poll: "Umfrage"
|
poll: "Umfrage"
|
||||||
desktop/views/components/settings.tags.vue:
|
desktop/views/components/settings.tags.vue:
|
||||||
@ -601,22 +648,22 @@ mobile/views/components/drive.file-detail.vue:
|
|||||||
download: "Download"
|
download: "Download"
|
||||||
rename: "Umbenennen"
|
rename: "Umbenennen"
|
||||||
mobile/views/components/note.vue:
|
mobile/views/components/note.vue:
|
||||||
private: "Dieser Post ist privat"
|
private: "Dieser Beitrag ist privat"
|
||||||
deleted: "Dieser Beitrag wurde entfernt"
|
deleted: "Dieser Beitrag wurde entfernt"
|
||||||
location: "Ort"
|
location: "Ort"
|
||||||
mobile/views/components/note-detail.vue:
|
mobile/views/components/note-detail.vue:
|
||||||
reply: "Antworten"
|
reply: "Antworten"
|
||||||
private: "Dieser Post ist privat"
|
private: "Dieser Beitrag ist privat"
|
||||||
deleted: "Dieser Beitrag wurde entfernt"
|
deleted: "Dieser Beitrag wurde entfernt"
|
||||||
location: "Ort"
|
location: "Ort"
|
||||||
mobile/views/components/notifications.vue:
|
mobile/views/components/notifications.vue:
|
||||||
empty: "Keine Benachrichtigungen"
|
empty: "Keine Benachrichtigungen"
|
||||||
mobile/views/components/post-form.vue:
|
mobile/views/components/post-form.vue:
|
||||||
reply: "Antworten"
|
reply: "Antworten"
|
||||||
renote: "Anmerkung"
|
renote: "Anmerken"
|
||||||
reply-placeholder: "Antworte auf diese Anmerkung..."
|
reply-placeholder: "Antworte auf diese Anmerkung..."
|
||||||
mobile/views/components/sub-note-content.vue:
|
mobile/views/components/sub-note-content.vue:
|
||||||
private: "Dieser Post ist privat"
|
private: "Dieser Beitrag ist privat"
|
||||||
deleted: "Dieser Beitrag wurde entfernt"
|
deleted: "Dieser Beitrag wurde entfernt"
|
||||||
poll: "Umfrage"
|
poll: "Umfrage"
|
||||||
mobile/views/components/ui.nav.vue:
|
mobile/views/components/ui.nav.vue:
|
||||||
@ -633,7 +680,7 @@ mobile/views/pages/home.vue:
|
|||||||
global: "Global"
|
global: "Global"
|
||||||
mobile/views/pages/widgets.vue:
|
mobile/views/pages/widgets.vue:
|
||||||
add-widget: "Hinzufügen"
|
add-widget: "Hinzufügen"
|
||||||
customization-tips: "Anpassung-Tipps"
|
customization-tips: "Anpassungs-Tipps"
|
||||||
mobile/views/pages/widgets/activity.vue:
|
mobile/views/pages/widgets/activity.vue:
|
||||||
activity: "Aktivität"
|
activity: "Aktivität"
|
||||||
mobile/views/pages/note.vue:
|
mobile/views/pages/note.vue:
|
||||||
@ -654,4 +701,50 @@ deck:
|
|||||||
list: "Listen"
|
list: "Listen"
|
||||||
rename: "Umbenennen"
|
rename: "Umbenennen"
|
||||||
deck/deck.user-column.vue:
|
deck/deck.user-column.vue:
|
||||||
|
following: "Folgen"
|
||||||
|
followers: "Folgende"
|
||||||
|
images: "Bilder"
|
||||||
activity: "Aktivität"
|
activity: "Aktivität"
|
||||||
|
timeline: "Zeitleiste"
|
||||||
|
pinned-notes: "Angeheftete Beiträge"
|
||||||
|
docs:
|
||||||
|
edit-this-page-on-github: "Hast Du einen Fehler gefunden oder Lust, diese Dokumentation zu verbessern?"
|
||||||
|
edit-this-page-on-github-link: "Seite auf GitHub bearbeiten!"
|
||||||
|
api:
|
||||||
|
entities:
|
||||||
|
properties: "Eigenschaften"
|
||||||
|
endpoints:
|
||||||
|
params: "Parameter"
|
||||||
|
no-params: "Keine Parameter."
|
||||||
|
res: "Antwort"
|
||||||
|
require-credential: "Dieser Endpunkt erfordert eine Authentifizierung."
|
||||||
|
require-permission: "Dieser Endpunkt erfordert die {permission} Berechtigung."
|
||||||
|
has-limit: "Es gibt eine Ratenbegrenzung."
|
||||||
|
duration-limit: "Es sind maximal {max} Anfragen pro {duration} Millisekunden möglich."
|
||||||
|
min-interval-limit: "Es ist nur eine Anfrage alle {interval} Millisekunden möglich."
|
||||||
|
show-src: "Quellcode anzeigen."
|
||||||
|
show-src-link: "Quellcode auf GitHub anzeigen"
|
||||||
|
generated: "Dieses Dokument wird automatisch anhand der API-Definition generiert."
|
||||||
|
props:
|
||||||
|
name: "Name"
|
||||||
|
type: "Typ"
|
||||||
|
description: "Beschreibung"
|
||||||
|
dev/views/index.vue:
|
||||||
|
manage-apps: "Anwendungen verwalten"
|
||||||
|
dev/views/apps.vue:
|
||||||
|
manage-apps: "Anwendungen verwalten"
|
||||||
|
create-app: "Anwendung erstellen"
|
||||||
|
app-missing: "Keine Anwendungen"
|
||||||
|
dev/views/new-app.vue:
|
||||||
|
create-app: "Erstelle Anwendung"
|
||||||
|
app-name: "Name der Anwendung"
|
||||||
|
app-name-desc: "Der Name der Anwendung"
|
||||||
|
app-name-ex: "z.B. Misskey für iOS"
|
||||||
|
app-overview: "Beschreibung der Anwendung"
|
||||||
|
app-desc: "Eine kurze Beschreibung oder Einführung der Anwendung."
|
||||||
|
app-desc-ex: "z.B. Ein iOS-Client für Misskey."
|
||||||
|
callback-url: "Callback-URL (optional)"
|
||||||
|
callback-url-desc: "Die URL, auf die nach erfolgreicher Authentifizierung umgeleitet werden soll."
|
||||||
|
authority: "Berechtigungen"
|
||||||
|
authority-desc: "Nur die hier eingetragenen Berechtigungen, werden per API zur Verfügung stehen."
|
||||||
|
authority-warning: "Dies kann auch nach dem erstellen der Anwendung geändert werden, allerdings werden dann alle bisher generierten Token ungültig."
|
||||||
|
@ -74,10 +74,26 @@ common:
|
|||||||
favorites: "お気に入り"
|
favorites: "お気に入り"
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
'read:account': "アカウントの情報を見る"
|
"read:account": "アカウントの情報を見る"
|
||||||
'write:account': "アカウントの情報を変更する"
|
"write:account": "アカウントの情報を変更する"
|
||||||
'read:drive': "ドライブを見る"
|
"read:blocks": "ブロックを見る"
|
||||||
'write:drive': "ドライブを操作する"
|
"write:blocks": "ブロックを操作する"
|
||||||
|
"read:drive": "ドライブを見る"
|
||||||
|
"write:drive": "ドライブを操作する"
|
||||||
|
"read:favorites": "お気に入りを見る"
|
||||||
|
"write:favorites": "お気に入りを操作する"
|
||||||
|
"read:following": "フォローの情報を見る"
|
||||||
|
"write:following": "フォロー・フォロー解除する"
|
||||||
|
"read:messaging": "トークを見る"
|
||||||
|
"write:messaging": "トークを操作する"
|
||||||
|
"read:mutes": "ミュートを見る"
|
||||||
|
"write:mutes": "ミュートを操作する"
|
||||||
|
"write:notes": "投稿を作成・削除する"
|
||||||
|
"read:notifications": "通知を見る"
|
||||||
|
"write:notifications": "通知を操作する"
|
||||||
|
"read:reactions": "リアクションを見る"
|
||||||
|
"write:reactions": "リアクションを操作する"
|
||||||
|
"write:votes": "投票する"
|
||||||
|
|
||||||
empty-timeline-info:
|
empty-timeline-info:
|
||||||
follow-users-to-make-your-timeline: "ユーザーをフォローすると投稿がタイムラインに表示されます。"
|
follow-users-to-make-your-timeline: "ユーザーをフォローすると投稿がタイムラインに表示されます。"
|
||||||
@ -1804,14 +1820,17 @@ dev/views/apps.vue:
|
|||||||
app-missing: "アプリなし"
|
app-missing: "アプリなし"
|
||||||
|
|
||||||
dev/views/new-app.vue:
|
dev/views/new-app.vue:
|
||||||
|
new-app: "新しいアプリケーション"
|
||||||
|
new-app-info: "アプリケーションはAPIからでも作成できます。 (app/create)"
|
||||||
create-app: "アプリケーションの作成"
|
create-app: "アプリケーションの作成"
|
||||||
app-name: "アプリケーション名"
|
app-name: "アプリケーション名"
|
||||||
|
app-name-placeholder: "ex) Misskey for iOS"
|
||||||
app-name-desc: "あなたのアプリの名称。"
|
app-name-desc: "あなたのアプリの名称。"
|
||||||
app-name-ex: "ex) Misskey for iOS"
|
|
||||||
app-overview: "アプリの概要"
|
app-overview: "アプリの概要"
|
||||||
app-desc: "あなたのアプリの簡単な説明や紹介。"
|
app-overview-placeholder: " ex) Misskey iOSクライアント。"
|
||||||
app-desc-ex: "ex) Misskey iOSクライアント。"
|
app-overview-desc: "あなたのアプリの簡単な説明や紹介。"
|
||||||
callback-url: "コールバックURL (オプション)"
|
callback-url: "コールバックURL (オプション)"
|
||||||
|
callback-url-placeholder: "ex) https://your.app.example.com/callback.php"
|
||||||
callback-url-desc: "ユーザーが認証フォームで認証した際にリダイレクトするURLを設定できます。"
|
callback-url-desc: "ユーザーが認証フォームで認証した際にリダイレクトするURLを設定できます。"
|
||||||
authority: "権限"
|
authority: "権限"
|
||||||
authority-desc: "ここで要求した機能だけがAPIからアクセスできます。"
|
authority-desc: "ここで要求した機能だけがAPIからアクセスできます。"
|
||||||
|
@ -966,6 +966,7 @@ common/views/components/password-settings.vue:
|
|||||||
changed: "비밀번호를 변경하였습니다"
|
changed: "비밀번호를 변경하였습니다"
|
||||||
failed: "비밀번호 변경을 실패하였습니다."
|
failed: "비밀번호 변경을 실패하였습니다."
|
||||||
common/views/components/post-form-attaches.vue:
|
common/views/components/post-form-attaches.vue:
|
||||||
|
attach-cancel: "첨부 취소"
|
||||||
mark-as-sensitive: "열람주의로 설정"
|
mark-as-sensitive: "열람주의로 설정"
|
||||||
unmark-as-sensitive: "열람주의 해제"
|
unmark-as-sensitive: "열람주의 해제"
|
||||||
desktop/views/components/sub-note-content.vue:
|
desktop/views/components/sub-note-content.vue:
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"author": "syuilo <i@syuilo.com>",
|
"author": "syuilo <i@syuilo.com>",
|
||||||
"version": "11.0.1",
|
"version": "11.1.0",
|
||||||
"codename": "daybreak",
|
"codename": "daybreak",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -75,6 +75,7 @@
|
|||||||
"@types/portscanner": "2.1.0",
|
"@types/portscanner": "2.1.0",
|
||||||
"@types/pug": "2.0.4",
|
"@types/pug": "2.0.4",
|
||||||
"@types/qrcode": "1.3.2",
|
"@types/qrcode": "1.3.2",
|
||||||
|
"@types/random-seed": "0.3.3",
|
||||||
"@types/ratelimiter": "2.1.28",
|
"@types/ratelimiter": "2.1.28",
|
||||||
"@types/redis": "2.8.12",
|
"@types/redis": "2.8.12",
|
||||||
"@types/rename": "1.0.1",
|
"@types/rename": "1.0.1",
|
||||||
@ -103,6 +104,7 @@
|
|||||||
"bootstrap-vue": "2.0.0-rc.13",
|
"bootstrap-vue": "2.0.0-rc.13",
|
||||||
"bull": "3.7.0",
|
"bull": "3.7.0",
|
||||||
"cafy": "15.1.1",
|
"cafy": "15.1.1",
|
||||||
|
"canvas": "2.4.1",
|
||||||
"chai": "4.2.0",
|
"chai": "4.2.0",
|
||||||
"chalk": "2.4.2",
|
"chalk": "2.4.2",
|
||||||
"cli-highlight": "2.1.0",
|
"cli-highlight": "2.1.0",
|
||||||
@ -188,6 +190,7 @@
|
|||||||
"pug": "2.0.3",
|
"pug": "2.0.3",
|
||||||
"punycode": "2.1.1",
|
"punycode": "2.1.1",
|
||||||
"qrcode": "1.3.3",
|
"qrcode": "1.3.3",
|
||||||
|
"random-seed": "0.3.0",
|
||||||
"randomcolor": "0.5.4",
|
"randomcolor": "0.5.4",
|
||||||
"ratelimiter": "3.3.0",
|
"ratelimiter": "3.3.0",
|
||||||
"recaptcha-promise": "0.1.3",
|
"recaptcha-promise": "0.1.3",
|
||||||
|
@ -36,7 +36,9 @@ export default Vue.extend({
|
|||||||
return this.host === localHost ? `@${this.username}` : `@${this.username}@${toUnicode(this.host)}`;
|
return this.host === localHost ? `@${this.username}` : `@${this.username}@${toUnicode(this.host)}`;
|
||||||
},
|
},
|
||||||
isMe(): boolean {
|
isMe(): boolean {
|
||||||
return this.$store.getters.isSignedIn && this.canonical.toLowerCase() === `@${this.$store.state.i.username}@${toUnicode(localHost)}`.toLowerCase();
|
return this.$store.getters.isSignedIn && (
|
||||||
|
`@${this.username}@${toUnicode(this.host)}` === `@${this.$store.state.i.username}@${toUnicode(localHost)}`.toLowerCase()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -118,7 +118,6 @@ export default Vue.extend({
|
|||||||
fileType: image,
|
fileType: image,
|
||||||
excludeNsfw: !this.$store.state.device.alwaysShowNsfw,
|
excludeNsfw: !this.$store.state.device.alwaysShowNsfw,
|
||||||
limit: 9,
|
limit: 9,
|
||||||
untilDate: new Date().getTime() + 1000 * 86400 * 365
|
|
||||||
}).then(notes => {
|
}).then(notes => {
|
||||||
for (const note of notes) {
|
for (const note of notes) {
|
||||||
for (const file of note.files) {
|
for (const file of note.files) {
|
||||||
|
@ -46,7 +46,6 @@ export default Vue.extend({
|
|||||||
fileType: image,
|
fileType: image,
|
||||||
excludeNsfw: !this.$store.state.device.alwaysShowNsfw,
|
excludeNsfw: !this.$store.state.device.alwaysShowNsfw,
|
||||||
limit: 9,
|
limit: 9,
|
||||||
untilDate: new Date().getTime() + 1000 * 86400 * 365
|
|
||||||
}).then(notes => {
|
}).then(notes => {
|
||||||
for (const note of notes) {
|
for (const note of notes) {
|
||||||
for (const file of note.files) {
|
for (const file of note.files) {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<mk-ui>
|
<mk-ui>
|
||||||
<b-card :header="$t('header')">
|
<b-card :header="$t('manage-apps')">
|
||||||
<b-button to="/app/new" variant="primary">{{ $t('create-app') }}</b-button>
|
<b-button to="/app/new" variant="primary">{{ $t('create-app') }}</b-button>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="apps">
|
<div class="apps">
|
||||||
|
@ -1,35 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<mk-ui>
|
<mk-ui>
|
||||||
<b-card :header="$t('header')">
|
<b-card :header="$t('new-app')">
|
||||||
|
<b-alert show variant="info"><fa icon="info-circle"/> {{ $t('new-app-info') }}</b-alert>
|
||||||
<b-form @submit.prevent="onSubmit" autocomplete="off">
|
<b-form @submit.prevent="onSubmit" autocomplete="off">
|
||||||
<b-form-group :label="$t('app-name')" :description="$t('description')">
|
<b-form-group :label="$t('app-name')" :description="$t('app-name-desc')">
|
||||||
<b-form-input v-model="name" type="text" :placeholder="$t('placeholder')" autocomplete="off" required/>
|
<b-form-input v-model="name" type="text" :placeholder="$t('app-name-placeholder')" autocomplete="off" required/>
|
||||||
</b-form-group>
|
</b-form-group>
|
||||||
<b-form-group :label="$t('app-overview')" :description="$t('description')">
|
<b-form-group :label="$t('app-overview')" :description="$t('app-overview-desc')">
|
||||||
<b-textarea v-model="description" :placeholder="$t('placeholder')" autocomplete="off" required></b-textarea>
|
<b-textarea v-model="description" :placeholder="$t('app-overview-placeholder')" autocomplete="off" required></b-textarea>
|
||||||
</b-form-group>
|
</b-form-group>
|
||||||
<b-form-group :label="$t('callback-url')" :description="$t('description')">
|
<b-form-group :label="$t('callback-url')" :description="$t('callback-url-desc')">
|
||||||
<b-input v-model="cb" type="url" placeholder="ex) https://your.app.example.com/callback.php" autocomplete="off"/>
|
<b-input v-model="cb" type="url" :placeholder="$t('callback-url-placeholder')" autocomplete="off"/>
|
||||||
</b-form-group>
|
</b-form-group>
|
||||||
<b-card :header="$t('header')">
|
<b-card :header="$t('authority')">
|
||||||
<b-form-group :description="$t('description')">
|
<b-form-group :description="$t('authority-desc')">
|
||||||
<b-alert show variant="warning"><fa icon="exclamation-triangle"/> {{ $t('authority-warning') }}</b-alert>
|
<b-alert show variant="warning"><fa icon="exclamation-triangle"/> {{ $t('authority-warning') }}</b-alert>
|
||||||
<b-form-checkbox-group v-model="permission" stacked>
|
<b-form-checkbox-group v-model="permission" stacked>
|
||||||
<b-form-checkbox value="read:account">{{ $t('read:account') }}</b-form-checkbox>
|
<b-form-checkbox v-for="v in permissionsList" :value="v" :key="v">{{ $t(`@.permissions.${v}`) }} ({{ v }})</b-form-checkbox>
|
||||||
<b-form-checkbox value="write:account">{{ $t('write:account') }}</b-form-checkbox>
|
|
||||||
<b-form-checkbox value="write:notes">{{ $t('write:notes') }}</b-form-checkbox>
|
|
||||||
<b-form-checkbox value="read:reactions">{{ $t('read:reactions') }}</b-form-checkbox>
|
|
||||||
<b-form-checkbox value="write:reactions">{{ $t('write:reactions') }}</b-form-checkbox>
|
|
||||||
<b-form-checkbox value="read:following">{{ $t('read:following') }}</b-form-checkbox>
|
|
||||||
<b-form-checkbox value="write:following">{{ $t('write:following') }}</b-form-checkbox>
|
|
||||||
<b-form-checkbox value="read:mutes">{{ $t('read:mutes') }}</b-form-checkbox>
|
|
||||||
<b-form-checkbox value="write:mutes">{{ $t('write:mutes') }}</b-form-checkbox>
|
|
||||||
<b-form-checkbox value="read:blocks">{{ $t('read:blocks') }}</b-form-checkbox>
|
|
||||||
<b-form-checkbox value="write:blocks">{{ $t('write:blocks') }}</b-form-checkbox>
|
|
||||||
<b-form-checkbox value="read:drive">{{ $t('read:drive') }}</b-form-checkbox>
|
|
||||||
<b-form-checkbox value="write:drive">{{ $t('write:drive') }}</b-form-checkbox>
|
|
||||||
<b-form-checkbox value="read:notifications">{{ $t('read:notifications') }}</b-form-checkbox>
|
|
||||||
<b-form-checkbox value="write:notifications">{{ $t('write:notifications') }}</b-form-checkbox>
|
|
||||||
</b-form-checkbox-group>
|
</b-form-checkbox-group>
|
||||||
</b-form-group>
|
</b-form-group>
|
||||||
</b-card>
|
</b-card>
|
||||||
@ -43,6 +30,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import i18n from '../../i18n';
|
import i18n from '../../i18n';
|
||||||
|
import { kinds } from '../../../../server/api/kinds';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
i18n: i18n('dev/views/new-app.vue'),
|
i18n: i18n('dev/views/new-app.vue'),
|
||||||
data() {
|
data() {
|
||||||
@ -51,7 +40,8 @@ export default Vue.extend({
|
|||||||
description: '',
|
description: '',
|
||||||
cb: '',
|
cb: '',
|
||||||
nidState: null,
|
nidState: null,
|
||||||
permission: []
|
permission: [],
|
||||||
|
permissionsList: kinds
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
<span :data-active="src == 'messages'" @click="src = 'messages'"><fa :icon="['far', 'envelope']"/> {{ $t('messages') }}<i class="badge" v-if="$store.state.i.hasUnreadSpecifiedNotes"><fa icon="circle"/></i></span>
|
<span :data-active="src == 'messages'" @click="src = 'messages'"><fa :icon="['far', 'envelope']"/> {{ $t('messages') }}<i class="badge" v-if="$store.state.i.hasUnreadSpecifiedNotes"><fa icon="circle"/></i></span>
|
||||||
<template v-if="lists">
|
<template v-if="lists">
|
||||||
<div class="hr" v-if="lists.length > 0"></div>
|
<div class="hr" v-if="lists.length > 0"></div>
|
||||||
<span v-for="l in lists" :data-active="src == 'list' && list == l" @click="src = 'list'; list = l" :key="l.id"><fa icon="list"/> {{ l.title }}</span>
|
<span v-for="l in lists" :data-active="src == 'list' && list == l" @click="src = 'list'; list = l" :key="l.id"><fa icon="list"/> {{ l.name }}</span>
|
||||||
</template>
|
</template>
|
||||||
<div class="hr" v-if="$store.state.settings.tagTimelines && $store.state.settings.tagTimelines.length > 0"></div>
|
<div class="hr" v-if="$store.state.settings.tagTimelines && $store.state.settings.tagTimelines.length > 0"></div>
|
||||||
<span v-for="tl in $store.state.settings.tagTimelines" :data-active="src == 'tag' && tagTl == tl" @click="src = 'tag'; tagTl = tl" :key="tl.id"><fa icon="hashtag"/> {{ tl.title }}</span>
|
<span v-for="tl in $store.state.settings.tagTimelines" :data-active="src == 'tag' && tagTl == tl" @click="src = 'tag'; tagTl = tl" :key="tl.id"><fa icon="hashtag"/> {{ tl.title }}</span>
|
||||||
|
@ -23,7 +23,6 @@ export default Vue.extend({
|
|||||||
mounted() {
|
mounted() {
|
||||||
this.$root.api('users/notes', {
|
this.$root.api('users/notes', {
|
||||||
userId: this.user.id,
|
userId: this.user.id,
|
||||||
untilDate: new Date().getTime() + 1000 * 86400 * 365
|
|
||||||
}).then(notes => {
|
}).then(notes => {
|
||||||
this.notes = notes;
|
this.notes = notes;
|
||||||
this.fetching = false;
|
this.fetching = false;
|
||||||
|
@ -37,7 +37,6 @@ export default Vue.extend({
|
|||||||
fileType: image,
|
fileType: image,
|
||||||
excludeNsfw: !this.$store.state.device.alwaysShowNsfw,
|
excludeNsfw: !this.$store.state.device.alwaysShowNsfw,
|
||||||
limit: 9,
|
limit: 9,
|
||||||
untilDate: new Date().getTime() + 1000 * 86400 * 365
|
|
||||||
}).then(notes => {
|
}).then(notes => {
|
||||||
for (const note of notes) {
|
for (const note of notes) {
|
||||||
for (const file of note.files) {
|
for (const file of note.files) {
|
||||||
|
89
src/misc/gen-avatar.ts
Normal file
89
src/misc/gen-avatar.ts
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
/**
|
||||||
|
* Random avatar generator
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { createCanvas } from 'canvas';
|
||||||
|
import * as gen from 'random-seed';
|
||||||
|
|
||||||
|
const size = 512; // px
|
||||||
|
const n = 5; // resolution
|
||||||
|
const margin = (size / n) / 1.5;
|
||||||
|
const colors = [
|
||||||
|
'#e57373',
|
||||||
|
'#F06292',
|
||||||
|
'#BA68C8',
|
||||||
|
'#9575CD',
|
||||||
|
'#7986CB',
|
||||||
|
'#64B5F6',
|
||||||
|
'#4FC3F7',
|
||||||
|
'#4DD0E1',
|
||||||
|
'#4DB6AC',
|
||||||
|
'#81C784',
|
||||||
|
'#8BC34A',
|
||||||
|
'#AFB42B',
|
||||||
|
'#F57F17',
|
||||||
|
'#FF5722',
|
||||||
|
'#795548',
|
||||||
|
'#455A64',
|
||||||
|
];
|
||||||
|
const bg = '#e9e9e9';
|
||||||
|
|
||||||
|
const actualSize = size - (margin * 2);
|
||||||
|
const cellSize = actualSize / n;
|
||||||
|
const sideN = Math.floor(n / 2);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate buffer of random avatar by seed
|
||||||
|
*/
|
||||||
|
export function genAvatar(seed: string) {
|
||||||
|
const rand = gen.create(seed);
|
||||||
|
const canvas = createCanvas(size, size);
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
ctx.fillStyle = bg;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.fillRect(0, 0, size, size);
|
||||||
|
|
||||||
|
ctx.fillStyle = colors[rand(colors.length)];
|
||||||
|
|
||||||
|
// side bitmap (filled by false)
|
||||||
|
const side: boolean[][] = new Array(sideN);
|
||||||
|
for (let i = 0; i < side.length; i++) {
|
||||||
|
side[i] = new Array(n).fill(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1*n (filled by false)
|
||||||
|
const center: boolean[] = new Array(n).fill(false);
|
||||||
|
|
||||||
|
// tslint:disable-next-line:prefer-for-of
|
||||||
|
for (let x = 0; x < side.length; x++) {
|
||||||
|
for (let y = 0; y < side[x].length; y++) {
|
||||||
|
side[x][y] = rand(3) === 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < center.length; i++) {
|
||||||
|
center[i] = rand(3) === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw
|
||||||
|
for (let x = 0; x < n; x++) {
|
||||||
|
for (let y = 0; y < n; y++) {
|
||||||
|
const isXCenter = x === ((n - 1) / 2);
|
||||||
|
if (isXCenter && !center[y]) continue;
|
||||||
|
|
||||||
|
const isLeftSide = x < ((n - 1) / 2);
|
||||||
|
if (isLeftSide && !side[x][y]) continue;
|
||||||
|
|
||||||
|
const isRightSide = x > ((n - 1) / 2);
|
||||||
|
if (isRightSide && !side[sideN - (x - sideN)][y]) continue;
|
||||||
|
|
||||||
|
const actualX = margin + (cellSize * x);
|
||||||
|
const actualY = margin + (cellSize * y);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.fillRect(actualX, actualY, cellSize, cellSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return canvas.toBuffer();
|
||||||
|
}
|
@ -25,6 +25,7 @@ export class AppRepository extends Repository<App> {
|
|||||||
return {
|
return {
|
||||||
id: app.id,
|
id: app.id,
|
||||||
name: app.name,
|
name: app.name,
|
||||||
|
callbackUrl: app.callbackUrl,
|
||||||
...(opts.includeSecret ? { secret: app.secret } : {}),
|
...(opts.includeSecret ? { secret: app.secret } : {}),
|
||||||
...(me ? {
|
...(me ? {
|
||||||
isAuthorized: await AccessTokens.count({
|
isAuthorized: await AccessTokens.count({
|
||||||
|
@ -14,7 +14,8 @@ export class AuthSessionRepository extends Repository<AuthSession> {
|
|||||||
|
|
||||||
return await rap({
|
return await rap({
|
||||||
id: session.id,
|
id: session.id,
|
||||||
app: Apps.pack(session.appId, me)
|
app: Apps.pack(session.appId, me),
|
||||||
|
token: session.token
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ export class DriveFileRepository extends Repository<DriveFile> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getPublicUrl(file: DriveFile, thumbnail = false): string | null {
|
public getPublicUrl(file: DriveFile, thumbnail = false): string | null {
|
||||||
return thumbnail ? (file.thumbnailUrl || file.webpublicUrl || null) : (file.webpublicUrl || file.thumbnailUrl || file.url);
|
return thumbnail ? (file.thumbnailUrl || file.webpublicUrl || null) : (file.webpublicUrl || file.url);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async clacDriveUsageOf(user: User['id'] | User): Promise<number> {
|
public async clacDriveUsageOf(user: User['id'] | User): Promise<number> {
|
||||||
|
@ -3,6 +3,7 @@ import { User, ILocalUser, IRemoteUser } from '../entities/user';
|
|||||||
import { Emojis, Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles } from '..';
|
import { Emojis, Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles } from '..';
|
||||||
import rap from '@prezzemolo/rap';
|
import rap from '@prezzemolo/rap';
|
||||||
import { ensure } from '../../prelude/ensure';
|
import { ensure } from '../../prelude/ensure';
|
||||||
|
import config from '../../config';
|
||||||
|
|
||||||
@EntityRepository(User)
|
@EntityRepository(User)
|
||||||
export class UserRepository extends Repository<User> {
|
export class UserRepository extends Repository<User> {
|
||||||
@ -88,7 +89,7 @@ export class UserRepository extends Repository<User> {
|
|||||||
name: user.name,
|
name: user.name,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
host: user.host,
|
host: user.host,
|
||||||
avatarUrl: user.avatarUrl,
|
avatarUrl: user.avatarUrl ? user.avatarUrl : config.url + '/avatar/' + user.id,
|
||||||
avatarColor: user.avatarColor,
|
avatarColor: user.avatarColor,
|
||||||
isAdmin: user.isAdmin || undefined,
|
isAdmin: user.isAdmin || undefined,
|
||||||
isBot: user.isBot || undefined,
|
isBot: user.isBot || undefined,
|
||||||
@ -122,6 +123,7 @@ export class UserRepository extends Repository<User> {
|
|||||||
bannerUrl: user.bannerUrl,
|
bannerUrl: user.bannerUrl,
|
||||||
bannerColor: user.bannerColor,
|
bannerColor: user.bannerColor,
|
||||||
isLocked: user.isLocked,
|
isLocked: user.isLocked,
|
||||||
|
isModerator: user.isModerator || undefined,
|
||||||
description: profile!.description,
|
description: profile!.description,
|
||||||
location: profile!.location,
|
location: profile!.location,
|
||||||
birthday: profile!.birthday,
|
birthday: profile!.birthday,
|
||||||
|
@ -115,3 +115,8 @@ export function cumulativeSum(xs: number[]): number[] {
|
|||||||
for (let i = 1; i < ys.length; i++) ys[i] += ys[i - 1];
|
for (let i = 1; i < ys.length; i++) ys[i] += ys[i - 1];
|
||||||
return ys;
|
return ys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Object.fromEntries()
|
||||||
|
export function fromEntries(xs: [string, any][]): { [x: string]: any; } {
|
||||||
|
return xs.reduce((obj, [k, v]) => Object.assign(obj, { [k]: v }), {} as { [x: string]: any; });
|
||||||
|
}
|
||||||
|
@ -59,7 +59,7 @@ export default async (endpoint: string, user: User | null | undefined, app: App
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ep.meta.requireCredential && ep.meta.limit) {
|
if (ep.meta.requireCredential && ep.meta.limit && !user!.isAdmin && !user!.isModerator) {
|
||||||
// Rate limit
|
// Rate limit
|
||||||
await limiter(ep, user!).catch(e => {
|
await limiter(ep, user!).catch(e => {
|
||||||
throw new ApiError({
|
throw new ApiError({
|
||||||
|
@ -3,6 +3,7 @@ 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';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ['app'],
|
tags: ['app'],
|
||||||
@ -34,6 +35,9 @@ export default define(meta, async (ps, user) => {
|
|||||||
// Generate secret
|
// Generate secret
|
||||||
const secret = rndstr('a-zA-Z0-9', 32);
|
const secret = rndstr('a-zA-Z0-9', 32);
|
||||||
|
|
||||||
|
// for backward compatibility
|
||||||
|
const permission = unique(ps.permission.map(v => v.replace(/^(.+)(\/|-)(read|write)$/, '$3:$1')));
|
||||||
|
|
||||||
// Create account
|
// Create account
|
||||||
const app = await Apps.save({
|
const app = await Apps.save({
|
||||||
id: genId(),
|
id: genId(),
|
||||||
@ -41,7 +45,7 @@ export default define(meta, async (ps, user) => {
|
|||||||
userId: user ? user.id : null,
|
userId: user ? user.id : null,
|
||||||
name: ps.name,
|
name: ps.name,
|
||||||
description: ps.description,
|
description: ps.description,
|
||||||
permission: ps.permission,
|
permission,
|
||||||
callbackUrl: ps.callbackUrl,
|
callbackUrl: ps.callbackUrl,
|
||||||
secret: secret
|
secret: secret
|
||||||
});
|
});
|
||||||
|
@ -5,7 +5,7 @@ import create from '../../../../services/blocking/create';
|
|||||||
import define from '../../define';
|
import define from '../../define';
|
||||||
import { ApiError } from '../../error';
|
import { ApiError } from '../../error';
|
||||||
import { getUser } from '../../common/getters';
|
import { getUser } from '../../common/getters';
|
||||||
import { Blockings, NoteWatchings } from '../../../../models';
|
import { Blockings, NoteWatchings, Users } from '../../../../models';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
stability: 'stable',
|
stability: 'stable',
|
||||||
@ -89,5 +89,5 @@ export default define(meta, async (ps, user) => {
|
|||||||
noteUserId: blockee.id
|
noteUserId: blockee.id
|
||||||
});
|
});
|
||||||
|
|
||||||
return await Blockings.pack(blockee.id, user);
|
return await Users.pack(blockee.id, user);
|
||||||
});
|
});
|
||||||
|
@ -5,7 +5,7 @@ import deleteBlocking from '../../../../services/blocking/delete';
|
|||||||
import define from '../../define';
|
import define from '../../define';
|
||||||
import { ApiError } from '../../error';
|
import { ApiError } from '../../error';
|
||||||
import { getUser } from '../../common/getters';
|
import { getUser } from '../../common/getters';
|
||||||
import { Blockings } from '../../../../models';
|
import { Blockings, Users } from '../../../../models';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
stability: 'stable',
|
stability: 'stable',
|
||||||
@ -84,5 +84,5 @@ export default define(meta, async (ps, user) => {
|
|||||||
// Delete blocking
|
// Delete blocking
|
||||||
await deleteBlocking(blocker, blockee);
|
await deleteBlocking(blocker, blockee);
|
||||||
|
|
||||||
return await Blockings.pack(blockee.id, user);
|
return await Users.pack(blockee.id, user);
|
||||||
});
|
});
|
||||||
|
@ -48,6 +48,7 @@ export default define(meta, async (ps) => {
|
|||||||
const hashtags = await Hashtags.createQueryBuilder('tag')
|
const hashtags = await Hashtags.createQueryBuilder('tag')
|
||||||
.where('tag.name like :q', { q: ps.query.toLowerCase() + '%' })
|
.where('tag.name like :q', { q: ps.query.toLowerCase() + '%' })
|
||||||
.orderBy('tag.count', 'DESC')
|
.orderBy('tag.count', 'DESC')
|
||||||
|
.groupBy('tag.id')
|
||||||
.take(ps.limit!)
|
.take(ps.limit!)
|
||||||
.skip(ps.offset)
|
.skip(ps.offset)
|
||||||
.getMany();
|
.getMany();
|
||||||
|
@ -14,7 +14,7 @@ export const meta = {
|
|||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
||||||
kind: 'favorites-read',
|
kind: 'read:favorites',
|
||||||
|
|
||||||
params: {
|
params: {
|
||||||
limit: {
|
limit: {
|
||||||
|
@ -14,7 +14,7 @@ export const meta = {
|
|||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
||||||
kind: 'messaging-read',
|
kind: 'read:messaging',
|
||||||
|
|
||||||
params: {
|
params: {
|
||||||
limit: {
|
limit: {
|
||||||
|
@ -17,7 +17,7 @@ export const meta = {
|
|||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
||||||
kind: 'messaging-read',
|
kind: 'read:messaging',
|
||||||
|
|
||||||
params: {
|
params: {
|
||||||
userId: {
|
userId: {
|
||||||
|
@ -20,7 +20,7 @@ export const meta = {
|
|||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
||||||
kind: 'messaging-write',
|
kind: 'write:messaging',
|
||||||
|
|
||||||
params: {
|
params: {
|
||||||
userId: {
|
userId: {
|
||||||
|
@ -18,7 +18,7 @@ export const meta = {
|
|||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
||||||
kind: 'messaging-write',
|
kind: 'write:messaging',
|
||||||
|
|
||||||
limit: {
|
limit: {
|
||||||
duration: ms('1hour'),
|
duration: ms('1hour'),
|
||||||
|
@ -15,7 +15,7 @@ export const meta = {
|
|||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
||||||
kind: 'messaging-write',
|
kind: 'write:messaging',
|
||||||
|
|
||||||
params: {
|
params: {
|
||||||
messageId: {
|
messageId: {
|
||||||
|
@ -18,7 +18,7 @@ export const meta = {
|
|||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
||||||
kind: 'favorite-write',
|
kind: 'write:favorites',
|
||||||
|
|
||||||
params: {
|
params: {
|
||||||
noteId: {
|
noteId: {
|
||||||
|
@ -17,7 +17,7 @@ export const meta = {
|
|||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
||||||
kind: 'favorite-write',
|
kind: 'write:favorites',
|
||||||
|
|
||||||
params: {
|
params: {
|
||||||
noteId: {
|
noteId: {
|
||||||
|
@ -35,6 +35,7 @@ export default define(meta, async (ps, user) => {
|
|||||||
const day = 1000 * 60 * 60 * 24 * 3; // 3日前まで
|
const day = 1000 * 60 * 60 * 24 * 3; // 3日前まで
|
||||||
|
|
||||||
const query = Notes.createQueryBuilder('note')
|
const query = Notes.createQueryBuilder('note')
|
||||||
|
.addSelect('note.score')
|
||||||
.where('note.userHost IS NULL')
|
.where('note.userHost IS NULL')
|
||||||
.andWhere(`note.createdAt > :date`, { date: new Date(Date.now() - day) })
|
.andWhere(`note.createdAt > :date`, { date: new Date(Date.now() - day) })
|
||||||
.andWhere(`note.visibility = 'public'`)
|
.andWhere(`note.visibility = 'public'`)
|
||||||
|
@ -26,7 +26,7 @@ export const meta = {
|
|||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
||||||
kind: 'vote-write',
|
kind: 'write:votes',
|
||||||
|
|
||||||
params: {
|
params: {
|
||||||
noteId: {
|
noteId: {
|
||||||
|
@ -44,6 +44,7 @@ export default define(meta, async (ps, user) => {
|
|||||||
|
|
||||||
await SwSubscriptions.save({
|
await SwSubscriptions.save({
|
||||||
id: genId(),
|
id: genId(),
|
||||||
|
createdAt: new Date(),
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
endpoint: ps.endpoint,
|
endpoint: ps.endpoint,
|
||||||
auth: ps.auth,
|
auth: ps.auth,
|
||||||
|
@ -65,6 +65,10 @@ export default define(meta, async (ps, me) => {
|
|||||||
let user;
|
let user;
|
||||||
|
|
||||||
if (ps.userIds) {
|
if (ps.userIds) {
|
||||||
|
if (ps.userIds.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
const users = await Users.find({
|
const users = await Users.find({
|
||||||
id: In(ps.userIds)
|
id: In(ps.userIds)
|
||||||
});
|
});
|
||||||
|
22
src/server/api/kinds.ts
Normal file
22
src/server/api/kinds.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
export const kinds = [
|
||||||
|
'read:account',
|
||||||
|
'write:account',
|
||||||
|
'read:blocks',
|
||||||
|
'write:blocks',
|
||||||
|
'read:drive',
|
||||||
|
'write:drive',
|
||||||
|
'read:favorites',
|
||||||
|
'write:favorites',
|
||||||
|
'read:following',
|
||||||
|
'write:following',
|
||||||
|
'read:messaging',
|
||||||
|
'write:messaging',
|
||||||
|
'read:mutes',
|
||||||
|
'write:mutes',
|
||||||
|
'write:notes',
|
||||||
|
'read:notifications',
|
||||||
|
'write:notifications',
|
||||||
|
'read:reactions',
|
||||||
|
'write:reactions',
|
||||||
|
'write:votes'
|
||||||
|
];
|
@ -1,6 +1,49 @@
|
|||||||
import config from '../../../config';
|
import config from '../../../config';
|
||||||
|
import endpoints from '../endpoints';
|
||||||
|
import * as locale from '../../../../locales/';
|
||||||
|
import { fromEntries } from '../../../prelude/array';
|
||||||
|
import { kinds as kindsList } from '../kinds';
|
||||||
|
|
||||||
|
export interface IKindInfo {
|
||||||
|
endpoints: string[];
|
||||||
|
descs: { [x: string]: string; };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function kinds() {
|
||||||
|
const kinds = fromEntries(
|
||||||
|
kindsList
|
||||||
|
.map(k => [k, {
|
||||||
|
endpoints: [],
|
||||||
|
descs: fromEntries(
|
||||||
|
Object.keys(locale)
|
||||||
|
.map(l => [l, locale[l].common.permissions[k] as string] as [string, string])
|
||||||
|
) as { [x: string]: string; }
|
||||||
|
}] as [ string, IKindInfo ])
|
||||||
|
) as { [x: string]: IKindInfo; };
|
||||||
|
|
||||||
|
const errors = [] as string[][];
|
||||||
|
|
||||||
|
for (const endpoint of endpoints.filter(ep => !ep.meta.secure)) {
|
||||||
|
if (endpoint.meta.kind) {
|
||||||
|
const kind = endpoint.meta.kind;
|
||||||
|
if (kind in kinds) kinds[kind].endpoints.push(endpoint.name);
|
||||||
|
else errors.push([kind, endpoint.name]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errors.length > 0) throw Error('\n ' + errors.map((e) => `Unknown kind (permission) "${e[0]}" found at ${e[1]}.`).join('\n '));
|
||||||
|
|
||||||
|
return kinds;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDescription(lang = 'ja-JP'): string {
|
||||||
|
const permissionTable = (Object.entries(kinds()) as [string, IKindInfo][])
|
||||||
|
.map(e => `|${e[0]}|${e[1].descs[lang]}|${e[1].endpoints.map(f => `[${f}](#operation/${f})`).join(', ')}|`)
|
||||||
|
.join('\n');
|
||||||
|
|
||||||
|
const descriptions = {
|
||||||
|
'ja-JP': `**Misskey is a decentralized microblogging platform.**
|
||||||
|
|
||||||
export const description = `
|
|
||||||
## Usage
|
## Usage
|
||||||
**APIはすべてPOSTでリクエスト/レスポンスともにJSON形式です。**
|
**APIはすべてPOSTでリクエスト/レスポンスともにJSON形式です。**
|
||||||
一部のAPIはリクエストに認証情報(APIキー)が必要です。リクエストの際に\`i\`というパラメータでAPIキーを添付してください。
|
一部のAPIはリクエストに認証情報(APIキー)が必要です。リクエストの際に\`i\`というパラメータでAPIキーを添付してください。
|
||||||
@ -44,4 +87,12 @@ APIキーの生成方法を擬似コードで表すと次のようになりま
|
|||||||
\`\`\` js
|
\`\`\` js
|
||||||
const i = sha256(userToken + secretKey);
|
const i = sha256(userToken + secretKey);
|
||||||
\`\`\`
|
\`\`\`
|
||||||
`;
|
|
||||||
|
## Permissions
|
||||||
|
|Permisson (kind)|Description|Endpoints|
|
||||||
|
|:--|:--|:--|
|
||||||
|
${permissionTable}
|
||||||
|
`
|
||||||
|
} as { [x: string]: string };
|
||||||
|
return lang in descriptions ? descriptions[lang] : descriptions['ja-JP'];
|
||||||
|
}
|
||||||
|
@ -3,7 +3,7 @@ import { Context } from 'cafy';
|
|||||||
import config from '../../../config';
|
import config from '../../../config';
|
||||||
import { errors as basicErrors } from './errors';
|
import { errors as basicErrors } from './errors';
|
||||||
import { schemas } from './schemas';
|
import { schemas } from './schemas';
|
||||||
import { description } from './description';
|
import { getDescription } from './description';
|
||||||
import { convertOpenApiSchema } from '../../../misc/schema';
|
import { convertOpenApiSchema } from '../../../misc/schema';
|
||||||
|
|
||||||
export function genOpenapiSpec(lang = 'ja-JP') {
|
export function genOpenapiSpec(lang = 'ja-JP') {
|
||||||
@ -13,7 +13,7 @@ export function genOpenapiSpec(lang = 'ja-JP') {
|
|||||||
info: {
|
info: {
|
||||||
version: 'v1',
|
version: 'v1',
|
||||||
title: 'Misskey API',
|
title: 'Misskey API',
|
||||||
description: '**Misskey is a decentralized microblogging platform.**\n\n' + description,
|
description: getDescription(lang),
|
||||||
'x-logo': { url: '/assets/api-doc.png' }
|
'x-logo': { url: '/assets/api-doc.png' }
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -110,7 +110,10 @@ export function genOpenapiSpec(lang = 'ja-JP') {
|
|||||||
|
|
||||||
let desc = (endpoint.meta.desc ? endpoint.meta.desc[lang] : 'No description provided.') + '\n\n';
|
let desc = (endpoint.meta.desc ? endpoint.meta.desc[lang] : 'No description provided.') + '\n\n';
|
||||||
desc += `**Credential required**: *${endpoint.meta.requireCredential ? 'Yes' : 'No'}*`;
|
desc += `**Credential required**: *${endpoint.meta.requireCredential ? 'Yes' : 'No'}*`;
|
||||||
if (endpoint.meta.kind) desc += ` / **Permission**: *${endpoint.meta.kind}*`;
|
if (endpoint.meta.kind) {
|
||||||
|
const kind = endpoint.meta.kind;
|
||||||
|
desc += ` / **Permission**: *${kind}*`;
|
||||||
|
}
|
||||||
|
|
||||||
const info = {
|
const info = {
|
||||||
operationId: endpoint.name,
|
operationId: endpoint.name,
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 1.2 KiB |
@ -21,12 +21,6 @@ app.use(async (ctx, next) => {
|
|||||||
// Init router
|
// Init router
|
||||||
const router = new Router();
|
const router = new Router();
|
||||||
|
|
||||||
router.get('/default-avatar.jpg', ctx => {
|
|
||||||
const file = fs.createReadStream(`${__dirname}/assets/avatar.jpg`);
|
|
||||||
ctx.set('Content-Type', 'image/jpeg');
|
|
||||||
ctx.body = file;
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/app-default.jpg', ctx => {
|
router.get('/app-default.jpg', ctx => {
|
||||||
const file = fs.createReadStream(`${__dirname}/assets/dummy.png`);
|
const file = fs.createReadStream(`${__dirname}/assets/dummy.png`);
|
||||||
ctx.set('Content-Type', 'image/jpeg');
|
ctx.set('Content-Type', 'image/jpeg');
|
||||||
|
@ -25,6 +25,7 @@ import Logger from '../services/logger';
|
|||||||
import { program } from '../argv';
|
import { program } from '../argv';
|
||||||
import { UserProfiles } from '../models';
|
import { UserProfiles } from '../models';
|
||||||
import { networkChart } from '../services/chart';
|
import { networkChart } from '../services/chart';
|
||||||
|
import { genAvatar } from '../misc/gen-avatar';
|
||||||
|
|
||||||
export const serverLogger = new Logger('server', 'gray', false);
|
export const serverLogger = new Logger('server', 'gray', false);
|
||||||
|
|
||||||
@ -72,6 +73,12 @@ router.use(activityPub.routes());
|
|||||||
router.use(nodeinfo.routes());
|
router.use(nodeinfo.routes());
|
||||||
router.use(wellKnown.routes());
|
router.use(wellKnown.routes());
|
||||||
|
|
||||||
|
router.get('/avatar/:x', ctx => {
|
||||||
|
const avatar = genAvatar(ctx.params.x);
|
||||||
|
ctx.set('Content-Type', 'image/png');
|
||||||
|
ctx.body = avatar;
|
||||||
|
});
|
||||||
|
|
||||||
router.get('/verify-email/:code', async ctx => {
|
router.get('/verify-email/:code', async ctx => {
|
||||||
const profile = await UserProfiles.findOne({
|
const profile = await UserProfiles.findOne({
|
||||||
emailVerifyCode: ctx.params.code
|
emailVerifyCode: ctx.params.code
|
||||||
|
@ -209,7 +209,7 @@ async function deleteOldFile(user: IRemoteUser) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (user.bannerId) {
|
if (user.bannerId) {
|
||||||
q.andWhere('file.id != :bannerId', { bannerId: user.bannerId })
|
q.andWhere('file.id != :bannerId', { bannerId: user.bannerId });
|
||||||
}
|
}
|
||||||
|
|
||||||
q.orderBy('file.id', 'DESC');
|
q.orderBy('file.id', 'DESC');
|
||||||
|
@ -11,15 +11,15 @@ export default (
|
|||||||
noteId: Note['id']
|
noteId: Note['id']
|
||||||
) => new Promise<any>(async (resolve, reject) => {
|
) => new Promise<any>(async (resolve, reject) => {
|
||||||
// Remove document
|
// Remove document
|
||||||
const res = await NoteUnreads.delete({
|
/*const res = */await NoteUnreads.delete({
|
||||||
userId: userId,
|
userId: userId,
|
||||||
noteId: noteId
|
noteId: noteId
|
||||||
});
|
});
|
||||||
|
|
||||||
// v11 TODO: https://github.com/typeorm/typeorm/issues/2415
|
// v11 TODO: https://github.com/typeorm/typeorm/issues/2415
|
||||||
if (res.affected == 0) {
|
//if (res.affected == 0) {
|
||||||
return;
|
// return;
|
||||||
}
|
//}
|
||||||
|
|
||||||
const count1 = await NoteUnreads.count({
|
const count1 = await NoteUnreads.count({
|
||||||
userId: userId,
|
userId: userId,
|
||||||
|
@ -792,7 +792,7 @@ describe('API', () => {
|
|||||||
parentId: folderA.id
|
parentId: folderA.id
|
||||||
}, arisugawa);
|
}, arisugawa);
|
||||||
|
|
||||||
expect(res).have.status(400);
|
assert.strictEqual(res.status, 400);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('存在しない親フォルダを設定できない', async(async () => {
|
it('存在しない親フォルダを設定できない', async(async () => {
|
||||||
|
@ -1141,7 +1141,7 @@ describe('MFM', () => {
|
|||||||
it('exlude emotes', () => {
|
it('exlude emotes', () => {
|
||||||
const tokens = parse('*.*');
|
const tokens = parse('*.*');
|
||||||
assert.deepStrictEqual(tokens, [
|
assert.deepStrictEqual(tokens, [
|
||||||
text("*.*"),
|
text('*.*'),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user