Merge remote-tracking branch 'misskey-dev/develop' into prismisskey

# Conflicts:
#	package.json
#	packages/frontend/src/pages/settings/general.vue
This commit is contained in:
mattyatea 2023-09-22 20:03:34 +09:00
commit a63bef2d8f
58 changed files with 741 additions and 517 deletions

View File

@ -30,6 +30,9 @@
- Feat: プロフィールでのリンク検証
- Feat: 通知をテストできるようになりました
- Feat: PWAのアイコンが設定できるようになりました
- Enhance: アンテナの受信ソースに指定したユーザを除外するものを追加
- Enhance: 二要素認証設定時のセキュリティを強化
- パスワード入力が必要な操作を行う際、二要素認証が有効であれば確認コードの入力も必要になりました
- Enhance: manifest.jsonをオーバーライド可能に
- Enhance: 依存関係の更新
- Enhance: ローカリゼーションの更新
@ -40,10 +43,9 @@
- Feat: Playで直接投稿フォームを埋め込めるように(`Ui:C:postForm`)
- Feat: クライアントを起動している間、デバイスの画面が自動でオフになるのを防ぐオプションを追加
- Feat: 新しい実績を追加
- Enhance: ノート詳細ページを改修
- 読み込み時のパフォーマンスが向上しました
- リノート一覧、リアクション一覧がタブとして追加されました
- ノートのメニューからは当該項目は消えました
- Enhance: ノート詳細ページでリノート一覧、リアクション一覧タブを追加
- ノートのメニューからは当該項目は消えました
- Enhance: センシティブなメディアを目立たせる設定を追加
- Enhance: プロフィールにその人が作ったPlayの一覧出せるように
- Enhance: メニューのスイッチの動作を改善
- Enhance: 絵文字ピッカーの検索の表示件数を100件に増加
@ -62,6 +64,7 @@
- Enhance: AiScriptで`LOCALE`として現在の設定言語を取得できるように
- Enhance: Mk:apiが失敗した時にエラー型の値AiScript 0.16.0で追加)を返すように
- Enhance: ScratchpadでAsync:系関数やボタンのコールバックなどのエラーにもダイアログを出すように試験的なためPlayなどには未実装
- Enhance: ノート詳細ページ読み込み時のパフォーマンスが向上しました
- Enhance: タイムラインでリスト/アンテナ選択時のパフォーマンスを改善
- Enhance: 「Moderation note」、「Add moderation note」をローカライズできるように
- Enhance: 細かなデザインの調整
@ -89,6 +92,7 @@
- Fix: muteがapiからのuser list timeline取得で機能しない問題を修正
- Fix: ジョブキュー管理画面の認証を回避できる問題を修正
- Fix: 一部のサーバー内部エラーがスタックトレースを返さないように修正
- Fix: 一部のリモートユーザーをフォローすることができない問題を修正
## 13.14.2

View File

@ -1679,7 +1679,6 @@ _timelineTutorial:
_2fa:
alreadyRegistered: "Již jste zaregistrovali dvoufaktorové ověřovací zařízení."
registerTOTP: "Registrovat aplikaci autentizátoru"
passwordToTOTP: "Zadejte své heslo"
step1: "Nejprve si do zařízení nainstalujte aplikaci pro ověřování (například {a} nebo {b})."
step2: "Poté naskenujte QR kód zobrazený na této obrazovce."
step2Click: "Kliknutím na tento QR kód můžete zaregistrovat 2FA do bezpečnostního klíče nebo aplikace autentizace telefonu."

View File

@ -1112,6 +1112,10 @@ renotes: "Renotes"
loadReplies: "Antworten anzeigen"
loadConversation: "Unterhaltung anzeigen"
pinnedList: "Angeheftete Liste"
keepScreenOn: "Bildschirm angeschaltet lassen"
verifiedLink: "Link-Besitz wurde verifiziert"
notifyNotes: "Über neue Notizen benachrichtigen"
unnotifyNotes: "Nicht über neue Notizen benachrichtigen"
_announcement:
forExistingUsers: "Nur für existierende Nutzer"
forExistingUsersDescription: "Ist diese Option aktiviert, wird diese Ankündigung nur Nutzern angezeigt, die zum Zeitpunkt der Ankündigung bereits registriert sind. Ist sie deaktiviert, wird sie auch Nutzern, die sich nach dessen Veröffentlichung registrieren, angezeigt."
@ -1712,7 +1716,6 @@ _timelineTutorial:
_2fa:
alreadyRegistered: "Du hast bereits ein Gerät für Zwei-Faktor-Authentifizierung registriert."
registerTOTP: "Authentifizierungs-App registrieren"
passwordToTOTP: "Bitte Passwort eingeben"
step1: "Installiere zuerst eine Authentifizierungsapp (z.B. {a} oder {b}) auf deinem Gerät."
step2: "Dann, scanne den angezeigten QR-Code mit deinem Gerät."
step2Click: "Durch Klicken dieses QR-Codes kannst du Verifikation mit deinem Security-Token oder einer App registrieren."
@ -1890,6 +1893,7 @@ _profile:
metadataContent: "Inhalt"
changeAvatar: "Profilbild ändern"
changeBanner: "Banner ändern"
verifiedLinkDescription: "Gibst du hier eine URL ein, die einen Link zu deinem Profile enthält, wird neben diesem Feld ein Icon zur Besitzbestätigung angezeigt."
_exportOrImport:
allNotes: "Alle Notizen"
favoritedNotes: "Als Favorit markierte Notizen"
@ -2008,6 +2012,7 @@ _notification:
youReceivedFollowRequest: "Du hast eine Follow-Anfrage erhalten"
yourFollowRequestAccepted: "Deine Follow-Anfrage wurde akzeptiert"
pollEnded: "Umfrageergebnisse sind verfügbar"
newNote: "Neue Notiz"
unreadAntennaNote: "Antenne {name}"
emptyPushNotificationMessage: "Push-Benachrichtigungen wurden aktualisiert"
achievementEarned: "Errungenschaft freigeschaltet"

View File

@ -1119,6 +1119,10 @@ renotes: "Renotes"
loadReplies: "Show replies"
loadConversation: "Show conversation"
pinnedList: "Pinned list"
keepScreenOn: "Keep screen on"
verifiedLink: "Link ownership has been verified"
notifyNotes: "Notify about new notes"
unnotifyNotes: "Stop notifying about new notes"
_announcement:
forExistingUsers: "Existing users only"
forExistingUsersDescription: "This announcement will only be shown to users existing at the point of publishment if enabled. If disabled, those newly signing up after it has been posted will also see it."
@ -1721,7 +1725,6 @@ _timelineTutorial:
_2fa:
alreadyRegistered: "You have already registered a 2-factor authentication device."
registerTOTP: "Register authenticator app"
passwordToTOTP: "Enter your password"
step1: "First, install an authentication app (such as {a} or {b}) on your device."
step2: "Then, scan the QR code displayed on this screen."
step2Click: "Clicking on this QR code will allow you to register 2FA to your security key or phone authenticator app."
@ -1899,6 +1902,7 @@ _profile:
metadataContent: "Content"
changeAvatar: "Change avatar"
changeBanner: "Change banner"
verifiedLinkDescription: "By entering an URL that contains a link to your profile here, an ownership verification icon can be displayed next to the field."
_exportOrImport:
allNotes: "All notes"
favoritedNotes: "Favorite notes"
@ -2018,6 +2022,7 @@ _notification:
youReceivedFollowRequest: "You've received a follow request"
yourFollowRequestAccepted: "Your follow request was accepted"
pollEnded: "Poll results have become available"
newNote: "New note"
unreadAntennaNote: "Antenna {name}"
emptyPushNotificationMessage: "Push notifications have been updated"
achievementEarned: "Achievement unlocked"

View File

@ -1705,7 +1705,6 @@ _timelineTutorial:
_2fa:
alreadyRegistered: "Ya has completado la configuración."
registerTOTP: "Registrar aplicación autenticadora"
passwordToTOTP: "Ingresa tu contraseña"
step1: "Primero, instale en su dispositivo la aplicación de autenticación {a} o {b} u otra."
step2: "Luego, escanee con la aplicación el código QR mostrado en pantalla."
step2Click: "Clicking on this QR code will allow you to register 2FA to your security key or phone authenticator app.\nTocar este código QR te permitirá registrar la autenticación 2FA a tu llave de seguridad o aplicación autenticadora."

View File

@ -480,6 +480,7 @@ createAccount: "Créer un compte"
existingAccount: "Compte existant"
regenerate: "Générer à nouveau"
fontSize: "Taille de la police"
limitTo: "Limiter à {x}"
noFollowRequests: "Vous navez aucune demande dabonnement en attente"
openImageInNewTab: "Ouvrir les images dans un nouvel onglet"
dashboard: "Tableau de bord"

View File

@ -1683,7 +1683,6 @@ _timelineTutorial:
_2fa:
alreadyRegistered: "Kamu telah mendaftarkan perangkat otentikasi dua faktor."
registerTOTP: "Daftarkan aplikasi autentikator"
passwordToTOTP: "Masukkan kata sandimu"
step1: "Pertama, pasang aplikasi otentikasi (seperti {a} atau {b}) di perangkat kamu."
step2: "Lalu, pindai kode QR yang ada di layar."
step2Click: "Mengeklik kode QR ini akan membolehkanmu untuk mendaftarkan 2FA ke security-key atau aplikasi autentikator ponsel."

5
locales/index.d.ts vendored
View File

