diff --git a/app/Http/Controllers/Api/UsersController.php b/app/Http/Controllers/Api/UsersController.php index 7e978652c..1af144611 100755 --- a/app/Http/Controllers/Api/UsersController.php +++ b/app/Http/Controllers/Api/UsersController.php @@ -339,21 +339,25 @@ class UsersController extends AbstractController } /** - * @api {get} api/users/reg/needinvite 是否需要邀请码 + * @api {get} api/users/reg/needinvite 注册状态 * - * @apiDescription 用于判断注册是否需要邀请码 + * @apiDescription 用于判断是否允许注册及是否需要邀请码 * @apiVersion 1.0.0 * @apiGroup users * @apiName reg__needinvite * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) - * @apiSuccess {Object} data 返回数据 + * @apiSuccess {Object} data 返回数据 + * @apiSuccess {Boolean} data.enabled 是否允许注册 + * @apiSuccess {Boolean} data.need 是否需要邀请码 */ public function reg__needinvite() { + $reg = Base::settingFind('system', 'reg', 'open'); return Base::retSuccess('success', [ - 'need' => Base::settingFind('system', 'reg') == 'invite' + 'enabled' => $reg !== 'close', + 'need' => $reg === 'invite', ]); } diff --git a/resources/ai-kb/zh/concept/user-account/reg-need-invite.md b/resources/ai-kb/zh/concept/user-account/reg-need-invite.md index a82e1991d..732be7f0f 100644 --- a/resources/ai-kb/zh/concept/user-account/reg-need-invite.md +++ b/resources/ai-kb/zh/concept/user-account/reg-need-invite.md @@ -19,7 +19,7 @@ negative: - 邀请码与「邀请同事加入项目」是两回事;这里指的是系统级开放注册的密钥 - 邀请码只有一个全局值,不区分用户、不限次数、不过期 - 验证不通过会直接拒绝,没有「邀请码错了几次锁定」的限制 -last_verified: v1.7.90 +last_verified: v1.8.89 --- # 注册是否需要邀请码 @@ -39,9 +39,11 @@ DooTask 的注册方式由系统设置 `system.reg` 控制,共三档: - 注册时把这个字符串填到「邀请码」字段提交,后端比对 `Request.invite == setting.reg_invite` ## 接口判定 -前端可调 `api/users/reg/needinvite` 拿到 `{ need: true/false }`,据此决定登录页注册 tab 是否展示「邀请码」输入框: +前端可调 `api/users/reg/needinvite` 拿到 `{ enabled: true/false, need: true/false }`,据此决定是否显示注册入口,以及注册表单是否展示「邀请码」输入框: +- `enabled=false` → reg=close,隐藏注册入口;即使通过 `/login?type=reg` 直达也停留在登录表单 +- `enabled=true` → reg=open 或 reg=invite,显示注册入口 - `need=true` → reg=invite,显示邀请码输入框 -- `need=false` → reg=open(reg=close 时注册入口本身就该隐藏) +- `need=false` → reg=open 或 reg=close,以 `enabled` 为准 ## 与「项目邀请加入」的区别 - **本概念**:决定能否成为 DooTask 用户(账号级别) diff --git a/resources/ai-kb/zh/howto/user-account/register.md b/resources/ai-kb/zh/howto/user-account/register.md index b6cf00f73..251d145ef 100644 --- a/resources/ai-kb/zh/howto/user-account/register.md +++ b/resources/ai-kb/zh/howto/user-account/register.md @@ -21,7 +21,7 @@ negative: - 注册方式为「邀请码」时必须填正确邀请码,否则报「请输入正确的邀请码」 - 邮箱长度和密码长度都不能超过 32 个字符 - 开启「注册需邮箱验证」时,注册后必须先验证邮箱才能登录 -last_verified: v1.7.90 +last_verified: v1.8.89 --- # 注册账号 @@ -35,7 +35,7 @@ last_verified: v1.7.90 - **open(开放)**:直接填邮箱+密码即可注册 - **invite(邀请码)**:除邮箱+密码外,还需填邀请码;可调 `api/users/reg/needinvite` 提前判断(详见 [[user-account.reg-need-invite.concept]]) -- **close(关闭)**:注册入口不可用,提示「未开放注册」 +- **close(关闭)**:登录页隐藏注册入口,直达 `/login?type=reg` 也停留在登录表单;直接提交注册接口时提示「未开放注册」 ## 操作步骤 1. 进入登录页,切到「注册」tab diff --git a/resources/assets/js/pages/login.vue b/resources/assets/js/pages/login.vue index 059abb3ce..a09d09fb0 100644 --- a/resources/assets/js/pages/login.vue +++ b/resources/assets/js/pages/login.vue @@ -100,7 +100,7 @@
{{$L('已经有帐号?')}} {{$L('登录帐号')}}
-
{{$L('还没有帐号?')}} {{$L('注册帐号')}}
+
{{$L('还没有帐号?')}} {{$L('注册帐号')}}
@@ -199,6 +199,7 @@ export default { invite: '', needInvite: false, + registrationAvailable: false, privacyShow: false, } @@ -225,7 +226,7 @@ export default { }, activated() { - this.loginType = this.$route.query.type === 'reg' ? 'reg' : 'login' + this.refreshRegistrationSetting(); // this.getDemoAccount(); }, @@ -284,20 +285,14 @@ export default { watch: { '$route' ({query}) { - if (query.type=='reg'){ - this.$nextTick(()=>{ - this.loginType = "reg" - }) + if (this.routeName !== 'login') { + return; } + this.refreshRegistrationSetting(query.type === 'reg'); }, loginMode() { this.qrcodeRefresh() }, - loginType(val) { - if (val == 'reg') { - this.getNeedInvite(); - } - }, }, methods: { @@ -321,13 +316,23 @@ export default { }); }, - getNeedInvite() { - this.$store.dispatch("call", { + refreshRegistrationSetting(requestRegistration = this.$route.query.type === 'reg') { + if (this.isNotServer()) { + this.registrationAvailable = false; + this.loginType = 'login'; + return Promise.resolve(); + } + return this.$store.dispatch("call", { url: 'users/reg/needinvite', }).then(({data}) => { this.needInvite = !!data.need; + // Older servers only return `need`; preserve their original registration behavior. + this.registrationAvailable = typeof data.enabled === 'undefined' || !!data.enabled; + this.loginType = requestRegistration && this.registrationAvailable ? 'reg' : 'login'; }).catch(_ => { this.needInvite = false; + this.registrationAvailable = false; + this.loginType = 'login'; }); }, diff --git a/routes/api-map.md b/routes/api-map.md index 82ab72f88..37e219c36 100644 --- a/routes/api-map.md +++ b/routes/api-map.md @@ -23,7 +23,7 @@ API 使用动态路由(见 `routes/web.php`),URL 段映射为控制器方 | api/users/login/codejson | login__codejson() | get | 验证码json | | api/users/logout | logout() | get | 退出登录 | | api/users/token/expire | token__expire() | get | 查询 token 过期时间 | -| api/users/reg/needinvite | reg__needinvite() | get | 是否需要邀请码 | +| api/users/reg/needinvite | reg__needinvite() | get | 注册状态 | | api/users/info | info() | get | 获取我的信息 | | api/users/info/managed_departments | info__managed_departments() | get | 获取我可切换负责人视角的部门列表 | | api/users/info/departments | info__departments() | get | 获取我的部门列表 | diff --git a/tests/Feature/RegistrationAvailabilityTest.php b/tests/Feature/RegistrationAvailabilityTest.php new file mode 100644 index 000000000..a45728e4b --- /dev/null +++ b/tests/Feature/RegistrationAvailabilityTest.php @@ -0,0 +1,34 @@ + 'close'], true); + + $this->getJson('/api/users/reg/needinvite') + ->assertOk() + ->assertJsonPath('ret', 1) + ->assertJsonPath('data.enabled', false) + ->assertJsonPath('data.need', false); + } + + public function test_registration_status_reports_invite_registration(): void + { + Base::setting('system', ['reg' => 'invite'], true); + + $this->getJson('/api/users/reg/needinvite') + ->assertOk() + ->assertJsonPath('ret', 1) + ->assertJsonPath('data.enabled', true) + ->assertJsonPath('data.need', true); + } +}