diff --git a/package.json b/package.json
index f0e35d1..e8ca996 100644
--- a/package.json
+++ b/package.json
@@ -42,6 +42,7 @@
"colord": "^2.9.3",
"compose-middleware": "^5.0.1",
"compression": "^1.7.4",
+ "copy-to-clipboard": "^3.3.3",
"cors": "^2.8.5",
"croner": "^7.0.1",
"dayjs": "^1.11.9",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 84e6def..24b551f 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -61,6 +61,9 @@ dependencies:
compression:
specifier: ^1.7.4
version: 1.7.4
+ copy-to-clipboard:
+ specifier: ^3.3.3
+ version: 3.3.3
cors:
specifier: ^2.8.5
version: 2.8.5
diff --git a/src/client/components/monitor/MonitorBadgeView.tsx b/src/client/components/monitor/MonitorBadgeView.tsx
new file mode 100644
index 0000000..9116a99
--- /dev/null
+++ b/src/client/components/monitor/MonitorBadgeView.tsx
@@ -0,0 +1,101 @@
+import { Checkbox, Divider, Input, message } from 'antd';
+import React, { useMemo, useState } from 'react';
+import { useEvent } from '../../hooks/useEvent';
+import copy from 'copy-to-clipboard';
+
+export const MonitorBadgeView: React.FC<{
+ workspaceId: string;
+ monitorId: string;
+}> = React.memo((props) => {
+ const { workspaceId, monitorId } = props;
+
+ const [showDetail, setShowDetail] = useState(false);
+
+ const url = useMemo(() => {
+ let url = `${window.location.origin}/monitor/${workspaceId}/${monitorId}/badge.svg`;
+
+ if (showDetail) {
+ url += `?showDetail=true`;
+ }
+
+ return url;
+ }, [showDetail]);
+
+ const handleCopy = useEvent((text: string) => {
+ copy(text);
+ message.success('Copy success!');
+ });
+
+ return (
+
+
+
+
+
This will show your recent result of your monitor
+
+ setShowDetail(e.target.checked)}
+ >
+ Show Detail Number
+
+
+
+
+
+
Share with...
+
+
+
`}
+ addonAfter={
+
handleCopy(`
`)}
+ >
+ Copy
+
+ }
+ />
+
+
handleCopy(`![](${url})`)}
+ >
+ Copy
+
+ }
+ />
+
+
handleCopy(`[img]${url}[/img]`)}
+ >
+ Copy
+
+ }
+ />
+
+ handleCopy(url)}>
+ Copy
+
+ }
+ />
+
+
+ );
+});
+MonitorBadgeView.displayName = 'MonitorBadgeView';
diff --git a/src/client/components/monitor/MonitorInfo.tsx b/src/client/components/monitor/MonitorInfo.tsx
index 9edc4a1..9bff6f2 100644
--- a/src/client/components/monitor/MonitorInfo.tsx
+++ b/src/client/components/monitor/MonitorInfo.tsx
@@ -19,7 +19,8 @@ import { MonitorEventList } from './MonitorEventList';
import { useEvent } from '../../hooks/useEvent';
import { MonitorDataMetrics } from './MonitorDataMetrics';
import { MonitorDataChart } from './MonitorDataChart';
-import { DeleteOutlined } from '@ant-design/icons';
+import { DeleteOutlined, MoreOutlined } from '@ant-design/icons';
+import { MonitorBadgeView } from './MonitorBadgeView';
interface MonitorInfoProps {
monitorId: string;
@@ -29,6 +30,7 @@ export const MonitorInfo: React.FC = React.memo((props) => {
const { monitorId } = props;
const [currectResponse, setCurrentResponse] = useState(0);
const navigate = useNavigate();
+ const [showBadge, setShowBadge] = useState(false);
const {
data: monitorInfo,
@@ -209,6 +211,35 @@ export const MonitorInfo: React.FC = React.memo((props) => {
>
+
+ setShowBadge(true),
+ },
+ ],
+ }}
+ >
+ } />
+
+
+ setShowBadge(false)}
+ onOk={() => setShowBadge(false)}
+ destroyOnClose={true}
+ centered={true}
+ >
+
+
diff --git a/src/server/main.ts b/src/server/main.ts
index 0680a4f..3ba3b8b 100644
--- a/src/server/main.ts
+++ b/src/server/main.ts
@@ -25,6 +25,7 @@ import cors from 'cors';
import { serverStatusRouter } from './router/serverStatus';
import { initCronjob } from './cronjob';
import { logger } from './utils/logger';
+import { monitorRouter } from './router/monitor';
const port = settings.port;
@@ -57,6 +58,7 @@ app.use(
app.use('/api/website', websiteRouter);
app.use('/api/workspace', workspaceRouter);
+app.use('/monitor', monitorRouter);
app.use('/telemetry', telemetryRouter);
app.use('/serverStatus', serverStatusRouter);
diff --git a/src/server/router/monitor.ts b/src/server/router/monitor.ts
new file mode 100644
index 0000000..28d4520
--- /dev/null
+++ b/src/server/router/monitor.ts
@@ -0,0 +1,39 @@
+import { Router } from 'express';
+import { param, validate, query } from '../middleware/validate';
+import { numify } from '../utils/common';
+import { makeBadge } from 'badge-maker';
+import { getMonitorPublicInfos, getMonitorRecentData } from '../model/monitor';
+import { checkEnvTrusty } from '../utils/env';
+
+export const monitorRouter = Router();
+
+monitorRouter.get(
+ '/:workspaceId/:monitorId/badge.svg',
+ validate(
+ param('workspaceId').isString(),
+ param('monitorId').isString(),
+ query('showDetail').optional().isString()
+ ),
+ async (req, res) => {
+ const { workspaceId, monitorId } = req.params;
+ const showDetail = checkEnvTrusty(String(req.query.showDetail));
+
+ const [info] = await getMonitorPublicInfos([monitorId]);
+ const [{ value }] = await getMonitorRecentData(workspaceId, monitorId, 1);
+
+ const svg =
+ value >= 0
+ ? makeBadge({
+ label: info.name,
+ message: showDetail ? numify(value) : 'Health',
+ color: 'green',
+ })
+ : makeBadge({
+ label: info.name,
+ message: 'Error',
+ color: 'red',
+ });
+
+ res.header('Content-Type', 'image/svg+xml').send(svg);
+ }
+);