Skip to content

Commit

Permalink
feat: support unforced smtp registration (#42)
Browse files Browse the repository at this point in the history
  • Loading branch information
zmh-program committed Jan 25, 2024
1 parent 0bbc2d9 commit 06ccf4e
Show file tree
Hide file tree
Showing 17 changed files with 134 additions and 17 deletions.
7 changes: 7 additions & 0 deletions app/src/admin/api/info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
setBuyLink,
setDocsUrl,
} from "@/conf/env.ts";
import { infoEvent } from "@/events/info.ts";

export type SiteInfo = {
title: string;
Expand All @@ -15,6 +16,7 @@ export type SiteInfo = {
file: string;
announcement: string;
buy_link: string;
mail: boolean;
};

export async function getSiteInfo(): Promise<SiteInfo> {
Expand All @@ -30,6 +32,7 @@ export async function getSiteInfo(): Promise<SiteInfo> {
file: "",
announcement: "",
buy_link: "",
mail: false,
};
}
}
Expand All @@ -42,5 +45,9 @@ export function syncSiteInfo() {
setBlobEndpoint(info.file);
setAnnouncement(info.announcement);
setBuyLink(info.buy_link);

infoEvent.emit({
mail: info.mail,
});
});
}
4 changes: 4 additions & 0 deletions app/src/assets/ui.less
Original file line number Diff line number Diff line change
Expand Up @@ -320,3 +320,7 @@ input[type="number"] {
transform: translateY(-1px);
}
}

.text-secondary {
color: hsl(var(--text-secondary)) !important;
}
16 changes: 14 additions & 2 deletions app/src/components/Paragraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,21 @@ function ParagraphItem({
);
}

