Compare commits

..

52 Commits

Author SHA1 Message Date
syuilo
80d8af84dd Merge branch 'develop' 2019-06-18 19:16:41 +09:00
syuilo
aac519bf80 11.22.0 2019-06-18 19:15:55 +09:00
syuilo
d64dffbdda Add index 2019-06-18 17:11:28 +09:00
syuilo
4015ccef2f Add chart indices 2019-06-18 17:10:28 +09:00
syuilo
b6f985abaf Update summaly to 2.3.0 2019-06-18 16:58:32 +09:00
syuilo
2dac8d3d1f Update db.vue 2019-06-18 16:53:14 +09:00
syuilo
4f284e1bc0 Resolve #5063 2019-06-18 16:49:58 +09:00
syuilo
b0280355e8 Better request interval 2019-06-18 16:17:20 +09:00
syuilo
048d88b784 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-06-18 15:56:31 +09:00
syuilo
bb52ebdc3e Provide version of postgresql 2019-06-18 15:56:03 +09:00
Satsuki Yanagi
5f2fda85ba Resolve #5069 (#5070) 2019-06-18 15:42:08 +09:00
syuilo
1331f0b953 Use WHATWG API
> New application code should use the WHATWG API.
2019-06-18 15:27:13 +09:00
syuilo
736fdabc1d Fix #2637 2019-06-18 15:19:19 +09:00
syuilo
7096c0ca49 Add note 2019-06-18 14:04:41 +09:00
syuilo
1a984de8e8 Better error handling 2019-06-18 13:58:59 +09:00
syuilo
285d0d13f9 Fix MFM URL parsing 2019-06-17 20:15:19 +09:00
syuilo
63c659bc8f Fix MFM strike parsing 2019-06-16 21:42:57 +09:00
syuilo
af60b45ee7 Fix MFM italic parsing 2019-06-16 21:30:51 +09:00
syuilo
e7effd606d Fix test 2019-06-16 21:30:26 +09:00
syuilo
5042d23bc4 Simplify test 2019-06-16 21:29:31 +09:00
syuilo
b134467bd3 Add some MFM tests 2019-06-16 21:26:43 +09:00
syuilo
5cc1aab530 Merge branch 'develop' 2019-06-16 16:09:04 +09:00
syuilo
49d57ce049 11.21.0 2019-06-16 16:08:36 +09:00
syuilo
f1feceaf1a Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-06-16 16:08:29 +09:00
syuilo
1204e1c5e4 Update dependencies 🚀 2019-06-16 16:07:57 +09:00
syuilo
96cf4c30cf New Crowdin translations (#5053)
* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Korean)

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

* New translations ja-JP.yml (Danish)

