From 9299b583f506ed1ca3a8cd646d011cde358d7712 Mon Sep 17 00:00:00 2001 From: alin Date: Sat, 19 Apr 2025 15:22:46 +0800 Subject: [PATCH 01/13] add request log detail ui --- frontEnd/src/App.tsx | 28 ++++--- frontEnd/src/component/Layout.tsx | 28 +++---- .../page/RequestLogDetail/RequestBodyCard.tsx | 11 +++ .../RequestLogDetail/RequestHeadersCard.tsx | 21 +++++ .../RequestLogDetailPage.module.scss | 79 +++++++++++++++++++ .../RequestLogDetail/RequestLogDetailPage.tsx | 32 ++++++++ 6 files changed, 174 insertions(+), 25 deletions(-) create mode 100644 frontEnd/src/page/RequestLogDetail/RequestBodyCard.tsx create mode 100644 frontEnd/src/page/RequestLogDetail/RequestHeadersCard.tsx create mode 100644 frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.module.scss create mode 100644 frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.tsx diff --git a/frontEnd/src/App.tsx b/frontEnd/src/App.tsx index 57d760f..f264140 100644 --- a/frontEnd/src/App.tsx +++ b/frontEnd/src/App.tsx @@ -9,11 +9,12 @@ import { setProjectList } from "./slice/projectSlice"; import { useAppSelector } from "./store"; import { Toaster } from "react-hot-toast"; import { ConfigProvider, Spin, theme, App as AntApp } from "antd"; -import { Route, Routes, Navigate } from "react-router-dom"; +import { Route, Routes, Navigate, HashRouter } from "react-router-dom"; import ExpectationPage from "./page/ExpectationPage"; import LogPage from "./page/LogPage"; import ConfigPage from "./page/ConfigPage"; import { useEffect } from "react"; +import RequestLogDetailPage from "./page/RequestLogDetail/RequestLogDetailPage"; function App() { const dispatch = useDispatch(); @@ -30,11 +31,11 @@ function App() { useEffect(() => { if (systemConfigState.mode === "dark") { document.body.className = "dark_mode"; - document.documentElement.style.colorScheme = 'dark'; + document.documentElement.style.colorScheme = "dark"; document.getElementsByTagName("html")[0].style.background = "linear-gradient(to bottom, #262626 0, #262626 73px, #595959 73px) repeat-x"; } else { - document.documentElement.style.colorScheme = 'light'; + document.documentElement.style.colorScheme = "light"; document.getElementsByTagName("html")[0].style.background = "linear-gradient(to bottom, rgb(36, 41, 47) 0, rgb(36, 41, 47) 73px, white 73px) repeat-x"; document.body.className = ""; @@ -55,14 +56,23 @@ function App() { projectList.length === 0 ? ( ) : ( - + - } /> - } /> - } /> - } /> + }> + } /> + } /> + } /> + } + /> + + } + /> - + ) ) : ( diff --git a/frontEnd/src/component/Layout.tsx b/frontEnd/src/component/Layout.tsx index c50e1ab..df56e6f 100644 --- a/frontEnd/src/component/Layout.tsx +++ b/frontEnd/src/component/Layout.tsx @@ -1,28 +1,24 @@ import mStyle from "./Layout.module.scss"; import LeftNav from "./LeftNav"; -import { HashRouter } from "react-router-dom"; +import { HashRouter, Outlet } from "react-router-dom"; import ProjectInfo from "./project/ProjectInfo"; import * as React from "react"; -const Layout:React.FC<{ - children:React.ReactNode -}> = (props) => { +const Layout: React.FC<{}> = () => { return ( - -
-
- +
+
+ +
+
+
+
-
-
- -
-
- {props.children} -
+
+
- +
); }; diff --git a/frontEnd/src/page/RequestLogDetail/RequestBodyCard.tsx b/frontEnd/src/page/RequestLogDetail/RequestBodyCard.tsx new file mode 100644 index 0000000..a10f315 --- /dev/null +++ b/frontEnd/src/page/RequestLogDetail/RequestBodyCard.tsx @@ -0,0 +1,11 @@ + +import mStyle from "./RequestLogDetailPage.module.scss"; + +const RequestBodyCard = () =>{ + return
+
Request Body
+ +
+} + +export default RequestBodyCard; \ No newline at end of file diff --git a/frontEnd/src/page/RequestLogDetail/RequestHeadersCard.tsx b/frontEnd/src/page/RequestLogDetail/RequestHeadersCard.tsx new file mode 100644 index 0000000..619f665 --- /dev/null +++ b/frontEnd/src/page/RequestLogDetail/RequestHeadersCard.tsx @@ -0,0 +1,21 @@ +import mStyle from "./RequestLogDetailPage.module.scss"; + +const RequestHeadersCard = () => { + return ( +
+
Request Headers
+
+
+
Content-Type
+
application/json
+
+
+
Accept
+
*/*
+
+
+
+ ); +}; + +export default RequestHeadersCard; diff --git a/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.module.scss b/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.module.scss new file mode 100644 index 0000000..8f07f21 --- /dev/null +++ b/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.module.scss @@ -0,0 +1,79 @@ +.req_log_detail { + .req_tile { + color: #fff; + background-color: rgb(37, 99, 235); + padding: 20px; + display: flex; + align-items: center; + justify-content: space-between; + } + .til_left { + display: flex; + align-items: center; + } + .til_right { + display: flex; + align-items: center; + } + .req_method { + font-size: 22px; + font-weight: bold; + line-height: 35px; + } + .req_path { + font-size: 22px; + line-height: 35px; + } + .req_time { + font-size: 18px; + line-height: 25px; + } + .req_status { + font-size: 18px; + line-height: 25px; + background-color: rgb(16, 185, 129); + padding: 5px 20px; + border-radius: 99px; + } + + .req_content { + display: grid; + padding: 30px; + gap: 30px; + grid-template-columns: repeat(2, minmax(0, 1fr)); + + .request_col { + } + .response_col { + } + .card { + padding: 20px; + background-color: rgb(249, 250, 251); + border-radius: 10px; + color: #000; + } + .card_til { + font-size: 22px; + line-height: 35px; + display: flex; + align-items: center; + margin-bottom: 15px; + font-weight: 600; + } + .card_content { + } + .property_row { + font-size: 18px; + line-height: 22px; + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + margin-top: 10px; + margin-bottom: 10px; + .pro_label { + color: rgb(75 85 99 ); + } + .pro_value { + } + } + } +} diff --git a/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.tsx b/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.tsx new file mode 100644 index 0000000..b47683f --- /dev/null +++ b/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.tsx @@ -0,0 +1,32 @@ +import mStyle from "./RequestLogDetailPage.module.scss"; +import RequestHeadersCard from "./RequestHeadersCard"; + + + + + +const RequestLogDetailPage = () => { + return ( +
+
+
+
GET
+
/api/v1/users
+
+
+
2025-01-10 12:00:00
+
200
+
+
+ +
+
+ +
+
+
+
+ ); +}; + +export default RequestLogDetailPage; From c8713019d81fba75fd6e28b0fd13444c3a79de1c Mon Sep 17 00:00:00 2001 From: alin Date: Sun, 20 Apr 2025 21:32:57 +0800 Subject: [PATCH 02/13] add request log detail ui --- backEnd/src/controller/logController.ts | 38 +++++++++++ core/struct/params/LogParams.ts | 66 ++++++++++--------- frontEnd/src/App.tsx | 2 +- frontEnd/src/page/LogPage.tsx | 4 +- frontEnd/src/page/LogPageColumn.tsx | 39 ++++++++--- .../page/RequestLogDetail/RequestBodyCard.tsx | 35 +++++++--- .../RequestLogDetailPage.module.scss | 12 +++- .../RequestLogDetail/RequestLogDetailPage.tsx | 9 +-- 8 files changed, 148 insertions(+), 57 deletions(-) diff --git a/backEnd/src/controller/logController.ts b/backEnd/src/controller/logController.ts index 6eef790..d1992b0 100644 --- a/backEnd/src/controller/logController.ts +++ b/backEnd/src/controller/logController.ts @@ -13,6 +13,10 @@ import { DeleteAllRequestLogsPathParam, DeleteAllRequestLogsReqBody, DeleteAllRequestLogsReqQuery, + GetLogDetailPathParam, + GetLogDetailReqBody, + GetLogDetailReqQuery, + GetLogDetailResponse, ListLogPathParam, ListLogReqBody, ListLogReqQuery, @@ -78,6 +82,7 @@ export async function getLogRouter(path: string): Promise { } ); + /** * list the log view */ @@ -104,6 +109,39 @@ export async function getLogRouter(path: string): Promise { } ); + + /** + * get log item + */ + router.get( + `/detail/:logId`, + async ( + req: Request< + GetLogDetailPathParam, + GetLogDetailResponse, + GetLogDetailReqBody, + GetLogDetailReqQuery + >, + res: Response + ) => { + addCross(res); + const logId = parseInt(req.params.logId); + if (!logId) { + throw new ServerError(400, "log id not exist!"); + } + const projectId = req.body.projectId; + const collection = await getLogCollection(projectId, path); + + const logItem = collection.findOne({ id: logId }); + if (logItem === null) { + throw new ServerError(500, "log item not found!"); + } + res.json({ + logItem, + }); + } + ); + /** * get the log view logs */ diff --git a/core/struct/params/LogParams.ts b/core/struct/params/LogParams.ts index 62fa38f..676b44a 100644 --- a/core/struct/params/LogParams.ts +++ b/core/struct/params/LogParams.ts @@ -1,66 +1,68 @@ +import { LogM } from "../log"; + /** * list log */ -export interface ListLogPathParam{ +export interface ListLogPathParam {} -} +export interface ListLogReqBody {} -export interface ListLogReqBody{ +export interface ListLogReqQuery { + maxLogId?: number; + projectId: string; +} - +/** + * get log detail + */ +export interface GetLogDetailPathParam { + logId: string; } -export interface ListLogReqQuery{ - maxLogId?:number; - projectId:string; +export interface GetLogDetailReqBody { + projectId: string; } +export interface GetLogDetailReqQuery {} + +export interface GetLogDetailResponse { + logItem: LogM; +} /** * list log view */ -export interface ListLogViewPathParam{ - -} +export interface ListLogViewPathParam {} -export interface ListLogViewReqBody{ +export interface ListLogViewReqBody {} +export interface ListLogViewReqQuery { + projectId: string; } -export interface ListLogViewReqQuery{ - projectId:string; -} - - /** * list logs by logView */ -export interface ListLogViewLogsPathParam{ - logViewId:string; +export interface ListLogViewLogsPathParam { + logViewId: string; } -export interface ListLogViewLogsReqBody{ - -} +export interface ListLogViewLogsReqBody {} -export interface ListLogViewLogsReqQuery{ - projectId:string; - maxLogId:string|null; +export interface ListLogViewLogsReqQuery { + projectId: string; + maxLogId: string | null; } /** * delete all logs by logView */ -export interface DeleteAllRequestLogsPathParam{ - -} +export interface DeleteAllRequestLogsPathParam {} -export interface DeleteAllRequestLogsReqBody{ +export interface DeleteAllRequestLogsReqBody {} +export interface DeleteAllRequestLogsReqQuery { + projectId: string; } - -export interface DeleteAllRequestLogsReqQuery{ - projectId:string; -} \ No newline at end of file diff --git a/frontEnd/src/App.tsx b/frontEnd/src/App.tsx index f264140..709242c 100644 --- a/frontEnd/src/App.tsx +++ b/frontEnd/src/App.tsx @@ -68,7 +68,7 @@ function App() { /> } /> diff --git a/frontEnd/src/page/LogPage.tsx b/frontEnd/src/page/LogPage.tsx index 1f3e6e4..4a22457 100644 --- a/frontEnd/src/page/LogPage.tsx +++ b/frontEnd/src/page/LogPage.tsx @@ -51,6 +51,7 @@ import ChatMainComponent, { } from "src/component/chat/ChatMainComponent"; import { LinkOutlined, DisconnectOutlined } from "@ant-design/icons"; import { red, green } from "@ant-design/colors"; +import {useNavigate} from "react-router-dom"; function onLogsInsert( insertLog: LogM, @@ -134,6 +135,7 @@ const LogPage: React.FC = () => { const currentProject = projectState.projectList[projectState.curProjectIndex]; const [socketInstance, setSocketInstance] = useState(null); const [logs, setLogs] = useImmer>([]); + const navigate = useNavigate(); const expectationState = useAppSelector((state) => state.expectation); const getLogViewQuery = useQuery([currentProject.id], () => { @@ -217,7 +219,7 @@ const LogPage: React.FC = () => { ) .filter((item, index) => defaultColumnVisible[index]) .concat(customColumns) - .concat(getConfigColumn(dispatch)); + .concat(getConfigColumn(dispatch,navigate)); updateLogColumn(newLogColumn); }, [ tableColumns, diff --git a/frontEnd/src/page/LogPageColumn.tsx b/frontEnd/src/page/LogPageColumn.tsx index d821724..f18f163 100644 --- a/frontEnd/src/page/LogPageColumn.tsx +++ b/frontEnd/src/page/LogPageColumn.tsx @@ -5,6 +5,7 @@ import { DeleteOutlined, FilterOutlined, MessageOutlined, + FileSearchOutlined, } from "@ant-design/icons"; import { createSimpleFilter, FilterType, LogM } from "livemock-core/struct/log"; import { Dispatch, useState } from "react"; @@ -29,7 +30,7 @@ import mStyle from "./LogPageColumn.module.scss"; import { ReactComponent as Equalizer } from "../svg/equalizer.svg"; import { ReactComponent as Eye } from "../svg/eye.svg"; import { ReactComponent as EyeBlocked } from "../svg/eye-blocked.svg"; -import _ from "lodash"; +import _, { after } from "lodash"; import ReactJson from "react-json-view"; import { v4 as uuId } from "uuid"; import TextColumn from "../component/table/TextColumn"; @@ -37,14 +38,32 @@ import { addLogFilterReq } from "../server/logFilterServer"; import { toastPromise } from "../component/common"; import { ExpectationM } from "livemock-core/struct/expectation"; import ExpectationBriefComponent from "../component/log/ExpectationBriefComponent"; +import { NavigateFunction } from "react-router-dom"; -export function getConfigColumn(dispatch: Dispatch) { +export function getConfigColumn( + dispatch: Dispatch, + navigate: NavigateFunction, +) { return [ { dataIndex: "config", key: "config", width: "100px", - render: () =>
, + render: (text: string, record: LogM) => ( +
+
+ ), title: () => { return (
@@ -61,7 +80,7 @@ export function getConfigColumn(dispatch: Dispatch) { path: "", displayType: ColumnDisplayType.TEXT, visible: true, - }) + }), ); }} type={"text"} @@ -91,7 +110,7 @@ export function getDefaultColumn( refreshLogList: () => void, expectationMap: { [key: string]: ExpectationM; - } + }, ): ColumnsType { const res = [ { @@ -306,7 +325,7 @@ export function getDefaultColumnTitles() { function getDefaultColumnHead( name: string, dispatch: Dispatch, - index: number + index: number, ) { //const dispatch = useDispatch(); @@ -323,7 +342,7 @@ function getDefaultColumnHead( setDefaultColumnVisible({ index, visible: false, - }) + }), ); }} > @@ -385,7 +404,7 @@ const CustomColumnHead = ({ modifyTableColumn({ ...item, visible: false, - }) + }), ); }} > @@ -418,7 +437,7 @@ const CustomColumnHead = ({ export function getCustomColumn( items: Array, dispatch: Dispatch, - mode: "light" | "dark" + mode: "light" | "dark", ) { let res: ColumnType[] = []; items.forEach((item) => { @@ -429,7 +448,7 @@ export function getCustomColumn( export function transferColumn( item: TableColumnItem, dispatch: Dispatch, - mode: "light" | "dark" + mode: "light" | "dark", ): ColumnType { return { title: ( diff --git a/frontEnd/src/page/RequestLogDetail/RequestBodyCard.tsx b/frontEnd/src/page/RequestLogDetail/RequestBodyCard.tsx index a10f315..2393f0a 100644 --- a/frontEnd/src/page/RequestLogDetail/RequestBodyCard.tsx +++ b/frontEnd/src/page/RequestLogDetail/RequestBodyCard.tsx @@ -1,11 +1,30 @@ - import mStyle from "./RequestLogDetailPage.module.scss"; -const RequestBodyCard = () =>{ - return
-
Request Body
- -
-} +const RequestBodyCard = () => { + return ( +
+
Request Body
+
+

+      
+
+ ); +}; -export default RequestBodyCard; \ No newline at end of file +export default RequestBodyCard; diff --git a/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.module.scss b/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.module.scss index 8f07f21..3f9b275 100644 --- a/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.module.scss +++ b/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.module.scss @@ -70,10 +70,20 @@ margin-top: 10px; margin-bottom: 10px; .pro_label { - color: rgb(75 85 99 ); + color: rgb(75 85 99); } .pro_value { } } } + + .json_pre { + font-size: 16px; + line-height: 22px; + padding: 20px; + white-space: pre-wrap; + color: rgb(52 211 153); + background-color: rgb(17 24 39); + border-radius: 10px; + } } diff --git a/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.tsx b/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.tsx index b47683f..4f34ffd 100644 --- a/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.tsx +++ b/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.tsx @@ -1,11 +1,10 @@ import mStyle from "./RequestLogDetailPage.module.scss"; import RequestHeadersCard from "./RequestHeadersCard"; - - - - +import RequestBodyCard from "./RequestBodyCard"; +import { useParams } from "react-router-dom"; const RequestLogDetailPage = () => { + const params = useParams(); return (
@@ -22,9 +21,11 @@ const RequestLogDetailPage = () => {
+
+
{JSON.stringify(params)}
); }; From 6511e51b7002d84a6e8d8bc337079ae6e7ac2e0c Mon Sep 17 00:00:00 2001 From: alin Date: Sun, 20 Apr 2025 23:01:33 +0800 Subject: [PATCH 03/13] add request log detail ui --- backEnd/src/controller/logController.ts | 2 +- core/struct/params/LogParams.ts | 5 +-- .../page/RequestLogDetail/RequestBodyCard.tsx | 23 +++---------- .../RequestLogDetail/RequestHeadersCard.tsx | 23 ++++++++----- .../RequestLogDetail/RequestLogDetailPage.tsx | 33 ++++++++++++++----- frontEnd/src/server/logServer.ts | 12 +++++++ 6 files changed, 60 insertions(+), 38 deletions(-) diff --git a/backEnd/src/controller/logController.ts b/backEnd/src/controller/logController.ts index d1992b0..a215087 100644 --- a/backEnd/src/controller/logController.ts +++ b/backEnd/src/controller/logController.ts @@ -129,7 +129,7 @@ export async function getLogRouter(path: string): Promise { if (!logId) { throw new ServerError(400, "log id not exist!"); } - const projectId = req.body.projectId; + const projectId = req.query.projectId; const collection = await getLogCollection(projectId, path); const logItem = collection.findOne({ id: logId }); diff --git a/core/struct/params/LogParams.ts b/core/struct/params/LogParams.ts index 676b44a..10b2fcf 100644 --- a/core/struct/params/LogParams.ts +++ b/core/struct/params/LogParams.ts @@ -20,10 +20,11 @@ export interface GetLogDetailPathParam { } export interface GetLogDetailReqBody { - projectId: string; } -export interface GetLogDetailReqQuery {} +export interface GetLogDetailReqQuery { + projectId: string; +} export interface GetLogDetailResponse { logItem: LogM; diff --git a/frontEnd/src/page/RequestLogDetail/RequestBodyCard.tsx b/frontEnd/src/page/RequestLogDetail/RequestBodyCard.tsx index 2393f0a..0e0d116 100644 --- a/frontEnd/src/page/RequestLogDetail/RequestBodyCard.tsx +++ b/frontEnd/src/page/RequestLogDetail/RequestBodyCard.tsx @@ -1,27 +1,14 @@ +import React from "react"; import mStyle from "./RequestLogDetailPage.module.scss"; -const RequestBodyCard = () => { +const RequestBodyCard: React.FunctionComponent<{ + body: any; +}> = ({ body }) => { return (
Request Body
-

+        
{JSON.stringify(body, null, 2)}
); diff --git a/frontEnd/src/page/RequestLogDetail/RequestHeadersCard.tsx b/frontEnd/src/page/RequestLogDetail/RequestHeadersCard.tsx index 619f665..711e771 100644 --- a/frontEnd/src/page/RequestLogDetail/RequestHeadersCard.tsx +++ b/frontEnd/src/page/RequestLogDetail/RequestHeadersCard.tsx @@ -1,18 +1,23 @@ +import React from "react"; import mStyle from "./RequestLogDetailPage.module.scss"; -const RequestHeadersCard = () => { +const RequestHeadersCard: React.FunctionComponent<{ + headers: { + [key: string]: string | null | undefined; + }; +}> = ({ headers }) => { return (
Request Headers
-
-
Content-Type
-
application/json
-
-
-
Accept
-
*/*
-
+ {Object.keys(headers).map((key: string) => { + return ( +
+
{key}
+
{headers[key] ?? ''}
+
+ ); + })}
); diff --git a/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.tsx b/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.tsx index 4f34ffd..29021a2 100644 --- a/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.tsx +++ b/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.tsx @@ -2,30 +2,47 @@ import mStyle from "./RequestLogDetailPage.module.scss"; import RequestHeadersCard from "./RequestHeadersCard"; import RequestBodyCard from "./RequestBodyCard"; import { useParams } from "react-router-dom"; +import { useQuery } from "@tanstack/react-query"; +import { getRequestLogDetail } from "../../server/logServer"; +import { useAppSelector } from "../../store"; const RequestLogDetailPage = () => { - const params = useParams(); + const params = useParams<{ + logId: string; + }>(); + const projectState = useAppSelector((state) => state.project); + const currentProject = projectState.projectList[projectState.curProjectIndex]; + const getLogDetailQuery = useQuery([params.logId], () => { + if (!params.logId) { + throw new Error("No log detail found."); + } + return getRequestLogDetail(parseInt(params.logId), { + projectId: currentProject.id, + }); + }); return (
-
GET
-
/api/v1/users
+
{getLogDetailQuery.data?.logItem.req?.method}
+
{getLogDetailQuery.data?.logItem.req?.path}
2025-01-10 12:00:00
-
200
+
{getLogDetailQuery.data?.logItem.res?.status}
- - + + +
+
+ +
-
-
{JSON.stringify(params)}
); }; diff --git a/frontEnd/src/server/logServer.ts b/frontEnd/src/server/logServer.ts index 1c4aa6d..861afbd 100644 --- a/frontEnd/src/server/logServer.ts +++ b/frontEnd/src/server/logServer.ts @@ -1,5 +1,7 @@ import { DeleteAllRequestLogsReqQuery, + GetLogDetailReqQuery, + GetLogDetailResponse, ListLogViewLogsReqQuery, ListLogViewReqQuery, } from "livemock-core/struct/params/LogParams"; @@ -36,3 +38,13 @@ export async function deleteAllRequestLogs( const response = await superagent.delete(`${ServerUrl}/log`).query(query); return response.body; } + +export async function getRequestLogDetail( + logId: number, + query: GetLogDetailReqQuery, +): Promise { + const response = await superagent + .get(`${ServerUrl}/log/detail/${logId}`) + .query(query); + return response.body; +} From d601ce24437a7e1c09714d88e627209c08909315 Mon Sep 17 00:00:00 2001 From: alin Date: Tue, 22 Apr 2025 22:15:05 +0800 Subject: [PATCH 04/13] add request log detail ui --- frontEnd/src/App.css | 14 +++++++ frontEnd/src/page/LogPage.tsx | 2 +- frontEnd/src/page/LogPageColumn.tsx | 3 +- .../RequestLogDetailPage.module.scss | 4 ++ .../RequestLogDetail/RequestLogDetailPage.tsx | 40 +++++++++++++------ 5 files changed, 49 insertions(+), 14 deletions(-) diff --git a/frontEnd/src/App.css b/frontEnd/src/App.css index 93846f4..a70434a 100644 --- a/frontEnd/src/App.css +++ b/frontEnd/src/App.css @@ -141,3 +141,17 @@ html { ) repeat-x; } + +.blank10, +.blank20 { + height: 10px; + width: 100%; + clear: both; + display: block; + overflow: hidden; + line-height: 10px; +} +.blank20{ + height: 20px; + line-height: 20px; +} \ No newline at end of file diff --git a/frontEnd/src/page/LogPage.tsx b/frontEnd/src/page/LogPage.tsx index 4a22457..365a03d 100644 --- a/frontEnd/src/page/LogPage.tsx +++ b/frontEnd/src/page/LogPage.tsx @@ -219,7 +219,7 @@ const LogPage: React.FC = () => { ) .filter((item, index) => defaultColumnVisible[index]) .concat(customColumns) - .concat(getConfigColumn(dispatch,navigate)); + .concat(getConfigColumn(dispatch,navigate,currentProject.id)); updateLogColumn(newLogColumn); }, [ tableColumns, diff --git a/frontEnd/src/page/LogPageColumn.tsx b/frontEnd/src/page/LogPageColumn.tsx index f18f163..36f3ef5 100644 --- a/frontEnd/src/page/LogPageColumn.tsx +++ b/frontEnd/src/page/LogPageColumn.tsx @@ -43,6 +43,7 @@ import { NavigateFunction } from "react-router-dom"; export function getConfigColumn( dispatch: Dispatch, navigate: NavigateFunction, + projectId:string, ) { return [ { @@ -58,7 +59,7 @@ export function getConfigColumn( onClick={() => { const currentUrl = window.location.href.split("#")[0]; const newUrl = - currentUrl + "#" + `/requestLog/detail/${record.id}`; + currentUrl + "#" + `/requestLog/detail/${record.id}?projectId=${projectId}`; window.open(newUrl, "_blank"); }} /> diff --git a/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.module.scss b/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.module.scss index 3f9b275..0d83613 100644 --- a/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.module.scss +++ b/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.module.scss @@ -19,6 +19,7 @@ font-size: 22px; font-weight: bold; line-height: 35px; + margin-right: 20px; } .req_path { font-size: 22px; @@ -27,6 +28,7 @@ .req_time { font-size: 18px; line-height: 25px; + margin-right: 20px; } .req_status { font-size: 18px; @@ -41,6 +43,7 @@ padding: 30px; gap: 30px; grid-template-columns: repeat(2, minmax(0, 1fr)); + background:#fff; .request_col { } @@ -69,6 +72,7 @@ grid-template-columns: repeat(2, minmax(0, 1fr)); margin-top: 10px; margin-bottom: 10px; + word-break: break-all; .pro_label { color: rgb(75 85 99); } diff --git a/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.tsx b/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.tsx index 29021a2..154f233 100644 --- a/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.tsx +++ b/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.tsx @@ -1,46 +1,62 @@ import mStyle from "./RequestLogDetailPage.module.scss"; import RequestHeadersCard from "./RequestHeadersCard"; import RequestBodyCard from "./RequestBodyCard"; -import { useParams } from "react-router-dom"; +import {useLocation, useParams, useSearchParams} from "react-router-dom"; import { useQuery } from "@tanstack/react-query"; import { getRequestLogDetail } from "../../server/logServer"; import { useAppSelector } from "../../store"; +import {ClockCircleOutlined} from "@ant-design/icons"; const RequestLogDetailPage = () => { const params = useParams<{ logId: string; }>(); - const projectState = useAppSelector((state) => state.project); - const currentProject = projectState.projectList[projectState.curProjectIndex]; + const [searchParam, setSearchParam] = useSearchParams(); + const projectId = searchParam.get("projectId"); + if (projectId === null) { + throw new Error("No projectId provided"); + } const getLogDetailQuery = useQuery([params.logId], () => { if (!params.logId) { throw new Error("No log detail found."); } return getRequestLogDetail(parseInt(params.logId), { - projectId: currentProject.id, + projectId: projectId, }); }); return (
-
{getLogDetailQuery.data?.logItem.req?.method}
-
{getLogDetailQuery.data?.logItem.req?.path}
+
+ {getLogDetailQuery.data?.logItem.req?.method} +
+
+ {getLogDetailQuery.data?.logItem.req?.path} +
-
2025-01-10 12:00:00
-
{getLogDetailQuery.data?.logItem.res?.status}
+
2025-01-10 12:00:00
+
+ {getLogDetailQuery.data?.logItem.res?.status} +
- - + +
+
- - + +
+
From c1771bbeac291c2df3f5f447f96d9aefd5789ff4 Mon Sep 17 00:00:00 2001 From: alin Date: Sat, 17 May 2025 08:47:51 +0800 Subject: [PATCH 05/13] add desktop logdetail support --- core/struct/events/desktopEvents.ts | 2 + desktop/electron/handler/logViewHandler.ts | 41 ++++++- desktop/electron/main.ts | 53 ++++++-- desktop/electron/preload.ts | 29 ++++- desktop/src/App.css | 14 +++ desktop/src/App.tsx | 26 ++-- desktop/src/component/Layout.tsx | 28 ++--- desktop/src/page/LogPage.tsx | 9 +- desktop/src/page/LogPageColumn.tsx | 29 ++++- .../page/RequestLogDetail/RequestBodyCard.tsx | 17 +++ .../RequestLogDetail/RequestHeadersCard.tsx | 26 ++++ .../RequestLogDetailPage.module.scss | 114 ++++++++++++++++++ .../RequestLogDetail/RequestLogDetailPage.tsx | 69 +++++++++++ desktop/src/server/logServer.ts | 12 ++ .../RequestLogDetailPage.module.scss | 77 +++++++----- .../RequestLogDetail/RequestLogDetailPage.tsx | 2 +- 16 files changed, 473 insertions(+), 75 deletions(-) create mode 100644 desktop/src/page/RequestLogDetail/RequestBodyCard.tsx create mode 100644 desktop/src/page/RequestLogDetail/RequestHeadersCard.tsx create mode 100644 desktop/src/page/RequestLogDetail/RequestLogDetailPage.module.scss create mode 100644 desktop/src/page/RequestLogDetail/RequestLogDetailPage.tsx diff --git a/core/struct/events/desktopEvents.ts b/core/struct/events/desktopEvents.ts index 4f8194f..97b0669 100644 --- a/core/struct/events/desktopEvents.ts +++ b/core/struct/events/desktopEvents.ts @@ -35,6 +35,7 @@ export enum ActionEvents { export enum LogViewEvents{ ListLogView="ListLogView", ListLogViewLogs="ListLogViewLogs", + GetLogDetail="GetLogDetail", DeleteAllRequestLogs="DeleteAllRequestLogs", OnLogAdd="OnLogViewLogAdd", OnLogUpdate="OnLogViewLogUpdate", @@ -54,4 +55,5 @@ export enum LogFilterEvents{ export enum SystemEvents{ OpenAboutWindow = "OpenAboutWindow", + OpenNewWindow = "OpenNewWindow", } \ No newline at end of file diff --git a/desktop/electron/handler/logViewHandler.ts b/desktop/electron/handler/logViewHandler.ts index bab88f8..509cfc9 100644 --- a/desktop/electron/handler/logViewHandler.ts +++ b/desktop/electron/handler/logViewHandler.ts @@ -1,10 +1,16 @@ import * as electron from "electron"; import ipcMain = electron.ipcMain; -import { LogEvents, LogViewEvents } from "livemock-core/struct/events/desktopEvents"; +import { + LogEvents, + LogViewEvents, +} from "livemock-core/struct/events/desktopEvents"; import { DeleteAllRequestLogsPathParam, DeleteAllRequestLogsReqBody, DeleteAllRequestLogsReqQuery, + GetLogDetailPathParam, + GetLogDetailReqBody, + GetLogDetailReqQuery, ListLogViewLogsPathParam, ListLogViewLogsReqBody, ListLogViewLogsReqQuery, @@ -48,7 +54,7 @@ export async function setLogViewHandler(path: string) { reqQuery: ListLogViewLogsReqQuery, reqBody: ListLogViewLogsReqBody ) => { - let { maxLogId, projectId } = reqQuery; + const { maxLogId, projectId } = reqQuery; const lovViewId = reqParam.logViewId; if (!projectId) { throw new ServerError(400, "project id not exist!"); @@ -73,6 +79,29 @@ export async function setLogViewHandler(path: string) { } ); + ipcMain.handle( + LogViewEvents.GetLogDetail, + async ( + event, + reqParam: GetLogDetailPathParam, + reqQuery: GetLogDetailReqQuery, + reqBody: GetLogDetailReqBody + ) => { + const logId = parseInt(reqParam.logId); + if (!logId) { + throw new ServerError(400, "log id not exist!"); + } + const projectId = reqQuery.projectId; + const collection = await getLogCollection(projectId, path); + + const logItem = collection.findOne({ id: logId }); + if (logItem === null) { + throw new ServerError(500, "log item not found!"); + } + return { logItem }; + } + ); + ipcMain.handle( LogViewEvents.DeleteAllRequestLogs, async ( @@ -100,24 +129,24 @@ export function logViewEventHandler(webContent: WebContents) { } logViewEventHandlerInit = true; logViewEventEmitter.on("insert", (arg: { log: LogM; logViewId: string }) => { - let { log, logViewId } = arg; + const { log, logViewId } = arg; webContent.send(LogViewEvents.OnLogAdd, { log, logViewId }); }); logViewEventEmitter.on("update", (arg: { log: LogM; logViewId: string }) => { - let { log, logViewId } = arg; + const { log, logViewId } = arg; webContent.send(LogViewEvents.OnLogUpdate, { log, logViewId }); }); logViewEventEmitter.on("delete", (arg: { log: LogM; logViewId: string }) => { - let { log, logViewId } = arg; + const { log, logViewId } = arg; webContent.send(LogViewEvents.OnLogDelete, { log, logViewId }); }); logEventEmitter.on( "update", (arg: { projectId: string; log: LogM; oldLog: LogM }) => { - let { oldLog, log, projectId } = arg; + const { oldLog, log, projectId } = arg; webContent.send(LogEvents.OnLogUpdate, { log, projectId }); } ); diff --git a/desktop/electron/main.ts b/desktop/electron/main.ts index 0e19d90..2136545 100644 --- a/desktop/electron/main.ts +++ b/desktop/electron/main.ts @@ -16,10 +16,10 @@ import { systemVersion } from "./config"; import * as electron from "electron"; import ipcMain = electron.ipcMain; import { SystemEvents } from "livemock-core/struct/events/desktopEvents"; -import log from 'electron-log/main'; +import log from "electron-log/main"; import { sysEventEmitter } from "./common/eventEmitters"; import { SystemEvent } from "livemock-core/struct/events/systemEvent"; -import {addWsEventListeners} from "./common/eventListener"; +import { addWsEventListeners } from "./common/eventListener"; log.initialize(); log.errorHandler.startCatching(); @@ -44,8 +44,7 @@ const VITE_DEV_SERVER_URL = process.env["VITE_DEV_SERVER_URL"]; const env = process.env["PROJECT_ENV"]; - -sysEventEmitter.on(SystemEvent.START,async () => { +sysEventEmitter.on(SystemEvent.START, async () => { const systemCollection = await getSystemCollection(app.getPath("userData")); const systemConfig = systemCollection.findOne({}); if (systemConfig) { @@ -63,7 +62,7 @@ async function createWindow() { }, }); await Promise.all( - sysEventEmitter.listeners(SystemEvent.START).map((listener) => listener()) + sysEventEmitter.listeners(SystemEvent.START).map((listener) => listener()) ); await setProjectHandler(app.getPath("userData")); @@ -89,10 +88,10 @@ async function createWindow() { if (env === "dev") { win.loadURL("http://localhost:5173"); - win.webContents.on('did-fail-load', () => { + win.webContents.on("did-fail-load", () => { setTimeout(() => { win?.loadURL("http://localhost:5173"); - },3000); + }, 3000); }); win.webContents.openDevTools(); } else { @@ -101,6 +100,31 @@ async function createWindow() { } } +function openNewWindow(hash: string, width: number, height: number) { + if (!win) { + return; + } + const newWin = new BrowserWindow({ + modal: false, + show: true, + width: width, + height: height, + icon: path.join(process.env.PUBLIC, "logo.png"), + webPreferences: { + preload: path.join(__dirname, "preload.js"), + nodeIntegration: true, + }, + }); + if (env === "dev") { + newWin.loadURL(`http://localhost:5173/#${hash}`); + newWin.webContents.openDevTools(); + } else { + newWin.loadFile(path.join(process.env.DIST, "index.html"), { + hash: hash, + }); + } +} + function createAboutWindow() { if (!win) { return; @@ -121,10 +145,14 @@ function createAboutWindow() { aboutWin.loadFile(path.join(process.env.DIST, `about.html`)); } aboutWin.webContents.on("did-finish-load", () => { - aboutWin.webContents.executeJavaScript(` + aboutWin.webContents + .executeJavaScript( + ` const ele = document.querySelector("#version"); ele.innerHTML = '${app.getVersion()}'; - `).catch(console.error); + ` + ) + .catch(console.error); }); } @@ -132,6 +160,13 @@ ipcMain.handle(SystemEvents.OpenAboutWindow, () => { createAboutWindow(); }); +ipcMain.handle( + SystemEvents.OpenNewWindow, + (event,hash: string, width: number, height: number) => { + openNewWindow(hash, width, height); + } +); + app.on("window-all-closed", () => { win = null; app.quit(); diff --git a/desktop/electron/preload.ts b/desktop/electron/preload.ts index a2f7ef2..b8ef25c 100644 --- a/desktop/electron/preload.ts +++ b/desktop/electron/preload.ts @@ -18,10 +18,12 @@ import * as electron from "electron"; import { ActionEvents, ExpectationEvents, + LogEvents, LogFilterEvents, LogViewEvents, MatcherEvents, ProjectEvents, + SystemEvents, } from "livemock-core/struct/events/desktopEvents"; import { CreateExpectationPathParam, @@ -67,6 +69,9 @@ import { DeleteAllRequestLogsPathParam, DeleteAllRequestLogsReqBody, DeleteAllRequestLogsReqQuery, + GetLogDetailPathParam, + GetLogDetailReqBody, + GetLogDetailReqQuery, ListLogViewLogsPathParam, ListLogViewLogsReqBody, ListLogViewLogsReqQuery, @@ -143,7 +148,7 @@ export const api = { ); }, deleteProject: ({ projectId }: { projectId: string }) => { - return ipcRenderer.invoke(ProjectEvents.DeleteProject,projectId); + return ipcRenderer.invoke(ProjectEvents.DeleteProject, projectId); }, startProject: ({ projectId }: { projectId: string }) => { return ipcRenderer.invoke(ProjectEvents.StartProject, projectId); @@ -339,6 +344,18 @@ export const api = { reqBody ); }, + getLogDetail: ( + reqParam: GetLogDetailPathParam, + reqQuery: GetLogDetailReqQuery, + reqBody: GetLogDetailReqBody + ) => { + return ipcRenderer.invoke( + LogViewEvents.GetLogDetail, + reqParam, + reqQuery, + reqBody + ); + }, }, logFilter: { createLogFilter: ( @@ -390,6 +407,16 @@ export const api = { ); }, }, + system: { + openNewWindow: (hash: string, width: number, height: number) => { + return ipcRenderer.invoke( + SystemEvents.OpenNewWindow, + hash, + width, + height + ); + }, + }, }; electron.contextBridge.exposeInMainWorld("api", api); diff --git a/desktop/src/App.css b/desktop/src/App.css index 61f471a..6754d1c 100644 --- a/desktop/src/App.css +++ b/desktop/src/App.css @@ -130,3 +130,17 @@ html { ) repeat-x; } + +.blank10, +.blank20 { + height: 10px; + width: 100%; + clear: both; + display: block; + overflow: hidden; + line-height: 10px; +} +.blank20{ + height: 20px; + line-height: 20px; +} diff --git a/desktop/src/App.tsx b/desktop/src/App.tsx index 6b234c7..8938989 100644 --- a/desktop/src/App.tsx +++ b/desktop/src/App.tsx @@ -9,11 +9,12 @@ import { setProjectList } from "./slice/projectSlice"; import { useAppSelector } from "./store"; import { Toaster } from "react-hot-toast"; import { Spin, ConfigProvider, theme, App as AntApp } from "antd"; -import { Route, Routes, Navigate } from "react-router-dom"; +import {Route, Routes, Navigate, HashRouter} from "react-router-dom"; import ExpectationPage from "./page/ExpectationPage"; import ConfigPage from "./page/ConfigPage"; import LogPage from "./page/LogPage"; import { useEffect } from "react"; +import RequestLogDetailPage from "./page/RequestLogDetail/RequestLogDetailPage"; function App() { const dispatch = useDispatch(); @@ -54,14 +55,21 @@ function App() { projectList.length === 0 ? ( ) : ( - - - } /> - }> - } /> - } /> - - + + + }> + } /> + }> + } /> + } /> + + } + /> + + + ) ) : ( diff --git a/desktop/src/component/Layout.tsx b/desktop/src/component/Layout.tsx index c50e1ab..71c3659 100644 --- a/desktop/src/component/Layout.tsx +++ b/desktop/src/component/Layout.tsx @@ -1,28 +1,24 @@ import mStyle from "./Layout.module.scss"; import LeftNav from "./LeftNav"; -import { HashRouter } from "react-router-dom"; +import { Outlet } from "react-router-dom"; import ProjectInfo from "./project/ProjectInfo"; import * as React from "react"; -const Layout:React.FC<{ - children:React.ReactNode -}> = (props) => { +const Layout: React.FC = () => { return ( - -
-
- +
+
+ +
+
+
+
-
-
- -
-
- {props.children} -
+
+
- +
); }; diff --git a/desktop/src/page/LogPage.tsx b/desktop/src/page/LogPage.tsx index eb4e2c4..9820c77 100644 --- a/desktop/src/page/LogPage.tsx +++ b/desktop/src/page/LogPage.tsx @@ -35,7 +35,10 @@ import { listLogViewReq, listLogViewLogs } from "../server/logServer"; import { binarySearch, toastPromise } from "../component/common"; import { Updater, useImmer } from "use-immer"; import { ColumnsType } from "antd/es/table/interface"; -import { LogEvents, LogViewEvents } from "livemock-core/struct/events/desktopEvents"; +import { + LogEvents, + LogViewEvents, +} from "livemock-core/struct/events/desktopEvents"; import IpcRendererEvent = Electron.IpcRendererEvent; import FilterRowComponent from "../component/log/FilterRowComponent"; import PresetFilterRowComponent from "../component/log/PresetFilterRowComponent"; @@ -48,6 +51,7 @@ import ChatMainComponent, { } from "../component/chat/ChatMainComponent"; import { DisconnectOutlined, LinkOutlined } from "@ant-design/icons"; import { red, green } from "@ant-design/colors"; +import { useNavigate } from "react-router-dom"; function onLogsInsert( insertLog: LogM, @@ -131,6 +135,7 @@ const LogPage: React.FC = () => { const projectState = useAppSelector((state) => state.project); const currentProject = projectState.projectList[projectState.curProjectIndex]; const [logs, setLogs] = useImmer>([]); + const navigate = useNavigate(); const expectationState = useAppSelector((state) => state.expectation); @@ -215,7 +220,7 @@ const LogPage: React.FC = () => { ) .filter((item, index) => defaultColumnVisible[index]) .concat(customColumns) - .concat(getConfigColumn(dispatch)); + .concat(getConfigColumn(dispatch, navigate, currentProject.id)); updateLogColumn(newLogColumn); }, [ tableColumns, diff --git a/desktop/src/page/LogPageColumn.tsx b/desktop/src/page/LogPageColumn.tsx index b6f7650..62b282d 100644 --- a/desktop/src/page/LogPageColumn.tsx +++ b/desktop/src/page/LogPageColumn.tsx @@ -5,6 +5,7 @@ import { DeleteOutlined, FilterOutlined, MessageOutlined, + FileSearchOutlined, } from "@ant-design/icons"; import { createSimpleFilter, FilterType, LogM } from "livemock-core/struct/log"; import { Dispatch, useState } from "react"; @@ -29,7 +30,7 @@ import mStyle from "./LogPageColumn.module.scss"; import { ReactComponent as Equalizer } from "../svg/equalizer.svg"; import { ReactComponent as Eye } from "../svg/eye.svg"; import { ReactComponent as EyeBlocked } from "../svg/eye-blocked.svg"; -import _ from "lodash"; +import _, { after } from "lodash"; import ReactJson from "react-json-view"; import { v4 as uuId } from "uuid"; import TextColumn from "../component/table/TextColumn"; @@ -37,14 +38,36 @@ import { addLogFilterReq } from "../server/logFilterServer"; import { toastPromise } from "../component/common"; import ExpectationBriefComponent from "../component/log/ExpectationBriefComponent"; import { ExpectationM } from "livemock-core/build/struct/expectation"; +import { NavigateFunction } from "react-router-dom"; -export function getConfigColumn(dispatch: Dispatch) { +export function getConfigColumn( + dispatch: Dispatch, + navigate: NavigateFunction, + projectId: string +) { return [ { dataIndex: "config", key: "config", width: "100px", - render: () =>
, + render: (text: string, record: LogM) => { + return ( +
+
+ ); + }, title: () => { return (
diff --git a/desktop/src/page/RequestLogDetail/RequestBodyCard.tsx b/desktop/src/page/RequestLogDetail/RequestBodyCard.tsx new file mode 100644 index 0000000..0e0d116 --- /dev/null +++ b/desktop/src/page/RequestLogDetail/RequestBodyCard.tsx @@ -0,0 +1,17 @@ +import React from "react"; +import mStyle from "./RequestLogDetailPage.module.scss"; + +const RequestBodyCard: React.FunctionComponent<{ + body: any; +}> = ({ body }) => { + return ( +
+
Request Body
+
+
{JSON.stringify(body, null, 2)}
+
+
+ ); +}; + +export default RequestBodyCard; diff --git a/desktop/src/page/RequestLogDetail/RequestHeadersCard.tsx b/desktop/src/page/RequestLogDetail/RequestHeadersCard.tsx new file mode 100644 index 0000000..711e771 --- /dev/null +++ b/desktop/src/page/RequestLogDetail/RequestHeadersCard.tsx @@ -0,0 +1,26 @@ +import React from "react"; +import mStyle from "./RequestLogDetailPage.module.scss"; + +const RequestHeadersCard: React.FunctionComponent<{ + headers: { + [key: string]: string | null | undefined; + }; +}> = ({ headers }) => { + return ( +
+
Request Headers
+
+ {Object.keys(headers).map((key: string) => { + return ( +
+
{key}
+
{headers[key] ?? ''}
+
+ ); + })} +
+
+ ); +}; + +export default RequestHeadersCard; diff --git a/desktop/src/page/RequestLogDetail/RequestLogDetailPage.module.scss b/desktop/src/page/RequestLogDetail/RequestLogDetailPage.module.scss new file mode 100644 index 0000000..fa5cc0c --- /dev/null +++ b/desktop/src/page/RequestLogDetail/RequestLogDetailPage.module.scss @@ -0,0 +1,114 @@ +.req_log_detail { + .req_tile { + color: #fff; + background-color: rgb(37, 99, 235); + padding: 2rem; + display: flex; + align-items: center; + justify-content: space-between; + } + .til_left { + display: flex; + align-items: center; + } + .til_right { + display: flex; + align-items: center; + } + .req_method { + font-size: 2.2rem; + font-weight: bold; + line-height: 3.5rem; + margin-right: 2rem; + } + .req_path { + font-size: 2.2rem; + line-height: 3.5rem; + } + .req_time { + font-size: 1.8rem; + line-height: 2.5rem; + margin-right: 2rem; + } + .req_status { + font-size: 1.8rem; + line-height: 2.5rem; + padding: .5rem 2rem; + border-radius: 9.9rem; + } + + .req_status_processing{ + background-color: #ff7a45; + } + .req_status_error{ + background-color: #ff4d4f; + } + .req_status_success{ + // background-color: #52c41a; + background-color: rgb(16, 185, 129); + } + + .req_content { + display: grid; + padding: 2rem; + gap: 2rem; + grid-template-columns: repeat(2, minmax(0, 1fr)); + background:#fff; + + .request_col { + } + .response_col { + } + .card { + padding: 2rem; + background-color: rgb(249, 250, 251); + border-radius: 1rem; + color: #000; + } + .card_til { + font-size: 2.2rem; + line-height: 3.5rem; + display: flex; + align-items: center; + margin-bottom: 1.5rem; + font-weight: 600; + } + .card_content { + } + .property_row { + font-size: 1.6rem; + line-height: 2.2rem; + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + margin-top: 1rem; + margin-bottom: 1rem; + word-break: break-all; + .pro_label { + color: rgb(75 85 99); + } + .pro_value { + } + } + } + + .json_pre { + font-size: 1.4rem; + line-height: 2.2rem; + padding: 2rem; + white-space: pre-wrap; + color: rgb(52 211 153); + background-color: rgb(17 24 39); + border-radius: 1rem; + } +} + +html{ + font-size: 8px; + @media (min-width: 768px) { + font-size: 10px; + } + @media (min-width: 1024px) { + font-size: 12px; + } +} + diff --git a/desktop/src/page/RequestLogDetail/RequestLogDetailPage.tsx b/desktop/src/page/RequestLogDetail/RequestLogDetailPage.tsx new file mode 100644 index 0000000..ffbffdd --- /dev/null +++ b/desktop/src/page/RequestLogDetail/RequestLogDetailPage.tsx @@ -0,0 +1,69 @@ +import mStyle from "./RequestLogDetailPage.module.scss"; +import RequestHeadersCard from "./RequestHeadersCard"; +import RequestBodyCard from "./RequestBodyCard"; +import { useParams, useSearchParams } from "react-router-dom"; +import { useQuery } from "@tanstack/react-query"; +import { getRequestLogDetail } from "../../server/logServer"; +import { ClockCircleOutlined } from "@ant-design/icons"; + +const RequestLogDetailPage = () => { + const params = useParams<{ + logId: string; + }>(); + const [searchParam, setSearchParam] = useSearchParams(); + const projectId = searchParam.get("projectId"); + if (projectId === null) { + throw new Error("No projectId provided"); + } + const getLogDetailQuery = useQuery([params.logId], () => { + if (!params.logId) { + throw new Error("No log detail found."); + } + return getRequestLogDetail(params.logId, projectId); + }); + return ( +
+
+
+
+ {getLogDetailQuery.data?.logItem.req?.method} +
+
+ {getLogDetailQuery.data?.logItem.req?.path} +
+
+
+
+ 2025-01-10 12:00:00 +
+
+ {getLogDetailQuery.data?.logItem.res?.status} +
+
+
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ ); +}; + +export default RequestLogDetailPage; diff --git a/desktop/src/server/logServer.ts b/desktop/src/server/logServer.ts index c15c3c7..f474fd5 100644 --- a/desktop/src/server/logServer.ts +++ b/desktop/src/server/logServer.ts @@ -1,5 +1,6 @@ import { DeleteAllRequestLogsReqQuery, + GetLogDetailResponse, ListLogViewLogsReqQuery, ListLogViewReqQuery, } from "livemock-core/struct/params/LogParams"; @@ -27,3 +28,14 @@ export async function deleteAllRequestLogs( ): Promise { return window.api.logView.deleteAllRequestLogs({}, query, {}); } + +export async function getRequestLogDetail( + logId: string, + projectId: string +): Promise { + return window.api.logView.getLogDetail( + { logId }, + { projectId: projectId }, + {} + ); +} diff --git a/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.module.scss b/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.module.scss index 0d83613..fa5cc0c 100644 --- a/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.module.scss +++ b/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.module.scss @@ -2,7 +2,7 @@ .req_tile { color: #fff; background-color: rgb(37, 99, 235); - padding: 20px; + padding: 2rem; display: flex; align-items: center; justify-content: space-between; @@ -16,32 +16,42 @@ align-items: center; } .req_method { - font-size: 22px; + font-size: 2.2rem; font-weight: bold; - line-height: 35px; - margin-right: 20px; + line-height: 3.5rem; + margin-right: 2rem; } .req_path { - font-size: 22px; - line-height: 35px; + font-size: 2.2rem; + line-height: 3.5rem; } .req_time { - font-size: 18px; - line-height: 25px; - margin-right: 20px; + font-size: 1.8rem; + line-height: 2.5rem; + margin-right: 2rem; } .req_status { - font-size: 18px; - line-height: 25px; + font-size: 1.8rem; + line-height: 2.5rem; + padding: .5rem 2rem; + border-radius: 9.9rem; + } + + .req_status_processing{ + background-color: #ff7a45; + } + .req_status_error{ + background-color: #ff4d4f; + } + .req_status_success{ + // background-color: #52c41a; background-color: rgb(16, 185, 129); - padding: 5px 20px; - border-radius: 99px; } .req_content { display: grid; - padding: 30px; - gap: 30px; + padding: 2rem; + gap: 2rem; grid-template-columns: repeat(2, minmax(0, 1fr)); background:#fff; @@ -50,28 +60,28 @@ .response_col { } .card { - padding: 20px; + padding: 2rem; background-color: rgb(249, 250, 251); - border-radius: 10px; + border-radius: 1rem; color: #000; } .card_til { - font-size: 22px; - line-height: 35px; + font-size: 2.2rem; + line-height: 3.5rem; display: flex; align-items: center; - margin-bottom: 15px; + margin-bottom: 1.5rem; font-weight: 600; } .card_content { } .property_row { - font-size: 18px; - line-height: 22px; + font-size: 1.6rem; + line-height: 2.2rem; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); - margin-top: 10px; - margin-bottom: 10px; + margin-top: 1rem; + margin-bottom: 1rem; word-break: break-all; .pro_label { color: rgb(75 85 99); @@ -82,12 +92,23 @@ } .json_pre { - font-size: 16px; - line-height: 22px; - padding: 20px; + font-size: 1.4rem; + line-height: 2.2rem; + padding: 2rem; white-space: pre-wrap; color: rgb(52 211 153); background-color: rgb(17 24 39); - border-radius: 10px; + border-radius: 1rem; } } + +html{ + font-size: 8px; + @media (min-width: 768px) { + font-size: 10px; + } + @media (min-width: 1024px) { + font-size: 12px; + } +} + diff --git a/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.tsx b/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.tsx index 154f233..a1509c6 100644 --- a/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.tsx +++ b/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.tsx @@ -37,7 +37,7 @@ const RequestLogDetailPage = () => {
2025-01-10 12:00:00
-
+
{getLogDetailQuery.data?.logItem.res?.status}
From c506115922f8e9006767e5ef2a794dbd7ab8d86a Mon Sep 17 00:00:00 2001 From: alin Date: Sat, 17 May 2025 09:07:17 +0800 Subject: [PATCH 06/13] add request log detail support --- .../RequestLogDetail/RequestLogDetailPage.tsx | 21 +++++++++++-- .../RequestLogDetail/RequestLogDetailPage.tsx | 31 ++++++++++++++++--- 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/desktop/src/page/RequestLogDetail/RequestLogDetailPage.tsx b/desktop/src/page/RequestLogDetail/RequestLogDetailPage.tsx index ffbffdd..ddf606e 100644 --- a/desktop/src/page/RequestLogDetail/RequestLogDetailPage.tsx +++ b/desktop/src/page/RequestLogDetail/RequestLogDetailPage.tsx @@ -21,6 +21,20 @@ const RequestLogDetailPage = () => { } return getRequestLogDetail(params.logId, projectId); }); + function getStatusClass(reqCode: number | undefined) { + if (!reqCode) { + return ""; + } + if (reqCode < 200) { + return mStyle.req_status_processing; + } else if (reqCode < 300) { + return mStyle.req_status_success; + } else if (reqCode < 400) { + return mStyle.req_status_processing; + } else { + return mStyle.req_status_error; + } + } return (
@@ -37,9 +51,10 @@ const RequestLogDetailPage = () => { 2025-01-10 12:00:00
{getLogDetailQuery.data?.logItem.res?.status}
diff --git a/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.tsx b/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.tsx index a1509c6..e8634d0 100644 --- a/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.tsx +++ b/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.tsx @@ -1,11 +1,11 @@ import mStyle from "./RequestLogDetailPage.module.scss"; import RequestHeadersCard from "./RequestHeadersCard"; import RequestBodyCard from "./RequestBodyCard"; -import {useLocation, useParams, useSearchParams} from "react-router-dom"; +import { useLocation, useParams, useSearchParams } from "react-router-dom"; import { useQuery } from "@tanstack/react-query"; import { getRequestLogDetail } from "../../server/logServer"; import { useAppSelector } from "../../store"; -import {ClockCircleOutlined} from "@ant-design/icons"; +import { ClockCircleOutlined } from "@ant-design/icons"; const RequestLogDetailPage = () => { const params = useParams<{ @@ -24,6 +24,22 @@ const RequestLogDetailPage = () => { projectId: projectId, }); }); + + function getStatusClass(reqCode: number | undefined) { + if (!reqCode) { + return ""; + } + if (reqCode < 200) { + return mStyle.req_status_processing; + } else if (reqCode < 300) { + return mStyle.req_status_success; + } else if (reqCode < 400) { + return mStyle.req_status_processing; + } else { + return mStyle.req_status_error; + } + } + return (
@@ -36,8 +52,15 @@ const RequestLogDetailPage = () => {
-
2025-01-10 12:00:00
-
+
+ 2025-01-10 12:00:00 +
+
{getLogDetailQuery.data?.logItem.res?.status}
From 32dd9d0d72dd96d798667c9fa1d7cc9167953c1c Mon Sep 17 00:00:00 2001 From: alin Date: Sat, 17 May 2025 16:31:22 +0800 Subject: [PATCH 07/13] add docker deploy method --- .dockerignore | 13 +++++++++ Dockerfile | 52 ++++++++++++++++++++++++++++++++++++ backEnd/src/config/config.ts | 50 ++++++++++++++++++++++++++++++++++ backEnd/src/index.ts | 20 ++++++++------ docker-compose.yml | 18 +++++++++++++ package.json | 8 ++++-- 6 files changed, 151 insertions(+), 10 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 backEnd/src/config/config.ts create mode 100644 docker-compose.yml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..b1c8554 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,13 @@ +.git +.gitignore +node_modules +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +*.log +desktop/dist +desktop/node_modules +.idea +README.md +img \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..51df230 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,52 @@ +# Build stage +FROM node:20-alpine as builder + +WORKDIR /app + +# Copy package.json and yarn related files +COPY package.json .yarnrc.yml yarn.lock ./ +COPY .yarn ./.yarn + +# Copy workspace package.json files +COPY core/package.json ./core/ +COPY frontEnd/package.json ./frontEnd/ +COPY backEnd/package.json ./backEnd/ + +# Install dependencies +RUN yarn install + +# Copy source code +COPY . . + +# Build application +RUN yarn web-build + +# Production stage +FROM node:20-alpine + +WORKDIR /app + +# Create data directory +RUN mkdir -p /app/data + +# Copy necessary files +COPY --from=builder /app/package.json /app/yarn.lock /app/.yarnrc.yml ./ +COPY --from=builder /app/.yarn ./.yarn +COPY --from=builder /app/backEnd ./backEnd +COPY --from=builder /app/frontEnd/dist ./frontEnd/dist +COPY --from=builder /app/core/dist ./core/dist + +# Install production dependencies +RUN yarn workspaces focus --production + +# Set data directory permissions +RUN chown -R node:node /app/data + +# Switch to non-root user +USER node + +# Expose port +EXPOSE 3000 + +# Start application +CMD ["yarn", "web-start"] \ No newline at end of file diff --git a/backEnd/src/config/config.ts b/backEnd/src/config/config.ts new file mode 100644 index 0000000..c8870d6 --- /dev/null +++ b/backEnd/src/config/config.ts @@ -0,0 +1,50 @@ +import path from 'path'; +import fs from 'fs'; + +interface Config { + database: { + path: string; + } +} + +// Default configuration +const defaultConfig: Config = { + database: { + path: 'db' + } +}; + +// Ensure directory exists +function ensureDirectoryExists(dirPath: string) { + if (!fs.existsSync(dirPath)) { + fs.mkdirSync(dirPath, { recursive: true }); + } +} + +// Check if running in test environment +function isTestEnvironment(): boolean { + return process.env.NODE_ENV === 'test' || + process.env.JEST_WORKER_ID !== undefined || + process.argv.some(arg => arg.includes('jest')); +} + +// Load configuration from environment variables +export function loadConfig(): Config { + const config = { + database: { + path: process.env.LIVEMOCK_DB_PATH || defaultConfig.database.path + } + }; + + // Only create directory in non-test environment + if (!isTestEnvironment()) { + ensureDirectoryExists(config.database.path); + } + + return config; +} + +// Get current configuration +export function getConfig(): Config { + return loadConfig(); +} \ No newline at end of file diff --git a/backEnd/src/index.ts b/backEnd/src/index.ts index 0074aa4..46b62c5 100644 --- a/backEnd/src/index.ts +++ b/backEnd/src/index.ts @@ -10,6 +10,7 @@ import { getSystemCollection } from "./db/dbManager"; import { sysEventEmitter } from "./common/eventEmitters"; import { SystemEvent } from "livemock-core/struct/events/systemEvent"; import { addWsEventListeners } from "./common/eventListener"; +import { getConfig } from "./config/config"; const { Server } = require("socket.io"); @@ -22,8 +23,11 @@ const io = new Server(http, { }); export const systemVersion = 801; +const config = getConfig(); +const dbPath = config.database.path; + sysEventEmitter.on(SystemEvent.START, async () => { - const systemCollection = await getSystemCollection("db"); + const systemCollection = await getSystemCollection(dbPath); const systemConfig = systemCollection.findOne({}); if (systemConfig) { } else { @@ -39,17 +43,17 @@ addWsEventListeners(); sysEventEmitter.listeners(SystemEvent.START).map((listener) => listener()) ); - server.use("/project", await getProjectRouter("db")); - server.use("/expectation", getExpectationRouter("db")); - server.use("/matcher", getMatcherRouter("db")); - server.use("/action", await getActionRouter("db")); - server.use("/logFilter", await getLogFilterRouter("db")); - server.use("/log", await getLogRouter("db")); + server.use("/project", await getProjectRouter(dbPath)); + server.use("/expectation", getExpectationRouter(dbPath)); + server.use("/matcher", getMatcherRouter(dbPath)); + server.use("/action", await getActionRouter(dbPath)); + server.use("/logFilter", await getLogFilterRouter(dbPath)); + server.use("/log", await getLogRouter(dbPath)); server.use("/dashboard", express.static("../frontEnd/dist")); server.all("/", (req, res) => { res.redirect("/dashboard"); }); - await addLogListener(io, "db"); + await addLogListener(io, dbPath); server.use(CustomErrorMiddleware); http.listen(9002, () => { diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..a542903 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,18 @@ +version: '3.8' + +services: + livemock: + build: + context: . + dockerfile: Dockerfile + ports: + - "3000:3000" + environment: + - NODE_ENV=production + - LIVEMOCK_DB_PATH=/app/data/db + restart: unless-stopped + volumes: + - livemock_data:/app/data + +volumes: + livemock_data: \ No newline at end of file diff --git a/package.json b/package.json index eb66388..870c73b 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,11 @@ "web-dev": "concurrently \"npm run back-end-dev\" \"npm run front-end-dev\"", "desktop-dev": "yarn workspace livemock-core build && yarn workspace livemock dev", "web-build": "yarn workspace livemock-core build && yarn workspace front-end build", - "web-start": "yarn workspace back-end start" + "web-start": "yarn workspace back-end start", + "test": "yarn workspaces foreach run test", + "test:back-end": "yarn workspace back-end test", + "test:core": "yarn workspace livemock-core test", + "test:watch": "yarn workspaces foreach run test --watch" }, "repository": { "type": "git", @@ -46,5 +50,5 @@ "express": "4.18.2", "uuid": "9.0.1" }, - "packageManager": "yarn@4.5.2" + "packageManager": "yarn@4.5.3" } From 92d7e0766f9a79deaeec91fb9672152adcadd365 Mon Sep 17 00:00:00 2001 From: alin Date: Sun, 18 May 2025 09:03:22 +0800 Subject: [PATCH 08/13] update lokijs --- backEnd/package.json | 2 +- desktop/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backEnd/package.json b/backEnd/package.json index 37d8d7c..91f3399 100644 --- a/backEnd/package.json +++ b/backEnd/package.json @@ -41,7 +41,7 @@ "http-proxy": "^1.18.1", "livemock-core": "workspace:*", "lodash": "^4.17.21", - "lokijs": "git+https://github.com/alinGmail/LokiJS.git#94aa10183ab444d3f11d59a19314db39d3cb6ef3", + "lokijs": "git+https://github.com/alinGmail/LokiJS.git#265bbcaeafccac11ab556599e478380cd07ffca7" "micromatch": "^4.0.5", "mockjs": "^1.1.0", "nedb": "^1.8.0", diff --git a/desktop/package.json b/desktop/package.json index 3cbe1ed..6f0bb67 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -27,7 +27,7 @@ "http-proxy": "^1.18.1", "livemock-core": "workspace:*", "lodash": "^4.17.21", - "lokijs": "git+https://github.com/alinGmail/LokiJS.git#94aa10183ab444d3f11d59a19314db39d3cb6ef3", + "lokijs": "git+https://github.com/alinGmail/LokiJS.git#265bbcaeafccac11ab556599e478380cd07ffca7", "micromatch": "^4.0.5", "mockjs": "^1.1.0", "modern-css-reset": "^1.4.0", From 8d9c5aa8429bb98cc915d88265d9f2a947e20f19 Mon Sep 17 00:00:00 2001 From: alin Date: Sun, 18 May 2025 09:07:56 +0800 Subject: [PATCH 09/13] update lokijs --- backEnd/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backEnd/package.json b/backEnd/package.json index 91f3399..5298bff 100644 --- a/backEnd/package.json +++ b/backEnd/package.json @@ -41,7 +41,7 @@ "http-proxy": "^1.18.1", "livemock-core": "workspace:*", "lodash": "^4.17.21", - "lokijs": "git+https://github.com/alinGmail/LokiJS.git#265bbcaeafccac11ab556599e478380cd07ffca7" + "lokijs": "git+https://github.com/alinGmail/LokiJS.git#265bbcaeafccac11ab556599e478380cd07ffca7", "micromatch": "^4.0.5", "mockjs": "^1.1.0", "nedb": "^1.8.0", From d4824db89836bdf7bc6c6b9de8dee2bcc0ce54cd Mon Sep 17 00:00:00 2001 From: alin Date: Sun, 18 May 2025 10:40:09 +0800 Subject: [PATCH 10/13] add docker support --- frontEnd/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/frontEnd/package.json b/frontEnd/package.json index 24ae366..8935a6d 100644 --- a/frontEnd/package.json +++ b/frontEnd/package.json @@ -23,6 +23,7 @@ "antd": "^5.4.6", "lodash": "^4.17.21", "modern-css-reset": "^1.4.0", + "monaco-editor": "^0.47.0", "react": "^18.2.0", "react-contenteditable": "^3.3.7", "react-dom": "^18.2.0", From 4abf7d4492958a7a7b06150178d4a16314061c73 Mon Sep 17 00:00:00 2001 From: alin Date: Mon, 19 May 2025 21:10:46 +0800 Subject: [PATCH 11/13] add docker support --- .dockerignore | 27 ++++++++++++++++++++++++++- Dockerfile | 29 ++++++++++++++--------------- backEnd/package.json | 13 ++++++------- docker-compose.yml | 6 +++--- 4 files changed, 49 insertions(+), 26 deletions(-) diff --git a/.dockerignore b/.dockerignore index b1c8554..d23f273 100644 --- a/.dockerignore +++ b/.dockerignore @@ -10,4 +10,29 @@ desktop/dist desktop/node_modules .idea README.md -img \ No newline at end of file +img + +**/.DS_Store +**/.git +**/.gitignore +**/.vscode +**/.idea + +**/node_modules +**/npm-debug.log +**/yarn-error.log +**/package-lock.json + +**/dist +**/build +**/coverage + +**/.env +**/.env.local +**/.env.*.local + + +**/README.md +**/CHANGELOG.md +**/logs +**/*.log diff --git a/Dockerfile b/Dockerfile index 51df230..58b5ba9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,21 +3,17 @@ FROM node:20-alpine as builder WORKDIR /app -# Copy package.json and yarn related files -COPY package.json .yarnrc.yml yarn.lock ./ -COPY .yarn ./.yarn +RUN apk add --no-cache git + +# Copy source code +COPY . . + +RUN corepack enable -# Copy workspace package.json files -COPY core/package.json ./core/ -COPY frontEnd/package.json ./frontEnd/ -COPY backEnd/package.json ./backEnd/ # Install dependencies RUN yarn install -# Copy source code -COPY . . - # Build application RUN yarn web-build @@ -26,18 +22,21 @@ FROM node:20-alpine WORKDIR /app + +RUN apk add --no-cache git + # Create data directory RUN mkdir -p /app/data # Copy necessary files COPY --from=builder /app/package.json /app/yarn.lock /app/.yarnrc.yml ./ -COPY --from=builder /app/.yarn ./.yarn COPY --from=builder /app/backEnd ./backEnd COPY --from=builder /app/frontEnd/dist ./frontEnd/dist -COPY --from=builder /app/core/dist ./core/dist +COPY --from=builder /app/core ./core +RUN corepack enable # Install production dependencies -RUN yarn workspaces focus --production +RUN yarn workspace back-end install # Set data directory permissions RUN chown -R node:node /app/data @@ -46,7 +45,7 @@ RUN chown -R node:node /app/data USER node # Expose port -EXPOSE 3000 +EXPOSE 9002 # Start application -CMD ["yarn", "web-start"] \ No newline at end of file +CMD ["yarn", "web-start"] diff --git a/backEnd/package.json b/backEnd/package.json index 5298bff..88b0572 100644 --- a/backEnd/package.json +++ b/backEnd/package.json @@ -22,16 +22,12 @@ "@types/supertest": "^2.0.12", "@types/type-is": "^1.6.3", "jest": "^28.1.1", - "livemock-core": "workspace:*", "mockserver-client": "^5.13.2", "mockserver-node": "^5.13.2", "mockttp": "^3.1.0", "nodemon": "^2.0.16", "prettier": "2.8.7", - "supertest": "^6.3.3", - "ts-jest": "^28.0.5", - "ts-node": "^10.7.0", - "tsconfig-paths": "^4.2.0" + "ts-jest": "^28.0.5" }, "dependencies": { "@types/micromatch": "^4.0.7", @@ -39,7 +35,6 @@ "express": "4.18.1", "form-data": "^4.0.0", "http-proxy": "^1.18.1", - "livemock-core": "workspace:*", "lodash": "^4.17.21", "lokijs": "git+https://github.com/alinGmail/LokiJS.git#265bbcaeafccac11ab556599e478380cd07ffca7", "micromatch": "^4.0.5", @@ -48,6 +43,10 @@ "node-mocks-http": "^1.12.2", "requires-port": "^1.0.0", "socket.io": "^4.6.2", + "ts-node": "^10.7.0", + "tsconfig-paths": "^4.2.0", + "livemock-core": "workspace:*", + "supertest": "^6.3.3", "type-is": "^1.6.18" } -} +} \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index a542903..8fa730b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,4 @@ -version: '3.8' +version: "3.8" services: livemock: @@ -6,7 +6,7 @@ services: context: . dockerfile: Dockerfile ports: - - "3000:3000" + - "9002:9002" environment: - NODE_ENV=production - LIVEMOCK_DB_PATH=/app/data/db @@ -15,4 +15,4 @@ services: - livemock_data:/app/data volumes: - livemock_data: \ No newline at end of file + livemock_data: \ No newline at end of file From 6b3bf95748d3dea25af832b3f1c78c230fb91add Mon Sep 17 00:00:00 2001 From: alin Date: Mon, 19 May 2025 21:22:13 +0800 Subject: [PATCH 12/13] fix docker support --- frontEnd/src/config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontEnd/src/config.ts b/frontEnd/src/config.ts index fce69ad..fd74269 100644 --- a/frontEnd/src/config.ts +++ b/frontEnd/src/config.ts @@ -1,4 +1,4 @@ -export const ServerUrl = "http://localhost:9002"; +export const ServerUrl = ""; -export const debounceWait = 1000; \ No newline at end of file +export const debounceWait = 1000; From 5f65d6052759f29c7921e948732d2b546db99d51 Mon Sep 17 00:00:00 2001 From: alin Date: Tue, 20 May 2025 22:30:05 +0800 Subject: [PATCH 13/13] add request query row in log detail page --- .../RequestLogDetail/RequestLogDetailPage.tsx | 18 ++++++++++++++++++ .../page/RequestLogDetail/RequestQueryCard.tsx | 17 +++++++++++++++++ .../RequestLogDetail/RequestLogDetailPage.tsx | 17 +++++++++++++++++ .../page/RequestLogDetail/RequestQueryCard.tsx | 17 +++++++++++++++++ 4 files changed, 69 insertions(+) create mode 100644 desktop/src/page/RequestLogDetail/RequestQueryCard.tsx create mode 100644 frontEnd/src/page/RequestLogDetail/RequestQueryCard.tsx diff --git a/desktop/src/page/RequestLogDetail/RequestLogDetailPage.tsx b/desktop/src/page/RequestLogDetail/RequestLogDetailPage.tsx index ddf606e..47869f3 100644 --- a/desktop/src/page/RequestLogDetail/RequestLogDetailPage.tsx +++ b/desktop/src/page/RequestLogDetail/RequestLogDetailPage.tsx @@ -5,6 +5,16 @@ import { useParams, useSearchParams } from "react-router-dom"; import { useQuery } from "@tanstack/react-query"; import { getRequestLogDetail } from "../../server/logServer"; import { ClockCircleOutlined } from "@ant-design/icons"; +import RequestQueryCard from "front-end/src/page/RequestLogDetail/RequestQueryCard"; + + +function isEmptyObject(obj: any): boolean { + if(!obj){ + return true; + } + return Object.keys(obj).length === 0; +} + const RequestLogDetailPage = () => { const params = useParams<{ @@ -66,6 +76,14 @@ const RequestLogDetailPage = () => { + {!isEmptyObject(getLogDetailQuery.data?.logItem.req?.query) && ( + <> +
+ + + )}
diff --git a/desktop/src/page/RequestLogDetail/RequestQueryCard.tsx b/desktop/src/page/RequestLogDetail/RequestQueryCard.tsx new file mode 100644 index 0000000..fa7bed1 --- /dev/null +++ b/desktop/src/page/RequestLogDetail/RequestQueryCard.tsx @@ -0,0 +1,17 @@ +import React from "react"; +import mStyle from "./RequestLogDetailPage.module.scss"; + +const RequestBodyCard: React.FunctionComponent<{ + query: any; +}> = ({ query }) => { + return ( +
+
Request Query
+
+
{JSON.stringify(query, null, 2)}
+
+
+ ); +}; + +export default RequestBodyCard; diff --git a/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.tsx b/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.tsx index e8634d0..04c1a3f 100644 --- a/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.tsx +++ b/frontEnd/src/page/RequestLogDetail/RequestLogDetailPage.tsx @@ -6,6 +6,15 @@ import { useQuery } from "@tanstack/react-query"; import { getRequestLogDetail } from "../../server/logServer"; import { useAppSelector } from "../../store"; import { ClockCircleOutlined } from "@ant-design/icons"; +import RequestQueryCard from "./RequestQueryCard"; + + +function isEmptyObject(obj: any): boolean { + if(!obj){ + return true; + } + return Object.keys(obj).length === 0; +} const RequestLogDetailPage = () => { const params = useParams<{ @@ -71,6 +80,14 @@ const RequestLogDetailPage = () => { + {!isEmptyObject(getLogDetailQuery.data?.logItem.req?.query) && ( + <> +
+ + + )}
diff --git a/frontEnd/src/page/RequestLogDetail/RequestQueryCard.tsx b/frontEnd/src/page/RequestLogDetail/RequestQueryCard.tsx new file mode 100644 index 0000000..fa7bed1 --- /dev/null +++ b/frontEnd/src/page/RequestLogDetail/RequestQueryCard.tsx @@ -0,0 +1,17 @@ +import React from "react"; +import mStyle from "./RequestLogDetailPage.module.scss"; + +const RequestBodyCard: React.FunctionComponent<{ + query: any; +}> = ({ query }) => { + return ( +
+
Request Query
+
+
{JSON.stringify(query, null, 2)}
+
+
+ ); +}; + +export default RequestBodyCard;