export function ParagraphDescription({ children }: { children: string }) {
type ParagraphDescriptionProps = {
children: string;
border?: boolean;
};
export function ParagraphDescription({
children,
border,
}: ParagraphDescriptionProps) {
return (
<div className={`paragraph-description`}>
<div
className={cn(
"paragraph-description",
border && `px-4 py-4 border rounded-lg`,
)}
>
<Info size={16} />
<Markdown children={children} />
</div>
Expand Down
7 changes: 7 additions & 0 deletions app/src/components/app/AppProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,17 @@ import { Model } from "@/api/types.ts";
import { ChargeProps, nonBilling } from "@/admin/charge.ts";
import { dispatchSubscriptionData } from "@/store/globals.ts";
import { marketEvent } from "@/events/market.ts";
import { useEffect } from "react";
import { infoEvent } from "@/events/info.ts";
import { setForm } from "@/store/info.ts";

function AppProvider() {
const dispatch = useDispatch();

useEffect(() => {
infoEvent.bind((data) => dispatch(setForm(data)));
}, []);

useEffectAsync(async () => {
marketEvent.emit(false);

Expand Down
5 changes: 4 additions & 1 deletion app/src/resources/i18n/cn.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"username-or-email-placeholder": "请输入用户名或邮箱",
"code": "验证码",
"code-placeholder": "请输入验证码",
"code-disabled-placeholder": "无需进行邮箱验证",
"send-code": "发送",
"incorrect-info": "填错信息?",
"fall-back": "回退一步",
Expand All @@ -79,7 +80,8 @@
"send-code-failed": "发送失败",
"send-code-failed-prompt": "验证码发送失败,原因:{{reason}}",
"register-success": "注册成功",
"register-success-prompt": "您已成功注册,欢迎你的到来!"
"register-success-prompt": "您已成功注册,欢迎你的到来!",
"disabled-mail": "当前站点的邮箱已被禁用,请联系管理员开启发件功能。"
},
"tag": {
"free": "免费",
Expand Down Expand Up @@ -588,6 +590,7 @@
"mailPass": "密码",
"mailFrom": "发件人",
"mailEnableWhitelist": "启用域名后缀白名单",
"mailConfNotValid": "SMTP 发件参数未正确配置,已禁用邮箱验证",
"mailWhitelist": "域名后缀白名单",
"mailWhitelistSelected": "已选 {{length}} 个域名邮箱",
"mailWhitelistSearchPlaceholder": "搜索域名后缀",
Expand Down
7 changes: 5 additions & 2 deletions app/src/resources/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,8 @@
"mailWhitelistSearchPlaceholder": "Search Domain Suffixes",
"customWhitelistPlaceholder": "Please enter a list of custom domain suffixes (which will appear in the list of options to choose from), separated by commas, e.g.: example.com, example.net",
"buyLink": "Buy Link",
"buyLinkPlaceholder": "Please enter the card secret purchase link, leave blank to not show the purchase button"
"buyLinkPlaceholder": "Please enter the card secret purchase link, leave blank to not show the purchase button",
"mailConfNotValid": "SMTP send parameters are not configured correctly, mailbox verification is disabled"
},
"user": "User Management",
"invitation-code": "Invitation Code",
Expand Down Expand Up @@ -601,7 +602,9 @@
"send-code-failed": "Send failed",
"send-code-failed-prompt": "Failed to send verification code, reason: {{reason}}",
"register-success": "Account created !",
"register-success-prompt": "You have successfully registered, welcome!"
"register-success-prompt": "You have successfully registered, welcome!",
"disabled-mail": "The mailbox of the current site has been disabled, please contact the administrator to enable the mailing function.",
"code-disabled-placeholder": "No email verification required"
},
"reset": "Reset",
"request-error": "Request failed for {{reason}}",
Expand Down
7 changes: 5 additions & 2 deletions app/src/resources/i18n/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,8 @@
"mailWhitelistSearchPlaceholder": "ドメイン接尾辞を検索",
"customWhitelistPlaceholder": "カスタムドメインサフィックスのリスト(選択するオプションのリストに表示されます)をカンマで区切って入力してください。例: example.com、example.net",
"buyLink": "購入リンク",
"buyLinkPlaceholder": "カードシークレット購入リンクを入力してください。購入ボタンを表示しない場合は空白のままにしてください"
"buyLinkPlaceholder": "カードシークレット購入リンクを入力してください。購入ボタンを表示しない場合は空白のままにしてください",
"mailConfNotValid": "SMTP送信パラメータが正しく設定されていません。メールボックスの検証が無効になっています"
},
"user": "ユーザー管理",
"invitation-code": "招待コード",
Expand Down Expand Up @@ -601,7 +602,9 @@
"send-code-failed": "送信失敗",
"send-code-failed-prompt": "認証コードの送信に失敗しました。理由:{{ reason}}",
"register-success": "登録に成功しました",
"register-success-prompt": "登録が完了しました。ようこそ!"
"register-success-prompt": "登録が完了しました。ようこそ!",
"disabled-mail": "現在のサイトのメールボックスは無効になっています。管理者に連絡して郵送機能を有効にしてください。",
"code-disabled-placeholder": "メールアドレスの認証は必要ありません"
},
"reset": "リセット",
"request-error": "{{reason}}のためにリクエストできませんでした",
Expand Down
7 changes: 5 additions & 2 deletions app/src/resources/i18n/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,8 @@
"mailWhitelistSearchPlaceholder": "Поиск суффиксов доменов",
"customWhitelistPlaceholder": "Введите список пользовательских суффиксов домена (которые появятся в списке опций на выбор), разделенных запятыми, например: example.com, example.net",
"buyLink": "Ссылка на покупку",
"buyLinkPlaceholder": "Введите ссылку на секретную покупку карты, оставьте поле пустым, чтобы не показывать кнопку покупки"
"buyLinkPlaceholder": "Введите ссылку на секретную покупку карты, оставьте поле пустым, чтобы не показывать кнопку покупки",
"mailConfNotValid": "Параметры отправки SMTP настроены неправильно, проверка почтового ящика отключена"
},
"user": "Управление пользователями",
"invitation-code": "Код приглашения",
Expand Down Expand Up @@ -601,7 +602,9 @@
"send-code-failed": "Не удалось отправить",
"send-code-failed-prompt": "Не удалось отправить код подтверждения, причина: {{reason}}",
"register-success": "Регистрация прошла успешно",
"register-success-prompt": "Вы успешно зарегистрировались, добро пожаловать!"
"register-success-prompt": "Вы успешно зарегистрировались, добро пожаловать!",
"disabled-mail": "Почтовый ящик текущего сайта отключен. Чтобы включить функцию рассылки, обратитесь к администратору.",
"code-disabled-placeholder": "Подтверждение адреса электронной почты не требуется"
},
"reset": "сброс",
"request-error": "Запрос не выполнен по {{reason}}",
Expand Down
20 changes: 19 additions & 1 deletion app/src/routes/Forgot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,20 @@ import Require, {
LengthRangeRequired,
SameRequired,
} from "@/components/Require.tsx";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { Input } from "@/components/ui/input.tsx";
import { Button } from "@/components/ui/button.tsx";
import TickButton from "@/components/TickButton.tsx";
import { appLogo } from "@/conf/env.ts";
import { useSelector } from "react-redux";
import { infoMailSelector } from "@/store/info.ts";
import { AlertCircle } from "lucide-react";

