├── .env.example
├── .github
└── workflows
│ └── ci.yml
├── .gitignore
├── .yarn
└── releases
│ └── yarn-1.22.19.cjs
├── .yarnrc.yml
├── README.md
├── _routes.json
├── _worker.js
├── babel.config.js
├── package-lock.json
├── package.json
├── public
├── conf.example.json
├── core.example.php
├── favicon.ico
└── index.html
├── src
├── App.vue
├── assets
│ └── logo.png
├── main.js
├── router
│ └── index.js
└── views
│ └── Home.vue
├── vue.config.js
└── yarn.lock
/.env.example:
--------------------------------------------------------------------------------
1 | VUE_APP_use_env = 1 # 此参数为 1 时启用环境变量代替conf.json,无法读取此参数时使用conf.json
2 |
3 | VUE_APP_config_title = 状态监控 # 页面标题
4 | VUE_APP_config_title_english = StatusLive #页面副标题
5 |
6 | VUE_APP_config_mode = 2 # 模式选项,1为公开模式,2为隐私模式
7 | VUE_APP_config_readonly_apikey = USE_SERVER_APIKEY # 公开模式用,填写从UptimeRobot后台获得的ReadOnly-ApiKey
8 | VUE_APP_config_proxy_link = /core.php # 隐私模式用,反代访问路径
9 | VUE_APP_config_history_time = 60 # 获取过去 X 天的可用率,单位为天
10 | VUE_APP_config_logs_history_days = 30 # 获取过去 X 天的状态日志,单位为天
11 |
12 | VUE_APP_config_success_min = 98 # 合格(success)等级标准,低于此数字为警告(warning)等级
13 | VUE_APP_config_warning_min = 90 # 警告(warning)等级标准,低于此数字为危险(danger)等级
14 | VUE_APP_config_auto_refresh_seconds = 60 # 自动刷新时间,单位为秒,填写0为禁用自动刷新
15 |
16 | VUE_APP_logs_each_page = 10 # 日志模块每页展示行数,v2.1新增,作用于日志查看区
17 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: Status-Vercel
2 |
3 | on:
4 | push:
5 | branches: [ "master" , "freejishu-dev", "cloudflare-try" ]
6 | pull_request:
7 | branches: [ "master" , "freejishu-dev", "cloudflare-try" ]
8 |
9 | jobs:
10 | ci:
11 | name: Build & Test
12 | runs-on: ubuntu-latest
13 | strategy:
14 | matrix:
15 | node-version: [16.x]
16 | steps:
17 | - uses: actions/checkout@v3
18 | - name: Use Node.js ${{ matrix.node-version }}
19 | uses: actions/setup-node@v3
20 | with:
21 | node-version: ${{ matrix.node-version }}
22 | cache: 'npm'
23 | env:
24 | CI: true
25 | - name: install yarn
26 | run: npm install yarn -g
27 | - name: yarn
28 | run: yarn
29 | - name: yarn build
30 | run: yarn build
31 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 |
6 | # local env files & example file
7 | .env.local
8 | .env.*.local
9 | .dev.vars
10 |
11 | # Log files
12 | npm-debug.log*
13 | yarn-debug.log*
14 | yarn-error.log*
15 | pnpm-debug.log*
16 |
17 | # Editor directories and files
18 | .idea
19 | .vscode
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 | config.json
26 | core.php
--------------------------------------------------------------------------------
/.yarnrc.yml:
--------------------------------------------------------------------------------
1 | yarnPath: .yarn/releases/yarn-1.22.19.cjs
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # StatusLive
2 |
3 |
简洁 · 快速 · 轻便
4 |
5 | 
6 |
7 | ## What's StatusLive?
8 |
9 | StatusLive 是一个基于 Uptimerobot 的状态页,数据基于 Uptimerobot API 而来,开箱即用。
10 |
11 | StatusLive is a status page based on Uptimerobot. The data is based on Uptimerobot API.
12 |
13 | 注册一个 Uptimerobot 账户并添加监测点,即可搭建属于自己的、高可用的状态页面。
14 |
15 | Register an Uptimerobot account and add monitoring points to build your own status page.
16 |
17 | ## Demo
18 |
19 | https://status.freejishu.com/
20 |
21 | ## How to use
22 |
23 | - 最经典的打开方式 ( `dist` + `conf.json` + `core.php` ) :
24 |
25 | 1. 注册一个 `UptimeRobot` 账户并添加监控节点。
26 |
27 | 2. ⭐Star 一下,然后从 [Releases][1] 下载最新版本并解压。
28 |
29 | 3. 复制 `conf.example.conf` 到 `conf.json` ,并配置配置文件 [conf.json][2]
30 | ```
31 | {
32 | "config_title": "状态监控", //页面标题
33 | "config_title_english": "StatusLive", //页面副标题
34 |
35 | "config_mode": 2, //模式选项,1为公开模式,2为隐私模式(模式区别请看下方)
36 | "config_readonly_apikey": "USE_SERVER_APIKEY", //公开模式用,填写从UptimeRobot后台获得的ReadOnly-ApiKey
37 | "config_proxy_link": "/core.php", //隐私模式用,反代访问路径
38 |
39 | "config_history_time": 60, //获取过去 X 天的可用率,单位为天
40 | "config_logs_history_days": 30, //获取过去 X 天的状态日志,单位为天
41 |
42 | "config_success_min": 98, //合格(success)等级标准,低于此数字为警告(warning)等级
43 | "config_warning_min": 90, //警告(warning)等级标准,低于此数字为危险(danger)等级
44 | "config_auto_refresh_seconds": 60, //自动刷新时间,单位为秒,填写0为禁用自动刷新
45 |
46 | "logs_each_page": 10 //日志模块每页展示行数,v2.1新增,作用于日志查看区
47 | }
48 | ```
49 | - **公开模式(不推荐)**
50 |
51 | 最简单、快速的开箱方式。系统会根据 `conf.json` 内的 `config_readonly_apikey` 直接请求 UptimeRobot API 接口。此模式不需要 `core.php` 。
52 |
53 | `conf.json` 内应该如此填写:
54 |
55 | ```
56 | "config_mode": 1,
57 | "config_readonly_apikey": "ur609xxx-27fxxxxxxxxxxxxxxxxxxxxx",
58 | ```
59 |
60 | **注意:此模式下一定要使用 `只读ApiKey (Read-Only API Key)`,非只读ApiKey的泄露会导致其他人使用官方 API 操纵账户!**
61 |
62 | 如果觉得直接请求 UptimeRobot API 接口速度有些差,或不想暴露部分关键字段,您也可以使用下面的隐私模式反代以提高速度。
63 |
64 | - **隐私模式(推荐)**
65 |
66 | 由于UptimeRobot API返回数据内包含 `url` 、 `http_username` 、 `http_password` 、 `port` 等字段,直接请求可能会导致真实域名、IP等泄露;同时对免费账户,UptimeRobot 的 API 存在 QPS 限制。故推荐使用隐私模式,可隐去关键字段并针对性缓存。
67 |
68 | `conf.json` 内应该如此填写:
69 |
70 | ```
71 | "config_mode": 2,
72 | "config_readonly_apikey": "USE_SERVER_APIKEY", //无需再填写apikey,以core.php中为准
73 | "config_proxy_link": "/core.php", //填写你的core.php路径
74 | ```
75 |
76 | 本程序自带一个php的反代文件。对反代文件 `core.example.php` ,您需要先复制到 `core.php` (当然其他名字也可以,`config.json` 中的 `config_proxy_link` 字段需同步更新),再修改 `core.php` 的部分配置:
77 |
78 | ```
79 | //在这里填入你的API_KEY,如果使用公开模式则置空避免key被更改。
80 | $apikey = 'ur609264-xxxxxxxxxxxxxxxxxxxxxxxx';
81 |
82 | //json缓存文件名,可自行配置
83 | $file_name = 'uptime.json';
84 |
85 | //缓存时间,单位为秒,因为UptimeRobotAPI免费用户调用限制为10次/分钟故不建议低于6
86 | $cache_time = 10;
87 | ```
88 |
89 | 接下来您可以将其放到任意服务器上,只需做好 CORS 和在 `config_proxy_link` 中填写好地址即可。
90 |
91 | 关于反代机制,除了部署 `core.php` ,还有很多玩法,请参照下文中**更多打开方式**部分。
92 |
93 | - `conf.json` 的其余字段根据上文提示填写即可。注意JSON文件不能存在`注释`。
94 |
95 | 4. 上传到服务器,然后 Enjoy it!
96 |
97 |
98 |
99 | - 更多打开方式:
100 |
101 | 1. `conf.json` 可以被环境变量替代。若您部署到类似 Cloudflare Pages 或 Vercel 此类平台时,可通过填写环境变量生成 `.env` 文件。当检测到环境变量存在时,无需再修改或部署 `conf.json`,程序将根据环境变量启动。详见:[如何部署 StatusLive 到静态资源平台?](https://github.com/freejishu/StatusLive/discussions/30)。
102 |
103 | 2. 隐私模式需要用到的 `core.php` 可以有多种部署方式,如:
104 | - 可以使用 Cloudflare Worker 替代,详见:[使用 Cloudflare Workers 替代 Core.php 实现反代](https://github.com/freejishu/StatusLive/discussions/28)。
105 | - 如果计划将 `core.php` 用于如 `Vercel` 等 Serverless Functions 平台,请注释掉 `core.php` 的[第49行](https://github.com/freejishu/StatusLive/blob/67ebdce931332255f06dc0635aa0d88aa589999d/public/core.example.php#L49)避免写入错误。
106 | - 如果懒得架设 `core.php` ,可以使用由开发者提供的公共反代。请参照 [StatusLive公共反代使用说明](https://github.com/freejishu/StatusLive/discussions/15) 。
107 | - 关于 `core.php` 的更多细节,请参照 [常见问题汇总(v2.0)](https://github.com/freejishu/StatusLive/discussions/3)。
108 |
109 |
110 | ## Migration from v1.x
111 |
112 | v1.x 使用参照:https://www.freejishu.com/statuslive-for-you/
113 |
114 | 基于各种各样的考虑,v2.x 采用了全新的技术栈,故 v1.x 不能直接迁移到 v2.x 。但是不用担心,基于开箱即用的特性,你会很快上手 v2.x 的。
115 |
116 | 注:v2.x 通过切换分支的形式实现过渡,即原 master 分支被重命名为 v1.x,而 v2.x 重命名为 master ,并切换了一次默认分支。如果之前 fork 过项目,可能需要重新 fork 或进行同步操作才能继续操作。
117 |
118 | ## Licenses
119 |
120 | MIT
121 |
122 | ## How to Rebuild
123 |
124 | 1. Clone the code
125 | 2. Project setup
126 |
127 | ```
128 | yarn install
129 | ```
130 |
131 | 3. Compiles and hot-reloads for development & Compiles and minifies for production
132 |
133 | ```
134 | yarn serve
135 | yarn build
136 | ```
137 |
138 | 4. Customize configuration See [Configuration Reference](https://cli.vuejs.org/config/).
139 |
140 |
141 | [1]: https://github.com/freejishu/StatusLive/releases/latest
142 | [2]: https://github.com/freejishu/StatusLive/blob/master/public/conf.json
143 | [3]: https://github.com/freejishu/StatusLive/discussions/3
144 |
--------------------------------------------------------------------------------
/_routes.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "include": ["/api"],
4 | "exclude": []
5 | }
--------------------------------------------------------------------------------
/_worker.js:
--------------------------------------------------------------------------------
1 | let api_key = '';//你的apikey
2 | let cache_time = 10;//缓存时间(秒)
3 | const url = 'https://api.uptimerobot.com/v2/getMonitors';
4 |
5 | export default {
6 | async fetch(request, env) {
7 | api_key = env.api_key ;
8 | cache_time = env.cache_time ? env.cache_time : 10;
9 | console.log(cache_time);
10 | const url = new URL(request.url);
11 | if (url.pathname.startsWith('/api')) {
12 | if (request.method === 'OPTIONS') {
13 | return new Response(null, {
14 | status: 200,
15 | statusText: 'OK',
16 | headers: {
17 | 'content-type': 'application/json;charset=UTF-8',
18 | 'Access-Control-Allow-Origin': '*',
19 | 'Access-Control-Allow-Methods': '*',
20 | 'Access-Control-Allow-Credentials': 'true',
21 | 'Access-Control-Allow-Headers': 'Content-Type',
22 | 'Access-Control-Expose-Headers': '*'
23 | }
24 | })
25 | } else if (request.method === 'POST') {
26 | return await handleRequest(request,env);
27 | } else {
28 | return new Response(null, {
29 | status: 405,
30 | statusText: 'Method Not Allowed',
31 | })
32 | }
33 | }
34 | // Otherwise, serve the static assets.
35 | // Without this, the Worker will error and no assets will be served.
36 | return env.ASSETS.fetch(request);
37 | },
38 | }
39 |
40 |
41 |
42 |
43 | async function gatherResponse(response) {
44 | const { headers } = response;
45 | const contentType = headers.get('content-type') || '';
46 | if (contentType.includes('application/json')) {
47 | return JSON.stringify(await response.json());
48 | }
49 | return response.text();
50 | }
51 |
52 | async function readRequestBody(request) {
53 | const { headers } = request;
54 | const contentType = headers.get('content-type') || '';
55 |
56 | if (contentType.includes('application/json')) {
57 | return JSON.stringify(await request.json());
58 | } else if (contentType.includes('application/text')) {
59 | return request.text();
60 | } else if (contentType.includes('text/html')) {
61 | return request.text();
62 | } else if (contentType.includes('form')) {
63 | const formData = await request.formData();
64 | const body = {};
65 | for (const entry of formData.entries()) {
66 | body[entry[0]] = entry[1];
67 | }
68 | return JSON.stringify(body);
69 | } else {
70 | return 'a file';
71 | }
72 | }
73 |
74 | async function handleRequest(request,env) {
75 |
76 | const timestamp = (Date.parse(new Date()))/1000;
77 | const lasttime = await env.statuslive.get("statuslive_lasttime");
78 | let results = '';
79 | let cache_tag = '';
80 | if (timestamp - lasttime <= cache_time) {
81 | results = await env.statuslive.get("statuslive_json_cache");
82 | cache_tag = 'Cache Hit - Time:'+lasttime;
83 | }else{
84 | const post_data = JSON.parse(await readRequestBody(request));
85 | post_data.api_key = api_key;
86 |
87 | const init = {
88 | headers: {
89 | 'content-type': 'application/json;charset=UTF-8',
90 | },
91 | method: 'POST',
92 | body: await JSON.stringify(post_data)
93 | };
94 | const response = await fetch(url, init);
95 | results = await JSON.parse(await gatherResponse(response));
96 |
97 | for (let index = 0; index < results.monitors.length; index++) {
98 | results.monitors[index].url = "";
99 | results.monitors[index].http_username = "";
100 | results.monitors[index].http_password = "";
101 | results.monitors[index].port = "";
102 | }
103 |
104 | results = await JSON.stringify(results);
105 | await env.statuslive.put("statuslive_json_cache", results, {expirationTtl: (cache_time < 60 ? 60 : cache_time)})
106 | await env.statuslive.put("statuslive_lasttime", timestamp.toString(), {expirationTtl: (cache_time < 60 ? 60 : cache_time)})
107 | cache_tag = 'Cache Miss - Time:'+timestamp;
108 | }
109 |
110 | let response_init = {
111 | headers: {
112 | 'content-type': 'application/json;charset=UTF-8',
113 | 'Access-Control-Allow-Origin': '*',
114 | 'Access-Control-Allow-Methods': '*',
115 | 'Access-Control-Allow-Credentials': 'true',
116 | 'Access-Control-Allow-Headers': 'Content-Type',
117 | 'Access-Control-Expose-Headers': '*',
118 | 'StatusLive-Cache-Tag' : cache_tag
119 | }
120 | }
121 | return new Response(results, response_init);
122 | }
123 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/cli-plugin-babel/preset'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "statuslive_vue",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "build": "vue-cli-service build",
8 | "lint": "vue-cli-service lint"
9 | },
10 | "dependencies": {
11 | "axios": "^0.21.4",
12 | "core-js": "^3.6.5",
13 | "echarts": "^5.4.0",
14 | "element-ui": "^2.15.6",
15 | "v-charts-v2": "^2.0.9",
16 | "vue": "^2.6.11",
17 | "vue-echarts": "^6.2.3",
18 | "vue-router": "^3.2.0"
19 | },
20 | "devDependencies": {
21 | "@vue/cli-plugin-babel": "~4.5.0",
22 | "@vue/cli-plugin-eslint": "~4.5.0",
23 | "@vue/cli-plugin-router": "~4.5.0",
24 | "@vue/cli-service": "~4.5.0",
25 | "@vue/composition-api": "^1.7.1",
26 | "babel-eslint": "^10.1.0",
27 | "eslint": "^6.7.2",
28 | "eslint-plugin-vue": "^6.2.2",
29 | "vue-template-compiler": "^2.6.11"
30 | },
31 | "eslintConfig": {
32 | "root": true,
33 | "env": {
34 | "node": true
35 | },
36 | "extends": [
37 | "plugin:vue/essential",
38 | "eslint:recommended"
39 | ],
40 | "parserOptions": {
41 | "parser": "babel-eslint"
42 | },
43 | "rules": {}
44 | },
45 | "browserslist": [
46 | "> 1%",
47 | "last 2 versions",
48 | "not dead"
49 | ],
50 | "engines": {
51 | "yarn": "=1.22.19",
52 | "node": "=16"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/public/conf.example.json:
--------------------------------------------------------------------------------
1 | {
2 | "config_title": "状态监控",
3 | "config_title_english": "StatusLive",
4 |
5 | "config_mode": 2,
6 | "config_readonly_apikey": "USE_SERVER_APIKEY",
7 | "config_proxy_link": "/core.php",
8 | "config_history_time": 60,
9 | "config_logs_history_days": 30,
10 |
11 | "config_success_min": 98,
12 | "config_warning_min": 90,
13 | "config_auto_refresh_seconds": 60,
14 |
15 | "logs_each_page": 10
16 | }
--------------------------------------------------------------------------------
/public/core.example.php:
--------------------------------------------------------------------------------
1 | api_key = $apikey;
26 | }
27 | $data = http_build_query($data);
28 | $curl = curl_init();
29 | curl_setopt($curl, CURLOPT_URL, $link);
30 | curl_setopt($curl, CURLOPT_POST, 1);
31 | curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
32 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
33 | $output = curl_exec($curl);
34 | curl_close($curl);
35 | $o = json_decode($output, true);
36 | if(@$o['stat']=='ok'){
37 | header("StatusLiveCache: Miss.");
38 | //替换删除敏感数据,如有其他需要可以自行增加
39 | for ($i = 0; $i < count($o['monitors']); $i++) {
40 | $o['monitors'][$i]['url'] = "";
41 | $o['monitors'][$i]['http_username'] = "";
42 | $o['monitors'][$i]['http_password'] = "";
43 | $o['monitors'][$i]['port'] = "";
44 | }
45 | $json = [];
46 | $json['time'] = time();
47 | $json['json'] = json_encode($o);
48 |
49 | file_put_contents($file_name, json_encode($json));
50 |
51 | exit($json['json']);
52 | }else{
53 | //请求错误不缓存
54 | header("StatusLiveCache: Miss, Request Fail.");
55 | exit(json_encode($o));
56 | }
57 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/freejishu/StatusLive/37d992868bb65ce564e197fab0459a12e7d643a2/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | <%= htmlWebpackPlugin.options.title %>
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
24 |
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/freejishu/StatusLive/37d992868bb65ce564e197fab0459a12e7d643a2/src/assets/logo.png
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import ElementUI from 'element-ui';
3 | import 'element-ui/lib/theme-chalk/index.css';
4 | import 'element-ui/lib/theme-chalk/display.css';
5 | import axios from 'axios';
6 |
7 | import App from './App.vue';
8 | import router from './router';
9 |
10 |
11 | Vue.config.productionTip = false;
12 |
13 | Vue.prototype.$axios = axios;
14 | //axios.defaults.baseURL = '/v2';
15 |
16 |
17 |
18 | Vue.use(ElementUI);
19 | router.beforeEach((to, from, next) => {
20 | if(to.meta.title){
21 | document.title = to.meta.title
22 | }
23 | next();
24 | })
25 |
26 | new Vue({
27 | router,
28 | render: h => h(App)
29 | }).$mount('#app')
30 |
--------------------------------------------------------------------------------
/src/router/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import VueRouter from 'vue-router'
3 | import Home from '../views/Home.vue'
4 |
5 | Vue.use(VueRouter)
6 |
7 | const routes = [
8 | {
9 | path: '/',
10 | name: 'Home',
11 | component: Home,
12 | meta:{
13 | title: 'StatusLive'
14 | }
15 | }
16 | ]
17 |
18 | const router = new VueRouter({
19 | routes
20 | })
21 |
22 | export default router
23 |
--------------------------------------------------------------------------------
/src/views/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
12 |
13 |
14 |
15 |
16 | 实时总览 Ontime
17 |
18 |
19 |
20 |
21 | 正常运转 {{success}} 个
22 |
23 |
24 |
25 |
26 |
27 | 发生故障 {{danger}} 个
28 |
29 |
30 |
31 |
32 |
33 |
34 | 暂停监控 {{info}} 个
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
53 |
54 |
55 |
56 |
57 | 数据中心 DataCenter
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | {{scope.row.custom_uptime_ratio}}%
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 | 网站 WebSite
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 | {{scope.row.custom_uptime_ratio}}%
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 | 服务 Service
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 | {{scope.row.custom_uptime_ratio}}%
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 | 状态日志 Logs
162 |
163 |
164 |
165 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 | {{logs.name}} {{type_to_text(logs.type)}} - 具体信息:{{logs.reason.code}} - {{logs.reason.detail}} - 持续 {{duration_to_text(logs.duration)}}
179 |
180 |
181 |
182 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
325 |
326 |
851 |
--------------------------------------------------------------------------------
/vue.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | devServer: {
3 | disableHostCheck: true
4 | }
5 | }
--------------------------------------------------------------------------------