fix: fix domain schema check problem in add website server

and change add website / get website in website list request to trpc
This commit is contained in:
moonrailgun 2024-01-03 15:37:28 +08:00
parent 824bd89ede
commit a04aa67f85
6 changed files with 97 additions and 52 deletions

View File

@ -1,9 +1,8 @@
import { Button, Form, Input, Typography } from 'antd';
import React from 'react';
import { slugRegex } from '../../../../shared';
import { z } from 'zod';
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
import { MonitorPicker } from '../MonitorPicker';
import { urlSlugValidator } from '../../../utils/validator';
const { Text } = Typography;
@ -62,14 +61,7 @@ export const MonitorStatusPageEditForm: React.FC<MonitorStatusPageEditFormProps>
required: true,
},
{
validator(rule, value, callback) {
try {
z.string().regex(slugRegex).parse(value);
callback();
} catch (err) {
callback('Not valid slug');
}
},
validator: urlSlugValidator,
},
]}
>

View File

@ -1,8 +1,7 @@
import { Form, Input } from 'antd';
import React from 'react';
import { MonitorProvider } from './types';
import { z } from 'zod';
import { hostnameRegex } from '../../../../shared';
import { hostnameValidator } from '../../../utils/validator';
export const MonitorPing: React.FC = React.memo(() => {
return (
@ -13,17 +12,7 @@ export const MonitorPing: React.FC = React.memo(() => {
rules={[
{ required: true },
{
validator(rule, value, callback) {
try {
z.union([
z.string().ip(),
z.string().regex(hostnameRegex),
]).parse(value);
callback();
} catch (err) {
callback('Not valid host, it should be ip or hostname');
}
},
validator: hostnameValidator,
},
]}
>

View File

@ -16,6 +16,7 @@ import {
} from '../../api/trpc';
import { useQueryClient } from '@tanstack/react-query';
import { useEvent } from '../../hooks/useEvent';
import { hostnameValidator } from '../../utils/validator';
export const WebsiteInfo: React.FC = React.memo(() => {
const workspaceId = useCurrentWorkspaceId();
@ -102,7 +103,12 @@ export const WebsiteInfo: React.FC = React.memo(() => {
<Form.Item
label="Domain"
name="domain"
rules={[{ required: true }]}
rules={[
{ required: true },
{
validator: hostnameValidator,
},
]}
>
<Input size="large" />
</Form.Item>

View File

@ -7,43 +7,40 @@ import {
import { Button, Form, Input, Modal, Table, Typography } from 'antd';
import { ColumnsType } from 'antd/es/table';
import React, { useMemo, useState } from 'react';
import {
addWorkspaceWebsite,
refreshWorkspaceWebsites,
useWorspaceWebsites,
WebsiteInfo,
} from '../../api/model/website';
import { WebsiteInfo } from '../../api/model/website';
import { Loading } from '../Loading';
import { NoWorkspaceTip } from '../NoWorkspaceTip';
import { useRequest } from '../../hooks/useRequest';
import { useUserStore } from '../../store/user';
import { useCurrentWorkspaceId } from '../../store/user';
import { useEvent } from '../../hooks/useEvent';
import { useNavigate } from 'react-router';
import { PageHeader } from '../PageHeader';
import { ModalButton } from '../ModalButton';
import { hostnameValidator } from '../../utils/validator';
import { trpc } from '../../api/trpc';
export const WebsiteList: React.FC = React.memo(() => {
const [isModalOpen, setIsModalOpen] = useState(false);
const currentWorkspace = useUserStore(
(state) => state.info?.currentWorkspace
);
const workspaceId = useCurrentWorkspaceId();
const [form] = Form.useForm();
const addWebsiteMutation = trpc.website.add.useMutation();
const utils = trpc.useContext();
const [{ loading }, handleAddWebsite] = useRequest(async () => {
const handleAddWebsite = useEvent(async () => {
await form.validateFields();
const values = form.getFieldsValue();
await addWorkspaceWebsite(currentWorkspace!.id, values.name, values.domain);
refreshWorkspaceWebsites(currentWorkspace!.id);
await addWebsiteMutation.mutateAsync({
workspaceId,
name: values.name,
domain: values.domain,
});
utils.website.all.refetch();
setIsModalOpen(false);
form.resetFields();
});
if (!currentWorkspace) {
return <NoWorkspaceTip />;
}
return (
<div>
<PageHeader
@ -62,13 +59,13 @@ export const WebsiteList: React.FC = React.memo(() => {
}
/>
<WebsiteListTable workspaceId={currentWorkspace.id} />
<WebsiteListTable workspaceId={workspaceId} />
<Modal
title="Add Server"
open={isModalOpen}
okButtonProps={{
loading,
loading: addWebsiteMutation.isLoading,
}}
onOk={() => handleAddWebsite()}
onCancel={() => setIsModalOpen(false)}
@ -86,7 +83,12 @@ export const WebsiteList: React.FC = React.memo(() => {
label="Domain"
name="domain"
tooltip="Your server domain, or ip."
rules={[{ required: true }]}
rules={[
{ required: true },
{
validator: hostnameValidator,
},
]}
>
<Input />
</Form.Item>
@ -99,7 +101,9 @@ WebsiteList.displayName = 'WebsiteList';
const WebsiteListTable: React.FC<{ workspaceId: string }> = React.memo(
(props) => {
const { websites, isLoading } = useWorspaceWebsites(props.workspaceId);
const { data: websites = [], isLoading } = trpc.website.all.useQuery({
workspaceId: props.workspaceId,
});
const navigate = useNavigate();
const handleEdit = useEvent((websiteId) => {

View File

@ -0,0 +1,27 @@
import { RuleObject } from 'antd/es/form';
import { z } from 'zod';
import { hostnameRegex, slugRegex } from '../../shared';
type Validator = (
rule: RuleObject,
value: any,
callback: (error?: string) => void
) => Promise<void | any> | void;
export const hostnameValidator: Validator = (rule, value, callback) => {
try {
z.union([z.string().ip(), z.string().regex(hostnameRegex)]).parse(value);
callback();
} catch (err) {
callback('Not valid host, it should be ip or hostname');
}
};
export const urlSlugValidator: Validator = (rule, value, callback) => {
try {
z.string().regex(slugRegex).parse(value);
callback();
} catch (err) {
callback('Not valid slug');
}
};

View File

@ -18,6 +18,13 @@ import { getSessionMetrics, getPageviewMetrics } from '../../model/website';
import { websiteInfoSchema } from '../../model/_schema';
import { OpenApiMeta } from 'trpc-openapi';
import { hostnameRegex } from '../../../shared';
import { addWorkspaceWebsite } from '../../model/workspace';
const websiteNameSchema = z.string().max(100);
const websiteDomainSchema = z.union([
z.string().max(500).regex(hostnameRegex),
z.string().max(500).ip(),
]);
export const websiteRouter = router({
onlineCount: workspaceProcedure
@ -205,6 +212,29 @@ export const websiteRouter = router({
return [];
}),
add: workspaceOwnerProcedure
.meta({
openapi: {
method: 'POST',
tags: [OPENAPI_TAG.WEBSITE],
protect: true,
path: `/workspace/{workspaceId}/website/add`,
},
})
.input(
z.object({
name: websiteNameSchema,
domain: websiteDomainSchema,
})
)
.output(websiteInfoSchema)
.mutation(async ({ input }) => {
const { workspaceId, name, domain } = input;
const website = await addWorkspaceWebsite(workspaceId, name, domain);
return website;
}),
updateInfo: workspaceOwnerProcedure
.meta(
buildWebsiteOpenapi({
@ -215,11 +245,8 @@ export const websiteRouter = router({
.input(
z.object({
websiteId: z.string().cuid2(),
name: z.string().max(100),
domain: z.union([
z.string().max(500).regex(hostnameRegex),
z.string().max(500).ip(),
]),
name: websiteNameSchema,
domain: websiteDomainSchema,
monitorId: z.string().cuid2().nullish(),
})
)