feat: website info and edit
This commit is contained in:
parent
6441230df4
commit
a4c4a6d9e9
@ -10,38 +10,51 @@ import { useUserStore } from './store/user';
|
||||
import { Register } from './pages/Register';
|
||||
import { QueryClientProvider } from '@tanstack/react-query';
|
||||
import { queryClient } from './api/cache';
|
||||
function App() {
|
||||
import { TokenLoginContainer } from './components/TokenLoginContainer';
|
||||
import React from 'react';
|
||||
|
||||
export const AppRoutes: React.FC = React.memo(() => {
|
||||
const { info } = useUserStore();
|
||||
|
||||
return (
|
||||
<Routes>
|
||||
{info ? (
|
||||
<Route element={<Layout />}>
|
||||
<Route path="/dashboard" element={<Dashboard />} />
|
||||
<Route path="/monitor" element={<Monitor />} />
|
||||
<Route path="/website" element={<Website />} />
|
||||
<Route path="/servers" element={<Servers />} />
|
||||
<Route path="/settings/*" element={<Settings />} />
|
||||
</Route>
|
||||
) : (
|
||||
<Route>
|
||||
<Route path="/login" element={<Login />} />
|
||||
<Route path="/register" element={<Register />} />
|
||||
</Route>
|
||||
)}
|
||||
|
||||
<Route
|
||||
path="*"
|
||||
element={
|
||||
<Navigate to={info ? '/dashboard' : '/login'} replace={true} />
|
||||
}
|
||||
/>
|
||||
</Routes>
|
||||
);
|
||||
});
|
||||
AppRoutes.displayName = 'AppRoutes';
|
||||
|
||||
export const App: React.FC = React.memo(() => {
|
||||
return (
|
||||
<div className="App">
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
{info && (
|
||||
<Route element={<Layout />}>
|
||||
<Route path="/dashboard" element={<Dashboard />} />
|
||||
<Route path="/monitor" element={<Monitor />} />
|
||||
<Route path="/website" element={<Website />} />
|
||||
<Route path="/servers" element={<Servers />} />
|
||||
<Route path="/settings" element={<Settings />} />
|
||||
</Route>
|
||||
)}
|
||||
|
||||
<Route path="/login" element={<Login />} />
|
||||
<Route path="/register" element={<Register />} />
|
||||
|
||||
<Route
|
||||
path="*"
|
||||
element={
|
||||
<Navigate to={info ? '/dashboard' : '/login'} replace={true} />
|
||||
}
|
||||
/>
|
||||
</Routes>
|
||||
<TokenLoginContainer>
|
||||
<AppRoutes />
|
||||
</TokenLoginContainer>
|
||||
</BrowserRouter>
|
||||
</QueryClientProvider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
});
|
||||
App.displayName = 'App';
|
||||
|
@ -26,6 +26,33 @@ export async function getWorkspaceWebsites(
|
||||
return data.websites;
|
||||
}
|
||||
|
||||
export async function getWorkspaceWebsiteInfo(
|
||||
workspaceId: string,
|
||||
websiteId: string
|
||||
): Promise<WebsiteInfo | null> {
|
||||
const { data } = await request.get(`/api/workspace/website/${websiteId}`, {
|
||||
params: {
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
return data.website;
|
||||
}
|
||||
|
||||
export async function updateWorkspaceWebsiteInfo(
|
||||
workspaceId: string,
|
||||
websiteId: string,
|
||||
info: { name: string; domain: string }
|
||||
) {
|
||||
await request.post(`/api/workspace/website/${websiteId}`, {
|
||||
workspaceId,
|
||||
name: info.name,
|
||||
domain: info.domain,
|
||||
});
|
||||
|
||||
queryClient.resetQueries(['websites', workspaceId]);
|
||||
}
|
||||
|
||||
export function useWorspaceWebsites(workspaceId: string) {
|
||||
const { data: websites = [], isLoading } = useQuery(
|
||||
['websites', workspaceId],
|
||||
@ -37,6 +64,21 @@ export function useWorspaceWebsites(workspaceId: string) {
|
||||
return { websites, isLoading };
|
||||
}
|
||||
|
||||
export function useWorkspaceWebsiteInfo(
|
||||
workspaceId: string,
|
||||
websiteId: string
|
||||
) {
|
||||
const { data: website = null, isLoading } = useQuery(
|
||||
['website', workspaceId, websiteId],
|
||||
() => {
|
||||
return getWorkspaceWebsiteInfo(workspaceId, websiteId);
|
||||
},
|
||||
{ cacheTime: 0 }
|
||||
);
|
||||
|
||||
return { website, isLoading };
|
||||
}
|
||||
|
||||
export function refreshWorkspaceWebsites(workspaceId: string) {
|
||||
queryClient.refetchQueries(['websites', workspaceId]);
|
||||
}
|
||||
|
6
src/client/components/ErrorTip.tsx
Normal file
6
src/client/components/ErrorTip.tsx
Normal file
@ -0,0 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
export const ErrorTip: React.FC = React.memo(() => {
|
||||
return <div>An unexpected error has occurred</div>;
|
||||
});
|
||||
ErrorTip.displayName = 'ErrorTip';
|
27
src/client/components/TokenLoginContainer.tsx
Normal file
27
src/client/components/TokenLoginContainer.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { getJWT } from '../api/auth';
|
||||
import { loginWithToken } from '../api/model/user';
|
||||
import { Loading } from './Loading';
|
||||
|
||||
export const TokenLoginContainer: React.FC<React.PropsWithChildren> =
|
||||
React.memo((props) => {
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const token = getJWT();
|
||||
if (token) {
|
||||
loginWithToken().then(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
} else {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
return <>{props.children}</>;
|
||||
});
|
||||
TokenLoginContainer.displayName = 'TokenLoginContainer';
|
87
src/client/components/WebsiteInfo.tsx
Normal file
87
src/client/components/WebsiteInfo.tsx
Normal file
@ -0,0 +1,87 @@
|
||||
import { Button, Form, Input, message } from 'antd';
|
||||
import React from 'react';
|
||||
import { useParams } from 'react-router';
|
||||
import {
|
||||
updateWorkspaceWebsiteInfo,
|
||||
useWorkspaceWebsiteInfo,
|
||||
} from '../api/model/website';
|
||||
import { useRequest } from '../hooks/useRequest';
|
||||
import { useCurrentWorkspaceId } from '../store/user';
|
||||
import { ErrorTip } from './ErrorTip';
|
||||
import { Loading } from './Loading';
|
||||
import { NoWorkspaceTip } from './NoWorkspaceTip';
|
||||
|
||||
export const WebsiteInfo: React.FC = React.memo(() => {
|
||||
const { websiteId } = useParams<{
|
||||
websiteId: string;
|
||||
}>();
|
||||
const currentWorkspaceId = useCurrentWorkspaceId();
|
||||
const { website, isLoading } = useWorkspaceWebsiteInfo(
|
||||
currentWorkspaceId!,
|
||||
websiteId!
|
||||
);
|
||||
|
||||
const [, handleSave] = useRequest(
|
||||
async (values: { name: string; domain: string }) => {
|
||||
await updateWorkspaceWebsiteInfo(currentWorkspaceId!, websiteId!, {
|
||||
name: values.name,
|
||||
domain: values.domain,
|
||||
});
|
||||
|
||||
message.success('Save Success');
|
||||
}
|
||||
);
|
||||
|
||||
if (!currentWorkspaceId) {
|
||||
return <NoWorkspaceTip />;
|
||||
}
|
||||
|
||||
if (!websiteId) {
|
||||
return <ErrorTip />;
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
if (!website) {
|
||||
return <ErrorTip />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="h-24 flex items-center">
|
||||
<div className="text-2xl flex-1">Website Info</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Form
|
||||
layout="vertical"
|
||||
initialValues={{
|
||||
id: website.id,
|
||||
name: website.name,
|
||||
domain: website.domain,
|
||||
}}
|
||||
onFinish={handleSave}
|
||||
>
|
||||
<Form.Item label="Website ID" name="id">
|
||||
<Input size="large" disabled={true} />
|
||||
</Form.Item>
|
||||
<Form.Item label="Name" name="name" rules={[{ required: true }]}>
|
||||
<Input size="large" />
|
||||
</Form.Item>
|
||||
<Form.Item label="Domain" name="domain" rules={[{ required: true }]}>
|
||||
<Input size="large" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item>
|
||||
<Button size="large" htmlType="submit">
|
||||
Save
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
WebsiteInfo.displayName = 'WebsiteInfo';
|
135
src/client/components/WebsiteList.tsx
Normal file
135
src/client/components/WebsiteList.tsx
Normal file
@ -0,0 +1,135 @@
|
||||
import {
|
||||
BarChartOutlined,
|
||||
EditOutlined,
|
||||
PlusOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { Button, Form, Input, Modal, Table } from 'antd';
|
||||
import { ColumnsType } from 'antd/es/table';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import {
|
||||
addWorkspaceWebsite,
|
||||
refreshWorkspaceWebsites,
|
||||
useWorspaceWebsites,
|
||||
WebsiteInfo,
|
||||
} from '../api/model/website';
|
||||
import { Loading } from './Loading';
|
||||
import { NoWorkspaceTip } from './NoWorkspaceTip';
|
||||
import { useRequest } from '../hooks/useRequest';
|
||||
import { useUserStore } from '../store/user';
|
||||
import { useEvent } from '../hooks/useEvent';
|
||||
import { useNavigate } from 'react-router';
|
||||
|
||||
export const WebsiteList: React.FC = React.memo(() => {
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const currentWorkspace = useUserStore(
|
||||
(state) => state.info?.currentWorkspace
|
||||
);
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const [{ loading }, handleAddWebsite] = useRequest(async () => {
|
||||
await form.validateFields();
|
||||
const values = form.getFieldsValue();
|
||||
|
||||
await addWorkspaceWebsite(currentWorkspace!.id, values.name, values.domain);
|
||||
refreshWorkspaceWebsites(currentWorkspace!.id);
|
||||
setIsModalOpen(false);
|
||||
|
||||
form.resetFields();
|
||||
});
|
||||
|
||||
if (!currentWorkspace) {
|
||||
return <NoWorkspaceTip />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="h-24 flex items-center">
|
||||
<div className="text-2xl flex-1">Websites</div>
|
||||
<div>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<PlusOutlined />}
|
||||
size="large"
|
||||
onClick={() => setIsModalOpen(true)}
|
||||
>
|
||||
Add Website
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<WebsiteListTable workspaceId={currentWorkspace.id} />
|
||||
|
||||
<Modal
|
||||
title="Add Server"
|
||||
open={isModalOpen}
|
||||
okButtonProps={{
|
||||
loading,
|
||||
}}
|
||||
onOk={() => handleAddWebsite()}
|
||||
onCancel={() => setIsModalOpen(false)}
|
||||
>
|
||||
<Form layout="vertical" form={form}>
|
||||
<Form.Item
|
||||
label="Server Name"
|
||||
name="name"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label="Domain" name="domain" rules={[{ required: true }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
WebsiteList.displayName = 'WebsiteList';
|
||||
|
||||
const WebsiteListTable: React.FC<{ workspaceId: string }> = React.memo(
|
||||
(props) => {
|
||||
const { websites, isLoading } = useWorspaceWebsites(props.workspaceId);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleEdit = useEvent((websiteId) => {
|
||||
console.log(`/settings/website/${websiteId}`);
|
||||
navigate(`/settings/website/${websiteId}`);
|
||||
});
|
||||
|
||||
const columns = useMemo((): ColumnsType<WebsiteInfo> => {
|
||||
return [
|
||||
{
|
||||
dataIndex: 'name',
|
||||
title: 'Name',
|
||||
},
|
||||
{
|
||||
dataIndex: 'domain',
|
||||
title: 'Domain',
|
||||
},
|
||||
{
|
||||
key: 'action',
|
||||
render: (record) => {
|
||||
return (
|
||||
<div className="flex gap-2 justify-end">
|
||||
<Button
|
||||
icon={<EditOutlined />}
|
||||
onClick={() => handleEdit(record.id)}
|
||||
>
|
||||
Edit
|
||||
</Button>
|
||||
<Button icon={<BarChartOutlined />}>View</Button>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
] as ColumnsType<WebsiteInfo>;
|
||||
}, []);
|
||||
|
||||
if (isLoading) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
return <Table columns={columns} dataSource={websites} pagination={false} />;
|
||||
}
|
||||
);
|
||||
WebsiteListTable.displayName = 'WebsiteListTable';
|
@ -3,7 +3,7 @@ import './index.css';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
|
||||
import App from './App';
|
||||
import { App } from './App';
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
||||
<React.StrictMode>
|
||||
|
@ -1,9 +1,7 @@
|
||||
import { Button, Form, Input, Typography } from 'antd';
|
||||
import React, { useEffect } from 'react';
|
||||
import React from 'react';
|
||||
import { model } from '../api/model';
|
||||
import { useNavigate } from 'react-router';
|
||||
import { loginWithToken } from '../api/model/user';
|
||||
import { getJWT } from '../api/auth';
|
||||
import { useRequest } from '../hooks/useRequest';
|
||||
|
||||
export const Login: React.FC = React.memo(() => {
|
||||
@ -14,15 +12,6 @@ export const Login: React.FC = React.memo(() => {
|
||||
navigate('/dashboard');
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const token = getJWT();
|
||||
if (token) {
|
||||
loginWithToken().then(() => {
|
||||
navigate('/dashboard');
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="w-full h-full flex justify-center items-center">
|
||||
<div className="w-80 -translate-y-1/4">
|
||||
|
@ -1,6 +1,49 @@
|
||||
import { Menu, MenuProps } from 'antd';
|
||||
import React from 'react';
|
||||
import { Routes, Route, useLocation, useNavigate } from 'react-router-dom';
|
||||
import { WebsiteInfo } from '../components/WebsiteInfo';
|
||||
import { WebsiteList } from '../components/WebsiteList';
|
||||
import { useEvent } from '../hooks/useEvent';
|
||||
|
||||
export const Settings: React.FC = React.memo(() => {
|
||||
return <div>Settings</div>;
|
||||
const navigate = useNavigate();
|
||||
const { pathname } = useLocation();
|
||||
|
||||
const items: MenuProps['items'] = [
|
||||
{
|
||||
key: 'websites',
|
||||
label: 'Websites',
|
||||
},
|
||||
];
|
||||
|
||||
const onClick: MenuProps['onClick'] = useEvent((e) => {
|
||||
navigate(`/settings/${e.key}`);
|
||||
});
|
||||
|
||||
const selectedKey =
|
||||
(items.find((item) => pathname.startsWith(`/settings/${item?.key}`))
|
||||
?.key as string) ?? 'websites';
|
||||
|
||||
return (
|
||||
<div className="flex h-full">
|
||||
<div className="w-full md:w-1/6 pt-10">
|
||||
<Menu
|
||||
className="h-full"
|
||||
onClick={onClick}
|
||||
selectedKeys={[selectedKey]}
|
||||
mode="vertical"
|
||||
items={items}
|
||||
/>
|
||||
{pathname}
|
||||
</div>
|
||||
<div className="w-full md:w-5/6 py-2 px-4">
|
||||
<Routes>
|
||||
<Route path="/" element={<WebsiteList />} />
|
||||
<Route path="/websites" element={<WebsiteList />} />
|
||||
<Route path="/website/:websiteId" element={<WebsiteInfo />} />
|
||||
</Routes>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
Settings.displayName = 'Settings';
|
||||
|
@ -1,120 +1,7 @@
|
||||
import {
|
||||
BarChartOutlined,
|
||||
EditOutlined,
|
||||
PlusOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { Button, Form, Input, Modal, Table } from 'antd';
|
||||
import { ColumnsType } from 'antd/es/table';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import {
|
||||
addWorkspaceWebsite,
|
||||
refreshWorkspaceWebsites,
|
||||
useWorspaceWebsites,
|
||||
WebsiteInfo,
|
||||
} from '../api/model/website';
|
||||
import { Loading } from '../components/Loading';
|
||||
import { NoWorkspaceTip } from '../components/NoWorkspaceTip';
|
||||
import { useRequest } from '../hooks/useRequest';
|
||||
import { useUserStore } from '../store/user';
|
||||
import React from 'react';
|
||||
import { WebsiteList } from '../components/WebsiteList';
|
||||
|
||||
export const Website: React.FC = React.memo(() => {
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const currentWorkspace = useUserStore(
|
||||
(state) => state.info?.currentWorkspace
|
||||
);
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const [{ loading }, handleAddWebsite] = useRequest(async () => {
|
||||
await form.validateFields();
|
||||
const values = form.getFieldsValue();
|
||||
|
||||
await addWorkspaceWebsite(currentWorkspace!.id, values.name, values.domain);
|
||||
refreshWorkspaceWebsites(currentWorkspace!.id);
|
||||
setIsModalOpen(false);
|
||||
|
||||
form.resetFields();
|
||||
});
|
||||
|
||||
if (!currentWorkspace) {
|
||||
return <NoWorkspaceTip />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="h-24 flex items-center">
|
||||
<div className="text-2xl flex-1">Websites</div>
|
||||
<div>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<PlusOutlined />}
|
||||
size="large"
|
||||
onClick={() => setIsModalOpen(true)}
|
||||
>
|
||||
Add Website
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<WebsiteList workspaceId={currentWorkspace.id} />
|
||||
|
||||
<Modal
|
||||
title="Add Server"
|
||||
open={isModalOpen}
|
||||
okButtonProps={{
|
||||
loading,
|
||||
}}
|
||||
onOk={() => handleAddWebsite()}
|
||||
onCancel={() => setIsModalOpen(false)}
|
||||
>
|
||||
<Form layout="vertical" form={form}>
|
||||
<Form.Item
|
||||
label="Server Name"
|
||||
name="name"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label="Domain" name="domain" rules={[{ required: true }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
return <WebsiteList />;
|
||||
});
|
||||
Website.displayName = 'Website';
|
||||
|
||||
const WebsiteList: React.FC<{ workspaceId: string }> = React.memo((props) => {
|
||||
const { websites, isLoading } = useWorspaceWebsites(props.workspaceId);
|
||||
|
||||
const columns = useMemo((): ColumnsType<WebsiteInfo> => {
|
||||
return [
|
||||
{
|
||||
dataIndex: 'name',
|
||||
title: 'Name',
|
||||
},
|
||||
{
|
||||
dataIndex: 'domain',
|
||||
title: 'Domain',
|
||||
},
|
||||
{
|
||||
key: 'action',
|
||||
render: () => {
|
||||
return (
|
||||
<div className="flex gap-2 justify-end">
|
||||
<Button icon={<EditOutlined />}>Edit</Button>
|
||||
<Button icon={<BarChartOutlined />}>View</Button>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
}, []);
|
||||
|
||||
if (isLoading) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
return <Table columns={columns} dataSource={websites} pagination={false} />;
|
||||
});
|
||||
WebsiteList.displayName = 'WebsiteList';
|
||||
|
@ -22,3 +22,11 @@ export function setUserInfo(info: UserLoginInfo) {
|
||||
info,
|
||||
});
|
||||
}
|
||||
|
||||
export function useCurrentWorkspaceId() {
|
||||
const currentWorkspaceId = useUserStore(
|
||||
(state) => state.info?.currentWorkspace?.id
|
||||
);
|
||||
|
||||
return currentWorkspaceId;
|
||||
}
|
||||
|
@ -3,7 +3,8 @@ import { checkIsWorkspaceUser } from '../model/workspace';
|
||||
|
||||
export function workspacePermission(): Handler {
|
||||
return async (req, res, next) => {
|
||||
const workspaceId = req.body.workspaceId ?? req.query.workspaceId;
|
||||
const workspaceId =
|
||||
req.body.workspaceId ?? req.query.workspaceId ?? req.params.workspaceId;
|
||||
|
||||
if (!workspaceId) {
|
||||
throw new Error('Cannot find workspace id');
|
||||
|
@ -35,6 +35,40 @@ export async function getWorkspaceWebsites(workspaceId: string) {
|
||||
return workspace?.websites ?? [];
|
||||
}
|
||||
|
||||
export async function getWorkspaceWebsiteInfo(
|
||||
workspaceId: string,
|
||||
websiteId: string
|
||||
) {
|
||||
const websiteInfo = await prisma.website.findUnique({
|
||||
where: {
|
||||
id: websiteId,
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
return websiteInfo;
|
||||
}
|
||||
|
||||
export async function updateWorkspaceWebsiteInfo(
|
||||
workspaceId: string,
|
||||
websiteId: string,
|
||||
name: string,
|
||||
domain: string
|
||||
) {
|
||||
const websiteInfo = await prisma.website.update({
|
||||
where: {
|
||||
id: websiteId,
|
||||
workspaceId,
|
||||
},
|
||||
data: {
|
||||
name,
|
||||
domain,
|
||||
},
|
||||
});
|
||||
|
||||
return websiteInfo;
|
||||
}
|
||||
|
||||
export async function addWorkspaceWebsite(
|
||||
workspaceId: string,
|
||||
name: string,
|
||||
|
@ -1,8 +1,13 @@
|
||||
import { Router } from 'express';
|
||||
import { auth } from '../middleware/auth';
|
||||
import { body, query, validate } from '../middleware/validate';
|
||||
import { body, param, query, validate } from '../middleware/validate';
|
||||
import { workspacePermission } from '../middleware/workspace';
|
||||
import { addWorkspaceWebsite, getWorkspaceWebsites } from '../model/workspace';
|
||||
import {
|
||||
addWorkspaceWebsite,
|
||||
getWorkspaceWebsiteInfo,
|
||||
getWorkspaceWebsites,
|
||||
updateWorkspaceWebsiteInfo,
|
||||
} from '../model/workspace';
|
||||
|
||||
export const workspaceRouter = Router();
|
||||
|
||||
@ -34,8 +39,16 @@ workspaceRouter.post(
|
||||
.withMessage('workspaceId should be string')
|
||||
.isUUID()
|
||||
.withMessage('workspaceId should be UUID'),
|
||||
body('name').isString().withMessage('name should be a string'),
|
||||
body('domain').isURL().withMessage('domain should be URL')
|
||||
body('name')
|
||||
.isString()
|
||||
.withMessage('name should be string')
|
||||
.isLength({ max: 100 })
|
||||
.withMessage('length should be under 100'),
|
||||
body('domain')
|
||||
.isURL()
|
||||
.withMessage('domain should be URL')
|
||||
.isLength({ max: 500 })
|
||||
.withMessage('length should be under 500')
|
||||
),
|
||||
auth(),
|
||||
workspacePermission(),
|
||||
@ -47,3 +60,71 @@ workspaceRouter.post(
|
||||
res.json({ website });
|
||||
}
|
||||
);
|
||||
|
||||
workspaceRouter.get(
|
||||
'/website/:websiteId',
|
||||
validate(
|
||||
query('workspaceId')
|
||||
.isString()
|
||||
.withMessage('workspaceId should be string')
|
||||
.isUUID()
|
||||
.withMessage('workspaceId should be UUID'),
|
||||
param('websiteId')
|
||||
.isString()
|
||||
.withMessage('workspaceId should be string')
|
||||
.isUUID()
|
||||
.withMessage('workspaceId should be UUID')
|
||||
),
|
||||
auth(),
|
||||
workspacePermission(),
|
||||
async (req, res) => {
|
||||
const workspaceId = req.query.workspaceId as string;
|
||||
const websiteId = req.params.websiteId;
|
||||
|
||||
const website = await getWorkspaceWebsiteInfo(workspaceId, websiteId);
|
||||
|
||||
res.json({ website });
|
||||
}
|
||||
);
|
||||
|
||||
workspaceRouter.post(
|
||||
'/website/:websiteId',
|
||||
validate(
|
||||
body('workspaceId')
|
||||
.isString()
|
||||
.withMessage('workspaceId should be string')
|
||||
.isUUID()
|
||||
.withMessage('workspaceId should be UUID'),
|
||||
param('websiteId')
|
||||
.isString()
|
||||
.withMessage('workspaceId should be string')
|
||||
.isUUID()
|
||||
.withMessage('workspaceId should be UUID'),
|
||||
body('name')
|
||||
.isString()
|
||||
.withMessage('name should be string')
|
||||
.isLength({ max: 100 })
|
||||
.withMessage('length should be under 100'),
|
||||
body('domain')
|
||||
.isURL()
|
||||
.withMessage('domain should be URL')
|
||||
.isLength({ max: 500 })
|
||||
.withMessage('length should be under 500')
|
||||
),
|
||||
auth(),
|
||||
workspacePermission(),
|
||||
async (req, res) => {
|
||||
const workspaceId = req.query.workspaceId as string;
|
||||
const websiteId = req.params.websiteId;
|
||||
const { name, domain } = req.body;
|
||||
|
||||
const website = await updateWorkspaceWebsiteInfo(
|
||||
workspaceId,
|
||||
websiteId,
|
||||
name,
|
||||
domain
|
||||
);
|
||||
|
||||
res.json({ website });
|
||||
}
|
||||
);
|
||||
|
@ -1,3 +1,4 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"include": ["./**/*"]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user