├── .browserslistrc ├── .gitignore ├── LICENSE ├── README.md ├── eslint.config.js ├── package.json ├── src ├── background.js ├── images │ ├── logo.svg │ ├── sev_-1.svg │ ├── sev_0.svg │ ├── sev_1.svg │ ├── sev_2.svg │ ├── sev_3.svg │ ├── sev_4.svg │ ├── sev_5.svg │ └── unconfigured.svg ├── lib │ ├── crypto.js │ ├── sjcl.js │ └── zabbix-promise.js ├── manifest.json ├── options │ ├── options.html │ ├── options.js │ └── options.vue ├── popup │ ├── popup.html │ ├── popup.js │ └── popup.vue └── public │ ├── _locales │ ├── en │ │ └── messages.json │ ├── nl │ │ └── messages.json │ ├── pl │ │ └── messages.json │ ├── pt_BR │ │ └── messages.json │ ├── pt_PT │ │ └── messages.json │ └── ru │ │ └── messages.json │ └── sounds │ ├── audio.html │ └── drip.mp3 └── vite.config.js /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | 25 | # Vue Browser Extension Output 26 | *.pem 27 | *.pub 28 | *.zip 29 | /artifacts 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Christian McHugh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Zabbix Vue 2 | A browser extension to display problems from a Zabbix monitoring server. The browser icon's color is updated with the severity and number of detected problems. 3 | 4 | Firefox: [https://addons.mozilla.org/en-US/firefox/addon/zabbix-vue/](https://addons.mozilla.org/en-US/firefox/addon/zabbix-vue/) 5 | 6 | Chrome: [https://chrome.google.com/webstore/detail/goinajfhamfchlmkddedkncmlgfcieac/](https://chrome.google.com/webstore/detail/goinajfhamfchlmkddedkncmlgfcieac/) 7 | 8 | 9 | 10 | ## Features 11 | Badge display with popup showing current issues along with maintenance and acknowledgement icons 12 | 13 | 14 | Clicking a problem displays a row of actions 15 | 16 | 17 | Filter input allows for quick filtering of results. Both system and description fields are searchable 18 | 19 | 20 | Popup table also reorders from clicking on headers 21 | 22 | 23 | Browser notifications display the system and newly detected problem 24 | 25 | 26 | 27 | Options screen displays many settings including the ability to only show problems for certain host groups 28 | 29 | 30 | 31 | ## Build 32 | `npm install` 33 | `TARGET=chrome npm run build` or `TARGET=firefox npm run build` 34 | 35 | Then create a zip file from the dist/ directory's contents in Firefox Developer Edition or load the dist/ directory in Chrome. 36 | 37 | ## Attribution 38 | Includes notification from freesound.org: 39 | ["Drip Echo"](https://freesound.org/people/SpiceProgram/sounds/399191/) by [SpiceProgram](https://spiceprogram.org/) 40 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import globals from "globals"; 2 | import pluginJs from "@eslint/js"; 3 | import pluginVue from "eslint-plugin-vue"; 4 | import vuetify from 'eslint-plugin-vuetify' 5 | 6 | /** @type {import('eslint').Linter.Config[]} */ 7 | export default [ 8 | {files: ["**/*.{js,mjs,cjs,vue}"]}, 9 | {languageOptions: { globals: {...globals.browser, ...globals.node} }}, 10 | pluginJs.configs.recommended, 11 | ...pluginVue.configs["flat/recommended"], 12 | ...vuetify.configs['flat/base'], 13 | { 14 | rules: { 15 | 'vue/multi-word-component-names': 'off', 16 | 'vue/no-deprecated-v-bind-sync': 'off', 17 | } 18 | } 19 | ]; 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zabbix-vue", 3 | "private": true, 4 | "version": "3.0.7", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build" 9 | }, 10 | "dependencies": { 11 | "vue": "^3.3" 12 | }, 13 | "devDependencies": { 14 | "@eslint/js": "^9.19.0", 15 | "@mdi/js": "^7.4.47", 16 | "@vitejs/plugin-vue": "^4.5.0", 17 | "eslint": "^9.19.0", 18 | "eslint-plugin-vue": "^9.32.0", 19 | "eslint-plugin-vuetify": "^2.5.1", 20 | "globals": "^15.14.0", 21 | "vite": "^5.0.0", 22 | "vite-plugin-eslint": "^1.8.1", 23 | "vite-plugin-render-svg": "^1.1.1", 24 | "vite-plugin-web-extension": "^4.4.3", 25 | "vue-plugin-webextension-i18n": "^0.1.3", 26 | "vuetify": "^3.7.9", 27 | "webextension-polyfill": "^0.10.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/background.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import { Zabbix } from './lib/zabbix-promise.js'; 4 | import browser from "webextension-polyfill"; 5 | import { manifest } from 'virtual:render-svg' 6 | import { encryptSettingKeys, decryptSettings } from './lib/crypto.js' 7 | 8 | const ZABBIX_SERVERS_KEY = "ZabbixServers"; 9 | 10 | browser.runtime.onMessage.addListener(handleMessage); 11 | browser.alarms.onAlarm.addListener(initalize); 12 | 13 | 14 | browser.runtime.onInstalled.addListener( async () => { 15 | console.log(`onInstalled()`); 16 | 17 | await migrateOldSettings(); 18 | await initalize(); 19 | }); 20 | browser.runtime.onStartup.addListener( async () => { 21 | console.log(`onStartup()`); 22 | 23 | await initalize(); 24 | }); 25 | self.addEventListener("activate", (event) => { 26 | console.log("activated for " + JSON.stringify(event)) 27 | 28 | setAlarmState(60).then(); 29 | }); 30 | 31 | async function getSettings() { 32 | const settings = await browser.storage.local.get(ZABBIX_SERVERS_KEY); 33 | return settings[ZABBIX_SERVERS_KEY] ? JSON.parse(settings[ZABBIX_SERVERS_KEY]) : null; 34 | } 35 | 36 | async function migrateOldSettings() { 37 | /* 38 | * Up to version 2 of extension encrypted all data. Only pass and key are sensitive data 39 | * Converts old all encrypted format to only encrypt those two fields 40 | */ 41 | var settings = await getSettings(); 42 | if (settings) { 43 | if (Object.keys(settings).includes('iv')) { 44 | console.log("Found previous encrypted settings. Migrating") 45 | settings = decryptSettings(JSON.stringify(settings)) 46 | settings = encryptSettingKeys(JSON.parse(settings)); 47 | await browser.storage.local.set({"ZabbixServers": JSON.stringify(settings)}); 48 | console.log("Migration complete") 49 | } else { 50 | //console.log("no IV keys " + JSON.stringify(settings)) 51 | } 52 | } else { 53 | //console.log("no ZabbixServer keys") 54 | } 55 | } 56 | 57 | 58 | async function setAlarmState(interval) { 59 | const alarmName = "default-alarm"; 60 | const alarm = await browser.alarms.get(alarmName); 61 | 62 | if (!alarm) { 63 | await browser.alarms.create(alarmName, { 64 | delayInMinutes: interval / 60, 65 | periodInMinutes: interval / 60, 66 | }); 67 | } 68 | } 69 | 70 | 71 | async function initalize() { 72 | /* 73 | * Set Zabbix poll alarm, listeners, and activate polling 74 | */ 75 | const settings = await getSettings(); 76 | if (settings) { 77 | // settings have been configured 78 | try { 79 | var interval = settings["global"]["interval"]; 80 | if (interval) { 81 | console.log("Updating alarm to " + interval + " seconds"); 82 | await setAlarmState(interval); 83 | } 84 | } catch (_) { // eslint-disable-line no-unused-vars 85 | await setAlarmState(60); 86 | console.log("No previous polling interval set. Using default."); 87 | } 88 | await getAllTriggers(); 89 | } 90 | } 91 | 92 | async function getServerTriggers( 93 | server, 94 | user, 95 | pass, 96 | apiToken, 97 | version, 98 | groups, 99 | hideAck, 100 | hideMaintenance, 101 | minPriority 102 | ) { 103 | /* 104 | * Return data from zabbix trigger.get call to a specifc server 105 | */ 106 | let popupTable = await browser.storage.session.get("popupTable"); 107 | popupTable = popupTable["popupTable"] 108 | if (popupTable && "error" in popupTable) { 109 | console.log("Error found in popupTable. Clearning and refreshing triggers"); 110 | delete popupTable["error"]; 111 | delete popupTable["errorMessage"]; 112 | delete popupTable["errorDetails"]; 113 | await browser.storage.session.set({"popupTable": popupTable}); 114 | } 115 | 116 | //console.log("getServerTriggers for: " + JSON.stringify(server)) 117 | let requestObject = { 118 | expandDescription: 1, 119 | skipDependent: 1, 120 | selectHosts: ["host", "name", "hostid", "maintenance_status"], 121 | selectLastEvent: ["eventid", "acknowledged"], 122 | monitored: 1, 123 | min_severity: minPriority, 124 | active: 1, 125 | filter: { 126 | // Value: 0 = OK | 1 = PROBLEM | 2 = UNKNOWN 127 | value: 1, 128 | status: 0, 129 | }, 130 | output: ["triggerid", "description", "priority", "lastchange"], 131 | sortfield: "priority", 132 | sortorder: "DESC", 133 | }; 134 | 135 | if (hideAck) { 136 | // Don't show acknowledged 137 | requestObject.withLastEventUnacknowledged = 1; 138 | } 139 | if (hideMaintenance) { 140 | requestObject.maintenance = false; 141 | } 142 | if (groups.length > 0) { 143 | requestObject.groupids = groups; 144 | } 145 | 146 | const zabbix = new Zabbix( 147 | server + "/api_jsonrpc.php", 148 | user, 149 | pass, 150 | apiToken, 151 | version 152 | ); 153 | let triggerResults = {}; 154 | try { 155 | await zabbix.login(); 156 | let result = await zabbix.call("trigger.get", requestObject); 157 | zabbix.logout(); 158 | 159 | if ("result" in result) { 160 | triggerResults = result["result"]; 161 | } else { 162 | let errorMessage = "Error communicating with: " + server.toString(); 163 | console.log(errorMessage); 164 | let details = result.error.message + " " + result.error.data; 165 | console.log(details); 166 | triggerResults = { 167 | "error": true, 168 | "errorMessage": errorMessage, 169 | "errorDetails": details, 170 | }; 171 | } 172 | } catch (err) { // eslint-disable-line no-unused-vars 173 | let errorMessage = "Error communicating with: " + server.toString(); 174 | console.log(errorMessage); 175 | console.log(err.message); 176 | 177 | triggerResults = { 178 | "error": true, 179 | "errorMessage": errorMessage, 180 | "errorDetails": err.message, 181 | }; 182 | } 183 | 184 | return triggerResults; 185 | } 186 | 187 | async function getAllTriggers() { 188 | /* 189 | * Loop over each server found in settings 190 | * Get trigger results 191 | * Get diff of new results from previous and send browser notifications 192 | * Update browser badge color and count 193 | * Call setActiveTriggersTable function to update popup dataset 194 | */ 195 | var triggerCount = 0; 196 | let settings = await getSettings(); 197 | if ( 198 | !settings || 199 | settings.length === 0 || 200 | !settings["servers"] || 201 | settings["servers"].length == 0 202 | ) { 203 | console.log("No servers defined for trigger processing"); 204 | return null; 205 | } 206 | 207 | let triggerResults = await browser.storage.local.get("triggerResults"); 208 | triggerResults = triggerResults["triggerResults"] 209 | if (!triggerResults) { 210 | triggerResults = {} 211 | } 212 | //console.log("Current triggerResults: " + JSON.stringify(triggerResults)); 213 | 214 | let serversChecked = []; 215 | for (var serverIndex in settings["servers"]) { 216 | var serverError = false; 217 | 218 | let server = settings["servers"][serverIndex].alias; 219 | let serverURL = settings["servers"][serverIndex].url; 220 | let user = settings["servers"][serverIndex].user; 221 | let pass = decryptSettings(settings["servers"][serverIndex].pass); 222 | let version = settings["servers"][serverIndex].version; 223 | let apiToken = decryptSettings(settings["servers"][serverIndex].apiToken); 224 | let groups = settings["servers"][serverIndex].hostGroups; 225 | let hideAck = settings["servers"][serverIndex].hide; 226 | let hideMaintenance = settings["servers"][serverIndex].maintenance; 227 | let minPriority = settings["servers"][serverIndex].minSeverity; 228 | serversChecked.push(server); 229 | //console.log("Found server: " + server); 230 | let newTriggerData = {}; 231 | newTriggerData = await getServerTriggers( 232 | serverURL, 233 | user, 234 | pass, 235 | apiToken, 236 | version, 237 | groups, 238 | hideAck, 239 | hideMaintenance, 240 | minPriority 241 | ); 242 | 243 | //console.log("New trigger data for server: " + server + " : " + JSON.stringify(newTriggerData)); 244 | if ("error" in newTriggerData) { 245 | // Error state already set. Break out of function 246 | serverError = true; 247 | } else { 248 | // Check if new triggers are different from existing 249 | // Find triggerid values that are in new results but previous 250 | let oldTriggers = triggerResults[server] || []; 251 | let triggerDiff = newTriggerData.filter(function (obj) { 252 | return !oldTriggers.some(function (obj2) { 253 | return obj.triggerid == obj2.triggerid; 254 | }); 255 | }); 256 | if (settings["global"]["notify"]) { 257 | // Notify popup for new triggers 258 | for (let trig of triggerDiff) { 259 | await sendNotify(trig, settings.global.displayName); 260 | } 261 | } 262 | // Play sounds 263 | if (triggerDiff && triggerDiff.length) { 264 | playSounds(settings); 265 | } 266 | } 267 | // Record new trigger list 268 | triggerResults[server] = newTriggerData; 269 | //console.log('triggerResults for server '+ JSON.stringify(server)+ ": " + JSON.stringify(triggerResults[server])); 270 | if (!serverError) { 271 | // When not erroring, update trigger count 272 | triggerCount += triggerResults[server].length; 273 | } 274 | } 275 | 276 | // all server checks now complete 277 | 278 | // Remove trigger.get data for old servers 279 | for (let trigServer in triggerResults) { 280 | if (!serversChecked.includes(trigServer)) { 281 | console.log("Removing old results for: " + trigServer); 282 | delete triggerResults[trigServer]; 283 | } 284 | } 285 | 286 | // Remove server errors from triggerResults and persist data 287 | const completeTriggerResults = structuredClone(triggerResults); 288 | for (let trigServer in triggerResults) { 289 | if (triggerResults[trigServer].error) { 290 | delete triggerResults[trigServer]; 291 | } 292 | } 293 | browser.storage.local.set({"triggerResults": triggerResults}); 294 | 295 | if (triggerCount > 0) { 296 | // Set bage for the number of active triggers 297 | browser.action.setBadgeBackgroundColor({ color: "#888888" }); 298 | browser.action.setBadgeText({ text: triggerCount.toString() }); 299 | } else { 300 | // Clear badge as there are no active triggers 301 | browser.action.setBadgeText({ text: "" }); 302 | } 303 | 304 | await setActiveTriggersTable(completeTriggerResults); 305 | } 306 | 307 | async function sendNotify(message, displayName) { 308 | /* 309 | * Create a browser notification popup 310 | */ 311 | if (__BROWSER__ === "firefox") { // eslint-disable-line no-undef 312 | await browser.notifications.create( 313 | "notification", 314 | { 315 | type: "basic", 316 | title: message.hosts[0][displayName], 317 | message: message.description, 318 | iconUrl: manifest["1"]["sev_" + message.priority], 319 | 320 | } 321 | ); 322 | } else { 323 | // MV3 chrome notification 324 | registration.showNotification( // eslint-disable-line no-undef 325 | message.hosts[0][displayName], 326 | { 327 | body: message.description, 328 | icon: manifest["1"]["sev_" + message.priority], 329 | } 330 | ) 331 | } 332 | } 333 | 334 | function playSounds(settings) { 335 | if (settings["global"]["sound"]) { 336 | if (__BROWSER__ === "firefox") { // eslint-disable-line no-undef 337 | // mv2 firefox & older chrome sound support 338 | var myAudio = new Audio( 339 | browser.runtime.getURL("sounds/drip.mp3") 340 | ); 341 | myAudio.play(); 342 | } else { 343 | // MV3 chrome sound support 344 | browser.offscreen.createDocument({ 345 | url: browser.runtime.getURL('./sounds/audio.html'), 346 | reasons: ['AUDIO_PLAYBACK'], 347 | justification: 'notification', 348 | }); 349 | } 350 | } 351 | } 352 | 353 | async function setBrowserIcon(severity) { 354 | /* 355 | * unconfigured 356 | * -1 no problems 357 | * 0 not classified 358 | * 1 information 359 | * 2 warning 360 | * 3 average 361 | * 4 high 362 | * 5 disaster 363 | */ 364 | //console.log('Setting icon for priority: ' + severity); 365 | await browser.action.setIcon({ path: manifest["1"][severity]}); 366 | } 367 | 368 | async function setActiveTriggersTable(triggerResults) { 369 | /* 370 | * Generate object for display in popup window 371 | */ 372 | 373 | //console.log('getActiveTriggersTable activated. Current triggerResults: ' + JSON.stringify(triggerResults)) 374 | const settings = await getSettings(); 375 | let hasError = false; 376 | 377 | if ( 378 | Object.keys(triggerResults).length === 0 && 379 | triggerResults.constructor === Object 380 | ) { 381 | console.log("No current triggers or servers"); 382 | return null; 383 | } 384 | 385 | let topSeverity = -1; 386 | const popupHeaders = [ 387 | { title: browser.i18n.getMessage("headerSystem"), 388 | sortable: true, 389 | value: "system" }, 390 | { 391 | title: browser.i18n.getMessage("headerDescription"), 392 | sortable: true, 393 | value: "description", 394 | }, 395 | { title: browser.i18n.getMessage("headerPriority"), 396 | sortable: true, 397 | value: "priority" }, 398 | { title: browser.i18n.getMessage("headerAge"), 399 | sortable: true, 400 | value: "age" }, 401 | ]; 402 | 403 | let popupTable = { 404 | "servers": [], 405 | "headers": popupHeaders, 406 | "loaded": false, 407 | }; 408 | let servers = Object.keys(triggerResults); 409 | for (var i = 0; i < servers.length; i++) { 410 | // Iterate over each configured server, generate trigger list 411 | let triggerTable = []; 412 | let server = servers[i]; 413 | let serverObject = { 414 | "server": server, 415 | "search": "", 416 | "expanded": [], 417 | }; 418 | 419 | if ( 420 | Object.hasOwn(triggerResults[server], 'error') 421 | ) { 422 | console.log("Error found in triggerResults for server: " + server); 423 | hasError = true; 424 | serverObject["error"] = triggerResults[server]["error"]; 425 | serverObject["errorMessage"] = triggerResults[server]["errorMessage"]; 426 | serverObject["errorDetails"] = triggerResults[server]["errorDetails"]; 427 | } else { 428 | // Iterate over found triggers and format for popup 429 | console.log( 430 | "Generating trigger table for server: " + server 431 | ); 432 | for (var t = 0; t < triggerResults[server].length; t++) { 433 | let priority = triggerResults[server][t]["priority"]; 434 | // Set priority number if higher than current 435 | // Used to set browser icon 436 | if (priority > topSeverity) { 437 | topSeverity = priority; 438 | } 439 | triggerTable.push({ 440 | system: triggerResults[server][t]["hosts"][0][settings.global.displayName], 441 | description: triggerResults[server][t]["description"], 442 | priority: priority, 443 | age: triggerResults[server][t]["lastchange"], 444 | triggerid: triggerResults[server][t]["triggerid"], 445 | hostid: triggerResults[server][t]["hosts"][0]["hostid"], 446 | eventid: triggerResults[server][t]["lastEvent"]["eventid"], 447 | acknowledged: Number(triggerResults[server][t]["lastEvent"]["acknowledged"]), 448 | maintenance_status: Number(triggerResults[server][t]["hosts"][0]["maintenance_status"]), 449 | }); 450 | } 451 | serverObject["triggers"] = triggerTable; 452 | 453 | // Lookup zabbix url from settings 454 | for (var x = 0; x < settings["servers"].length; x++) { 455 | if (settings["servers"][x]["alias"] === server) { 456 | serverObject["url"] = settings["servers"][x]["url"]; 457 | serverObject["version"] = settings["servers"][x]["version"]; 458 | serverObject["sortBy"] = settings["servers"][i]["sortBy"]; 459 | } 460 | } 461 | } 462 | 463 | popupTable["servers"].push(serverObject); 464 | } 465 | 466 | await browser.storage.session.set({"popupTable": popupTable}) 467 | 468 | if (hasError) { 469 | await setBrowserIcon("unconfigured"); 470 | } else { 471 | await setBrowserIcon("sev_" + topSeverity); 472 | } 473 | } 474 | 475 | // Activate messaging to popup.js and options.js 476 | // eslint-disable-next-line no-unused-vars 477 | async function handleMessage(request, sender, sendResponse) { 478 | switch (request.method) { 479 | case "reinitalize": { 480 | console.log("Background triggered reinialize") 481 | // Sent by options to alert to config changes in order to refresh 482 | await initalize(); 483 | break; 484 | } 485 | case "submitPagination": { 486 | // Message sent by popup to save header sorting 487 | var settings = await getSettings(); 488 | const newSort = [{ 489 | "key": request.sortBy, 490 | "order": request.descending 491 | }] 492 | settings.servers[request.index]["sortBy"] = newSort; 493 | 494 | await browser.storage.local.set({"ZabbixServers": JSON.stringify(settings)}); 495 | await setActiveTriggersTable(); 496 | break; 497 | } 498 | } 499 | return true; 500 | } 501 | -------------------------------------------------------------------------------- /src/images/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/images/sev_-1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/images/sev_0.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/images/sev_1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/images/sev_2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/images/sev_3.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/images/sev_4.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/images/sev_5.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/images/unconfigured.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/lib/crypto.js: -------------------------------------------------------------------------------- 1 | import { sjcl } from './sjcl.js'; 2 | 3 | var pass = navigator.appName + navigator.language + navigator.platform; 4 | var salt = sjcl.codec.base64.fromBits(sjcl.hash.sha256.hash(navigator.appName)); 5 | var decoderRing = sjcl.codec.hex.fromBits(sjcl.misc.pbkdf2(pass, salt)); 6 | 7 | const encryptSettingKeys = settings => { 8 | /* 9 | * Given full settings object, walk servers, and encrypt the apiToken and password fields 10 | * Returns settings object with encrypted fields 11 | */ 12 | for (let serverIndex in settings["servers"]) { 13 | settings.servers[serverIndex].apiToken = sjcl.encrypt(decoderRing, settings.servers[serverIndex].apiToken) 14 | settings.servers[serverIndex].pass = sjcl.encrypt(decoderRing, settings.servers[serverIndex].pass) 15 | } 16 | 17 | return settings; 18 | } 19 | 20 | const decryptSettings = encryptedData => { 21 | var decrypted = sjcl.decrypt(decoderRing, encryptedData); 22 | return decrypted; 23 | } 24 | 25 | export { encryptSettingKeys, decryptSettings} -------------------------------------------------------------------------------- /src/lib/sjcl.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* eslint-disable */ 3 | /* eslint quotes: "off" */ var sjcl = { 4 | cipher: {}, 5 | hash: {}, 6 | keyexchange: {}, 7 | mode: {}, 8 | misc: {}, 9 | codec: {}, 10 | exception: { 11 | corrupt: function (a) { 12 | this.toString = function () { 13 | return "CORRUPT: " + this.message; 14 | }; 15 | this.message = a; 16 | }, 17 | invalid: function (a) { 18 | this.toString = function () { 19 | return "INVALID: " + this.message; 20 | }; 21 | this.message = a; 22 | }, 23 | bug: function (a) { 24 | this.toString = function () { 25 | return "BUG: " + this.message; 26 | }; 27 | this.message = a; 28 | }, 29 | notReady: function (a) { 30 | this.toString = function () { 31 | return "NOT READY: " + this.message; 32 | }; 33 | this.message = a; 34 | }, 35 | }, 36 | }; 37 | sjcl.cipher.aes = function (a) { 38 | this.s[0][0][0] || this.O(); 39 | var b, 40 | c, 41 | d, 42 | e, 43 | f = this.s[0][4], 44 | g = this.s[1]; 45 | b = a.length; 46 | var h = 1; 47 | if (4 !== b && 6 !== b && 8 !== b) 48 | throw new sjcl.exception.invalid("invalid aes key size"); 49 | this.b = [(d = a.slice(0)), (e = [])]; 50 | for (a = b; a < 4 * b + 28; a++) { 51 | c = d[a - 1]; 52 | if (0 === a % b || (8 === b && 4 === a % b)) 53 | (c = 54 | (f[c >>> 24] << 24) ^ 55 | (f[(c >> 16) & 255] << 16) ^ 56 | (f[(c >> 8) & 255] << 8) ^ 57 | f[c & 255]), 58 | 0 === a % b && 59 | ((c = (c << 8) ^ (c >>> 24) ^ (h << 24)), 60 | (h = (h << 1) ^ (283 * (h >> 7)))); 61 | d[a] = d[a - b] ^ c; 62 | } 63 | for (b = 0; a; b++, a--) 64 | (c = d[b & 3 ? a : a - 4]), 65 | (e[b] = 66 | 4 >= a || 4 > b 67 | ? c 68 | : g[0][f[c >>> 24]] ^ 69 | g[1][f[(c >> 16) & 255]] ^ 70 | g[2][f[(c >> 8) & 255]] ^ 71 | g[3][f[c & 255]]); 72 | }; 73 | sjcl.cipher.aes.prototype = { 74 | encrypt: function (a) { 75 | return t(this, a, 0); 76 | }, 77 | decrypt: function (a) { 78 | return t(this, a, 1); 79 | }, 80 | s: [ 81 | [[], [], [], [], []], 82 | [[], [], [], [], []], 83 | ], 84 | O: function () { 85 | var a = this.s[0], 86 | b = this.s[1], 87 | c = a[4], 88 | d = b[4], 89 | e, 90 | f, 91 | g, 92 | h = [], 93 | k = [], 94 | l, 95 | n, 96 | m, 97 | p; 98 | for (e = 0; 0x100 > e; e++) k[(h[e] = (e << 1) ^ (283 * (e >> 7))) ^ e] = e; 99 | for (f = g = 0; !c[f]; f ^= l || 1, g = k[g] || 1) 100 | for ( 101 | m = g ^ (g << 1) ^ (g << 2) ^ (g << 3) ^ (g << 4), 102 | m = (m >> 8) ^ (m & 255) ^ 99, 103 | c[f] = m, 104 | d[m] = f, 105 | n = h[(e = h[(l = h[f])])], 106 | p = (0x1010101 * n) ^ (0x10001 * e) ^ (0x101 * l) ^ (0x1010100 * f), 107 | n = (0x101 * h[m]) ^ (0x1010100 * m), 108 | e = 0; 109 | 4 > e; 110 | e++ 111 | ) 112 | (a[e][f] = n = (n << 24) ^ (n >>> 8)), 113 | (b[e][m] = p = (p << 24) ^ (p >>> 8)); 114 | for (e = 0; 5 > e; e++) (a[e] = a[e].slice(0)), (b[e] = b[e].slice(0)); 115 | }, 116 | }; 117 | function t(a, b, c) { 118 | if (4 !== b.length) 119 | throw new sjcl.exception.invalid("invalid aes block size"); 120 | var d = a.b[c], 121 | e = b[0] ^ d[0], 122 | f = b[c ? 3 : 1] ^ d[1], 123 | g = b[2] ^ d[2]; 124 | b = b[c ? 1 : 3] ^ d[3]; 125 | var h, 126 | k, 127 | l, 128 | n = d.length / 4 - 2, 129 | m, 130 | p = 4, 131 | r = [0, 0, 0, 0]; 132 | h = a.s[c]; 133 | a = h[0]; 134 | var q = h[1], 135 | v = h[2], 136 | w = h[3], 137 | x = h[4]; 138 | for (m = 0; m < n; m++) 139 | (h = 140 | a[e >>> 24] ^ q[(f >> 16) & 255] ^ v[(g >> 8) & 255] ^ w[b & 255] ^ d[p]), 141 | (k = 142 | a[f >>> 24] ^ 143 | q[(g >> 16) & 255] ^ 144 | v[(b >> 8) & 255] ^ 145 | w[e & 255] ^ 146 | d[p + 1]), 147 | (l = 148 | a[g >>> 24] ^ 149 | q[(b >> 16) & 255] ^ 150 | v[(e >> 8) & 255] ^ 151 | w[f & 255] ^ 152 | d[p + 2]), 153 | (b = 154 | a[b >>> 24] ^ 155 | q[(e >> 16) & 255] ^ 156 | v[(f >> 8) & 255] ^ 157 | w[g & 255] ^ 158 | d[p + 3]), 159 | (p += 4), 160 | (e = h), 161 | (f = k), 162 | (g = l); 163 | for (m = 0; 4 > m; m++) 164 | (r[c ? 3 & -m : m] = 165 | (x[e >>> 24] << 24) ^ 166 | (x[(f >> 16) & 255] << 16) ^ 167 | (x[(g >> 8) & 255] << 8) ^ 168 | x[b & 255] ^ 169 | d[p++]), 170 | (h = e), 171 | (e = f), 172 | (f = g), 173 | (g = b), 174 | (b = h); 175 | return r; 176 | } 177 | sjcl.bitArray = { 178 | bitSlice: function (a, b, c) { 179 | a = sjcl.bitArray.$(a.slice(b / 32), 32 - (b & 31)).slice(1); 180 | return void 0 === c ? a : sjcl.bitArray.clamp(a, c - b); 181 | }, 182 | extract: function (a, b, c) { 183 | var d = Math.floor((-b - c) & 31); 184 | return ( 185 | (((b + c - 1) ^ b) & -32 186 | ? (a[(b / 32) | 0] << (32 - d)) ^ (a[(b / 32 + 1) | 0] >>> d) 187 | : a[(b / 32) | 0] >>> d) & 188 | ((1 << c) - 1) 189 | ); 190 | }, 191 | concat: function (a, b) { 192 | if (0 === a.length || 0 === b.length) return a.concat(b); 193 | var c = a[a.length - 1], 194 | d = sjcl.bitArray.getPartial(c); 195 | return 32 === d 196 | ? a.concat(b) 197 | : sjcl.bitArray.$(b, d, c | 0, a.slice(0, a.length - 1)); 198 | }, 199 | bitLength: function (a) { 200 | var b = a.length; 201 | return 0 === b ? 0 : 32 * (b - 1) + sjcl.bitArray.getPartial(a[b - 1]); 202 | }, 203 | clamp: function (a, b) { 204 | if (32 * a.length < b) return a; 205 | a = a.slice(0, Math.ceil(b / 32)); 206 | var c = a.length; 207 | b = b & 31; 208 | 0 < c && 209 | b && 210 | (a[c - 1] = sjcl.bitArray.partial( 211 | b, 212 | a[c - 1] & (2147483648 >> (b - 1)), 213 | 1 214 | )); 215 | return a; 216 | }, 217 | partial: function (a, b, c) { 218 | return 32 === a ? b : (c ? b | 0 : b << (32 - a)) + 0x10000000000 * a; 219 | }, 220 | getPartial: function (a) { 221 | return Math.round(a / 0x10000000000) || 32; 222 | }, 223 | equal: function (a, b) { 224 | if (sjcl.bitArray.bitLength(a) !== sjcl.bitArray.bitLength(b)) return !1; 225 | var c = 0, 226 | d; 227 | for (d = 0; d < a.length; d++) c |= a[d] ^ b[d]; 228 | return 0 === c; 229 | }, 230 | $: function (a, b, c, d) { 231 | var e; 232 | e = 0; 233 | for (void 0 === d && (d = []); 32 <= b; b -= 32) d.push(c), (c = 0); 234 | if (0 === b) return d.concat(a); 235 | for (e = 0; e < a.length; e++) 236 | d.push(c | (a[e] >>> b)), (c = a[e] << (32 - b)); 237 | e = a.length ? a[a.length - 1] : 0; 238 | a = sjcl.bitArray.getPartial(e); 239 | d.push(sjcl.bitArray.partial((b + a) & 31, 32 < b + a ? c : d.pop(), 1)); 240 | return d; 241 | }, 242 | i: function (a, b) { 243 | return [a[0] ^ b[0], a[1] ^ b[1], a[2] ^ b[2], a[3] ^ b[3]]; 244 | }, 245 | byteswapM: function (a) { 246 | var b, c; 247 | for (b = 0; b < a.length; ++b) 248 | (c = a[b]), 249 | (a[b] = 250 | (c >>> 24) | ((c >>> 8) & 0xff00) | ((c & 0xff00) << 8) | (c << 24)); 251 | return a; 252 | }, 253 | }; 254 | sjcl.codec.utf8String = { 255 | fromBits: function (a) { 256 | var b = "", 257 | c = sjcl.bitArray.bitLength(a), 258 | d, 259 | e; 260 | for (d = 0; d < c / 8; d++) 261 | 0 === (d & 3) && (e = a[d / 4]), 262 | (b += String.fromCharCode(((e >>> 8) >>> 8) >>> 8)), 263 | (e <<= 8); 264 | return decodeURIComponent(escape(b)); 265 | }, 266 | toBits: function (a) { 267 | a = unescape(encodeURIComponent(a)); 268 | var b = [], 269 | c, 270 | d = 0; 271 | for (c = 0; c < a.length; c++) 272 | (d = (d << 8) | a.charCodeAt(c)), 3 === (c & 3) && (b.push(d), (d = 0)); 273 | c & 3 && b.push(sjcl.bitArray.partial(8 * (c & 3), d)); 274 | return b; 275 | }, 276 | }; 277 | sjcl.codec.hex = { 278 | fromBits: function (a) { 279 | var b = "", 280 | c; 281 | for (c = 0; c < a.length; c++) 282 | b += ((a[c] | 0) + 0xf00000000000).toString(16).substr(4); 283 | return b.substr(0, sjcl.bitArray.bitLength(a) / 4); 284 | }, 285 | toBits: function (a) { 286 | var b, 287 | c = [], 288 | d; 289 | a = a.replace(/\s|0x/g, ""); 290 | d = a.length; 291 | a = a + "00000000"; 292 | for (b = 0; b < a.length; b += 8) c.push(parseInt(a.substr(b, 8), 16) ^ 0); 293 | return sjcl.bitArray.clamp(c, 4 * d); 294 | }, 295 | }; 296 | sjcl.codec.base32 = { 297 | B: "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", 298 | X: "0123456789ABCDEFGHIJKLMNOPQRSTUV", 299 | BITS: 32, 300 | BASE: 5, 301 | REMAINING: 27, 302 | fromBits: function (a, b, c) { 303 | var d = sjcl.codec.base32.BASE, 304 | e = sjcl.codec.base32.REMAINING, 305 | f = "", 306 | g = 0, 307 | h = sjcl.codec.base32.B, 308 | k = 0, 309 | l = sjcl.bitArray.bitLength(a); 310 | c && (h = sjcl.codec.base32.X); 311 | for (c = 0; f.length * d < l; ) 312 | (f += h.charAt((k ^ (a[c] >>> g)) >>> e)), 313 | g < d ? ((k = a[c] << (d - g)), (g += e), c++) : ((k <<= d), (g -= d)); 314 | for (; f.length & 7 && !b; ) f += "="; 315 | return f; 316 | }, 317 | toBits: function (a, b) { 318 | a = a.replace(/\s|=/g, "").toUpperCase(); 319 | var c = sjcl.codec.base32.BITS, 320 | d = sjcl.codec.base32.BASE, 321 | e = sjcl.codec.base32.REMAINING, 322 | f = [], 323 | g, 324 | h = 0, 325 | k = sjcl.codec.base32.B, 326 | l = 0, 327 | n, 328 | m = "base32"; 329 | b && ((k = sjcl.codec.base32.X), (m = "base32hex")); 330 | for (g = 0; g < a.length; g++) { 331 | n = k.indexOf(a.charAt(g)); 332 | if (0 > n) { 333 | if (!b) 334 | try { 335 | return sjcl.codec.base32hex.toBits(a); 336 | } catch (p) {} 337 | throw new sjcl.exception.invalid("this isn't " + m + "!"); 338 | } 339 | h > e 340 | ? ((h -= e), f.push(l ^ (n >>> h)), (l = n << (c - h))) 341 | : ((h += d), (l ^= n << (c - h))); 342 | } 343 | h & 56 && f.push(sjcl.bitArray.partial(h & 56, l, 1)); 344 | return f; 345 | }, 346 | }; 347 | sjcl.codec.base32hex = { 348 | fromBits: function (a, b) { 349 | return sjcl.codec.base32.fromBits(a, b, 1); 350 | }, 351 | toBits: function (a) { 352 | return sjcl.codec.base32.toBits(a, 1); 353 | }, 354 | }; 355 | sjcl.codec.base64 = { 356 | B: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", 357 | fromBits: function (a, b, c) { 358 | var d = "", 359 | e = 0, 360 | f = sjcl.codec.base64.B, 361 | g = 0, 362 | h = sjcl.bitArray.bitLength(a); 363 | c && (f = f.substr(0, 62) + "-_"); 364 | for (c = 0; 6 * d.length < h; ) 365 | (d += f.charAt((g ^ (a[c] >>> e)) >>> 26)), 366 | 6 > e ? ((g = a[c] << (6 - e)), (e += 26), c++) : ((g <<= 6), (e -= 6)); 367 | for (; d.length & 3 && !b; ) d += "="; 368 | return d; 369 | }, 370 | toBits: function (a, b) { 371 | a = a.replace(/\s|=/g, ""); 372 | var c = [], 373 | d, 374 | e = 0, 375 | f = sjcl.codec.base64.B, 376 | g = 0, 377 | h; 378 | b && (f = f.substr(0, 62) + "-_"); 379 | for (d = 0; d < a.length; d++) { 380 | h = f.indexOf(a.charAt(d)); 381 | if (0 > h) throw new sjcl.exception.invalid("this isn't base64!"); 382 | 26 < e 383 | ? ((e -= 26), c.push(g ^ (h >>> e)), (g = h << (32 - e))) 384 | : ((e += 6), (g ^= h << (32 - e))); 385 | } 386 | e & 56 && c.push(sjcl.bitArray.partial(e & 56, g, 1)); 387 | return c; 388 | }, 389 | }; 390 | sjcl.codec.base64url = { 391 | fromBits: function (a) { 392 | return sjcl.codec.base64.fromBits(a, 1, 1); 393 | }, 394 | toBits: function (a) { 395 | return sjcl.codec.base64.toBits(a, 1); 396 | }, 397 | }; 398 | sjcl.hash.sha256 = function (a) { 399 | this.b[0] || this.O(); 400 | a 401 | ? ((this.F = a.F.slice(0)), (this.A = a.A.slice(0)), (this.l = a.l)) 402 | : this.reset(); 403 | }; 404 | sjcl.hash.sha256.hash = function (a) { 405 | return new sjcl.hash.sha256().update(a).finalize(); 406 | }; 407 | sjcl.hash.sha256.prototype = { 408 | blockSize: 512, 409 | reset: function () { 410 | this.F = this.Y.slice(0); 411 | this.A = []; 412 | this.l = 0; 413 | return this; 414 | }, 415 | update: function (a) { 416 | "string" === typeof a && (a = sjcl.codec.utf8String.toBits(a)); 417 | var b, 418 | c = (this.A = sjcl.bitArray.concat(this.A, a)); 419 | b = this.l; 420 | a = this.l = b + sjcl.bitArray.bitLength(a); 421 | if (0x1fffffffffffff < a) 422 | throw new sjcl.exception.invalid("Cannot hash more than 2^53 - 1 bits"); 423 | if ("undefined" !== typeof Uint32Array) { 424 | var d = new Uint32Array(c), 425 | e = 0; 426 | for (b = 512 + b - ((512 + b) & 0x1ff); b <= a; b += 512) 427 | u(this, d.subarray(16 * e, 16 * (e + 1))), (e += 1); 428 | c.splice(0, 16 * e); 429 | } else 430 | for (b = 512 + b - ((512 + b) & 0x1ff); b <= a; b += 512) 431 | u(this, c.splice(0, 16)); 432 | return this; 433 | }, 434 | finalize: function () { 435 | var a, 436 | b = this.A, 437 | c = this.F, 438 | b = sjcl.bitArray.concat(b, [sjcl.bitArray.partial(1, 1)]); 439 | for (a = b.length + 2; a & 15; a++) b.push(0); 440 | b.push(Math.floor(this.l / 0x100000000)); 441 | for (b.push(this.l | 0); b.length; ) u(this, b.splice(0, 16)); 442 | this.reset(); 443 | return c; 444 | }, 445 | Y: [], 446 | b: [], 447 | O: function () { 448 | function a(a) { 449 | return (0x100000000 * (a - Math.floor(a))) | 0; 450 | } 451 | for (var b = 0, c = 2, d, e; 64 > b; c++) { 452 | e = !0; 453 | for (d = 2; d * d <= c; d++) 454 | if (0 === c % d) { 455 | e = !1; 456 | break; 457 | } 458 | e && 459 | (8 > b && (this.Y[b] = a(Math.pow(c, 0.5))), 460 | (this.b[b] = a(Math.pow(c, 1 / 3))), 461 | b++); 462 | } 463 | }, 464 | }; 465 | function u(a, b) { 466 | var c, 467 | d, 468 | e, 469 | f = a.F, 470 | g = a.b, 471 | h = f[0], 472 | k = f[1], 473 | l = f[2], 474 | n = f[3], 475 | m = f[4], 476 | p = f[5], 477 | r = f[6], 478 | q = f[7]; 479 | for (c = 0; 64 > c; c++) 480 | 16 > c 481 | ? (d = b[c]) 482 | : ((d = b[(c + 1) & 15]), 483 | (e = b[(c + 14) & 15]), 484 | (d = b[c & 15] = 485 | (((d >>> 7) ^ (d >>> 18) ^ (d >>> 3) ^ (d << 25) ^ (d << 14)) + 486 | ((e >>> 17) ^ (e >>> 19) ^ (e >>> 10) ^ (e << 15) ^ (e << 13)) + 487 | b[c & 15] + 488 | b[(c + 9) & 15]) | 489 | 0)), 490 | (d = 491 | d + 492 | q + 493 | ((m >>> 6) ^ 494 | (m >>> 11) ^ 495 | (m >>> 25) ^ 496 | (m << 26) ^ 497 | (m << 21) ^ 498 | (m << 7)) + 499 | (r ^ (m & (p ^ r))) + 500 | g[c]), 501 | (q = r), 502 | (r = p), 503 | (p = m), 504 | (m = (n + d) | 0), 505 | (n = l), 506 | (l = k), 507 | (k = h), 508 | (h = 509 | (d + 510 | ((k & l) ^ (n & (k ^ l))) + 511 | ((k >>> 2) ^ 512 | (k >>> 13) ^ 513 | (k >>> 22) ^ 514 | (k << 30) ^ 515 | (k << 19) ^ 516 | (k << 10))) | 517 | 0); 518 | f[0] = (f[0] + h) | 0; 519 | f[1] = (f[1] + k) | 0; 520 | f[2] = (f[2] + l) | 0; 521 | f[3] = (f[3] + n) | 0; 522 | f[4] = (f[4] + m) | 0; 523 | f[5] = (f[5] + p) | 0; 524 | f[6] = (f[6] + r) | 0; 525 | f[7] = (f[7] + q) | 0; 526 | } 527 | sjcl.mode.ccm = { 528 | name: "ccm", 529 | G: [], 530 | listenProgress: function (a) { 531 | sjcl.mode.ccm.G.push(a); 532 | }, 533 | unListenProgress: function (a) { 534 | a = sjcl.mode.ccm.G.indexOf(a); 535 | -1 < a && sjcl.mode.ccm.G.splice(a, 1); 536 | }, 537 | fa: function (a) { 538 | var b = sjcl.mode.ccm.G.slice(), 539 | c; 540 | for (c = 0; c < b.length; c += 1) b[c](a); 541 | }, 542 | encrypt: function (a, b, c, d, e) { 543 | var f, 544 | g = b.slice(0), 545 | h = sjcl.bitArray, 546 | k = h.bitLength(c) / 8, 547 | l = h.bitLength(g) / 8; 548 | e = e || 64; 549 | d = d || []; 550 | if (7 > k) 551 | throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes"); 552 | for (f = 2; 4 > f && l >>> (8 * f); f++); 553 | f < 15 - k && (f = 15 - k); 554 | c = h.clamp(c, 8 * (15 - f)); 555 | b = sjcl.mode.ccm.V(a, b, c, d, e, f); 556 | g = sjcl.mode.ccm.C(a, g, c, b, e, f); 557 | return h.concat(g.data, g.tag); 558 | }, 559 | decrypt: function (a, b, c, d, e) { 560 | e = e || 64; 561 | d = d || []; 562 | var f = sjcl.bitArray, 563 | g = f.bitLength(c) / 8, 564 | h = f.bitLength(b), 565 | k = f.clamp(b, h - e), 566 | l = f.bitSlice(b, h - e), 567 | h = (h - e) / 8; 568 | if (7 > g) 569 | throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes"); 570 | for (b = 2; 4 > b && h >>> (8 * b); b++); 571 | b < 15 - g && (b = 15 - g); 572 | c = f.clamp(c, 8 * (15 - b)); 573 | k = sjcl.mode.ccm.C(a, k, c, l, e, b); 574 | a = sjcl.mode.ccm.V(a, k.data, c, d, e, b); 575 | if (!f.equal(k.tag, a)) 576 | throw new sjcl.exception.corrupt("ccm: tag doesn't match"); 577 | return k.data; 578 | }, 579 | na: function (a, b, c, d, e, f) { 580 | var g = [], 581 | h = sjcl.bitArray, 582 | k = h.i; 583 | d = [h.partial(8, (b.length ? 64 : 0) | ((d - 2) << 2) | (f - 1))]; 584 | d = h.concat(d, c); 585 | d[3] |= e; 586 | d = a.encrypt(d); 587 | if (b.length) 588 | for ( 589 | c = h.bitLength(b) / 8, 590 | 65279 >= c 591 | ? (g = [h.partial(16, c)]) 592 | : 0xffffffff >= c && (g = h.concat([h.partial(16, 65534)], [c])), 593 | g = h.concat(g, b), 594 | b = 0; 595 | b < g.length; 596 | b += 4 597 | ) 598 | d = a.encrypt(k(d, g.slice(b, b + 4).concat([0, 0, 0]))); 599 | return d; 600 | }, 601 | V: function (a, b, c, d, e, f) { 602 | var g = sjcl.bitArray, 603 | h = g.i; 604 | e /= 8; 605 | if (e % 2 || 4 > e || 16 < e) 606 | throw new sjcl.exception.invalid("ccm: invalid tag length"); 607 | if (0xffffffff < d.length || 0xffffffff < b.length) 608 | throw new sjcl.exception.bug("ccm: can't deal with 4GiB or more data"); 609 | c = sjcl.mode.ccm.na(a, d, c, e, g.bitLength(b) / 8, f); 610 | for (d = 0; d < b.length; d += 4) 611 | c = a.encrypt(h(c, b.slice(d, d + 4).concat([0, 0, 0]))); 612 | return g.clamp(c, 8 * e); 613 | }, 614 | C: function (a, b, c, d, e, f) { 615 | var g, 616 | h = sjcl.bitArray; 617 | g = h.i; 618 | var k = b.length, 619 | l = h.bitLength(b), 620 | n = k / 50, 621 | m = n; 622 | c = h 623 | .concat([h.partial(8, f - 1)], c) 624 | .concat([0, 0, 0]) 625 | .slice(0, 4); 626 | d = h.bitSlice(g(d, a.encrypt(c)), 0, e); 627 | if (!k) return { tag: d, data: [] }; 628 | for (g = 0; g < k; g += 4) 629 | g > n && (sjcl.mode.ccm.fa(g / k), (n += m)), 630 | c[3]++, 631 | (e = a.encrypt(c)), 632 | (b[g] ^= e[0]), 633 | (b[g + 1] ^= e[1]), 634 | (b[g + 2] ^= e[2]), 635 | (b[g + 3] ^= e[3]); 636 | return { tag: d, data: h.clamp(b, l) }; 637 | }, 638 | }; 639 | sjcl.mode.ocb2 = { 640 | name: "ocb2", 641 | encrypt: function (a, b, c, d, e, f) { 642 | if (128 !== sjcl.bitArray.bitLength(c)) 643 | throw new sjcl.exception.invalid("ocb iv must be 128 bits"); 644 | var g, 645 | h = sjcl.mode.ocb2.S, 646 | k = sjcl.bitArray, 647 | l = k.i, 648 | n = [0, 0, 0, 0]; 649 | c = h(a.encrypt(c)); 650 | var m, 651 | p = []; 652 | d = d || []; 653 | e = e || 64; 654 | for (g = 0; g + 4 < b.length; g += 4) 655 | (m = b.slice(g, g + 4)), 656 | (n = l(n, m)), 657 | (p = p.concat(l(c, a.encrypt(l(c, m))))), 658 | (c = h(c)); 659 | m = b.slice(g); 660 | b = k.bitLength(m); 661 | g = a.encrypt(l(c, [0, 0, 0, b])); 662 | m = k.clamp(l(m.concat([0, 0, 0]), g), b); 663 | n = l(n, l(m.concat([0, 0, 0]), g)); 664 | n = a.encrypt(l(n, l(c, h(c)))); 665 | d.length && (n = l(n, f ? d : sjcl.mode.ocb2.pmac(a, d))); 666 | return p.concat(k.concat(m, k.clamp(n, e))); 667 | }, 668 | decrypt: function (a, b, c, d, e, f) { 669 | if (128 !== sjcl.bitArray.bitLength(c)) 670 | throw new sjcl.exception.invalid("ocb iv must be 128 bits"); 671 | e = e || 64; 672 | var g = sjcl.mode.ocb2.S, 673 | h = sjcl.bitArray, 674 | k = h.i, 675 | l = [0, 0, 0, 0], 676 | n = g(a.encrypt(c)), 677 | m, 678 | p, 679 | r = sjcl.bitArray.bitLength(b) - e, 680 | q = []; 681 | d = d || []; 682 | for (c = 0; c + 4 < r / 32; c += 4) 683 | (m = k(n, a.decrypt(k(n, b.slice(c, c + 4))))), 684 | (l = k(l, m)), 685 | (q = q.concat(m)), 686 | (n = g(n)); 687 | p = r - 32 * c; 688 | m = a.encrypt(k(n, [0, 0, 0, p])); 689 | m = k(m, h.clamp(b.slice(c), p).concat([0, 0, 0])); 690 | l = k(l, m); 691 | l = a.encrypt(k(l, k(n, g(n)))); 692 | d.length && (l = k(l, f ? d : sjcl.mode.ocb2.pmac(a, d))); 693 | if (!h.equal(h.clamp(l, e), h.bitSlice(b, r))) 694 | throw new sjcl.exception.corrupt("ocb: tag doesn't match"); 695 | return q.concat(h.clamp(m, p)); 696 | }, 697 | pmac: function (a, b) { 698 | var c, 699 | d = sjcl.mode.ocb2.S, 700 | e = sjcl.bitArray, 701 | f = e.i, 702 | g = [0, 0, 0, 0], 703 | h = a.encrypt([0, 0, 0, 0]), 704 | h = f(h, d(d(h))); 705 | for (c = 0; c + 4 < b.length; c += 4) 706 | (h = d(h)), (g = f(g, a.encrypt(f(h, b.slice(c, c + 4))))); 707 | c = b.slice(c); 708 | 128 > e.bitLength(c) && 709 | ((h = f(h, d(h))), (c = e.concat(c, [-2147483648, 0, 0, 0]))); 710 | g = f(g, c); 711 | return a.encrypt(f(d(f(h, d(h))), g)); 712 | }, 713 | S: function (a) { 714 | return [ 715 | (a[0] << 1) ^ (a[1] >>> 31), 716 | (a[1] << 1) ^ (a[2] >>> 31), 717 | (a[2] << 1) ^ (a[3] >>> 31), 718 | (a[3] << 1) ^ (135 * (a[0] >>> 31)), 719 | ]; 720 | }, 721 | }; 722 | sjcl.mode.gcm = { 723 | name: "gcm", 724 | encrypt: function (a, b, c, d, e) { 725 | var f = b.slice(0); 726 | b = sjcl.bitArray; 727 | d = d || []; 728 | a = sjcl.mode.gcm.C(!0, a, f, d, c, e || 128); 729 | return b.concat(a.data, a.tag); 730 | }, 731 | decrypt: function (a, b, c, d, e) { 732 | var f = b.slice(0), 733 | g = sjcl.bitArray, 734 | h = g.bitLength(f); 735 | e = e || 128; 736 | d = d || []; 737 | e <= h 738 | ? ((b = g.bitSlice(f, h - e)), (f = g.bitSlice(f, 0, h - e))) 739 | : ((b = f), (f = [])); 740 | a = sjcl.mode.gcm.C(!1, a, f, d, c, e); 741 | if (!g.equal(a.tag, b)) 742 | throw new sjcl.exception.corrupt("gcm: tag doesn't match"); 743 | return a.data; 744 | }, 745 | ka: function (a, b) { 746 | var c, 747 | d, 748 | e, 749 | f, 750 | g, 751 | h = sjcl.bitArray.i; 752 | e = [0, 0, 0, 0]; 753 | f = b.slice(0); 754 | for (c = 0; 128 > c; c++) { 755 | (d = 0 !== (a[Math.floor(c / 32)] & (1 << (31 - (c % 32))))) && 756 | (e = h(e, f)); 757 | g = 0 !== (f[3] & 1); 758 | for (d = 3; 0 < d; d--) f[d] = (f[d] >>> 1) | ((f[d - 1] & 1) << 31); 759 | f[0] >>>= 1; 760 | g && (f[0] ^= -0x1f000000); 761 | } 762 | return e; 763 | }, 764 | j: function (a, b, c) { 765 | var d, 766 | e = c.length; 767 | b = b.slice(0); 768 | for (d = 0; d < e; d += 4) 769 | (b[0] ^= 0xffffffff & c[d]), 770 | (b[1] ^= 0xffffffff & c[d + 1]), 771 | (b[2] ^= 0xffffffff & c[d + 2]), 772 | (b[3] ^= 0xffffffff & c[d + 3]), 773 | (b = sjcl.mode.gcm.ka(b, a)); 774 | return b; 775 | }, 776 | C: function (a, b, c, d, e, f) { 777 | var g, 778 | h, 779 | k, 780 | l, 781 | n, 782 | m, 783 | p, 784 | r, 785 | q = sjcl.bitArray; 786 | m = c.length; 787 | p = q.bitLength(c); 788 | r = q.bitLength(d); 789 | h = q.bitLength(e); 790 | g = b.encrypt([0, 0, 0, 0]); 791 | 96 === h 792 | ? ((e = e.slice(0)), (e = q.concat(e, [1]))) 793 | : ((e = sjcl.mode.gcm.j(g, [0, 0, 0, 0], e)), 794 | (e = sjcl.mode.gcm.j(g, e, [ 795 | 0, 796 | 0, 797 | Math.floor(h / 0x100000000), 798 | h & 0xffffffff, 799 | ]))); 800 | h = sjcl.mode.gcm.j(g, [0, 0, 0, 0], d); 801 | n = e.slice(0); 802 | d = h.slice(0); 803 | a || (d = sjcl.mode.gcm.j(g, h, c)); 804 | for (l = 0; l < m; l += 4) 805 | n[3]++, 806 | (k = b.encrypt(n)), 807 | (c[l] ^= k[0]), 808 | (c[l + 1] ^= k[1]), 809 | (c[l + 2] ^= k[2]), 810 | (c[l + 3] ^= k[3]); 811 | c = q.clamp(c, p); 812 | a && (d = sjcl.mode.gcm.j(g, h, c)); 813 | a = [ 814 | Math.floor(r / 0x100000000), 815 | r & 0xffffffff, 816 | Math.floor(p / 0x100000000), 817 | p & 0xffffffff, 818 | ]; 819 | d = sjcl.mode.gcm.j(g, d, a); 820 | k = b.encrypt(e); 821 | d[0] ^= k[0]; 822 | d[1] ^= k[1]; 823 | d[2] ^= k[2]; 824 | d[3] ^= k[3]; 825 | return { tag: q.bitSlice(d, 0, f), data: c }; 826 | }, 827 | }; 828 | sjcl.misc.hmac = function (a, b) { 829 | this.W = b = b || sjcl.hash.sha256; 830 | var c = [[], []], 831 | d, 832 | e = b.prototype.blockSize / 32; 833 | this.w = [new b(), new b()]; 834 | a.length > e && (a = b.hash(a)); 835 | for (d = 0; d < e; d++) 836 | (c[0][d] = a[d] ^ 909522486), (c[1][d] = a[d] ^ 1549556828); 837 | this.w[0].update(c[0]); 838 | this.w[1].update(c[1]); 839 | this.R = new b(this.w[0]); 840 | }; 841 | sjcl.misc.hmac.prototype.encrypt = sjcl.misc.hmac.prototype.mac = function (a) { 842 | if (this.aa) 843 | throw new sjcl.exception.invalid("encrypt on already updated hmac called!"); 844 | this.update(a); 845 | return this.digest(a); 846 | }; 847 | sjcl.misc.hmac.prototype.reset = function () { 848 | this.R = new this.W(this.w[0]); 849 | this.aa = !1; 850 | }; 851 | sjcl.misc.hmac.prototype.update = function (a) { 852 | this.aa = !0; 853 | this.R.update(a); 854 | }; 855 | sjcl.misc.hmac.prototype.digest = function () { 856 | var a = this.R.finalize(), 857 | a = new this.W(this.w[1]).update(a).finalize(); 858 | this.reset(); 859 | return a; 860 | }; 861 | sjcl.misc.pbkdf2 = function (a, b, c, d, e) { 862 | c = c || 1e4; 863 | if (0 > d || 0 > c) 864 | throw new sjcl.exception.invalid("invalid params to pbkdf2"); 865 | "string" === typeof a && (a = sjcl.codec.utf8String.toBits(a)); 866 | "string" === typeof b && (b = sjcl.codec.utf8String.toBits(b)); 867 | e = e || sjcl.misc.hmac; 868 | a = new e(a); 869 | var f, 870 | g, 871 | h, 872 | k, 873 | l = [], 874 | n = sjcl.bitArray; 875 | for (k = 1; 32 * l.length < (d || 1); k++) { 876 | e = f = a.encrypt(n.concat(b, [k])); 877 | for (g = 1; g < c; g++) 878 | for (f = a.encrypt(f), h = 0; h < f.length; h++) e[h] ^= f[h]; 879 | l = l.concat(e); 880 | } 881 | d && (l = n.clamp(l, d)); 882 | return l; 883 | }; 884 | sjcl.prng = function (a) { 885 | this.c = [new sjcl.hash.sha256()]; 886 | this.m = [0]; 887 | this.P = 0; 888 | this.H = {}; 889 | this.N = 0; 890 | this.U = {}; 891 | this.Z = this.f = this.o = this.ha = 0; 892 | this.b = [0, 0, 0, 0, 0, 0, 0, 0]; 893 | this.h = [0, 0, 0, 0]; 894 | this.L = void 0; 895 | this.M = a; 896 | this.D = !1; 897 | this.K = { progress: {}, seeded: {} }; 898 | this.u = this.ga = 0; 899 | this.I = 1; 900 | this.J = 2; 901 | this.ca = 0x10000; 902 | this.T = [0, 48, 64, 96, 128, 192, 0x100, 384, 512, 768, 1024]; 903 | this.da = 3e4; 904 | this.ba = 80; 905 | }; 906 | sjcl.prng.prototype = { 907 | randomWords: function (a, b) { 908 | var c = [], 909 | d; 910 | d = this.isReady(b); 911 | var e; 912 | if (d === this.u) 913 | throw new sjcl.exception.notReady("generator isn't seeded"); 914 | if (d & this.J) { 915 | d = !(d & this.I); 916 | e = []; 917 | var f = 0, 918 | g; 919 | this.Z = e[0] = new Date().valueOf() + this.da; 920 | for (g = 0; 16 > g; g++) e.push((0x100000000 * Math.random()) | 0); 921 | for ( 922 | g = 0; 923 | g < this.c.length && 924 | ((e = e.concat(this.c[g].finalize())), 925 | (f += this.m[g]), 926 | (this.m[g] = 0), 927 | d || !(this.P & (1 << g))); 928 | g++ 929 | ); 930 | this.P >= 1 << this.c.length && 931 | (this.c.push(new sjcl.hash.sha256()), this.m.push(0)); 932 | this.f -= f; 933 | f > this.o && (this.o = f); 934 | this.P++; 935 | this.b = sjcl.hash.sha256.hash(this.b.concat(e)); 936 | this.L = new sjcl.cipher.aes(this.b); 937 | for ( 938 | d = 0; 939 | 4 > d && ((this.h[d] = (this.h[d] + 1) | 0), !this.h[d]); 940 | d++ 941 | ); 942 | } 943 | for (d = 0; d < a; d += 4) 944 | 0 === (d + 1) % this.ca && y(this), 945 | (e = z(this)), 946 | c.push(e[0], e[1], e[2], e[3]); 947 | y(this); 948 | return c.slice(0, a); 949 | }, 950 | setDefaultParanoia: function (a, b) { 951 | if ( 952 | 0 === a && 953 | "Setting paranoia=0 will ruin your security; use it only for testing" !== 954 | b 955 | ) 956 | throw new sjcl.exception.invalid( 957 | "Setting paranoia=0 will ruin your security; use it only for testing" 958 | ); 959 | this.M = a; 960 | }, 961 | addEntropy: function (a, b, c) { 962 | c = c || "user"; 963 | var d, 964 | e, 965 | f = new Date().valueOf(), 966 | g = this.H[c], 967 | h = this.isReady(), 968 | k = 0; 969 | d = this.U[c]; 970 | void 0 === d && (d = this.U[c] = this.ha++); 971 | void 0 === g && (g = this.H[c] = 0); 972 | this.H[c] = (this.H[c] + 1) % this.c.length; 973 | switch (typeof a) { 974 | case "number": 975 | void 0 === b && (b = 1); 976 | this.c[g].update([d, this.N++, 1, b, f, 1, a | 0]); 977 | break; 978 | case "object": 979 | c = Object.prototype.toString.call(a); 980 | if ("[object Uint32Array]" === c) { 981 | e = []; 982 | for (c = 0; c < a.length; c++) e.push(a[c]); 983 | a = e; 984 | } else 985 | for ( 986 | "[object Array]" !== c && (k = 1), c = 0; 987 | c < a.length && !k; 988 | c++ 989 | ) 990 | "number" !== typeof a[c] && (k = 1); 991 | if (!k) { 992 | if (void 0 === b) 993 | for (c = b = 0; c < a.length; c++) 994 | for (e = a[c]; 0 < e; ) b++, (e = e >>> 1); 995 | this.c[g].update([d, this.N++, 2, b, f, a.length].concat(a)); 996 | } 997 | break; 998 | case "string": 999 | void 0 === b && (b = a.length); 1000 | this.c[g].update([d, this.N++, 3, b, f, a.length]); 1001 | this.c[g].update(a); 1002 | break; 1003 | default: 1004 | k = 1; 1005 | } 1006 | if (k) 1007 | throw new sjcl.exception.bug( 1008 | "random: addEntropy only supports number, array of numbers or string" 1009 | ); 1010 | this.m[g] += b; 1011 | this.f += b; 1012 | h === this.u && 1013 | (this.isReady() !== this.u && A("seeded", Math.max(this.o, this.f)), 1014 | A("progress", this.getProgress())); 1015 | }, 1016 | isReady: function (a) { 1017 | a = this.T[void 0 !== a ? a : this.M]; 1018 | return this.o && this.o >= a 1019 | ? this.m[0] > this.ba && new Date().valueOf() > this.Z 1020 | ? this.J | this.I 1021 | : this.I 1022 | : this.f >= a 1023 | ? this.J | this.u 1024 | : this.u; 1025 | }, 1026 | getProgress: function (a) { 1027 | a = this.T[a ? a : this.M]; 1028 | return this.o >= a ? 1 : this.f > a ? 1 : this.f / a; 1029 | }, 1030 | startCollectors: function () { 1031 | if (!this.D) { 1032 | this.a = { 1033 | loadTimeCollector: B(this, this.ma), 1034 | mouseCollector: B(this, this.oa), 1035 | keyboardCollector: B(this, this.la), 1036 | accelerometerCollector: B(this, this.ea), 1037 | touchCollector: B(this, this.qa), 1038 | }; 1039 | if (window.addEventListener) 1040 | window.addEventListener("load", this.a.loadTimeCollector, !1), 1041 | window.addEventListener("mousemove", this.a.mouseCollector, !1), 1042 | window.addEventListener("keypress", this.a.keyboardCollector, !1), 1043 | window.addEventListener( 1044 | "devicemotion", 1045 | this.a.accelerometerCollector, 1046 | !1 1047 | ), 1048 | window.addEventListener("touchmove", this.a.touchCollector, !1); 1049 | else if (document.attachEvent) 1050 | document.attachEvent("onload", this.a.loadTimeCollector), 1051 | document.attachEvent("onmousemove", this.a.mouseCollector), 1052 | document.attachEvent("keypress", this.a.keyboardCollector); 1053 | else throw new sjcl.exception.bug("can't attach event"); 1054 | this.D = !0; 1055 | } 1056 | }, 1057 | stopCollectors: function () { 1058 | this.D && 1059 | (window.removeEventListener 1060 | ? (window.removeEventListener("load", this.a.loadTimeCollector, !1), 1061 | window.removeEventListener("mousemove", this.a.mouseCollector, !1), 1062 | window.removeEventListener("keypress", this.a.keyboardCollector, !1), 1063 | window.removeEventListener( 1064 | "devicemotion", 1065 | this.a.accelerometerCollector, 1066 | !1 1067 | ), 1068 | window.removeEventListener("touchmove", this.a.touchCollector, !1)) 1069 | : document.detachEvent && 1070 | (document.detachEvent("onload", this.a.loadTimeCollector), 1071 | document.detachEvent("onmousemove", this.a.mouseCollector), 1072 | document.detachEvent("keypress", this.a.keyboardCollector)), 1073 | (this.D = !1)); 1074 | }, 1075 | addEventListener: function (a, b) { 1076 | this.K[a][this.ga++] = b; 1077 | }, 1078 | removeEventListener: function (a, b) { 1079 | var c, 1080 | d, 1081 | e = this.K[a], 1082 | f = []; 1083 | for (d in e) e.hasOwnProperty(d) && e[d] === b && f.push(d); 1084 | for (c = 0; c < f.length; c++) (d = f[c]), delete e[d]; 1085 | }, 1086 | la: function () { 1087 | C(this, 1); 1088 | }, 1089 | oa: function (a) { 1090 | var b, c; 1091 | try { 1092 | (b = a.x || a.clientX || a.offsetX || 0), 1093 | (c = a.y || a.clientY || a.offsetY || 0); 1094 | } catch (d) { 1095 | c = b = 0; 1096 | } 1097 | 0 != b && 0 != c && this.addEntropy([b, c], 2, "mouse"); 1098 | C(this, 0); 1099 | }, 1100 | qa: function (a) { 1101 | a = a.touches[0] || a.changedTouches[0]; 1102 | this.addEntropy([a.pageX || a.clientX, a.pageY || a.clientY], 1, "touch"); 1103 | C(this, 0); 1104 | }, 1105 | ma: function () { 1106 | C(this, 2); 1107 | }, 1108 | ea: function (a) { 1109 | a = 1110 | a.accelerationIncludingGravity.x || 1111 | a.accelerationIncludingGravity.y || 1112 | a.accelerationIncludingGravity.z; 1113 | if (window.orientation) { 1114 | var b = window.orientation; 1115 | "number" === typeof b && this.addEntropy(b, 1, "accelerometer"); 1116 | } 1117 | a && this.addEntropy(a, 2, "accelerometer"); 1118 | C(this, 0); 1119 | }, 1120 | }; 1121 | function A(a, b) { 1122 | var c, 1123 | d = sjcl.random.K[a], 1124 | e = []; 1125 | for (c in d) d.hasOwnProperty(c) && e.push(d[c]); 1126 | for (c = 0; c < e.length; c++) e[c](b); 1127 | } 1128 | function C(a, b) { 1129 | "undefined" !== typeof window && 1130 | window.performance && 1131 | "function" === typeof window.performance.now 1132 | ? a.addEntropy(window.performance.now(), b, "loadtime") 1133 | : a.addEntropy(new Date().valueOf(), b, "loadtime"); 1134 | } 1135 | function y(a) { 1136 | a.b = z(a).concat(z(a)); 1137 | a.L = new sjcl.cipher.aes(a.b); 1138 | } 1139 | function z(a) { 1140 | for (var b = 0; 4 > b && ((a.h[b] = (a.h[b] + 1) | 0), !a.h[b]); b++); 1141 | return a.L.encrypt(a.h); 1142 | } 1143 | function B(a, b) { 1144 | return function () { 1145 | b.apply(a, arguments); 1146 | }; 1147 | } 1148 | sjcl.random = new sjcl.prng(6); 1149 | a: try { 1150 | var D, E, F, G; 1151 | if ((G = "undefined" !== typeof module && module.exports)) { 1152 | var H; 1153 | try { 1154 | H = require("crypto"); 1155 | } catch (a) { 1156 | H = null; 1157 | } 1158 | G = E = H; 1159 | } 1160 | if (G && E.randomBytes) 1161 | (D = E.randomBytes(128)), 1162 | (D = new Uint32Array(new Uint8Array(D).buffer)), 1163 | sjcl.random.addEntropy(D, 1024, "crypto['randomBytes']"); 1164 | else if ( 1165 | "undefined" !== typeof window && 1166 | "undefined" !== typeof Uint32Array 1167 | ) { 1168 | F = new Uint32Array(32); 1169 | if (window.crypto && window.crypto.getRandomValues) 1170 | window.crypto.getRandomValues(F); 1171 | else if (window.msCrypto && window.msCrypto.getRandomValues) 1172 | window.msCrypto.getRandomValues(F); 1173 | else break a; 1174 | sjcl.random.addEntropy(F, 1024, "crypto['getRandomValues']"); 1175 | } 1176 | } catch (a) { 1177 | "undefined" !== typeof window && 1178 | window.console && 1179 | (console.log("There was an error collecting entropy from the browser:"), 1180 | console.log(a)); 1181 | } 1182 | sjcl.json = { 1183 | defaults: { 1184 | v: 1, 1185 | iter: 1e4, 1186 | ks: 128, 1187 | ts: 64, 1188 | mode: "ccm", 1189 | adata: "", 1190 | cipher: "aes", 1191 | }, 1192 | ja: function (a, b, c, d) { 1193 | c = c || {}; 1194 | d = d || {}; 1195 | var e = sjcl.json, 1196 | f = e.g({ iv: sjcl.random.randomWords(4, 0) }, e.defaults), 1197 | g; 1198 | e.g(f, c); 1199 | c = f.adata; 1200 | "string" === typeof f.salt && (f.salt = sjcl.codec.base64.toBits(f.salt)); 1201 | "string" === typeof f.iv && (f.iv = sjcl.codec.base64.toBits(f.iv)); 1202 | if ( 1203 | !sjcl.mode[f.mode] || 1204 | !sjcl.cipher[f.cipher] || 1205 | ("string" === typeof a && 100 >= f.iter) || 1206 | (64 !== f.ts && 96 !== f.ts && 128 !== f.ts) || 1207 | (128 !== f.ks && 192 !== f.ks && 0x100 !== f.ks) || 1208 | 2 > f.iv.length || 1209 | 4 < f.iv.length 1210 | ) 1211 | throw new sjcl.exception.invalid("json encrypt: invalid parameters"); 1212 | "string" === typeof a 1213 | ? ((g = sjcl.misc.cachedPbkdf2(a, f)), 1214 | (a = g.key.slice(0, f.ks / 32)), 1215 | (f.salt = g.salt)) 1216 | : sjcl.ecc && 1217 | a instanceof sjcl.ecc.elGamal.publicKey && 1218 | ((g = a.kem()), (f.kemtag = g.tag), (a = g.key.slice(0, f.ks / 32))); 1219 | "string" === typeof b && (b = sjcl.codec.utf8String.toBits(b)); 1220 | "string" === typeof c && (f.adata = c = sjcl.codec.utf8String.toBits(c)); 1221 | g = new sjcl.cipher[f.cipher](a); 1222 | e.g(d, f); 1223 | d.key = a; 1224 | f.ct = 1225 | "ccm" === f.mode && 1226 | sjcl.arrayBuffer && 1227 | sjcl.arrayBuffer.ccm && 1228 | b instanceof ArrayBuffer 1229 | ? sjcl.arrayBuffer.ccm.encrypt(g, b, f.iv, c, f.ts) 1230 | : sjcl.mode[f.mode].encrypt(g, b, f.iv, c, f.ts); 1231 | return f; 1232 | }, 1233 | encrypt: function (a, b, c, d) { 1234 | var e = sjcl.json, 1235 | f = e.ja.apply(e, arguments); 1236 | return e.encode(f); 1237 | }, 1238 | ia: function (a, b, c, d) { 1239 | c = c || {}; 1240 | d = d || {}; 1241 | var e = sjcl.json; 1242 | b = e.g(e.g(e.g({}, e.defaults), b), c, !0); 1243 | var f, g; 1244 | f = b.adata; 1245 | "string" === typeof b.salt && (b.salt = sjcl.codec.base64.toBits(b.salt)); 1246 | "string" === typeof b.iv && (b.iv = sjcl.codec.base64.toBits(b.iv)); 1247 | if ( 1248 | !sjcl.mode[b.mode] || 1249 | !sjcl.cipher[b.cipher] || 1250 | ("string" === typeof a && 100 >= b.iter) || 1251 | (64 !== b.ts && 96 !== b.ts && 128 !== b.ts) || 1252 | (128 !== b.ks && 192 !== b.ks && 0x100 !== b.ks) || 1253 | !b.iv || 1254 | 2 > b.iv.length || 1255 | 4 < b.iv.length 1256 | ) 1257 | throw new sjcl.exception.invalid("json decrypt: invalid parameters"); 1258 | "string" === typeof a 1259 | ? ((g = sjcl.misc.cachedPbkdf2(a, b)), 1260 | (a = g.key.slice(0, b.ks / 32)), 1261 | (b.salt = g.salt)) 1262 | : sjcl.ecc && 1263 | a instanceof sjcl.ecc.elGamal.secretKey && 1264 | (a = a.unkem(sjcl.codec.base64.toBits(b.kemtag)).slice(0, b.ks / 32)); 1265 | "string" === typeof f && (f = sjcl.codec.utf8String.toBits(f)); 1266 | g = new sjcl.cipher[b.cipher](a); 1267 | f = 1268 | "ccm" === b.mode && 1269 | sjcl.arrayBuffer && 1270 | sjcl.arrayBuffer.ccm && 1271 | b.ct instanceof ArrayBuffer 1272 | ? sjcl.arrayBuffer.ccm.decrypt(g, b.ct, b.iv, b.tag, f, b.ts) 1273 | : sjcl.mode[b.mode].decrypt(g, b.ct, b.iv, f, b.ts); 1274 | e.g(d, b); 1275 | d.key = a; 1276 | return 1 === c.raw ? f : sjcl.codec.utf8String.fromBits(f); 1277 | }, 1278 | decrypt: function (a, b, c, d) { 1279 | var e = sjcl.json; 1280 | return e.ia(a, e.decode(b), c, d); 1281 | }, 1282 | encode: function (a) { 1283 | var b, 1284 | c = "{", 1285 | d = ""; 1286 | for (b in a) 1287 | if (a.hasOwnProperty(b)) { 1288 | if (!b.match(/^[a-z0-9]+$/i)) 1289 | throw new sjcl.exception.invalid( 1290 | "json encode: invalid property name" 1291 | ); 1292 | c += d + '"' + b + '":'; 1293 | d = ","; 1294 | switch (typeof a[b]) { 1295 | case "number": 1296 | case "boolean": 1297 | c += a[b]; 1298 | break; 1299 | case "string": 1300 | c += '"' + escape(a[b]) + '"'; 1301 | break; 1302 | case "object": 1303 | c += '"' + sjcl.codec.base64.fromBits(a[b], 0) + '"'; 1304 | break; 1305 | default: 1306 | throw new sjcl.exception.bug("json encode: unsupported type"); 1307 | } 1308 | } 1309 | return c + "}"; 1310 | }, 1311 | decode: function (a) { 1312 | a = a.replace(/\s/g, ""); 1313 | if (!a.match(/^\{.*\}$/)) 1314 | throw new sjcl.exception.invalid("json decode: this isn't json!"); 1315 | a = a.replace(/^\{|\}$/g, "").split(/,/); 1316 | var b = {}, 1317 | c, 1318 | d; 1319 | for (c = 0; c < a.length; c++) { 1320 | if ( 1321 | !(d = a[c].match( 1322 | /^\s*(?:(["']?)([a-z][a-z0-9]*)\1)\s*:\s*(?:(-?\d+)|"([a-z0-9+\/%*_.@=\-]*)"|(true|false))$/i 1323 | )) 1324 | ) 1325 | throw new sjcl.exception.invalid("json decode: this isn't json!"); 1326 | null != d[3] 1327 | ? (b[d[2]] = parseInt(d[3], 10)) 1328 | : null != d[4] 1329 | ? (b[d[2]] = d[2].match(/^(ct|adata|salt|iv)$/) 1330 | ? sjcl.codec.base64.toBits(d[4]) 1331 | : unescape(d[4])) 1332 | : null != d[5] && (b[d[2]] = "true" === d[5]); 1333 | } 1334 | return b; 1335 | }, 1336 | g: function (a, b, c) { 1337 | void 0 === a && (a = {}); 1338 | if (void 0 === b) return a; 1339 | for (var d in b) 1340 | if (b.hasOwnProperty(d)) { 1341 | if (c && void 0 !== a[d] && a[d] !== b[d]) 1342 | throw new sjcl.exception.invalid("required parameter overridden"); 1343 | a[d] = b[d]; 1344 | } 1345 | return a; 1346 | }, 1347 | sa: function (a, b) { 1348 | var c = {}, 1349 | d; 1350 | for (d in a) a.hasOwnProperty(d) && a[d] !== b[d] && (c[d] = a[d]); 1351 | return c; 1352 | }, 1353 | ra: function (a, b) { 1354 | var c = {}, 1355 | d; 1356 | for (d = 0; d < b.length; d++) void 0 !== a[b[d]] && (c[b[d]] = a[b[d]]); 1357 | return c; 1358 | }, 1359 | }; 1360 | sjcl.encrypt = sjcl.json.encrypt; 1361 | sjcl.decrypt = sjcl.json.decrypt; 1362 | sjcl.misc.pa = {}; 1363 | sjcl.misc.cachedPbkdf2 = function (a, b) { 1364 | var c = sjcl.misc.pa, 1365 | d; 1366 | b = b || {}; 1367 | d = b.iter || 1e3; 1368 | c = c[a] = c[a] || {}; 1369 | d = c[d] = c[d] || { 1370 | firstSalt: 1371 | b.salt && b.salt.length ? b.salt.slice(0) : sjcl.random.randomWords(2, 0), 1372 | }; 1373 | c = void 0 === b.salt ? d.firstSalt : b.salt; 1374 | d[c] = d[c] || sjcl.misc.pbkdf2(a, c, b.iter); 1375 | return { key: d[c].slice(0), salt: c.slice(0) }; 1376 | }; 1377 | "undefined" !== typeof module && module.exports && (module.exports = sjcl); 1378 | "function" === typeof define && 1379 | define([], function () { 1380 | return sjcl; 1381 | }); 1382 | 1383 | export { sjcl }; -------------------------------------------------------------------------------- /src/lib/zabbix-promise.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * Class representing Zabbix API client 5 | */ 6 | 7 | var _createClass = (function () { 8 | function defineProperties(target, props) { 9 | for (var i = 0; i < props.length; i++) { 10 | var descriptor = props[i]; 11 | descriptor.enumerable = descriptor.enumerable || false; 12 | descriptor.configurable = true; 13 | if ("value" in descriptor) descriptor.writable = true; 14 | Object.defineProperty(target, descriptor.key, descriptor); 15 | } 16 | } 17 | return function (Constructor, protoProps, staticProps) { 18 | if (protoProps) defineProperties(Constructor.prototype, protoProps); 19 | if (staticProps) defineProperties(Constructor, staticProps); 20 | return Constructor; 21 | }; 22 | })(); 23 | 24 | function _classCallCheck(instance, Constructor) { 25 | if (!(instance instanceof Constructor)) { 26 | throw new TypeError("Cannot call a class as a function"); 27 | } 28 | } 29 | 30 | export var Zabbix = (function () { 31 | /** 32 | * Create Zabbix API client. 33 | * @param {string} url - Zabbix API url e.g. http://localhost/zabbix/api_jsonrpc.php 34 | * @param {string} user - Zabbix user name 35 | * @param {string} password - Zabbix user password 36 | */ 37 | 38 | function Zabbix(url, user, password, apiToken, version) { 39 | _classCallCheck(this, Zabbix); 40 | 41 | this.url = url; 42 | this.user = user; 43 | this.password = password; 44 | this.apiToken = apiToken; 45 | this.version = version; 46 | } 47 | 48 | /** 49 | * Call Zabbix API method. 50 | * @param {string} method - Zabbix API method like "trigger.get", "host.create" 51 | * @param {object} params - params object like {filter: {host: ["Zabbix server"]}} 52 | * @return {Promise} Promise object 53 | */ 54 | 55 | _createClass(Zabbix, [ 56 | { 57 | key: "call", 58 | value: function call(method, params) { 59 | //console.log("ZABLIB method: " + JSON.stringify(method) + " Params: " + JSON.stringify(params)) 60 | var request = { 61 | jsonrpc: "2.0", 62 | id: "1", 63 | method: method, 64 | params: params, 65 | }; 66 | if (this.version) { 67 | let versionComp = this.version.split('.').slice(0,2).join('.') 68 | if (versionComp && versionComp < 7.0) { 69 | request["auth"] = this.auth; 70 | } 71 | } 72 | //console.log("ZABLIB request: " + JSON.stringify(request)) 73 | return this._postJsonRpc(this.url, JSON.stringify(request)); 74 | }, 75 | 76 | /** 77 | * Log in Zabbix server. 78 | * @return {Promise} Promise object 79 | */ 80 | }, 81 | { 82 | key: "login", 83 | value: function login() { 84 | var _this = this; 85 | 86 | // make login noop if using an api key 87 | if (this.apiToken) { 88 | _this.auth = this.apiToken; 89 | //console.log("ZABLIB got apiToken SKIPPING LOGIN") 90 | return Promise.resolve(); 91 | } 92 | 93 | var params = { 94 | password: this.password, 95 | }; 96 | _this.auth = undefined; 97 | let versionComp = this.version.split('.').slice(0,2).join('.') 98 | // API pre 6.0 needs user, 6.0+ username 99 | if (versionComp && versionComp < 6.0) { 100 | params["user"] = this.user; 101 | } else { 102 | params["username"] = this.user; 103 | } 104 | return this.call("user.login", params).then(function (reply) { 105 | _this.auth = reply.result; 106 | if (_this.auth === undefined) { 107 | throw new Error(JSON.stringify(reply.error)); 108 | } 109 | return reply; 110 | }); 111 | }, 112 | 113 | /** 114 | * Log out from Zabbix server. 115 | * @return {Promise} Promise object 116 | */ 117 | }, 118 | { 119 | key: "logout", 120 | value: function logout() { 121 | var _this2 = this; 122 | 123 | // if using an api Token, ignore logout 124 | if (this.apiToken) { 125 | //console.log("ZABLIB Using API Key. Skipping logout.") 126 | return Promise.resolve(); 127 | } 128 | return this.call("user.logout", []).then(function (reply) { 129 | if (reply.result !== true) { 130 | throw new Error(JSON.stringify(reply.error)); 131 | } 132 | 133 | _this2.auth = undefined; 134 | return reply; 135 | }); 136 | }, 137 | }, 138 | { 139 | key: "_postJsonRpc", 140 | value: async function _postJsonRpc(url, data) { 141 | const myHeaders = new Headers(); 142 | 143 | var _this3 = this; 144 | if (_this3.auth) { 145 | // API after 7.0 removes auth object 146 | myHeaders.append("Authorization", "Bearer " + _this3.auth) 147 | } 148 | myHeaders.append("Content-Type", "application/json-rpc"); 149 | 150 | try { 151 | const response = await fetch(url, { 152 | method: "POST", 153 | body: data, 154 | headers: myHeaders, 155 | }); 156 | if (!response.ok) { 157 | throw new Error(`Response status: ${response.status}`); 158 | } 159 | const zabResponse = await response.json() 160 | //console.log("ZABLIB response: " + JSON.stringify(zabResponse)) 161 | return zabResponse; 162 | } catch { 163 | throw new Error('Failed to communicate with server'); 164 | } 165 | }, 166 | }, 167 | ]); 168 | 169 | return Zabbix; 170 | })(); -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "__MSG_appName__", 3 | "{{version}}": "", 4 | "manifest_version": 3, 5 | "description": "__MSG_appDescription__", 6 | "author": "https://github.com/mchugh19/zabbix-vue", 7 | "icons": { 8 | "128": "images/logo-78b982a7.png" 9 | }, 10 | "default_locale": "en", 11 | "options_ui": { 12 | "page": "options/options.html", 13 | "{{firefox}}.open_in_tab": true 14 | }, 15 | "action": { 16 | "default_icon": { 17 | "128": "images/unconfigured-cf9f2b44.png" 18 | }, 19 | "default_popup": "popup/popup.html" 20 | }, 21 | "background": { 22 | "{{chrome}}.service_worker": "background.js", 23 | "{{firefox}}.scripts": ["background.js"] 24 | }, 25 | "{{chrome}}.permissions": [ 26 | "alarms", 27 | "notifications", 28 | "storage", 29 | "offscreen" 30 | ], 31 | "host_permissions": [ 32 | "" 33 | ], 34 | "content_security_policy": { 35 | "extension_pages": "script-src 'self'; object-src 'self'" 36 | }, 37 | "{{firefox}}.permissions": [ 38 | "alarms", 39 | "notifications", 40 | "storage" 41 | ], 42 | "{{firefox}}.browser_specific_settings": { 43 | "gecko": { 44 | "id": "{493da8e7-8299-4edd-9895-99452a03de66}" 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /src/options/options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Options 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/options/options.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue'; 2 | import options from "./options.vue"; 3 | import i18n from "vue-plugin-webextension-i18n"; 4 | import "vuetify/styles"; 5 | import { createVuetify } from 'vuetify'; 6 | import * as components from 'vuetify/components' 7 | import * as directives from 'vuetify/directives' 8 | import { aliases, mdi } from 'vuetify/iconsets/mdi-svg' 9 | 10 | 11 | 12 | const vuetify = createVuetify({ 13 | components, 14 | directives, 15 | icons: { 16 | defaultSet: 'mdi', 17 | aliases, 18 | sets: { 19 | mdi, 20 | }, 21 | }, 22 | }); 23 | 24 | // Create Vue application 25 | const app = createApp(options); 26 | 27 | // Use Vuetify and i18n plugins 28 | app.use(vuetify); 29 | app.use(i18n); 30 | 31 | // Mount the app 32 | app.mount('body'); 33 | -------------------------------------------------------------------------------- /src/options/options.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{ $i18n("serverSettings") }} 9 | 10 | 18 | 19 | 20 | {{ $i18n("server") }}: {{ server.alias }} 21 | 22 | 23 | 28 | 29 | 30 | 36 | 45 | 46 | 51 | 52 | 59 | 60 | 67 | 75 | 76 | 80 | 84 | 85 | 86 | 87 | 92 | 97 | 104 | 105 | 106 | 107 | 118 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | {{ $i18n("addServer") }} 139 | 140 | 141 | 142 | 143 | 144 | 145 | {{ $i18n("generalSettings") }} 146 | 148 | 155 | 160 | 165 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | {{ $i18n("save") }} 177 | 178 | 179 | 180 | 181 | 182 | 183 | 186 | 187 | 382 | 383 | 384 | -------------------------------------------------------------------------------- /src/popup/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Popup 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/popup/popup.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue'; 2 | import popup from "./popup.vue"; 3 | import i18n from "vue-plugin-webextension-i18n"; 4 | import "vuetify/styles"; 5 | import { createVuetify } from 'vuetify'; 6 | import * as components from 'vuetify/components' 7 | import * as directives from 'vuetify/directives' 8 | import { aliases, mdi } from 'vuetify/iconsets/mdi-svg' 9 | 10 | 11 | const vuetify = createVuetify({ 12 | components, 13 | directives, 14 | icons: { 15 | defaultSet: 'mdi', 16 | aliases, 17 | sets: { 18 | mdi, 19 | }, 20 | }, 21 | }); 22 | 23 | // Create Vue application 24 | const app = createApp(popup); 25 | 26 | // Use Vuetify and i18n plugins 27 | app.use(vuetify); 28 | app.use(i18n); 29 | 30 | // Mount the app 31 | app.mount("body"); 32 | -------------------------------------------------------------------------------- /src/popup/popup.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | {{ $i18n("checkConfig") }}: 10 | {{ triggerTableData.errorMessage }} 11 | 12 | 13 | 18 | 19 | 23 | 27 | {{ serverObj.server }} 28 | 29 | 34 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 66 | 67 | 68 | {{ item.system }} 69 | 70 | 71 | 72 | {{ item.description }} 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | {{ priority_name_filter(item.priority) }} 81 | {{ date_filter(item.age) }} 82 | 83 | 84 | 85 | 86 | 87 | 95 | {{ $i18n("hostDetails") }} 96 | 97 | 103 | {{ $i18n("latestData") }} 104 | 105 | 111 | {{ $i18n("hostGraphs") }} 112 | 113 | 121 | {{ $i18n("hostDashboards") }} 122 | 123 | 131 | {{ $i18n("problemDetails") }} 132 | 133 | 146 | {{ $i18n("eventDetails") }} 147 | 148 | 161 | {{ $i18n("ackEvent") }} 162 | 163 | 164 | 165 | 166 | 167 | 173 | {{ $i18n("noProb") }} 174 | 175 | 180 | {{ $i18n("noResults") }}: {{ serverObj.search }} 181 | 182 | 188 | {{ serverObj.errorMessage }} 189 | {{ serverObj.errorDetails }} 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 200 | 201 | 417 | 483 | -------------------------------------------------------------------------------- /src/public/_locales/en/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "appName": { 3 | "message": "Zabbix Vue", 4 | "description": "Extension name" 5 | }, 6 | "appDescription": { 7 | "message": "Monitor Zabbix problems from your browser", 8 | "description": "Description in extension store" 9 | }, 10 | 11 | "notClassified": { 12 | "message": "Not Classified", 13 | "description": "Zabbix severity" 14 | }, 15 | "information": { 16 | "message": "Information", 17 | "description": "Zabbix severity" 18 | }, 19 | "warning": { 20 | "message": "Warning", 21 | "description": "Zabbix severity" 22 | }, 23 | "average": { 24 | "message": "Average", 25 | "description": "Zabbix severity" 26 | }, 27 | "high": { 28 | "message": "High", 29 | "description": "Zabbix severity" 30 | }, 31 | "disaster": { 32 | "message": "Disaster", 33 | "description": "Zabbix severity" 34 | }, 35 | 36 | 37 | "serverSettings": { 38 | "message": "Server Settings", 39 | "description": "Options screen label" 40 | }, 41 | "server": { 42 | "message": "Server", 43 | "description": "Options screen label" 44 | }, 45 | "alias": { 46 | "message": "Alias", 47 | "description": "Options screen label" 48 | }, 49 | "URL": { 50 | "message": "URL", 51 | "description": "Options screen label" 52 | }, 53 | "useToken": { 54 | "message": "Use Token auth", 55 | "description": "Options screen label" 56 | }, 57 | "zabbixToken": { 58 | "message": "Zabbix API Token", 59 | "description": "Options screen label" 60 | }, 61 | "zabbixUser": { 62 | "message": "Zabbix User", 63 | "description": "Options screen label" 64 | }, 65 | "zabbixPass": { 66 | "message": "Zabbix Password", 67 | "description": "Options screen label" 68 | }, 69 | "hideEvents": { 70 | "message": "Hide Acknowledged Events", 71 | "description": "Options screen label" 72 | }, 73 | "ignoreHosts": { 74 | "message": "Ignore Hosts in Maintenance", 75 | "description": "Options screen label" 76 | }, 77 | "minSev": { 78 | "message": "Monitor Minimum Severity", 79 | "description": "Options screen label" 80 | }, 81 | "monGroups": { 82 | "message": "Monitor Hostgroups", 83 | "description": "Options screen label" 84 | }, 85 | "generalSettings": { 86 | "message": "General Settings", 87 | "description": "Options screen label" 88 | }, 89 | "updateInterval": { 90 | "message": "Update Interval", 91 | "description": "Options screen label" 92 | }, 93 | "notify": { 94 | "message": "Display Popup Notifications", 95 | "description": "Options screen label" 96 | }, 97 | "sound": { 98 | "message": "Play audio notifications", 99 | "description": "Options screen label" 100 | }, 101 | "selectName": { 102 | "message": "Display name in popup", 103 | "description": "Options screen label" 104 | }, 105 | "displayHost": { 106 | "message": "Host name", 107 | "description": "Options screen label" 108 | }, 109 | "displayName": { 110 | "message": "Visible name", 111 | "description": "Options screen label" 112 | }, 113 | "save": { 114 | "message": "Save", 115 | "description": "Options screen button" 116 | }, 117 | "addServer": { 118 | "message": "ADD SERVER", 119 | "description": "Options screen button" 120 | }, 121 | 122 | "required": { 123 | "message": "Item is required", 124 | "description": "Options screen validation message" 125 | }, 126 | "requiredURL": { 127 | "message": "Valid URL is required", 128 | "description": "Options screen validation message" 129 | }, 130 | "lessSeconds": { 131 | "message": "Don't check more frequently than every 10 seconds", 132 | "description": "Options screen validation message" 133 | }, 134 | "serverError": { 135 | "message": "Error contacting server", 136 | "description": "Generic error message" 137 | }, 138 | "checkConfig": { 139 | "message": "Check server configuration in extension options", 140 | "description": "Configuration error message" 141 | }, 142 | 143 | "headerSystem": { 144 | "message": "System", 145 | "description": "Popup headers" 146 | }, 147 | "headerDescription": { 148 | "message": "Description", 149 | "description": "Popup headers" 150 | }, 151 | "headerPriority": { 152 | "message": "Priority", 153 | "description": "Popup headers" 154 | }, 155 | "headerAge": { 156 | "message": "Age", 157 | "description": "Popup headers" 158 | }, 159 | 160 | "hostDetails": { 161 | "message": "Host Details", 162 | "description": "Popup button" 163 | }, 164 | "latestData": { 165 | "message": "Latest Data", 166 | "description": "Popup button" 167 | }, 168 | "hostGraphs": { 169 | "message": "Host Graphs", 170 | "description": "Popup button" 171 | }, 172 | "hostDashboards": { 173 | "message": "Host Dashboards", 174 | "description": "Popup button" 175 | }, 176 | "problemDetails": { 177 | "message": "Problem Details", 178 | "description": "Popup button" 179 | }, 180 | "eventDetails": { 181 | "message": "Event Details", 182 | "description": "Popup button" 183 | }, 184 | "ackEvent": { 185 | "message": "Ack Event", 186 | "description": "Popup button" 187 | }, 188 | "filter": { 189 | "message": "Filter", 190 | "description": "Popup label" 191 | }, 192 | "noProb": { 193 | "message": "No problems", 194 | "description": "Popup message" 195 | }, 196 | "noResults": { 197 | "message": "No results found for", 198 | "description": "Popup message" 199 | }, 200 | "noServers": { 201 | "message": "No servers defined", 202 | "description": "Popup error" 203 | }, 204 | "error": { 205 | "message": "Plugin Error", 206 | "description": "Popup error" 207 | } 208 | 209 | } 210 | -------------------------------------------------------------------------------- /src/public/_locales/nl/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "appName": { 3 | "message": "Zabbix Vue", 4 | "description": "Monitor Zabbix problemen vanuit je browser" 5 | }, 6 | "appDescription": { 7 | "message": "Zabbix Vue", 8 | "description": "Monitor Zabbix problemen vanuit je browser" 9 | }, 10 | 11 | "notClassified": { 12 | "message": "Niet geclassificeerd", 13 | "description": "Zabbix ernst" 14 | }, 15 | "information": { 16 | "message": "Informatie", 17 | "description": "Zabbix ernst" 18 | }, 19 | "warning": { 20 | "message": "Waarschuwing", 21 | "description": "Zabbix ernst" 22 | }, 23 | "average": { 24 | "message": "Gemiddeld", 25 | "description": "Zabbix ernst" 26 | }, 27 | "high": { 28 | "message": "Hoog", 29 | "description": "Zabbix ernst" 30 | }, 31 | "disaster": { 32 | "message": "Ramp", 33 | "description": "Zabbix ernst" 34 | }, 35 | 36 | 37 | "serverSettings": { 38 | "message": "Server Instellingen", 39 | "description": "Opties scherm label" 40 | }, 41 | "server": { 42 | "message": "Server", 43 | "description": "Opties scherm label" 44 | }, 45 | "alias": { 46 | "message": "Alias", 47 | "description": "Opties scherm label" 48 | }, 49 | "URL": { 50 | "message": "URL", 51 | "description": "Opties scherm label" 52 | }, 53 | "zabbixUser": { 54 | "message": "Zabbix Gebruiker", 55 | "description": "Opties scherm label" 56 | }, 57 | "zabbixPass": { 58 | "message": "Zabbix Wachtwoord", 59 | "description": "Opties scherm label" 60 | }, 61 | "hideEvents": { 62 | "message": "Verberg bevestigde Events", 63 | "description": "Opties scherm label" 64 | }, 65 | "ignoreHosts": { 66 | "message": "Negeer Hosts in Onderhoud", 67 | "description": "Opties scherm label" 68 | }, 69 | "minSev": { 70 | "message": "Monitor Minimum Ernst", 71 | "description": "Opties scherm label" 72 | }, 73 | "monGroups": { 74 | "message": "Monitor Hostgroups", 75 | "description": "Opties scherm label" 76 | }, 77 | "generalSettings": { 78 | "message": "Algemene Instellingen", 79 | "description": "Opties scherm label" 80 | }, 81 | "updateInterval": { 82 | "message": "Update Interval", 83 | "description": "Opties scherm label" 84 | }, 85 | "notify": { 86 | "message": "Scherm Popup Notificaties", 87 | "description": "Opties scherm label" 88 | }, 89 | "selectName": { 90 | "message": "Scherm naam in popup", 91 | "description": "Opties scherm label" 92 | }, 93 | "displayHost": { 94 | "message": "Host naam", 95 | "description": "Opties scherm label" 96 | }, 97 | "displayName": { 98 | "message": "Zichtbare naam", 99 | "description": "Opties scherm label" 100 | }, 101 | "save": { 102 | "message": "Opslaan", 103 | "description": "Opties scherm knop" 104 | }, 105 | "addServer": { 106 | "message": "SERVER TOEVOEGEN", 107 | "description": "Opties scherm knop" 108 | }, 109 | 110 | "required": { 111 | "message": "Item is verplicht", 112 | "description": "Opties scherm validatie bericht" 113 | }, 114 | "requiredURL": { 115 | "message": "Valide URL is verplicht", 116 | "description": "Opties scherm validatie bericht" 117 | }, 118 | "lessSeconds": { 119 | "message": "Controleer niet met een frequentie van minder dan 10 seconden", 120 | "description": "Opties scherm validatie bericht" 121 | }, 122 | "serverError": { 123 | "message": "Fout bij het bereiken van de server", 124 | "description": "Algemene foutmelding" 125 | }, 126 | "checkConfig": { 127 | "message": "Controleer de serverconfiguratie in extensie opties", 128 | "description": "Configuratie foutmelding" 129 | }, 130 | 131 | "headerSystem": { 132 | "message": "Systeem", 133 | "description": "Popup headers" 134 | }, 135 | "headerDescription": { 136 | "message": "Omschrijving", 137 | "description": "Popup headers" 138 | }, 139 | "headerPriority": { 140 | "message": "Prioriteit", 141 | "description": "Popup headers" 142 | }, 143 | "headerAge": { 144 | "message": "Leeftijd", 145 | "description": "Popup headers" 146 | }, 147 | 148 | "hostDetails": { 149 | "message": "Host Details", 150 | "description": "Popup button" 151 | }, 152 | "latestData": { 153 | "message": "Laatste Data", 154 | "description": "Popup button" 155 | }, 156 | "hostGraphs": { 157 | "message": "Host Grafieken", 158 | "description": "Popup button" 159 | }, 160 | "problemDetails": { 161 | "message": "Probleem Details", 162 | "description": "Popup button" 163 | }, 164 | "eventDetails": { 165 | "message": "Event Details", 166 | "description": "Popup button" 167 | }, 168 | "ackEvent": { 169 | "message": "Ack Event", 170 | "description": "Popup button" 171 | }, 172 | "filter": { 173 | "message": "Filter", 174 | "description": "Popup label" 175 | }, 176 | "noProb": { 177 | "message": "Geen problemen", 178 | "description": "Popup message" 179 | }, 180 | "noResults": { 181 | "message": "Geen resultaten gevonden voor", 182 | "description": "Popup message" 183 | }, 184 | "noServers": { 185 | "message": "Geen servers gedefineerd", 186 | "description": "Popup error" 187 | }, 188 | "error": { 189 | "message": "Plugin Error", 190 | "description": "Popup error" 191 | } 192 | 193 | } 194 | -------------------------------------------------------------------------------- /src/public/_locales/pl/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "appName": { 3 | "message": "Zabbix Vue", 4 | "description": "Monitoruje problemy Zabbixa z poziomy przegladarki internetowej" 5 | }, 6 | "appDescription": { 7 | "message": "Zabbix Vue", 8 | "description": "Monitoruje problemy Zabbixa z poziomy przegladarki internetowej" 9 | }, 10 | 11 | "notClassified": { 12 | "message": "Nie sklasyfikowany", 13 | "description": "Powaga ostrzeżnia" 14 | }, 15 | "information": { 16 | "message": "Informacja", 17 | "description": "Powaga ostrzeżnia" 18 | }, 19 | "warning": { 20 | "message": "Ostrzeżnie", 21 | "description": "Powaga ostrzeżnia" 22 | }, 23 | "average": { 24 | "message": "Średni", 25 | "description": "Powaga ostrzeżnia" 26 | }, 27 | "high": { 28 | "message": "Wysoki", 29 | "description": "Powaga ostrzeżnia" 30 | }, 31 | "disaster": { 32 | "message": "Katastrofalny", 33 | "description": "Powaga ostrzeżnia" 34 | }, 35 | 36 | 37 | "serverSettings": { 38 | "message": "Ustawienia Serwera", 39 | "description": "Etykieta na ekranie opcji" 40 | }, 41 | "server": { 42 | "message": "Serwer", 43 | "description": "Etykieta na ekranie opcji" 44 | }, 45 | "alias": { 46 | "message": "Alias", 47 | "description": "Etykieta na ekranie opcji" 48 | }, 49 | "URL": { 50 | "message": "URL", 51 | "description": "Etykieta na ekranie opcji" 52 | }, 53 | "zabbixUser": { 54 | "message": "Urzytkownik", 55 | "description": "Etykieta na ekranie opcji" 56 | }, 57 | "zabbixPass": { 58 | "message": "Hasło", 59 | "description": "Etykieta na ekranie opcji" 60 | }, 61 | "hideEvents": { 62 | "message": "Ukryj potwierdzone zdarzenia", 63 | "description": "Etykieta na ekranie opcji" 64 | }, 65 | "ignoreHosts": { 66 | "message": "Ignoruj hosty w konserwacji", 67 | "description": "Etykieta na ekranie opcji" 68 | }, 69 | "minSev": { 70 | "message": "Monitoruj minimalną powagę", 71 | "description": "Etykieta na ekranie opcji" 72 | }, 73 | "monGroups": { 74 | "message": "Monitoruj grupy hostów", 75 | "description": "Etykieta na ekranie opcji" 76 | }, 77 | "generalSettings": { 78 | "message": "Ustawienia główne", 79 | "description": "Etykieta na ekranie opcji" 80 | }, 81 | "updateInterval": { 82 | "message": "Interwal odswierzeń", 83 | "description": "Etykieta na ekranie opcji" 84 | }, 85 | "notify": { 86 | "message": "Wyświetl przedział powiadomień", 87 | "description": "Etykieta na ekranie opcji" 88 | }, 89 | "selectName": { 90 | "message": "Wyświetl nazwę w powiadomieniu", 91 | "description": "Etykieta na ekranie opcji" 92 | }, 93 | "displayHost": { 94 | "message": "Nazwa Hosta", 95 | "description": "Etykieta na ekranie opcji" 96 | }, 97 | "displayName": { 98 | "message": "Widoczna Nazwa", 99 | "description": "Etykieta na ekranie opcji" 100 | }, 101 | "save": { 102 | "message": "Zapisz", 103 | "description": "Przycisk ekranu opcji" 104 | }, 105 | "addServer": { 106 | "message": "Dodaj Serwer", 107 | "description": "Przycisk ekranu opcji" 108 | }, 109 | 110 | "required": { 111 | "message": "Przedmiot jest wymagany", 112 | "description": "Komunikat o sprawdzeniu ekranu opcji" 113 | }, 114 | "requiredURL": { 115 | "message": "Prawidłowy adres URL jest wymagany", 116 | "description": "Komunikat o sprawdzeniu ekranu opcji" 117 | }, 118 | "lessSeconds": { 119 | "message": "Nie sprawdzaj częściej niż co 10 sekund", 120 | "description": "Komunikat o sprawdzeniu ekranu opcji" 121 | }, 122 | "serverError": { 123 | "message": "Błąd podczas łączenia się z serwerem", 124 | "description": "Ogólny komunikat o błędzie" 125 | }, 126 | "checkConfig": { 127 | "message": "Sprawdź konfigurację serwera w opcjach rozszerzenia", 128 | "description": "Komunikat o błędzie konfiguracji" 129 | }, 130 | 131 | "headerSystem": { 132 | "message": "System", 133 | "description": "Nagłówki Powiadomien" 134 | }, 135 | "headerDescription": { 136 | "message": "Opis", 137 | "description": "Nagłówki Powiadomien" 138 | }, 139 | "headerPriority": { 140 | "message": "Priorytet", 141 | "description": "Nagłówki Powiadomien" 142 | }, 143 | "headerAge": { 144 | "message": "Wiek", 145 | "description": "Nagłówki Powiadomien" 146 | }, 147 | 148 | "hostDetails": { 149 | "message": "Szczegóły Hosta", 150 | "description": "Przycisk Powiadomienia" 151 | }, 152 | "latestData": { 153 | "message": "Najnowsze dane", 154 | "description": "Przycisk Powiadomienia" 155 | }, 156 | "hostGraphs": { 157 | "message": "Wykresy hosta", 158 | "description": "Przycisk Powiadomienia" 159 | }, 160 | "problemDetails": { 161 | "message": "Szczegóły problemu", 162 | "description": "Przycisk Powiadomienia" 163 | }, 164 | "eventDetails": { 165 | "message": "Szczegóły wydarzenia", 166 | "description": "Przycisk Powiadomienia" 167 | }, 168 | "ackEvent": { 169 | "message": "Potwierdź zdarzenie", 170 | "description": "Przycisk Powiadomienia" 171 | }, 172 | "filter": { 173 | "message": "Filter", 174 | "description": "Etykieta Powiadomienia" 175 | }, 176 | "noProb": { 177 | "message": "Brak Problemów", 178 | "description": "Widomosc Powiadomienia" 179 | }, 180 | "noResults": { 181 | "message": "Nie znaleziono wyników dla", 182 | "description": "Widomosc Powiadomienia" 183 | }, 184 | "noServers": { 185 | "message": "Brak zdefiniowanych serwerów", 186 | "description": "Powiadomienie o Błędzie" 187 | }, 188 | "error": { 189 | "message": "Błąd Wtyczki", 190 | "description": "Błąd Wtyczki" 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /src/public/_locales/pt_BR/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "appName": { 3 | "message": "Zabbix Vue", 4 | "description": "Monitor Zabbix problemas no seu browser" 5 | }, 6 | "appDescription": { 7 | "message": "Zabbix Vue", 8 | "description": "Monitor Zabbix problemas no seu browser" 9 | }, 10 | 11 | "notClassified": { 12 | "message": "Nao classificada", 13 | "description": "Severidade Zabbix" 14 | }, 15 | "information": { 16 | "message": "Informacao", 17 | "description": "Severidade Zabbix" 18 | }, 19 | "warning": { 20 | "message": "Aviso", 21 | "description": "Severidade Zabbix" 22 | }, 23 | "average": { 24 | "message": "Media", 25 | "description": "Severidade Zabbix" 26 | }, 27 | "high": { 28 | "message": "Alta", 29 | "description": "Severidade Zabbix" 30 | }, 31 | "disaster": { 32 | "message": "Desastre", 33 | "description": "Severidade Zabbix" 34 | }, 35 | 36 | 37 | "serverSettings": { 38 | "message": "Configuracoes do servidor", 39 | "description": "etiqueta do ecra de opcoes" 40 | }, 41 | "server": { 42 | "message": "Servidor", 43 | "description": "etiqueta do ecra de opcoes" 44 | }, 45 | "alias": { 46 | "message": "Alias", 47 | "description": "etiqueta do ecra de opcoes" 48 | }, 49 | "URL": { 50 | "message": "URL", 51 | "description": "etiqueta do ecra de opcoes" 52 | }, 53 | "zabbixUser": { 54 | "message": "utilizador Zabbix", 55 | "description": "etiqueta do ecra de opcoes" 56 | }, 57 | "zabbixPass": { 58 | "message": "password Zabbix", 59 | "description": "etiqueta do ecra de opcoes" 60 | }, 61 | "hideEvents": { 62 | "message": "esconder eventos ja aceites", 63 | "description": "etiqueta do ecra de opcoes" 64 | }, 65 | "ignoreHosts": { 66 | "message": "ignorar maquinas em mnutencao", 67 | "description": "etiqueta do ecra de opcoes" 68 | }, 69 | "minSev": { 70 | "message": "monitorizar severidade minima", 71 | "description": "etiqueta do ecra de opcoes" 72 | }, 73 | "monGroups": { 74 | "message": "monitorizar grupo de maquinas", 75 | "description": "etiqueta do ecra de opcoes" 76 | }, 77 | "generalSettings": { 78 | "message": "Opcoes gerais", 79 | "description": "etiqueta do ecra de opcoes" 80 | }, 81 | "updateInterval": { 82 | "message": "intervalo de actualizacao", 83 | "description": "etiqueta do ecra de opcoes" 84 | }, 85 | "notify": { 86 | "message": "mostrar motificacoes popup", 87 | "description": "etiqueta do ecra de opcoes" 88 | }, 89 | "selectName": { 90 | "message": "mostrar nome na popup", 91 | "description": "etiqueta do ecra de opcoes" 92 | }, 93 | "displayHost": { 94 | "message": "nome da maquina", 95 | "description": "etiqueta do ecra de opcoes" 96 | }, 97 | "displayName": { 98 | "message": "nome visivel", 99 | "description": "etiqueta do ecra de opcoes" 100 | }, 101 | "save": { 102 | "message": "guardar", 103 | "description": "botao do ecra de opcoes" 104 | }, 105 | "addServer": { 106 | "message": "adicionar servidor", 107 | "description": "botao do ecra de opcoes" 108 | }, 109 | 110 | "required": { 111 | "message": "Item obrigatorio", 112 | "description": "mensagem de validacao do ecra de opcoes" 113 | }, 114 | "requiredURL": { 115 | "message": "URL valido obrigatorio", 116 | "description": "mensagem de validacao do ecra de opcoes" 117 | }, 118 | "lessSeconds": { 119 | "message": "nao verificar com mais frequencia do que 10 segundos", 120 | "description": "mensagem de validacao do ecra de opcoes" 121 | }, 122 | "serverError": { 123 | "message": "erro ao connectar com o servidor", 124 | "description": "mensagem de erro generica" 125 | }, 126 | "checkConfig": { 127 | "message": "verificar as configuracoes do servidor nas opcoes extra", 128 | "description": "mensagem de erro nas configuracoes" 129 | }, 130 | 131 | "headerSystem": { 132 | "message": "Sistema", 133 | "description": "cabecalho popup" 134 | }, 135 | "headerDescription": { 136 | "message": "Descricao", 137 | "description": "cabecalho popup" 138 | }, 139 | "headerPriority": { 140 | "message": "Prioridade", 141 | "description": "cabecalho popup" 142 | }, 143 | "headerAge": { 144 | "message": "Idade", 145 | "description": "cabecalho popup" 146 | }, 147 | 148 | "hostDetails": { 149 | "message": "detalhes da maquina", 150 | "description": "botao popup" 151 | }, 152 | "latestData": { 153 | "message": "ultima informacao", 154 | "description": "botao popup" 155 | }, 156 | "hostGraphs": { 157 | "message": "grafico das maquinas", 158 | "description": "botao popup" 159 | }, 160 | "problemDetails": { 161 | "message": "detalhes do problema", 162 | "description": "botao popup" 163 | }, 164 | "eventDetails": { 165 | "message": "detalhes do evento", 166 | "description": "botao popup" 167 | }, 168 | "ackEvent": { 169 | "message": "evento de confirmacao", 170 | "description": "botao popup" 171 | }, 172 | "filter": { 173 | "message": "filtro", 174 | "description": "identificacao popup" 175 | }, 176 | "noProb": { 177 | "message": "nao ha problemas", 178 | "description": "mensagem popup" 179 | }, 180 | "noResults": { 181 | "message": "Nao ha resultados encontrados para", 182 | "description": "mensagem popup" 183 | }, 184 | "noServers": { 185 | "message": "Nao ha servidores definidos", 186 | "description": "erro popup" 187 | }, 188 | "error": { 189 | "message": "erro no Plugin", 190 | "description": "erro popup" 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /src/public/_locales/pt_PT/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "appName": { 3 | "message": "Zabbix Vue", 4 | "description": "Monitor Zabbix problemas no seu browser" 5 | }, 6 | "appDescription": { 7 | "message": "Zabbix Vue", 8 | "description": "Monitor Zabbix problemas no seu browser" 9 | }, 10 | 11 | "notClassified": { 12 | "message": "Nao classificada", 13 | "description": "Severidade Zabbix" 14 | }, 15 | "information": { 16 | "message": "Informacao", 17 | "description": "Severidade Zabbix" 18 | }, 19 | "warning": { 20 | "message": "Aviso", 21 | "description": "Severidade Zabbix" 22 | }, 23 | "average": { 24 | "message": "Media", 25 | "description": "Severidade Zabbix" 26 | }, 27 | "high": { 28 | "message": "Alta", 29 | "description": "Severidade Zabbix" 30 | }, 31 | "disaster": { 32 | "message": "Desastre", 33 | "description": "Severidade Zabbix" 34 | }, 35 | 36 | 37 | "serverSettings": { 38 | "message": "Configuracoes do servidor", 39 | "description": "etiqueta do ecra de opcoes" 40 | }, 41 | "server": { 42 | "message": "Servidor", 43 | "description": "etiqueta do ecra de opcoes" 44 | }, 45 | "alias": { 46 | "message": "Alias", 47 | "description": "etiqueta do ecra de opcoes" 48 | }, 49 | "URL": { 50 | "message": "URL", 51 | "description": "etiqueta do ecra de opcoes" 52 | }, 53 | "zabbixUser": { 54 | "message": "utilizador Zabbix", 55 | "description": "etiqueta do ecra de opcoes" 56 | }, 57 | "zabbixPass": { 58 | "message": "password Zabbix", 59 | "description": "etiqueta do ecra de opcoes" 60 | }, 61 | "hideEvents": { 62 | "message": "esconder eventos ja aceites", 63 | "description": "etiqueta do ecra de opcoes" 64 | }, 65 | "ignoreHosts": { 66 | "message": "ignorar maquinas em mnutencao", 67 | "description": "etiqueta do ecra de opcoes" 68 | }, 69 | "minSev": { 70 | "message": "monitorizar severidade minima", 71 | "description": "etiqueta do ecra de opcoes" 72 | }, 73 | "monGroups": { 74 | "message": "monitorizar grupo de maquinas", 75 | "description": "etiqueta do ecra de opcoes" 76 | }, 77 | "generalSettings": { 78 | "message": "Opcoes gerais", 79 | "description": "etiqueta do ecra de opcoes" 80 | }, 81 | "updateInterval": { 82 | "message": "intervalo de actualizacao", 83 | "description": "etiqueta do ecra de opcoes" 84 | }, 85 | "notify": { 86 | "message": "mostrar motificacoes popup", 87 | "description": "etiqueta do ecra de opcoes" 88 | }, 89 | "selectName": { 90 | "message": "mostrar nome na popup", 91 | "description": "etiqueta do ecra de opcoes" 92 | }, 93 | "displayHost": { 94 | "message": "nome da maquina", 95 | "description": "etiqueta do ecra de opcoes" 96 | }, 97 | "displayName": { 98 | "message": "nome visivel", 99 | "description": "etiqueta do ecra de opcoes" 100 | }, 101 | "save": { 102 | "message": "guardar", 103 | "description": "botao do ecra de opcoes" 104 | }, 105 | "addServer": { 106 | "message": "adicionar servidor", 107 | "description": "botao do ecra de opcoes" 108 | }, 109 | 110 | "required": { 111 | "message": "Item obrigatorio", 112 | "description": "mensagem de validacao do ecra de opcoes" 113 | }, 114 | "requiredURL": { 115 | "message": "URL valido obrigatorio", 116 | "description": "mensagem de validacao do ecra de opcoes" 117 | }, 118 | "lessSeconds": { 119 | "message": "nao verificar com mais frequencia do que 10 segundos", 120 | "description": "mensagem de validacao do ecra de opcoes" 121 | }, 122 | "serverError": { 123 | "message": "erro ao connectar com o servidor", 124 | "description": "mensagem de erro generica" 125 | }, 126 | "checkConfig": { 127 | "message": "verificar as configuracoes do servidor nas opcoes extra", 128 | "description": "mensagem de erro nas configuracoes" 129 | }, 130 | 131 | "headerSystem": { 132 | "message": "Sistema", 133 | "description": "cabecalho popup" 134 | }, 135 | "headerDescription": { 136 | "message": "Descricao", 137 | "description": "cabecalho popup" 138 | }, 139 | "headerPriority": { 140 | "message": "Prioridade", 141 | "description": "cabecalho popup" 142 | }, 143 | "headerAge": { 144 | "message": "Idade", 145 | "description": "cabecalho popup" 146 | }, 147 | 148 | "hostDetails": { 149 | "message": "detalhes da maquina", 150 | "description": "botao popup" 151 | }, 152 | "latestData": { 153 | "message": "ultima informacao", 154 | "description": "botao popup" 155 | }, 156 | "hostGraphs": { 157 | "message": "grafico das maquinas", 158 | "description": "botao popup" 159 | }, 160 | "problemDetails": { 161 | "message": "detalhes do problema", 162 | "description": "botao popup" 163 | }, 164 | "eventDetails": { 165 | "message": "detalhes do evento", 166 | "description": "botao popup" 167 | }, 168 | "ackEvent": { 169 | "message": "evento de confirmacao", 170 | "description": "botao popup" 171 | }, 172 | "filter": { 173 | "message": "filtro", 174 | "description": "identificacao popup" 175 | }, 176 | "noProb": { 177 | "message": "nao ha problemas", 178 | "description": "mensagem popup" 179 | }, 180 | "noResults": { 181 | "message": "Nao ha resultados encontrados para", 182 | "description": "mensagem popup" 183 | }, 184 | "noServers": { 185 | "message": "Nao ha servidores definidos", 186 | "description": "erro popup" 187 | }, 188 | "error": { 189 | "message": "erro no Plugin", 190 | "description": "erro popup" 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /src/public/_locales/ru/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "appName": { 3 | "message": "Zabbix Vue", 4 | "description": "Уведомления о проблемах из заббикса в браузере" 5 | }, 6 | "appDescription": { 7 | "message": "Zabbix Vue", 8 | "description": "Уведомления о проблемах из заббикса в браузере" 9 | }, 10 | 11 | "notClassified": { 12 | "message": "Не классифицированно", 13 | "description": "Уровень важности" 14 | }, 15 | "information": { 16 | "message": "Информация", 17 | "description": "Уровень важности" 18 | }, 19 | "warning": { 20 | "message": "Предупреждение", 21 | "description": "Уровень важности" 22 | }, 23 | "average": { 24 | "message": "Средний", 25 | "description": "Уровень важности" 26 | }, 27 | "high": { 28 | "message": "Высокий", 29 | "description": "ZУровень важности" 30 | }, 31 | "disaster": { 32 | "message": "Черезвычайный", 33 | "description": "Уровень важности" 34 | }, 35 | 36 | 37 | "serverSettings": { 38 | "message": "Настройки сервера", 39 | "description": "Options screen label" 40 | }, 41 | "server": { 42 | "message": "Сервер", 43 | "description": "Options screen label" 44 | }, 45 | "alias": { 46 | "message": "Выводимое имя", 47 | "description": "Options screen label" 48 | }, 49 | "URL": { 50 | "message": "Адрес URL", 51 | "description": "Options screen label" 52 | }, 53 | "zabbixUser": { 54 | "message": "Логин", 55 | "description": "Options screen label" 56 | }, 57 | "zabbixPass": { 58 | "message": "Пароль", 59 | "description": "Options screen label" 60 | }, 61 | "hideEvents": { 62 | "message": "Скрывать подтверждённые события", 63 | "description": "Options screen label" 64 | }, 65 | "ignoreHosts": { 66 | "message": "Не показывать хосты в обслуживании", 67 | "description": "Options screen label" 68 | }, 69 | "minSev": { 70 | "message": "Показывать с минимальной важностью", 71 | "description": "Options screen label" 72 | }, 73 | "monGroups": { 74 | "message": "Показывать эти группы хостов", 75 | "description": "Options screen label" 76 | }, 77 | "generalSettings": { 78 | "message": "Основные настройки", 79 | "description": "Options screen label" 80 | }, 81 | "updateInterval": { 82 | "message": "Интервал обновления", 83 | "description": "Options screen label" 84 | }, 85 | "notify": { 86 | "message": "Показывать всплывающие уведомления", 87 | "description": "Options screen label" 88 | }, 89 | "selectName": { 90 | "message": "Показывать во всплывающие уведомлении имя", 91 | "description": "Options screen label" 92 | }, 93 | "displayHost": { 94 | "message": "Имя хоста", 95 | "description": "Options screen label" 96 | }, 97 | "displayName": { 98 | "message": "Отображаемое имя", 99 | "description": "Options screen label" 100 | }, 101 | "save": { 102 | "message": "Сохранить", 103 | "description": "Options screen button" 104 | }, 105 | "addServer": { 106 | "message": "Добавить сервер", 107 | "description": "Options screen button" 108 | }, 109 | 110 | "required": { 111 | "message": "Настройка обязательна", 112 | "description": "Options screen validation message" 113 | }, 114 | "requiredURL": { 115 | "message": "Неправильный URL", 116 | "description": "Options screen validation message" 117 | }, 118 | "lessSeconds": { 119 | "message": "Не проверять чаще одного раза в 10 секунд.", 120 | "description": "Options screen validation message" 121 | }, 122 | "serverError": { 123 | "message": "Ошибка подключения к серверу", 124 | "description": "Generic error message" 125 | }, 126 | "checkConfig": { 127 | "message": "Проверьте настройки сервера в параметрах расширения", 128 | "description": "Configuration error message" 129 | }, 130 | 131 | "headerSystem": { 132 | "message": "Сервер", 133 | "description": "Popup headers" 134 | }, 135 | "headerDescription": { 136 | "message": "Описание", 137 | "description": "Popup headers" 138 | }, 139 | "headerPriority": { 140 | "message": "Приоритет", 141 | "description": "Popup headers" 142 | }, 143 | "headerAge": { 144 | "message": "Возраст", 145 | "description": "Popup headers" 146 | }, 147 | 148 | "hostDetails": { 149 | "message": "Детали хоста", 150 | "description": "Popup button" 151 | }, 152 | "latestData": { 153 | "message": "Последние данные", 154 | "description": "Popup button" 155 | }, 156 | "hostGraphs": { 157 | "message": "Графики хоста", 158 | "description": "Popup button" 159 | }, 160 | "problemDetails": { 161 | "message": "Детали проблемы", 162 | "description": "Popup button" 163 | }, 164 | "eventDetails": { 165 | "message": "Детали события", 166 | "description": "Popup button" 167 | }, 168 | "ackEvent": { 169 | "message": "Подтвердить", 170 | "description": "Popup button" 171 | }, 172 | "filter": { 173 | "message": "Фильтр", 174 | "description": "Popup label" 175 | }, 176 | "noProb": { 177 | "message": "Проблем нет", 178 | "description": "Popup message" 179 | }, 180 | "noResults": { 181 | "message": "Не найдено", 182 | "description": "Popup message" 183 | }, 184 | "noServers": { 185 | "message": "Сервера не указаны", 186 | "description": "Popup error" 187 | }, 188 | "error": { 189 | "message": "Ошибка расширения", 190 | "description": "Popup error" 191 | } 192 | 193 | } 194 | -------------------------------------------------------------------------------- /src/public/sounds/audio.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Play MP3 File 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/public/sounds/drip.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mchugh19/zabbix-vue/b573c77a55d5fdf8dc8f57d095c6138f4220ee9f/src/public/sounds/drip.mp3 -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import { resolve } from 'path'; 3 | import vue from "@vitejs/plugin-vue"; 4 | import webExtension, { readJsonFile } from "vite-plugin-web-extension"; 5 | import { renderSVG } from 'vite-plugin-render-svg' 6 | import eslint from 'vite-plugin-eslint'; 7 | 8 | const target = process.env.TARGET || "chrome"; 9 | 10 | 11 | function generateManifest() { 12 | const manifestFile = readJsonFile("src/manifest.json"); 13 | const pkg = readJsonFile("package.json"); 14 | return { 15 | version: pkg.version, 16 | ...manifestFile, 17 | }; 18 | } 19 | 20 | 21 | export default defineConfig({ 22 | root: 'src', 23 | assetsInclude: ['*.mp3', '*.html'], 24 | build: { 25 | minify: false, 26 | outDir: '../dist', 27 | emptyOutDir: true, 28 | }, 29 | plugins: [ 30 | vue(), 31 | eslint(), 32 | webExtension({ 33 | manifest: generateManifest, 34 | watchFilePaths: ["package.json", "manifest.json"], 35 | browser: process.env.TARGET || "chrome", 36 | }), 37 | renderSVG({ 38 | pattern: 'images/*.svg', 39 | urlPrefix: 'images/', 40 | scales: [1], 41 | outputOriginal: false 42 | }), 43 | ], 44 | define: { 45 | __BROWSER__: JSON.stringify(target), 46 | }, 47 | }); 48 | --------------------------------------------------------------------------------
{{ $i18n("serverSettings") }}
{{ $i18n("generalSettings") }}