@ -717,6 +717,7 @@ export interface Locale {
"alwaysMarkSensitive": string;
"loadRawImages": string;
"disableShowingAnimatedImages": string;
"highlightSensitiveMedia": string;
"verificationEmailSent": string;
"notSet": string;
"emailVerified": string;
@ -1126,6 +1127,8 @@ export interface Locale {
"verifiedLink": string;
"notifyNotes": string;
"unnotifyNotes": string;
"authentication": string;
"authenticationRequiredToContinue": string;
"_announcement": {
"forExistingUsers": string;
"forExistingUsersDescription": string;
@ -1842,7 +1845,6 @@ export interface Locale {
"_2fa": {
"alreadyRegistered": string;
"registerTOTP": string;
"passwordToTOTP": string;
"step1": string;
"step2": string;
"step2Click": string;
@ -1924,6 +1926,7 @@ export interface Locale {
"homeTimeline": string;
"users": string;
"userList": string;
"userBlacklist": string;
};
"_weekday": {
"sunday": string;

View File

@ -134,7 +134,7 @@ renoteMute: "Silenzia i Rinota"
renoteUnmute: "Non silenziare i Rinota"
block: "Blocca"
unblock: "Sblocca"
suspend: "Sospendi"
suspend: "Sospensione"
unsuspend: "Revoca la sospensione"
blockConfirm: "Vuoi davvero bloccare il profilo?"
unblockConfirm: "Vuoi davvero sbloccare il profilo?"
@ -180,7 +180,7 @@ youHaveNoLists: "Non hai ancora creato nessuna lista"
followConfirm: "Vuoi seguire {name}?"
proxyAccount: "Profilo proxy"
proxyAccountDescription: "Un profilo proxy funziona come follower per i profili remoti, sotto certe condizioni. Ad esempio, quando un profilo locale ne inserisce uno remoto in una lista (senza seguirlo), se nessun altro segue quel profilo remoto, le attività non possono essere distribuite. Dunque, il profilo proxy le seguirà per tutti."
host: "Server remoto"
host: "Host"
selectUser: "Seleziona profilo"
recipient: "Destinatario"
annotation: "Annotazione preventiva"
@ -287,7 +287,7 @@ images: "Immagini"
image: "Immagini"
birthday: "Compleanno"
yearsOld: "{age} anni"
registeredDate: "Iscrizione a.."
registeredDate: "Data iscrizione"
location: "Posizione"
theme: "Tema"
themeForLightMode: "Tema da utilizzare per il modo chiaro"
@ -496,7 +496,7 @@ noFollowRequests: "Non hai alcuna richiesta di follow"
openImageInNewTab: "Apri le immagini in un nuovo tab"
dashboard: "Pannello di controllo"
local: "Locale"
remote: "Remoto"
remote: "Remota"
total: "Totale"
weekOverWeekChanges: "Settimanale"
dayOverDayChanges: "Giornaliero"
@ -551,8 +551,8 @@ installedDate: "Data installazione"
lastUsedDate: "Data di ultimo uso"
state: "Stato"
sort: "Ordina per"
ascendingOrder: "Ascendente"
descendingOrder: "Discendente"
ascendingOrder: "Aumenta"
descendingOrder: "Diminuisce"
scratchpad: "ScratchPad"
scratchpadDescription: "Lo Scratchpad offre un ambiente per esperimenti di AiScript. È possibile scrivere, eseguire e confermare i risultati dell'interazione del codice con Misskey."
output: "Uscita"
@ -621,7 +621,7 @@ emailConfigInfo: "Utilizzato per verificare il tuo indirizzo di posta elettronic
email: "Email"
emailAddress: "Indirizzo di posta elettronica"
smtpConfig: "Impostazioni del server SMTP"
smtpHost: "Server remoto"
smtpHost: "Host SMTP"
smtpPort: "Porta"
smtpUser: "Nome utente"
smtpPass: "Password"
@ -1112,6 +1112,10 @@ renotes: "Rinota"
loadReplies: "Leggi le risposte"
loadConversation: "Leggi la conversazione"
pinnedList: "Elenco in primo piano"
keepScreenOn: "Mantieni lo schermo acceso"
verifiedLink: "Abbiamo confermato la validità di questo collegamento"
notifyNotes: "Notifica nuove Note"
unnotifyNotes: "Interrompi le notifiche di nuove Note"
_announcement:
forExistingUsers: "Solo ai profili attuali"
forExistingUsersDescription: "L'annuncio sarà visibile solo ai profili esistenti in questo momento. Se disabilitato, sarà visibile anche ai profili che verranno creati dopo la pubblicazione di questo annuncio."
@ -1462,10 +1466,10 @@ _role:
_condition:
isLocal: "Profilo locale"
isRemote: "Profilo remoto"
createdLessThan: "Creato meno di"
createdMoreThan: "Creato più di"
followersLessThanOrEq: "Ha meno di N follower"
followersMoreThanOrEq: "Ha più di N follower"
createdLessThan: "Profilo creato da meno di N"
createdMoreThan: "Profilo creato da più di N"
followersLessThanOrEq: "Profilo con N follower o meno"
followersMoreThanOrEq: "Profilo con N follower o più"
followingLessThanOrEq: "Segue N profili o meno"
followingMoreThanOrEq: "Segue N profili o più"
notesLessThanOrEq: "Conteggio Note inferiore o uguale a"
@ -1712,7 +1716,6 @@ _timelineTutorial:
_2fa:
alreadyRegistered: "La configurazione è stata già completata."
registerTOTP: "Registra un'app di autenticazione"
passwordToTOTP: "Inserire la password"
step1: "Innanzitutto, installare sul dispositivo un'applicazione di autenticazione come {a} o {b}."
step2: "Quindi, scansionare il codice QR visualizzato con l'app."
step2Click: "Cliccando sul codice QR, puoi registrarlo con l'app di autenticazione o il portachiavi installato sul tuo dispositivo."
@ -1890,6 +1893,7 @@ _profile:
metadataContent: "Contenuto"
changeAvatar: "Modifica immagine profilo"
changeBanner: "Cambia intestazione"
verifiedLinkDescription: "Puoi verificare il tuo profilo mostrando una icona. Devi inserire la URL alla pagina che contiene un link al tuo profilo."
_exportOrImport:
allNotes: "Tutte le note"
favoritedNotes: "Note preferite"
@ -2008,6 +2012,7 @@ _notification:
youReceivedFollowRequest: "Hai ricevuto una richiesta di follow"
yourFollowRequestAccepted: "La tua richiesta di follow è stata accettata"
pollEnded: "Risultati del sondaggio."
newNote: "Nuove Note"
unreadAntennaNote: "Antenna {name}"
emptyPushNotificationMessage: "Le notifiche push sono state aggiornate."
achievementEarned: "Obiettivo raggiunto"

View File

@ -714,6 +714,7 @@ lockedAccountInfo: "フォローを承認制にしても、ノートの公開範
alwaysMarkSensitive: "デフォルトでメディアをセンシティブ設定にする"
loadRawImages: "添付画像のサムネイルをオリジナル画質にする"
disableShowingAnimatedImages: "アニメーション画像を再生しない"
highlightSensitiveMedia: "メディアがセンシティブであることを分かりやすく表示"
verificationEmailSent: "確認のメールを送信しました。メールに記載されたリンクにアクセスして、設定を完了してください。"
notSet: "未設定"
emailVerified: "メールアドレスが確認されました"
@ -1123,6 +1124,8 @@ keepScreenOn: "デバイスの画面を常にオンにする"
verifiedLink: "このリンク先の所有者であることが確認されました"
notifyNotes: "投稿を通知"
unnotifyNotes: "投稿の通知を解除"
authentication: "認証"
authenticationRequiredToContinue: "続けるには認証を行ってください"
_announcement:
forExistingUsers: "既存ユーザーのみ"
@ -1759,7 +1762,6 @@ _timelineTutorial:
_2fa:
alreadyRegistered: "既に設定は完了しています。"
registerTOTP: "認証アプリの設定を開始"
passwordToTOTP: "パスワードを入力してください"
step1: "まず、{a}や{b}などの認証アプリをお使いのデバイスにインストールします。"
step2: "次に、表示されているQRコードをアプリでスキャンします。"
step2Click: "QRコードをクリックすると、お使いの端末にインストールされている認証アプリやキーリングに登録できます。"
@ -1841,6 +1843,7 @@ _antennaSources:
homeTimeline: "フォローしているユーザーのノート"
users: "指定した一人または複数のユーザーのノート"
userList: "指定したリストのユーザーのノート"
userBlacklist: "指定した一人または複数のユーザーを除いた全てのノート"
_weekday:
sunday: "日曜日"

View File

@ -1697,7 +1697,6 @@ _timelineTutorial:
_2fa:
alreadyRegistered: "もう設定終わっとるわ。"
registerTOTP: "認証アプリの設定はじめる"
passwordToTOTP: "パスワードを入れてーや"
step1: "ほんなら、{a}や{b}とかの認証アプリを使っとるデバイスにインストールしてな。"
step2: "次に、ここにあるQRコードをアプリでスキャンしてな。"
step2Click: "QRコードをクリックすると、今使とる端末に入っとる認証アプリとかキーリングに登録できるで。"

View File

@ -1699,7 +1699,6 @@ _timelineTutorial:
_2fa:
alreadyRegistered: "이미 설정이 완료되었습니다."
registerTOTP: "인증 앱 설정 시작"
passwordToTOTP: "비밀번호를 입력하세요."
step1: "먼저, {a}나 {b}등의 인증 앱을 사용 중인 디바이스에 설치합니다."
step2: "그 후, 표시되어 있는 QR코드를 앱으로 스캔합니다."
step2Click: "QR 코드를 클릭하면 기기에 설치된 인증 앱에 등록할 수 있습니다."

View File

@ -411,6 +411,7 @@ aboutMisskey: "Sobre Misskey"
administrator: "Administrador"
token: "Símbolo"
2fa: "Autenticação de dois fatores"
setupOf2fa: "Configuração de autenticação de dois fatores"
totp: "Aplicativo Autenticador"
totpDescription: "Digite a senha de uso único informado pelo aplicativo autenticador"
moderator: "Moderador"
@ -918,6 +919,7 @@ pleaseSelect: "Por favor, selecione."
reverse: "Inversão"
colored: "Colorido"
refreshInterval: "Intervalo de atualização"
label: "Etiqueta"
type: "Tipo"
speed: "Velocidade"
slow: "Lento"
@ -1008,6 +1010,7 @@ waitingForMailAuth: "Verificação de e-mail pendente "
icon: "Avatar"
replies: "Responder"
renotes: "Repostar"
keepScreenOn: "Manter a tela do dispositivo sempre ligada"
_initialAccountSetting:
followUsers: "Siga usuários que lhe interessam para criar a sua linha do tempo."
_serverSettings:

View File

@ -1608,7 +1608,6 @@ _timelineTutorial:
_2fa:
alreadyRegistered: "Двухфакторная аутентификация уже настроена."
registerTOTP: "Начните настраивать приложение-аутентификатор"
passwordToTOTP: "Пожалуйста, введите свой пароль"
step1: "Прежде всего, установите на устройство приложение для аутентификации, например, {a} или {b}."
step2: "Далее отсканируйте отображаемый QR-код при помощи приложения."
step2Click: "Нажав на QR-код, вы можете зарегистрироваться с помощью приложения для аутентификации или брелка для ключей, установленного на вашем устройстве."

View File

@ -510,7 +510,6 @@ _sfx:
chat: "Chatt"
antenna: "Antenner"
_2fa:
passwordToTOTP: "Skriv in ditt lösenord"
renewTOTPCancel: "Nej tack"
_antennaSources:
all: "Alla noter"

View File

@ -1696,7 +1696,6 @@ _timelineTutorial:
_2fa:
alreadyRegistered: "คุณได้ลงทะเบียนอุปกรณ์ยืนยันตัวตนแบบ 2 ชั้นแล้ว"
registerTOTP: "ลงทะเบียนแอพตัวตรวจสอบสิทธิ์"
passwordToTOTP: "กรอกรหัสผ่าน"
step1: "ขั้นตอนแรก ติดตั้งแอปยืนยันตัวตน (เช่น {a} หรือ {b}) บนอุปกรณ์ของคุณ"
step2: "จากนั้นสแกนรหัส QR ที่แสดงบนหน้าจอนี้"
step2Click: "การคลิกที่รหัส QR นี้จะช่วยให้คุณนั้นสามารถลงทะเบียน 2FA กับคีย์ความปลอดภัยหรือแอปตรวจสอบความถูกต้องของโทรศัพท์ได้"

View File

@ -1006,9 +1006,9 @@ enableChartsForRemoteUser: "Tạo biểu đồ người dùng từ xa"
video: "Video"
videos: "Các video"
dataSaver: "Tiết kiệm dung lượng"
accountMigration: "Gộp chung tài khoản"
accountMigration: "Chuyển tài khoản"
accountMoved: "Người dùng này đã chuyển sang một tài khoản mới:"
accountMovedShort: "Tài khoản này đã được gộp"
accountMovedShort: "Tài khoản này đã được chuyển"
operationForbidden: "Thao tác này không thể thực hiện"
forceShowAds: "Luôn hiện quảng cáo"
notificationDisplay: "Thông báo"
@ -1046,9 +1046,12 @@ renotes: "Đăng lại"
loadReplies: "Hiển thị các trả lời"
pinnedList: "Các mục đã được ghim"
keepScreenOn: "Giữ màn hình luôn bật"
verifiedLink: "Chúng tôi đã xác nhận bạn là chủ sở hữu của đường dẫn này"
_announcement:
forExistingUsers: "Chỉ những người dùng đã tồn tại"
forExistingUsersDescription: "Nếu được bật, thông báo này sẽ chỉ hiển thị với những người dùng đã tồn tại vào lúc thông báo được tạo. Nếu tắt đi, những tài khoản mới đăng ký sau khi thông báo được đăng lên cũng sẽ thấy nó."
end: "Lưu trữ thông báo"
tooManyActiveAnnouncementDescription: "Có quá nhiều thông báo sẽ làm trải nghiệm của người dùng tệ đi. Vui lòng lưu trữ những thông báo đã hết hiệu lực."
readConfirmTitle: "Đánh dấu là đã đọc?"
readConfirmText: "Điều này sẽ đánh dấu nội dung của \"{title}\" là đã đọc."
_initialAccountSetting:
@ -1056,7 +1059,28 @@ _initialAccountSetting:
letsStartAccountSetup: "Để bắt đầu, hãy cùng thiết lập tài khoản nhé."
letsFillYourProfile: "Đầu tiên, hãy thiết lập hồ sơ của bạn."
profileSetting: "Thiết lập hồ sơ"
privacySetting: "Cài đặt quyền riêng tư"
theseSettingsCanEditLater: "Bạn vẫn có thể thay đổi những cài đặt này."
youCanEditMoreSettingsInSettingsPageLater: "Còn rất nhiều những cài đặt khác bạn có thể thay đổi ở trang \"Cài đặt\". Hãy nhớ ghé thăm trong lần sau nhé."
followUsers: "Thử theo dõi một vài người mà bạn có thể thích để xây dựng dòng thời gian của mình."
pushNotificationDescription: "Bật thông báo đẩy sẽ cho phép bạn nhận thông báo từ {name} trực tiếp từ thiết bị của bạn."
initialAccountSettingCompleted: "Thiết lập tài khoản thành công!"
haveFun: "Hãy tận hưởng {name} nhé!"
ifYouNeedLearnMore: "Nếu bạn muốn tìm hiểu thêm về cách sử dụng {name} (Misskey), hãy vào {link}."
skipAreYouSure: "Bạn thực sự muốn bỏ qua mục thiết lập tài khoản?"
laterAreYouSure: "Bạn thực sự muốn thiết lập tài khoản vào lúc khác?"
_serverSettings:
iconUrl: "Biểu tượng URL"
appIconResolutionMustBe: "Độ phân giải tối thiểu là {resolution}."
manifestJsonOverride: "Ghi đè manifest.json"
_accountMigration:
moveFrom: "Chuyển một tài khoản khác vào tài khoản này"
moveFromLabel: "Tài khoản gốc #{n}"
moveTo: "Chuyển tài khoản này vào một tài khoản khác"
moveCannotBeUndone: "Việc chuyển tài khoản không thể huỷ."
moveAccountDescription: "Điều này sẽ chuyển tài khoản này sang một tài khoản khác.\n ・Những người theo dõi sẽ tự động được chuyển sang tài khoản mới\n ・Tài khoản này sẽ tự bỏ theo dõi những người mà bạn đã theo dõi trước đây\n ・Bạn sẽ không thể đăng tút mới, v.v trên tài khoản này\n\nDù việc chuyển người theo dõi được diễn ra tự động, bạn vẫn phải tự chuẩn bị một vài bước để chuyển danh sách những người dùng bạn đang theo dõi. Để làm vậy, vui lòng thực hiện việc xuất dữ liệu những người dùng đã theo dõi mà sau này bạn sẽ dùng để nhập vào tài khoản mới ở menu Cài đặt. Hành động tương tự áp dụng với danh sách những người dùng bị chặn hoặc tắt tiếng.\n\n(Điều này áp dụng cho phiên bản Misskey v13.12.0 và sau này. Các phần mềm ActivityPub khác , ví dụ như Mastodon, sẽ có thể hoạt động khác đi.)"
startMigration: "Chuyển"
movedAndCannotBeUndone: "\nTài khoản này đã được chuyển đi.\nViệc di chuyển tài khoản không thể bị huỷ bỏ."
movedTo: "Tài khoản mới:"
_achievements:
earnedAt: "Ngày thu nhận"
@ -1096,6 +1120,8 @@ _achievements:
title: "Hàng tinh đăng bài"
description: "Đã đăng bài 50,000 lần rồi"
_notes100000:
title: "ALL YOUR NOTE ARE BELONG TO US"
description: "Đăng 100,000 tút"
flavor: "Liệu viết bài gì tầm này vậy? "
_login3:
title: "Sơ cấp I"
@ -1127,6 +1153,15 @@ _achievements:
_login400:
title: "Khách hàng thường xuyên cấp III"
description: "Tổng số ngày đăng nhập đạt 400 ngày"
_login1000:
flavor: "Cảm ơn bạn đã sử dụng Misskey!"
_noteFavorited1:
title: "Nhà thiên văn học"
_myNoteFavorited1:
title: "Đi tìm những ngôi sao"
_profileFilled:
title: "Luôn sẵn sàng"
description: "Thiết lập tài khoản của bạn"
_markedAsCat:
title: "Tôi là một con mèo"
description: "Bật chế độ mèo"
@ -1152,8 +1187,18 @@ _achievements:
_followers10:
title: "FOLLOW ME!!"
description: "Người theo dõi bạn vượt lên 10 người"
_followers50:
title: "Từng chút một"
description: "Đạt được 50 lượt theo dõi"
_followers100:
title: "Người nổi tiếng"
description: "Đạt được 100 lượt theo dõi"
_followers300:
title: "Vui lòng xếp thành hàng nào"
description: "Đạt được 300 lượt theo dõi"
_followers500:
title: "Trạm phát sóng"
description: "Đạt được 500 lượt theo dõi"
_followers1000:
title: "Người có tầm ảnh hưởng"
description: "Người theo dõi bạn vượt lên 1000 người"
@ -1172,11 +1217,15 @@ _achievements:
description: "Tìm thấy được những kho báu cất giấu"
_client30min:
title: "Giải lao xỉu"
description: "Giữ Misskey mở trong ít nhất 30 phút"
_client60min:
description: "Giữ Misskey mở trong ít nhất 60 phút"
_noteDeletedWithin1min:
title: "Xem như không có gì đâu nha"
_postedAtLateNight:
title: "Loài ăn đêm"
description: "Đăng bài trong đêm khuya "
flavor: "Đến giờ đi ngủ rồi."
_postedAt0min0sec:
title: "Tín hiệu báo giờ"
description: "Đăng bài vào 0 phút 0 giây"
@ -1207,6 +1256,8 @@ _achievements:
_setNameToSyuilo:
title: "Ngưỡng mộ với vị thần"
description: "Đạt tên là syuilo"
_passedSinceAccountCreated1:
title: "Kỷ niệm một năm"
_loggedInOnBirthday:
title: "Sinh nhật vủi vẻ"
description: "Đăng nhập vào ngày sinh"
@ -1466,7 +1517,6 @@ _timelineTutorial:
_2fa:
alreadyRegistered: "Bạn đã đăng ký thiết bị xác minh 2 bước."
registerTOTP: "Đăng ký ứng dụng xác thực"
passwordToTOTP: "Nhắn mật mã"
step1: "Trước tiên, hãy cài đặt một ứng dụng xác minh (chẳng hạn như {a} hoặc {b}) trên thiết bị của bạn."
step2: "Sau đó, quét mã QR hiển thị trên màn hình này."
step2Click: "Quét mã QR trên ứng dụng xác thực (Authy, Google authenticator, v.v.)"

View File

@ -1113,6 +1113,9 @@ loadReplies: "查看回复"
loadConversation: "查看对话"
pinnedList: "已置顶的列表"
keepScreenOn: "保持设备屏幕开启"
verifiedLink: "已验证的链接"
notifyNotes: "打开发帖通知"
unnotifyNotes: "关闭发帖通知"
_announcement:
forExistingUsers: "仅限现有用户"
forExistingUsersDescription: "若启用,该公告将仅对创建此公告时存在的用户可见。 如果禁用,则在创建此公告后注册的用户也可以看到该公告。"
@ -1713,7 +1716,6 @@ _timelineTutorial:
_2fa:
alreadyRegistered: "此设备已被注册"
registerTOTP: "开始设置认证应用"
passwordToTOTP: "请输入您的密码"
step1: "首先,在您的设备上安装验证应用,例如 {a} 或 {b}。"
step2: "然后,扫描屏幕上显示的二维码。"
step2Click: "通过点击二维码,您可以使用设备上安装的身份验证器应用程序或密钥环进行注册"
@ -1891,6 +1893,7 @@ _profile:
metadataContent: "内容"
changeAvatar: "修改头像"
changeBanner: "修改横幅"
verifiedLinkDescription: "如果将内容设置为 URL当链接所指向的网页内包含自己的个人资料链接时可以显示一个已验证图标。"
_exportOrImport:
allNotes: "所有帖子"
favoritedNotes: "收藏的帖子"
@ -2009,6 +2012,7 @@ _notification:
youReceivedFollowRequest: "您有新的关注请求"
yourFollowRequestAccepted: "您的关注请求已通过"
pollEnded: "问卷调查结果已生成。"
newNote: "新的帖子"
unreadAntennaNote: "天线 {name}"
emptyPushNotificationMessage: "推送通知已更新"
achievementEarned: "获得成就"

View File

@ -15,7 +15,7 @@ gotIt: "知道了"
cancel: "取消"
noThankYou: "現在不要"
enterUsername: "輸入使用者名稱"
renotedBy: "{user} 轉傳了"
renotedBy: "{user} 轉"
noNotes: "無貼文"
noNotifications: "沒有通知"
instance: "伺服器"
@ -106,10 +106,10 @@ unfollow: "取消追隨"
followRequestPending: "追隨許可待批准"
enterEmoji: "輸入表情符號"
renote: "轉發"
unrenote: "取消轉"
renoted: "轉成功"
cantRenote: "無法轉此貼文。"
cantReRenote: "無法轉傳之前已經轉傳過的內容。"
unrenote: "取消轉"
renoted: "轉成功"
cantRenote: "無法轉此貼文。"
cantReRenote: "無法轉發之前已經轉發過的內容。"
quote: "引用"
inChannelRenote: "在頻道內轉發"
inChannelQuote: "在頻道內引用"
@ -546,7 +546,7 @@ recentUsed: "最近使用"
install: "安裝"
uninstall: "解除安裝"
installedApps: "已授權的應用程式"
nothing: "無"
nothing: "項目"
installedDate: "安裝時間"
lastUsedDate: "最後上線日期"
state: "狀態"
@ -657,7 +657,7 @@ behavior: "行為"
sample: "範例"
abuseReports: "檢舉"
reportAbuse: "檢舉"
reportAbuseRenote: "檢舉轉貼"
reportAbuseRenote: "檢舉轉"
reportAbuseOf: "檢舉{name}"
fillAbuseReportDescription: "請填寫檢舉的詳細理由。如有需要,請附上相關 URL。"
abuseReported: "檢舉完成。感謝您的報告。"
@ -1112,6 +1112,10 @@ renotes: "轉發"
loadReplies: "閱覽回覆"
loadConversation: "閱覽對話"
pinnedList: "已置頂的清單"
keepScreenOn: "保持設備螢幕開啟"
verifiedLink: "已驗證連結"
notifyNotes: "開啟貼文通知"
unnotifyNotes: "關閉貼文通知"
_announcement:
forExistingUsers: "僅限既有的使用者"
forExistingUsersDescription: "啟用代表僅向現存使用者顯示;停用代表張貼後註冊的新使用者也會看到。"
@ -1687,7 +1691,7 @@ _ago:
future: "未來"
justNow: "剛剛"
secondsAgo: "{n} 秒前"
minutesAgo: "{n}分鐘前 "
minutesAgo: "{n} 分鐘前 "
hoursAgo: "{n} 小時前"
daysAgo: "{n} 天前"
weeksAgo: "{n} 週前"
@ -1712,7 +1716,6 @@ _timelineTutorial:
_2fa:
alreadyRegistered: "此裝置已被註冊過了"
registerTOTP: "開始設定驗證應用程式"
passwordToTOTP: "請輸入密碼"
step1: "首先,在您的裝置上安裝驗證程式,例如 {a} 或 {b}。"
step2: "然後,掃描螢幕上的 QR 碼。"
step2Click: "您可以點擊 QR 碼,以使用裝置上的驗證應用程式或金鑰環註冊。"
@ -1890,6 +1893,7 @@ _profile:
metadataContent: "内容"
changeAvatar: "更換大頭貼"
changeBanner: "變更橫幅圖像"
verifiedLinkDescription: "如果輸入包含您個人資料的網站 URL欄位旁邊將出現驗證圖示。"
_exportOrImport:
allNotes: "所有貼文"
favoritedNotes: "「我的最愛」貼文"
@ -2008,6 +2012,7 @@ _notification:
youReceivedFollowRequest: "您有新的追隨請求"
yourFollowRequestAccepted: "您的追隨請求已通過"
pollEnded: "問卷調查已產生結果"
newNote: "新的貼文"
unreadAntennaNote: "天線 {name}"
emptyPushNotificationMessage: "推送通知已更新"
achievementEarned: "獲得成就"

View File

@ -1,6 +1,6 @@
{
"name": "misskey",
"version": "2023.9.0-beta.10.prismisskey.1",
"version": "2023.9.0-beta.11-prismisskey.1",
"codename": "nasubi",
"repository": {
"type": "git",
@ -56,7 +56,7 @@
"cross-env": "7.0.3",
"cypress": "13.2.0",
"eslint": "8.49.0",
"start-server-and-test": "2.0.0"
"start-server-and-test": "2.0.1"
},
"optionalDependencies": {
"@tensorflow/tfjs-core": "4.4.0"

View File

@ -0,0 +1,10 @@
export class UserBlacklistAnntena1689325027964 {
name = 'UserBlacklistAnntena1689325027964'
async up(queryRunner) {
await queryRunner.query(`ALTER TYPE "antenna_src_enum" ADD VALUE 'users_blacklist' AFTER 'list'`);
}
async down(queryRunner) {
}
}

View File

@ -78,7 +78,7 @@
"@simplewebauthn/server": "8.1.1",
"@sinonjs/fake-timers": "11.1.0",
"@swc/cli": "0.1.62",
"@swc/core": "1.3.86",
"@swc/core": "1.3.87",
"accepts": "1.3.8",
"ajv": "8.12.0",
"archiver": "6.0.1",
@ -86,7 +86,7 @@
"bcryptjs": "2.4.3",
"blurhash": "2.0.5",
"body-parser": "1.20.2",
"bullmq": "4.11.2",
"bullmq": "4.11.3",
"cacheable-lookup": "7.0.0",
"cbor": "9.0.1",
"chalk": "5.3.0",

View File

@ -119,6 +119,12 @@ export class AntennaService implements OnApplicationShutdown {
return this.utilityService.getFullApAccount(username, host).toLowerCase();
});
if (!accts.includes(this.utilityService.getFullApAccount(noteUser.username, noteUser.host).toLowerCase())) return false;
} else if (antenna.src === 'users_blacklist') {
const accts = antenna.users.map(x => {
const { username, host } = Acct.parse(x);
return this.utilityService.getFullApAccount(username, host).toLowerCase();
});
if (accts.includes(this.utilityService.getFullApAccount(noteUser.username, noteUser.host).toLowerCase())) return false;
}
const keywords = antenna.keywords

View File

@ -51,6 +51,7 @@ import { UserKeypairService } from './UserKeypairService.js';
import { UserListService } from './UserListService.js';
import { UserMutingService } from './UserMutingService.js';
import { UserSuspendService } from './UserSuspendService.js';
import { UserAuthService } from './UserAuthService.js';
import { VideoProcessingService } from './VideoProcessingService.js';
import { WebhookService } from './WebhookService.js';
import { ProxyAccountService } from './ProxyAccountService.js';
@ -177,6 +178,7 @@ const $UserKeypairService: Provider = { provide: 'UserKeypairService', useExisti
const $UserListService: Provider = { provide: 'UserListService', useExisting: UserListService };
const $UserMutingService: Provider = { provide: 'UserMutingService', useExisting: UserMutingService };
const $UserSuspendService: Provider = { provide: 'UserSuspendService', useExisting: UserSuspendService };
const $UserAuthService: Provider = { provide: 'UserAuthService', useExisting: UserAuthService };
const $VideoProcessingService: Provider = { provide: 'VideoProcessingService', useExisting: VideoProcessingService };
const $WebhookService: Provider = { provide: 'WebhookService', useExisting: WebhookService };
const $UtilityService: Provider = { provide: 'UtilityService', useExisting: UtilityService };
@ -306,6 +308,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
UserListService,
UserMutingService,
UserSuspendService,
UserAuthService,
VideoProcessingService,
WebhookService,
UtilityService,
@ -428,6 +431,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$UserListService,
$UserMutingService,
$UserSuspendService,
$UserAuthService,
$VideoProcessingService,
$WebhookService,
$UtilityService,
@ -551,6 +555,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
UserListService,
UserMutingService,
UserSuspendService,
UserAuthService,
VideoProcessingService,
WebhookService,
UtilityService,
@ -672,6 +677,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$UserListService,
$UserMutingService,
$UserSuspendService,
$UserAuthService,
$VideoProcessingService,
$WebhookService,
$UtilityService,

View File

@ -0,0 +1,45 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Inject, Injectable } from '@nestjs/common';
import { QueryFailedError } from 'typeorm';
import * as OTPAuth from 'otpauth';
import { DI } from '@/di-symbols.js';
import type { MiUserProfile, UserProfilesRepository, UsersRepository } from '@/models/_.js';
import { bindThis } from '@/decorators.js';
import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js';
import type { MiLocalUser } from '@/models/User.js';
@Injectable()
export class UserAuthService {
constructor(
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
@Inject(DI.userProfilesRepository)
private userProfilesRepository: UserProfilesRepository,
) {
}
@bindThis
public async twoFactorAuthenticate(profile: MiUserProfile, token: string): Promise<void> {
if (profile.twoFactorBackupSecret?.includes(token)) {
await this.userProfilesRepository.update({ userId: profile.userId }, {
twoFactorBackupSecret: profile.twoFactorBackupSecret.filter((secret) => secret !== token),
});
} else {
const delta = OTPAuth.TOTP.validate({
secret: OTPAuth.Secret.fromBase32(profile.twoFactorSecret!),
digits: 6,
token,
window: 5,
});
if (delta === null) {
throw new Error('authentication failed');
}
}
}
}

View File

@ -4,9 +4,10 @@
*/
import { Inject, Injectable } from '@nestjs/common';
import { IsNull, Not } from 'typeorm';
import type { MiLocalUser, MiRemoteUser } from '@/models/User.js';
import { InstanceActorService } from '@/core/InstanceActorService.js';
import type { NotesRepository, PollsRepository, NoteReactionsRepository, UsersRepository } from '@/models/_.js';
import type { NotesRepository, PollsRepository, NoteReactionsRepository, UsersRepository, FollowRequestsRepository } from '@/models/_.js';
import type { Config } from '@/config.js';
import { MetaService } from '@/core/MetaService.js';
import { HttpRequestService } from '@/core/HttpRequestService.js';
@ -32,6 +33,7 @@ export class Resolver {
private notesRepository: NotesRepository,
private pollsRepository: PollsRepository,
private noteReactionsRepository: NoteReactionsRepository,
private followRequestsRepository: FollowRequestsRepository,
private utilityService: UtilityService,
private instanceActorService: InstanceActorService,
private metaService: MetaService,
@ -146,13 +148,24 @@ export class Resolver {
return this.noteReactionsRepository.findOneByOrFail({ id: parsed.id }).then(async reaction =>
this.apRendererService.addContext(await this.apRendererService.renderLike(reaction, { uri: null })));
case 'follows':
// rest should be <followee id>
if (parsed.rest == null || !/^\w+$/.test(parsed.rest)) throw new Error('resolveLocal: invalid follow URI');
return Promise.all(
[parsed.id, parsed.rest].map(id => this.usersRepository.findOneByOrFail({ id })),
)
.then(([follower, followee]) => this.apRendererService.addContext(this.apRendererService.renderFollow(follower as MiLocalUser | MiRemoteUser, followee as MiLocalUser | MiRemoteUser, url)));
return this.followRequestsRepository.findOneBy({ id: parsed.id })
.then(async followRequest => {
if (followRequest == null) throw new Error('resolveLocal: invalid follow request ID');
const [follower, followee] = await Promise.all([
this.usersRepository.findOneBy({
id: followRequest.followerId,
host: IsNull(),
}),
this.usersRepository.findOneBy({
id: followRequest.followeeId,
host: Not(IsNull()),
}),
]);
if (follower == null || followee == null) {
throw new Error('resolveLocal: follower or followee does not exist');
}
return this.apRendererService.addContext(this.apRendererService.renderFollow(follower as MiLocalUser | MiRemoteUser, followee as MiLocalUser | MiRemoteUser, url));
});
default:
throw new Error(`resolveLocal: type ${parsed.type} unhandled`);
}
@ -177,6 +190,9 @@ export class ApResolverService {
@Inject(DI.noteReactionsRepository)
private noteReactionsRepository: NoteReactionsRepository,
@Inject(DI.followRequestsRepository)
private followRequestsRepository: FollowRequestsRepository,
private utilityService: UtilityService,
private instanceActorService: InstanceActorService,
private metaService: MetaService,
@ -196,6 +212,7 @@ export class ApResolverService {
this.notesRepository,
this.pollsRepository,
this.noteReactionsRepository,
this.followRequestsRepository,
this.utilityService,
this.instanceActorService,
this.metaService,

View File

@ -41,8 +41,8 @@ export class MiAntenna {
})
public name: string;
@Column('enum', { enum: ['home', 'all', 'users', 'list'] })
public src: 'home' | 'all' | 'users' | 'list';
@Column('enum', { enum: ['home', 'all', 'users', 'list', 'users_blacklist'] })
public src: 'home' | 'all' | 'users' | 'list' | 'users_blacklist';
@Column({
...id(),

View File

@ -47,7 +47,7 @@ export const packedAntennaSchema = {
src: {
type: 'string',
optional: false, nullable: false,
enum: ['home', 'all', 'users', 'list'],
enum: ['home', 'all', 'users', 'list', 'users_blacklist'],
},
userListId: {
type: 'string',

View File

@ -19,6 +19,7 @@ import type { MiLocalUser } from '@/models/User.js';
import { IdService } from '@/core/IdService.js';
import { bindThis } from '@/decorators.js';
import { WebAuthnService } from '@/core/WebAuthnService.js';
import { UserAuthService } from '@/core/UserAuthService.js';
import { RateLimiterService } from './RateLimiterService.js';
import { SigninService } from './SigninService.js';
import type { AuthenticationResponseJSON } from '@simplewebauthn/typescript-types';
@ -42,6 +43,7 @@ export class SigninApiService {
private idService: IdService,
private rateLimiterService: RateLimiterService,
private signinService: SigninService,
private userAuthService: UserAuthService,
private webAuthnService: WebAuthnService,
) {
}
@ -124,7 +126,7 @@ export class SigninApiService {
const same = await bcrypt.compare(password, profile.password!);
const fail = async (status?: number, failure?: { id: string }) => {
// Append signin history
// Append signin history
await this.signinsRepository.insert({
id: this.idService.genId(),
createdAt: new Date(),
@ -154,27 +156,15 @@ export class SigninApiService {
});
}
if (profile.twoFactorBackupSecret?.includes(token)) {
await this.userProfilesRepository.update({ userId: profile.userId }, {
twoFactorBackupSecret: profile.twoFactorBackupSecret.filter((secret) => secret !== token),
});
return this.signinService.signin(request, reply, user);
}
const delta = OTPAuth.TOTP.validate({
secret: OTPAuth.Secret.fromBase32(profile.twoFactorSecret!),
digits: 6,
token,
window: 1,
});
if (delta === null) {
try {
await this.userAuthService.twoFactorAuthenticate(profile, token);
} catch (e) {
return await fail(403, {
id: 'cdf1235b-ac71-46d4-a3a6-84ccce48df6f',
});
} else {
return this.signinService.signin(request, reply, user);
}
return this.signinService.signin(request, reply, user);
} else if (body.credential) {
if (!same && !profile.usePasswordLessLogin) {
return await fail(403, {
@ -203,6 +193,6 @@ export class SigninApiService {
reply.code(200);
return authRequest;
}
// never get here
// never get here
}
}

View File

@ -47,7 +47,7 @@ export const paramDef = {
type: 'object',
properties: {
name: { type: 'string', minLength: 1, maxLength: 100 },
src: { type: 'string', enum: ['home', 'all', 'users', 'list'] },
src: { type: 'string', enum: ['home', 'all', 'users', 'list', 'users_blacklist'] },
userListId: { type: 'string', format: 'misskey:id', nullable: true },
keywords: { type: 'array', items: {
type: 'array', items: {

View File

@ -46,7 +46,7 @@ export const paramDef = {
properties: {
antennaId: { type: 'string', format: 'misskey:id' },
name: { type: 'string', minLength: 1, maxLength: 100 },
src: { type: 'string', enum: ['home', 'all', 'users', 'list'] },
src: { type: 'string', enum: ['home', 'all', 'users', 'list', 'users_blacklist'] },
userListId: { type: 'string', format: 'misskey:id', nullable: true },
keywords: { type: 'array', items: {
type: 'array', items: {

View File

@ -47,7 +47,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
secret: OTPAuth.Secret.fromBase32(profile.twoFactorTempSecret),
digits: 6,
token,
window: 1,
window: 5,
});
if (delta === null) {

View File

@ -12,6 +12,7 @@ import { GlobalEventService } from '@/core/GlobalEventService.js';
import type { UserProfilesRepository, UserSecurityKeysRepository } from '@/models/_.js';
import { WebAuthnService } from '@/core/WebAuthnService.js';
import { ApiError } from '@/server/api/error.js';
import { UserAuthService } from '@/core/UserAuthService.js';
export const meta = {
requireCredential: true,
@ -37,6 +38,7 @@ export const paramDef = {
type: 'object',
properties: {
password: { type: 'string' },
token: { type: 'string', nullable: true },
name: { type: 'string', minLength: 1, maxLength: 30 },
credential: { type: 'object' },
},
@ -54,16 +56,28 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
private userSecurityKeysRepository: UserSecurityKeysRepository,
private webAuthnService: WebAuthnService,
private userAuthService: UserAuthService,
private userEntityService: UserEntityService,
private globalEventService: GlobalEventService,
) {
super(meta, paramDef, async (ps, me) => {
const token = ps.token;
const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id });
// Compare password
const same = await bcrypt.compare(ps.password, profile.password ?? '');
if (profile.twoFactorEnabled) {
if (token == null) {
throw new Error('authentication failed');
}
if (!same) {
try {
await this.userAuthService.twoFactorAuthenticate(profile, token);
} catch (e) {
throw new Error('authentication failed');
}
}
const passwordMatched = await bcrypt.compare(ps.password, profile.password ?? '');
if (!passwordMatched) {
throw new ApiError(meta.errors.incorrectPassword);
}

View File

@ -10,6 +10,7 @@ import type { UserProfilesRepository } from '@/models/_.js';
import { DI } from '@/di-symbols.js';
import { WebAuthnService } from '@/core/WebAuthnService.js';
import { ApiError } from '@/server/api/error.js';
import { UserAuthService } from '@/core/UserAuthService.js';
export const meta = {
requireCredential: true,
@ -41,6 +42,7 @@ export const paramDef = {
type: 'object',
properties: {
password: { type: 'string' },
token: { type: 'string', nullable: true },
},
required: ['password'],
} as const;
@ -53,8 +55,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
private userProfilesRepository: UserProfilesRepository,
private webAuthnService: WebAuthnService,
private userAuthService: UserAuthService,
) {
super(meta, paramDef, async (ps, me) => {
const token = ps.token;
const profile = await this.userProfilesRepository.findOne({
where: {
userId: me.id,
@ -66,10 +70,20 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
throw new ApiError(meta.errors.userNotFound);
}
// Compare password
const same = await bcrypt.compare(ps.password, profile.password ?? '');
if (profile.twoFactorEnabled) {
if (token == null) {
throw new Error('authentication failed');
}
if (!same) {
try {
await this.userAuthService.twoFactorAuthenticate(profile, token);
} catch (e) {
throw new Error('authentication failed');
}
}
const passwordMatched = await bcrypt.compare(ps.password, profile.password ?? '');
if (!passwordMatched) {
throw new ApiError(meta.errors.incorrectPassword);
}

View File

@ -12,6 +12,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js';
import { ApiError } from '@/server/api/error.js';
import { UserAuthService } from '@/core/UserAuthService.js';
export const meta = {
requireCredential: true,
@ -31,6 +32,7 @@ export const paramDef = {
type: 'object',
properties: {
password: { type: 'string' },
token: { type: 'string', nullable: true },
},
required: ['password'],
} as const;
@ -43,14 +45,27 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
@Inject(DI.userProfilesRepository)
private userProfilesRepository: UserProfilesRepository,
private userAuthService: UserAuthService,
) {
super(meta, paramDef, async (ps, me) => {
const token = ps.token;
const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id });
// Compare password
const same = await bcrypt.compare(ps.password, profile.password ?? '');
if (profile.twoFactorEnabled) {
if (token == null) {
throw new Error('authentication failed');
}
if (!same) {
try {
await this.userAuthService.twoFactorAuthenticate(profile, token);
} catch (e) {
throw new Error('authentication failed');
}
}
const passwordMatched = await bcrypt.compare(ps.password, profile.password ?? '');
if (!passwordMatched) {
throw new ApiError(meta.errors.incorrectPassword);
}

View File

@ -11,6 +11,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import { DI } from '@/di-symbols.js';
import { ApiError } from '@/server/api/error.js';
import { UserAuthService } from '@/core/UserAuthService.js';
export const meta = {
requireCredential: true,
@ -30,6 +31,7 @@ export const paramDef = {
type: 'object',
properties: {
password: { type: 'string' },
token: { type: 'string', nullable: true },
credentialId: { type: 'string' },
},
required: ['password', 'credentialId'],
@ -45,15 +47,27 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private userProfilesRepository: UserProfilesRepository,
private userEntityService: UserEntityService,
private userAuthService: UserAuthService,
private globalEventService: GlobalEventService,
) {
super(meta, paramDef, async (ps, me) => {
const token = ps.token;
const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id });
// Compare password
const same = await bcrypt.compare(ps.password, profile.password ?? '');
if (profile.twoFactorEnabled) {
if (token == null) {
throw new Error('authentication failed');
}
if (!same) {
try {
await this.userAuthService.twoFactorAuthenticate(profile, token);
} catch (e) {
throw new Error('authentication failed');
}
}
const passwordMatched = await bcrypt.compare(ps.password, profile.password ?? '');
if (!passwordMatched) {
throw new ApiError(meta.errors.incorrectPassword);
}

View File

@ -11,6 +11,7 @@ import type { UserProfilesRepository } from '@/models/_.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import { DI } from '@/di-symbols.js';
import { ApiError } from '@/server/api/error.js';
import { UserAuthService } from '@/core/UserAuthService.js';
export const meta = {
requireCredential: true,
@ -30,6 +31,7 @@ export const paramDef = {
type: 'object',
properties: {
password: { type: 'string' },
token: { type: 'string', nullable: true },
},
required: ['password'],
} as const;
@ -41,15 +43,27 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private userProfilesRepository: UserProfilesRepository,
private userEntityService: UserEntityService,
private userAuthService: UserAuthService,
private globalEventService: GlobalEventService,
) {
super(meta, paramDef, async (ps, me) => {
const token = ps.token;
const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id });
// Compare password
const same = await bcrypt.compare(ps.password, profile.password ?? '');
if (profile.twoFactorEnabled) {
if (token == null) {
throw new Error('authentication failed');
}
if (!same) {
try {
await this.userAuthService.twoFactorAuthenticate(profile, token);
} catch (e) {
throw new Error('authentication failed');
}
}
const passwordMatched = await bcrypt.compare(ps.password, profile.password ?? '');
if (!passwordMatched) {
throw new ApiError(meta.errors.incorrectPassword);
}

View File

@ -8,6 +8,7 @@ import { Inject, Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import type { UserProfilesRepository } from '@/models/_.js';
import { DI } from '@/di-symbols.js';
import { UserAuthService } from '@/core/UserAuthService.js';
export const meta = {
requireCredential: true,
@ -20,6 +21,7 @@ export const paramDef = {
properties: {
currentPassword: { type: 'string' },
newPassword: { type: 'string', minLength: 1 },
token: { type: 'string', nullable: true },
},
required: ['currentPassword', 'newPassword'],
} as const;
@ -29,14 +31,28 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
constructor(
@Inject(DI.userProfilesRepository)
private userProfilesRepository: UserProfilesRepository,
private userAuthService: UserAuthService,
) {
super(meta, paramDef, async (ps, me) => {
const token = ps.token;
const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id });
// Compare password
const same = await bcrypt.compare(ps.currentPassword, profile.password!);
if (profile.twoFactorEnabled) {
if (token == null) {
throw new Error('authentication failed');
}
if (!same) {
try {
await this.userAuthService.twoFactorAuthenticate(profile, token);
} catch (e) {
throw new Error('authentication failed');
}
}
const passwordMatched = await bcrypt.compare(ps.currentPassword, profile.password!);
if (!passwordMatched) {
throw new Error('incorrect password');
}

View File

@ -9,6 +9,7 @@ import type { UsersRepository, UserProfilesRepository } from '@/models/_.js';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { DeleteAccountService } from '@/core/DeleteAccountService.js';
import { DI } from '@/di-symbols.js';
import { UserAuthService } from '@/core/UserAuthService.js';
export const meta = {
requireCredential: true,
@ -20,6 +21,7 @@ export const paramDef = {
type: 'object',
properties: {
password: { type: 'string' },
token: { type: 'string', nullable: true },
},
required: ['password'],
} as const;
@ -33,19 +35,32 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
@Inject(DI.userProfilesRepository)
private userProfilesRepository: UserProfilesRepository,
private userAuthService: UserAuthService,
private deleteAccountService: DeleteAccountService,
) {
super(meta, paramDef, async (ps, me) => {
const token = ps.token;
const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id });
if (profile.twoFactorEnabled) {
if (token == null) {
throw new Error('authentication failed');
}
try {
await this.userAuthService.twoFactorAuthenticate(profile, token);
} catch (e) {
throw new Error('authentication failed');
}
}
const userDetailed = await this.usersRepository.findOneByOrFail({ id: me.id });
if (userDetailed.isDeleted) {
return;
}
// Compare password
const same = await bcrypt.compare(ps.password, profile.password!);
if (!same) {
const passwordMatched = await bcrypt.compare(ps.password, profile.password!);
if (!passwordMatched) {
throw new Error('incorrect password');
}

View File

@ -14,6 +14,7 @@ import type { Config } from '@/config.js';
import { DI } from '@/di-symbols.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import { L_CHARS, secureRndstr } from '@/misc/secure-rndstr.js';
import { UserAuthService } from '@/core/UserAuthService.js';
import { ApiError } from '../../error.js';
export const meta = {
@ -46,6 +47,7 @@ export const paramDef = {
properties: {
password: { type: 'string' },
email: { type: 'string', nullable: true },
token: { type: 'string', nullable: true },
},
required: ['password'],
} as const;
@ -61,15 +63,27 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private userEntityService: UserEntityService,
private emailService: EmailService,
private userAuthService: UserAuthService,
private globalEventService: GlobalEventService,
) {
super(meta, paramDef, async (ps, me) => {
const token = ps.token;
const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id });
// Compare password
const same = await bcrypt.compare(ps.password, profile.password!);
if (profile.twoFactorEnabled) {
if (token == null) {
throw new Error('authentication failed');
}
if (!same) {
try {
await this.userAuthService.twoFactorAuthenticate(profile, token);
} catch (e) {
throw new Error('authentication failed');
}
}
const passwordMatched = await bcrypt.compare(ps.password, profile.password!);
if (!passwordMatched) {
throw new ApiError(meta.errors.incorrectPassword);
}

View File

@ -60,10 +60,12 @@ describe('2要素認証', () => {
};
const keyDoneParam = (param: {
token: string,
keyName: string,
credentialId: Buffer,
creationOptions: PublicKeyCredentialCreationOptionsJSON,
}): {
token: string,
password: string,
name: string,
credential: RegistrationResponseJSON,
@ -94,6 +96,7 @@ describe('2要素認証', () => {
return {
password,
token: param.token,
name: param.keyName,
credential: <RegistrationResponseJSON>{
id: param.credentialId.toString('base64url'),
@ -218,6 +221,12 @@ describe('2要素認証', () => {
});
assert.strictEqual(signinResponse.status, 200);
assert.notEqual(signinResponse.body.i, undefined);
// 後片付け
await api('/i/2fa/unregister', {
password,
token: otpToken(registerResponse.body.secret),
}, alice);
});
test('が設定でき、セキュリティキーでログインできる。', async () => {
@ -233,6 +242,7 @@ describe('2要素認証', () => {
const registerKeyResponse = await api('/i/2fa/register-key', {
password,
token: otpToken(registerResponse.body.secret),
}, alice);
assert.strictEqual(registerKeyResponse.status, 200);
assert.notEqual(registerKeyResponse.body.rp, undefined);
@ -241,6 +251,7 @@ describe('2要素認証', () => {
const keyName = 'example-key';
const credentialId = crypto.randomBytes(0x41);
const keyDoneResponse = await api('/i/2fa/key-done', keyDoneParam({
token: otpToken(registerResponse.body.secret),
keyName,
credentialId,
creationOptions: registerKeyResponse.body,
@ -271,6 +282,12 @@ describe('2要素認証', () => {
}));
assert.strictEqual(signinResponse2.status, 200);
assert.notEqual(signinResponse2.body.i, undefined);
// 後片付け
await api('/i/2fa/unregister', {
password,
token: otpToken(registerResponse.body.secret),
}, alice);
});
test('が設定でき、セキュリティキーでパスワードレスログインできる。', async () => {
@ -285,6 +302,7 @@ describe('2要素認証', () => {
assert.strictEqual(doneResponse.status, 200);
const registerKeyResponse = await api('/i/2fa/register-key', {
token: otpToken(registerResponse.body.secret),
password,
}, alice);
assert.strictEqual(registerKeyResponse.status, 200);
@ -292,6 +310,7 @@ describe('2要素認証', () => {
const keyName = 'example-key';
const credentialId = crypto.randomBytes(0x41);
const keyDoneResponse = await api('/i/2fa/key-done', keyDoneParam({
token: otpToken(registerResponse.body.secret),
keyName,
credentialId,
creationOptions: registerKeyResponse.body,
@ -326,6 +345,12 @@ describe('2要素認証', () => {
});
assert.strictEqual(signinResponse2.status, 200);
assert.notEqual(signinResponse2.body.i, undefined);
// 後片付け
await api('/i/2fa/unregister', {
password,
token: otpToken(registerResponse.body.secret),
}, alice);
});
test('が設定でき、設定したセキュリティキーの名前を変更できる。', async () => {
@ -340,6 +365,7 @@ describe('2要素認証', () => {
assert.strictEqual(doneResponse.status, 200);
const registerKeyResponse = await api('/i/2fa/register-key', {
token: otpToken(registerResponse.body.secret),
password,
}, alice);
assert.strictEqual(registerKeyResponse.status, 200);
@ -347,6 +373,7 @@ describe('2要素認証', () => {
const keyName = 'example-key';
const credentialId = crypto.randomBytes(0x41);
const keyDoneResponse = await api('/i/2fa/key-done', keyDoneParam({
token: otpToken(registerResponse.body.secret),
keyName,
credentialId,
creationOptions: registerKeyResponse.body,
@ -367,6 +394,12 @@ describe('2要素認証', () => {
assert.strictEqual(securityKeys.length, 1);
assert.strictEqual(securityKeys[0].name, renamedKey);
assert.notEqual(securityKeys[0].lastUsed, undefined);
// 後片付け
await api('/i/2fa/unregister', {
password,
token: otpToken(registerResponse.body.secret),
}, alice);
});
test('が設定でき、設定したセキュリティキーを削除できる。', async () => {
@ -381,6 +414,7 @@ describe('2要素認証', () => {
assert.strictEqual(doneResponse.status, 200);
const registerKeyResponse = await api('/i/2fa/register-key', {
token: otpToken(registerResponse.body.secret),
password,
}, alice);
assert.strictEqual(registerKeyResponse.status, 200);
@ -388,6 +422,7 @@ describe('2要素認証', () => {
const keyName = 'example-key';
const credentialId = crypto.randomBytes(0x41);
const keyDoneResponse = await api('/i/2fa/key-done', keyDoneParam({
token: otpToken(registerResponse.body.secret),
keyName,
credentialId,
creationOptions: registerKeyResponse.body,
@ -400,6 +435,7 @@ describe('2要素認証', () => {
assert.strictEqual(iResponse.status, 200);
for (const key of iResponse.body.securityKeysList) {
const removeKeyResponse = await api('/i/2fa/remove-key', {
token: otpToken(registerResponse.body.secret),
password,
credentialId: key.id,
}, alice);
@ -418,6 +454,12 @@ describe('2要素認証', () => {
});
assert.strictEqual(signinResponse.status, 200);
assert.notEqual(signinResponse.body.i, undefined);
// 後片付け
await api('/i/2fa/unregister', {
password,
token: otpToken(registerResponse.body.secret),
}, alice);
});
test('が設定でき、設定解除できる。(パスワードのみでログインできる。)', async () => {
@ -438,6 +480,7 @@ describe('2要素認証', () => {
assert.strictEqual(usersShowResponse.body.twoFactorEnabled, true);
const unregisterResponse = await api('/i/2fa/unregister', {
token: otpToken(registerResponse.body.secret),
password,
}, alice);
assert.strictEqual(unregisterResponse.status, 204);
@ -447,5 +490,11 @@ describe('2要素認証', () => {
});
assert.strictEqual(signinResponse.status, 200);
assert.notEqual(signinResponse.body.i, undefined);
// 後片付け
await api('/i/2fa/unregister', {
password,
token: otpToken(registerResponse.body.secret),
}, alice);
});
});

View File

@ -132,6 +132,7 @@ describe('ユーザー', () => {
isBlocked: user.isBlocked ?? false,
isMuted: user.isMuted ?? false,
isRenoteMuted: user.isRenoteMuted ?? false,
notify: user.notify ?? 'none',
});
};

View File

@ -15,7 +15,7 @@ import type { LoggerService } from '@/core/LoggerService.js';
import type { MetaService } from '@/core/MetaService.js';
import type { UtilityService } from '@/core/UtilityService.js';
import { bindThis } from '@/decorators.js';
import type { NoteReactionsRepository, NotesRepository, PollsRepository, UsersRepository } from '@/models/_.js';
import type { NoteReactionsRepository, NotesRepository, PollsRepository, UsersRepository, FollowRequestsRepository } from '@/models/_.js';
type MockResponse = {
type: string;
@ -33,6 +33,7 @@ export class MockResolver extends Resolver {
{} as NotesRepository,
{} as PollsRepository,
{} as NoteReactionsRepository,
{} as FollowRequestsRepository,
{} as UtilityService,
{} as InstanceActorService,
{} as MetaService,

View File

@ -110,7 +110,7 @@
"@types/ws": "8.5.5",
"@typescript-eslint/eslint-plugin": "6.7.2",
"@typescript-eslint/parser": "6.7.2",
"@vitest/coverage-v8": "0.34.4",
"@vitest/coverage-v8": "0.34.5",
"@vue/runtime-core": "3.3.4",
"acorn": "8.10.0",
"cross-env": "7.0.3",
@ -127,14 +127,14 @@
"prettier": "3.0.3",
"react": "18.2.0",
"react-dom": "18.2.0",
"start-server-and-test": "2.0.0",
"storybook": "7.4.2",
"start-server-and-test": "2.0.1",
"storybook": "7.4.3",
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
"summaly": "github:misskey-dev/summaly",
"vite-plugin-turbosnap": "1.0.3",
"vitest": "0.34.4",
"vitest": "0.34.5",
"vitest-fetch-mock": "0.2.2",
"vue-eslint-parser": "9.3.1",
"vue-tsc": "1.8.11"
"vue-tsc": "1.8.13"
}
}

View File

@ -155,6 +155,10 @@ onMounted(() => {
}
});
});
defineExpose({
focus,
});
</script>
<style lang="scss" module>

View File

@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div :class="hide ? $style.hidden : $style.visible" :style="darkMode ? '--c: rgb(255 255 255 / 2%);' : '--c: rgb(0 0 0 / 2%);'" @click="onclick">
<div :class="[hide ? $style.hidden : $style.visible, (image.isSensitive && defaultStore.state.highlightSensitiveMedia) && $style.sensitive]" :style="darkMode ? '--c: rgb(255 255 255 / 2%);' : '--c: rgb(0 0 0 / 2%);'" @click="onclick">
<component
:is="disableImageLink ? 'div' : 'a'"
v-bind="disableImageLink ? {
@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:title="image.comment || image.name"
:width="image.properties.width"
:height="image.properties.height"
:style="hide ? 'filter: brightness(0.5);' : null"
:style="hide ? 'filter: brightness(0.7);' : null"
/>
</component>
<template v-if="hide">
@ -124,6 +124,22 @@ function showMenu(ev: MouseEvent) {
position: relative;
}
.sensitive {
position: relative;
&::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
border-radius: inherit;
box-shadow: inset 0 0 0 4px var(--warn);
}
}
.hiddenText {
position: absolute;
left: 0;

View File

@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div v-if="hide" :class="$style.hidden" @click="hide = false">
<div v-if="hide" :class="[$style.hidden, (video.isSensitive && defaultStore.state.highlightSensitiveMedia) && $style.sensitiveContainer]" @click="hide = false">
<!-- 注意dataSaverMode が有効になっている際にはhide false になるまでサムネイルや動画を読み込まないようにすること -->
<div :class="$style.sensitive">
<b v-if="video.isSensitive" style="display: block;"><i class="ti ti-alert-triangle"></i> {{ i18n.ts.sensitive }}{{ defaultStore.state.enableDataSaverMode ? ` (${i18n.ts.video}${video.size ? ' ' + bytes(video.size) : ''})` : '' }}</b>
@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<span>{{ i18n.ts.clickToShow }}</span>
</div>
</div>
<div v-else :class="$style.visible">
<div v-else :class="[$style.visible, (video.isSensitive && defaultStore.state.highlightSensitiveMedia) && $style.sensitiveContainer]">
<video
:class="$style.video"
:poster="video.thumbnailUrl"
@ -49,6 +49,22 @@ const hide = ref((defaultStore.state.nsfw === 'force' || defaultStore.state.enab
position: relative;
}
.sensitiveContainer {
position: relative;
&::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
border-radius: inherit;
box-shadow: inset 0 0 0 4px var(--warn);
}
}
.hide {
display: block;
position: absolute;

View File

@ -0,0 +1,70 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<MkModalWindow
ref="dialog"
:width="370"
:height="400"
@close="onClose"
@closed="emit('closed')"
>
<template #header>{{ i18n.ts.authentication }}</template>
<MkSpacer :marginMin="20" :marginMax="28">
<div style="padding: 0 0 16px 0; text-align: center;">
<i class="ti ti-lock" style="font-size: 32px; color: var(--accent);"></i>
<div style="margin-top: 10px;">{{ i18n.ts.authenticationRequiredToContinue }}</div>
</div>
<div class="_gaps">
<MkInput ref="passwordInput" v-model="password" :placeholder="i18n.ts.password" type="password" autocomplete="current-password webauthn" :withPasswordToggle="true">
<template #prefix><i class="ti ti-password"></i></template>
</MkInput>
<MkInput v-if="$i.twoFactorEnabled" v-model="token" type="text" pattern="^([0-9]{6}|[A-Z0-9]{32})$" autocomplete="one-time-code" :spellcheck="false">
<template #label>{{ i18n.ts.token }} ({{ i18n.ts['2fa'] }})</template>
<template #prefix><i class="ti ti-123"></i></template>
</MkInput>
<MkButton :disabled="(password ?? '') == '' || ($i.twoFactorEnabled && (token ?? '') == '')" primary rounded style="margin: 0 auto;" @click="done"><i class="ti ti-lock-open"></i> {{ i18n.ts.continue }}</MkButton>
</div>
</MkSpacer>
</MkModalWindow>
</template>
<script lang="ts" setup>
import { onMounted } from 'vue';
import MkInput from '@/components/MkInput.vue';
import MkButton from '@/components/MkButton.vue';
import MkModalWindow from '@/components/MkModalWindow.vue';
import { i18n } from '@/i18n.js';
import { $i } from '@/account.js';
const emit = defineEmits<{
(ev: 'done', v: { password: string; token: string | null; }): void;
(ev: 'closed'): void;
(ev: 'cancelled'): void;
}>();
const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>();
const passwordInput = $shallowRef<InstanceType<typeof MkInput>>();
const password = $ref('');
const token = $ref(null);
function onClose() {
emit('cancelled');
if (dialog) dialog.close();
}
function done(res) {
emit('done', { password, token });
if (dialog) dialog.close();
}
onMounted(() => {
if (passwordInput) passwordInput.focus();
});
</script>

View File

@ -17,6 +17,7 @@ import MkWaitingDialog from '@/components/MkWaitingDialog.vue';
import MkPageWindow from '@/components/MkPageWindow.vue';
import MkToast from '@/components/MkToast.vue';
import MkDialog from '@/components/MkDialog.vue';
import MkPasswordDialog from '@/components/MkPasswordDialog.vue';
import MkEmojiPickerDialog from '@/components/MkEmojiPickerDialog.vue';
import MkEmojiPickerWindow from '@/components/MkEmojiPickerWindow.vue';
import MkPopupMenu from '@/components/MkPopupMenu.vue';
@ -333,6 +334,18 @@ export function inputDate(props: {
});
}
export function authenticateDialog(): Promise<{ canceled: true; result: undefined; } | {
canceled: false; result: { password: string; token: string | null; };
}> {
return new Promise((resolve, reject) => {
popup(MkPasswordDialog, {}, {
done: result => {
resolve(result ? { canceled: false, result } : { canceled: true, result: undefined });
},
}, 'closed');
});
}
export function select<C = any>(props: {
title?: string | null;
text?: string | null;

View File

@ -16,12 +16,13 @@ SPDX-License-Identifier: AGPL-3.0-only
<!--<option value="home">{{ i18n.ts._antennaSources.homeTimeline }}</option>-->
<option value="users">{{ i18n.ts._antennaSources.users }}</option>
<!--<option value="list">{{ i18n.ts._antennaSources.userList }}</option>-->
<option value="users_blacklist">{{ i18n.ts._antennaSources.userBlacklist }}</option>
</MkSelect>
<MkSelect v-if="src === 'list'" v-model="userListId">
<template #label>{{ i18n.ts.userList }}</template>
<option v-for="list in userLists" :key="list.id" :value="list.id">{{ list.name }}</option>
</MkSelect>
<MkTextarea v-else-if="src === 'users'" v-model="users">
<MkTextarea v-else-if="src === 'users' || src === 'users_blacklist'" v-model="users">
<template #label>{{ i18n.ts.users }}</template>
<template #caption>{{ i18n.ts.antennaUsersDescription }} <button class="_textButton" @click="addUser">{{ i18n.ts.addUser }}</button></template>
</MkTextarea>

View File

@ -94,16 +94,12 @@ withDefaults(defineProps<{
const usePasswordLessLogin = $computed(() => $i?.usePasswordLessLogin ?? false);
async function registerTOTP(): Promise<void> {
const password = await os.inputText({
title: i18n.ts._2fa.registerTOTP,
text: i18n.ts._2fa.passwordToTOTP,
type: 'password',
autocomplete: 'current-password',
});
if (password.canceled) return;
const auth = await os.authenticateDialog();
if (auth.canceled) return;
const twoFactorData = await os.apiWithDialog('i/2fa/register', {
password: password.result,
password: auth.result.password,
token: auth.result.token,
});
os.popup(defineAsyncComponent(() => import('./2fa.qrdialog.vue')), {
@ -111,20 +107,17 @@ async function registerTOTP(): Promise<void> {
}, {}, 'closed');
}
function unregisterTOTP(): void {
os.inputText({
title: i18n.ts.password,
type: 'password',
autocomplete: 'current-password',
}).then(({ canceled, result: password }) => {
if (canceled) return;
os.apiWithDialog('i/2fa/unregister', {
password: password,
}).catch(error => {
os.alert({
type: 'error',
text: error,
});
async function unregisterTOTP(): Promise<void> {
const auth = await os.authenticateDialog();
if (auth.canceled) return;
os.apiWithDialog('i/2fa/unregister', {
password: auth.result.password,
token: auth.result.token,
}).catch(error => {
os.alert({
type: 'error',
text: error,
});
});
}
@ -150,15 +143,12 @@ async function unregisterKey(key) {
});
if (confirm.canceled) return;
const password = await os.inputText({
title: i18n.ts.password,
type: 'password',
autocomplete: 'current-password',
});
if (password.canceled) return;
const auth = await os.authenticateDialog();
if (auth.canceled) return;
await os.apiWithDialog('i/2fa/remove-key', {
password: password.result,
password: auth.result.password,
token: auth.result.token,
credentialId: key.id,
});
os.success();
@ -181,16 +171,13 @@ async function renameKey(key) {
}
async function addSecurityKey() {
const password = await os.inputText({
title: i18n.ts.password,
type: 'password',
autocomplete: 'current-password',
});
if (password.canceled) return;
const auth = await os.authenticateDialog();
if (auth.canceled) return;
const registrationOptions = parseCreationOptionsFromJSON({
publicKey: await os.apiWithDialog('i/2fa/register-key', {
password: password.result,
password: auth.result.password,
token: auth.result.token,
}),
});
@ -211,8 +198,12 @@ async function addSecurityKey() {
);
if (!credential) return;
const auth2 = await os.authenticateDialog();
if (auth2.canceled) return;
await os.apiWithDialog('i/2fa/key-done', {
password: password.result,
password: auth.result.password,
token: auth.result.token,
name: name.result,
credential: credential.toJSON(),
});

View File

@ -67,18 +67,16 @@ const onChangeReceiveAnnouncementEmail = (v) => {
});
};
const saveEmailAddress = () => {
os.inputText({
title: i18n.ts.password,
type: 'password',
}).then(({ canceled, result: password }) => {
if (canceled) return;
os.apiWithDialog('i/update-email', {
password: password,
email: emailAddress.value,
});
async function saveEmailAddress() {
const auth = await os.authenticateDialog();
if (auth.canceled) return;
os.apiWithDialog('i/update-email', {
password: auth.result.password,
token: auth.result.token,
email: emailAddress.value,
});
};
}
const emailNotification_mention = ref($i!.emailNotificationTypes.includes('mention'));
const emailNotification_reply = ref($i!.emailNotificationTypes.includes('reply'));

View File

@ -116,6 +116,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSwitch v-model="useBlurEffect">{{ i18n.ts.useBlurEffect }}</MkSwitch>
<MkSwitch v-model="useBlurEffectForModal">{{ i18n.ts.useBlurEffectForModal }}</MkSwitch>
<MkSwitch v-model="disableShowingAnimatedImages">{{ i18n.ts.disableShowingAnimatedImages }}</MkSwitch>
<MkSwitch v-model="highlightSensitiveMedia">{{ i18n.ts.highlightSensitiveMedia }}</MkSwitch>
<MkSwitch v-model="squareAvatars">{{ i18n.ts.squareAvatars }}</MkSwitch>
<MkSwitch v-model="useSystemFont">{{ i18n.ts.useSystemFont }}</MkSwitch>
<MkSwitch v-model="disableDrawer">{{ i18n.ts.disableDrawer }}</MkSwitch>
@ -243,6 +244,7 @@ const enableDataSaverMode = computed(defaultStore.makeGetterSetter('enableDataSa
const enableCellularWithDataSaver = computed(defaultStore.makeGetterSetter('enableCellularWithDataSaver'));
const enableUltimateDataSaverMode = computed(defaultStore.makeGetterSetter('enableUltimateDataSaverMode'))
const enableCellularWithUltimateDataSaver = computed(defaultStore.makeGetterSetter('enableCellularWithUltimateDataSaver'));
const highlightSensitiveMedia = computed(defaultStore.makeGetterSetter('highlightSensitiveMedia'));
const imageNewTab = computed(defaultStore.makeGetterSetter('imageNewTab'));
const nsfw = computed(defaultStore.makeGetterSetter('nsfw'));
const showFixedPostForm = computed(defaultStore.makeGetterSetter('showFixedPostForm'));
@ -293,6 +295,7 @@ watch([
overridedDeviceKind,
mediaListWithOneImageAppearance,
reactionsDisplaySize,
highlightSensitiveMedia,
keepScreenOn,
], async () => {
await reloadAsk();

View File

@ -113,14 +113,12 @@ async function deleteAccount() {
if (canceled) return;
}
const { canceled, result: password } = await os.inputText({
title: i18n.ts.password,
type: 'password',
});
if (canceled) return;
const auth = await os.authenticateDialog();
if (auth.canceled) return;
await os.apiWithDialog('i/delete-account', {
password: password,
password: auth.result.password,
token: auth.result.token,
});
await os.alert({

View File

@ -55,13 +55,6 @@ const pagination = {
};
async function change() {
const { canceled: canceled1, result: currentPassword } = await os.inputText({
title: i18n.ts.currentPassword,
type: 'password',
autocomplete: 'current-password',
});
if (canceled1) return;
const { canceled: canceled2, result: newPassword } = await os.inputText({
title: i18n.ts.newPassword,
type: 'password',
@ -84,21 +77,23 @@ async function change() {
return;
}
const auth = await os.authenticateDialog();
if (auth.canceled) return;
os.apiWithDialog('i/change-password', {
currentPassword,
currentPassword: auth.result.password,
token: auth.result.token,
newPassword,
});
}
function regenerateToken() {
os.inputText({
title: i18n.ts.password,
type: 'password',
}).then(({ canceled, result: password }) => {
if (canceled) return;
os.api('i/regenerate-token', {
password: password,
});
async function regenerateToken() {
const auth = await os.authenticateDialog();
if (auth.canceled) return;
os.api('i/regenerate-token', {
password: auth.result.password,
token: auth.result.token,
});
}

View File

@ -123,7 +123,7 @@ export const defaultStore = markRaw(new Storage('base', {
where: 'deviceAccount',
default: [
'notifications',
'favorites',
'clips',
'drive',
'followRequests',
'-',
@ -190,6 +190,10 @@ export const defaultStore = markRaw(new Storage('base', {
where: 'device',
default: 'respect' as 'respect' | 'force' | 'ignore',
},
highlightSensitiveMedia: {
where: 'device',
default: false,
},
animation: {
where: 'device',
default: !window.matchMedia('(prefers-reduced-motion)').matches,

View File

@ -39,7 +39,7 @@
],
"dependencies": {
"@swc/cli": "0.1.62",
"@swc/core": "1.3.86",
"@swc/core": "1.3.87",
"eventemitter3": "5.0.1",
"reconnecting-websocket": "4.4.0"
}

View File

@ -51,8 +51,8 @@ importers:
specifier: 8.49.0
version: 8.49.0
start-server-and-test:
specifier: 2.0.0
version: 2.0.0
specifier: 2.0.1
version: 2.0.1
packages/backend:
dependencies:
@ -121,10 +121,10 @@ importers:
version: 2.1.5
'@swc/cli':
specifier: 0.1.62
version: 0.1.62(@swc/core@1.3.86)(chokidar@3.5.3)
version: 0.1.62(@swc/core@1.3.87)(chokidar@3.5.3)
'@swc/core':
specifier: 1.3.86
version: 1.3.86
specifier: 1.3.87
version: 1.3.87
accepts:
specifier: 1.3.8
version: 1.3.8
@ -147,8 +147,8 @@ importers:
specifier: 1.20.2
version: 1.20.2
bullmq:
specifier: 4.11.2
version: 4.11.2
specifier: 4.11.3
version: 4.11.3
cacheable-lookup:
specifier: 7.0.0
version: 7.0.0
@ -489,7 +489,7 @@ importers:
version: 8.0.0
'@swc/jest':
specifier: 0.2.29
version: 0.2.29(@swc/core@1.3.86)
version: 0.2.29(@swc/core@1.3.87)
'@types/accepts':
specifier: 1.3.5
version: 1.3.5
@ -845,7 +845,7 @@ importers:
version: 7.4.3
'@storybook/jest':
specifier: 0.2.2
version: 0.2.2(vitest@0.34.4)
version: 0.2.2(vitest@0.34.5)
'@storybook/manager-api':
specifier: 7.4.3
version: 7.4.3(react-dom@18.2.0)(react@18.2.0)
@ -919,8 +919,8 @@ importers:
specifier: 6.7.2
version: 6.7.2(eslint@8.49.0)(typescript@5.2.2)
'@vitest/coverage-v8':
specifier: 0.34.4
version: 0.34.4(vitest@0.34.4)
specifier: 0.34.5
version: 0.34.5(vitest@0.34.5)
'@vue/runtime-core':
specifier: 3.3.4
version: 3.3.4
@ -970,11 +970,11 @@ importers:
specifier: 18.2.0
version: 18.2.0(react@18.2.0)
start-server-and-test:
specifier: 2.0.0
version: 2.0.0
specifier: 2.0.1
version: 2.0.1
storybook:
specifier: 7.4.2
version: 7.4.2
specifier: 7.4.3
version: 7.4.3
storybook-addon-misskey-theme:
specifier: github:misskey-dev/storybook-addon-misskey-theme
version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.4.3)(@storybook/components@7.4.3)(@storybook/core-events@7.4.3)(@storybook/manager-api@7.4.3)(@storybook/preview-api@7.4.3)(@storybook/theming@7.4.3)(@storybook/types@7.4.3)(react-dom@18.2.0)(react@18.2.0)
@ -985,26 +985,26 @@ importers:
specifier: 1.0.3
version: 1.0.3
vitest:
specifier: 0.34.4
version: 0.34.4(happy-dom@10.0.3)(sass@1.68.0)(terser@5.20.0)
specifier: 0.34.5
version: 0.34.5(happy-dom@10.0.3)(sass@1.68.0)(terser@5.20.0)
vitest-fetch-mock:
specifier: 0.2.2
version: 0.2.2(vitest@0.34.4)
version: 0.2.2(vitest@0.34.5)
vue-eslint-parser:
specifier: 9.3.1
version: 9.3.1(eslint@8.49.0)
vue-tsc:
specifier: 1.8.11
version: 1.8.11(typescript@5.2.2)
specifier: 1.8.13
version: 1.8.13(typescript@5.2.2)
packages/misskey-js:
dependencies:
'@swc/cli':
specifier: 0.1.62
version: 0.1.62(@swc/core@1.3.86)(chokidar@3.5.3)
version: 0.1.62(@swc/core@1.3.87)(chokidar@3.5.3)
'@swc/core':
specifier: 1.3.86
version: 1.3.86
specifier: 1.3.87
version: 1.3.87
eventemitter3:
specifier: 5.0.1
version: 5.0.1
@ -1017,7 +1017,7 @@ importers:
version: 7.37.0(@types/node@20.6.3)
'@swc/jest':
specifier: 0.2.29
version: 0.2.29(@swc/core@1.3.86)
version: 0.2.29(@swc/core@1.3.87)
'@types/jest':
specifier: 29.5.5
version: 29.5.5
@ -6218,30 +6218,6 @@ packages:
- supports-color
dev: true
/@storybook/builder-manager@7.4.2:
resolution: {integrity: sha512-MgdXr9QJ2sNk0fUshQ7hk4Ec9IkbPWR6alrmDByIOEU9bThx0j4OxU9uTLBy8r5uZsSL6nNtRyCvSP8YSKaQHQ==}
dependencies:
'@fal-works/esbuild-plugin-global-externals': 2.1.2
'@storybook/core-common': 7.4.2
'@storybook/manager': 7.4.2
'@storybook/node-logger': 7.4.2
'@types/ejs': 3.1.2
'@types/find-cache-dir': 3.2.1
'@yarnpkg/esbuild-plugin-pnp': 3.0.0-rc.15(esbuild@0.18.17)
browser-assert: 1.2.1
ejs: 3.1.8
esbuild: 0.18.17
esbuild-plugin-alias: 0.2.1
express: 4.18.2
find-cache-dir: 3.3.2
fs-extra: 11.1.1
process: 0.11.10
util: 0.12.5
transitivePeerDependencies:
- encoding
- supports-color
dev: true
/@storybook/builder-manager@7.4.3:
resolution: {integrity: sha512-6jzxZ2J1jFaZXn7ZucEgV6XyUe+FJ9uuoMRZcZefoCKeXK/BOPCefijYWP3DPgqqVh3/JLUglIpz0MH9k8cBaw==}
dependencies:
@ -6307,17 +6283,6 @@ packages:
- supports-color
dev: true
/@storybook/channels@7.4.2:
resolution: {integrity: sha512-Q95KnV+fTGaAV3S875+d5LlGg+bdC3bUnki3engODDS4ViSRHJ1bnXnqxKmAaS3O/52geIyWWR766YvwHw3avw==}
dependencies:
'@storybook/client-logger': 7.4.2
'@storybook/core-events': 7.4.2
'@storybook/global': 5.0.0
qs: 6.11.1
telejson: 7.2.0
tiny-invariant: 1.3.1
dev: true
/@storybook/channels@7.4.3:
resolution: {integrity: sha512-lIoRX3EV0wKPX8ojIrJUtsOv4+Gv8r9pfJpam/NdyYd+rs0AjDK13ieINRfBMnJkfjsWa3vmZtGMBEVvDKwTMw==}
dependencies:
@ -6329,22 +6294,22 @@ packages:
tiny-invariant: 1.3.1
dev: true
/@storybook/cli@7.4.2:
resolution: {integrity: sha512-WleObtC7OU2lT+pI2vTdXZPFMKDGbg3bkUJ+PG8+yqGg53ea5ZkwKWg9qHpXuiMkYDztqhbA8kYrny1GqFuVdg==}
/@storybook/cli@7.4.3:
resolution: {integrity: sha512-/lGtXbzNropsCF4srEGxiHzCU7b2wlV13LrSj3H3zOnHEAJlFcNpyNzO+4jKHfNTjjqEtcRGJ1OxrSYuGZTVjg==}
hasBin: true
dependencies:
'@babel/core': 7.22.11
'@babel/preset-env': 7.22.9(@babel/core@7.22.11)
'@babel/types': 7.22.17
'@ndelangen/get-tarball': 3.0.7
'@storybook/codemod': 7.4.2
'@storybook/core-common': 7.4.2
'@storybook/core-events': 7.4.2
'@storybook/core-server': 7.4.2
'@storybook/csf-tools': 7.4.2
'@storybook/node-logger': 7.4.2
'@storybook/telemetry': 7.4.2
'@storybook/types': 7.4.2
'@storybook/codemod': 7.4.3
'@storybook/core-common': 7.4.3
'@storybook/core-events': 7.4.3
'@storybook/core-server': 7.4.3
'@storybook/csf-tools': 7.4.3
'@storybook/node-logger': 7.4.3
'@storybook/telemetry': 7.4.3
'@storybook/types': 7.4.3
'@types/semver': 7.5.2
'@yarnpkg/fslib': 2.10.3
'@yarnpkg/libzip': 2.3.0
@ -6381,28 +6346,22 @@ packages:
- utf-8-validate
dev: true
/@storybook/client-logger@7.4.2:
resolution: {integrity: sha512-LC8tYrYSJwF4DHRdNYh6y8hSvccwUIv5/WOZKJDmKx7mcEm6HsVuUu16C9jsl7iy6IqJYxgVz1va3WS6852E+A==}
dependencies:
'@storybook/global': 5.0.0
dev: true
/@storybook/client-logger@7.4.3:
resolution: {integrity: sha512-Nhngo9X4HjN00aRhgIVGWbwkWPe0Fz8PySuxnd8nAxSsz7KpdLFyYo2TbZZ3TX51FG5Fxcb0G5OHuunItP7EWQ==}
dependencies:
'@storybook/global': 5.0.0
dev: true
/@storybook/codemod@7.4.2:
resolution: {integrity: sha512-wU+SLHG/PpLptI0aWEhPxwFPcX7uYe+Id21DKNPg/HvYaLG3N+/DPDef+lm3Vaov9w4OD74iuQ3knT67SSkvmw==}
/@storybook/codemod@7.4.3:
resolution: {integrity: sha512-UwnsyVeUa+wLIeE/zO0slV3mwsPgS3DstZAWbjWUfFlJKZjgg1++Zkv0GmxkEyirsnf/g4r6Aq+KhIdIHmdzag==}
dependencies:
'@babel/core': 7.22.11
'@babel/preset-env': 7.22.9(@babel/core@7.22.11)
'@babel/types': 7.22.17
'@storybook/csf': 0.1.0
'@storybook/csf-tools': 7.4.2
'@storybook/node-logger': 7.4.2
'@storybook/types': 7.4.2
'@storybook/csf-tools': 7.4.3
'@storybook/node-logger': 7.4.3
'@storybook/types': 7.4.3
'@types/cross-spawn': 6.0.2
cross-spawn: 7.0.3
globby: 11.1.0
@ -6444,37 +6403,6 @@ packages:
'@storybook/preview-api': 7.4.3
dev: true
/@storybook/core-common@7.4.2:
resolution: {integrity: sha512-Qj9S97TYO+jSNdC2+LrMFtZRvTnELeFnRtn/MDWhkM6mpZgRglxlZuXi5enJjqTh0dISAUxPpTtXNAJDfX99JA==}
dependencies:
'@storybook/core-events': 7.4.2
'@storybook/node-logger': 7.4.2
'@storybook/types': 7.4.2
'@types/find-cache-dir': 3.2.1
'@types/node': 16.18.46
'@types/node-fetch': 2.6.4
'@types/pretty-hrtime': 1.0.1
chalk: 4.1.2
esbuild: 0.18.17
esbuild-register: 3.4.2(esbuild@0.18.17)
file-system-cache: 2.3.0
find-cache-dir: 3.3.2
find-up: 5.0.0
fs-extra: 11.1.1
glob: 10.3.0
handlebars: 4.7.7
lazy-universal-dotenv: 4.0.0
node-fetch: 2.7.0
picomatch: 2.3.1
pkg-dir: 5.0.0
pretty-hrtime: 1.0.3
resolve-from: 5.0.0
ts-dedent: 2.2.0
transitivePeerDependencies:
- encoding
- supports-color
dev: true
/@storybook/core-common@7.4.3:
resolution: {integrity: sha512-jwIBUnWitZzw0VfKC77yN8DvTyePLVnAjbA2lPMbMIdO9ZY2lfD4AQ4QpuWsxJyAllFC4slOFDNgCDHx2AlYWw==}
dependencies:
@ -6506,70 +6434,12 @@ packages:
- supports-color
dev: true
/@storybook/core-events@7.4.2:
resolution: {integrity: sha512-WCEBw+Ew8DrccnB0hpP9TXadreoOlMnWCyuXU2XrvmK/vde009leWQIsLs1rY+L17zDVuogBms62AxrDDJmMpw==}
dependencies:
ts-dedent: 2.2.0
dev: true
/@storybook/core-events@7.4.3:
resolution: {integrity: sha512-FRfipCijMnVbGxL1ZjOLM836lyd/TGQcUFeVjTQWW/+pIGHELqDHiYeq68hqoGTKl0G0np59CJPWYTUZA4Dl9Q==}
dependencies:
ts-dedent: 2.2.0
dev: true
/@storybook/core-server@7.4.2:
resolution: {integrity: sha512-4aaFQTjb8jsbzJeCs+VTo3gdyK3r3VhQN2sxn6k/lcKjQFeO84+iqGgGmb+oWUVz2TJL+JrNh7SUXkVsMZBXVQ==}
dependencies:
'@aw-web-design/x-default-browser': 1.4.126
'@discoveryjs/json-ext': 0.5.7
'@storybook/builder-manager': 7.4.2
'@storybook/channels': 7.4.2
'@storybook/core-common': 7.4.2
'@storybook/core-events': 7.4.2
'@storybook/csf': 0.1.0
'@storybook/csf-tools': 7.4.2
'@storybook/docs-mdx': 0.1.0
'@storybook/global': 5.0.0
'@storybook/manager': 7.4.2
'@storybook/node-logger': 7.4.2
'@storybook/preview-api': 7.4.2
'@storybook/telemetry': 7.4.2
'@storybook/types': 7.4.2
'@types/detect-port': 1.3.2
'@types/node': 16.18.46
'@types/pretty-hrtime': 1.0.1
'@types/semver': 7.5.2
better-opn: 3.0.2
chalk: 4.1.2
cli-table3: 0.6.3
compression: 1.7.4
detect-port: 1.5.1
express: 4.18.2
fs-extra: 11.1.1
globby: 11.1.0
ip: 2.0.0
lodash: 4.17.21
open: 8.4.2
pretty-hrtime: 1.0.3
prompts: 2.4.2
read-pkg-up: 7.0.1
semver: 7.5.4
serve-favicon: 2.5.0
telejson: 7.2.0
tiny-invariant: 1.3.1
ts-dedent: 2.2.0
util: 0.12.5
util-deprecate: 1.0.2
watchpack: 2.4.0
ws: 8.14.2(bufferutil@4.0.7)(utf-8-validate@6.0.3)
transitivePeerDependencies:
- bufferutil
- encoding
- supports-color
- utf-8-validate
dev: true
/@storybook/core-server@7.4.3:
resolution: {integrity: sha512-yl9HaVwk/xJV9zq76n/oR1cE39wAFmNmKVPOJAtr3+c7wS0tnBkw7T+GqZ2Seyv+xkcZUWS8KRH74HqwPwG0Bw==}
dependencies:
@ -6631,22 +6501,6 @@ packages:
- supports-color
dev: true
/@storybook/csf-tools@7.4.2:
resolution: {integrity: sha512-5AvF2YRcYHIqQqskb3R8JvsmSWnNwkP0CGmP8Zq7zIfK/C+npKb/onv5YQlbSgh+2UrVxVdIDLc9AepBeXC3uQ==}
dependencies:
'@babel/generator': 7.22.10
'@babel/parser': 7.22.16
'@babel/traverse': 7.22.11
'@babel/types': 7.22.17
'@storybook/csf': 0.1.0
'@storybook/types': 7.4.2
fs-extra: 11.1.1
recast: 0.23.1
ts-dedent: 2.2.0
transitivePeerDependencies:
- supports-color
dev: true
/@storybook/csf-tools@7.4.3:
resolution: {integrity: sha512-nkVakGx2kzou91lGcxnyFNiSEdnpx1a53lQTl/DLm0QpDbqQuu3ZbZWXZCpXV97t/6YPeCCnGLXodnI7PZyZBA==}
dependencies:
@ -6707,11 +6561,11 @@ packages:
'@storybook/preview-api': 7.4.3
dev: true
/@storybook/jest@0.2.2(vitest@0.34.4):
/@storybook/jest@0.2.2(vitest@0.34.5):
resolution: {integrity: sha512-PUfp9WoqUA8NdAmiz3UahUsyAMr+g1Dv3BB0fqJZsE2IuE5o1Mgsv4iLGzFm+ohcQLIDQvwvvbQIpxe8eY7TNw==}
dependencies:
'@storybook/expect': 28.1.3-5
'@testing-library/jest-dom': 6.1.2(@types/jest@28.1.3)(vitest@0.34.4)
'@testing-library/jest-dom': 6.1.2(@types/jest@28.1.3)(vitest@0.34.5)
'@types/jest': 28.1.3
jest-mock: 27.5.1
transitivePeerDependencies:
@ -6745,10 +6599,6 @@ packages:
ts-dedent: 2.2.0
dev: true
/@storybook/manager@7.4.2:
resolution: {integrity: sha512-MtjmbAaf4hUObAa2ETQkm0+SzESoPeNm+TyzwZU5qq3Ouj4IOj2Ugd8EJPO3isdHxYt26A255tW/G9mb9v20fQ==}
dev: true
/@storybook/manager@7.4.3:
resolution: {integrity: sha512-7U92tYwjt0DIKX7vCKNSZefuEavdnJYa5/zSjdlo0LtfBmGRBak1eq/sVLGfzrZ+wKIlCXgNh3f8OLy8RMnOOw==}
dev: true
@ -6757,10 +6607,6 @@ packages:
resolution: {integrity: sha512-dBAnEL4HfxxJmv7LdEYUoZlQbWj9APZNIbOaq0tgF8XkxiIbzqvgB0jhL/9UOrysSDbQWBiCRTu2wOVxedGfmw==}
dev: true
/@storybook/node-logger@7.4.2:
resolution: {integrity: sha512-iSBjhnMpWY9Hs5KGnf/xfHjGtGl740LUg4Gce874DuL773Mdc4hdppSKr4X/Pp1/AD67mNuifSXYx3V7d6XzTQ==}
dev: true
/@storybook/node-logger@7.4.3:
resolution: {integrity: sha512-pL13PPMUttflTWKVeDIKxPIJtBRl50Fzck12/7uiNROtBIrSV9DZSgOjInAazjo4tl+7fDj9lgkGeMEz00E8aQ==}
dev: true
@ -6769,25 +6615,6 @@ packages:
resolution: {integrity: sha512-6NMaAvL4a26jR50UPz+Q6VATY3lHZWw1ru/weFgiV0rat632RFdiFyrMMrjbUWu9HDJE4fbCzrIZU0jGVs1wlQ==}
dev: true
/@storybook/preview-api@7.4.2:
resolution: {integrity: sha512-ihTHRYzI/sI6bD215aYppiWF+1u38TrlsNjFYJ/Grftbti5d40g5wCwvAXK41SxJNYpk6CRtfvNKOwbEAC33gg==}
dependencies:
'@storybook/channels': 7.4.2
'@storybook/client-logger': 7.4.2
'@storybook/core-events': 7.4.2
'@storybook/csf': 0.1.0
'@storybook/global': 5.0.0
'@storybook/types': 7.4.2
'@types/qs': 6.9.7
dequal: 2.0.3
lodash: 4.17.21
memoizerific: 1.11.3
qs: 6.11.1
synchronous-promise: 2.0.17
ts-dedent: 2.2.0
util-deprecate: 1.0.2
dev: true
/@storybook/preview-api@7.4.3:
resolution: {integrity: sha512-qKwfH2+qN1Zpz2UX6dQLiTU5x2JH3o/+jOY4GYF6c3atTm5WAu1OvCYAJVb6MdXfAhZNuPwDKnJR8VmzWplWBg==}
dependencies:
@ -6917,22 +6744,6 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: true
/@storybook/telemetry@7.4.2:
resolution: {integrity: sha512-ZAh1Bjk9JVpL5j0Aj3PHr3XEDZcOrFeugVyRuuul2gAyu6SbpPhl8Dd2Wr9YJS0ZDWs3u0CgKRCeFPPAi3QosA==}
dependencies:
'@storybook/client-logger': 7.4.2
'@storybook/core-common': 7.4.2
'@storybook/csf-tools': 7.4.2
chalk: 4.1.2
detect-package-manager: 2.0.1
fetch-retry: 5.0.4
fs-extra: 11.1.1
read-pkg-up: 7.0.1
transitivePeerDependencies:
- encoding
- supports-color
dev: true
/@storybook/telemetry@7.4.3:
resolution: {integrity: sha512-gA7QfQSdDocNKP0KfrmIhD8ZgW5G4zZD/NL0OsATlkL3H/DehH3Ugjfffh7Ao2JZRXogHp8p9EQCVfPW7iKgBQ==}
dependencies:
@ -6971,15 +6782,6 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: true
/@storybook/types@7.4.2:
resolution: {integrity: sha512-OOJ2TeS3Zzc6spHbdH+JXml0q4IHuYt9axmXAv1/pkhqHjA5072pyUacmlYNQeihpQOOsKLiCQUQlvtMy9fTnQ==}
dependencies:
'@storybook/channels': 7.4.2
'@types/babel__core': 7.20.0
'@types/express': 4.17.17
file-system-cache: 2.3.0
dev: true
/@storybook/types@7.4.3:
resolution: {integrity: sha512-DrHC1hIiw9TqDILLokDnvbUPNxGz5iJaYFEv30uvYE0s9MvgEUPblCChEUjaHOps7zQTznMPf8ULfoXlgqxk2A==}
dependencies:
@ -7041,7 +6843,7 @@ packages:
- supports-color
dev: true
/@swc/cli@0.1.62(@swc/core@1.3.86)(chokidar@3.5.3):
/@swc/cli@0.1.62(@swc/core@1.3.87)(chokidar@3.5.3):
resolution: {integrity: sha512-kOFLjKY3XH1DWLfXL1/B5MizeNorHR8wHKEi92S/Zi9Md/AK17KSqR8MgyRJ6C1fhKHvbBCl8wboyKAFXStkYw==}
engines: {node: '>= 12.13'}
hasBin: true
@ -7053,7 +6855,7 @@ packages:
optional: true
dependencies:
'@mole-inc/bin-wrapper': 8.0.1
'@swc/core': 1.3.86
'@swc/core': 1.3.87
chokidar: 3.5.3
commander: 7.2.0
fast-glob: 3.3.1
@ -7082,8 +6884,8 @@ packages:
dev: false
optional: true
/@swc/core-darwin-arm64@1.3.86:
resolution: {integrity: sha512-hMvSDms0sJJHNtRa3Vhmr9StWN1vmikbf5VE0IZUYGnF1/JZTkXU1h6CdNUY4Hr6i7uCZjH6BEhxFHX1JtKV4w==}
/@swc/core-darwin-arm64@1.3.87:
resolution: {integrity: sha512-/LxLjPat1LA9CXS7Cn2M4MIqwNOoDF4KjcikPkO08H54rd6WubhaJnr0sLDjms3adRr+pmcCL0yfsUBTX//85A==}
engines: {node: '>=10'}
cpu: [arm64]
os: [darwin]
@ -7099,8 +6901,8 @@ packages:
dev: false
optional: true
/@swc/core-darwin-x64@1.3.86:
resolution: {integrity: sha512-Jro6HVH4uSOBM7tTDaQNKLNc8BJV7n+SO+Ft2HAZINyeKJS/8MfEYneG7Vmqg18gv00c6dz9AOCcyz+BR7BFkQ==}
/@swc/core-darwin-x64@1.3.87:
resolution: {integrity: sha512-hjSQNcW9BN8gEz3UQZ7Ye80ymbkFHLkUDeEek4lorRyq6S+uxvbL1f1mJAZnFPBpove7AXusykIalWMPvyOR2A==}
engines: {node: '>=10'}
cpu: [x64]
os: [darwin]
@ -7127,8 +6929,8 @@ packages:
dev: false
optional: true
/@swc/core-linux-arm-gnueabihf@1.3.86:
resolution: {integrity: sha512-wYB9m0pzXJVSzedXSl4JwS3gKtvcPinpe9MbkddezpqL7OjyDP6pHHW9qIucsfgCrtMtbPC2nqulXLPtAAyIjw==}
/@swc/core-linux-arm-gnueabihf@1.3.87:
resolution: {integrity: sha512-JVyNIO3tGLPSQ59rJXeKaykTpPhRNozB+7PtYMvMcxpUbYGpEzWxTPkFAX2KKPvl0ejBdA0GW5OXeuPMvTwE0w==}
engines: {node: '>=10'}
cpu: [arm]
os: [linux]
@ -7144,8 +6946,8 @@ packages:
dev: false
optional: true
/@swc/core-linux-arm64-gnu@1.3.86:
resolution: {integrity: sha512-fR44IyK5cdCaO8cC++IEH0Jn03tWnunJnjzA99LxlE5TRInSIOvFm+g5OSUQZDAvEXmQ38sd31LO2HOoDS1Edw==}
/@swc/core-linux-arm64-gnu@1.3.87:
resolution: {integrity: sha512-gLdZKIoql5vjrNjrwwsiS7d3vOAIzYUWqN97iGCSscQOg0MgYbfUnSTO4UEvH4BYlwRNlHepfTZ7ALoG8areUQ==}
engines: {node: '>=10'}
cpu: [arm64]
os: [linux]
@ -7161,8 +6963,8 @@ packages:
dev: false
optional: true
/@swc/core-linux-arm64-musl@1.3.86:
resolution: {integrity: sha512-EUPfdbK4dUk/nkX3Vmv/47XH+DqHOa9JI0CTthvJ8/ZXei1MKDUsUc+tI1zMQX2uCuSkSWsEIEpCmA0tMwFhtw==}
/@swc/core-linux-arm64-musl@1.3.87:
resolution: {integrity: sha512-WQ5tirVBiU8lUODQ25dt8JRCZHyRDInBe4fkGuxzImMa017zYPWa2WxrKK8LdDF7DzrAITlGl9VeoeE/l0WJbw==}
engines: {node: '>=10'}
cpu: [arm64]
os: [linux]
@ -7178,8 +6980,8 @@ packages:
dev: false
optional: true
/@swc/core-linux-x64-gnu@1.3.86:
resolution: {integrity: sha512-snVZZWv8XgNVaKrTxtO3rUN+BbbB6I8Fqwe8zM/DWGJ096J13r89doQ48x5ZyO+bW4D48eZIWP5pdfSW7oBE3w==}
/@swc/core-linux-x64-gnu@1.3.87:
resolution: {integrity: sha512-/vQSH7ZKOuT1It9GzpJ9UFnsOP/dQr1VLUrKQFBlHp9owIWNb2oUrZdNla+KhljCIIahh0JfQ08sycKeycCNzQ==}
engines: {node: '>=10'}
cpu: [x64]
os: [linux]
@ -7195,8 +6997,8 @@ packages:
dev: false
optional: true
/@swc/core-linux-x64-musl@1.3.86:
resolution: {integrity: sha512-PnnksUJymEJkdnbV2orOSOSB441UqsxYbJge9zbr5UTRXUfWO3eFRV0iTBegjTlOQGbW6yN+YRSDkenTbmCI6g==}
/@swc/core-linux-x64-musl@1.3.87:
resolution: {integrity: sha512-C1NUeISJDyMlIk4919bjcpHvjyjzbkjW7v53gUdN41Y4BPlEk7UKcLez7UHMjdMGA/o9721SLqYVp4/NrQErUw==}
engines: {node: '>=10'}
cpu: [x64]
os: [linux]
@ -7212,8 +7014,8 @@ packages:
dev: false
optional: true
/@swc/core-win32-arm64-msvc@1.3.86:
resolution: {integrity: sha512-XlGEGyHwLndm08VvgeAPGj40L+Hx575MQC+2fsyB1uSNUN+uf7fvke+wc7k50a92CaQe/8foLyIR5faayozEJA==}
/@swc/core-win32-arm64-msvc@1.3.87:
resolution: {integrity: sha512-AE7JKDJ0OsV9LsYGFfYKMTkGNfsy1au4RT5jT1rxr5MTOsmMD7P2mgiRF8drgc1WX3uOJbTHQfgdVTYroAGfdA==}
engines: {node: '>=10'}
cpu: [arm64]
os: [win32]
@ -7229,8 +7031,8 @@ packages:
dev: false
optional: true
/@swc/core-win32-ia32-msvc@1.3.86:
resolution: {integrity: sha512-U1BhZa1x9yn+wZGTQmt1cYR79a0FzW/wL6Jas1Pn0bykKLxdRU4mCeZt2P+T3buLm8jr8LpPWiCrbvr658PzwA==}
/@swc/core-win32-ia32-msvc@1.3.87:
resolution: {integrity: sha512-2V+5uvisaTPXd5lvTujNLNlEC2LPo07gEUQVGdKGsbhtLAYAggVXBnHjxU1TkuyA6NlciMS59tPKW+L2u2KpTw==}
engines: {node: '>=10'}
cpu: [ia32]
os: [win32]
@ -7246,16 +7048,16 @@ packages:
dev: false
optional: true
/@swc/core-win32-x64-msvc@1.3.86:
resolution: {integrity: sha512-wRoQUajqpE3wITHhZVj/6BPu/QwHriFHLHuJA+9y6PeGtUtTmntL42aBKXIFhfL767dYFtohyNg1uZ9eqbGyGQ==}
/@swc/core-win32-x64-msvc@1.3.87:
resolution: {integrity: sha512-2Xak7TidlRuNQamLZC3fEOdUCmMiBzD2BW8+Dnn29f4odzamgAFfeYJ/PnqN7jdTWOINLn95tex4JBm3Pm11HQ==}
engines: {node: '>=10'}
cpu: [x64]
os: [win32]
requiresBuild: true
optional: true
/@swc/core@1.3.86:
resolution: {integrity: sha512-bEXUtm37bcmJ3q+geG7Zy4rJNUzpxalXQUrrqX1ZoGj3HRtzdeVZ0L/um3fG2j16qe61t8TX/OIZ2G6j6dkG/w==}
/@swc/core@1.3.87:
resolution: {integrity: sha512-u33Mi/EBvb+g/xpYKyxODB5XvKYqISmy81J+lhFS/Oahja0PbJWZdKEGwSQEFvBecp6E+PfaTOLPOoF1EWcRrw==}
engines: {node: '>=10'}
requiresBuild: true
peerDependencies:
@ -7266,25 +7068,25 @@ packages:
dependencies:
'@swc/types': 0.1.4
optionalDependencies:
'@swc/core-darwin-arm64': 1.3.86
'@swc/core-darwin-x64': 1.3.86
'@swc/core-linux-arm-gnueabihf': 1.3.86
'@swc/core-linux-arm64-gnu': 1.3.86
'@swc/core-linux-arm64-musl': 1.3.86
'@swc/core-linux-x64-gnu': 1.3.86
'@swc/core-linux-x64-musl': 1.3.86
'@swc/core-win32-arm64-msvc': 1.3.86
'@swc/core-win32-ia32-msvc': 1.3.86
'@swc/core-win32-x64-msvc': 1.3.86
'@swc/core-darwin-arm64': 1.3.87
'@swc/core-darwin-x64': 1.3.87
'@swc/core-linux-arm-gnueabihf': 1.3.87
'@swc/core-linux-arm64-gnu': 1.3.87
'@swc/core-linux-arm64-musl': 1.3.87
'@swc/core-linux-x64-gnu': 1.3.87
'@swc/core-linux-x64-musl': 1.3.87
'@swc/core-win32-arm64-msvc': 1.3.87
'@swc/core-win32-ia32-msvc': 1.3.87
'@swc/core-win32-x64-msvc': 1.3.87
/@swc/jest@0.2.29(@swc/core@1.3.86):
/@swc/jest@0.2.29(@swc/core@1.3.87):
resolution: {integrity: sha512-8reh5RvHBsSikDC3WGCd5ZTd2BXKkyOdK7QwynrCH58jk2cQFhhHhFBg/jvnWZehUQe/EoOImLENc9/DwbBFow==}
engines: {npm: '>= 7.0.0'}
peerDependencies:
'@swc/core': '*'
dependencies:
'@jest/create-cache-key-function': 27.5.1
'@swc/core': 1.3.86
'@swc/core': 1.3.87
jsonc-parser: 3.2.0
dev: true
@ -7461,7 +7263,7 @@ packages:
pretty-format: 27.5.1
dev: true
/@testing-library/jest-dom@6.1.2(@types/jest@28.1.3)(vitest@0.34.4):
/@testing-library/jest-dom@6.1.2(@types/jest@28.1.3)(vitest@0.34.5):
resolution: {integrity: sha512-NP9jl1Q2qDDtx+cqogowtQtmgD2OVs37iMSIsTv5eN5ETRkf26Kj6ugVwA93/gZzzFWQAsgkKkcftDe91BJCkQ==}
engines: {node: '>=14', npm: '>=6', yarn: '>=1'}
peerDependencies:
@ -7488,7 +7290,7 @@ packages:
dom-accessibility-api: 0.5.16
lodash: 4.17.21
redent: 3.0.0
vitest: 0.34.4(happy-dom@10.0.3)(sass@1.68.0)(terser@5.20.0)
vitest: 0.34.5(happy-dom@10.0.3)(sass@1.68.0)(terser@5.20.0)
dev: true
/@testing-library/user-event@14.4.3(@testing-library/dom@9.2.0):
@ -8301,8 +8103,8 @@ packages:
vite: 4.4.9(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0)
vue: 3.3.4
/@vitest/coverage-v8@0.34.4(vitest@0.34.4):
resolution: {integrity: sha512-TZ5ghzhmg3COQqfBShL+zRQEInHmV9TSwghTdfkHpCTyTOr+rxo6x41vCNcVfWysWULtqtBVpY6YFNovxnESfA==}
/@vitest/coverage-v8@0.34.5(vitest@0.34.5):
resolution: {integrity: sha512-97xjhRTSdmeeHCm2nNHhT3hLsMYkAhHXm/rwj6SZ3voka8xiCJrwgtfIjoZIFEL4OO0KezGmVuHWQXcMunULIA==}
peerDependencies:
vitest: '>=0.32.0 <1'
dependencies:
@ -8317,43 +8119,43 @@ packages:
std-env: 3.3.3
test-exclude: 6.0.0
v8-to-istanbul: 9.1.0
vitest: 0.34.4(happy-dom@10.0.3)(sass@1.68.0)(terser@5.20.0)
vitest: 0.34.5(happy-dom@10.0.3)(sass@1.68.0)(terser@5.20.0)
transitivePeerDependencies:
- supports-color
dev: true
/@vitest/expect@0.34.4:
resolution: {integrity: sha512-XlMKX8HyYUqB8dsY8Xxrc64J2Qs9pKMt2Z8vFTL4mBWXJsg4yoALHzJfDWi8h5nkO4Zua4zjqtapQ/IluVkSnA==}
/@vitest/expect@0.34.5:
resolution: {integrity: sha512-/3RBIV9XEH+nRpRMqDJBufKIOQaYUH2X6bt0rKSCW0MfKhXFLYsR5ivHifeajRSTsln0FwJbitxLKHSQz/Xwkw==}
dependencies:
'@vitest/spy': 0.34.4
'@vitest/utils': 0.34.4
'@vitest/spy': 0.34.5
'@vitest/utils': 0.34.5
chai: 4.3.7
dev: true
/@vitest/runner@0.34.4:
resolution: {integrity: sha512-hwwdB1StERqUls8oV8YcpmTIpVeJMe4WgYuDongVzixl5hlYLT2G8afhcdADeDeqCaAmZcSgLTLtqkjPQF7x+w==}
/@vitest/runner@0.34.5:
resolution: {integrity: sha512-RDEE3ViVvl7jFSCbnBRyYuu23XxmvRTSZWW6W4M7eC5dOsK75d5LIf6uhE5Fqf809DQ1+9ICZZNxhIolWHU4og==}
dependencies:
'@vitest/utils': 0.34.4
'@vitest/utils': 0.34.5
p-limit: 4.0.0
pathe: 1.1.1
dev: true
/@vitest/snapshot@0.34.4:
resolution: {integrity: sha512-GCsh4coc3YUSL/o+BPUo7lHQbzpdttTxL6f4q0jRx2qVGoYz/cyTRDJHbnwks6TILi6560bVWoBpYC10PuTLHw==}
/@vitest/snapshot@0.34.5:
resolution: {integrity: sha512-+ikwSbhu6z2yOdtKmk/aeoDZ9QPm2g/ZO5rXT58RR9Vmu/kB2MamyDSx77dctqdZfP3Diqv4mbc/yw2kPT8rmA==}
dependencies:
magic-string: 0.30.3
pathe: 1.1.1
pretty-format: 29.7.0
dev: true
/@vitest/spy@0.34.4:
resolution: {integrity: sha512-PNU+fd7DUPgA3Ya924b1qKuQkonAW6hL7YUjkON3wmBwSTIlhOSpy04SJ0NrRsEbrXgMMj6Morh04BMf8k+w0g==}
/@vitest/spy@0.34.5:
resolution: {integrity: sha512-epsicsfhvBjRjCMOC/3k00mP/TBGQy8/P0DxOFiWyLt55gnZ99dqCfCiAsKO17BWVjn4eZRIjKvcqNmSz8gvmg==}
dependencies:
tinyspy: 2.1.1
dev: true
/@vitest/utils@0.34.4:
resolution: {integrity: sha512-yR2+5CHhp/K4ySY0Qtd+CAL9f5Yh1aXrKfAT42bq6CtlGPh92jIDDDSg7ydlRow1CP+dys4TrOrbELOyNInHSg==}
/@vitest/utils@0.34.5:
resolution: {integrity: sha512-ur6CmmYQoeHMwmGb0v+qwkwN3yopZuZyf4xt1DBBSGBed8Hf9Gmbm/5dEWqgpLPdRx6Av6jcWXrjcKfkTzg/pw==}
dependencies:
diff-sequences: 29.6.3
loupe: 2.3.6
@ -8449,8 +8251,8 @@ packages:
'@vue/compiler-dom': 3.3.4
'@vue/shared': 3.3.4
/@vue/language-core@1.8.11(typescript@5.2.2):
resolution: {integrity: sha512-+MZOBGqGwfld6hpo0DB47x8eNM0dNqk15ZdfOhj19CpvuYuOWCeVdOEGZunKDyo3QLkTn3kLOSysJzg7FDOQBA==}
/@vue/language-core@1.8.13(typescript@5.2.2):
resolution: {integrity: sha512-nata2fYBZAkl4QJrU+IcArJCMTHt1VP8ePL/Z7eUPC2AF+Cm7Qgo9ksNCPBzZRh1LYjCaSaqV7njqNogwpsMVg==}
peerDependencies:
typescript: '*'
peerDependenciesMeta:
@ -8519,11 +8321,11 @@ packages:
'@vue/server-renderer': 3.3.4(vue@3.3.4)
dev: true
/@vue/typescript@1.8.11(typescript@5.2.2):
resolution: {integrity: sha512-skUmMDiPUUtu1flPmf2YybF+PX8IzBtMioQOaNn6Ck/RhdrPJGj1AX/7s3Buf9G6ln+/KHR1XQuti/FFxw5XVA==}
/@vue/typescript@1.8.13(typescript@5.2.2):
resolution: {integrity: sha512-ALJjHFqQ3dgZVCI/ogAS/dZ7JEhIi1N0Em5I7uwabY1p9RDRK3odLsycMHyxZRjm5dLI15c07eeBloHiD2Otlg==}
dependencies:
'@volar/typescript': 1.10.0
'@vue/language-core': 1.8.11(typescript@5.2.2)
'@vue/language-core': 1.8.13(typescript@5.2.2)
transitivePeerDependencies:
- typescript
dev: true
@ -9449,8 +9251,8 @@ packages:
dependencies:
node-gyp-build: 4.6.0
/bullmq@4.11.2:
resolution: {integrity: sha512-ffDw8B2+N8x+HVRTb9/nEngb3ubEYb8vdKKTJfUiUoPWPUcHzCkylSVWWf7dcr6KjzKElaCWEjVsKzHjmyhmnw==}
/bullmq@4.11.3:
resolution: {integrity: sha512-Z5zmgK2gJ+FIFhPBHpB5zRGGD2/89MMm/qIM3SMrt1fgP7RCy7y4qGrQVj7OmuNr43y8UG0UtQlYVADpSZAVTA==}
dependencies:
cron-parser: 4.8.1
glob: 8.1.0
@ -10852,14 +10654,6 @@ packages:
/ee-first@1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
/ejs@3.1.8:
resolution: {integrity: sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==}
engines: {node: '>=0.10.0'}
hasBin: true
dependencies:
jake: 10.8.5
dev: true
/ejs@3.1.9:
resolution: {integrity: sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==}
engines: {node: '>=0.10.0'}
@ -17942,9 +17736,9 @@ packages:
resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==}
dev: false
/start-server-and-test@2.0.0:
resolution: {integrity: sha512-UqKLw0mJbfrsG1jcRLTUlvuRi9sjNuUiDOLI42r7R5fA9dsFoywAy9DoLXNYys9B886E4RCKb+qM1Gzu96h7DQ==}
engines: {node: '>=6'}
/start-server-and-test@2.0.1:
resolution: {integrity: sha512-8PFo4DLLLCDMuS51/BEEtE1m9CAXw1LNVtZSS1PzkYQh6Qf9JUwM4huYeSoUumaaoAyuwYBwCa9OsrcpMqcOdQ==}
engines: {node: '>=16'}
hasBin: true
dependencies:
arg: 5.0.2
@ -17978,11 +17772,11 @@ packages:
resolution: {integrity: sha512-siT1RiqlfQnGqgT/YzXVUNsom9S0H1OX+dpdGN1xkyYATo4I6sep5NmsRD/40s3IIOvlCq6akxkqG82urIZW1w==}
dev: true
/storybook@7.4.2:
resolution: {integrity: sha512-UuYmdxEWEQAepfjgQFbbHTq47Xxpw16naAvJ9n/nsjMnOhYupm1ZIdWYaeNjz4LOfz+1WzgU7us0IvaBrxzl4g==}
/storybook@7.4.3:
resolution: {integrity: sha512-afp7trR23jKt8ruGMPjkNAk3A/4CaLo20iPWAODznlF7u+XWnqGm1S+ZUiJFf13Jzj8jmJf/d7/xDHxY3qVMUA==}
hasBin: true
dependencies:
'@storybook/cli': 7.4.2
'@storybook/cli': 7.4.3
transitivePeerDependencies:
- bufferutil
- encoding
@ -19135,8 +18929,8 @@ packages:
core-util-is: 1.0.2
extsprintf: 1.3.0
/vite-node@0.34.4(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0):
resolution: {integrity: sha512-ho8HtiLc+nsmbwZMw8SlghESEE3KxJNp04F/jPUCLVvaURwt0d+r9LxEqCX5hvrrOQ0GSyxbYr5ZfRYhQ0yVKQ==}
/vite-node@0.34.5(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0):
resolution: {integrity: sha512-RNZ+DwbCvDoI5CbCSQSyRyzDTfFvFauvMs6Yq4ObJROKlIKuat1KgSX/Ako5rlDMfVCyMcpMRMTkJBxd6z8YRA==}
engines: {node: '>=v14.18.0'}
hasBin: true
dependencies:
@ -19198,20 +18992,20 @@ packages:
optionalDependencies:
fsevents: 2.3.2
/vitest-fetch-mock@0.2.2(vitest@0.34.4):
/vitest-fetch-mock@0.2.2(vitest@0.34.5):
resolution: {integrity: sha512-XmH6QgTSjCWrqXoPREIdbj40T7i1xnGmAsTAgfckoO75W1IEHKR8hcPCQ7SO16RsdW1t85oUm6pcQRLeBgjVYQ==}
engines: {node: '>=14.14.0'}
peerDependencies:
vitest: '>=0.16.0'
dependencies:
cross-fetch: 3.1.5
vitest: 0.34.4(happy-dom@10.0.3)(sass@1.68.0)(terser@5.20.0)
vitest: 0.34.5(happy-dom@10.0.3)(sass@1.68.0)(terser@5.20.0)
transitivePeerDependencies:
- encoding
dev: true
/vitest@0.34.4(happy-dom@10.0.3)(sass@1.68.0)(terser@5.20.0):
resolution: {integrity: sha512-SE/laOsB6995QlbSE6BtkpXDeVNLJc1u2LHRG/OpnN4RsRzM3GQm4nm3PQCK5OBtrsUqnhzLdnT7se3aeNGdlw==}
/vitest@0.34.5(happy-dom@10.0.3)(sass@1.68.0)(terser@5.20.0):
resolution: {integrity: sha512-CPI68mmnr2DThSB3frSuE5RLm9wo5wU4fbDrDwWQQB1CWgq9jQVoQwnQSzYAjdoBOPoH2UtXpOgHVge/uScfZg==}
engines: {node: '>=v14.18.0'}
hasBin: true
peerDependencies:
@ -19244,11 +19038,11 @@ packages:
'@types/chai': 4.3.5
'@types/chai-subset': 1.3.3
'@types/node': 20.6.3
'@vitest/expect': 0.34.4
'@vitest/runner': 0.34.4
'@vitest/snapshot': 0.34.4
'@vitest/spy': 0.34.4
'@vitest/utils': 0.34.4
'@vitest/expect': 0.34.5
'@vitest/runner': 0.34.5
'@vitest/snapshot': 0.34.5
'@vitest/spy': 0.34.5
'@vitest/utils': 0.34.5
acorn: 8.10.0
acorn-walk: 8.2.0
cac: 6.7.14
@ -19264,7 +19058,7 @@ packages:
tinybench: 2.5.0
tinypool: 0.7.0
vite: 4.4.9(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0)
vite-node: 0.34.4(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0)
vite-node: 0.34.5(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0)
why-is-node-running: 2.2.2
transitivePeerDependencies:
- less
@ -19344,14 +19138,14 @@ packages:
he: 1.2.0
dev: true
/vue-tsc@1.8.11(typescript@5.2.2):
resolution: {integrity: sha512-BzfiMdPqDHBlysx4g26NkfVHSQwGD/lTRausmxN9sFyjXz34OWfsbkh0YsVkX84Hu65In1fFlxHiG39Tr4Vojg==}
/vue-tsc@1.8.13(typescript@5.2.2):
resolution: {integrity: sha512-Hl8zUXPVK2KzPtbXeMCN0CSFkwvD96YOtYt9KvJPG9W8QGcNpGk9KHwPuGMxA8blWXSIli7gtsoC+clICEVdVg==}
hasBin: true
peerDependencies:
typescript: '*'
dependencies:
'@vue/language-core': 1.8.11(typescript@5.2.2)
'@vue/typescript': 1.8.11(typescript@5.2.2)
'@vue/language-core': 1.8.13(typescript@5.2.2)
'@vue/typescript': 1.8.13(typescript@5.2.2)
semver: 7.5.4
typescript: 5.2.2
dev: true