2023-12-09 09:48:25 +00:00
/* eslint-disable camelcase */
2023-10-13 14:42:45 +00:00
/ * !
2021-07-30 03:23:04 +00:00
// Common Util for frontend and backend
2021-09-21 13:28:45 +00:00
//
// DOT NOT MODIFY util.js!
2023-10-07 12:51:56 +00:00
// Need to run "npm run tsc" to compile if there are any changes.
2021-09-21 13:28:45 +00:00
//
2021-07-30 03:23:04 +00:00
// Backend uses the compiled file util.js
// Frontend uses util.ts
2023-10-13 14:42:45 +00:00
* /
2021-07-30 03:23:04 +00:00
2024-06-25 10:08:02 +00:00
import dayjs from "dayjs" ;
2023-10-13 14:42:45 +00:00
// For loading dayjs plugins, don't remove event though it is not used in this file
// eslint-disable-next-line @typescript-eslint/no-unused-vars
2022-09-27 16:48:15 +00:00
import * as timezone from "dayjs/plugin/timezone" ;
2023-10-13 14:42:45 +00:00
// eslint-disable-next-line @typescript-eslint/no-unused-vars
2022-09-27 16:48:15 +00:00
import * as utc from "dayjs/plugin/utc" ;
2021-08-16 18:09:40 +00:00
2024-06-05 22:09:53 +00:00
import * as jsonata from "jsonata" ;
2021-08-16 18:09:40 +00:00
export const isDev = process . env . NODE_ENV === "development" ;
2023-12-09 09:48:25 +00:00
export const isNode = typeof process !== "undefined" && process ? . versions ? . node ;
2021-08-08 03:03:22 +00:00
export const appName = "Uptime Kuma" ;
2021-07-30 03:23:04 +00:00
export const DOWN = 0 ;
export const UP = 1 ;
export const PENDING = 2 ;
2022-01-23 14:22:00 +00:00
export const MAINTENANCE = 3 ;
2021-07-30 03:23:04 +00:00
2021-09-16 06:37:57 +00:00
export const STATUS_PAGE_ALL_DOWN = 0 ;
export const STATUS_PAGE_ALL_UP = 1 ;
export const STATUS_PAGE_PARTIAL_DOWN = 2 ;
2022-01-23 14:22:00 +00:00
export const STATUS_PAGE_MAINTENANCE = 3 ;
2021-09-16 06:37:57 +00:00
2022-10-09 18:28:03 +00:00
export const SQL_DATE_FORMAT = "YYYY-MM-DD" ;
export const SQL_DATETIME_FORMAT = "YYYY-MM-DD HH:mm:ss" ;
2022-10-11 10:23:17 +00:00
export const SQL_DATETIME_FORMAT_WITHOUT_SECOND = "YYYY-MM-DD HH:mm" ;
2022-10-09 18:28:03 +00:00
2022-12-08 15:21:55 +00:00
export const MAX_INTERVAL_SECOND = 2073600 ; // 24 days
export const MIN_INTERVAL_SECOND = 20 ; // 20 seconds
2023-10-13 19:00:34 +00:00
// Console colors
// https://stackoverflow.com/questions/9781218/how-to-change-node-jss-console-font-color
export const CONSOLE_STYLE_Reset = "\x1b[0m" ;
export const CONSOLE_STYLE_Bright = "\x1b[1m" ;
export const CONSOLE_STYLE_Dim = "\x1b[2m" ;
export const CONSOLE_STYLE_Underscore = "\x1b[4m" ;
export const CONSOLE_STYLE_Blink = "\x1b[5m" ;
export const CONSOLE_STYLE_Reverse = "\x1b[7m" ;
export const CONSOLE_STYLE_Hidden = "\x1b[8m" ;
export const CONSOLE_STYLE_FgBlack = "\x1b[30m" ;
export const CONSOLE_STYLE_FgRed = "\x1b[31m" ;
export const CONSOLE_STYLE_FgGreen = "\x1b[32m" ;
export const CONSOLE_STYLE_FgYellow = "\x1b[33m" ;
export const CONSOLE_STYLE_FgBlue = "\x1b[34m" ;
export const CONSOLE_STYLE_FgMagenta = "\x1b[35m" ;
export const CONSOLE_STYLE_FgCyan = "\x1b[36m" ;
export const CONSOLE_STYLE_FgWhite = "\x1b[37m" ;
export const CONSOLE_STYLE_FgGray = "\x1b[90m" ;
export const CONSOLE_STYLE_FgOrange = "\x1b[38;5;208m" ;
export const CONSOLE_STYLE_FgLightGreen = "\x1b[38;5;119m" ;
export const CONSOLE_STYLE_FgLightBlue = "\x1b[38;5;117m" ;
export const CONSOLE_STYLE_FgViolet = "\x1b[38;5;141m" ;
export const CONSOLE_STYLE_FgBrown = "\x1b[38;5;130m" ;
export const CONSOLE_STYLE_FgPink = "\x1b[38;5;219m" ;
export const CONSOLE_STYLE_BgBlack = "\x1b[40m" ;
export const CONSOLE_STYLE_BgRed = "\x1b[41m" ;
export const CONSOLE_STYLE_BgGreen = "\x1b[42m" ;
export const CONSOLE_STYLE_BgYellow = "\x1b[43m" ;
export const CONSOLE_STYLE_BgBlue = "\x1b[44m" ;
export const CONSOLE_STYLE_BgMagenta = "\x1b[45m" ;
export const CONSOLE_STYLE_BgCyan = "\x1b[46m" ;
export const CONSOLE_STYLE_BgWhite = "\x1b[47m" ;
export const CONSOLE_STYLE_BgGray = "\x1b[100m" ;
const consoleModuleColors = [
CONSOLE_STYLE_FgCyan ,
CONSOLE_STYLE_FgGreen ,
CONSOLE_STYLE_FgLightGreen ,
CONSOLE_STYLE_FgBlue ,
CONSOLE_STYLE_FgLightBlue ,
CONSOLE_STYLE_FgMagenta ,
CONSOLE_STYLE_FgOrange ,
CONSOLE_STYLE_FgViolet ,
CONSOLE_STYLE_FgBrown ,
CONSOLE_STYLE_FgPink ,
] ;
const consoleLevelColors : Record < string , string > = {
"INFO" : CONSOLE_STYLE_FgCyan ,
"WARN" : CONSOLE_STYLE_FgYellow ,
"ERROR" : CONSOLE_STYLE_FgRed ,
"DEBUG" : CONSOLE_STYLE_FgGray ,
} ;
2023-10-13 14:42:45 +00:00
/ * *
* Flip the status of s
2023-12-09 09:48:25 +00:00
* @param s input status : UP or DOWN
* @returns { number } UP or DOWN
2023-10-13 14:42:45 +00:00
* /
2023-12-10 12:40:40 +00:00
export const badgeConstants = {
naColor : "#999" ,
defaultUpColor : "#66c20a" ,
defaultWarnColor : "#eed202" ,
defaultDownColor : "#c2290a" ,
defaultPendingColor : "#f8a306" ,
defaultMaintenanceColor : "#1747f5" ,
defaultPingColor : "blue" , // as defined by badge-maker / shields.io
defaultStyle : "flat" ,
defaultPingValueSuffix : "ms" ,
defaultPingLabelSuffix : "h" ,
defaultUptimeValueSuffix : "%" ,
defaultUptimeLabelSuffix : "h" ,
defaultCertExpValueSuffix : " days" ,
defaultCertExpLabelSuffix : "h" ,
// Values Come From Default Notification Times
defaultCertExpireWarnDays : "14" ,
defaultCertExpireDownDays : "7"
} ;
2022-06-02 13:32:38 +00:00
/** Flip the status of s */
2021-08-23 03:33:24 +00:00
export function flipStatus ( s : number ) {
2021-07-30 16:01:04 +00:00
if ( s === UP ) {
return DOWN ;
}
if ( s === DOWN ) {
return UP ;
}
return s ;
}
2022-06-02 13:32:38 +00:00
/ * *
* Delays for specified number of seconds
* @param ms Number of milliseconds to sleep for
2023-12-09 09:48:25 +00:00
* @returns { Promise < void > } Promise that resolves after ms
2022-06-02 13:32:38 +00:00
* /
2021-08-23 03:33:24 +00:00
export function sleep ( ms : number ) {
2021-07-30 03:23:04 +00:00
return new Promise ( resolve = > setTimeout ( resolve , ms ) ) ;
}
/ * *
* PHP ' s ucfirst
2023-12-09 09:48:25 +00:00
* @param str string input
* @returns { string } string with first letter capitalized
2021-07-30 03:23:04 +00:00
* /
2021-08-23 03:33:24 +00:00
export function ucfirst ( str : string ) {
if ( ! str ) {
2021-07-30 03:23:04 +00:00
return str ;
}
const firstLetter = str . substr ( 0 , 1 ) ;
return firstLetter . toUpperCase ( ) + str . substr ( 1 ) ;
}
2022-04-13 15:33:37 +00:00
class Logger {
2022-04-16 06:50:48 +00:00
/ * *
* UPTIME_KUMA_HIDE_LOG = debug_monitor , info_monitor
*
* Example :
* [
* "debug_monitor" , // Hide all logs that level is debug and the module is monitor
* "info_monitor" ,
* ]
* /
2023-10-13 14:42:45 +00:00
hideLog : Record < string , string [ ] > = {
2022-04-16 06:50:48 +00:00
info : [ ] ,
warn : [ ] ,
error : [ ] ,
debug : [ ] ,
} ;
2023-10-13 14:42:45 +00:00
/ * *
*
* /
2022-04-16 06:50:48 +00:00
constructor ( ) {
if ( typeof process !== "undefined" && process . env . UPTIME_KUMA_HIDE_LOG ) {
2023-10-13 14:42:45 +00:00
const list = process . env . UPTIME_KUMA_HIDE_LOG . split ( "," ) . map ( v = > v . toLowerCase ( ) ) ;
2022-04-16 06:50:48 +00:00
2023-10-13 14:42:45 +00:00
for ( const pair of list ) {
2022-04-16 06:50:48 +00:00
// split first "_" only
2023-10-13 14:42:45 +00:00
const values = pair . split ( / _ ( . * ) / s ) ;
2022-04-16 06:50:48 +00:00
if ( values . length >= 2 ) {
this . hideLog [ values [ 0 ] ] . push ( values [ 1 ] ) ;
}
}
this . debug ( "server" , "UPTIME_KUMA_HIDE_LOG is set" ) ;
this . debug ( "server" , this . hideLog ) ;
}
}
2022-06-02 13:32:38 +00:00
/ * *
* Write a message to the log
2024-09-24 09:51:21 +00:00
* @private
2022-06-02 13:32:38 +00:00
* @param module The module the log comes from
* @param msg Message to write
2024-09-24 09:51:21 +00:00
* @param level { "INFO" | "WARN" | "ERROR" | "DEBUG" } Log level
2023-12-09 09:48:25 +00:00
* @returns { void }
2022-06-02 13:32:38 +00:00
* /
2024-09-24 09:51:21 +00:00
log ( module : string , msg : unknown , level : "INFO" | "WARN" | "ERROR" | "DEBUG" ) : void {
2023-11-24 08:03:35 +00:00
if ( level === "DEBUG" && ! isDev ) {
return ;
}
2022-12-12 09:19:22 +00:00
if ( this . hideLog [ level ] && this . hideLog [ level ] . includes ( module . toLowerCase ( ) ) ) {
2022-04-16 06:50:48 +00:00
return ;
}
2022-12-26 06:46:12 +00:00
let now ;
if ( dayjs . tz ) {
now = dayjs . tz ( new Date ( ) ) . format ( ) ;
} else {
now = dayjs ( ) . format ( ) ;
}
2023-10-13 19:00:34 +00:00
const levelColor = consoleLevelColors [ level ] ;
const moduleColor = consoleModuleColors [ intHash ( module , consoleModuleColors . length ) ] ;
2024-09-24 09:51:21 +00:00
let timePart : string = now ;
let modulePart : string = module ;
let levelPart : string = level ;
let msgPart : unknown = msg ;
if ( process . env . UPTIME_KUMA_LOG_FORMAT === "json" ) {
console . log ( JSON . stringify ( {
time : timePart ,
module : modulePart ,
level : levelPart ,
msg : typeof msg === "string" ? msg : JSON.stringify ( msg ) ,
} ) ) ;
return ;
}
// Console rendering:
module = module . toUpperCase ( ) ;
2023-12-09 09:48:25 +00:00
if ( isNode ) {
// Add console colors
switch ( level ) {
case "DEBUG" :
timePart = CONSOLE_STYLE_FgGray + now + CONSOLE_STYLE_Reset ;
break ;
default :
timePart = CONSOLE_STYLE_FgCyan + now + CONSOLE_STYLE_Reset ;
break ;
2023-10-14 09:48:41 +00:00
}
2023-12-09 09:48:25 +00:00
modulePart = "[" + moduleColor + module + CONSOLE_STYLE_Reset + "]" ;
levelPart = levelColor + ` ${ level } : ` + CONSOLE_STYLE_Reset ;
switch ( level ) {
case "ERROR" :
if ( typeof msg === "string" ) {
msgPart = CONSOLE_STYLE_FgRed + msg + CONSOLE_STYLE_Reset ;
}
break ;
case "DEBUG" :
if ( typeof msg === "string" ) {
msgPart = CONSOLE_STYLE_FgGray + msg + CONSOLE_STYLE_Reset ;
}
break ;
2022-04-13 15:33:37 +00:00
}
} else {
2023-12-09 09:48:25 +00:00
// No console colors
modulePart = ` [ ${ module } ] ` ;
levelPart = ` ${ level } : ` ;
}
// Write to console
switch ( level ) {
case "ERROR" :
console . error ( timePart , modulePart , levelPart , msgPart ) ;
break ;
case "WARN" :
console . warn ( timePart , modulePart , levelPart , msgPart ) ;
break ;
case "INFO" :
console . info ( timePart , modulePart , levelPart , msgPart ) ;
break ;
case "DEBUG" :
if ( isDev ) {
console . debug ( timePart , modulePart , levelPart , msgPart ) ;
}
break ;
default :
console . log ( timePart , modulePart , levelPart , msgPart ) ;
break ;
2021-11-11 11:31:28 +00:00
}
2021-07-30 03:23:04 +00:00
}
2021-08-09 05:49:37 +00:00
2022-06-02 13:32:38 +00:00
/ * *
* Log an INFO message
* @param module Module log comes from
* @param msg Message to write
2023-12-09 09:48:25 +00:00
* @returns { void }
2022-06-02 13:32:38 +00:00
* /
2024-09-24 09:51:21 +00:00
info ( module : string , msg : string ) : void {
this . log ( module , msg , "INFO" ) ;
2022-04-13 15:33:37 +00:00
}
2021-11-15 16:52:28 +00:00
2022-06-02 13:32:38 +00:00
/ * *
* Log a WARN message
* @param module Module log comes from
* @param msg Message to write
2023-12-09 09:48:25 +00:00
* @returns { void }
2022-06-02 13:32:38 +00:00
* /
2024-09-24 09:51:21 +00:00
warn ( module : string , msg : string ) : void {
this . log ( module , msg , "WARN" ) ;
2022-04-13 15:33:37 +00:00
}
2021-11-15 16:52:28 +00:00
2022-06-02 13:32:38 +00:00
/ * *
* Log an ERROR message
* @param module Module log comes from
* @param msg Message to write
2023-12-09 09:48:25 +00:00
* @returns { void }
2022-06-02 13:32:38 +00:00
* /
2024-09-24 09:51:21 +00:00
error ( module : string , msg : string ) : void {
this . log ( module , msg , "ERROR" ) ;
2022-04-13 15:33:37 +00:00
}
2021-11-15 16:52:28 +00:00
2022-06-02 13:32:38 +00:00
/ * *
* Log a DEBUG message
* @param module Module log comes from
* @param msg Message to write
2023-12-09 09:48:25 +00:00
* @returns { void }
2022-06-02 13:32:38 +00:00
* /
2024-09-24 09:51:21 +00:00
debug ( module : string , msg : string ) : void {
this . log ( module , msg , "DEBUG" ) ;
2022-04-13 15:33:37 +00:00
}
2022-06-02 13:32:38 +00:00
/ * *
2023-10-13 14:42:45 +00:00
* Log an exception as an ERROR
2022-06-02 13:32:38 +00:00
* @param module Module log comes from
2023-10-13 14:42:45 +00:00
* @param exception The exception to include
2022-06-02 13:32:38 +00:00
* @param msg The message to write
2023-12-09 09:48:25 +00:00
* @returns { void }
2022-06-02 13:32:38 +00:00
* /
2023-10-13 14:42:45 +00:00
exception ( module : string , exception : unknown , msg : unknown ) {
let finalMessage = exception ;
2022-04-13 15:33:37 +00:00
if ( msg ) {
2023-10-13 14:42:45 +00:00
finalMessage = ` ${ msg } : ${ exception } ` ;
2022-04-13 15:33:37 +00:00
}
2024-09-24 09:51:21 +00:00
this . log ( module , finalMessage , "ERROR" ) ;
2021-07-30 03:23:04 +00:00
}
}
2021-08-09 05:49:37 +00:00
2022-04-13 15:33:37 +00:00
export const log = new Logger ( ) ;
2021-08-23 03:33:24 +00:00
declare global { interface String { replaceAll ( str : string , newStr : string ) : string ; } }
2022-06-02 13:32:38 +00:00
/ * *
* String . prototype . replaceAll ( ) polyfill
* https : //gomakethings.com/how-to-replace-a-section-of-a-string-with-another-one-with-vanilla-js/
* @author Chris Ferdinandi
* @license MIT
2023-12-09 09:48:25 +00:00
* @returns { void }
2022-06-02 13:32:38 +00:00
* /
2021-08-09 05:49:37 +00:00
export function polyfill() {
if ( ! String . prototype . replaceAll ) {
2021-08-23 03:33:24 +00:00
String . prototype . replaceAll = function ( str : string , newStr : string ) {
2021-08-09 05:49:37 +00:00
// If a regex pattern
if ( Object . prototype . toString . call ( str ) . toLowerCase ( ) === "[object regexp]" ) {
return this . replace ( str , newStr ) ;
}
// If a string
return this . replace ( new RegExp ( str , "g" ) , newStr ) ;
} ;
}
}
2021-08-16 18:09:40 +00:00
export class TimeLogger {
2021-08-23 03:33:24 +00:00
startTime : number ;
2023-10-13 14:42:45 +00:00
/ * *
*
* /
2021-08-16 18:09:40 +00:00
constructor ( ) {
this . startTime = dayjs ( ) . valueOf ( ) ;
}
2023-10-13 14:42:45 +00:00
2022-06-02 13:32:38 +00:00
/ * *
* Output time since start of monitor
* @param name Name of monitor
2023-12-09 09:48:25 +00:00
* @returns { void }
2022-06-02 13:32:38 +00:00
* /
2024-09-24 09:51:21 +00:00
print ( name : string ) : void {
2021-10-08 05:35:04 +00:00
if ( isDev && process . env . TIMELOGGER === "1" ) {
2023-10-13 14:42:45 +00:00
console . log ( name + ": " + ( dayjs ( ) . valueOf ( ) - this . startTime ) + "ms" ) ;
2021-08-16 18:09:40 +00:00
}
}
}
2021-08-19 10:33:52 +00:00
/ * *
* Returns a random number between min ( inclusive ) and max ( exclusive )
2023-12-09 09:48:25 +00:00
* @param min minumim value , inclusive
* @param max maximum value , exclusive
* @returns { number } Random number
2021-08-19 10:33:52 +00:00
* /
2021-08-23 03:33:24 +00:00
export function getRandomArbitrary ( min : number , max : number ) {
2021-08-19 10:33:52 +00:00
return Math . random ( ) * ( max - min ) + min ;
}
/ * *
* From : https : //stackoverflow.com/questions/1527803/generating-random-whole-numbers-in-javascript-in-a-specific-range
*
* Returns a random integer between min ( inclusive ) and max ( inclusive ) .
* The value is no lower than min ( or the next integer greater than min
* if min isn ' t an integer ) and no greater than max ( or the next integer
* lower than max if max isn ' t an integer ) .
* Using Math . round ( ) will give you a non - uniform distribution !
2023-12-09 09:48:25 +00:00
* @param min minumim value , inclusive
* @param max maximum value , exclusive
* @returns { number } Random number
2021-08-19 10:33:52 +00:00
* /
2021-08-23 03:33:24 +00:00
export function getRandomInt ( min : number , max : number ) {
2021-08-19 10:33:52 +00:00
min = Math . ceil ( min ) ;
max = Math . floor ( max ) ;
return Math . floor ( Math . random ( ) * ( max - min + 1 ) ) + min ;
}
2021-09-30 16:09:43 +00:00
2021-10-17 23:06:20 +00:00
/ * *
* Returns either the NodeJS crypto . randomBytes ( ) function or its
* browser equivalent implemented via window . crypto . getRandomValues ( )
2023-12-09 09:48:25 +00:00
* @returns { Uint8Array } Random bytes
2021-10-17 23:06:20 +00:00
* /
2023-10-13 14:42:45 +00:00
const getRandomBytes = (
( typeof window !== "undefined" && window . crypto )
2021-10-17 23:06:20 +00:00
// Browsers
? function ( ) {
return ( numBytes : number ) = > {
2023-10-13 14:42:45 +00:00
const randomBytes = new Uint8Array ( numBytes ) ;
2021-10-17 23:06:20 +00:00
for ( let i = 0 ; i < numBytes ; i += 65536 ) {
window . crypto . getRandomValues ( randomBytes . subarray ( i , i + Math . min ( numBytes - i , 65536 ) ) ) ;
}
return randomBytes ;
} ;
}
2023-10-13 14:42:45 +00:00
// Node
: function ( ) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
2021-10-17 23:06:20 +00:00
return require ( "crypto" ) . randomBytes ;
}
) ( ) ;
2022-06-02 13:32:38 +00:00
/ * *
* Get a random integer suitable for use in cryptography between upper
* and lower bounds .
* @param min Minimum value of integer
* @param max Maximum value of integer
* @returns Cryptographically suitable random integer
* /
2021-10-17 23:06:20 +00:00
export function getCryptoRandomInt ( min : number , max : number ) : number {
// synchronous version of: https://github.com/joepie91/node-random-number-csprng
2023-10-13 14:42:45 +00:00
const range = max - min ;
if ( range >= Math . pow ( 2 , 32 ) ) {
console . log ( "Warning! Range is too large." ) ;
}
2021-10-17 23:06:20 +00:00
2023-10-13 14:42:45 +00:00
let tmpRange = range ;
let bitsNeeded = 0 ;
let bytesNeeded = 0 ;
let mask = 1 ;
2021-10-17 23:06:20 +00:00
while ( tmpRange > 0 ) {
2023-10-13 14:42:45 +00:00
if ( bitsNeeded % 8 === 0 ) {
bytesNeeded += 1 ;
}
bitsNeeded += 1 ;
mask = mask << 1 | 1 ;
tmpRange = tmpRange >>> 1 ;
2021-10-17 23:06:20 +00:00
}
2023-10-13 14:42:45 +00:00
const randomBytes = getRandomBytes ( bytesNeeded ) ;
let randomValue = 0 ;
2021-10-17 23:06:20 +00:00
for ( let i = 0 ; i < bytesNeeded ; i ++ ) {
2023-10-13 14:42:45 +00:00
randomValue |= randomBytes [ i ] << 8 * i ;
2021-10-17 23:06:20 +00:00
}
randomValue = randomValue & mask ;
if ( randomValue <= range ) {
2023-10-13 14:42:45 +00:00
return min + randomValue ;
2021-10-17 23:06:20 +00:00
} else {
2023-10-13 14:42:45 +00:00
return getCryptoRandomInt ( min , max ) ;
2021-10-17 23:06:20 +00:00
}
2021-10-10 19:58:23 +00:00
}
2022-06-02 13:32:38 +00:00
/ * *
2022-06-02 15:40:56 +00:00
* Generate a random alphanumeric string of fixed length
* @param length Length of string to generate
* @returns string
2022-06-02 13:32:38 +00:00
* /
2021-09-30 16:09:43 +00:00
export function genSecret ( length = 64 ) {
let secret = "" ;
2021-10-10 19:58:23 +00:00
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" ;
const charsLength = chars . length ;
2021-10-10 19:59:23 +00:00
for ( let i = 0 ; i < length ; i ++ ) {
2021-10-10 23:18:33 +00:00
secret += chars . charAt ( getCryptoRandomInt ( 0 , charsLength - 1 ) ) ;
2021-09-30 16:09:43 +00:00
}
return secret ;
}
2021-10-07 09:39:58 +00:00
2022-06-02 13:32:38 +00:00
/ * *
* Get the path of a monitor
* @param id ID of monitor
* @returns Formatted relative path
* /
2021-10-07 09:39:58 +00:00
export function getMonitorRelativeURL ( id : string ) {
2022-04-30 12:33:54 +00:00
return "/dashboard/" + id ;
2022-01-23 14:22:00 +00:00
}
2023-01-05 22:55:51 +00:00
/ * *
* Get relative path for maintenance
* @param id ID of maintenance
* @returns Formatted relative path
* /
2022-01-23 14:22:00 +00:00
export function getMaintenanceRelativeURL ( id : string ) {
2022-09-17 14:00:11 +00:00
return "/maintenance/" + id ;
2021-10-07 09:39:58 +00:00
}
2022-09-24 11:18:24 +00:00
/ * *
* Parse to Time Object that used in VueDatePicker
* @param { string } time E . g . 12 :00
* @returns object
2023-12-09 09:48:25 +00:00
* @throws { Error } if time string is invalid
2022-09-24 11:18:24 +00:00
* /
2022-09-25 11:38:28 +00:00
export function parseTimeObject ( time : string ) {
2022-09-24 11:18:24 +00:00
if ( ! time ) {
return {
hours : 0 ,
minutes : 0 ,
} ;
}
2023-10-13 14:42:45 +00:00
const array = time . split ( ":" ) ;
2022-09-24 11:18:24 +00:00
if ( array . length < 2 ) {
throw new Error ( "parseVueDatePickerTimeFormat: Invalid Time" ) ;
}
2023-10-13 14:42:45 +00:00
const obj = {
2022-09-24 11:18:24 +00:00
hours : parseInt ( array [ 0 ] ) ,
minutes : parseInt ( array [ 1 ] ) ,
seconds : 0 ,
2023-10-13 14:42:45 +00:00
} ;
2022-09-24 11:18:24 +00:00
if ( array . length >= 3 ) {
obj . seconds = parseInt ( array [ 2 ] ) ;
}
return obj ;
}
/ * *
2023-12-09 09:48:25 +00:00
* Parse time to string from object { hours : number , minutes : number , seconds? : number }
* @param obj object to parse
* @returns { string } e . g . 12 :00
2022-09-24 11:18:24 +00:00
* /
2022-09-25 11:38:28 +00:00
export function parseTimeFromTimeObject ( obj : any ) {
2022-09-24 11:18:24 +00:00
if ( ! obj ) {
return obj ;
}
let result = "" ;
2023-10-13 14:42:45 +00:00
result += obj . hours . toString ( ) . padStart ( 2 , "0" ) + ":" + obj . minutes . toString ( ) . padStart ( 2 , "0" ) ;
2022-09-24 11:18:24 +00:00
if ( obj . seconds ) {
2023-10-13 14:42:45 +00:00
result += ":" + obj . seconds . toString ( ) . padStart ( 2 , "0" ) ;
2022-09-24 11:18:24 +00:00
}
return result ;
}
2022-09-25 11:38:28 +00:00
2023-01-05 22:55:51 +00:00
/ * *
* Convert ISO date to UTC
* @param input Date
* @returns ISO Date time
* /
2022-09-27 16:48:15 +00:00
export function isoToUTCDateTime ( input : string ) {
2022-10-09 18:28:03 +00:00
return dayjs ( input ) . utc ( ) . format ( SQL_DATETIME_FORMAT ) ;
2022-09-27 16:48:15 +00:00
}
/ * *
2023-12-09 09:48:25 +00:00
* @param input valid datetime string
* @returns { string } ISO DateTime string
2022-09-27 16:48:15 +00:00
* /
export function utcToISODateTime ( input : string ) {
return dayjs . utc ( input ) . toISOString ( ) ;
}
2022-10-09 18:28:03 +00:00
/ * *
* For SQL_DATETIME_FORMAT
2023-12-09 09:48:25 +00:00
* @param input valid datetime string
* @param format Format to return
2023-10-13 14:42:45 +00:00
* @returns A string date of SQL_DATETIME_FORMAT
2022-10-09 18:28:03 +00:00
* /
2023-10-13 14:42:45 +00:00
export function utcToLocal ( input : string , format = SQL_DATETIME_FORMAT ) : string {
2022-10-11 10:23:17 +00:00
return dayjs . utc ( input ) . local ( ) . format ( format ) ;
2022-10-09 18:28:03 +00:00
}
2023-01-05 22:55:51 +00:00
/ * *
* Convert local datetime to UTC
* @param input Local date
* @param format Format to return
* @returns Date in requested format
* /
2022-10-11 10:23:17 +00:00
export function localToUTC ( input : string , format = SQL_DATETIME_FORMAT ) {
return dayjs ( input ) . utc ( ) . format ( format ) ;
2022-10-09 18:28:03 +00:00
}
2023-10-13 19:00:34 +00:00
/ * *
* Generate a decimal integer number from a string
* @param str Input
* @param length Default is 10 which means 0 - 9
2023-12-09 09:48:25 +00:00
* @returns { number } output number
2023-10-13 19:00:34 +00:00
* /
export function intHash ( str : string , length = 10 ) : number {
// A simple hashing function (you can use more complex hash functions if needed)
let hash = 0 ;
for ( let i = 0 ; i < str . length ; i ++ ) {
hash += str . charCodeAt ( i ) ;
}
// Normalize the hash to the range [0, 10]
return ( hash % length + length ) % length ; // Ensure the result is non-negative
}
2024-05-07 16:02:05 +00:00
2024-06-05 22:09:53 +00:00
/ * *
* Evaluate a JSON query expression against the provided data .
* @param data The data to evaluate the JSON query against .
* @param jsonPath The JSON path or custom JSON query expression .
* @param jsonPathOperator The operator to use for comparison .
* @param expectedValue The expected value to compare against .
2024-06-06 16:09:35 +00:00
* @returns An object containing the status and the evaluation result .
2024-06-05 22:09:53 +00:00
* @throws Error if the evaluation returns undefined .
* /
2024-06-07 00:52:33 +00:00
export async function evaluateJsonQuery ( data : any , jsonPath : string , jsonPathOperator : string , expectedValue : any ) : Promise < { status : boolean ; response : any } > {
// Attempt to parse data as JSON; if unsuccessful, handle based on data type.
2024-06-06 16:09:35 +00:00
let response : any ;
2024-06-05 22:09:53 +00:00
try {
2024-06-06 16:09:35 +00:00
response = JSON . parse ( data ) ;
} catch {
2024-06-14 22:45:51 +00:00
response = ( typeof data === "object" || typeof data === "number" ) && ! Buffer . isBuffer ( data ) ? data : data.toString ( ) ;
2024-06-05 22:09:53 +00:00
}
2024-06-07 22:35:06 +00:00
try {
// If a JSON path is provided, pre-evaluate the data using it.
response = ( jsonPath ) ? await jsonata ( jsonPath ) . evaluate ( response ) : response ;
2024-06-14 22:45:51 +00:00
if ( response === null || response === undefined ) {
throw new Error ( "Empty or undefined response. Check query syntax and response structure" ) ;
}
if ( typeof response === "object" || response instanceof Date || typeof response === "function" ) {
2024-06-10 18:49:41 +00:00
throw new Error ( ` The post-JSON query evaluated response from the server is of type ${ typeof response } , which cannot be directly compared to the expected value ` ) ;
}
2024-06-07 22:35:06 +00:00
// Perform the comparison logic using the chosen operator
let jsonQueryExpression ;
switch ( jsonPathOperator ) {
case ">" :
case ">=" :
case "<" :
case "<=" :
2024-06-10 18:25:55 +00:00
jsonQueryExpression = ` $ number( $ .value) ${ jsonPathOperator } $ number( $ .expected) ` ;
2024-06-07 22:35:06 +00:00
break ;
case "!=" :
2024-06-10 18:25:55 +00:00
jsonQueryExpression = "$.value != $.expected" ;
2024-06-07 22:35:06 +00:00
break ;
case "==" :
2024-06-10 18:25:55 +00:00
jsonQueryExpression = "$.value = $.expected" ;
2024-06-07 22:35:06 +00:00
break ;
case "contains" :
2024-06-10 18:25:55 +00:00
jsonQueryExpression = "$contains($.value, $.expected)" ;
2024-06-07 22:35:06 +00:00
break ;
default :
throw new Error ( ` Invalid condition ${ jsonPathOperator } ` ) ;
2024-06-07 00:52:33 +00:00
}
2024-06-07 22:35:06 +00:00
// Evaluate the JSON Query Expression
const expression = jsonata ( jsonQueryExpression ) ;
const status = await expression . evaluate ( {
2024-06-10 18:25:55 +00:00
value : response.toString ( ) ,
expected : expectedValue.toString ( )
2024-06-07 22:35:06 +00:00
} ) ;
2024-06-05 22:09:53 +00:00
2024-06-14 22:45:51 +00:00
if ( status === undefined ) {
2024-06-07 22:35:06 +00:00
throw new Error ( "Query evaluation returned undefined. Check query syntax and the structure of the response data" ) ;
}
2024-06-06 16:09:35 +00:00
2024-06-07 22:35:06 +00:00
return {
status , // The evaluation of the json query
response // The response from the server or result from initial json-query evaluation
} ;
} catch ( err : any ) {
2024-06-14 22:45:51 +00:00
response = JSON . stringify ( response ) ; // Ensure the response is treated as a string for the console
response = ( response && response . length > 50 ) ? ` ${ response . substring ( 0 , 100 ) } … (truncated) ` : response ; // Truncate long responses to the console
2024-06-07 22:35:06 +00:00
throw new Error ( ` Error evaluating JSON query: ${ err . message } . Response from server was: ${ response } ` ) ;
2024-06-05 22:09:53 +00:00
}
}