Erik Thuning 670c88c7f3 Added display of expiration date to the user interface
Also stopped ever displaying creation time and rearranged some CSS
2025-03-26 15:34:55 +01:00

270 lines
11 KiB
JavaScript

'use strict';
(function() {
window.addEventListener('DOMContentLoaded', (event) => {
setup_page();
});
function close_modal(node) {
let current = node;
while(current.nodeName != 'BACKDROP') {
current = current.parentNode;
if(current.nodeName === 'HTML') {
console.error("can't find modal parent");
return;
}
}
current.parentNode.removeChild(current);
}
function deny_access() {
const topbox = document.querySelector('topbox');
while(topbox.firstChild) {
topbox.removeChild(topbox.lastChild);
}
const placeholder = document.querySelector('placeholder');
placeholder.replaceWith('');
const template = document.querySelector('template#access-denied')
.content.cloneNode(true);
topbox.appendChild(template);
}
function display_create_form() {
const template = document.querySelector('template#create-form')
.content.cloneNode(true);
const form = template.querySelector('form');
form.addEventListener('submit', (event) => {
event.preventDefault();
const id = crypto.randomUUID();
const name = form.name.value;
if(!name) {
return;
}
const description = form.description.value.trim();
make_api_request('POST',
'/configs/' + id + '/create',
{'name': name,
'description': description})
.then((response) => {
if(response.result == 'success') {
display_configs(id);
close_modal(form);
} else {
throw new Error(response.reason);
}
})
.catch((exception) => {
console.error('creation failed for: '+id, exception);
});
});
display_modal(template, form.name);
}
function display_configs(...config_ids) {
const configs_parent = document.querySelector('configs');
const dlprefix = 'data:text/plain;charset:utf-8,';
config_ids.forEach((config_id) => {
const old = configs_parent.querySelector('#config-'+config_id);
if(old) {
old.parentNode.removeChild(old);
}
const template = document.querySelector('template#display-config')
.content.cloneNode(true);
const config = template.querySelector('config');
make_api_request('GET', '/configs/' + config_id)
.then((data) => {
const qr = QRCode({msg: data.data,
dim: 280,
pad: 0,
ecl: 'M'});
qr.setAttribute('aria-label', 'QR code');
config.id = 'config-' + config_id;
config.querySelector('name').textContent = data.name;
config.querySelector('description').textContent = data.description;
config.querySelector('data').textContent = data.data;
config.querySelector('qrcode').replaceWith(qr);
const expires = config.querySelector('expires');
if(data.expires) {
expires.textContent = data.expires;
} else {
config.removeChild(expires);
}
const link = config.querySelector('.conffile');
link.setAttribute('href',
dlprefix + encodeURIComponent(data.data));
link.setAttribute('download', data.name + '.conf');
config.querySelector('button.edit')
.addEventListener('click', (event) => {
display_edit_form(config_id, data);
});
config.querySelector('button.download')
.addEventListener('click', (event) => {
link.click();
});
const config_children = configs_parent.children;
for(let i = 0; i < config_children.length; i++) {
const child = config_children[i];
if(child.nodeName === 'PLACEHOLDER') {
configs_parent.insertBefore(template, child);
break;
}
const name = child.querySelector('name').textContent;
if(data.name.toLowerCase() < name.toLowerCase()) {
configs_parent.insertBefore(template, child);
}
}
update_create_button();
});
});
}
function display_edit_form(config_id, config) {
const template = document.querySelector('template#update-form')
.content.cloneNode(true);
const form = template.querySelector('form');
form.name.value = config.name;
form.description.value = config.description;
form.addEventListener('submit', (event) => {
event.preventDefault();
make_api_request('POST',
'/configs/' + config_id + '/update',
{'name': form.name.value,
'description': form.description.value.trim()})
.then((response) => {
if(response.result == 'success') {
display_configs(config_id);
close_modal(form);
} else {
throw new Error(response.reason);
}
})
.catch((exception) => {
console.error('update failed for: '+config_id,
exception);
});
});
const delete_button = form.querySelector('button.delete');
delete_button.addEventListener('click', (event) => {
if(!window.confirm('Are you sure you want to delete this client?')) {
return;
}
make_api_request('POST',
'/configs/' + config_id + '/delete')
.then((response) => {
document.querySelector('#config-'+config_id).remove();
update_create_button();
close_modal(form);
})
.catch((exception) => {
console.error('deletion failed for: '+config_id,
exception);
});
});
display_modal(template, form.name);
}
function display_modal(fragment, focus_element) {
const modal = document.querySelector('template#modal')
.content.cloneNode(true);
modal.querySelector('wrapper').appendChild(fragment);
const backdrop = modal.querySelector('backdrop');
const cancel = modal.querySelector('button.cancel');
backdrop.addEventListener('click', (event) => {
if(event.target === backdrop
|| event.target === cancel) {
close_modal(backdrop);
}
});
document.querySelector('body').appendChild(modal);
focus_element.focus();
}
function get_cookies() {
var out = new Object();
const cookies = document.cookie.split('; ');
cookies.forEach((cookie) => {
const temp = cookie.split('=');
const name = temp[0];
const value = temp.slice(1).join('=');
out[name] = value;
});
return out;
}
function login() {
const current_path = window.location.pathname;
return window.location.replace('/api/login?return='
+ current_path);
}
async function make_api_request(method, path, body) {
const data = {'method': method,
'headers': {'Content-Type': 'application/json'}};
if(method != 'GET') {
data['body'] = JSON.stringify(body);
}
const request = new Request('/api' + path, data);
const response = await fetch(request);
if(response.status === 403) {
const cookies = get_cookies();
const access = cookies['access'];
if(access === 'denied') {
throw new Error('access denied');
}
login();
}
return response.json();
}
function update_create_button() {
const visible_configs = document.querySelectorAll('configs > config');
const max_clients = sessionStorage.getItem('max_clients');
let button_disabled = false;
let button_message = 'Add a client';
if(max_clients > 0 && visible_configs.length >= max_clients) {
button_disabled = true;
button_message = 'Limit of '+max_clients+' clients reached';
}
const button = document.querySelector('button#create-config');
button.disabled = button_disabled;
button.innerHTML = button_message;
}
async function setup_page(route) {
try {
const configs = await make_api_request('GET', '/configs/');
const cookies = get_cookies();
const settings = JSON.parse(atob(cookies['server_settings']));
sessionStorage.setItem('max_clients', settings['client_limit']);
document.querySelector('head > title')
.textContent = settings['site_name'];
if('topbox_content' in settings) {
fetch(settings['topbox_content'])
.then((response) => response.text())
.then((data) => {
const template = document.createElement('template');
template.innerHTML = data;
document.querySelector('topbox > details')
.replaceWith(template.content.cloneNode(true));
});
}
document.querySelector('user#banner-userid')
.textContent = cookies['username'];
document.querySelector('button#create-config')
.addEventListener('click', (event) => {
display_create_form();
});
await display_configs(...configs);
} catch(e) {
if(e.message === 'access denied') {
deny_access();
return;
}
throw e;
}
}
})();