function Forgot() {
const { t } = useTranslation();
const { toast } = useToast();
const enabled = useSelector(infoMailSelector);

const [form, dispatch] = useReducer(formReducer<ResetForm>(), {
email: "",
code: "",
Expand Down Expand Up @@ -63,6 +69,12 @@ function Forgot() {
<Card className={`auth-card`}>
<CardContent className={`pb-0`}>
<div className={`auth-wrapper`}>
{!enabled && (
<Alert className={`p-4`}>
<AlertCircle className={`h-4 w-4`} />
<AlertDescription>{t("auth.disabled-mail")}</AlertDescription>
</Alert>
)}
<Label>
<Require />
{t("auth.email")}
Expand Down Expand Up @@ -99,6 +111,7 @@ function Forgot() {
loading={true}
onClick={onVerify}
tick={60}
disabled={!enabled}
>
{t("auth.send-code")}
</TickButton>
Expand Down Expand Up @@ -144,7 +157,12 @@ function Forgot() {
}
/>

<Button onClick={onSubmit} className={`mt-2`} loading={true}>
<Button
disabled={!enabled}
onClick={onSubmit}
className={`mt-2`}
loading={true}
>
{t("reset")}
</Button>
</div>
Expand Down
16 changes: 13 additions & 3 deletions app/src/routes/Register.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ import { doRegister, RegisterForm, sendCode } from "@/api/auth.ts";
import { useToast } from "@/components/ui/use-toast.ts";
import TickButton from "@/components/TickButton.tsx";
import { validateToken } from "@/store/auth.ts";
import { useDispatch } from "react-redux";
import { useDispatch, useSelector } from "react-redux";
import { appLogo, appName } from "@/conf/env.ts";
import { infoMailSelector } from "@/store/info.ts";

type CompProps = {
form: RegisterForm;
Expand Down Expand Up @@ -128,10 +129,13 @@ function Verify({ form, dispatch, setNext }: CompProps) {
const { toast } = useToast();
const globalDispatch = useDispatch();

const mail = useSelector(infoMailSelector);

const onSubmit = async () => {
const data = doFormat(form);

if (!isEmailValid(data.email) || !data.code.trim().length) return;
if (!isEmailValid(data.email)) return;
if (mail && data.code.trim().length === 0) return;

const resp = await doRegister(data);
if (!resp.status) {
Expand Down Expand Up @@ -177,7 +181,12 @@ function Verify({ form, dispatch, setNext }: CompProps) {

<div className={`flex flex-row`}>
<Input
placeholder={t("auth.code-placeholder")}
disabled={!mail}
placeholder={
mail
? t("auth.code-placeholder")
: t("auth.code-disabled-placeholder")
}
value={form.code}
onChange={(e) =>
dispatch({
Expand All @@ -191,6 +200,7 @@ function Verify({ form, dispatch, setNext }: CompProps) {
loading={true}
onClick={onVerify}
tick={60}
disabled={!mail}
>
{t("auth.send-code")}
</TickButton>
Expand Down
18 changes: 18 additions & 0 deletions app/src/routes/admin/System.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,19 @@ function Mail({ data, dispatch, onChange }: CompProps<MailState>) {

const [mailDialog, setMailDialog] = useState<boolean>(false);

const valid = useMemo((): boolean => {
return (
data.host.length > 0 &&
data.port > 0 &&
data.port < 65535 &&
data.username.length > 0 &&
data.password.length > 0 &&
data.from.length > 0 &&
/\w+@\w+\.\w+/.test(data.from) &&
!data.username.includes("@")
);
}, [data]);

const onTest = async () => {
if (!email.trim()) return;
await onChange(false);
Expand All @@ -262,6 +275,11 @@ function Mail({ data, dispatch, onChange }: CompProps<MailState>) {
configParagraph={true}
isCollapsed={true}
>
{!valid && (
<ParagraphDescription border={true}>
{t("admin.system.mailConfNotValid")}
</ParagraphDescription>
)}
<ParagraphItem>
<Label>
<Require /> {t("admin.system.mailHost")}
Expand Down
2 changes: 2 additions & 0 deletions app/src/store/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { configureStore } from "@reduxjs/toolkit";
import infoReducer from "./info";
import globalReducer from "./globals";
import menuReducer from "./menu";
import authReducer from "./auth";
Expand All @@ -13,6 +14,7 @@ import settingsReducer from "./settings";

const store = configureStore({
reducer: {
info: infoReducer,
global: globalReducer,
menu: menuReducer,
auth: authReducer,
Expand Down
23 changes: 23 additions & 0 deletions app/src/store/info.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { createSlice } from "@reduxjs/toolkit";
import { InfoForm } from "@/events/info.ts";
import { RootState } from "@/store/index.ts";

export const infoSlice = createSlice({
name: "info",
initialState: {
mail: false,
} as InfoForm,
reducers: {
setForm: (state, action) => {
const form = action.payload as InfoForm;
state.mail = form.mail ?? false;
},
},
});

export const { setForm } = infoSlice.actions;

export default infoSlice.reducer;

export const infoDataSelector = (state: RootState): InfoForm => state.info;
export const infoMailSelector = (state: RootState): boolean => state.info.mail;
6 changes: 4 additions & 2 deletions auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,13 @@ func SignUp(c *gin.Context, form RegisterForm) (string, error) {
email := strings.TrimSpace(form.Email)
code := strings.TrimSpace(form.Code)

enableVerify := channel.SystemInstance.IsMailValid()

if !utils.All(
validateUsername(username),
validatePassword(password),
validateEmail(email),
validateCode(code),
!enableVerify || validateCode(code),
) {
return "", errors.New("invalid username/password/email format")
}
Expand All @@ -127,7 +129,7 @@ func SignUp(c *gin.Context, form RegisterForm) (string, error) {
return "", fmt.Errorf("email is already taken, please try another one email (your current email: %s)", email)
}

if !checkCode(c, cache, email, code) {
if enableVerify && !checkCode(c, cache, email, code) {
return "", errors.New("invalid email verification code")
}

Expand Down
2 changes: 1 addition & 1 deletion auth/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type RegisterForm struct {
Username string `form:"username" binding:"required"`
Password string `form:"password" binding:"required"`
Email string `form:"email" binding:"required"`
Code string `form:"code" binding:"required"`
Code string `form:"code"`
}

type VerifyForm struct {
Expand Down
2 changes: 2 additions & 0 deletions channel/system.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type ApiInfo struct {
Docs string `json:"docs"`
Announcement string `json:"announcement"`
BuyLink string `json:"buy_link"`
Mail bool `json:"mail"`
}

type generalState struct {
Expand Down Expand Up @@ -87,6 +88,7 @@ func (c *SystemConfig) AsInfo() ApiInfo {
Docs: c.General.Docs,
Announcement: c.Site.Announcement,
BuyLink: c.Site.BuyLink,
Mail: c.IsMailValid(),
}
}

Expand Down
Loading

0 comments on commit 06ccf4e

Please sign in to comment.