diff --git a/db/patch-add-docker-columns.sql b/db/patch-add-docker-columns.sql
index 564756678..4cea448d7 100644
--- a/db/patch-add-docker-columns.sql
+++ b/db/patch-add-docker-columns.sql
@@ -1,13 +1,18 @@
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
BEGIN TRANSACTION;
+CREATE TABLE docker_host (
+ id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
+ user_id INT NOT NULL,
+ docker_daemon VARCHAR(255),
+ docker_type VARCHAR(255),
+ name VARCHAR(255)
+);
+
ALTER TABLE monitor
- ADD docker_daemon VARCHAR(255);
+ ADD docker_host INTEGER REFERENCES docker_host(id);
ALTER TABLE monitor
ADD docker_container VARCHAR(255);
-ALTER TABLE monitor
- ADD docker_type VARCHAR(255);
-
COMMIT;
diff --git a/server/client.js b/server/client.js
index f6f133d19..614038427 100644
--- a/server/client.js
+++ b/server/client.js
@@ -122,10 +122,30 @@ async function sendInfo(socket) {
});
}
+async function sendDockerHostList(socket) {
+ const timeLogger = new TimeLogger();
+
+ let result = [];
+ let list = await R.find("docker_host", " user_id = ? ", [
+ socket.userID,
+ ]);
+
+ for (let bean of list) {
+ result.push(bean.export());
+ }
+
+ io.to(socket.userID).emit("dockerHostList", result);
+
+ timeLogger.print("Send Docker Host List");
+
+ return list;
+}
+
module.exports = {
sendNotificationList,
sendImportantHeartbeatList,
sendHeartbeatList,
sendProxyList,
sendInfo,
+ sendDockerHostList
};
diff --git a/server/docker.js b/server/docker.js
new file mode 100644
index 000000000..a13236aa9
--- /dev/null
+++ b/server/docker.js
@@ -0,0 +1,67 @@
+const axios = require("axios");
+const { R } = require("redbean-node");
+const version = require("../package.json").version;
+const https = require("https");
+
+class DockerHost {
+ static async save(dockerHost, dockerHostID, userID) {
+ let bean;
+
+ if (dockerHostID) {
+ bean = await R.findOne("docker_host", " id = ? AND user_id = ? ", [ dockerHostID, userID ]);
+
+ if (!bean) {
+ throw new Error("docker host not found");
+ }
+
+ } else {
+ bean = R.dispense("docker_host");
+ }
+
+ bean.user_id = userID;
+ bean.docker_daemon = dockerHost.docker_daemon;
+ bean.docker_type = dockerHost.docker_type;
+ bean.name = dockerHost.name;
+
+ await R.store(bean);
+
+ return bean;
+ }
+
+ static async delete(dockerHostID, userID) {
+ let bean = await R.findOne("docker_host", " id = ? AND user_id = ? ", [ dockerHostID, userID ]);
+
+ if (!bean) {
+ throw new Error("docker host not found");
+ }
+
+ await R.trash(bean);
+ }
+
+ static async getAmountContainer(dockerHost) {
+ const options = {
+ url: "/containers/json?all=true",
+ headers: {
+ "Accept": "*/*",
+ "User-Agent": "Uptime-Kuma/" + version
+ },
+ httpsAgent: new https.Agent({
+ maxCachedSessions: 0, // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940)
+ rejectUnauthorized: false,
+ }),
+ };
+
+ if (dockerHost.docker_type === "socket") {
+ options.socketPath = dockerHost.docker_daemon;
+ } else if (dockerHost.docker_type === "tcp") {
+ options.baseURL = dockerHost.docker_daemon;
+ }
+
+ let res = await axios.request(options);
+ return res.data.length;
+ }
+}
+
+module.exports = {
+ DockerHost,
+}
\ No newline at end of file
diff --git a/server/model/docker_host.js b/server/model/docker_host.js
new file mode 100644
index 000000000..26f3035a5
--- /dev/null
+++ b/server/model/docker_host.js
@@ -0,0 +1,19 @@
+const { BeanModel } = require("redbean-node/dist/bean-model");
+
+class DockerHost extends BeanModel {
+ /**
+ * Returns an object that ready to parse to JSON
+ * @returns {Object}
+ */
+ toJSON() {
+ return {
+ id: this._id,
+ userId: this._user_id,
+ daemon: this._dockerDaemon,
+ type: this._dockerType,
+ name: this._name,
+ }
+ }
+}
+
+module.exports = DockerHost;
\ No newline at end of file
diff --git a/server/model/monitor.js b/server/model/monitor.js
index eff167c6c..373796e96 100644
--- a/server/model/monitor.js
+++ b/server/model/monitor.js
@@ -89,8 +89,7 @@ class Monitor extends BeanModel {
dns_last_result: this.dns_last_result,
pushToken: this.pushToken,
docker_container: this.docker_container,
- docker_daemon: this.docker_daemon,
- docker_type: this.docker_type,
+ docker_host: this.docker_host,
proxyId: this.proxy_id,
notificationIDList,
tags: tags,
@@ -471,6 +470,8 @@ class Monitor extends BeanModel {
} else if (this.type === "docker") {
log.debug(`[${this.name}] Prepare Options for Axios`);
+ const docker_host = await R.load("docker_host", this.docker_host);
+
const options = {
url: `/containers/${this.docker_container}/json`,
headers: {
@@ -483,10 +484,10 @@ class Monitor extends BeanModel {
}),
};
- if (this.docker_type === "socket") {
- options.socketPath = this.docker_daemon;
- } else if (this.docker_type === "tcp") {
- options.baseURL = this.docker_daemon;
+ if (docker_host._dockerType === "socket") {
+ options.socketPath = docker_host._dockerDaemon;
+ } else if (docker_host._dockerType === "tcp") {
+ options.baseURL = docker_host._dockerDaemon;
}
log.debug(`[${this.name}] Axios Request`);
diff --git a/server/server.js b/server/server.js
index 71a9a7b5d..1e5661482 100644
--- a/server/server.js
+++ b/server/server.js
@@ -118,13 +118,14 @@ if (config.demoMode) {
}
// Must be after io instantiation
-const { sendNotificationList, sendHeartbeatList, sendImportantHeartbeatList, sendInfo, sendProxyList } = require("./client");
+const { sendNotificationList, sendHeartbeatList, sendImportantHeartbeatList, sendInfo, sendProxyList, sendDockerHostList } = require("./client");
const { statusPageSocketHandler } = require("./socket-handlers/status-page-socket-handler");
const databaseSocketHandler = require("./socket-handlers/database-socket-handler");
const TwoFA = require("./2fa");
const StatusPage = require("./model/status_page");
const { cloudflaredSocketHandler, autoStart: cloudflaredAutoStart, stop: cloudflaredStop } = require("./socket-handlers/cloudflared-socket-handler");
const { proxySocketHandler } = require("./socket-handlers/proxy-socket-handler");
+const { dockerSocketHandler } = require("./socket-handlers/docker-socket-handler");
app.use(express.json());
@@ -665,8 +666,7 @@ let needSetup = false;
bean.dns_resolve_server = monitor.dns_resolve_server;
bean.pushToken = monitor.pushToken;
bean.docker_container = monitor.docker_container;
- bean.docker_daemon = monitor.docker_daemon;
- bean.docker_type = monitor.docker_type;
+ bean.docker_host = monitor.docker_host;
bean.proxyId = Number.isInteger(monitor.proxyId) ? monitor.proxyId : null;
bean.mqttUsername = monitor.mqttUsername;
bean.mqttPassword = monitor.mqttPassword;
@@ -1425,6 +1425,7 @@ let needSetup = false;
cloudflaredSocketHandler(socket);
databaseSocketHandler(socket);
proxySocketHandler(socket);
+ dockerSocketHandler(socket);
log.debug("server", "added all socket handlers");
@@ -1525,6 +1526,7 @@ async function afterLogin(socket, user) {
let monitorList = await server.sendMonitorList(socket);
sendNotificationList(socket);
sendProxyList(socket);
+ sendDockerHostList(socket);
await sleep(500);
diff --git a/server/socket-handlers/docker-socket-handler.js b/server/socket-handlers/docker-socket-handler.js
new file mode 100644
index 000000000..eddcd7b84
--- /dev/null
+++ b/server/socket-handlers/docker-socket-handler.js
@@ -0,0 +1,67 @@
+const { sendDockerHostList } = require("../client");
+const { checkLogin } = require("../util-server");
+const { DockerHost } = require("../docker");
+
+module.exports.dockerSocketHandler = (socket) => {
+ socket.on("addDockerHost", async (dockerHost, dockerHostID, callback) => {
+ try {
+ checkLogin(socket);
+
+ let dockerHostBean = await DockerHost.save(dockerHost, dockerHostID, socket.userID);
+ await sendDockerHostList(socket);
+
+ callback({
+ ok: true,
+ msg: "Saved",
+ id: dockerHostBean.id,
+ });
+
+ } catch (e) {
+ callback({
+ ok: false,
+ msg: e.message,
+ })
+ }
+ });
+
+ socket.on("deleteDockerHost", async (dockerHostID, callback) => {
+ try {
+ checkLogin(socket);
+
+ await DockerHost.delete(dockerHostID, socket.userID);
+ await sendDockerHostList(socket);
+
+ callback({
+ ok: true,
+ msg: "Deleted",
+ });
+
+ } catch (e) {
+ callback({
+ ok: false,
+ msg: e.message,
+ })
+ }
+ });
+
+ socket.on("testDockerHost", async (dockerHost, callback) => {
+ try {
+ checkLogin(socket);
+
+ let amount = await DockerHost.getAmountContainer(dockerHost);
+
+ callback({
+ ok: true,
+ msg: "Amount of containers: " + amount,
+ });
+
+ } catch (e) {
+ console.error(e);
+
+ callback({
+ ok: false,
+ msg: e.message,
+ })
+ }
+ })
+}
\ No newline at end of file
diff --git a/src/components/DockerHostDialog.vue b/src/components/DockerHostDialog.vue
new file mode 100644
index 000000000..e52c4ecf3
--- /dev/null
+++ b/src/components/DockerHostDialog.vue
@@ -0,0 +1,160 @@
+
+
+
+
+ {{ $t("Not available, please setup.") }} +
- - -