Compare commits
45 Commits
Author | SHA1 | Date | |
---|---|---|---|
80d8af84dd | |||
aac519bf80 | |||
d64dffbdda | |||
4015ccef2f | |||
b6f985abaf | |||
2dac8d3d1f | |||
4f284e1bc0 | |||
b0280355e8 | |||
048d88b784 | |||
bb52ebdc3e | |||
5f2fda85ba | |||
1331f0b953 | |||
736fdabc1d | |||
7096c0ca49 | |||
1a984de8e8 | |||
285d0d13f9 | |||
63c659bc8f | |||
af60b45ee7 | |||
e7effd606d | |||
5042d23bc4 | |||
b134467bd3 | |||
5cc1aab530 | |||
49d57ce049 | |||
f1feceaf1a | |||
1204e1c5e4 | |||
96cf4c30cf | |||
a3853611ad | |||
ef7eaaabfa | |||
a4a96710b0 | |||
6d08909b2f | |||
2615368b1e | |||
2bd03ca725 | |||
67dda01fcb | |||
e9dc54c4d9 | |||
fde9fc2891 | |||
6e59798e82 | |||
5e3cc0a3c6 | |||
cfb35324d0 | |||
9bfbc12afa | |||
b41cddaf5a | |||
1432a7193d | |||
20630cb3a0 | |||
2eb02ae581 | |||
801f9027aa | |||
802739a2df |
@ -62,6 +62,9 @@ db:
|
||||
user: example-misskey-user
|
||||
pass: example-misskey-pass
|
||||
|
||||
# Whether disable Caching queries
|
||||
#disableCache: true
|
||||
|
||||
# Extra Connection options
|
||||
#extra:
|
||||
# ssl: true
|
||||
|
35
CHANGELOG.md
35
CHANGELOG.md
@ -17,6 +17,41 @@ npm i -g ts-node
|
||||
npm run migrate
|
||||
```
|
||||
|
||||
11.22.0 (2019/06/18)
|
||||
--------------------
|
||||
### ✨Improvements
|
||||
* 管理画面でデータベースの各テーブルのレコード数やサイズを確認できるように
|
||||
* サーバー情報にPostgreSQLのバージョンを追加
|
||||
|
||||
### 🐛Fixes
|
||||
* リモートファイルのダウンロードに失敗することがある問題を修正
|
||||
* アンケートの期間を日時指定で選択すると日時がUTCになってしまう問題を修正
|
||||
* MFMのパースを修正
|
||||
|
||||
11.21.0 (2019/06/16)
|
||||
--------------------
|
||||
### ✨Improvements
|
||||
* Unicode 12.0 の絵文字に対応
|
||||
* 閉鎖しているホストにはAP deliverしないように
|
||||
* image以外はproxyしないように
|
||||
|
||||
### 🐛Fixes
|
||||
* サムネイル生成でエラーになるとファイルのアップロードに失敗する問題を修正
|
||||
* オートコンプリートにアニメーション停止が効かない問題を修正
|
||||
|
||||
11.20.4 (2019/06/13)
|
||||
--------------------
|
||||
### 🐛Fixes
|
||||
* 検索結果がループする問題を修正
|
||||
* 設定でPostgreSQLのクエリー結果のキャッシュを無効できるように
|
||||
* 「投稿内の動きのあるテキストを無効にする」だけ反応しない問題を修正
|
||||
* 特定の操作のデータベースのパフォーマンス調整
|
||||
|
||||
11.20.3 (2019/06/10)
|
||||
--------------------
|
||||
### 🐛Fixes
|
||||
* 絵文字サジェストが動作しなくなっていた問題を修正
|
||||
|
||||
11.20.2 (2019/06/10)
|
||||
--------------------
|
||||
### 🐛Fixes
|
||||
|
@ -124,7 +124,6 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
|
||||
<table><tr>
|
||||
<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/12021162/963128bb8d14476dbd8407943db8f31a/1.png?token-time=2145916800&token-hash=FMV7cPKBD1TU2WTbl1jg6AcdKSvTb2BSFcDhgc-EO8w%3D" alt="gutfuckllc" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/11357794/923ce94cd8c44ba788ee931907881839/1.png?token-time=2145916800&token-hash=9nEQje_eMvUjq9a7L3uBqW-MQbS-rRMaMgd7UYVoFNM%3D" alt="mydarkstar" width="100"></td>
|
||||
<td><img src="https://c8.patreon.com/2/200/12718187" alt="Peter G." width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13039004/509d0c412eb14ae08d6a812a3054f7d6/1.jpe?token-time=2145916800&token-hash=UQRWf01TwHDV4Cls1K0YAOAjM29ssif7hLVq0ESQ0hs%3D" alt="nemu" width="100"></td>
|
||||
@ -135,7 +134,6 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
|
||||
</tr><tr>
|
||||
<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/gutfuckllc">gutfuckllc</a></td>
|
||||
<td><a href="https://www.patreon.com/mydarkstar">mydarkstar</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=12718187">Peter G.</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=13039004">nemu</a></td>
|
||||
@ -179,7 +177,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
|
||||
<td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td>
|
||||
</tr></table>
|
||||
|
||||
**Last updated:** Tue, 04 Jun 2019 04:21:06 UTC
|
||||
**Last updated:** Tue, 11 Jun 2019 01:46:06 UTC
|
||||
<!-- PATREON_END -->
|
||||
|
||||
:four_leaf_clover: Copyright
|
||||
|
@ -94,9 +94,20 @@ common:
|
||||
follow-users-to-make-your-timeline: "Poznámky sledujících se zobrazí ve vaší časové ose"
|
||||
explore: "Najít uživatele"
|
||||
post-form:
|
||||
attach-location-information: "Přidat informace o lokaci"
|
||||
hide-contents: "Schovat obsah"
|
||||
reply-placeholder: "Odpovědět na tento příspěvek"
|
||||
quote-placeholder: "Citovat tento příspěvek"
|
||||
submit: "Odeslat"
|
||||
reply: "Odpovědět"
|
||||
renote: "Renotovat"
|
||||
attach-media-from-local: "Uplodovat soubor z vašeho zařízení"
|
||||
insert-a-kao: "v('ω')v"
|
||||
create-poll: "Vytvořit anketu"
|
||||
text-remain: "zbývá ještě {} znaků"
|
||||
recent-tags: "Nejnovější"
|
||||
visibility: "Viditelnost"
|
||||
geolocation-alert: "Vaše zařízení nedalo k dispozici lokaci"
|
||||
error: "Chyba"
|
||||
enter-username: "Zadejte své uživatelské jméno"
|
||||
add-visible-user: "Přidat uživatele"
|
||||
@ -316,7 +327,10 @@ auth/views/form.vue:
|
||||
accept: "Povolit přístup"
|
||||
auth/views/index.vue:
|
||||
loading: "Načítám..."
|
||||
denied-paragraph: "Tato aplikace nebude mít přístup k Vašemu účtu."
|
||||
already-authorized: "Tato aplikace byla již autorizována."
|
||||
callback-url: "Zpátky do aplikace."
|
||||
please-go-back: "Prosím vraťte se zpátky do aplikace."
|
||||
error: "Taková relace neexistuje."
|
||||
sign-in: "Prosím přihlaste se."
|
||||
common/views/pages/explore.vue:
|
||||
@ -366,20 +380,25 @@ common/views/components/games/reversi/reversi.room.vue:
|
||||
waiting-for-both: "Připravuji"
|
||||
cancel: "Zrušit"
|
||||
ready: "Připraveno"
|
||||
cancel-ready: "Pokračovat v přípravě"
|
||||
common/views/components/connect-failed.vue:
|
||||
title: "Nelze se připojit k serveru"
|
||||
description: "Nastal problém s Vaším připojením k internetu, nebo server není dostupný nebo zrovna probíhá údržba. Prosím {zkuste to znova} za pár minut."
|
||||
thanks: "Děkujeme že jste použili Misskey."
|
||||
common/views/components/connect-failed.troubleshooter.vue:
|
||||
title: "Poradce při potížích"
|
||||
network: "Síťové připojení"
|
||||
checking-network: "Prověřit síťové připojení"
|
||||
internet: "Připojení k internetu"
|
||||
checking-internet: "Ověřuji připojení k internetu."
|
||||
server: "Připojení k serveru"
|
||||
checking-server: "Spojuji se se serverem"
|
||||
no-network-desc: "Ujistěte se že jste připojeni k Internetu."
|
||||
no-internet: "Nejste připojeni k internetu"
|
||||
no-internet-desc: "Jste připojen k síti, ale zdá se že stále chybí připojení k Internetu. Prosím zkontrolujte Vaše připojení k Internetu."
|
||||
no-server: "Nelze se připojit k serveru Misskey"
|
||||
success: "Úspěšně se podařilo spojit s Misskey serverem"
|
||||
flush: "Vyčistit mezipaměť"
|
||||
common/views/components/media-banner.vue:
|
||||
sensitive: "Choulostivý obsah"
|
||||
click-to-show: "Klikněte pro zobrazení"
|
||||
@ -403,8 +422,10 @@ common/views/components/theme.vue:
|
||||
find-more-theme: "Najít další vzhledy"
|
||||
theme-name: "Jméno vzhledu"
|
||||
preview-created-theme: "Náhled"
|
||||
invalid-theme: "Vzhled není validní"
|
||||
already-installed: "Tento vzhled je již nainstalován."
|
||||
saved: "Uloženo"
|
||||
manage-themes: "Správa vzhledů"
|
||||
builtin-themes: "Standardní vzhledy"
|
||||
my-themes: "Moje vzhledy"
|
||||
installed-themes: "Nainstalované vzhledy"
|
||||
@ -415,6 +436,7 @@ common/views/components/theme.vue:
|
||||
desc: "Popis"
|
||||
export: "Exportovat"
|
||||
import: "Importovat"
|
||||
import-by-code: "nebo zkopírujte kód"
|
||||
theme-name-required: "Jméno vzhledu je povinné"
|
||||
common/views/components/cw-button.vue:
|
||||
hide: "Skrýt"
|
||||
@ -487,10 +509,13 @@ common/views/components/poll-editor.vue:
|
||||
remove: "Odstranit tuto možnost"
|
||||
add: "+ Přidat možnost"
|
||||
destroy: "Zahodit dotazník"
|
||||
multiple: "Více odpovědí je povoleno"
|
||||
expiration: "Termín"
|
||||
infinite: "Nekonečne"
|
||||
at: "Výběr data a času"
|
||||
no-more: "Více už přidat nemůžete"
|
||||
deadline-date: "Termín ukončení"
|
||||
deadline-time: "Doba trvání"
|
||||
interval: "Trvání"
|
||||
second: "Sekunda"
|
||||
minute: "Minuta"
|
||||
@ -498,6 +523,7 @@ common/views/components/poll-editor.vue:
|
||||
day: "Ne"
|
||||
common/views/components/reaction-picker.vue:
|
||||
choose-reaction: "Vyberte svoji reakci"
|
||||
input-reaction-placeholder: "nebo vložte Emoji"
|
||||
common/views/components/emoji-picker.vue:
|
||||
custom-emoji: "Emoji"
|
||||
people: "Lidé"
|
||||
@ -511,6 +537,7 @@ common/views/components/emoji-picker.vue:
|
||||
common/views/components/signin.vue:
|
||||
username: "Přezdívka"
|
||||
password: "Heslo"
|
||||
token: "Token"
|
||||
signing-in: "Přihlašování..."
|
||||
or: "Nebo"
|
||||
signin-with-twitter: "Přihlásit se pomocí účtu Twitter"
|
||||
@ -538,6 +565,7 @@ common/views/components/signup.vue:
|
||||
password-matched: "OK"
|
||||
password-not-matched: "Neshodují se"
|
||||
recaptcha: "Potvrzení"
|
||||
tos: "Podmínky užívání"
|
||||
create: "Vytvořit účet"
|
||||
some-error: "Pokus o vytvoření účtu selhal. Prosím zkuste to znovu."
|
||||
common/views/components/special-message.vue:
|
||||
@ -576,6 +604,7 @@ common/views/components/visibility-chooser.vue:
|
||||
home: "Domů"
|
||||
specified-desc: "Poslat pouze zmíněným uživatelům"
|
||||
local-public: "Veřejná (pouze místní)"
|
||||
local-public-desc: "Nepublikovat na vzdálených serverech"
|
||||
local-home: "Domovská (pouze místní)"
|
||||
local-followers: "Pro sledující (pouze místní)"
|
||||
common/views/components/trends.vue:
|
||||
@ -585,6 +614,8 @@ common/views/components/language-settings.vue:
|
||||
title: "Zobrazit jazyky"
|
||||
pick-language: "Zvolte jazyk"
|
||||
recommended: "Doporučené"
|
||||
auto: "Automaticky"
|
||||
specify-language: "Vyberte jazyk"
|
||||
info: "Pro aktivování změn musíte znovu načíst stránky."
|
||||
common/views/components/profile-editor.vue:
|
||||
title: "Profil"
|
||||
@ -611,6 +642,7 @@ common/views/components/profile-editor.vue:
|
||||
email-not-verified: "Váš email není potvrzen. Prosím zkontrolujte si svou schránku."
|
||||
export: "Exportovat"
|
||||
import: "Importovat"
|
||||
export-and-import: "Import / Export"
|
||||
export-targets:
|
||||
following-list: "Seznam sledujících"
|
||||
mute-list: "Seznam ztlumených uživatelů"
|
||||
@ -630,19 +662,31 @@ common/views/components/user-list-editor.vue:
|
||||
add-user: "Přidat uživatele"
|
||||
common/views/components/user-group-editor.vue:
|
||||
users: "Členové"
|
||||
rename: "Přejmenovat skupinu"
|
||||
delete: "Odstranit skupinu"
|
||||
transfer: "Přesunout skupinu"
|
||||
transfer-are-you-sure: "Jste si jistí že chcete přidat @$2 do skupiny: $1?"
|
||||
transferred: "Skupina přesunuta"
|
||||
remove-user: "Odebrat uživatele z této skupiny"
|
||||
delete-are-you-sure: "Jste si jistí že chcete smazat skupinu \"$1\"?"
|
||||
deleted: "Smazáno"
|
||||
invite: "Pozvat"
|
||||
invited: "Pozvánka byla úspěšně odeslána"
|
||||
common/views/components/user-lists.vue:
|
||||
user-lists: "Seznamy"
|
||||
create-list: "Vytvořit seznam"
|
||||
list-name: "Název seznamu"
|
||||
common/views/components/user-groups.vue:
|
||||
user-groups: "Skupiny"
|
||||
create-group: "Vytvořit skupinu"
|
||||
group-name: "Název skupiny"
|
||||
owned-groups: "Moje skupiny"
|
||||
invites: "Pozvat"
|
||||
reject-invite: "Odmítnout"
|
||||
common/views/widgets/broadcast.vue:
|
||||
fetching: "Načítám"
|
||||
no-broadcasts: "Žádná nová oznámení"
|
||||
have-a-nice-day: "Přejeme Vám příjemný den!"
|
||||
next: "Další"
|
||||
common/views/widgets/calendar.vue:
|
||||
year: "Rok {}"
|
||||
@ -800,6 +844,7 @@ desktop/views/components/renote-form-window.vue:
|
||||
desktop/views/components/settings.2fa.vue:
|
||||
detail: "Více…"
|
||||
url: "https://www.google.cz/landing/2step/"
|
||||
token: "Token"
|
||||
common/views/components/media-image.vue:
|
||||
click-to-show: "Klikněte pro zobrazení"
|
||||
common/views/components/api-settings.vue:
|
||||
|
@ -1761,8 +1761,7 @@ pages:
|
||||
_divide:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
remind: "÷ Tomo"
|
||||
_remind:
|
||||
_mod:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
eq: "A og B er ens"
|
||||
|
@ -1857,8 +1857,8 @@ pages:
|
||||
_divide:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
remind: "÷ Remaindering"
|
||||
_remind:
|
||||
mod: "÷ Remaindering"
|
||||
_mod:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
eq: "A and B are equal"
|
||||
|
@ -12,7 +12,7 @@ common:
|
||||
rich-contents: "Notes"
|
||||
rich-contents-desc: "Partagez vos idées, les événements et les sujets qui vous tiennent à cœur ainsi que tout autre chose que vous souhaitez partager avec les autres. Si vous le désirez, vous pouvez décorer vos messages en utilisant une syntaxe différente ou en y joignant des sondages et des fichiers, tels que les photos ou les vidéos que vous aimez."
|
||||
reaction: "Réactions"
|
||||
reaction-desc: "Une manière simple d'exprimer vos émotions. Misskey peut attacher diverses réactions aux publications des autres utilisateurs. Si vous essayez les réactions sur Misskey, vous ne pourrez plus retourner sur une autre plateforme de réseaux sociaux n'offrant que des « J'aime »."
|
||||
reaction-desc: "Une manière simple d'exprimer vos émotions. Misskey peut attacher diverses réactions aux publications des autres utilisateur·rice·s. Si vous essayez les réactions sur Misskey, vous ne pourrez plus retourner sur une autre plateforme de réseaux sociaux n'offrant que des « J'aime »."
|
||||
ui: "Interface"
|
||||
ui-desc: "Aucune interface graphique ne peut plaire à tout le monde. Par conséquent, Misskey possède une interface utilisateur hautement personnalisable selon vos goûts. Vous pouvez rendre votre page d'accueil originale en modifiant la mise en page de votre fil et en déplaçant les widgets que vous pouvez facilement ajuster pour vous approprier cet espace."
|
||||
drive: "Drive"
|
||||
@ -89,8 +89,8 @@ common:
|
||||
"write:reactions": "Gérer vos réactions"
|
||||
"write:votes": "Vote"
|
||||
empty-timeline-info:
|
||||
follow-users-to-make-your-timeline: "Les utilisateurs suivants afficheront leurs publications sur votre fil."
|
||||
explore: "Trouver des utilisateurs"
|
||||
follow-users-to-make-your-timeline: "Les utilisateur·rice·s suivant·e·s afficheront leurs publications sur votre fil."
|
||||
explore: "Trouver des utilisateur·rice·s"
|
||||
post-form:
|
||||
attach-location-information: "Joindre des informations de localisation"
|
||||
hide-contents: "Masquer les contenus"
|
||||
@ -148,13 +148,13 @@ common:
|
||||
public: "Public"
|
||||
home: "Principal"
|
||||
home-desc: "Publier sur le fil principal uniquement"
|
||||
followers: "Abonnés"
|
||||
followers-desc: "Publier à vos abonnés uniquement"
|
||||
followers: "Abonné·e·s"
|
||||
followers-desc: "Publier à vos abonné·e·s uniquement"
|
||||
specified: "Direct"
|
||||
specified-desc: "Publier uniquement aux utilisateurs mentionnés"
|
||||
specified-desc: "Publier uniquement aux utilisateur·rice·s mentionné·e·s"
|
||||
local-public: "Local (Public)"
|
||||
local-home: "Accueil (local uniquement)"
|
||||
local-followers: "Local (Abonnés)"
|
||||
local-followers: "Abonné·e·s (Local uniquement)"
|
||||
note-placeholders:
|
||||
a: "Que faites-vous maintenant ?"
|
||||
b: "Quoi de neuf ?"
|
||||
@ -314,7 +314,7 @@ common:
|
||||
version: "Version"
|
||||
broadcast: "Diffusion"
|
||||
notifications: "Notifications"
|
||||
users: "Utilisateurs recommandés"
|
||||
users: "Utilisateur·rice·s recommandé·e·s"
|
||||
polls: "Sondages"
|
||||
post-form: "Champs de publication"
|
||||
server: "Infos sur le serveur"
|
||||
@ -342,13 +342,13 @@ auth/views/index.vue:
|
||||
sign-in: "Veuillez vous connecter"
|
||||
common/views/pages/explore.vue:
|
||||
pinned-users: "Utilisateur·rice·s épinglé·e·s"
|
||||
popular-users: "Utilisateurs populaires"
|
||||
recently-updated-users: "Utilisateurs actifs récemment"
|
||||
popular-users: "Utilisateur·rice·s populaires"
|
||||
recently-updated-users: "Utilisateur·rice·s actif·ve·s récemment"
|
||||
recently-registered-users: "Les nouveaux inscrits"
|
||||
popular-tags: "Mots-clés populaires"
|
||||
federated: "Du Fédiverse"
|
||||
explore: "Explorer {host}"
|
||||
users-info: "Actuellement, {users} utilisateurs se sont inscrit ici"
|
||||
users-info: "Actuellement, {users} utilisateur·rice·s se sont inscrit ici"
|
||||
common/views/components/url-preview.vue:
|
||||
enable-player: "Activer la lecture"
|
||||
disable-player: "Fermer le lecteur"
|
||||
@ -653,13 +653,13 @@ common/views/components/visibility-chooser.vue:
|
||||
followers: "Abonné·e·s"
|
||||
followers-desc: "Publier à vos abonné·e·s uniquement"
|
||||
specified: "Direct"
|
||||
specified-desc: "Publier uniquement aux utilisateurs mentionnés"
|
||||
specified-desc: "Publier uniquement aux utilisateur·rice·s mentionné·e·s"
|
||||
local-public: "Local (Public)"
|
||||
local-public-desc: "Ne pas publier pour les distants"
|
||||
local-home: "Accueil (local uniquement)"
|
||||
local-followers: "Local (Abonnés)"
|
||||
local-followers: "Abonné·e·s (Local uniquement)"
|
||||
common/views/components/trends.vue:
|
||||
count: "{} utilisateurs mentionnés"
|
||||
count: "{} utilisateur·rice·s mentionné·e·s"
|
||||
empty: "Aucune tendance"
|
||||
common/views/components/language-settings.vue:
|
||||
title: "Langue "
|
||||
@ -897,7 +897,7 @@ desktop/views/components/media-video.vue:
|
||||
sensitive: "Le contenu est NSFW"
|
||||
click-to-show: "Cliquer pour afficher"
|
||||
desktop/views/components/followers-window.vue:
|
||||
followers: "{} abonné·e·s"
|
||||
followers: "Abonné·e·s de {}"
|
||||
desktop/views/components/followers.vue:
|
||||
empty: "Il semble que vous n’avez pas encore d’abonné·e·s."
|
||||
desktop/views/components/following-window.vue:
|
||||
@ -1224,7 +1224,7 @@ admin/views/charts.vue:
|
||||
per-hour: "par heure"
|
||||
federation: "Fédération"
|
||||
notes: "Publications"
|
||||
users: "Utilisateurs"
|
||||
users: "Utilisateur·rice·s"
|
||||
drive: "Lecteur"
|
||||
network: "Réseau"
|
||||
charts:
|
||||
@ -1234,8 +1234,8 @@ admin/views/charts.vue:
|
||||
local-notes: "Nombre des publications : augmentation/diminution (Local)"
|
||||
remote-notes: "Nombre de publications : augmentation/diminution (distants)"
|
||||
notes-total: "Total des notes"
|
||||
users: "Nombre d’utilisateurs : augmentation/diminution"
|
||||
users-total: "Nombre total des utilisateurs"
|
||||
users: "Nombre d’utilisateur·rice·s : augmentation/diminution"
|
||||
users-total: "Nombre total des utilisateur·rice·s"
|
||||
active-users: "Utilisateur·rice·s actif·ve·s"
|
||||
drive: "Capacité utilisée comme stockage : augmentation/diminution"
|
||||
drive-total: "Utilisation totale du lecteur"
|
||||
@ -1266,6 +1266,7 @@ admin/views/drive.vue:
|
||||
unmark-as-sensitive: "Ne pas marquer comme sensible"
|
||||
marked-as-sensitive: "Marqué comme sensible"
|
||||
unmarked-as-sensitive: "Marqué comme non sensible"
|
||||
clean-remote-files: "Nettoyer le cache des fichiers distants"
|
||||
clean-up: "Nettoyage"
|
||||
admin/views/users.vue:
|
||||
operation: "Actions"
|
||||
@ -1374,7 +1375,7 @@ admin/views/federation.vue:
|
||||
lastCommunicatedAtAsc: "La date et l'heure des interactions plus anciennes"
|
||||
lastCommunicatedAtDesc: "La date et l'heure des nouvelles interactions"
|
||||
notesDesc: "Description des notes"
|
||||
usersAsc: "Peu d'abonnés"
|
||||
usersAsc: "Peu d'abonné·e·s"
|
||||
followingAsc: "Les moins suivies"
|
||||
followingDesc: "Ayant le plus d'abonné·e·s"
|
||||
followersAsc: "Ayant le moins d'abonné·e·s"
|
||||
@ -1389,7 +1390,7 @@ admin/views/federation.vue:
|
||||
charts: "Graphs"
|
||||
chart-srcs:
|
||||
requests: "Requêtes"
|
||||
users: "Nombre d’utilisateurs·trices : augmentation/diminution"
|
||||
users: "Nombre d’utilisateur·trice·s : augmentation/diminution"
|
||||
users-total: "Nombre total des utilisateur·rice·s"
|
||||
notes: "Augmentation/diminution du nombre des notes"
|
||||
notes-total: "Nombre total des notes"
|
||||
@ -1431,7 +1432,7 @@ desktop/views/pages/user-list.users.vue:
|
||||
desktop/views/pages/user/user.followers-you-know.vue:
|
||||
title: "Abonné·e·s que vous connaissez"
|
||||
loading: "Chargement en cours"
|
||||
no-users: "Aucun abonné connu"
|
||||
no-users: "Aucun·e abonné·e connu·e"
|
||||
desktop/views/pages/user/user.friends.vue:
|
||||
title: "Mentions fréquentes"
|
||||
loading: "Chargement en cours"
|
||||
@ -1646,7 +1647,7 @@ deck/deck.user-column.vue:
|
||||
follows-you: "Vous suit"
|
||||
posts: "Notes"
|
||||
following: "Suit"
|
||||
followers: "Abonnés"
|
||||
followers: "Abonné·e·s"
|
||||
images: "Images"
|
||||
activity: "Activité"
|
||||
timeline: "Fil d’actualité"
|
||||
@ -1688,6 +1689,7 @@ pages:
|
||||
view-source: "Afficher la source"
|
||||
view-page: "Afficher la page"
|
||||
like: "Bien"
|
||||
liked-pages: "Pages favorites"
|
||||
my-pages: "Mes pages"
|
||||
inspector: "Inspecteur"
|
||||
content: "Bloc de page"
|
||||
@ -1801,7 +1803,7 @@ pages:
|
||||
_divide:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
_remind:
|
||||
_mod:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
eq: "A et B sont équivalents"
|
||||
|
@ -1226,8 +1226,12 @@ admin/views/index.vue:
|
||||
abuse: "スパム報告"
|
||||
queue: "ジョブキュー"
|
||||
logs: "ログ"
|
||||
db: "データベース"
|
||||
back-to-misskey: "Misskeyに戻る"
|
||||
|
||||
admin/views/db.vue:
|
||||
tables: "テーブル"
|
||||
|
||||
admin/views/dashboard.vue:
|
||||
dashboard: "ダッシュボード"
|
||||
accounts: "アカウント"
|
||||
@ -2060,8 +2064,8 @@ pages:
|
||||
_divide:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
remind: "÷ 割った余り"
|
||||
_remind:
|
||||
mod: "÷ 割った余り"
|
||||
_mod:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
eq: "AとBが同じ"
|
||||
|
@ -1857,8 +1857,8 @@ pages:
|
||||
_divide:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
remind: "÷ 나눈 나머지"
|
||||
_remind:
|
||||
mod: "÷ 나눈 나머지"
|
||||
_mod:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
eq: "A와 B가 동일"
|
||||
|
@ -1857,8 +1857,8 @@ pages:
|
||||
_divide:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
remind: "÷ 取模"
|
||||
_remind:
|
||||
mod: "÷ 取模"
|
||||
_mod:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
eq: "A和B相等"
|
||||
|
16
package.json
16
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"author": "syuilo <i@syuilo.com>",
|
||||
"version": "11.20.2",
|
||||
"version": "11.22.0",
|
||||
"codename": "daybreak",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -99,7 +99,7 @@
|
||||
"@types/websocket": "0.0.40",
|
||||
"@types/ws": "6.0.1",
|
||||
"animejs": "3.0.1",
|
||||
"apexcharts": "3.7.1",
|
||||
"apexcharts": "3.8.0",
|
||||
"autobind-decorator": "2.4.0",
|
||||
"autosize": "4.0.2",
|
||||
"autwh": "0.1.0",
|
||||
@ -165,11 +165,11 @@
|
||||
"loader-utils": "1.2.3",
|
||||
"lolex": "3.1.0",
|
||||
"lookup-dns-cache": "2.1.0",
|
||||
"minio": "7.0.8",
|
||||
"minio": "7.0.10",
|
||||
"mocha": "6.1.4",
|
||||
"moji": "0.5.1",
|
||||
"moment": "2.24.0",
|
||||
"ms": "2.1.1",
|
||||
"ms": "2.1.2",
|
||||
"nested-property": "0.0.7",
|
||||
"node-fetch": "2.6.0",
|
||||
"nodemailer": "6.2.1",
|
||||
@ -214,8 +214,8 @@
|
||||
"style-loader": "0.23.1",
|
||||
"stylus": "0.54.5",
|
||||
"stylus-loader": "3.0.2",
|
||||
"summaly": "2.2.0",
|
||||
"systeminformation": "4.1.6",
|
||||
"summaly": "2.3.0",
|
||||
"systeminformation": "4.11.1",
|
||||
"syuilo-password-strength": "0.0.1",
|
||||
"terser-webpack-plugin": "1.3.0",
|
||||
"textarea-caret": "3.1.0",
|
||||
@ -255,8 +255,8 @@
|
||||
"vuex": "3.1.1",
|
||||
"vuex-persistedstate": "2.5.4",
|
||||
"web-push": "3.3.5",
|
||||
"webpack": "4.33.0",
|
||||
"webpack-cli": "3.3.2",
|
||||
"webpack": "4.34.0",
|
||||
"webpack-cli": "3.3.4",
|
||||
"websocket": "1.0.28",
|
||||
"ws": "7.0.0",
|
||||
"xev": "2.0.1"
|
||||
|
@ -124,7 +124,7 @@ export default Vue.extend({
|
||||
this.connection = this.$root.stream.useSharedConnection('serverStats');
|
||||
|
||||
this.updateStats();
|
||||
this.clock = setInterval(this.updateStats, 1000);
|
||||
this.clock = setInterval(this.updateStats, 3000);
|
||||
|
||||
this.$root.getMeta().then(meta => {
|
||||
this.meta = meta;
|
||||
|
39
src/client/app/admin/views/db.vue
Normal file
39
src/client/app/admin/views/db.vue
Normal file
@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<div>
|
||||
<ui-card>
|
||||
<template #title><fa :icon="faDatabase"/> {{ $t('tables') }}</template>
|
||||
<section v-if="tables">
|
||||
<div v-for="table in Object.keys(tables)"><b>{{ table }}</b> {{ tables[table].count | number }} {{ tables[table].size | bytes }}</div>
|
||||
</section>
|
||||
</ui-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../i18n';
|
||||
import { faDatabase } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('admin/views/db.vue'),
|
||||
|
||||
data() {
|
||||
return {
|
||||
tables: null,
|
||||
faDatabase
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.fetch();
|
||||
},
|
||||
|
||||
methods: {
|
||||
fetch() {
|
||||
this.$root.api('admin/get-table-stats').then(tables => {
|
||||
this.tables = tables;
|
||||
});
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
@ -22,6 +22,7 @@
|
||||
<li @click="nav('instance')" :class="{ active: page == 'instance' }"><fa icon="cog" fixed-width/>{{ $t('instance') }}</li>
|
||||
<li @click="nav('queue')" :class="{ active: page == 'queue' }"><fa :icon="faTasks" fixed-width/>{{ $t('queue') }}</li>
|
||||
<li @click="nav('logs')" :class="{ active: page == 'logs' }"><fa :icon="faStream" fixed-width/>{{ $t('logs') }}</li>
|
||||
<li @click="nav('db')" :class="{ active: page == 'db' }"><fa :icon="faDatabase" fixed-width/>{{ $t('db') }}</li>
|
||||
<li @click="nav('moderators')" :class="{ active: page == 'moderators' }"><fa :icon="faHeadset" fixed-width/>{{ $t('moderators') }}</li>
|
||||
<li @click="nav('users')" :class="{ active: page == 'users' }"><fa icon="users" fixed-width/>{{ $t('users') }}</li>
|
||||
<li @click="nav('drive')" :class="{ active: page == 'drive' }"><fa icon="cloud" fixed-width/>{{ $t('@.drive') }}</li>
|
||||
@ -43,6 +44,7 @@
|
||||
<div v-if="page == 'instance'"><x-instance/></div>
|
||||
<div v-if="page == 'queue'"><x-queue/></div>
|
||||
<div v-if="page == 'logs'"><x-logs/></div>
|
||||
<div v-if="page == 'db'"><x-db/></div>
|
||||
<div v-if="page == 'moderators'"><x-moderators/></div>
|
||||
<div v-if="page == 'users'"><x-users/></div>
|
||||
<div v-if="page == 'emoji'"><x-emoji/></div>
|
||||
@ -59,19 +61,20 @@
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../i18n';
|
||||
import { version } from '../../config';
|
||||
import XDashboard from "./dashboard.vue";
|
||||
import XInstance from "./instance.vue";
|
||||
import XQueue from "./queue.vue";
|
||||
import XLogs from "./logs.vue";
|
||||
import XModerators from "./moderators.vue";
|
||||
import XEmoji from "./emoji.vue";
|
||||
import XAnnouncements from "./announcements.vue";
|
||||
import XUsers from "./users.vue";
|
||||
import XDrive from "./drive.vue";
|
||||
import XAbuse from "./abuse.vue";
|
||||
import XFederation from "./federation.vue";
|
||||
import XDashboard from './dashboard.vue';
|
||||
import XInstance from './instance.vue';
|
||||
import XQueue from './queue.vue';
|
||||
import XLogs from './logs.vue';
|
||||
import XDb from './db.vue';
|
||||
import XModerators from './moderators.vue';
|
||||
import XEmoji from './emoji.vue';
|
||||
import XAnnouncements from './announcements.vue';
|
||||
import XUsers from './users.vue';
|
||||
import XDrive from './drive.vue';
|
||||
import XAbuse from './abuse.vue';
|
||||
import XFederation from './federation.vue';
|
||||
|
||||
import { faHeadset, faArrowLeft, faGlobe, faExclamationCircle, faTasks, faStream } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faHeadset, faArrowLeft, faGlobe, faExclamationCircle, faTasks, faStream, faDatabase } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faGrin } from '@fortawesome/free-regular-svg-icons';
|
||||
|
||||
// Detect the user agent
|
||||
@ -85,6 +88,7 @@ export default Vue.extend({
|
||||
XInstance,
|
||||
XQueue,
|
||||
XLogs,
|
||||
XDb,
|
||||
XModerators,
|
||||
XEmoji,
|
||||
XAnnouncements,
|
||||
@ -108,7 +112,8 @@ export default Vue.extend({
|
||||
faGlobe,
|
||||
faExclamationCircle,
|
||||
faTasks,
|
||||
faStream
|
||||
faStream,
|
||||
faDatabase,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
|
@ -5,6 +5,7 @@ export default (opts) => ({
|
||||
return {
|
||||
items: [],
|
||||
queue: [],
|
||||
offset: 0,
|
||||
fetching: true,
|
||||
moreFetching: false,
|
||||
inited: false,
|
||||
@ -80,6 +81,7 @@ export default (opts) => ({
|
||||
this.items = x;
|
||||
this.more = false;
|
||||
}
|
||||
this.offset = x.length;
|
||||
this.inited = true;
|
||||
this.fetching = false;
|
||||
if (opts.onInited) opts.onInited(this);
|
||||
@ -96,7 +98,11 @@ export default (opts) => ({
|
||||
if (params && params.then) params = await params;
|
||||
await this.$root.api(this.pagination.endpoint, {
|
||||
limit: (this.pagination.limit || 10) + 1,
|
||||
untilId: this.items[this.items.length - 1].id,
|
||||
...(this.pagination.endpoint === 'notes/search' ? {
|
||||
offset: this.offset,
|
||||
} : {
|
||||
untilId: this.items[this.items.length - 1].id,
|
||||
}),
|
||||
...params
|
||||
}).then(x => {
|
||||
if (x.length == (this.pagination.limit || 10) + 1) {
|
||||
@ -107,6 +113,7 @@ export default (opts) => ({
|
||||
this.items = this.items.concat(x);
|
||||
this.more = false;
|
||||
}
|
||||
this.offset += x.length;
|
||||
this.moreFetching = false;
|
||||
}, e => {
|
||||
this.moreFetching = false;
|
||||
|
@ -16,10 +16,10 @@
|
||||
</ol>
|
||||
<ol class="emojis" ref="suggests" v-if="emojis.length > 0">
|
||||
<li v-for="emoji in emojis" @click="complete(type, emoji.emoji)" @keydown="onKeydown" tabindex="-1">
|
||||
<span class="emoji" v-if="emoji.isCustomEmoji"><img :src="emoji.url" :alt="emoji.emoji"/></span>
|
||||
<span class="emoji" v-if="emoji.isCustomEmoji"><img :src="$store.state.device.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url" :alt="emoji.emoji"/></span>
|
||||
<span class="emoji" v-else-if="!useOsDefaultEmojis"><img :src="emoji.url" :alt="emoji.emoji"/></span>
|
||||
<span class="emoji" v-else>{{ emoji.emoji }}</span>
|
||||
<span class="name">{{ beforeQ }}<b>{{ q }}</b>{{ afterQ }}</span>
|
||||
<span class="name" v-html="emoji.name.replace(q, `<b>${q}</b>`)"></span>
|
||||
<span class="alias" v-if="emoji.aliasOf">({{ emoji.aliasOf }})</span>
|
||||
</li>
|
||||
</ol>
|
||||
@ -31,6 +31,7 @@ import Vue from 'vue';
|
||||
import * as emojilib from 'emojilib';
|
||||
import contains from '../../../common/scripts/contains';
|
||||
import { twemojiBase } from '../../../../../misc/twemoji-base';
|
||||
import { getStaticImageUrl } from '../../../common/scripts/get-static-image-url';
|
||||
|
||||
type EmojiDef = {
|
||||
emoji: string;
|
||||
@ -78,6 +79,7 @@ export default Vue.extend({
|
||||
|
||||
data() {
|
||||
return {
|
||||
getStaticImageUrl,
|
||||
fetching: true,
|
||||
users: [],
|
||||
hashtags: [],
|
||||
@ -89,14 +91,6 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
computed: {
|
||||
beforeQ(): string {
|
||||
return this.emoji.name.split(this.q)[0];
|
||||
},
|
||||
|
||||
afterQ(): string {
|
||||
return this.emoji.name.split(this.q)[1] || '';
|
||||
},
|
||||
|
||||
items(): HTMLCollection {
|
||||
return (this.$refs.suggests as Element).children;
|
||||
},
|
||||
|
@ -89,9 +89,7 @@ export default Vue.extend({
|
||||
|
||||
get() {
|
||||
const at = () => {
|
||||
const [date] = moment(this.atDate).toISOString().split('T');
|
||||
const [hour, minute] = this.atTime.split(':');
|
||||
return moment(`${date}T${hour}:${minute}Z`).valueOf();
|
||||
return moment(`${this.atDate} ${this.atTime}`).valueOf();
|
||||
};
|
||||
|
||||
const after = () => {
|
||||
|
@ -3,6 +3,7 @@
|
||||
<p>Maintainer: <b><a :href="'mailto:' + meta.maintainerEmail" target="_blank">{{ meta.maintainerName }}</a></b></p>
|
||||
<p>Machine: {{ meta.machine }}</p>
|
||||
<p>Node: {{ meta.node }}</p>
|
||||
<p>PSQL: {{ meta.psql }}</p>
|
||||
<p>Version: {{ meta.version }} </p>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -33,6 +33,7 @@ const defaultSettings = {
|
||||
mutedWords: [],
|
||||
gamesReversiShowBoardLabels: false,
|
||||
gamesReversiUseAvatarStones: true,
|
||||
disableAnimatedMfm: false,
|
||||
};
|
||||
|
||||
const defaultDeviceSettings = {
|
||||
|
@ -3,7 +3,6 @@
|
||||
*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import { URL } from 'url';
|
||||
import * as yaml from 'js-yaml';
|
||||
import { Source, Mixin } from './types';
|
||||
import * as pkg from '../../package.json';
|
||||
|
@ -14,6 +14,7 @@ export type Source = {
|
||||
db: string;
|
||||
user: string;
|
||||
pass: string;
|
||||
disableCache?: boolean;
|
||||
extra?: { [x: string]: string };
|
||||
};
|
||||
redis: {
|
||||
|
@ -96,7 +96,7 @@ export function initDb(justBorrow = false, sync = false, log = false) {
|
||||
extra: config.db.extra,
|
||||
synchronize: process.env.NODE_ENV === 'test' || sync,
|
||||
dropSchema: process.env.NODE_ENV === 'test' && !justBorrow,
|
||||
cache: {
|
||||
cache: !config.db.disableCache ? {
|
||||
type: 'redis',
|
||||
options: {
|
||||
host: config.redis.host,
|
||||
@ -107,7 +107,7 @@ export function initDb(justBorrow = false, sync = false, log = false) {
|
||||
db: config.redis.db || 0
|
||||
}
|
||||
}
|
||||
},
|
||||
} : false,
|
||||
logging: log,
|
||||
logger: log ? new MyCustomLogger() : undefined,
|
||||
entities: [
|
||||
|
@ -257,7 +257,7 @@ export default class Reversi {
|
||||
public get winner(): Color | null {
|
||||
return this.isEnded ?
|
||||
this.blackCount == this.whiteCount ? null :
|
||||
this.opts.isLlotheo === this.blackCount > this.whiteCount ? WHITE : BLACK :
|
||||
this.opts.isLlotheo === this.blackCount > this.whiteCount ? WHITE : BLACK :
|
||||
undefined as never;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { parseFragment, DefaultTreeDocumentFragment } from 'parse5';
|
||||
import { URL } from 'url';
|
||||
import { urlRegex } from './prelude';
|
||||
|
||||
export function fromHtml(html: string): string {
|
||||
|
@ -98,13 +98,13 @@ export const mfmLanguage = P.createLanguage({
|
||||
const text = input.substr(i);
|
||||
const match = text.match(/^(\*|_)([a-zA-Z0-9]+?[\s\S]*?)\1/);
|
||||
if (!match) return P.makeFailure(i, 'not a italic');
|
||||
if (input[i - 1] != null && input[i - 1].match(/[a-z0-9]/i)) return P.makeFailure(i, 'not a italic');
|
||||
if (input[i - 1] != null && input[i - 1] != ' ' && input[i - 1] != '\n') return P.makeFailure(i, 'not a italic');
|
||||
return P.makeSuccess(i + match[0].length, match[2]);
|
||||
});
|
||||
|
||||
return P.alt(xml, underscore).map(x => createTree('italic', r.inline.atLeast(1).tryParse(x), {}));
|
||||
},
|
||||
strike: r => P.regexp(/~~(.+?)~~/, 1).map(x => createTree('strike', r.inline.atLeast(1).tryParse(x), {})),
|
||||
strike: r => P.regexp(/~~([^\n~]+?)~~/, 1).map(x => createTree('strike', r.inline.atLeast(1).tryParse(x), {})),
|
||||
motion: r => {
|
||||
const paren = P.regexp(/\(\(\(([\s\S]+?)\)\)\)/, 1);
|
||||
const xml = P.regexp(/<motion>(.+?)<\/motion>/, 1);
|
||||
@ -164,8 +164,10 @@ export const mfmLanguage = P.createLanguage({
|
||||
} else
|
||||
url = match[0];
|
||||
url = removeOrphanedBrackets(url);
|
||||
if (url.endsWith('.')) url = url.substr(0, url.lastIndexOf('.'));
|
||||
if (url.endsWith(',')) url = url.substr(0, url.lastIndexOf(','));
|
||||
while (url.endsWith('.') || url.endsWith(',')) {
|
||||
if (url.endsWith('.')) url = url.substr(0, url.lastIndexOf('.'));
|
||||
if (url.endsWith(',')) url = url.substr(0, url.lastIndexOf(','));
|
||||
}
|
||||
return P.makeSuccess(i + url.length, url);
|
||||
}).map(x => createLeaf('url', { url: x }));
|
||||
},
|
||||
|
@ -161,7 +161,7 @@ export class ASEvaluator {
|
||||
subtract: (a: number, b: number) => a - b,
|
||||
multiply: (a: number, b: number) => a * b,
|
||||
divide: (a: number, b: number) => a / b,
|
||||
remind: (a: number, b: number) => a % b,
|
||||
mod: (a: number, b: number) => a % b,
|
||||
strLen: (a: string) => a.length,
|
||||
strPick: (a: string, b: number) => a[b - 1],
|
||||
strReplace: (a: string, b: string, c: string) => a.split(b).join(c),
|
||||
|
@ -58,7 +58,7 @@ export const funcDefs: Record<string, { in: any[]; out: any; category: string; i
|
||||
subtract: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faMinus, },
|
||||
multiply: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faTimes, },
|
||||
divide: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faDivide, },
|
||||
remind: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faDivide, },
|
||||
mod: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faDivide, },
|
||||
eq: { in: [0, 0], out: 'boolean', category: 'comparison', icon: faEquals, },
|
||||
notEq: { in: [0, 0], out: 'boolean', category: 'comparison', icon: faNotEqual, },
|
||||
gt: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: faGreaterThan, },
|
||||
|
@ -1,6 +1,5 @@
|
||||
import config from '../config';
|
||||
import { toASCII } from 'punycode';
|
||||
import { URL } from 'url';
|
||||
|
||||
export function getFullApAccount(username: string, host: string | null) {
|
||||
return host ? `${username}@${toPuny(host)}` : `${username}@${toPuny(config.host)}`;
|
||||
|
@ -25,10 +25,8 @@ export async function downloadUrl(url: string, path: string) {
|
||||
rej(error);
|
||||
});
|
||||
|
||||
const requestUrl = new URL(url).pathname.match(/[^\u0021-\u00ff]/) ? encodeURI(url) : url;
|
||||
|
||||
const req = request({
|
||||
url: requestUrl,
|
||||
url: new URL(url).href, // https://github.com/syuilo/misskey/issues/2637
|
||||
proxy: config.proxy,
|
||||
timeout: 10 * 1000,
|
||||
headers: {
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,4 +1,4 @@
|
||||
export const twemojiBase = 'https://cdn.jsdelivr.net/npm/twemoji@11.3.0';
|
||||
// https://cdn.jsdelivr.net/npm/twemoji@11.3.0
|
||||
// https://cdnjs.cloudflare.com/ajax/libs/twemoji/11.3.0
|
||||
export const twemojiBase = 'https://cdn.jsdelivr.net/npm/twemoji@12.0.1';
|
||||
// https://cdn.jsdelivr.net/npm/twemoji@12.0.1
|
||||
// https://cdnjs.cloudflare.com/ajax/libs/twemoji/12.0.1
|
||||
// https://twemoji.maxcdn.com
|
||||
|
@ -72,6 +72,7 @@ export class DriveFile {
|
||||
})
|
||||
public properties: Record<string, any>;
|
||||
|
||||
@Index()
|
||||
@Column('boolean')
|
||||
public storedInternal: boolean;
|
||||
|
||||
@ -146,6 +147,7 @@ export class DriveFile {
|
||||
/**
|
||||
* 外部の(信頼されていない)URLへの直リンクか否か
|
||||
*/
|
||||
@Index()
|
||||
@Column('boolean', {
|
||||
default: false,
|
||||
comment: 'Whether the DriveFile is direct link to remote server.'
|
||||
|
@ -8,7 +8,7 @@ export type PackedMessagingMessage = SchemaType<typeof packedMessagingMessageSch
|
||||
|
||||
@EntityRepository(MessagingMessage)
|
||||
export class MessagingMessageRepository extends Repository<MessagingMessage> {
|
||||
public isValidText(text: string): boolean {
|
||||
public validateText(text: string): boolean {
|
||||
return text.trim().length <= 1000 && text.trim() != '';
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
import $ from 'cafy';
|
||||
import { EntityRepository, Repository, In } from 'typeorm';
|
||||
import { User, ILocalUser, IRemoteUser } from '../entities/user';
|
||||
import { Emojis, Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles, UserGroupJoinings } from '..';
|
||||
@ -231,29 +232,13 @@ export class UserRepository extends Repository<User> {
|
||||
}
|
||||
|
||||
//#region Validators
|
||||
public validateUsername(username: string, remote = false): boolean {
|
||||
return typeof username == 'string' && (remote ? /^\w([\w-]*\w)?$/ : /^\w{1,20}$/).test(username);
|
||||
}
|
||||
|
||||
public validatePassword(password: string): boolean {
|
||||
return typeof password == 'string' && password != '';
|
||||
}
|
||||
|
||||
public isValidName(name?: string): boolean {
|
||||
return name === null || (typeof name == 'string' && name.length < 50 && name.trim() != '');
|
||||
}
|
||||
|
||||
public isValidDescription(description: string): boolean {
|
||||
return typeof description == 'string' && description.length < 500 && description.trim() != '';
|
||||
}
|
||||
|
||||
public isValidLocation(location: string): boolean {
|
||||
return typeof location == 'string' && location.length < 50 && location.trim() != '';
|
||||
}
|
||||
|
||||
public isValidBirthday(birthday: string): boolean {
|
||||
return typeof birthday == 'string' && /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/.test(birthday);
|
||||
}
|
||||
public validateLocalUsername = $.str.match(/^\w{1,20}$/);
|
||||
public validateRemoteUsername = $.str.match(/^\w([\w-]*\w)?$/);
|
||||
public validatePassword = $.str.min(1);
|
||||
public validateName = $.str.min(1).max(50);
|
||||
public validateDescription = $.str.min(1).max(500);
|
||||
public validateLocation = $.str.min(1).max(50);
|
||||
public validateBirthday = $.str.match(/^([0-9]{4})-([0-9]{2})-([0-9]{2})$/);
|
||||
//#endregion
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,6 @@ import * as httpSignature from 'http-signature';
|
||||
import { IRemoteUser } from '../../models/entities/user';
|
||||
import perform from '../../remote/activitypub/perform';
|
||||
import { resolvePerson, updatePerson } from '../../remote/activitypub/models/person';
|
||||
import { URL } from 'url';
|
||||
import { publishApLogStream } from '../../services/stream';
|
||||
import Logger from '../../services/logger';
|
||||
import { registerOrFetchInstanceDoc } from '../../services/register-or-fetch-instance-doc';
|
||||
|
@ -6,7 +6,6 @@ import { resolveImage } from './image';
|
||||
import { isCollectionOrOrderedCollection, isCollection, IPerson } from '../type';
|
||||
import { DriveFile } from '../../../models/entities/drive-file';
|
||||
import { fromHtml } from '../../../mfm/fromHtml';
|
||||
import { URL } from 'url';
|
||||
import { resolveNote, extractEmojis } from './note';
|
||||
import { registerOrFetchInstanceDoc } from '../../../services/register-or-fetch-instance-doc';
|
||||
import { ITag, extractHashtags } from './tag';
|
||||
@ -53,12 +52,14 @@ function validatePerson(x: any, uri: string) {
|
||||
return new Error('invalid person: inbox is not a string');
|
||||
}
|
||||
|
||||
if (!Users.validateUsername(x.preferredUsername, true)) {
|
||||
if (!Users.validateRemoteUsername.ok(x.preferredUsername)) {
|
||||
return new Error('invalid person: invalid username');
|
||||
}
|
||||
|
||||
if (!Users.isValidName(x.name == '' ? null : x.name)) {
|
||||
return new Error('invalid person: invalid name');
|
||||
if (x.name != null && x.name != '') {
|
||||
if (!Users.validateName.ok(x.name)) {
|
||||
return new Error('invalid person: invalid name');
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof x.id !== 'string') {
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { request } from 'https';
|
||||
import { sign } from 'http-signature';
|
||||
import { URL } from 'url';
|
||||
import * as crypto from 'crypto';
|
||||
import { lookup, IRunOptions } from 'lookup-dns-cache';
|
||||
import * as promiseAny from 'promise-any';
|
||||
@ -9,7 +8,7 @@ import config from '../../config';
|
||||
import { ILocalUser } from '../../models/entities/user';
|
||||
import { publishApLogStream } from '../../services/stream';
|
||||
import { apLogger } from './logger';
|
||||
import { UserKeypairs } from '../../models';
|
||||
import { UserKeypairs, Instances } from '../../models';
|
||||
import { fetchMeta } from '../../misc/fetch-meta';
|
||||
import { toPuny } from '../../misc/convert-host';
|
||||
import { ensure } from '../../prelude/ensure';
|
||||
@ -17,15 +16,30 @@ import { ensure } from '../../prelude/ensure';
|
||||
export const logger = apLogger.createSubLogger('deliver');
|
||||
|
||||
export default async (user: ILocalUser, url: string, object: any) => {
|
||||
logger.info(`--> ${url}`);
|
||||
|
||||
const timeout = 10 * 1000;
|
||||
|
||||
const { protocol, host, hostname, port, pathname, search } = new URL(url);
|
||||
|
||||
// ブロックしてたら中断
|
||||
const meta = await fetchMeta();
|
||||
if (meta.blockedHosts.includes(toPuny(host))) return;
|
||||
if (meta.blockedHosts.includes(toPuny(host))) {
|
||||
logger.info(`skip (blocked) ${url}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// closedなら中断
|
||||
const closedHosts = await Instances.find({
|
||||
where: {
|
||||
isMarkedAsClosed: true
|
||||
},
|
||||
cache: 60 * 1000
|
||||
});
|
||||
if (closedHosts.map(x => x.host).includes(toPuny(host))) {
|
||||
logger.info(`skip (closed) ${url}`);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info(`--> ${url}`);
|
||||
|
||||
const data = JSON.stringify(object);
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
import webFinger from './webfinger';
|
||||
import config from '../config';
|
||||
import { createPerson, updatePerson } from './activitypub/models/person';
|
||||
import { URL } from 'url';
|
||||
import { remoteLogger } from './logger';
|
||||
import chalk from 'chalk';
|
||||
import { User, IRemoteUser } from '../models/entities/user';
|
||||
|
@ -1,6 +1,5 @@
|
||||
import config from '../config';
|
||||
import * as request from 'request-promise-native';
|
||||
import { URL } from 'url';
|
||||
import { query as urlQuery } from '../prelude/url';
|
||||
|
||||
type ILink = {
|
||||
|
37
src/server/api/endpoints/admin/get-table-stats.ts
Normal file
37
src/server/api/endpoints/admin/get-table-stats.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import define from '../../define';
|
||||
import { getConnection } from 'typeorm';
|
||||
|
||||
export const meta = {
|
||||
requireCredential: false,
|
||||
|
||||
desc: {
|
||||
'en-US': 'Get table stats'
|
||||
},
|
||||
|
||||
tags: ['meta'],
|
||||
|
||||
params: {
|
||||
},
|
||||
};
|
||||
|
||||
export default define(meta, async () => {
|
||||
const sizes = await
|
||||
getConnection().query(`
|
||||
SELECT relname AS "table", reltuples as "count", pg_total_relation_size(C.oid) AS "size"
|
||||
FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
|
||||
WHERE nspname NOT IN ('pg_catalog', 'information_schema')
|
||||
AND C.relkind <> 'i'
|
||||
AND nspname !~ '^pg_toast';`)
|
||||
.then(recs => {
|
||||
const res = {} as Record<string, { count: number; size: number; }>;
|
||||
for (const rec of recs) {
|
||||
res[rec.table] = {
|
||||
count: parseInt(rec.count, 10),
|
||||
size: parseInt(rec.size, 10),
|
||||
};
|
||||
}
|
||||
return res;
|
||||
});
|
||||
|
||||
return sizes;
|
||||
});
|
@ -8,6 +8,7 @@ import * as bcrypt from 'bcryptjs';
|
||||
import { Users, UserProfiles } from '../../../../models';
|
||||
import { ensure } from '../../../../prelude/ensure';
|
||||
import { sendEmail } from '../../../../services/send-email';
|
||||
import { ApiError } from '../../error';
|
||||
|
||||
export const meta = {
|
||||
requireCredential: true,
|
||||
@ -27,6 +28,14 @@ export const meta = {
|
||||
email: {
|
||||
validator: $.optional.nullable.str
|
||||
},
|
||||
},
|
||||
|
||||
errors: {
|
||||
incorrectPassword: {
|
||||
message: 'Incorrect password.',
|
||||
code: 'INCORRECT_PASSWORD',
|
||||
id: 'e54c1d7e-e7d6-4103-86b6-0a95069b4ad3'
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@ -37,7 +46,7 @@ export default define(meta, async (ps, user) => {
|
||||
const same = await bcrypt.compare(ps.password, profile.password!);
|
||||
|
||||
if (!same) {
|
||||
throw new Error('incorrect password');
|
||||
throw new ApiError(meta.errors.incorrectPassword);
|
||||
}
|
||||
|
||||
await UserProfiles.update({ userId: user.id }, {
|
||||
|
@ -29,14 +29,14 @@ export const meta = {
|
||||
|
||||
params: {
|
||||
name: {
|
||||
validator: $.optional.nullable.str.pipe(Users.isValidName),
|
||||
validator: $.optional.nullable.use(Users.validateName),
|
||||
desc: {
|
||||
'ja-JP': '名前(ハンドルネームやニックネーム)'
|
||||
}
|
||||
},
|
||||
|
||||
description: {
|
||||
validator: $.optional.nullable.str.pipe(Users.isValidDescription),
|
||||
validator: $.optional.nullable.use(Users.validateDescription),
|
||||
desc: {
|
||||
'ja-JP': 'アカウントの説明や自己紹介'
|
||||
}
|
||||
@ -50,14 +50,14 @@ export const meta = {
|
||||
},
|
||||
|
||||
location: {
|
||||
validator: $.optional.nullable.str.pipe(Users.isValidLocation),
|
||||
validator: $.optional.nullable.use(Users.validateLocation),
|
||||
desc: {
|
||||
'ja-JP': '住んでいる地域、所在'
|
||||
}
|
||||
},
|
||||
|
||||
birthday: {
|
||||
validator: $.optional.nullable.str.pipe(Users.isValidBirthday),
|
||||
validator: $.optional.nullable.use(Users.validateBirthday),
|
||||
desc: {
|
||||
'ja-JP': '誕生日 (YYYY-MM-DD形式)'
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ export const meta = {
|
||||
},
|
||||
|
||||
text: {
|
||||
validator: $.optional.str.pipe(MessagingMessages.isValidText)
|
||||
validator: $.optional.str.pipe(MessagingMessages.validateText)
|
||||
},
|
||||
|
||||
fileId: {
|
||||
|
@ -6,6 +6,7 @@ import { fetchMeta } from '../../../misc/fetch-meta';
|
||||
import * as pkg from '../../../../package.json';
|
||||
import { Emojis } from '../../../models';
|
||||
import { types, bool } from '../../../misc/schema';
|
||||
import { getConnection } from 'typeorm';
|
||||
|
||||
export const meta = {
|
||||
stability: 'stable',
|
||||
@ -114,6 +115,7 @@ export default define(meta, async (ps, me) => {
|
||||
machine: os.hostname(),
|
||||
os: os.platform(),
|
||||
node: process.version,
|
||||
psql: await getConnection().query('SHOW server_version').then(x => x[0].server_version),
|
||||
|
||||
cpu: {
|
||||
model: os.cpus()[0].model,
|
||||
|
@ -9,7 +9,7 @@ export const meta = {
|
||||
|
||||
params: {
|
||||
username: {
|
||||
validator: $.str.pipe(Users.validateUsername)
|
||||
validator: $.use(Users.validateLocalUsername)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -66,7 +66,7 @@ export const meta = {
|
||||
};
|
||||
|
||||
export default define(meta, async (ps, me) => {
|
||||
const isUsername = Users.validateUsername(ps.query.replace('@', ''), !ps.localOnly);
|
||||
const isUsername = ps.localOnly ? Users.validateLocalUsername.ok(ps.query.replace('@', '')) : Users.validateRemoteUsername.ok(ps.query.replace('@', ''));
|
||||
|
||||
let users: User[] = [];
|
||||
|
||||
|
@ -58,13 +58,13 @@ export default async (ctx: Koa.BaseContext) => {
|
||||
}
|
||||
|
||||
// Validate username
|
||||
if (!Users.validateUsername(username)) {
|
||||
if (!Users.validateLocalUsername.ok(username)) {
|
||||
ctx.status = 400;
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate password
|
||||
if (!Users.validatePassword(password)) {
|
||||
if (!Users.validatePassword.ok(password)) {
|
||||
ctx.status = 400;
|
||||
return;
|
||||
}
|
||||
|
@ -17,6 +17,8 @@ export async function proxyMedia(ctx: Koa.BaseContext) {
|
||||
|
||||
const [type, ext] = await detectMine(path);
|
||||
|
||||
if (!type.startsWith('image/')) throw 403;
|
||||
|
||||
let image: IImage;
|
||||
|
||||
if ('static' in ctx.query && ['image/png', 'image/gif'].includes(type)) {
|
||||
|
@ -36,7 +36,7 @@ module.exports = async (ctx: Koa.BaseContext) => {
|
||||
|
||||
ctx.body = summary;
|
||||
} catch (e) {
|
||||
logger.error(`Failed to get preview of ${ctx.query.url}: ${e}`);
|
||||
logger.warn(`Failed to get preview of ${ctx.query.url}: ${e}`);
|
||||
ctx.status = 200;
|
||||
ctx.set('Cache-Control', 'max-age=86400, immutable');
|
||||
ctx.body = '{}';
|
||||
|
@ -163,6 +163,19 @@ export default abstract class Chart<T extends Record<string, any>> {
|
||||
},
|
||||
...Chart.convertSchemaToFlatColumnDefinitions(schema)
|
||||
},
|
||||
indices: [{
|
||||
columns: ['date']
|
||||
}, {
|
||||
columns: ['span']
|
||||
}, {
|
||||
columns: ['group']
|
||||
}, {
|
||||
columns: ['span', 'date']
|
||||
}, {
|
||||
columns: ['date', 'group']
|
||||
}, {
|
||||
columns: ['span', 'date', 'group']
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -149,18 +149,22 @@ export async function generateAlts(path: string, type: string, generateWeb: bool
|
||||
if (generateWeb) {
|
||||
logger.info(`creating web image`);
|
||||
|
||||
if (['image/jpeg'].includes(type)) {
|
||||
webpublic = await convertToJpeg(path, 2048, 2048);
|
||||
} else if (['image/webp'].includes(type)) {
|
||||
webpublic = await convertToWebp(path, 2048, 2048);
|
||||
} else if (['image/png'].includes(type)) {
|
||||
webpublic = await convertToPng(path, 2048, 2048);
|
||||
} else if (['image/apng', 'image/vnd.mozilla.apng'].includes(type)) {
|
||||
webpublic = await convertToApng(path);
|
||||
} else if (['image/gif'].includes(type)) {
|
||||
webpublic = await convertToGif(path);
|
||||
} else {
|
||||
logger.info(`web image not created (not an image)`);
|
||||
try {
|
||||
if (['image/jpeg'].includes(type)) {
|
||||
webpublic = await convertToJpeg(path, 2048, 2048);
|
||||
} else if (['image/webp'].includes(type)) {
|
||||
webpublic = await convertToWebp(path, 2048, 2048);
|
||||
} else if (['image/png'].includes(type)) {
|
||||
webpublic = await convertToPng(path, 2048, 2048);
|
||||
} else if (['image/apng', 'image/vnd.mozilla.apng'].includes(type)) {
|
||||
webpublic = await convertToApng(path);
|
||||
} else if (['image/gif'].includes(type)) {
|
||||
webpublic = await convertToGif(path);
|
||||
} else {
|
||||
logger.info(`web image not created (not an image)`);
|
||||
}
|
||||
} catch (e) {
|
||||
logger.warn(`web image not created (an error occured)`, e);
|
||||
}
|
||||
} else {
|
||||
logger.info(`web image not created (from remote)`);
|
||||
@ -170,18 +174,22 @@ export async function generateAlts(path: string, type: string, generateWeb: bool
|
||||
// #region thumbnail
|
||||
let thumbnail: IImage | null = null;
|
||||
|
||||
if (['image/jpeg', 'image/webp'].includes(type)) {
|
||||
thumbnail = await convertToJpeg(path, 498, 280);
|
||||
} else if (['image/png'].includes(type)) {
|
||||
thumbnail = await convertToPng(path, 498, 280);
|
||||
} else if (['image/gif'].includes(type)) {
|
||||
thumbnail = await convertToGif(path);
|
||||
} else if (type.startsWith('video/')) {
|
||||
try {
|
||||
thumbnail = await GenerateVideoThumbnail(path);
|
||||
} catch (e) {
|
||||
logger.error(`GenerateVideoThumbnail failed: ${e}`);
|
||||
try {
|
||||
if (['image/jpeg', 'image/webp'].includes(type)) {
|
||||
thumbnail = await convertToJpeg(path, 498, 280);
|
||||
} else if (['image/png'].includes(type)) {
|
||||
thumbnail = await convertToPng(path, 498, 280);
|
||||
} else if (['image/gif'].includes(type)) {
|
||||
thumbnail = await convertToGif(path);
|
||||
} else if (type.startsWith('video/')) {
|
||||
try {
|
||||
thumbnail = await GenerateVideoThumbnail(path);
|
||||
} catch (e) {
|
||||
logger.error(`GenerateVideoThumbnail failed: ${e}`);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
logger.warn(`thumbnail not created (an error occured)`, e);
|
||||
}
|
||||
// #endregion thumbnail
|
||||
|
||||
|
@ -90,7 +90,7 @@ export default class Logger {
|
||||
}
|
||||
}
|
||||
|
||||
public warn(message: string, data?: Record<string, any> | null, important = false): void { // 実行を継続できるが改善すべき状況で使う
|
||||
public warn(message: string, data?: Record<string, any> | null, important = false): void { // 実行を継続できるが改善すべき状況で使う
|
||||
this.log('warning', message, data, important);
|
||||
}
|
||||
|
||||
|
34
test/mfm.ts
34
test/mfm.ts
@ -804,6 +804,14 @@ describe('MFM', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('ignore trailing periods', () => {
|
||||
const tokens = parse('https://example.com...');
|
||||
assert.deepStrictEqual(tokens, [
|
||||
leaf('url', { url: 'https://example.com' }),
|
||||
text('...')
|
||||
]);
|
||||
});
|
||||
|
||||
it('with comma', () => {
|
||||
const tokens = parse('https://example.com/foo?bar=a,b');
|
||||
assert.deepStrictEqual(tokens, [
|
||||
@ -1116,6 +1124,14 @@ describe('MFM', () => {
|
||||
], {}),
|
||||
]);
|
||||
});
|
||||
|
||||
// https://misskey.io/notes/7u1kv5dmia
|
||||
it('ignore internal tilde', () => {
|
||||
const tokens = parse('~~~~~');
|
||||
assert.deepStrictEqual(tokens, [
|
||||
text('~~~~~')
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('italic', () => {
|
||||
@ -1173,6 +1189,24 @@ describe('MFM', () => {
|
||||
text('foo_bar_baz'),
|
||||
]);
|
||||
});
|
||||
|
||||
it('require spaces', () => {
|
||||
const tokens = parse('4日目_L38b a_b');
|
||||
assert.deepStrictEqual(tokens, [
|
||||
text('4日目_L38b a_b'),
|
||||
]);
|
||||
});
|
||||
|
||||
it('newline sandwich', () => {
|
||||
const tokens = parse('foo\n_bar_\nbaz');
|
||||
assert.deepStrictEqual(tokens, [
|
||||
text('foo\n'),
|
||||
tree('italic', [
|
||||
text('bar')
|
||||
], {}),
|
||||
text('\nbaz'),
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
Reference in New Issue
Block a user