diff --git a/locales/en-US.yml b/locales/en-US.yml index 86567cb75173..7c484a8e979a 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -37,6 +37,7 @@ signup: "Sign Up" uploading: "Uploading..." save: "Save" users: "Users" +approvals: "Approvals" addUser: "Add a user" favorite: "Add to favorites" favorites: "Favorites" @@ -152,6 +153,8 @@ unsuspend: "Unsuspend" blockConfirm: "Are you sure that you want to block this account?" unblockConfirm: "Are you sure that you want to unblock this account?" suspendConfirm: "Are you sure that you want to suspend this account?" +registerApproveConfirm: "Are you sure that you want to approve this account?" +registerApproveConfirmDescription: "This action cannot be undone. An email notification will be sent to the user." unsuspendConfirm: "Are you sure that you want to unsuspend this account?" selectList: "Select a list" editList: "Edit list" @@ -909,6 +912,8 @@ itsOff: "Disabled" on: "On" off: "Off" emailRequiredForSignup: "Require email address for sign-up" +approvalRequiredForSignup: "Manual approval required for sign-up" +signupPendingApprovals: "Pending Approvals" unread: "Unread" filter: "Filter" controlPanel: "Control Panel" @@ -963,6 +968,7 @@ recentNHours: "Last {n} hours" recentNDays: "Last {n} days" noEmailServerWarning: "Email server not configured." thereIsUnresolvedAbuseReportWarning: "There are unsolved reports." +pendingUserApprovals: "There are accounts waiting for approvals." recommended: "Recommended" check: "Check" driveCapOverrideLabel: "Change the drive capacity for this user" @@ -971,6 +977,11 @@ requireAdminForView: "You must log in with an administrator account to view this isSystemAccount: "An account created and automatically operated by the system." typeToConfirm: "Please enter {x} to confirm" deleteAccount: "Delete account" +approveAccount: "Approve account" +denyAccount: "Reject and delete account" +approved: "Approved" +notApproved: "Not approved" +approvalStatus: "Approval status" document: "Documentation" numberOfPageCache: "Number of cached pages" numberOfPageCacheDescription: "Increasing this number will improve convenience for but cause more load as more memory usage on the user's device." @@ -1063,6 +1074,10 @@ disableFederationConfirm: "Really disable federation?" disableFederationConfirmWarn: "Even if defederated, posts will continue to be public unless set otherwise. You usually do not need to do this." disableFederationOk: "Disable" invitationRequiredToRegister: "This instance is invite-only. You must enter a valid invite code sign up." +approvalRequiredToRegister: "New sign-ups require manual approval. Please provide the reason why you want to create your account here." +registerReason: "Reason for your registeration" +registerHasNotBeenApprovedYet: "Your account is still waiting for approval. Please try again later. We will let you know once your account is approved via email." +registerApprovalEmailRecommended: "Setting email address required is highly recommended so that users will be notified via email when their accounts are approved." emailNotSupported: "This instance does not support sending emails" postToTheChannel: "Post to channel" cannotBeChangedLater: "This cannot be changed later." @@ -1839,6 +1854,8 @@ _signup: almostThere: "Almost there" emailAddressInfo: "Please enter your email address. It will not be made public." emailSent: "A confirmation email has been sent to your email address ({email}). Please click the included link to complete account creation." + approvalPending: "Your account has been created and it is currently waiting for approval." + reasonInfo: "Please provide the reason for your registeration." _accountDelete: accountDelete: "Delete account" mayTakeTime: "As account deletion is a resource-heavy process, it may take some time to complete depending on how much content you have created and how many files you have uploaded." @@ -2542,6 +2559,7 @@ _moderationLogTypes: updateRole: "Role updated" assignRole: "Assigned to role" unassignRole: "Removed from role" + approve: "Approve" suspend: "Suspended" unsuspend: "Unsuspended" addCustomEmoji: "Custom emoji added" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index 101a3a68a977..0c64940b7961 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -37,6 +37,7 @@ signup: "회원 가입" uploading: "업로드 중" save: "저장" users: "유저" +approvals: "승인" addUser: "유저 추가" favorite: "즐겨찾기" favorites: "즐겨찾기" @@ -152,6 +153,8 @@ unsuspend: "정지 해제" blockConfirm: "이 계정을 차단하시겠습니까?" unblockConfirm: "이 계정의 차단을 해제하시겠습니까?" suspendConfirm: "이 계정을 정지하시겠습니까?" +registerApproveConfirm: "이 계정 등록을 승인합니까?" +registerApproveConfirmDescription: "승인 후 되돌릴 수 없습니다. 승인되었다는 안내를 담은 이메일이 사용자에게 발송됩니다." unsuspendConfirm: "이 계정의 정지를 해제하시겠습니까?" selectList: "리스트 선택" editList: "리스트 편집" @@ -909,6 +912,8 @@ itsOff: "꺼져 있습니다" on: "켜짐" off: "꺼짐" emailRequiredForSignup: "가입할 때 이메일 주소 입력을 필수로 하기" +approvalRequiredForSignup: "계정 등록을 승인제로 하기" +signupPendingApprovals: "계정 승인" unread: "읽지 않음" filter: "필터" controlPanel: "제어판" @@ -963,6 +968,7 @@ recentNHours: "최근 {n}시간" recentNDays: "최근 {n}일" noEmailServerWarning: "메일 서버가 설정되어 있지 않습니다." thereIsUnresolvedAbuseReportWarning: "해결되지 않은 신고가 있습니다." +pendingUserApprovals: "승인을 기다리는 계정이 있습니다." recommended: "추천" check: "체크" driveCapOverrideLabel: "이 유저의 드라이브 용량을 변경" @@ -971,6 +977,11 @@ requireAdminForView: "열람하려면 관리자 계정으로 로그인해야 합 isSystemAccount: "시스템에 의해 자동으로 생성되어 관리되는 계정입니다." typeToConfirm: "계속하시려면 {x} 을 입력하세요" deleteAccount: "계정 삭제" +approveAccount: "승인하기" +denyAccount: "거절하고 계정 삭제" +approved: "승인됨" +notApproved: "미승인" +approvalStatus: "승인 현황" document: "문서" numberOfPageCache: "페이지 캐시 수" numberOfPageCacheDescription: "숫자가 클 수록 편리성이 높아지지만, 시스템 자원과 메모리를 더 많이 사용합니다." @@ -1063,6 +1074,10 @@ disableFederationConfirm: "정말로 연합을 끄시겠습니까?" disableFederationConfirmWarn: "연합을 끄더라도 게시물이 비공개로 전환되는 것은 아닙니다. 대부분의 경우 연합을 비활성화할 필요가 없습니다." disableFederationOk: "연합을 끄기" invitationRequiredToRegister: "현재 이 서버는 비공개입니다. 회원가입을 하시려면 초대 코드가 필요합니다." +approvalRequiredToRegister: "이 서버의 계정 등록은 승인제입니다. 가입하고자 하는 사유를 기재하면 관리자가 검토합니다." +registerReason: "가입 사유" +registerHasNotBeenApprovedYet: "계정이 아직 승인되지 않았습니다. 잠시 후에 다시 시도해주세요. 등록 시 이메일 주소를 기입하셨다면, 등록이 승인될 때 메일로 알려드리겠습니다." +registerApprovalEmailRecommended: "계정 등록의 승인시 이메일을 통해 통지하지 때문에, 계정 등록시 이메일을 기입을 필수로 할 것을 권장합니다." emailNotSupported: "이 서버에서는 메일 전송을 지원하지 않습니다" postToTheChannel: "채널에 게시하기" cannotBeChangedLater: "나중에 변경할 수 없습니다." @@ -1842,6 +1857,8 @@ _signup: almostThere: "거의 다 끝났습니다" emailAddressInfo: "당신이 사용하고 있는 이메일 주소를 입력해 주세요. 이메일 주소는 다른 유저에게 공개되지 않습니다." emailSent: "입력하신 메일 주소({email})로 확인 메일을 보내드렸습니다. 가입을 완료하시려면 보내드린 메일에 있는 링크로 접속해 주세요." + approvalPending: "계정은 생성되었고 승인을 대기하고 있습니다." + reasonInfo: "가입하고 싶은 사유를 입력하세요." _accountDelete: accountDelete: "계정 삭제" mayTakeTime: "계정 삭제는 서버에 부하를 가하기 때문에, 작성한 콘텐츠나 업로드한 파일의 수가 많으면 완료까지 시간이 걸릴 수 있습니다." @@ -2545,6 +2562,7 @@ _moderationLogTypes: updateRole: "역할 수정" assignRole: "역할 할당" unassignRole: "역할 해제" + approve: "승인" suspend: "정지" unsuspend: "정지 해제" addCustomEmoji: "커스텀 이모지 추가" diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts index d40b3c9b75b3..c722354b2658 100644 --- a/packages/backend/src/config.ts +++ b/packages/backend/src/config.ts @@ -99,8 +99,6 @@ type Source = { perUserNotificationsMaxCount?: number; deactivateAntennaThreshold?: number; pidFile: string; - - approvalRequiredForSignup: boolean; }; export type Config = { @@ -192,8 +190,6 @@ export type Config = { perUserNotificationsMaxCount: number; deactivateAntennaThreshold: number; pidFile: string; - - approvalRequiredForSignup: boolean; }; const _filename = fileURLToPath(import.meta.url); @@ -306,7 +302,6 @@ export function loadConfig(): Config { deactivateAntennaThreshold: config.deactivateAntennaThreshold ?? (1000 * 60 * 60 * 24 * 7), pidFile: config.pidFile, skebStatus: undefined, - approvalRequiredForSignup: config.approvalRequiredForSignup, }; } diff --git a/packages/backend/src/core/entities/MetaEntityService.ts b/packages/backend/src/core/entities/MetaEntityService.ts index 3fba0295ab5b..9a58a7c47902 100644 --- a/packages/backend/src/core/entities/MetaEntityService.ts +++ b/packages/backend/src/core/entities/MetaEntityService.ts @@ -131,7 +131,7 @@ export class MetaEntityService { noteSearchableScope: (this.config.meilisearch == null || this.config.meilisearch.scope !== 'local') ? 'global' : 'local', maxFileSize: this.config.maxFileSize, - approvalRequiredForSignup: this.config.approvalRequiredForSignup, + approvalRequiredForSignup: instance.approvalRequiredForSignup, }; return packed; diff --git a/packages/backend/src/server/api/endpoints/admin/show-users.ts b/packages/backend/src/server/api/endpoints/admin/show-users.ts index 4f5a3b713347..5ff29d31c02c 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-users.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-users.ts @@ -35,7 +35,7 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, offset: { type: 'integer', default: 0 }, sort: { type: 'string', enum: ['+follower', '-follower', '+createdAt', '-createdAt', '+updatedAt', '-updatedAt', '+lastActiveDate', '-lastActiveDate'] }, - state: { type: 'string', enum: ['all', 'alive', 'available', 'admin', 'moderator', 'adminOrModerator', 'suspended', 'approved'], default: 'all' }, + state: { type: 'string', enum: ['all', 'alive', 'available', 'admin', 'moderator', 'adminOrModerator', 'suspended', 'approved', 'waitingForApproval'], default: 'all' }, origin: { type: 'string', enum: ['combined', 'local', 'remote'], default: 'combined' }, username: { type: 'string', nullable: true, default: null }, hostname: { @@ -65,6 +65,7 @@ export default class extends Endpoint { // eslint- case 'alive': query.where('user.updatedAt > :date', { date: new Date(Date.now() - 1000 * 60 * 60 * 24 * 5) }); break; case 'suspended': query.where('user.isSuspended = TRUE'); break; case 'approved': query.where('user.approved = TRUE'); break; + case 'waitingForApproval': query.where('user.approved = FALSE'); break; case 'admin': { const adminIds = await this.roleService.getAdministratorIds(); if (adminIds.length === 0) return []; diff --git a/packages/frontend/src/components/MkSignupDialog.form.vue b/packages/frontend/src/components/MkSignupDialog.form.vue index 1caf234632f3..63925beb465f 100644 --- a/packages/frontend/src/components/MkSignupDialog.form.vue +++ b/packages/frontend/src/components/MkSignupDialog.form.vue @@ -270,6 +270,7 @@ async function onSubmit(): Promise { 'g-recaptcha-response': reCaptchaResponse.value, 'turnstile-response': turnstileResponse.value, 'testcaptcha-response': testcaptchaResponse.value, + reason: reason.value, }; const res = await fetch(`${config.apiUrl}/signup`, { diff --git a/packages/frontend/src/components/MkVisitorDashboard.vue b/packages/frontend/src/components/MkVisitorDashboard.vue index 9a53dbc5424b..f5edfaedaf3d 100644 --- a/packages/frontend/src/components/MkVisitorDashboard.vue +++ b/packages/frontend/src/components/MkVisitorDashboard.vue @@ -25,7 +25,9 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts.approvalRequiredToRegister }}
- {{ i18n.ts.joinThisServer }} +
+ {{ i18n.ts.joinThisServer }} +
{{ i18n.ts.exploreOtherServers }} {{ i18n.ts.login }}
diff --git a/packages/frontend/src/pages/admin/approvals.vue b/packages/frontend/src/pages/admin/approvals.vue index e0bf7ad0d52a..cb76bf37097d 100644 --- a/packages/frontend/src/pages/admin/approvals.vue +++ b/packages/frontend/src/pages/admin/approvals.vue @@ -37,7 +37,7 @@ const pagination = { limit: 10, params: computed(() => ({ sort: '+createdAt', - state: 'approved', + state: 'waitingForApproval', origin: 'local', })), offsetMode: true, diff --git a/packages/frontend/src/pages/admin/index.vue b/packages/frontend/src/pages/admin/index.vue index 97c238992f27..e8f898af6068 100644 --- a/packages/frontend/src/pages/admin/index.vue +++ b/packages/frontend/src/pages/admin/index.vue @@ -78,7 +78,7 @@ misskeyApi('admin/abuse-user-reports', { }); misskeyApi('admin/show-users', { - state: 'approved', + state: 'waitingForApproval', origin: 'local', limit: 1, }).then(approvals => { diff --git a/packages/frontend/src/pages/admin/moderation.vue b/packages/frontend/src/pages/admin/moderation.vue index 5eaae7c7f68e..e2e5973eecc6 100644 --- a/packages/frontend/src/pages/admin/moderation.vue +++ b/packages/frontend/src/pages/admin/moderation.vue @@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only - + @@ -200,6 +200,14 @@ function onChange_emailRequiredForSignup(value: boolean) { }); } +function onChange_approvalRequiredForSignup(value: boolean) { + os.apiWithDialog('admin/update-meta', { + approvalRequiredForSignup: value, + }).then(() => { + fetchInstance(true); + }); +} + function save_preservedUsernames() { os.apiWithDialog('admin/update-meta', { disableRegistration: !enableRegistration.value, diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts index f0ff169fd480..3320a3edf8ec 100644 --- a/packages/misskey-js/src/entities.ts +++ b/packages/misskey-js/src/entities.ts @@ -261,6 +261,7 @@ export type SignupRequest = { 'g-recaptcha-response'?: string | null; 'turnstile-response'?: string | null; 'm-captcha-response'?: string | null; + reason?: string | null; } export type SignupResponse = MeDetailed & {