"更新 uptime-status 为 jj-status,增加新依赖和功能

- 重命名项目从 uptime-status 到 jj-status。
- 添加 react-simple-typewriter 依赖,引入打字机效果。
- 更新相关配置文件和代码,适应新依赖和特性。
- 增加博客链接和云助手导航,移除旧的 Homepage 和 GitHub 导航。
- 引入每日一言(hitokoto)功能。
- 调整样式变量和组件
pull/55/head
GongJinZheng 2024-08-15 16:14:34 +08:00
parent bb67f7f2a7
commit 3fad902619
8 changed files with 149 additions and 80 deletions

23
package-lock.json generated
View File

@ -1,11 +1,11 @@
{ {
"name": "uptime-status", "name": "jj-status",
"version": "2.0.0", "version": "2.0.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "uptime-status", "name": "jj-status",
"version": "2.0.0", "version": "2.0.0",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -14,6 +14,7 @@
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-scripts": "^5.0.1", "react-scripts": "^5.0.1",
"react-simple-typewriter": "^5.0.1",
"react-tooltip": "^4.2.21", "react-tooltip": "^4.2.21",
"sass": "^1.53.0" "sass": "^1.53.0"
} }
@ -13538,6 +13539,18 @@
} }
} }
}, },
"node_modules/react-simple-typewriter": {
"version": "5.0.1",
"resolved": "https://registry.npmmirror.com/react-simple-typewriter/-/react-simple-typewriter-5.0.1.tgz",
"integrity": "sha512-vA5HkABwJKL/DJ4RshSlY/igdr+FiVY4MLsSQYJX6FZG/f1/VwN4y1i3mPXRyfaswrvI8xii1kOVe1dYtO2Row==",
"engines": {
"node": ">=14"
},
"peerDependencies": {
"react": ">=18.0.0",
"react-dom": ">=18.0.0"
}
},
"node_modules/react-tooltip": { "node_modules/react-tooltip": {
"version": "4.2.21", "version": "4.2.21",
"resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-4.2.21.tgz", "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-4.2.21.tgz",
@ -25752,6 +25765,12 @@
"workbox-webpack-plugin": "^6.4.1" "workbox-webpack-plugin": "^6.4.1"
} }
}, },
"react-simple-typewriter": {
"version": "5.0.1",
"resolved": "https://registry.npmmirror.com/react-simple-typewriter/-/react-simple-typewriter-5.0.1.tgz",
"integrity": "sha512-vA5HkABwJKL/DJ4RshSlY/igdr+FiVY4MLsSQYJX6FZG/f1/VwN4y1i3mPXRyfaswrvI8xii1kOVe1dYtO2Row==",
"requires": {}
},
"react-tooltip": { "react-tooltip": {
"version": "4.2.21", "version": "4.2.21",
"resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-4.2.21.tgz", "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-4.2.21.tgz",

View File

@ -1,5 +1,5 @@
{ {
"name": "uptime-status", "name": "jj-status",
"version": "2.0.0", "version": "2.0.0",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
@ -12,6 +12,7 @@
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-scripts": "^5.0.1", "react-scripts": "^5.0.1",
"react-simple-typewriter": "^5.0.1",
"react-tooltip": "^4.2.21", "react-tooltip": "^4.2.21",
"sass": "^1.53.0" "sass": "^1.53.0"
}, },

View File

@ -1,15 +1,12 @@
window.Config = { window.Config = {
// 显示标题 // 显示标题
SiteName: 'Public Status', SiteName: '站点检测',
// UptimeRobot Api Keys // UptimeRobot Api Keys
// 支持 Monitor-Specific 和 Read-Only // 支持 Monitor-Specific 和 Read-Only
ApiKeys: [ ApiKeys: [
'm784488775-dd1ad84b209c05f8e185c33e', 'ur2648744-49ccec99d7a4ea70c9e441f3'
'm784490063-7b5da437e7f1e0d67613714d',
'm784497419-de55aa09902ccb3ab22d548a',
'm784496436-71a4bf7b1e3bdf7756be131b',
], ],
// 日志天数 // 日志天数
@ -21,16 +18,17 @@ window.Config = {
// 导航栏菜单 // 导航栏菜单
Navi: [ Navi: [
{ {
text: 'Homepage', text: 'Blog',
url: 'https://status.org.cn/' url: 'https://wzue.cn/'
},
{
text: '云助手',
url: 'https://mz.wzue.cn/'
}, },
{ {
text: 'GitHub', text: 'GitHub',
url: 'https://github.com/yb/uptime-status' url: 'https://github.com/9075512'
},
{
text: 'Blog',
url: 'https://abo.xyz/'
}, },
], ],
}; };

View File

@ -9,6 +9,7 @@
<link rel="shortcut icon" href="./favicon.ico" type="image/x-icon" /> <link rel="shortcut icon" href="./favicon.ico" type="image/x-icon" />
<title>Uptime Status</title> <title>Uptime Status</title>
<script src="./config.js"></script> <script src="./config.js"></script>
<!-- 本例不能添加链接内容,放在此处只是因为此接口比较方便,也许能够解决大部分的需求-->
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>

View File

@ -27,7 +27,7 @@ a {
} }
#header { #header {
background-color: #121a26; background-color: $primary-color;
padding: 30px 0 60px 0; padding: 30px 0 60px 0;
color: $primary-color; color: $primary-color;
width: 100%; width: 100%;
@ -39,6 +39,7 @@ a {
.logo { .logo {
font-size: 20px; font-size: 20px;
font-weight: bold; font-weight: bold;
color: #ffffff;
} }
.navi { .navi {
font-size: 14px; font-size: 14px;
@ -48,7 +49,7 @@ a {
transition: color ease 150ms; transition: color ease 150ms;
} }
a:hover { a:hover {
color: $primary-color; // color: $primary-color;
} }
} }
} }
@ -170,4 +171,9 @@ a {
font-weight: bold; font-weight: bold;
color: $primary-color; color: $primary-color;
} }
.hitokoto{
font-weight: bold;
color: $primary-color;
cursor: pointer;
}
} }

