mirror of
				https://github.com/louislam/uptime-kuma.git
				synced 2025-11-04 21:56:12 +08:00 
			
		
		
		
	Add reverse proxy setting page for controlling cloudflared
This commit is contained in:
		@@ -83,7 +83,7 @@
 | 
			
		||||
        "jsonwebtoken": "~8.5.1",
 | 
			
		||||
        "jwt-decode": "^3.1.2",
 | 
			
		||||
        "limiter": "^2.1.0",
 | 
			
		||||
        "node-cloudflared-tunnel": "~1.0.0",
 | 
			
		||||
        "node-cloudflared-tunnel": "~1.0.6",
 | 
			
		||||
        "nodemailer": "~6.6.5",
 | 
			
		||||
        "notp": "~2.0.3",
 | 
			
		||||
        "password-hash": "~1.2.2",
 | 
			
		||||
 
 | 
			
		||||
@@ -1,19 +1,72 @@
 | 
			
		||||
const { checkLogin } = require("../util-server");
 | 
			
		||||
const { checkLogin, setSetting, setting } = require("../util-server");
 | 
			
		||||
const { CloudflaredTunnel } = require("node-cloudflared-tunnel");
 | 
			
		||||
const { io } = require("../server");
 | 
			
		||||
 | 
			
		||||
const prefix = "cloudflared_";
 | 
			
		||||
const cloudflared = new CloudflaredTunnel();
 | 
			
		||||
 | 
			
		||||
let isRunning;
 | 
			
		||||
 | 
			
		||||
