Skip to content

Commit

Permalink
Feat: 가입 승인제 기능 구현
Browse files Browse the repository at this point in the history
Cherry-pick commits from: nekoplanet#20
- nekoplanet@736c808
- nekoplanet@dbd3028
- nekoplanet@effcdaa
- nekoplanet@c5cd665
- nekoplanet@db463c9

Fix: signup-approve (serafuku patch)
- state enum의 approved 를 pending 으로 변경
- metaEntityService 에서 approvalRequiredForSignup 넣어주도록 수정
- SignupRequest type에 reason 추가
- 모더레이션 페이지에서 승인 설정 바꾸면 update-meta 하도록 수정
- 기타 어쩌구 저쩌구 프론트&백앤드 버그수정
- Fix lint
- fix E2E
- 한글 번역 추가 (yozumina)
- Fix: user Approved index 추가
- 가입화면 & 가입승인관리 UI 조정
- Fix: 가입 오픈 상태에서는 approved를 자동으로 true

Co-authored-by: HotoRas <[email protected]>
Co-authored-by: Squarecat-meow <[email protected]>
  • Loading branch information
3 people committed Nov 23, 2024
1 parent a4f1273 commit ee4a022
Show file tree
Hide file tree
Showing 50 changed files with 805 additions and 17 deletions.
72 changes: 72 additions & 0 deletions locales/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,10 @@ export interface Locale extends ILocale {
* ユーザー
*/
"users": string;
/**
* 承認
*/
"approvals": string;
/**
* ユーザーを追加
*/
Expand Down Expand Up @@ -626,6 +630,14 @@ export interface Locale extends ILocale {
* 凍結しますか?
*/
"suspendConfirm": string;
/**
* 登録を承認しますか?
*/
"registerApproveConfirm": string;
/**
* この操作は取り消せません。承認後、このユーザーに登録が承認された旨が記載されたメールが送信されます。
*/
"registerApproveConfirmDescription": string;
/**
* 解凍しますか?
*/
Expand Down Expand Up @@ -3666,6 +3678,14 @@ export interface Locale extends ILocale {
* アカウント登録にメールアドレスを必須にする
*/
"emailRequiredForSignup": string;
/**
* アカウント登録を承認制にする
*/
"approvalRequiredForSignup": string;
/**
* アカウント登録の承認
*/
"signupPendingApprovals": string;
/**
* 未読
*/
Expand Down Expand Up @@ -3882,6 +3902,10 @@ export interface Locale extends ILocale {
* 未対応の通報があります。
*/
"thereIsUnresolvedAbuseReportWarning": string;
/**
* 承認待ちのユーザーがいます。
*/
"pendingUserApprovals": string;
/**
* 推奨
*/
Expand Down Expand Up @@ -3914,6 +3938,26 @@ export interface Locale extends ILocale {
* アカウント削除
*/
"deleteAccount": string;
/**
* 承認する
*/
"approveAccount": string;
/**
* 拒否してアカウント削除
*/
"denyAccount": string;
/**
* 承認済み
*/
"approved": string;
/**
* 未承認
*/
"notApproved": string;
/**
* 承認状況
*/
"approvalStatus": string;
/**
* ドキュメント
*/
Expand Down Expand Up @@ -4282,6 +4326,22 @@ export interface Locale extends ILocale {
* 現在このサーバーは招待制です。招待コードをお持ちの方のみ登録できます。
*/
"invitationRequiredToRegister": string;
/**
* 現在このサーバーは承認制です。参加したい理由を記入し、承認された方のみ登録できます。
*/
"approvalRequiredToRegister": string;
/**
* 登録理由
*/
"registerReason": string;
/**
* サーバーへの登録はまだ承認されていません。しばらくしてから再度お試しください。登録時にメールアドレスを記入した場合は、登録が承認されたらメールでお知らせします。
*/
"registerHasNotBeenApprovedYet": string;
/**
* サーバーへの登録が承認されたかどうかの通知を行うために、併せてアカウント登録にメールアドレスを必須にすることを強く推奨します。
*/
"registerApprovalEmailRecommended": string;
/**
* このサーバーではメール配信はサポートされていません
*/
Expand Down Expand Up @@ -7192,6 +7252,14 @@ export interface Locale extends ILocale {
* 入力されたメールアドレス({email})宛に確認のメールが送信されました。メールに記載されたリンクにアクセスすると、アカウントの作成が完了します。メールに記載されているリンクの有効期限は30分です。
*/
"emailSent": ParameterizedString<"email">;
/**
* アカウントが作成され、承認待ちの状態です。
*/
"approvalPending": string;
/**
* このサーバーに参加したい理由を入力してください。
*/
"reasonInfo": string;
};
"_accountDelete": {
/**
Expand Down Expand Up @@ -9878,6 +9946,10 @@ export interface Locale extends ILocale {
* ロールのアサイン解除
*/
"unassignRole": string;
/**
* 承認済み
*/
"approve": string;
/**
* 凍結
*/
Expand Down
18 changes: 18 additions & 0 deletions locales/ja-JP.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ signup: "新規登録"
uploading: "アップロード中"
save: "保存"
users: "ユーザー"
approvals: "承認"
addUser: "ユーザーを追加"
favorite: "お気に入り"
favorites: "お気に入り"
Expand Down Expand Up @@ -152,6 +153,8 @@ unsuspend: "解凍"
blockConfirm: "ブロックしますか?"
unblockConfirm: "ブロック解除しますか?"
suspendConfirm: "凍結しますか?"
registerApproveConfirm: "登録を承認しますか?"
registerApproveConfirmDescription: "この操作は取り消せません。承認後、このユーザーに登録が承認された旨が記載されたメールが送信されます。"
unsuspendConfirm: "解凍しますか?"
selectList: "リストを選択"
editList: "リストを編集"
Expand Down Expand Up @@ -912,6 +915,8 @@ itsOff: "オフになっています"
on: "オン"
off: "オフ"
emailRequiredForSignup: "アカウント登録にメールアドレスを必須にする"
approvalRequiredForSignup: "アカウント登録を承認制にする"
signupPendingApprovals: "アカウント登録の承認"
unread: "未読"
filter: "フィルタ"
controlPanel: "コントロールパネル"
Expand Down Expand Up @@ -966,6 +971,7 @@ recentNHours: "直近{n}時間"
recentNDays: "直近{n}日"
noEmailServerWarning: "メールサーバーの設定がされていません。"
thereIsUnresolvedAbuseReportWarning: "未対応の通報があります。"
pendingUserApprovals: "承認待ちのユーザーがいます。"
recommended: "推奨"
check: "チェック"
driveCapOverrideLabel: "このユーザーのドライブ容量上限を変更"
Expand All @@ -974,6 +980,11 @@ requireAdminForView: "閲覧するには管理者アカウントでログイン
isSystemAccount: "システムにより自動で作成・管理されているアカウントです。"
typeToConfirm: "この操作を行うには {x} と入力してください"
deleteAccount: "アカウント削除"
approveAccount: "承認する"
denyAccount: "拒否してアカウント削除"
approved: "承認済み"
notApproved: "未承認"
approvalStatus: "承認状況"
document: "ドキュメント"
numberOfPageCache: "ページキャッシュ数"
numberOfPageCacheDescription: "多くすると利便性が向上しますが、負荷とメモリ使用量が増えます。"
Expand Down Expand Up @@ -1066,6 +1077,10 @@ disableFederationConfirm: "連合なしにしますか?"
disableFederationConfirmWarn: "連合なしにしても投稿は非公開になりません。ほとんどの場合、連合なしにする必要はありません。"
disableFederationOk: "連合なしにする"
invitationRequiredToRegister: "現在このサーバーは招待制です。招待コードをお持ちの方のみ登録できます。"
approvalRequiredToRegister: "現在このサーバーは承認制です。参加したい理由を記入し、承認された方のみ登録できます。"
registerReason: "登録理由"
registerHasNotBeenApprovedYet: "サーバーへの登録はまだ承認されていません。しばらくしてから再度お試しください。登録時にメールアドレスを記入した場合は、登録が承認されたらメールでお知らせします。"
registerApprovalEmailRecommended: "サーバーへの登録が承認されたかどうかの通知を行うために、併せてアカウント登録にメールアドレスを必須にすることを強く推奨します。"
emailNotSupported: "このサーバーではメール配信はサポートされていません"
postToTheChannel: "チャンネルに投稿"
cannotBeChangedLater: "後から変更できません。"
Expand Down Expand Up @@ -1863,6 +1878,8 @@ _signup:
almostThere: "ほとんど完了です"
emailAddressInfo: "あなたが使っているメールアドレスを入力してください。メールアドレスが公開されることはありません。"
emailSent: "入力されたメールアドレス({email})宛に確認のメールが送信されました。メールに記載されたリンクにアクセスすると、アカウントの作成が完了します。メールに記載されているリンクの有効期限は30分です。"
approvalPending: "アカウントが作成され、承認待ちの状態です。"
reasonInfo: "このサーバーに参加したい理由を入力してください。"

_accountDelete:
accountDelete: "アカウントの削除"
Expand Down Expand Up @@ -2619,6 +2636,7 @@ _moderationLogTypes:
updateRole: "ロールを更新"
assignRole: "ロールへアサイン"
unassignRole: "ロールのアサイン解除"
approve: "承認済み"
suspend: "凍結"
unsuspend: "凍結解除"
addCustomEmoji: "カスタム絵文字追加"
Expand Down
18 changes: 18 additions & 0 deletions locales/ko-KR.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ signup: "회원 가입"
uploading: "업로드 중"
save: "저장"
users: "유저"
approvals: "승인"
addUser: "유저 추가"
favorite: "즐겨찾기"
favorites: "즐겨찾기"
Expand Down Expand Up @@ -152,6 +153,8 @@ unsuspend: "정지 해제"
blockConfirm: "이 계정을 차단하시겠습니까?"
unblockConfirm: "이 계정의 차단을 해제하시겠습니까?"
suspendConfirm: "이 계정을 정지하시겠습니까?"
registerApproveConfirm: "가입을 승인하시겠습니까?"
registerApproveConfirmDescription: "이 동작은 되돌릴 수 없습니다. 승인 후, 이 유저에게 가입 승인 안내 메일이 송신됩니다."
unsuspendConfirm: "이 계정의 정지를 해제하시겠습니까?"
selectList: "리스트 선택"
editList: "리스트 편집"
Expand Down Expand Up @@ -912,6 +915,8 @@ itsOff: "꺼져 있습니다"
on: "켜짐"
off: "꺼짐"
emailRequiredForSignup: "가입할 때 이메일 주소 입력을 필수로 하기"
approvalRequiredForSignup: "계정 가입을 승인제로 하기"
signupPendingApprovals: "계정 가입 승인"
unread: "읽지 않음"
filter: "필터"
controlPanel: "제어판"
Expand Down Expand Up @@ -966,6 +971,7 @@ recentNHours: "최근 {n}시간"
recentNDays: "최근 {n}일"
noEmailServerWarning: "메일 서버가 설정되어 있지 않습니다."
thereIsUnresolvedAbuseReportWarning: "해결되지 않은 신고가 있습니다."
pendingUserApprovals: "승인 대기중인 유저가 있습니다."
recommended: "추천"
check: "체크"
driveCapOverrideLabel: "이 유저의 드라이브 용량을 변경"
Expand All @@ -974,6 +980,11 @@ requireAdminForView: "열람하려면 관리자 계정으로 로그인해야 합
isSystemAccount: "시스템에 의해 자동으로 생성되어 관리되는 계정입니다."
typeToConfirm: "계속하시려면 {x} 을 입력하세요"
deleteAccount: "계정 삭제"
approveAccount: "승인"
denyAccount: "거부하고 계정 삭제"
approved: "승인됨"
notApproved: "미승인"
approvalStatus: "승인상태"
document: "문서"
numberOfPageCache: "페이지 캐시 수"
numberOfPageCacheDescription: "숫자가 클 수록 편리성이 높아지지만, 시스템 자원과 메모리를 더 많이 사용합니다."
Expand Down Expand Up @@ -1066,6 +1077,10 @@ disableFederationConfirm: "정말로 연합을 끄시겠습니까?"
disableFederationConfirmWarn: "연합을 끄더라도 게시물이 비공개로 전환되는 것은 아닙니다. 대부분의 경우 연합을 비활성화할 필요가 없습니다."
disableFederationOk: "연합을 끄기"
invitationRequiredToRegister: "현재 이 서버는 비공개입니다. 회원가입을 하시려면 초대 코드가 필요합니다."
approvalRequiredToRegister: "현재 이 서버는 승인제입니다. 가입하고 싶은 이유를 기입하고, 승인된 사람만이 가입할 수 있습니다."
registerReason: "가입 이유"
registerHasNotBeenApprovedYet: "서버 가입이 아직 승인되지 않았습니다. 잠시 뒤 다시 시도해주세요. 가입 시 이메일 주소를 입력한 경우, 가입이 승인된 후에 메일로 알려드립니다."
registerApprovalEmailRecommended: "서버 가입 승인이 이루어졌는지 알려주기 위해서 가입 시 이메일 입력 필수 기능을 켜는 것을 추천드립니다."
emailNotSupported: "이 서버에서는 메일 전송을 지원하지 않습니다"
postToTheChannel: "채널에 게시하기"
cannotBeChangedLater: "나중에 변경할 수 없습니다."
Expand Down Expand Up @@ -1847,6 +1862,8 @@ _signup:
almostThere: "거의 다 끝났습니다"
emailAddressInfo: "당신이 사용하고 있는 이메일 주소를 입력해 주세요. 이메일 주소는 다른 유저에게 공개되지 않습니다."
emailSent: "입력하신 메일 주소({email})로 확인 메일을 보내드렸습니다. 가입을 완료하시려면 보내드린 메일에 있는 링크로 접속해 주세요."
approvalPending: "계정이 작성되어, 승인을 기다리고 있습니다."
reasonInfo: "이 서버에 가입하고 싶은 이유를 적어주세요."
_accountDelete:
accountDelete: "계정 삭제"
mayTakeTime: "계정 삭제는 서버에 부하를 가하기 때문에, 작성한 콘텐츠나 업로드한 파일의 수가 많으면 완료까지 시간이 걸릴 수 있습니다."
Expand Down Expand Up @@ -2550,6 +2567,7 @@ _moderationLogTypes:
updateRole: "역할 수정"
assignRole: "역할 할당"
unassignRole: "역할 해제"
approve: "승인됨"
suspend: "정지"
unsuspend: "정지 해제"
addCustomEmoji: "커스텀 이모지 추가"
Expand Down
24 changes: 24 additions & 0 deletions packages/backend/migration/1697580470000-approvalSignup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/

export class ApprovalSignup1697580470000 {
name = 'ApprovalSignup1697580470000'

async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" ADD "approvalRequiredForSignup" boolean DEFAULT false NOT NULL`);
await queryRunner.query(`ALTER TABLE "user" ADD "approved" boolean DEFAULT false NOT NULL`);
//▼ 既存のユーザーについては全員Approveにする
await queryRunner.query(`UPDATE "user" SET "approved" = true`);
await queryRunner.query(`ALTER TABLE "user" ADD "signupReason" character varying(1000) NULL`);
await queryRunner.query(`ALTER TABLE "user_pending" ADD "reason" character varying(1000) NULL`);
}

async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "approvalRequiredForSignup"`);
await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "approved"`);
await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "signupReason"`);
await queryRunner.query(`ALTER TABLE "user_pending" DROP COLUMN "reason"`);
}
}
19 changes: 19 additions & 0 deletions packages/backend/migration/1729528715258-Index_user_Approved.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/

export class IndexUserApproved1729528715258 {
name = 'IndexUserApproved1729528715258';
async up(queryRunner) {
await queryRunner.query(`
CREATE INDEX "IDX_USER_APPROVED" ON "user" ("approved")
`);
}

async down(queryRunner) {
await queryRunner.query(`
DROP INDEX "public"."IDX_USER_APPROVED"
`);
}
}
1 change: 1 addition & 0 deletions packages/backend/src/core/CreateSystemUserService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export class CreateSystemUserService {
isRoot: false,
isLocked: true,
isExplorable: false,
approved: true,
isBot: true,
}).then(x => transactionalEntityManager.findOneByOrFail(MiUser, x.identifiers[0]));

Expand Down
2 changes: 2 additions & 0 deletions packages/backend/src/core/SignupService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export class SignupService {
passwordHash?: MiUserProfile['password'] | null;
host?: string | null;
ignorePreservedUsernames?: boolean;
reason?: string | null;
}) {
const { username, password, passwordHash, host } = opts;
let hash = passwordHash;
Expand Down Expand Up @@ -130,6 +131,7 @@ export class SignupService {
host: this.utilityService.toPunyNullable(host),
token: secret,
isRoot: isTheFirstUser,
signupReason: opts.reason,
}));

await transactionalEntityManager.save(new MiUserKeypair({
Expand Down
3 changes: 3 additions & 0 deletions packages/backend/src/core/WebhookTestService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ function generateDummyUser(override?: Partial<MiUser>): MiUser {
requireSigninToViewContents: false,
makeNotesFollowersOnlyBefore: null,
makeNotesHiddenBefore: null,
approved: true,
signupReason: '',
emojis: [],
score: 0,
host: null,
Expand Down Expand Up @@ -196,6 +198,7 @@ function toPackedUserLite(user: MiUser, override?: Packed<'UserLite'>): Packed<'
})),
isBot: user.isBot,
isCat: user.isCat,
approved: user.approved,
emojis: user.emojis,
onlineStatus: 'active',
badgeRoles: [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,7 @@ export class ApPersonService implements OnModuleInit {
alsoKnownAs: person.alsoKnownAs,
isExplorable: person.discoverable,
username: person.preferredUsername,
approved: true,
usernameLower: person.preferredUsername?.toLowerCase(),
host,
inbox: person.inbox,
Expand Down Expand Up @@ -572,6 +573,7 @@ export class ApPersonService implements OnModuleInit {
emojis: emojiNames,
name: truncate(person.name, nameLength),
tags,
approved: true,
isBot: getApType(object) === 'Service' || getApType(object) === 'Application',
isCat: (person as any).isCat === true,
isLocked: person.manuallyApprovesFollowers,
Expand Down
1 change: 1 addition & 0 deletions packages/backend/src/core/entities/MetaEntityService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ export class MetaEntityService {
inquiryUrl: instance.inquiryUrl,
disableRegistration: instance.disableRegistration,
emailRequiredForSignup: instance.emailRequiredForSignup,
approvalRequiredForSignup: instance.approvalRequiredForSignup,
enableHcaptcha: instance.enableHcaptcha,
hcaptchaSiteKey: instance.hcaptchaSiteKey,
enableMcaptcha: instance.enableMcaptcha,
Expand Down
Loading

0 comments on commit ee4a022

Please sign in to comment.