diff --git a/.eslintrc.js b/.eslintrc.js
index 4b60d6728..ef556a346 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -84,7 +84,7 @@ module.exports = {
"checkLoops": false,
}],
"space-before-blocks": "warn",
- //'no-console': 'warn',
+ //"no-console": "warn",
"no-extra-boolean-cast": "off",
"no-multiple-empty-lines": [ "warn", {
"max": 1,
@@ -96,7 +96,8 @@ module.exports = {
"no-unneeded-ternary": "error",
"array-bracket-newline": [ "error", "consistent" ],
"eol-last": [ "error", "always" ],
- //'prefer-template': 'error',
+ //"prefer-template": "error",
+ "template-curly-spacing": [ "warn", "never" ],
"comma-dangle": [ "warn", "only-multiline" ],
"no-empty": [ "error", {
"allowEmptyCatch": true
diff --git a/.github/workflows/auto-test.yml b/.github/workflows/auto-test.yml
index 227693199..853548ffe 100644
--- a/.github/workflows/auto-test.yml
+++ b/.github/workflows/auto-test.yml
@@ -27,10 +27,10 @@ jobs:
steps:
- run: git config --global core.autocrlf false # Mainly for Windows
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node }}
- uses: actions/setup-node@v3
+ uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- run: npm install npm@9 -g
@@ -55,10 +55,10 @@ jobs:
steps:
- run: git config --global core.autocrlf false # Mainly for Windows
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node }}
- uses: actions/setup-node@v3
+ uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- run: npm install npm@9 -g
@@ -69,40 +69,39 @@ jobs:
steps:
- run: git config --global core.autocrlf false # Mainly for Windows
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Use Node.js 20
- uses: actions/setup-node@v3
+ uses: actions/setup-node@v4
with:
node-version: 20
- run: npm install
- - run: npm run lint
+ - run: npm run lint:prod
-# TODO: Temporarily disable, as it cannot pass the test in 2.0.0 yet
-# e2e-tests:
-# needs: [ check-linters ]
-# runs-on: ubuntu-latest
-# steps:
-# - run: git config --global core.autocrlf false # Mainly for Windows
-# - uses: actions/checkout@v3
-#
-# - name: Use Node.js 14
-# uses: actions/setup-node@v3
-# with:
-# node-version: 14
-# - run: npm install
-# - run: npm run build
-# - run: npm run cy:test
+ e2e-tests:
+ needs: [ check-linters ]
+ runs-on: ubuntu-latest
+ steps:
+ - run: git config --global core.autocrlf false # Mainly for Windows
+ - uses: actions/checkout@v4
+
+ - name: Use Node.js 14
+ uses: actions/setup-node@v4
+ with:
+ node-version: 14
+ - run: npm install
+ - run: npm run build
+ - run: npm run cy:test
frontend-unit-tests:
needs: [ check-linters ]
runs-on: ubuntu-latest
steps:
- run: git config --global core.autocrlf false # Mainly for Windows
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Use Node.js 14
- uses: actions/setup-node@v3
+ uses: actions/setup-node@v4
with:
node-version: 14
- run: npm install
diff --git a/.github/workflows/close-incorrect-issue.yml b/.github/workflows/close-incorrect-issue.yml
index 762bc9688..e26cf5e5e 100644
--- a/.github/workflows/close-incorrect-issue.yml
+++ b/.github/workflows/close-incorrect-issue.yml
@@ -14,10 +14,10 @@ jobs:
node-version: [16]
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
- uses: actions/setup-node@v3
+ uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
diff --git a/.github/workflows/json-yaml-validate.yml b/.github/workflows/json-yaml-validate.yml
index 104e37a17..b6437ec49 100644
--- a/.github/workflows/json-yaml-validate.yml
+++ b/.github/workflows/json-yaml-validate.yml
@@ -6,7 +6,7 @@ on:
pull_request:
branches:
- master
- - 2.0.X
+ - 1.23.X
workflow_dispatch:
permissions:
@@ -17,11 +17,11 @@ jobs:
json-yaml-validate:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: json-yaml-validate
id: json-yaml-validate
- uses: GrantBirki/json-yaml-validate@v1.3.0
+ uses: GrantBirki/json-yaml-validate@v2.4.0
with:
comment: "true" # enable comment mode
exclude_file: ".github/config/exclude.txt" # gitignore style file for exclusions
diff --git a/config/vite.config.js b/config/vite.config.js
index 5d9c5c1d5..8f2a076fb 100644
--- a/config/vite.config.js
+++ b/config/vite.config.js
@@ -2,7 +2,6 @@ import vue from "@vitejs/plugin-vue";
import { defineConfig } from "vite";
import visualizer from "rollup-plugin-visualizer";
import viteCompression from "vite-plugin-compression";
-import commonjs from "vite-plugin-commonjs";
const postCssScss = require("postcss-scss");
const postcssRTLCSS = require("postcss-rtlcss");
@@ -21,7 +20,6 @@ export default defineConfig({
"CODESPACE_NAME": JSON.stringify(process.env.CODESPACE_NAME),
},
plugins: [
- commonjs(),
vue(),
visualizer({
filename: "tmp/dist-stats.html"
diff --git a/extra/healthcheck.js b/extra/healthcheck.js
index 5e06c2120..c9391c410 100644
--- a/extra/healthcheck.js
+++ b/extra/healthcheck.js
@@ -6,7 +6,7 @@
* ⚠️ Deprecated: Changed to healthcheck.go, it will be deleted in the future.
* This script should be run after a period of time (180s), because the server may need some time to prepare.
*/
-const { FBSD } = require("../server/util-server");
+const FBSD = /^freebsd/.test(process.platform);
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
diff --git a/extra/reset-password.js b/extra/reset-password.js
index 2fbc622de..ec3ede6cc 100644
--- a/extra/reset-password.js
+++ b/extra/reset-password.js
@@ -5,6 +5,8 @@ const { R } = require("redbean-node");
const readline = require("readline");
const { initJWTSecret } = require("../server/util-server");
const User = require("../server/model/user");
+const { io } = require("socket.io-client");
+const { localWebSocketURL } = require("../server/config");
const args = require("args-parser")(process.argv);
const rl = readline.createInterface({
input: process.stdin,
@@ -50,6 +52,9 @@ const main = async () => {
// Reset all sessions by reset jwt secret
await initJWTSecret();
+
+ // Disconnect all other socket clients of the user
+ await disconnectAllSocketClients(user.username, password);
}
break;
} else {
@@ -57,6 +62,7 @@ const main = async () => {
}
}
console.log("Password reset successfully.");
+
}
} catch (e) {
console.error("Error: " + e.message);
@@ -81,6 +87,45 @@ function question(question) {
});
}
+function disconnectAllSocketClients(username, password) {
+ return new Promise((resolve) => {
+ console.log("Connecting to " + localWebSocketURL + " to disconnect all other socket clients");
+
+ // Disconnect all socket connections
+ const socket = io(localWebSocketURL, {
+ transports: [ "websocket" ],
+ reconnection: false,
+ timeout: 5000,
+ });
+ socket.on("connect", () => {
+ socket.emit("login", {
+ username,
+ password,
+ }, (res) => {
+ if (res.ok) {
+ console.log("Logged in.");
+ socket.emit("disconnectOtherSocketClients");
+ } else {
+ console.warn("Login failed.");
+ console.warn("Please restart the server to disconnect all sessions.");
+ }
+ socket.close();
+ });
+ });
+
+ socket.on("connect_error", function () {
+ // The localWebSocketURL is not guaranteed to be working for some complicated Uptime Kuma setup
+ // Ask the user to restart the server manually
+ console.warn("Failed to connect to " + localWebSocketURL);
+ console.warn("Please restart the server to disconnect all sessions manually.");
+ resolve();
+ });
+ socket.on("disconnect", () => {
+ resolve();
+ });
+ });
+}
+
if (!process.env.TEST_BACKEND) {
main();
}
diff --git a/package.json b/package.json
index 456e2c5de..292894474 100644
--- a/package.json
+++ b/package.json
@@ -11,10 +11,12 @@
},
"scripts": {
"lint:js": "eslint --ext \".js,.vue\" --ignore-path .gitignore .",
+ "lint:js-prod": "npm run lint:js -- --max-warnings 0",
"lint-fix:js": "eslint --ext \".js,.vue\" --fix --ignore-path .gitignore .",
"lint:style": "stylelint \"**/*.{vue,css,scss}\" --ignore-path .gitignore",
"lint-fix:style": "stylelint \"**/*.{vue,css,scss}\" --fix --ignore-path .gitignore",
"lint": "npm run lint:js && npm run lint:style",
+ "lint:prod": "npm run lint:js-prod && npm run lint:style",
"dev": "concurrently -k -r \"wait-on tcp:3000 && npm run start-server-dev \" \"npm run start-frontend-dev\"",
"start-frontend-dev": "cross-env NODE_ENV=development vite --host --config ./config/vite.config.js",
"start-frontend-devcontainer": "cross-env NODE_ENV=development DEVCONTAINER=1 vite --host --config ./config/vite.config.js",
@@ -44,7 +46,7 @@
"build-docker-nightly-local": "npm run build && docker build -f docker/dockerfile -t louislam/uptime-kuma:nightly2 --target nightly .",
"build-docker-pr-test": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64 -t louislam/uptime-kuma:pr-test2 --target pr-test2 . --push",
"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.23.8 && npm ci --production && npm run download-dist",
+ "setup": "git checkout 1.23.9 && npm ci --production && npm run download-dist",
"download-dist": "node extra/download-dist.js",
"mark-as-nightly": "node extra/mark-as-nightly.js",
"reset-password": "node extra/reset-password.js",
@@ -191,7 +193,6 @@
"typescript": "~4.4.4",
"v-pagination-3": "~0.1.7",
"vite": "~4.4.1",
- "vite-plugin-commonjs": "^0.8.0",
"vite-plugin-compression": "^0.5.1",
"vue": "~3.3.4",
"vue-chartjs": "~5.2.0",
diff --git a/public/icon.svg b/public/icon.svg
index bad8394c2..c4217915d 100644
--- a/public/icon.svg
+++ b/public/icon.svg
@@ -1,10 +1,9 @@
-
diff --git a/server/config.js b/server/config.js
index 77f9e74b3..635c37e7f 100644
--- a/server/config.js
+++ b/server/config.js
@@ -1,29 +1,42 @@
+const isFreeBSD = /^freebsd/.test(process.platform);
+
// Interop with browser
const args = (typeof process !== "undefined") ? require("args-parser")(process.argv) : {};
-const demoMode = args["demo"] || false;
-const badgeConstants = {
- naColor: "#999",
- defaultUpColor: "#66c20a",
- defaultWarnColor: "#eed202",
- defaultDownColor: "#c2290a",
- defaultPendingColor: "#f8a306",
- defaultMaintenanceColor: "#1747f5",
- defaultPingColor: "blue", // as defined by badge-maker / shields.io
- defaultStyle: "flat",
- defaultPingValueSuffix: "ms",
- defaultPingLabelSuffix: "h",
- defaultUptimeValueSuffix: "%",
- defaultUptimeLabelSuffix: "h",
- defaultCertExpValueSuffix: " days",
- defaultCertExpLabelSuffix: "h",
- // Values Come From Default Notification Times
- defaultCertExpireWarnDays: "14",
- defaultCertExpireDownDays: "7"
-};
+// If host is omitted, the server will accept connections on the unspecified IPv6 address (::) when IPv6 is available and the unspecified IPv4 address (0.0.0.0) otherwise.
+// Dual-stack support for (::)
+// Also read HOST if not FreeBSD, as HOST is a system environment variable in FreeBSD
+let hostEnv = isFreeBSD ? null : process.env.HOST;
+const hostname = args.host || process.env.UPTIME_KUMA_HOST || hostEnv;
+
+const port = [ args.port, process.env.UPTIME_KUMA_PORT, process.env.PORT, 3001 ]
+ .map(portValue => parseInt(portValue))
+ .find(portValue => !isNaN(portValue));
+
+const sslKey = args["ssl-key"] || process.env.UPTIME_KUMA_SSL_KEY || process.env.SSL_KEY || undefined;
+const sslCert = args["ssl-cert"] || process.env.UPTIME_KUMA_SSL_CERT || process.env.SSL_CERT || undefined;
+const sslKeyPassphrase = args["ssl-key-passphrase"] || process.env.UPTIME_KUMA_SSL_KEY_PASSPHRASE || process.env.SSL_KEY_PASSPHRASE || undefined;
+
+const isSSL = sslKey && sslCert;
+
+function getLocalWebSocketURL() {
+ const protocol = isSSL ? "wss" : "ws";
+ const host = hostname || "localhost";
+ return `${protocol}://${host}:${port}`;
+}
+
+const localWebSocketURL = getLocalWebSocketURL();
+
+const demoMode = args["demo"] || false;
module.exports = {
args,
+ hostname,
+ port,
+ sslKey,
+ sslCert,
+ sslKeyPassphrase,
+ isSSL,
+ localWebSocketURL,
demoMode,
- badgeConstants,
};
diff --git a/server/routers/api-router.js b/server/routers/api-router.js
index 7b14a6dac..b63b5123b 100644
--- a/server/routers/api-router.js
+++ b/server/routers/api-router.js
@@ -11,11 +11,10 @@ const { R } = require("redbean-node");
const apicache = require("../modules/apicache");
const Monitor = require("../model/monitor");
const dayjs = require("dayjs");
-const { UP, MAINTENANCE, DOWN, PENDING, flipStatus, log } = require("../../src/util");
+const { UP, MAINTENANCE, DOWN, PENDING, flipStatus, log, badgeConstants } = require("../../src/util");
const StatusPage = require("../model/status_page");
const { UptimeKumaServer } = require("../uptime-kuma-server");
const { makeBadge } = require("badge-maker");
-const { badgeConstants } = require("../config");
const { Prometheus } = require("../prometheus");
const Database = require("../database");
const { UptimeCalculator } = require("../uptime-calculator");
diff --git a/server/routers/status-page-router.js b/server/routers/status-page-router.js
index 86d2af9a9..29acaf8ea 100644
--- a/server/routers/status-page-router.js
+++ b/server/routers/status-page-router.js
@@ -4,7 +4,7 @@ const { UptimeKumaServer } = require("../uptime-kuma-server");
const StatusPage = require("../model/status_page");
const { allowDevAllOrigin, sendHttpError } = require("../util-server");
const { R } = require("redbean-node");
-const { badgeConstants } = require("../config");
+const { badgeConstants } = require("../../src/util");
const { makeBadge } = require("badge-maker");
const { UptimeCalculator } = require("../uptime-calculator");
diff --git a/server/server.js b/server/server.js
index 098b731f9..1994303fa 100644
--- a/server/server.js
+++ b/server/server.js
@@ -46,8 +46,13 @@ if (! process.env.NODE_ENV) {
process.env.NODE_ENV = "production";
}
+if (!process.env.UPTIME_KUMA_WS_ORIGIN_CHECK) {
+ process.env.UPTIME_KUMA_WS_ORIGIN_CHECK = "cors-like";
+}
+
log.info("server", "Env: " + process.env.NODE_ENV);
log.debug("server", "Inside Container: " + (process.env.UPTIME_KUMA_IS_CONTAINER === "1"));
+log.info("server", "WebSocket Origin Check: " + process.env.UPTIME_KUMA_WS_ORIGIN_CHECK);
const checkVersion = require("./check-version");
log.info("server", "Uptime Kuma Version: " + checkVersion.version);
@@ -72,8 +77,7 @@ const notp = require("notp");
const base32 = require("thirty-two");
const { UptimeKumaServer } = require("./uptime-kuma-server");
-
-const server = UptimeKumaServer.getInstance(args);
+const server = UptimeKumaServer.getInstance();
const io = module.exports.io = server.io;
const app = server.app;
@@ -82,7 +86,7 @@ const Monitor = require("./model/monitor");
const User = require("./model/user");
log.debug("server", "Importing Settings");
-const { getSettings, setSettings, setting, initJWTSecret, checkLogin, FBSD, doubleCheckPassword, startE2eTests, shake256, SHAKE256_LENGTH, allowDevAllOrigin,
+const { getSettings, setSettings, setting, initJWTSecret, checkLogin, startUnitTest, doubleCheckPassword, startE2eTests, shake256, SHAKE256_LENGTH, allowDevAllOrigin,
} = require("./util-server");
log.debug("server", "Importing Notification");
@@ -100,19 +104,13 @@ const { apiAuth } = require("./auth");
const { login } = require("./auth");
const passwordHash = require("./password-hash");
-// If host is omitted, the server will accept connections on the unspecified IPv6 address (::) when IPv6 is available and the unspecified IPv4 address (0.0.0.0) otherwise.
-// Dual-stack support for (::)
-// Also read HOST if not FreeBSD, as HOST is a system environment variable in FreeBSD
-let hostEnv = FBSD ? null : process.env.HOST;
-let hostname = args.host || process.env.UPTIME_KUMA_HOST || hostEnv;
+const hostname = config.hostname;
if (hostname) {
log.info("server", "Custom hostname: " + hostname);
}
-const port = [ args.port, process.env.UPTIME_KUMA_PORT, process.env.PORT, 3001 ]
- .map(portValue => parseInt(portValue))
- .find(portValue => !isNaN(portValue));
+const port = config.port;
const disableFrameSameOrigin = !!process.env.UPTIME_KUMA_DISABLE_FRAME_SAMEORIGIN || args["disable-frame-sameorigin"] || false;
const cloudflaredToken = args["cloudflared-token"] || process.env.UPTIME_KUMA_CLOUDFLARED_TOKEN || undefined;
@@ -1265,6 +1263,8 @@ let needSetup = false;
let user = await doubleCheckPassword(socket, password.currentPassword);
await user.resetPassword(password.newPassword);
+ server.disconnectAllSocketClient(user.id, socket.id);
+
callback({
ok: true,
msg: "successAuthChangePassword",
diff --git a/server/socket-handlers/general-socket-handler.js b/server/socket-handlers/general-socket-handler.js
index 1269bc25e..68e1f814c 100644
--- a/server/socket-handlers/general-socket-handler.js
+++ b/server/socket-handlers/general-socket-handler.js
@@ -109,4 +109,14 @@ module.exports.generalSocketHandler = (socket, server) => {
msg: "Not found",
});
});
+
+ // Disconnect all other socket clients of the user
+ socket.on("disconnectOtherSocketClients", async () => {
+ try {
+ checkLogin(socket);
+ server.disconnectAllSocketClients(socket.userID, socket.id);
+ } catch (e) {
+ log.warn("disconnectAllSocketClients", e.message);
+ }
+ });
};
diff --git a/server/uptime-kuma-server.js b/server/uptime-kuma-server.js
index f3a1c7b72..d5ed5687c 100644
--- a/server/uptime-kuma-server.js
+++ b/server/uptime-kuma-server.js
@@ -4,7 +4,7 @@ const fs = require("fs");
const http = require("http");
const { Server } = require("socket.io");
const { R } = require("redbean-node");
-const { log } = require("../src/util");
+const { log, isDev } = require("../src/util");
const Database = require("./database");
const util = require("util");
const { Settings } = require("./settings");
@@ -12,6 +12,7 @@ const dayjs = require("dayjs");
const childProcessAsync = require("promisify-child-process");
const path = require("path");
const axios = require("axios");
+const { isSSL, sslKey, sslCert, sslKeyPassphrase } = require("./config");
// DO NOT IMPORT HERE IF THE MODULES USED `UptimeKumaServer.getInstance()`, put at the bottom of this file instead.
/**
@@ -67,9 +68,9 @@ class UptimeKumaServer {
* @param {object} args Arguments to pass to instance constructor
* @returns {UptimeKumaServer} Server instance
*/
- static getInstance(args) {
+ static getInstance() {
if (UptimeKumaServer.instance == null) {
- UptimeKumaServer.instance = new UptimeKumaServer(args);
+ UptimeKumaServer.instance = new UptimeKumaServer();
}
return UptimeKumaServer.instance;
}
@@ -77,7 +78,7 @@ class UptimeKumaServer {
/**
* @param {object} args Arguments to initialise server with
*/
- constructor(args) {
+ constructor() {
// SSL
const sslKey = args["ssl-key"] || process.env.UPTIME_KUMA_SSL_KEY || process.env.SSL_KEY || undefined;
const sslCert = args["ssl-cert"] || process.env.UPTIME_KUMA_SSL_CERT || process.env.SSL_CERT || undefined;
@@ -91,7 +92,7 @@ class UptimeKumaServer {
log.info("server", "Creating express and socket.io instance");
this.app = express();
- if (sslKey && sslCert) {
+ if (isSSL) {
log.info("server", "Server Type: HTTPS");
this.httpServer = https.createServer({
key: fs.readFileSync(sslKey),
@@ -119,7 +120,41 @@ class UptimeKumaServer {
UptimeKumaServer.monitorTypeList["dns"] = new DnsMonitorType();
UptimeKumaServer.monitorTypeList["mqtt"] = new MqttMonitorType();
- this.io = new Server(this.httpServer);
+ this.io = new Server(this.httpServer, {
+ allowRequest: (req, callback) => {
+ let isOriginValid = true;
+ const bypass = isDev || process.env.UPTIME_KUMA_WS_ORIGIN_CHECK === "bypass";
+
+ if (!bypass) {
+ let host = req.headers.host;
+
+ // If this is set, it means the request is from the browser
+ let origin = req.headers.origin;
+
+ // If this is from the browser, check if the origin is allowed
+ if (origin) {
+ try {
+ let originURL = new URL(origin);
+
+ if (host !== originURL.host) {
+ isOriginValid = false;
+ log.error("auth", `Origin (${origin}) does not match host (${host}), IP: ${req.socket.remoteAddress}`);
+ }
+ } catch (e) {
+ // Invalid origin url, probably not from browser
+ isOriginValid = false;
+ log.error("auth", `Invalid origin url (${origin}), IP: ${req.socket.remoteAddress}`);
+ }
+ } else {
+ log.info("auth", `Origin is not set, IP: ${req.socket.remoteAddress}`);
+ }
+ } else {
+ log.debug("auth", "Origin check is bypassed");
+ }
+
+ callback(null, isOriginValid);
+ }
+ });
}
/**
@@ -424,6 +459,25 @@ class UptimeKumaServer {
getUserAgent() {
return "Uptime-Kuma/" + require("../package.json").version;
}
+
+ /**
+ * Force connected sockets of a user to refresh and disconnect.
+ * Used for resetting password.
+ * @param {string} userID
+ * @param {string?} currentSocketID
+ */
+ disconnectAllSocketClients(userID, currentSocketID = undefined) {
+ for (const socket of this.io.sockets.sockets.values()) {
+ if (socket.userID === userID && socket.id !== currentSocketID) {
+ try {
+ socket.emit("refresh");
+ socket.disconnect();
+ } catch (e) {
+
+ }
+ }
+ }
+ }
}
module.exports = {
diff --git a/server/util-server.js b/server/util-server.js
index 3246925e9..5f8c063bf 100644
--- a/server/util-server.js
+++ b/server/util-server.js
@@ -1,14 +1,13 @@
const tcpp = require("tcp-ping");
const ping = require("@louislam/ping");
const { R } = require("redbean-node");
-const { log, genSecret } = require("../src/util");
+const { log, genSecret, badgeConstants } = require("../src/util");
const passwordHash = require("./password-hash");
const { Resolver } = require("dns");
const childProcess = require("child_process");
const iconv = require("iconv-lite");
const chardet = require("chardet");
const chroma = require("chroma-js");
-const { badgeConstants } = require("./config");
const mssql = require("mssql");
const { Client } = require("pg");
const postgresConParse = require("pg-connection-string").parse;
diff --git a/src/components/ActionInput.vue b/src/components/ActionInput.vue
index 5303e4271..a61e4f961 100644
--- a/src/components/ActionInput.vue
+++ b/src/components/ActionInput.vue
@@ -8,7 +8,7 @@
:placeholder="placeholder"
:disabled="!enabled"
>
-