cloudflared.change = (running, message) => {
 | 
			
		||||
    io.to("cloudflared").emit(prefix + "running", running);
 | 
			
		||||
    io.to("cloudflared").emit(prefix + "message", message);
 | 
			
		||||
    isRunning = running;
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
cloudflared.error = (errorMessage) => {
 | 
			
		||||
    io.to("cloudflared").emit(prefix + "errorMessage", errorMessage);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module.exports.cloudflaredSocketHandler = (socket) => {
 | 
			
		||||
 | 
			
		||||
    socket.on(prefix + "start", async (callback) => {
 | 
			
		||||
    socket.on(prefix + "join", async () => {
 | 
			
		||||
        try {
 | 
			
		||||
            checkLogin(socket);
 | 
			
		||||
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            callback({
 | 
			
		||||
                ok: false,
 | 
			
		||||
                msg: error.message,
 | 
			
		||||
            socket.join("cloudflared");
 | 
			
		||||
            io.to(socket.userID).emit(prefix + "installed", cloudflared.checkInstalled());
 | 
			
		||||
            io.to(socket.userID).emit(prefix + "running", isRunning);
 | 
			
		||||
            io.to(socket.userID).emit(prefix + "token", await setting("cloudflaredTunnelToken"));
 | 
			
		||||
        } catch (error) { }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    socket.on(prefix + "leave", async () => {
 | 
			
		||||
        try {
 | 
			
		||||
            checkLogin(socket);
 | 
			
		||||
            socket.leave("cloudflared");
 | 
			
		||||
        } catch (error) { }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    socket.on(prefix + "start", async (token) => {
 | 
			
		||||
        try {
 | 
			
		||||
            checkLogin(socket);
 | 
			
		||||
            if (token && typeof token === "string") {
 | 
			
		||||
                token = token.trim();
 | 
			
		||||
 | 
			
		||||
                // try to strip out "cloudflared.exe service install"
 | 
			
		||||
                let array = token.split(" ");
 | 
			
		||||
                if (array.length > 1) {
 | 
			
		||||
                    for (let i = 0; i < array.length - 1; i++) {
 | 
			
		||||
                        if (array[i] === "install") {
 | 
			
		||||
                            token = array[i + 1];
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                await setSetting("cloudflaredTunnelToken", token);
 | 
			
		||||
                cloudflared.token = token;
 | 
			
		||||
            } else {
 | 
			
		||||
                cloudflared.token = null;
 | 
			
		||||
            }
 | 
			
		||||
            cloudflared.start();
 | 
			
		||||
        } catch (error) { }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    socket.on(prefix + "stop", async () => {
 | 
			
		||||
        try {
 | 
			
		||||
            checkLogin(socket);
 | 
			
		||||
            cloudflared.stop();
 | 
			
		||||
        } catch (error) { }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										111
									
								
								src/components/settings/ReverseProxy.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								src/components/settings/ReverseProxy.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,111 @@
 | 
			
		||||
<template>
 | 
			
		||||
    <div>
 | 
			
		||||
        <h4 class="mt-4">Cloudflare Tunnel</h4>
 | 
			
		||||
 | 
			
		||||
        <div class="my-3">
 | 
			
		||||
            <div>
 | 
			
		||||
                cloudflared:
 | 
			
		||||
                <span v-if="installed === true" class="text-primary">{{ $t("Installed") }}</span>
 | 
			
		||||
                <span v-else-if="installed === false" class="text-danger">{{ $t("Not installed") }}</span>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <div>
 | 
			
		||||
                {{ $t("Status") }}:
 | 
			
		||||
                <span v-if="running" class="text-primary">{{ $t("Running") }}</span>
 | 
			
		||||
                <span v-else-if="!running" class="text-danger">{{ $t("Not running") }}</span>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <div v-if="false">
 | 
			
		||||
                {{ message }}
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <div class="mt-3">
 | 
			
		||||
                Message:
 | 
			
		||||
                <textarea v-model="errorMessage" class="form-control" readonly></textarea>
 | 
			
		||||
            </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>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <!-- If installed show token input -->
 | 
			
		||||
        <div v-if="installed" class="mb-2">
 | 
			
		||||
            <div class="mb-4">
 | 
			
		||||
                <label class="form-label" for="cloudflareTunnelToken">
 | 
			
		||||
                    Cloudflare Tunnel {{ $t("Token") }}
 | 
			
		||||
                </label>
 | 
			
		||||
                <HiddenInput
 | 
			
		||||
                    id="cloudflareTunnelToken"
 | 
			
		||||
                    v-model="cloudflareTunnelToken"
 | 
			
		||||
                    autocomplete="one-time-code"
 | 
			
		||||
                />
 | 
			
		||||
                <div class="form-text">
 | 
			
		||||
                    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">
 | 
			
		||||
                        https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy-with-Cloudflare-Tunnel
 | 
			
		||||
                    </a>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <!-- Save Button -->
 | 
			
		||||
            <div>
 | 
			
		||||
                <button v-if="!running" class="btn btn-primary" type="submit" @click="start">
 | 
			
		||||
                    {{ $t("Start") }} cloudflared
 | 
			
		||||
                </button>
 | 
			
		||||
 | 
			
		||||
                <button v-if="running" class="btn btn-danger" type="submit" @click="stop">
 | 
			
		||||
                    {{ $t("Stop") }} cloudflared
 | 
			
		||||
                </button>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <h4 class="mt-4">Other Software</h4>
 | 
			
		||||
        <div>
 | 
			
		||||
            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>.
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import HiddenInput from "../../components/HiddenInput.vue";
 | 
			
		||||
 | 
			
		||||
const prefix = "cloudflared_";
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    components: {
 | 
			
		||||
        HiddenInput,
 | 
			
		||||
    },
 | 
			
		||||
    data() {
 | 
			
		||||
        return this.$root.cloudflared;
 | 
			
		||||
    },
 | 
			
		||||
    computed: {
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
    watch: {
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
    mounted() {
 | 
			
		||||
        this.$root.getSocket().emit(prefix + "join");
 | 
			
		||||
    },
 | 
			
		||||
    unmounted() {
 | 
			
		||||
        this.$root.getSocket().emit(prefix + "leave");
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        start() {
 | 
			
		||||
            this.$root.getSocket().emit(prefix + "start", this.cloudflareTunnelToken);
 | 
			
		||||
        },
 | 
			
		||||
        stop() {
 | 
			
		||||
            this.$root.getSocket().emit(prefix + "stop");
 | 
			
		||||
        },
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.logo {
 | 
			
		||||
    margin: 4em 1em;
 | 
			
		||||
}
 | 
			
		||||
.update-link {
 | 
			
		||||
    font-size: 0.9em;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
@@ -42,6 +42,13 @@ export default {
 | 
			
		||||
            statusPageList: [],
 | 
			
		||||
            connectionErrorMsg: "Cannot connect to the socket server. Reconnecting...",
 | 
			
		||||
            showReverseProxyGuide: true,
 | 
			
		||||
            cloudflared: {
 | 
			
		||||
                cloudflareTunnelToken: "",
 | 
			
		||||
                installed: null,
 | 
			
		||||
                running: false,
 | 
			
		||||
                message: "",
 | 
			
		||||
                errorMessage: "",
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -231,6 +238,12 @@ export default {
 | 
			
		||||
                this.socket.firstConnect = false;
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            // cloudflared
 | 
			
		||||
            socket.on("cloudflared_installed", (res) => this.cloudflared.installed = res);
 | 
			
		||||
            socket.on("cloudflared_running", (res) => this.cloudflared.running = res);
 | 
			
		||||
            socket.on("cloudflared_message", (res) => this.cloudflared.message = res);
 | 
			
		||||
            socket.on("cloudflared_errorMessage", (res) => this.cloudflared.errorMessage = res);
 | 
			
		||||
            socket.on("cloudflared_token", (res) => this.cloudflared.cloudflareTunnelToken = res);
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        storage() {
 | 
			
		||||
 
 | 
			
		||||
@@ -75,6 +75,9 @@ export default {
 | 
			
		||||
                notifications: {
 | 
			
		||||
                    title: this.$t("Notifications"),
 | 
			
		||||
                },
 | 
			
		||||
                "reverse-proxy": {
 | 
			
		||||
                    title: this.$t("Reverse Proxy"),
 | 
			
		||||
                },
 | 
			
		||||
                "monitor-history": {
 | 
			
		||||
                    title: this.$t("Monitor History"),
 | 
			
		||||
                },
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,7 @@ import Entry from "./pages/Entry.vue";
 | 
			
		||||
import Appearance from "./components/settings/Appearance.vue";
 | 
			
		||||
import General from "./components/settings/General.vue";
 | 
			
		||||
import Notifications from "./components/settings/Notifications.vue";
 | 
			
		||||
import ReverseProxy from "./components/settings/ReverseProxy.vue";
 | 
			
		||||
import MonitorHistory from "./components/settings/MonitorHistory.vue";
 | 
			
		||||
import Security from "./components/settings/Security.vue";
 | 
			
		||||
import Backup from "./components/settings/Backup.vue";
 | 
			
		||||
@@ -83,6 +84,10 @@ const routes = [
 | 
			
		||||
                                path: "notifications",
 | 
			
		||||
                                component: Notifications,
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                path: "reverse-proxy",
 | 
			
		||||
                                component: ReverseProxy,
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                path: "monitor-history",
 | 
			
		||||
                                component: MonitorHistory,
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user