Compare commits
62 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9ffecf25dc | ||
![]() |
35fd523edf | ||
![]() |
6721d4216c | ||
![]() |
e3275e916b | ||
![]() |
3ba5541a66 | ||
![]() |
945c50db1f | ||
![]() |
30dce42e03 | ||
![]() |
d4fb201d05 | ||
![]() |
2a2e8d0cf6 | ||
![]() |
520ed8cb4d | ||
![]() |
8cab16c824 | ||
![]() |
ae63a1f494 | ||
![]() |
117ac53505 | ||
![]() |
2c379732d2 | ||
![]() |
9ca1197759 | ||
![]() |
8d3283e2a5 | ||
![]() |
6589e8a390 | ||
![]() |
b62894ff56 | ||
![]() |
da274cd458 | ||
![]() |
a2268a95be | ||
![]() |
9fd1b35d95 | ||
![]() |
869854eae7 | ||
![]() |
238f923b41 | ||
![]() |
a5df2b0293 | ||
![]() |
e6eae558d3 | ||
![]() |
083fa53d9c | ||
![]() |
7b73dd2d62 | ||
![]() |
7028b7331b | ||
![]() |
eefebab530 | ||
![]() |
683ddbef3e | ||
![]() |
bd23522c76 | ||
![]() |
c1dfbe2623 | ||
![]() |
ed9facbb33 | ||
![]() |
70df8c77fa | ||
![]() |
2c52655b17 | ||
![]() |
6c4c071ae9 | ||
![]() |
b19dba80f4 | ||
![]() |
09f4b9e546 | ||
![]() |
2e6d8c792b | ||
![]() |
e6338a555d | ||
![]() |
313a489ba0 | ||
![]() |
b906ff3fed | ||
![]() |
ede96eca28 | ||
![]() |
42f3d9188b | ||
![]() |
a35e0e9261 | ||
![]() |
80a400a67c | ||
![]() |
7a6534f30b | ||
![]() |
68a523ec6d | ||
![]() |
97d6c1ee86 | ||
![]() |
19c93151ce | ||
![]() |
039a2af3ab | ||
![]() |
945129c371 | ||
![]() |
da32be3ef3 | ||
![]() |
468ec36830 | ||
![]() |
492fb9a115 | ||
![]() |
bd8b624bae | ||
![]() |
9dacf11702 | ||
![]() |
26ae2dfc0f | ||
![]() |
a7f43d5312 | ||
![]() |
7fdf298bd4 | ||
![]() |
7d7167df6d | ||
![]() |
aa339be2ab |
44
CHANGELOG.md
44
CHANGELOG.md
@@ -9,6 +9,49 @@
|
||||
You should also include the user name that made the change.
|
||||
-->
|
||||
|
||||
## 13.2.2 (2023/01/25)
|
||||
### Improvements
|
||||
- サーバーのパフォーマンスを改善
|
||||
|
||||
### Bugfixes
|
||||
- サインイン時に誤ったレートリミットがかかることがある問題を修正
|
||||
- MFMのposition、rotate、scaleで小数が使えない問題を修正
|
||||
|
||||
## 13.2.1 (2023/01/24)
|
||||
### Improvements
|
||||
- デザインの調整
|
||||
- サーバーのパフォーマンスを改善
|
||||
|
||||
## 13.2.0 (2023/01/23)
|
||||
|
||||
### Improvements
|
||||
- onlyServer / onlyQueue オプションを復活
|
||||
- 他人の実績閲覧時は獲得条件を表示しないように
|
||||
- アニメーション減らすオプション有効時はリアクションのアニメーションを無効に
|
||||
- カスタム絵文字一覧のパフォーマンスを改善
|
||||
|
||||
### Bugfixes
|
||||
- Aiscript: button is not defined
|
||||
|
||||
## 13.1.7 (2023/01/22)
|
||||
|
||||
### Improvements
|
||||
- 新たな実績を追加
|
||||
- MFMにscaleタグを追加
|
||||
|
||||
## 13.1.4 (2023/01/22)
|
||||
|
||||
### Improvements
|
||||
- 新たな実績を追加
|
||||
|
||||
### Bugfixes
|
||||
- Client: ローカリゼーション更新時にリロードが繰り返されることがあるのを修正
|
||||
|
||||
## 13.1.3 (2023/01/22)
|
||||
|
||||
### Bugfixes
|
||||
- Client: リアクションのカスタム絵文字の表示の問題を修正
|
||||
|
||||
## 13.1.2 (2023/01/22)
|
||||
|
||||
### Bugfixes
|
||||
@@ -60,6 +103,7 @@ You should also include the user name that made the change.
|
||||
- Node.js 18.x or later is required
|
||||
- PostgreSQL 15.x is required
|
||||
- Misskey not using 15 specific features at 13.0.0, but may do so in the future.
|
||||
- Docker環境でPostgreSQLのアップデートを行う際のガイドはこちら: https://github.com/misskey-dev/misskey/pull/9641#issue-1536336620
|
||||
- Elasticsearchのサポートが削除されました
|
||||
- 代わりに今後任意の検索プロバイダを設定できる仕組みを構想しています。その仕組みを使えば今まで通りElasticsearchも利用できます
|
||||
- Yarnからpnpmに移行されました
|
||||
|
@@ -20,7 +20,7 @@ gulp.task('copy:frontend:fonts', () =>
|
||||
);
|
||||
|
||||
gulp.task('copy:frontend:tabler-icons', () =>
|
||||
gulp.src('./packages/frontend/node_modules/@tabler/icons/iconfont/**/*').pipe(gulp.dest('./built/_frontend_dist_/tabler-icons/'))
|
||||
gulp.src('./packages/frontend/node_modules/@tabler/icons-webfont/**/*').pipe(gulp.dest('./built/_frontend_dist_/tabler-icons/'))
|
||||
);
|
||||
|
||||
gulp.task('copy:frontend:locales', cb => {
|
||||
|
@@ -1048,6 +1048,9 @@ _achievements:
|
||||
_noteFavorited1:
|
||||
title: "Sternengucker"
|
||||
description: "Eine Notiz als Favorit markiert"
|
||||
_myNoteFavorited1:
|
||||
title: "Sternensucher"
|
||||
description: "Ein anderer Benutzer hat eine deiner Notizen als Favoriten markiert"
|
||||
_profileFilled:
|
||||
title: "Perfekte Vorbereitung"
|
||||
description: "Fülle dein Profil aus"
|
||||
@@ -1101,9 +1104,12 @@ _achievements:
|
||||
title: "I Love Misskey"
|
||||
description: "Sende \"I ❤ #Misskey\""
|
||||
flavor: "Danke, dass du Misskey verwendest! - vom Entwicklerteam"
|
||||
_foundTreasure:
|
||||
title: "Schatzsuche"
|
||||
description: "Du hast einen verborgenen Schatz gefunden"
|
||||
_client30min:
|
||||
title: "Kleine Pause"
|
||||
description: "Seit dem Öffnen deines Clients sind 30 Minuten vergangen"
|
||||
title: "Kurze Pause"
|
||||
description: "Habe Misskey für 30 Minuten geöffnet"
|
||||
_noteDeletedWithin1min:
|
||||
title: "Ups"
|
||||
description: "Lösche eine Notiz innerhalb von 1 Minute nachdem sie gesendet wurde"
|
||||
@@ -1121,6 +1127,9 @@ _achievements:
|
||||
_htl20npm:
|
||||
title: "Fließende Chronik"
|
||||
description: "Deine Startseitenchronik erreicht eine Geschwindigkeit von 20 npm (Notizen pro Minute)"
|
||||
_viewInstanceChart:
|
||||
title: "Analyst"
|
||||
description: "Schau dir die Messwerte der Instanz an"
|
||||
_outputHelloWorldOnScratchpad:
|
||||
title: "Hallo Welt!"
|
||||
description: "Gib \"hello world\" in der Testumgebung aus"
|
||||
|
@@ -945,7 +945,7 @@ _achievements:
|
||||
_notes1:
|
||||
title: "just setting up my msky"
|
||||
description: "Post your first note"
|
||||
flavor: "Have a good Misskey life!"
|
||||
flavor: "Have a good time with Misskey!"
|
||||
_notes10:
|
||||
title: "Some notes"
|
||||
description: "Post 10 notes"
|
||||
@@ -1048,6 +1048,9 @@ _achievements:
|
||||
_noteFavorited1:
|
||||
title: "Stargazer"
|
||||
description: "Favorite your first note"
|
||||
_myNoteFavorited1:
|
||||
title: "Seeking Stars"
|
||||
description: "Have somebody else favorite one of your notes"
|
||||
_profileFilled:
|
||||
title: "Well-prepared"
|
||||
description: "Set up your profile"
|
||||
@@ -1101,6 +1104,9 @@ _achievements:
|
||||
title: "I Love Misskey"
|
||||
description: "Post \"I ❤ #Misskey\""
|
||||
flavor: "Misskey's development team greatly appreciates your support!"
|
||||
_foundTreasure:
|
||||
title: "Treasure Hunt"
|
||||
description: "You've found the hidden treasure"
|
||||
_client30min:
|
||||
title: "Short break"
|
||||
description: "Spend 30 minutes on Misskey"
|
||||
@@ -1121,6 +1127,9 @@ _achievements:
|
||||
_htl20npm:
|
||||
title: "Flowing Timeline"
|
||||
description: "Have the speed of your home timeline exceed 20 npm (notes per minute)"
|
||||
_viewInstanceChart:
|
||||
title: "Analyst"
|
||||
description: "View your instance's charts"
|
||||
_outputHelloWorldOnScratchpad:
|
||||
title: "Hello, world!"
|
||||
description: "Output \"hello world\" in the Scratchpad"
|
||||
|
@@ -1,7 +1,7 @@
|
||||
---
|
||||
_lang_: "Italiano"
|
||||
headlineMisskey: "Rete collegata tramite note"
|
||||
introMisskey: "Eccoci! Misskey è un servizio di microblogging decentralizzato, libero e aperto. \n📡 Puoi pubblicare «Note» per condividere ciò che sta succedendo o per dire a tutti qualcosa su di te. \n👍 Puoi reagire inviando emoji rapidi alle «Note» provenienti da altri profili nel Fediverso.\n🚀 Esplora un nuovo mondo insieme a noi!"
|
||||
introMisskey: "Eccoci! Misskey è un servizio di microblogging decentralizzato, libero e aperto. \n\n📡 Puoi pubblicare «Note» per condividere ciò che sta succedendo o per dire a tutti qualcosa su di te. \n\n👍 Puoi reagire inviando emoji rapidi alle «Note» provenienti da altri profili nel Fediverso.\n\n🚀 Esplora un nuovo mondo insieme a noi!"
|
||||
poweredByMisskeyDescription: "{name} è uno dei servizi (chiamati istanze) che utilizzano la piattaforma open source <b>Misskey</b>."
|
||||
monthAndDay: "{day}/{month}"
|
||||
search: "Cerca"
|
||||
@@ -95,7 +95,7 @@ follow: "Segui"
|
||||
followRequest: "Richiesta di follow"
|
||||
followRequests: "Richieste di follow"
|
||||
unfollow: "Smetti di seguire"
|
||||
followRequestPending: "La richiesta di follow deve essere approvata"
|
||||
followRequestPending: "Richiesta in approvazione"
|
||||
enterEmoji: "Inserisci emoji"
|
||||
renote: "Rinota"
|
||||
unrenote: "Annulla rinota"
|
||||
@@ -938,6 +938,243 @@ cannotPerformTemporary: "Indisponibilità temporanea"
|
||||
cannotPerformTemporaryDescription: "L'attività non può essere svolta, poiché si è raggiunto il limite di esecuzioni possibili. Per favore, riprova più tardi."
|
||||
preset: "Preimpostato"
|
||||
selectFromPresets: "Seleziona preimpostato"
|
||||
achievements: "Obiettivi raggiunti"
|
||||
_achievements:
|
||||
earnedAt: "Data di conseguimento"
|
||||
_types:
|
||||
_notes1:
|
||||
title: "Hai iniziato a usare Misskey"
|
||||
description: "Hai pubblicato la prima Nota"
|
||||
flavor: "Goditi la vita su Misskey!"
|
||||
_notes10:
|
||||
title: "Alcune Note"
|
||||
description: "Hai inserito 10 Note"
|
||||
_notes100:
|
||||
title: "Un po' di Note"
|
||||
description: "Hai inserito 100 Note"
|
||||
_notes500:
|
||||
title: "Un bel po' di Note"
|
||||
description: "Hai inserito 500 Note"
|
||||
_notes1000:
|
||||
title: "Una montagna di Note"
|
||||
description: "Hai inserito 1.000 Note"
|
||||
_notes5000:
|
||||
title: "Un sovraccarico di Note!"
|
||||
description: "Hai inserito 5.000 Note"
|
||||
_notes10000:
|
||||
title: "SuperNote!"
|
||||
description: "Hai inserito 10.000 Note"
|
||||
_notes20000:
|
||||
title: "Voglio più... Note!"
|
||||
description: "Hai inserito 20.000 Note"
|
||||
_notes30000:
|
||||
title: "Note, Note, Note!"
|
||||
description: "Hai inserito 30.000 Note"
|
||||
_notes40000:
|
||||
title: "Una fabbrica di Note"
|
||||
description: "Hai inserito 40.000 Note"
|
||||
_notes50000:
|
||||
title: "Un pianeta di Note"
|
||||
description: "Hai inserito 50.000 Note"
|
||||
_notes60000:
|
||||
title: "Un quasar di Note"
|
||||
description: "Hai inserito 60.000 Note"
|
||||
_notes70000:
|
||||
title: "Un buco nero supermassiccio di Note"
|
||||
description: "Hai inserito 70.000 Note"
|
||||
_notes80000:
|
||||
title: "Una galassia di Note"
|
||||
description: "Hai inserito 80.000 Note"
|
||||
_notes90000:
|
||||
title: "Un universo di Note!"
|
||||
description: "Hai inserito 90.000 Note"
|
||||
_notes100000:
|
||||
title: "ALL YOUR NOTE ARE BELONG TO US"
|
||||
description: "Hai inserito 100.000 Note"
|
||||
flavor: "Hai molto da scrivere?"
|
||||
_login3:
|
||||
title: "Principiante I"
|
||||
description: "Accedi per un totale di 3 giorni"
|
||||
flavor: "Da oggi, chiamatemi Misskist"
|
||||
_login7:
|
||||
title: "Principiante II"
|
||||
description: "Accedi per un totale di 7 giorni"
|
||||
flavor: "Ti sembra di avere la situazione sotto controllo?"
|
||||
_login15:
|
||||
title: "Principiante III"
|
||||
description: "Accedi per un totale di 15 giorni"
|
||||
_login30:
|
||||
title: "Misskist I"
|
||||
description: "Accedi per un totale di 30 giorni"
|
||||
_login60:
|
||||
title: "Misskeist II"
|
||||
description: "Accedi per un totale di 60 giorni"
|
||||
_login100:
|
||||
title: "Misskeist III"
|
||||
description: "Accedi per un totale di 100 giorni"
|
||||
flavor: "Violent Misskeist"
|
||||
_login200:
|
||||
title: "Regolare I"
|
||||
description: "Accedi per un totale di 200 giorni"
|
||||
_login300:
|
||||
title: "Regolare II"
|
||||
description: "Accedi per un totale di 300 giorni"
|
||||
_login400:
|
||||
title: "Regolare III"
|
||||
description: "Accedi per un totale di 400 giorni"
|
||||
_login500:
|
||||
title: "Professionista I"
|
||||
description: "Accedi per un totale di 500 giorni"
|
||||
flavor: "Amici cari, mi piacciono le Note"
|
||||
_login600:
|
||||
title: "Professionista II"
|
||||
description: "Accedi per un totale di 600 giorni"
|
||||
_login700:
|
||||
title: "Professionista III"
|
||||
description: "Accedi per un totale di 700 giorni"
|
||||
_login800:
|
||||
title: "Maestro di Note I"
|
||||
description: "Accedi per un totale di 800 giorni"
|
||||
_login900:
|
||||
title: "Maestro di Note II"
|
||||
description: "Accedi per un totale di 900 giorni"
|
||||
_login1000:
|
||||
title: "Maestro di Note III"
|
||||
description: "Accedi per un totale di 1.000 giorni"
|
||||
flavor: "Grazie per aver usato Misskey!"
|
||||
_noteClipped1:
|
||||
title: "Devo clippare!"
|
||||
description: "Ho raccolto in Clip la prima Nota"
|
||||
_noteFavorited1:
|
||||
title: "Guarda le stelle"
|
||||
description: "Aggiungi una Nota ai preferiti per la prima volta"
|
||||
_myNoteFavorited1:
|
||||
title: "Fornitura stelline"
|
||||
description: "Qualcuno ha preferito una delle tue Note"
|
||||
_profileFilled:
|
||||
title: "Perfettamente"
|
||||
description: "Imposta il tuo profilo"
|
||||
_markedAsCat:
|
||||
title: "Io sono un gatto"
|
||||
description: "Aggiungi le orecchie da gatto al tuo profilo"
|
||||
flavor: "Ti chiamerò..."
|
||||
_following1:
|
||||
title: "Il mio primo Follow"
|
||||
description: "Hai seguito il tuo primo profilo"
|
||||
_following10:
|
||||
title: "Segui, segui!"
|
||||
description: "Hai seguito 10 profili"
|
||||
_following50:
|
||||
title: "Tanti amici"
|
||||
description: "Hai seguito 50 profili"
|
||||
_following100:
|
||||
title: "Cento amici"
|
||||
description: "Hai seguito 100 profili"
|
||||
_following300:
|
||||
title: "Sovraccarico di amici"
|
||||
description: "Hai seguito 300 profili"
|
||||
_followers1:
|
||||
title: "Il primo profilo tuo Follower"
|
||||
description: "Hai ottenuto il tuo primo Follower"
|
||||
_followers10:
|
||||
title: "Follow me!"
|
||||
description: "Hai ottenuto 10 profili Follower"
|
||||
_followers50:
|
||||
title: "Follower a frotte"
|
||||
description: "Hai ottenuto 50 Follower"
|
||||
_followers100:
|
||||
title: "Popolare"
|
||||
description: "Hai ottenuto 100 profili Follower"
|
||||
_followers300:
|
||||
title: "Mettetevi in fila"
|
||||
description: "Hai ottenuto 300 Follower"
|
||||
_followers500:
|
||||
title: "Trasmettitore"
|
||||
description: "Hai ottenuto 500 Follower"
|
||||
_followers1000:
|
||||
title: "Influenzer"
|
||||
description: "Hai superato i 1.000 profili Follower"
|
||||
_collectAchievements30:
|
||||
title: "Collezionista di successi"
|
||||
description: "Hai raggiunto 30 obiettivi"
|
||||
_viewAchievements3min:
|
||||
title: "Mi piacciono i risultati"
|
||||
description: "Guarda la tua collezione di obiettivi per almeno 3 minuti"
|
||||
_iLoveMisskey:
|
||||
title: "I LOVE Misskey"
|
||||
description: "Pubblica «I ♥ #Misskey»"
|
||||
flavor: "Grazie per aver utilizzato Misskey! Dal team di sviluppo"
|
||||
_foundTreasure:
|
||||
title: "Caccia al tesoro"
|
||||
description: "Hai trovato un tesoro nascosto"
|
||||
_client30min:
|
||||
title: "Piccola pausa"
|
||||
description: "Hai passato più di 30 minuti su Misskey"
|
||||
_noteDeletedWithin1min:
|
||||
title: "Ooops!"
|
||||
description: "Hai eliminato una nota entro un minuto dalla sua pubblicazione"
|
||||
_postedAtLateNight:
|
||||
title: "Biassanot!"
|
||||
description: "Hai pubblicato una nota in tarda notte"
|
||||
flavor: "Andiamo a dormire presto"
|
||||
_postedAt0min0sec:
|
||||
title: "Mezzanotte"
|
||||
description: "Hai pubblicato una Nota a mezzanotte in punto"
|
||||
flavor: "tic, tac, tic, tac! Gong!"
|
||||
_selfQuote:
|
||||
title: "Autoreferenziale"
|
||||
description: "Hai citato una delle tue Note"
|
||||
_htl20npm:
|
||||
title: "Timeline scorrevole"
|
||||
description: "La tua Timeline personale ha superato la velocità di 20 Note orarie (Note al minuto)"
|
||||
_viewInstanceChart:
|
||||
title: "Analista"
|
||||
description: "Visualizza i grafici dell'istanza"
|
||||
_outputHelloWorldOnScratchpad:
|
||||
title: "Hello, world!"
|
||||
description: "Hai scritto «Hello world» nel blocco appunti"
|
||||
_open3windows:
|
||||
title: "Finestrato"
|
||||
description: "Hai aperto almeno 3 finestre contemporaneamente"
|
||||
_driveFolderCircularReference:
|
||||
title: "Riferimento circolare"
|
||||
description: "Hai provato a nidificare in modo ricorsivo le cartelle del Drive"
|
||||
_reactWithoutRead:
|
||||
title: "Hai letto bene?"
|
||||
description: "Hai reagito ad una Nota più lunga di 100 caratteri entro 3 secondi dalla sua pubblicazione"
|
||||
_clickedClickHere:
|
||||
title: "Clicca qui"
|
||||
description: "Hai cliccato qui"
|
||||
_justPlainLucky:
|
||||
title: "Proprio fortunato"
|
||||
description: "Ottenuto con una probabilità dello 0,01% ogni 10 secondi"
|
||||
_setNameToSyuilo:
|
||||
title: "Complesso divino"
|
||||
description: "Hai impostati il tuo nome in «syuilo»"
|
||||
_passedSinceAccountCreated1:
|
||||
title: "Primo Anniversario"
|
||||
description: "È passato un anno da quando hai creato il profilo"
|
||||
_passedSinceAccountCreated2:
|
||||
title: "Secondo Anniversario"
|
||||
description: "Sono passati due anni da quando hai creato il profilo"
|
||||
_passedSinceAccountCreated3:
|
||||
title: "Terzo Anniversario"
|
||||
description: "Sono passati tre anni da quando hai creato il profilo"
|
||||
_loggedInOnBirthday:
|
||||
title: "Buon compleanno!"
|
||||
description: "Hai effettuato l'accesso il giorno del tuo compleanno"
|
||||
_loggedInOnNewYearsDay:
|
||||
title: "Buon anno nuovo!"
|
||||
description: "Hai usato effettuato l'accesso il giorno di capodanno"
|
||||
flavor: "Anche quest'anno, grazie per il tuo continuo supporto a questa istanza"
|
||||
_cookieClicked:
|
||||
title: "Clicca il biscotto"
|
||||
description: "Hai giocato a cliccare il cookie"
|
||||
flavor: "Hai autorizzato i cookie?"
|
||||
_brainDiver:
|
||||
title: "Brain Diver"
|
||||
description: "Pubblica un link a Brain Diver"
|
||||
flavor: "Sulle note di Brain Diver"
|
||||
_role:
|
||||
new: "Nuovo ruolo"
|
||||
edit: "Modifica ruolo"
|
||||
@@ -1270,8 +1507,8 @@ _sfx:
|
||||
channel: "Notifiche di canale"
|
||||
_ago:
|
||||
future: "Futuro"
|
||||
justNow: "Ora"
|
||||
secondsAgo: "{n}s fa"
|
||||
justNow: "Adesso"
|
||||
secondsAgo: "{n} sec fa"
|
||||
minutesAgo: "{n} min fa"
|
||||
hoursAgo: "{n} ore fa"
|
||||
daysAgo: "{n} gg fa"
|
||||
@@ -1293,7 +1530,7 @@ _tutorial:
|
||||
step3_1: "Hai finito di impostare il tuo profilo?"
|
||||
step3_2: "Ora puoi pubblicare una «Nota». Proviamo subito! Premi il bottone con l'icona «penna» per iniziare a scrivere in una finestra di dialogo. "
|
||||
step3_3: "Scritto il testo della nota, puoi pubblicarla premendo il pulsante nella parte superiore destra della finestra di dialogo."
|
||||
step3_4: "Non ti viene niente in mente? Perché non scrivi semplicemente \"Ho appena cominciato a usare Misskey\"?"
|
||||
step3_4: "Non ti viene niente in mente? Perché non scrivi semplicemente \"Ho appena iniziato a usare Misskey\"?"
|
||||
step4_1: "Hai pubblicato qualcosa?"
|
||||
step4_2: "Se puoi visualizzare la tua nota sulla timeline, ce l'hai fatta!"
|
||||
step5_1: "Adesso, cerca di seguire altre persone per vivacizzare la tua timeline. "
|
||||
@@ -1587,6 +1824,7 @@ _notification:
|
||||
pollEnded: "Risultati del sondaggio."
|
||||
unreadAntennaNote: "Antenna {name}"
|
||||
emptyPushNotificationMessage: "Le notifiche push sono state aggiornate."
|
||||
achievementEarned: "Obiettivo raggiunto"
|
||||
_types:
|
||||
all: "Tutto"
|
||||
follow: "Novità follower"
|
||||
|
@@ -1049,6 +1049,9 @@ _achievements:
|
||||
_noteFavorited1:
|
||||
title: "星をみるひと"
|
||||
description: "初めてノートをお気に入りに登録した"
|
||||
_myNoteFavorited1:
|
||||
title: "星が欲しい"
|
||||
description: "自分のノートが他の人からお気に入りに登録された"
|
||||
_profileFilled:
|
||||
title: "準備万端"
|
||||
description: "プロフィール設定を行った"
|
||||
@@ -1102,6 +1105,9 @@ _achievements:
|
||||
title: "I Love Misskey"
|
||||
description: "\"I ❤ #Misskey\"を投稿した"
|
||||
flavor: "Misskeyを使ってくださりありがとうございます! by 開発チーム"
|
||||
_foundTreasure:
|
||||
title: "宝探し"
|
||||
description: "隠されたお宝を発見した"
|
||||
_client30min:
|
||||
title: "ひとやすみ"
|
||||
description: "クライアントを起動してから30分以上経過した"
|
||||
@@ -1122,6 +1128,9 @@ _achievements:
|
||||
_htl20npm:
|
||||
title: "流れるTL"
|
||||
description: "ホームタイムラインの流速が20npmを越す"
|
||||
_viewInstanceChart:
|
||||
title: "アナリスト"
|
||||
description: "インスタンスのチャートを表示した"
|
||||
_outputHelloWorldOnScratchpad:
|
||||
title: "Hello, world!"
|
||||
description: "スクラッチパッドで hello world を出力した"
|
||||
|
@@ -938,13 +938,13 @@ cannotPerformTemporary: "일시적으로 사용할 수 없음"
|
||||
cannotPerformTemporaryDescription: "조작 횟수 제한을 초과하여 일시적으로 사용이 불가합니다. 잠시 후 다시 시도해 주세요."
|
||||
preset: "프리셋"
|
||||
selectFromPresets: "프리셋에서 선택"
|
||||
achievements: "도전과제"
|
||||
achievements: "도전 과제"
|
||||
_achievements:
|
||||
earnedAt: "달성 일시"
|
||||
_types:
|
||||
_notes1:
|
||||
title: "미스키 설정하고 있었는데요"
|
||||
description: "첫 노트를 포스트했습니다"
|
||||
title: "미스키 시작했는데요"
|
||||
description: "첫 노트를 작성했습니다"
|
||||
flavor: "Misskey에 오신 것을 환영합니다!"
|
||||
_notes10:
|
||||
title: "노트 조금"
|
||||
@@ -962,7 +962,7 @@ _achievements:
|
||||
title: "노트가 어디서 솟아?"
|
||||
description: "5,000개의 노트를 작성했습니다"
|
||||
_notes10000:
|
||||
title: "슈퍼-노트"
|
||||
title: "슈퍼 노트"
|
||||
description: "10,000개의 노트를 작성했습니다"
|
||||
_notes20000:
|
||||
title: "노트 더 없어?"
|
||||
@@ -989,7 +989,7 @@ _achievements:
|
||||
title: "노트 우주"
|
||||
description: "90,000개의 노트를 작성했습니다"
|
||||
_notes100000:
|
||||
title: "네 모든 노트는 내 거야"
|
||||
title: "ALL YOUR NOTE ARE BELONG TO US"
|
||||
description: "100,000개의 노트를 작성했습니다"
|
||||
flavor: "이만큼 쓸 일도 없겠지만... 다른 할 일이 있진 않으신가요?"
|
||||
_login3:
|
||||
@@ -1012,7 +1012,7 @@ _achievements:
|
||||
_login100:
|
||||
title: "미스키스트 III"
|
||||
description: "총 100일간 로그인했습니다"
|
||||
flavor: "그 유저, 미스키스트를 위하여"
|
||||
flavor: "그 유저, 미스키스트이다"
|
||||
_login200:
|
||||
title: "단골 I"
|
||||
description: "총 200일간 로그인했습니다"
|
||||
@@ -1025,7 +1025,7 @@ _achievements:
|
||||
_login500:
|
||||
title: "베테랑 I"
|
||||
description: "총 500일간 로그인했습니다"
|
||||
flavor: "여러분, 저 이 노트들 좋아해요"
|
||||
flavor: "제군, 나는 노트가 좋다"
|
||||
_login600:
|
||||
title: "베테랑 II"
|
||||
description: "총 600일간 로그인했습니다"
|
||||
@@ -1041,13 +1041,16 @@ _achievements:
|
||||
_login1000:
|
||||
title: "노트 마스터 III"
|
||||
description: "총 1,000일간 로그인했습니다"
|
||||
flavor: "미스키를 사용해 주셔서 감사합니다!"
|
||||
flavor: "Misskey를 사용해 주셔서 감사합니다!"
|
||||
_noteClipped1:
|
||||
title: "클립할 수밖에 없었어"
|
||||
description: "처음으로 노트를 클립했습니다"
|
||||
_noteFavorited1:
|
||||
title: "별을 바라보는 자"
|
||||
description: "처음으로 노트를 즐겨찾기했습니다"
|
||||
_myNoteFavorited1:
|
||||
title: "별을 원하는 자"
|
||||
description: "다른 사람이 당신의 노트를 즐겨찾기했습니다"
|
||||
_profileFilled:
|
||||
title: "준비 완료"
|
||||
description: "프로필 설정을 완료했습니다"
|
||||
@@ -1074,7 +1077,7 @@ _achievements:
|
||||
title: "첫 팔로워"
|
||||
description: "사용자가 처음으로 팔로잉했습니다"
|
||||
_followers10:
|
||||
title: "날 따라와!"
|
||||
title: "팔로우 미!"
|
||||
description: "10명의 사용자가 팔로우했습니다"
|
||||
_followers50:
|
||||
title: "이곳저곳"
|
||||
@@ -1092,15 +1095,18 @@ _achievements:
|
||||
title: "유명인사"
|
||||
description: "1,000명의 사용자가 팔로우했습니다"
|
||||
_collectAchievements30:
|
||||
title: "도전과제 콜렉터"
|
||||
title: "도전 과제 콜렉터"
|
||||
description: "30개의 도전과제를 획득했습니다"
|
||||
_viewAchievements3min:
|
||||
title: "저 도전과제 좋아해요"
|
||||
description: "도전과제 목록을 3분 이상 보세요"
|
||||
description: "도전 과제 목록을 3분 이상 쳐다봤습니다"
|
||||
_iLoveMisskey:
|
||||
title: "I Love Misskey"
|
||||
description: "\"I ❤ #Misskey\"를 포스트했습니다"
|
||||
flavor: "Misskey를 이용해주셔서 감사합니다! - 개발팀 일동"
|
||||
_foundTreasure:
|
||||
title: "보물찾기"
|
||||
description: "숨겨진 보물을 발견했습니다"
|
||||
_client30min:
|
||||
title: "잠깐 쉬어"
|
||||
description: "클라이언트를 시작하고 30분이 경과하였습니다"
|
||||
@@ -1113,7 +1119,7 @@ _achievements:
|
||||
flavor: "잠 좀 자세요. 걱정돼요."
|
||||
_postedAt0min0sec:
|
||||
title: "정각"
|
||||
description: "1초도 어긋나지 않은 정각에 노트를 포스트했습니다"
|
||||
description: "0분 0초 정각에 노트를 작성했습니다"
|
||||
flavor: "째깍 째깍 째깍 땡!"
|
||||
_selfQuote:
|
||||
title: "혼잣말"
|
||||
@@ -1121,21 +1127,24 @@ _achievements:
|
||||
_htl20npm:
|
||||
title: "타임라인 폭주 중"
|
||||
description: "1분 사이에 홈 타임라인에 노트가 20개 넘게 생성되었습니다"
|
||||
_viewInstanceChart:
|
||||
title: "애널리스트"
|
||||
description: "인스턴스의 차트를 열었습니다"
|
||||
_outputHelloWorldOnScratchpad:
|
||||
title: "Hello, world!"
|
||||
description: "스크래치패드에서 hello world를 출력하세요"
|
||||
description: "스크래치패드에서 hello world를 출력했습니다"
|
||||
_open3windows:
|
||||
title: "멀티 윈도우"
|
||||
description: "3개 이상의 창을 여세요"
|
||||
description: "3개 이상의 창을 열었습니다"
|
||||
_driveFolderCircularReference:
|
||||
title: "순환 참조"
|
||||
description: "드라이브 폴더를 자신을 가리키도록 만드려 시도했습니다"
|
||||
_reactWithoutRead:
|
||||
title: "읽고 답하긴 하시는 건가요?"
|
||||
description: "100자가 넘는 포스트에 3초 안에 포스트했습니다"
|
||||
description: "100자가 넘는 노트가 작성되고 3초 안에 반응했습니다"
|
||||
_clickedClickHere:
|
||||
title: "여길 눌러보세요"
|
||||
description: "이 곳을 눌러봤습니다"
|
||||
description: "여길을 눌러봤습니다"
|
||||
_justPlainLucky:
|
||||
title: "그냥 운이 좋았어"
|
||||
description: "매 10초마다 0.01%의 확률로 달성됩니다"
|
||||
@@ -1143,25 +1152,25 @@ _achievements:
|
||||
title: "신 콤플렉스"
|
||||
description: "이름을 syuilo로 설정했습니다"
|
||||
_passedSinceAccountCreated1:
|
||||
title: "1년"
|
||||
title: "1주년"
|
||||
description: "계정을 생성하고 1년이 지났습니다"
|
||||
_passedSinceAccountCreated2:
|
||||
title: "2년"
|
||||
title: "2주년"
|
||||
description: "계정을 생성하고 2년이 지났습니다"
|
||||
_passedSinceAccountCreated3:
|
||||
title: "3년"
|
||||
title: "3주년"
|
||||
description: "계정을 생성하고 3년이 지났습니다"
|
||||
_loggedInOnBirthday:
|
||||
title: "생일 축하합니다!"
|
||||
description: "설정한 생일에 로그인했습니다"
|
||||
description: "생일에 로그인했습니다"
|
||||
_loggedInOnNewYearsDay:
|
||||
title: "새해 복 많이 받으세요"
|
||||
description: "새해 첫 날에 로그인했습니다"
|
||||
flavor: "올해에도 저희 인스턴스에 관심을 가져 주셔서 감사합니다"
|
||||
_cookieClicked:
|
||||
title: "쿠키 클리커 게임"
|
||||
title: "쿠키를 클릭하는 게임"
|
||||
description: "쿠키를 클릭했습니다"
|
||||
flavor: "뭔가 문제가 있나요?"
|
||||
flavor: "소프트웨어 착각하지 않으셨나요?"
|
||||
_brainDiver:
|
||||
title: "Brain Diver"
|
||||
description: "Brain Diver로의 링크를 첨부했습니다"
|
||||
|
@@ -891,20 +891,53 @@ cannotUploadBecauseNoFreeSpace: "Файл не может быть загруж
|
||||
beta: "Бета"
|
||||
enableAutoSensitive: "Автоматическое определение NSFW"
|
||||
enableAutoSensitiveDescription: "Если доступно, используйте машинное обучение для автоматической установки флага NSFW на носителе. Даже если эта функция отключена, она может быть установлена автоматически в зависимости от инстанта."
|
||||
navbar: "Панель навигации"
|
||||
shuffle: "Перемешать"
|
||||
account: "Учётные записи"
|
||||
move: "Переместить"
|
||||
pushNotification: "Push-уведомления"
|
||||
subscribePushNotification: "Включить push-уведомления"
|
||||
unsubscribePushNotification: "Выключить push-уведомления"
|
||||
pushNotificationAlreadySubscribed: "Push-уведомления уже включены"
|
||||
pushNotificationNotSupported: "Push-уведмления не поддерживаются инстансом или браузером"
|
||||
sendPushNotificationReadMessage: "Удалять push-уведомления когда сообщение или прочитано"
|
||||
sendPushNotificationReadMessageCaption: "На мгновение появится уведомление \"{emptyPushNotificationMessage}\". Расход заряда батареи может увеличиться "
|
||||
windowMaximize: "Развернуть"
|
||||
windowRestore: "Восстановить"
|
||||
caption: "Подпись (Automatic Translation)"
|
||||
loggedInAsBot: "Вы под аккаунтом бота!"
|
||||
tools: "Инструменты"
|
||||
cannotLoad: "Не удалось загрузить"
|
||||
numberOfProfileView: "Количество профилей для просмотра"
|
||||
like: "Нравится!"
|
||||
unlike: "Отменить «нравится»"
|
||||
numberOfLikes: "Количество лайков"
|
||||
show: "Отображение"
|
||||
neverShow: "Больше не показывать"
|
||||
remindMeLater: "Напомнить позже"
|
||||
didYouLikeMisskey: "Вам нравится Misskey?"
|
||||
pleaseDonate: "Сайт {host} работает на Misskey. Это бесплатное программное обеспечение, и ваши пожертвования очень бы помогли продолжать его разработку!"
|
||||
roles: "Роли"
|
||||
role: "Роль"
|
||||
normalUser: "Обычный пользователь"
|
||||
undefined: "неопределён"
|
||||
assign: "Назначить"
|
||||
unassign: "Отменить назначение"
|
||||
color: "Цвет"
|
||||
manageCustomEmojis: "Управлять пользовательскими эмодзи"
|
||||
youCannotCreateAnymore: "Вы достигли лимита создания."
|
||||
cannotPerformTemporary: "Временно недоступен"
|
||||
cannotPerformTemporaryDescription: "Это действие временно невозможно выполнить из-за превышения лимита выполнения."
|
||||
preset: "Шаблоны"
|
||||
selectFromPresets: "Выбрать из шаблонов"
|
||||
achievements: "Достижения"
|
||||
_achievements:
|
||||
earnedAt: "Разблокировано в"
|
||||
_types:
|
||||
_notes1:
|
||||
title: "Первые шаги в Misskey"
|
||||
description: "Опубликована первая заметка"
|
||||
flavor: "Приятных дней с Misskey!"
|
||||
_notes10:
|
||||
title: "Несколько заметок"
|
||||
description: "Опубликовано 10 заметок"
|
||||
@@ -950,9 +983,11 @@ _achievements:
|
||||
_notes100000:
|
||||
title: "ALL YOUR NOTE ARE BELONG TO US"
|
||||
description: "Опубликовано 100 000 заметок"
|
||||
flavor: "Вам правда нужно столько писать?"
|
||||
_login3:
|
||||
title: "Новичок Ⅰ"
|
||||
description: "3 дня на сайте"
|
||||
flavor: "С сегодняшнего дня зовите меня просто мискиец"
|
||||
_login7:
|
||||
title: "Новичок Ⅱ"
|
||||
description: "Неделя на сайте"
|
||||
@@ -969,6 +1004,7 @@ _achievements:
|
||||
_login100:
|
||||
title: "Мискиец Ⅲ"
|
||||
description: "100 дней на сайте"
|
||||
flavor: "Жестокий мискиец"
|
||||
_login200:
|
||||
title: "Завсегдатай Ⅰ"
|
||||
description: "200 дней на сайте"
|
||||
@@ -981,6 +1017,7 @@ _achievements:
|
||||
_login500:
|
||||
title: "Ветеран Ⅰ"
|
||||
description: "500 дней на сайте"
|
||||
flavor: "Господа, я люблю заметки"
|
||||
_login600:
|
||||
title: "Ветеран Ⅱ"
|
||||
description: "600 дней на сайте"
|
||||
@@ -996,32 +1033,59 @@ _achievements:
|
||||
_login1000:
|
||||
title: "Повелитель заметок Ⅲ"
|
||||
description: "1000 дней на сайте"
|
||||
flavor: "Спасибо, что пользуетесь Misskey!"
|
||||
_noteClipped1:
|
||||
title: "Нельзя не сохранить"
|
||||
description: "Первая заметка в подборке"
|
||||
_noteFavorited1:
|
||||
title: "Смотрящий на звёзды"
|
||||
description: "Первое добавление в избранное"
|
||||
_myNoteFavorited1:
|
||||
title: "В поиске звёзд"
|
||||
description: "Кому-то понравилась ваша заметка"
|
||||
_profileFilled:
|
||||
title: "Приготовления закончены"
|
||||
description: "Заполнен профиль"
|
||||
_markedAsCat:
|
||||
title: "Ваш покорный слуга кот"
|
||||
description: "Включена опция «Аккаунт кота»"
|
||||
flavor: "Позвольте представиться: я — кот, просто кот, у меня еще нет имени."
|
||||
_following1:
|
||||
title: "Я не один"
|
||||
description: "Сделана первая подписка"
|
||||
_following10:
|
||||
title: "Не останавливайся… Не останавливайся…"
|
||||
description: "Количество подписок достигло 10"
|
||||
_following50:
|
||||
title: "Много друзей"
|
||||
description: "Количество подписок достигло 50"
|
||||
_following100:
|
||||
title: "Сотня друзей"
|
||||
description: "Количество подписок достигло 100"
|
||||
_following300:
|
||||
title: "Друзья в избытке"
|
||||
description: "Количество подписок достигло 300"
|
||||
_followers1:
|
||||
title: "Первый подписчик"
|
||||
description: "Появился 1 подписчик"
|
||||
_followers10:
|
||||
title: "Следуй за мной!"
|
||||
description: "Количество подписчиков достигло 10"
|
||||
_followers50:
|
||||
title: "Один за другим"
|
||||
description: "Подписчиков больше 50"
|
||||
description: "Количество подписчиков достигло 50"
|
||||
_followers100:
|
||||
title: "Всеобщий любимец"
|
||||
description: "Подписчиков больше 100"
|
||||
description: "Количество подписчиков достигло 100"
|
||||
_followers300:
|
||||
title: "В очередь!"
|
||||
description: "Подписчиков больше 300"
|
||||
description: "Количество подписчиков достигло 300"
|
||||
_followers500:
|
||||
title: "Радиостанция"
|
||||
description: "Подписчиков больше 300"
|
||||
description: "Количество подписчиков достигло 500"
|
||||
_followers1000:
|
||||
title: "Авторитет"
|
||||
description: "Подписчиков больше 1000"
|
||||
description: "Количество подписчиков достигло 1000"
|
||||
_collectAchievements30:
|
||||
title: "Достигатор"
|
||||
description: "Получено 30 достижений"
|
||||
@@ -1031,6 +1095,7 @@ _achievements:
|
||||
_iLoveMisskey:
|
||||
title: "Я люблю Misskey"
|
||||
description: "Написана заметка «I ❤ #Misskey»"
|
||||
flavor: "Спасибо за поддержку Misskey! Ваша команда разработчиков"
|
||||
_client30min:
|
||||
title: "Перерыв на обед"
|
||||
description: "Прошло 30 минут с момента запуска клиента"
|
||||
@@ -1040,6 +1105,7 @@ _achievements:
|
||||
_postedAtLateNight:
|
||||
title: "Житель ночи"
|
||||
description: "Заметка опубликована в глухую ночь"
|
||||
flavor: "Вроде бы пора спать"
|
||||
_postedAt0min0sec:
|
||||
title: "Говорящие часы"
|
||||
description: "Заметка опубликована ровно в 0 минут 0 секунд"
|
||||
@@ -1057,6 +1123,7 @@ _achievements:
|
||||
title: "Многооконный"
|
||||
description: "Открыто одновременно 3 окна"
|
||||
_driveFolderCircularReference:
|
||||
title: "Циклическая ссылка"
|
||||
description: "Попытка создать на «диске» рекурсивно вложенную папку"
|
||||
_reactWithoutRead:
|
||||
title: "Не читай @ отвечай!"
|
||||
@@ -1085,6 +1152,7 @@ _achievements:
|
||||
_loggedInOnNewYearsDay:
|
||||
title: "С Новым годом!"
|
||||
description: "Вход на сайт в первый день года"
|
||||
flavor: "Желаем отличного года на нашем сайте!"
|
||||
_cookieClicked:
|
||||
title: "Игра, в которой вы щёлкаете по печенькам"
|
||||
description: "Нажато печенье"
|
||||
@@ -1094,11 +1162,34 @@ _achievements:
|
||||
description: "Опубликована ссылка на песню «Brain Diver»"
|
||||
flavor: "Мисски-Мисски Ла-Ту-Ма"
|
||||
_role:
|
||||
new: "Новая роль"
|
||||
edit: "Изменить роль"
|
||||
name: "Название роли"
|
||||
description: "Описание роли"
|
||||
permission: "Ролевые полномочия"
|
||||
descriptionOfPermission: "<b>Модераторы</b> могут изменять базовые операции для модераторов.\n<b>Администраторы</b> могут изменять полностью настройки инстанса."
|
||||
assignTarget: "Метод присвоения"
|
||||
descriptionOfAssignTarget: "<b>Вручную</b> чтобы указать кому выдавать роль, а кому нет.\n<b>По условию<b> чтобы автоматически выдавать и удалять роль при условиях."
|
||||
manual: "Вручную"
|
||||
conditional: "По условию"
|
||||
condition: "Условия"
|
||||
isConditionalRole: "Эта роль выдаётся по условию."
|
||||
isPublic: "Общедоступная роль"
|
||||
descriptionOfIsPublic: "Список тех, кому назначена эта роль будет доступен всем. Кроме того эта роль будет отмечена у каждого в профиле."
|
||||
options: "Настройки ролей"
|
||||
policies: "Политики"
|
||||
baseRole: "Шаблон роли"
|
||||
useBaseValue: "Использовать значение из шаблона"
|
||||
chooseRoleToAssign: "Выберите роль, которую хотите выдать"
|
||||
canEditMembersByModerator: "Могут назначать модераторы"
|
||||
descriptionOfCanEditMembersByModerator: "Если включено, на эту роль могут назначать пользователей как администраторы, так и модераторы. Если выключено, назначать могут только администраторы."
|
||||
priority: "Приоритет"
|
||||
_priority:
|
||||
low: "Низкий"
|
||||
middle: "Средне"
|
||||
high: "Высокий"
|
||||
_options:
|
||||
canManageCustomEmojis: "Управлять пользовательскими эмодзи"
|
||||
_sensitiveMediaDetection:
|
||||
description: "Машинное обучение может быть использовано для автоматического обнаружения чувствительных медиа для модерации. Нагрузка на сервер увеличивается незначительно."
|
||||
setSensitiveFlagAutomatically: "Установить флаг NSFW"
|
||||
@@ -1145,6 +1236,11 @@ _plugin:
|
||||
install: "Установка расширений"
|
||||
installWarn: "Пожалуйста, не устанавливайте расширения, которым не доверяете."
|
||||
manage: "Управление расширениями"
|
||||
_preferencesBackups:
|
||||
saveConfirm: "Сохранить бэкап как {name}?"
|
||||
deleteConfirm: "Удалить резервную копию {name}?"
|
||||
renameConfirm: "Переименовать резервную копию с \"{old}\" на \"{new}\"?"
|
||||
noBackups: "Резервной копии не существует. Вы можете создать резервную копию в настройках на этом инстансе с помощью \"Создать новую резервную копию\"."
|
||||
_registry:
|
||||
scope: "Область"
|
||||
key: "Ключ"
|
||||
@@ -1641,6 +1737,7 @@ _notification:
|
||||
youReceivedFollowRequest: "У вас новый запрос на подписку."
|
||||
yourFollowRequestAccepted: "Ваш запрос на подписку одобрен."
|
||||
youWereInvitedToGroup: "Вы приглашены в группу."
|
||||
achievementEarned: "Получено достижение"
|
||||
_types:
|
||||
all: "Все"
|
||||
follow: "Подписки"
|
||||
|
@@ -942,14 +942,51 @@ achievements: "ความสำเร็จ"
|
||||
_achievements:
|
||||
earnedAt: "ได้รับเมื่อ"
|
||||
_types:
|
||||
_notes1:
|
||||
title: "เพียงแค่ตั้งค่า msky ของฉัน"
|
||||
_followers100:
|
||||
title: "บุคคลที่เป็นที่นิยม"
|
||||
_followers500:
|
||||
title: "เสาสัญญาณ"
|
||||
_followers1000:
|
||||
title: "ผู้ทรงอิทธิพล"
|
||||
_iLoveMisskey:
|
||||
title: "ฉันรัก Misskey"
|
||||
description: "โพสต์ \"I ❤ #Misskey\""
|
||||
_foundTreasure:
|
||||
title: "ล่าสมบัติ"
|
||||
description: "คุณพบสมบัติที่ซ่อนอยู่"
|
||||
_client30min:
|
||||
title: "พักผ่อนสักหน่อย"
|
||||
_noteDeletedWithin1min:
|
||||
title: "ไม่เป็นไร"
|
||||
_postedAtLateNight:
|
||||
title: "กลางคืน"
|
||||
_viewInstanceChart:
|
||||
title: "วิเคราะห์"
|
||||
description: "ดูแผนภูมิอินสแตนซ์ของคุณ"
|
||||
_driveFolderCircularReference:
|
||||
title: "อ้างอิงวงจร"
|
||||
_clickedClickHere:
|
||||
title: "คลิ๊กที่นี่"
|
||||
description: "คุณได้คลิกที่นี่"
|
||||
_passedSinceAccountCreated1:
|
||||
title: "ครบรอบหนึ่งปี"
|
||||
_passedSinceAccountCreated2:
|
||||
title: "ครบรอบสองปี"
|
||||
_passedSinceAccountCreated3:
|
||||
title: "ครบรอบสามปี"
|
||||
_loggedInOnBirthday:
|
||||
title: "สุขสันต์วันเกิด"
|
||||
description: "เข้าสู่ระบบในวันเกิดของคุณ"
|
||||
_loggedInOnNewYearsDay:
|
||||
title: "สวัสดีปีใหม่!"
|
||||
description: "เข้าสู่ระบบในวันแรกของปี"
|
||||
_cookieClicked:
|
||||
description: "คลิกคุกกี้"
|
||||
_brainDiver:
|
||||
title: "Brain Diver"
|
||||
flavor: "Misskey-Misskey La-Tu-Ma"
|
||||
_role:
|
||||
new: "บทบาทใหม่"
|
||||
edit: "แก้ไขบทบาท"
|
||||
|
@@ -899,6 +899,134 @@ unlike: "Не вподобати"
|
||||
numberOfLikes: "Вподобання"
|
||||
show: "Відображення"
|
||||
color: "Колір"
|
||||
achievements: "Досягнення"
|
||||
_achievements:
|
||||
earnedAt: "Відкрито"
|
||||
_types:
|
||||
_notes1:
|
||||
title: "налаштовую свій msky"
|
||||
description: "Перша нотатка"
|
||||
flavor: "Приємного часу з Misskey!"
|
||||
_notes10:
|
||||
title: "Декілька нотаток"
|
||||
description: "10 нотаток відправлено"
|
||||
_notes100:
|
||||
title: "Купа нотаток"
|
||||
description: "100 нотаток відправлено"
|
||||
_notes500:
|
||||
title: "Все в нотатках"
|
||||
description: "500 нотаток відправлено"
|
||||
_notes1000:
|
||||
title: "Гора нотаток"
|
||||
description: "1 000 нотаток відправлено"
|
||||
_notes5000:
|
||||
title: "Переповнюючі нотатки"
|
||||
description: "5 000 нотаток відправлено"
|
||||
_notes10000:
|
||||
title: "Супернотатка"
|
||||
description: "10 000 нотаток відправлено"
|
||||
_notes20000:
|
||||
title: "Треба Більше Нотаток"
|
||||
description: "20 000 нотаток відправлено"
|
||||
_notes30000:
|
||||
title: "Нотатки нотатки нотатки"
|
||||
description: "30 000 нотаток відправлено"
|
||||
_notes40000:
|
||||
title: "Фабрика нотаток"
|
||||
description: "40 000 нотаток відправлено"
|
||||
_notes50000:
|
||||
title: "Планета нотаток"
|
||||
description: "50 000 нотаток відправлено"
|
||||
_notes60000:
|
||||
title: "Нотатковий квазар"
|
||||
description: "60 000 нотаток відправлено"
|
||||
_notes70000:
|
||||
title: "Чорна нотаткова діра"
|
||||
description: "70 000 нотаток відправлено"
|
||||
_notes80000:
|
||||
title: "Галактика нотаток"
|
||||
description: "80 000 нотаток відправлено"
|
||||
_notes90000:
|
||||
title: "Нотатковерс"
|
||||
description: "90 000 нотаток відправлено"
|
||||
_notes100000:
|
||||
title: "ALL YOUR NOTE ARE BELONG TO US"
|
||||
description: "100 000 нотаток відправлено"
|
||||
flavor: "Так багато потрібно сказати?"
|
||||
_login3:
|
||||
title: "Новачок I"
|
||||
description: "3 дні користування загально"
|
||||
_login7:
|
||||
title: "Новачок II"
|
||||
description: "7 днів користування загально"
|
||||
_login15:
|
||||
title: "Новачок III"
|
||||
description: "15 днів користування загально"
|
||||
_login30:
|
||||
title: "Міскієць I"
|
||||
description: "30 днів користування загально"
|
||||
_login60:
|
||||
title: "Міскієць II"
|
||||
description: "60 днів користування загально"
|
||||
_login100:
|
||||
title: "Міскієць III"
|
||||
description: "100 днів користування загально"
|
||||
_login200:
|
||||
title: "Завсідник I"
|
||||
description: "200 днів користування загально"
|
||||
_login300:
|
||||
title: "Завсідник II"
|
||||
description: "300 днів користування загально"
|
||||
_login400:
|
||||
title: "Завсідник III"
|
||||
description: "400 днів користування загально"
|
||||
_login500:
|
||||
title: "Ветеран I"
|
||||
description: "500 днів користування загально"
|
||||
_login600:
|
||||
title: "Ветеран II"
|
||||
description: "600 днів користування загально"
|
||||
_login700:
|
||||
title: "Ветеран III"
|
||||
description: "700 днів користування загально"
|
||||
_login800:
|
||||
description: "800 днів користування загально"
|
||||
_login900:
|
||||
description: "900 днів користування загально"
|
||||
_login1000:
|
||||
description: "1000 днів користування загально"
|
||||
flavor: "Дякуємо, що користуєтеся Misskey!"
|
||||
_following50:
|
||||
description: "Кількість підписок сягнула 50"
|
||||
_following100:
|
||||
title: "100 друзів"
|
||||
description: "Кількість підписок сягнула 100"
|
||||
_following300:
|
||||
title: "Надлишок друзів"
|
||||
description: "Кількість підписок сягнула 300"
|
||||
_followers1:
|
||||
title: "Перший підписник"
|
||||
description: "З'явився перший підписник"
|
||||
_followers10:
|
||||
title: "Follow me!"
|
||||
description: "Кількість підписників досягла 10"
|
||||
_followers50:
|
||||
description: "Кількість підписників досягла 50"
|
||||
_followers100:
|
||||
description: "Кількість підписників досягла 100"
|
||||
_followers300:
|
||||
description: "Кількість підписників досягла 300"
|
||||
_followers500:
|
||||
description: "Кількість підписників досягла 500"
|
||||
_followers1000:
|
||||
title: "Інфлюенсер"
|
||||
description: "Кількість підписників досягла 1000"
|
||||
_passedSinceAccountCreated3:
|
||||
description: "Минуло 3 роки з моменту створення акаунта"
|
||||
_loggedInOnBirthday:
|
||||
title: "З Днем народження!"
|
||||
_brainDiver:
|
||||
flavor: "Misskey-Misskey La-Tu-Ma"
|
||||
_role:
|
||||
priority: "Пріоритет"
|
||||
_priority:
|
||||
|
@@ -956,7 +956,7 @@ _achievements:
|
||||
title: "满是帖子"
|
||||
description: "发布了500篇帖子"
|
||||
_notes1000:
|
||||
title: "帖子成山"
|
||||
title: "积帖成山"
|
||||
description: "发布了1,000篇帖子"
|
||||
_notes5000:
|
||||
title: "帖如泉涌"
|
||||
@@ -992,22 +992,56 @@ _achievements:
|
||||
title: "ALL YOUR NOTE ARE BELONG TO US"
|
||||
description: "发布了100,000篇帖子"
|
||||
flavor: "真的有那么多可以写的东西吗?"
|
||||
_login3:
|
||||
title: "初学者 I"
|
||||
description: "连续登录3天"
|
||||
_login7:
|
||||
description: "连续登录7天"
|
||||
_login15:
|
||||
description: "连续登录15天"
|
||||
_login30:
|
||||
description: "连续登录30天"
|
||||
_login60:
|
||||
description: "连续登录60天"
|
||||
_login1000:
|
||||
flavor: "感谢您使用Misskey!"
|
||||
_noteFavorited1:
|
||||
title: "观星者"
|
||||
_profileFilled:
|
||||
title: "整装待发"
|
||||
description: "设置了个人资料"
|
||||
_markedAsCat:
|
||||
title: "我是猫"
|
||||
description: "将账户设定为一只猫"
|
||||
_following10:
|
||||
title: "关注,跟随"
|
||||
_following50:
|
||||
title: "我的朋友很多"
|
||||
_following300:
|
||||
description: "关注数超过300"
|
||||
_followers100:
|
||||
title: "胜友如云"
|
||||
_collectAchievements30:
|
||||
description: "获得超过30个成就"
|
||||
_viewAchievements3min:
|
||||
description: "盯着成就看三分钟"
|
||||
_iLoveMisskey:
|
||||
title: "I Love Misskey"
|
||||
description: "发布\"I ❤ #Misskey\"帖子"
|
||||
flavor: "感谢您使用 Misskey ! by 开发团队"
|
||||
_noteDeletedWithin1min:
|
||||
description: "发帖后一分钟内就将其删除"
|
||||
_postedAtLateNight:
|
||||
title: "夜行者"
|
||||
description: "深夜发布帖子"
|
||||
_outputHelloWorldOnScratchpad:
|
||||
title: "Hello, world!"
|
||||
_passedSinceAccountCreated1:
|
||||
description: "账户创建时间超过1年"
|
||||
_passedSinceAccountCreated2:
|
||||
description: "账户创建时间超过2年"
|
||||
_passedSinceAccountCreated3:
|
||||
description: "账户创建时间超过3年"
|
||||
_loggedInOnBirthday:
|
||||
title: "生日快乐"
|
||||
description: "在生日当天登录"
|
||||
|
@@ -950,13 +950,13 @@ _achievements:
|
||||
title: "若干貼文"
|
||||
description: "發表了10則貼文"
|
||||
_notes100:
|
||||
title: "許多的貼文"
|
||||
title: "許多貼文"
|
||||
description: "發表了100則貼文"
|
||||
_notes500:
|
||||
title: "滿滿的貼文"
|
||||
description: "發表了500則貼文"
|
||||
_notes1000:
|
||||
title: "一堆貼文"
|
||||
title: "堆積如山的貼文"
|
||||
description: "發表了1000則貼文"
|
||||
_notes5000:
|
||||
title: "滔滔不絕的貼文"
|
||||
@@ -1042,22 +1042,71 @@ _achievements:
|
||||
title: "貼文大師ⅠⅠⅠ"
|
||||
description: "總登入天數為1,000天"
|
||||
flavor: "感謝您使用Misskey!"
|
||||
_noteClipped1:
|
||||
title: "忍不住要收進摘錄裡"
|
||||
description: "第一次將貼文收進摘錄"
|
||||
_noteFavorited1:
|
||||
title: "觀星者"
|
||||
description: "第一次將貼文收藏至我的最愛"
|
||||
_myNoteFavorited1:
|
||||
title: "想要星星"
|
||||
description: "自己的貼文被他人收藏至「我的最愛」了"
|
||||
_profileFilled:
|
||||
title: "有備而來"
|
||||
description: "設定了個人檔案"
|
||||
_markedAsCat:
|
||||
title: "我是貓"
|
||||
description: "已將帳戶設定為貓"
|
||||
flavor: "還沒有名字。"
|
||||
_following1:
|
||||
title: "首次追隨"
|
||||
description: "首次追隨了"
|
||||
_following10:
|
||||
title: "跟著跟著"
|
||||
description: "跟隨超過10人了"
|
||||
_following50:
|
||||
title: "朋友很多"
|
||||
description: "跟隨超過50人了"
|
||||
_following100:
|
||||
title: "100位朋友"
|
||||
description: "跟隨超過100人了"
|
||||
_following300:
|
||||
title: "朋友過多"
|
||||
description: "跟隨超過300人了"
|
||||
_followers1:
|
||||
title: "第一個追隨者"
|
||||
description: "第一次被追隨"
|
||||
_followers10:
|
||||
title: "Follow me!"
|
||||
description: "跟隨者超過10人了"
|
||||
_followers50:
|
||||
title: "成群結隊"
|
||||
description: "跟隨者超過50人了"
|
||||
_followers100:
|
||||
title: "紅人"
|
||||
description: "跟隨者超過100人了"
|
||||
_followers300:
|
||||
title: "請排成一排"
|
||||
description: "跟隨者超過300人了"
|
||||
_followers500:
|
||||
title: "基站"
|
||||
description: "超過500名追隨者"
|
||||
description: "超過500名追隨者了"
|
||||
_followers1000:
|
||||
title: "影響者"
|
||||
description: "超過1000名追隨者"
|
||||
description: "超過1000名追隨者了"
|
||||
_collectAchievements30:
|
||||
title: "成就收藏家"
|
||||
description: "獲得30個以上的成就"
|
||||
_viewAchievements3min:
|
||||
title: "喜愛成就"
|
||||
description: "看成就列表要花了3分鐘以上"
|
||||
description: "看成就列表要花3分鐘以上"
|
||||
_iLoveMisskey:
|
||||
title: "I Love Misskey"
|
||||
description: "發布「I ❤ #Misskey」"
|
||||
flavor: "感謝您使用Misskey! by 開發團隊"
|
||||
_foundTreasure:
|
||||
title: "尋寶"
|
||||
description: "發現了隱藏的寶藏"
|
||||
_client30min:
|
||||
title: "休息一下"
|
||||
description: "用戶端啟動已超過30分鐘"
|
||||
@@ -1071,18 +1120,22 @@ _achievements:
|
||||
_postedAt0min0sec:
|
||||
title: "報時"
|
||||
description: "在0分0秒發佈貼文"
|
||||
flavor: "啵.啵.啵.嗶ー"
|
||||
_selfQuote:
|
||||
title: "自我引用"
|
||||
description: "引用了自己的貼文"
|
||||
_htl20npm:
|
||||
title: "流動的TL"
|
||||
description: "在首頁時間軸的流速超過20npm"
|
||||
_viewInstanceChart:
|
||||
title: "分析師"
|
||||
description: "顯示了實例的圖表"
|
||||
_outputHelloWorldOnScratchpad:
|
||||
title: "Hello world!"
|
||||
description: "在暫存記憶體輸出了 hello world"
|
||||
_open3windows:
|
||||
title: "多重視窗"
|
||||
description: "開啟3個以上的視窗"
|
||||
description: "開啟了3個以上的視窗"
|
||||
_driveFolderCircularReference:
|
||||
title: "循環引用"
|
||||
description: "試圖遞迴套入雲端硬碟資料夾"
|
||||
@@ -1098,6 +1151,30 @@ _achievements:
|
||||
_setNameToSyuilo:
|
||||
title: "神的情結"
|
||||
description: "將名稱設定為 syuilo"
|
||||
_passedSinceAccountCreated1:
|
||||
title: "一周年"
|
||||
description: "自建立帳戶開始過了1年"
|
||||
_passedSinceAccountCreated2:
|
||||
title: "二周年"
|
||||
description: "自建立帳戶開始過了2年"
|
||||
_passedSinceAccountCreated3:
|
||||
title: "三周年"
|
||||
description: "自建立帳戶開始過了3年"
|
||||
_loggedInOnBirthday:
|
||||
title: "生日快樂"
|
||||
description: "在生日當天登入了"
|
||||
_loggedInOnNewYearsDay:
|
||||
title: "新年快樂"
|
||||
description: "在元旦當天登入了"
|
||||
flavor: "今年也請對敝實例多多指教"
|
||||
_cookieClicked:
|
||||
title: "點擊餅乾的遊戲"
|
||||
description: "點擊了餅乾"
|
||||
flavor: "是不是軟體有問題?"
|
||||
_brainDiver:
|
||||
title: "Brain Driver"
|
||||
description: "發佈了Brain Driver的連結"
|
||||
flavor: "Misskey-Misskey La-Tu-Ma"
|
||||
_role:
|
||||
new: "建立角色"
|
||||
edit: "編輯角色"
|
||||
@@ -1747,6 +1824,7 @@ _notification:
|
||||
pollEnded: "問卷調查已產生結果"
|
||||
unreadAntennaNote: "天線 {name}"
|
||||
emptyPushNotificationMessage: "推送通知已更新"
|
||||
achievementEarned: "獲得成就"
|
||||
_types:
|
||||
all: "全部 "
|
||||
follow: "追隨中"
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"version": "13.1.2",
|
||||
"version": "13.2.2",
|
||||
"codename": "nasubi",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -54,12 +54,12 @@
|
||||
"devDependencies": {
|
||||
"@types/gulp": "4.0.10",
|
||||
"@types/gulp-rename": "2.0.1",
|
||||
"@typescript-eslint/eslint-plugin": "5.48.2",
|
||||
"@typescript-eslint/parser": "5.48.2",
|
||||
"@typescript-eslint/eslint-plugin": "5.49.0",
|
||||
"@typescript-eslint/parser": "5.49.0",
|
||||
"cross-env": "7.0.3",
|
||||
"cypress": "12.3.0",
|
||||
"eslint": "^8.32.0",
|
||||
"start-server-and-test": "1.15.2"
|
||||
"start-server-and-test": "1.15.3"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@tensorflow/tfjs-core": "^4.2.0"
|
||||
|
@@ -19,13 +19,13 @@
|
||||
"test-and-coverage": "pnpm jest-and-coverage"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@tensorflow/tfjs": "^4.1.0",
|
||||
"@tensorflow/tfjs-node": "4.1.0"
|
||||
"@tensorflow/tfjs": "^4.2.0",
|
||||
"@tensorflow/tfjs-node": "4.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@bull-board/api": "^4.10.2",
|
||||
"@bull-board/fastify": "^4.10.2",
|
||||
"@bull-board/ui": "^4.10.2",
|
||||
"@bull-board/api": "^4.11.0",
|
||||
"@bull-board/fastify": "^4.11.0",
|
||||
"@bull-board/ui": "^4.11.0",
|
||||
"@discordapp/twemoji": "14.0.2",
|
||||
"@fastify/accepts": "4.1.0",
|
||||
"@fastify/cookie": "^8.3.0",
|
||||
@@ -63,15 +63,14 @@
|
||||
"file-type": "18.2.0",
|
||||
"fluent-ffmpeg": "2.1.2",
|
||||
"form-data": "^4.0.0",
|
||||
"got": "12.5.3",
|
||||
"got": "^12.5.3",
|
||||
"hpagent": "1.2.0",
|
||||
"ioredis": "4.28.5",
|
||||
"ip-cidr": "3.0.11",
|
||||
"is-svg": "4.3.2",
|
||||
"js-yaml": "4.1.0",
|
||||
"jsdom": "21.0.0",
|
||||
"jsdom": "21.1.0",
|
||||
"json5": "2.2.3",
|
||||
"json5-loader": "4.0.1",
|
||||
"jsonld": "8.1.0",
|
||||
"jsrsasign": "10.6.1",
|
||||
"mfm-js": "0.23.3",
|
||||
@@ -120,14 +119,14 @@
|
||||
"typeorm": "0.3.11",
|
||||
"typescript": "4.9.4",
|
||||
"ulid": "2.3.0",
|
||||
"undici": "^5.15.1",
|
||||
"unzipper": "0.10.11",
|
||||
"uuid": "9.0.0",
|
||||
"vary": "1.1.2",
|
||||
"web-push": "3.5.0",
|
||||
"websocket": "1.0.34",
|
||||
"ws": "8.12.0",
|
||||
"xev": "3.0.2"
|
||||
"xev": "3.0.2",
|
||||
"node-fetch": "3.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@redocly/openapi-core": "1.0.0-beta.120",
|
||||
@@ -176,14 +175,13 @@
|
||||
"@types/web-push": "3.3.2",
|
||||
"@types/websocket": "1.0.5",
|
||||
"@types/ws": "8.5.4",
|
||||
"@typescript-eslint/eslint-plugin": "5.48.2",
|
||||
"@typescript-eslint/parser": "5.48.2",
|
||||
"@typescript-eslint/eslint-plugin": "5.49.0",
|
||||
"@typescript-eslint/parser": "5.49.0",
|
||||
"cross-env": "7.0.3",
|
||||
"eslint": "8.32.0",
|
||||
"eslint-plugin-import": "2.27.5",
|
||||
"execa": "6.1.0",
|
||||
"jest": "29.3.1",
|
||||
"jest-mock": "^29.3.1",
|
||||
"node-fetch": "3.3.0"
|
||||
"jest-mock": "^29.3.1"
|
||||
}
|
||||
}
|
||||
|
35
packages/backend/src/boot/common.ts
Normal file
35
packages/backend/src/boot/common.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { ChartManagementService } from '@/core/chart/ChartManagementService.js';
|
||||
import { QueueProcessorService } from '@/queue/QueueProcessorService.js';
|
||||
import { NestLogger } from '@/NestLogger.js';
|
||||
import { QueueProcessorModule } from '@/queue/QueueProcessorModule.js';
|
||||
import { JanitorService } from '@/daemons/JanitorService.js';
|
||||
import { QueueStatsService } from '@/daemons/QueueStatsService.js';
|
||||
import { ServerStatsService } from '@/daemons/ServerStatsService.js';
|
||||
import { ServerService } from '@/server/ServerService.js';
|
||||
import { MainModule } from '@/MainModule.js';
|
||||
|
||||
export async function server() {
|
||||
const app = await NestFactory.createApplicationContext(MainModule, {
|
||||
logger: new NestLogger(),
|
||||
});
|
||||
app.enableShutdownHooks();
|
||||
|
||||
const serverService = app.get(ServerService);
|
||||
serverService.launch();
|
||||
|
||||
app.get(ChartManagementService).start();
|
||||
app.get(JanitorService).start();
|
||||
app.get(QueueStatsService).start();
|
||||
app.get(ServerStatsService).start();
|
||||
}
|
||||
|
||||
export async function jobQueue() {
|
||||
const jobQueue = await NestFactory.createApplicationContext(QueueProcessorModule, {
|
||||
logger: new NestLogger(),
|
||||
});
|
||||
jobQueue.enableShutdownHooks();
|
||||
|
||||
jobQueue.get(QueueProcessorService).start();
|
||||
jobQueue.get(ChartManagementService).start();
|
||||
}
|
@@ -6,21 +6,12 @@ import cluster from 'node:cluster';
|
||||
import chalk from 'chalk';
|
||||
import chalkTemplate from 'chalk-template';
|
||||
import semver from 'semver';
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import Logger from '@/logger.js';
|
||||
import { loadConfig } from '@/config.js';
|
||||
import type { Config } from '@/config.js';
|
||||
import { lessThan } from '@/misc/prelude/array.js';
|
||||
import { showMachineInfo } from '@/misc/show-machine-info.js';
|
||||
import { DaemonModule } from '@/daemons/DaemonModule.js';
|
||||
import { JanitorService } from '@/daemons/JanitorService.js';
|
||||
import { QueueStatsService } from '@/daemons/QueueStatsService.js';
|
||||
import { ServerStatsService } from '@/daemons/ServerStatsService.js';
|
||||
import { NestLogger } from '@/NestLogger.js';
|
||||
import { ChartManagementService } from '@/core/chart/ChartManagementService.js';
|
||||
import { ServerService } from '@/server/ServerService.js';
|
||||
import { MainModule } from '@/MainModule.js';
|
||||
import { envOption } from '../env.js';
|
||||
import { envOption } from '@/env.js';
|
||||
import { jobQueue, server } from './common.js';
|
||||
|
||||
const _filename = fileURLToPath(import.meta.url);
|
||||
const _dirname = dirname(_filename);
|
||||
@@ -73,14 +64,13 @@ export async function masterMain() {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const app = await NestFactory.createApplicationContext(MainModule, {
|
||||
logger: new NestLogger(),
|
||||
});
|
||||
app.enableShutdownHooks();
|
||||
|
||||
// start server
|
||||
const serverService = app.get(ServerService);
|
||||
serverService.launch();
|
||||
if (envOption.onlyServer) {
|
||||
await server();
|
||||
} else if (envOption.onlyQueue) {
|
||||
await jobQueue();
|
||||
} else {
|
||||
await server();
|
||||
}
|
||||
|
||||
bootLogger.succ('Misskey initialized');
|
||||
|
||||
@@ -89,11 +79,6 @@ export async function masterMain() {
|
||||
}
|
||||
|
||||
bootLogger.succ(`Now listening on port ${config.port} on ${config.url}`, null, true);
|
||||
|
||||
app.get(ChartManagementService).start();
|
||||
app.get(JanitorService).start();
|
||||
app.get(QueueStatsService).start();
|
||||
app.get(ServerStatsService).start();
|
||||
}
|
||||
|
||||
function showEnvironment(): void {
|
||||
|
@@ -1,23 +1,18 @@
|
||||
import cluster from 'node:cluster';
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { ChartManagementService } from '@/core/chart/ChartManagementService.js';
|
||||
import { QueueProcessorService } from '@/queue/QueueProcessorService.js';
|
||||
import { NestLogger } from '@/NestLogger.js';
|
||||
import { QueueProcessorModule } from '@/queue/QueueProcessorModule.js';
|
||||
import { envOption } from '@/env.js';
|
||||
import { jobQueue, server } from './common.js';
|
||||
|
||||
/**
|
||||
* Init worker process
|
||||
*/
|
||||
export async function workerMain() {
|
||||
const jobQueue = await NestFactory.createApplicationContext(QueueProcessorModule, {
|
||||
logger: new NestLogger(),
|
||||
});
|
||||
jobQueue.enableShutdownHooks();
|
||||
|
||||
// start job queue
|
||||
jobQueue.get(QueueProcessorService).start();
|
||||
|
||||
jobQueue.get(ChartManagementService).start();
|
||||
if (envOption.onlyServer) {
|
||||
await server();
|
||||
} else if (envOption.onlyQueue) {
|
||||
await jobQueue();
|
||||
} else {
|
||||
await jobQueue();
|
||||
}
|
||||
|
||||
if (cluster.isWorker) {
|
||||
// Send a 'ready' message to parent process
|
||||
|
@@ -44,6 +44,7 @@ const ACHIEVEMENT_TYPES = [
|
||||
'loggedInOnNewYearsDay',
|
||||
'noteClipped1',
|
||||
'noteFavorited1',
|
||||
'myNoteFavorited1',
|
||||
'profileFilled',
|
||||
'markedAsCat',
|
||||
'following1',
|
||||
@@ -61,12 +62,14 @@ const ACHIEVEMENT_TYPES = [
|
||||
'collectAchievements30',
|
||||
'viewAchievements3min',
|
||||
'iLoveMisskey',
|
||||
'foundTreasure',
|
||||
'client30min',
|
||||
'noteDeletedWithin1min',
|
||||
'postedAtLateNight',
|
||||
'postedAt0min0sec',
|
||||
'selfQuote',
|
||||
'htl20npm',
|
||||
'viewInstanceChart',
|
||||
'outputHelloWorldOnScratchpad',
|
||||
'open3windows',
|
||||
'driveFolderCircularReference',
|
||||
@@ -94,7 +97,7 @@ export class AchievementService {
|
||||
@bindThis
|
||||
public async create(
|
||||
userId: User['id'],
|
||||
type: string,
|
||||
type: typeof ACHIEVEMENT_TYPES[number],
|
||||
): Promise<void> {
|
||||
if (!ACHIEVEMENT_TYPES.includes(type)) return;
|
||||
|
||||
|
@@ -77,10 +77,16 @@ export class AntennaService implements OnApplicationShutdown {
|
||||
const { type, body } = obj.message as StreamMessages['internal']['payload'];
|
||||
switch (type) {
|
||||
case 'antennaCreated':
|
||||
this.antennas.push(body);
|
||||
this.antennas.push({
|
||||
...body,
|
||||
createdAt: new Date(body.createdAt),
|
||||
});
|
||||
break;
|
||||
case 'antennaUpdated':
|
||||
this.antennas[this.antennas.findIndex(a => a.id === body.id)] = body;
|
||||
this.antennas[this.antennas.findIndex(a => a.id === body.id)] = {
|
||||
...body,
|
||||
createdAt: new Date(body.createdAt),
|
||||
};
|
||||
break;
|
||||
case 'antennaDeleted':
|
||||
this.antennas = this.antennas.filter(a => a.id !== body.id);
|
||||
|
@@ -21,18 +21,13 @@ export class CaptchaService {
|
||||
response,
|
||||
});
|
||||
|
||||
const res = await this.httpRequestService.fetch(
|
||||
url,
|
||||
{
|
||||
method: 'POST',
|
||||
body: params,
|
||||
const res = await this.httpRequestService.send(url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(params),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
{
|
||||
noOkError: true,
|
||||
}
|
||||
).catch(err => {
|
||||
throw `${err.message ?? err}`;
|
||||
});
|
||||
}, { throwErrorWhenResponseNotOk: false });
|
||||
|
||||
if (!res.ok) {
|
||||
throw `${res.status}`;
|
||||
|
@@ -4,16 +4,15 @@ import * as util from 'node:util';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import IPCIDR from 'ip-cidr';
|
||||
import PrivateIp from 'private-ip';
|
||||
import got, * as Got from 'got';
|
||||
import chalk from 'chalk';
|
||||
import got, * as Got from 'got';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type { Config } from '@/config.js';
|
||||
import { HttpRequestService, UndiciFetcher } from '@/core/HttpRequestService.js';
|
||||
import { HttpRequestService } from '@/core/HttpRequestService.js';
|
||||
import { createTemp } from '@/misc/create-temp.js';
|
||||
import { StatusError } from '@/misc/status-error.js';
|
||||
import { LoggerService } from '@/core/LoggerService.js';
|
||||
import type Logger from '@/logger.js';
|
||||
import { buildConnector } from 'undici';
|
||||
|
||||
const pipeline = util.promisify(stream.pipeline);
|
||||
import { bindThis } from '@/decorators.js';
|
||||
@@ -21,7 +20,6 @@ import { bindThis } from '@/decorators.js';
|
||||
@Injectable()
|
||||
export class DownloadService {
|
||||
private logger: Logger;
|
||||
private undiciFetcher: UndiciFetcher;
|
||||
|
||||
constructor(
|
||||
@Inject(DI.config)
|
||||
@@ -31,24 +29,6 @@ export class DownloadService {
|
||||
private loggerService: LoggerService,
|
||||
) {
|
||||
this.logger = this.loggerService.getLogger('download');
|
||||
|
||||
this.undiciFetcher = new UndiciFetcher(this.httpRequestService.getStandardUndiciFetcherOption(
|
||||
{
|
||||
connect: process.env.NODE_ENV === 'development' ?
|
||||
this.httpRequestService.clientDefaults.connect
|
||||
:
|
||||
this.httpRequestService.getConnectorWithIpCheck(
|
||||
buildConnector({
|
||||
...this.httpRequestService.clientDefaults.connect,
|
||||
}),
|
||||
(ip) => !this.isPrivateIp(ip)
|
||||
),
|
||||
bodyTimeout: 30 * 1000,
|
||||
},
|
||||
{
|
||||
connect: this.httpRequestService.clientDefaults.connect,
|
||||
}
|
||||
), this.logger);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
@@ -59,14 +39,60 @@ export class DownloadService {
|
||||
const operationTimeout = 60 * 1000;
|
||||
const maxSize = this.config.maxFileSize ?? 262144000;
|
||||
|
||||
const response = await this.undiciFetcher.fetch(url);
|
||||
const req = got.stream(url, {
|
||||
headers: {
|
||||
'User-Agent': this.config.userAgent,
|
||||
},
|
||||
timeout: {
|
||||
lookup: timeout,
|
||||
connect: timeout,
|
||||
secureConnect: timeout,
|
||||
socket: timeout, // read timeout
|
||||
response: timeout,
|
||||
send: timeout,
|
||||
request: operationTimeout, // whole operation timeout
|
||||
},
|
||||
agent: {
|
||||
http: this.httpRequestService.httpAgent,
|
||||
https: this.httpRequestService.httpsAgent,
|
||||
},
|
||||
http2: false, // default
|
||||
retry: {
|
||||
limit: 0,
|
||||
},
|
||||
}).on('response', (res: Got.Response) => {
|
||||
if ((process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'test') && !this.config.proxy && res.ip) {
|
||||
if (this.isPrivateIp(res.ip)) {
|
||||
this.logger.warn(`Blocked address: ${res.ip}`);
|
||||
req.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
if (response.body === null) {
|
||||
throw new StatusError('No body', 400, 'No body');
|
||||
const contentLength = res.headers['content-length'];
|
||||
if (contentLength != null) {
|
||||
const size = Number(contentLength);
|
||||
if (size > maxSize) {
|
||||
this.logger.warn(`maxSize exceeded (${size} > ${maxSize}) on response`);
|
||||
req.destroy();
|
||||
}
|
||||
}
|
||||
}).on('downloadProgress', (progress: Got.Progress) => {
|
||||
if (progress.transferred > maxSize) {
|
||||
this.logger.warn(`maxSize exceeded (${progress.transferred} > ${maxSize}) on downloadProgress`);
|
||||
req.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
await pipeline(req, fs.createWriteStream(path));
|
||||
} catch (e) {
|
||||
if (e instanceof Got.HTTPError) {
|
||||
throw new StatusError(`${e.response.statusCode} ${e.response.statusMessage}`, e.response.statusCode, e.response.statusMessage);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
await pipeline(stream.Readable.fromWeb(response.body), fs.createWriteStream(path));
|
||||
|
||||
this.logger.succ(`Download finished: ${chalk.cyan(url)}`);
|
||||
}
|
||||
|
||||
|
@@ -2,6 +2,7 @@ import { URL } from 'node:url';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { JSDOM } from 'jsdom';
|
||||
import tinycolor from 'tinycolor2';
|
||||
import fetch from 'node-fetch';
|
||||
import type { Instance } from '@/models/entities/Instance.js';
|
||||
import type { InstancesRepository } from '@/models/index.js';
|
||||
import { AppLockService } from '@/core/AppLockService.js';
|
||||
@@ -190,7 +191,9 @@ export class FetchInstanceMetadataService {
|
||||
|
||||
const faviconUrl = url + '/favicon.ico';
|
||||
|
||||
const favicon = await this.httpRequestService.fetch(faviconUrl, {}, { noOkError: true });
|
||||
const favicon = await this.httpRequestService.send(faviconUrl, {
|
||||
method: 'HEAD',
|
||||
}, { throwErrorWhenResponseNotOk: false });
|
||||
|
||||
if (favicon.ok) {
|
||||
return faviconUrl;
|
||||
|
@@ -1,257 +1,67 @@
|
||||
import * as http from 'node:http';
|
||||
import * as https from 'node:https';
|
||||
import CacheableLookup from 'cacheable-lookup';
|
||||
import fetch from 'node-fetch';
|
||||
import { HttpProxyAgent, HttpsProxyAgent } from 'hpagent';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type { Config } from '@/config.js';
|
||||
import { StatusError } from '@/misc/status-error.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import * as undici from 'undici';
|
||||
import { LookupFunction } from 'node:net';
|
||||
import { LoggerService } from '@/core/LoggerService.js';
|
||||
import type Logger from '@/logger.js';
|
||||
|
||||
// true to allow, false to deny
|
||||
export type IpChecker = (ip: string) => boolean;
|
||||
|
||||
/*
|
||||
* Child class to create and save Agent for fetch.
|
||||
* You should construct this when you want
|
||||
* to change timeout, size limit, socket connect function, etc.
|
||||
*/
|
||||
export class UndiciFetcher {
|
||||
/**
|
||||
* Get http non-proxy agent (undici)
|
||||
*/
|
||||
public nonProxiedAgent: undici.Agent;
|
||||
|
||||
/**
|
||||
* Get http proxy or non-proxy agent (undici)
|
||||
*/
|
||||
public agent: undici.ProxyAgent | undici.Agent;
|
||||
|
||||
private proxyBypassHosts: string[];
|
||||
private userAgent: string | undefined;
|
||||
|
||||
private logger: Logger | undefined;
|
||||
|
||||
constructor(
|
||||
args: {
|
||||
agentOptions: undici.Agent.Options;
|
||||
proxy?: {
|
||||
uri: string;
|
||||
options?: undici.Agent.Options; // Override of agentOptions
|
||||
},
|
||||
proxyBypassHosts?: string[];
|
||||
userAgent?: string;
|
||||
},
|
||||
logger?: Logger,
|
||||
) {
|
||||
this.logger = logger;
|
||||
this.logger?.debug('UndiciFetcher constructor', args);
|
||||
|
||||
this.proxyBypassHosts = args.proxyBypassHosts ?? [];
|
||||
this.userAgent = args.userAgent;
|
||||
|
||||
this.nonProxiedAgent = new undici.Agent({
|
||||
...args.agentOptions,
|
||||
connect: (process.env.NODE_ENV !== 'production' && typeof args.agentOptions.connect !== 'function')
|
||||
? (options, cb) => {
|
||||
// Custom connector for debug
|
||||
undici.buildConnector(args.agentOptions.connect as undici.buildConnector.BuildOptions)(options, (err, socket) => {
|
||||
this.logger?.debug('Socket connector called', socket);
|
||||
if (err) {
|
||||
this.logger?.debug(`Socket error`, err);
|
||||
cb(new Error(`Error while socket connecting\n${err}`), null);
|
||||
return;
|
||||
}
|
||||
this.logger?.debug(`Socket connected: port ${socket.localPort} => remote ${socket.remoteAddress}`);
|
||||
cb(null, socket);
|
||||
});
|
||||
} : args.agentOptions.connect,
|
||||
});
|
||||
|
||||
this.agent = args.proxy
|
||||
? new undici.ProxyAgent({
|
||||
...args.agentOptions,
|
||||
...args.proxy.options,
|
||||
|
||||
uri: args.proxy.uri,
|
||||
|
||||
connect: (process.env.NODE_ENV !== 'production' && typeof (args.proxy?.options?.connect ?? args.agentOptions.connect) !== 'function')
|
||||
? (options, cb) => {
|
||||
// Custom connector for debug
|
||||
undici.buildConnector((args.proxy?.options?.connect ?? args.agentOptions.connect) as undici.buildConnector.BuildOptions)(options, (err, socket) => {
|
||||
this.logger?.debug('Socket connector called (secure)', socket);
|
||||
if (err) {
|
||||
this.logger?.debug(`Socket error`, err);
|
||||
cb(new Error(`Error while socket connecting\n${err}`), null);
|
||||
return;
|
||||
}
|
||||
this.logger?.debug(`Socket connected (secure): port ${socket.localPort} => remote ${socket.remoteAddress}`);
|
||||
cb(null, socket);
|
||||
});
|
||||
} : (args.proxy?.options?.connect ?? args.agentOptions.connect),
|
||||
})
|
||||
: this.nonProxiedAgent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get agent by URL
|
||||
* @param url URL
|
||||
* @param bypassProxy Allways bypass proxy
|
||||
*/
|
||||
@bindThis
|
||||
public getAgentByUrl(url: URL, bypassProxy = false): undici.Agent | undici.ProxyAgent {
|
||||
if (bypassProxy || this.proxyBypassHosts.includes(url.hostname)) {
|
||||
return this.nonProxiedAgent;
|
||||
} else {
|
||||
return this.agent;
|
||||
}
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async fetch(
|
||||
url: string | URL,
|
||||
options: undici.RequestInit = {},
|
||||
privateOptions: { noOkError?: boolean; bypassProxy?: boolean; } = { noOkError: false, bypassProxy: false }
|
||||
): Promise<undici.Response> {
|
||||
const res = await undici.fetch(url, {
|
||||
dispatcher: this.getAgentByUrl(new URL(url), privateOptions.bypassProxy),
|
||||
...options,
|
||||
headers: {
|
||||
'User-Agent': this.userAgent ?? '',
|
||||
...(options.headers ?? {}),
|
||||
},
|
||||
}).catch((err) => {
|
||||
this.logger?.error(`fetch error to ${typeof url === 'string' ? url : url.href}`, err);
|
||||
throw new StatusError('Resource Unreachable', 500, 'Resource Unreachable');
|
||||
});
|
||||
if (!res.ok && !privateOptions.noOkError) {
|
||||
throw new StatusError(`${res.status} ${res.statusText}`, res.status, res.statusText);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async getJson<T extends unknown>(url: string, accept = 'application/json, */*', headers?: Record<string, string>): Promise<T> {
|
||||
const res = await this.fetch(
|
||||
url,
|
||||
{
|
||||
headers: Object.assign({
|
||||
Accept: accept,
|
||||
}, headers ?? {}),
|
||||
}
|
||||
);
|
||||
|
||||
return await res.json() as T;
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async getHtml(url: string, accept = 'text/html, */*', headers?: Record<string, string>): Promise<string> {
|
||||
const res = await this.fetch(
|
||||
url,
|
||||
{
|
||||
headers: Object.assign({
|
||||
Accept: accept,
|
||||
}, headers ?? {}),
|
||||
}
|
||||
);
|
||||
|
||||
return await res.text();
|
||||
}
|
||||
}
|
||||
import type { Response } from 'node-fetch';
|
||||
import type { URL } from 'node:url';
|
||||
|
||||
@Injectable()
|
||||
export class HttpRequestService {
|
||||
public defaultFetcher: UndiciFetcher;
|
||||
public fetch: UndiciFetcher['fetch'];
|
||||
public getHtml: UndiciFetcher['getHtml'];
|
||||
public defaultJsonFetcher: UndiciFetcher;
|
||||
public getJson: UndiciFetcher['getJson'];
|
||||
|
||||
//#region for old http/https, only used in S3Service
|
||||
// http non-proxy agent
|
||||
/**
|
||||
* Get http non-proxy agent
|
||||
*/
|
||||
private http: http.Agent;
|
||||
|
||||
// https non-proxy agent
|
||||
/**
|
||||
* Get https non-proxy agent
|
||||
*/
|
||||
private https: https.Agent;
|
||||
|
||||
// http proxy or non-proxy agent
|
||||
/**
|
||||
* Get http proxy or non-proxy agent
|
||||
*/
|
||||
public httpAgent: http.Agent;
|
||||
|
||||
// https proxy or non-proxy agent
|
||||
/**
|
||||
* Get https proxy or non-proxy agent
|
||||
*/
|
||||
public httpsAgent: https.Agent;
|
||||
//#endregion
|
||||
|
||||
public readonly dnsCache: CacheableLookup;
|
||||
public readonly clientDefaults: undici.Agent.Options;
|
||||
private maxSockets: number;
|
||||
|
||||
private logger: Logger;
|
||||
|
||||
constructor(
|
||||
@Inject(DI.config)
|
||||
private config: Config,
|
||||
private loggerService: LoggerService,
|
||||
) {
|
||||
this.logger = this.loggerService.getLogger('http-request');
|
||||
|
||||
this.dnsCache = new CacheableLookup({
|
||||
const cache = new CacheableLookup({
|
||||
maxTtl: 3600, // 1hours
|
||||
errorTtl: 30, // 30secs
|
||||
lookup: false, // nativeのdns.lookupにfallbackしない
|
||||
});
|
||||
|
||||
this.clientDefaults = {
|
||||
keepAliveTimeout: 30 * 1000,
|
||||
keepAliveMaxTimeout: 10 * 60 * 1000,
|
||||
keepAliveTimeoutThreshold: 1 * 1000,
|
||||
strictContentLength: true,
|
||||
headersTimeout: 10 * 1000,
|
||||
bodyTimeout: 10 * 1000,
|
||||
maxHeaderSize: 16364, // default
|
||||
maxResponseSize: 10 * 1024 * 1024,
|
||||
maxRedirections: 3,
|
||||
connect: {
|
||||
timeout: 10 * 1000, // コネクションが確立するまでのタイムアウト
|
||||
maxCachedSessions: 300, // TLSセッションのキャッシュ数 https://github.com/nodejs/undici/blob/v5.14.0/lib/core/connect.js#L80
|
||||
lookup: this.dnsCache.lookup as LookupFunction, // https://github.com/nodejs/undici/blob/v5.14.0/lib/core/connect.js#L98
|
||||
},
|
||||
}
|
||||
|
||||
this.maxSockets = Math.max(64, this.config.deliverJobConcurrency ?? 128);
|
||||
|
||||
this.defaultFetcher = new UndiciFetcher(this.getStandardUndiciFetcherOption(), this.logger);
|
||||
|
||||
this.fetch = this.defaultFetcher.fetch;
|
||||
this.getHtml = this.defaultFetcher.getHtml;
|
||||
|
||||
this.defaultJsonFetcher = new UndiciFetcher(this.getStandardUndiciFetcherOption({
|
||||
maxResponseSize: 1024 * 256,
|
||||
}), this.logger);
|
||||
|
||||
this.getJson = this.defaultJsonFetcher.getJson;
|
||||
|
||||
//#region for old http/https, only used in S3Service
|
||||
|
||||
this.http = new http.Agent({
|
||||
keepAlive: true,
|
||||
keepAliveMsecs: 30 * 1000,
|
||||
lookup: this.dnsCache.lookup,
|
||||
lookup: cache.lookup,
|
||||
} as http.AgentOptions);
|
||||
|
||||
this.https = new https.Agent({
|
||||
keepAlive: true,
|
||||
keepAliveMsecs: 30 * 1000,
|
||||
lookup: this.dnsCache.lookup,
|
||||
lookup: cache.lookup,
|
||||
} as https.AgentOptions);
|
||||
|
||||
|
||||
const maxSockets = Math.max(256, config.deliverJobConcurrency ?? 128);
|
||||
|
||||
this.httpAgent = config.proxy
|
||||
? new HttpProxyAgent({
|
||||
keepAlive: true,
|
||||
keepAliveMsecs: 30 * 1000,
|
||||
maxSockets: this.maxSockets,
|
||||
maxSockets,
|
||||
maxFreeSockets: 256,
|
||||
scheduling: 'lifo',
|
||||
proxy: config.proxy,
|
||||
@@ -262,42 +72,21 @@ export class HttpRequestService {
|
||||
? new HttpsProxyAgent({
|
||||
keepAlive: true,
|
||||
keepAliveMsecs: 30 * 1000,
|
||||
maxSockets: this.maxSockets,
|
||||
maxSockets,
|
||||
maxFreeSockets: 256,
|
||||
scheduling: 'lifo',
|
||||
proxy: config.proxy,
|
||||
})
|
||||
: this.https;
|
||||
//#endregion
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public getStandardUndiciFetcherOption(opts: undici.Agent.Options = {}, proxyOpts: undici.Agent.Options = {}) {
|
||||
return {
|
||||
agentOptions: {
|
||||
...this.clientDefaults,
|
||||
...opts,
|
||||
},
|
||||
...(this.config.proxy ? {
|
||||
proxy: {
|
||||
uri: this.config.proxy,
|
||||
options: {
|
||||
connections: this.maxSockets,
|
||||
...proxyOpts,
|
||||
}
|
||||
}
|
||||
} : {}),
|
||||
userAgent: this.config.userAgent,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get http agent by URL
|
||||
* Get agent by URL
|
||||
* @param url URL
|
||||
* @param bypassProxy Allways bypass proxy
|
||||
*/
|
||||
@bindThis
|
||||
public getHttpAgentByUrl(url: URL, bypassProxy = false): http.Agent | https.Agent {
|
||||
public getAgentByUrl(url: URL, bypassProxy = false): http.Agent | https.Agent {
|
||||
if (bypassProxy || (this.config.proxyBypassHosts || []).includes(url.hostname)) {
|
||||
return url.protocol === 'http:' ? this.http : this.https;
|
||||
} else {
|
||||
@@ -305,37 +94,67 @@ export class HttpRequestService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* check ip
|
||||
*/
|
||||
@bindThis
|
||||
public getConnectorWithIpCheck(connector: undici.buildConnector.connector, checkIp: IpChecker): undici.buildConnector.connectorAsync {
|
||||
return (options, cb) => {
|
||||
connector(options, (err, socket) => {
|
||||
this.logger.debug('Socket connector (with ip checker) called', socket);
|
||||
if (err) {
|
||||
this.logger.error(`Socket error`, err)
|
||||
cb(new Error(`Error while socket connecting\n${err}`), null);
|
||||
return;
|
||||
}
|
||||
public async getJson(url: string, accept = 'application/json, */*', headers?: Record<string, string>): Promise<unknown> {
|
||||
const res = await this.send(url, {
|
||||
method: 'GET',
|
||||
headers: Object.assign({
|
||||
'User-Agent': this.config.userAgent,
|
||||
Accept: accept,
|
||||
}, headers ?? {}),
|
||||
timeout: 5000,
|
||||
size: 1024 * 256,
|
||||
});
|
||||
|
||||
if (socket.remoteAddress == undefined) {
|
||||
this.logger.error(`Socket error: remoteAddress is undefined`);
|
||||
cb(new Error('remoteAddress is undefined (maybe socket destroyed)'), null);
|
||||
return;
|
||||
}
|
||||
return await res.json();
|
||||
}
|
||||
|
||||
// allow
|
||||
if (checkIp(socket.remoteAddress)) {
|
||||
this.logger.debug(`Socket connected (ip ok): ${socket.localPort} => ${socket.remoteAddress}`);
|
||||
cb(null, socket);
|
||||
return;
|
||||
}
|
||||
@bindThis
|
||||
public async getHtml(url: string, accept = 'text/html, */*', headers?: Record<string, string>): Promise<string> {
|
||||
const res = await this.send(url, {
|
||||
method: 'GET',
|
||||
headers: Object.assign({
|
||||
'User-Agent': this.config.userAgent,
|
||||
Accept: accept,
|
||||
}, headers ?? {}),
|
||||
timeout: 5000,
|
||||
});
|
||||
|
||||
this.logger.error('IP is not allowed', socket);
|
||||
cb(new StatusError('IP is not allowed', 403, 'IP is not allowed'), null);
|
||||
socket.destroy();
|
||||
});
|
||||
};
|
||||
return await res.text();
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async send(url: string, args: {
|
||||
method?: string,
|
||||
body?: string,
|
||||
headers?: Record<string, string>,
|
||||
timeout?: number,
|
||||
size?: number,
|
||||
} = {}, extra: {
|
||||
throwErrorWhenResponseNotOk: boolean;
|
||||
} = {
|
||||
throwErrorWhenResponseNotOk: true,
|
||||
}): Promise<Response> {
|
||||
const timeout = args.timeout ?? 5000;
|
||||
|
||||
const controller = new AbortController();
|
||||
setTimeout(() => {
|
||||
controller.abort();
|
||||
}, timeout);
|
||||
|
||||
const res = await fetch(url, {
|
||||
method: args.method ?? 'GET',
|
||||
headers: args.headers,
|
||||
body: args.body,
|
||||
size: args.size ?? 10 * 1024 * 1024,
|
||||
agent: (url) => this.getAgentByUrl(url),
|
||||
signal: controller.signal,
|
||||
});
|
||||
|
||||
if (!res.ok && extra.throwErrorWhenResponseNotOk) {
|
||||
throw new StatusError(`${res.status} ${res.statusText}`, res.status, res.statusText);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
@@ -91,10 +91,12 @@ export class RoleService implements OnApplicationShutdown {
|
||||
case 'roleCreated': {
|
||||
const cached = this.rolesCache.get(null);
|
||||
if (cached) {
|
||||
body.createdAt = new Date(body.createdAt);
|
||||
body.updatedAt = new Date(body.updatedAt);
|
||||
body.lastUsedAt = new Date(body.lastUsedAt);
|
||||
cached.push(body);
|
||||
cached.push({
|
||||
...body,
|
||||
createdAt: new Date(body.createdAt),
|
||||
updatedAt: new Date(body.updatedAt),
|
||||
lastUsedAt: new Date(body.lastUsedAt),
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -103,10 +105,12 @@ export class RoleService implements OnApplicationShutdown {
|
||||
if (cached) {
|
||||
const i = cached.findIndex(x => x.id === body.id);
|
||||
if (i > -1) {
|
||||
body.createdAt = new Date(body.createdAt);
|
||||
body.updatedAt = new Date(body.updatedAt);
|
||||
body.lastUsedAt = new Date(body.lastUsedAt);
|
||||
cached[i] = body;
|
||||
cached[i] = {
|
||||
...body,
|
||||
createdAt: new Date(body.createdAt),
|
||||
updatedAt: new Date(body.updatedAt),
|
||||
lastUsedAt: new Date(body.lastUsedAt),
|
||||
};
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -121,8 +125,10 @@ export class RoleService implements OnApplicationShutdown {
|
||||
case 'userRoleAssigned': {
|
||||
const cached = this.roleAssignmentByUserIdCache.get(body.userId);
|
||||
if (cached) {
|
||||
body.createdAt = new Date(body.createdAt);
|
||||
cached.push(body);
|
||||
cached.push({
|
||||
...body,
|
||||
createdAt: new Date(body.createdAt),
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@@ -33,7 +33,7 @@ export class S3Service {
|
||||
? false
|
||||
: meta.objectStorageS3ForcePathStyle,
|
||||
httpOptions: {
|
||||
agent: this.httpRequestService.getHttpAgentByUrl(new URL(u), !meta.objectStorageUseProxy),
|
||||
agent: this.httpRequestService.getAgentByUrl(new URL(u), !meta.objectStorageUseProxy),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@@ -21,11 +21,11 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||
import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
|
||||
import type { UserKeypair } from '@/models/entities/UserKeypair.js';
|
||||
import type { UsersRepository, UserProfilesRepository, NotesRepository, DriveFilesRepository, EmojisRepository, PollsRepository } from '@/models/index.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { LdSignatureService } from './LdSignatureService.js';
|
||||
import { ApMfmService } from './ApMfmService.js';
|
||||
import type { IActivity, IObject } from './type.js';
|
||||
import type { IIdentifier } from './models/identifier.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
|
||||
@Injectable()
|
||||
export class ApRendererService {
|
||||
|
@@ -5,7 +5,7 @@ import { DI } from '@/di-symbols.js';
|
||||
import type { Config } from '@/config.js';
|
||||
import type { User } from '@/models/entities/User.js';
|
||||
import { UserKeypairStoreService } from '@/core/UserKeypairStoreService.js';
|
||||
import { HttpRequestService, UndiciFetcher } from '@/core/HttpRequestService.js';
|
||||
import { HttpRequestService } from '@/core/HttpRequestService.js';
|
||||
import { LoggerService } from '@/core/LoggerService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import type Logger from '@/logger.js';
|
||||
@@ -30,7 +30,6 @@ type PrivateKey = {
|
||||
|
||||
@Injectable()
|
||||
export class ApRequestService {
|
||||
private undiciFetcher: UndiciFetcher;
|
||||
private logger: Logger;
|
||||
|
||||
constructor(
|
||||
@@ -41,10 +40,8 @@ export class ApRequestService {
|
||||
private httpRequestService: HttpRequestService,
|
||||
private loggerService: LoggerService,
|
||||
) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
this.logger = this.loggerService?.getLogger('ap-request'); // なぜか TypeError: Cannot read properties of undefined (reading 'getLogger') と言われる
|
||||
this.undiciFetcher = new UndiciFetcher(this.httpRequestService.getStandardUndiciFetcherOption({
|
||||
maxRedirections: 0,
|
||||
}), this.logger );
|
||||
}
|
||||
|
||||
@bindThis
|
||||
@@ -163,14 +160,11 @@ export class ApRequestService {
|
||||
},
|
||||
});
|
||||
|
||||
await this.undiciFetcher.fetch(
|
||||
url,
|
||||
{
|
||||
method: req.request.method,
|
||||
headers: req.request.headers,
|
||||
body,
|
||||
}
|
||||
);
|
||||
await this.httpRequestService.send(url, {
|
||||
method: req.request.method,
|
||||
headers: req.request.headers,
|
||||
body,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -192,13 +186,10 @@ export class ApRequestService {
|
||||
},
|
||||
});
|
||||
|
||||
const res = await this.httpRequestService.fetch(
|
||||
url,
|
||||
{
|
||||
method: req.request.method,
|
||||
headers: req.request.headers,
|
||||
}
|
||||
);
|
||||
const res = await this.httpRequestService.send(url, {
|
||||
method: req.request.method,
|
||||
headers: req.request.headers,
|
||||
});
|
||||
|
||||
return await res.json();
|
||||
}
|
||||
|
@@ -4,22 +4,21 @@ import { InstanceActorService } from '@/core/InstanceActorService.js';
|
||||
import type { NotesRepository, PollsRepository, NoteReactionsRepository, UsersRepository } from '@/models/index.js';
|
||||
import type { Config } from '@/config.js';
|
||||
import { MetaService } from '@/core/MetaService.js';
|
||||
import { HttpRequestService, UndiciFetcher } from '@/core/HttpRequestService.js';
|
||||
import { HttpRequestService } from '@/core/HttpRequestService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { UtilityService } from '@/core/UtilityService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { LoggerService } from '@/core/LoggerService.js';
|
||||
import type Logger from '@/logger.js';
|
||||
import { isCollectionOrOrderedCollection } from './type.js';
|
||||
import { ApDbResolverService } from './ApDbResolverService.js';
|
||||
import { ApRendererService } from './ApRendererService.js';
|
||||
import { ApRequestService } from './ApRequestService.js';
|
||||
import { LoggerService } from '@/core/LoggerService.js';
|
||||
import type { IObject, ICollection, IOrderedCollection } from './type.js';
|
||||
import type Logger from '@/logger.js';
|
||||
|
||||
export class Resolver {
|
||||
private history: Set<string>;
|
||||
private user?: ILocalUser;
|
||||
private undiciFetcher: UndiciFetcher;
|
||||
private logger: Logger;
|
||||
|
||||
constructor(
|
||||
@@ -39,10 +38,8 @@ export class Resolver {
|
||||
private recursionLimit = 100,
|
||||
) {
|
||||
this.history = new Set();
|
||||
this.logger = this.loggerService?.getLogger('ap-resolve'); // なぜか TypeError: Cannot read properties of undefined (reading 'getLogger') と言われる
|
||||
this.undiciFetcher = new UndiciFetcher(this.httpRequestService.getStandardUndiciFetcherOption({
|
||||
maxRedirections: 0,
|
||||
}), this.logger);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
this.logger = this.loggerService?.getLogger('ap-resolve'); // なぜか TypeError: Cannot read properties of undefined (reading 'getLogger') と言われる
|
||||
}
|
||||
|
||||
@bindThis
|
||||
@@ -106,7 +103,7 @@ export class Resolver {
|
||||
|
||||
const object = (this.user
|
||||
? await this.apRequestService.signedGet(value, this.user) as IObject
|
||||
: await this.undiciFetcher.getJson<IObject>(value, 'application/activity+json, application/ld+json'));
|
||||
: await this.httpRequestService.getJson(value, 'application/activity+json, application/ld+json')) as IObject;
|
||||
|
||||
if (object == null || (
|
||||
Array.isArray(object['@context']) ?
|
||||
|
@@ -9,7 +9,7 @@ import { CONTEXTS } from './misc/contexts.js';
|
||||
class LdSignature {
|
||||
public debug = false;
|
||||
public preLoad = true;
|
||||
public loderTimeout = 10 * 1000;
|
||||
public loderTimeout = 5000;
|
||||
|
||||
constructor(
|
||||
private httpRequestService: HttpRequestService,
|
||||
@@ -115,19 +115,12 @@ class LdSignature {
|
||||
|
||||
@bindThis
|
||||
private async fetchDocument(url: string) {
|
||||
const json = await this.httpRequestService.fetch(
|
||||
url,
|
||||
{
|
||||
headers: {
|
||||
Accept: 'application/ld+json, application/json',
|
||||
},
|
||||
// TODO
|
||||
//timeout: this.loderTimeout,
|
||||
const json = await this.httpRequestService.send(url, {
|
||||
headers: {
|
||||
Accept: 'application/ld+json, application/json',
|
||||
},
|
||||
{
|
||||
noOkError: true,
|
||||
}
|
||||
).then(res => {
|
||||
timeout: this.loderTimeout,
|
||||
}, { throwErrorWhenResponseNotOk: false }).then(res => {
|
||||
if (!res.ok) {
|
||||
throw `${res.status} ${res.statusText}`;
|
||||
} else {
|
||||
|
@@ -566,22 +566,22 @@ export class ApPersonService implements OnModuleInit {
|
||||
|
||||
this.logger.info(`Updating the featured: ${user.uri}`);
|
||||
|
||||
if (resolver == null) resolver = this.apResolverService.createResolver();
|
||||
const _resolver = resolver ?? this.apResolverService.createResolver();
|
||||
|
||||
// Resolve to (Ordered)Collection Object
|
||||
const collection = await resolver.resolveCollection(user.featured);
|
||||
const collection = await _resolver.resolveCollection(user.featured);
|
||||
if (!isCollectionOrOrderedCollection(collection)) throw new Error('Object is not Collection or OrderedCollection');
|
||||
|
||||
// Resolve to Object(may be Note) arrays
|
||||
const unresolvedItems = isCollection(collection) ? collection.items : collection.orderedItems;
|
||||
const items = await Promise.all(toArray(unresolvedItems).map(x => resolver.resolve(x)));
|
||||
const items = await Promise.all(toArray(unresolvedItems).map(x => _resolver.resolve(x)));
|
||||
|
||||
// Resolve and regist Notes
|
||||
const limit = promiseLimit<Note | null>(2);
|
||||
const featuredNotes = await Promise.all(items
|
||||
.filter(item => getApType(item) === 'Note') // TODO: Noteでなくてもいいかも
|
||||
.slice(0, 5)
|
||||
.map(item => limit(() => this.apNoteService.resolveNote(item, resolver))));
|
||||
.map(item => limit(() => this.apNoteService.resolveNote(item, _resolver))));
|
||||
|
||||
await this.db.transaction(async transactionalEntityManager => {
|
||||
await transactionalEntityManager.delete(UserNotePining, { userId: user.id });
|
||||
|
@@ -32,8 +32,8 @@ export class EmojiEntityService {
|
||||
name: emoji.name,
|
||||
category: emoji.category,
|
||||
host: opts.omitHost ? undefined : emoji.host,
|
||||
// ?? emoji.originalUrl してるのは後方互換性のため
|
||||
url: opts.withUrl ? (emoji.publicUrl ?? emoji.originalUrl) : undefined,
|
||||
// || emoji.originalUrl してるのは後方互換性のため(publicUrlはstringなので??はだめ)
|
||||
url: opts.withUrl ? (emoji.publicUrl || emoji.originalUrl) : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
|
11
packages/backend/src/misc/dev-null.ts
Normal file
11
packages/backend/src/misc/dev-null.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Writable, WritableOptions } from "node:stream";
|
||||
|
||||
export class DevNull extends Writable implements NodeJS.WritableStream {
|
||||
constructor(opts?: WritableOptions) {
|
||||
super(opts);
|
||||
}
|
||||
|
||||
_write (chunk: any, encoding: BufferEncoding, cb: (err?: Error | null) => void) {
|
||||
setImmediate(cb);
|
||||
}
|
||||
}
|
@@ -1,9 +1,14 @@
|
||||
import IPCIDR from 'ip-cidr';
|
||||
|
||||
export function getIpHash(ip: string) {
|
||||
// because a single person may control many IPv6 addresses,
|
||||
// only a /64 subnet prefix of any IP will be taken into account.
|
||||
// (this means for IPv4 the entire address is used)
|
||||
const prefix = IPCIDR.createAddress(ip).mask(64);
|
||||
return 'ip-' + BigInt('0b' + prefix).toString(36);
|
||||
try {
|
||||
// because a single person may control many IPv6 addresses,
|
||||
// only a /64 subnet prefix of any IP will be taken into account.
|
||||
// (this means for IPv4 the entire address is used)
|
||||
const prefix = IPCIDR.createAddress(ip).mask(64);
|
||||
return 'ip-' + BigInt('0b' + prefix).toString(36);
|
||||
} catch (e) {
|
||||
const prefix = IPCIDR.createAddress(ip.replace(/:[0-9]+$/, '')).mask(64);
|
||||
return 'ip-' + BigInt('0b' + prefix).toString(36);
|
||||
}
|
||||
}
|
||||
|
@@ -6,10 +6,10 @@ import type { Config } from '@/config.js';
|
||||
import type Logger from '@/logger.js';
|
||||
import { HttpRequestService } from '@/core/HttpRequestService.js';
|
||||
import { StatusError } from '@/misc/status-error.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { QueueLoggerService } from '../QueueLoggerService.js';
|
||||
import type Bull from 'bull';
|
||||
import type { WebhookDeliverJobData } from '../types.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
|
||||
@Injectable()
|
||||
export class WebhookDeliverProcessorService {
|
||||
@@ -33,26 +33,23 @@ export class WebhookDeliverProcessorService {
|
||||
try {
|
||||
this.logger.debug(`delivering ${job.data.webhookId}`);
|
||||
|
||||
const res = await this.httpRequestService.fetch(
|
||||
job.data.to,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'User-Agent': 'Misskey-Hooks',
|
||||
'X-Misskey-Host': this.config.host,
|
||||
'X-Misskey-Hook-Id': job.data.webhookId,
|
||||
'X-Misskey-Hook-Secret': job.data.secret,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
hookId: job.data.webhookId,
|
||||
userId: job.data.userId,
|
||||
eventId: job.data.eventId,
|
||||
createdAt: job.data.createdAt,
|
||||
type: job.data.type,
|
||||
body: job.data.content,
|
||||
}),
|
||||
}
|
||||
);
|
||||
const res = await this.httpRequestService.send(job.data.to, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'User-Agent': 'Misskey-Hooks',
|
||||
'X-Misskey-Host': this.config.host,
|
||||
'X-Misskey-Hook-Id': job.data.webhookId,
|
||||
'X-Misskey-Hook-Secret': job.data.secret,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
hookId: job.data.webhookId,
|
||||
userId: job.data.userId,
|
||||
eventId: job.data.eventId,
|
||||
createdAt: job.data.createdAt,
|
||||
type: job.data.type,
|
||||
body: job.data.content,
|
||||
}),
|
||||
});
|
||||
|
||||
this.webhooksRepository.update({ id: job.data.webhookId }, {
|
||||
latestSentAt: new Date(),
|
||||
|
@@ -33,16 +33,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
private httpRequestService: HttpRequestService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const res = await this.httpRequestService.fetch(
|
||||
ps.url,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/rss+xml, */*',
|
||||
},
|
||||
// timeout: 5000,
|
||||
}
|
||||
);
|
||||
const res = await this.httpRequestService.send(ps.url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/rss+xml, */*',
|
||||
},
|
||||
timeout: 5000,
|
||||
});
|
||||
|
||||
const text = await res.text();
|
||||
|
||||
|
@@ -6,6 +6,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import { GetterService } from '@/server/api/GetterService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { ApiError } from '../../../error.js';
|
||||
import { AchievementService } from '@/core/AchievementService.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['notes', 'favorites'],
|
||||
@@ -51,6 +52,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
|
||||
private idService: IdService,
|
||||
private getterService: GetterService,
|
||||
private achievementService: AchievementService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
// Get favoritee
|
||||
@@ -76,6 +78,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
noteId: note.id,
|
||||
userId: me.id,
|
||||
});
|
||||
|
||||
if (note.userHost == null) {
|
||||
this.achievementService.create(note.userId, 'myNoteFavorited1');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -7,8 +7,8 @@ import { DI } from '@/di-symbols.js';
|
||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { MetaService } from '@/core/MetaService.js';
|
||||
import { HttpRequestService } from '@/core/HttpRequestService.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
import { GetterService } from '@/server/api/GetterService.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['notes'],
|
||||
@@ -83,20 +83,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
|
||||
const endpoint = instance.deeplIsPro ? 'https://api.deepl.com/v2/translate' : 'https://api-free.deepl.com/v2/translate';
|
||||
|
||||
const res = await this.httpRequestService.fetch(
|
||||
endpoint,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
Accept: 'application/json, */*',
|
||||
},
|
||||
body: params.toString(),
|
||||
const res = await this.httpRequestService.send(endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
Accept: 'application/json, */*',
|
||||
},
|
||||
{
|
||||
noOkError: false,
|
||||
}
|
||||
);
|
||||
body: params.toString(),
|
||||
});
|
||||
|
||||
const json = (await res.json()) as {
|
||||
translations: {
|
||||
|
@@ -30,7 +30,7 @@ export interface InternalStreamTypes {
|
||||
remoteUserUpdated: Serialized<{ id: User['id']; }>;
|
||||
follow: Serialized<{ followerId: User['id']; followeeId: User['id']; }>;
|
||||
unfollow: Serialized<{ followerId: User['id']; followeeId: User['id']; }>;
|
||||
policiesUpdated: Serialized<Role['options']>;
|
||||
policiesUpdated: Serialized<Role['policies']>;
|
||||
roleCreated: Serialized<Role>;
|
||||
roleDeleted: Serialized<Role>;
|
||||
roleUpdated: Serialized<Role>;
|
||||
|
@@ -22,18 +22,13 @@
|
||||
renderError('SOMETHING_HAPPENED_IN_PROMISE', e);
|
||||
};
|
||||
|
||||
const v = localStorage.getItem('v') || VERSION;
|
||||
|
||||
let forceError = localStorage.getItem('forceError');
|
||||
if (forceError != null) {
|
||||
renderError('FORCED_ERROR', 'This error is forced by having forceError in local storage.')
|
||||
}
|
||||
|
||||
//#region Detect language & fetch translations
|
||||
const localeVersion = localStorage.getItem('localeVersion');
|
||||
const localeOutdated = (localeVersion == null || localeVersion !== v);
|
||||
|
||||
if (!localStorage.hasOwnProperty('locale') || localeOutdated) {
|
||||
if (!localStorage.hasOwnProperty('locale')) {
|
||||
const supportedLangs = LANGS;
|
||||
let lang = localStorage.getItem('lang');
|
||||
if (lang == null || !supportedLangs.includes(lang)) {
|
||||
@@ -47,13 +42,31 @@
|
||||
}
|
||||
}
|
||||
|
||||
const res = await window.fetch(`/assets/locales/${lang}.${v}.json`);
|
||||
if (res.status === 200) {
|
||||
const metaRes = await window.fetch('/api/meta', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({}),
|
||||
credentials: 'omit',
|
||||
cache: 'no-cache',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
if (metaRes.status !== 200) {
|
||||
renderError('META_FETCH');
|
||||
return;
|
||||
}
|
||||
const meta = await metaRes.json();
|
||||
const v = meta.version;
|
||||
if (v == null) {
|
||||
renderError('META_FETCH_V');
|
||||
return;
|
||||
}
|
||||
const localRes = await window.fetch(`/assets/locales/${lang}.${v}.json`);
|
||||
if (localRes.status === 200) {
|
||||
localStorage.setItem('lang', lang);
|
||||
localStorage.setItem('locale', await res.text());
|
||||
localStorage.setItem('locale', await localRes.text());
|
||||
localStorage.setItem('localeVersion', v);
|
||||
} else {
|
||||
await checkUpdate();
|
||||
renderError('LOCALE_FETCH');
|
||||
return;
|
||||
}
|
||||
@@ -64,7 +77,6 @@
|
||||
function importAppScript() {
|
||||
import(`/vite/${CLIENT_ENTRY}`)
|
||||
.catch(async e => {
|
||||
await checkUpdate();
|
||||
console.error(e);
|
||||
renderError('APP_IMPORT', e);
|
||||
});
|
||||
@@ -291,48 +303,4 @@
|
||||
}
|
||||
`)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-inner-declarations
|
||||
async function checkUpdate() {
|
||||
try {
|
||||
const res = await window.fetch('/api/meta', {
|
||||
method: 'POST',
|
||||
cache: 'no-cache',
|
||||
body: '{}',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
const meta = await res.json();
|
||||
|
||||
if (meta.version == null) {
|
||||
throw new Error('failed to fetch instance metadata');
|
||||
}
|
||||
|
||||
if (meta.version != v) {
|
||||
localStorage.setItem('v', meta.version);
|
||||
refresh();
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
renderError('UPDATE_CHECK', e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-inner-declarations
|
||||
function refresh() {
|
||||
// Clear cache (service worker)
|
||||
try {
|
||||
navigator.serviceWorker.controller.postMessage('clear');
|
||||
navigator.serviceWorker.getRegistrations().then(registrations => {
|
||||
registrations.forEach(registration => registration.unregister());
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
location.reload();
|
||||
}
|
||||
})();
|
||||
|
@@ -35,7 +35,7 @@ html
|
||||
link(rel='prefetch' href='https://xn--931a.moe/assets/info.jpg')
|
||||
link(rel='prefetch' href='https://xn--931a.moe/assets/not-found.jpg')
|
||||
link(rel='prefetch' href='https://xn--931a.moe/assets/error.jpg')
|
||||
link(rel='stylesheet' href='/assets/tabler-icons/tabler-icons.css')
|
||||
link(rel='stylesheet' href='/assets/tabler-icons/tabler-icons.min.css')
|
||||
link(rel='modulepreload' href=`/vite/${clientEntry.file}`)
|
||||
|
||||
if !config.clientManifestExists
|
||||
|
@@ -12,7 +12,7 @@
|
||||
"@rollup/plugin-json": "6.0.0",
|
||||
"@rollup/pluginutils": "5.0.2",
|
||||
"@syuilo/aiscript": "0.12.2",
|
||||
"@tabler/icons": "^1.118.0",
|
||||
"@tabler/icons-webfont": "^2.0.0",
|
||||
"@vitejs/plugin-vue": "4.0.0",
|
||||
"@vue/compiler-sfc": "3.2.45",
|
||||
"autobind-decorator": "2.4.0",
|
||||
@@ -82,15 +82,15 @@
|
||||
"@types/uuid": "9.0.0",
|
||||
"@types/websocket": "1.0.5",
|
||||
"@types/ws": "8.5.4",
|
||||
"@typescript-eslint/eslint-plugin": "5.48.2",
|
||||
"@typescript-eslint/parser": "5.48.2",
|
||||
"@typescript-eslint/eslint-plugin": "5.49.0",
|
||||
"@typescript-eslint/parser": "5.49.0",
|
||||
"@vue/runtime-core": "3.2.45",
|
||||
"cross-env": "7.0.3",
|
||||
"cypress": "12.3.0",
|
||||
"eslint": "8.32.0",
|
||||
"eslint-plugin-import": "2.27.5",
|
||||
"eslint-plugin-vue": "9.9.0",
|
||||
"start-server-and-test": "1.15.2",
|
||||
"start-server-and-test": "1.15.3",
|
||||
"vue-eslint-parser": "^9.1.0",
|
||||
"vue-tsc": "^1.0.24"
|
||||
}
|
||||
|
@@ -16,8 +16,8 @@
|
||||
<time v-tooltip="new Date(achievement.unlockedAt).toLocaleString()">{{ new Date(achievement.unlockedAt).getFullYear() }}/{{ new Date(achievement.unlockedAt).getMonth() + 1 }}/{{ new Date(achievement.unlockedAt).getDate() }}</time>
|
||||
</span>
|
||||
</div>
|
||||
<div :class="$style.description">{{ i18n.ts._achievements._types['_' + achievement.name].description }}</div>
|
||||
<div v-if="i18n.ts._achievements._types['_' + achievement.name].flavor" :class="$style.flavor">{{ i18n.ts._achievements._types['_' + achievement.name].flavor }}</div>
|
||||
<div :class="$style.description">{{ withDescription ? i18n.ts._achievements._types['_' + achievement.name].description : '???' }}</div>
|
||||
<div v-if="i18n.ts._achievements._types['_' + achievement.name].flavor && withDescription" :class="$style.flavor">{{ i18n.ts._achievements._types['_' + achievement.name].flavor }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="withLocked">
|
||||
@@ -49,8 +49,10 @@ import { ACHIEVEMENT_TYPES, ACHIEVEMENT_BADGES, claimAchievement } from '@/scrip
|
||||
const props = withDefaults(defineProps<{
|
||||
user: misskey.entities.User;
|
||||
withLocked: boolean;
|
||||
withDescription: boolean;
|
||||
}>(), {
|
||||
withLocked: true,
|
||||
withDescription: true,
|
||||
});
|
||||
|
||||
let achievements = $ref();
|
||||
|
@@ -15,7 +15,7 @@
|
||||
<i v-else-if="notification.type === 'mention'" class="ti ti-at"></i>
|
||||
<i v-else-if="notification.type === 'quote'" class="ti ti-quote"></i>
|
||||
<i v-else-if="notification.type === 'pollEnded'" class="ti ti-chart-arrows"></i>
|
||||
<i v-else-if="notification.type === 'achievementEarned'" class="ti ti-military-award"></i>
|
||||
<i v-else-if="notification.type === 'achievementEarned'" class="ti ti-medal"></i>
|
||||
<!-- notification.reaction が null になることはまずないが、ここでoptional chaining使うと一部ブラウザで刺さるので念の為 -->
|
||||
<MkReactionIcon
|
||||
v-else-if="notification.type === 'reaction'"
|
||||
@@ -249,7 +249,7 @@ useTooltip(reactionRef, (showing) => {
|
||||
|
||||
.t_achievementEarned {
|
||||
padding: 3px;
|
||||
background: #88a6b7;
|
||||
background: #cb9a11;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
@@ -21,6 +21,7 @@ import { useTooltip } from '@/scripts/use-tooltip';
|
||||
import { $i } from '@/account';
|
||||
import MkReactionEffect from '@/components/MkReactionEffect.vue';
|
||||
import { claimAchievement } from '@/scripts/achievements';
|
||||
import { defaultStore } from '@/store';
|
||||
|
||||
const props = defineProps<{
|
||||
reaction: string;
|
||||
@@ -61,6 +62,7 @@ const toggleReaction = () => {
|
||||
|
||||
const anime = () => {
|
||||
if (document.hidden) return;
|
||||
if (!defaultStore.state.animation) return;
|
||||
|
||||
const rect = buttonEl.value.getBoundingClientRect();
|
||||
const x = rect.left + 16;
|
||||
|
@@ -31,11 +31,11 @@ const useOsNativeEmojis = computed(() => defaultStore.state.emojiStyle === 'nati
|
||||
const url = computed(() => {
|
||||
if (char.value) {
|
||||
return char2path(char.value);
|
||||
} else if (props.host == null) {
|
||||
} else if (props.host == null && !customEmojiName.includes('@')) {
|
||||
const found = customEmojis.find(x => x.name === customEmojiName);
|
||||
return found ? found.url : null;
|
||||
} else {
|
||||
const rawUrl = `/emoji/${customEmojiName}@${props.host}.webp`;
|
||||
const rawUrl = props.host ? `/emoji/${customEmojiName}@${props.host}.webp` : `/emoji/${customEmojiName}.webp`;
|
||||
return defaultStore.state.disableShowingAnimatedImages
|
||||
? getStaticImageUrl(rawUrl)
|
||||
: rawUrl;
|
||||
|
@@ -190,16 +190,22 @@ export default defineComponent({
|
||||
return h(MkSparkle, {}, genEl(token.children));
|
||||
}
|
||||
case 'rotate': {
|
||||
const degrees = parseInt(token.props.args.deg) ?? '90';
|
||||
const degrees = parseFloat(token.props.args.deg) ?? '90';
|
||||
style = `transform: rotate(${degrees}deg); transform-origin: center center;`;
|
||||
break;
|
||||
}
|
||||
case 'position': {
|
||||
const x = parseInt(token.props.args.x ?? '0');
|
||||
const y = parseInt(token.props.args.y ?? '0');
|
||||
const x = parseFloat(token.props.args.x ?? '0');
|
||||
const y = parseFloat(token.props.args.y ?? '0');
|
||||
style = `transform: translateX(${x}em) translateY(${y}em);`;
|
||||
break;
|
||||
}
|
||||
case 'scale': {
|
||||
const x = Math.min(parseFloat(token.props.args.x ?? '1'), 5);
|
||||
const y = Math.min(parseFloat(token.props.args.y ?? '1'), 5);
|
||||
style = `transform: scale(${x}, ${y});`;
|
||||
break;
|
||||
}
|
||||
case 'fg': {
|
||||
let color = token.props.args.color;
|
||||
if (!/^[0-9a-f]{3,6}$/i.test(color)) color = 'f00';
|
||||
|
@@ -10,8 +10,12 @@ export const apiUrl = url + '/api';
|
||||
export const wsUrl = url.replace('http://', 'ws://').replace('https://', 'wss://') + '/streaming';
|
||||
export const lang = miLocalStorage.getItem('lang');
|
||||
export const langs = _LANGS_;
|
||||
export const locale = JSON.parse(miLocalStorage.getItem('locale'));
|
||||
export let locale = JSON.parse(miLocalStorage.getItem('locale'));
|
||||
export const version = _VERSION_;
|
||||
export const instanceName = siteName === 'Misskey' ? host : siteName;
|
||||
export const ui = miLocalStorage.getItem('ui');
|
||||
export const debug = miLocalStorage.getItem('debug') === 'true';
|
||||
|
||||
export function updateLocale(newLocale) {
|
||||
locale = newLocale;
|
||||
}
|
||||
|
@@ -3,3 +3,7 @@ import { locale } from '@/config';
|
||||
import { I18n } from '@/scripts/i18n';
|
||||
|
||||
export const i18n = markRaw(new I18n(locale));
|
||||
|
||||
export function updateI18n(newLocale) {
|
||||
i18n.ts = newLocale;
|
||||
}
|
||||
|
@@ -25,10 +25,10 @@ import JSON5 from 'json5';
|
||||
import widgets from '@/widgets';
|
||||
import directives from '@/directives';
|
||||
import components from '@/components';
|
||||
import { version, ui, lang, host } from '@/config';
|
||||
import { version, ui, lang, host, updateLocale } from '@/config';
|
||||
import { applyTheme } from '@/scripts/theme';
|
||||
import { isDeviceDarkmode } from '@/scripts/is-device-darkmode';
|
||||
import { i18n } from '@/i18n';
|
||||
import { i18n, updateI18n } from '@/i18n';
|
||||
import { confirm, alert, post, popup, toast } from '@/os';
|
||||
import { stream } from '@/stream';
|
||||
import * as sound from '@/scripts/sound';
|
||||
@@ -87,9 +87,12 @@ import { fetchCustomEmojis } from './custom-emojis';
|
||||
if (localeOutdated) {
|
||||
const res = await window.fetch(`/assets/locales/${lang}.${version}.json`);
|
||||
if (res.status === 200) {
|
||||
miLocalStorage.setItem('locale', await res.text());
|
||||
const newLocale = await res.text();
|
||||
const parsedNewLocale = JSON.parse(newLocale);
|
||||
miLocalStorage.setItem('locale', newLocale);
|
||||
miLocalStorage.setItem('localeVersion', version);
|
||||
location.reload();
|
||||
updateLocale(parsedNewLocale);
|
||||
updateI18n(parsedNewLocale);
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
|
@@ -105,7 +105,7 @@ export const navbarItemDef = reactive({
|
||||
},
|
||||
achievements: {
|
||||
title: i18n.ts.achievements,
|
||||
icon: 'ti ti-military-award',
|
||||
icon: 'ti ti-medal',
|
||||
show: computed(() => $i != null),
|
||||
to: '/my/achievements',
|
||||
},
|
||||
|
@@ -4,11 +4,14 @@
|
||||
<div style="overflow: clip;">
|
||||
<MkSpacer :content-max="600" :margin-min="20">
|
||||
<div class="_gaps_m znqjceqz">
|
||||
<div ref="containerEl" v-panel class="about" :class="{ playing: easterEggEngine != null }">
|
||||
<img src="/client-assets/about-icon.png" alt="" class="icon" draggable="false" @load="iconLoaded" @click="gravity"/>
|
||||
<div class="misskey">Misskey</div>
|
||||
<div class="version">v{{ version }}</div>
|
||||
<span v-for="emoji in easterEggEmojis" :key="emoji.id" class="emoji" :data-physics-x="emoji.left" :data-physics-y="emoji.top" :class="{ _physics_circle_: !emoji.emoji.startsWith(':') }"><MkEmoji class="emoji" :emoji="emoji.emoji" :is-reaction="false" :normal="true" :no-style="true"/></span>
|
||||
<div v-panel class="about">
|
||||
<div ref="containerEl" class="container" :class="{ playing: easterEggEngine != null }">
|
||||
<img src="/client-assets/about-icon.png" alt="" class="icon" draggable="false" @load="iconLoaded" @click="gravity"/>
|
||||
<div class="misskey">Misskey</div>
|
||||
<div class="version">v{{ version }}</div>
|
||||
<span v-for="emoji in easterEggEmojis" :key="emoji.id" class="emoji" :data-physics-x="emoji.left" :data-physics-y="emoji.top" :class="{ _physics_circle_: !emoji.emoji.startsWith(':') }"><MkEmoji class="emoji" :emoji="emoji.emoji" :is-reaction="false" :normal="true" :no-style="true"/></span>
|
||||
</div>
|
||||
<button v-if="thereIsTreasure" class="_button treasure" @click="getTreasure"><img src="/fluent-emoji/1f3c6.png" class="treasureImg"></button>
|
||||
</div>
|
||||
<div style="text-align: center;">
|
||||
{{ i18n.ts._aboutMisskey.about }}<br><a href="https://misskey-hub.net/docs/misskey.html" target="_blank" class="_link">{{ i18n.ts.learnMore }}</a>
|
||||
@@ -37,11 +40,31 @@
|
||||
</FormSection>
|
||||
<FormSection>
|
||||
<template #label>{{ i18n.ts._aboutMisskey.contributors }}</template>
|
||||
<div class="_formLinksGrid">
|
||||
<FormLink to="https://github.com/syuilo" external>@syuilo</FormLink>
|
||||
<FormLink to="https://github.com/tamaina" external>@tamaina</FormLink>
|
||||
<FormLink to="https://github.com/acid-chicken" external>@acid-chicken</FormLink>
|
||||
<FormLink to="https://github.com/rinsuki" external>@rinsuki</FormLink>
|
||||
<div :class="$style.contributors">
|
||||
<a href="https://github.com/syuilo" target="_blank" :class="$style.contributor">
|
||||
<img src="https://avatars.githubusercontent.com/u/4439005?v=4" :class="$style.contributorAvatar">
|
||||
<span :class="$style.contributorUsername">@syuilo</span>
|
||||
</a>
|
||||
<a href="https://github.com/tamaina" target="_blank" :class="$style.contributor">
|
||||
<img src="https://avatars.githubusercontent.com/u/7973572?v=4" :class="$style.contributorAvatar">
|
||||
<span :class="$style.contributorUsername">@tamaina</span>
|
||||
</a>
|
||||
<a href="https://github.com/acid-chicken" target="_blank" :class="$style.contributor">
|
||||
<img src="https://avatars.githubusercontent.com/u/20679825?v=4" :class="$style.contributorAvatar">
|
||||
<span :class="$style.contributorUsername">@acid-chicken</span>
|
||||
</a>
|
||||
<a href="https://github.com/rinsuki" target="_blank" :class="$style.contributor">
|
||||
<img src="https://avatars.githubusercontent.com/u/6533808?v=4" :class="$style.contributorAvatar">
|
||||
<span :class="$style.contributorUsername">@rinsuki</span>
|
||||
</a>
|
||||
<a href="https://github.com/mei23" target="_blank" :class="$style.contributor">
|
||||
<img src="https://avatars.githubusercontent.com/u/30769358?v=4" :class="$style.contributorAvatar">
|
||||
<span :class="$style.contributorUsername">@mei23</span>
|
||||
</a>
|
||||
<a href="https://github.com/robflop" target="_blank" :class="$style.contributor">
|
||||
<img src="https://avatars.githubusercontent.com/u/8159402?v=4" :class="$style.contributorAvatar">
|
||||
<span :class="$style.contributorUsername">@robflop</span>
|
||||
</a>
|
||||
</div>
|
||||
<template #caption><MkLink url="https://github.com/misskey-dev/misskey/graphs/contributors">{{ i18n.ts._aboutMisskey.allContributors }}</MkLink></template>
|
||||
</FormSection>
|
||||
@@ -70,6 +93,8 @@ import { i18n } from '@/i18n';
|
||||
import { defaultStore } from '@/store';
|
||||
import * as os from '@/os';
|
||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||
import { claimAchievement, claimedAchievements } from '@/scripts/achievements';
|
||||
import { $i } from '@/account';
|
||||
|
||||
const patrons = [
|
||||
'まっちゃとーにゅ',
|
||||
@@ -152,6 +177,8 @@ const patrons = [
|
||||
'pixeldesu',
|
||||
];
|
||||
|
||||
let thereIsTreasure = $ref($i && !claimedAchievements.includes('foundTreasure'));
|
||||
|
||||
let easterEggReady = false;
|
||||
let easterEggEmojis = $ref([]);
|
||||
let easterEggEngine = $ref(null);
|
||||
@@ -187,6 +214,11 @@ function iLoveMisskey() {
|
||||
});
|
||||
}
|
||||
|
||||
function getTreasure() {
|
||||
thereIsTreasure = false;
|
||||
claimAchievement('foundTreasure');
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (easterEggEngine) {
|
||||
easterEggEngine.stop();
|
||||
@@ -207,54 +239,114 @@ definePageMetadata({
|
||||
.znqjceqz {
|
||||
> .about {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
padding: 16px;
|
||||
border-radius: var(--radius);
|
||||
|
||||
&.playing {
|
||||
&, * {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
* {
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
> .emoji {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
> .icon {
|
||||
display: block;
|
||||
width: 80px;
|
||||
margin: 0 auto;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
> .misskey {
|
||||
margin: 0.75em auto 0 auto;
|
||||
width: max-content;
|
||||
}
|
||||
|
||||
> .version {
|
||||
margin: 0 auto;
|
||||
width: max-content;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
> .emoji {
|
||||
> .treasure {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
top: 60px;
|
||||
left: 0;
|
||||
visibility: hidden;
|
||||
right: 0;
|
||||
margin: 0 auto;
|
||||
width: min-content;
|
||||
|
||||
> .treasureImg {
|
||||
width: 25px;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
}
|
||||
|
||||
> .container {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
padding: 16px;
|
||||
|
||||
&.playing {
|
||||
&, * {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
* {
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
> .emoji {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
> .icon {
|
||||
display: block;
|
||||
width: 80px;
|
||||
margin: 0 auto;
|
||||
border-radius: 16px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
> .misskey {
|
||||
margin: 0.75em auto 0 auto;
|
||||
width: max-content;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
> .version {
|
||||
margin: 0 auto;
|
||||
width: max-content;
|
||||
opacity: 0.5;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
> .emoji {
|
||||
pointer-events: none;
|
||||
font-size: 24px;
|
||||
width: 24px;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 0;
|
||||
left: 0;
|
||||
visibility: hidden;
|
||||
|
||||
> .emoji {
|
||||
pointer-events: none;
|
||||
font-size: 24px;
|
||||
width: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" module>
|
||||
.contributors {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
grid-gap: 12px;
|
||||
}
|
||||
|
||||
.contributor {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
background: var(--buttonBg);
|
||||
border-radius: 6px;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
background: var(--buttonHoverBg);
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: var(--accent);
|
||||
background: var(--buttonHoverBg);
|
||||
}
|
||||
}
|
||||
|
||||
.contributorAvatar {
|
||||
width: 30px;
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
.contributorUsername {
|
||||
margin-left: 12px;
|
||||
}
|
||||
</style>
|
||||
|
@@ -86,7 +86,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { ref, computed, watch } from 'vue';
|
||||
import XEmojis from './about.emojis.vue';
|
||||
import XFederation from './about.federation.vue';
|
||||
import { version, instanceName, host } from '@/config';
|
||||
@@ -100,6 +100,7 @@ import * as os from '@/os';
|
||||
import number from '@/filters/number';
|
||||
import { i18n } from '@/i18n';
|
||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||
import { claimAchievement } from '@/scripts/achievements';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
initialTab?: string;
|
||||
@@ -110,6 +111,12 @@ const props = withDefaults(defineProps<{
|
||||
let stats = $ref(null);
|
||||
let tab = $ref(props.initialTab);
|
||||
|
||||
watch($$(tab), () => {
|
||||
if (tab === 'charts') {
|
||||
claimAchievement('viewInstanceChart');
|
||||
}
|
||||
});
|
||||
|
||||
const initStats = () => os.api('stats', {
|
||||
}).then((res) => {
|
||||
stats = res;
|
||||
|
@@ -45,7 +45,7 @@ onDeactivated(() => {
|
||||
|
||||
definePageMetadata({
|
||||
title: i18n.ts.achievements,
|
||||
icon: 'ti ti-military-award',
|
||||
icon: 'ti ti-medal',
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@@ -38,7 +38,7 @@
|
||||
<template #label>Access key</template>
|
||||
</MkInput>
|
||||
|
||||
<MkInput v-model="objectStorageSecretKey">
|
||||
<MkInput v-model="objectStorageSecretKey" type="password">
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>Secret key</template>
|
||||
</MkInput>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<button class="zuvgdzyu _button" @click="menu">
|
||||
<img :src="`/emoji/${emoji.name}.webp`" class="img" loading="lazy"/>
|
||||
<img :src="emoji.url" class="img" loading="lazy"/>
|
||||
<div class="body">
|
||||
<div class="name _monospace">{{ emoji.name }}</div>
|
||||
<div class="info">{{ emoji.aliases.join(' ') }}</div>
|
||||
@@ -15,7 +15,12 @@ import copyToClipboard from '@/scripts/copy-to-clipboard';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
const props = defineProps<{
|
||||
emoji: Record<string, unknown>; // TODO
|
||||
emoji: {
|
||||
name: string;
|
||||
aliases: string[];
|
||||
category: string;
|
||||
url: string;
|
||||
};
|
||||
}>();
|
||||
|
||||
function menu(ev) {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<MkSpacer :content-max="1200">
|
||||
<MkAchievements :user="user" :with-locked="false"/>
|
||||
<MkAchievements :user="user" :with-locked="false" :with-description="$i != null && (props.user.id === $i.id)"/>
|
||||
</MkSpacer>
|
||||
</template>
|
||||
|
||||
|
@@ -81,7 +81,7 @@ const headerTabs = $computed(() => user ? [{
|
||||
}, ...(user.host == null ? [{
|
||||
key: 'achievements',
|
||||
title: i18n.ts.achievements,
|
||||
icon: 'ti ti-military-award',
|
||||
icon: 'ti ti-medal',
|
||||
}] : []), ...($i && ($i.id === user.id)) || user.publicReactions ? [{
|
||||
key: 'reactions',
|
||||
title: i18n.ts.reaction,
|
||||
|
@@ -40,6 +40,7 @@ export const ACHIEVEMENT_TYPES = [
|
||||
'loggedInOnNewYearsDay',
|
||||
'noteClipped1',
|
||||
'noteFavorited1',
|
||||
'myNoteFavorited1',
|
||||
'profileFilled',
|
||||
'markedAsCat',
|
||||
'following1',
|
||||
@@ -57,12 +58,14 @@ export const ACHIEVEMENT_TYPES = [
|
||||
'collectAchievements30',
|
||||
'viewAchievements3min',
|
||||
'iLoveMisskey',
|
||||
'foundTreasure',
|
||||
'client30min',
|
||||
'noteDeletedWithin1min',
|
||||
'postedAtLateNight',
|
||||
'postedAt0min0sec',
|
||||
'selfQuote',
|
||||
'htl20npm',
|
||||
'viewInstanceChart',
|
||||
'outputHelloWorldOnScratchpad',
|
||||
'open3windows',
|
||||
'driveFolderCircularReference',
|
||||
@@ -240,6 +243,11 @@ export const ACHIEVEMENT_BADGES = {
|
||||
bg: null,
|
||||
frame: 'bronze',
|
||||
},
|
||||
'myNoteFavorited1': {
|
||||
img: '/fluent-emoji/1f320.png',
|
||||
bg: null,
|
||||
frame: 'silver',
|
||||
},
|
||||
'profileFilled': {
|
||||
img: '/fluent-emoji/1f44c.png',
|
||||
bg: 'linear-gradient(0deg, rgb(187 183 59), rgb(255 143 77))',
|
||||
@@ -325,6 +333,11 @@ export const ACHIEVEMENT_BADGES = {
|
||||
bg: 'linear-gradient(0deg, rgb(255 77 77), rgb(247 155 214))',
|
||||
frame: 'silver',
|
||||
},
|
||||
'foundTreasure': {
|
||||
img: '/fluent-emoji/1f3c6.png',
|
||||
bg: 'linear-gradient(0deg, rgb(197 69 192), rgb(2 112 155))',
|
||||
frame: 'gold',
|
||||
},
|
||||
'client30min': {
|
||||
img: '/fluent-emoji/1f552.png',
|
||||
bg: 'linear-gradient(0deg, rgb(220 223 225), rgb(172 192 207))',
|
||||
@@ -355,6 +368,11 @@ export const ACHIEVEMENT_BADGES = {
|
||||
bg: 'linear-gradient(0deg, rgb(220 223 225), rgb(172 192 207))',
|
||||
frame: 'bronze',
|
||||
},
|
||||
'viewInstanceChart': {
|
||||
img: '/fluent-emoji/1f4ca.png',
|
||||
bg: 'linear-gradient(0deg, rgb(58 231 198), rgb(37 194 255))',
|
||||
frame: 'bronze',
|
||||
},
|
||||
'outputHelloWorldOnScratchpad': {
|
||||
img: '/fluent-emoji/1f530.png',
|
||||
bg: 'linear-gradient(0deg, rgb(58 231 198), rgb(37 194 255))',
|
||||
@@ -431,11 +449,12 @@ export const ACHIEVEMENT_BADGES = {
|
||||
frame: 'bronze' | 'silver' | 'gold' | 'platinum';
|
||||
}>;
|
||||
|
||||
export const claimedAchievements = ($i && $i.achievements) ? $i.achievements.map(x => x.name) : [];
|
||||
export const claimedAchievements: typeof ACHIEVEMENT_TYPES[number][] = ($i && $i.achievements) ? $i.achievements.map(x => x.name) : [];
|
||||
|
||||
const claimingQueue = new Set<string>();
|
||||
|
||||
export async function claimAchievement(type: typeof ACHIEVEMENT_TYPES[number]) {
|
||||
if ($i == null) return;
|
||||
if (claimedAchievements.includes(type)) return;
|
||||
claimingQueue.add(type);
|
||||
claimedAchievements.push(type);
|
||||
|
@@ -303,7 +303,7 @@ function getButtonOptions(def: values.Value | undefined, call: (fn: values.VFn,
|
||||
if (primary) utils.assertBoolean(primary);
|
||||
const rounded = def.value.get('rounded');
|
||||
if (rounded) utils.assertBoolean(rounded);
|
||||
const disabled = button.value.get('disabled');
|
||||
const disabled = def.value.get('disabled');
|
||||
if (disabled) utils.assertBoolean(disabled);
|
||||
|
||||
return {
|
||||
|
@@ -1 +1 @@
|
||||
export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'position', 'fg', 'bg', 'font', 'blur', 'rainbow', 'sparkle', 'rotate'];
|
||||
export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'scale', 'position', 'fg', 'bg', 'font', 'blur', 'rainbow', 'sparkle', 'rotate'];
|
||||
|
@@ -127,11 +127,13 @@ hr {
|
||||
}
|
||||
|
||||
.ti {
|
||||
vertical-align: -10%;
|
||||
line-height: 0.9em;
|
||||
vertical-align: -40%;
|
||||
line-height: 1em;
|
||||
|
||||
&:before {
|
||||
font-size: 130%;
|
||||
display: inline-block;
|
||||
font-size: 165%;
|
||||
width: 0.74em;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -54,6 +54,8 @@ export default defineConfig(({ command, mode }) => {
|
||||
'@/': __dirname + '/src/',
|
||||
'/client-assets/': __dirname + '/assets/',
|
||||
'/static-assets/': __dirname + '/../backend/assets/',
|
||||
'/fluent-emojis/': __dirname + '/../../fluent-emojis/dist/',
|
||||
'/fluent-emoji/': __dirname + '/../../fluent-emojis/dist/',
|
||||
},
|
||||
},
|
||||
|
||||
|
351
pnpm-lock.yaml
generated
351
pnpm-lock.yaml
generated
@@ -11,8 +11,8 @@ importers:
|
||||
'@tensorflow/tfjs-core': ^4.2.0
|
||||
'@types/gulp': 4.0.10
|
||||
'@types/gulp-rename': 2.0.1
|
||||
'@typescript-eslint/eslint-plugin': 5.48.2
|
||||
'@typescript-eslint/parser': 5.48.2
|
||||
'@typescript-eslint/eslint-plugin': 5.49.0
|
||||
'@typescript-eslint/parser': 5.49.0
|
||||
cross-env: 7.0.3
|
||||
cypress: 12.3.0
|
||||
eslint: ^8.32.0
|
||||
@@ -23,7 +23,7 @@ importers:
|
||||
gulp-replace: 1.1.4
|
||||
gulp-terser: 2.1.0
|
||||
js-yaml: 4.1.0
|
||||
start-server-and-test: 1.15.2
|
||||
start-server-and-test: 1.15.3
|
||||
typescript: 4.9.4
|
||||
dependencies:
|
||||
execa: 5.1.1
|
||||
@@ -39,18 +39,18 @@ importers:
|
||||
devDependencies:
|
||||
'@types/gulp': 4.0.10
|
||||
'@types/gulp-rename': 2.0.1
|
||||
'@typescript-eslint/eslint-plugin': 5.48.2_caon6io6stgpr7lz2rtbhekxqy
|
||||
'@typescript-eslint/parser': 5.48.2_7uibuqfxkfaozanbtbziikiqje
|
||||
'@typescript-eslint/eslint-plugin': 5.49.0_iu322prlnwsygkcra5kbpy22si
|
||||
'@typescript-eslint/parser': 5.49.0_7uibuqfxkfaozanbtbziikiqje
|
||||
cross-env: 7.0.3
|
||||
cypress: 12.3.0
|
||||
eslint: 8.32.0
|
||||
start-server-and-test: 1.15.2
|
||||
start-server-and-test: 1.15.3
|
||||
|
||||
packages/backend:
|
||||
specifiers:
|
||||
'@bull-board/api': ^4.10.2
|
||||
'@bull-board/fastify': ^4.10.2
|
||||
'@bull-board/ui': ^4.10.2
|
||||
'@bull-board/api': ^4.11.0
|
||||
'@bull-board/fastify': ^4.11.0
|
||||
'@bull-board/ui': ^4.11.0
|
||||
'@discordapp/twemoji': 14.0.2
|
||||
'@fastify/accepts': 4.1.0
|
||||
'@fastify/cookie': ^8.3.0
|
||||
@@ -68,8 +68,8 @@ importers:
|
||||
'@swc/cli': ^0.1.59
|
||||
'@swc/core': 1.3.27
|
||||
'@swc/jest': 0.2.24
|
||||
'@tensorflow/tfjs': ^4.1.0
|
||||
'@tensorflow/tfjs-node': 4.1.0
|
||||
'@tensorflow/tfjs': ^4.2.0
|
||||
'@tensorflow/tfjs-node': 4.2.0
|
||||
'@types/accepts': 1.3.5
|
||||
'@types/archiver': 5.3.1
|
||||
'@types/bcryptjs': 2.4.2
|
||||
@@ -112,8 +112,8 @@ importers:
|
||||
'@types/web-push': 3.3.2
|
||||
'@types/websocket': 1.0.5
|
||||
'@types/ws': 8.5.4
|
||||
'@typescript-eslint/eslint-plugin': 5.48.2
|
||||
'@typescript-eslint/parser': 5.48.2
|
||||
'@typescript-eslint/eslint-plugin': 5.49.0
|
||||
'@typescript-eslint/parser': 5.49.0
|
||||
accepts: ^1.3.8
|
||||
ajv: 8.12.0
|
||||
archiver: 5.3.1
|
||||
@@ -142,7 +142,7 @@ importers:
|
||||
file-type: 18.2.0
|
||||
fluent-ffmpeg: 2.1.2
|
||||
form-data: ^4.0.0
|
||||
got: 12.5.3
|
||||
got: ^12.5.3
|
||||
hpagent: 1.2.0
|
||||
ioredis: 4.28.5
|
||||
ip-cidr: 3.0.11
|
||||
@@ -150,9 +150,8 @@ importers:
|
||||
jest: 29.3.1
|
||||
jest-mock: ^29.3.1
|
||||
js-yaml: 4.1.0
|
||||
jsdom: 21.0.0
|
||||
jsdom: 21.1.0
|
||||
json5: 2.2.3
|
||||
json5-loader: 4.0.1
|
||||
jsonld: 8.1.0
|
||||
jsrsasign: 10.6.1
|
||||
mfm-js: 0.23.3
|
||||
@@ -202,7 +201,6 @@ importers:
|
||||
typeorm: 0.3.11
|
||||
typescript: 4.9.4
|
||||
ulid: 2.3.0
|
||||
undici: ^5.15.1
|
||||
unzipper: 0.10.11
|
||||
uuid: 9.0.0
|
||||
vary: 1.1.2
|
||||
@@ -211,9 +209,9 @@ importers:
|
||||
ws: 8.12.0
|
||||
xev: 3.0.2
|
||||
dependencies:
|
||||
'@bull-board/api': 4.10.2
|
||||
'@bull-board/fastify': 4.10.2
|
||||
'@bull-board/ui': 4.10.2
|
||||
'@bull-board/api': 4.11.0
|
||||
'@bull-board/fastify': 4.11.0
|
||||
'@bull-board/ui': 4.11.0
|
||||
'@discordapp/twemoji': 14.0.2
|
||||
'@fastify/accepts': 4.1.0
|
||||
'@fastify/cookie': 8.3.0
|
||||
@@ -257,9 +255,8 @@ importers:
|
||||
ip-cidr: 3.0.11
|
||||
is-svg: 4.3.2
|
||||
js-yaml: 4.1.0
|
||||
jsdom: 21.0.0
|
||||
jsdom: 21.1.0
|
||||
json5: 2.2.3
|
||||
json5-loader: 4.0.1
|
||||
jsonld: 8.1.0
|
||||
jsrsasign: 10.6.1
|
||||
mfm-js: 0.23.3
|
||||
@@ -267,8 +264,9 @@ importers:
|
||||
misskey-js: 0.0.14
|
||||
ms: 3.0.0-canary.1
|
||||
nested-property: 4.0.0
|
||||
node-fetch: 3.3.0
|
||||
nodemailer: 6.9.0
|
||||
nsfwjs: 2.4.2_@tensorflow+tfjs@4.1.0
|
||||
nsfwjs: 2.4.2_@tensorflow+tfjs@4.2.0
|
||||
oauth: 0.10.0
|
||||
os-utils: 0.0.14
|
||||
parse5: 7.1.2
|
||||
@@ -308,7 +306,6 @@ importers:
|
||||
typeorm: 0.3.11_ioredis@4.28.5+pg@8.8.0
|
||||
typescript: 4.9.4
|
||||
ulid: 2.3.0
|
||||
undici: 5.15.1
|
||||
unzipper: 0.10.11
|
||||
uuid: 9.0.0
|
||||
vary: 1.1.2
|
||||
@@ -317,8 +314,8 @@ importers:
|
||||
ws: 8.12.0
|
||||
xev: 3.0.2
|
||||
optionalDependencies:
|
||||
'@tensorflow/tfjs': 4.1.0_seedrandom@3.0.5
|
||||
'@tensorflow/tfjs-node': 4.1.0_seedrandom@3.0.5
|
||||
'@tensorflow/tfjs': 4.2.0_seedrandom@3.0.5
|
||||
'@tensorflow/tfjs-node': 4.2.0_seedrandom@3.0.5
|
||||
devDependencies:
|
||||
'@redocly/openapi-core': 1.0.0-beta.120
|
||||
'@swc/cli': 0.1.59_2w2rsb5d2wh3txrlxuiknf4vra
|
||||
@@ -366,15 +363,14 @@ importers:
|
||||
'@types/web-push': 3.3.2
|
||||
'@types/websocket': 1.0.5
|
||||
'@types/ws': 8.5.4
|
||||
'@typescript-eslint/eslint-plugin': 5.48.2_caon6io6stgpr7lz2rtbhekxqy
|
||||
'@typescript-eslint/parser': 5.48.2_7uibuqfxkfaozanbtbziikiqje
|
||||
'@typescript-eslint/eslint-plugin': 5.49.0_iu322prlnwsygkcra5kbpy22si
|
||||
'@typescript-eslint/parser': 5.49.0_7uibuqfxkfaozanbtbziikiqje
|
||||
cross-env: 7.0.3
|
||||
eslint: 8.32.0
|
||||
eslint-plugin-import: 2.27.5_2l6piu6guil2f63lj3qmhzbnn4
|
||||
eslint-plugin-import: 2.27.5_6savw6y3b7jng6f64kgkyoij64
|
||||
execa: 6.1.0
|
||||
jest: 29.3.1_@types+node@18.11.18
|
||||
jest-mock: 29.3.1
|
||||
node-fetch: 3.3.0
|
||||
|
||||
packages/frontend:
|
||||
specifiers:
|
||||
@@ -383,7 +379,7 @@ importers:
|
||||
'@rollup/plugin-json': 6.0.0
|
||||
'@rollup/pluginutils': 5.0.2
|
||||
'@syuilo/aiscript': 0.12.2
|
||||
'@tabler/icons': ^1.118.0
|
||||
'@tabler/icons-webfont': ^2.0.0
|
||||
'@types/escape-regexp': 0.0.1
|
||||
'@types/glob': 8.0.1
|
||||
'@types/gulp': 4.0.10
|
||||
@@ -398,8 +394,8 @@ importers:
|
||||
'@types/uuid': 9.0.0
|
||||
'@types/websocket': 1.0.5
|
||||
'@types/ws': 8.5.4
|
||||
'@typescript-eslint/eslint-plugin': 5.48.2
|
||||
'@typescript-eslint/parser': 5.48.2
|
||||
'@typescript-eslint/eslint-plugin': 5.49.0
|
||||
'@typescript-eslint/parser': 5.49.0
|
||||
'@vitejs/plugin-vue': 4.0.0
|
||||
'@vue/compiler-sfc': 3.2.45
|
||||
'@vue/runtime-core': 3.2.45
|
||||
@@ -442,7 +438,7 @@ importers:
|
||||
sanitize-html: ^2.8.1
|
||||
sass: 1.57.1
|
||||
seedrandom: 3.0.5
|
||||
start-server-and-test: 1.15.2
|
||||
start-server-and-test: 1.15.3
|
||||
strict-event-emitter-types: 2.0.0
|
||||
stringz: 2.1.0
|
||||
syuilo-password-strength: 0.0.1
|
||||
@@ -468,7 +464,7 @@ importers:
|
||||
'@rollup/plugin-json': 6.0.0_rollup@3.10.1
|
||||
'@rollup/pluginutils': 5.0.2_rollup@3.10.1
|
||||
'@syuilo/aiscript': 0.12.2
|
||||
'@tabler/icons': 1.119.0
|
||||
'@tabler/icons-webfont': 2.0.0
|
||||
'@vitejs/plugin-vue': 4.0.0_vite@4.0.4+vue@3.2.45
|
||||
'@vue/compiler-sfc': 3.2.45
|
||||
autobind-decorator: 2.4.0
|
||||
@@ -537,15 +533,15 @@ importers:
|
||||
'@types/uuid': 9.0.0
|
||||
'@types/websocket': 1.0.5
|
||||
'@types/ws': 8.5.4
|
||||
'@typescript-eslint/eslint-plugin': 5.48.2_caon6io6stgpr7lz2rtbhekxqy
|
||||
'@typescript-eslint/parser': 5.48.2_7uibuqfxkfaozanbtbziikiqje
|
||||
'@typescript-eslint/eslint-plugin': 5.49.0_iu322prlnwsygkcra5kbpy22si
|
||||
'@typescript-eslint/parser': 5.49.0_7uibuqfxkfaozanbtbziikiqje
|
||||
'@vue/runtime-core': 3.2.45
|
||||
cross-env: 7.0.3
|
||||
cypress: 12.3.0
|
||||
eslint: 8.32.0
|
||||
eslint-plugin-import: 2.27.5_2l6piu6guil2f63lj3qmhzbnn4
|
||||
eslint-plugin-import: 2.27.5_6savw6y3b7jng6f64kgkyoij64
|
||||
eslint-plugin-vue: 9.9.0_eslint@8.32.0
|
||||
start-server-and-test: 1.15.2
|
||||
start-server-and-test: 1.15.3
|
||||
vue-eslint-parser: 9.1.0_eslint@8.32.0
|
||||
vue-tsc: 1.0.24_typescript@4.9.4
|
||||
|
||||
@@ -915,17 +911,17 @@ packages:
|
||||
resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
|
||||
dev: true
|
||||
|
||||
/@bull-board/api/4.10.2:
|
||||
resolution: {integrity: sha512-lRHo0A7hsz71aOx1ZN0SmLLWfSuvKdL6EZ4imlgo5SuXGozybvlRc5KPIJU2/E1w5meoUGi+nFezBwp1gT/SMw==}
|
||||
/@bull-board/api/4.11.0:
|
||||
resolution: {integrity: sha512-qFnG63s3BkaslsNnXYagEUcdeK9SijZTJ+I4vMU+OiZt9VlKh01X1KjNlqjF9guvWcjk+Rg8UOMm0fWwM6PK6g==}
|
||||
dependencies:
|
||||
redis-info: 3.1.0
|
||||
dev: false
|
||||
|
||||
/@bull-board/fastify/4.10.2:
|
||||
resolution: {integrity: sha512-yp/TlfyfMBgR6vibyCUBOTKn2BFlBSLxkJxzlvIcMDs2Vl3PMXMs4YoCr2o3m+dXxSNzpTfomxAc5XQfJdp0VQ==}
|
||||
/@bull-board/fastify/4.11.0:
|
||||
resolution: {integrity: sha512-G4sAFVx5kGT5T/F2gL91RjZ2+YrMHNrLWoVo/QfHMcZe3RoOP4PpbOLgLC/O67KYn3JK6t8ysGyyP035OSJ2HA==}
|
||||
dependencies:
|
||||
'@bull-board/api': 4.10.2
|
||||
'@bull-board/ui': 4.10.2
|
||||
'@bull-board/api': 4.11.0
|
||||
'@bull-board/ui': 4.11.0
|
||||
'@fastify/static': 6.6.1
|
||||
'@fastify/view': 7.4.0
|
||||
ejs: 3.1.8
|
||||
@@ -933,10 +929,10 @@ packages:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/@bull-board/ui/4.10.2:
|
||||
resolution: {integrity: sha512-vaHGojG5D3xjnaed3nwOaLy4Y06RgDJdYRaFR5E06SjZ0vOvjVYGD6s4cykK512Aw/ElFhKDPwzhf8BvpwAtDQ==}
|
||||
/@bull-board/ui/4.11.0:
|
||||
resolution: {integrity: sha512-/Yb7x/3tWxysA2jRDQ89xEPnscvfGQdf4MmDRzWH5lDkVmL8b1HvZAQxLrHcxwB6y16oorQTf/1KbRZl0rJDHg==}
|
||||
dependencies:
|
||||
'@bull-board/api': 4.10.2
|
||||
'@bull-board/api': 4.11.0
|
||||
dev: false
|
||||
|
||||
/@chainsafe/is-ip/2.0.1:
|
||||
@@ -1077,7 +1073,7 @@ packages:
|
||||
dependencies:
|
||||
ky: 0.30.0
|
||||
ky-universal: 0.10.1_ky@0.30.0
|
||||
undici: 5.15.1
|
||||
undici: 5.16.0
|
||||
transitivePeerDependencies:
|
||||
- web-streams-polyfill
|
||||
dev: false
|
||||
@@ -1402,7 +1398,7 @@ packages:
|
||||
fastify-plugin: 4.5.0
|
||||
pump: 3.0.0
|
||||
tiny-lru: 10.0.1
|
||||
undici: 5.15.1
|
||||
undici: 5.16.0
|
||||
dev: false
|
||||
|
||||
/@fastify/send/1.0.0:
|
||||
@@ -2323,65 +2319,47 @@ packages:
|
||||
defer-to-connect: 2.0.1
|
||||
dev: false
|
||||
|
||||
/@tabler/icons/1.119.0:
|
||||
resolution: {integrity: sha512-Fk3Qq4w2SXcTjc/n1cuL5bccPkylrOMo7cYpQIf/yw6zP76LQV9dtLcHQUjFiUnaYuswR645CnURIhlafyAh9g==}
|
||||
peerDependencies:
|
||||
react: ^16.x || 17.x || 18.x
|
||||
react-dom: ^16.x || 17.x || 18.x
|
||||
peerDependenciesMeta:
|
||||
react:
|
||||
optional: true
|
||||
react-dom:
|
||||
optional: true
|
||||
/@tabler/icons-webfont/2.0.0:
|
||||
resolution: {integrity: sha512-ApVVupe7WKZOJzK6T2iw15/k6VrTALsL5YzAmvgvcriuX8sRCKlcWaRljcf2sZMUrqyY+Yq6xiOpL2p2NHgQBQ==}
|
||||
dependencies:
|
||||
'@tabler/icons': 2.0.0
|
||||
dev: false
|
||||
|
||||
/@tensorflow/tfjs-backend-cpu/4.1.0_npjwttp6o2hhjgfcmiedqvkgoa:
|
||||
resolution: {integrity: sha512-NqxrwpYa/hXGaBa1RzxfHsLAsaniIZryhF5CxQM9uSp1ny8wUJMH+Mda/Y8+llS3weHug8JDAbAW2cabn8l84A==}
|
||||
/@tabler/icons/2.0.0:
|
||||
resolution: {integrity: sha512-ye93cVD8baCwJJ7J3GKlUM3FN+qW6lsEz4uaH8bHCwC8un2R4p+ZzyRNc/ksqVgMQJ4PKQ8xbYpv4dnbbRffsA==}
|
||||
dev: false
|
||||
|
||||
/@tensorflow/tfjs-backend-cpu/4.2.0_tkoh6rxfpzme3tc2ndqbqcrg7y:
|
||||
resolution: {integrity: sha512-8HWg9J69m0Ovc6w8TVhhixMOcwA3t/NPXLblOA/sgJ+/JD5gsbpLWJk4QISQyb1RnpSVzw6PX3BSMTJU7hWVOg==}
|
||||
engines: {yarn: '>= 1.3.2'}
|
||||
peerDependencies:
|
||||
'@tensorflow/tfjs-core': 4.1.0
|
||||
'@tensorflow/tfjs-core': 4.2.0
|
||||
dependencies:
|
||||
'@tensorflow/tfjs-core': 4.1.0
|
||||
'@tensorflow/tfjs-core': 4.2.0
|
||||
'@types/seedrandom': 2.4.30
|
||||
seedrandom: 3.0.5
|
||||
dev: false
|
||||
|
||||
/@tensorflow/tfjs-backend-webgl/4.1.0_npjwttp6o2hhjgfcmiedqvkgoa:
|
||||
resolution: {integrity: sha512-RDjwbFCPESjh+QTrTvKC623MO58gIqxQvtX3DfJLhlsSbnsvIpA5s1CXNJYzNVmFDia35mPYKmQQWvEvhl9weA==}
|
||||
/@tensorflow/tfjs-backend-webgl/4.2.0_tkoh6rxfpzme3tc2ndqbqcrg7y:
|
||||
resolution: {integrity: sha512-Qvf+hD5pSh+xi48kChSGzcDKJemkc4EKfoVVjuxl4k25ZUPwuEd7zZUAtinkLu1dzgHNyvePZY8k+9rVm59HJA==}
|
||||
engines: {yarn: '>= 1.3.2'}
|
||||
peerDependencies:
|
||||
'@tensorflow/tfjs-core': 4.1.0
|
||||
'@tensorflow/tfjs-core': 4.2.0
|
||||
dependencies:
|
||||
'@tensorflow/tfjs-backend-cpu': 4.1.0_npjwttp6o2hhjgfcmiedqvkgoa
|
||||
'@tensorflow/tfjs-core': 4.1.0
|
||||
'@tensorflow/tfjs-backend-cpu': 4.2.0_tkoh6rxfpzme3tc2ndqbqcrg7y
|
||||
'@tensorflow/tfjs-core': 4.2.0
|
||||
'@types/offscreencanvas': 2019.3.0
|
||||
'@types/seedrandom': 2.4.30
|
||||
'@types/webgl-ext': 0.0.30
|
||||
seedrandom: 3.0.5
|
||||
dev: false
|
||||
|
||||
/@tensorflow/tfjs-converter/4.1.0_npjwttp6o2hhjgfcmiedqvkgoa:
|
||||
resolution: {integrity: sha512-pR4TSUI949a/5uUWjHga8xzxD7Y9AbobHJtCmg86Bldfl2GV8aibz87cNjpoO+iUhH8WZo1TOJy8GpM+MPT2DA==}
|
||||
/@tensorflow/tfjs-converter/4.2.0_tkoh6rxfpzme3tc2ndqbqcrg7y:
|
||||
resolution: {integrity: sha512-m+E2KJM6yGQdi8ElzWpChdD/JaqhWMCi9yK70v/ndkOaCL2q2UN48nYP2T5S15vkDvMIgzAQyZfh7hxQsMuvRQ==}
|
||||
peerDependencies:
|
||||
'@tensorflow/tfjs-core': 4.1.0
|
||||
'@tensorflow/tfjs-core': 4.2.0
|
||||
dependencies:
|
||||
'@tensorflow/tfjs-core': 4.1.0
|
||||
dev: false
|
||||
|
||||
/@tensorflow/tfjs-core/4.1.0:
|
||||
resolution: {integrity: sha512-zERD4xtxNq3dV3+wPEMi2HWSV68FA8WucSAogzErp38/q98Byv9G1x8DhxQ+jowTcqN4srGWcBfK59jYwjlAvA==}
|
||||
engines: {yarn: '>= 1.3.2'}
|
||||
dependencies:
|
||||
'@types/long': 4.0.2
|
||||
'@types/offscreencanvas': 2019.7.0
|
||||
'@types/seedrandom': 2.4.30
|
||||
'@types/webgl-ext': 0.0.30
|
||||
'@webgpu/types': 0.1.21
|
||||
long: 4.0.0
|
||||
node-fetch: 2.6.7
|
||||
seedrandom: 3.0.5
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
'@tensorflow/tfjs-core': 4.2.0
|
||||
dev: false
|
||||
|
||||
/@tensorflow/tfjs-core/4.2.0:
|
||||
@@ -2400,15 +2378,14 @@ packages:
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@tensorflow/tfjs-data/4.1.0_x6uqorr55fevqojbxncmwlp444:
|
||||
resolution: {integrity: sha512-H6ef6NXPwi2xKYB6tVNZi5RqniMFpfUSlxG3q0BNxnyuyeCUSjJvHJFpah9hT/ysYjVdwspzh/I6B67h+lBchg==}
|
||||
/@tensorflow/tfjs-data/4.2.0_qsavyspjcgrxymzlcpasp6rzxe:
|
||||
resolution: {integrity: sha512-11t7Q+ikseduJgkd9iSeRrtor1aA3o5PVCFhC5yYvR3JLO55ic1+4Ryo0EJfhRoismS6zBUJrpzX4K0zlLbIfw==}
|
||||
peerDependencies:
|
||||
'@tensorflow/tfjs-core': 4.1.0
|
||||
'@tensorflow/tfjs-core': 4.2.0
|
||||
seedrandom: ^3.0.5
|
||||
dependencies:
|
||||
'@tensorflow/tfjs-core': 4.1.0
|
||||
'@tensorflow/tfjs-core': 4.2.0
|
||||
'@types/node-fetch': 2.6.2
|
||||
node-fetch: 2.6.7
|
||||
seedrandom: 3.0.5
|
||||
@@ -2417,21 +2394,21 @@ packages:
|
||||
- encoding
|
||||
dev: false
|
||||
|
||||
/@tensorflow/tfjs-layers/4.1.0_npjwttp6o2hhjgfcmiedqvkgoa:
|
||||
resolution: {integrity: sha512-lzHNTZu1GwKl7hW5tt2COSpflE0m7xrsOf8AzRzpTDVJYYRx/x5ScMt/y//5jbRuaDOnb3EjT1FxWxwkD44/sg==}
|
||||
/@tensorflow/tfjs-layers/4.2.0_tkoh6rxfpzme3tc2ndqbqcrg7y:
|
||||
resolution: {integrity: sha512-SO0KTmCFOjrW+PlP9nKYXz07XGFq6uE7am9yH2bRaRPWpEeaKT/+k0C9vFMxI/GzRwY8AK4sLe4U+jE1mhYxGw==}
|
||||
peerDependencies:
|
||||
'@tensorflow/tfjs-core': 4.1.0
|
||||
'@tensorflow/tfjs-core': 4.2.0
|
||||
dependencies:
|
||||
'@tensorflow/tfjs-core': 4.1.0
|
||||
'@tensorflow/tfjs-core': 4.2.0
|
||||
dev: false
|
||||
|
||||
/@tensorflow/tfjs-node/4.1.0_seedrandom@3.0.5:
|
||||
resolution: {integrity: sha512-2H7UJqVafuM9fbsMdzUkcfWLpH/owgvVHWES+gQsiueIqyOjpq6ZGSQLDxyuKtHP2D/8/kWeVIbnzAjnIRniuA==}
|
||||
/@tensorflow/tfjs-node/4.2.0_seedrandom@3.0.5:
|
||||
resolution: {integrity: sha512-dkyExfqTjMVDpKLBzHH2b7JWzGK+QORtYswvoNHbeHwx7kvYYRAQeiHxp+xrgry6LdbWfZs88IWCwNGoOiZ21w==}
|
||||
engines: {node: '>=8.11.0'}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
'@mapbox/node-pre-gyp': 1.0.9
|
||||
'@tensorflow/tfjs': 4.1.0_seedrandom@3.0.5
|
||||
'@tensorflow/tfjs': 4.2.0_seedrandom@3.0.5
|
||||
adm-zip: 0.5.10
|
||||
google-protobuf: 3.21.2
|
||||
https-proxy-agent: 2.2.4
|
||||
@@ -2445,17 +2422,16 @@ packages:
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@tensorflow/tfjs/4.1.0_seedrandom@3.0.5:
|
||||
resolution: {integrity: sha512-jlrJ6MIBos8NkmF+NHIWBnKVBGYJTG06QmW/A0vgyXwkp+3PgzX8TJ4MWIv/7oZr7g27zfY6dXA1OFzrrgvklA==}
|
||||
/@tensorflow/tfjs/4.2.0_seedrandom@3.0.5:
|
||||
resolution: {integrity: sha512-iZmtyGC9IJkx+TpFnkgDol8BHv2BU3zJ01HyNcuvnm1w1EqoNe+1n8bwvLzI/sxHMcHTqzuu7VugMaphryxE+A==}
|
||||
hasBin: true
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
'@tensorflow/tfjs-backend-cpu': 4.1.0_npjwttp6o2hhjgfcmiedqvkgoa
|
||||
'@tensorflow/tfjs-backend-webgl': 4.1.0_npjwttp6o2hhjgfcmiedqvkgoa
|
||||
'@tensorflow/tfjs-converter': 4.1.0_npjwttp6o2hhjgfcmiedqvkgoa
|
||||
'@tensorflow/tfjs-core': 4.1.0
|
||||
'@tensorflow/tfjs-data': 4.1.0_x6uqorr55fevqojbxncmwlp444
|
||||
'@tensorflow/tfjs-layers': 4.1.0_npjwttp6o2hhjgfcmiedqvkgoa
|
||||
'@tensorflow/tfjs-backend-cpu': 4.2.0_tkoh6rxfpzme3tc2ndqbqcrg7y
|
||||
'@tensorflow/tfjs-backend-webgl': 4.2.0_tkoh6rxfpzme3tc2ndqbqcrg7y
|
||||
'@tensorflow/tfjs-converter': 4.2.0_tkoh6rxfpzme3tc2ndqbqcrg7y
|
||||
'@tensorflow/tfjs-core': 4.2.0
|
||||
'@tensorflow/tfjs-data': 4.2.0_qsavyspjcgrxymzlcpasp6rzxe
|
||||
'@tensorflow/tfjs-layers': 4.2.0_tkoh6rxfpzme3tc2ndqbqcrg7y
|
||||
argparse: 1.0.10
|
||||
chalk: 4.1.2
|
||||
core-js: 3.27.1
|
||||
@@ -2669,6 +2645,7 @@ packages:
|
||||
|
||||
/@types/json-schema/7.0.11:
|
||||
resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==}
|
||||
dev: true
|
||||
|
||||
/@types/json5/0.0.29:
|
||||
resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
|
||||
@@ -2956,8 +2933,8 @@ packages:
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@typescript-eslint/eslint-plugin/5.48.2_caon6io6stgpr7lz2rtbhekxqy:
|
||||
resolution: {integrity: sha512-sR0Gja9Ky1teIq4qJOl0nC+Tk64/uYdX+mi+5iB//MH8gwyx8e3SOyhEzeLZEFEEfCaLf8KJq+Bd/6je1t+CAg==}
|
||||
/@typescript-eslint/eslint-plugin/5.49.0_iu322prlnwsygkcra5kbpy22si:
|
||||
resolution: {integrity: sha512-IhxabIpcf++TBaBa1h7jtOWyon80SXPRLDq0dVz5SLFC/eW6tofkw/O7Ar3lkx5z5U6wzbKDrl2larprp5kk5Q==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
'@typescript-eslint/parser': ^5.0.0
|
||||
@@ -2967,10 +2944,10 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/parser': 5.48.2_7uibuqfxkfaozanbtbziikiqje
|
||||
'@typescript-eslint/scope-manager': 5.48.2
|
||||
'@typescript-eslint/type-utils': 5.48.2_7uibuqfxkfaozanbtbziikiqje
|
||||
'@typescript-eslint/utils': 5.48.2_7uibuqfxkfaozanbtbziikiqje
|
||||
'@typescript-eslint/parser': 5.49.0_7uibuqfxkfaozanbtbziikiqje
|
||||
'@typescript-eslint/scope-manager': 5.49.0
|
||||
'@typescript-eslint/type-utils': 5.49.0_7uibuqfxkfaozanbtbziikiqje
|
||||
'@typescript-eslint/utils': 5.49.0_7uibuqfxkfaozanbtbziikiqje
|
||||
debug: 4.3.4
|
||||
eslint: 8.32.0
|
||||
ignore: 5.2.4
|
||||
@@ -3003,8 +2980,8 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/parser/5.48.2_7uibuqfxkfaozanbtbziikiqje:
|
||||
resolution: {integrity: sha512-38zMsKsG2sIuM5Oi/olurGwYJXzmtdsHhn5mI/pQogP+BjYVkK5iRazCQ8RGS0V+YLk282uWElN70zAAUmaYHw==}
|
||||
/@typescript-eslint/parser/5.49.0_7uibuqfxkfaozanbtbziikiqje:
|
||||
resolution: {integrity: sha512-veDlZN9mUhGqU31Qiv2qEp+XrJj5fgZpJ8PW30sHU+j/8/e5ruAhLaVDAeznS7A7i4ucb/s8IozpDtt9NqCkZg==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||
@@ -3013,9 +2990,9 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/scope-manager': 5.48.2
|
||||
'@typescript-eslint/types': 5.48.2
|
||||
'@typescript-eslint/typescript-estree': 5.48.2_typescript@4.9.4
|
||||
'@typescript-eslint/scope-manager': 5.49.0
|
||||
'@typescript-eslint/types': 5.49.0
|
||||
'@typescript-eslint/typescript-estree': 5.49.0_typescript@4.9.4
|
||||
debug: 4.3.4
|
||||
eslint: 8.32.0
|
||||
typescript: 4.9.4
|
||||
@@ -3031,16 +3008,16 @@ packages:
|
||||
'@typescript-eslint/visitor-keys': 5.48.1
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/scope-manager/5.48.2:
|
||||
resolution: {integrity: sha512-zEUFfonQid5KRDKoI3O+uP1GnrFd4tIHlvs+sTJXiWuypUWMuDaottkJuR612wQfOkjYbsaskSIURV9xo4f+Fw==}
|
||||
/@typescript-eslint/scope-manager/5.49.0:
|
||||
resolution: {integrity: sha512-clpROBOiMIzpbWNxCe1xDK14uPZh35u4QaZO1GddilEzoCLAEz4szb51rBpdgurs5k2YzPtJeTEN3qVbG+LRUQ==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 5.48.2
|
||||
'@typescript-eslint/visitor-keys': 5.48.2
|
||||
'@typescript-eslint/types': 5.49.0
|
||||
'@typescript-eslint/visitor-keys': 5.49.0
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/type-utils/5.48.2_7uibuqfxkfaozanbtbziikiqje:
|
||||
resolution: {integrity: sha512-QVWx7J5sPMRiOMJp5dYshPxABRoZV1xbRirqSk8yuIIsu0nvMTZesKErEA3Oix1k+uvsk8Cs8TGJ6kQ0ndAcew==}
|
||||
/@typescript-eslint/type-utils/5.49.0_7uibuqfxkfaozanbtbziikiqje:
|
||||
resolution: {integrity: sha512-eUgLTYq0tR0FGU5g1YHm4rt5H/+V2IPVkP0cBmbhRyEmyGe4XvJ2YJ6sYTmONfjmdMqyMLad7SB8GvblbeESZA==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
eslint: '*'
|
||||
@@ -3049,8 +3026,8 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/typescript-estree': 5.48.2_typescript@4.9.4
|
||||
'@typescript-eslint/utils': 5.48.2_7uibuqfxkfaozanbtbziikiqje
|
||||
'@typescript-eslint/typescript-estree': 5.49.0_typescript@4.9.4
|
||||
'@typescript-eslint/utils': 5.49.0_7uibuqfxkfaozanbtbziikiqje
|
||||
debug: 4.3.4
|
||||
eslint: 8.32.0
|
||||
tsutils: 3.21.0_typescript@4.9.4
|
||||
@@ -3064,8 +3041,8 @@ packages:
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/types/5.48.2:
|
||||
resolution: {integrity: sha512-hE7dA77xxu7ByBc6KCzikgfRyBCTst6dZQpwaTy25iMYOnbNljDT4hjhrGEJJ0QoMjrfqrx+j1l1B9/LtKeuqA==}
|
||||
/@typescript-eslint/types/5.49.0:
|
||||
resolution: {integrity: sha512-7If46kusG+sSnEpu0yOz2xFv5nRz158nzEXnJFCGVEHWnuzolXKwrH5Bsf9zsNlOQkyZuk0BZKKoJQI+1JPBBg==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
dev: true
|
||||
|
||||
@@ -3090,8 +3067,8 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/typescript-estree/5.48.2_typescript@4.9.4:
|
||||
resolution: {integrity: sha512-bibvD3z6ilnoVxUBFEgkO0k0aFvUc4Cttt0dAreEr+nrAHhWzkO83PEVVuieK3DqcgL6VAK5dkzK8XUVja5Zcg==}
|
||||
/@typescript-eslint/typescript-estree/5.49.0_typescript@4.9.4:
|
||||
resolution: {integrity: sha512-PBdx+V7deZT/3GjNYPVQv1Nc0U46dAHbIuOG8AZ3on3vuEKiPDwFE/lG1snN2eUB9IhF7EyF7K1hmTcLztNIsA==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
@@ -3099,8 +3076,8 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 5.48.2
|
||||
'@typescript-eslint/visitor-keys': 5.48.2
|
||||
'@typescript-eslint/types': 5.49.0
|
||||
'@typescript-eslint/visitor-keys': 5.49.0
|
||||
debug: 4.3.4
|
||||
globby: 11.1.0
|
||||
is-glob: 4.0.3
|
||||
@@ -3111,17 +3088,17 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/utils/5.48.2_7uibuqfxkfaozanbtbziikiqje:
|
||||
resolution: {integrity: sha512-2h18c0d7jgkw6tdKTlNaM7wyopbLRBiit8oAxoP89YnuBOzCZ8g8aBCaCqq7h208qUTroL7Whgzam7UY3HVLow==}
|
||||
/@typescript-eslint/utils/5.49.0_7uibuqfxkfaozanbtbziikiqje:
|
||||
resolution: {integrity: sha512-cPJue/4Si25FViIb74sHCLtM4nTSBXtLx1d3/QT6mirQ/c65bV8arBEebBJJizfq8W2YyMoPI/WWPFWitmNqnQ==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||
dependencies:
|
||||
'@types/json-schema': 7.0.11
|
||||
'@types/semver': 7.3.13
|
||||
'@typescript-eslint/scope-manager': 5.48.2
|
||||
'@typescript-eslint/types': 5.48.2
|
||||
'@typescript-eslint/typescript-estree': 5.48.2_typescript@4.9.4
|
||||
'@typescript-eslint/scope-manager': 5.49.0
|
||||
'@typescript-eslint/types': 5.49.0
|
||||
'@typescript-eslint/typescript-estree': 5.49.0_typescript@4.9.4
|
||||
eslint: 8.32.0
|
||||
eslint-scope: 5.1.1
|
||||
eslint-utils: 3.0.0_eslint@8.32.0
|
||||
@@ -3139,11 +3116,11 @@ packages:
|
||||
eslint-visitor-keys: 3.3.0
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/visitor-keys/5.48.2:
|
||||
resolution: {integrity: sha512-z9njZLSkwmjFWUelGEwEbdf4NwKvfHxvGC0OcGN1Hp/XNDIcJ7D5DpPNPv6x6/mFvc1tQHsaWmpD/a4gOvvCJQ==}
|
||||
/@typescript-eslint/visitor-keys/5.49.0:
|
||||
resolution: {integrity: sha512-v9jBMjpNWyn8B6k/Mjt6VbUS4J1GvUlR4x3Y+ibnP1z7y7V4n0WRz+50DY6+Myj0UaXVSuUlHohO+eZ8IJEnkg==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 5.48.2
|
||||
'@typescript-eslint/types': 5.49.0
|
||||
eslint-visitor-keys: 3.3.0
|
||||
dev: true
|
||||
|
||||
@@ -3386,14 +3363,6 @@ packages:
|
||||
ajv: 8.12.0
|
||||
dev: false
|
||||
|
||||
/ajv-keywords/3.5.2_ajv@6.12.6:
|
||||
resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==}
|
||||
peerDependencies:
|
||||
ajv: ^6.9.1
|
||||
dependencies:
|
||||
ajv: 6.12.6
|
||||
dev: false
|
||||
|
||||
/ajv/6.12.6:
|
||||
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
|
||||
dependencies:
|
||||
@@ -3835,10 +3804,11 @@ packages:
|
||||
- debug
|
||||
dev: false
|
||||
|
||||
/axios/0.25.0_debug@4.3.4:
|
||||
resolution: {integrity: sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==}
|
||||
/axios/0.27.2_debug@4.3.4:
|
||||
resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==}
|
||||
dependencies:
|
||||
follow-redirects: 1.15.2
|
||||
form-data: 4.0.0
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
dev: true
|
||||
@@ -3978,10 +3948,6 @@ packages:
|
||||
engines: {node: '>=0.6'}
|
||||
dev: false
|
||||
|
||||
/big.js/5.2.2:
|
||||
resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==}
|
||||
dev: false
|
||||
|
||||
/bin-check/4.1.0:
|
||||
resolution: {integrity: sha512-b6weQyEUKsDGFlACWSIOfveEnImkJyK/FGW6FAG42loyoquvjdtOIqO6yBFzHyqyVVhNgNkQxxx09SFLK28YnA==}
|
||||
engines: {node: '>=4'}
|
||||
@@ -5671,11 +5637,6 @@ packages:
|
||||
/emoji-regex/8.0.0:
|
||||
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||
|
||||
/emojis-list/3.0.0:
|
||||
resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==}
|
||||
engines: {node: '>= 4'}
|
||||
dev: false
|
||||
|
||||
/encode-utf8/1.0.3:
|
||||
resolution: {integrity: sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==}
|
||||
dev: false
|
||||
@@ -6109,7 +6070,7 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/eslint-module-utils/2.7.4_kvyj4idustix6trhy5lyssy2sq:
|
||||
/eslint-module-utils/2.7.4_cnxxylyx37asr43xy64ejg3pwe:
|
||||
resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==}
|
||||
engines: {node: '>=4'}
|
||||
peerDependencies:
|
||||
@@ -6130,7 +6091,7 @@ packages:
|
||||
eslint-import-resolver-webpack:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/parser': 5.48.2_7uibuqfxkfaozanbtbziikiqje
|
||||
'@typescript-eslint/parser': 5.49.0_7uibuqfxkfaozanbtbziikiqje
|
||||
debug: 3.2.7
|
||||
eslint: 8.32.0
|
||||
eslint-import-resolver-node: 0.3.7
|
||||
@@ -6200,7 +6161,7 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/eslint-plugin-import/2.27.5_2l6piu6guil2f63lj3qmhzbnn4:
|
||||
/eslint-plugin-import/2.27.5_6savw6y3b7jng6f64kgkyoij64:
|
||||
resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==}
|
||||
engines: {node: '>=4'}
|
||||
peerDependencies:
|
||||
@@ -6210,7 +6171,7 @@ packages:
|
||||
'@typescript-eslint/parser':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/parser': 5.48.2_7uibuqfxkfaozanbtbziikiqje
|
||||
'@typescript-eslint/parser': 5.49.0_7uibuqfxkfaozanbtbziikiqje
|
||||
array-includes: 3.1.6
|
||||
array.prototype.flat: 1.3.1
|
||||
array.prototype.flatmap: 1.3.1
|
||||
@@ -6218,7 +6179,7 @@ packages:
|
||||
doctrine: 2.1.0
|
||||
eslint: 8.32.0
|
||||
eslint-import-resolver-node: 0.3.7
|
||||
eslint-module-utils: 2.7.4_kvyj4idustix6trhy5lyssy2sq
|
||||
eslint-module-utils: 2.7.4_cnxxylyx37asr43xy64ejg3pwe
|
||||
has: 1.0.3
|
||||
is-core-module: 2.11.0
|
||||
is-glob: 4.0.3
|
||||
@@ -7069,7 +7030,6 @@ packages:
|
||||
asynckit: 0.4.0
|
||||
combined-stream: 1.0.8
|
||||
mime-types: 2.1.35
|
||||
dev: false
|
||||
|
||||
/formdata-polyfill/4.0.10:
|
||||
resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
|
||||
@@ -9125,8 +9085,8 @@ packages:
|
||||
engines: {node: '>=0.1.90'}
|
||||
dev: false
|
||||
|
||||
/jsdom/21.0.0:
|
||||
resolution: {integrity: sha512-AIw+3ZakSUtDYvhwPwWHiZsUi3zHugpMEKlNPaurviseYoBqo0zBd3zqoUi3LPCNtPFlEP8FiW9MqCZdjb2IYA==}
|
||||
/jsdom/21.1.0:
|
||||
resolution: {integrity: sha512-m0lzlP7qOtthD918nenK3hdItSd2I+V3W9IrBcB36sqDwG+KnUs66IF5GY7laGWUnlM9vTsD0W1QwSEBYWWcJg==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
canvas: ^2.5.0
|
||||
@@ -9199,17 +9159,6 @@ packages:
|
||||
/json-stringify-safe/5.0.1:
|
||||
resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==}
|
||||
|
||||
/json5-loader/4.0.1:
|
||||
resolution: {integrity: sha512-c9viNZlZTz0MTIcf/4qvek5Dz1/PU3DNCB4PwUhlEZIV3qb1bSD6vQQymlV17/Wm6ncra1aCvmIPsuRj+KfEEg==}
|
||||
engines: {node: '>= 10.13.0'}
|
||||
peerDependencies:
|
||||
webpack: ^4.0.0 || ^5.0.0
|
||||
dependencies:
|
||||
json5: 2.2.3
|
||||
loader-utils: 2.0.4
|
||||
schema-utils: 3.1.1
|
||||
dev: false
|
||||
|
||||
/json5/1.0.1:
|
||||
resolution: {integrity: sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==}
|
||||
hasBin: true
|
||||
@@ -9539,15 +9488,6 @@ packages:
|
||||
strip-bom: 2.0.0
|
||||
dev: false
|
||||
|
||||
/loader-utils/2.0.4:
|
||||
resolution: {integrity: sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==}
|
||||
engines: {node: '>=8.9.0'}
|
||||
dependencies:
|
||||
big.js: 5.2.2
|
||||
emojis-list: 3.0.0
|
||||
json5: 2.2.3
|
||||
dev: false
|
||||
|
||||
/locate-path/5.0.0:
|
||||
resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -10403,13 +10343,13 @@ packages:
|
||||
set-blocking: 2.0.0
|
||||
dev: false
|
||||
|
||||
/nsfwjs/2.4.2_@tensorflow+tfjs@4.1.0:
|
||||
/nsfwjs/2.4.2_@tensorflow+tfjs@4.2.0:
|
||||
resolution: {integrity: sha512-i4Pp2yt59qPQgeZFyg3wXFBX52uSeu/hkDoqdZfe+sILRxNBUu0VDogj7Lmqak0GlrXviS/wLiVeIx40IDUu7A==}
|
||||
peerDependencies:
|
||||
'@tensorflow/tfjs': ^3.18.0
|
||||
dependencies:
|
||||
'@nsfw-filter/gif-frames': 1.0.2
|
||||
'@tensorflow/tfjs': 4.1.0_seedrandom@3.0.5
|
||||
'@tensorflow/tfjs': 4.2.0_seedrandom@3.0.5
|
||||
dev: false
|
||||
|
||||
/nth-check/1.0.2:
|
||||
@@ -12262,15 +12202,6 @@ packages:
|
||||
xmlchars: 2.2.0
|
||||
dev: false
|
||||
|
||||
/schema-utils/3.1.1:
|
||||
resolution: {integrity: sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==}
|
||||
engines: {node: '>= 10.13.0'}
|
||||
dependencies:
|
||||
'@types/json-schema': 7.0.11
|
||||
ajv: 6.12.6
|
||||
ajv-keywords: 3.5.2_ajv@6.12.6
|
||||
dev: false
|
||||
|
||||
/secure-json-parse/2.7.0:
|
||||
resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==}
|
||||
dev: false
|
||||
@@ -12688,8 +12619,8 @@ packages:
|
||||
/standard-as-callback/2.1.0:
|
||||
resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==}
|
||||
|
||||
/start-server-and-test/1.15.2:
|
||||
resolution: {integrity: sha512-t5xJX04Hg7hqxiKHMJBz/n4zIMsE6G7hpAcerFAH+4Vh9le/LeyFcJERJM7WLiPygWF9TOg33oroJF1XOzJtYQ==}
|
||||
/start-server-and-test/1.15.3:
|
||||
resolution: {integrity: sha512-4GqkqghvUR9cJ8buvtgkyT0AHgVwCJ5EN8eDEhe9grTChGwWUxGm2nqfSeE9+0PZkLRdFqcwTwxVHe1y3ViutQ==}
|
||||
engines: {node: '>=6'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
@@ -12700,7 +12631,7 @@ packages:
|
||||
execa: 5.1.1
|
||||
lazy-ass: 1.6.0
|
||||
ps-tree: 1.2.0
|
||||
wait-on: 6.0.1_debug@4.3.4
|
||||
wait-on: 7.0.1_debug@4.3.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
@@ -13541,8 +13472,8 @@ packages:
|
||||
undertaker-registry: 1.0.1
|
||||
dev: false
|
||||
|
||||
/undici/5.15.1:
|
||||
resolution: {integrity: sha512-XLk8g0WAngdvFqTI+VKfBtM4YWXgdxkf1WezC771Es0Dd+Pm1KmNx8t93WTC+Hh9tnghmVxkclU1HN+j+CvIUA==}
|
||||
/undici/5.16.0:
|
||||
resolution: {integrity: sha512-KWBOXNv6VX+oJQhchXieUznEmnJMqgXMbs0xxH2t8q/FUAWSJvOSr/rMaZKnX5RIVq7JDn0JbP4BOnKG2SGXLQ==}
|
||||
engines: {node: '>=12.18'}
|
||||
dependencies:
|
||||
busboy: 1.6.0
|
||||
@@ -13945,12 +13876,12 @@ packages:
|
||||
xml-name-validator: 4.0.0
|
||||
dev: false
|
||||
|
||||
/wait-on/6.0.1_debug@4.3.4:
|
||||
resolution: {integrity: sha512-zht+KASY3usTY5u2LgaNqn/Cd8MukxLGjdcZxT2ns5QzDmTFc4XoWBgC+C/na+sMRZTuVygQoMYwdcVjHnYIVw==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
/wait-on/7.0.1_debug@4.3.4:
|
||||
resolution: {integrity: sha512-9AnJE9qTjRQOlTZIldAaf/da2eW0eSRSgcqq85mXQja/DW3MriHxkpODDSUEg+Gri/rKEcXUZHe+cevvYItaog==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
axios: 0.25.0_debug@4.3.4
|
||||
axios: 0.27.2_debug@4.3.4
|
||||
joi: 17.7.0
|
||||
lodash: 4.17.21
|
||||
minimist: 1.2.7
|
||||
|
Reference in New Issue
Block a user