Преглед изворни кода

feat: add user agreement and privacy policy to login page

キュビビイ пре 4 месеци
родитељ
комит
17dafa3b03
2 измењених фајлова са 117 додато и 5 уклоњено
  1. 113 1
      web/src/components/auth/LoginForm.jsx
  2. 4 4
      web/src/components/auth/RegisterForm.jsx

+ 113 - 1
web/src/components/auth/LoginForm.jsx

@@ -37,7 +37,7 @@ import {
   isPasskeySupported,
   isPasskeySupported,
 } from '../../helpers';
 } from '../../helpers';
 import Turnstile from 'react-turnstile';
 import Turnstile from 'react-turnstile';
-import { Button, Card, Divider, Form, Icon, Modal } from '@douyinfe/semi-ui';
+import { Button, Card, Checkbox, Divider, Form, Icon, Modal } from '@douyinfe/semi-ui';
 import Title from '@douyinfe/semi-ui/lib/es/typography/title';
 import Title from '@douyinfe/semi-ui/lib/es/typography/title';
 import Text from '@douyinfe/semi-ui/lib/es/typography/text';
 import Text from '@douyinfe/semi-ui/lib/es/typography/text';
 import TelegramLoginButton from 'react-telegram-login';
 import TelegramLoginButton from 'react-telegram-login';
@@ -84,6 +84,9 @@ const LoginForm = () => {
   const [showTwoFA, setShowTwoFA] = useState(false);
   const [showTwoFA, setShowTwoFA] = useState(false);
   const [passkeySupported, setPasskeySupported] = useState(false);
   const [passkeySupported, setPasskeySupported] = useState(false);
   const [passkeyLoading, setPasskeyLoading] = useState(false);
   const [passkeyLoading, setPasskeyLoading] = useState(false);
+  const [agreedToTerms, setAgreedToTerms] = useState(false);
+  const [hasUserAgreement, setHasUserAgreement] = useState(false);
+  const [hasPrivacyPolicy, setHasPrivacyPolicy] = useState(false);
 
 
   const logo = getLogo();
   const logo = getLogo();
   const systemName = getSystemName();
   const systemName = getSystemName();
@@ -103,6 +106,10 @@ const LoginForm = () => {
       setTurnstileEnabled(true);
       setTurnstileEnabled(true);
       setTurnstileSiteKey(status.turnstile_site_key);
       setTurnstileSiteKey(status.turnstile_site_key);
     }
     }
