Merge tag '2024.3.1-io.6' into host
This commit is contained in:
commit
4c8e8b8ea0
39 changed files with 2105 additions and 1579 deletions
|
@ -749,6 +749,8 @@ loadRawImages: "Load original images instead of showing thumbnails"
|
||||||
disableShowingAnimatedImages: "Don't play animated images"
|
disableShowingAnimatedImages: "Don't play animated images"
|
||||||
highlightSensitiveMedia: "Highlight sensitive media"
|
highlightSensitiveMedia: "Highlight sensitive media"
|
||||||
verificationEmailSent: "A verification email has been sent. Please follow the included link to complete verification."
|
verificationEmailSent: "A verification email has been sent. Please follow the included link to complete verification."
|
||||||
|
emailRegistrationRequired: "Email registration required"
|
||||||
|
emailRegistrationRequiredBanner: "{instance} requires email registration. Click here to register your email address."
|
||||||
notSet: "Not set"
|
notSet: "Not set"
|
||||||
emailVerified: "Email has been verified"
|
emailVerified: "Email has been verified"
|
||||||
noteFavoritesCount: "Number of favorite notes"
|
noteFavoritesCount: "Number of favorite notes"
|
||||||
|
@ -1216,6 +1218,7 @@ wellKnownWebsites: "Well-known websites"
|
||||||
wellKnownWebsitesDescription: "Separate with spaces for AND, new lines for OR. Surround with slashes for regex. Domain names only will match the end of the domain of the URL. If matched, the warning of external links will not be displayed."
|
wellKnownWebsitesDescription: "Separate with spaces for AND, new lines for OR. Surround with slashes for regex. Domain names only will match the end of the domain of the URL. If matched, the warning of external links will not be displayed."
|
||||||
warningRedirectingExternalWebsiteTitle: "You are leaving our site!"
|
warningRedirectingExternalWebsiteTitle: "You are leaving our site!"
|
||||||
warningRedirectingExternalWebsiteDescription: "You are about to jump to another site.\nPlease make sure this link is reliable before proceeding.\n\n{url}"
|
warningRedirectingExternalWebsiteDescription: "You are about to jump to another site.\nPlease make sure this link is reliable before proceeding.\n\n{url}"
|
||||||
|
warningRedirectingExternalWebsiteTrustThisSite: "I trust this site"
|
||||||
code: "Code"
|
code: "Code"
|
||||||
reloadRequiredToApplySettings: "Reloading is required to apply the settings."
|
reloadRequiredToApplySettings: "Reloading is required to apply the settings."
|
||||||
remainingN: "Remaining: {n}"
|
remainingN: "Remaining: {n}"
|
||||||
|
@ -1242,6 +1245,7 @@ enableHorizontalSwipe: "Swipe to switch tabs"
|
||||||
loading: "Loading"
|
loading: "Loading"
|
||||||
surrender: "Cancel"
|
surrender: "Cancel"
|
||||||
gameRetry: "Retry"
|
gameRetry: "Retry"
|
||||||
|
here: "here"
|
||||||
_bubbleGame:
|
_bubbleGame:
|
||||||
howToPlay: "How to play"
|
howToPlay: "How to play"
|
||||||
hold: "Hold"
|
hold: "Hold"
|
||||||
|
@ -2011,9 +2015,13 @@ _2fa:
|
||||||
renewTOTPCancel: "Cancel"
|
renewTOTPCancel: "Cancel"
|
||||||
checkBackupCodesBeforeCloseThisWizard: "Before you close this window, please note the following backup codes."
|
checkBackupCodesBeforeCloseThisWizard: "Before you close this window, please note the following backup codes."
|
||||||
backupCodes: "Backup codes"
|
backupCodes: "Backup codes"
|
||||||
backupCodesDescription: "You can use these codes to gain access to your account in case of becoming unable to use your two-factor authentificator app. Each can only be used once. Please keep them in a safe place."
|
backupCodesDescription: "You can use these codes to gain access to your account in case of becoming unable to use your two-factor authenticator app. Each can only be used once. Please keep them in a safe place."
|
||||||
backupCodeUsedWarning: "A backup code has been used. Please reconfigure two-factor authentification as soon as possible if you are no longer able to use it."
|
backupCodeUsedWarning: "A backup code has been used. Please reconfigure two-factor authentication as soon as possible if you are no longer able to use it."
|
||||||
backupCodesExhaustedWarning: "All backup codes have been used. Should you lose access to your two-factor authentification app, you will be unable to access this account. Please reconfigure two-factor authentification."
|
backupCodesExhaustedWarning: "All backup codes have been used. Should you lose access to your two-factor authentication app, you will be unable to access this account. Please reconfigure two-factor authentication."
|
||||||
|
backupCodesSavedConfirmTitle: "Did you save your backup codes?"
|
||||||
|
backupCodesSavedConfirmDescription: "If you lose both your two-factor authentication app and backup codes, YOU WILL LOSE ACCESS TO YOUR ACCOUNT.\nKeep them safe and secure, and do not share them with anyone.\n\n$[x2 Two-factor authentication settings CANNOT be changed by anyone other than yourself, $[fg.color=red AND THE ADMINISTRATOR CANNOT DISABLE IT EITHER.]]"
|
||||||
|
backupCodesSavedConfirmChecked: "I have saved my backup codes"
|
||||||
|
howto2fa: "If you are having trouble setting up, please refer to {link}."
|
||||||
_permissions:
|
_permissions:
|
||||||
"read:account": "View your account information"
|
"read:account": "View your account information"
|
||||||
"write:account": "Edit your account information"
|
"write:account": "Edit your account information"
|
||||||
|
|
35
locales/index.d.ts
vendored
35
locales/index.d.ts
vendored
|
@ -3008,6 +3008,14 @@ export interface Locale extends ILocale {
|
||||||
* 確認のメールを送信しました。メールに記載されたリンクにアクセスして、設定を完了してください。
|
* 確認のメールを送信しました。メールに記載されたリンクにアクセスして、設定を完了してください。
|
||||||
*/
|
*/
|
||||||
"verificationEmailSent": string;
|
"verificationEmailSent": string;
|
||||||
|
/**
|
||||||
|
* メールアドレスの登録が必要です
|
||||||
|
*/
|
||||||
|
"emailRegistrationRequired": string;
|
||||||
|
/**
|
||||||
|
* {instance}はメールアドレスの登録が必要です。ここをクリックしてメールアドレスを登録してください。
|
||||||
|
*/
|
||||||
|
"emailRegistrationRequiredBanner": ParameterizedString<"instance">;
|
||||||
/**
|
/**
|
||||||
* 未設定
|
* 未設定
|
||||||
*/
|
*/
|
||||||
|
@ -4879,6 +4887,10 @@ export interface Locale extends ILocale {
|
||||||
* {url}
|
* {url}
|
||||||
*/
|
*/
|
||||||
"warningRedirectingExternalWebsiteDescription": ParameterizedString<"url">;
|
"warningRedirectingExternalWebsiteDescription": ParameterizedString<"url">;
|
||||||
|
/**
|
||||||
|
* このサイトを信頼する
|
||||||
|
*/
|
||||||
|
"warningRedirectingExternalWebsiteTrustThisSite": string;
|
||||||
/**
|
/**
|
||||||
* サムネイルの表示を制限するURL
|
* サムネイルの表示を制限するURL
|
||||||
*/
|
*/
|
||||||
|
@ -5023,6 +5035,10 @@ export interface Locale extends ILocale {
|
||||||
* このユーザーをミュートする
|
* このユーザーをミュートする
|
||||||
*/
|
*/
|
||||||
"muteThisUser": string;
|
"muteThisUser": string;
|
||||||
|
/**
|
||||||
|
* こちら
|
||||||
|
*/
|
||||||
|
"here": string;
|
||||||
"_bubbleGame": {
|
"_bubbleGame": {
|
||||||
/**
|
/**
|
||||||
* 遊び方
|
* 遊び方
|
||||||
|
@ -7844,6 +7860,25 @@ export interface Locale extends ILocale {
|
||||||
* バックアップコードが全て使用されました。認証アプリを利用できない場合、これ以上アカウントにアクセスできなくなります。認証アプリを再登録してください。
|
* バックアップコードが全て使用されました。認証アプリを利用できない場合、これ以上アカウントにアクセスできなくなります。認証アプリを再登録してください。
|
||||||
*/
|
*/
|
||||||
"backupCodesExhaustedWarning": string;
|
"backupCodesExhaustedWarning": string;
|
||||||
|
/**
|
||||||
|
* バックアップコードを保存しましたか?
|
||||||
|
*/
|
||||||
|
"backupCodesSavedConfirmTitle": string;
|
||||||
|
/**
|
||||||
|
* 二要素認証アプリとバックアップコードの両方を紛失した場合、アカウントにアクセスできなくなります。
|
||||||
|
* 誰とも共有せず、適切な方法で保管してください。
|
||||||
|
*
|
||||||
|
* $[x2 二要素認証設定は自分以外の誰にも変更できませんので、$[fg.color=red 運営チームも無効化することはできません。]]
|
||||||
|
*/
|
||||||
|
"backupCodesSavedConfirmDescription": string;
|
||||||
|
/**
|
||||||
|
* バックアップコードを保存しました
|
||||||
|
*/
|
||||||
|
"backupCodesSavedConfirmChecked": string;
|
||||||
|
/**
|
||||||
|
* 設定方法でお困りの際は、{link}を参照してください。
|
||||||
|
*/
|
||||||
|
"howto2fa": ParameterizedString<"link">;
|
||||||
};
|
};
|
||||||
"_permissions": {
|
"_permissions": {
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -748,6 +748,8 @@ loadRawImages: "添付画像のサムネイルをオリジナル画質にする"
|
||||||
disableShowingAnimatedImages: "アニメーション画像を再生しない"
|
disableShowingAnimatedImages: "アニメーション画像を再生しない"
|
||||||
highlightSensitiveMedia: "メディアがセンシティブであることを分かりやすく表示"
|
highlightSensitiveMedia: "メディアがセンシティブであることを分かりやすく表示"
|
||||||
verificationEmailSent: "確認のメールを送信しました。メールに記載されたリンクにアクセスして、設定を完了してください。"
|
verificationEmailSent: "確認のメールを送信しました。メールに記載されたリンクにアクセスして、設定を完了してください。"
|
||||||
|
emailRegistrationRequired: "メールアドレスの登録が必要です"
|
||||||
|
emailRegistrationRequiredBanner: "{instance}はメールアドレスの登録が必要です。ここをクリックしてメールアドレスを登録してください。"
|
||||||
notSet: "未設定"
|
notSet: "未設定"
|
||||||
emailVerified: "メールアドレスが確認されました"
|
emailVerified: "メールアドレスが確認されました"
|
||||||
noteFavoritesCount: "お気に入りノートの数"
|
noteFavoritesCount: "お気に入りノートの数"
|
||||||
|
@ -1215,6 +1217,7 @@ wellKnownWebsites: "よく知られたウェブサイト"
|
||||||
wellKnownWebsitesDescription: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります。スラッシュで囲むと正規表現になります。ドメイン名だけ書くと後方一致になります。一致した場合、外部サイトへのリダイレクトの警告を省略させることができます。"
|
wellKnownWebsitesDescription: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります。スラッシュで囲むと正規表現になります。ドメイン名だけ書くと後方一致になります。一致した場合、外部サイトへのリダイレクトの警告を省略させることができます。"
|
||||||
warningRedirectingExternalWebsiteTitle: "外部サイトへ移動します"
|
warningRedirectingExternalWebsiteTitle: "外部サイトへ移動します"
|
||||||
warningRedirectingExternalWebsiteDescription: "別のサイトにジャンプしようとしています。\nリンク先の安全性を十分に確認した上で進んでください。\n\n{url}"
|
warningRedirectingExternalWebsiteDescription: "別のサイトにジャンプしようとしています。\nリンク先の安全性を十分に確認した上で進んでください。\n\n{url}"
|
||||||
|
warningRedirectingExternalWebsiteTrustThisSite: "このサイトを信頼する"
|
||||||
urlPreviewDenyList: "サムネイルの表示を制限するURL"
|
urlPreviewDenyList: "サムネイルの表示を制限するURL"
|
||||||
urlPreviewDenyListDescription: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります。スラッシュで囲むと正規表現になります。一致した場合、サムネイルがぼかされて表示されます。"
|
urlPreviewDenyListDescription: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります。スラッシュで囲むと正規表現になります。一致した場合、サムネイルがぼかされて表示されます。"
|
||||||
code: "コード"
|
code: "コード"
|
||||||
|
@ -1251,6 +1254,7 @@ selectCategory: "カテゴリを選択"
|
||||||
reportComplete: "通報完了"
|
reportComplete: "通報完了"
|
||||||
blockThisUser: "このユーザーをブロックする"
|
blockThisUser: "このユーザーをブロックする"
|
||||||
muteThisUser: "このユーザーをミュートする"
|
muteThisUser: "このユーザーをミュートする"
|
||||||
|
here: "こちら"
|
||||||
|
|
||||||
_bubbleGame:
|
_bubbleGame:
|
||||||
howToPlay: "遊び方"
|
howToPlay: "遊び方"
|
||||||
|
@ -2061,6 +2065,10 @@ _2fa:
|
||||||
backupCodesDescription: "認証アプリが使用できなくなった場合、以下のバックアップコードを使ってアカウントにアクセスできます。これらのコードは必ず安全な場所に保管してください。各コードは一回だけ使用できます。"
|
backupCodesDescription: "認証アプリが使用できなくなった場合、以下のバックアップコードを使ってアカウントにアクセスできます。これらのコードは必ず安全な場所に保管してください。各コードは一回だけ使用できます。"
|
||||||
backupCodeUsedWarning: "バックアップコードが使用されました。認証アプリが使えなくなっている場合、なるべく早く認証アプリを再設定してください。"
|
backupCodeUsedWarning: "バックアップコードが使用されました。認証アプリが使えなくなっている場合、なるべく早く認証アプリを再設定してください。"
|
||||||
backupCodesExhaustedWarning: "バックアップコードが全て使用されました。認証アプリを利用できない場合、これ以上アカウントにアクセスできなくなります。認証アプリを再登録してください。"
|
backupCodesExhaustedWarning: "バックアップコードが全て使用されました。認証アプリを利用できない場合、これ以上アカウントにアクセスできなくなります。認証アプリを再登録してください。"
|
||||||
|
backupCodesSavedConfirmTitle: "バックアップコードを保存しましたか?"
|
||||||
|
backupCodesSavedConfirmDescription: "二要素認証アプリとバックアップコードの両方を紛失した場合、アカウントにアクセスできなくなります。\n誰とも共有せず、適切な方法で保管してください。\n\n$[x2 二要素認証設定は自分以外の誰にも変更できませんので、$[fg.color=red 運営チームも無効化することはできません。]]"
|
||||||
|
backupCodesSavedConfirmChecked: "バックアップコードを保存しました"
|
||||||
|
howto2fa: "設定方法でお困りの際は、{link}を参照してください。"
|
||||||
|
|
||||||
_permissions:
|
_permissions:
|
||||||
"read:account": "アカウントの情報を見る"
|
"read:account": "アカウントの情報を見る"
|
||||||
|
|
|
@ -748,6 +748,8 @@ loadRawImages: "첨부한 이미지의 썸네일을 원본화질로 표시"
|
||||||
disableShowingAnimatedImages: "움직이는 이미지를 자동으로 재생하지 않음"
|
disableShowingAnimatedImages: "움직이는 이미지를 자동으로 재생하지 않음"
|
||||||
highlightSensitiveMedia: "미디어가 민감한 내용이라는 것을 알기 쉽게 표시"
|
highlightSensitiveMedia: "미디어가 민감한 내용이라는 것을 알기 쉽게 표시"
|
||||||
verificationEmailSent: "확인 메일을 발송하였습니다. 설정을 완료하려면 메일에 첨부된 링크를 확인해 주세요."
|
verificationEmailSent: "확인 메일을 발송하였습니다. 설정을 완료하려면 메일에 첨부된 링크를 확인해 주세요."
|
||||||
|
emailRegistrationRequired: "이메일 주소 등록이 필요합니다"
|
||||||
|
emailRegistrationRequiredBanner: "{instance}에서는 이메일 주소 등록이 필요합니다. 여기를 클릭하여 이메일 주소를 등록해 주세요."
|
||||||
notSet: "설정되지 않음"
|
notSet: "설정되지 않음"
|
||||||
emailVerified: "메일 주소가 확인되었습니다."
|
emailVerified: "메일 주소가 확인되었습니다."
|
||||||
noteFavoritesCount: "즐겨찾기한 노트 수"
|
noteFavoritesCount: "즐겨찾기한 노트 수"
|
||||||
|
@ -1213,6 +1215,7 @@ wellKnownWebsites: "잘 알려진 웹사이트"
|
||||||
wellKnownWebsitesDescription: "공백으로 구분하면 AND 지정이 되며, 개행으로 구분하면 OR 지정이 됩니다. 슬래시로 둘러싸면 정규 표현식이 됩니다. 도메인명만 쓰면 후방 일치가 됩니다. 일치하는 경우 외부 사이트로의 경고를 생략할 수 있습니다."
|
wellKnownWebsitesDescription: "공백으로 구분하면 AND 지정이 되며, 개행으로 구분하면 OR 지정이 됩니다. 슬래시로 둘러싸면 정규 표현식이 됩니다. 도메인명만 쓰면 후방 일치가 됩니다. 일치하는 경우 외부 사이트로의 경고를 생략할 수 있습니다."
|
||||||
warningRedirectingExternalWebsiteTitle: "외부 사이트로 이동합니다"
|
warningRedirectingExternalWebsiteTitle: "외부 사이트로 이동합니다"
|
||||||
warningRedirectingExternalWebsiteDescription: "다른 사이트로 이동하려고 합니다.\n링크가 안전한지 충분히 확인한 후 이동해주세요.\n\n{url}"
|
warningRedirectingExternalWebsiteDescription: "다른 사이트로 이동하려고 합니다.\n링크가 안전한지 충분히 확인한 후 이동해주세요.\n\n{url}"
|
||||||
|
warningRedirectingExternalWebsiteTrustThisSite: "이 사이트를 신뢰합니다"
|
||||||
code: "문자열"
|
code: "문자열"
|
||||||
reloadRequiredToApplySettings: "설정을 적용하려면 새로고침을 해야 합니다."
|
reloadRequiredToApplySettings: "설정을 적용하려면 새로고침을 해야 합니다."
|
||||||
remainingN: "나머지: {n}"
|
remainingN: "나머지: {n}"
|
||||||
|
@ -1235,6 +1238,7 @@ withSensitive: "민감한 파일이 포함된 노트 보기"
|
||||||
userSaysSomethingSensitive: "{name}의 민감한 파일이 포함된 게시물"
|
userSaysSomethingSensitive: "{name}의 민감한 파일이 포함된 게시물"
|
||||||
enableHorizontalSwipe: "스와이프하여 탭 전환"
|
enableHorizontalSwipe: "스와이프하여 탭 전환"
|
||||||
surrender: "그만두기"
|
surrender: "그만두기"
|
||||||
|
here: "여기"
|
||||||
_bubbleGame:
|
_bubbleGame:
|
||||||
howToPlay: "설명"
|
howToPlay: "설명"
|
||||||
_howToPlay:
|
_howToPlay:
|
||||||
|
@ -1989,9 +1993,13 @@ _2fa:
|
||||||
renewTOTPCancel: "취소"
|
renewTOTPCancel: "취소"
|
||||||
checkBackupCodesBeforeCloseThisWizard: "이 위자드를 닫기 전에 아래 백업 코드를 확인하십시오"
|
checkBackupCodesBeforeCloseThisWizard: "이 위자드를 닫기 전에 아래 백업 코드를 확인하십시오"
|
||||||
backupCodes: "백업 코드"
|
backupCodes: "백업 코드"
|
||||||
backupCodesDescription: "인증 앱을 사용할 수 없게 된 경우 아래 백업 코드를 사용하여 계정에 액세스 할 수 있습니다.이 코드들은 반드시 안전한 장소에 보관하십시오.각 코드는 한 번만 사용할 수 있습니다."
|
backupCodesDescription: "인증 앱을 사용할 수 없게 된 경우 아래 백업 코드를 사용하여 계정에 액세스 할 수 있습니다. 이 코드들은 반드시 안전한 장소에 보관하십시오. 각 코드는 한 번만 사용할 수 있습니다."
|
||||||
backupCodeUsedWarning: "백업 코드가 사용되었습니다.인증 앱을 사용할 수 없게 된 경우, 조속히 인증 앱을 다시 설정해 주십시오."
|
backupCodeUsedWarning: "백업 코드가 사용되었습니다. 인증 앱을 사용할 수 없게 된 경우, 조속히 인증 앱을 다시 설정해 주십시오."
|
||||||
backupCodesExhaustedWarning: "백업 코드가 모두 사용되었습니다.인증 앱을 사용할 수 없는 경우 더 이상 계정에 액세스하는 것이 불가능합니다.인증 앱을 다시 등록해 주세요."
|
backupCodesExhaustedWarning: "백업 코드가 모두 사용되었습니다. 인증 앱을 사용할 수 없는 경우 더 이상 계정에 액세스하는 것이 불가능합니다. 인증 앱을 다시 등록해 주세요."
|
||||||
|
backupCodesSavedConfirmTitle: "백업 코드를 저장했습니까?"
|
||||||
|
backupCodesSavedConfirmDescription: "인증 앱과 백업 코드를 모두 분실하면\n계정에 액세스할 수 없게 됩니다.\n자신만이 알 수 있도록 안전한 장소에 보관해 주십시오.\n\n$[x2 2단계 인증 설정은\n본인만이 변경할 수 있으며, $[fg.color=red 운영팀도 해제할 수 없습니다.]]"
|
||||||
|
backupCodesSavedConfirmChecked: "백업 코드를 저장했습니다"
|
||||||
|
howto2fa: "설정 방법에 대한 자세한 내용은 {link}를 참조하세요."
|
||||||
_permissions:
|
_permissions:
|
||||||
"read:account": "계정의 정보를 봅니다"
|
"read:account": "계정의 정보를 봅니다"
|
||||||
"write:account": "계정의 정보를 변경합니다"
|
"write:account": "계정의 정보를 변경합니다"
|
||||||
|
|
16
package.json
16
package.json
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"version": "2024.3.1-host.5b",
|
"version": "2024.3.1-host.6",
|
||||||
"codename": "nasubi",
|
"codename": "nasubi",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/MisskeyIO/misskey.git"
|
"url": "https://github.com/MisskeyIO/misskey.git"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@8.15.5",
|
"packageManager": "pnpm@8.15.6",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"packages/frontend",
|
"packages/frontend",
|
||||||
"packages/backend",
|
"packages/backend",
|
||||||
|
@ -56,15 +56,15 @@
|
||||||
"execa": "8.0.1",
|
"execa": "8.0.1",
|
||||||
"js-yaml": "4.1.0",
|
"js-yaml": "4.1.0",
|
||||||
"postcss": "8.4.38",
|
"postcss": "8.4.38",
|
||||||
"terser": "5.30.0",
|
"terser": "5.30.3",
|
||||||
"typescript": "5.4.3"
|
"typescript": "5.4.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "20.12.2",
|
"@types/node": "20.12.7",
|
||||||
"@typescript-eslint/eslint-plugin": "7.4.0",
|
"@typescript-eslint/eslint-plugin": "7.6.0",
|
||||||
"@typescript-eslint/parser": "7.4.0",
|
"@typescript-eslint/parser": "7.6.0",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"cypress": "13.7.1",
|
"cypress": "13.7.3",
|
||||||
"eslint": "8.57.0",
|
"eslint": "8.57.0",
|
||||||
"ncp": "2.0.0",
|
"ncp": "2.0.0",
|
||||||
"start-server-and-test": "2.0.3"
|
"start-server-and-test": "2.0.3"
|
||||||
|
|
|
@ -66,8 +66,8 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@authenio/samlify-node-xmllint": "2.0.0",
|
"@authenio/samlify-node-xmllint": "2.0.0",
|
||||||
"@aws-sdk/client-s3": "3.540.0",
|
"@aws-sdk/client-s3": "3.554.0",
|
||||||
"@aws-sdk/lib-storage": "3.540.0",
|
"@aws-sdk/lib-storage": "3.554.0",
|
||||||
"@bull-board/api": "5.15.3",
|
"@bull-board/api": "5.15.3",
|
||||||
"@bull-board/fastify": "5.15.3",
|
"@bull-board/fastify": "5.15.3",
|
||||||
"@bull-board/ui": "5.15.3",
|
"@bull-board/ui": "5.15.3",
|
||||||
|
@ -79,7 +79,7 @@
|
||||||
"@fastify/formbody": "7.4.0",
|
"@fastify/formbody": "7.4.0",
|
||||||
"@fastify/http-proxy": "9.5.0",
|
"@fastify/http-proxy": "9.5.0",
|
||||||
"@fastify/multipart": "8.2.0",
|
"@fastify/multipart": "8.2.0",
|
||||||
"@fastify/static": "7.0.2",
|
"@fastify/static": "7.0.3",
|
||||||
"@fastify/view": "9.0.0",
|
"@fastify/view": "9.0.0",
|
||||||
"@misskey-dev/sharp-read-bmp": "1.2.0",
|
"@misskey-dev/sharp-read-bmp": "1.2.0",
|
||||||
"@misskey-dev/summaly": "5.1.0",
|
"@misskey-dev/summaly": "5.1.0",
|
||||||
|
@ -87,12 +87,12 @@
|
||||||
"@nestjs/core": "10.3.7",
|
"@nestjs/core": "10.3.7",
|
||||||
"@nestjs/testing": "10.3.7",
|
"@nestjs/testing": "10.3.7",
|
||||||
"@peertube/http-signature": "1.7.0",
|
"@peertube/http-signature": "1.7.0",
|
||||||
"@simplewebauthn/server": "9.0.3",
|
"@simplewebauthn/server": "10.0.0",
|
||||||
"@sinonjs/fake-timers": "11.2.2",
|
"@sinonjs/fake-timers": "11.2.2",
|
||||||
"@smithy/node-http-handler": "2.5.0",
|
"@smithy/node-http-handler": "2.5.0",
|
||||||
"@swc/cli": "0.1.65",
|
"@swc/cli": "0.1.65",
|
||||||
"@swc/core": "1.3.107",
|
"@swc/core": "1.3.107",
|
||||||
"@twemoji/parser": "15.1.0",
|
"@twemoji/parser": "15.1.1",
|
||||||
"accepts": "1.3.8",
|
"accepts": "1.3.8",
|
||||||
"ajv": "8.12.0",
|
"ajv": "8.12.0",
|
||||||
"archiver": "6.0.1",
|
"archiver": "6.0.1",
|
||||||
|
@ -100,7 +100,7 @@
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"blurhash": "2.0.5",
|
"blurhash": "2.0.5",
|
||||||
"body-parser": "1.20.2",
|
"body-parser": "1.20.2",
|
||||||
"bullmq": "5.4.6",
|
"bullmq": "5.7.1",
|
||||||
"cacheable-lookup": "7.0.0",
|
"cacheable-lookup": "7.0.0",
|
||||||
"cbor": "9.0.2",
|
"cbor": "9.0.2",
|
||||||
"chalk": "5.3.0",
|
"chalk": "5.3.0",
|
||||||
|
@ -112,7 +112,7 @@
|
||||||
"date-fns": "3.6.0",
|
"date-fns": "3.6.0",
|
||||||
"deep-email-validator": "0.1.21",
|
"deep-email-validator": "0.1.21",
|
||||||
"fastify": "4.26.2",
|
"fastify": "4.26.2",
|
||||||
"fastify-http-errors-enhanced": "5.0.3",
|
"fastify-http-errors-enhanced": "5.0.4",
|
||||||
"fastify-raw-body": "4.3.0",
|
"fastify-raw-body": "4.3.0",
|
||||||
"feed": "4.2.2",
|
"feed": "4.2.2",
|
||||||
"file-type": "19.0.0",
|
"file-type": "19.0.0",
|
||||||
|
@ -127,7 +127,7 @@
|
||||||
"ip-cidr": "3.1.0",
|
"ip-cidr": "3.1.0",
|
||||||
"ipaddr.js": "2.1.0",
|
"ipaddr.js": "2.1.0",
|
||||||
"is-svg": "5.0.0",
|
"is-svg": "5.0.0",
|
||||||
"jose": "5.2.3",
|
"jose": "5.2.4",
|
||||||
"js-yaml": "4.1.0",
|
"js-yaml": "4.1.0",
|
||||||
"jsdom": "23.2.0",
|
"jsdom": "23.2.0",
|
||||||
"json5": "2.2.3",
|
"json5": "2.2.3",
|
||||||
|
@ -140,7 +140,7 @@
|
||||||
"misskey-js": "workspace:*",
|
"misskey-js": "workspace:*",
|
||||||
"misskey-reversi": "workspace:*",
|
"misskey-reversi": "workspace:*",
|
||||||
"ms": "3.0.0-canary.1",
|
"ms": "3.0.0-canary.1",
|
||||||
"nanoid": "5.0.6",
|
"nanoid": "5.0.7",
|
||||||
"nested-property": "4.0.0",
|
"nested-property": "4.0.0",
|
||||||
"node-fetch": "3.3.2",
|
"node-fetch": "3.3.2",
|
||||||
"node-forge": "1.3.1",
|
"node-forge": "1.3.1",
|
||||||
|
@ -150,10 +150,10 @@
|
||||||
"oauth2orize": "1.12.0",
|
"oauth2orize": "1.12.0",
|
||||||
"oauth2orize-pkce": "0.1.2",
|
"oauth2orize-pkce": "0.1.2",
|
||||||
"os-utils": "0.0.14",
|
"os-utils": "0.0.14",
|
||||||
"otpauth": "9.2.2",
|
"otpauth": "9.2.3",
|
||||||
"parse5": "7.1.2",
|
"parse5": "7.1.2",
|
||||||
"pg": "8.11.3",
|
"pg": "8.11.5",
|
||||||
"pino": "8.19.0",
|
"pino": "8.20.0",
|
||||||
"pino-pretty": "11.0.0",
|
"pino-pretty": "11.0.0",
|
||||||
"pkce-challenge": "4.1.0",
|
"pkce-challenge": "4.1.0",
|
||||||
"probe-image-size": "7.2.3",
|
"probe-image-size": "7.2.3",
|
||||||
|
@ -177,13 +177,13 @@
|
||||||
"slacc": "0.0.10",
|
"slacc": "0.0.10",
|
||||||
"strict-event-emitter-types": "2.0.0",
|
"strict-event-emitter-types": "2.0.0",
|
||||||
"stringz": "2.1.0",
|
"stringz": "2.1.0",
|
||||||
"systeminformation": "5.22.6",
|
"systeminformation": "5.22.7",
|
||||||
"tinycolor2": "1.6.0",
|
"tinycolor2": "1.6.0",
|
||||||
"tmp": "0.2.3",
|
"tmp": "0.2.3",
|
||||||
"tsc-alias": "1.8.8",
|
"tsc-alias": "1.8.8",
|
||||||
"tsconfig-paths": "4.2.0",
|
"tsconfig-paths": "4.2.0",
|
||||||
"typeorm": "0.3.20",
|
"typeorm": "0.3.20",
|
||||||
"typescript": "5.4.3",
|
"typescript": "5.4.5",
|
||||||
"ulid": "2.3.0",
|
"ulid": "2.3.0",
|
||||||
"vary": "1.1.2",
|
"vary": "1.1.2",
|
||||||
"web-push": "3.6.7",
|
"web-push": "3.6.7",
|
||||||
|
@ -195,7 +195,7 @@
|
||||||
"@jest/globals": "29.7.0",
|
"@jest/globals": "29.7.0",
|
||||||
"@misskey-dev/eslint-plugin": "1.0.0",
|
"@misskey-dev/eslint-plugin": "1.0.0",
|
||||||
"@nestjs/platform-express": "10.3.7",
|
"@nestjs/platform-express": "10.3.7",
|
||||||
"@simplewebauthn/types": "9.0.1",
|
"@simplewebauthn/types": "10.0.0",
|
||||||
"@swc/jest": "0.2.36",
|
"@swc/jest": "0.2.36",
|
||||||
"@types/accepts": "1.3.7",
|
"@types/accepts": "1.3.7",
|
||||||
"@types/archiver": "6.0.2",
|
"@types/archiver": "6.0.2",
|
||||||
|
@ -213,13 +213,13 @@
|
||||||
"@types/jsrsasign": "10.5.13",
|
"@types/jsrsasign": "10.5.13",
|
||||||
"@types/mime-types": "2.1.4",
|
"@types/mime-types": "2.1.4",
|
||||||
"@types/ms": "0.7.34",
|
"@types/ms": "0.7.34",
|
||||||
"@types/node": "20.12.2",
|
"@types/node": "20.12.7",
|
||||||
"@types/node-forge": "1.3.11",
|
"@types/node-forge": "1.3.11",
|
||||||
"@types/nodemailer": "6.4.14",
|
"@types/nodemailer": "6.4.14",
|
||||||
"@types/oauth": "0.9.4",
|
"@types/oauth": "0.9.4",
|
||||||
"@types/oauth2orize": "1.11.5",
|
"@types/oauth2orize": "1.11.5",
|
||||||
"@types/oauth2orize-pkce": "0.1.2",
|
"@types/oauth2orize-pkce": "0.1.2",
|
||||||
"@types/pg": "8.11.4",
|
"@types/pg": "8.11.5",
|
||||||
"@types/pug": "2.0.10",
|
"@types/pug": "2.0.10",
|
||||||
"@types/punycode": "2.1.4",
|
"@types/punycode": "2.1.4",
|
||||||
"@types/qrcode": "1.5.5",
|
"@types/qrcode": "1.5.5",
|
||||||
|
@ -235,8 +235,8 @@
|
||||||
"@types/vary": "1.1.3",
|
"@types/vary": "1.1.3",
|
||||||
"@types/web-push": "3.6.3",
|
"@types/web-push": "3.6.3",
|
||||||
"@types/ws": "8.5.10",
|
"@types/ws": "8.5.10",
|
||||||
"@typescript-eslint/eslint-plugin": "7.4.0",
|
"@typescript-eslint/eslint-plugin": "7.6.0",
|
||||||
"@typescript-eslint/parser": "7.4.0",
|
"@typescript-eslint/parser": "7.6.0",
|
||||||
"aws-sdk-client-mock": "4.0.0",
|
"aws-sdk-client-mock": "4.0.0",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"eslint": "8.57.0",
|
"eslint": "8.57.0",
|
||||||
|
|
|
@ -11,7 +11,11 @@ import {
|
||||||
verifyAuthenticationResponse,
|
verifyAuthenticationResponse,
|
||||||
verifyRegistrationResponse,
|
verifyRegistrationResponse,
|
||||||
} from '@simplewebauthn/server';
|
} from '@simplewebauthn/server';
|
||||||
import { AttestationFormat, isoCBOR } from '@simplewebauthn/server/helpers';
|
import {
|
||||||
|
AttestationFormat,
|
||||||
|
isoCBOR,
|
||||||
|
isoUint8Array,
|
||||||
|
} from '@simplewebauthn/server/helpers';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { UserSecurityKeysRepository } from '@/models/_.js';
|
import type { UserSecurityKeysRepository } from '@/models/_.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
|
@ -26,7 +30,6 @@ import type {
|
||||||
AuthenticatorTransportFuture,
|
AuthenticatorTransportFuture,
|
||||||
CredentialDeviceType,
|
CredentialDeviceType,
|
||||||
PublicKeyCredentialCreationOptionsJSON,
|
PublicKeyCredentialCreationOptionsJSON,
|
||||||
PublicKeyCredentialDescriptorFuture,
|
|
||||||
PublicKeyCredentialRequestOptionsJSON,
|
PublicKeyCredentialRequestOptionsJSON,
|
||||||
RegistrationResponseJSON,
|
RegistrationResponseJSON,
|
||||||
} from '@simplewebauthn/types';
|
} from '@simplewebauthn/types';
|
||||||
|
@ -56,7 +59,7 @@ export class WebAuthnService {
|
||||||
const instance = await this.metaService.fetch();
|
const instance = await this.metaService.fetch();
|
||||||
return {
|
return {
|
||||||
origin: this.config.url,
|
origin: this.config.url,
|
||||||
rpId: this.config.host,
|
rpId: this.config.hostname,
|
||||||
rpName: instance.name ?? this.config.host,
|
rpName: instance.name ?? this.config.host,
|
||||||
rpIcon: instance.iconUrl ?? undefined,
|
rpIcon: instance.iconUrl ?? undefined,
|
||||||
};
|
};
|
||||||
|
@ -72,13 +75,12 @@ export class WebAuthnService {
|
||||||
const registrationOptions = await generateRegistrationOptions({
|
const registrationOptions = await generateRegistrationOptions({
|
||||||
rpName: relyingParty.rpName,
|
rpName: relyingParty.rpName,
|
||||||
rpID: relyingParty.rpId,
|
rpID: relyingParty.rpId,
|
||||||
userID: userId,
|
userID: isoUint8Array.fromUTF8String(userId),
|
||||||
userName: userName,
|
userName: userName,
|
||||||
userDisplayName: userDisplayName,
|
userDisplayName: userDisplayName,
|
||||||
attestationType: 'indirect',
|
attestationType: 'indirect',
|
||||||
excludeCredentials: keys.map(key => (<PublicKeyCredentialDescriptorFuture>{
|
excludeCredentials: keys.map(key => (<{ id: string; transports?: AuthenticatorTransportFuture[]; }>{
|
||||||
id: Buffer.from(key.id, 'base64url'),
|
id: key.id,
|
||||||
type: 'public-key',
|
|
||||||
transports: key.transports ?? undefined,
|
transports: key.transports ?? undefined,
|
||||||
})),
|
})),
|
||||||
authenticatorSelection: {
|
authenticatorSelection: {
|
||||||
|
@ -94,7 +96,7 @@ export class WebAuthnService {
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async verifyRegistration(userId: MiUser['id'], response: RegistrationResponseJSON): Promise<{
|
public async verifyRegistration(userId: MiUser['id'], response: RegistrationResponseJSON): Promise<{
|
||||||
credentialID: Uint8Array;
|
credentialID: string;
|
||||||
credentialPublicKey: Uint8Array;
|
credentialPublicKey: Uint8Array;
|
||||||
attestationObject: Uint8Array;
|
attestationObject: Uint8Array;
|
||||||
fmt: AttestationFormat;
|
fmt: AttestationFormat;
|
||||||
|
@ -151,6 +153,7 @@ export class WebAuthnService {
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async initiateAuthentication(userId: MiUser['id']): Promise<PublicKeyCredentialRequestOptionsJSON> {
|
public async initiateAuthentication(userId: MiUser['id']): Promise<PublicKeyCredentialRequestOptionsJSON> {
|
||||||
|
const relyingParty = await this.getRelyingParty();
|
||||||
const keys = await this.userSecurityKeysRepository.findBy({
|
const keys = await this.userSecurityKeysRepository.findBy({
|
||||||
userId: userId,
|
userId: userId,
|
||||||
});
|
});
|
||||||
|
@ -160,9 +163,9 @@ export class WebAuthnService {
|
||||||
}
|
}
|
||||||
|
|
||||||
const authenticationOptions = await generateAuthenticationOptions({
|
const authenticationOptions = await generateAuthenticationOptions({
|
||||||
allowCredentials: keys.map(key => (<PublicKeyCredentialDescriptorFuture>{
|
rpID: relyingParty.rpId,
|
||||||
id: Buffer.from(key.id, 'base64url'),
|
allowCredentials: keys.map(key => (<{ id: string; transports?: AuthenticatorTransportFuture[]; }>{
|
||||||
type: 'public-key',
|
id: key.id,
|
||||||
transports: key.transports ?? undefined,
|
transports: key.transports ?? undefined,
|
||||||
})),
|
})),
|
||||||
userVerification: 'preferred',
|
userVerification: 'preferred',
|
||||||
|
@ -226,7 +229,7 @@ export class WebAuthnService {
|
||||||
expectedOrigin: relyingParty.origin,
|
expectedOrigin: relyingParty.origin,
|
||||||
expectedRPID: relyingParty.rpId,
|
expectedRPID: relyingParty.rpId,
|
||||||
authenticator: {
|
authenticator: {
|
||||||
credentialID: Buffer.from(key.id, 'base64url'),
|
credentialID: key.id,
|
||||||
credentialPublicKey: Buffer.from(key.publicKey, 'base64url'),
|
credentialPublicKey: Buffer.from(key.publicKey, 'base64url'),
|
||||||
counter: key.counter,
|
counter: key.counter,
|
||||||
transports: key.transports ? key.transports as AuthenticatorTransportFuture[] : undefined,
|
transports: key.transports ? key.transports as AuthenticatorTransportFuture[] : undefined,
|
||||||
|
|
|
@ -11,6 +11,8 @@ import * as ep___admin_abuseUserReports from './endpoints/admin/abuse-user-repor
|
||||||
import * as ep___admin_accounts_create from './endpoints/admin/accounts/create.js';
|
import * as ep___admin_accounts_create from './endpoints/admin/accounts/create.js';
|
||||||
import * as ep___admin_accounts_delete from './endpoints/admin/accounts/delete.js';
|
import * as ep___admin_accounts_delete from './endpoints/admin/accounts/delete.js';
|
||||||
import * as ep___admin_accounts_findByEmail from './endpoints/admin/accounts/find-by-email.js';
|
import * as ep___admin_accounts_findByEmail from './endpoints/admin/accounts/find-by-email.js';
|
||||||
|
import * as ep___admin_accounts_pending_list from './endpoints/admin/accounts/pending/list.js';
|
||||||
|
import * as ep___admin_accounts_pending_revoke from './endpoints/admin/accounts/pending/revoke.js';
|
||||||
import * as ep___admin_ad_create from './endpoints/admin/ad/create.js';
|
import * as ep___admin_ad_create from './endpoints/admin/ad/create.js';
|
||||||
import * as ep___admin_ad_delete from './endpoints/admin/ad/delete.js';
|
import * as ep___admin_ad_delete from './endpoints/admin/ad/delete.js';
|
||||||
import * as ep___admin_ad_list from './endpoints/admin/ad/list.js';
|
import * as ep___admin_ad_list from './endpoints/admin/ad/list.js';
|
||||||
|
@ -398,6 +400,8 @@ const $admin_abuseUserReports: Provider = { provide: 'ep:admin/abuse-user-report
|
||||||
const $admin_accounts_create: Provider = { provide: 'ep:admin/accounts/create', useClass: ep___admin_accounts_create.default };
|
const $admin_accounts_create: Provider = { provide: 'ep:admin/accounts/create', useClass: ep___admin_accounts_create.default };
|
||||||
const $admin_accounts_delete: Provider = { provide: 'ep:admin/accounts/delete', useClass: ep___admin_accounts_delete.default };
|
const $admin_accounts_delete: Provider = { provide: 'ep:admin/accounts/delete', useClass: ep___admin_accounts_delete.default };
|
||||||
const $admin_accounts_findByEmail: Provider = { provide: 'ep:admin/accounts/find-by-email', useClass: ep___admin_accounts_findByEmail.default };
|
const $admin_accounts_findByEmail: Provider = { provide: 'ep:admin/accounts/find-by-email', useClass: ep___admin_accounts_findByEmail.default };
|
||||||
|
const $admin_accounts_pending_list: Provider = { provide: 'ep:admin/accounts/pending/list', useClass: ep___admin_accounts_pending_list.default };
|
||||||
|
const $admin_accounts_pending_revoke: Provider = { provide: 'ep:admin/accounts/pending/revoke', useClass: ep___admin_accounts_pending_revoke.default };
|
||||||
const $admin_ad_create: Provider = { provide: 'ep:admin/ad/create', useClass: ep___admin_ad_create.default };
|
const $admin_ad_create: Provider = { provide: 'ep:admin/ad/create', useClass: ep___admin_ad_create.default };
|
||||||
const $admin_ad_delete: Provider = { provide: 'ep:admin/ad/delete', useClass: ep___admin_ad_delete.default };
|
const $admin_ad_delete: Provider = { provide: 'ep:admin/ad/delete', useClass: ep___admin_ad_delete.default };
|
||||||
const $admin_ad_list: Provider = { provide: 'ep:admin/ad/list', useClass: ep___admin_ad_list.default };
|
const $admin_ad_list: Provider = { provide: 'ep:admin/ad/list', useClass: ep___admin_ad_list.default };
|
||||||
|
@ -789,6 +793,8 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__
|
||||||
$admin_accounts_create,
|
$admin_accounts_create,
|
||||||
$admin_accounts_delete,
|
$admin_accounts_delete,
|
||||||
$admin_accounts_findByEmail,
|
$admin_accounts_findByEmail,
|
||||||
|
$admin_accounts_pending_list,
|
||||||
|
$admin_accounts_pending_revoke,
|
||||||
$admin_ad_create,
|
$admin_ad_create,
|
||||||
$admin_ad_delete,
|
$admin_ad_delete,
|
||||||
$admin_ad_list,
|
$admin_ad_list,
|
||||||
|
@ -1174,6 +1180,8 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__
|
||||||
$admin_accounts_create,
|
$admin_accounts_create,
|
||||||
$admin_accounts_delete,
|
$admin_accounts_delete,
|
||||||
$admin_accounts_findByEmail,
|
$admin_accounts_findByEmail,
|
||||||
|
$admin_accounts_pending_list,
|
||||||
|
$admin_accounts_pending_revoke,
|
||||||
$admin_ad_create,
|
$admin_ad_create,
|
||||||
$admin_ad_delete,
|
$admin_ad_delete,
|
||||||
$admin_ad_list,
|
$admin_ad_list,
|
||||||
|
|
|
@ -11,6 +11,8 @@ import * as ep___admin_abuseUserReports from './endpoints/admin/abuse-user-repor
|
||||||
import * as ep___admin_accounts_create from './endpoints/admin/accounts/create.js';
|
import * as ep___admin_accounts_create from './endpoints/admin/accounts/create.js';
|
||||||
import * as ep___admin_accounts_delete from './endpoints/admin/accounts/delete.js';
|
import * as ep___admin_accounts_delete from './endpoints/admin/accounts/delete.js';
|
||||||
import * as ep___admin_accounts_findByEmail from './endpoints/admin/accounts/find-by-email.js';
|
import * as ep___admin_accounts_findByEmail from './endpoints/admin/accounts/find-by-email.js';
|
||||||
|
import * as ep___admin_accounts_pending_list from './endpoints/admin/accounts/pending/list.js';
|
||||||
|
import * as ep___admin_accounts_pending_revoke from './endpoints/admin/accounts/pending/revoke.js';
|
||||||
import * as ep___admin_ad_create from './endpoints/admin/ad/create.js';
|
import * as ep___admin_ad_create from './endpoints/admin/ad/create.js';
|
||||||
import * as ep___admin_ad_delete from './endpoints/admin/ad/delete.js';
|
import * as ep___admin_ad_delete from './endpoints/admin/ad/delete.js';
|
||||||
import * as ep___admin_ad_list from './endpoints/admin/ad/list.js';
|
import * as ep___admin_ad_list from './endpoints/admin/ad/list.js';
|
||||||
|
@ -396,6 +398,8 @@ const eps = [
|
||||||
['admin/accounts/create', ep___admin_accounts_create],
|
['admin/accounts/create', ep___admin_accounts_create],
|
||||||
['admin/accounts/delete', ep___admin_accounts_delete],
|
['admin/accounts/delete', ep___admin_accounts_delete],
|
||||||
['admin/accounts/find-by-email', ep___admin_accounts_findByEmail],
|
['admin/accounts/find-by-email', ep___admin_accounts_findByEmail],
|
||||||
|
['admin/accounts/pending/list', ep___admin_accounts_pending_list],
|
||||||
|
['admin/accounts/pending/revoke', ep___admin_accounts_pending_revoke],
|
||||||
['admin/ad/create', ep___admin_ad_create],
|
['admin/ad/create', ep___admin_ad_create],
|
||||||
['admin/ad/delete', ep___admin_ad_delete],
|
['admin/ad/delete', ep___admin_ad_delete],
|
||||||
['admin/ad/list', ep___admin_ad_list],
|
['admin/ad/list', ep___admin_ad_list],
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import type { UserPendingsRepository } from '@/models/_.js';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
tags: ['admin'],
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
requireAdmin: true,
|
||||||
|
kind: 'read:admin:account',
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'array',
|
||||||
|
nullable: false, optional: false,
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
nullable: false, optional: false,
|
||||||
|
properties: {
|
||||||
|
id: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: false, optional: false,
|
||||||
|
format: 'id',
|
||||||
|
example: 'xxxxxxxxxx',
|
||||||
|
},
|
||||||
|
createdAt: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: false, optional: false,
|
||||||
|
format: 'date-time',
|
||||||
|
},
|
||||||
|
code: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: false, optional: false,
|
||||||
|
},
|
||||||
|
username: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: false, optional: false,
|
||||||
|
example: 'ai',
|
||||||
|
},
|
||||||
|
email: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: true, optional: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const paramDef = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||||
|
offset: { type: 'integer', default: 0 },
|
||||||
|
sort: { type: 'string', enum: ['+createdAt', '-createdAt'] },
|
||||||
|
username: { type: 'string', nullable: true, default: null },
|
||||||
|
email: { type: 'string', nullable: true, default: null },
|
||||||
|
code: { type: 'string', nullable: true, default: null },
|
||||||
|
},
|
||||||
|
required: [],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.userPendingsRepository)
|
||||||
|
private userPendingsRepository: UserPendingsRepository,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
const query = this.userPendingsRepository.createQueryBuilder('pending');
|
||||||
|
|
||||||
|
if (ps.username != null) {
|
||||||
|
query.andWhere('pending.username = :username', { username: ps.username });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ps.email != null) {
|
||||||
|
query.andWhere('pending.email = :email', { email: ps.email });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ps.code != null) {
|
||||||
|
query.andWhere('pending.code = :code', { code: ps.code });
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (ps.sort) {
|
||||||
|
case '+createdAt': query.orderBy('pending.id', 'DESC'); break;
|
||||||
|
case '-createdAt': query.orderBy('pending.id', 'ASC'); break;
|
||||||
|
default: query.orderBy('pending.id', 'DESC'); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
query.limit(ps.limit);
|
||||||
|
query.offset(ps.offset);
|
||||||
|
|
||||||
|
const pendings = await query.getMany();
|
||||||
|
return pendings.map(pending => ({
|
||||||
|
id: pending.id,
|
||||||
|
createdAt: pending.createdAt.toISOString(),
|
||||||
|
code: pending.code,
|
||||||
|
username: pending.username,
|
||||||
|
email: pending.email,
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import type { UserPendingsRepository } from '@/models/_.js';
|
||||||
|
import { ApiError } from '@/server/api/error.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
tags: ['admin'],
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
requireAdmin: true,
|
||||||
|
kind: 'write:admin:account',
|
||||||
|
|
||||||
|
errors: {
|
||||||
|
pendingUserNotFound: {
|
||||||
|
message: 'Pending User not found.',
|
||||||
|
code: 'PENDING_USER_NOT_FOUND',
|
||||||
|
id: 'a04d6118-65e6-4508-9144-9900a331bbf3',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const paramDef = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
id: { type: 'string', format: 'misskey:id' },
|
||||||
|
code: { type: 'string' },
|
||||||
|
},
|
||||||
|
anyOf: [
|
||||||
|
{ required: ['id'] },
|
||||||
|
{ required: ['code'] },
|
||||||
|
],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.userPendingsRepository)
|
||||||
|
private userPendingsRepository: UserPendingsRepository,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
const pendingUser = await this.userPendingsRepository.findOneBy({ id: ps.id, code: ps.code });
|
||||||
|
|
||||||
|
if (pendingUser == null) throw new ApiError(meta.errors.pendingUserNotFound);
|
||||||
|
|
||||||
|
await this.userPendingsRepository.delete(pendingUser.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -97,9 +97,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
|
|
||||||
const keyInfo = await this.webAuthnService.verifyRegistration(me.id, ps.credential);
|
const keyInfo = await this.webAuthnService.verifyRegistration(me.id, ps.credential);
|
||||||
|
|
||||||
const credentialId = Buffer.from(keyInfo.credentialID).toString('base64url');
|
|
||||||
await this.userSecurityKeysRepository.insert({
|
await this.userSecurityKeysRepository.insert({
|
||||||
id: credentialId,
|
id: keyInfo.credentialID,
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
name: ps.name,
|
name: ps.name,
|
||||||
publicKey: Buffer.from(keyInfo.credentialPublicKey).toString('base64url'),
|
publicKey: Buffer.from(keyInfo.credentialPublicKey).toString('base64url'),
|
||||||
|
@ -116,7 +115,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: credentialId,
|
id: keyInfo.credentialID,
|
||||||
name: ps.name,
|
name: ps.name,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { DI } from '@/di-symbols.js';
|
||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
import { L_CHARS, secureRndstr } from '@/misc/secure-rndstr.js';
|
import { L_CHARS, secureRndstr } from '@/misc/secure-rndstr.js';
|
||||||
import { UserAuthService } from '@/core/UserAuthService.js';
|
import { UserAuthService } from '@/core/UserAuthService.js';
|
||||||
|
import { MetaService } from '@/core/MetaService.js';
|
||||||
import { ApiError } from '../../error.js';
|
import { ApiError } from '../../error.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
|
@ -46,6 +47,12 @@ export const meta = {
|
||||||
code: 'UNAVAILABLE',
|
code: 'UNAVAILABLE',
|
||||||
id: 'a2defefb-f220-8849-0af6-17f816099323',
|
id: 'a2defefb-f220-8849-0af6-17f816099323',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
emailRequired: {
|
||||||
|
message: 'Email address is required.',
|
||||||
|
code: 'EMAIL_REQUIRED',
|
||||||
|
id: '324c7a88-59f2-492f-903f-89134f93e47e',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
res: {
|
res: {
|
||||||
|
@ -73,6 +80,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
@Inject(DI.userProfilesRepository)
|
@Inject(DI.userProfilesRepository)
|
||||||
private userProfilesRepository: UserProfilesRepository,
|
private userProfilesRepository: UserProfilesRepository,
|
||||||
|
|
||||||
|
private metaService: MetaService,
|
||||||
private userEntityService: UserEntityService,
|
private userEntityService: UserEntityService,
|
||||||
private emailService: EmailService,
|
private emailService: EmailService,
|
||||||
private userAuthService: UserAuthService,
|
private userAuthService: UserAuthService,
|
||||||
|
@ -100,6 +108,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
if (!res.available) {
|
if (!res.available) {
|
||||||
throw new ApiError(meta.errors.unavailable);
|
throw new ApiError(meta.errors.unavailable);
|
||||||
}
|
}
|
||||||
|
} else if ((await this.metaService.fetch()).emailRequiredForSignup) {
|
||||||
|
throw new ApiError(meta.errors.emailRequired);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.userProfilesRepository.update(me.id, {
|
await this.userProfilesRepository.update(me.id, {
|
||||||
|
|
|
@ -44,6 +44,7 @@ import type { MiLocalUser } from '@/models/User.js';
|
||||||
import { LoggerService } from '@/core/LoggerService.js';
|
import { LoggerService } from '@/core/LoggerService.js';
|
||||||
import Logger from '@/logger.js';
|
import Logger from '@/logger.js';
|
||||||
import { StatusError } from '@/misc/status-error.js';
|
import { StatusError } from '@/misc/status-error.js';
|
||||||
|
import { normalizeEmailAddress } from '@/misc/normalize-email-address.js';
|
||||||
import type { ServerResponse } from 'node:http';
|
import type { ServerResponse } from 'node:http';
|
||||||
import type { FastifyInstance } from 'fastify';
|
import type { FastifyInstance } from 'fastify';
|
||||||
|
|
||||||
|
@ -508,25 +509,31 @@ export class OAuth2ProviderService {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const accessToken = await this.accessTokensRepository.findOne({ where: { token }, relations: ['user'] });
|
const accessToken = await this.accessTokensRepository.findOneBy({ token });
|
||||||
if (!accessToken) {
|
if (!accessToken) {
|
||||||
reply.code(401);
|
reply.code(401);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await this.userProfilesRepository.findOneBy({ userId: accessToken.userId });
|
const user = await this.usersRepository.findOneBy({ id: accessToken.userId });
|
||||||
|
if (!user) {
|
||||||
|
reply.code(401);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id });
|
||||||
|
|
||||||
reply.code(200);
|
reply.code(200);
|
||||||
return {
|
return {
|
||||||
sub: accessToken.userId,
|
sub: user.id,
|
||||||
name: accessToken.user?.name,
|
name: user.name ? `${user.name} (@${user.username})` : `@${user.username}`,
|
||||||
preferred_username: accessToken.user?.username,
|
preferred_username: user.username,
|
||||||
profile: accessToken.user ? `${this.config.url}/@${accessToken.user.username}` : undefined,
|
profile: `${this.config.url}/@${user.username}`,
|
||||||
picture: accessToken.user?.avatarUrl,
|
picture: user.avatarUrl ?? undefined,
|
||||||
email: user?.email,
|
email: profile.emailVerified ? normalizeEmailAddress(profile.email) : `${user.username}@${this.config.hostname}`,
|
||||||
email_verified: user?.emailVerified,
|
email_verified: profile.emailVerified,
|
||||||
mfa_enabled: user?.twoFactorEnabled,
|
mfa_enabled: profile.twoFactorEnabled,
|
||||||
updated_at: Math.floor((accessToken.user?.updatedAt?.getTime() ?? accessToken.user?.createdAt.getTime() ?? 0) / 1000),
|
updated_at: Math.floor((user.updatedAt?.getTime() ?? user.createdAt.getTime()) / 1000),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -178,7 +178,7 @@ export class JWTIdentifyProviderService {
|
||||||
preferred_username: user.username,
|
preferred_username: user.username,
|
||||||
profile: `${this.config.url}/@${user.username}`,
|
profile: `${this.config.url}/@${user.username}`,
|
||||||
picture: user.avatarUrl ?? undefined,
|
picture: user.avatarUrl ?? undefined,
|
||||||
email: profile.emailVerified ? normalizeEmailAddress(profile.email) : undefined,
|
email: profile.emailVerified ? normalizeEmailAddress(profile.email) : `${user.username}@${this.config.hostname}`,
|
||||||
email_verified: profile.emailVerified,
|
email_verified: profile.emailVerified,
|
||||||
mfa_enabled: profile.twoFactorEnabled,
|
mfa_enabled: profile.twoFactorEnabled,
|
||||||
updated_at: Math.floor((user.updatedAt?.getTime() ?? user.createdAt.getTime()) / 1000),
|
updated_at: Math.floor((user.updatedAt?.getTime() ?? user.createdAt.getTime()) / 1000),
|
||||||
|
|
|
@ -440,9 +440,10 @@ export class SAMLIdentifyProviderService {
|
||||||
'#text': `${this.config.url}/sso/saml/${ssoServiceProvider.id}/metadata`,
|
'#text': `${this.config.url}/sso/saml/${ssoServiceProvider.id}/metadata`,
|
||||||
},
|
},
|
||||||
'saml:Subject': {
|
'saml:Subject': {
|
||||||
'saml:NameID': profile.emailVerified
|
'saml:NameID': {
|
||||||
? { '@Format': 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress', '#text': normalizeEmailAddress(profile.email) }
|
'@Format': 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
|
||||||
: { '@Format': 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent', '#text': user.id },
|
'#text': profile.emailVerified ? normalizeEmailAddress(profile.email) : `${user.username}@${this.config.hostname}`,
|
||||||
|
},
|
||||||
'saml:SubjectConfirmation': {
|
'saml:SubjectConfirmation': {
|
||||||
'@Method': 'urn:oasis:names:tc:SAML:2.0:cm:bearer',
|
'@Method': 'urn:oasis:names:tc:SAML:2.0:cm:bearer',
|
||||||
'saml:SubjectConfirmationData': {
|
'saml:SubjectConfirmationData': {
|
||||||
|
@ -540,14 +541,14 @@ export class SAMLIdentifyProviderService {
|
||||||
'#text': user.avatarUrl,
|
'#text': user.avatarUrl,
|
||||||
},
|
},
|
||||||
}] : []),
|
}] : []),
|
||||||
...(profile.emailVerified ? [{
|
{
|
||||||
'@Name': 'email',
|
'@Name': 'email',
|
||||||
'@NameFormat': 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic',
|
'@NameFormat': 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic',
|
||||||
'saml:AttributeValue': {
|
'saml:AttributeValue': {
|
||||||
'@xsi:type': 'xs:string',
|
'@xsi:type': 'xs:string',
|
||||||
'#text': normalizeEmailAddress(profile.email),
|
'#text': profile.emailVerified ? normalizeEmailAddress(profile.email) : `${user.username}@${this.config.hostname}`,
|
||||||
},
|
},
|
||||||
}] : []),
|
},
|
||||||
{
|
{
|
||||||
'@Name': 'email_verified',
|
'@Name': 'email_verified',
|
||||||
'@NameFormat': 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic',
|
'@NameFormat': 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic',
|
||||||
|
|
|
@ -27,8 +27,8 @@
|
||||||
"@rollup/plugin-typescript": "11.1.6",
|
"@rollup/plugin-typescript": "11.1.6",
|
||||||
"@rollup/pluginutils": "5.1.0",
|
"@rollup/pluginutils": "5.1.0",
|
||||||
"@syuilo/aiscript": "0.17.0",
|
"@syuilo/aiscript": "0.17.0",
|
||||||
"@tabler/icons-webfont": "3.1.0",
|
"@tabler/icons-webfont": "3.2.0",
|
||||||
"@twemoji/parser": "15.1.0",
|
"@twemoji/parser": "15.1.1",
|
||||||
"@vitejs/plugin-vue": "5.0.4",
|
"@vitejs/plugin-vue": "5.0.4",
|
||||||
"@vue/compiler-sfc": "3.4.15",
|
"@vue/compiler-sfc": "3.4.15",
|
||||||
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.2",
|
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.2",
|
||||||
|
@ -59,10 +59,10 @@
|
||||||
"misskey-reversi": "workspace:*",
|
"misskey-reversi": "workspace:*",
|
||||||
"photoswipe": "5.4.3",
|
"photoswipe": "5.4.3",
|
||||||
"punycode": "2.3.1",
|
"punycode": "2.3.1",
|
||||||
"rollup": "4.13.2",
|
"rollup": "4.14.2",
|
||||||
"sanitize-html": "2.13.0",
|
"sanitize-html": "2.13.0",
|
||||||
"sass": "1.72.0",
|
"sass": "1.75.0",
|
||||||
"shiki": "1.2.1",
|
"shiki": "1.3.0",
|
||||||
"strict-event-emitter-types": "2.0.0",
|
"strict-event-emitter-types": "2.0.0",
|
||||||
"textarea-caret": "3.1.0",
|
"textarea-caret": "3.1.0",
|
||||||
"three": "0.163.0",
|
"three": "0.163.0",
|
||||||
|
@ -70,56 +70,56 @@
|
||||||
"tinycolor2": "1.6.0",
|
"tinycolor2": "1.6.0",
|
||||||
"tsc-alias": "1.8.8",
|
"tsc-alias": "1.8.8",
|
||||||
"tsconfig-paths": "4.2.0",
|
"tsconfig-paths": "4.2.0",
|
||||||
"typescript": "5.4.3",
|
"typescript": "5.4.5",
|
||||||
"uuid": "9.0.1",
|
"uuid": "9.0.1",
|
||||||
"v-code-diff": "1.11.0",
|
"v-code-diff": "1.11.0",
|
||||||
"vite": "5.2.7",
|
"vite": "5.2.8",
|
||||||
"vue": "3.4.15",
|
"vue": "3.4.15",
|
||||||
"vuedraggable": "next"
|
"vuedraggable": "next"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@misskey-dev/eslint-plugin": "1.0.0",
|
"@misskey-dev/eslint-plugin": "1.0.0",
|
||||||
"@misskey-dev/summaly": "5.1.0",
|
"@misskey-dev/summaly": "5.1.0",
|
||||||
"@storybook/addon-actions": "8.0.5",
|
"@storybook/addon-actions": "8.0.8",
|
||||||
"@storybook/addon-essentials": "8.0.5",
|
"@storybook/addon-essentials": "8.0.8",
|
||||||
"@storybook/addon-interactions": "8.0.5",
|
"@storybook/addon-interactions": "8.0.8",
|
||||||
"@storybook/addon-links": "8.0.5",
|
"@storybook/addon-links": "8.0.8",
|
||||||
"@storybook/addon-mdx-gfm": "8.0.5",
|
"@storybook/addon-mdx-gfm": "8.0.8",
|
||||||
"@storybook/addon-storysource": "8.0.5",
|
"@storybook/addon-storysource": "8.0.8",
|
||||||
"@storybook/blocks": "8.0.5",
|
"@storybook/blocks": "8.0.8",
|
||||||
"@storybook/components": "8.0.5",
|
"@storybook/components": "8.0.8",
|
||||||
"@storybook/core-events": "8.0.5",
|
"@storybook/core-events": "8.0.8",
|
||||||
"@storybook/manager-api": "8.0.5",
|
"@storybook/manager-api": "8.0.8",
|
||||||
"@storybook/preview-api": "8.0.5",
|
"@storybook/preview-api": "8.0.8",
|
||||||
"@storybook/react": "8.0.5",
|
"@storybook/react": "8.0.8",
|
||||||
"@storybook/react-vite": "8.0.5",
|
"@storybook/react-vite": "8.0.8",
|
||||||
"@storybook/test": "8.0.5",
|
"@storybook/test": "8.0.8",
|
||||||
"@storybook/theming": "8.0.5",
|
"@storybook/theming": "8.0.8",
|
||||||
"@storybook/types": "8.0.5",
|
"@storybook/types": "8.0.8",
|
||||||
"@storybook/vue3": "8.0.5",
|
"@storybook/vue3": "8.0.8",
|
||||||
"@storybook/vue3-vite": "8.0.5",
|
"@storybook/vue3-vite": "8.0.8",
|
||||||
"@testing-library/vue": "8.0.3",
|
"@testing-library/vue": "8.0.3",
|
||||||
"@types/escape-regexp": "0.0.3",
|
"@types/escape-regexp": "0.0.3",
|
||||||
"@types/estree": "1.0.5",
|
"@types/estree": "1.0.5",
|
||||||
"@types/matter-js": "0.19.6",
|
"@types/matter-js": "0.19.6",
|
||||||
"@types/micromatch": "4.0.6",
|
"@types/micromatch": "4.0.6",
|
||||||
"@types/node": "20.12.2",
|
"@types/node": "20.12.7",
|
||||||
"@types/punycode": "2.1.4",
|
"@types/punycode": "2.1.4",
|
||||||
"@types/sanitize-html": "2.11.0",
|
"@types/sanitize-html": "2.11.0",
|
||||||
"@types/throttle-debounce": "5.0.2",
|
"@types/throttle-debounce": "5.0.2",
|
||||||
"@types/tinycolor2": "1.4.6",
|
"@types/tinycolor2": "1.4.6",
|
||||||
"@types/uuid": "9.0.8",
|
"@types/uuid": "9.0.8",
|
||||||
"@types/ws": "8.5.10",
|
"@types/ws": "8.5.10",
|
||||||
"@typescript-eslint/eslint-plugin": "7.4.0",
|
"@typescript-eslint/eslint-plugin": "7.6.0",
|
||||||
"@typescript-eslint/parser": "7.4.0",
|
"@typescript-eslint/parser": "7.6.0",
|
||||||
"@vitest/coverage-v8": "0.34.6",
|
"@vitest/coverage-v8": "0.34.6",
|
||||||
"@vue/runtime-core": "3.4.15",
|
"@vue/runtime-core": "3.4.15",
|
||||||
"acorn": "8.11.3",
|
"acorn": "8.11.3",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"cypress": "13.7.1",
|
"cypress": "13.7.3",
|
||||||
"eslint": "8.57.0",
|
"eslint": "8.57.0",
|
||||||
"eslint-plugin-import": "2.29.1",
|
"eslint-plugin-import": "2.29.1",
|
||||||
"eslint-plugin-vue": "9.24.0",
|
"eslint-plugin-vue": "9.24.1",
|
||||||
"fast-glob": "3.3.2",
|
"fast-glob": "3.3.2",
|
||||||
"happy-dom": "13.6.2",
|
"happy-dom": "13.6.2",
|
||||||
"intersection-observer": "0.12.2",
|
"intersection-observer": "0.12.2",
|
||||||
|
@ -131,13 +131,13 @@
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"start-server-and-test": "2.0.3",
|
"start-server-and-test": "2.0.3",
|
||||||
"storybook": "8.0.5",
|
"storybook": "8.0.8",
|
||||||
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
|
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
|
||||||
"vite-plugin-turbosnap": "1.0.3",
|
"vite-plugin-turbosnap": "1.0.3",
|
||||||
"vitest": "0.34.6",
|
"vitest": "0.34.6",
|
||||||
"vitest-fetch-mock": "0.2.2",
|
"vitest-fetch-mock": "0.2.2",
|
||||||
"vue-component-type-helpers": "2.0.7",
|
"vue-component-type-helpers": "2.0.13",
|
||||||
"vue-eslint-parser": "9.4.2",
|
"vue-eslint-parser": "9.4.2",
|
||||||
"vue-tsc": "2.0.7"
|
"vue-tsc": "2.0.13"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<option v-for="item in select.items" :value="item.value">{{ item.text }}</option>
|
<option v-for="item in select.items" :value="item.value">{{ item.text }}</option>
|
||||||
</template>
|
</template>
|
||||||
</MkSelect>
|
</MkSelect>
|
||||||
|
<MkSwitch v-if="switchLabel" v-model="switchValue" style="display: flex; margin: 1em 0; justify-content: center;">{{ switchLabel }}</MkSwitch>
|
||||||
<details v-if="details" class="_acrylic" style="margin: 1em 0;">
|
<details v-if="details" class="_acrylic" style="margin: 1em 0;">
|
||||||
<summary>{{ i18n.ts.details }}</summary>
|
<summary>{{ i18n.ts.details }}</summary>
|
||||||
<div class="_gaps_s" style="text-align: initial;">
|
<div class="_gaps_s" style="text-align: initial;">
|
||||||
|
@ -58,7 +59,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
<div v-if="(showOkButton || showCancelButton) && !actions" :class="$style.buttons">
|
<div v-if="(showOkButton || showCancelButton) && !actions" :class="$style.buttons">
|
||||||
<MkButton v-if="showOkButton" data-cy-modal-dialog-ok inline primary rounded :autofocus="!input && !select" :disabled="okButtonDisabledReason" @click="ok">{{ okText ?? ((showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt) }}</MkButton>
|
<MkButton v-if="showOkButton" data-cy-modal-dialog-ok inline primary rounded :autofocus="!input && !select" :disabled="okDisabled || okButtonDisabledReason" @click="ok">{{ okText ?? ((showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt) }}<span v-if="okDisabled && okWaitInitiated"> ({{ sec }})</span></MkButton>
|
||||||
<MkButton v-if="showCancelButton || input || select" data-cy-modal-dialog-cancel inline rounded @click="cancel">{{ cancelText ?? i18n.ts.cancel }}</MkButton>
|
<MkButton v-if="showCancelButton || input || select" data-cy-modal-dialog-cancel inline rounded @click="cancel">{{ cancelText ?? i18n.ts.cancel }}</MkButton>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="actions" :class="$style.buttons">
|
<div v-if="actions" :class="$style.buttons">
|
||||||
|
@ -69,11 +70,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onBeforeUnmount, onMounted, ref, shallowRef, computed } from 'vue';
|
import { onBeforeUnmount, onMounted, ref, shallowRef, computed, watch } from 'vue';
|
||||||
import MkModal from '@/components/MkModal.vue';
|
import MkModal from '@/components/MkModal.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
import MkSelect from '@/components/MkSelect.vue';
|
import MkSelect from '@/components/MkSelect.vue';
|
||||||
|
import MkSwitch from '@/components/MkSwitch.vue';
|
||||||
import MkTextarea from '@/components/MkTextarea.vue';
|
import MkTextarea from '@/components/MkTextarea.vue';
|
||||||
import MkKeyValue from '@/components/MkKeyValue.vue';
|
import MkKeyValue from '@/components/MkKeyValue.vue';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
|
@ -104,6 +106,7 @@ const props = withDefaults(defineProps<{
|
||||||
text?: string | null;
|
text?: string | null;
|
||||||
input?: Input;
|
input?: Input;
|
||||||
select?: Select;
|
select?: Select;
|
||||||
|
switchLabel?: string | null;
|
||||||
details?: Record<string, string>;
|
details?: Record<string, string>;
|
||||||
actions?: {
|
actions?: {
|
||||||
text: string;
|
text: string;
|
||||||
|
@ -115,6 +118,8 @@ const props = withDefaults(defineProps<{
|
||||||
showCancelButton?: boolean;
|
showCancelButton?: boolean;
|
||||||
cancelableByBgClick?: boolean;
|
cancelableByBgClick?: boolean;
|
||||||
okText?: string;
|
okText?: string;
|
||||||
|
okWaitInitiate?: 'dialog' | 'input' | 'switch';
|
||||||
|
okWaitDuration?: number;
|
||||||
cancelText?: string;
|
cancelText?: string;
|
||||||
}>(), {
|
}>(), {
|
||||||
type: 'info',
|
type: 'info',
|
||||||
|
@ -123,17 +128,20 @@ const props = withDefaults(defineProps<{
|
||||||
text: undefined,
|
text: undefined,
|
||||||
input: undefined,
|
input: undefined,
|
||||||
select: undefined,
|
select: undefined,
|
||||||
|
switchLabel: undefined,
|
||||||
details: undefined,
|
details: undefined,
|
||||||
actions: undefined,
|
actions: undefined,
|
||||||
showOkButton: true,
|
showOkButton: true,
|
||||||
showCancelButton: false,
|
showCancelButton: false,
|
||||||
cancelableByBgClick: true,
|
cancelableByBgClick: true,
|
||||||
okText: undefined,
|
okText: undefined,
|
||||||
|
okWaitInitiate: undefined,
|
||||||
|
okWaitDuration: 0,
|
||||||
cancelText: undefined,
|
cancelText: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(ev: 'done', v: { canceled: true } | { canceled: false, result: Result }): void;
|
(ev: 'done', v: { canceled: true } | { canceled: false, result: Result, toggle: boolean }): void;
|
||||||
(ev: 'closed'): void;
|
(ev: 'closed'): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
@ -141,6 +149,16 @@ const modal = shallowRef<InstanceType<typeof MkModal>>();
|
||||||
|
|
||||||
const inputValue = ref<string | number | null>(props.input?.default ?? null);
|
const inputValue = ref<string | number | null>(props.input?.default ?? null);
|
||||||
const selectedValue = ref(props.select?.default ?? null);
|
const selectedValue = ref(props.select?.default ?? null);
|
||||||
|
const switchValue = ref<boolean>(false);
|
||||||
|
|
||||||
|
const sec = ref(props.okWaitDuration);
|
||||||
|
const okWaitInitiated = computed(() => {
|
||||||
|
if (props.okWaitInitiate === 'dialog') return true;
|
||||||
|
if (props.okWaitInitiate === 'input') return inputValue.value !== null;
|
||||||
|
if (props.okWaitInitiate === 'switch') return switchValue.value;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
const okDisabled = computed(() => sec.value > 0);
|
||||||
|
|
||||||
const okButtonDisabledReason = computed<null | 'charactersExceeded' | 'charactersBelow'>(() => {
|
const okButtonDisabledReason = computed<null | 'charactersExceeded' | 'charactersBelow'>(() => {
|
||||||
if (props.input) {
|
if (props.input) {
|
||||||
|
@ -161,9 +179,9 @@ const okButtonDisabledReason = computed<null | 'charactersExceeded' | 'character
|
||||||
|
|
||||||
// overload function を使いたいので lint エラーを無視する
|
// overload function を使いたいので lint エラーを無視する
|
||||||
function done(canceled: true): void;
|
function done(canceled: true): void;
|
||||||
function done(canceled: false, result: Result): void; // eslint-disable-line no-redeclare
|
function done(canceled: false, result: Result, toggle: boolean): void; // eslint-disable-line no-redeclare
|
||||||
function done(canceled: boolean, result?: Result): void { // eslint-disable-line no-redeclare
|
function done(canceled: boolean, result?: Result, toggle?: boolean ): void { // eslint-disable-line no-redeclare
|
||||||
emit('done', { canceled, result } as { canceled: true } | { canceled: false, result: Result });
|
emit('done', { canceled, result, toggle } as { canceled: true } | { canceled: false, result: Result, toggle: boolean });
|
||||||
modal.value?.close();
|
modal.value?.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,18 +192,13 @@ async function ok() {
|
||||||
props.input ? inputValue.value :
|
props.input ? inputValue.value :
|
||||||
props.select ? selectedValue.value :
|
props.select ? selectedValue.value :
|
||||||
true;
|
true;
|
||||||
done(false, result);
|
done(false, result, switchValue.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
function cancel() {
|
function cancel() {
|
||||||
done(true);
|
done(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
function onBgClick() {
|
|
||||||
if (props.cancelableByBgClick) cancel();
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
function onKeydown(evt: KeyboardEvent) {
|
function onKeydown(evt: KeyboardEvent) {
|
||||||
if (evt.key === 'Escape') cancel();
|
if (evt.key === 'Escape') cancel();
|
||||||
}
|
}
|
||||||
|
@ -198,8 +211,24 @@ function onInputKeydown(evt: KeyboardEvent) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watch(okWaitInitiated, () => {
|
||||||
|
sec.value = props.okWaitDuration;
|
||||||
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
document.addEventListener('keydown', onKeydown);
|
document.addEventListener('keydown', onKeydown);
|
||||||
|
|
||||||
|
sec.value = props.okWaitDuration;
|
||||||
|
if (sec.value > 0) {
|
||||||
|
const waitTimer = setInterval(() => {
|
||||||
|
if (!okWaitInitiated.value) return;
|
||||||
|
|
||||||
|
if (sec.value < 0) {
|
||||||
|
clearInterval(waitTimer);
|
||||||
|
}
|
||||||
|
sec.value = sec.value - 1;
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
|
|
|
@ -162,7 +162,9 @@ const props = withDefaults(defineProps<{
|
||||||
provide('mock', props.mock);
|
provide('mock', props.mock);
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
(ev: 'posting'): void;
|
||||||
(ev: 'posted'): void;
|
(ev: 'posted'): void;
|
||||||
|
(ev: 'postError'): void;
|
||||||
(ev: 'cancel'): void;
|
(ev: 'cancel'): void;
|
||||||
(ev: 'esc'): void;
|
(ev: 'esc'): void;
|
||||||
|
|
||||||
|
@ -861,7 +863,9 @@ async function post(ev?: MouseEvent) {
|
||||||
type: 'error',
|
type: 'error',
|
||||||
text: err.message + '\n' + (err as any).id,
|
text: err.message + '\n' + (err as any).id,
|
||||||
});
|
});
|
||||||
|
emit("postError");
|
||||||
});
|
});
|
||||||
|
emit("posting");
|
||||||
}
|
}
|
||||||
|
|
||||||
function cancel() {
|
function cancel() {
|
||||||
|
|
|
@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkModal ref="modal" :preferType="'dialog'" @click="modal?.close()" @closed="onModalClosed()">
|
<MkModal ref="modal" :preferType="'dialog'" @click="modal?.close()" @closed="onModalClosed()">
|
||||||
<MkPostForm ref="form" :class="$style.form" v-bind="props" autofocus freezeAfterPosted @posted="onPosted" @cancel="modal?.close()" @esc="modal?.close()"/>
|
<MkPostForm ref="form" :class="$style.form" v-bind="props" autofocus freezeAfterPosted @posting="onPosting" @postError="onPostError" @cancel="modal?.close()" @esc="modal?.close()"/>
|
||||||
</MkModal>
|
</MkModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ import { shallowRef } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import MkModal from '@/components/MkModal.vue';
|
import MkModal from '@/components/MkModal.vue';
|
||||||
import MkPostForm from '@/components/MkPostForm.vue';
|
import MkPostForm from '@/components/MkPostForm.vue';
|
||||||
|
import * as os from '@/os.js';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
reply?: Misskey.entities.Note;
|
reply?: Misskey.entities.Note;
|
||||||
|
@ -40,12 +41,16 @@ const emit = defineEmits<{
|
||||||
const modal = shallowRef<InstanceType<typeof MkModal>>();
|
const modal = shallowRef<InstanceType<typeof MkModal>>();
|
||||||
const form = shallowRef<InstanceType<typeof MkPostForm>>();
|
const form = shallowRef<InstanceType<typeof MkPostForm>>();
|
||||||
|
|
||||||
function onPosted() {
|
function onPosting() {
|
||||||
modal.value?.close({
|
modal.value?.close({
|
||||||
useSendAnimation: true,
|
useSendAnimation: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onPostError() {
|
||||||
|
os.post();
|
||||||
|
}
|
||||||
|
|
||||||
function onModalClosed() {
|
function onModalClosed() {
|
||||||
emit('closed');
|
emit('closed');
|
||||||
}
|
}
|
||||||
|
|
|
@ -221,7 +221,10 @@ export function alert(props: {
|
||||||
type?: 'error' | 'info' | 'success' | 'warning' | 'waiting' | 'question';
|
type?: 'error' | 'info' | 'success' | 'warning' | 'waiting' | 'question';
|
||||||
title?: string | null;
|
title?: string | null;
|
||||||
text?: string | null;
|
text?: string | null;
|
||||||
|
switchLabel?: string | null;
|
||||||
details?: Record<string, string>;
|
details?: Record<string, string>;
|
||||||
|
okWaitInitiate?: 'dialog' | 'input' | 'switch';
|
||||||
|
okWaitDuration?: number;
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
popup(MkDialog, props, {
|
popup(MkDialog, props, {
|
||||||
|
@ -236,10 +239,13 @@ export function confirm(props: {
|
||||||
type: 'error' | 'info' | 'success' | 'warning' | 'waiting' | 'question';
|
type: 'error' | 'info' | 'success' | 'warning' | 'waiting' | 'question';
|
||||||
title?: string | null;
|
title?: string | null;
|
||||||
text?: string | null;
|
text?: string | null;
|
||||||
|
switchLabel?: string | null;
|
||||||
details?: Record<string, string>;
|
details?: Record<string, string>;
|
||||||
okText?: string;
|
okText?: string;
|
||||||
|
okWaitInitiate?: 'dialog' | 'input' | 'switch';
|
||||||
|
okWaitDuration?: number;
|
||||||
cancelText?: string;
|
cancelText?: string;
|
||||||
}): Promise<{ canceled: boolean }> {
|
}): Promise<{ canceled: boolean, result?: string | number | true | null, toggle?: boolean }> {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
popup(MkDialog, {
|
popup(MkDialog, {
|
||||||
...props,
|
...props,
|
||||||
|
|
|
@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkModalWindow
|
<MkModalWindow
|
||||||
ref="dialog"
|
ref="dialog"
|
||||||
:width="500"
|
:width="500"
|
||||||
:height="550"
|
:height="600"
|
||||||
@close="cancel"
|
@close="cancel"
|
||||||
@closed="emit('closed')"
|
@closed="emit('closed')"
|
||||||
>
|
>
|
||||||
|
@ -76,15 +76,11 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<div class="_gaps">
|
<div class="_gaps">
|
||||||
<MkInfo warn>{{ i18n.ts._2fa.backupCodesDescription }}</MkInfo>
|
<MkInfo warn>{{ i18n.ts._2fa.backupCodesDescription }}</MkInfo>
|
||||||
|
<MkButton primary rounded gradate full @click="downloadBackupCodes"><i class="ti ti-download"></i> {{ i18n.ts.download }}</MkButton>
|
||||||
|
|
||||||
<div v-for="(code, i) in backupCodes" :key="code" class="_gaps_s">
|
<div v-for="(code, i) in backupCodes" :key="code" class="_gaps_s">
|
||||||
<MkKeyValue :copy="code">
|
<span style="text-align: center;">#{{ i + 1 }}. <code class="_monospace">{{ code }}</code></span>
|
||||||
<template #key>#{{ i + 1 }}</template>
|
|
||||||
<template #value><code class="_monospace">{{ code }}</code></template>
|
|
||||||
</MkKeyValue>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<MkButton primary rounded gradate @click="downloadBackupCodes"><i class="ti ti-download"></i> {{ i18n.ts.download }}</MkButton>
|
|
||||||
</div>
|
</div>
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
</div>
|
</div>
|
||||||
|
@ -107,6 +103,7 @@ import MkKeyValue from '@/components/MkKeyValue.vue';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
|
import * as config from '@/config.js';
|
||||||
import MkFolder from '@/components/MkFolder.vue';
|
import MkFolder from '@/components/MkFolder.vue';
|
||||||
import MkInfo from '@/components/MkInfo.vue';
|
import MkInfo from '@/components/MkInfo.vue';
|
||||||
import { confetti } from '@/scripts/confetti.js';
|
import { confetti } from '@/scripts/confetti.js';
|
||||||
|
@ -131,7 +128,8 @@ const token = ref<string | number | null>(null);
|
||||||
const backupCodes = ref<string[]>();
|
const backupCodes = ref<string[]>();
|
||||||
|
|
||||||
function cancel() {
|
function cancel() {
|
||||||
dialog.value.close();
|
if (page.value !== 2) dialog.value?.close();
|
||||||
|
else allDone();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function tokenDone() {
|
async function tokenDone() {
|
||||||
|
@ -150,15 +148,27 @@ async function tokenDone() {
|
||||||
|
|
||||||
function downloadBackupCodes() {
|
function downloadBackupCodes() {
|
||||||
if (backupCodes.value !== undefined) {
|
if (backupCodes.value !== undefined) {
|
||||||
const txtBlob = new Blob([backupCodes.value.join('\n')], { type: 'text/plain' });
|
const txtBlob = new Blob([backupCodes.value.reduce((acc, code, i) => `${acc}#${i + 1}. ${code}\r\n`, `${config.hostname} 2FA Backup Codes\r\n\r\n`)], { type: 'text/plain' });
|
||||||
const dummya = document.createElement('a');
|
const dummya = document.createElement('a');
|
||||||
dummya.href = URL.createObjectURL(txtBlob);
|
dummya.href = URL.createObjectURL(txtBlob);
|
||||||
dummya.download = `${$i.username}-2fa-backup-codes.txt`;
|
dummya.download = `${config.hostname}-${$i.username}-2fa-backup-codes.txt`;
|
||||||
dummya.click();
|
dummya.click();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function allDone() {
|
async function allDone() {
|
||||||
|
const { canceled } = await os.confirm({
|
||||||
|
type: 'warning',
|
||||||
|
title: i18n.ts._2fa.backupCodesSavedConfirmTitle,
|
||||||
|
text: i18n.ts._2fa.backupCodesSavedConfirmDescription,
|
||||||
|
switchLabel: i18n.ts._2fa.backupCodesSavedConfirmChecked,
|
||||||
|
okText: i18n.ts.gotIt,
|
||||||
|
okWaitInitiate: 'switch',
|
||||||
|
okWaitDuration: 5,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (canceled) return;
|
||||||
|
|
||||||
dialog.value.close();
|
dialog.value.close();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -21,16 +21,22 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<template #caption>{{ i18n.ts.totpDescription }}</template>
|
<template #caption>{{ i18n.ts.totpDescription }}</template>
|
||||||
<template #suffix><i v-if="$i.twoFactorEnabled" class="ti ti-check" style="color: var(--success)"></i></template>
|
<template #suffix><i v-if="$i.twoFactorEnabled" class="ti ti-check" style="color: var(--success)"></i></template>
|
||||||
|
|
||||||
<div v-if="$i.twoFactorEnabled" class="_gaps_s">
|
<div class="_gaps_s">
|
||||||
<div v-text="i18n.ts._2fa.alreadyRegistered"/>
|
<MkInfo>
|
||||||
<template v-if="$i.securityKeysList.length > 0">
|
<Mfm :text="i18n.tsx._2fa.howto2fa({ link: `[${i18n.ts.here}](https://go.misskey.io/howto-2fa)`})"/>
|
||||||
<MkButton @click="renewTOTP">{{ i18n.ts._2fa.renewTOTP }}</MkButton>
|
</MkInfo>
|
||||||
<MkInfo>{{ i18n.ts._2fa.whyTOTPOnlyRenew }}</MkInfo>
|
<MkInfo v-if="$i.securityKeysList.length > 0">{{ i18n.ts._2fa.whyTOTPOnlyRenew }}</MkInfo>
|
||||||
</template>
|
|
||||||
<MkButton v-else danger @click="unregisterTOTP">{{ i18n.ts.unregister }}</MkButton>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<MkButton v-else-if="!$i.twoFactorEnabled" primary gradate @click="registerTOTP">{{ i18n.ts._2fa.registerTOTP }}</MkButton>
|
<div v-if="$i.twoFactorEnabled" class="_gaps_s">
|
||||||
|
<div v-text="i18n.ts._2fa.alreadyRegistered"/>
|
||||||
|
<MkButton v-if="$i.securityKeysList.length > 0" @click="renewTOTP">{{ i18n.ts._2fa.renewTOTP }}</MkButton>
|
||||||
|
<MkButton v-else danger @click="unregisterTOTP">{{ i18n.ts.unregister }}</MkButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<MkButton v-else-if="!$i.twoFactorEnabled" primary gradate @click="registerTOTP">
|
||||||
|
{{ i18n.ts._2fa.registerTOTP }}
|
||||||
|
</MkButton>
|
||||||
|
</div>
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
|
|
||||||
<MkFolder>
|
<MkFolder>
|
||||||
|
|
|
@ -7,11 +7,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<div v-if="instance.enableEmail" class="_gaps_m">
|
<div v-if="instance.enableEmail" class="_gaps_m">
|
||||||
<FormSection first>
|
<FormSection first>
|
||||||
<template #label>{{ i18n.ts.emailAddress }}</template>
|
<template #label>{{ i18n.ts.emailAddress }}</template>
|
||||||
<MkInput v-model="emailAddress" type="email" manualSave>
|
<div class="_gaps_s">
|
||||||
<template #prefix><i class="ti ti-mail"></i></template>
|
<MkInfo v-if="instance.emailRequiredForSignup && !emailAddress" warn>{{ i18n.ts.emailRegistrationRequired }}</MkInfo>
|
||||||
<template v-if="$i.email && !$i.emailVerified" #caption>{{ i18n.ts.verificationEmailSent }}</template>
|
<MkInput v-model="emailAddress" type="email" manualSave>
|
||||||
<template v-else-if="emailAddress === $i.email && $i.emailVerified" #caption><i class="ti ti-check" style="color: var(--success);"></i> {{ i18n.ts.emailVerified }}</template>
|
<template #prefix><i class="ti ti-mail"></i></template>
|
||||||
</MkInput>
|
<template v-if="$i.email && !$i.emailVerified" #caption>{{ i18n.ts.verificationEmailSent }}</template>
|
||||||
|
<template v-else-if="emailAddress === $i.email && $i.emailVerified" #caption><i class="ti ti-check" style="color: var(--success);"></i> {{ i18n.ts.emailVerified }}</template>
|
||||||
|
</MkInput>
|
||||||
|
</div>
|
||||||
</FormSection>
|
</FormSection>
|
||||||
|
|
||||||
<FormSection>
|
<FormSection>
|
||||||
|
|
|
@ -113,6 +113,7 @@ const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [
|
||||||
'defaultWithReplies',
|
'defaultWithReplies',
|
||||||
'disableStreamingTimeline',
|
'disableStreamingTimeline',
|
||||||
'useGroupedNotifications',
|
'useGroupedNotifications',
|
||||||
|
'trustedExternalWebsites',
|
||||||
'sound_masterVolume',
|
'sound_masterVolume',
|
||||||
'sound_note',
|
'sound_note',
|
||||||
'sound_noteMy',
|
'sound_noteMy',
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { url as local } from '@/config.js';
|
import { url as local } from '@/config.js';
|
||||||
|
import { defaultStore } from '@/store.js';
|
||||||
import { instance } from '@/instance.js';
|
import { instance } from '@/instance.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
|
@ -16,8 +17,9 @@ export async function warningExternalWebsite(ev: MouseEvent, url: string) {
|
||||||
} else if (expression.includes(' ')) return expression.split(' ').every(keyword => url.includes(keyword));
|
} else if (expression.includes(' ')) return expression.split(' ').every(keyword => url.includes(keyword));
|
||||||
else return domain.endsWith(expression);
|
else return domain.endsWith(expression);
|
||||||
});
|
});
|
||||||
|
const isTrusted = defaultStore.reactiveState.trustedExternalWebsites.value.includes(domain);
|
||||||
|
|
||||||
if (!self && !isWellKnownWebsite) {
|
if (!self && !isWellKnownWebsite && !isTrusted) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
|
|
||||||
|
@ -25,10 +27,15 @@ export async function warningExternalWebsite(ev: MouseEvent, url: string) {
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
title: i18n.ts.warningRedirectingExternalWebsiteTitle,
|
title: i18n.ts.warningRedirectingExternalWebsiteTitle,
|
||||||
text: i18n.tsx.warningRedirectingExternalWebsiteDescription({ url: `\`\`\`\n${url}\n\`\`\`` }),
|
text: i18n.tsx.warningRedirectingExternalWebsiteDescription({ url: `\`\`\`\n${url}\n\`\`\`` }),
|
||||||
|
switchLabel: i18n.ts.warningRedirectingExternalWebsiteTrustThisSite,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (confirm.canceled) return false;
|
if (confirm.canceled) return false;
|
||||||
|
|
||||||
|
if (confirm.toggle) {
|
||||||
|
await defaultStore.set('trustedExternalWebsites', [...defaultStore.reactiveState.trustedExternalWebsites.value, domain]);
|
||||||
|
}
|
||||||
|
|
||||||
window.open(url, '_blank', 'noopener');
|
window.open(url, '_blank', 'noopener');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -454,6 +454,10 @@ export const defaultStore = markRaw(new Storage('base', {
|
||||||
where: 'device',
|
where: 'device',
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
trustedExternalWebsites: {
|
||||||
|
where: 'device',
|
||||||
|
default: [] as string[],
|
||||||
|
},
|
||||||
|
|
||||||
sound_masterVolume: {
|
sound_masterVolume: {
|
||||||
where: 'device',
|
where: 'device',
|
||||||
|
|
|
@ -5,6 +5,18 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="$style.root">
|
<div :class="$style.root">
|
||||||
|
<MkA v-if="instance.emailRequiredForSignup && $i && !$i.email" :class="$style.item" to="/settings/email">
|
||||||
|
<span :class="$style.icon">
|
||||||
|
<i class="ti ti-circle-x" style="color: var(--error);"></i>
|
||||||
|
</span>
|
||||||
|
<span :class="$style.title">{{ i18n.tsx.emailRegistrationRequiredBanner({ instance: instanceName }) }}</span>
|
||||||
|
</MkA>
|
||||||
|
<MkA v-if="$i && $i.email && !$i.emailVerified" :class="$style.item" to="/settings/email">
|
||||||
|
<span :class="$style.icon">
|
||||||
|
<i class="ti ti-mail"></i>
|
||||||
|
</span>
|
||||||
|
<span :class="$style.title">{{ i18n.ts.verificationEmailSent }}</span>
|
||||||
|
</MkA>
|
||||||
<MkA
|
<MkA
|
||||||
v-for="announcement in $i.unreadAnnouncements.filter(x => x.display === 'banner')"
|
v-for="announcement in $i.unreadAnnouncements.filter(x => x.display === 'banner')"
|
||||||
:key="announcement.id"
|
:key="announcement.id"
|
||||||
|
@ -24,7 +36,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { instanceName } from '@/config.js';
|
||||||
|
import { instance } from '@/instance.js';
|
||||||
import { $i } from '@/account.js';
|
import { $i } from '@/account.js';
|
||||||
|
import { i18n } from '@/i18n.js';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
|
|
@ -26,13 +26,13 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@misskey-dev/eslint-plugin": "1.0.0",
|
"@misskey-dev/eslint-plugin": "1.0.0",
|
||||||
"@types/matter-js": "0.19.6",
|
"@types/matter-js": "0.19.6",
|
||||||
"@types/node": "20.12.2",
|
"@types/node": "20.12.7",
|
||||||
"@types/seedrandom": "3.0.8",
|
"@types/seedrandom": "3.0.8",
|
||||||
"@typescript-eslint/eslint-plugin": "7.4.0",
|
"@typescript-eslint/eslint-plugin": "7.6.0",
|
||||||
"@typescript-eslint/parser": "7.4.0",
|
"@typescript-eslint/parser": "7.6.0",
|
||||||
"eslint": "8.57.0",
|
"eslint": "8.57.0",
|
||||||
"nodemon": "3.1.0",
|
"nodemon": "3.1.0",
|
||||||
"typescript": "5.4.3"
|
"typescript": "5.4.5"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"built"
|
"built"
|
||||||
|
|
|
@ -70,6 +70,15 @@ type AdminAccountsFindByEmailRequest = operations['admin___accounts___find-by-em
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
type AdminAccountsFindByEmailResponse = operations['admin___accounts___find-by-email']['responses']['200']['content']['application/json'];
|
type AdminAccountsFindByEmailResponse = operations['admin___accounts___find-by-email']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
|
// @public (undocumented)
|
||||||
|
type AdminAccountsPendingListRequest = operations['admin___accounts___pending___list']['requestBody']['content']['application/json'];
|
||||||
|
|
||||||
|
// @public (undocumented)
|
||||||
|
type AdminAccountsPendingListResponse = operations['admin___accounts___pending___list']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
|
// @public (undocumented)
|
||||||
|
type AdminAccountsPendingRevokeRequest = operations['admin___accounts___pending___revoke']['requestBody']['content']['application/json'];
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
type AdminAdCreateRequest = operations['admin___ad___create']['requestBody']['content']['application/json'];
|
type AdminAdCreateRequest = operations['admin___ad___create']['requestBody']['content']['application/json'];
|
||||||
|
|
||||||
|
@ -1189,6 +1198,9 @@ declare namespace entities {
|
||||||
AdminAccountsDeleteRequest,
|
AdminAccountsDeleteRequest,
|
||||||
AdminAccountsFindByEmailRequest,
|
AdminAccountsFindByEmailRequest,
|
||||||
AdminAccountsFindByEmailResponse,
|
AdminAccountsFindByEmailResponse,
|
||||||
|
AdminAccountsPendingListRequest,
|
||||||
|
AdminAccountsPendingListResponse,
|
||||||
|
AdminAccountsPendingRevokeRequest,
|
||||||
AdminAdCreateRequest,
|
AdminAdCreateRequest,
|
||||||
AdminAdCreateResponse,
|
AdminAdCreateResponse,
|
||||||
AdminAdDeleteRequest,
|
AdminAdDeleteRequest,
|
||||||
|
|
|
@ -9,15 +9,15 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@misskey-dev/eslint-plugin": "^1.0.0",
|
"@misskey-dev/eslint-plugin": "^1.0.0",
|
||||||
"@readme/openapi-parser": "2.5.0",
|
"@readme/openapi-parser": "2.5.0",
|
||||||
"@types/node": "20.12.2",
|
"@types/node": "20.12.7",
|
||||||
"@typescript-eslint/eslint-plugin": "7.4.0",
|
"@typescript-eslint/eslint-plugin": "7.6.0",
|
||||||
"@typescript-eslint/parser": "7.4.0",
|
"@typescript-eslint/parser": "7.6.0",
|
||||||
"eslint": "8.57.0",
|
"eslint": "8.57.0",
|
||||||
"openapi-types": "12.1.3",
|
"openapi-types": "12.1.3",
|
||||||
"openapi-typescript": "6.7.5",
|
"openapi-typescript": "6.7.5",
|
||||||
"ts-case-convert": "2.0.7",
|
"ts-case-convert": "2.0.7",
|
||||||
"tsx": "4.7.1",
|
"tsx": "4.7.2",
|
||||||
"typescript": "5.4.3"
|
"typescript": "5.4.5"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"built"
|
"built"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"name": "misskey-js",
|
"name": "misskey-js",
|
||||||
"version": "2024.3.1-host.5b",
|
"version": "2024.3.1-host.6",
|
||||||
"description": "Misskey SDK for JavaScript",
|
"description": "Misskey SDK for JavaScript",
|
||||||
"types": "./built/dts/index.d.ts",
|
"types": "./built/dts/index.d.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
@ -35,13 +35,13 @@
|
||||||
"url": "git+https://github.com/misskey-dev/misskey.js.git"
|
"url": "git+https://github.com/misskey-dev/misskey.js.git"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@microsoft/api-extractor": "7.43.0",
|
"@microsoft/api-extractor": "7.43.1",
|
||||||
"@misskey-dev/eslint-plugin": "1.0.0",
|
"@misskey-dev/eslint-plugin": "1.0.0",
|
||||||
"@swc/jest": "0.2.36",
|
"@swc/jest": "0.2.36",
|
||||||
"@types/jest": "29.5.12",
|
"@types/jest": "29.5.12",
|
||||||
"@types/node": "20.12.2",
|
"@types/node": "20.12.7",
|
||||||
"@typescript-eslint/eslint-plugin": "7.4.0",
|
"@typescript-eslint/eslint-plugin": "7.6.0",
|
||||||
"@typescript-eslint/parser": "7.4.0",
|
"@typescript-eslint/parser": "7.6.0",
|
||||||
"eslint": "8.57.0",
|
"eslint": "8.57.0",
|
||||||
"jest": "29.7.0",
|
"jest": "29.7.0",
|
||||||
"jest-fetch-mock": "3.0.3",
|
"jest-fetch-mock": "3.0.3",
|
||||||
|
@ -49,8 +49,8 @@
|
||||||
"mock-socket": "9.3.1",
|
"mock-socket": "9.3.1",
|
||||||
"ncp": "2.0.0",
|
"ncp": "2.0.0",
|
||||||
"nodemon": "3.1.0",
|
"nodemon": "3.1.0",
|
||||||
"tsd": "0.30.7",
|
"tsd": "0.31.0",
|
||||||
"typescript": "5.4.3"
|
"typescript": "5.4.5"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"built",
|
"built",
|
||||||
|
|
|
@ -58,6 +58,28 @@ declare module '../api.js' {
|
||||||
credential?: string | null,
|
credential?: string | null,
|
||||||
): Promise<SwitchCaseResponseType<E, P>>;
|
): Promise<SwitchCaseResponseType<E, P>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *read:admin:account*
|
||||||
|
*/
|
||||||
|
request<E extends 'admin/accounts/pending/list', P extends Endpoints[E]['req']>(
|
||||||
|
endpoint: E,
|
||||||
|
params: P,
|
||||||
|
credential?: string | null,
|
||||||
|
): Promise<SwitchCaseResponseType<E, P>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *write:admin:account*
|
||||||
|
*/
|
||||||
|
request<E extends 'admin/accounts/pending/revoke', P extends Endpoints[E]['req']>(
|
||||||
|
endpoint: E,
|
||||||
|
params: P,
|
||||||
|
credential?: string | null,
|
||||||
|
): Promise<SwitchCaseResponseType<E, P>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* No description provided.
|
* No description provided.
|
||||||
*
|
*
|
||||||
|
|
|
@ -9,6 +9,9 @@ import type {
|
||||||
AdminAccountsDeleteRequest,
|
AdminAccountsDeleteRequest,
|
||||||
AdminAccountsFindByEmailRequest,
|
AdminAccountsFindByEmailRequest,
|
||||||
AdminAccountsFindByEmailResponse,
|
AdminAccountsFindByEmailResponse,
|
||||||
|
AdminAccountsPendingListRequest,
|
||||||
|
AdminAccountsPendingListResponse,
|
||||||
|
AdminAccountsPendingRevokeRequest,
|
||||||
AdminAdCreateRequest,
|
AdminAdCreateRequest,
|
||||||
AdminAdCreateResponse,
|
AdminAdCreateResponse,
|
||||||
AdminAdDeleteRequest,
|
AdminAdDeleteRequest,
|
||||||
|
@ -583,6 +586,8 @@ export type Endpoints = {
|
||||||
'admin/accounts/create': { req: AdminAccountsCreateRequest; res: AdminAccountsCreateResponse };
|
'admin/accounts/create': { req: AdminAccountsCreateRequest; res: AdminAccountsCreateResponse };
|
||||||
'admin/accounts/delete': { req: AdminAccountsDeleteRequest; res: EmptyResponse };
|
'admin/accounts/delete': { req: AdminAccountsDeleteRequest; res: EmptyResponse };
|
||||||
'admin/accounts/find-by-email': { req: AdminAccountsFindByEmailRequest; res: AdminAccountsFindByEmailResponse };
|
'admin/accounts/find-by-email': { req: AdminAccountsFindByEmailRequest; res: AdminAccountsFindByEmailResponse };
|
||||||
|
'admin/accounts/pending/list': { req: AdminAccountsPendingListRequest; res: AdminAccountsPendingListResponse };
|
||||||
|
'admin/accounts/pending/revoke': { req: AdminAccountsPendingRevokeRequest; res: EmptyResponse };
|
||||||
'admin/ad/create': { req: AdminAdCreateRequest; res: AdminAdCreateResponse };
|
'admin/ad/create': { req: AdminAdCreateRequest; res: AdminAdCreateResponse };
|
||||||
'admin/ad/delete': { req: AdminAdDeleteRequest; res: EmptyResponse };
|
'admin/ad/delete': { req: AdminAdDeleteRequest; res: EmptyResponse };
|
||||||
'admin/ad/list': { req: AdminAdListRequest; res: AdminAdListResponse };
|
'admin/ad/list': { req: AdminAdListRequest; res: AdminAdListResponse };
|
||||||
|
|
|
@ -12,6 +12,9 @@ export type AdminAccountsCreateResponse = operations['admin___accounts___create'
|
||||||
export type AdminAccountsDeleteRequest = operations['admin___accounts___delete']['requestBody']['content']['application/json'];
|
export type AdminAccountsDeleteRequest = operations['admin___accounts___delete']['requestBody']['content']['application/json'];
|
||||||
export type AdminAccountsFindByEmailRequest = operations['admin___accounts___find-by-email']['requestBody']['content']['application/json'];
|
export type AdminAccountsFindByEmailRequest = operations['admin___accounts___find-by-email']['requestBody']['content']['application/json'];
|
||||||
export type AdminAccountsFindByEmailResponse = operations['admin___accounts___find-by-email']['responses']['200']['content']['application/json'];
|
export type AdminAccountsFindByEmailResponse = operations['admin___accounts___find-by-email']['responses']['200']['content']['application/json'];
|
||||||
|
export type AdminAccountsPendingListRequest = operations['admin___accounts___pending___list']['requestBody']['content']['application/json'];
|
||||||
|
export type AdminAccountsPendingListResponse = operations['admin___accounts___pending___list']['responses']['200']['content']['application/json'];
|
||||||
|
export type AdminAccountsPendingRevokeRequest = operations['admin___accounts___pending___revoke']['requestBody']['content']['application/json'];
|
||||||
export type AdminAdCreateRequest = operations['admin___ad___create']['requestBody']['content']['application/json'];
|
export type AdminAdCreateRequest = operations['admin___ad___create']['requestBody']['content']['application/json'];
|
||||||
export type AdminAdCreateResponse = operations['admin___ad___create']['responses']['200']['content']['application/json'];
|
export type AdminAdCreateResponse = operations['admin___ad___create']['responses']['200']['content']['application/json'];
|
||||||
export type AdminAdDeleteRequest = operations['admin___ad___delete']['requestBody']['content']['application/json'];
|
export type AdminAdDeleteRequest = operations['admin___ad___delete']['requestBody']['content']['application/json'];
|
||||||
|
|
|
@ -57,6 +57,24 @@ export type paths = {
|
||||||
*/
|
*/
|
||||||
post: operations['admin___accounts___find-by-email'];
|
post: operations['admin___accounts___find-by-email'];
|
||||||
};
|
};
|
||||||
|
'/admin/accounts/pending/list': {
|
||||||
|
/**
|
||||||
|
* admin/accounts/pending/list
|
||||||
|
* @description No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *read:admin:account*
|
||||||
|
*/
|
||||||
|
post: operations['admin___accounts___pending___list'];
|
||||||
|
};
|
||||||
|
'/admin/accounts/pending/revoke': {
|
||||||
|
/**
|
||||||
|
* admin/accounts/pending/revoke
|
||||||
|
* @description No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *write:admin:account*
|
||||||
|
*/
|
||||||
|
post: operations['admin___accounts___pending___revoke'];
|
||||||
|
};
|
||||||
'/admin/ad/create': {
|
'/admin/ad/create': {
|
||||||
/**
|
/**
|
||||||
* admin/ad/create
|
* admin/ad/create
|
||||||
|
@ -5523,6 +5541,135 @@ export type operations = {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* admin/accounts/pending/list
|
||||||
|
* @description No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *read:admin:account*
|
||||||
|
*/
|
||||||
|
admin___accounts___pending___list: {
|
||||||
|
requestBody: {
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
/** @default 10 */
|
||||||
|
limit?: number;
|
||||||
|
/** @default 0 */
|
||||||
|
offset?: number;
|
||||||
|
/** @enum {string} */
|
||||||
|
sort?: '+createdAt' | '-createdAt';
|
||||||
|
/** @default null */
|
||||||
|
username?: string | null;
|
||||||
|
/** @default null */
|
||||||
|
email?: string | null;
|
||||||
|
/** @default null */
|
||||||
|
code?: string | null;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** @description OK (with results) */
|
||||||
|
200: {
|
||||||
|
content: {
|
||||||
|
'application/json': ({
|
||||||
|
/**
|
||||||
|
* Format: id
|
||||||
|
* @example xxxxxxxxxx
|
||||||
|
*/
|
||||||
|
id: string;
|
||||||
|
/** Format: date-time */
|
||||||
|
createdAt: string;
|
||||||
|
code: string;
|
||||||
|
/** @example ai */
|
||||||
|
username: string;
|
||||||
|
email?: string | null;
|
||||||
|
})[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Client error */
|
||||||
|
400: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Authentication error */
|
||||||
|
401: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Forbidden error */
|
||||||
|
403: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description I'm Ai */
|
||||||
|
418: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Internal server error */
|
||||||
|
500: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* admin/accounts/pending/revoke
|
||||||
|
* @description No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *write:admin:account*
|
||||||
|
*/
|
||||||
|
admin___accounts___pending___revoke: {
|
||||||
|
requestBody: {
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
/** Format: misskey:id */
|
||||||
|
id?: string;
|
||||||
|
code?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** @description OK (without any results) */
|
||||||
|
204: {
|
||||||
|
content: never;
|
||||||
|
};
|
||||||
|
/** @description Client error */
|
||||||
|
400: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Authentication error */
|
||||||
|
401: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Forbidden error */
|
||||||
|
403: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description I'm Ai */
|
||||||
|
418: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Internal server error */
|
||||||
|
500: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
/**
|
/**
|
||||||
* admin/ad/create
|
* admin/ad/create
|
||||||
* @description No description provided.
|
* @description No description provided.
|
||||||
|
|
|
@ -25,12 +25,12 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@misskey-dev/eslint-plugin": "1.0.0",
|
"@misskey-dev/eslint-plugin": "1.0.0",
|
||||||
"@types/node": "20.12.2",
|
"@types/node": "20.12.7",
|
||||||
"@typescript-eslint/eslint-plugin": "7.4.0",
|
"@typescript-eslint/eslint-plugin": "7.6.0",
|
||||||
"@typescript-eslint/parser": "7.4.0",
|
"@typescript-eslint/parser": "7.6.0",
|
||||||
"eslint": "8.57.0",
|
"eslint": "8.57.0",
|
||||||
"nodemon": "3.1.0",
|
"nodemon": "3.1.0",
|
||||||
"typescript": "5.4.3"
|
"typescript": "5.4.5"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"crc-32": "1.2.2",
|
"crc-32": "1.2.2",
|
||||||
|
|
|
@ -16,11 +16,11 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@misskey-dev/eslint-plugin": "1.0.0",
|
"@misskey-dev/eslint-plugin": "1.0.0",
|
||||||
"@types/serviceworker": "0.0.84",
|
"@types/serviceworker": "0.0.84",
|
||||||
"@typescript-eslint/parser": "7.4.0",
|
"@typescript-eslint/parser": "7.6.0",
|
||||||
"eslint": "8.57.0",
|
"eslint": "8.57.0",
|
||||||
"eslint-plugin-import": "2.29.1",
|
"eslint-plugin-import": "2.29.1",
|
||||||
"nodemon": "3.1.0",
|
"nodemon": "3.1.0",
|
||||||
"typescript": "5.4.3"
|
"typescript": "5.4.5"
|
||||||
},
|
},
|
||||||
"type": "module"
|
"type": "module"
|
||||||
}
|
}
|
||||||
|
|
2839
pnpm-lock.yaml
generated
2839
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue