UNPKG

@gregoriusrippenstein/node-red-contrib-nodedev

Version:

Support the development of Node-RED nodes within Node-RED.

484 lines (394 loc) 19.4 kB
<script type="text/javascript"> (function() { var globalYourConfigNode = null; function doGenerateOTP(cfgnode, cb) { ensureYourConfigNodeExists(); $.ajax({ url: "NodeDevOtpGenerator", type: "POST", contentType: "application/json; charset=utf-8", data: JSON.stringify({ cfgnode: cfgnode }), success: function (resp) { $('#node-input-nodedev-sidebar-otp').val(resp.data.otp) cb(resp) }, error: function (jqXHR, textStatus, errorThrown) { RED.notify("Generating failed: " + textStatus, { type: "error", id: "NodeFactorySidebar", timeout: 2000 }); console.log(textStatus,errorThrown ) } }); } function doSubmission(pkgname,pkgversion) { ensureYourConfigNodeExists(); $.ajax({ url: "NodeFactorySidebarCfg", type: "POST", contentType: "application/json; charset=utf-8", data: JSON.stringify({ pkgname: $("#node-input-nodefactorysidebar-package-name").val().trim(), pkgversion: $("#node-input-nodefactorysidebar-package-version").val().trim(), pkgregistry: $("#node-input-nodefactorysidebar-package-registry").val().trim() }), success: function (resp) { RED.notify("Submission triggered", { type: "warning", id: "NodeFactorySidebar", timeout: 2000 }); }, error: function (jqXHR, textStatus, errorThrown) { RED.notify("Submission failed: " + textStatus, { type: "error", id: "NodeFactorySidebar", timeout: 2000 }); console.log(textStatus,errorThrown ) } }); } function ensureYourConfigNodeExists() { // This function makes sure there is 1 instance of your config node is available, and that the globalYourConfigNode variable refers to it. // Explained in the next step of this tutorial... --> https://discourse.nodered.org/t/tutorial-create-a-sidebar-plugin-and-persist-the-data-in-a-config-node/82020 // If we had found it previously, check if it has been deleted by the user behind our back if (globalYourConfigNode !== null) { var configNode = RED.nodes.node(globalYourConfigNode.id); if (configNode === null) { globalYourConfigNode = null; } } // If not found previously, let's go find it if (globalYourConfigNode === null) { var configNodes = []; RED.nodes.eachConfig(function(configNode) { if (configNode.type === 'NodeFactorySidebarCfg') { configNodes.push(configNode); } }); // Make sure we only have 1 config node while (configNodes.length > 1) { var configNode = configNodes.pop(); RED.nodes.remove(configNode.id); RED.nodes.dirty(true); } // When we found a config node, let's use that one if (configNodes.length === 1) { globalYourConfigNode = configNodes[0]; } } // When it doesn't exist yet, create it if required if (globalYourConfigNode === null) { // Remark: since this config node is dynamically created (and only used in this sidebar which isn't another node), the config // node is in fact "unused". But since we don't want it to appear "unused" in the "config nodes" panel, we need to set hasUsers // to false (see https://github.com/node-red/node-red/blob/master/CHANGELOG.md#0161-maintenance-release). // The hasUsers needs also to be specified in the RED.nodes.registerType statement! let typ = RED.nodes.getType("NodeFactorySidebarCfg"); globalYourConfigNode = { id: RED.nodes.id(), // on the server side, this is called RED.util.generateId() _def: typ, type: "NodeFactorySidebarCfg", hasUsers: false, users: [], name: "NodeFactorySidebar", label: function() { return this.name || "NodeFactorySidebar"}, /* values and data defined by this config node */ pkgname: typ.defaults.pkgname.value, pkgversion: typ.defaults.pkgversion.value, otptype: typ.defaults.otptype.value, algorithm: typ.defaults.algorithm.value, secret: typ.defaults.secret.value, secretType: typ.defaults.secretType.value, issuer: typ.defaults.issuer.value, otplabel: typ.defaults.otplabel.value, digits: typ.defaults.digits.value, counter: typ.defaults.counter.value, period: typ.defaults.period.value } // Add the new config node to the collection of Node-RED nodes RED.nodes.add(globalYourConfigNode); // Make sure the "Deploy" button becomes active RED.nodes.dirty(true); } } // Add your plugin as a new tabsheet in the right sidebar AFTER the flow editor is completely started var initialiseConfigNodeOnce = () => { RED.events.off('runtime-state', initialiseConfigNodeOnce); // The html content of the sidebar has been specified below as a data-template, from where it can be loaded: var content = $($('script[type="text/x-red"][data-template-name="NodeFactorySidebar"]').i18n().html()); // Add a "Your sidebar" tabsheet to the right sidebar panel, in which this sidebar panel can be displayed // --> more details: https://nodered.org/docs/api/ui/sidebar/ RED.sidebar.addTab({ id: "NodeFactorySidebar", label: "Node Dev", // short name for the tab name: "Node Dev", // long name for the menu content: content, closeable: true, // disableOnEdit: true, enableOnEdit: true, iconClass: "fa fa-industry" // your fontawesome icon }); var tabs = RED.tabs.create({ id: 'func-nodedev-pkgimporter-tabs', onchange: function(tab) { $('#func-nodedev-pkgimporter-tabs-content').children().hide(); $('#' + tab.id).show(); } }); tabs.addTab({ id: 'func-nodedev-pkgimporter-tab-pkgimport', iconClass: 'fa fa-industry', label: 'PkgImporter' }); tabs.addTab({ id: 'func-nodedev-pkgimporter-tab-otpgenerator', iconClass: 'fa fa-user-secret', label: 'OTP Generator' }); tabs.addTab({ id: 'func-nodedev-pkgimporter-tab-options', iconClass: 'fa fa-cog', label: 'Options' }); ensureYourConfigNodeExists(); /* ************ Package importer handlers */ var doSomething = (e) => { if (e) { e.preventDefault(); } doSubmission() } $('#node-input-nodefactorysidebar-generate-btn').on('click', doSomething ); $("#node-input-nodefactorysidebar-package-version").on("change", function() { ensureYourConfigNodeExists(); let data = $(this).val(); if (globalYourConfigNode.pkgversion != data) { globalYourConfigNode.pkgversion = data.trim(); RED.nodes.dirty(true); } }) $("#node-input-nodefactorysidebar-package-version").val(globalYourConfigNode.pkgversion); $("#node-input-nodefactorysidebar-package-name").on("change", function() { ensureYourConfigNodeExists(); let data = $(this).val(); if (globalYourConfigNode.pkgname != data) { globalYourConfigNode.pkgname = data.trim(); RED.nodes.dirty(true); } }) $("#node-input-nodefactorysidebar-package-name").val(globalYourConfigNode.pkgname); $("#node-input-nodefactorysidebar-package-registry").on("change", function() { ensureYourConfigNodeExists(); let data = $(this).val(); if (globalYourConfigNode.pkgregistry != data) { globalYourConfigNode.pkgregistry = data.trim(); RED.nodes.dirty(true); } }) $("#node-input-nodefactorysidebar-package-registry").val(globalYourConfigNode.pkgregistry); /* ************* OTP generator handlers */ $("#node-input-nodedev-sidebar-otp-secret").typedInput({ types:["env", "msg", "global", "cred"], typeField: "#node-input-nodedev-sidebar-otp-secretType" }); $("#node-input-nodedev-sidebar-otp-secret").typedInput( 'value', globalYourConfigNode.secret || globalYourConfigNode._def.defaults.secret.value ); $("#node-input-nodedev-sidebar-otp-secret").typedInput( 'type', globalYourConfigNode.secretType || globalYourConfigNode._def.defaults.secretType.value); $("#node-input-nodedev-sidebar-otp-secret").on('change', () => { ensureYourConfigNodeExists(); let val = $("#node-input-nodedev-sidebar-otp-secret").typedInput('value'); let typ = $("#node-input-nodedev-sidebar-otp-secret").typedInput('type'); if ( val != globalYourConfigNode.secret ) { globalYourConfigNode.secret = val; RED.nodes.dirty(true); } if ( typ != globalYourConfigNode.secretType ) { globalYourConfigNode.secretType = typ; RED.nodes.dirty(true); } }) $('#node-input-nodedev-sidebar-otp-otptype').val(globalYourConfigNode.otptype || globalYourConfigNode._def.defaults.otptype.value) $('#node-input-nodedev-sidebar-otp-algorithm').val(globalYourConfigNode.algorithm || globalYourConfigNode._def.defaults.algorithm.value) $('#node-input-nodedev-sidebar-otp-issuer').val(globalYourConfigNode.issuer || globalYourConfigNode._def.defaults.issuer.value) $('#node-input-nodedev-sidebar-otp-label').val(globalYourConfigNode.otplabel || globalYourConfigNode._def.defaults.otplabel.value) $('#node-input-nodedev-sidebar-otp-digits').val(globalYourConfigNode.digits || globalYourConfigNode._def.defaults.digits.value) $('#node-input-nodedev-sidebar-otp-period').val(globalYourConfigNode.period || globalYourConfigNode._def.defaults.period.value) $('#node-input-nodedev-sidebar-otp-counter').val(globalYourConfigNode.counter || globalYourConfigNode._def.defaults.counter.value) $('#node-input-nodedev-sidebar-otp-otptype').on('change', () => { ensureYourConfigNodeExists(); let val = $('#node-input-nodedev-sidebar-otp-otptype').val() if ( val != globalYourConfigNode.otptype) { globalYourConfigNode.otptype = val RED.nodes.dirty(true); } }) $('#node-input-nodedev-sidebar-otp-algorithm').on('change', () => { ensureYourConfigNodeExists(); let val = $('#node-input-nodedev-sidebar-otp-algorithm').val() if ( val != globalYourConfigNode.algorithm) { globalYourConfigNode.algorithm = val RED.nodes.dirty(true); } }) $('#node-input-nodedev-sidebar-otp-issuer').on('change', () => { ensureYourConfigNodeExists(); let val = $('#node-input-nodedev-sidebar-otp-issuer').val() if ( val != globalYourConfigNode.issuer) { globalYourConfigNode.issuer = val RED.nodes.dirty(true); } }) $('#node-input-nodedev-sidebar-otp-label').on('change', () => { ensureYourConfigNodeExists(); let val = $('#node-input-nodedev-sidebar-otp-label').val() if ( val != globalYourConfigNode.otplabel) { globalYourConfigNode.otplabel = val RED.nodes.dirty(true); } }) $('#node-input-nodedev-sidebar-otp-digits').on('change', () => { ensureYourConfigNodeExists(); let val = $('#node-input-nodedev-sidebar-otp-digits').val() if ( val != globalYourConfigNode.digits) { globalYourConfigNode.digits = val RED.nodes.dirty(true); } }) $('#node-input-nodedev-sidebar-otp-period').on('change', () => { ensureYourConfigNodeExists(); let val = $('#node-input-nodedev-sidebar-otp-period').val() if ( val != globalYourConfigNode.period) { globalYourConfigNode.period = val RED.nodes.dirty(true); } }) $('#node-input-nodedev-sidebar-otp-counter').on('change', () => { ensureYourConfigNodeExists(); let val = $('#node-input-nodedev-sidebar-otp-counter').val() if ( val != globalYourConfigNode.counter) { globalYourConfigNode.counter = val RED.nodes.dirty(true); } }) $('#nodedev-sidebar-otp-generate-btn').on('click', e => { if ( e) { e.preventDefault()} ensureYourConfigNodeExists(); doGenerateOTP(globalYourConfigNode, (resp) => { $('#node-input-nodedev-sidebar-otp').val(resp.data.otp) }) }) }; RED.events.on('runtime-state', initialiseConfigNodeOnce); })(); </script> <!-- The html for the right sidebar plugin screen --> <script type="text/x-red" data-template-name="NodeFactorySidebar"> <div class="form-row func-nodedev-pkgimporter-tabs-row"> <ul style="min-width: 600px; margin-bottom: 20px;" id="func-nodedev-pkgimporter-tabs"></ul> </div> <div id="func-nodedev-pkgimporter-tabs-content" style="min-height: calc(100% - 95px);"> <div id="func-nodedev-pkgimporter-tab-pkgimport" style="display:none"> <div class="form-row" style="margin-left: 10px; margin-top: 30px;"> Import <a href="https://npmjs.com" target=_blank>npmJs.com</a> packages as a flow. packages are imported as a collection of PkgFile nodes. This can help to learn from other peoples experiences and code. </div> <div class="form-row" style="margin-left: 10px;"> Importing GitHub repos is also supported. For this, the package name should be either <code>https://github.com/&lt;user&gt;/&lt;repo&gt;</code> or <code>git@github.com:&lt;user&gt;/&lt;repo&gt;</code>, the version is assumed to be branch or tag name and the Registry option is ignored. </div> <div class="form-row" style="margin-left: 10px; margin-top: 30px;"> Enter package details here: </div> <div class="form-row" style="margin-left: 10px;"> <label for="node-input-nodefactorysidebar-package-name"> <i class="fa fa-tag"></i> Name </label> <input type="text" id="node-input-nodefactorysidebar-package-name" placeholder="node-red-dashboard"> </div> <div class="form-row" style="margin-left: 10px;"> <label for="node-input-nodefactorysidebar-package-version"> <i class="fa fa-tag"></i> Version </label> <input type="text" id="node-input-nodefactorysidebar-package-version" placeholder="3.5.0"> </div> <div class="form-row" style="margin-left: 10px;"> <label for="node-input-nodefactorysidebar-package-registry"> <i class="fa fa-tag"></i> Registry </label> <input type="text" id="node-input-nodefactorysidebar-package-registry" placeholder="https://registry.npmjs.org"> </div> <div class="form-row"> <div style="position: relative; height: 100%; margin: 15px;"> <button id="node-input-nodefactorysidebar-generate-btn" class="red-ui-button" style="width: 100%; margin-top: 30px">Import Package</button> </div> </div> </div> <div id="func-nodedev-pkgimporter-tab-otpgenerator" style="display:none"> <div class="form-row" style="margin-left: 10px;"> <label for="node-input-nodedev-sidebar-otp-otptype"><i class="fa fa-tag"></i> Type</label> <select id="node-input-nodedev-sidebar-otp-otptype"> <option value="totp">Time-Based (TOTP)</option> <option value="hotp">HMAC-based (HOTP)</option> </select> </div> <div class="form-row" style="margin-left: 10px;"> <label for="node-input-nodedev-sidebar-otp-secret"> <i class="fa fa-key"></i> Secret </label> <input type="text" id="node-input-nodedev-sidebar-otp-secret"> <input type="hidden" id="node-input-nodedev-sidebar-otp-secretType"> </div> <div class="form-row" style="margin-left: 10px;"> <label for="node-input-nodedev-sidebar-otp-algorithm"><i class="fa fa-tag"></i> Algorithm</label> <select id="node-input-nodedev-sidebar-otp-algorithm"> <option value="SHA1">SHA1</option> <option value="SHA256">SHA256</option> <option value="SHA512">SHA512</option> </select> </div> <div class="form-row" style="margin-left: 10px;"> <label for="node-input-nodedev-sidebar-otp-issuer"><i class="fa fa-tag"></i> Issuer</label> <input type="text" id="node-input-nodedev-sidebar-otp-issuer" placeholder="Issuer"> </div> <div class="form-row" style="margin-left: 10px;"> <label for="node-input-nodedev-sidebar-otp-label"><i class="fa fa-tag"></i> Label</label> <input type="text" id="node-input-nodedev-sidebar-otp-label" placeholder="Label"> </div> <div class="form-row" style="margin-left: 10px;"> <label for="node-input-nodedev-sidebar-otp-digits"><i class="fa fa-tag"></i> Digits</label> <input type="text" id="node-input-nodedev-sidebar-otp-digits" placeholder="Digits"> </div> <div class="form-row" style="margin-left: 10px;"> <label for="node-input-nodedev-sidebar-otp-period"><i class="fa fa-tag"></i> Period</label> <input type="text" id="node-input-nodedev-sidebar-otp-period" placeholder="Period"> </div> <div class="form-row" style="margin-left: 10px;"> <label for="node-input-nodedev-sidebar-otp-counter"><i class="fa fa-tag"></i> Counter</label> <input type="text" id="node-input-nodedev-sidebar-otp-counter" placeholder="Counter"> </div> <div class="form-row" style="margin-left: 10px;"> <button id="nodedev-sidebar-otp-generate-btn" class="button">Generate</button> <input type="text" id="node-input-nodedev-sidebar-otp" placeholder="OTP..."> </div> </div> <div id="func-nodedev-pkgimporter-tab-options" style="display:none"> <div class="form-row" style="margin-left: 10px;"> Drag & Drop </div> <div class="form-row" style="margin-left: 10px;"> <input style="width: 10%;" type="checkbox" id="nodedev-drag-drop-generate-pkgfile-nodes"/> <label for="nodedev-drag-drop-generate-pkgfile-nodes" style="width: 40%;"><i class="fa fa-code" ></i> Create PkgFile nodes on drop?</label> </div> </div> </div> </script>