6
src/common/hitokoto.js Normal file
View File

@ -0,0 +1,6 @@
import axios from 'axios';
export async function GetHitokoto() {
const response = await axios.get('https://v1.hitokoto.cn');
return response;
}

View File

@ -1,30 +1,54 @@
import { useMemo } from 'react'; import { useMemo } from "react";
import Link from './link'; import Link from "./link";
import Header from './header'; import Header from "./header";
import UptimeRobot from './uptimerobot'; import UptimeRobot from "./uptimerobot";
import Package from '../../package.json'; import Package from "../../package.json";
import { GetHitokoto } from "../common/hitokoto";
import { useEffect, useState } from "react";
import { Typewriter } from 'react-simple-typewriter'
function App() { function App() {
const apikeys = useMemo(() => { const apikeys = useMemo(() => {
const { ApiKeys } = window.Config; const { ApiKeys } = window.Config;
if (Array.isArray(ApiKeys)) return ApiKeys; if (Array.isArray(ApiKeys)) return ApiKeys;
if (typeof ApiKeys === 'string') return [ApiKeys]; if (typeof ApiKeys === "string") return [ApiKeys];
return []; return [];
}, []); }, []);
const [hitokoto, setHitokoto] = useState(['结局是什么,我们自己决定!']);
function getText() {
GetHitokoto().then(({ data }) => {
setHitokoto([...hitokoto,data.hitokoto]);
});
}
useEffect(() => {
getText();
}, []);
return ( return (
<> <>
<Header /> <Header />
<div className='container'> <div className="container">
<div id='uptime'> <div id="uptime">
{apikeys.map((key) => ( {apikeys.map((key) => (
<UptimeRobot key={key} apikey={key} /> <UptimeRobot key={key} apikey={key} />
))} ))}
</div> </div>
<div id='footer'> <div id="footer">
<p>基于 <Link to='https://uptimerobot.com/' text='UptimeRobot' /> 接口制作检测频率 5 分钟</p> <p className="hitokoto">
<p>&copy; 2020 <Link to='https://status.org.cn/' text='STATUS.ORG.CN' />, Version {Package.version}</p> <Typewriter cursor="true" delaySpeed="6000" cursorStyle="⚡" words={hitokoto} onDelay={getText}/>
</p>
<p>
基于 <Link to="https://uptimerobot.com/" text="UptimeRobot" />{" "}
接口制作检测频率 5 分钟
</p>
<p>
&copy; 2020{" "}
<Link to="https://status.wzue.cn/" text="STATUS.WZUE.CN" />, Version{" "}
{Package.version}
</p>
</div> </div>
</div> </div>
</> </>

View File

@ -1,15 +1,14 @@
import ReactTooltip from 'react-tooltip'; import ReactTooltip from "react-tooltip";
import { useEffect, useState } from 'react'; import { useEffect, useState } from "react";
import { GetMonitors } from '../common/uptimerobot'; import { GetMonitors } from "../common/uptimerobot";
import { formatDuration, formatNumber } from '../common/helper'; import { formatDuration, formatNumber } from "../common/helper";
import Link from './link'; import Link from "./link";
function UptimeRobot({ apikey }) { function UptimeRobot({ apikey }) {
const status = { const status = {
ok: '正常', ok: "正常",
down: '无法访问', down: "无法访问",
unknow: '未知' unknow: "未知",
}; };
const { CountDays, ShowLink } = window.Config; const { CountDays, ShowLink } = window.Config;
@ -20,48 +19,63 @@ function UptimeRobot({ apikey }) {
GetMonitors(apikey, CountDays).then(setMonitors); GetMonitors(apikey, CountDays).then(setMonitors);
}, [apikey, CountDays]); }, [apikey, CountDays]);
if (monitors) return monitors.map((site) => ( if (monitors)
<div key={site.id} className='site'> return monitors.map((site) => (
<div className='meta'> <div key={site.id} className="site">
<span className='name' dangerouslySetInnerHTML={{ __html: site.name }} /> <div className="meta">
{ShowLink && <Link className='link' to={site.url} text={site.name} />} <span
<span className={'status ' + site.status}>{status[site.status]}</span> className="name"
dangerouslySetInnerHTML={{ __html: site.name }}
/>
{ShowLink && <Link className="link" to={site.url} text={site.name} />}
<span className={"status " + site.status}>{status[site.status]}</span>
</div> </div>
<div className='timeline'> <div className="timeline">
{site.daily.map((data, index) => { {site.daily.map((data, index) => {
let status = ''; let status = "";
let text = data.date.format('YYYY-MM-DD '); let text = data.date.format("YYYY-MM-DD ");
if (data.uptime >= 100) { if (data.uptime >= 100) {
status = 'ok'; status = "ok";
text += `可用率 ${formatNumber(data.uptime)}%`; text += `可用率 ${formatNumber(data.uptime)}%`;
} else if (data.uptime <= 0 && data.down.times === 0) {
status = "none";
text += "无数据";
} else {
status = "down";
text += `故障 ${data.down.times} 次,累计 ${formatDuration(
data.down.duration
)}可用率 ${formatNumber(data.uptime)}%`;
} }
else if (data.uptime <= 0 && data.down.times === 0) { return <i key={index} className={status} data-tip={text} />;
status = 'none';
text += '无数据';
}
else {
status = 'down';
text += `故障 ${data.down.times} 次,累计 ${formatDuration(data.down.duration)},可用率 ${formatNumber(data.uptime)}%`;
}
return (<i key={index} className={status} data-tip={text} />)
})} })}
</div> </div>
<div className='summary'> <div className="summary">
<span>今天</span> <span>今天</span>
<span> <span>
{site.total.times {site.total.times
? `最近 ${CountDays} 天故障 ${site.total.times} 次,累计 ${formatDuration(site.total.duration)},平均可用率 ${site.average}%` ? `最近 ${CountDays} 天故障 ${
site.total.times
} 累计 ${formatDuration(site.total.duration)}平均可用率 ${
site.average
}%`
: `最近 ${CountDays} 天可用率 ${site.average}%`} : `最近 ${CountDays} 天可用率 ${site.average}%`}
</span> </span>
<span>{site.daily[site.daily.length - 1].date.format('YYYY-MM-DD')}</span> <span>
{site.daily[site.daily.length - 1].date.format("YYYY-MM-DD")}
</span>
</div> </div>
<ReactTooltip className='tooltip' place='top' type='dark' effect='solid' /> <ReactTooltip
className="tooltip"
place="top"
type="dark"
effect="solid"
/>
</div> </div>
)); ));
else
else return ( return (
<div className='site'> <div className="site">
<div className='loading' /> <div className="loading" />
</div> </div>
); );
} }