mirror of
				https://github.com/louislam/uptime-kuma.git
				synced 2025-11-04 13:46:13 +08:00 
			
		
		
		
	Merge pull request #2489 from mathiash98/mathiash98/clone-monitor
Feature: Clone existing monitor
This commit is contained in:
		@@ -18,9 +18,15 @@
 | 
				
			|||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					* @typedef {import('./TagsManager.vue').Tag} Tag
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
    props: {
 | 
					    props: {
 | 
				
			||||||
        /** Object representing tag */
 | 
					        /** Object representing tag
 | 
				
			||||||
 | 
					         * @type {Tag}
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
        item: {
 | 
					        item: {
 | 
				
			||||||
            type: Object,
 | 
					            type: Object,
 | 
				
			||||||
            required: true,
 | 
					            required: true,
 | 
				
			||||||
@@ -32,7 +38,7 @@ export default {
 | 
				
			|||||||
        },
 | 
					        },
 | 
				
			||||||
        /**
 | 
					        /**
 | 
				
			||||||
         * Size of tag
 | 
					         * Size of tag
 | 
				
			||||||
         * @values normal, small
 | 
					         * @type {"normal" | "small"}
 | 
				
			||||||
         */
 | 
					         */
 | 
				
			||||||
        size: {
 | 
					        size: {
 | 
				
			||||||
            type: String,
 | 
					            type: String,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -134,13 +134,27 @@ import { colorOptions } from "../util-frontend";
 | 
				
			|||||||
import Tag from "../components/Tag.vue";
 | 
					import Tag from "../components/Tag.vue";
 | 
				
			||||||
const toast = useToast();
 | 
					const toast = useToast();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @typedef Tag
 | 
				
			||||||
 | 
					 * @type {object}
 | 
				
			||||||
 | 
					 * @property {number | undefined} id
 | 
				
			||||||
 | 
					 * @property {number | undefined} monitor_id
 | 
				
			||||||
 | 
					 * @property {number | undefined} tag_id
 | 
				
			||||||
 | 
					 * @property {string} value
 | 
				
			||||||
 | 
					 * @property {string} name
 | 
				
			||||||
 | 
					 * @property {string} color
 | 
				
			||||||
 | 
					 * @property {boolean | undefined} new
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
    components: {
 | 
					    components: {
 | 
				
			||||||
        Tag,
 | 
					        Tag,
 | 
				
			||||||
        VueMultiselect,
 | 
					        VueMultiselect,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    props: {
 | 
					    props: {
 | 
				
			||||||
        /** Array of tags to be pre-selected */
 | 
					        /** Array of tags to be pre-selected
 | 
				
			||||||
 | 
					         * @type {Tag[]}
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
        preSelectedTags: {
 | 
					        preSelectedTags: {
 | 
				
			||||||
            type: Array,
 | 
					            type: Array,
 | 
				
			||||||
            default: () => [],
 | 
					            default: () => [],
 | 
				
			||||||
@@ -148,10 +162,14 @@ export default {
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    data() {
 | 
					    data() {
 | 
				
			||||||
        return {
 | 
					        return {
 | 
				
			||||||
 | 
					            /** @type {Modal | null} */
 | 
				
			||||||
            modal: null,
 | 
					            modal: null,
 | 
				
			||||||
 | 
					            /** @type {Tag[]} */
 | 
				
			||||||
            existingTags: [],
 | 
					            existingTags: [],
 | 
				
			||||||
            processing: false,
 | 
					            processing: false,
 | 
				
			||||||
 | 
					            /** @type {Tag[]} */
 | 
				
			||||||
            newTags: [],
 | 
					            newTags: [],
 | 
				
			||||||
 | 
					            /** @type {Tag[]} */
 | 
				
			||||||
            deleteTags: [],
 | 
					            deleteTags: [],
 | 
				
			||||||
            newDraftTag: {
 | 
					            newDraftTag: {
 | 
				
			||||||
                name: null,
 | 
					                name: null,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,9 @@ import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Add Free Font Awesome Icons
 | 
					// Add Free Font Awesome Icons
 | 
				
			||||||
// https://fontawesome.com/v5.15/icons?d=gallery&p=2&s=solid&m=free
 | 
					// https://fontawesome.com/v5.15/icons?d=gallery&p=2&s=solid&m=free
 | 
				
			||||||
 | 
					// In order to add an icon, you have to:
 | 
				
			||||||
 | 
					// 1) add the icon name in the import statement below;
 | 
				
			||||||
 | 
					// 2) add the icon name to the library.add() statement below.
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    faArrowAltCircleUp,
 | 
					    faArrowAltCircleUp,
 | 
				
			||||||
    faCog,
 | 
					    faCog,
 | 
				
			||||||
@@ -45,6 +48,7 @@ import {
 | 
				
			|||||||
    faHeartbeat,
 | 
					    faHeartbeat,
 | 
				
			||||||
    faFilter,
 | 
					    faFilter,
 | 
				
			||||||
    faInfoCircle,
 | 
					    faInfoCircle,
 | 
				
			||||||
 | 
					    faClone,
 | 
				
			||||||
} from "@fortawesome/free-solid-svg-icons";
 | 
					} from "@fortawesome/free-solid-svg-icons";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
library.add(
 | 
					library.add(
 | 
				
			||||||
@@ -90,6 +94,7 @@ library.add(
 | 
				
			|||||||
    faHeartbeat,
 | 
					    faHeartbeat,
 | 
				
			||||||
    faFilter,
 | 
					    faFilter,
 | 
				
			||||||
    faInfoCircle,
 | 
					    faInfoCircle,
 | 
				
			||||||
 | 
					    faClone,
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export { FontAwesomeIcon };
 | 
					export { FontAwesomeIcon };
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -439,6 +439,9 @@
 | 
				
			|||||||
    "uninstalling": "Uninstalling",
 | 
					    "uninstalling": "Uninstalling",
 | 
				
			||||||
    "confirmUninstallPlugin": "Are you sure want to uninstall this plugin?",
 | 
					    "confirmUninstallPlugin": "Are you sure want to uninstall this plugin?",
 | 
				
			||||||
    "notificationRegional": "Regional",
 | 
					    "notificationRegional": "Regional",
 | 
				
			||||||
 | 
					    "Clone Monitor": "Clone Monitor",
 | 
				
			||||||
 | 
					    "Clone": "Clone",
 | 
				
			||||||
 | 
					    "cloneOf": "Clone of {0}",
 | 
				
			||||||
    "smtp": "Email (SMTP)",
 | 
					    "smtp": "Email (SMTP)",
 | 
				
			||||||
    "secureOptionNone": "None / STARTTLS (25, 587)",
 | 
					    "secureOptionNone": "None / STARTTLS (25, 587)",
 | 
				
			||||||
    "secureOptionTLS": "TLS (465)",
 | 
					    "secureOptionTLS": "TLS (465)",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,6 +30,9 @@
 | 
				
			|||||||
                    <router-link :to=" '/edit/' + monitor.id " class="btn btn-normal">
 | 
					                    <router-link :to=" '/edit/' + monitor.id " class="btn btn-normal">
 | 
				
			||||||
                        <font-awesome-icon icon="edit" /> {{ $t("Edit") }}
 | 
					                        <font-awesome-icon icon="edit" /> {{ $t("Edit") }}
 | 
				
			||||||
                    </router-link>
 | 
					                    </router-link>
 | 
				
			||||||
 | 
					                    <router-link :to=" '/clone/' + monitor.id " class="btn btn-normal">
 | 
				
			||||||
 | 
					                        <font-awesome-icon icon="clone" /> {{ $t("Clone") }}
 | 
				
			||||||
 | 
					                    </router-link>
 | 
				
			||||||
                    <button class="btn btn-danger" @click="deleteDialog">
 | 
					                    <button class="btn btn-danger" @click="deleteDialog">
 | 
				
			||||||
                        <font-awesome-icon icon="trash" /> {{ $t("Delete") }}
 | 
					                        <font-awesome-icon icon="trash" /> {{ $t("Delete") }}
 | 
				
			||||||
                    </button>
 | 
					                    </button>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -682,13 +682,23 @@ export default {
 | 
				
			|||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        pageName() {
 | 
					        pageName() {
 | 
				
			||||||
            return this.$t((this.isAdd) ? "Add New Monitor" : "Edit");
 | 
					            let name = "Add New Monitor";
 | 
				
			||||||
 | 
					            if (this.isClone) {
 | 
				
			||||||
 | 
					                name = "Clone Monitor";
 | 
				
			||||||
 | 
					            } else if (this.isEdit) {
 | 
				
			||||||
 | 
					                name = "Edit";
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return this.$t(name);
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        isAdd() {
 | 
					        isAdd() {
 | 
				
			||||||
            return this.$route.path === "/add";
 | 
					            return this.$route.path === "/add";
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        isClone() {
 | 
				
			||||||
 | 
					            return this.$route.path.startsWith("/clone");
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        isEdit() {
 | 
					        isEdit() {
 | 
				
			||||||
            return this.$route.path.startsWith("/edit");
 | 
					            return this.$route.path.startsWith("/edit");
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
@@ -906,11 +916,23 @@ message HealthCheckResponse {
 | 
				
			|||||||
                        this.monitor.notificationIDList[this.$root.notificationList[i].id] = true;
 | 
					                        this.monitor.notificationIDList[this.$root.notificationList[i].id] = true;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            } else if (this.isEdit) {
 | 
					            } else if (this.isEdit || this.isClone) {
 | 
				
			||||||
                this.$root.getSocket().emit("getMonitor", this.$route.params.id, (res) => {
 | 
					                this.$root.getSocket().emit("getMonitor", this.$route.params.id, (res) => {
 | 
				
			||||||
                    if (res.ok) {
 | 
					                    if (res.ok) {
 | 
				
			||||||
                        this.monitor = res.monitor;
 | 
					                        this.monitor = res.monitor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if (this.isClone) {
 | 
				
			||||||
 | 
					                            /*
 | 
				
			||||||
 | 
					                         * Cloning a monitor will include properties that can not be posted to backend
 | 
				
			||||||
 | 
					                         * as they are not valid columns in the SQLite table.
 | 
				
			||||||
 | 
					                         */
 | 
				
			||||||
 | 
					                            this.monitor.id = undefined; // Remove id when cloning as we want a new id
 | 
				
			||||||
 | 
					                            this.monitor.includeSensitiveData = undefined;
 | 
				
			||||||
 | 
					                            this.monitor.maintenance = undefined;
 | 
				
			||||||
 | 
					                            this.monitor.name = this.$t("cloneOf", [ this.monitor.name ]);
 | 
				
			||||||
 | 
					                            this.monitor.tags = undefined; // FIXME: Cloning tags does not work yet
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        // Handling for monitors that are created before 1.7.0
 | 
					                        // Handling for monitors that are created before 1.7.0
 | 
				
			||||||
                        if (this.monitor.retryInterval === 0) {
 | 
					                        if (this.monitor.retryInterval === 0) {
 | 
				
			||||||
                            this.monitor.retryInterval = this.monitor.interval;
 | 
					                            this.monitor.retryInterval = this.monitor.interval;
 | 
				
			||||||
@@ -981,7 +1003,7 @@ message HealthCheckResponse {
 | 
				
			|||||||
                this.monitor.url = this.monitor.url.trim();
 | 
					                this.monitor.url = this.monitor.url.trim();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (this.isAdd) {
 | 
					            if (this.isAdd || this.isClone) {
 | 
				
			||||||
                this.$root.add(this.monitor, async (res) => {
 | 
					                this.$root.add(this.monitor, async (res) => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if (res.ok) {
 | 
					                    if (res.ok) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -67,6 +67,10 @@ const routes = [
 | 
				
			|||||||
                                    },
 | 
					                                    },
 | 
				
			||||||
                                ],
 | 
					                                ],
 | 
				
			||||||
                            },
 | 
					                            },
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                path: "/clone/:id",
 | 
				
			||||||
 | 
					                                component: EditMonitor,
 | 
				
			||||||
 | 
					                            },
 | 
				
			||||||
                            {
 | 
					                            {
 | 
				
			||||||
                                path: "/add",
 | 
					                                path: "/add",
 | 
				
			||||||
                                component: EditMonitor,
 | 
					                                component: EditMonitor,
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user