bosun.org@v0.0.0-20210513094433-e25bc3e69a1f/cmd/bosun/web/static/js/0-bosun.ts (about) 1 /// <reference path="angular.d.ts" /> 2 /// <reference path="angular-route.d.ts" /> 3 /// <reference path="angular-sanitize.d.ts" /> 4 /// <reference path="bootstrap.d.ts" /> 5 /// <reference path="jquery.d.ts" /> 6 /// <reference path="underscore.d.ts" /> 7 /// <reference path="models.ts" /> 8 9 declare var d3: any; 10 11 var bosunApp = angular.module('bosunApp', [ 12 'ngRoute', 13 'bosunControllers', 14 'mgcrea.ngStrap', 15 'mgcrea.ngStrap.tooltip', 16 'ngSanitize', 17 'ui.ace', 18 'ngclipboard', 19 ]); 20 21 interface ITitleRoute extends ng.route.IRoute { 22 title?: string; 23 } 24 25 bosunApp.config(['$routeProvider', '$locationProvider', '$httpProvider', function ($routeProvider: ng.route.IRouteProvider, $locationProvider: ng.ILocationProvider, $httpProvider: ng.IHttpProvider) { 26 $locationProvider.html5Mode({ 27 enabled: true, 28 requireBase: false 29 }); 30 31 var when = (u: string, r: ITitleRoute) => { 32 $routeProvider.when(u, r); 33 } 34 35 when('/', { 36 title: 'Dashboard', 37 templateUrl: 'partials/dashboard.html', 38 controller: 'DashboardCtrl', 39 }) 40 when('/items', { 41 title: 'Items', 42 templateUrl: 'partials/items.html', 43 controller: 'ItemsCtrl', 44 }) 45 when('/expr', { 46 title: 'Expression', 47 templateUrl: 'partials/expr.html', 48 controller: 'ExprCtrl', 49 }) 50 when('/errors', { 51 title: 'Errors', 52 templateUrl: 'partials/errors.html', 53 controller: 'ErrorCtrl', 54 }) 55 when('/graph', { 56 title: 'Graph', 57 templateUrl: 'partials/graph.html', 58 controller: 'GraphCtrl', 59 }) 60 when('/host', { 61 title: 'Host View', 62 templateUrl: 'partials/host.html', 63 controller: 'HostCtrl', 64 reloadOnSearch: false, 65 }) 66 when('/silence', { 67 title: 'Silence', 68 templateUrl: 'partials/silence.html', 69 controller: 'SilenceCtrl', 70 }) 71 when('/config', { 72 title: 'Configuration', 73 templateUrl: 'partials/config.html', 74 controller: 'ConfigCtrl', 75 reloadOnSearch: false, 76 }) 77 when('/action', { 78 title: 'Action', 79 templateUrl: 'partials/action.html', 80 controller: 'ActionCtrl', 81 }) 82 when('/history', { 83 title: 'Alert History', 84 templateUrl: 'partials/history.html', 85 controller: 'HistoryCtrl', 86 }) 87 when('/put', { 88 title: 'Data Entry', 89 templateUrl: 'partials/put.html', 90 controller: 'PutCtrl', 91 }) 92 when('/annotation', { 93 title: 'Annotation', 94 templateUrl: 'partials/annotation.html', 95 controller: 'AnnotationCtrl', 96 }) 97 when('/incident', { 98 title: 'Incident', 99 templateUrl: 'partials/incident.html', 100 controller: 'IncidentCtrl', 101 }) 102 when('/tokens', { 103 title: 'Access Tokens', 104 template: `<token-list></token-list>`, 105 }) 106 when('/tokens/new', { 107 title: 'New Access Token', 108 template: `<new-token></new-token>`, 109 }) 110 $routeProvider.otherwise({ 111 redirectTo: '/', 112 }); 113 $httpProvider.interceptors.push(function ($q) { 114 return { 115 'request': function (config) { 116 config.headers['X-Miniprofiler'] = 'true'; 117 return config; 118 }, 119 }; 120 }); 121 }]); 122 123 interface IRootScope extends ng.IScope { 124 title: string; 125 shortlink: boolean; 126 } 127 128 interface IAuthService { 129 Init: (authEnabled: boolean, username: string, roles: RoleDefs, userPerms: number) => void; 130 HasPermission: (role: string) => boolean; 131 GetRoles: () => RoleDefs; 132 Username: (u: string) => string; 133 GetUsername: () => string; 134 Enabled: () => boolean; 135 PermissionsFor: (bits: number) => string[]; 136 RoleFor: (bits: number) => string; 137 } 138 139 interface ILinkService { 140 GetEditSilenceLink: (silence: any, silenceId: string) => string; 141 } 142 143 bosunApp.run(['$location', '$rootScope', function ($location: ng.ILocationService, $rootScope: IRootScope) { 144 $rootScope.$on('$routeChangeSuccess', function (event, current, previous) { 145 $rootScope.title = current.$$route.title; 146 $rootScope.shortlink = false; 147 }); 148 }]); 149 150 var bosunControllers = angular.module('bosunControllers', []); 151 152 interface RootScope extends ng.IScope { 153 setKey: (key: string, value: any) => void; 154 getKey: (key: string) => any; 155 } 156 157 interface IBosunScope extends RootScope { 158 active: (v: string) => any; 159 json: (v: any) => string; 160 btoa: (v: any) => string; 161 encode: (v: string) => string; 162 panelClass: (v: string) => string; 163 timeanddate: number[]; 164 req_from_m: (m: string) => GraphRequest; 165 schedule: StateGroups; 166 refresh: (filter: string) => any; 167 animate: () => any; 168 stop: (all?: boolean) => any; 169 shorten: () => any; 170 shortlink: any; 171 values: any; 172 annotateEnabled: boolean; 173 opentsdbEnabled: boolean; 174 saveEnabled: boolean; 175 quiet: boolean; 176 version: any; 177 init: any; 178 auth: IAuthService; 179 tokensEnabled: boolean; 180 } 181 182 bosunControllers.controller('BosunCtrl', ['$scope', '$route', '$http', '$q', '$rootScope', 'authService', 183 function ($scope: IBosunScope, $route: ng.route.IRouteService, $http: ng.IHttpService, $q: ng.IQService, $rootScope: IRootScope, AuthService: IAuthService) { 184 $scope.$on('$routeChangeSuccess', function (event, current, previous) { 185 $scope.stop(true); 186 }); 187 $scope.active = (v: string) => { 188 if (!$route.current) { 189 return null; 190 } 191 if ($route.current.loadedTemplateUrl == 'partials/' + v + '.html') { 192 return { active: true }; 193 } 194 return null; 195 }; 196 $scope.init = (settings: any) => { 197 $scope.saveEnabled = settings.SaveEnabled; 198 $scope.annotateEnabled = settings.AnnotateEnabled; 199 $scope.quiet = settings.Quiet; 200 $scope.version = settings.Version; 201 $scope.opentsdbEnabled = $scope.version.Major != 0 && $scope.version.Minor != 0; 202 $scope.exampleExpression = settings.ExampleExpression; 203 $scope.tokensEnabled = settings.TokensEnabled; 204 $scope.auth = AuthService; 205 AuthService.Init(settings.AuthEnabled, settings.Username, settings.Roles, settings.Permissions) 206 } 207 $scope.json = (v: any) => { 208 return JSON.stringify(v, null, ' '); 209 }; 210 $scope.btoa = (v: any) => { 211 return encodeURIComponent(btoa(v)); 212 }; 213 $scope.encode = (v: string) => { 214 return encodeURIComponent(v); 215 }; 216 $scope.req_from_m = (m: string) => { 217 var r = new GraphRequest(); 218 var q = new Query(false); 219 q.metric = m; 220 r.queries.push(q); 221 return r; 222 }; 223 $scope.panelClass = (status: string, prefix = "panel-") => { 224 switch (status) { 225 case "critical": return prefix + "danger"; 226 case "unknown": return prefix + "info"; 227 case "warning": return prefix + "warning"; 228 case "normal": return prefix + "success"; 229 case "error": return prefix + "danger"; 230 default: return prefix + "default"; 231 } 232 }; 233 $scope.values = {}; 234 $scope.setKey = (key: string, value: any) => { 235 if (value === undefined) { 236 delete $scope.values[key]; 237 } else { 238 $scope.values[key] = value; 239 } 240 }; 241 $scope.getKey = (key: string) => { 242 return $scope.values[key]; 243 }; 244 var scheduleFilter: string; 245 $scope.refresh = (filter: string) => { 246 var d = $q.defer(); 247 scheduleFilter = filter; 248 $scope.animate(); 249 250 var p = $http.get('/api/alerts?filter=' + encodeURIComponent(filter || "")) 251 .success((data: any) => { 252 $scope.schedule = new StateGroups(data); 253 $scope.timeanddate = data.TimeAndDate; 254 d.resolve(); 255 }) 256 .error(err => { 257 d.reject(err); 258 }); 259 p.finally($scope.stop); 260 return d.promise; 261 }; 262 // Size of the logo in (width and height) of the Bosun logo in the navbar 263 var sz = 25; 264 var orig = 700; 265 var light = '#4ba2d9'; 266 var dark = '#1f5296'; 267 var med = '#356eb6'; 268 var mult = sz / orig; 269 var bgrad = 25 * mult; 270 var circles = [ 271 [150, 150, dark], 272 [550, 150, dark], 273 [150, 550, light], 274 [550, 550, light], 275 ]; 276 var svg = d3.select('#logo') 277 .append('svg') 278 .attr('height', sz) 279 .attr('width', sz); 280 svg.selectAll('rect.bg') 281 .data([[0, light], [sz / 2, dark]]) 282 .enter() 283 .append('rect') 284 .attr('class', 'bg') 285 .attr('width', sz) 286 .attr('height', sz / 2) 287 .attr('rx', bgrad) 288 .attr('ry', bgrad) 289 .attr('fill', (d: any) => { return d[1]; }) 290 .attr('y', (d: any) => { return d[0]; }); 291 svg.selectAll('path.diamond') 292 .data([150, 550]) 293 .enter() 294 .append('path') 295 .attr('d', (d: any) => { 296 var s = 'M ' + d * mult + ' ' + 150 * mult; 297 var w = 200 * mult; 298 s += ' l ' + w + ' ' + w; 299 s += ' l ' + -w + ' ' + w; 300 s += ' l ' + -w + ' ' + -w + ' Z'; 301 return s; 302 }) 303 .attr('fill', med) 304 .attr('stroke', 'white') 305 .attr('stroke-width', 15 * mult); 306 svg.selectAll('rect.white') 307 .data([150, 350, 550]) 308 .enter() 309 .append('rect') 310 .attr('class', 'white') 311 .attr('width', .5) 312 .attr('height', '100%') 313 .attr('fill', 'white') 314 .attr('x', (d: any) => { return d * mult; }); 315 svg.selectAll('circle') 316 .data(circles) 317 .enter() 318 .append('circle') 319 .attr('cx', (d: any) => { return d[0] * mult; }) 320 .attr('cy', (d: any) => { return d[1] * mult; }) 321 .attr('r', 62.5 * mult) 322 .attr('fill', (d: any) => { return d[2]; }) 323 .attr('stroke', 'white') 324 .attr('stroke-width', 25 * mult); 325 var transitionDuration = 750; 326 var animateCount = 0; 327 $scope.animate = () => { 328 animateCount++; 329 if (animateCount == 1) { 330 doAnimate(); 331 } 332 }; 333 function doAnimate() { 334 if (!animateCount) { 335 return; 336 } 337 d3.shuffle(circles); 338 svg.selectAll('circle') 339 .data(circles, (d: any, i: any) => { return i; }) 340 .transition() 341 .duration(transitionDuration) 342 .attr('cx', (d: any) => { return d[0] * mult; }) 343 .attr('cy', (d: any) => { return d[1] * mult; }) 344 .attr('fill', (d: any) => { return d[2]; }); 345 setTimeout(doAnimate, transitionDuration); 346 } 347 $scope.stop = (all = false) => { 348 if (all) { 349 animateCount = 0; 350 } else if (animateCount > 0) { 351 animateCount--; 352 } 353 }; 354 var short: any = $('#shortlink')[0]; 355 $scope.shorten = () => { 356 $http.get('/api/shorten').success((data: any) => { 357 if (data.id) { 358 short.value = data.id; 359 $rootScope.shortlink = true; 360 setTimeout(() => { 361 short.setSelectionRange(0, data.id.length); 362 }); 363 } 364 }); 365 }; 366 }]); 367 368 369 var tsdbDateFormat = 'YYYY/MM/DD-HH:mm:ss'; 370 371 interface MomentStatic { 372 defaultFormat: string; 373 } 374 375 moment.defaultFormat = tsdbDateFormat; 376 377 moment.locale('en', { 378 relativeTime: { 379 future: "in %s", 380 past: "%s-ago", 381 s: "%ds", 382 m: "%dm", 383 mm: "%dm", 384 h: "%dh", 385 hh: "%dh", 386 d: "%dd", 387 dd: "%dd", 388 M: "%dn", 389 MM: "%dn", 390 y: "%dy", 391 yy: "%dy" 392 }, 393 }); 394 395 function ruleUrl(ak, fromTime) { 396 var openBrack = ak.indexOf("{"); 397 var closeBrack = ak.indexOf("}"); 398 var alertName = ak.substr(0, openBrack); 399 var template = ak.substring(openBrack + 1, closeBrack); 400 var url = '/api/rule?' + 401 'alert=' + encodeURIComponent(alertName) + 402 '&from=' + encodeURIComponent(fromTime.format()) + 403 '&template_group=' + encodeURIComponent(template); 404 return url 405 } 406 407 function configUrl(ak, fromTime) { 408 var openBrack = ak.indexOf("{"); 409 var closeBrack = ak.indexOf("}"); 410 var alertName = ak.substr(0, openBrack); 411 var template = ak.substring(openBrack + 1, closeBrack); 412 // http://bosun/config?alert=haproxy.server.downtime.ny&fromDate=2016-07-10&fromTime=21%3A03 413 var url = '/config?' + 414 'alert=' + encodeURIComponent(alertName) + 415 '&fromDate=' + encodeURIComponent(fromTime.format("YYYY-MM-DD")) + 416 '&fromTime=' + encodeURIComponent(fromTime.format("HH:mm")); 417 return url 418 } 419 420 421 // From http://www.quirksmode.org/js/cookies.html 422 423 declare function escape(string: string): string; 424 425 declare function unescape(string: string): string; 426 427 function createCookie(name, value, days) { 428 var expires; 429 if (days) { 430 var date = new Date(); 431 date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); 432 expires = "; expires=" + date.toUTCString(); 433 } else { 434 expires = ""; 435 } 436 document.cookie = escape(name) + "=" + escape(value) + expires + "; path=/"; 437 } 438 439 function readCookie(name) { 440 var nameEQ = escape(name) + "="; 441 var ca = document.cookie.split(';'); 442 for (var i = 0; i < ca.length; i++) { 443 var c = ca[i]; 444 while (c.charAt(0) === ' ') c = c.substring(1, c.length); 445 if (c.indexOf(nameEQ) === 0) return unescape(c.substring(nameEQ.length, c.length)); 446 } 447 return null; 448 } 449 450 function eraseCookie(name) { 451 createCookie(name, "", -1); 452 } 453 454 function getOwner() { 455 return readCookie('action-owner'); 456 } 457 458 function setOwner(name) { 459 createCookie('action-owner', name, 1000); 460 } 461 462 function getShowAnnotations() { 463 return readCookie('annotations-show'); 464 } 465 466 function setShowAnnotations(yes) { 467 createCookie('annotations-show', yes, 1000); 468 } 469 470 471 472 // from: http://stackoverflow.com/a/15267754/864236 473 474 bosunApp.filter('reverse', function () { 475 return function (items) { 476 if (!angular.isArray(items)) { 477 return []; 478 } 479 return items.slice().reverse(); 480 }; 481 }); 482 483 var timeFormat = 'YYYY-MM-DDTHH:mm:ssZ';