chore: improve i18n

This commit is contained in:
Fu Diwei
2024-10-14 21:00:50 +08:00
parent 9bd279a8a0
commit e397793153
69 changed files with 1866 additions and 1509 deletions

1
ui/dist/assets/index-CV_7sKTK.css vendored Normal file
View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

324
ui/dist/assets/index-Dm8Q4Rp7.js vendored Normal file
View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

28
ui/dist/index.html vendored
View File

@@ -1,14 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Certimate - Your Trusted SSL Automation Partner</title>
<script type="module" crossorigin src="/assets/index-DvxNVikK.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CWUb5Xuf.css">
</head>
<body class="bg-background">
<div id="root"></div>
</body>
</html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Certimate - Your Trusted SSL Automation Partner</title>
<script type="module" crossorigin src="/assets/index-Dm8Q4Rp7.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CV_7sKTK.css">
</head>
<body class="bg-background">
<div id="root"></div>
</body>

View File

@@ -11,7 +11,7 @@ import {
} from "@/components/ui/dropdown-menu";
export default function LocaleToggle() {
const { i18n } = useTranslation()
const { i18n } = useTranslation();
return (
<DropdownMenu>
@@ -22,7 +22,7 @@ export default function LocaleToggle() {
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
{Object.keys(i18n.store.data).map(key => (
{Object.keys(i18n.store.data).map((key) => (
<DropdownMenuItem onClick={() => i18n.changeLanguage(key)}>
{i18n.store.data[key].name as string}
</DropdownMenuItem>

View File

@@ -1,5 +1,5 @@
import { Moon, Sun } from "lucide-react";
import { useTranslation } from 'react-i18next'
import { useTranslation } from "react-i18next";
import { Button } from "@/components/ui/button";
import {
@@ -14,7 +14,6 @@ export function ThemeToggle() {
const { setTheme } = useTheme();
const { t } = useTranslation();
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
@@ -26,13 +25,13 @@ export function ThemeToggle() {
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => setTheme("light")}>
{t('theme.light')}
{t("common.theme.light")}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("dark")}>
{t('theme.dark')}
{t("common.theme.dark")}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("system")}>
{t('theme.system')}
{t("common.theme.system")}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>

View File

@@ -15,7 +15,12 @@ import {
} from "@/components/ui/form";
import { Button } from "@/components/ui/button";
import { Access, accessFormType, AliyunConfig, getUsageByConfigType } from "@/domain/access";
import {
Access,
accessFormType,
AliyunConfig,
getUsageByConfigType,
} from "@/domain/access";
import { save } from "@/repository/access";
import { useConfig } from "@/providers/config";
@@ -35,10 +40,19 @@ const AccessAliyunForm = ({
const { t } = useTranslation();
const formSchema = z.object({
id: z.string().optional(),
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
name: z
.string()
.min(1, "access.authorization.form.name.placeholder")
.max(64, t("common.errmsg.string_max", { max: 64 })),
configType: accessFormType,
accessKeyId: z.string().min(1, 'access.form.access.key.id.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
accessSecretId: z.string().min(1, 'access.form.access.key.secret.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
accessKeyId: z
.string()
.min(1, "access.authorization.form.access_key_id.placeholder")
.max(64, t("common.errmsg.string_max", { max: 64 })),
accessSecretId: z
.string()
.min(1, "access.authorization.form.access_key_secret..placeholder")
.max(64, t("common.errmsg.string_max", { max: 64 })),
});
let config: AliyunConfig = {
@@ -51,7 +65,7 @@ const AccessAliyunForm = ({
resolver: zodResolver(formSchema),
defaultValues: {
id: data?.id,
name: data?.name || '',
name: data?.name || "",
configType: "aliyun",
accessKeyId: config.accessKeyId,
accessSecretId: config.accessKeySecret,
@@ -71,7 +85,7 @@ const AccessAliyunForm = ({
};
try {
req.id = op == "copy" ? "" : req.id;
req.id = op == "copy" ? "" : req.id;
const rs = await save(req);
onAfterReq();
@@ -117,9 +131,12 @@ const AccessAliyunForm = ({
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>{t('name')}</FormLabel>
<FormLabel>{t("access.authorization.form.name.label")}</FormLabel>
<FormControl>
<Input placeholder={t('access.form.name.not.empty')} {...field} />
<Input
placeholder={t("access.authorization.form.name.placeholder")}
{...field}
/>
</FormControl>
<FormMessage />
@@ -132,7 +149,7 @@ const AccessAliyunForm = ({
name="id"
render={({ field }) => (
<FormItem className="hidden">
<FormLabel>{t('access.form.config.field')}</FormLabel>
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
@@ -147,7 +164,7 @@ const AccessAliyunForm = ({
name="configType"
render={({ field }) => (
<FormItem className="hidden">
<FormLabel>{t('access.form.config.field')}</FormLabel>
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
@@ -162,9 +179,12 @@ const AccessAliyunForm = ({
name="accessKeyId"
render={({ field }) => (
<FormItem>
<FormLabel>{t('access.form.access.key.id')}</FormLabel>
<FormLabel>{t("access.authorization.form.access_key_id.label")}</FormLabel>
<FormControl>
<Input placeholder={t('access.form.access.key.id.not.empty')} {...field} />
<Input
placeholder={t("access.authorization.form.access_key_id.placeholder")}
{...field}
/>
</FormControl>
<FormMessage />
@@ -177,9 +197,12 @@ const AccessAliyunForm = ({
name="accessSecretId"
render={({ field }) => (
<FormItem>
<FormLabel>{t('access.form.access.key.secret')}</FormLabel>
<FormLabel>{t("access.authorization.form.access_key_secret.label")}</FormLabel>
<FormControl>
<Input placeholder={t('access.form.access.key.secret.not.empty')} {...field} />
<Input
placeholder={t("access.authorization.form.access_key_secret..placeholder")}
{...field}
/>
</FormControl>
<FormMessage />
@@ -190,7 +213,7 @@ const AccessAliyunForm = ({
<FormMessage />
<div className="flex justify-end">
<Button type="submit">{t('save')}</Button>
<Button type="submit">{t("common.save")}</Button>
</div>
</form>
</Form>

View File

@@ -15,7 +15,12 @@ import {
} from "@/components/ui/form";
import { Button } from "@/components/ui/button";
import { Access, accessFormType, CloudflareConfig, getUsageByConfigType } from "@/domain/access";
import {
Access,
accessFormType,
CloudflareConfig,
getUsageByConfigType,
} from "@/domain/access";
import { save } from "@/repository/access";
import { useConfig } from "@/providers/config";
import { ClientResponseError } from "pocketbase";
@@ -34,9 +39,15 @@ const AccessCloudflareForm = ({
const { t } = useTranslation();
const formSchema = z.object({
id: z.string().optional(),
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
name: z
.string()
.min(1, "access.authorization.form.name.placeholder")
.max(64, t("common.errmsg.string_max", { max: 64 })),
configType: accessFormType,
dnsApiToken: z.string().min(1, 'access.form.cloud.dns.api.token.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
dnsApiToken: z
.string()
.min(1, "access.authorization.form.cloud_dns_api_token.placeholder")
.max(64, t("common.errmsg.string_max", { max: 64 })),
});
let config: CloudflareConfig = {
@@ -48,7 +59,7 @@ const AccessCloudflareForm = ({
resolver: zodResolver(formSchema),
defaultValues: {
id: data?.id,
name: data?.name || '',
name: data?.name || "",
configType: "cloudflare",
dnsApiToken: config.dnsApiToken,
},
@@ -67,7 +78,7 @@ const AccessCloudflareForm = ({
};
try {
req.id = op == "copy" ? "" : req.id;
req.id = op == "copy" ? "" : req.id;
const rs = await save(req);
onAfterReq();
@@ -111,9 +122,12 @@ const AccessCloudflareForm = ({
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>{t('name')}</FormLabel>
<FormLabel>{t("access.authorization.form.name.label")}</FormLabel>
<FormControl>
<Input placeholder={t('access.form.name.not.empty')} {...field} />
<Input
placeholder={t("access.authorization.form.name.placeholder")}
{...field}
/>
</FormControl>
<FormMessage />
@@ -126,7 +140,7 @@ const AccessCloudflareForm = ({
name="id"
render={({ field }) => (
<FormItem className="hidden">
<FormLabel>{t('access.form.config.field')}</FormLabel>
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
@@ -141,7 +155,7 @@ const AccessCloudflareForm = ({
name="configType"
render={({ field }) => (
<FormItem className="hidden">
<FormLabel>{t('access.form.config.field')}</FormLabel>
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
@@ -156,9 +170,14 @@ const AccessCloudflareForm = ({
name="dnsApiToken"
render={({ field }) => (
<FormItem>
<FormLabel>{t('access.form.cloud.dns.api.token')}</FormLabel>
<FormLabel>{t("access.authorization.form.cloud_dns_api_token.label")}</FormLabel>
<FormControl>
<Input placeholder={t('access.form.cloud.dns.api.token.not.empty')} {...field} />
<Input
placeholder={t(
"access.authorization.form.cloud_dns_api_token.placeholder"
)}
{...field}
/>
</FormControl>
<FormMessage />
@@ -167,7 +186,7 @@ const AccessCloudflareForm = ({
/>
<div className="flex justify-end">
<Button type="submit">{t('save')}</Button>
<Button type="submit">{t("common.save")}</Button>
</div>
</form>
</Form>

View File

@@ -180,15 +180,15 @@ export function AccessEdit({
<DialogHeader>
<DialogTitle>
{op == "add"
? t("access.add")
? t("access.authorization.add")
: op == "edit"
? t("access.edit")
: t("access.copy")}
? t("access.authorization.edit")
: t("access.authorization.copy")}
</DialogTitle>
</DialogHeader>
<ScrollArea className="max-h-[80vh]">
<div className="container py-3">
<Label>{t("access.type")}</Label>
<Label>{t("access.authorization.form.type.label")}</Label>
<Select
onValueChange={(val) => {
@@ -197,11 +197,11 @@ export function AccessEdit({
defaultValue={configType}
>
<SelectTrigger className="mt-3">
<SelectValue placeholder={t("access.type.not.empty")} />
<SelectValue placeholder={t("access.authorization.form.type.placeholder")} />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>{t("access.type")}</SelectLabel>
<SelectLabel>{t("access.authorization.form.type.list")}</SelectLabel>
{typeKeys.map((key) => (
<SelectItem value={key} key={key}>
<div

View File

@@ -39,10 +39,19 @@ const AccessGodaddyFrom = ({
const { t } = useTranslation();
const formSchema = z.object({
id: z.string().optional(),
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
name: z
.string()
.min(1, "access.authorization.form.name.placeholder")
.max(64, t("common.errmsg.string_max", { max: 64 })),
configType: accessFormType,
apiKey: z.string().min(1, 'access.form.go.daddy.api.key.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
apiSecret: z.string().min(1, 'access.form.go.daddy.api.secret.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
apiKey: z
.string()
.min(1, "access.authorization.form.godaddy_api_key.placeholder")
.max(64, t("common.errmsg.string_max", { max: 64 })),
apiSecret: z
.string()
.min(1, "access.authorization.form.godaddy_api_secret.placeholder")
.max(64, t("common.errmsg.string_max", { max: 64 })),
});
let config: GodaddyConfig = {
@@ -55,7 +64,7 @@ const AccessGodaddyFrom = ({
resolver: zodResolver(formSchema),
defaultValues: {
id: data?.id,
name: data?.name || '',
name: data?.name || "",
configType: "godaddy",
apiKey: config.apiKey,
apiSecret: config.apiSecret,
@@ -76,7 +85,7 @@ const AccessGodaddyFrom = ({
};
try {
req.id = op == "copy" ? "" : req.id;
req.id = op == "copy" ? "" : req.id;
const rs = await save(req);
onAfterReq();
@@ -120,9 +129,12 @@ const AccessGodaddyFrom = ({
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>{t('name')}</FormLabel>
<FormLabel>{t("access.authorization.form.name.label")}</FormLabel>
<FormControl>
<Input placeholder={t('access.form.name.not.empty')} {...field} />
<Input
placeholder={t("access.authorization.form.name.placeholder")}
{...field}
/>
</FormControl>
<FormMessage />
@@ -135,7 +147,7 @@ const AccessGodaddyFrom = ({
name="id"
render={({ field }) => (
<FormItem className="hidden">
<FormLabel>{t('access.form.config.field')}</FormLabel>
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
@@ -150,7 +162,7 @@ const AccessGodaddyFrom = ({
name="configType"
render={({ field }) => (
<FormItem className="hidden">
<FormLabel>{t('access.form.config.field')}</FormLabel>
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
@@ -165,9 +177,12 @@ const AccessGodaddyFrom = ({
name="apiKey"
render={({ field }) => (
<FormItem>
<FormLabel>{t('access.form.go.daddy.api.key')}</FormLabel>
<FormLabel>{t("access.authorization.form.godaddy_api_key.label")}</FormLabel>
<FormControl>
<Input placeholder={t('access.form.go.daddy.api.key.not.empty')} {...field} />
<Input
placeholder={t("access.authorization.form.godaddy_api_key.placeholder")}
{...field}
/>
</FormControl>
<FormMessage />
@@ -180,9 +195,14 @@ const AccessGodaddyFrom = ({
name="apiSecret"
render={({ field }) => (
<FormItem>
<FormLabel>{t('access.form.go.daddy.api.secret')}</FormLabel>
<FormLabel>{t("access.authorization.form.godaddy_api_secret.label")}</FormLabel>
<FormControl>
<Input placeholder={t('access.form.go.daddy.api.secret.not.empty')} {...field} />
<Input
placeholder={t(
"access.authorization.form.godaddy_api_secret.placeholder"
)}
{...field}
/>
</FormControl>
<FormMessage />
@@ -191,7 +211,7 @@ const AccessGodaddyFrom = ({
/>
<div className="flex justify-end">
<Button type="submit">{t('save')}</Button>
<Button type="submit">{t("common.save")}</Button>
</div>
</form>
</Form>

View File

@@ -39,7 +39,10 @@ const AccessGroupEdit = ({ className, trigger }: AccessGroupEditProps) => {
const { t } = useTranslation();
const formSchema = z.object({
name: z.string().min(1, 'access.group.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
name: z
.string()
.min(1, "access.group.form.name.errmsg.empty")
.max(64, t("common.errmsg.string_max", { max: 64 })),
});
const form = useForm<z.infer<typeof formSchema>>({
@@ -80,7 +83,7 @@ const AccessGroupEdit = ({ className, trigger }: AccessGroupEditProps) => {
</DialogTrigger>
<DialogContent className="sm:max-w-[600px] w-full dark:text-stone-200">
<DialogHeader>
<DialogTitle>{t('access.group.add')}</DialogTitle>
<DialogTitle>{t("access.group.add")}</DialogTitle>
</DialogHeader>
<div className="container py-3">
@@ -97,9 +100,13 @@ const AccessGroupEdit = ({ className, trigger }: AccessGroupEditProps) => {
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>{t('access.group.name')}</FormLabel>
<FormLabel>{t("access.group.form.name.label")}</FormLabel>
<FormControl>
<Input placeholder={t('access.group.name.not.empty')} {...field} type="text" />
<Input
placeholder={t("access.group.form.name.errmsg.empty")}
{...field}
type="text"
/>
</FormControl>
<FormMessage />
@@ -108,7 +115,7 @@ const AccessGroupEdit = ({ className, trigger }: AccessGroupEditProps) => {
/>
<div className="flex justify-end">
<Button type="submit">{t('save')}</Button>
<Button type="submit">{t("common.save")}</Button>
</div>
</form>
</Form>

View File

@@ -48,7 +48,7 @@ const AccessGroupList = () => {
reloadAccessGroups();
} catch (e) {
toast({
title: t('delete.failed'),
title: t("common.delete.failed.message"),
description: getErrMessage(e),
variant: "destructive",
});
@@ -69,10 +69,10 @@ const AccessGroupList = () => {
</span>
<div className="text-center text-sm text-muted-foreground mt-3">
{t('access.group.domain.empty')}
{t("access.group.domains.nodata")}
</div>
<AccessGroupEdit
trigger={<Button>{t('access.group.add')}</Button>}
trigger={<Button>{t("access.group.add")}</Button>}
className="mt-3"
/>
</div>
@@ -86,7 +86,11 @@ const AccessGroupList = () => {
<CardHeader>
<CardTitle>{accessGroup.name}</CardTitle>
<CardDescription>
{t('access.group.total', { total: accessGroup.expand ? accessGroup.expand.access.length : 0 })}
{t("access.group.total", {
total: accessGroup.expand
? accessGroup.expand.access.length
: 0,
})}
</CardDescription>
</CardHeader>
<CardContent className="min-h-[180px]">
@@ -120,9 +124,7 @@ const AccessGroupList = () => {
<div>
<Group size={40} />
</div>
<div className="ml-2">
{t('access.group.empty')}
</div>
<div className="ml-2">{t("access.group.nodata")}</div>
</div>
</>
)}
@@ -149,7 +151,7 @@ const AccessGroupList = () => {
);
}}
>
{t('access.all')}
{t("access.group.domains")}
</Button>
</div>
</Show>
@@ -157,14 +159,14 @@ const AccessGroupList = () => {
<Show
when={
!accessGroup.expand ||
accessGroup.expand.access.length == 0
accessGroup.expand.access.length == 0
? true
: false
}
>
<div>
<Button size="sm" onClick={handleAddAccess}>
{t('access.add')}
{t("access.authorization.add")}
</Button>
</div>
</Show>
@@ -173,21 +175,21 @@ const AccessGroupList = () => {
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant={"destructive"} size={"sm"}>
{t('delete')}
{t("common.delete")}
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle className="dark:text-gray-200">
{t('access.group.delete')}
{t("access.group.delete")}
</AlertDialogTitle>
<AlertDialogDescription>
{t('access.group.delete.confirm')}
{t("access.group.delete.confirm")}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel className="dark:text-gray-200">
{t('cancel')}
{t("common.cancel")}
</AlertDialogCancel>
<AlertDialogAction
onClick={() => {
@@ -196,7 +198,7 @@ const AccessGroupList = () => {
);
}}
>
{t('confirm')}
{t("common.confirm")}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>

View File

@@ -15,7 +15,12 @@ import {
} from "@/components/ui/form";
import { Button } from "@/components/ui/button";
import { Access, accessFormType, HuaweicloudConfig, getUsageByConfigType } from "@/domain/access";
import {
Access,
accessFormType,
HuaweicloudConfig,
getUsageByConfigType,
} from "@/domain/access";
import { save } from "@/repository/access";
import { useConfig } from "@/providers/config";
@@ -35,11 +40,23 @@ const AccessHuaweicloudForm = ({
const { t } = useTranslation();
const formSchema = z.object({
id: z.string().optional(),
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
name: z
.string()
.min(1, "access.authorization.form.name.placeholder")
.max(64, t("common.errmsg.string_max", { max: 64 })),
configType: accessFormType,
region: z.string().min(1, 'access.form.region.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
accessKeyId: z.string().min(1, 'access.form.access.key.id.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
secretAccessKey: z.string().min(1, 'access.form.access.key.secret.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
region: z
.string()
.min(1, "access.authorization.form.region.placeholder")
.max(64, t("common.errmsg.string_max", { max: 64 })),
accessKeyId: z
.string()
.min(1, "access.authorization.form.access_key_id.placeholder")
.max(64, t("common.errmsg.string_max", { max: 64 })),
secretAccessKey: z
.string()
.min(1, "access.authorization.form.access_key_secret..placeholder")
.max(64, t("common.errmsg.string_max", { max: 64 })),
});
let config: HuaweicloudConfig = {
@@ -53,7 +70,7 @@ const AccessHuaweicloudForm = ({
resolver: zodResolver(formSchema),
defaultValues: {
id: data?.id,
name: data?.name || '',
name: data?.name || "",
configType: "huaweicloud",
region: config.region,
accessKeyId: config.accessKeyId,
@@ -75,7 +92,7 @@ const AccessHuaweicloudForm = ({
};
try {
req.id = op == "copy" ? "" : req.id;
req.id = op == "copy" ? "" : req.id;
const rs = await save(req);
onAfterReq();
@@ -121,9 +138,12 @@ const AccessHuaweicloudForm = ({
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>{t('name')}</FormLabel>
<FormLabel>{t("access.authorization.form.name.label")}</FormLabel>
<FormControl>
<Input placeholder={t('access.form.name.not.empty')} {...field} />
<Input
placeholder={t("access.authorization.form.name.placeholder")}
{...field}
/>
</FormControl>
<FormMessage />
@@ -136,7 +156,7 @@ const AccessHuaweicloudForm = ({
name="id"
render={({ field }) => (
<FormItem className="hidden">
<FormLabel>{t('access.form.config.field')}</FormLabel>
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
@@ -151,7 +171,7 @@ const AccessHuaweicloudForm = ({
name="configType"
render={({ field }) => (
<FormItem className="hidden">
<FormLabel>{t('access.form.config.field')}</FormLabel>
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
@@ -166,9 +186,12 @@ const AccessHuaweicloudForm = ({
name="region"
render={({ field }) => (
<FormItem>
<FormLabel>{t('access.form.region')}</FormLabel>
<FormLabel>{t("access.authorization.form.region.label")}</FormLabel>
<FormControl>
<Input placeholder={t('access.form.region.not.empty')} {...field} />
<Input
placeholder={t("access.authorization.form.region.placeholder")}
{...field}
/>
</FormControl>
<FormMessage />
@@ -181,9 +204,12 @@ const AccessHuaweicloudForm = ({
name="accessKeyId"
render={({ field }) => (
<FormItem>
<FormLabel>{t('access.form.access.key.id')}</FormLabel>
<FormLabel>{t("access.authorization.form.access_key_id.label")}</FormLabel>
<FormControl>
<Input placeholder={t('access.form.access.key.id.not.empty')} {...field} />
<Input
placeholder={t("access.authorization.form.access_key_id.placeholder")}
{...field}
/>
</FormControl>
<FormMessage />
@@ -196,9 +222,12 @@ const AccessHuaweicloudForm = ({
name="secretAccessKey"
render={({ field }) => (
<FormItem>
<FormLabel>{t('access.form.access.key.secret')}</FormLabel>
<FormLabel>{t("access.authorization.form.access_key_secret.label")}</FormLabel>
<FormControl>
<Input placeholder={t('access.form.access.key.secret.not.empty')} {...field} />
<Input
placeholder={t("access.authorization.form.access_key_secret..placeholder")}
{...field}
/>
</FormControl>
<FormMessage />
@@ -209,7 +238,7 @@ const AccessHuaweicloudForm = ({
<FormMessage />
<div className="flex justify-end">
<Button type="submit">{t('save')}</Button>
<Button type="submit">{t("common.save")}</Button>
</div>
</form>
</Form>

View File

@@ -35,8 +35,8 @@ const AccessLocalForm = ({
id: z.string().optional(),
name: z
.string()
.min(1, "access.form.name.not.empty")
.max(64, t("zod.rule.string.max", { max: 64 })),
.min(1, "access.authorization.form.name.placeholder")
.max(64, t("common.errmsg.string_max", { max: 64 })),
configType: accessFormType,
});
@@ -107,10 +107,10 @@ const AccessLocalForm = ({
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>{t("name")}</FormLabel>
<FormLabel>{t("access.authorization.form.name.label")}</FormLabel>
<FormControl>
<Input
placeholder={t("access.form.name.not.empty")}
placeholder={t("access.authorization.form.name.placeholder")}
{...field}
/>
</FormControl>
@@ -125,7 +125,7 @@ const AccessLocalForm = ({
name="id"
render={({ field }) => (
<FormItem className="hidden">
<FormLabel>{t("access.form.config.field")}</FormLabel>
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
@@ -140,7 +140,7 @@ const AccessLocalForm = ({
name="configType"
render={({ field }) => (
<FormItem className="hidden">
<FormLabel>{t("access.form.config.field")}</FormLabel>
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
@@ -153,7 +153,7 @@ const AccessLocalForm = ({
<FormMessage />
<div className="flex justify-end">
<Button type="submit">{t("save")}</Button>
<Button type="submit">{t("common.save")}</Button>
</div>
</form>
</Form>

View File

@@ -15,7 +15,12 @@ import {
} from "@/components/ui/form";
import { Button } from "@/components/ui/button";
import { Access, accessFormType, getUsageByConfigType, NamesiloConfig } from "@/domain/access";
import {
Access,
accessFormType,
getUsageByConfigType,
NamesiloConfig,
} from "@/domain/access";
import { save } from "@/repository/access";
import { useConfig } from "@/providers/config";
import { ClientResponseError } from "pocketbase";
@@ -34,9 +39,15 @@ const AccessNamesiloForm = ({
const { t } = useTranslation();
const formSchema = z.object({
id: z.string().optional(),
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
name: z
.string()
.min(1, "access.authorization.form.name.placeholder")
.max(64, t("common.errmsg.string_max", { max: 64 })),
configType: accessFormType,
apiKey: z.string().min(1, 'access.form.namesilo.api.key.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
apiKey: z
.string()
.min(1, "access.authorization.form.namesilo_api_key.placeholder")
.max(64, t("common.errmsg.string_max", { max: 64 })),
});
let config: NamesiloConfig = {
@@ -48,7 +59,7 @@ const AccessNamesiloForm = ({
resolver: zodResolver(formSchema),
defaultValues: {
id: data?.id,
name: data?.name || '',
name: data?.name || "",
configType: "namesilo",
apiKey: config.apiKey,
},
@@ -66,7 +77,7 @@ const AccessNamesiloForm = ({
};
try {
req.id = op == "copy" ? "" : req.id;
req.id = op == "copy" ? "" : req.id;
const rs = await save(req);
onAfterReq();
@@ -110,9 +121,12 @@ const AccessNamesiloForm = ({
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>{t('name')}</FormLabel>
<FormLabel>{t("access.authorization.form.name.label")}</FormLabel>
<FormControl>
<Input placeholder={t('access.form.name.not.empty')} {...field} />
<Input
placeholder={t("access.authorization.form.name.placeholder")}
{...field}
/>
</FormControl>
<FormMessage />
@@ -125,7 +139,7 @@ const AccessNamesiloForm = ({
name="id"
render={({ field }) => (
<FormItem className="hidden">
<FormLabel>{t('access.form.config.field')}</FormLabel>
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
@@ -140,7 +154,7 @@ const AccessNamesiloForm = ({
name="configType"
render={({ field }) => (
<FormItem className="hidden">
<FormLabel>{t('access.form.config.field')}</FormLabel>
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
@@ -155,9 +169,12 @@ const AccessNamesiloForm = ({
name="apiKey"
render={({ field }) => (
<FormItem>
<FormLabel>{t('access.form.namesilo.api.key')}</FormLabel>
<FormLabel>{t("access.authorization.form.namesilo_api_key.label")}</FormLabel>
<FormControl>
<Input placeholder={t('access.form.namesilo.api.key.not.empty')} {...field} />
<Input
placeholder={t("access.authorization.form.namesilo_api_key.placeholder")}
{...field}
/>
</FormControl>
<FormMessage />
@@ -166,7 +183,7 @@ const AccessNamesiloForm = ({
/>
<div className="flex justify-end">
<Button type="submit">{t('save')}</Button>
<Button type="submit">{t("common.save")}</Button>
</div>
</form>
</Form>

View File

@@ -15,7 +15,12 @@ import {
} from "@/components/ui/form";
import { Button } from "@/components/ui/button";
import { Access, accessFormType, getUsageByConfigType, QiniuConfig } from "@/domain/access";
import {
Access,
accessFormType,
getUsageByConfigType,
QiniuConfig,
} from "@/domain/access";
import { save } from "@/repository/access";
import { useConfig } from "@/providers/config";
@@ -35,10 +40,13 @@ const AccessQiniuForm = ({
const { t } = useTranslation();
const formSchema = z.object({
id: z.string().optional(),
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
name: z
.string()
.min(1, "access.authorization.form.name.placeholder")
.max(64, t("common.errmsg.string_max", { max: 64 })),
configType: accessFormType,
accessKey: z.string().min(1, 'access.form.access.key.not.empty').max(64),
secretKey: z.string().min(1, 'access.form.secret.key.not.empty').max(64),
accessKey: z.string().min(1, "access.authorization.form.access_key.placeholder").max(64),
secretKey: z.string().min(1, "access.authorization.form.secret_key.placeholder").max(64),
});
let config: QiniuConfig = {
@@ -51,7 +59,7 @@ const AccessQiniuForm = ({
resolver: zodResolver(formSchema),
defaultValues: {
id: data?.id,
name: data?.name || '',
name: data?.name || "",
configType: "qiniu",
accessKey: config.accessKey,
secretKey: config.secretKey,
@@ -71,7 +79,7 @@ const AccessQiniuForm = ({
};
try {
req.id = op == "copy" ? "" : req.id;
req.id = op == "copy" ? "" : req.id;
const rs = await save(req);
onAfterReq();
@@ -116,9 +124,12 @@ const AccessQiniuForm = ({
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>{t('name')}</FormLabel>
<FormLabel>{t("access.authorization.form.name.label")}</FormLabel>
<FormControl>
<Input placeholder={t('access.form.name.not.empty')} {...field} />
<Input
placeholder={t("access.authorization.form.name.placeholder")}
{...field}
/>
</FormControl>
<FormMessage />
@@ -131,7 +142,7 @@ const AccessQiniuForm = ({
name="id"
render={({ field }) => (
<FormItem className="hidden">
<FormLabel>{t('access.form.config.field')}</FormLabel>
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
@@ -146,7 +157,7 @@ const AccessQiniuForm = ({
name="configType"
render={({ field }) => (
<FormItem className="hidden">
<FormLabel>{t('access.form.config.field')}</FormLabel>
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
@@ -161,9 +172,12 @@ const AccessQiniuForm = ({
name="accessKey"
render={({ field }) => (
<FormItem>
<FormLabel>{t('access.form.access.key')}</FormLabel>
<FormLabel>{t("access.authorization.form.access_key.label")}</FormLabel>
<FormControl>
<Input placeholder={t('access.form.access.key.not.empty')} {...field} />
<Input
placeholder={t("access.authorization.form.access_key.placeholder")}
{...field}
/>
</FormControl>
<FormMessage />
@@ -176,9 +190,12 @@ const AccessQiniuForm = ({
name="secretKey"
render={({ field }) => (
<FormItem>
<FormLabel>{t('access.form.secret.key')}</FormLabel>
<FormLabel>{t("access.authorization.form.secret_key.label")}</FormLabel>
<FormControl>
<Input placeholder={t('access.form.secret.key.not.empty')} {...field} />
<Input
placeholder={t("access.authorization.form.secret_key.placeholder")}
{...field}
/>
</FormControl>
<FormMessage />
@@ -189,7 +206,7 @@ const AccessQiniuForm = ({
<FormMessage />
<div className="flex justify-end">
<Button type="submit">{t('save')}</Button>
<Button type="submit">{t("common.save")}</Button>
</div>
</form>
</Form>

View File

@@ -67,34 +67,34 @@ const AccessSSHForm = ({
id: z.string().optional(),
name: z
.string()
.min(1, "access.form.name.not.empty")
.max(64, t("zod.rule.string.max", { max: 64 })),
.min(1, "access.authorization.form.name.placeholder")
.max(64, t("common.errmsg.string_max", { max: 64 })),
configType: accessFormType,
host: z.string().refine(
(str) => {
return ipReg.test(str) || domainReg.test(str);
},
{
message: "zod.rule.ssh.host",
message: "common.errmsg.host_invalid",
}
),
group: z.string().optional(),
port: z
.string()
.min(1, "access.form.ssh.port.not.empty")
.max(5, t("zod.rule.string.max", { max: 5 })),
.min(1, "access.authorization.form.ssh_port.placeholder")
.max(5, t("common.errmsg.string_max", { max: 5 })),
username: z
.string()
.min(1, "username.not.empty")
.max(64, t("zod.rule.string.max", { max: 64 })),
.max(64, t("common.errmsg.string_max", { max: 64 })),
password: z
.string()
.min(0, "password.not.empty")
.max(64, t("zod.rule.string.max", { max: 64 })),
.max(64, t("common.errmsg.string_max", { max: 64 })),
key: z
.string()
.min(0, "access.form.ssh.key.not.empty")
.max(20480, t("zod.rule.string.max", { max: 20480 })),
.min(0, "access.authorization.form.ssh_key.placeholder")
.max(20480, t("common.errmsg.string_max", { max: 20480 })),
keyFile: z.any().optional(),
});
@@ -225,10 +225,14 @@ const AccessSSHForm = ({
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>{t("name")}</FormLabel>
<FormLabel>
{t("access.authorization.form.name.label")}
</FormLabel>
<FormControl>
<Input
placeholder={t("access.form.name.not.empty")}
placeholder={t(
"access.authorization.form.name.placeholder"
)}
{...field}
/>
</FormControl>
@@ -244,12 +248,12 @@ const AccessSSHForm = ({
render={({ field }) => (
<FormItem>
<FormLabel className="w-full flex justify-between">
<div>{t("access.form.ssh.group.label")}</div>
<div>{t("access.authorization.form.ssh_group.label")}</div>
<AccessGroupEdit
trigger={
<div className="font-normal text-primary hover:underline cursor-pointer flex items-center">
<Plus size={14} />
{t("add")}
{t("common.add")}
</div>
}
/>
@@ -265,7 +269,9 @@ const AccessSSHForm = ({
>
<SelectTrigger>
<SelectValue
placeholder={t("access.group.not.empty")}
placeholder={t(
"access.authorization.form.access_group.placeholder"
)}
/>
</SelectTrigger>
<SelectContent>
@@ -306,7 +312,9 @@ const AccessSSHForm = ({
name="id"
render={({ field }) => (
<FormItem className="hidden">
<FormLabel>{t("access.form.config.field")}</FormLabel>
<FormLabel>
{t("access.authorization.form.config.label")}
</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
@@ -321,7 +329,9 @@ const AccessSSHForm = ({
name="configType"
render={({ field }) => (
<FormItem className="hidden">
<FormLabel>{t("access.form.config.field")}</FormLabel>
<FormLabel>
{t("access.authorization.form.config.label")}
</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
@@ -336,10 +346,14 @@ const AccessSSHForm = ({
name="host"
render={({ field }) => (
<FormItem className="grow">
<FormLabel>{t("access.form.ssh.host")}</FormLabel>
<FormLabel>
{t("access.authorization.form.ssh_host.label")}
</FormLabel>
<FormControl>
<Input
placeholder={t("access.form.ssh.host.not.empty")}
placeholder={t(
"access.authorization.form.ssh_host.placeholder"
)}
{...field}
/>
</FormControl>
@@ -354,10 +368,14 @@ const AccessSSHForm = ({
name="port"
render={({ field }) => (
<FormItem>
<FormLabel>{t("access.form.ssh.port")}</FormLabel>
<FormLabel>
{t("access.authorization.form.ssh_port.label")}
</FormLabel>
<FormControl>
<Input
placeholder={t("access.form.ssh.port.not.empty")}
placeholder={t(
"access.authorization.form.ssh_port.placeholder"
)}
{...field}
type="number"
/>
@@ -374,9 +392,16 @@ const AccessSSHForm = ({
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>{t("username")}</FormLabel>
<FormLabel>
{t("access.authorization.form.username.label")}
</FormLabel>
<FormControl>
<Input placeholder={t("username.not.empty")} {...field} />
<Input
placeholder={t(
"access.authorization.form.username.placeholder"
)}
{...field}
/>
</FormControl>
<FormMessage />
@@ -389,10 +414,14 @@ const AccessSSHForm = ({
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>{t("password")}</FormLabel>
<FormLabel>
{t("access.authorization.form.password.label")}
</FormLabel>
<FormControl>
<Input
placeholder={t("password.not.empty")}
placeholder={t(
"access.authorization.form.password.placeholder"
)}
{...field}
type="password"
/>
@@ -408,10 +437,14 @@ const AccessSSHForm = ({
name="key"
render={({ field }) => (
<FormItem hidden>
<FormLabel>{t("access.form.ssh.key")}</FormLabel>
<FormLabel>
{t("access.authorization.form.ssh_key.label")}
</FormLabel>
<FormControl>
<Input
placeholder={t("access.form.ssh.key.not.empty")}
placeholder={t(
"access.authorization.form.ssh_key.placeholder"
)}
{...field}
/>
</FormControl>
@@ -426,7 +459,9 @@ const AccessSSHForm = ({
name="keyFile"
render={({ field }) => (
<FormItem>
<FormLabel>{t("access.form.ssh.key")}</FormLabel>
<FormLabel>
{t("access.authorization.form.ssh_key.label")}
</FormLabel>
<FormControl>
<div>
<Button
@@ -438,10 +473,14 @@ const AccessSSHForm = ({
>
{fileName
? fileName
: t("access.form.ssh.key.file.not.empty")}
: t(
"access.authorization.form.ssh_key_file.placeholder"
)}
</Button>
<Input
placeholder={t("access.form.ssh.key.not.empty")}
placeholder={t(
"access.authorization.form.ssh_key.placeholder"
)}
{...field}
ref={fileInputRef}
className="hidden"
@@ -460,7 +499,7 @@ const AccessSSHForm = ({
<FormMessage />
<div className="flex justify-end">
<Button type="submit">{t("save")}</Button>
<Button type="submit">{t("common.save")}</Button>
</div>
</form>
</Form>

View File

@@ -15,7 +15,12 @@ import {
} from "@/components/ui/form";
import { Button } from "@/components/ui/button";
import { Access, accessFormType, getUsageByConfigType, TencentConfig } from "@/domain/access";
import {
Access,
accessFormType,
getUsageByConfigType,
TencentConfig,
} from "@/domain/access";
import { save } from "@/repository/access";
import { useConfig } from "@/providers/config";
import { ClientResponseError } from "pocketbase";
@@ -34,10 +39,19 @@ const AccessTencentForm = ({
const { t } = useTranslation();
const formSchema = z.object({
id: z.string().optional(),
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
name: z
.string()
.min(1, "access.authorization.form.name.placeholder")
.max(64, t("common.errmsg.string_max", { max: 64 })),
configType: accessFormType,
secretId: z.string().min(1, 'access.form.secret.id.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
secretKey: z.string().min(1, 'access.form.secret.key.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
secretId: z
.string()
.min(1, "access.authorization.form.secret_id.placeholder")
.max(64, t("common.errmsg.string_max", { max: 64 })),
secretKey: z
.string()
.min(1, "access.authorization.form.secret_key.placeholder")
.max(64, t("common.errmsg.string_max", { max: 64 })),
});
let config: TencentConfig = {
@@ -50,7 +64,7 @@ const AccessTencentForm = ({
resolver: zodResolver(formSchema),
defaultValues: {
id: data?.id,
name: data?.name || '',
name: data?.name || "",
configType: "tencent",
secretId: config.secretId,
secretKey: config.secretKey,
@@ -70,7 +84,7 @@ const AccessTencentForm = ({
};
try {
req.id = op == "copy" ? "" : req.id;
req.id = op == "copy" ? "" : req.id;
const rs = await save(req);
onAfterReq();
@@ -113,9 +127,12 @@ const AccessTencentForm = ({
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>{t('name')}</FormLabel>
<FormLabel>{t("access.authorization.form.name.label")}</FormLabel>
<FormControl>
<Input placeholder={t('access.form.name.not.empty')} {...field} />
<Input
placeholder={t("access.authorization.form.name.placeholder")}
{...field}
/>
</FormControl>
<FormMessage />
@@ -128,7 +145,7 @@ const AccessTencentForm = ({
name="id"
render={({ field }) => (
<FormItem className="hidden">
<FormLabel>{t('access.form.config.field')}</FormLabel>
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
@@ -143,7 +160,7 @@ const AccessTencentForm = ({
name="configType"
render={({ field }) => (
<FormItem className="hidden">
<FormLabel>{t('access.form.config.field')}</FormLabel>
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
@@ -158,9 +175,12 @@ const AccessTencentForm = ({
name="secretId"
render={({ field }) => (
<FormItem>
<FormLabel>{t('access.form.secret.id')}</FormLabel>
<FormLabel>{t("access.authorization.form.secret_id.label")}</FormLabel>
<FormControl>
<Input placeholder={t('access.form.secret.id.not.empty')} {...field} />
<Input
placeholder={t("access.authorization.form.secret_id.placeholder")}
{...field}
/>
</FormControl>
<FormMessage />
@@ -173,9 +193,12 @@ const AccessTencentForm = ({
name="secretKey"
render={({ field }) => (
<FormItem>
<FormLabel>{t('access.form.secret.key')}</FormLabel>
<FormLabel>{t("access.authorization.form.secret_key.label")}</FormLabel>
<FormControl>
<Input placeholder={t('access.form.secret.key.not.empty')} {...field} />
<Input
placeholder={t("access.authorization.form.secret_key.placeholder")}
{...field}
/>
</FormControl>
<FormMessage />
@@ -184,7 +207,7 @@ const AccessTencentForm = ({
/>
<div className="flex justify-end">
<Button type="submit">{t('save')}</Button>
<Button type="submit">{t("common.save")}</Button>
</div>
</form>
</Form>

View File

@@ -15,7 +15,12 @@ import {
} from "@/components/ui/form";
import { Button } from "@/components/ui/button";
import { Access, accessFormType, getUsageByConfigType, WebhookConfig } from "@/domain/access";
import {
Access,
accessFormType,
getUsageByConfigType,
WebhookConfig,
} from "@/domain/access";
import { save } from "@/repository/access";
import { useConfig } from "@/providers/config";
import { ClientResponseError } from "pocketbase";
@@ -34,9 +39,12 @@ const WebhookForm = ({
const { t } = useTranslation();
const formSchema = z.object({
id: z.string().optional(),
name: z.string().min(1, 'access.form.name.not.empty').max(64, t('zod.rule.string.max', { max: 64 })),
name: z
.string()
.min(1, "access.authorization.form.name.placeholder")
.max(64, t("common.errmsg.string_max", { max: 64 })),
configType: accessFormType,
url: z.string().url('zod.rule.url'),
url: z.string().url("common.errmsg.url_invalid"),
});
let config: WebhookConfig = {
@@ -48,7 +56,7 @@ const WebhookForm = ({
resolver: zodResolver(formSchema),
defaultValues: {
id: data?.id,
name: data?.name || '',
name: data?.name || "",
configType: "webhook",
url: config.url,
},
@@ -66,7 +74,7 @@ const WebhookForm = ({
};
try {
req.id = op == "copy" ? "" : req.id;
req.id = op == "copy" ? "" : req.id;
const rs = await save(req);
onAfterReq();
@@ -110,9 +118,12 @@ const WebhookForm = ({
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>{t('name')}</FormLabel>
<FormLabel>{t("access.authorization.form.name.label")}</FormLabel>
<FormControl>
<Input placeholder={t('access.form.name.not.empty')} {...field} />
<Input
placeholder={t("access.authorization.form.name.placeholder")}
{...field}
/>
</FormControl>
<FormMessage />
@@ -125,7 +136,7 @@ const WebhookForm = ({
name="id"
render={({ field }) => (
<FormItem className="hidden">
<FormLabel>{t('access.form.config.field')}</FormLabel>
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
@@ -140,7 +151,7 @@ const WebhookForm = ({
name="configType"
render={({ field }) => (
<FormItem className="hidden">
<FormLabel>{t('access.form.config.field')}</FormLabel>
<FormLabel>{t("access.authorization.form.config.label")}</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
@@ -155,9 +166,12 @@ const WebhookForm = ({
name="url"
render={({ field }) => (
<FormItem>
<FormLabel>{t('access.form.webhook.url')}</FormLabel>
<FormLabel>{t("access.authorization.form.webhook_url.label")}</FormLabel>
<FormControl>
<Input placeholder={t('access.form.webhook.url.not.empty')} {...field} />
<Input
placeholder={t("access.authorization.form.webhook_url.placeholder")}
{...field}
/>
</FormControl>
<FormMessage />
@@ -166,7 +180,7 @@ const WebhookForm = ({
/>
<div className="flex justify-end">
<Button type="submit">{t('save')}</Button>
<Button type="submit">{t("common.save")}</Button>
</div>
</form>
</Form>

View File

@@ -113,13 +113,13 @@ const DeployList = ({ deploys, onChange }: DeployListProps) => {
fallback={
<Alert className="w-full border dark:border-stone-400">
<AlertDescription className="flex flex-col items-center">
<div>{t("deployment.not.added")}</div>
<div>{t("domain.deployment.nodata")}</div>
<div className="flex justify-end mt-2">
<DeployEditDialog
onSave={(config: DeployConfig) => {
handleAdd(config);
}}
trigger={<Button size={"sm"}>{t("add")}</Button>}
trigger={<Button size={"sm"}>{t("common.add")}</Button>}
/>
</div>
</AlertDescription>
@@ -128,7 +128,7 @@ const DeployList = ({ deploys, onChange }: DeployListProps) => {
>
<div className="flex justify-end py-2 border-b dark:border-stone-400">
<DeployEditDialog
trigger={<Button size={"sm"}>{t("add")}</Button>}
trigger={<Button size={"sm"}>{t("common.add")}</Button>}
onSave={(config: DeployConfig) => {
handleAdd(config);
}}
@@ -308,13 +308,13 @@ const DeployEditDialog = ({
// 关闭弹框
const newError = { ...error };
if (locDeployConfig.type === "") {
newError.type = t("domain.management.edit.access.not.empty.message");
newError.type = t("domain.deployment.form.access.placeholder");
} else {
newError.type = "";
}
if (locDeployConfig.access === "") {
newError.access = t("domain.management.edit.access.not.empty.message");
newError.access = t("domain.deployment.form.access.placeholder");
} else {
newError.access = "";
}
@@ -351,12 +351,13 @@ const DeployEditDialog = ({
<DialogTrigger>{trigger}</DialogTrigger>
<DialogContent className="dark:text-stone-200">
<DialogHeader>
<DialogTitle>{t("deployment")}</DialogTitle>
<DialogTitle>{t("history.page.title")}</DialogTitle>
<DialogDescription></DialogDescription>
</DialogHeader>
{/* 授权类型 */}
{/* 部署方式 */}
<div>
<Label>{t("deployment.access.type")}</Label>
<Label>{t("domain.deployment.form.type.label")}</Label>
<Select
value={locDeployConfig.type}
@@ -366,15 +367,13 @@ const DeployEditDialog = ({
>
<SelectTrigger className="mt-2">
<SelectValue
placeholder={t(
"domain.management.edit.access.not.empty.message"
)}
placeholder={t("domain.deployment.form.type.placeholder")}
/>
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>
{t("domain.management.edit.access.label")}
{t("domain.deployment.form.type.list")}
</SelectLabel>
{targetTypeKeys.map((item) => (
<SelectItem key={item} value={item}>
@@ -393,15 +392,16 @@ const DeployEditDialog = ({
<div className="text-red-500 text-sm mt-1">{error.type}</div>
</div>
{/* 授权 */}
{/* 授权配置 */}
<div>
<Label className="flex justify-between">
<div>{t("deployment.access.config")}</div>
<div>{t("domain.deployment.form.access.label")}</div>
<AccessEdit
trigger={
<div className="font-normal text-primary hover:underline cursor-pointer flex items-center">
<Plus size={14} />
{t("add")}
{t("common.add")}
</div>
}
op="add"
@@ -417,14 +417,14 @@ const DeployEditDialog = ({
<SelectTrigger className="mt-2">
<SelectValue
placeholder={t(
"domain.management.edit.access.not.empty.message"
"domain.deployment.form.access.placeholder"
)}
/>
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>
{t("domain.management.edit.access.label")}
{t("domain.deployment.form.access.list")}
</SelectLabel>
{targetAccesses.map((item) => (
<SelectItem key={item.id} value={item.id}>
@@ -444,6 +444,7 @@ const DeployEditDialog = ({
<div className="text-red-500 text-sm mt-1">{error.access}</div>
</div>
{/* 其他参数 */}
<DeployEdit type={deployType!} />
<DialogFooter>
@@ -453,7 +454,7 @@ const DeployEditDialog = ({
handleSaveClick();
}}
>
{t("save")}
{t("common.save")}
</Button>
</DialogFooter>
</DialogContent>
@@ -516,9 +517,9 @@ const DeploySSH = () => {
<>
<div className="flex flex-col space-y-2">
<div>
<Label>{t("access.form.ssh.cert.path")}</Label>
<Label>{t("access.authorization.form.ssh_cert_path.label")}</Label>
<Input
placeholder={t("access.form.ssh.cert.path")}
placeholder={t("access.authorization.form.ssh_cert_path.label")}
className="w-full mt-1"
value={data?.config?.certPath}
onChange={(e) => {
@@ -533,9 +534,11 @@ const DeploySSH = () => {
/>
</div>
<div>
<Label>{t("access.form.ssh.key.path")}</Label>
<Label>{t("access.authorization.form.ssh_key_path.label")}</Label>
<Input
placeholder={t("access.form.ssh.key.path")}
placeholder={t(
"access.authorization.form.ssh_key_path.placeholder"
)}
className="w-full mt-1"
value={data?.config?.keyPath}
onChange={(e) => {
@@ -551,11 +554,13 @@ const DeploySSH = () => {
</div>
<div>
<Label>{t("access.form.ssh.pre.command")}</Label>
<Label>{t("access.authorization.form.ssh_pre_command.label")}</Label>
<Textarea
className="mt-1"
value={data?.config?.preCommand}
placeholder={t("access.form.ssh.pre.command.not.empty")}
placeholder={t(
"access.authorization.form.ssh_pre_command.placeholder"
)}
onChange={(e) => {
const newData = produce(data, (draft) => {
if (!draft.config) {
@@ -569,11 +574,11 @@ const DeploySSH = () => {
</div>
<div>
<Label>{t("access.form.ssh.command")}</Label>
<Label>{t("access.authorization.form.ssh_command.label")}</Label>
<Textarea
className="mt-1"
value={data?.config?.command}
placeholder={t("access.form.ssh.command.not.empty")}
placeholder={t("access.authorization.form.ssh_command.placeholder")}
onChange={(e) => {
const newData = produce(data, (draft) => {
if (!draft.config) {
@@ -617,15 +622,17 @@ const DeployCDN = () => {
const domainSchema = z
.string()
.regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, {
message: t("domain.not.empty.verify.message"),
message: t("common.errmsg.domain_invalid"),
});
return (
<div className="flex flex-col space-y-2">
<div>
<Label>{t("deployment.access.cdn.deploy.to.domain")}</Label>
<Label>{t("domain.deployment.form.cdn_domain.label")}</Label>
<Input
placeholder={t("deployment.access.cdn.deploy.to.domain")}
placeholder={t(
"domain.deployment.form.cdn_domain.placeholder"
)}
className="w-full mt-1"
value={data?.config?.domain}
onChange={(e) => {
@@ -714,17 +721,17 @@ const DeployOSS = () => {
const domainSchema = z
.string()
.regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, {
message: t("domain.not.empty.verify.message"),
message: t("common.errmsg.domain_invalid"),
});
const bucketSchema = z.string().min(1, {
message: t("deployment.access.oss.bucket.not.empty"),
message: t("domain.deployment.form.oss_bucket.placeholder"),
});
return (
<div className="flex flex-col space-y-2">
<div>
<Label>{t("deployment.access.oss.endpoint")}</Label>
<Label>{t("domain.deployment.form.oss_endpoint.label")}</Label>
<Input
className="w-full mt-1"
@@ -743,9 +750,11 @@ const DeployOSS = () => {
/>
<div className="text-red-600 text-sm mt-1">{error?.endpoint}</div>
<Label>{t("deployment.access.oss.bucket")}</Label>
<Label>{t("domain.deployment.form.oss_bucket")}</Label>
<Input
placeholder={t("deployment.access.oss.bucket.not.empty")}
placeholder={t(
"domain.deployment.form.oss_bucket.placeholder"
)}
className="w-full mt-1"
value={data?.config?.bucket}
onChange={(e) => {
@@ -775,9 +784,9 @@ const DeployOSS = () => {
/>
<div className="text-red-600 text-sm mt-1">{error?.bucket}</div>
<Label>{t("deployment.access.cdn.deploy.to.domain")}</Label>
<Label>{t("domain.deployment.form.cdn_domain.label")}</Label>
<Input
placeholder={t("deployment.access.cdn.deploy.to.domain")}
placeholder={t("domain.deployment.form.cdn_domain.label")}
className="w-full mt-1"
value={data?.config?.domain}
onChange={(e) => {

View File

@@ -1,7 +1,6 @@
import { useTranslation } from "react-i18next";
import { cn } from "@/lib/utils"
import { cn } from "@/lib/utils";
import { Separator } from "../ui/separator";
@@ -16,58 +15,52 @@ const DeployProgress = ({ phase, phaseSuccess }: DeployProgressProps) => {
let step = 0;
if (phase === "check") {
step = 1
step = 1;
} else if (phase === "apply") {
step = 2
step = 2;
} else if (phase === "deploy") {
step = 3
step = 3;
}
return (
<div className="flex items-center">
<div className={
cn(
<div
className={cn(
"text-xs text-nowrap",
step === 1 ? phaseSuccess ? "text-green-600" : "text-red-600" : "",
step > 1 ? "text-green-600" : "",
)
}>
{t('deploy.progress.check')}
step === 1 ? (phaseSuccess ? "text-green-600" : "text-red-600") : "",
step > 1 ? "text-green-600" : ""
)}
>
{t("history.props.stage.progress.check")}
</div>
<Separator className={
cn(
"h-1 grow max-w-[60px]",
step > 1 ? "bg-green-600" : "",
)
} />
<div className={
cn(
<Separator
className={cn("h-1 grow max-w-[60px]", step > 1 ? "bg-green-600" : "")}
/>
<div
className={cn(
"text-xs text-nowrap",
step < 2 ? "text-muted-foreground" : "",
step === 2 ? phaseSuccess ? "text-green-600" : "text-red-600" : "",
step > 2 ? "text-green-600" : "",
)
}>
{t('deploy.progress.apply')}
step === 2 ? (phaseSuccess ? "text-green-600" : "text-red-600") : "",
step > 2 ? "text-green-600" : ""
)}
>
{t("history.props.stage.progress.apply")}
</div>
<Separator className={
cn(
"h-1 grow max-w-[60px]",
step > 2 ? "bg-green-600" : "",
)
} />
<div className={
cn(
<Separator
className={cn("h-1 grow max-w-[60px]", step > 2 ? "bg-green-600" : "")}
/>
<div
className={cn(
"text-xs text-nowrap",
step < 3 ? "text-muted-foreground" : "",
step === 3 ? phaseSuccess ? "text-green-600" : "text-red-600" : "",
step > 3 ? "text-green-600" : "",
)
}>
{t('deploy.progress.deploy')}
step === 3 ? (phaseSuccess ? "text-green-600" : "text-red-600") : "",
step > 3 ? "text-green-600" : ""
)}
>
{t("history.props.stage.progress.deploy")}
</div>
</div>
)
);
};
export default DeployProgress;

View File

@@ -43,7 +43,7 @@ const EmailsEdit = ({ className, trigger }: EmailsEditProps) => {
const { t } = useTranslation();
const formSchema = z.object({
email: z.string().email("email.valid.message"),
email: z.string().email("common.errmsg.email_invalid"),
});
const form = useForm<z.infer<typeof formSchema>>({
@@ -56,7 +56,7 @@ const EmailsEdit = ({ className, trigger }: EmailsEditProps) => {
const onSubmit = async (data: z.infer<typeof formSchema>) => {
if ((emails.content as EmailsSetting).emails.includes(data.email)) {
form.setError("email", {
message: "email.already.exist",
message: "common.errmsg.email_duplicate",
});
return;
}
@@ -102,7 +102,7 @@ const EmailsEdit = ({ className, trigger }: EmailsEditProps) => {
</DialogTrigger>
<DialogContent className="sm:max-w-[600px] w-full dark:text-stone-200">
<DialogHeader>
<DialogTitle>{t('email.add')}</DialogTitle>
<DialogTitle>{t("domain.application.form.email.add")}</DialogTitle>
</DialogHeader>
<div className="container py-3">
@@ -120,9 +120,13 @@ const EmailsEdit = ({ className, trigger }: EmailsEditProps) => {
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>{t('email')}</FormLabel>
<FormLabel>{t("domain.application.form.email.label")}</FormLabel>
<FormControl>
<Input placeholder={t('email.not.empty.message')} {...field} type="email" />
<Input
placeholder={t("common.errmsg.email_empty")}
{...field}
type="email"
/>
</FormControl>
<FormMessage />
@@ -131,7 +135,7 @@ const EmailsEdit = ({ className, trigger }: EmailsEditProps) => {
/>
<div className="flex justify-end">
<Button type="submit">{t('save')}</Button>
<Button type="submit">{t("common.save")}</Button>
</div>
</form>
</Form>

View File

@@ -71,7 +71,7 @@ const KVList = ({ variables, onValueChange }: KVListProps) => {
return (
<>
<div className="flex justify-between dark:text-stone-200">
<Label>{t("variable")}</Label>
<Label>{t("domain.deployment.form.variables.label")}</Label>
<Show when={!!locVariables?.length}>
<KVEdit
variable={{
@@ -82,7 +82,7 @@ const KVList = ({ variables, onValueChange }: KVListProps) => {
<div className="flex items-center text-primary">
<Plus size={16} className="cursor-pointer " />
<div className="text-sm ">{t("add")}</div>
<div className="text-sm ">{t("common.add")}</div>
</div>
}
onSave={(variable) => {
@@ -97,7 +97,7 @@ const KVList = ({ variables, onValueChange }: KVListProps) => {
fallback={
<div className="border rounded-md p-3 text-sm mt-2 flex flex-col items-center">
<div className="text-muted-foreground">
{t("variable.not.added")}
{t("domain.deployment.form.variables.empty")}
</div>
<KVEdit
@@ -105,7 +105,7 @@ const KVList = ({ variables, onValueChange }: KVListProps) => {
<div className="flex items-center text-primary">
<Plus size={16} className="cursor-pointer " />
<div className="text-sm ">{t("add")}</div>
<div className="text-sm ">{t("common.add")}</div>
</div>
}
variable={{
@@ -175,14 +175,14 @@ const KVEdit = ({ variable, trigger, onSave }: KVEditProps) => {
const handleSaveClick = () => {
if (!locVariable.key) {
setErr({
key: t("variable.name.required"),
key: t("domain.deployment.form.variables.key.required"),
});
return;
}
if (!locVariable.value) {
setErr({
value: t("variable.value.required"),
value: t("domain.deployment.form.variables.value.required"),
});
return;
}
@@ -204,12 +204,12 @@ const KVEdit = ({ variable, trigger, onSave }: KVEditProps) => {
<DialogTrigger>{trigger}</DialogTrigger>
<DialogContent className="dark:text-stone-200">
<DialogHeader className="flex flex-col">
<DialogTitle>{t("variable")}</DialogTitle>
<DialogTitle>{t("domain.deployment.form.variables.label")}</DialogTitle>
<div className="pt-5 flex flex-col items-start">
<Label>{t("variable.name")}</Label>
<Label>{t("domain.deployment.form.variables.key")}</Label>
<Input
placeholder={t("variable.name.placeholder")}
placeholder={t("domain.deployment.form.variables.key.placeholder")}
value={locVariable?.key}
onChange={(e) => {
setLocVariable({ ...locVariable, key: e.target.value });
@@ -220,9 +220,9 @@ const KVEdit = ({ variable, trigger, onSave }: KVEditProps) => {
</div>
<div className="pt-2 flex flex-col items-start">
<Label>{t("variable.value")}</Label>
<Label>{t("domain.deployment.form.variables.value")}</Label>
<Input
placeholder={t("variable.value.placeholder")}
placeholder={t("domain.deployment.form.variables.value.placeholder")}
value={locVariable?.value}
onChange={(e) => {
setLocVariable({ ...locVariable, value: e.target.value });
@@ -240,7 +240,7 @@ const KVEdit = ({ variable, trigger, onSave }: KVEditProps) => {
handleSaveClick();
}}
>
{t("save")}
{t("common.save")}
</Button>
</div>
</DialogFooter>

View File

@@ -25,9 +25,9 @@ type StringListProps = {
};
const titles: Record<string, string> = {
domain: "domain",
ip: "IP",
dns: "dns",
domain: "common.text.domain",
ip: "common.text.ip",
dns: "common.text.dns",
};
const StringList = ({
@@ -90,7 +90,7 @@ const StringList = ({
<div className="flex items-center text-primary">
<Plus size={16} className="cursor-pointer " />
<div className="text-sm ">{t("add")}</div>
<div className="text-sm ">{t("common.add")}</div>
</div>
}
/>
@@ -102,12 +102,12 @@ const StringList = ({
fallback={
<div className="border rounded-md p-3 text-sm mt-2 flex flex-col items-center">
<div className="text-muted-foreground">
{t("not.added.yet." + valueType)}
{t('common.text.' + valueType + '.empty')}
</div>
<StringEdit
value={""}
trigger={t("add")}
trigger={t("common.add")}
onValueChange={addVal}
valueType={valueType}
/>
@@ -182,10 +182,10 @@ const StringEdit = ({
const domainSchema = z
.string()
.regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, {
message: t("domain.not.empty.verify.message"),
message: t("common.errmsg.domain_invalid"),
});
const ipSchema = z.string().ip({ message: t("ip.not.empty.verify.message") });
const ipSchema = z.string().ip({ message: t("common.errmsg.ip_invalid") });
const schedules: Record<ValueType, z.ZodString> = {
domain: domainSchema,
@@ -240,7 +240,7 @@ const StringEdit = ({
onSaveClick();
}}
>
{op === "add" ? t("add") : t("confirm")}
{op === "add" ? t("common.add") : t("common.confirm")}
</Button>
</DialogFooter>
</DialogContent>

View File

@@ -5,7 +5,7 @@ import { Separator } from "../ui/separator";
import { version } from "@/domain/version";
const Version = () => {
const { t } = useTranslation()
const { t } = useTranslation();
return (
<div className="fixed right-0 bottom-0 w-full flex justify-between p-5">
@@ -17,7 +17,7 @@ const Version = () => {
className="flex items-center"
>
<BookOpen size={16} />
<div className="ml-1">{t('document')}</div>
<div className="ml-1">{t("common.menu.document")}</div>
</a>
<Separator orientation="vertical" className="mx-2" />
<a

View File

@@ -8,7 +8,7 @@ import { useEffect, useState } from "react";
import { update } from "@/repository/settings";
import { getErrMessage } from "@/lib/error";
import { useToast } from "../ui/use-toast";
import { useTranslation } from 'react-i18next'
import { useTranslation } from "react-i18next";
type DingTalkSetting = {
id: string;
@@ -72,15 +72,17 @@ const DingTalk = () => {
setChannels(resp);
toast({
title: t('save.succeed'),
description: t('setting.notify.config.save.succeed'),
title: t("common.save.succeeded.message"),
description: t("settings.notification.config.saved.message"),
});
} catch (e) {
const msg = getErrMessage(e);
toast({
title: t('save.failed'),
description: `${t('setting.notify.config.save.failed')}: ${msg}`,
title: t("common.save.failed.message"),
description: `${t(
"settings.notification.config.failed.message"
)}: ${msg}`,
variant: "destructive",
});
}
@@ -102,7 +104,7 @@ const DingTalk = () => {
}}
/>
<Input
placeholder={t('access.form.ding.access.token.placeholder')}
placeholder={t("settings.notification.dingtalk.secret.placeholder")}
className="mt-2"
value={dingtalk.data.secret}
onChange={(e) => {
@@ -129,7 +131,9 @@ const DingTalk = () => {
});
}}
/>
<Label htmlFor="airplane-mode">{t('setting.notify.config.enable')}</Label>
<Label htmlFor="airplane-mode">
{t("settings.notification.config.enable")}
</Label>
</div>
<div className="flex justify-end mt-2">
@@ -138,7 +142,7 @@ const DingTalk = () => {
handleSaveClick();
}}
>
{t('save')}
{t("common.save")}
</Button>
</div>
</div>

View File

@@ -9,7 +9,7 @@ import {
} from "@/domain/settings";
import { getSetting, update } from "@/repository/settings";
import { useToast } from "../ui/use-toast";
import { useTranslation } from 'react-i18next'
import { useTranslation } from "react-i18next";
const NotifyTemplate = () => {
const [id, setId] = useState("");
@@ -68,8 +68,8 @@ const NotifyTemplate = () => {
}
toast({
title: t('save.succeed'),
description: t('setting.notify.template.save.succeed'),
title: t("common.save.succeeded.message"),
description: t("settings.notification.template.saved.message"),
});
};
@@ -83,7 +83,7 @@ const NotifyTemplate = () => {
/>
<div className="text-muted-foreground text-sm mt-1">
{t('setting.notify.template.variables.tips.title')}
{t("settings.notification.template.variables.tips.title")}
</div>
<Textarea
@@ -94,10 +94,10 @@ const NotifyTemplate = () => {
}}
></Textarea>
<div className="text-muted-foreground text-sm mt-1">
{t('setting.notify.template.variables.tips.content')}
{t("settings.notification.template.variables.tips.content")}
</div>
<div className="flex justify-end mt-2">
<Button onClick={handleSaveClick}>{t('save')}</Button>
<Button onClick={handleSaveClick}>{t("common.save")}</Button>
</div>
</div>
);

View File

@@ -50,7 +50,7 @@ const Telegram = () => {
const data = getDetailTelegram();
setTelegram({
id: config.id ?? "",
name: "telegram",
name: "common.provider.telegram",
data,
});
}, [config]);
@@ -72,15 +72,17 @@ const Telegram = () => {
setChannels(resp);
toast({
title: t('save.succeed'),
description: t('setting.notify.config.save.succeed'),
title: t("common.save.succeeded.message"),
description: t("settings.notification.config.saved.message"),
});
} catch (e) {
const msg = getErrMessage(e);
toast({
title: t('save.failed'),
description: `${t('setting.notify.config.save.failed')}: ${msg}`,
title: t("common.save.failed.message"),
description: `${t(
"settings.notification.config.failed.message"
)}: ${msg}`,
variant: "destructive",
});
}
@@ -130,7 +132,9 @@ const Telegram = () => {
});
}}
/>
<Label htmlFor="airplane-mode">{t('setting.notify.config.enable')}</Label>
<Label htmlFor="airplane-mode">
{t("settings.notification.config.enable")}
</Label>
</div>
<div className="flex justify-end mt-2">
@@ -139,7 +143,7 @@ const Telegram = () => {
handleSaveClick();
}}
>
{t('save')}
{t("common.save")}
</Button>
</div>
</div>

View File

@@ -9,7 +9,7 @@ import { update } from "@/repository/settings";
import { getErrMessage } from "@/lib/error";
import { useToast } from "../ui/use-toast";
import { isValidURL } from "@/lib/url";
import { useTranslation } from 'react-i18next'
import { useTranslation } from "react-i18next";
type WebhookSetting = {
id: string;
@@ -61,8 +61,8 @@ const Webhook = () => {
webhook.data.url = webhook.data.url.trim();
if (!isValidURL(webhook.data.url)) {
toast({
title: t('save.failed'),
description: t('setting.notify.config.save.failed.url.not.valid'),
title: t("common.save.failed.message"),
description: t("settings.notification.url.errmsg.invalid"),
variant: "destructive",
});
return;
@@ -81,15 +81,17 @@ const Webhook = () => {
setChannels(resp);
toast({
title: t('save.succeed'),
description: t('setting.notify.config.save.succeed'),
title: t("common.save.succeeded.message"),
description: t("settings.notification.config.saved.message"),
});
} catch (e) {
const msg = getErrMessage(e);
toast({
title: t('save.failed'),
description: `${t('setting.notify.config.save.failed')}: ${msg}`,
title: t("common.save.failed.message"),
description: `${t(
"settings.notification.config.failed.message"
)}: ${msg}`,
variant: "destructive",
});
}
@@ -125,7 +127,9 @@ const Webhook = () => {
});
}}
/>
<Label htmlFor="airplane-mode">{t('setting.notify.config.enable')}</Label>
<Label htmlFor="airplane-mode">
{t("settings.notification.config.enable")}
</Label>
</div>
<div className="flex justify-end mt-2">
@@ -134,7 +138,7 @@ const Webhook = () => {
handleSaveClick();
}}
>
{t('save')}
{t("common.save")}
</Button>
</div>
</div>

View File

@@ -1,10 +1,10 @@
import * as React from "react"
import * as AccordionPrimitive from "@radix-ui/react-accordion"
import { ChevronDown } from "lucide-react"
import * as React from "react";
import * as AccordionPrimitive from "@radix-ui/react-accordion";
import { ChevronDown } from "lucide-react";
import { cn } from "@/lib/utils"
import { cn } from "@/lib/utils";
const Accordion = AccordionPrimitive.Root
const Accordion = AccordionPrimitive.Root;
const AccordionItem = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Item>,
@@ -15,8 +15,8 @@ const AccordionItem = React.forwardRef<
className={cn("border-b", className)}
{...props}
/>
))
AccordionItem.displayName = "AccordionItem"
));
AccordionItem.displayName = "AccordionItem";
const AccordionTrigger = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Trigger>,
@@ -35,8 +35,8 @@ const AccordionTrigger = React.forwardRef<
<ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
</AccordionPrimitive.Trigger>
</AccordionPrimitive.Header>
))
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
));
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
const AccordionContent = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Content>,
@@ -49,8 +49,8 @@ const AccordionContent = React.forwardRef<
>
<div className={cn("pb-4 pt-0", className)}>{children}</div>
</AccordionPrimitive.Content>
))
));
AccordionContent.displayName = AccordionPrimitive.Content.displayName
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };

View File

@@ -1,7 +1,7 @@
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils"
import { cn } from "@/lib/utils";
const alertVariants = cva(
"relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground",
@@ -17,7 +17,7 @@ const alertVariants = cva(
variant: "default",
},
}
)
);
const Alert = React.forwardRef<
HTMLDivElement,
@@ -29,8 +29,8 @@ const Alert = React.forwardRef<
className={cn(alertVariants({ variant }), className)}
{...props}
/>
))
Alert.displayName = "Alert"
));
Alert.displayName = "Alert";
const AlertTitle = React.forwardRef<
HTMLParagraphElement,
@@ -41,8 +41,8 @@ const AlertTitle = React.forwardRef<
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
{...props}
/>
))
AlertTitle.displayName = "AlertTitle"
));
AlertTitle.displayName = "AlertTitle";
const AlertDescription = React.forwardRef<
HTMLParagraphElement,
@@ -53,7 +53,7 @@ const AlertDescription = React.forwardRef<
className={cn("text-sm [&_p]:leading-relaxed", className)}
{...props}
/>
))
AlertDescription.displayName = "AlertDescription"
));
AlertDescription.displayName = "AlertDescription";
export { Alert, AlertTitle, AlertDescription }
export { Alert, AlertTitle, AlertDescription };

View File

@@ -64,7 +64,7 @@ const PaginationPrevious = ({
className,
...props
}: React.ComponentProps<typeof PaginationLink>) => {
const { t } = useTranslation()
const { t } = useTranslation();
return (
<PaginationLink
@@ -74,9 +74,9 @@ const PaginationPrevious = ({
{...props}
>
<ChevronLeft className="h-4 w-4" />
<span>{t('pagination.prev')}</span>
<span>{t("common.pagination.prev")}</span>
</PaginationLink>
)
);
};
PaginationPrevious.displayName = "PaginationPrevious";
@@ -84,7 +84,7 @@ const PaginationNext = ({
className,
...props
}: React.ComponentProps<typeof PaginationLink>) => {
const { t } = useTranslation()
const { t } = useTranslation();
return (
<PaginationLink
@@ -93,26 +93,30 @@ const PaginationNext = ({
className={cn("gap-1 pr-2.5", className)}
{...props}
>
<span>{t('pagination.next')}</span>
<span>{t("common.pagination.next")}</span>
<ChevronRight className="h-4 w-4" />
</PaginationLink>
)
);
};
PaginationNext.displayName = "PaginationNext";
const PaginationEllipsis = ({
className,
...props
}: React.ComponentProps<"span">) => (
<span
aria-hidden
className={cn("flex h-9 w-9 items-center justify-center", className)}
{...props}
>
<MoreHorizontal className="h-4 w-4" />
<span className="sr-only">More pages</span>
</span>
);
}: React.ComponentProps<"span">) => {
const { t } = useTranslation();
return (
<span
aria-hidden
className={cn("flex h-9 w-9 items-center justify-center", className)}
{...props}
>
<MoreHorizontal className="h-4 w-4" />
<span className="sr-only">{t("common.pagination.more")}</span>
</span>
);
};
PaginationEllipsis.displayName = "PaginationEllipsis";
export {

View File

@@ -1,76 +1,73 @@
// Inspired by react-hot-toast library
import * as React from "react"
import * as React from "react";
import type {
ToastActionElement,
ToastProps,
} from "@/components/ui/toast"
import type { ToastActionElement, ToastProps } from "@/components/ui/toast";
const TOAST_LIMIT = 1
const TOAST_REMOVE_DELAY = 1000000
const TOAST_LIMIT = 1;
const TOAST_REMOVE_DELAY = 1000000;
type ToasterToast = ToastProps & {
id: string
title?: React.ReactNode
description?: React.ReactNode
action?: ToastActionElement
}
id: string;
title?: React.ReactNode;
description?: React.ReactNode;
action?: ToastActionElement;
};
const actionTypes = {
ADD_TOAST: "ADD_TOAST",
UPDATE_TOAST: "UPDATE_TOAST",
DISMISS_TOAST: "DISMISS_TOAST",
REMOVE_TOAST: "REMOVE_TOAST",
} as const
} as const;
let count = 0
let count = 0;
function genId() {
count = (count + 1) % Number.MAX_SAFE_INTEGER
return count.toString()
count = (count + 1) % Number.MAX_SAFE_INTEGER;
return count.toString();
}
type ActionType = typeof actionTypes
type ActionType = typeof actionTypes;
type Action =
| {
type: ActionType["ADD_TOAST"]
toast: ToasterToast
type: ActionType["ADD_TOAST"];
toast: ToasterToast;
}
| {
type: ActionType["UPDATE_TOAST"]
toast: Partial<ToasterToast>
type: ActionType["UPDATE_TOAST"];
toast: Partial<ToasterToast>;
}
| {
type: ActionType["DISMISS_TOAST"]
toastId?: ToasterToast["id"]
type: ActionType["DISMISS_TOAST"];
toastId?: ToasterToast["id"];
}
| {
type: ActionType["REMOVE_TOAST"]
toastId?: ToasterToast["id"]
}
type: ActionType["REMOVE_TOAST"];
toastId?: ToasterToast["id"];
};
interface State {
toasts: ToasterToast[]
toasts: ToasterToast[];
}
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>();
const addToRemoveQueue = (toastId: string) => {
if (toastTimeouts.has(toastId)) {
return
return;
}
const timeout = setTimeout(() => {
toastTimeouts.delete(toastId)
toastTimeouts.delete(toastId);
dispatch({
type: "REMOVE_TOAST",
toastId: toastId,
})
}, TOAST_REMOVE_DELAY)
});
}, TOAST_REMOVE_DELAY);
toastTimeouts.set(toastId, timeout)
}
toastTimeouts.set(toastId, timeout);
};
export const reducer = (state: State, action: Action): State => {
switch (action.type) {
@@ -78,7 +75,7 @@ export const reducer = (state: State, action: Action): State => {
return {
...state,
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
}
};
case "UPDATE_TOAST":
return {
@@ -86,19 +83,19 @@ export const reducer = (state: State, action: Action): State => {
toasts: state.toasts.map((t) =>
t.id === action.toast.id ? { ...t, ...action.toast } : t
),
}
};
case "DISMISS_TOAST": {
const { toastId } = action
const { toastId } = action;
// ! Side effects ! - This could be extracted into a dismissToast() action,
// but I'll keep it here for simplicity
if (toastId) {
addToRemoveQueue(toastId)
addToRemoveQueue(toastId);
} else {
state.toasts.forEach((toast) => {
addToRemoveQueue(toast.id)
})
addToRemoveQueue(toast.id);
});
}
return {
@@ -111,44 +108,44 @@ export const reducer = (state: State, action: Action): State => {
}
: t
),
}
};
}
case "REMOVE_TOAST":
if (action.toastId === undefined) {
return {
...state,
toasts: [],
}
};
}
return {
...state,
toasts: state.toasts.filter((t) => t.id !== action.toastId),
}
};
}
}
};
const listeners: Array<(state: State) => void> = []
const listeners: Array<(state: State) => void> = [];
let memoryState: State = { toasts: [] }
let memoryState: State = { toasts: [] };
function dispatch(action: Action) {
memoryState = reducer(memoryState, action)
memoryState = reducer(memoryState, action);
listeners.forEach((listener) => {
listener(memoryState)
})
listener(memoryState);
});
}
type Toast = Omit<ToasterToast, "id">
type Toast = Omit<ToasterToast, "id">;
function toast({ ...props }: Toast) {
const id = genId()
const id = genId();
const update = (props: ToasterToast) =>
dispatch({
type: "UPDATE_TOAST",
toast: { ...props, id },
})
const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id })
});
const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id });
dispatch({
type: "ADD_TOAST",
@@ -157,36 +154,36 @@ function toast({ ...props }: Toast) {
id,
open: true,
onOpenChange: (open) => {
if (!open) dismiss()
if (!open) dismiss();
},
},
})
});
return {
id: id,
dismiss,
update,
}
};
}
function useToast() {
const [state, setState] = React.useState<State>(memoryState)
const [state, setState] = React.useState<State>(memoryState);
React.useEffect(() => {
listeners.push(setState)
listeners.push(setState);
return () => {
const index = listeners.indexOf(setState)
const index = listeners.indexOf(setState);
if (index > -1) {
listeners.splice(index, 1)
listeners.splice(index, 1);
}
}
}, [state])
};
}, [state]);
return {
...state,
toast,
dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
}
};
}
export { useToast, toast }
export { useToast, toast };

View File

@@ -1,16 +1,16 @@
import { z } from "zod";
export const accessTypeMap: Map<string, [string, string]> = new Map([
["aliyun", ["aliyun", "/imgs/providers/aliyun.svg"]],
["tencent", ["tencent", "/imgs/providers/tencent.svg"]],
["huaweicloud", ["huaweicloud", "/imgs/providers/huaweicloud.svg"]],
["qiniu", ["qiniu", "/imgs/providers/qiniu.svg"]],
["cloudflare", ["cloudflare", "/imgs/providers/cloudflare.svg"]],
["namesilo", ["namesilo", "/imgs/providers/namesilo.svg"]],
["godaddy", ["go.daddy", "/imgs/providers/godaddy.svg"]],
["local", ["local.deployment", "/imgs/providers/local.svg"]],
["ssh", ["ssh", "/imgs/providers/ssh.svg"]],
["webhook", ["webhook", "/imgs/providers/webhook.svg"]],
["aliyun", ["common.provider.aliyun", "/imgs/providers/aliyun.svg"]],
["tencent", ["common.provider.tencent", "/imgs/providers/tencent.svg"]],
["huaweicloud", ["common.provider.huaweicloud", "/imgs/providers/huaweicloud.svg"]],
["qiniu", ["common.provider.qiniu", "/imgs/providers/qiniu.svg"]],
["cloudflare", ["common.provider.cloudflare", "/imgs/providers/cloudflare.svg"]],
["namesilo", ["common.provider.namesilo", "/imgs/providers/namesilo.svg"]],
["godaddy", ["common.provider.godaddy", "/imgs/providers/godaddy.svg"]],
["local", ["common.provider.local", "/imgs/providers/local.svg"]],
["ssh", ["common.provider.ssh", "/imgs/providers/ssh.svg"]],
["webhook", ["common.provider.webhook", "/imgs/providers/webhook.svg"]],
]);
export const getProviderInfo = (t: string) => {
@@ -30,7 +30,7 @@ export const accessFormType = z.union(
z.literal("ssh"),
z.literal("webhook"),
],
{ message: "access.not.empty" }
{ message: "access.common.type.errmsg.empty" }
);
type AccessUsage = "apply" | "deploy" | "all";

View File

@@ -66,14 +66,14 @@ export const getLastDeployment = (domain: Domain): Deployment | undefined => {
};
export const targetTypeMap: Map<string, [string, string]> = new Map([
["aliyun-cdn", ["aliyun.cdn", "/imgs/providers/aliyun.svg"]],
["aliyun-oss", ["aliyun.oss", "/imgs/providers/aliyun.svg"]],
["aliyun-dcdn", ["aliyun.dcdn", "/imgs/providers/aliyun.svg"]],
["tencent-cdn", ["tencent.cdn", "/imgs/providers/tencent.svg"]],
["ssh", ["ssh", "/imgs/providers/ssh.svg"]],
["qiniu-cdn", ["qiniu.cdn", "/imgs/providers/qiniu.svg"]],
["webhook", ["webhook", "/imgs/providers/webhook.svg"]],
["local", ["local.deployment", "/imgs/providers/local.svg"]],
["aliyun-oss", ["common.provider.aliyun.oss", "/imgs/providers/aliyun.svg"]],
["aliyun-cdn", ["common.provider.aliyun.cdn", "/imgs/providers/aliyun.svg"]],
["aliyun-dcdn", ["common.provider.aliyun.dcdn", "/imgs/providers/aliyun.svg"]],
["tencent-cdn", ["common.provider.tencent.cdn", "/imgs/providers/tencent.svg"]],
["qiniu-cdn", ["common.provider.qiniu.cdn", "/imgs/providers/qiniu.svg"]],
["local", ["common.provider.local", "/imgs/providers/local.svg"]],
["ssh", ["common.provider.ssh", "/imgs/providers/ssh.svg"]],
["webhook", ["common.provider.webhook", "/imgs/providers/webhook.svg"]],
]);
export const targetTypeKeys = Array.from(targetTypeMap.keys());

View File

@@ -50,8 +50,8 @@ export type NotifyChannelWebhook = {
};
export const defaultNotifyTemplate: NotifyTemplate = {
title: "您有{COUNT}张证书即将过期",
content: "有{COUNT}张证书即将过期,域名分别为{DOMAINS},请保持关注!",
title: "您有 {COUNT} 张证书即将过期",
content: "有 {COUNT} 张证书即将过期域名分别为 {DOMAINS}请保持关注!",
};
export type SSLProvider = "letsencrypt" | "zerossl";

View File

@@ -1,22 +1,22 @@
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import resources from './locales'
import resources from "./locales";
i18n
.use(LanguageDetector)
.use(initReactI18next)
.init({
resources,
fallbackLng: 'zh',
debug: true,
interpolation: {
escapeValue: false,
},
backend: {
loadPath: '/locales/{{lng}}.json',
}
});
.use(LanguageDetector)
.use(initReactI18next)
.init({
resources,
fallbackLng: "zh",
debug: true,
interpolation: {
escapeValue: false,
},
backend: {
loadPath: "/locales/{{lng}}.json",
},
});
export default i18n;

View File

@@ -1,247 +0,0 @@
{
"ca": "Certificate Authority",
"username": "Username",
"username.not.empty": "Please enter username",
"password": "Password",
"password.not.empty": "Please enter password",
"ip.not.empty.verify.message": "Please enter Ip",
"email": "Email",
"logout": "Logout",
"setting": "Settings",
"account": "Account",
"template": "Template",
"save": "Save",
"next": "Next",
"no.data": "No data available",
"status": "Status",
"operation": "Operation",
"enable": "Enable",
"disable": "Disable",
"deploy": "Deploy",
"download": "Download",
"delete": "Delete",
"cancel": "Cancel",
"confirm": "Confirm",
"edit": "Edit",
"copy": "Copy",
"succeed": "Successful",
"add": "Add",
"document": "Document",
"variables": "Variables",
"dns": "Domain Name Server",
"name": "Name",
"timeout": "Time Out",
"not.added.yet.domain": "Domain not added yet.",
"not.added.yet.dns": "Nameserver not added yet.",
"create.time": "CreateTime",
"update.time": "UpdateTime",
"created.in": "Created in",
"updated.in": "Updated in",
"apply.setting": "Apply Settings",
"deploy.setting": "Deploy Settings",
"operation.succeed": "Operation Successful",
"save.succeed": "Save Successful",
"save.failed": "Save Failed",
"update.succeed": "Update Successful",
"update.failed": "Update Failed",
"delete.failed": "Delete Failed",
"ding.talk": "Ding Talk",
"telegram": "Telegram",
"webhook": "Webhook",
"local.deployment": "Local Deployment",
"tencent": "Tencent",
"tencent.cdn": "Tencent-CDN",
"aliyun": "Alibaba Cloud",
"aliyun.cdn": "Alibaba Cloud-CDN",
"aliyun.oss": "Alibaba Cloud-OSS",
"aliyun.dcdn": "Alibaba Cloud-DCDN",
"huaweicloud": "Huawei Cloud",
"qiniu": "Qiniu",
"qiniu.cdn": "Qiniu-CDN",
"cloudflare": "Cloudflare",
"namesilo": "Namesilo",
"go.daddy": "GoDaddy",
"ssh": "SSH Deployment",
"zod.rule.string.max": "Please enter no more than {{max}} characters",
"zod.rule.url": "Please enter a valid URL",
"zod.rule.ssh.host": "Please enter the correct domain name or IP",
"login.submit": "Log In",
"login.username.no.empty.message": "Please enter a valid email address",
"login.password.length.message": "Password should be at least 10 characters",
"menu.auth.management": "Authorization Management",
"theme.light": "Light",
"theme.dark": "Dark",
"theme.system": "System",
"dashboard": "Dashboard",
"dashboard.all": "All",
"dashboard.near.expired": "About to Expire",
"dashboard.enabled": "Enabled",
"dashboard.not.enabled": "Not Enabled",
"dashboard.unit": "Unit",
"deployment.log.name": "Deployment History",
"deployment.log.empty": "You have not created any deployments yet, please add a domain to start deployment!",
"deployment.log.status": "Status",
"deployment.log.stage": "Stage",
"deployment.log.last.execution.time": "Last Execution Time",
"deployment.log.detail.button.text": "Log",
"deployment.log.detail": "Deployment Details",
"pagination.next": "Next",
"pagination.prev": "Previous",
"domain": "Domain",
"domain.add": "Add Domain",
"domain.edit": "Edit Domain",
"domain.delete": "Delete Domain",
"domain.not.empty.verify.message": "Please enter domain",
"domain.management.name": "Domain List",
"domain.management.start.deploy.succeed.tips": "Deployment initiated, please check the deployment log later.",
"domain.management.execution.failed": "Execution Failed",
"domain.management.execution.failed.tips": "Execution failed, please check the details in <1>Deployment History</1>.",
"domain.management.empty": "Please add a domain to start deploying the certificate.",
"domain.management.expiry.date": "Validity Period",
"domain.management.expiry.date1": "Valid for {{date}} days",
"domain.management.expiry.date2": "Expiry on {{date}}",
"domain.management.last.execution.time": "Last Execution Time",
"domain.management.last.execution.status": "Last Execution Status",
"domain.management.last.execution.stage": "Last Execution Stage",
"domain.management.enable": "Enable",
"domain.management.start.deploying": "Deploy Now",
"domain.management.forced.deployment": "Force Deployment",
"domain.management.delete.confirm": "Are you sure you want to delete this domain?",
"domain.management.edit.title": "Edit Domain",
"domain.management.edit.dns.access.label": "DNS Provider Authorization Configuration",
"domain.management.edit.dns.access.not.empty.message": "Please select DNS provider authorization configuration",
"domain.management.edit.access.label": "Provider Authorization Configuration",
"domain.management.edit.access.not.empty.message": "Please select authorization configuration",
"domain.management.edit.target.type": "Deployment Service Type",
"domain.management.edit.target.type.not.empty.message": "Please select deployment service type",
"domain.management.edit.succeed.tips": "Successful domain editing",
"domain.management.edit.target.access": "Deployment Service Provider Authorization Configuration",
"domain.management.edit.target.access.content.label": "Provider Authorization Configuration",
"domain.management.edit.target.access.not.empty.message": "Please select authorization configuration",
"domain.management.edit.target.access.verify.msg": "At least one of the deployment authorization and deployment authorization group must be selected",
"domain.management.edit.group.label": "Deployment Configuration Group (used to deploy a domain certificate to multiple ssh hosts)",
"domain.management.edit.group.not.empty.message": "Please select group",
"domain.management.edit.email.not.empty.message": "Please select email",
"domain.management.edit.email.description": "(A email is required to apply for a certificate)",
"domain.management.edit.variables.placeholder": "It can be used in SSH deployment, like:\nkey=val;\nkey2=val2;",
"domain.management.edit.dns.placeholder": "Custom domain name server, separates multiple entries with semicolon, like:\n8.8.8.8;\n8.8.4.4;",
"domain.management.add.succeed.tips": "Domain added successfully",
"domain.management.edit.timeout.placeholder": "Timeout (seconds)",
"domain.management.edit.deploy.error": "Please save applyment configuration first",
"domain.management.enabled.failed": "Enable failed",
"domain.management.enabled.without.deployments": "Failed to enable, no deployment configuration found",
"email.add": "Add Email",
"email.list": "Email List",
"email.valid.message": "Please enter a valid email address",
"email.already.exist": "Email already exists",
"email.not.empty.message": "Please enter email",
"setting.notify.menu": "Notification Push",
"setting.submit": "Confirm Changes",
"setting.account.email.valid.message": "Please enter a valid email address",
"setting.account.email.placeholder": "Please enter email",
"setting.account.email.change.succeed": "Account email altered successfully",
"setting.account.email.change.failed": "Account email alteration failed",
"setting.account.log.back.in": "Please login again",
"setting.password.length.message": "Password should be at least 10 characters",
"setting.password.not.match": "Passwords do not match",
"setting.password.change.succeed": "Password changed successfully",
"setting.password.change.failed": "Password change failed",
"setting.password.current.password": "Current Password",
"setting.password.new.password": "New Password",
"setting.password.confirm.password": "Confirm Password",
"setting.notify.template.save.succeed": "Notification template saved successfully",
"setting.notify.template.variables.tips.title": "Optional variables, COUNT: number of expiring soon",
"setting.notify.template.variables.tips.content": "Optional variables, COUNT: number of expiring soon, DOMAINS: Domain list",
"setting.notify.config.enable": "Enable",
"setting.notify.config.save.succeed": "Configuration saved successfully",
"setting.notify.config.save.failed": "Configuration save failed",
"setting.notify.config.save.failed.url.not.valid": "Invalid Url format",
"setting.ca.not.empty": "Please select a Certificate Authority",
"setting.ca.eab_kid.not.empty": "Please enter EAB_KID",
"setting.ca.eab_hmac_key.not.empty": "Please enter EAB_HMAC_KEY.",
"setting.ca.eab_kid_hmac_key.not.empty": "Please enter EAB_KID and EAB_HMAC_KEY",
"deploy.progress.check": "Check",
"deploy.progress.apply": "Apply",
"deploy.progress.deploy": "Deploy",
"access.management": "Authorization Management",
"access.add": "Add Authorization",
"access.edit": "Edit Authorization",
"access.copy": "Copy Authorization",
"access.delete.confirm": "Are you sure you want to delete the deployment authorization?",
"access.all": "All Authorizations",
"access.list": "Authorization List",
"access.type": "Provider",
"access.type.not.empty": "Please select a provider",
"access.not.empty": "Please select a cloud provider",
"access.empty": "Please add authorization to start deploying certificate.",
"access.group.management": "Authorization Group Management",
"access.group.add": "Add Authorization Group",
"access.group.not.empty": "Please select a group",
"access.group.name": "Group Name",
"access.group.name.not.empty": "Please enter group name",
"access.group.delete": "Delete Group",
"access.group.delete.confirm": "Are you sure you want to delete the deployment authorization group?",
"access.group.domain.empty": "Please add a domain to start deploying the certificate.",
"access.group.empty": "No deployment authorization configuration yet, please add after starting use.",
"access.group.total": "Totally {{total}} deployment authorization configuration",
"access.form.name.not.empty": "Please enter authorization name",
"access.form.config.field": "Configuration Type",
"access.form.access.key.id": "AccessKeyId",
"access.form.access.key.id.not.empty": "Please enter AccessKeyId",
"access.form.access.key.secret": "AccessKeySecret",
"access.form.access.key.secret.not.empty": "Please enter AccessKeySecret",
"access.form.cloud.dns.api.token": "CLOUD_DNS_API_TOKEN",
"access.form.cloud.dns.api.token.not.empty": "Please enter CLOUD_DNS_API_TOKEN",
"access.form.go.daddy.api.key": "GO_DADDY_API_KEY",
"access.form.go.daddy.api.key.not.empty": "Please enter GO_DADDY_API_KEY",
"access.form.go.daddy.api.secret": "GO_DADDY_API_SECRET",
"access.form.go.daddy.api.secret.not.empty": "Please enter GO_DADDY_API_SECRET",
"access.form.namesilo.api.key": "NAMESILO_API_KEY",
"access.form.namesilo.api.key.not.empty": "Please enter NAMESILO_API_KEY",
"access.form.secret.id": "SecretId",
"access.form.secret.id.not.empty": "Please enter SecretId",
"access.form.secret.key": "SecretKey",
"access.form.secret.key.not.empty": "Please enter SecretKey",
"access.form.access.key": "AccessKey",
"access.form.access.key.not.empty": "Please enter AccessKey",
"access.form.region": "Region",
"access.form.region.not.empty": "Please enter Region",
"access.form.webhook.url": "Webhook URL",
"access.form.webhook.url.not.empty": "Please enter Webhook URL",
"access.form.ssh.group.label": "Authorization Configuration Group (used to deploy a single domain certificate to multiple SSH hosts)",
"access.form.ssh.host": "Server Host",
"access.form.ssh.host.not.empty": "Please enter Host",
"access.form.ssh.port": "SSH Port",
"access.form.ssh.port.not.empty": "Please enter Port",
"access.form.ssh.key": "Key (Log in using certificate)",
"access.form.ssh.key.not.empty": "Please enter Key",
"access.form.ssh.key.file.not.empty": "Please select file",
"access.form.ssh.cert.path": "Certificate Save Path",
"access.form.ssh.cert.path.not.empty": "Please enter certificate save path",
"access.form.ssh.key.path": "Private Key Save Path",
"access.form.ssh.key.path.not.empty": "Please enter private key save path",
"access.form.ssh.pre.command": "Pre-deployment Command",
"access.form.ssh.pre.command.not.empty": "Command to be executed before deploying the certificate",
"access.form.ssh.command": "Command",
"access.form.ssh.command.not.empty": "Please enter command",
"access.form.ding.access.token.placeholder": "Signature for signed addition",
"variable": "Variable",
"variable.name": "Name",
"variable.value": "Value",
"variable.not.added": "Variable not added yet",
"variable.name.required": "Variable name cannot be empty",
"variable.value.required": "Variable value cannot be empty",
"variable.name.placeholder": "Variable name",
"variable.value.placeholder": "Variable value",
"deployment": "Deployment",
"deployment.not.added": "Deployment not added yet",
"deployment.access.type": "Access Type",
"deployment.access.config": "Access Configuration",
"deployment.access.cdn.deploy.to.domain": "Deploy to domain",
"deployment.access.oss.bucket": "Bucket",
"deployment.access.oss.bucket.not.empty": "Please enter Bucket",
"deployment.access.oss.endpoint": "Endpoint"
}

View File

@@ -0,0 +1,17 @@
import nlsCommon from "./nls.common.json";
import nlsLogin from "./nls.login.json";
import nlsDashboard from "./nls.dashboard.json";
import nlsSettings from "./nls.settings.json";
import nlsDomain from "./nls.domain.json";
import nlsAccess from "./nls.access.json";
import nlsHistory from "./nls.history.json";
export default Object.freeze({
...nlsCommon,
...nlsLogin,
...nlsDashboard,
...nlsSettings,
...nlsDomain,
...nlsAccess,
...nlsHistory,
});

View File

@@ -0,0 +1,79 @@
{
"access.page.title": "Authorization Management",
"access.authorization.tab": "Authorization",
"access.authorization.nodata": "Please add authorization to start deploying certificate.",
"access.authorization.add": "Add Authorization",
"access.authorization.edit": "Edit Authorization",
"access.authorization.copy": "Copy Authorization",
"access.authorization.delete": "Delete Authorization",
"access.authorization.delete.confirm": "Are you sure you want to delete the deployment authorization?",
"access.authorization.form.type.label": "Provider",
"access.authorization.form.type.placeholder": "Please select a provider",
"access.authorization.form.type.list": "Authorization List",
"access.authorization.form.name.label": "Name",
"access.authorization.form.name.placeholder": "Please enter authorization name",
"access.authorization.form.config.label": "Configuration Type",
"access.authorization.form.region.label": "Region",
"access.authorization.form.region.placeholder": "Please enter Region",
"access.authorization.form.access_key_id.label": "AccessKeyId",
"access.authorization.form.access_key_id.placeholder": "Please enter AccessKeyId",
"access.authorization.form.access_key_secret.label": "AccessKeySecret",
"access.authorization.form.access_key_secret..placeholder": "Please enter AccessKeySecret",
"access.authorization.form.access_key.label": "AccessKey",
"access.authorization.form.access_key.placeholder": "Please enter AccessKey",
"access.authorization.form.secret_id.label": "SecretId",
"access.authorization.form.secret_id.placeholder": "Please enter SecretId",
"access.authorization.form.secret_key.label": "SecretKey",
"access.authorization.form.secret_key.placeholder": "Please enter SecretKey",
"access.authorization.form.cloud_dns_api_token.label": "CLOUD_DNS_API_TOKEN",
"access.authorization.form.cloud_dns_api_token.placeholder": "Please enter CLOUD_DNS_API_TOKEN",
"access.authorization.form.godaddy_api_key.label": "GO_DADDY_API_KEY",
"access.authorization.form.godaddy_api_key.placeholder": "Please enter GO_DADDY_API_KEY",
"access.authorization.form.godaddy_api_secret.label": "GO_DADDY_API_SECRET",
"access.authorization.form.godaddy_api_secret.placeholder": "Please enter GO_DADDY_API_SECRET",
"access.authorization.form.namesilo_api_key.label": "NAMESILO_API_KEY",
"access.authorization.form.namesilo_api_key.placeholder": "Please enter NAMESILO_API_KEY",
"access.authorization.form.username.label": "Username",
"access.authorization.form.username.placeholder": "Please enter username",
"access.authorization.form.password.label": "Password",
"access.authorization.form.password.placeholder": "Please enter password",
"access.authorization.form.access_group.placeholder": "Please select a group",
"access.authorization.form.ssh_group.label": "Authorization Configuration Group (used to deploy a single domain certificate to multiple SSH hosts)",
"access.authorization.form.ssh_host.label": "Server Host",
"access.authorization.form.ssh_host.placeholder": "Please enter Host",
"access.authorization.form.ssh_port.label": "SSH Port",
"access.authorization.form.ssh_port.placeholder": "Please enter Port",
"access.authorization.form.ssh_key.label": "Key (Log in using private key)",
"access.authorization.form.ssh_key.placeholder": "Please enter Key",
"access.authorization.form.ssh_key_file.placeholder": "Please select file",
"access.authorization.form.ssh_key_path.label": "Private Key Save Path",
"access.authorization.form.ssh_key_path.placeholder": "Please enter private key save path",
"access.authorization.form.ssh_cert_path.label": "Certificate Save Path",
"access.authorization.form.ssh_cert_path.placeholder": "Please enter certificate save path",
"access.authorization.form.ssh_pre_command.label": "Pre-deployment Command",
"access.authorization.form.ssh_pre_command.placeholder": "Command to be executed before deploying the certificate",
"access.authorization.form.ssh_command.label": "Command",
"access.authorization.form.ssh_command.placeholder": "Please enter command",
"access.authorization.form.webhook_url.label": "Webhook URL",
"access.authorization.form.webhook_url.placeholder": "Please enter Webhook URL",
"access.group.tab": "Authorization Group",
"access.group.nodata": "No deployment authorization configuration yet, please add after starting use.",
"access.group.total": "Totally {{total}} deployment authorization configuration",
"access.group.add": "Add Group",
"access.group.delete": "Delete Group",
"access.group.delete.confirm": "Are you sure you want to delete the deployment authorization group?",
"access.group.form.name.label": "Group Name",
"access.group.form.name.errmsg.empty": "Please enter group name",
"access.group.domains": "All Authorizations",
"access.group.domains.nodata": "Please add a domain to start deploying the certificate.",
"access.common.type.errmsg.empty": "Please select a provider"
}

View File

@@ -0,0 +1,72 @@
{
"common.save": "Save",
"common.save.succeeded.message": "Save Successful",
"common.save.failed.message": "Save Failed",
"common.add": "Add",
"common.edit": "Edit",
"common.copy": "Copy",
"common.download": "Download",
"common.delete": "Delete",
"common.delete.succeeded.message": "Delete Successful",
"common.delete.failed.message": "Delete Failed",
"common.next": "Next",
"common.confirm": "Confirm",
"common.cancel": "Cancel",
"common.submit": "Submit",
"common.update": "Update",
"common.update.succeeded.message": "Update Successful",
"common.update.failed.message": "Update Failed",
"common.text.domain": "Domain",
"common.text.domain.empty": "No Domain",
"common.text.ip": "IP Address",
"common.text.ip.empty": "No IP address",
"common.text.dns": "Domain Name Server",
"common.text.dns.empty": "No DNS",
"common.text.ca": "Certificate Authority",
"common.text.provider": "Provider",
"common.text.name": "Name",
"common.text.created_at": "Created At",
"common.text.updated_at": "Updated At",
"common.text.operations": "Operations",
"common.text.nodata": "No data available",
"common.menu.settings": "Settings",
"common.menu.logout": "Logout",
"common.menu.document": "Document",
"common.pagination.next": "Next",
"common.pagination.prev": "Previous",
"common.pagination.more": "More pages",
"common.theme.light": "Light",
"common.theme.dark": "Dark",
"common.theme.system": "System",
"common.errmsg.string_max": "Please enter no more than {{max}} characters",
"common.errmsg.email_invalid": "Please enter a valid email address",
"common.errmsg.email_empty": "Please enter email",
"common.errmsg.email_duplicate": "Email already exists",
"common.errmsg.domain_invalid": "Please enter domain",
"common.errmsg.host_invalid": "Please enter the correct domain name or IP",
"common.errmsg.ip_invalid": "Please enter IP",
"common.errmsg.url_invalid": "Please enter a valid URL",
"common.provider.aliyun": "Alibaba Cloud",
"common.provider.aliyun.cdn": "Alibaba Cloud-CDN",
"common.provider.aliyun.oss": "Alibaba Cloud-OSS",
"common.provider.aliyun.dcdn": "Alibaba Cloud-DCDN",
"common.provider.tencent": "Tencent",
"common.provider.tencent.cdn": "Tencent-CDN",
"common.provider.huaweicloud": "Huawei Cloud",
"common.provider.qiniu": "Qiniu",
"common.provider.qiniu.cdn": "Qiniu-CDN",
"common.provider.cloudflare": "Cloudflare",
"common.provider.namesilo": "Namesilo",
"common.provider.godaddy": "GoDaddy",
"common.provider.local": "Local Deployment",
"common.provider.ssh": "SSH Deployment",
"common.provider.webhook": "Webhook",
"common.provider.dingtalk": "DingTalk",
"common.provider.telegram": "Telegram"
}

View File

@@ -0,0 +1,11 @@
{
"dashboard.page.title": "Dashboard",
"dashboard.statistics.all": "All",
"dashboard.statistics.near_expired": "About to Expire",
"dashboard.statistics.enabled": "Enabled",
"dashboard.statistics.disabled": "Not Enabled",
"dashboard.statistics.unit": "",
"dashboard.history": "Deployment History"
}

View File

@@ -0,0 +1,65 @@
{
"domain.page.title": "Domain List",
"domain.nodata": "Please add a domain to start deploying the certificate.",
"domain.add": "Add Domain",
"domain.edit": "Edit Domain",
"domain.delete": "Delete Domain",
"domain.delete.confirm": "Are you sure you want to delete this domain?",
"domain.history": "Deployment History",
"domain.deploy": "Deploy Now",
"domain.deploy.started.message": "Deploy Started",
"domain.deploy.started.tips": "Deployment initiated, please check the deployment log later.",
"domain.deploy.failed.message": "Execution Failed",
"domain.deploy.failed.tips": "Execution failed, please check the details in <1>Deployment History</1>.",
"domain.deploy_forced": "Force Deployment",
"domain.props.expiry": "Validity Period",
"domain.props.expiry.date1": "Valid for {{date}} days",
"domain.props.expiry.date2": "Expiry on {{date}}",
"domain.props.last_execution_status": "Last Execution Status",
"domain.props.last_execution_stage": "Last Execution Stage",
"domain.props.last_execution_time": "Last Execution Time",
"domain.props.enable": "Enable",
"domain.props.enable.enabled": "Enable",
"domain.props.enable.disabled": "Disable",
"domain.application.tab": "Apply Settings",
"domain.application.form.domain.added.message": "Domain added successfully",
"domain.application.form.domain.changed.message": "Domain updated successfully",
"domain.application.form.email.label": "Email",
"domain.application.form.email.tips": "(A email is required to apply for a certificate)",
"domain.application.form.email.add": "Add Email",
"domain.application.form.email.list": "Email List",
"domain.application.form.email.errmsg.empty": "Please select email",
"domain.application.form.access.label": "DNS Provider Authorization Configuration",
"domain.application.form.access.placeholder": "Please select DNS provider authorization configuration",
"domain.application.form.access.errmsg.empty": "Please select DNS provider authorization configuration",
"domain.application.form.access.list": "Provider Authorization Configurations",
"domain.application.form.timeout.label": "Timeout",
"domain.application.form.timeoue.placeholder": "Timeout (seconds)",
"domain.application.unsaved.message": "Please save applyment configuration first",
"domain.deployment.tab": "Deploy Settings",
"domain.deployment.nodata": "Deployment not added yet",
"domain.deployment.form.type.label": "Deploy Method",
"domain.deployment.form.type.placeholder": "Please select deploy method",
"domain.deployment.form.type.list": "Deploy Method List",
"domain.deployment.form.access.label": "Access Configuration",
"domain.deployment.form.access.placeholder": "Please select provider authorization configuration",
"domain.deployment.form.access.list": "Provider Authorization Configurations",
"domain.deployment.form.cdn_domain.label": "Deploy to domain",
"domain.deployment.form.cdn_domain.placeholder": "Please enter CDN domain",
"domain.deployment.form.oss_endpoint.label": "Endpoint",
"domain.deployment.form.oss_bucket": "Bucket",
"domain.deployment.form.oss_bucket.placeholder": "Please enter Bucket",
"domain.deployment.form.variables.label": "Variable",
"domain.deployment.form.variables.key": "Name",
"domain.deployment.form.variables.value": "Value",
"domain.deployment.form.variables.empty": "Variable not added yet",
"domain.deployment.form.variables.key.required": "Variable name cannot be empty",
"domain.deployment.form.variables.value.required": "Variable value cannot be empty",
"domain.deployment.form.variables.key.placeholder": "Variable name",
"domain.deployment.form.variables.value.placeholder": "Variable value"
}

View File

@@ -0,0 +1,16 @@
{
"history.page.title": "Deployment",
"history.nodata": "You have not created any deployments yet, please add a domain to start deployment!",
"history.props.domain": "Domain",
"history.props.status": "Status",
"history.props.stage": "Stage",
"history.props.last_execution_time": "Last Execution Time",
"history.props.stage.progress.check": "Check",
"history.props.stage.progress.apply": "Apply",
"history.props.stage.progress.deploy": "Deploy",
"history.log": "Log"
}

View File

@@ -0,0 +1,9 @@
{
"login.username.label": "Username",
"login.username.placeholder": "Username/Email",
"login.username.errmsg.invalid": "Please enter a valid email address",
"login.password.label": "Password",
"login.password.placeholder": "Password",
"login.password.errmsg.invalid": "Password should be at least 10 characters",
"login.submit": "Log In"
}

View File

@@ -0,0 +1,41 @@
{
"settings.page.title": "Settings",
"settings.account.relogin.message": "Please login again",
"settings.account.tab": "Account",
"settings.account.email.label": "Email",
"settings.account.email.placeholder": "Please enter email",
"settings.account.email.errmsg.invalid": "Please enter a valid email address",
"settings.account.email.changed.message": "Account email altered successfully",
"settings.account.email.failed.message": "Account email alteration failed",
"settings.password.tab": "Password",
"settings.password.current_password.label": "Current Password",
"settings.password.current_password.placeholder": "Please enter the current password",
"settings.password.new_password.label": "New Password",
"settings.password.new_password.placeholder": "Please enter the new password",
"settings.password.confirm_password.label": "Confirm Password",
"settings.password.confirm_password.placeholder": "Please enter the new password again",
"settings.password.password.errmsg.length": "Password should be at least 10 characters",
"settings.password.password.errmsg.not_matched": "Passwords do not match",
"settings.password.changed.message": "Password changed successfully",
"settings.password.failed.message": "Password change failed",
"settings.notification.tab": "Notification",
"settings.notification.template.label": "Template",
"settings.notification.template.saved.message": "Notification template saved successfully",
"settings.notification.template.variables.tips.title": "Optional variables ({COUNT}: number of expiring soon)",
"settings.notification.template.variables.tips.content": "Optional variables ({COUNT}: number of expiring soon. {DOMAINS}: Domain list)",
"settings.notification.config.enable": "Enable",
"settings.notification.config.saved.message": "Configuration saved successfully",
"settings.notification.config.failed.message": "Configuration save failed",
"settings.notification.dingtalk.secret.placeholder": "Signature for signed addition",
"settings.notification.url.errmsg.invalid": "Invalid Url format",
"settings.ca.tab": "Certificate Authority",
"settings.ca.provider.errmsg.empty": "Please select a Certificate Authority",
"settings.ca.eab_kid.errmsg.empty": "Please enter EAB_KID",
"settings.ca.eab_hmac_key.errmsg.empty": "Please enter EAB_HMAC_KEY.",
"settings.ca.eab_kid_hmac_key.errmsg.empty": "Please enter EAB_KID and EAB_HMAC_KEY"
}

View File

@@ -1,17 +1,17 @@
import { Resource } from 'i18next'
import { Resource } from "i18next";
import zh from './zh.json'
import en from './en.json'
import zh from "./zh";
import en from "./en";
const resources: Resource = {
zh: {
name: '简体中文',
translation: zh
},
en: {
name: 'English',
translation: en
}
}
zh: {
name: "简体中文",
translation: zh,
},
en: {
name: "English",
translation: en,
},
};
export default resources;
export default resources;

View File

@@ -1,247 +0,0 @@
{
"ca": "证书颁发机构",
"username": "用户名",
"username.not.empty": "请输入用户名",
"password": "密码",
"password.not.empty": "请输入密码",
"ip.not.empty.verify.message": "请输入 IP",
"email": "邮箱",
"logout": "退出登录",
"setting": "设置",
"account": "账户",
"template": "模版",
"save": "保存",
"next": "下一步",
"no.data": "暂无数据",
"status": "状态",
"operation": "操作",
"enable": "启用",
"disable": "禁用",
"deploy": "部署",
"download": "下载",
"delete": "删除",
"cancel": "取消",
"confirm": "确认",
"edit": "编辑",
"copy": "复制",
"succeed": "成功",
"add": "新增",
"document": "文档",
"variables": "变量",
"dns": "域名服务器",
"name": "名称",
"timeout": "超时时间",
"not.added.yet.domain": "域名未添加",
"not.added.yet.dns": "域名服务器暂未添加",
"create.time": "创建时间",
"update.time": "更新时间",
"created.in": "创建于",
"updated.in": "更新于",
"apply.setting": "申请设置",
"deploy.setting": "部署设置",
"operation.succeed": "操作成功",
"save.succeed": "保存成功",
"save.failed": "保存失败",
"update.succeed": "修改成功",
"update.failed": "修改失败",
"delete.failed": "删除失败",
"ding.talk": "钉钉",
"telegram": "Telegram",
"webhook": "Webhook",
"local.deployment": "本地部署",
"tencent": "腾讯云",
"tencent.cdn": "腾讯云-CDN",
"aliyun": "阿里云",
"aliyun.cdn": "阿里云-CDN",
"aliyun.oss": "阿里云-OSS",
"aliyun.dcdn": "阿里云-DCDN",
"huaweicloud": "华为云",
"qiniu": "七牛云",
"qiniu.cdn": "七牛云-CDN",
"cloudflare": "Cloudflare",
"namesilo": "Namesilo",
"go.daddy": "GoDaddy",
"ssh": "SSH 部署",
"zod.rule.string.max": "请输入不超过 {{max}} 个字符",
"zod.rule.url": "请输入有效的 url 地址",
"zod.rule.ssh.host": "请输入正确的域名或IP",
"login.submit": "登录",
"login.username.no.empty.message": "请输入正确的邮箱地址",
"login.password.length.message": "密码至少10个字符",
"menu.auth.management": "授权管理",
"theme.light": "浅色",
"theme.dark": "暗黑",
"theme.system": "系统",
"dashboard": "控制面板",
"dashboard.all": "所有",
"dashboard.near.expired": "即将过期",
"dashboard.enabled": "启用中",
"dashboard.not.enabled": "未启用",
"dashboard.unit": "个",
"deployment.log.name": "部署历史",
"deployment.log.empty": "你暂未创建任何部署,请先添加域名进行部署吧!",
"deployment.log.status": "状态",
"deployment.log.stage": "阶段",
"deployment.log.last.execution.time": "最近执行时间",
"deployment.log.detail.button.text": "日志",
"deployment.log.detail": "部署详情",
"pagination.next": "下一页",
"pagination.prev": "上一页",
"domain": "域名",
"domain.add": "新增域名",
"domain.edit": "编辑域名",
"domain.delete": "删除域名",
"domain.not.empty.verify.message": "请输入域名",
"domain.management.name": "域名列表",
"domain.management.start.deploy.succeed.tips": "已发起部署,请稍后查看部署日志。",
"domain.management.execution.failed": "执行失败",
"domain.management.execution.failed.tips": "执行失败,请在 <1>部署历史</1> 查看详情。",
"domain.management.empty": "请添加域名开始部署证书吧。",
"domain.management.expiry.date": "有效期限",
"domain.management.expiry.date1": "有效期 {{date}} 天",
"domain.management.expiry.date2": "{{date}} 到期",
"domain.management.last.execution.time": "最近执行时间",
"domain.management.last.execution.status": "最近执行状态",
"domain.management.last.execution.stage": "最近执行阶段",
"domain.management.enable": "是否启用",
"domain.management.start.deploying": "立即部署",
"domain.management.forced.deployment": "强行部署",
"domain.management.delete.confirm": "确定要删除域名吗?",
"domain.management.edit.title": "编辑域名",
"domain.management.edit.dns.access.label": "DNS 服务商授权配置",
"domain.management.edit.dns.access.not.empty.message": "请选择DNS服务商授权配置",
"domain.management.edit.access.label": "服务商授权配置",
"domain.management.edit.access.not.empty.message": "请选择授权配置",
"domain.management.edit.target.type": "部署服务类型",
"domain.management.edit.target.type.not.empty.message": "请选择部署服务类型",
"domain.management.edit.succeed.tips": "域名编辑成功",
"domain.management.edit.target.access": "部署服务商授权配置",
"domain.management.edit.target.access.content.label": "服务商授权配置",
"domain.management.edit.target.access.not.empty.message": "请选择授权配置",
"domain.management.edit.target.access.verify.msg": "部署授权和部署授权组至少选一个",
"domain.management.edit.group.label": "部署配置组(用于将一个域名证书部署到多个 ssh 主机)",
"domain.management.edit.group.not.empty.message": "请选择分组",
"domain.management.edit.email.not.empty.message": "请选择邮箱",
"domain.management.edit.email.description": "(申请证书需要提供邮箱)",
"domain.management.edit.variables.placeholder": "可在SSH部署中使用,形如:\nkey=val;\nkey2=val2;",
"domain.management.edit.dns.placeholder": "自定义域名服务器,多个用分号隔开,如:\n8.8.8.8;\n8.8.4.4;",
"domain.management.add.succeed.tips": "域名添加成功",
"domain.management.edit.timeout.placeholder": "超时时间(单位:秒)",
"domain.management.edit.deploy.error": "请先保存申请配置",
"domain.management.enabled.failed": "启用失败",
"domain.management.enabled.without.deployments": "启用失败,请先设置部署配置",
"email.add": "添加邮箱",
"email.list": "邮箱列表",
"email.valid.message": "请输入正确的邮箱地址",
"email.already.exist": "邮箱已存在",
"email.not.empty.message": "请输入邮箱",
"setting.notify.menu": "消息推送",
"setting.submit": "确认修改",
"setting.account.email.valid.message": "请输入正确的邮箱地址",
"setting.account.email.placeholder": "请输入邮箱",
"setting.account.email.change.succeed": "修改账户邮箱成功",
"setting.account.email.change.failed": "修改账户邮箱失败",
"setting.account.log.back.in": "请重新登录",
"setting.password.length.message": "密码至少10个字符",
"setting.password.not.match": "两次密码不一致",
"setting.password.change.succeed": "修改密码成功",
"setting.password.change.failed": "修改密码失败",
"setting.password.current.password": "当前密码",
"setting.password.new.password": "新密码",
"setting.password.confirm.password": "确认密码",
"setting.notify.template.save.succeed": "通知模板保存成功",
"setting.notify.template.variables.tips.title": "可选的变量, COUNT:即将过期张数",
"setting.notify.template.variables.tips.content": "可选的变量, COUNT:即将过期张数DOMAINS:域名列表",
"setting.notify.config.enable": "是否启用",
"setting.notify.config.save.succeed": "配置保存成功",
"setting.notify.config.save.failed": "配置保存失败",
"setting.notify.config.save.failed.url.not.valid": "Url格式不正确",
"setting.ca.not.empty": "请选择证书分发机构",
"setting.ca.eab_kid.not.empty": "请输入EAB_KID",
"setting.ca.eab_hmac_key.not.empty": "请输入EAB_HMAC_KEY",
"setting.ca.eab_kid_hmac_key.not.empty": "请输入EAB_KID和EAB_HMAC_KEY",
"deploy.progress.check": "检查",
"deploy.progress.apply": "获取",
"deploy.progress.deploy": "部署",
"access.management": "授权管理",
"access.add": "添加授权",
"access.edit": "编辑授权",
"access.copy": "复制授权",
"access.delete.confirm": "确定要删除授权吗?",
"access.all": "所有授权",
"access.list": "授权列表",
"access.type": "服务商",
"access.type.not.empty": "请选择服务商",
"access.not.empty": "请选择云服务商",
"access.empty": "请添加授权开始部署证书吧。",
"access.group.management": "授权组管理",
"access.group.add": "添加授权组",
"access.group.not.empty": "请选择分组",
"access.group.name": "组名",
"access.group.name.not.empty": "请输入组名",
"access.group.delete": "删除组",
"access.group.delete.confirm": "确定要删除部署授权组吗?",
"access.group.domain.empty": "请添加域名开始部署证书吧。",
"access.group.empty": "暂无部署授权配置,请添加后开始使用吧",
"access.group.total": "共有 {{total}} 个部署授权配置",
"access.form.name.not.empty": "请输入授权名称",
"access.form.config.field": "配置类型",
"access.form.access.key.id": "AccessKeyId",
"access.form.access.key.id.not.empty": "请输入 AccessKeyId",
"access.form.access.key.secret": "AccessKeySecret",
"access.form.access.key.secret.not.empty": "请输入 AccessKeySecret",
"access.form.cloud.dns.api.token": "CLOUD_DNS_API_TOKEN",
"access.form.cloud.dns.api.token.not.empty": "请输入 CLOUD_DNS_API_TOKEN",
"access.form.go.daddy.api.key": "GO_DADDY_API_KEY",
"access.form.go.daddy.api.key.not.empty": "请输入 GO_DADDY_API_KEY",
"access.form.go.daddy.api.secret": "GO_DADDY_API_SECRET",
"access.form.go.daddy.api.secret.not.empty": "请输入 GO_DADDY_API_SECRET",
"access.form.namesilo.api.key": "NAMESILO_API_KEY",
"access.form.namesilo.api.key.not.empty": "请输入 NAMESILO_API_KEY",
"access.form.secret.id": "SecretId",
"access.form.secret.id.not.empty": "请输入 SecretId",
"access.form.secret.key": "SecretKey",
"access.form.secret.key.not.empty": "请输入 SecretKey",
"access.form.access.key": "AccessKey",
"access.form.access.key.not.empty": "请输入 AccessKey",
"access.form.region": "Region",
"access.form.region.not.empty": "请输入区域",
"access.form.webhook.url": "Webhook URL",
"access.form.webhook.url.not.empty": "请输入 Webhook URL",
"access.form.ssh.group.label": "授权配置组(用于将一个域名证书部署到多个 ssh 主机)",
"access.form.ssh.host": "服务器 Host",
"access.form.ssh.host.not.empty": "请输入 Host",
"access.form.ssh.port": "SSH 端口",
"access.form.ssh.port.not.empty": "请输入 Port",
"access.form.ssh.key": "Key使用证书登录",
"access.form.ssh.key.not.empty": "请输入 Key",
"access.form.ssh.key.file.not.empty": "请选择文件",
"access.form.ssh.cert.path": "证书保存路径",
"access.form.ssh.cert.path.not.empty": "请输入证书保存路径",
"access.form.ssh.key.path": "私钥保存路径",
"access.form.ssh.key.path.not.empty": "请输入私钥保存路径",
"access.form.ssh.pre.command": "前置 Command",
"access.form.ssh.pre.command.not.empty": "在部署证书前执行的前置命令",
"access.form.ssh.command": "Command",
"access.form.ssh.command.not.empty": "请输入要执行的命令",
"access.form.ding.access.token.placeholder": "加签的签名",
"variable": "变量",
"variable.name": "变量名",
"variable.value": "值",
"variable.not.added": "尚未添加变量",
"variable.name.required": "变量名不能为空",
"variable.value.required": "变量值不能为空",
"variable.name.placeholder": "请输入变量名",
"variable.value.placeholder": "请输入变量值",
"deployment": "部署",
"deployment.not.added": "暂无部署配置,请添加后开始部署证书吧",
"deployment.access.type": "授权类型",
"deployment.access.config": "授权配置",
"deployment.access.cdn.deploy.to.domain": "部署到域名",
"deployment.access.oss.bucket": "Bucket",
"deployment.access.oss.bucket.not.empty": "请输入 Bucket",
"deployment.access.oss.endpoint": "Endpoint"
}

View File

@@ -0,0 +1,17 @@
import nlsCommon from "./nls.common.json";
import nlsLogin from "./nls.login.json";
import nlsDashboard from "./nls.dashboard.json";
import nlsSettings from "./nls.settings.json";
import nlsDomain from "./nls.domain.json";
import nlsAccess from "./nls.access.json";
import nlsHistory from "./nls.history.json";
export default Object.freeze({
...nlsCommon,
...nlsLogin,
...nlsDashboard,
...nlsSettings,
...nlsDomain,
...nlsAccess,
...nlsHistory,
});

View File

@@ -0,0 +1,80 @@
{
"access.page.title": "授权管理",
"access.authorization.tab": "授权",
"access.authorization.nodata": "请添加授权开始部署证书吧。",
"access.authorization.add": "新增授权",
"access.authorization.edit": "编辑授权",
"access.authorization.copy": "复制授权",
"access.authorization.delete": "删除授权",
"access.authorization.delete.confirm": "确定要删除授权吗?",
"access.authorization.form.type.label": "服务商",
"access.authorization.form.type.placeholder": "请选择服务商",
"access.authorization.form.type.list": "服务商列表",
"access.authorization.form.name.label": "名称",
"access.authorization.form.name.placeholder": "请输入授权名称",
"access.authorization.form.config.label": "配置类型",
"access.authorization.form.region.label": "Region",
"access.authorization.form.region.placeholder": "请输入区域",
"access.authorization.form.access_key_id.label": "AccessKeyId",
"access.authorization.form.access_key_id.placeholder": "请输入 AccessKeyId",
"access.authorization.form.access_key_secret.label": "AccessKeySecret",
"access.authorization.form.access_key_secret..placeholder": "请输入 AccessKeySecret",
"access.authorization.form.access_key.label": "AccessKey",
"access.authorization.form.access_key.placeholder": "请输入 AccessKey",
"access.authorization.form.secret_id.label": "SecretId",
"access.authorization.form.secret_id.placeholder": "请输入 SecretId",
"access.authorization.form.secret_key.label": "SecretKey",
"access.authorization.form.secret_key.placeholder": "请输入 SecretKey",
"access.authorization.form.cloud_dns_api_token.label": "CLOUD_DNS_API_TOKEN",
"access.authorization.form.cloud_dns_api_token.placeholder": "请输入 CLOUD_DNS_API_TOKEN",
"access.authorization.form.godaddy_api_key.label": "GO_DADDY_API_KEY",
"access.authorization.form.godaddy_api_key.placeholder": "请输入 GO_DADDY_API_KEY",
"access.authorization.form.godaddy_api_secret.label": "GO_DADDY_API_SECRET",
"access.authorization.form.godaddy_api_secret.placeholder": "请输入 GO_DADDY_API_SECRET",
"access.authorization.form.namesilo_api_key.label": "NAMESILO_API_KEY",
"access.authorization.form.namesilo_api_key.placeholder": "请输入 NAMESILO_API_KEY",
"access.authorization.form.username.label": "用户名",
"access.authorization.form.username.placeholder": "请输入用户名",
"access.authorization.form.password.label": "密码",
"access.authorization.form.password.placeholder": "请输入密码",
"access.authorization.form.access_group.placeholder": "请选择分组",
"access.authorization.form.ssh_group.label": "授权配置组(用于将一个域名证书部署到多个 SSH 主机)",
"access.authorization.form.ssh_host.label": "服务器 Host",
"access.authorization.form.ssh_host.placeholder": "请输入 Host",
"access.authorization.form.ssh_port.label": "SSH 端口",
"access.authorization.form.ssh_port.placeholder": "请输入 Port",
"access.authorization.form.ssh_key.label": "Key使用私钥登录",
"access.authorization.form.ssh_key.placeholder": "请输入 Key",
"access.authorization.form.ssh_key_file.placeholder": "请选择文件",
"access.authorization.form.ssh_key.label.passphrase": "私钥密码",
"access.authorization.form.ssh_key_path.label": "私钥保存路径",
"access.authorization.form.ssh_key_path.placeholder": "请输入私钥保存路径",
"access.authorization.form.ssh_cert_path.label": "证书保存路径",
"access.authorization.form.ssh_cert_path.placeholder": "请输入证书保存路径",
"access.authorization.form.ssh_pre_command.label": "前置 Command",
"access.authorization.form.ssh_pre_command.placeholder": "在部署证书前执行的前置命令",
"access.authorization.form.ssh_command.label": "Command",
"access.authorization.form.ssh_command.placeholder": "请输入要执行的命令",
"access.authorization.form.webhook_url.label": "Webhook URL",
"access.authorization.form.webhook_url.placeholder": "请输入 Webhook URL",
"access.group.tab": "授权组",
"access.group.nodata": "暂无部署授权配置,请添加后开始使用吧",
"access.group.total": "共有 {{total}} 个部署授权配置",
"access.group.add": "添加授权组",
"access.group.delete": "删除组",
"access.group.delete.confirm": "确定要删除部署授权组吗?",
"access.group.form.name.label": "组名",
"access.group.form.name.errmsg.empty": "请输入组名",
"access.group.domains": "所有授权",
"access.group.domains.nodata": "请添加域名开始部署证书吧。",
"access.common.type.errmsg.empty": "请选择服务商"
}

View File

@@ -0,0 +1,72 @@
{
"common.add": "新增",
"common.save": "保存",
"common.save.succeeded.message": "保存成功",
"common.save.failed.message": "保存失败",
"common.edit": "编辑",
"common.copy": "复制",
"common.download": "下载",
"common.delete": "刪除",
"common.delete.succeeded.message": "删除成功",
"common.delete.failed.message": "删除失败",
"common.next": "下一步",
"common.confirm": "确认",
"common.cancel": "取消",
"common.submit": "提交",
"common.update": "更新",
"common.update.succeeded.message": "修改成功",
"common.update.failed.message": "修改失败",
"common.text.domain": "域名",
"common.text.domain.empty": "无域名",
"common.text.ip": "IP 地址",
"common.text.ip.empty": "无 IP 地址",
"common.text.dns": "DNS域名服务器",
"common.text.dns.empty": "无 DNS 地址",
"common.text.ca": "CA证书颁发机构",
"common.text.name": "名称",
"common.text.provider": "服务商",
"common.text.created_at": "创建时间",
"common.text.updated_at": "更新时间",
"common.text.operations": "操作",
"common.text.nodata": "暂无数据",
"common.menu.settings": "系统设置",
"common.menu.logout": "退出登录",
"common.menu.document": "文档",
"common.pagination.next": "下一页",
"common.pagination.prev": "上一页",
"common.pagination.more": "更多",
"common.theme.light": "浅色",
"common.theme.dark": "暗黑",
"common.theme.system": "跟随系统",
"common.errmsg.string_max": "请输入不超过 {{max}} 个字符",
"common.errmsg.email_empty": "请输入邮箱",
"common.errmsg.email_invalid": "请输入正确的邮箱",
"common.errmsg.email_duplicate": "邮箱已存在",
"common.errmsg.domain_invalid": "请输入正确的域名",
"common.errmsg.host_invalid": "请输入正确的域名或 IP 地址",
"common.errmsg.ip_invalid": "请输入正确的 IP 地址",
"common.errmsg.url_invalid": "请输入正确的 URL",
"common.provider.tencent": "腾讯云",
"common.provider.tencent.cdn": "腾讯云-CDN",
"common.provider.aliyun": "阿里云",
"common.provider.aliyun.cdn": "阿里云-CDN",
"common.provider.aliyun.oss": "阿里云-OSS",
"common.provider.aliyun.dcdn": "阿里云-DCDN",
"common.provider.huaweicloud": "华为云",
"common.provider.qiniu": "七牛云",
"common.provider.qiniu.cdn": "七牛云-CDN",
"common.provider.cloudflare": "Cloudflare",
"common.provider.namesilo": "Namesilo",
"common.provider.godaddy": "GoDaddy",
"common.provider.local": "本地部署",
"common.provider.ssh": "SSH 部署",
"common.provider.webhook": "Webhook",
"common.provider.dingtalk": "钉钉",
"common.provider.telegram": "Telegram"
}

View File

@@ -0,0 +1,11 @@
{
"dashboard.page.title": "仪表盘",
"dashboard.statistics.all": "所有",
"dashboard.statistics.near_expired": "即将过期",
"dashboard.statistics.enabled": "启用中",
"dashboard.statistics.disabled": "未启用",
"dashboard.statistics.unit": "个",
"dashboard.history": "部署历史"
}

View File

@@ -0,0 +1,65 @@
{
"domain.page.title": "域名列表",
"domain.nodata": "请添加域名开始部署证书吧。",
"domain.add": "新增域名",
"domain.edit": "编辑域名",
"domain.delete": "删除域名",
"domain.delete.confirm": "确定要删除域名吗?",
"domain.history": "部署历史",
"domain.deploy": "立即部署",
"domain.deploy.started.message": "开始部署",
"domain.deploy.started.tips": "已发起部署,请稍后查看部署日志。",
"domain.deploy.failed.message": "执行失败",
"domain.deploy.failed.tips": "执行失败,请在 <1>部署历史</1> 查看详情。",
"domain.deploy_forced": "强行部署",
"domain.props.expiry": "有效期限",
"domain.props.expiry.date1": "有效期 {{date}} 天",
"domain.props.expiry.date2": "{{date}} 到期",
"domain.props.last_execution_status": "最近执行状态",
"domain.props.last_execution_stage": "最近执行阶段",
"domain.props.last_execution_time": "最近执行时间",
"domain.props.enable": "是否启用",
"domain.props.enable.enabled": "启用",
"domain.props.enable.disabled": "禁用",
"domain.application.tab": "申请配置",
"domain.application.form.domain.added.message": "域名添加成功",
"domain.application.form.domain.changed.message": "域名编辑成功",
"domain.application.form.email.label": "邮箱",
"domain.application.form.email.tips": "(申请证书需要提供邮箱)",
"domain.application.form.email.add": "添加邮箱",
"domain.application.form.email.list": "邮箱列表",
"domain.application.form.email.errmsg.empty": "请选择邮箱",
"domain.application.form.access.label": "DNS 服务商授权配置",
"domain.application.form.access.placeholder": "请选择 DNS 服务商授权配置",
"domain.application.form.access.errmsg.empty": "请选择 DNS 服务商授权配置",
"domain.application.form.access.list": "已有的 DNS 服务商授权配置",
"domain.application.form.timeout.label": "超时时间",
"domain.application.form.timeoue.placeholder": "超时时间(单位:秒)",
"domain.application.unsaved.message": "请先保存申请配置",
"domain.deployment.tab": "部署配置",
"domain.deployment.nodata": "暂无部署配置,请添加后开始部署证书吧",
"domain.deployment.form.type.label": "部署方式",
"domain.deployment.form.type.placeholder": "请选择部署方式",
"domain.deployment.form.type.list": "支持的部署方式",
"domain.deployment.form.access.label": "授权配置",
"domain.deployment.form.access.placeholder": "请选择授权配置",
"domain.deployment.form.access.list": "已有的服务商授权配置",
"domain.deployment.form.cdn_domain.label": "部署到域名",
"domain.deployment.form.cdn_domain.placeholder": "请输入 CDN 域名",
"domain.deployment.form.oss_endpoint.label": "Endpoint",
"domain.deployment.form.oss_bucket": "存储桶",
"domain.deployment.form.oss_bucket.placeholder": "请输入存储桶名",
"domain.deployment.form.variables.label": "变量",
"domain.deployment.form.variables.key": "变量名",
"domain.deployment.form.variables.value": "值",
"domain.deployment.form.variables.empty": "尚未添加变量",
"domain.deployment.form.variables.key.required": "变量名不能为空",
"domain.deployment.form.variables.value.required": "变量值不能为空",
"domain.deployment.form.variables.key.placeholder": "请输入变量名",
"domain.deployment.form.variables.value.placeholder": "请输入变量值"
}

View File

@@ -0,0 +1,15 @@
{
"history.page.title": "部署",
"history.nodata": "你暂未创建任何部署,请先添加域名进行部署吧!",
"history.props.domain": "域名",
"history.props.status": "状态",
"history.props.stage": "阶段",
"history.props.stage.progress.check": "检查",
"history.props.stage.progress.apply": "获取",
"history.props.stage.progress.deploy": "部署",
"history.props.last_execution_time": "最近执行时间",
"history.log": "日志"
}

View File

@@ -0,0 +1,9 @@
{
"login.username.label": "用户名",
"login.username.placeholder": "请输入用户名/邮箱",
"login.username.errmsg.invalid": "请输入正确的用户名/邮箱",
"login.password.label": "密码",
"login.password.placeholder": "请输入密码",
"login.password.errmsg.invalid": "密码至少 10 个字符",
"login.submit": "登录"
}

View File

@@ -0,0 +1,41 @@
{
"settings.page.title": "系统设置",
"settings.account.relogin.message": "请重新登录",
"settings.account.tab": "账号",
"settings.account.email.label": "登录邮箱",
"settings.account.email.errmsg.invalid": "请输入正确的邮箱地址",
"settings.account.email.placeholder": "请输入邮箱",
"settings.account.email.changed.message": "修改账户邮箱成功",
"settings.account.email.failed.message": "修改账户邮箱失败",
"settings.password.tab": "密码",
"settings.password.password.errmsg.length": "密码至少10个字符",
"settings.password.password.errmsg.not_matched": "两次密码不一致",
"settings.password.current_password.label": "当前密码",
"settings.password.current_password.placeholder": "请输入旧密码",
"settings.password.new_password.label": "新密码",
"settings.password.new_password.placeholder": "请输入新密码",
"settings.password.confirm_password.label": "确认密码",
"settings.password.confirm_password.placeholder": "请再次输入新密码",
"settings.password.changed.message": "修改密码成功",
"settings.password.failed.message": "修改密码失败",
"settings.notification.tab": "消息推送",
"settings.notification.template.label": "内容模板",
"settings.notification.template.saved.message": "通知模板保存成功",
"settings.notification.template.variables.tips.title": "可选的变量({COUNT}: 即将过期张数)",
"settings.notification.template.variables.tips.content": "可选的变量({COUNT}: 即将过期张数;{DOMAINS}: 域名列表)",
"settings.notification.config.enable": "是否启用",
"settings.notification.config.saved.message": "配置保存成功",
"settings.notification.config.failed.message": "配置保存失败",
"settings.notification.dingtalk.secret.placeholder": "加签的签名",
"settings.notification.url.errmsg.invalid": "URL 格式不正确",
"settings.ca.tab": "证书颁发机构CA",
"settings.ca.provider.errmsg.empty": "请选择证书分发机构",
"settings.ca.eab_kid.errmsg.empty": "请输入EAB_KID",
"settings.ca.eab_hmac_key.errmsg.empty": "请输入EAB_HMAC_KEY",
"settings.ca.eab_kid_hmac_key.errmsg.empty": "请输入EAB_KID和EAB_HMAC_KEY"
}

View File

@@ -14,8 +14,6 @@ import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet";
@@ -30,7 +28,7 @@ import Version from "@/components/certimate/Version";
export default function Dashboard() {
const navigate = useNavigate();
const location = useLocation();
const { t } = useTranslation()
const { t } = useTranslation();
if (!getPb().authStore.isValid || !getPb().authStore.isAdmin) {
return <Navigate to="/login" />;
@@ -73,7 +71,7 @@ export default function Dashboard() {
)}
>
<Home className="h-4 w-4" />
{t('dashboard')}
{t("dashboard.page.title")}
</Link>
<Link
to="/domains"
@@ -83,7 +81,7 @@ export default function Dashboard() {
)}
>
<Earth className="h-4 w-4" />
{t('domain.management.name')}
{t("domain.page.title")}
</Link>
<Link
to="/access"
@@ -93,7 +91,7 @@ export default function Dashboard() {
)}
>
<Server className="h-4 w-4" />
{t('menu.auth.management')}
{t("access.page.title")}
</Link>
<Link
@@ -104,7 +102,7 @@ export default function Dashboard() {
)}
>
<History className="h-4 w-4" />
{t('deployment.log.name')}
{t("history.page.title")}
</Link>
</nav>
</div>
@@ -141,7 +139,7 @@ export default function Dashboard() {
)}
>
<Home className="h-5 w-5" />
{t('dashboard')}
{t("dashboard.page.title")}
</Link>
<Link
to="/domains"
@@ -151,7 +149,7 @@ export default function Dashboard() {
)}
>
<Earth className="h-5 w-5" />
{t('domain.management.name')}
{t("domain.page.title")}
</Link>
<Link
to="/access"
@@ -161,7 +159,7 @@ export default function Dashboard() {
)}
>
<Server className="h-5 w-5" />
{t('menu.auth.management')}
{t("access.page.title")}
</Link>
<Link
@@ -172,7 +170,7 @@ export default function Dashboard() {
)}
>
<History className="h-5 w-5" />
{t('deployment.log.name')}
{t("history.page.title")}
</Link>
</nav>
</SheetContent>
@@ -192,15 +190,11 @@ export default function Dashboard() {
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>{t('account')}</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={handleSettingClick}>
{t('setting')}
{t("common.menu.settings")}
</DropdownMenuItem>
<DropdownMenuItem onClick={handleLogoutClick}>
{t('logout')}
{t("common.menu.logout")}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>

View File

@@ -22,7 +22,7 @@ const SettingLayout = () => {
<div>
<Toaster />
<div className="text-muted-foreground border-b dark:border-stone-500 py-5">
{t("setting")}
{t("settings.page.title")}
</div>
<div className="w-full mt-5 p-0 md:p-3 flex justify-center">
<Tabs defaultValue="account" className="w-full" value={tabValue}>
@@ -35,8 +35,9 @@ const SettingLayout = () => {
className="px-5"
>
<UserRound size={14} />
<div className="ml-1">{t("account")}</div>
<div className="ml-1">{t("settings.account.tab")}</div>
</TabsTrigger>
<TabsTrigger
value="password"
onClick={() => {
@@ -45,7 +46,7 @@ const SettingLayout = () => {
className="px-5"
>
<KeyRound size={14} />
<div className="ml-1">{t("password")}</div>
<div className="ml-1">{t("settings.password.tab")}</div>
</TabsTrigger>
<TabsTrigger
@@ -56,8 +57,9 @@ const SettingLayout = () => {
className="px-5"
>
<Megaphone size={14} />
<div className="ml-1">{t("setting.notify.menu")}</div>
<div className="ml-1">{t("settings.notification.tab")}</div>
</TabsTrigger>
<TabsTrigger
value="ssl-provider"
onClick={() => {
@@ -66,7 +68,7 @@ const SettingLayout = () => {
className="px-5"
>
<ShieldCheck size={14} />
<div className="ml-1">{t("ca")}</div>
<div className="ml-1">{t("settings.ca.tab")}</div>
</TabsTrigger>
</TabsList>
<TabsContent value={tabValue}>

View File

@@ -16,10 +16,12 @@ import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent, AlertDialogDescription, AlertDialogFooter,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger
AlertDialogTrigger,
} from "@/components/ui/alert-dialog.tsx";
const Access = () => {
@@ -56,9 +58,9 @@ const Access = () => {
return (
<div className="">
<div className="flex justify-between items-center">
<div className="text-muted-foreground">{t("access.management")}</div>
<div className="text-muted-foreground">{t("access.page.title")}</div>
{tab != "access_group" ? (
<AccessEdit trigger={<Button>{t("access.add")}</Button>} op="add" />
<AccessEdit trigger={<Button>{t("access.authorization.add")}</Button>} op="add" />
) : (
<AccessGroupEdit trigger={<Button>{t("access.group.add")}</Button>} />
)}
@@ -76,7 +78,7 @@ const Access = () => {
handleTabItemClick("access");
}}
>
{t("access.management")}
{t("access.authorization.tab")}
</TabsTrigger>
<TabsTrigger
value="access_group"
@@ -84,7 +86,7 @@ const Access = () => {
handleTabItemClick("access_group");
}}
>
{t("access.group.management")}
{t("access.group.tab")}
</TabsTrigger>
</TabsList>
<TabsContent value="access">
@@ -95,10 +97,10 @@ const Access = () => {
</span>
<div className="text-center text-sm text-muted-foreground mt-3">
{t("access.empty")}
{t("access.authorization.nodata")}
</div>
<AccessEdit
trigger={<Button>{t("access.add")}</Button>}
trigger={<Button>{t("access.authorization.add")}</Button>}
op="add"
className="mt-3"
/>
@@ -106,15 +108,12 @@ const Access = () => {
) : (
<>
<div className="hidden sm:flex sm:flex-row text-muted-foreground text-sm border-b dark:border-stone-500 sm:p-2 mt-5">
<div className="w-48">{t("name")}</div>
<div className="w-48">{t("access.type")}</div>
<div className="w-48">{t("common.text.name")}</div>
<div className="w-48">{t("common.text.provider")}</div>
<div className="w-60">{t("create.time")}</div>
<div className="w-60">{t("update.time")}</div>
<div className="grow">{t("operation")}</div>
</div>
<div className="sm:hidden flex text-sm text-muted-foreground">
{t("access.list")}
<div className="w-60">{t("common.text.created_at")}</div>
<div className="w-60">{t("common.text.updated_at")}</div>
<div className="grow">{t("common.text.operations")}</div>
</div>
{accesses
.filter((item) => {
@@ -140,18 +139,16 @@ const Access = () => {
</div>
<div className="sm:w-60 w-full pt-1 sm:pt-0 flex items-center">
{t("created.in")}{" "}
{access.created && convertZulu2Beijing(access.created)}
</div>
<div className="sm:w-60 w-full pt-1 sm:pt-0 flex items-center">
{t("updated.in")}{" "}
{access.updated && convertZulu2Beijing(access.updated)}
</div>
<div className="flex items-center grow justify-start pt-1 sm:pt-0">
<AccessEdit
trigger={
<Button variant={"link"} className="p-0">
{t("edit")}
{t("common.edit")}
</Button>
}
op="edit"
@@ -159,40 +156,40 @@ const Access = () => {
/>
<Separator orientation="vertical" className="h-4 mx-2" />
<AccessEdit
trigger={
<Button variant={"link"} className="p-0">
{t("copy")}
</Button>
}
op="copy"
data={access}
trigger={
<Button variant={"link"} className="p-0">
{t("common.copy")}
</Button>
}
op="copy"
data={access}
/>
<Separator orientation="vertical" className="h-4 mx-2" />
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant={"link"} size={"sm"}>
{t('delete')}
<Button variant={"link"} className="p-0">
{t("common.delete")}
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle className="dark:text-gray-200">
{t('access.group.delete')}
{t("access.authorization.delete")}
</AlertDialogTitle>
<AlertDialogDescription>
{t('access.delete.confirm')}
{t("access.authorization.delete.confirm")}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel className="dark:text-gray-200">
{t('cancel')}
{t("common.cancel")}
</AlertDialogCancel>
<AlertDialogAction
onClick={() => {
handleDelete(access);
}}
onClick={() => {
handleDelete(access);
}}
>
{t('confirm')}
{t("common.confirm")}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>

View File

@@ -57,7 +57,7 @@ const Dashboard = () => {
return (
<div className="flex flex-col">
<div className="flex justify-between items-center">
<div className="text-muted-foreground">{t("dashboard")}</div>
<div className="text-muted-foreground">{t("dashboard.page.title")}</div>
</div>
<div className="flex mt-10 gap-5 flex-col flex-wrap md:flex-row">
<div className="w-full md:w-[250px] 3xl:w-[300px] flex items-center rounded-md p-3 shadow-lg border">
@@ -66,7 +66,7 @@ const Dashboard = () => {
</div>
<div>
<div className="text-muted-foreground font-semibold">
{t("dashboard.all")}
{t("dashboard.statistics.all")}
</div>
<div className="flex items-baseline">
<div className="text-3xl text-stone-700 dark:text-stone-200">
@@ -79,7 +79,7 @@ const Dashboard = () => {
)}
</div>
<div className="ml-1 text-stone-700 dark:text-stone-200">
{t("dashboard.unit")}
{t("dashboard.statistics.unit")}
</div>
</div>
</div>
@@ -91,7 +91,7 @@ const Dashboard = () => {
</div>
<div>
<div className="text-muted-foreground font-semibold">
{t("dashboard.near.expired")}
{t("dashboard.statistics.near_expired")}
</div>
<div className="flex items-baseline">
<div className="text-3xl text-stone-700 dark:text-stone-200">
@@ -104,7 +104,7 @@ const Dashboard = () => {
)}
</div>
<div className="ml-1 text-stone-700 dark:text-stone-200">
{t("dashboard.unit")}
{t("dashboard.statistics.unit")}
</div>
</div>
</div>
@@ -120,7 +120,7 @@ const Dashboard = () => {
</div>
<div>
<div className="text-muted-foreground font-semibold">
{t("dashboard.enabled")}
{t("dashboard.statistics.enabled")}
</div>
<div className="flex items-baseline">
<div className="text-3xl text-stone-700 dark:text-stone-200">
@@ -133,7 +133,7 @@ const Dashboard = () => {
)}
</div>
<div className="ml-1 text-stone-700 dark:text-stone-200">
{t("dashboard.unit")}
{t("dashboard.statistics.unit")}
</div>
</div>
</div>
@@ -145,7 +145,7 @@ const Dashboard = () => {
</div>
<div>
<div className="text-muted-foreground font-semibold">
{t("dashboard.not.enabled")}
{t("dashboard.statistics.disabled")}
</div>
<div className="flex items-baseline">
<div className="text-3xl text-stone-700 dark:text-stone-200">
@@ -161,28 +161,32 @@ const Dashboard = () => {
)}
</div>
<div className="ml-1 text-stone-700 dark:text-stone-200">
{t("dashboard.unit")}
{t("dashboard.statistics.unit")}
</div>
</div>
</div>
</div>
</div>
<div className="my-4">
<hr />
</div>
<div>
<div className="text-muted-foreground mt-5 text-sm">
{t("deployment.log.name")}
{t("dashboard.history")}
</div>
{deployments?.length == 0 ? (
<>
<Alert className="max-w-[40em] mt-10">
<AlertTitle>{t("no.data")}</AlertTitle>
<AlertTitle>{t("common.text.nodata")}</AlertTitle>
<AlertDescription>
<div className="flex items-center mt-5">
<div>
<Smile className="text-yellow-400" size={36} />
</div>
<div className="ml-2"> {t("deployment.log.empty")}</div>
<div className="ml-2"> {t("history.nodata")}</div>
</div>
<div className="mt-2 flex justify-end">
<Button
@@ -199,18 +203,15 @@ const Dashboard = () => {
) : (
<>
<div className="hidden sm:flex sm:flex-row text-muted-foreground text-sm border-b dark:border-stone-500 sm:p-2 mt-5">
<div className="w-48">{t("domain")}</div>
<div className="w-48">{t("history.props.domain")}</div>
<div className="w-24">{t("deployment.log.status")}</div>
<div className="w-56">{t("deployment.log.stage")}</div>
<div className="w-24">{t("history.props.status")}</div>
<div className="w-56">{t("history.props.stage")}</div>
<div className="w-56 sm:ml-2 text-center">
{t("deployment.log.last.execution.time")}
{t("history.props.last_execution_time")}
</div>
<div className="grow">{t("operation")}</div>
</div>
<div className="sm:hidden flex text-sm text-muted-foreground">
{t("deployment.log.name")}
<div className="grow">{t("common.text.operations")}</div>
</div>
{deployments?.map((deployment) => (
@@ -244,14 +245,14 @@ const Dashboard = () => {
<Sheet>
<SheetTrigger asChild>
<Button variant={"link"} className="p-0">
{t("deployment.log.detail.button.text")}
{t("history.log")}
</Button>
</SheetTrigger>
<SheetContent className="sm:max-w-5xl">
<SheetHeader>
<SheetTitle>
{deployment.expand.domain?.domain}-{deployment.id}
{t("deployment.log.detail")}
{t("history.log")}
</SheetTitle>
</SheetHeader>
<div className="bg-gray-950 text-stone-100 p-5 text-sm h-[80dvh]">

View File

@@ -77,11 +77,11 @@ const Edit = () => {
const formSchema = z.object({
id: z.string().optional(),
domain: z.string().min(1, {
message: "domain.not.empty.verify.message",
message: "common.errmsg.domain_invalid",
}),
email: z.string().email("email.valid.message").optional(),
email: z.string().email("common.errmsg.email_invalid").optional(),
access: z.string().regex(/^[a-zA-Z0-9]+$/, {
message: "domain.management.edit.dns.access.not.empty.message",
message: "domain.application.form.access.errmsg.empty",
}),
nameservers: z.string().optional(),
timeout: z.number().optional(),
@@ -106,7 +106,6 @@ const Edit = () => {
domain: domain.domain,
email: domain.applyConfig?.email,
access: domain.applyConfig?.access,
nameservers: domain.applyConfig?.nameservers,
timeout: domain.applyConfig?.timeout,
});
@@ -133,13 +132,13 @@ const Edit = () => {
try {
const resp = await save(req);
let description = t("domain.management.edit.succeed.tips");
let description = t("domain.application.form.domain.changed.message");
if (req.id == "") {
description = t("domain.management.add.succeed.tips");
description = t("domain.application.form.domain.added.message");
}
toast({
title: t("succeed"),
title: t("common.save.succeeded.message"),
description,
});
@@ -168,13 +167,13 @@ const Edit = () => {
};
try {
const resp = await save(req);
let description = t("domain.management.edit.succeed.tips");
let description = t("domain.application.form.domain.changed.message");
if (req.id == "") {
description = t("domain.management.add.succeed.tips");
description = t("domain.application.form.domain.added.message");
}
toast({
title: t("succeed"),
title: t("common.save.succeeded.message"),
description,
});
@@ -205,7 +204,7 @@ const Edit = () => {
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbLink href="#/domains">
{t("domain.management.name")}
{t("domain.page.title")}
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
@@ -229,7 +228,7 @@ const Edit = () => {
setTab("apply");
}}
>
{t("apply.setting")}
{t("domain.application.tab")}
</div>
<div
className={cn(
@@ -239,8 +238,8 @@ const Edit = () => {
onClick={() => {
if (!domain?.id) {
toast({
title: t("domain.management.edit.deploy.error"),
description: t("domain.management.edit.deploy.error"),
title: t("domain.application.unsaved.message"),
description: t("domain.application.unsaved.message"),
variant: "destructive",
});
return;
@@ -248,7 +247,7 @@ const Edit = () => {
setTab("deploy");
}}
>
{t("deploy.setting")}
{t("domain.deployment.tab")}
</div>
</div>
<div className="flex flex-col">
@@ -278,7 +277,6 @@ const Edit = () => {
}}
/>
</>
<FormMessage />
</FormItem>
)}
@@ -291,14 +289,15 @@ const Edit = () => {
<FormItem>
<FormLabel className="flex w-full justify-between">
<div>
{t("email") +
t("domain.management.edit.email.description")}
{t("domain.application.form.email.label") +
" " +
t("domain.application.form.email.tips")}
</div>
<EmailsEdit
trigger={
<div className="font-normal text-primary hover:underline cursor-pointer flex items-center">
<Plus size={14} />
{t("add")}
{t("common.add")}
</div>
}
/>
@@ -314,13 +313,15 @@ const Edit = () => {
<SelectTrigger>
<SelectValue
placeholder={t(
"domain.management.edit.email.not.empty.message"
"domain.application.form.email.errmsg.empty"
)}
/>
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>{t("email.list")}</SelectLabel>
<SelectLabel>
{t("domain.application.form.email.list")}
</SelectLabel>
{(emails.content as EmailsSetting).emails.map(
(item) => (
<SelectItem key={item} value={item}>
@@ -344,14 +345,12 @@ const Edit = () => {
render={({ field }) => (
<FormItem>
<FormLabel className="flex w-full justify-between">
<div>
{t("domain.management.edit.dns.access.label")}
</div>
<div>{t("domain.application.form.access.label")}</div>
<AccessEdit
trigger={
<div className="font-normal text-primary hover:underline cursor-pointer flex items-center">
<Plus size={14} />
{t("add")}
{t("common.add")}
</div>
}
op="add"
@@ -368,14 +367,14 @@ const Edit = () => {
<SelectTrigger>
<SelectValue
placeholder={t(
"domain.management.edit.access.not.empty.message"
"domain.application.form.access.placeholder"
)}
/>
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>
{t("domain.management.edit.access.label")}
{t("domain.application.form.access.list")}
</SelectLabel>
{accesses
.filter((item) => item.usage != "deploy")
@@ -410,12 +409,14 @@ const Edit = () => {
name="timeout"
render={({ field }) => (
<FormItem>
<FormLabel>{t("timeout")}</FormLabel>
<FormLabel>
{t("domain.application.form.timeout.label")}
</FormLabel>
<FormControl>
<Input
type="number"
placeholder={t(
"domain.management.edit.timeout.placeholder"
"ddomain.application.form.timeout.placeholder"
)}
{...field}
value={field.value}
@@ -454,7 +455,7 @@ const Edit = () => {
<div className="flex justify-end">
<Button type="submit">
{domain?.id ? t("save") : t("next")}
{domain?.id ? t("common.save") : t("common.next")}
</Button>
</div>
</form>

View File

@@ -129,15 +129,15 @@ const Home = () => {
await save(domain);
toast.toast({
title: t("operation.succeed"),
description: t("domain.management.start.deploy.succeed.tips"),
title: t("domain.deploy.started.message"),
description: t("domain.deploy.started.tips"),
});
} catch (e) {
toast.toast({
title: t("domain.management.execution.failed"),
title: t("domain.deploy.failed.message"),
description: (
// 这里的 text 只是占位作用,实际文案在 src/i18n/locales/[lang].json
<Trans i18nKey="domain.management.execution.failed.tips">
<Trans i18nKey="domain.deploy.failed.tips">
text1
<Link
to={`/history?domain=${domain.id}`}
@@ -178,9 +178,7 @@ const Home = () => {
<div className="">
<Toaster />
<div className="flex justify-between items-center">
<div className="text-muted-foreground">
{t("domain.management.name")}
</div>
<div className="text-muted-foreground">{t("domain.page.title")}</div>
<Button onClick={handleCreateClick}>{t("domain.add")}</Button>
</div>
@@ -192,7 +190,7 @@ const Home = () => {
</span>
<div className="text-center text-sm text-muted-foreground mt-3">
{t("domain.management.empty")}
{t("domain.nodata")}
</div>
<Button onClick={handleCreateClick} className="mt-3">
{t("domain.add")}
@@ -202,22 +200,19 @@ const Home = () => {
) : (
<>
<div className="hidden sm:flex sm:flex-row text-muted-foreground text-sm border-b dark:border-stone-500 sm:p-2 mt-5">
<div className="w-36">{t("domain")}</div>
<div className="w-40">{t("domain.management.expiry.date")}</div>
<div className="w-36">{t("common.text.domain")}</div>
<div className="w-40">{t("domain.props.expiry")}</div>
<div className="w-32">
{t("domain.management.last.execution.status")}
{t("domain.props.last_execution_status")}
</div>
<div className="w-64">
{t("domain.management.last.execution.stage")}
{t("domain.props.last_execution_stage")}
</div>
<div className="w-40 sm:ml-2">
{t("domain.management.last.execution.time")}
{t("domain.props.last_execution_time")}
</div>
<div className="w-24">{t("domain.management.enable")}</div>
<div className="grow">{t("operation")}</div>
</div>
<div className="sm:hidden flex text-sm text-muted-foreground">
{t("domain")}
<div className="w-24">{t("domain.props.enable")}</div>
<div className="grow">{t("common.text.operations")}</div>
</div>
{domains.map((domain) => (
@@ -238,10 +233,10 @@ const Home = () => {
{domain.expiredAt ? (
<>
<div>
{t("domain.management.expiry.date1", { date: 90 })}
{t("domain.props.expiry.date1", { date: 90 })}
</div>
<div>
{t("domain.management.expiry.date2", {
{t("domain.props.expiry.date2", {
date: getDate(domain.expiredAt),
})}
</div>
@@ -288,7 +283,9 @@ const Home = () => {
</TooltipTrigger>
<TooltipContent>
<div className="border rounded-sm px-3 bg-background text-muted-foreground text-xs">
{domain.enabled ? t("disable") : t("enable")}
{domain.enabled
? t("domain.props.enable.disabled")
: t("domain.props.enable.enabled")}
</div>
</TooltipContent>
</Tooltip>
@@ -300,7 +297,7 @@ const Home = () => {
className="p-0"
onClick={() => handleHistoryClick(domain.id ?? "")}
>
{t("deployment.log.name")}
{t("domain.history")}
</Button>
<Show when={domain.enabled ? true : false}>
<Separator orientation="vertical" className="h-4 mx-2" />
@@ -309,7 +306,7 @@ const Home = () => {
className="p-0"
onClick={() => handleRightNowClick(domain)}
>
{t("domain.management.start.deploying")}
{t("domain.deploy")}
</Button>
</Show>
@@ -326,7 +323,7 @@ const Home = () => {
className="p-0"
onClick={() => handleForceClick(domain)}
>
{t("domain.management.forced.deployment")}
{t("domain.deploy_forced")}
</Button>
</Show>
@@ -337,7 +334,7 @@ const Home = () => {
className="p-0"
onClick={() => handleDownloadClick(domain)}
>
{t("download")}
{t("common.download")}
</Button>
</Show>
@@ -347,7 +344,7 @@ const Home = () => {
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant={"link"} className="p-0">
{t("delete")}
{t("common.delete")}
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
@@ -356,17 +353,19 @@ const Home = () => {
{t("domain.delete")}
</AlertDialogTitle>
<AlertDialogDescription>
{t("domain.management.delete.confirm")}
{t("domain.delete.confirm")}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>{t("cancel")}</AlertDialogCancel>
<AlertDialogCancel>
{t("common.cancel")}
</AlertDialogCancel>
<AlertDialogAction
onClick={() => {
handleDeleteClick(domain.id ?? "");
}}
>
{t("confirm")}
{t("common.confirm")}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
@@ -378,7 +377,7 @@ const Home = () => {
className="p-0"
onClick={() => handleEditClick(domain.id ?? "")}
>
{t("edit")}
{t("common.edit")}
</Button>
</>
)}

View File

@@ -40,17 +40,17 @@ const History = () => {
return (
<ScrollArea className="h-[80vh] overflow-hidden">
<div className="text-muted-foreground">{t("deployment.log.name")}</div>
<div className="text-muted-foreground">{t("history.page.title")}</div>
{!deployments?.length ? (
<>
<Alert className="max-w-[40em] mx-auto mt-20">
<AlertTitle>{t("no.data")}</AlertTitle>
<AlertTitle>{t("common.text.nodata")}</AlertTitle>
<AlertDescription>
<div className="flex items-center mt-5">
<div>
<Smile className="text-yellow-400" size={36} />
</div>
<div className="ml-2"> {t("deployment.log.empty")}</div>
<div className="ml-2"> {t("history.nodata")}</div>
</div>
<div className="mt-2 flex justify-end">
<Button
@@ -67,18 +67,15 @@ const History = () => {
) : (
<>
<div className="hidden sm:flex sm:flex-row text-muted-foreground text-sm border-b dark:border-stone-500 sm:p-2 mt-5">
<div className="w-48">{t("domain")}</div>
<div className="w-48">{t("history.props.domain")}</div>
<div className="w-24">{t("deployment.log.status")}</div>
<div className="w-56">{t("deployment.log.stage")}</div>
<div className="w-24">{t("history.props.status")}</div>
<div className="w-56">{t("history.props.stage")}</div>
<div className="w-56 sm:ml-2 text-center">
{t("deployment.log.last.execution.time")}
{t("history.props.last_execution_time")}
</div>
<div className="grow">{t("operation")}</div>
</div>
<div className="sm:hidden flex text-sm text-muted-foreground">
{t("deployment.log.name")}
<div className="grow">{t("common.text.operations")}</div>
</div>
{deployments?.map((deployment) => (
@@ -112,14 +109,14 @@ const History = () => {
<Sheet>
<SheetTrigger asChild>
<Button variant={"link"} className="p-0">
{t("deployment.log.detail.button.text")}
{t("history.log")}
</Button>
</SheetTrigger>
<SheetContent className="sm:max-w-5xl">
<SheetHeader>
<SheetTitle>
{deployment.expand.domain?.domain}-{deployment.id}
{t("deployment.log.detail")}
{t("history.log")}
</SheetTitle>
</SheetHeader>
<div className="bg-gray-950 text-stone-100 p-5 text-sm h-[80dvh]">

View File

@@ -1,7 +1,7 @@
import { useForm } from "react-hook-form";
import { useNavigate } from "react-router-dom";
import { z } from "zod";
import { useTranslation } from 'react-i18next'
import { useTranslation } from "react-i18next";
import { Button } from "@/components/ui/button";
import {
@@ -19,15 +19,15 @@ import { zodResolver } from "@hookform/resolvers/zod";
const formSchema = z.object({
username: z.string().email({
message: "login.username.no.empty.message",
message: "login.username.errmsg.invalid",
}),
password: z.string().min(10, {
message: "login.password.length.message",
message: "login.password.errmsg.invalid",
}),
});
const Login = () => {
const { t } = useTranslation()
const { t } = useTranslation();
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
@@ -65,9 +65,9 @@ const Login = () => {
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>{t('username')}</FormLabel>
<FormLabel>{t("login.username.label")}</FormLabel>
<FormControl>
<Input placeholder="email" {...field} />
<Input placeholder={t("login.username.placeholder")} {...field} />
</FormControl>
<FormMessage />
@@ -80,9 +80,9 @@ const Login = () => {
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>{t('password')}</FormLabel>
<FormLabel>{t("login.password.label")}</FormLabel>
<FormControl>
<Input placeholder="password" {...field} type="password" />
<Input placeholder={t("login.password.placeholder")} {...field} type="password" />
</FormControl>
<FormMessage />
@@ -90,7 +90,7 @@ const Login = () => {
)}
/>
<div className="flex justify-end">
<Button type="submit">{t('login.submit')}</Button>
<Button type="submit">{t("login.submit")}</Button>
</div>
</form>
</Form>

View File

@@ -20,7 +20,7 @@ import { useTranslation } from "react-i18next";
import { z } from "zod";
const formSchema = z.object({
email: z.string().email("setting.account.email.valid.message"),
email: z.string().email("settings.account.email.errmsg.invalid"),
});
const Account = () => {
@@ -45,8 +45,8 @@ const Account = () => {
getPb().authStore.clear();
toast({
title: t("setting.account.email.change.succeed"),
description: t("setting.account.log.back.in"),
title: t("settings.account.email.changed.message"),
description: t("settings.account.relogin.message"),
});
setTimeout(() => {
navigate("/login");
@@ -54,7 +54,7 @@ const Account = () => {
} catch (e) {
const message = getErrMessage(e);
toast({
title: t("setting.account.email.change.failed"),
title: t("settings.account.email.failed.message"),
description: message,
variant: "destructive",
});
@@ -74,10 +74,10 @@ const Account = () => {
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>{t('email')}</FormLabel>
<FormLabel>{t("settings.account.email.label")}</FormLabel>
<FormControl>
<Input
placeholder={t('setting.email.placeholder')}
placeholder={t("settings.account.email.placeholder")}
{...field}
type="email"
onChange={(e) => {
@@ -94,10 +94,10 @@ const Account = () => {
<div className="flex justify-end">
{changed ? (
<Button type="submit">{t('setting.submit')}</Button>
<Button type="submit">{t("common.update")}</Button>
) : (
<Button type="submit" disabled variant={"secondary"}>
{t('setting.submit')}
{t("common.update")}
</Button>
)}
</div>

View File

@@ -20,7 +20,9 @@ const Notify = () => {
<div className="border rounded-sm p-5 shadow-lg">
<Accordion type={"multiple"} className="dark:text-stone-200">
<AccordionItem value="item-1" className="dark:border-stone-200">
<AccordionTrigger>{t('template')}</AccordionTrigger>
<AccordionTrigger>
{t("settings.notification.template.label")}
</AccordionTrigger>
<AccordionContent>
<NotifyTemplate />
</AccordionContent>
@@ -30,21 +32,21 @@ const Notify = () => {
<div className="border rounded-md p-5 mt-7 shadow-lg">
<Accordion type={"single"} className="dark:text-stone-200">
<AccordionItem value="item-2" className="dark:border-stone-200">
<AccordionTrigger>{t('ding.talk')}</AccordionTrigger>
<AccordionTrigger>{t("common.provider.dingtalk")}</AccordionTrigger>
<AccordionContent>
<DingTalk />
</AccordionContent>
</AccordionItem>
<AccordionItem value="item-4" className="dark:border-stone-200">
<AccordionTrigger>{t('telegram')}</AccordionTrigger>
<AccordionTrigger>{t("common.provider.telegram")}</AccordionTrigger>
<AccordionContent>
<Telegram />
</AccordionContent>
</AccordionItem>
<AccordionItem value="item-5" className="dark:border-stone-200">
<AccordionTrigger>{t('webhook')}</AccordionTrigger>
<AccordionTrigger>{t("common.provider.webhook")}</AccordionTrigger>
<AccordionContent>
<Webhook />
</AccordionContent>

View File

@@ -21,17 +21,17 @@ import { z } from "zod";
const formSchema = z
.object({
oldPassword: z.string().min(10, {
message: "setting.password.length.message",
message: "settings.password.password.errmsg.length",
}),
newPassword: z.string().min(10, {
message: "setting.password.length.message",
message: "settings.password.password.errmsg.length",
}),
confirmPassword: z.string().min(10, {
message: "setting.password.length.message",
message: "settings.password.password.errmsg.length",
}),
})
.refine((data) => data.newPassword === data.confirmPassword, {
message: "setting.password.not.match",
message: "settings.password.password.errmsg.not_matched",
path: ["confirmPassword"],
});
@@ -68,8 +68,8 @@ const Password = () => {
getPb().authStore.clear();
toast({
title: t('setting.password.change.succeed'),
description: t("setting.account.log.back.in"),
title: t("settings.password.changed.message"),
description: t("settings.account.relogin.message"),
});
setTimeout(() => {
navigate("/login");
@@ -77,7 +77,7 @@ const Password = () => {
} catch (e) {
const message = getErrMessage(e);
toast({
title: t('setting.password.change.failed'),
title: t("settings.password.failed.message"),
description: message,
variant: "destructive",
});
@@ -97,9 +97,17 @@ const Password = () => {
name="oldPassword"
render={({ field }) => (
<FormItem>
<FormLabel>{t('setting.password.current.password')}</FormLabel>
<FormLabel>
{t("settings.password.current_password.label")}
</FormLabel>
<FormControl>
<Input placeholder={t('setting.password.current.password')} {...field} type="password" />
<Input
placeholder={t(
"settings.password.current_password.placeholder"
)}
{...field}
type="password"
/>
</FormControl>
<FormMessage />
@@ -112,10 +120,14 @@ const Password = () => {
name="newPassword"
render={({ field }) => (
<FormItem>
<FormLabel>{t('setting.password.new.password')}</FormLabel>
<FormLabel>
{t("settings.password.new_password.label")}
</FormLabel>
<FormControl>
<Input
placeholder="newPassword"
placeholder={t(
"settings.password.new_password.placeholder"
)}
{...field}
type="password"
/>
@@ -131,10 +143,14 @@ const Password = () => {
name="confirmPassword"
render={({ field }) => (
<FormItem>
<FormLabel>{t('setting.password.confirm.password')}</FormLabel>
<FormLabel>
{t("settings.password.confirm_password.label")}
</FormLabel>
<FormControl>
<Input
placeholder="confirmPassword"
placeholder={t(
"settings.password.confirm_password.placeholder"
)}
{...field}
type="password"
/>
@@ -145,7 +161,7 @@ const Password = () => {
)}
/>
<div className="flex justify-end">
<Button type="submit">{t('setting.submit')}</Button>
<Button type="submit">{t("common.update")}</Button>
</div>
</form>
</Form>

View File

@@ -35,7 +35,7 @@ const SSLProvider = () => {
const formSchema = z.object({
provider: z.enum(["letsencrypt", "zerossl"], {
message: t("setting.ca.not.empty"),
message: t("settings.ca.provider.errmsg.empty"),
}),
eabKid: z.string().optional(),
eabHmacKey: z.string().optional(),
@@ -89,12 +89,12 @@ const SSLProvider = () => {
if (values.provider === "zerossl") {
if (!values.eabKid) {
form.setError("eabKid", {
message: t("setting.ca.eab_kid_hmac_key.not.empty"),
message: t("settings.ca.eab_kid_hmac_key.errmsg.empty"),
});
}
if (!values.eabHmacKey) {
form.setError("eabHmacKey", {
message: t("setting.ca.eab_kid_hmac_key.not.empty"),
message: t("settings.ca.eab_kid_hmac_key.errmsg.empty"),
});
}
if (!values.eabKid || !values.eabHmacKey) {
@@ -120,13 +120,13 @@ const SSLProvider = () => {
try {
await update(setting);
toast({
title: t("update.succeed"),
description: t("update.succeed"),
title: t("common.update.succeeded.message"),
description: t("common.update.succeeded.message"),
});
} catch (e) {
const message = getErrMessage(e);
toast({
title: t("update.failed"),
title: t("common.update.failed.message"),
description: message,
variant: "destructive",
});
@@ -146,7 +146,7 @@ const SSLProvider = () => {
name="provider"
render={({ field }) => (
<FormItem>
<FormLabel>{t("ca")}</FormLabel>
<FormLabel>{t("common.text.ca")}</FormLabel>
<FormControl>
<RadioGroup
{...field}
@@ -202,7 +202,7 @@ const SSLProvider = () => {
<FormLabel>EAB_KID</FormLabel>
<FormControl>
<Input
placeholder={t("setting.ca.eab_kid.not.empty")}
placeholder={t("settings.ca.eab_kid.errmsg.empty")}
{...field}
type="text"
/>
@@ -221,7 +221,9 @@ const SSLProvider = () => {
<FormLabel>EAB_HMAC_KEY</FormLabel>
<FormControl>
<Input
placeholder={t("setting.ca.eab_hmac_key.not.empty")}
placeholder={t(
"settings.ca.eab_hmac_key.errmsg.empty"
)}
{...field}
type="text"
/>
@@ -238,7 +240,7 @@ const SSLProvider = () => {
/>
<div className="flex justify-end">
<Button type="submit">{t("setting.submit")}</Button>
<Button type="submit">{t("common.update")}</Button>
</div>
</form>
</Form>