+    
+    // 从 status 获取用户协议和隐私政策的启用状态
+    setHasUserAgreement(status.user_agreement_enabled || false);
+    setHasPrivacyPolicy(status.privacy_policy_enabled || false);
   }, [status]);
   }, [status]);
 
 
   useEffect(() => {
   useEffect(() => {
@@ -118,6 +125,10 @@ const LoginForm = () => {
   }, []);
   }, []);
 
 
   const onWeChatLoginClicked = () => {
   const onWeChatLoginClicked = () => {
+    if ((hasUserAgreement || hasPrivacyPolicy) && !agreedToTerms) {
+      showInfo(t('请先阅读并同意用户协议和隐私政策'));
+      return;
+    }
     setWechatLoading(true);
     setWechatLoading(true);
     setShowWeChatLoginModal(true);
     setShowWeChatLoginModal(true);
     setWechatLoading(false);
     setWechatLoading(false);
@@ -157,6 +168,10 @@ const LoginForm = () => {
   }
   }
 
 
   async function handleSubmit(e) {
   async function handleSubmit(e) {
+    if ((hasUserAgreement || hasPrivacyPolicy) && !agreedToTerms) {
+      showInfo(t('请先阅读并同意用户协议和隐私政策'));
+      return;
+    }
     if (turnstileEnabled && turnstileToken === '') {
     if (turnstileEnabled && turnstileToken === '') {
       showInfo('请稍后几秒重试,Turnstile 正在检查用户环境!');
       showInfo('请稍后几秒重试,Turnstile 正在检查用户环境!');
       return;
       return;
@@ -208,6 +223,10 @@ const LoginForm = () => {
 
 
   // 添加Telegram登录处理函数
   // 添加Telegram登录处理函数
   const onTelegramLoginClicked = async (response) => {
   const onTelegramLoginClicked = async (response) => {
+    if ((hasUserAgreement || hasPrivacyPolicy) && !agreedToTerms) {
+      showInfo(t('请先阅读并同意用户协议和隐私政策'));
+      return;
+    }
     const fields = [
     const fields = [
       'id',
       'id',
       'first_name',
       'first_name',
@@ -244,6 +263,10 @@ const LoginForm = () => {
 
 
   // 包装的GitHub登录点击处理
   // 包装的GitHub登录点击处理
   const handleGitHubClick = () => {
   const handleGitHubClick = () => {
+    if ((hasUserAgreement || hasPrivacyPolicy) && !agreedToTerms) {
+      showInfo(t('请先阅读并同意用户协议和隐私政策'));
+      return;
+    }
     setGithubLoading(true);
     setGithubLoading(true);
     try {
     try {
       onGitHubOAuthClicked(status.github_client_id);
       onGitHubOAuthClicked(status.github_client_id);
@@ -255,6 +278,10 @@ const LoginForm = () => {
 
 
   // 包装的OIDC登录点击处理
   // 包装的OIDC登录点击处理
   const handleOIDCClick = () => {
   const handleOIDCClick = () => {
+    if ((hasUserAgreement || hasPrivacyPolicy) && !agreedToTerms) {
+      showInfo(t('请先阅读并同意用户协议和隐私政策'));
+      return;
+    }
     setOidcLoading(true);
     setOidcLoading(true);
     try {
     try {
       onOIDCClicked(status.oidc_authorization_endpoint, status.oidc_client_id);
       onOIDCClicked(status.oidc_authorization_endpoint, status.oidc_client_id);
@@ -266,6 +293,10 @@ const LoginForm = () => {
 
 
   // 包装的LinuxDO登录点击处理
   // 包装的LinuxDO登录点击处理
   const handleLinuxDOClick = () => {
   const handleLinuxDOClick = () => {
+    if ((hasUserAgreement || hasPrivacyPolicy) && !agreedToTerms) {
+      showInfo(t('请先阅读并同意用户协议和隐私政策'));
+      return;
+    }
     setLinuxdoLoading(true);
     setLinuxdoLoading(true);
     try {
     try {
       onLinuxDOOAuthClicked(status.linuxdo_client_id);
       onLinuxDOOAuthClicked(status.linuxdo_client_id);
@@ -283,6 +314,10 @@ const LoginForm = () => {
   };
   };
 
 
   const handlePasskeyLogin = async () => {
   const handlePasskeyLogin = async () => {
+    if ((hasUserAgreement || hasPrivacyPolicy) && !agreedToTerms) {
+      showInfo(t('请先阅读并同意用户协议和隐私政策'));
+      return;
+    }
     if (!passkeySupported) {
     if (!passkeySupported) {
       showInfo('当前环境无法使用 Passkey 登录');
       showInfo('当前环境无法使用 Passkey 登录');
       return;
       return;
@@ -486,6 +521,44 @@ const LoginForm = () => {
                 </Button>
                 </Button>
               </div>
               </div>
 
 
+              {(hasUserAgreement || hasPrivacyPolicy) && (
+                <div className='mt-6'>
+                  <Checkbox
+                    checked={agreedToTerms}
+                    onChange={(e) => setAgreedToTerms(e.target.checked)}
+                  >
+                    <Text size='small' className='text-gray-600'>
+                      {t('我已阅读并同意')}
+                      {hasUserAgreement && (
+                        <>
+                          <a
+                            href='/user-agreement'
+                            target='_blank'
+                            rel='noopener noreferrer'
+                            className='text-blue-600 hover:text-blue-800 mx-1'
+                          >
+                            {t('用户协议')}
+                          </a>
+                        </>
+                      )}
+                      {hasUserAgreement && hasPrivacyPolicy && t('和')}
+                      {hasPrivacyPolicy && (
+                        <>
+                          <a
+                            href='/privacy-policy'
+                            target='_blank'
+                            rel='noopener noreferrer'
+                            className='text-blue-600 hover:text-blue-800 mx-1'
+                          >
+                            {t('隐私政策')}
+                          </a>
+                        </>
+                        )}
+                      </Text>
+                    </Checkbox>
+                  </div>
+                )}
+
               {!status.self_use_mode_enabled && (
               {!status.self_use_mode_enabled && (
                 <div className='mt-6 text-center text-sm'>
                 <div className='mt-6 text-center text-sm'>
                   <Text>
                   <Text>
@@ -554,6 +627,44 @@ const LoginForm = () => {
                   prefix={<IconLock />}
                   prefix={<IconLock />}
                 />
                 />
 
 
+                {(hasUserAgreement || hasPrivacyPolicy) && (
+                  <div className='pt-4'>
+                    <Checkbox
+                      checked={agreedToTerms}
+                      onChange={(e) => setAgreedToTerms(e.target.checked)}
+                    >
+                      <Text size='small' className='text-gray-600'>
+                        {t('我已阅读并同意')}
+                        {hasUserAgreement && (
+                          <>
+                            <a
+                              href='/user-agreement'
+                              target='_blank'
+                              rel='noopener noreferrer'
+                              className='text-blue-600 hover:text-blue-800 mx-1'
+                            >
+                              {t('用户协议')}
+                            </a>
+                          </>
+                        )}
+                        {hasUserAgreement && hasPrivacyPolicy && t('和')}
+                        {hasPrivacyPolicy && (
+                          <>
+                            <a
+                              href='/privacy-policy'
+                              target='_blank'
+                              rel='noopener noreferrer'
+                              className='text-blue-600 hover:text-blue-800 mx-1'
+                            >
+                              {t('隐私政策')}
+                            </a>
+                          </>
+                        )}
+                      </Text>
+                    </Checkbox>
+                  </div>
+                )}
+
                 <div className='space-y-2 pt-2'>
                 <div className='space-y-2 pt-2'>
                   <Button
                   <Button
                     theme='solid'
                     theme='solid'
@@ -562,6 +673,7 @@ const LoginForm = () => {
                     htmlType='submit'
                     htmlType='submit'
                     onClick={handleSubmit}
                     onClick={handleSubmit}
                     loading={loginLoading}
                     loading={loginLoading}
+                    disabled={(hasUserAgreement || hasPrivacyPolicy) && !agreedToTerms}
                   >
                   >
                     {t('继续')}
                     {t('继续')}
                   </Button>
                   </Button>

+ 4 - 4
web/src/components/auth/RegisterForm.jsx

@@ -30,7 +30,7 @@ import {
   setUserData,
   setUserData,
 } from '../../helpers';
 } from '../../helpers';
 import Turnstile from 'react-turnstile';
 import Turnstile from 'react-turnstile';
-import { Button, Card, Divider, Form, Icon, Modal } from '@douyinfe/semi-ui';
+import { Button, Card, Checkbox, Divider, Form, Icon, Modal } from '@douyinfe/semi-ui';
 import Title from '@douyinfe/semi-ui/lib/es/typography/title';
 import Title from '@douyinfe/semi-ui/lib/es/typography/title';
 import Text from '@douyinfe/semi-ui/lib/es/typography/text';
 import Text from '@douyinfe/semi-ui/lib/es/typography/text';
 import {
 import {
@@ -514,9 +514,9 @@ const RegisterForm = () => {
 
 
                 {(hasUserAgreement || hasPrivacyPolicy) && (
                 {(hasUserAgreement || hasPrivacyPolicy) && (
                   <div className='pt-4'>
                   <div className='pt-4'>
-                    <Form.Checkbox
+                    <Checkbox
                       checked={agreedToTerms}
                       checked={agreedToTerms}
-                      onChange={(checked) => setAgreedToTerms(checked)}
+                      onChange={(e) => setAgreedToTerms(e.target.checked)}
                     >
                     >
                       <Text size='small' className='text-gray-600'>
                       <Text size='small' className='text-gray-600'>
                         {t('我已阅读并同意')}
                         {t('我已阅读并同意')}
@@ -546,7 +546,7 @@ const RegisterForm = () => {
                           </>
                           </>
                         )}
                         )}
                       </Text>
                       </Text>
-                    </Form.Checkbox>
+                    </Checkbox>
                   </div>
                   </div>
                 )}
                 )}