feat: support email domain restriction
This commit is contained in:
parent
ec88c0c240
commit
7c011066d1
@ -42,6 +42,17 @@ var WeChatAuthEnabled = false
|
|||||||
var TurnstileCheckEnabled = false
|
var TurnstileCheckEnabled = false
|
||||||
var RegisterEnabled = true
|
var RegisterEnabled = true
|
||||||
|
|
||||||
|
var EmailDomainRestrictionEnabled = false
|
||||||
|
var RestrictedEmailDomains = []string{
|
||||||
|
"gmail.com",
|
||||||
|
"163.com",
|
||||||
|
"qq.com",
|
||||||
|
"outlook.com",
|
||||||
|
"hotmail.com",
|
||||||
|
"icloud.com",
|
||||||
|
"yahoo.com",
|
||||||
|
}
|
||||||
|
|
||||||
var LogConsumeEnabled = true
|
var LogConsumeEnabled = true
|
||||||
|
|
||||||
var SMTPServer = ""
|
var SMTPServer = ""
|
||||||
|
@ -3,10 +3,12 @@ package controller
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"one-api/common"
|
"one-api/common"
|
||||||
"one-api/model"
|
"one-api/model"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetStatus(c *gin.Context) {
|
func GetStatus(c *gin.Context) {
|
||||||
@ -78,6 +80,20 @@ func SendEmailVerification(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if common.EmailDomainRestrictionEnabled {
|
||||||
|
allowedEmailDomains := common.RestrictedEmailDomains
|
||||||
|
|
||||||
|
// Check if email suffix is allowed
|
||||||
|
allowed := strings.Contains(strings.Join(allowedEmailDomains, ","), strings.Split(email, "@")[1])
|
||||||
|
|
||||||
|
if !allowed {
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"success": false,
|
||||||
|
"message": "该邮箱地址不允许注册",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
if model.IsEmailAlreadyTaken(email) {
|
if model.IsEmailAlreadyTaken(email) {
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"success": false,
|
"success": false,
|
||||||
|
@ -2,11 +2,12 @@ package controller
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"one-api/common"
|
"one-api/common"
|
||||||
"one-api/model"
|
"one-api/model"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetOptions(c *gin.Context) {
|
func GetOptions(c *gin.Context) {
|
||||||
@ -49,6 +50,14 @@ func UpdateOption(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
case "EmailDomainRestrictionEnabled":
|
||||||
|
if option.Value == "true" && len(common.RestrictedEmailDomains) == 0 {
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"success": false,
|
||||||
|
"message": "无法启用邮箱域名限制,请先填入限制的邮箱域名!",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
case "WeChatAuthEnabled":
|
case "WeChatAuthEnabled":
|
||||||
if option.Value == "true" && common.WeChatServerAddress == "" {
|
if option.Value == "true" && common.WeChatServerAddress == "" {
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
@ -39,6 +39,8 @@ func InitOptionMap() {
|
|||||||
common.OptionMap["DisplayInCurrencyEnabled"] = strconv.FormatBool(common.DisplayInCurrencyEnabled)
|
common.OptionMap["DisplayInCurrencyEnabled"] = strconv.FormatBool(common.DisplayInCurrencyEnabled)
|
||||||
common.OptionMap["DisplayTokenStatEnabled"] = strconv.FormatBool(common.DisplayTokenStatEnabled)
|
common.OptionMap["DisplayTokenStatEnabled"] = strconv.FormatBool(common.DisplayTokenStatEnabled)
|
||||||
common.OptionMap["ChannelDisableThreshold"] = strconv.FormatFloat(common.ChannelDisableThreshold, 'f', -1, 64)
|
common.OptionMap["ChannelDisableThreshold"] = strconv.FormatFloat(common.ChannelDisableThreshold, 'f', -1, 64)
|
||||||
|
common.OptionMap["EmailDomainRestrictionEnabled"] = strconv.FormatBool(common.EmailDomainRestrictionEnabled)
|
||||||
|
common.OptionMap["RestrictedEmailDomains"] = strings.Join(common.RestrictedEmailDomains, ",")
|
||||||
common.OptionMap["SMTPServer"] = ""
|
common.OptionMap["SMTPServer"] = ""
|
||||||
common.OptionMap["SMTPFrom"] = ""
|
common.OptionMap["SMTPFrom"] = ""
|
||||||
common.OptionMap["SMTPPort"] = strconv.Itoa(common.SMTPPort)
|
common.OptionMap["SMTPPort"] = strconv.Itoa(common.SMTPPort)
|
||||||
@ -141,6 +143,8 @@ func updateOptionMap(key string, value string) (err error) {
|
|||||||
common.TurnstileCheckEnabled = boolValue
|
common.TurnstileCheckEnabled = boolValue
|
||||||
case "RegisterEnabled":
|
case "RegisterEnabled":
|
||||||
common.RegisterEnabled = boolValue
|
common.RegisterEnabled = boolValue
|
||||||
|
case "EmailDomainRestrictionEnabled":
|
||||||
|
common.EmailDomainRestrictionEnabled = boolValue
|
||||||
case "AutomaticDisableChannelEnabled":
|
case "AutomaticDisableChannelEnabled":
|
||||||
common.AutomaticDisableChannelEnabled = boolValue
|
common.AutomaticDisableChannelEnabled = boolValue
|
||||||
case "ApproximateTokenEnabled":
|
case "ApproximateTokenEnabled":
|
||||||
@ -154,6 +158,8 @@ func updateOptionMap(key string, value string) (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch key {
|
switch key {
|
||||||
|
case "RestrictedEmailDomains":
|
||||||
|
common.RestrictedEmailDomains = strings.Split(value, ",")
|
||||||
case "SMTPServer":
|
case "SMTPServer":
|
||||||
common.SMTPServer = value
|
common.SMTPServer = value
|
||||||
case "SMTPPort":
|
case "SMTPPort":
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { Divider, Form, Grid, Header, Message } from 'semantic-ui-react';
|
import { Button, Divider, Form, Grid, Header, Input, Message } from 'semantic-ui-react';
|
||||||
import { API, removeTrailingSlash, showError, verifyJSON } from '../helpers';
|
import { API, removeTrailingSlash, showError } from '../helpers';
|
||||||
|
|
||||||
const SystemSetting = () => {
|
const SystemSetting = () => {
|
||||||
let [inputs, setInputs] = useState({
|
let [inputs, setInputs] = useState({
|
||||||
@ -26,9 +26,13 @@ const SystemSetting = () => {
|
|||||||
TurnstileSiteKey: '',
|
TurnstileSiteKey: '',
|
||||||
TurnstileSecretKey: '',
|
TurnstileSecretKey: '',
|
||||||
RegisterEnabled: '',
|
RegisterEnabled: '',
|
||||||
|
EmailDomainRestrictionEnabled: '',
|
||||||
|
RestrictedEmailDomains: ''
|
||||||
});
|
});
|
||||||
const [originInputs, setOriginInputs] = useState({});
|
const [originInputs, setOriginInputs] = useState({});
|
||||||
let [loading, setLoading] = useState(false);
|
let [loading, setLoading] = useState(false);
|
||||||
|
const [restrictedEmailDomains, setRestrictedEmailDomains] = useState([]);
|
||||||
|
const [restrictedDomainInput, setRestrictedDomainInput] = useState('');
|
||||||
|
|
||||||
const getOptions = async () => {
|
const getOptions = async () => {
|
||||||
const res = await API.get('/api/option/');
|
const res = await API.get('/api/option/');
|
||||||
@ -38,8 +42,15 @@ const SystemSetting = () => {
|
|||||||
data.forEach((item) => {
|
data.forEach((item) => {
|
||||||
newInputs[item.key] = item.value;
|
newInputs[item.key] = item.value;
|
||||||
});
|
});
|
||||||
setInputs(newInputs);
|
setInputs({
|
||||||
|
...newInputs,
|
||||||
|
RestrictedEmailDomains: newInputs.RestrictedEmailDomains.split(',')
|
||||||
|
});
|
||||||
setOriginInputs(newInputs);
|
setOriginInputs(newInputs);
|
||||||
|
|
||||||
|
setRestrictedEmailDomains(newInputs.RestrictedEmailDomains.split(',').map((item) => {
|
||||||
|
return { key: item, text: item, value: item };
|
||||||
|
}));
|
||||||
} else {
|
} else {
|
||||||
showError(message);
|
showError(message);
|
||||||
}
|
}
|
||||||
@ -58,6 +69,7 @@ const SystemSetting = () => {
|
|||||||
case 'GitHubOAuthEnabled':
|
case 'GitHubOAuthEnabled':
|
||||||
case 'WeChatAuthEnabled':
|
case 'WeChatAuthEnabled':
|
||||||
case 'TurnstileCheckEnabled':
|
case 'TurnstileCheckEnabled':
|
||||||
|
case 'EmailDomainRestrictionEnabled':
|
||||||
case 'RegisterEnabled':
|
case 'RegisterEnabled':
|
||||||
value = inputs[key] === 'true' ? 'false' : 'true';
|
value = inputs[key] === 'true' ? 'false' : 'true';
|
||||||
break;
|
break;
|
||||||
@ -70,7 +82,12 @@ const SystemSetting = () => {
|
|||||||
});
|
});
|
||||||
const { success, message } = res.data;
|
const { success, message } = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
setInputs((inputs) => ({ ...inputs, [key]: value }));
|
if (key === 'RestrictedEmailDomains') {
|
||||||
|
value = value.split(',');
|
||||||
|
}
|
||||||
|
setInputs((inputs) => ({
|
||||||
|
...inputs, [key]: value
|
||||||
|
}));
|
||||||
} else {
|
} else {
|
||||||
showError(message);
|
showError(message);
|
||||||
}
|
}
|
||||||
@ -88,7 +105,8 @@ const SystemSetting = () => {
|
|||||||
name === 'WeChatServerToken' ||
|
name === 'WeChatServerToken' ||
|
||||||
name === 'WeChatAccountQRCodeImageURL' ||
|
name === 'WeChatAccountQRCodeImageURL' ||
|
||||||
name === 'TurnstileSiteKey' ||
|
name === 'TurnstileSiteKey' ||
|
||||||
name === 'TurnstileSecretKey'
|
name === 'TurnstileSecretKey' ||
|
||||||
|
name === 'RestrictedEmailDomains'
|
||||||
) {
|
) {
|
||||||
setInputs((inputs) => ({ ...inputs, [name]: value }));
|
setInputs((inputs) => ({ ...inputs, [name]: value }));
|
||||||
} else {
|
} else {
|
||||||
@ -123,6 +141,12 @@ const SystemSetting = () => {
|
|||||||
) {
|
) {
|
||||||
await updateOption('SMTPToken', inputs.SMTPToken);
|
await updateOption('SMTPToken', inputs.SMTPToken);
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
originInputs['RestrictedEmailDomains'] !== inputs.RestrictedEmailDomains.join(',') &&
|
||||||
|
inputs.SMTPToken !== ''
|
||||||
|
) {
|
||||||
|
await updateOption('RestrictedEmailDomains', inputs.RestrictedEmailDomains.join(','));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const submitWeChat = async () => {
|
const submitWeChat = async () => {
|
||||||
@ -282,12 +306,58 @@ const SystemSetting = () => {
|
|||||||
label='SMTP 访问凭证'
|
label='SMTP 访问凭证'
|
||||||
name='SMTPToken'
|
name='SMTPToken'
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
type='password'
|
checked={inputs.RegisterEnabled === 'true'}
|
||||||
autoComplete='new-password'
|
|
||||||
value={inputs.SMTPToken}
|
|
||||||
placeholder='敏感信息不会发送到前端显示'
|
placeholder='敏感信息不会发送到前端显示'
|
||||||
/>
|
/>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
|
<Form.Group widths={3}>
|
||||||
|
<Form.Checkbox
|
||||||
|
label='电子邮件域名限制'
|
||||||
|
name='EmailDomainRestrictionEnabled'
|
||||||
|
onChange={handleInputChange}
|
||||||
|
checked={inputs.EmailDomainRestrictionEnabled === 'true'}
|
||||||
|
/>
|
||||||
|
</Form.Group>
|
||||||
|
<Form.Group widths={3}>
|
||||||
|
<Form.Dropdown
|
||||||
|
label='受限电子邮件域名'
|
||||||
|
placeholder='受限电子邮件域名'
|
||||||
|
name='RestrictedEmailDomains'
|
||||||
|
required
|
||||||
|
fluid
|
||||||
|
multiple
|
||||||
|
selection
|
||||||
|
onChange={handleInputChange}
|
||||||
|
value={inputs.RestrictedEmailDomains}
|
||||||
|
autoComplete='new-password'
|
||||||
|
options={restrictedEmailDomains}
|
||||||
|
/>
|
||||||
|
<Form.Input
|
||||||
|
label='添加受限电子邮件域名'
|
||||||
|
action={
|
||||||
|
<Button type='button' onClick={() => {
|
||||||
|
const localDomainList = inputs.RestrictedEmailDomains;
|
||||||
|
if (restrictedDomainInput !== '' && !localDomainList.includes(restrictedDomainInput)) {
|
||||||
|
setRestrictedDomainInput('');
|
||||||
|
setInputs({
|
||||||
|
...inputs,
|
||||||
|
RestrictedEmailDomains: [...localDomainList, restrictedDomainInput],
|
||||||
|
});
|
||||||
|
setRestrictedEmailDomains([...restrictedEmailDomains, {
|
||||||
|
key: restrictedDomainInput,
|
||||||
|
text: restrictedDomainInput,
|
||||||
|
value: restrictedDomainInput,
|
||||||
|
}]);
|
||||||
|
}
|
||||||
|
}}>填入</Button>
|
||||||
|
}
|
||||||
|
placeholder='输入受限电子邮件域名'
|
||||||
|
value={restrictedDomainInput}
|
||||||
|
onChange={(e, { value }) => {
|
||||||
|
setRestrictedDomainInput(value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Form.Group>
|
||||||
<Form.Button onClick={submitSMTP}>保存 SMTP 设置</Form.Button>
|
<Form.Button onClick={submitSMTP}>保存 SMTP 设置</Form.Button>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Header as='h3'>
|
<Header as='h3'>
|
||||||
|
Loading…
Reference in New Issue
Block a user