diff --git a/frontend/src/Route.tsx b/frontend/src/Route.tsx
index ef46d14..0d5597a 100644
--- a/frontend/src/Route.tsx
+++ b/frontend/src/Route.tsx
@@ -9,6 +9,7 @@ import Layout from '@/views/Layout'
import UserAdmin from "@/views/admin/UserAdmin";
import NeedLogin from "@/components/NeedLogin";
import Index from "@/views/Index";
+import SignUpEmail from "@/views/SignUpEmail";
const router = createBrowserRouter([
{ path: "*", Component: Root },
@@ -23,6 +24,7 @@ function Root() {
404
} />
} />
} />
+ } />
}>
} />
diff --git a/frontend/src/apis/apis.ts b/frontend/src/apis/apis.ts
index b191fb8..76be4f4 100644
--- a/frontend/src/apis/apis.ts
+++ b/frontend/src/apis/apis.ts
@@ -117,4 +117,15 @@ export async function editUser(u: EditUser, token: string, uid: string) {
body: JSON.stringify(u)
})
return await apiGet(r)
+}
+
+export async function sendRegEmail(email: string, captchaToken: string) {
+ const r = await fetch(root() + "/api/v1/user/reg_email", {
+ method: "POST",
+ body: JSON.stringify({
+ "email": email,
+ "captchaToken": captchaToken
+ })
+ })
+ return await apiGet(r)
}
\ No newline at end of file
diff --git a/frontend/src/apis/model.ts b/frontend/src/apis/model.ts
index cb930d6..dffb170 100644
--- a/frontend/src/apis/model.ts
+++ b/frontend/src/apis/model.ts
@@ -40,6 +40,8 @@ export interface ApiConfig {
captcha: captcha
AllowChangeName: boolean
serverName: string
+ NeedEmail: boolean
+ AllowDomain: string[]
}
export interface UserInfo {
diff --git a/frontend/src/views/Login.tsx b/frontend/src/views/Login.tsx
index 4dc2751..a4fe5f0 100644
--- a/frontend/src/views/Login.tsx
+++ b/frontend/src/views/Login.tsx
@@ -64,6 +64,9 @@ export default function SignIn() {
console.warn(v)
if (v instanceof ApiErr) {
switch (v.code) {
+ case 10:
+ setErr("验证码错误")
+ return
case 6:
setErr("密码或用户名错误")
return
diff --git a/frontend/src/views/Register.tsx b/frontend/src/views/Register.tsx
index dfd1c7e..79f2093 100644
--- a/frontend/src/views/Register.tsx
+++ b/frontend/src/views/Register.tsx
@@ -69,6 +69,9 @@ export default function SignUp() {
console.warn(v)
if (v instanceof ApiErr) {
switch (v.code) {
+ case 10:
+ setRegErr("验证码错误")
+ return
case 3:
setRegErr("邮箱已存在")
return
diff --git a/frontend/src/views/SignUpEmail.tsx b/frontend/src/views/SignUpEmail.tsx
new file mode 100644
index 0000000..203ff79
--- /dev/null
+++ b/frontend/src/views/SignUpEmail.tsx
@@ -0,0 +1,176 @@
+import Avatar from '@mui/material/Avatar';
+import Button from '@mui/material/Button';
+import CssBaseline from '@mui/material/CssBaseline';
+import Grid from '@mui/material/Grid';
+import Box from '@mui/material/Box';
+import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
+import Typography from '@mui/material/Typography';
+import Container from '@mui/material/Container';
+import FormControl from '@mui/material/FormControl';
+import InputLabel from '@mui/material/InputLabel';
+import Select from '@mui/material/Select';
+import MenuItem from '@mui/material/MenuItem';
+import TextField from '@mui/material/TextField';
+import { useRequest, useTitle } from 'ahooks';
+import { getConfig, sendRegEmail } from '@/apis/apis';
+import { useEffect, useRef, useState } from 'react';
+import Snackbar from '@mui/material/Snackbar';
+import Alert from '@mui/material/Alert';
+import CaptchaWidget from '@/components/CaptchaWidget';
+import type { refType as CaptchaWidgetRef } from '@/components/CaptchaWidget'
+import Dialog from '@mui/material/Dialog';
+import DialogTitle from '@mui/material/DialogTitle';
+import DialogContent from '@mui/material/DialogContent';
+import DialogActions from '@mui/material/DialogActions';
+import { useNavigate } from "react-router-dom";
+import { ApiErr } from '@/apis/error';
+import Loading from '@/components/Loading';
+
+export default function SignUpEmail() {
+ const [err, setErr] = useState("");
+ const [domain, setDomain] = useState("");
+ const [email, setEmail] = useState("")
+ const captchaRef = useRef(null)
+ const [captchaToken, setCaptchaToken] = useState("");
+ const [open, setOpen] = useState(false);
+ useTitle("注册")
+ const navigate = useNavigate();
+ const [helperText, setHelperText] = useState("")
+ const [loading, setLoading] = useState(false);
+
+ const server = useRequest(getConfig, {
+ cacheKey: "/api/v1/config",
+ staleTime: 60000,
+ onError: e => {
+ console.warn(e)
+ setErr(String(e))
+ }
+ })
+
+ useEffect(() => {
+ if (server.data?.AllowDomain.length != 0) {
+ setDomain(server.data?.AllowDomain[0] ?? "")
+ }
+ }, [server.data?.AllowDomain])
+
+ const emailonChange = (e: React.ChangeEvent) => {
+ setEmail(e.target.value)
+ if (e.target.value == "") {
+ setHelperText("邮箱不得为空")
+ }
+ setHelperText("")
+ }
+
+ const onSubmit = (e: React.FormEvent) => {
+ e.preventDefault()
+ if (email == "") {
+ setHelperText("邮箱不得为空")
+ }
+ const sendEmail = (() => {
+ if (domain != "") {
+ return `${email}@${domain}`
+ }
+ return email
+ })()
+
+ if (!/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(sendEmail)){
+ setHelperText("邮箱格式错误")
+ return
+ }
+
+ if (server.data?.captcha.type != "" && captchaToken == "") {
+ return
+ }
+ setLoading(true)
+ sendRegEmail(sendEmail, captchaToken).then(() => setOpen(true)).catch(e => {
+ captchaRef.current?.reload()
+ console.warn(e)
+ if (e instanceof ApiErr) {
+ switch (e.code) {
+ case 10:
+ setErr("验证码错误")
+ return
+ case 11:
+ setErr("暂时无法对此邮箱发送邮件")
+ return
+ }
+ }
+ setErr(String(e))
+ }).finally(() => setLoading(false))
+
+ }
+
+ const handleClose = () => {
+ navigate("/")
+ }
+
+
+ return (
+
+
+
+
+
+
+
+ 输入邮箱
+
+
+
+
+
+ {
+ server.data?.AllowDomain.length != 0 &&
+
+ 域名
+
+
+ }
+
+
+
+
+
+
+
+
+
+ setErr("")} severity="error">{err}
+
+
+ {loading && }
+
+ )
+}
\ No newline at end of file
diff --git a/model/model.go b/model/model.go
index 58cdce2..342c3d2 100644
--- a/model/model.go
+++ b/model/model.go
@@ -63,6 +63,8 @@ type Config struct {
Captcha Captcha `json:"captcha"`
AllowChangeName bool
ServerName string `json:"serverName"`
+ NeedEmail bool
+ AllowDomain []string
}
type EditUser struct {
diff --git a/server/route/route.go b/server/route/route.go
index adf35ad..39267d5 100644
--- a/server/route/route.go
+++ b/server/route/route.go
@@ -81,6 +81,7 @@ func newSkinApi(handel *handle.Handel, userHandel *handle.UserHandel, adminHande
r.Post("/user/reg", userHandel.Reg())
r.Post("/user/login", userHandel.Login())
r.Get("/config", handel.GetConfig())
+ r.Post("/user/reg_email", userHandel.SendRegEmail())
r.Group(func(r chi.Router) {
r.Use(adminHandel.NeedAuth)
diff --git a/server/wire_gen.go b/server/wire_gen.go
index 1d55ab1..c7d6992 100644
--- a/server/wire_gen.go
+++ b/server/wire_gen.go
@@ -63,9 +63,15 @@ func InitializeRoute(ctx context.Context, c config.Config) (*http.Server, func()
handleError := handelerror.NewHandleError(logger)
httpClient := ProvideHttpClient()
captchaService := captcha.NewCaptchaService(c, httpClient)
- userSerice := service.NewUserSerice(c, client, captchaService, authService, cache)
+ emailService, err := email.NewEmail(privateKey, c, cache)
+ if err != nil {
+ cleanup2()
+ cleanup()
+ return nil, nil, err
+ }
+ userService := service.NewUserSerice(c, client, captchaService, authService, cache, emailService)
textureService := service.NewTextureService(client, c, cache)
- userHandel := handle.NewUserHandel(handleError, validate, userSerice, logger, textureService)
+ userHandel := handle.NewUserHandel(handleError, validate, userService, logger, textureService)
adminService := service.NewAdminService(authService, client, c, cache)
adminHandel := handle.NewAdminHandel(handleError, adminService, validate)
httpHandler := route.NewRoute(yggdrasil3, handel, c, handler, userHandel, adminHandel)
diff --git a/service/email/email.go b/service/email/email.go
index 1fb2e27..10d600d 100644
--- a/service/email/email.go
+++ b/service/email/email.go
@@ -52,16 +52,23 @@ func NewEmail(pri *rsa.PrivateKey, c config.Config, cache cache.Cache) (*EmailSe
}, nil
}
-func (e EmailService) getRandEmailUser() EmailConfig {
+func (e EmailService) getRandEmailUser() (EmailConfig, error) {
+ if len(e.emailConfig) == 0 {
+ return EmailConfig{}, fmt.Errorf("没有可用的邮箱账号")
+ }
+
i := rand.Intn(len(e.emailConfig))
- return e.emailConfig[i]
+ return e.emailConfig[i], nil
}
func (e EmailService) SendEmail(ctx context.Context, to string, subject, body string) error {
- u := e.getRandEmailUser()
+ u, err := e.getRandEmailUser()
+ if err != nil {
+ return fmt.Errorf("SendRegVerify: %w", err)
+ }
m := mail.NewMsg()
- err := m.From(u.Name)
+ err = m.From(u.Name)
if err != nil {
return fmt.Errorf("SendRegVerify: %w", err)
}
@@ -114,7 +121,7 @@ func (e EmailService) SendVerifyUrl(ctx context.Context, email string, interval
u := url.URL{
Host: host,
Scheme: "http",
- Path: "/test?" + code,
+ Path: "/register?code=" + url.QueryEscape(code),
}
if e.config.WebBaseUrl != "" {
diff --git a/service/user.go b/service/user.go
index 423721b..8122787 100644
--- a/service/user.go
+++ b/service/user.go
@@ -258,7 +258,7 @@ func (w *UserService) SendRegEmail(ctx context.Context, email, CaptchaToken, hos
err = w.emailService.SendVerifyUrl(ctx, email, 60, host)
if err != nil {
- return fmt.Errorf("SendRegEmail: %w", ErrNotAllowDomain)
+ return fmt.Errorf("SendRegEmail: %w", err)
}
return nil
}
diff --git a/service/web.go b/service/web.go
index 2b08f4b..cfdaf29 100644
--- a/service/web.go
+++ b/service/web.go
@@ -35,5 +35,7 @@ func (w *WebService) GetConfig(ctx context.Context) model.Config {
},
ServerName: w.config.ServerName,
AllowChangeName: !w.config.OfflineUUID,
+ NeedEmail: w.config.Email.Enable,
+ AllowDomain: w.config.Email.AllowDomain,
}
}