* New translations ja-JP.yml (Korean)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (French)
2019-06-16 16:06:00 +09:00
MeiMei
a3853611ad Fix: オートコンプリートにアニメーション停止が効かない (#5061) 2019-06-16 16:04:27 +09:00
MeiMei
ef7eaaabfa Support Unicode 12.0 Emoji (#5062) 2019-06-16 14:24:37 +09:00
MeiMei
a4a96710b0 閉鎖しているホストにはAP deliverしないように (#5057)
* 閉鎖しているホストにはAP deliverしないように

* fix case
2019-06-15 17:09:59 +09:00
syuilo
6d08909b2f Refactor 2019-06-15 17:06:03 +09:00
syuilo
2615368b1e Resolve #365 2019-06-15 00:07:41 +09:00
Aya Morisawa
2bd03ca725 Use halfwidth space instead of fullwidth space in code (#5054) 2019-06-14 22:54:19 +09:00
MeiMei
67dda01fcb image以外はproxyしないように (#5051) 2019-06-14 12:14:23 +09:00
syuilo
e9dc54c4d9 サムネイル生成でエラーになってもファイルのアップロードを失敗しないように
#5050
2019-06-13 22:59:15 +09:00
syuilo
fde9fc2891 Make url preview log warn 2019-06-13 22:35:37 +09:00
syuilo
6e59798e82 Merge branch 'develop' 2019-06-13 18:22:49 +09:00
syuilo
5e3cc0a3c6 11.20.4 2019-06-13 18:22:31 +09:00
syuilo
cfb35324d0 Fix bug 2019-06-13 18:15:35 +09:00
syuilo
9bfbc12afa Add indexes 2019-06-13 15:30:51 +09:00
MeiMei
b41cddaf5a Fix: #5035 (#5048) 2019-06-12 13:47:58 +09:00
Satsuki Yanagi
1432a7193d 設定でPostgreSQLのクエリー結果のキャッシュを無効できるように (#5046) 2019-06-11 20:49:08 +09:00
Acid Chicken (硫酸鶏)
20630cb3a0 Update README.md [AUTOGEN] (#5044) 2019-06-11 16:57:52 +09:00
syuilo
2eb02ae581 Merge branch 'develop' 2019-06-10 23:11:22 +09:00
syuilo
801f9027aa 11.20.3 2019-06-10 23:11:04 +09:00
syuilo
802739a2df Fix bug 2019-06-10 23:09:36 +09:00
syuilo
95b1e1a21d Merge branch 'develop' 2019-06-10 11:44:01 +09:00
syuilo
b8060a522a 11.20.2 2019-06-10 11:43:45 +09:00
syuilo
fe2191a491 New Crowdin translations (#5040)
* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Korean)
2019-06-10 11:42:06 +09:00
syuilo
2891f52817 Merge branch 'riamu' into develop 2019-06-10 11:41:07 +09:00
Satsuki Yanagi
5bdc44c672 ✌️ (#5041)
related #5020
2019-06-10 01:02:33 +09:00
syuilo
65d359b57a Fix #5020 2019-06-09 23:07:32 +09:00
Acid Chicken (硫酸鶏)
155da0c6a3 Avoid roma-ji naming 2019-04-10 20:30:00 +09:00
57 changed files with 421 additions and 168 deletions

View File

@@ -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

View File

@@ -17,6 +17,48 @@ 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
* Redisにパスワードを設定している場合接続できない問題を修正
* i18n
* など
11.20.1 (2019/06/07)
--------------------
### 🐛Fixes

View File

@@ -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

View File

@@ -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:

View File

@@ -1761,8 +1761,7 @@ pages:
_divide:
arg1: "A"
arg2: "B"
remind: "÷ Tomo"
_remind:
_mod:
arg1: "A"
arg2: "B"
eq: "A og B er ens"

View File

@@ -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"

View File

@@ -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 navez pas encore dabonné·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 dutilisateurs : augmentation/diminution"
users-total: "Nombre total des utilisateurs"
users: "Nombre dutilisateur·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 dutilisateurs·trices : augmentation/diminution"
users: "Nombre dutilisateur·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 dactualité"
@@ -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"

View File

@@ -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が同じ"

View File

@@ -4,7 +4,7 @@ meta:
common:
misskey: "연합우주의 ⭐"
about-title: "연합우주의 ⭐."
about: "Misskey를 찾아주셔서 감사합니다. Misskey는 지구에서 태어난 <b>분산 마이크로 블로그 SNS </b> 입니다. Fediverse(다양한 SNS로 구성되는 우주)에 존재하 다른 SNS와 상호 연결되어 있습니다. 잠시 도시의 번잡함에서 벗어나 새로운 인터넷에 다이브 해 보지 않겠습니까."
about: "Misskey를 발견해주셔서 감사합니다! Misskey는 지구에서 태어난 <b>분산 마이크로 블로그 SNS</b> 입니다. Fediverse (다양한 SNS가 함께하는 우주)에 존재하고 있어서, 다른 SNS와 서로 연결되어 있습니다. 번잡한 도시에서 벗어나 새로운 인터넷에 빠져보지 않으시겠어요?"
intro:
title: "Misskey란?"
about: "Misskey는 오픈소스 <b>분산형 마이크로블로그 SNS</b>입니다. 다양하고 폭넓게 커스터마이징할 수 있는 UI, 글에 대한 리액션, 파일을 관리할 수 있는 드라이브 등의 선진적인 기능을 갖추고 있습니다. 더하여 Fediverse라고 부르는 네트워크에 연결할 수 있어 다른 SNS와도 주고받을 수 있습니다. 예를 들자면, 당신이 무언가를 게시하면, 해당 게시물은 Misskey 뿐만 아니라 다른 SNS에도 전해집니다. 살짝 어떤 행성에서 다른 행성으로 전파를 발신하고 있는 모습을 상상해주세요."
@@ -1857,8 +1857,8 @@ pages:
_divide:
arg1: "A"
arg2: "B"
remind: "÷ 나눈 나머지"
_remind:
mod: "÷ 나눈 나머지"
_mod:
arg1: "A"
arg2: "B"
eq: "A와 B가 동일"

View File

@@ -1857,8 +1857,8 @@ pages:
_divide:
arg1: "A"
arg2: "B"
remind: "÷ 取模"
_remind:
mod: "÷ 取模"
_mod:
arg1: "A"
arg2: "B"
eq: "A和B相等"

View File

@@ -1,7 +1,7 @@
{
"name": "misskey",
"author": "syuilo <i@syuilo.com>",
"version": "11.20.1",
"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"

View File

@@ -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;

View 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>

View File

@@ -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: {

View File

@@ -1,7 +1,7 @@
<template>
<div class="form">
<header>
<h1 v-html="$t('share-access', { name: app.name })"></h1>
<h1 v-html="$t('share-access', { name })"></h1>
<img :src="app.iconUrl"/>
</header>
<div class="app">
@@ -34,6 +34,11 @@ export default Vue.extend({
i18n: i18n('auth/views/form.vue'),
props: ['session'],
computed: {
name(): string {
const el = document.createElement('div');
el.textContent = this.app.name
return el.innerHTML;
},
app(): any {
return this.session.app;
}

View File

@@ -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;

View File

@@ -16,7 +16,7 @@
</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" v-html="emoji.name.replace(q, `<b>${q}</b>`)"></span>
@@ -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: [],

View File

@@ -1,7 +1,7 @@
<template>
<div class="onchrpzrvnoruiaenfcqvccjfuupzzwv" :class="{ isMobile: $root.isMobile }">
<div class="backdrop" ref="backdrop" @click="close"></div>
<div class="popover" :class="{ hukidasi }" ref="popover">
<div class="popover" :class="{ bubble }" ref="popover">
<template v-for="item, i in items">
<div v-if="item === null"></div>
<button v-if="item" @click="clicked(item.action)" :tabindex="i">
@@ -28,7 +28,7 @@ export default Vue.extend({
},
data() {
return {
hukidasi: !this.$root.isMobile
bubble: !this.$root.isMobile
};
},
mounted() {
@@ -56,12 +56,12 @@ export default Vue.extend({
if (left + width - window.pageXOffset > window.innerWidth) {
left = window.innerWidth - width + window.pageXOffset;
this.hukidasi = false;
this.bubble = false;
}
if (top + height - window.pageYOffset > window.innerHeight) {
top = window.innerHeight - height + window.pageYOffset;
this.hukidasi = false;
this.bubble = false;
}
if (top < 0) {
@@ -150,7 +150,7 @@ export default Vue.extend({
$balloon-size = 16px
&.hukidasi
&.bubble
margin-top $balloon-size
transform-origin center -($balloon-size)

View File

@@ -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 = () => {

View File

@@ -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>

View File

@@ -11,7 +11,7 @@
<div class="weekday"
v-for="(day, i) in Array(7).fill(0)"
:data-today="year == today.getFullYear() && month == today.getMonth() + 1 && today.getDay() == i"
:data-is-donichi="i == 0 || i == 6"
:data-is-weekend="i == 0 || i == 6"
>{{ weekdayText[i] }}</div>
</template>
<div v-for="n in paddingDays"></div>
@@ -19,7 +19,7 @@
:data-today="isToday(i + 1)"
:data-selected="isSelected(i + 1)"
:data-is-out-of-range="isOutOfRange(i + 1)"
:data-is-donichi="isDonichi(i + 1)"
:data-is-weekend="isWeekend(i + 1)"
@click="go(i + 1)"
:title="isOutOfRange(i + 1) ? null : $t('go')"
>
@@ -96,7 +96,7 @@ export default Vue.extend({
(this.start ? test < (this.start as any).getTime() : false);
},
isDonichi(day) {
isWeekend(day) {
const weekday = (new Date(this.year, this.month - 1, day)).getDay();
return weekday == 0 || weekday == 6;
},
@@ -199,14 +199,14 @@ export default Vue.extend({
&.weekday
color var(--calendarWeek)
&[data-is-donichi]
&[data-is-weekend]
color var(--calendarSaturdayOrSunday)
&[data-today]
box-shadow 0 0 0 var(--lineWidth) var(--calendarWeek) inset
border-radius 6px
&[data-is-donichi]
&[data-is-weekend]
box-shadow 0 0 0 var(--lineWidth) var(--calendarSaturdayOrSunday) inset
&.day
@@ -222,7 +222,7 @@ export default Vue.extend({
&:active > div
background var(--faceClearButtonActive)
&[data-is-donichi]
&[data-is-weekend]
color var(--calendarSaturdayOrSunday)
&[data-is-out-of-range]

View File

@@ -33,6 +33,7 @@ const defaultSettings = {
mutedWords: [],
gamesReversiShowBoardLabels: false,
gamesReversiUseAvatarStones: true,
disableAnimatedMfm: false,
};
const defaultDeviceSettings = {

View File

@@ -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';

View File

@@ -14,6 +14,7 @@ export type Source = {
db: string;
user: string;
pass: string;
disableCache?: boolean;
extra?: { [x: string]: string };
};
redis: {

View File

@@ -96,18 +96,18 @@ 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,
port: config.redis.port,
options:{
auth_pass: config.redis.pass,
password: config.redis.pass,
prefix: config.redis.prefix,
db: config.redis.db || 0
}
}
},
} : false,
logging: log,
logger: log ? new MyCustomLogger() : undefined,
entities: [

View File

@@ -5,7 +5,7 @@ export default redis.createClient(
config.redis.port,
config.redis.host,
{
auth_pass: config.redis.pass,
password: config.redis.pass,
prefix: config.redis.prefix,
db: config.redis.db || 0
}

View File

@@ -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;
}
}

View File

@@ -1,5 +1,4 @@
import { parseFragment, DefaultTreeDocumentFragment } from 'parse5';
import { URL } from 'url';
import { urlRegex } from './prelude';
export function fromHtml(html: string): string {

View File

@@ -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 }));
},

View File

@@ -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),

View File

@@ -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, },

View File

@@ -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)}`;

View File

@@ -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

View File

@@ -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

View File

@@ -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.'

View File

@@ -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() != '';
}

View File

@@ -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
}

View File

@@ -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';

View File

@@ -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') {

View File

@@ -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);

View File

@@ -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';

View File

@@ -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 = {

View 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;
});

View File

@@ -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 }, {

View File

@@ -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形式)'
}

View File

@@ -44,7 +44,7 @@ export const meta = {
},
text: {
validator: $.optional.str.pipe(MessagingMessages.isValidText)
validator: $.optional.str.pipe(MessagingMessages.validateText)
},
fileId: {

View File

@@ -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,

View File

@@ -9,7 +9,7 @@ export const meta = {
params: {
username: {
validator: $.str.pipe(Users.validateUsername)
validator: $.use(Users.validateLocalUsername)
}
}
};

View File

@@ -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[] = [];

View File

@@ -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;
}

View File

@@ -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)) {

View File

@@ -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 = '{}';

View File

@@ -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']
}]
});
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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('日目_L38b a_b');
assert.deepStrictEqual(tokens, [
text('日目_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'),
]);
});
});
});