2023-03-21 19:03:06 +09:00
//-----------------------------------------------------------
2023-03-22 10:00:57 +09:00
// Search on Users and Machines pages
2023-03-21 19:03:06 +09:00
//-----------------------------------------------------------
2023-03-22 10:31:10 +09:00
function show _search ( ) {
$ ( '#nav-search' ) . removeClass ( 'hidden' ) ;
$ ( '#nav-search' ) . addClass ( 'show' ) ;
$ ( '#nav-content' ) . removeClass ( 'show' ) ;
$ ( '#nav-content' ) . addClass ( 'hidden' ) ;
}
2023-03-21 20:23:53 +09:00
2023-03-22 10:31:10 +09:00
function hide _search ( ) {
$ ( '#nav-content' ) . removeClass ( 'hidden' ) ;
$ ( '#nav-content' ) . addClass ( 'show' ) ;
$ ( '#nav-search' ) . removeClass ( 'show' ) ;
$ ( '#nav-search' ) . addClass ( 'hidden' ) ;
2023-03-22 12:42:16 +09:00
// Also remove the contents of the searchbox:
document . getElementById ( "search" ) . value = ""
2023-03-22 12:48:49 +09:00
let cards = document . querySelectorAll ( '.searchable' ) ;
for ( var i = 0 ; i < cards . length ; i ++ ) {
cards [ i ] . classList . remove ( "hide" ) ;
}
2023-03-22 10:31:10 +09:00
}
function liveSearch ( ) {
let cards = document . querySelectorAll ( '.searchable' ) ;
2023-03-22 11:36:27 +09:00
let search _query = document . getElementById ( "search" ) . value ;
2023-04-21 01:28:56 +08:00
2023-03-22 10:31:10 +09:00
for ( var i = 0 ; i < cards . length ; i ++ ) {
2023-04-21 01:28:56 +08:00
if ( cards [ i ] . textContent . toLowerCase ( ) . includes ( search _query . toLowerCase ( ) ) ) {
2023-03-22 10:31:10 +09:00
cards [ i ] . classList . remove ( "hide" ) ;
} else {
cards [ i ] . classList . add ( "hide" ) ;
2023-03-22 10:00:57 +09:00
}
}
2023-03-22 10:31:10 +09:00
}
2023-02-06 04:58:09 +00:00
//-----------------------------------------------------------
// General Helpers
//-----------------------------------------------------------
2023-04-21 01:28:56 +08:00
function loading ( ) {
2023-02-06 04:58:09 +00:00
return ` <center>
< div class = "preloader-wrapper big active" >
< div class = "spinner-layer spinner-blue-only" >
< div class = "circle-clipper left" >
< div class = "circle" >
< / d i v >
< / d i v >
< div class = "gap-patch" >
< div class = "circle" >
< / d i v >
< / d i v >
< div class = "circle-clipper right" >
< div class = "circle" > < / d i v >
< / d i v >
< / d i v >
< / d i v >
< / c e n t e r > `
}
function get _color ( id ) {
// Define the colors... Seems like a good number to start with
var colors = [
"red lighten-1" ,
"teal lighten-1" ,
"blue lighten-1" ,
"blue-grey lighten-1" ,
"indigo lighten-2" ,
"green lighten-1" ,
"deep-orange lighten-1" ,
"yellow lighten-2" ,
"purple lighten-2" ,
"indigo lighten-2" ,
"brown lighten-1" ,
"grey lighten-1"
] ;
index = id % colors . length
return colors [ index ]
}
// Generic modal used for alerts / problems
function load _modal _generic ( type , title , message ) {
console . log ( "Loading the generic modal" )
2023-04-21 01:28:56 +08:00
element = document . getElementById ( 'generic_modal' )
2023-02-06 04:58:09 +00:00
content _element = document . getElementById ( 'generic_modal_content' )
2023-04-21 01:28:56 +08:00
title _element = document . getElementById ( 'generic_modal_title' )
2023-02-06 04:58:09 +00:00
content _element . innerHTML = loading ( )
title _element . innerHTML = "Loading..."
html = ""
switch ( type ) {
case "warning" || "Warning" :
title _html = "Warning"
2023-04-21 01:28:56 +08:00
content _html = `
2023-02-06 04:58:09 +00:00
< ul class = "collection" >
< li class = "collection-item avatar" >
< i class = "material-icons circle yellow" > priority _high < / i >
< span class = "title" > $ { title } < / s p a n >
< p > $ { message } < / p >
< / l i >
< / u l > `
break ;
case "success" || "Success" :
title _html = "Success"
2023-04-21 01:28:56 +08:00
content _html = `
2023-02-06 04:58:09 +00:00
< ul class = "collection" >
< li class = "collection-item avatar" >
< i class = "material-icons circle green" > check < / i >
< span class = "title" > $ { title } < / s p a n >
< p > $ { message } < / p >
< / l i >
< / u l > `
break ;
2023-04-21 01:28:56 +08:00
case "error" || "Error" :
2023-02-06 04:58:09 +00:00
title _html = "Error"
2023-04-21 01:28:56 +08:00
content _html = `
2023-02-06 04:58:09 +00:00
< ul class = "collection" >
< li class = "collection-item avatar" >
< i class = "material-icons circle red" > warning < / i >
< span class = "title" > $ { title } < / s p a n >
< p > $ { message } < / p >
< / l i >
< / u l > `
break ;
case "information" || "Information" :
title _html = "Information"
2023-04-21 01:28:56 +08:00
content _html = `
2023-02-06 04:58:09 +00:00
< ul class = "collection" >
< li class = "collection-item avatar" >
< i class = "material-icons circle grey" > help < / i >
< span class = "title" > $ { title } < / s p a n >
< p > $ { message } < / p >
< / l i >
< / u l > `
break ;
}
title _element . innerHTML = title _html
content _element . innerHTML = content _html
var instance = M . Modal . getInstance ( element ) ;
instance . open ( )
}
2023-03-17 18:19:28 +09:00
// https://stackoverflow.com/questions/3043775/how-to-escape-html#22706073
2023-04-21 01:28:56 +08:00
function escapeHTML ( str ) {
2023-03-17 18:19:28 +09:00
var p = document . createElement ( "p" ) ;
p . appendChild ( document . createTextNode ( str ) ) ;
return p . innerHTML ;
}
2023-02-06 04:58:09 +00:00
// Enables the Floating Action Button (FAB) for the Machines and Users page
2023-04-21 01:28:56 +08:00
document . addEventListener ( 'DOMContentLoaded' , function ( ) {
2023-02-06 04:58:09 +00:00
var elems = document . querySelectorAll ( '.fixed-action-btn' ) ;
2023-04-21 01:28:56 +08:00
var instances = M . FloatingActionButton . init ( elems , { hoverEnabled : false } ) ;
2023-02-06 04:58:09 +00:00
} ) ;
// Init the date picker when adding PreAuth keys
2023-04-21 01:28:56 +08:00
document . addEventListener ( 'DOMContentLoaded' , function ( ) {
2023-02-06 04:58:09 +00:00
var elems = document . querySelectorAll ( '.datepicker' ) ;
var instances = M . Datepicker . init ( elems ) ;
} ) ;
//-----------------------------------------------------------
// Settings Page Actions
//-----------------------------------------------------------
function test _key ( ) {
document . getElementById ( 'test_modal_results' ) . innerHTML = loading ( )
var data = $ . ajax ( {
2023-05-02 12:25:07 +09:00
type : "GET" ,
2023-02-06 04:58:09 +00:00
url : "api/test_key" ,
2023-04-21 01:28:56 +08:00
success : function ( response ) {
2023-05-02 12:25:07 +09:00
if ( response == "Unauthenticated" ) {
html = `
2023-02-06 04:58:09 +00:00
< ul class = "collection" >
< li class = "collection-item avatar" >
2023-05-02 12:25:07 +09:00
< i class = "material-icons circle red" > warning < / i >
< span class = "title" > Error < / s p a n >
< p > Key authentication failed . Check your key . < / p >
2023-02-06 04:58:09 +00:00
< / l i >
< / u l >
`
2023-05-02 12:25:07 +09:00
document . getElementById ( 'test_modal_results' ) . innerHTML = html
} else {
json = JSON . parse ( response )
var html = `
< ul class = "collection" >
< li class = "collection-item avatar" >
< i class = "material-icons circle green" > check < / i >
< span class = "title" > Success < / s p a n >
< p > Key authenticated with the Headscale server . < / p >
< / l i >
< / u l >
< h6 > Key Information < / h 6 >
< table class = "highlight" >
< tbody >
< tr >
< td > < b > Key ID < / b > < / t d >
< td > $ { json [ 'id' ] } < / t d >
< / t r >
< tr >
< td > < b > Prefix < / b > < / t d >
< td > $ { json [ 'prefix' ] } < / t d >
< / t r >
< tr >
< td > < b > Expiration Date < / b > < / t d >
< td > $ { json [ 'expiration' ] } < / t d >
< / t r >
< tr >
< td > < b > Creation Date < / b > < / t d >
< td > $ { json [ 'createdAt' ] } < / t d >
< / t r >
< / t b o d y >
< / t a b l e >
`
document . getElementById ( 'test_modal_results' ) . innerHTML = html
}
2023-02-06 04:58:09 +00:00
}
} )
}
function save _key ( ) {
var api _key = document . getElementById ( 'api_key' ) . value ;
if ( ! api _key ) {
html = `
< ul class = "collection" >
< li class = "collection-item avatar" >
< i class = "material-icons circle red" > warning < / i >
< span class = "title" > Error < / s p a n >
< p > You must enter an API key before saving . < / p >
< / l i >
< / u l >
`
document . getElementById ( 'test_modal_results' ) . innerHTML = html
return
} ;
2023-04-21 01:28:56 +08:00
var data = { "api_key" : api _key } ;
2023-02-06 04:58:09 +00:00
$ . ajax ( {
2023-04-21 01:28:56 +08:00
type : "POST" ,
2023-02-06 04:58:09 +00:00
url : "api/save_key" ,
data : JSON . stringify ( data ) ,
contentType : "application/json" ,
2023-04-21 01:28:56 +08:00
success : function ( response ) {
2023-05-02 12:25:07 +09:00
M . toast ( { html : 'Key saved. Testing...' } ) ;
2023-02-06 04:58:09 +00:00
test _key ( ) ;
}
} )
}
//-----------------------------------------------------------
// Modal Loaders
//-----------------------------------------------------------
function load _modal _rename _user ( user _id , old _name ) {
document . getElementById ( 'modal_content' ) . innerHTML = loading ( )
document . getElementById ( 'modal_title' ) . innerHTML = "Loading..."
document . getElementById ( 'modal_confirm' ) . className = "green btn-flat white-text"
document . getElementById ( 'modal_confirm' ) . innerText = "Rename"
2023-04-21 01:28:56 +08:00
modal = document . getElementById ( 'card_modal' ) ;
modal _title = document . getElementById ( 'modal_title' ) ;
modal _body = document . getElementById ( 'modal_content' ) ;
2023-02-06 04:58:09 +00:00
modal _confirm = document . getElementById ( 'modal_confirm' ) ;
2023-04-21 01:28:56 +08:00
modal _title . innerHTML = "Rename user '" + old _name + "'?"
2023-02-06 04:58:09 +00:00
body _html = `
< ul class = "collection" >
< li class = "collection-item avatar" >
< i class = "material-icons circle" > language < / i >
< span class = "title" > Information < / s p a n >
< p > You are about to rename the user '${old_name}' < / p >
< / l i >
< / u l >
< h6 > New Name < / h 6 >
< div class = "input-field" >
< i class = "material-icons prefix" > language < / i >
< input value = '${old_name}' id = "new_user_name_form" type = "text" data - length = "32" >
< / d i v >
`
modal _body . innerHTML = body _html
2023-04-21 01:28:56 +08:00
$ ( document ) . ready ( function ( ) { $ ( 'input#new_user_name_form' ) . characterCounter ( ) ; } ) ;
2023-02-06 04:58:09 +00:00
2023-04-21 01:28:56 +08:00
modal _confirm . setAttribute ( 'onclick' , 'rename_user(' + user _id + ', "' + old _name + '")' )
2023-02-06 04:58:09 +00:00
}
function load _modal _delete _user ( user _id , user _name ) {
document . getElementById ( 'modal_content' ) . innerHTML = loading ( )
document . getElementById ( 'modal_title' ) . innerHTML = "Loading..."
document . getElementById ( 'modal_confirm' ) . className = "red btn-flat white-text"
document . getElementById ( 'modal_confirm' ) . innerText = "Delete"
2023-04-21 01:28:56 +08:00
modal = document . getElementById ( 'card_modal' ) ;
modal _title = document . getElementById ( 'modal_title' ) ;
modal _body = document . getElementById ( 'modal_content' ) ;
2023-02-06 04:58:09 +00:00
modal _confirm = document . getElementById ( 'modal_confirm' ) ;
2023-04-21 01:28:56 +08:00
modal _title . innerHTML = "Delete user '" + user _name + "'?"
2023-02-06 04:58:09 +00:00
body _html = `
< ul class = "collection" >
< li class = "collection-item avatar" >
< i class = "material-icons circle red" > warning < / i >
< span class = "title" > Warning < / s p a n >
< p > Are you sure you want to delete the user '${user_name}' ? < / p >
< / l i >
< / u l >
`
modal _body . innerHTML = body _html
2023-04-21 01:28:56 +08:00
modal _confirm . setAttribute ( 'onclick' , 'delete_user("' + user _id + '", "' + user _name + '")' )
2023-02-06 04:58:09 +00:00
}
function load _modal _add _preauth _key ( user _name ) {
document . getElementById ( 'modal_content' ) . innerHTML = loading ( )
document . getElementById ( 'modal_title' ) . innerHTML = "Loading..."
document . getElementById ( 'modal_confirm' ) . className = "green btn-flat white-text"
document . getElementById ( 'modal_confirm' ) . innerText = "Add"
2023-04-21 01:28:56 +08:00
modal = document . getElementById ( 'card_modal' ) ;
modal _title = document . getElementById ( 'modal_title' ) ;
modal _body = document . getElementById ( 'modal_content' ) ;
2023-02-06 04:58:09 +00:00
modal _confirm = document . getElementById ( 'modal_confirm' ) ;
2023-04-21 01:28:56 +08:00
modal _title . innerHTML = "Adding a PreAuth key to '" + user _name + "'"
2023-02-06 04:58:09 +00:00
body _html = `
< ul class = "collection" >
< li class = "collection-item avatar" >
< i class = "material-icons circle" > help < / i >
< span class = "title" > Information < / s p a n >
< p >
< ul >
< li > Pre - Auth keys can be used to authenticate to Headscale without manually registering a machine . Use the flag < code > -- auth - key < / c o d e > t o d o s o . < / l i >
2023-05-02 12:25:07 +09:00
< li > "Ephemeral" keys can be used to register devices that frequently come on and drop off the newtork ( for example , docker containers ) < / l i >
< li > Keys that are "Reusable" can be used multiple times . Keys that are "One Time Use" will expire after their first use . < / l i >
2023-02-06 04:58:09 +00:00
< / u l >
< / p >
< / l i >
< / u l >
< h4 > PreAuth Key Information < / h 4 >
< br >
2023-02-28 21:36:02 +09:00
< input type = "text" class = "datepicker" id = "preauth_key_expiration_date" >
2023-02-06 04:58:09 +00:00
< p >
< label >
< input type = "checkbox" class = "filled-in" id = "checkbox-reusable" / >
< span > Reusable < / s p a n >
< / l a b e l >
< / p >
< p >
< label >
< input type = "checkbox" class = "filled-in" id = "checkbox-ephemeral" / >
< span > Ephemeral < / s p a n >
< / l a b e l >
< / p >
`
modal _body . innerHTML = body _html
// Init the date picker
2023-04-21 01:28:56 +08:00
M . Datepicker . init ( document . querySelector ( '.datepicker' ) , { format : 'yyyy-mm-dd' } ) ;
2023-02-06 04:58:09 +00:00
2023-04-21 01:28:56 +08:00
modal _confirm . setAttribute ( 'onclick' , 'add_preauth_key("' + user _name + '")' )
2023-02-06 04:58:09 +00:00
}
function load _modal _expire _preauth _key ( user _name , key ) {
document . getElementById ( 'modal_content' ) . innerHTML = loading ( )
document . getElementById ( 'modal_title' ) . innerHTML = "Loading..."
document . getElementById ( 'modal_confirm' ) . className = "red lighten-2 btn-flat white-text"
document . getElementById ( 'modal_confirm' ) . innerText = "Expire"
2023-04-21 01:28:56 +08:00
modal = document . getElementById ( 'card_modal' ) ;
modal _title = document . getElementById ( 'modal_title' ) ;
modal _body = document . getElementById ( 'modal_content' ) ;
2023-02-06 04:58:09 +00:00
modal _confirm = document . getElementById ( 'modal_confirm' ) ;
modal _title . innerHTML = "Expire PreAuth Key?"
body _html = `
< ul class = "collection" >
< li class = "collection-item avatar" >
< i class = "material-icons circle red" > warning < / i >
< span class = "title" > Warning < / s p a n >
< p > Are you sure you want to expire this key ? It will become unusable afterwards , and any machine currently using it will disconnect . < / p >
< / l i >
< / u l >
`
modal _body . innerHTML = body _html
2023-04-21 01:28:56 +08:00
modal _confirm . setAttribute ( 'onclick' , 'expire_preauth_key("' + user _name + '", "' + key + '")' )
2023-02-06 04:58:09 +00:00
}
function load _modal _move _machine ( machine _id ) {
document . getElementById ( 'modal_content' ) . innerHTML = loading ( )
document . getElementById ( 'modal_title' ) . innerHTML = "Loading..."
document . getElementById ( 'modal_confirm' ) . className = "green btn-flat white-text"
document . getElementById ( 'modal_confirm' ) . innerText = "Move"
2023-05-02 12:25:07 +09:00
var data = { "id" : machine _id }
2023-02-06 04:58:09 +00:00
$ . ajax ( {
2023-04-21 01:28:56 +08:00
type : "POST" ,
2023-02-06 04:58:09 +00:00
url : "api/machine_information" ,
data : JSON . stringify ( data ) ,
contentType : "application/json" ,
2023-04-21 01:28:56 +08:00
success : function ( headscale ) {
2023-02-06 04:58:09 +00:00
$ . ajax ( {
2023-04-21 01:28:56 +08:00
type : "POST" ,
2023-02-06 04:58:09 +00:00
url : "api/get_users" ,
2023-04-21 01:28:56 +08:00
success : function ( response ) {
modal = document . getElementById ( 'card_modal' ) ;
modal _title = document . getElementById ( 'modal_title' ) ;
modal _body = document . getElementById ( 'modal_content' ) ;
2023-02-06 04:58:09 +00:00
modal _confirm = document . getElementById ( 'modal_confirm' ) ;
2023-04-21 01:28:56 +08:00
modal _title . innerHTML = "Move machine '" + headscale . machine . givenName + "'?"
2023-02-06 04:58:09 +00:00
select _html = ` <h6>Select a User</h6><select id='move-select'> `
2023-04-21 01:28:56 +08:00
for ( let i = 0 ; i < response . users . length ; i ++ ) {
2023-02-06 04:58:09 +00:00
var name = response [ "users" ] [ i ] [ "name" ]
2023-04-21 01:28:56 +08:00
select _html = select _html + ` <option value=" ${ name } "> ${ name } </option> `
2023-02-06 04:58:09 +00:00
}
2023-04-21 01:28:56 +08:00
select _html = select _html + ` </select> `
2023-02-06 04:58:09 +00:00
body _html = `
< ul class = "collection" >
< li class = "collection-item avatar" >
< i class = "material-icons circle" > language < / i >
< span class = "title" > Information < / s p a n >
< p > You are about to move $ { headscale . machine . givenName } to a new user . < / p >
< / l i >
< / u l > `
2023-04-21 01:28:56 +08:00
body _html = body _html + select _html
body _html = body _html + ` <h6>Machine Information</h6>
2023-02-06 04:58:09 +00:00
< table class = "highlight" >
< tbody >
< tr >
< td > < b > Machine ID < / b > < / t d >
< td > $ { headscale . machine . id } < / t d >
< / t r >
< tr >
< td > < b > Hostname < / b > < / t d >
< td > $ { headscale . machine . name } < / t d >
< / t r >
< tr >
< td > < b > User < / b > < / t d >
< td > $ { headscale . machine . user . name } < / t d >
< / t r >
< / t b o d y >
< / t a b l e >
`
modal _body . innerHTML = body _html
M . FormSelect . init ( document . querySelectorAll ( 'select' ) )
}
} )
2023-04-21 01:28:56 +08:00
modal _confirm . setAttribute ( 'onclick' , 'move_machine(' + machine _id + ')' )
2023-02-06 04:58:09 +00:00
}
} )
}
function load _modal _delete _machine ( machine _id ) {
document . getElementById ( 'modal_content' ) . innerHTML = loading ( )
document . getElementById ( 'modal_title' ) . innerHTML = "Loading..."
document . getElementById ( 'modal_confirm' ) . className = "red btn-flat white-text"
document . getElementById ( 'modal_confirm' ) . innerText = "Delete"
2023-05-02 12:25:07 +09:00
var data = { "id" : machine _id }
2023-02-06 04:58:09 +00:00
$ . ajax ( {
2023-04-21 01:28:56 +08:00
type : "POST" ,
2023-02-06 04:58:09 +00:00
url : "api/machine_information" ,
data : JSON . stringify ( data ) ,
contentType : "application/json" ,
2023-04-21 01:28:56 +08:00
success : function ( response ) {
modal = document . getElementById ( 'card_modal' ) ;
modal _title = document . getElementById ( 'modal_title' ) ;
modal _body = document . getElementById ( 'modal_content' ) ;
2023-02-06 04:58:09 +00:00
modal _confirm = document . getElementById ( 'modal_confirm' ) ;
2023-04-21 01:28:56 +08:00
modal _title . innerHTML = "Delete machine '" + response . machine . givenName + "'?"
2023-02-06 04:58:09 +00:00
body _html = `
< ul class = "collection" >
< li class = "collection-item avatar" >
< i class = "material-icons circle red" > warning < / i >
< span class = "title" > Warning < / s p a n >
< p > Are you sure you want to delete $ { response . machine . givenName } ? < / p >
< / l i >
< / u l >
< h6 > Machine Information < / h 6 >
< table class = "highlight" >
< tbody >
< tr >
< td > < b > Machine ID < / b > < / t d >
< td > $ { response . machine . id } < / t d >
< / t r >
< tr >
< td > < b > Hostname < / b > < / t d >
< td > $ { response . machine . name } < / t d >
< / t r >
< tr >
< td > < b > User < / b > < / t d >
< td > $ { response . machine . user . name } < / t d >
< / t r >
< / t b o d y >
< / t a b l e >
`
modal _body . innerHTML = body _html
2023-04-21 01:28:56 +08:00
modal _confirm . setAttribute ( 'onclick' , 'delete_machine(' + machine _id + ')' )
2023-02-06 04:58:09 +00:00
}
} )
}
function load _modal _rename _machine ( machine _id ) {
document . getElementById ( 'modal_content' ) . innerHTML = loading ( )
document . getElementById ( 'modal_title' ) . innerHTML = "Loading..."
document . getElementById ( 'modal_confirm' ) . className = "green btn-flat white-text"
document . getElementById ( 'modal_confirm' ) . innerText = "Rename"
2023-05-02 12:25:07 +09:00
var data = { "id" : machine _id }
2023-02-06 04:58:09 +00:00
$ . ajax ( {
2023-04-21 01:28:56 +08:00
type : "POST" ,
2023-02-06 04:58:09 +00:00
url : "api/machine_information" ,
data : JSON . stringify ( data ) ,
contentType : "application/json" ,
2023-04-21 01:28:56 +08:00
success : function ( response ) {
modal = document . getElementById ( 'card_modal' ) ;
modal _title = document . getElementById ( 'modal_title' ) ;
modal _body = document . getElementById ( 'modal_content' ) ;
2023-02-06 04:58:09 +00:00
modal _confirm = document . getElementById ( 'modal_confirm' ) ;
2023-04-21 01:28:56 +08:00
modal _title . innerHTML = "Rename machine '" + response . machine . givenName + "'?"
2023-02-06 04:58:09 +00:00
body _html = `
< ul class = "collection" >
< li class = "collection-item avatar" >
< i class = "material-icons circle" > devices < / i >
< span class = "title" > Information < / s p a n >
< p > You are about to rename $ { response . machine . givenName } < / p >
< / l i >
< / u l >
< h6 > New Name < / h 6 >
< div class = "input-field" >
< input value = '${response.machine.givenName}' id = "new_name_form" type = "text" >
< label for = "new_name_form" class = "active" > New Machine Name < / l a b e l >
< / d i v >
< h6 > Machine Information < / h 6 >
< table class = "highlight" >
< tbody >
< tr >
< td > < b > Machine ID < / b > < / t d >
< td > $ { response . machine . id } < / t d >
< / t r >
< tr >
< td > < b > Hostname < / b > < / t d >
< td > $ { response . machine . name } < / t d >
< / t r >
< tr >
< td > < b > User < / b > < / t d >
< td > $ { response . machine . user . name } < / t d >
< / t r >
< / t b o d y >
< / t a b l e >
`
modal _body . innerHTML = body _html
2023-04-21 01:28:56 +08:00
modal _confirm . setAttribute ( 'onclick' , 'rename_machine(' + machine _id + ')' )
2023-02-06 04:58:09 +00:00
}
} )
}
function load _modal _add _machine ( ) {
$ . ajax ( {
2023-04-21 01:28:56 +08:00
type : "POST" ,
2023-02-06 04:58:09 +00:00
url : "api/get_users" ,
2023-04-21 01:28:56 +08:00
success : function ( response ) {
2023-02-06 04:58:09 +00:00
modal _body = document . getElementById ( 'default_add_new_machine_modal' ) ;
modal _confirm = document . getElementById ( 'new_machine_modal_confirm' ) ;
select _html = `
< div class = "col s12 m6" >
< div class = "input-field" >
< i class = "material-icons prefix" > language < / i >
< select id = 'add_machine_user_select' >
< option value = "" disabled selected > Select a User < / o p t i o n > `
2023-04-21 01:28:56 +08:00
for ( let i = 0 ; i < response . users . length ; i ++ ) {
2023-02-06 04:58:09 +00:00
var name = response [ "users" ] [ i ] [ "name" ]
2023-04-21 01:28:56 +08:00
select _html = select _html + ` <option value=" ${ name } "> ${ name } </option> `
2023-02-06 04:58:09 +00:00
}
select _html = select _html + `
< label > Select a User < / l a b e l >
< / s e l e c t >
< / d i v >
< / d i v > `
select _html = select _html + `
< div class = "col s12 m6" >
< div class = "input-field" >
< i class = "material-icons prefix" > vpn _key < / i >
< input id = "add_machine_key_field" type = "password" >
< label for = "add_machine_key_field" > Machine Registration Key < / l a b e l >
< / d i v >
< / d i v > `
2023-04-21 01:28:56 +08:00
for ( let i = 0 ; i < response . users . length ; i ++ ) {
2023-02-06 04:58:09 +00:00
var name = response [ "users" ] [ i ] [ "name" ]
}
modal _body . innerHTML = select _html
// Initialize the form and the machine tabs
2023-04-21 01:28:56 +08:00
M . FormSelect . init ( document . querySelectorAll ( 'select' ) , { classes : 'add_machine_selector_class' } )
2023-02-06 04:58:09 +00:00
M . Tabs . init ( document . getElementById ( 'new_machine_tabs' ) ) ;
}
} )
}
//-----------------------------------------------------------
// Machine Page Actions
//-----------------------------------------------------------
function delete _chip ( machine _id , chipsData ) {
// We need to get ALL the current tags -- We don't care about what's deleted, just what's remaining
// chipsData is an array generated from from the creation of the array.
chips = JSON . stringify ( chipsData )
var formattedData = [ ] ;
for ( let tag in chipsData ) {
2023-04-21 01:28:56 +08:00
formattedData [ tag ] = '"tag:' + chipsData [ tag ] . tag + '"'
2023-02-06 04:58:09 +00:00
}
2023-05-02 12:25:07 +09:00
var tags _list = '{"tags": [' + formattedData + ']}'
var data = { "id" : machine _id , "tags_list" : tags _list }
2023-02-06 04:58:09 +00:00
$ . ajax ( {
2023-04-21 01:28:56 +08:00
type : "POST" ,
2023-02-06 04:58:09 +00:00
url : "api/set_machine_tags" ,
data : JSON . stringify ( data ) ,
contentType : "application/json" ,
2023-04-21 01:28:56 +08:00
success : function ( response ) {
M . toast ( { html : 'Tag removed.' } ) ;
2023-02-06 04:58:09 +00:00
}
} )
}
function add _chip ( machine _id , chipsData ) {
chips = JSON . stringify ( chipsData ) . toLowerCase ( )
chipsData [ chipsData . length - 1 ] . tag = chipsData [ chipsData . length - 1 ] . tag . trim ( ) . replace ( /\s+/g , '-' )
last _chip _fixed = chipsData [ chipsData . length - 1 ] . tag
var formattedData = [ ] ;
for ( let tag in chipsData ) {
2023-04-21 01:28:56 +08:00
formattedData [ tag ] = '"tag:' + chipsData [ tag ] . tag + '"'
2023-02-06 04:58:09 +00:00
}
2023-05-02 12:25:07 +09:00
var tags _list = '{"tags": [' + formattedData + ']}'
var data = { "id" : machine _id , "tags_list" : tags _list }
2023-02-06 04:58:09 +00:00
$ . ajax ( {
2023-04-21 01:28:56 +08:00
type : "POST" ,
2023-02-06 04:58:09 +00:00
url : "api/set_machine_tags" ,
data : JSON . stringify ( data ) ,
contentType : "application/json" ,
2023-04-21 01:28:56 +08:00
success : function ( response ) {
M . toast ( { html : 'Tag "' + last _chip _fixed + '" added.' } ) ;
2023-02-06 04:58:09 +00:00
}
} )
}
function add _machine ( ) {
2023-04-21 01:28:56 +08:00
var key = document . getElementById ( 'add_machine_key_field' ) . value
2023-02-06 04:58:09 +00:00
var user = document . getElementById ( 'add_machine_user_select' ) . value
2023-04-21 01:28:56 +08:00
var data = { "key" : key , "user" : user }
2023-02-06 04:58:09 +00:00
if ( user == "" ) {
load _modal _generic ( "error" , "User is empty" , "Select a user before submitting" )
return
}
if ( key == "" ) {
load _modal _generic ( "error" , "Key is empty" , "Input the key generated by your <code>tailscale login</code> command" )
return
}
$ . ajax ( {
2023-04-21 01:28:56 +08:00
type : "POST" ,
2023-02-06 04:58:09 +00:00
url : "api/register_machine" ,
data : JSON . stringify ( data ) ,
contentType : "application/json" ,
2023-04-21 01:28:56 +08:00
success : function ( response ) {
2023-05-02 12:25:07 +09:00
if ( response . machine ) {
window . location . reload ( )
2023-05-02 13:25:46 +08:00
return
2023-05-02 12:25:07 +09:00
}
load _modal _generic ( "error" , "Error adding machine" , response . message )
return
2023-02-06 04:58:09 +00:00
}
} )
}
function rename _machine ( machine _id ) {
var new _name = document . getElementById ( 'new_name_form' ) . value ;
2023-05-02 12:25:07 +09:00
var data = { "id" : machine _id , "new_name" : new _name } ;
2023-02-06 04:58:09 +00:00
// String to test against
2023-04-21 01:28:56 +08:00
var regexIT = /[`!@#$%^&*()_+\=\[\]{};':"\\|,.<>\/?~]/ ;
if ( regexIT . test ( new _name ) ) { load _modal _generic ( "error" , "Invalid Name" , "Name cannot contain special characters ('" + regexIT + "')" ) ; return }
2023-02-06 04:58:09 +00:00
// If there are characters other than - and alphanumeric, throw an error
2023-04-21 01:28:56 +08:00
if ( new _name . includes ( ' ' ) ) { load _modal _generic ( "error" , "Name cannot have spaces" , "Allowed characters are dashes (-) and alphanumeric characters" ) ; return }
2023-02-06 04:58:09 +00:00
// If it is longer than 32 characters, throw an error
if ( new _name . length > 32 ) { load _modal _generic ( "error" , "Name is too long" , "The name name is too long. Maximum length is 32 characters" ) ; return }
// If the new_name is empty, throw an error
2023-04-21 01:28:56 +08:00
if ( ! new _name ) { load _modal _generic ( "error" , "Name can't be empty" , "Please enter a machine name before submitting." ) ; return }
2023-02-06 04:58:09 +00:00
$ . ajax ( {
2023-04-21 01:28:56 +08:00
type : "POST" ,
2023-02-06 04:58:09 +00:00
url : "api/rename_machine" ,
data : JSON . stringify ( data ) ,
contentType : "application/json" ,
2023-04-21 01:28:56 +08:00
success : function ( response ) {
2023-02-06 04:58:09 +00:00
2023-05-02 12:25:07 +09:00
if ( response . status == "True" ) {
// Get the modal element and close it
modal _element = document . getElementById ( 'card_modal' )
M . Modal . getInstance ( modal _element ) . close ( )
document . getElementById ( machine _id + '-name-container' ) . innerHTML = machine _id + ". " + escapeHTML ( new _name )
M . toast ( { html : 'Machine ' + machine _id + ' renamed to ' + escapeHTML ( new _name ) } ) ;
} else {
load _modal _generic ( "error" , "Error setting the machine name" , "Headscale response: " + JSON . stringify ( response . body . message ) )
}
2023-02-06 04:58:09 +00:00
}
} )
}
function move _machine ( machine _id ) {
new _user = document . getElementById ( 'move-select' ) . value
2023-05-02 12:25:07 +09:00
var data = { "id" : machine _id , "new_user" : new _user } ;
2023-02-06 04:58:09 +00:00
$ . ajax ( {
2023-04-21 01:28:56 +08:00
type : "POST" ,
2023-02-06 04:58:09 +00:00
url : "api/move_user" ,
data : JSON . stringify ( data ) ,
contentType : "application/json" ,
2023-04-21 01:28:56 +08:00
success : function ( response ) {
2023-02-06 04:58:09 +00:00
// Get the modal element and close it
modal _element = document . getElementById ( 'card_modal' )
M . Modal . getInstance ( modal _element ) . close ( )
2023-04-21 01:28:56 +08:00
document . getElementById ( machine _id + '-user-container' ) . innerHTML = response . machine . user . name
document . getElementById ( machine _id + '-ns-badge' ) . innerHTML = response . machine . user . name
2023-02-06 04:58:09 +00:00
// Get the color and set it
var user _color = get _color ( response . machine . user . id )
2023-04-21 01:28:56 +08:00
document . getElementById ( machine _id + '-ns-badge' ) . className = "badge ipinfo " + user _color + " white-text hide-on-small-only"
2023-02-06 04:58:09 +00:00
2023-04-21 01:28:56 +08:00
M . toast ( { html : "'" + response . machine . givenName + "' moved to user " + response . machine . user . name } ) ;
2023-02-06 04:58:09 +00:00
}
} )
}
function delete _machine ( machine _id ) {
2023-05-02 12:25:07 +09:00
var data = { "id" : machine _id } ;
2023-02-06 04:58:09 +00:00
$ . ajax ( {
2023-04-21 01:28:56 +08:00
type : "POST" ,
2023-02-06 04:58:09 +00:00
url : "api/delete_machine" ,
data : JSON . stringify ( data ) ,
contentType : "application/json" ,
2023-04-21 01:28:56 +08:00
success : function ( response ) {
2023-02-06 04:58:09 +00:00
// Get the modal element and close it
modal _element = document . getElementById ( 'card_modal' )
M . Modal . getInstance ( modal _element ) . close ( )
// When the machine is deleted, hide its collapsible:
2023-04-21 01:28:56 +08:00
document . getElementById ( machine _id + '-main-collapsible' ) . className = "collapsible popout hide" ;
2023-02-06 04:58:09 +00:00
2023-04-21 01:28:56 +08:00
M . toast ( { html : 'Machine deleted.' } ) ;
2023-02-06 04:58:09 +00:00
}
} )
}
2023-03-24 14:12:21 +09:00
function toggle _exit ( route1 , route2 , exit _id , current _state , page ) {
2023-04-21 01:28:56 +08:00
var data1 = { "route_id" : route1 , "current_state" : current _state }
var data2 = { "route_id" : route2 , "current_state" : current _state }
2023-03-24 14:12:21 +09:00
var element = document . getElementById ( exit _id ) ;
var disabledClass = ""
2023-04-21 01:28:56 +08:00
var enabledClass = ""
2023-03-24 14:12:21 +09:00
if ( page == "machines" ) {
disabledClass = "waves-effect waves-light btn-small red lighten-2 tooltipped" ;
2023-04-21 01:28:56 +08:00
enabledClass = "waves-effect waves-light btn-small green lighten-2 tooltipped" ;
2023-03-24 14:12:21 +09:00
}
if ( page == "routes" ) {
disabledClass = "material-icons red-text text-lighten-2 tooltipped" ;
2023-04-21 01:28:56 +08:00
enabledClass = "material-icons green-text text-lighten-2 tooltipped" ;
2023-03-24 14:12:21 +09:00
}
var disabledTooltip = "Click to enable"
2023-04-21 01:28:56 +08:00
var enabledTooltip = "Click to disable"
var disableState = "False"
var enableState = "True"
var action _taken = "unchanged." ;
2023-03-24 14:12:21 +09:00
2023-03-22 14:31:30 +09:00
$ . ajax ( {
2023-04-21 01:28:56 +08:00
type : "POST" ,
2023-03-22 14:31:30 +09:00
url : "api/update_route" ,
data : JSON . stringify ( data1 ) ,
contentType : "application/json" ,
2023-04-21 01:28:56 +08:00
success : function ( response ) {
2023-03-22 14:31:30 +09:00
$ . ajax ( {
2023-04-21 01:28:56 +08:00
type : "POST" ,
2023-03-22 14:31:30 +09:00
url : "api/update_route" ,
data : JSON . stringify ( data2 ) ,
contentType : "application/json" ,
2023-04-21 01:28:56 +08:00
success : function ( response ) {
2023-03-22 14:31:30 +09:00
// Response is a JSON object containing the Headscale API response of /v1/api/machines/<id>/route
if ( element . className == disabledClass ) {
2023-04-21 01:28:56 +08:00
element . className = enabledClass
action _taken = "enabled."
2023-03-22 14:31:30 +09:00
element . setAttribute ( 'data-tooltip' , enabledTooltip )
2023-04-21 01:28:56 +08:00
element . setAttribute ( 'onclick' , 'toggle_exit(' + route1 + ', ' + route2 + ', "' + exit _id + '", "' + enableState + '", "' + page + '")' )
2023-03-22 14:31:30 +09:00
} else if ( element . className == enabledClass ) {
2023-04-21 01:28:56 +08:00
element . className = disabledClass
action _taken = "disabled."
2023-03-22 14:31:30 +09:00
element . setAttribute ( 'data-tooltip' , disabledTooltip )
2023-04-21 01:28:56 +08:00
element . setAttribute ( 'onclick' , 'toggle_exit(' + route1 + ', ' + route2 + ', "' + exit _id + '", "' + disableState + '", "' + page + '")' )
2023-03-22 14:31:30 +09:00
}
2023-04-21 01:28:56 +08:00
M . toast ( { html : 'Exit Route ' + action _taken } ) ;
2023-03-22 14:31:30 +09:00
}
} )
}
} )
2023-03-22 13:58:51 +09:00
}
2023-03-24 12:10:38 +09:00
function toggle _route ( route _id , current _state , page ) {
2023-04-21 01:28:56 +08:00
var data = { "route_id" : route _id , "current_state" : current _state }
2023-03-24 11:49:49 +09:00
var element = document . getElementById ( route _id ) ;
2023-03-24 12:53:11 +09:00
2023-03-24 12:18:04 +09:00
var disabledClass = ""
2023-04-21 01:28:56 +08:00
var enabledClass = ""
2023-03-24 12:18:04 +09:00
2023-03-24 12:24:15 +09:00
if ( page == "machines" ) {
2023-03-24 13:02:32 +09:00
disabledClass = "waves-effect waves-light btn-small red lighten-2 tooltipped" ;
2023-04-21 01:28:56 +08:00
enabledClass = "waves-effect waves-light btn-small green lighten-2 tooltipped" ;
2023-03-24 13:02:32 +09:00
}
if ( page == "routes" ) {
2023-03-24 12:53:11 +09:00
disabledClass = "material-icons red-text text-lighten-2 tooltipped" ;
2023-04-21 01:28:56 +08:00
enabledClass = "material-icons green-text text-lighten-2 tooltipped" ;
2023-03-24 11:48:12 +09:00
}
2023-03-24 12:53:11 +09:00
2023-03-24 11:48:12 +09:00
var disabledTooltip = "Click to enable"
2023-04-21 01:28:56 +08:00
var enabledTooltip = "Click to disable"
var disableState = "False"
var enableState = "True"
var action _taken = "unchanged." ;
2023-03-24 08:16:44 +09:00
$ . ajax ( {
2023-04-21 01:28:56 +08:00
type : "POST" ,
2023-03-24 08:16:44 +09:00
url : "api/update_route" ,
data : JSON . stringify ( data ) ,
contentType : "application/json" ,
2023-04-21 01:28:56 +08:00
success : function ( response ) {
2023-03-24 08:16:44 +09:00
if ( element . className == disabledClass ) {
2023-03-24 11:48:12 +09:00
element . className = enabledClass
2023-04-21 01:28:56 +08:00
action _taken = "enabled."
2023-03-24 08:16:44 +09:00
element . setAttribute ( 'data-tooltip' , enabledTooltip )
2023-04-21 01:28:56 +08:00
element . setAttribute ( 'onclick' , 'toggle_route(' + route _id + ', "' + enableState + '", "' + page + '")' )
2023-03-24 08:16:44 +09:00
} else if ( element . className == enabledClass ) {
2023-03-24 11:48:12 +09:00
element . className = disabledClass
2023-04-21 01:28:56 +08:00
action _taken = "disabled."
2023-03-24 08:16:44 +09:00
element . setAttribute ( 'data-tooltip' , disabledTooltip )
2023-04-21 01:28:56 +08:00
element . setAttribute ( 'onclick' , 'toggle_route(' + route _id + ', "' + disableState + '", "' + page + '")' )
2023-03-24 08:16:44 +09:00
}
2023-04-21 01:28:56 +08:00
M . toast ( { html : 'Route ' + action _taken } ) ;
2023-03-24 08:16:44 +09:00
}
} )
}
2023-03-24 11:48:12 +09:00
2023-03-29 13:59:34 +09:00
function get _routes ( ) {
2023-03-30 10:01:49 +09:00
console . log ( "Getting info for all routes" )
2023-03-30 11:38:53 +09:00
var data
2023-03-29 13:59:34 +09:00
$ . ajax ( {
2023-03-30 11:26:10 +09:00
async : false ,
2023-04-21 01:28:56 +08:00
type : "POST" ,
2023-03-29 13:59:34 +09:00
url : "api/get_routes" ,
contentType : "application/json" ,
2023-04-21 01:28:56 +08:00
success : function ( response ) {
2023-03-30 11:09:04 +09:00
console . log ( "Got all routes." )
2023-03-30 11:38:53 +09:00
data = response
2023-03-29 13:59:34 +09:00
}
} )
2023-03-30 11:38:53 +09:00
return data
2023-03-29 13:59:34 +09:00
}
2023-03-30 09:47:27 +09:00
function toggle _failover _route _routespage ( routeid , current _state , prefix , route _id _list ) {
2023-03-29 13:59:34 +09:00
// First, toggle the route:
2023-03-29 17:58:52 +09:00
// toggle_route(route_id, current_state, page)
2023-04-21 01:28:56 +08:00
var data = { "route_id" : routeid , "current_state" : current _state }
console . log ( "Data: " + JSON . stringify ( data ) )
console . log ( "Passed in: " + routeid + ", " + current _state + ", " + prefix + ", " + route _id _list )
2023-03-30 09:47:27 +09:00
var element = document . getElementById ( routeid ) ;
2023-03-29 17:58:52 +09:00
var disabledClass = "material-icons red-text text-lighten-2 tooltipped" ;
2023-04-21 01:28:56 +08:00
var enabledClass = "material-icons green-text text-lighten-2 tooltipped" ;
2023-03-30 13:01:59 +09:00
var failover _disabledClass = "material-icons small left red-text text-lighten-2"
2023-04-21 01:28:56 +08:00
var failover _enabledClass = "material-icons small left green-text text-lighten-2"
2023-03-29 17:58:52 +09:00
var disabledTooltip = "Click to enable"
2023-04-21 01:28:56 +08:00
var enabledTooltip = "Click to disable"
2023-05-02 12:25:07 +09:00
var disableState = "False"
var enableState = "True"
2023-04-21 01:28:56 +08:00
var action _taken = "unchanged."
2023-03-30 10:00:20 +09:00
2023-03-29 17:58:52 +09:00
$ . ajax ( {
2023-04-21 01:28:56 +08:00
type : "POST" ,
2023-03-29 17:58:52 +09:00
url : "api/update_route" ,
data : JSON . stringify ( data ) ,
contentType : "application/json" ,
2023-04-21 01:28:56 +08:00
success : function ( response ) {
console . log ( "Success: Route ID: " + routeid )
console . log ( "Success: route_id_list: " + route _id _list )
2023-03-29 17:58:52 +09:00
if ( element . className == disabledClass ) {
element . className = enabledClass
2023-04-21 01:28:56 +08:00
action _taken = "enabled."
2023-03-29 17:58:52 +09:00
element . setAttribute ( 'data-tooltip' , enabledTooltip )
2023-04-21 01:28:56 +08:00
element . setAttribute ( 'onclick' , 'toggle_failover_route_routespage(' + routeid + ', "' + enableState + '", "' + prefix + '", [' + route _id _list + '])' )
2023-03-29 17:58:52 +09:00
} else if ( element . className == enabledClass ) {
element . className = disabledClass
2023-04-21 01:28:56 +08:00
action _taken = "disabled."
2023-03-29 17:58:52 +09:00
element . setAttribute ( 'data-tooltip' , disabledTooltip )
2023-04-21 01:28:56 +08:00
element . setAttribute ( 'onclick' , 'toggle_failover_route_routespage(' + routeid + ', "' + disableState + '", "' + prefix + '", [' + route _id _list + '])' )
2023-03-29 17:58:52 +09:00
}
2023-04-21 01:28:56 +08:00
M . toast ( { html : 'Route ' + action _taken } ) ;
2023-03-29 17:58:52 +09:00
2023-03-30 10:38:40 +09:00
// Get all route info:
2023-04-21 01:28:56 +08:00
console . log ( "Getting info for prefix " + prefix )
2023-03-29 17:58:52 +09:00
var routes = get _routes ( )
2023-03-30 13:20:18 +09:00
var failover _enabled = false
2023-03-30 10:38:40 +09:00
// Get the primary and enabled displays for the prefix:
2023-04-21 01:28:56 +08:00
for ( let i = 0 ; i < route _id _list . length ; i ++ ) {
console . log ( "route_id_list[" + i + "]: " + route _id _list [ i ] )
2023-03-30 12:33:37 +09:00
var route _id = route _id _list [ i ]
2023-04-21 01:28:56 +08:00
var route _index = route _id - 1
console . log ( "Looking for route " + route _id + " at index " + route _index )
console . log ( "isPrimary: " + routes [ "routes" ] [ route _index ] [ "isPrimary" ] )
2023-03-30 12:33:37 +09:00
2023-03-30 10:38:40 +09:00
// Set the Primary class:
2023-04-21 01:28:56 +08:00
var primary _element = document . getElementById ( route _id + "-primary" )
var primary _status = routes [ "routes" ] [ route _index ] [ "isPrimary" ]
var enabled _status = routes [ "routes" ] [ route _index ] [ "enabled" ]
2023-03-30 12:33:37 +09:00
2023-04-21 01:28:56 +08:00
console . log ( "enabled_status: " + enabled _status )
2023-03-30 13:01:59 +09:00
2023-03-30 12:57:35 +09:00
if ( enabled _status == true ) {
2023-03-30 13:13:26 +09:00
failover _enabled = true
2023-03-30 12:57:35 +09:00
}
2023-04-21 01:28:56 +08:00
console . log ( "Setting primary class '" + route _id + "-primary': " + primary _status )
2023-03-30 12:03:55 +09:00
if ( primary _status == true ) {
2023-03-30 10:48:41 +09:00
console . log ( "Detected this route is primary. Setting the class" )
2023-03-30 10:38:40 +09:00
primary _element . className = enabledClass
2023-03-30 12:03:55 +09:00
} else if ( primary _status == false ) {
2023-03-30 10:48:41 +09:00
console . log ( "Detected this route is NOT primary. Setting the class" )
2023-03-30 10:38:40 +09:00
primary _element . className = disabledClass
}
2023-03-30 12:57:35 +09:00
}
// if any route is enabled, set the prefix enable icon to enabled:
var failover _element = document . getElementById ( prefix )
2023-04-21 01:28:56 +08:00
console . log ( "Failover enabled: " + failover _enabled )
2023-03-30 12:57:35 +09:00
if ( failover _enabled == true ) {
2023-03-30 13:01:59 +09:00
failover _element . className = failover _enabledClass
2023-03-30 12:57:35 +09:00
}
else if ( failover _enabled == false ) {
2023-03-30 13:01:59 +09:00
failover _element . className = failover _disabledClass
2023-03-29 17:58:52 +09:00
}
}
} )
}
2023-03-29 13:59:34 +09:00
2023-03-22 21:07:22 +09:00
function toggle _failover _route ( route _id , current _state , color ) {
2023-04-21 01:28:56 +08:00
var data = { "route_id" : route _id , "current_state" : current _state }
2023-03-22 21:07:22 +09:00
$ . ajax ( {
2023-04-21 01:28:56 +08:00
type : "POST" ,
2023-03-22 21:07:22 +09:00
url : "api/update_route" ,
data : JSON . stringify ( data ) ,
contentType : "application/json" ,
2023-04-21 01:28:56 +08:00
success : function ( response ) {
2023-03-22 21:07:22 +09:00
// Response is a JSON object containing the Headscale API response of /v1/api/machines/<id>/route
2023-04-21 01:28:56 +08:00
var element = document . getElementById ( route _id ) ;
var disabledClass = "waves-effect waves-light btn-small red lighten-2 tooltipped" ;
var enabledClass = "waves-effect waves-light btn-small " + color + " lighten-2 tooltipped" ;
2023-03-22 22:07:12 +09:00
var disabledTooltip = "Click to enable (Failover Pair)"
2023-04-21 01:28:56 +08:00
var enabledTooltip = "Click to disable (Failover Pair)"
var disableState = "False"
var enableState = "True"
var action _taken = "unchanged." ;
2023-02-06 04:58:09 +00:00
if ( element . className == disabledClass ) {
// 1. Change the class to change the color of the icon
// 2. Change the "action taken" for the M.toast popup
// 3. Change the tooltip to say "Click to enable/disable"
2023-04-21 01:28:56 +08:00
element . className = enabledClass
var action _taken = "enabled."
2023-02-06 04:58:09 +00:00
element . setAttribute ( 'data-tooltip' , enabledTooltip )
2023-04-21 01:28:56 +08:00
element . setAttribute ( 'onclick' , 'toggle_failover_route(' + route _id + ', "' + enableState + '", "' + color + '")' )
2023-02-06 04:58:09 +00:00
} else if ( element . className == enabledClass ) {
2023-04-21 01:28:56 +08:00
element . className = disabledClass
var action _taken = "disabled."
2023-02-06 04:58:09 +00:00
element . setAttribute ( 'data-tooltip' , disabledTooltip )
2023-04-21 01:28:56 +08:00
element . setAttribute ( 'onclick' , 'toggle_failover_route(' + route _id + ', "' + disableState + '", "' + color + '")' )
2023-02-06 04:58:09 +00:00
}
2023-04-21 01:28:56 +08:00
M . toast ( { html : 'Route ' + action _taken } ) ;
2023-02-06 04:58:09 +00:00
}
} )
}
//-----------------------------------------------------------
// Machine Page Helpers
//-----------------------------------------------------------
function btn _toggle ( state ) {
2023-04-21 01:28:56 +08:00
if ( state == "show" ) { document . getElementById ( 'new_machine_modal_confirm' ) . className = 'green btn-flat white-text' }
else { document . getElementById ( 'new_machine_modal_confirm' ) . className = 'green btn-flat white-text hide' }
2023-02-06 04:58:09 +00:00
}
//-----------------------------------------------------------
// User Page Actions
//-----------------------------------------------------------
function rename _user ( user _id , old _name ) {
var new _name = document . getElementById ( 'new_user_name_form' ) . value ;
2023-04-21 01:28:56 +08:00
var data = { "old_name" : old _name , "new_name" : new _name }
2023-02-06 04:58:09 +00:00
// String to test against
2023-04-21 01:28:56 +08:00
var regexIT = /[`!@#$%^&*()_+\=\[\]{};':"\\|,.<>\/?~]/ ;
if ( regexIT . test ( new _name ) ) { load _modal _generic ( "error" , "Invalid Name" , "Name cannot contain special characters ('" + regexIT + "')" ) ; return }
2023-02-06 04:58:09 +00:00
// If there are characters other than - and alphanumeric, throw an error
2023-04-21 01:28:56 +08:00
if ( new _name . includes ( ' ' ) ) { load _modal _generic ( "error" , "Name cannot have spaces" , "Allowed characters are dashes (-) and alphanumeric characters" ) ; return }
2023-02-06 04:58:09 +00:00
// If it is longer than 32 characters, throw an error
if ( new _name . length > 32 ) { load _modal _generic ( "error" , "Name is too long" , "The user name is too long. Maximum length is 32 characters" ) ; return }
// If the new_name is empty, throw an error
2023-04-21 01:28:56 +08:00
if ( ! new _name ) { load _modal _generic ( "error" , "Name can't be empty" , "The user name cannot be empty." ) ; return }
2023-02-06 04:58:09 +00:00
$ . ajax ( {
2023-04-21 01:28:56 +08:00
type : "POST" ,
2023-02-06 04:58:09 +00:00
url : "api/rename_user" ,
data : JSON . stringify ( data ) ,
contentType : "application/json" ,
2023-04-21 01:28:56 +08:00
success : function ( response ) {
2023-05-02 12:25:07 +09:00
if ( response . status == "True" ) {
// Get the modal element and close it
modal _element = document . getElementById ( 'card_modal' )
M . Modal . getInstance ( modal _element ) . close ( )
// Rename the user on the page:
document . getElementById ( user _id + '-name-span' ) . innerHTML = escapeHTML ( new _name )
// Set the button to use the NEW name as the OLD name for both buttons
var rename _button _sm = document . getElementById ( user _id + '-rename-user-sm' )
rename _button _sm . setAttribute ( 'onclick' , 'load_modal_rename_user(' + user _id + ', "' + new _name + '")' )
var rename _button _lg = document . getElementById ( user _id + '-rename-user-lg' )
rename _button _lg . setAttribute ( 'onclick' , 'load_modal_rename_user(' + user _id + ', "' + new _name + '")' )
// Send the completion toast
M . toast ( { html : "User '" + old _name + "' renamed to '" + new _name + "'." } )
} else {
load _modal _generic ( "error" , "Error setting user name" , "Headscale response: " + JSON . stringify ( response . body . message ) )
}
2023-02-06 04:58:09 +00:00
}
} )
}
function delete _user ( user _id , user _name ) {
2023-04-21 01:28:56 +08:00
var data = { "name" : user _name } ;
2023-02-06 04:58:09 +00:00
$ . ajax ( {
2023-04-21 01:28:56 +08:00
type : "POST" ,
2023-02-06 04:58:09 +00:00
url : "api/delete_user" ,
data : JSON . stringify ( data ) ,
contentType : "application/json" ,
2023-04-21 01:28:56 +08:00
success : function ( response ) {
2023-05-02 12:25:07 +09:00
if ( response . status == "True" ) {
// Get the modal element and close it
modal _element = document . getElementById ( 'card_modal' )
M . Modal . getInstance ( modal _element ) . close ( )
// When the machine is deleted, hide its collapsible:
document . getElementById ( user _id + '-main-collapsible' ) . className = "collapsible popout hide" ;
M . toast ( { html : 'User deleted.' } ) ;
} else {
// We errored. Decipher the error Headscale sent us and display it:
load _modal _generic ( "error" , "Error deleting user" , "Headscale response: " + JSON . stringify ( response . body . message ) )
}
2023-02-06 04:58:09 +00:00
}
} )
}
function add _user ( ) {
var user _name = document . getElementById ( 'add_user_name_field' ) . value
2023-04-21 01:28:56 +08:00
var data = { "name" : user _name }
2023-02-06 04:58:09 +00:00
$ . ajax ( {
2023-04-21 01:28:56 +08:00
type : "POST" ,
2023-02-06 04:58:09 +00:00
url : "api/add_user" ,
data : JSON . stringify ( data ) ,
contentType : "application/json" ,
2023-04-21 01:28:56 +08:00
success : function ( response ) {
2023-05-02 12:25:07 +09:00
if ( response . status == "True" ) {
// Get the modal element and close it
modal _element = document . getElementById ( 'card_modal' )
M . Modal . getInstance ( modal _element ) . close ( )
// Send the completion toast
M . toast ( { html : "User '" + user _name + "' added to Headscale. Refreshing..." } )
window . location . reload ( )
} else {
// We errored. Decipher the error Headscale sent us and display it:
load _modal _generic ( "error" , "Error adding user" , "Headscale response: " + JSON . stringify ( response . body . message ) )
}
2023-02-06 04:58:09 +00:00
}
} )
}
function add _preauth _key ( user _name ) {
2023-04-21 01:28:56 +08:00
var date = document . getElementById ( 'preauth_key_expiration_date' ) . value
var ephemeral = document . getElementById ( 'checkbox-ephemeral' ) . checked
var reusable = document . getElementById ( 'checkbox-reusable' ) . checked
var expiration = date + "T00:00:00.000Z" // Headscale format.
2023-02-06 04:58:09 +00:00
// If there is no date, error:
2023-04-21 01:28:56 +08:00
if ( ! date ) { load _modal _generic ( "error" , "Invalid Date" , "Please enter a valid date" ) ; return }
2023-05-02 12:25:07 +09:00
var data = { "user" : user _name , "reusable" : reusable , "ephemeral" : ephemeral , "expiration" : expiration }
2023-02-06 04:58:09 +00:00
$ . ajax ( {
2023-04-21 01:28:56 +08:00
type : "POST" ,
2023-02-06 04:58:09 +00:00
url : "api/add_preauth_key" ,
data : JSON . stringify ( data ) ,
contentType : "application/json" ,
2023-04-21 01:28:56 +08:00
success : function ( response ) {
2023-05-02 12:25:07 +09:00
if ( response . status == "True" ) {
// Send the completion toast
M . toast ( { html : 'PreAuth key created in user ' + user _name } )
// If this is successfull, we should reload the table and close the modal:
var user _data = { "name" : user _name }
$ . ajax ( {
type : "POST" ,
url : "api/build_preauthkey_table" ,
data : JSON . stringify ( user _data ) ,
contentType : "application/json" ,
success : function ( table _data ) {
table = document . getElementById ( user _name + '-preauth-keys-collection' )
table . innerHTML = table _data
// The tooltips need to be re-initialized afterwards:
M . Tooltip . init ( document . querySelectorAll ( '.tooltipped' ) )
}
} )
// Get the modal element and close it
modal _element = document . getElementById ( 'card_modal' )
M . Modal . getInstance ( modal _element ) . close ( )
// The tooltips need to be re-initialized afterwards:
M . Tooltip . init ( document . querySelectorAll ( '.tooltipped' ) )
2023-02-06 04:58:09 +00:00
2023-05-02 12:25:07 +09:00
} else {
load _modal _generic ( "error" , "Error adding a pre-auth key" , "Headscale response: " + JSON . stringify ( response . body . message ) )
}
2023-02-06 04:58:09 +00:00
}
} )
}
function expire _preauth _key ( user _name , key ) {
2023-04-21 01:28:56 +08:00
var data = { "user" : user _name , "key" : key }
2023-02-06 04:58:09 +00:00
$ . ajax ( {
2023-04-21 01:28:56 +08:00
type : "POST" ,
2023-02-06 04:58:09 +00:00
url : "api/expire_preauth_key" ,
data : JSON . stringify ( data ) ,
contentType : "application/json" ,
2023-04-21 01:28:56 +08:00
success : function ( response ) {
2023-05-02 12:25:07 +09:00
if ( response . status == "True" ) {
// Send the completion toast
M . toast ( { html : 'PreAuth expired in ' + user _name } )
// If this is successfull, we should reload the table and close the modal:
var user _data = { "name" : user _name }
$ . ajax ( {
type : "POST" ,
url : "api/build_preauthkey_table" ,
data : JSON . stringify ( user _data ) ,
contentType : "application/json" ,
success : function ( table _data ) {
table = document . getElementById ( user _name + '-preauth-keys-collection' )
table . innerHTML = table _data
// The tooltips need to be re-initialized afterwards:
M . Tooltip . init ( document . querySelectorAll ( '.tooltipped' ) )
}
} )
// Get the modal element and close it
modal _element = document . getElementById ( 'card_modal' )
M . Modal . getInstance ( modal _element ) . close ( )
2023-02-06 04:58:09 +00:00
2023-05-02 12:25:07 +09:00
// The tooltips need to be re-initialized afterwards:
M . Tooltip . init ( document . querySelectorAll ( '.tooltipped' ) )
} else {
load _modal _generic ( "error" , "Error expiring a pre-auth key" , "Headscale response: " + JSON . stringify ( response . body . message ) )
}
2023-02-06 04:58:09 +00:00
}
} )
}
//-----------------------------------------------------------
// User Page Helpers
//-----------------------------------------------------------
// Toggle expired items on the Users PreAuth section:
function toggle _expired ( ) {
var toggle _hide = document . getElementsByClassName ( 'expired-row' ) ;
2023-04-21 01:28:56 +08:00
var hidden = document . getElementsByClassName ( 'expired-row hide' ) ;
2023-02-06 04:58:09 +00:00
if ( hidden . length == 0 ) {
for ( var i = 0 ; i < toggle _hide . length ; i ++ ) {
toggle _hide [ i ] . className = "expired-row hide" ;
}
} else if ( hidden . length > 0 ) {
for ( var i = 0 ; i < toggle _hide . length ; i ++ ) {
toggle _hide [ i ] . className = "expired-row" ;
}
}
}
2023-02-10 14:01:31 +09:00
// Copy a PreAuth Key to the clipboard. Show only the Prefix by default
function copy _preauth _key ( key ) {
navigator . clipboard . writeText ( key ) ;
2023-04-21 01:28:56 +08:00
M . toast ( { html : 'PreAuth key copied to clipboard.' } )
2023-02-10 14:01:31 +09:00
}