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:
parent
824bd89ede
commit
a04aa67f85
@ -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,
|
||||
},
|
||||
]}
|
||||
>
|
||||
|
@ -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,
|
||||
},
|
||||
]}
|
||||
>
|
||||
|
@ -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>
|
||||
|
@ -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) => {
|
||||
|
27
src/client/utils/validator.ts
Normal file
27
src/client/utils/validator.ts
Normal 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');
|
||||
}
|
||||
};
|
@ -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(),
|
||||
})
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user