mirror of
https://github.com/louislam/uptime-kuma.git
synced 2024-12-16 19:24:41 -05:00
Merge branch 'master' into status-page-domain
# Conflicts: # server/database.js
This commit is contained in:
commit
0afa0be5c2
18
README.md
18
README.md
@ -37,7 +37,6 @@ VPS is sponsored by Uptime Kuma sponsors on [Open Collective](https://opencollec
|
|||||||
### 🐳 Docker
|
### 🐳 Docker
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker volume create uptime-kuma
|
|
||||||
docker run -d --restart=always -p 3001:3001 -v uptime-kuma:/app/data --name uptime-kuma louislam/uptime-kuma:1
|
docker run -d --restart=always -p 3001:3001 -v uptime-kuma:/app/data --name uptime-kuma louislam/uptime-kuma:1
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -47,7 +46,10 @@ Browse to http://localhost:3001 after starting.
|
|||||||
|
|
||||||
### 💪🏻 Non-Docker
|
### 💪🏻 Non-Docker
|
||||||
|
|
||||||
Required Tools: Node.js >= 14, git and pm2.
|
Required Tools:
|
||||||
|
- [Node.js](https://nodejs.org/en/download/) >= 14
|
||||||
|
- [Git](https://git-scm.com/downloads)
|
||||||
|
- [pm2](https://pm2.keymetrics.io/) - For run in background
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Update your npm to the latest version
|
# Update your npm to the latest version
|
||||||
@ -67,11 +69,19 @@ npm install pm2 -g && pm2 install pm2-logrotate
|
|||||||
# Start Server
|
# Start Server
|
||||||
pm2 start server/server.js --name uptime-kuma
|
pm2 start server/server.js --name uptime-kuma
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
Browse to http://localhost:3001 after starting.
|
||||||
|
|
||||||
|
More useful PM2 Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
# If you want to see the current console output
|
# If you want to see the current console output
|
||||||
pm2 monit
|
pm2 monit
|
||||||
```
|
|
||||||
|
|
||||||
Browse to http://localhost:3001 after starting.
|
# If you want to add it to startup
|
||||||
|
pm2 save && pm2 startup
|
||||||
|
```
|
||||||
|
|
||||||
### Advanced Installation
|
### Advanced Installation
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ version: '3.3'
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
uptime-kuma:
|
uptime-kuma:
|
||||||
image: louislam/uptime-kuma
|
image: louislam/uptime-kuma:1
|
||||||
container_name: uptime-kuma
|
container_name: uptime-kuma
|
||||||
volumes:
|
volumes:
|
||||||
- ./uptime-kuma:/app/data
|
- ./uptime-kuma:/app/data
|
||||||
|
@ -4,7 +4,10 @@ const fs = require("fs");
|
|||||||
* to avoid the runtime deprecation warning triggered for using `fs.rmdirSync` with `{ recursive: true }` in Node.js v16,
|
* to avoid the runtime deprecation warning triggered for using `fs.rmdirSync` with `{ recursive: true }` in Node.js v16,
|
||||||
* or the `recursive` property removing completely in the future Node.js version.
|
* or the `recursive` property removing completely in the future Node.js version.
|
||||||
* See the link below.
|
* See the link below.
|
||||||
* @link https://nodejs.org/docs/latest-v16.x/api/deprecations.html#dep0147-fsrmdirpath--recursive-true-
|
*
|
||||||
|
* @todo Once we drop the support for Node.js v14 (or at least versions before v14.14.0), we can safely replace this function with `fs.rmSync`, since `fs.rmSync` was add in Node.js v14.14.0 and currently we supports all the Node.js v14 versions that include the versions before the v14.14.0, and this function have almost the same signature with `fs.rmSync`.
|
||||||
|
* @link https://nodejs.org/docs/latest-v16.x/api/deprecations.html#dep0147-fsrmdirpath--recursive-true- the deprecation infomation of `fs.rmdirSync`
|
||||||
|
* @link https://nodejs.org/docs/latest-v16.x/api/fs.html#fsrmsyncpath-options the document of `fs.rmSync`
|
||||||
* @param {fs.PathLike} path Valid types for path values in "fs".
|
* @param {fs.PathLike} path Valid types for path values in "fs".
|
||||||
* @param {fs.RmDirOptions} [options] options for `fs.rmdirSync`, if `fs.rmSync` is available and property `recursive` is true, it will automatically have property `force` with value `true`.
|
* @param {fs.RmDirOptions} [options] options for `fs.rmdirSync`, if `fs.rmSync` is available and property `recursive` is true, it will automatically have property `force` with value `true`.
|
||||||
*/
|
*/
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
console.log("== Uptime Kuma Reset Password Tool ==");
|
console.log("== Uptime Kuma Reset Password Tool ==");
|
||||||
|
|
||||||
console.log("Loading the database");
|
|
||||||
|
|
||||||
const Database = require("../server/database");
|
const Database = require("../server/database");
|
||||||
const { R } = require("redbean-node");
|
const { R } = require("redbean-node");
|
||||||
const readline = require("readline");
|
const readline = require("readline");
|
||||||
@ -13,8 +11,9 @@ const rl = readline.createInterface({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const main = async () => {
|
const main = async () => {
|
||||||
|
console.log("Connecting the database");
|
||||||
Database.init(args);
|
Database.init(args);
|
||||||
await Database.connect();
|
await Database.connect(false, false, true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// No need to actually reset the password for testing, just make sure no connection problem. It is ok for now.
|
// No need to actually reset the password for testing, just make sure no connection problem. It is ok for now.
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
"build-docker-nightly-alpine": "docker buildx build -f docker/dockerfile-alpine --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly-alpine --target nightly . --push",
|
"build-docker-nightly-alpine": "docker buildx build -f docker/dockerfile-alpine --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly-alpine --target nightly . --push",
|
||||||
"build-docker-nightly-amd64": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push --progress plain",
|
"build-docker-nightly-amd64": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push --progress plain",
|
||||||
"upload-artifacts": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:upload-artifact --build-arg VERSION --build-arg GITHUB_TOKEN --target upload-artifact . --progress plain",
|
"upload-artifacts": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:upload-artifact --build-arg VERSION --build-arg GITHUB_TOKEN --target upload-artifact . --progress plain",
|
||||||
"setup": "git checkout 1.13.1 && npm ci --production && npm run download-dist",
|
"setup": "git checkout 1.13.2 && npm ci --production && npm run download-dist",
|
||||||
"download-dist": "node extra/download-dist.js",
|
"download-dist": "node extra/download-dist.js",
|
||||||
"mark-as-nightly": "node extra/mark-as-nightly.js",
|
"mark-as-nightly": "node extra/mark-as-nightly.js",
|
||||||
"reset-password": "node extra/reset-password.js",
|
"reset-password": "node extra/reset-password.js",
|
||||||
|
@ -55,6 +55,7 @@ class Database {
|
|||||||
"patch-monitor-basic-auth.sql": true,
|
"patch-monitor-basic-auth.sql": true,
|
||||||
"patch-status-page.sql": true,
|
"patch-status-page.sql": true,
|
||||||
"patch-proxy.sql": true,
|
"patch-proxy.sql": true,
|
||||||
|
"patch-monitor-expiry-notification.sql": true,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -82,7 +83,7 @@ class Database {
|
|||||||
console.log(`Data Dir: ${Database.dataDir}`);
|
console.log(`Data Dir: ${Database.dataDir}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async connect(testMode = false) {
|
static async connect(testMode = false, autoloadModels = true, noLog = false) {
|
||||||
const acquireConnectionTimeout = 120 * 1000;
|
const acquireConnectionTimeout = 120 * 1000;
|
||||||
|
|
||||||
const Dialect = require("knex/lib/dialects/sqlite3/index.js");
|
const Dialect = require("knex/lib/dialects/sqlite3/index.js");
|
||||||
@ -112,7 +113,10 @@ class Database {
|
|||||||
|
|
||||||
// Auto map the model to a bean object
|
// Auto map the model to a bean object
|
||||||
R.freeze(true);
|
R.freeze(true);
|
||||||
await R.autoloadModels("./server/model");
|
|
||||||
|
if (autoloadModels) {
|
||||||
|
await R.autoloadModels("./server/model");
|
||||||
|
}
|
||||||
|
|
||||||
await R.exec("PRAGMA foreign_keys = ON");
|
await R.exec("PRAGMA foreign_keys = ON");
|
||||||
if (testMode) {
|
if (testMode) {
|
||||||
@ -130,10 +134,12 @@ class Database {
|
|||||||
// Read more: https://sqlite.org/pragma.html#pragma_synchronous
|
// Read more: https://sqlite.org/pragma.html#pragma_synchronous
|
||||||
await R.exec("PRAGMA synchronous = FULL");
|
await R.exec("PRAGMA synchronous = FULL");
|
||||||
|
|
||||||
console.log("SQLite config:");
|
if (!noLog) {
|
||||||
console.log(await R.getAll("PRAGMA journal_mode"));
|
console.log("SQLite config:");
|
||||||
console.log(await R.getAll("PRAGMA cache_size"));
|
console.log(await R.getAll("PRAGMA journal_mode"));
|
||||||
console.log("SQLite Version: " + await R.getCell("SELECT sqlite_version()"));
|
console.log(await R.getAll("PRAGMA cache_size"));
|
||||||
|
console.log("SQLite Version: " + await R.getCell("SELECT sqlite_version()"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static async patch() {
|
static async patch() {
|
||||||
|
@ -15,12 +15,17 @@ class Mattermost extends NotificationProvider {
|
|||||||
let mattermostTestData = {
|
let mattermostTestData = {
|
||||||
username: mattermostUserName,
|
username: mattermostUserName,
|
||||||
text: msg,
|
text: msg,
|
||||||
}
|
};
|
||||||
await axios.post(notification.mattermostWebhookUrl, mattermostTestData)
|
await axios.post(notification.mattermostWebhookUrl, mattermostTestData);
|
||||||
return okMsg;
|
return okMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mattermostChannel = notification.mattermostchannel.toLowerCase();
|
let mattermostChannel;
|
||||||
|
|
||||||
|
if (typeof notification.mattermostchannel === "string") {
|
||||||
|
mattermostChannel = notification.mattermostchannel.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
const mattermostIconEmoji = notification.mattermosticonemo;
|
const mattermostIconEmoji = notification.mattermosticonemo;
|
||||||
const mattermostIconUrl = notification.mattermosticonurl;
|
const mattermostIconUrl = notification.mattermosticonurl;
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ const HttpProxyAgent = require("http-proxy-agent");
|
|||||||
const HttpsProxyAgent = require("https-proxy-agent");
|
const HttpsProxyAgent = require("https-proxy-agent");
|
||||||
const SocksProxyAgent = require("socks-proxy-agent");
|
const SocksProxyAgent = require("socks-proxy-agent");
|
||||||
const { debug } = require("../src/util");
|
const { debug } = require("../src/util");
|
||||||
|
const server = require("./server");
|
||||||
|
|
||||||
class Proxy {
|
class Proxy {
|
||||||
|
|
||||||
@ -144,6 +145,22 @@ class Proxy {
|
|||||||
httpsAgent
|
httpsAgent
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reload proxy settings for current monitors
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
static async reloadProxy() {
|
||||||
|
let updatedList = await R.getAssoc("SELECT id, proxy_id FROM monitor");
|
||||||
|
|
||||||
|
for (let monitorID in server.monitorList) {
|
||||||
|
let monitor = server.monitorList[monitorID];
|
||||||
|
|
||||||
|
if (updatedList[monitorID]) {
|
||||||
|
monitor.proxy_id = updatedList[monitorID].proxy_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
154
server/server.js
154
server/server.js
@ -48,6 +48,27 @@ debug("Importing 2FA Modules");
|
|||||||
const notp = require("notp");
|
const notp = require("notp");
|
||||||
const base32 = require("thirty-two");
|
const base32 = require("thirty-two");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `module.exports` (alias: `server`) should be inside this class, in order to avoid circular dependency issue.
|
||||||
|
* @type {UptimeKumaServer}
|
||||||
|
*/
|
||||||
|
class UptimeKumaServer {
|
||||||
|
/**
|
||||||
|
* Main monitor list
|
||||||
|
* @type {{}}
|
||||||
|
*/
|
||||||
|
monitorList = {};
|
||||||
|
entryPage = "dashboard";
|
||||||
|
|
||||||
|
async sendMonitorList(socket) {
|
||||||
|
let list = await getMonitorJSONList(socket.userID);
|
||||||
|
io.to(socket.userID).emit("monitorList", list);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const server = module.exports = new UptimeKumaServer();
|
||||||
|
|
||||||
console.log("Importing this project modules");
|
console.log("Importing this project modules");
|
||||||
debug("Importing Monitor");
|
debug("Importing Monitor");
|
||||||
const Monitor = require("./model/monitor");
|
const Monitor = require("./model/monitor");
|
||||||
@ -115,20 +136,20 @@ if (config.demoMode) {
|
|||||||
console.log("Creating express and socket.io instance");
|
console.log("Creating express and socket.io instance");
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
let server;
|
let httpServer;
|
||||||
|
|
||||||
if (sslKey && sslCert) {
|
if (sslKey && sslCert) {
|
||||||
console.log("Server Type: HTTPS");
|
console.log("Server Type: HTTPS");
|
||||||
server = https.createServer({
|
httpServer = https.createServer({
|
||||||
key: fs.readFileSync(sslKey),
|
key: fs.readFileSync(sslKey),
|
||||||
cert: fs.readFileSync(sslCert)
|
cert: fs.readFileSync(sslCert)
|
||||||
}, app);
|
}, app);
|
||||||
} else {
|
} else {
|
||||||
console.log("Server Type: HTTP");
|
console.log("Server Type: HTTP");
|
||||||
server = http.createServer(app);
|
httpServer = http.createServer(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
const io = new Server(server);
|
const io = new Server(httpServer);
|
||||||
module.exports.io = io;
|
module.exports.io = io;
|
||||||
|
|
||||||
// Must be after io instantiation
|
// Must be after io instantiation
|
||||||
@ -138,6 +159,7 @@ const databaseSocketHandler = require("./socket-handlers/database-socket-handler
|
|||||||
const TwoFA = require("./2fa");
|
const TwoFA = require("./2fa");
|
||||||
const StatusPage = require("./model/status_page");
|
const StatusPage = require("./model/status_page");
|
||||||
const { cloudflaredSocketHandler, autoStart: cloudflaredAutoStart, stop: cloudflaredStop } = require("./socket-handlers/cloudflared-socket-handler");
|
const { cloudflaredSocketHandler, autoStart: cloudflaredAutoStart, stop: cloudflaredStop } = require("./socket-handlers/cloudflared-socket-handler");
|
||||||
|
const { proxySocketHandler } = require("./socket-handlers/proxy-socket-handler");
|
||||||
|
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
|
|
||||||
@ -162,12 +184,6 @@ let totalClient = 0;
|
|||||||
*/
|
*/
|
||||||
let jwtSecret = null;
|
let jwtSecret = null;
|
||||||
|
|
||||||
/**
|
|
||||||
* Main monitor list
|
|
||||||
* @type {{}}
|
|
||||||
*/
|
|
||||||
let monitorList = {};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show Setup Page
|
* Show Setup Page
|
||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
@ -190,8 +206,6 @@ try {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.entryPage = "dashboard";
|
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
Database.init(args);
|
Database.init(args);
|
||||||
await initDatabase(testMode);
|
await initDatabase(testMode);
|
||||||
@ -606,7 +620,7 @@ exports.entryPage = "dashboard";
|
|||||||
|
|
||||||
await updateMonitorNotification(bean.id, notificationIDList);
|
await updateMonitorNotification(bean.id, notificationIDList);
|
||||||
|
|
||||||
await sendMonitorList(socket);
|
await server.sendMonitorList(socket);
|
||||||
await startMonitor(socket.userID, bean.id);
|
await startMonitor(socket.userID, bean.id);
|
||||||
|
|
||||||
callback({
|
callback({
|
||||||
@ -635,7 +649,7 @@ exports.entryPage = "dashboard";
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Reset Prometheus labels
|
// Reset Prometheus labels
|
||||||
monitorList[monitor.id]?.prometheus()?.remove();
|
server.monitorList[monitor.id]?.prometheus()?.remove();
|
||||||
|
|
||||||
bean.name = monitor.name;
|
bean.name = monitor.name;
|
||||||
bean.type = monitor.type;
|
bean.type = monitor.type;
|
||||||
@ -669,7 +683,7 @@ exports.entryPage = "dashboard";
|
|||||||
await restartMonitor(socket.userID, bean.id);
|
await restartMonitor(socket.userID, bean.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
await sendMonitorList(socket);
|
await server.sendMonitorList(socket);
|
||||||
|
|
||||||
callback({
|
callback({
|
||||||
ok: true,
|
ok: true,
|
||||||
@ -689,7 +703,7 @@ exports.entryPage = "dashboard";
|
|||||||
socket.on("getMonitorList", async (callback) => {
|
socket.on("getMonitorList", async (callback) => {
|
||||||
try {
|
try {
|
||||||
checkLogin(socket);
|
checkLogin(socket);
|
||||||
await sendMonitorList(socket);
|
await server.sendMonitorList(socket);
|
||||||
callback({
|
callback({
|
||||||
ok: true,
|
ok: true,
|
||||||
});
|
});
|
||||||
@ -763,7 +777,7 @@ exports.entryPage = "dashboard";
|
|||||||
try {
|
try {
|
||||||
checkLogin(socket);
|
checkLogin(socket);
|
||||||
await startMonitor(socket.userID, monitorID);
|
await startMonitor(socket.userID, monitorID);
|
||||||
await sendMonitorList(socket);
|
await server.sendMonitorList(socket);
|
||||||
|
|
||||||
callback({
|
callback({
|
||||||
ok: true,
|
ok: true,
|
||||||
@ -782,7 +796,7 @@ exports.entryPage = "dashboard";
|
|||||||
try {
|
try {
|
||||||
checkLogin(socket);
|
checkLogin(socket);
|
||||||
await pauseMonitor(socket.userID, monitorID);
|
await pauseMonitor(socket.userID, monitorID);
|
||||||
await sendMonitorList(socket);
|
await server.sendMonitorList(socket);
|
||||||
|
|
||||||
callback({
|
callback({
|
||||||
ok: true,
|
ok: true,
|
||||||
@ -803,9 +817,9 @@ exports.entryPage = "dashboard";
|
|||||||
|
|
||||||
console.log(`Delete Monitor: ${monitorID} User ID: ${socket.userID}`);
|
console.log(`Delete Monitor: ${monitorID} User ID: ${socket.userID}`);
|
||||||
|
|
||||||
if (monitorID in monitorList) {
|
if (monitorID in server.monitorList) {
|
||||||
monitorList[monitorID].stop();
|
server.monitorList[monitorID].stop();
|
||||||
delete monitorList[monitorID];
|
delete server.monitorList[monitorID];
|
||||||
}
|
}
|
||||||
|
|
||||||
await R.exec("DELETE FROM monitor WHERE id = ? AND user_id = ? ", [
|
await R.exec("DELETE FROM monitor WHERE id = ? AND user_id = ? ", [
|
||||||
@ -818,7 +832,7 @@ exports.entryPage = "dashboard";
|
|||||||
msg: "Deleted Successfully.",
|
msg: "Deleted Successfully.",
|
||||||
});
|
});
|
||||||
|
|
||||||
await sendMonitorList(socket);
|
await server.sendMonitorList(socket);
|
||||||
// Clear heartbeat list on client
|
// Clear heartbeat list on client
|
||||||
await sendImportantHeartbeatList(socket, monitorID, true, true);
|
await sendImportantHeartbeatList(socket, monitorID, true, true);
|
||||||
|
|
||||||
@ -1118,52 +1132,6 @@ exports.entryPage = "dashboard";
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("addProxy", async (proxy, proxyID, callback) => {
|
|
||||||
try {
|
|
||||||
checkLogin(socket);
|
|
||||||
|
|
||||||
const proxyBean = await Proxy.save(proxy, proxyID, socket.userID);
|
|
||||||
await sendProxyList(socket);
|
|
||||||
|
|
||||||
if (proxy.applyExisting) {
|
|
||||||
await restartMonitors(socket.userID);
|
|
||||||
}
|
|
||||||
|
|
||||||
callback({
|
|
||||||
ok: true,
|
|
||||||
msg: "Saved",
|
|
||||||
id: proxyBean.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (e) {
|
|
||||||
callback({
|
|
||||||
ok: false,
|
|
||||||
msg: e.message,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on("deleteProxy", async (proxyID, callback) => {
|
|
||||||
try {
|
|
||||||
checkLogin(socket);
|
|
||||||
|
|
||||||
await Proxy.delete(proxyID, socket.userID);
|
|
||||||
await sendProxyList(socket);
|
|
||||||
await restartMonitors(socket.userID);
|
|
||||||
|
|
||||||
callback({
|
|
||||||
ok: true,
|
|
||||||
msg: "Deleted",
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (e) {
|
|
||||||
callback({
|
|
||||||
ok: false,
|
|
||||||
msg: e.message,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on("checkApprise", async (callback) => {
|
socket.on("checkApprise", async (callback) => {
|
||||||
try {
|
try {
|
||||||
checkLogin(socket);
|
checkLogin(socket);
|
||||||
@ -1190,8 +1158,8 @@ exports.entryPage = "dashboard";
|
|||||||
// If the import option is "overwrite" it'll clear most of the tables, except "settings" and "user"
|
// If the import option is "overwrite" it'll clear most of the tables, except "settings" and "user"
|
||||||
if (importHandle == "overwrite") {
|
if (importHandle == "overwrite") {
|
||||||
// Stops every monitor first, so it doesn't execute any heartbeat while importing
|
// Stops every monitor first, so it doesn't execute any heartbeat while importing
|
||||||
for (let id in monitorList) {
|
for (let id in server.monitorList) {
|
||||||
let monitor = monitorList[id];
|
let monitor = server.monitorList[id];
|
||||||
await monitor.stop();
|
await monitor.stop();
|
||||||
}
|
}
|
||||||
await R.exec("DELETE FROM heartbeat");
|
await R.exec("DELETE FROM heartbeat");
|
||||||
@ -1354,7 +1322,7 @@ exports.entryPage = "dashboard";
|
|||||||
}
|
}
|
||||||
|
|
||||||
await sendNotificationList(socket);
|
await sendNotificationList(socket);
|
||||||
await sendMonitorList(socket);
|
await server.sendMonitorList(socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
callback({
|
callback({
|
||||||
@ -1444,6 +1412,7 @@ exports.entryPage = "dashboard";
|
|||||||
statusPageSocketHandler(socket);
|
statusPageSocketHandler(socket);
|
||||||
cloudflaredSocketHandler(socket);
|
cloudflaredSocketHandler(socket);
|
||||||
databaseSocketHandler(socket);
|
databaseSocketHandler(socket);
|
||||||
|
proxySocketHandler(socket);
|
||||||
|
|
||||||
debug("added all socket handlers");
|
debug("added all socket handlers");
|
||||||
|
|
||||||
@ -1464,12 +1433,12 @@ exports.entryPage = "dashboard";
|
|||||||
|
|
||||||
console.log("Init the server");
|
console.log("Init the server");
|
||||||
|
|
||||||
server.once("error", async (err) => {
|
httpServer.once("error", async (err) => {
|
||||||
console.error("Cannot listen: " + err.message);
|
console.error("Cannot listen: " + err.message);
|
||||||
await shutdownFunction();
|
await shutdownFunction();
|
||||||
});
|
});
|
||||||
|
|
||||||
server.listen(port, hostname, () => {
|
httpServer.listen(port, hostname, () => {
|
||||||
if (hostname) {
|
if (hostname) {
|
||||||
console.log(`Listening on ${hostname}:${port}`);
|
console.log(`Listening on ${hostname}:${port}`);
|
||||||
} else {
|
} else {
|
||||||
@ -1516,17 +1485,11 @@ async function checkOwner(userID, monitorID) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendMonitorList(socket) {
|
|
||||||
let list = await getMonitorJSONList(socket.userID);
|
|
||||||
io.to(socket.userID).emit("monitorList", list);
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function afterLogin(socket, user) {
|
async function afterLogin(socket, user) {
|
||||||
socket.userID = user.id;
|
socket.userID = user.id;
|
||||||
socket.join(user.id);
|
socket.join(user.id);
|
||||||
|
|
||||||
let monitorList = await sendMonitorList(socket);
|
let monitorList = await server.sendMonitorList(socket);
|
||||||
sendNotificationList(socket);
|
sendNotificationList(socket);
|
||||||
sendProxyList(socket);
|
sendProxyList(socket);
|
||||||
|
|
||||||
@ -1609,11 +1572,11 @@ async function startMonitor(userID, monitorID) {
|
|||||||
monitorID,
|
monitorID,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (monitor.id in monitorList) {
|
if (monitor.id in server.monitorList) {
|
||||||
monitorList[monitor.id].stop();
|
server.monitorList[monitor.id].stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
monitorList[monitor.id] = monitor;
|
server.monitorList[monitor.id] = monitor;
|
||||||
monitor.start(io);
|
monitor.start(io);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1621,19 +1584,6 @@ async function restartMonitor(userID, monitorID) {
|
|||||||
return await startMonitor(userID, monitorID);
|
return await startMonitor(userID, monitorID);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function restartMonitors(userID) {
|
|
||||||
// Fetch all active monitors for user
|
|
||||||
const monitors = await R.getAll("SELECT id FROM monitor WHERE active = 1 AND user_id = ?", [userID]);
|
|
||||||
|
|
||||||
for (const monitor of monitors) {
|
|
||||||
// Start updated monitor
|
|
||||||
await startMonitor(userID, monitor.id);
|
|
||||||
|
|
||||||
// Give some delays, so all monitors won't make request at the same moment when just start the server.
|
|
||||||
await sleep(getRandomInt(300, 1000));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function pauseMonitor(userID, monitorID) {
|
async function pauseMonitor(userID, monitorID) {
|
||||||
await checkOwner(userID, monitorID);
|
await checkOwner(userID, monitorID);
|
||||||
|
|
||||||
@ -1644,8 +1594,8 @@ async function pauseMonitor(userID, monitorID) {
|
|||||||
userID,
|
userID,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (monitorID in monitorList) {
|
if (monitorID in server.monitorList) {
|
||||||
monitorList[monitorID].stop();
|
server.monitorList[monitorID].stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1656,7 +1606,7 @@ async function startMonitors() {
|
|||||||
let list = await R.find("monitor", " active = 1 ");
|
let list = await R.find("monitor", " active = 1 ");
|
||||||
|
|
||||||
for (let monitor of list) {
|
for (let monitor of list) {
|
||||||
monitorList[monitor.id] = monitor;
|
server.monitorList[monitor.id] = monitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let monitor of list) {
|
for (let monitor of list) {
|
||||||
@ -1671,8 +1621,8 @@ async function shutdownFunction(signal) {
|
|||||||
console.log("Called signal: " + signal);
|
console.log("Called signal: " + signal);
|
||||||
|
|
||||||
console.log("Stopping all monitors");
|
console.log("Stopping all monitors");
|
||||||
for (let id in monitorList) {
|
for (let id in server.monitorList) {
|
||||||
let monitor = monitorList[id];
|
let monitor = server.monitorList[id];
|
||||||
monitor.stop();
|
monitor.stop();
|
||||||
}
|
}
|
||||||
await sleep(2000);
|
await sleep(2000);
|
||||||
@ -1686,7 +1636,7 @@ function finalFunction() {
|
|||||||
console.log("Graceful shutdown successful!");
|
console.log("Graceful shutdown successful!");
|
||||||
}
|
}
|
||||||
|
|
||||||
gracefulShutdown(server, {
|
gracefulShutdown(httpServer, {
|
||||||
signals: "SIGINT SIGTERM",
|
signals: "SIGINT SIGTERM",
|
||||||
timeout: 30000, // timeout: 30 secs
|
timeout: 30000, // timeout: 30 secs
|
||||||
development: false, // not in dev mode
|
development: false, // not in dev mode
|
||||||
|
53
server/socket-handlers/proxy-socket-handler.js
Normal file
53
server/socket-handlers/proxy-socket-handler.js
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
const { checkLogin } = require("../util-server");
|
||||||
|
const { Proxy } = require("../proxy");
|
||||||
|
const { sendProxyList } = require("../client");
|
||||||
|
const server = require("../server");
|
||||||
|
|
||||||
|
module.exports.proxySocketHandler = (socket) => {
|
||||||
|
socket.on("addProxy", async (proxy, proxyID, callback) => {
|
||||||
|
try {
|
||||||
|
checkLogin(socket);
|
||||||
|
|
||||||
|
const proxyBean = await Proxy.save(proxy, proxyID, socket.userID);
|
||||||
|
await sendProxyList(socket);
|
||||||
|
|
||||||
|
if (proxy.applyExisting) {
|
||||||
|
await Proxy.reloadProxy();
|
||||||
|
await server.sendMonitorList(socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
callback({
|
||||||
|
ok: true,
|
||||||
|
msg: "Saved",
|
||||||
|
id: proxyBean.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
callback({
|
||||||
|
ok: false,
|
||||||
|
msg: e.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("deleteProxy", async (proxyID, callback) => {
|
||||||
|
try {
|
||||||
|
checkLogin(socket);
|
||||||
|
|
||||||
|
await Proxy.delete(proxyID, socket.userID);
|
||||||
|
await sendProxyList(socket);
|
||||||
|
await Proxy.reloadProxy();
|
||||||
|
|
||||||
|
callback({
|
||||||
|
ok: true,
|
||||||
|
msg: "Deleted",
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
callback({
|
||||||
|
ok: false,
|
||||||
|
msg: e.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
@ -11,23 +11,23 @@
|
|||||||
<table class="text-start">
|
<table class="text-start">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr class="my-3">
|
<tr class="my-3">
|
||||||
<td class="px-3">Subject:</td>
|
<td class="px-3">{{ $t("Subject:") }}</td>
|
||||||
<td>{{ formatSubject(cert.subject) }}</td>
|
<td>{{ formatSubject(cert.subject) }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="my-3">
|
<tr class="my-3">
|
||||||
<td class="px-3">Valid To:</td>
|
<td class="px-3">{{ $t("Valid To:") }}</td>
|
||||||
<td><Datetime :value="cert.validTo" /></td>
|
<td><Datetime :value="cert.validTo" /></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="my-3">
|
<tr class="my-3">
|
||||||
<td class="px-3">Days Remaining:</td>
|
<td class="px-3">{{ $t("Days Remaining:") }}</td>
|
||||||
<td>{{ cert.daysRemaining }}</td>
|
<td>{{ cert.daysRemaining }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="my-3">
|
<tr class="my-3">
|
||||||
<td class="px-3">Issuer:</td>
|
<td class="px-3">{{ $t("Issuer:") }}</td>
|
||||||
<td>{{ formatSubject(cert.issuer) }}</td>
|
<td>{{ formatSubject(cert.issuer) }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="my-3">
|
<tr class="my-3">
|
||||||
<td class="px-3">Fingerprint:</td>
|
<td class="px-3">{{ $t("Fingerprint:") }}</td>
|
||||||
<td>{{ cert.fingerprint }}</td>
|
<td>{{ cert.fingerprint }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -20,11 +20,16 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="errorMessage" class="mt-3">
|
<div v-if="errorMessage" class="mt-3">
|
||||||
Message:
|
{{ $t("Message:") }}
|
||||||
<textarea v-model="errorMessage" class="form-control" readonly></textarea>
|
<textarea v-model="errorMessage" class="form-control" readonly></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p v-if="installed === false">(Download cloudflared from <a href="https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation/">Cloudflare Website</a>)</p>
|
<i18n-t v-if="installed === false" tag="p" keypath="wayToGetCloudflaredURL">
|
||||||
|
<a
|
||||||
|
href="https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation/"
|
||||||
|
target="_blank"
|
||||||
|
>{{ $t("cloudflareWebsite") }}</a>
|
||||||
|
</i18n-t>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- If installed show token input -->
|
<!-- If installed show token input -->
|
||||||
@ -44,7 +49,7 @@
|
|||||||
<span v-if="!running" class="remove-token" @click="removeToken">{{ $t("Remove Token") }}</span>
|
<span v-if="!running" class="remove-token" @click="removeToken">{{ $t("Remove Token") }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Don't know how to get the token? Please read the guide:<br />
|
{{ $t("Don't know how to get the token? Please read the guide:") }}<br />
|
||||||
<a href="https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy-with-Cloudflare-Tunnel" target="_blank">
|
<a href="https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy-with-Cloudflare-Tunnel" target="_blank">
|
||||||
https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy-with-Cloudflare-Tunnel
|
https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy-with-Cloudflare-Tunnel
|
||||||
</a>
|
</a>
|
||||||
@ -61,7 +66,7 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<Confirm ref="confirmStop" btn-style="btn-danger" :yes-text="$t('Stop') + ' cloudflared'" :no-text="$t('Cancel')" @yes="stop">
|
<Confirm ref="confirmStop" btn-style="btn-danger" :yes-text="$t('Stop') + ' cloudflared'" :no-text="$t('Cancel')" @yes="stop">
|
||||||
The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.
|
{{ $t("The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.") }}
|
||||||
|
|
||||||
<div class="mt-3">
|
<div class="mt-3">
|
||||||
<label for="current-password2" class="form-label">
|
<label for="current-password2" class="form-label">
|
||||||
@ -79,10 +84,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h4 class="mt-4">Other Software</h4>
|
<h4 class="mt-4">{{ $t("Other Software") }}</h4>
|
||||||
<div>
|
<div>
|
||||||
For example: nginx, Apache and Traefik. <br />
|
{{ $t("For example: nginx, Apache and Traefik.") }} <br />
|
||||||
Please read <a href="https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy" target="_blank">https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy</a>.
|
{{ $t("Please read") }} <a href="https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy" target="_blank">https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy</a>.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -331,21 +331,21 @@ export default {
|
|||||||
dark: "dark",
|
dark: "dark",
|
||||||
Post: "Post",
|
Post: "Post",
|
||||||
"Please input title and content": "Please input title and content",
|
"Please input title and content": "Please input title and content",
|
||||||
"Created": "Created",
|
Created: "Created",
|
||||||
"Last Updated": "Last Updated",
|
"Last Updated": "Last Updated",
|
||||||
"Unpin": "Unpin",
|
Unpin: "Unpin",
|
||||||
"Switch to Light Theme": "Switch to Light Theme",
|
"Switch to Light Theme": "Switch to Light Theme",
|
||||||
"Switch to Dark Theme": "Switch to Dark Theme",
|
"Switch to Dark Theme": "Switch to Dark Theme",
|
||||||
"Show Tags": "Show Tags",
|
"Show Tags": "Show Tags",
|
||||||
"Hide Tags": "Hide Tags",
|
"Hide Tags": "Hide Tags",
|
||||||
"Description": "Description",
|
Description: "Description",
|
||||||
"No monitors available.": "No monitors available.",
|
"No monitors available.": "No monitors available.",
|
||||||
"Add one": "Add one",
|
"Add one": "Add one",
|
||||||
"No Monitors": "No Monitors",
|
"No Monitors": "No Monitors",
|
||||||
"Untitled Group": "Untitled Group",
|
"Untitled Group": "Untitled Group",
|
||||||
"Services": "Services",
|
Services: "Services",
|
||||||
"Discard": "Discard",
|
Discard: "Discard",
|
||||||
"Cancel": "Cancel",
|
Cancel: "Cancel",
|
||||||
"Powered by": "Powered by",
|
"Powered by": "Powered by",
|
||||||
shrinkDatabaseDescription: "Trigger database VACUUM for SQLite. If your database is created after 1.10.0, AUTO_VACUUM is already enabled and this action is not needed.",
|
shrinkDatabaseDescription: "Trigger database VACUUM for SQLite. If your database is created after 1.10.0, AUTO_VACUUM is already enabled and this action is not needed.",
|
||||||
serwersms: "SerwerSMS.pl",
|
serwersms: "SerwerSMS.pl",
|
||||||
@ -379,4 +379,67 @@ export default {
|
|||||||
proxyDescription: "Proxies must be assigned to a monitor to function.",
|
proxyDescription: "Proxies must be assigned to a monitor to function.",
|
||||||
enableProxyDescription: "This proxy will not effect on monitor requests until it is activated. You can control temporarily disable the proxy from all monitors by activation status.",
|
enableProxyDescription: "This proxy will not effect on monitor requests until it is activated. You can control temporarily disable the proxy from all monitors by activation status.",
|
||||||
setAsDefaultProxyDescription: "This proxy will be enabled by default for new monitors. You can still disable the proxy separately for each monitor.",
|
setAsDefaultProxyDescription: "This proxy will be enabled by default for new monitors. You can still disable the proxy separately for each monitor.",
|
||||||
|
"Certificate Chain": "Certificate Chain",
|
||||||
|
Valid: "Valid",
|
||||||
|
Invalid: "Invalid",
|
||||||
|
AccessKeyId: "AccessKey ID",
|
||||||
|
SecretAccessKey: "AccessKey Secret",
|
||||||
|
PhoneNumbers: "PhoneNumbers",
|
||||||
|
TemplateCode: "TemplateCode",
|
||||||
|
SignName: "SignName",
|
||||||
|
"Sms template must contain parameters: ": "Sms template must contain parameters: ",
|
||||||
|
"Bark Endpoint": "Bark Endpoint",
|
||||||
|
WebHookUrl: "WebHookUrl",
|
||||||
|
SecretKey: "SecretKey",
|
||||||
|
"For safety, must use secret key": "For safety, must use secret key",
|
||||||
|
"Device Token": "Device Token",
|
||||||
|
Platform: "Platform",
|
||||||
|
iOS: "iOS",
|
||||||
|
Android: "Android",
|
||||||
|
Huawei: "Huawei",
|
||||||
|
High: "High",
|
||||||
|
Retry: "Retry",
|
||||||
|
Topic: "Topic",
|
||||||
|
"WeCom Bot Key": "WeCom Bot Key",
|
||||||
|
"Setup Proxy": "Setup Proxy",
|
||||||
|
"Proxy Protocol": "Proxy Protocol",
|
||||||
|
"Proxy Server": "Proxy Server",
|
||||||
|
"Proxy server has authentication": "Proxy server has authentication",
|
||||||
|
User: "User",
|
||||||
|
Installed: "Installed",
|
||||||
|
"Not installed": "Not installed",
|
||||||
|
Running: "Running",
|
||||||
|
"Not running": "Not running",
|
||||||
|
"Remove Token": "Remove Token",
|
||||||
|
Start: "Start",
|
||||||
|
Stop: "Stop",
|
||||||
|
"Uptime Kuma": "Uptime Kuma",
|
||||||
|
"Add New Status Page": "Add New Status Page",
|
||||||
|
Slug: "Slug",
|
||||||
|
"Accept characters:": "Accept characters:",
|
||||||
|
"startOrEndWithOnly": "Start or end with {0} only",
|
||||||
|
"No consecutive dashes": "No consecutive dashes",
|
||||||
|
Next: "Next",
|
||||||
|
"The slug is already taken. Please choose another slug.": "The slug is already taken. Please choose another slug.",
|
||||||
|
"No Proxy": "No Proxy",
|
||||||
|
"HTTP Basic Auth": "HTTP Basic Auth",
|
||||||
|
"New Status Page": "New Status Page",
|
||||||
|
"Page Not Found": "Page Not Found",
|
||||||
|
"Reverse Proxy": "Reverse Proxy",
|
||||||
|
Backup: "Backup",
|
||||||
|
About: "About",
|
||||||
|
wayToGetCloudflaredURL: "(Download cloudflared from {0})",
|
||||||
|
cloudflareWebsite: "Cloudflare Website",
|
||||||
|
"Message:": "Message:",
|
||||||
|
"Don't know how to get the token? Please read the guide:": "Don't know how to get the token? Please read the guide:",
|
||||||
|
"The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.",
|
||||||
|
"Other Software": "Other Software",
|
||||||
|
"For example: nginx, Apache and Traefik.": "For example: nginx, Apache and Traefik.",
|
||||||
|
"Please read": "Please read",
|
||||||
|
"Subject:": "Subject:",
|
||||||
|
"Valid To:": "Valid To:",
|
||||||
|
"Days Remaining:": "Days Remaining:",
|
||||||
|
"Issuer:": "Issuer:",
|
||||||
|
"Fingerprint:": "Fingerprint:",
|
||||||
|
"No status pages": "No status pages",
|
||||||
};
|
};
|
||||||
|
@ -88,8 +88,8 @@ export default {
|
|||||||
Dark: "黑暗",
|
Dark: "黑暗",
|
||||||
Auto: "自动",
|
Auto: "自动",
|
||||||
"Theme - Heartbeat Bar": "主题 - 心跳栏",
|
"Theme - Heartbeat Bar": "主题 - 心跳栏",
|
||||||
Normal: "正常显示",
|
Normal: "正常", // 此处还供 Gorush 的通知优先级功能使用,不应翻译为“正常显示”
|
||||||
Bottom: "靠下显示",
|
Bottom: "靠下",
|
||||||
None: "不显示",
|
None: "不显示",
|
||||||
Timezone: "时区",
|
Timezone: "时区",
|
||||||
"Search Engine Visibility": "搜索引擎可见性",
|
"Search Engine Visibility": "搜索引擎可见性",
|
||||||
@ -373,4 +373,80 @@ export default {
|
|||||||
"For safety, must use secret key": "出于安全考虑,必须使用加签密钥",
|
"For safety, must use secret key": "出于安全考虑,必须使用加签密钥",
|
||||||
WeCom: "企业微信群机器人",
|
WeCom: "企业微信群机器人",
|
||||||
"WeCom Bot Key": "企业微信群机器人 Key",
|
"WeCom Bot Key": "企业微信群机器人 Key",
|
||||||
|
PushByTechulus: "Push by Techulus",
|
||||||
|
gorush: "Gorush",
|
||||||
|
alerta: "Alerta",
|
||||||
|
alertaApiEndpoint: "API 接入点",
|
||||||
|
alertaEnvironment: "环境参数",
|
||||||
|
alertaApiKey: "API Key",
|
||||||
|
alertaAlertState: "报警时的严重性",
|
||||||
|
alertaRecoverState: "恢复后的严重性",
|
||||||
|
deleteStatusPageMsg: "您确认要删除此状态页吗?",
|
||||||
|
Proxies: "代理",
|
||||||
|
default: "默认",
|
||||||
|
enabled: "启用",
|
||||||
|
setAsDefault: "设为默认",
|
||||||
|
deleteProxyMsg: "您确认要在所有监控项中删除此代理吗?",
|
||||||
|
proxyDescription: "代理必须配置到至少一个监控项后才会工作。",
|
||||||
|
enableProxyDescription: "此代理必须启用才能对监控项的网络请求起作用。您可以通过修改激活状态,临时在所有监控项中禁用此代理。",
|
||||||
|
setAsDefaultProxyDescription: "此代理会对新创建的监控项默认激活,您仍可以在监控项配置中单独禁用此代理。",
|
||||||
|
"Proxy Protocol": "代理协议",
|
||||||
|
"Proxy Server": "代理服务器",
|
||||||
|
"Server Address": "服务器地址",
|
||||||
|
"Certificate Chain": "证书链",
|
||||||
|
Valid: "有效",
|
||||||
|
Invalid: "无效",
|
||||||
|
AccessKeyId: "AccessKey ID",
|
||||||
|
SecretAccessKey: "AccessKey Secret",
|
||||||
|
/* 以下为阿里云短信服务 API Dysms#SendSms 的参数 */
|
||||||
|
PhoneNumbers: "PhoneNumbers",
|
||||||
|
TemplateCode: "TemplateCode",
|
||||||
|
SignName: "SignName",
|
||||||
|
/* 以上为阿里云短信服务 API Dysms#SendSms 的参数 */
|
||||||
|
"Bark Endpoint": "Bark 接入点",
|
||||||
|
"Device Token": "Apple Device Token",
|
||||||
|
Platform: "平台",
|
||||||
|
iOS: "iOS",
|
||||||
|
Android: "Android",
|
||||||
|
Huawei: "华为",
|
||||||
|
High: "高",
|
||||||
|
Retry: "重试次数",
|
||||||
|
Topic: "Gorush Topic",
|
||||||
|
"Setup Proxy": "设置代理",
|
||||||
|
"Proxy server has authentication": "代理服务器启用了身份验证功能",
|
||||||
|
User: "用户名",
|
||||||
|
Installed: "已安装",
|
||||||
|
"Not installed": "未安装",
|
||||||
|
Running: "运行中",
|
||||||
|
"Not running": "未运行",
|
||||||
|
"Message:": "信息:",
|
||||||
|
wayToGetCloudflaredURL: "(可从 {0} 下载 cloudflared)",
|
||||||
|
cloudflareWebsite: "Cloudflare 网站",
|
||||||
|
"Don't know how to get the token? Please read the guide:": "不知道如何获取 Token?请阅读指南:",
|
||||||
|
"The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "如果您正在通过 Cloudflare Tunnel 访问网站,则停止可能会导致当前连接断开。您确定要停止吗?请输入密码以确认。",
|
||||||
|
"Other Software": "其他软件",
|
||||||
|
"For example: nginx, Apache and Traefik.": "例如:nginx、Apache 和 Traefik。",
|
||||||
|
"Please read": "请阅读",
|
||||||
|
"Remove Token": "移除 Token",
|
||||||
|
Start: "启动",
|
||||||
|
Stop: "停止",
|
||||||
|
"Uptime Kuma": "Uptime Kuma",
|
||||||
|
"Add New Status Page": "添加新的状态页",
|
||||||
|
Slug: "路径",
|
||||||
|
"Accept characters:": "可接受的字符:",
|
||||||
|
"startOrEndWithOnly": "开头和结尾必须为 {0}",
|
||||||
|
"No consecutive dashes": "不能有连续的破折号",
|
||||||
|
Next: "下一步",
|
||||||
|
"The slug is already taken. Please choose another slug.": "该路径已被使用。请选择其他路径。",
|
||||||
|
"No Proxy": "无代理",
|
||||||
|
"HTTP Basic Auth": "HTTP 基础身份验证",
|
||||||
|
"New Status Page": "新的状态页",
|
||||||
|
"Page Not Found": "状态页未找到",
|
||||||
|
"Reverse Proxy": "反向代理",
|
||||||
|
"Subject:": "颁发给:",
|
||||||
|
"Valid To:": "有效期至:",
|
||||||
|
"Days Remaining:": "剩余有效天数:",
|
||||||
|
"Issuer:": "颁发者:",
|
||||||
|
"Fingerprint:": "指纹:",
|
||||||
|
"No status pages": "无状态页",
|
||||||
};
|
};
|
||||||
|
@ -200,4 +200,183 @@ export default {
|
|||||||
line: "Line Messenger",
|
line: "Line Messenger",
|
||||||
mattermost: "Mattermost",
|
mattermost: "Mattermost",
|
||||||
deleteStatusPageMsg: "是否確定刪除這個 Status Page?",
|
deleteStatusPageMsg: "是否確定刪除這個 Status Page?",
|
||||||
|
"Push URL": "推送網址",
|
||||||
|
needPushEvery: "您應每 {0} 秒呼叫此網址。",
|
||||||
|
pushOptionalParams: "選填參數:{0}",
|
||||||
|
defaultNotificationName: "我的 {notification} 通知 ({number})",
|
||||||
|
here: "此處",
|
||||||
|
Required: "必填",
|
||||||
|
"Bot Token": "機器人權杖",
|
||||||
|
wayToGetTelegramToken: "您可以從 {0} 取得 Token。",
|
||||||
|
"Chat ID": "聊天 ID",
|
||||||
|
supportTelegramChatID: "支援 對話/群組/頻道的聊天 ID",
|
||||||
|
wayToGetTelegramChatID: "傳送訊息給機器人,並前往以下網址以取得您的 chat ID:",
|
||||||
|
"YOUR BOT TOKEN HERE": "在此填入您的機器人權杖",
|
||||||
|
chatIDNotFound: "找不到 Chat ID;請先傳送訊息給機器人",
|
||||||
|
"Post URL": "Post 網址",
|
||||||
|
"Content Type": "Content Type",
|
||||||
|
webhookJsonDesc: "{0} 適合任何現代的 HTTP 伺服器,如 Express.js",
|
||||||
|
webhookFormDataDesc: "{multipart} 適合 PHP。 JSON 必須先經由 {decodeFunction} 剖析。",
|
||||||
|
secureOptionNone: "無 / STARTTLS (25, 587)",
|
||||||
|
secureOptionTLS: "TLS (465)",
|
||||||
|
"Ignore TLS Error": "忽略 TLS 錯誤",
|
||||||
|
"From Email": "寄件人",
|
||||||
|
emailCustomSubject: "自訂主旨",
|
||||||
|
"To Email": "收件人",
|
||||||
|
smtpCC: "CC",
|
||||||
|
smtpBCC: "BCC",
|
||||||
|
"Discord Webhook URL": "Discord Webhook 網址",
|
||||||
|
wayToGetDiscordURL: "您可以前往伺服器設定 -> 整合 -> Webhook -> 新 Webhook 以取得",
|
||||||
|
"Bot Display Name": "機器人顯示名稱",
|
||||||
|
"Prefix Custom Message": "前綴自訂訊息",
|
||||||
|
"Webhook URL": "Webhook 網址",
|
||||||
|
wayToGetTeamsURL: "您可以前往此頁面以了解如何建立 Webhook 網址 {0}。",
|
||||||
|
Number: "號碼",
|
||||||
|
Recipients: "收件人",
|
||||||
|
needSignalAPI: "您需要有 REST API 的 Signal 客戶端。",
|
||||||
|
wayToCheckSignalURL: "您可以前往下列網址以了解如何設定:",
|
||||||
|
signalImportant: "注意: 不得混合收件人的群組和號碼!",
|
||||||
|
"Application Token": "應用程式權杖",
|
||||||
|
"Server URL": "伺服器網址",
|
||||||
|
Priority: "優先度",
|
||||||
|
"Icon Emoji": "Emoji 圖示",
|
||||||
|
"Channel Name": "頻道名稱",
|
||||||
|
"Uptime Kuma URL": "Uptime Kuma 網址",
|
||||||
|
aboutWebhooks: "更多關於 Webhook 的資訊: {0}",
|
||||||
|
aboutChannelName: "如果您不想使用 Webhook 頻道,請在 {0} 頻道名稱欄位填入您想使用的頻道。例如: #其他頻道",
|
||||||
|
aboutKumaURL: "如果您未填入 Uptime Kuma 網址。將預設使用專案 Github 頁面。",
|
||||||
|
emojiCheatSheet: "Emoji 一覽表: {0}",
|
||||||
|
PushByTechulus: "Push by Techulus",
|
||||||
|
clicksendsms: "ClickSend SMS",
|
||||||
|
GoogleChat: "Google Chat (僅限 Google Workspace)",
|
||||||
|
"User Key": "使用者金鑰",
|
||||||
|
Device: "裝置",
|
||||||
|
"Message Title": "訊息標題",
|
||||||
|
"Notification Sound": "通知音效",
|
||||||
|
"More info on:": "更多資訊: {0}",
|
||||||
|
pushoverDesc1: "緊急優先度 (2) 的重試間隔為 30 秒並且會在 1 小時後過期。",
|
||||||
|
pushoverDesc2: "如果您想要傳送通知到不同裝置,請填寫裝置欄位。",
|
||||||
|
"SMS Type": "簡訊類型",
|
||||||
|
octopushTypePremium: "Premium (快速 - 建議用於警報)",
|
||||||
|
octopushTypeLowCost: "Low Cost (緩慢 - 有時會被營運商阻擋)",
|
||||||
|
checkPrice: "查看 {0} 價格:",
|
||||||
|
apiCredentials: "API 認證",
|
||||||
|
octopushLegacyHint: "您使用的是舊版的 Octopush (2011-2020) 還是新版?",
|
||||||
|
"Check octopush prices": "查看 octopush 價格 {0}。",
|
||||||
|
octopushPhoneNumber: "電話號碼 (intl 格式,例如:+33612345678) ",
|
||||||
|
octopushSMSSender: "簡訊寄件人名稱:3-11位英數字元及空白 (a-zA-Z0-9)",
|
||||||
|
"LunaSea Device ID": "LunaSea 裝置 ID",
|
||||||
|
"Apprise URL": "Apprise 網址",
|
||||||
|
"Example:": "範例:{0}",
|
||||||
|
"Read more:": "深入瞭解:{0}",
|
||||||
|
"Status:": "狀態:{0}",
|
||||||
|
"Read more": "深入瞭解",
|
||||||
|
appriseInstalled: "已安裝 Apprise。",
|
||||||
|
appriseNotInstalled: "尚未安裝 Apprise。{0}",
|
||||||
|
"Access Token": "存取權杖",
|
||||||
|
"Channel access token": "頻道存取權杖",
|
||||||
|
"Line Developers Console": "Line 開發者控制台",
|
||||||
|
lineDevConsoleTo: "Line 開發者控制台 - {0}",
|
||||||
|
"Basic Settings": "基本設定",
|
||||||
|
"User ID": "使用者 ID",
|
||||||
|
"Messaging API": "Messaging API",
|
||||||
|
wayToGetLineChannelToken: "首先,前往 {0},建立 provider 和 channel (Messaging API)。接著您就可以從上面提到的選單項目中取得頻道存取權杖及使用者 ID。",
|
||||||
|
"Icon URL": "圖示網址",
|
||||||
|
aboutIconURL: "您可以在 \"圖示網址\" 中提供圖片網址以覆蓋預設個人檔案圖片。若已設定 Emoji 圖示,將忽略此設定。",
|
||||||
|
aboutMattermostChannelName: "您可以在 \"頻道名稱\" 欄位中填寫頻道名稱以覆蓋 Webhook 的預設頻道。必須在 Mattermost 的 Webhook 設定中啟用。例如:#其他頻道",
|
||||||
|
matrix: "Matrix",
|
||||||
|
promosmsTypeEco: "SMS ECO - 便宜,但是很慢且經常過載。僅限位於波蘭的收件人。",
|
||||||
|
promosmsTypeFlash: "SMS FLASH - 訊息會自動在收件人的裝置上顯示。僅限位於波蘭的收件人。",
|
||||||
|
promosmsTypeFull: "SMS FULL - 高級版,您可以使用您的寄件人名稱 (必須先註冊名稱。對於警報來說十分可靠。",
|
||||||
|
promosmsTypeSpeed: "SMS SPEED - 系統中的最高優先度。快速、可靠,但昂貴 (約 SMS FULL 的兩倍價格)。",
|
||||||
|
promosmsPhoneNumber: "電話號碼 (若收件人位於波蘭則無需輸入區域代碼)",
|
||||||
|
promosmsSMSSender: "簡訊寄件人名稱:預先註冊的名稱或以下的預設名稱:InfoSMS、SMS Info、MaxSMS、INFO、SMS",
|
||||||
|
"Feishu WebHookUrl": "飛書 WebHook 網址",
|
||||||
|
matrixHomeserverURL: "Homeserver 網址 (開頭為 http(s)://,結尾可能帶連接埠)",
|
||||||
|
"Internal Room Id": "Internal Room ID",
|
||||||
|
matrixDesc1: "您可以在 Matrix 客戶端的房間設定中的進階選項找到 internal room ID。應該看起來像 !QMdRCpUIfLwsfjxye6:home.server。",
|
||||||
|
matrixDesc2: "使用您自己的 Matrix 使用者存取權杖將賦予存取您的帳號和您加入的房間的完整權限。建議建立新使用者,並邀請至您想要接收通知的房間中。您可以執行 {0} 以取得存取權杖",
|
||||||
|
Method: "方法",
|
||||||
|
Body: "主體",
|
||||||
|
Headers: "標頭",
|
||||||
|
PushUrl: "Push URL",
|
||||||
|
HeadersInvalidFormat: "要求標頭不是有效的 JSON:",
|
||||||
|
BodyInvalidFormat: "請求主體不是有效的 JSON:",
|
||||||
|
"Monitor History": "監測器歷史紀錄",
|
||||||
|
clearDataOlderThan: "保留 {0} 天內的監測器歷史紀錄。",
|
||||||
|
PasswordsDoNotMatch: "密碼不相符。",
|
||||||
|
records: "記錄",
|
||||||
|
"One record": "一項記錄",
|
||||||
|
"Showing {from} to {to} of {count} records": "正在顯示 {count} 項記錄中的 {from} 至 {to} 項",
|
||||||
|
steamApiKeyDescription: "若要監測 Steam 遊戲伺服器,您將需要 Steam Web-API 金鑰。您可以在此註冊您的 API 金鑰:",
|
||||||
|
"Current User": "目前使用者",
|
||||||
|
recent: "最近",
|
||||||
|
Done: "完成",
|
||||||
|
Info: "資訊",
|
||||||
|
Security: "安全性",
|
||||||
|
"Steam API Key": "Steam API 金鑰",
|
||||||
|
"Shrink Database": "壓縮資料庫",
|
||||||
|
"Pick a RR-Type...": "選擇資源記錄類型...",
|
||||||
|
"Pick Accepted Status Codes...": "選擇可接受的狀態碼...",
|
||||||
|
Default: "預設",
|
||||||
|
"HTTP Options": "HTTP 選項",
|
||||||
|
"Create Incident": "建立事件",
|
||||||
|
Title: "標題",
|
||||||
|
Content: "內容",
|
||||||
|
Style: "樣式",
|
||||||
|
info: "資訊",
|
||||||
|
warning: "警告",
|
||||||
|
danger: "危險",
|
||||||
|
primary: "主要",
|
||||||
|
light: "淺色",
|
||||||
|
dark: "暗色",
|
||||||
|
Post: "發佈",
|
||||||
|
"Please input title and content": "請輸入標題及內容",
|
||||||
|
Created: "建立",
|
||||||
|
"Last Updated": "最後更新",
|
||||||
|
Unpin: "取消釘選",
|
||||||
|
"Switch to Light Theme": "切換至淺色佈景主題",
|
||||||
|
"Switch to Dark Theme": "切換至深色佈景主題",
|
||||||
|
"Show Tags": "顯示標籤",
|
||||||
|
"Hide Tags": "隱藏標籤",
|
||||||
|
Description: "說明",
|
||||||
|
"No monitors available.": "沒有可用的監測器。",
|
||||||
|
"Add one": "新增一個",
|
||||||
|
"No Monitors": "無監測器",
|
||||||
|
"Untitled Group": "未命名群組",
|
||||||
|
Services: "服務",
|
||||||
|
Discard: "捨棄",
|
||||||
|
Cancel: "取消",
|
||||||
|
"Powered by": "技術支援",
|
||||||
|
shrinkDatabaseDescription: "觸發 SQLite 的資料庫清理 (VACUUM)。如果您的資料庫是在 1.10.0 版本後建立,AUTO_VACUUM 已自動啟用,則無需此操作。",
|
||||||
|
serwersms: "SerwerSMS.pl",
|
||||||
|
serwersmsAPIUser: "API 使用者名稱 (包括 webapi_ 前綴)",
|
||||||
|
serwersmsAPIPassword: "API 密碼",
|
||||||
|
serwersmsPhoneNumber: "電話號碼",
|
||||||
|
serwersmsSenderName: "SMS 寄件人名稱 (由客戶入口網站註冊)",
|
||||||
|
stackfield: "Stackfield",
|
||||||
|
smtpDkimSettings: "DKIM 設定",
|
||||||
|
smtpDkimDesc: "請參考 Nodemailer DKIM {0} 使用方式。",
|
||||||
|
documentation: "文件",
|
||||||
|
smtpDkimDomain: "網域名稱",
|
||||||
|
smtpDkimKeySelector: "DKIM 選取器",
|
||||||
|
smtpDkimPrivateKey: "私密金鑰",
|
||||||
|
smtpDkimHashAlgo: "雜湊演算法 (選填)",
|
||||||
|
smtpDkimheaderFieldNames: "要簽署的郵件標頭 (選填)",
|
||||||
|
smtpDkimskipFields: "不簽署的郵件標頭 (選填)",
|
||||||
|
gorush: "Gorush",
|
||||||
|
alerta: "Alerta",
|
||||||
|
alertaApiEndpoint: "API Endpoint",
|
||||||
|
alertaEnvironment: "環境",
|
||||||
|
alertaApiKey: "API 金鑰",
|
||||||
|
alertaAlertState: "警示狀態",
|
||||||
|
alertaRecoverState: "恢復狀態",
|
||||||
|
Proxies: "代理伺服器",
|
||||||
|
default: "預設",
|
||||||
|
enabled: "啟用",
|
||||||
|
setAsDefault: "設為預設",
|
||||||
|
deleteProxyMsg: "您確定要為所有監測器刪除此代理伺服器嗎?",
|
||||||
|
proxyDescription: "必須將代理伺服器指派給監測器才能運作。",
|
||||||
|
enableProxyDescription: "此代理伺服器在啟用前不會在監測器上生效,您可以藉由控制啟用狀態來暫時對所有的監測器停用代理伺服器。",
|
||||||
|
setAsDefaultProxyDescription: "預設情況下,新監測器將啟用此代理伺服器。您仍可分別停用各監測器的代理伺服器。",
|
||||||
};
|
};
|
||||||
|
@ -239,11 +239,13 @@ export default {
|
|||||||
"rocket.chat": "Rocket.Chat",
|
"rocket.chat": "Rocket.Chat",
|
||||||
pushover: "Pushover",
|
pushover: "Pushover",
|
||||||
pushy: "Pushy",
|
pushy: "Pushy",
|
||||||
|
PushByTechulus: "Push by Techulus",
|
||||||
octopush: "Octopush",
|
octopush: "Octopush",
|
||||||
promosms: "PromoSMS",
|
promosms: "PromoSMS",
|
||||||
clicksendsms: "ClickSend SMS",
|
clicksendsms: "ClickSend SMS",
|
||||||
lunasea: "LunaSea",
|
lunasea: "LunaSea",
|
||||||
apprise: "Apprise (支援 50 種以上的通知服務)",
|
apprise: "Apprise (支援 50 種以上的通知服務)",
|
||||||
|
GoogleChat: "Google Chat (僅限 Google Workspace)",
|
||||||
pushbullet: "Pushbullet",
|
pushbullet: "Pushbullet",
|
||||||
line: "Line Messenger",
|
line: "Line Messenger",
|
||||||
mattermost: "Mattermost",
|
mattermost: "Mattermost",
|
||||||
@ -352,5 +354,30 @@ export default {
|
|||||||
serwersmsAPIPassword: "API 密碼",
|
serwersmsAPIPassword: "API 密碼",
|
||||||
serwersmsPhoneNumber: "電話號碼",
|
serwersmsPhoneNumber: "電話號碼",
|
||||||
serwersmsSenderName: "SMS 寄件人名稱 (由客戶入口網站註冊)",
|
serwersmsSenderName: "SMS 寄件人名稱 (由客戶入口網站註冊)",
|
||||||
"stackfield": "Stackfield",
|
stackfield: "Stackfield",
|
||||||
|
smtpDkimSettings: "DKIM 設定",
|
||||||
|
smtpDkimDesc: "請參考 Nodemailer DKIM {0} 使用方式。",
|
||||||
|
documentation: "文件",
|
||||||
|
smtpDkimDomain: "網域名稱",
|
||||||
|
smtpDkimKeySelector: "DKIM 選取器",
|
||||||
|
smtpDkimPrivateKey: "私密金鑰",
|
||||||
|
smtpDkimHashAlgo: "雜湊演算法 (選填)",
|
||||||
|
smtpDkimheaderFieldNames: "要簽署的郵件標頭 (選填)",
|
||||||
|
smtpDkimskipFields: "不簽署的郵件標頭 (選填)",
|
||||||
|
gorush: "Gorush",
|
||||||
|
alerta: "Alerta",
|
||||||
|
alertaApiEndpoint: "API Endpoint",
|
||||||
|
alertaEnvironment: "環境",
|
||||||
|
alertaApiKey: "API 金鑰",
|
||||||
|
alertaAlertState: "警示狀態",
|
||||||
|
alertaRecoverState: "恢復狀態",
|
||||||
|
deleteStatusPageMsg: "您確定要刪除此狀態頁嗎?",
|
||||||
|
Proxies: "代理伺服器",
|
||||||
|
default: "預設",
|
||||||
|
enabled: "啟用",
|
||||||
|
setAsDefault: "設為預設",
|
||||||
|
deleteProxyMsg: "您確定要為所有監測器刪除此代理伺服器嗎?",
|
||||||
|
proxyDescription: "必須將代理伺服器指派給監測器才能運作。",
|
||||||
|
enableProxyDescription: "此代理伺服器在啟用前不會在監測器上生效,您可以藉由控制啟用狀態來暫時對所有的監測器停用代理伺服器。",
|
||||||
|
setAsDefaultProxyDescription: "預設情況下,新監測器將啟用此代理伺服器。您仍可分別停用各監測器的代理伺服器。",
|
||||||
};
|
};
|
||||||
|
@ -21,7 +21,9 @@
|
|||||||
<div class="form-text">
|
<div class="form-text">
|
||||||
<ul>
|
<ul>
|
||||||
<li>{{ $t("Accept characters:") }} <mark>a-z</mark> <mark>0-9</mark> <mark>-</mark></li>
|
<li>{{ $t("Accept characters:") }} <mark>a-z</mark> <mark>0-9</mark> <mark>-</mark></li>
|
||||||
<li>{{ $t("Start or end with") }} <mark>a-z</mark> <mark>0-9</mark> only</li>
|
<i18n-t tag="li" keypath="startOrEndWithOnly">
|
||||||
|
<mark>a-z</mark> <mark>0-9</mark>
|
||||||
|
</i18n-t>
|
||||||
<li>{{ $t("No consecutive dashes") }} <mark>--</mark></li>
|
<li>{{ $t("No consecutive dashes") }} <mark>--</mark></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -232,31 +232,33 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Proxies -->
|
<!-- Proxies -->
|
||||||
<h2 class="mt-5 mb-2">{{ $t("Proxies") }}</h2>
|
<div v-if="monitor.type === 'http' || monitor.type === 'keyword'">
|
||||||
<p v-if="$root.proxyList.length === 0">
|
<h2 class="mt-5 mb-2">{{ $t("Proxy") }}</h2>
|
||||||
{{ $t("Not available, please setup.") }}
|
<p v-if="$root.proxyList.length === 0">
|
||||||
</p>
|
{{ $t("Not available, please setup.") }}
|
||||||
|
</p>
|
||||||
|
|
||||||
<div v-if="$root.proxyList.length > 0" class="form-check form-switch my-3">
|
<div v-if="$root.proxyList.length > 0" class="form-check my-3">
|
||||||
<input id="proxy-disable" v-model="monitor.proxyId" :value="null" name="proxy" class="form-check-input" type="radio">
|
<input id="proxy-disable" v-model="monitor.proxyId" :value="null" name="proxy" class="form-check-input" type="radio">
|
||||||
<label class="form-check-label" for="proxy-disable">{{ $t("No Proxy") }}</label>
|
<label class="form-check-label" for="proxy-disable">{{ $t("No Proxy") }}</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-for="proxy in $root.proxyList" :key="proxy.id" class="form-check my-3">
|
||||||
|
<input :id="`proxy-${proxy.id}`" v-model="monitor.proxyId" :value="proxy.id" name="proxy" class="form-check-input" type="radio">
|
||||||
|
|
||||||
|
<label class="form-check-label" :for="`proxy-${proxy.id}`">
|
||||||
|
{{ proxy.host }}:{{ proxy.port }} ({{ proxy.protocol }})
|
||||||
|
<a href="#" @click="$refs.proxyDialog.show(proxy.id)">{{ $t("Edit") }}</a>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<span v-if="proxy.default === true" class="badge bg-primary ms-2">{{ $t("default") }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="btn btn-primary me-2" type="button" @click="$refs.proxyDialog.show()">
|
||||||
|
{{ $t("Setup Proxy") }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-for="proxy in $root.proxyList" :key="proxy.id" class="form-check form-switch my-3">
|
|
||||||
<input :id="`proxy-${proxy.id}`" v-model="monitor.proxyId" :value="proxy.id" name="proxy" class="form-check-input" type="radio">
|
|
||||||
|
|
||||||
<label class="form-check-label" :for="`proxy-${proxy.id}`">
|
|
||||||
{{ proxy.host }}:{{ proxy.port }} ({{ proxy.protocol }})
|
|
||||||
<a href="#" @click="$refs.proxyDialog.show(proxy.id)">{{ $t("Edit") }}</a>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<span v-if="proxy.default === true" class="badge bg-primary ms-2">{{ $t("default") }}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button class="btn btn-primary me-2" type="button" @click="$refs.proxyDialog.show()">
|
|
||||||
{{ $t("Setup Proxy") }}
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- HTTP Options -->
|
<!-- HTTP Options -->
|
||||||
<template v-if="monitor.type === 'http' || monitor.type === 'keyword' ">
|
<template v-if="monitor.type === 'http' || monitor.type === 'keyword' ">
|
||||||
<h2 class="mt-5 mb-2">{{ $t("HTTP Options") }}</h2>
|
<h2 class="mt-5 mb-2">{{ $t("HTTP Options") }}</h2>
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
<div class="shadow-box">
|
<div class="shadow-box">
|
||||||
<template v-if="$root.statusPageListLoaded">
|
<template v-if="$root.statusPageListLoaded">
|
||||||
<span v-if="Object.keys($root.statusPageList).length === 0" class="d-flex align-items-center justify-content-center my-3">
|
<span v-if="Object.keys($root.statusPageList).length === 0" class="d-flex align-items-center justify-content-center my-3">
|
||||||
No status pages
|
{{ $t("No status pages") }}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<!-- use <a> instead of <router-link>, because the heartbeat won't load. -->
|
<!-- use <a> instead of <router-link>, because the heartbeat won't load. -->
|
||||||
|
Loading…
Reference in New Issue
Block a user