Implemented support for multiple keys.
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "alfis"
|
||||
version = "0.5.4"
|
||||
version = "0.5.5"
|
||||
authors = ["Revertron <alfis@revertron.com>"]
|
||||
edition = "2018"
|
||||
build = "build.rs"
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
# The hash of first block in a chain to know with which nodes to work
|
||||
origin = "0000001D2A77D63477172678502E51DE7F346061FF7EB188A2445ECA3FC0780E"
|
||||
# A path to your key file to load automatically
|
||||
key_file = "key1.toml"
|
||||
# Paths to your key files to load automatically
|
||||
key_files = ["key1.toml", "key2.toml", "key3.toml", "key4.toml", "key5.toml"]
|
||||
# How many last blocks to check on start
|
||||
check_blocks = 8
|
||||
|
||||
|
||||
+18
-16
@@ -253,12 +253,12 @@ impl Chain {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_sign_block(&self, keystore: &Option<Keystore>) -> Option<Block> {
|
||||
pub fn get_sign_block(&self, keys: &Vec<Keystore>) -> Option<(Block, Keystore)> {
|
||||
if self.get_height() < BLOCK_SIGNERS_START {
|
||||
trace!("Too early to start block signings");
|
||||
return None;
|
||||
}
|
||||
if keystore.is_none() {
|
||||
if keys.is_empty() {
|
||||
trace!("We can't sign blocks without keys");
|
||||
return None;
|
||||
}
|
||||
@@ -288,22 +288,24 @@ impl Chain {
|
||||
None => { return None; }
|
||||
};
|
||||
|
||||
let keystore = keystore.clone().unwrap().clone();
|
||||
let signers: HashSet<Bytes> = self.get_block_signers(&block).into_iter().collect();
|
||||
if signers.contains(&keystore.get_public()) {
|
||||
for index in block.index..=self.get_height() {
|
||||
let b = self.get_block(index).unwrap();
|
||||
if b.pub_key == keystore.get_public() {
|
||||
info!("We already mined signing block for block {}", block.index);
|
||||
return None;
|
||||
'key_loop: for keystore in keys {
|
||||
if signers.contains(&keystore.get_public()) {
|
||||
for index in block.index..=self.get_height() {
|
||||
let b = self.get_block(index).unwrap();
|
||||
if b.pub_key == keystore.get_public() {
|
||||
debug!("We already mined signing block for block {} by {:?}", block.index, &b.pub_key);
|
||||
continue 'key_loop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
info!("We have an honor to mine signing block!");
|
||||
let mut block = Block::new(None, Bytes::default(), last_hash, SIGNER_DIFFICULTY);
|
||||
block.index = last_index + 1;
|
||||
return Some(block);
|
||||
} else if !signers.is_empty() {
|
||||
info!("We have an honor to mine signing block!");
|
||||
let mut block = Block::new(None, Bytes::default(), last_hash, SIGNER_DIFFICULTY);
|
||||
block.index = last_index + 1;
|
||||
return Some((block, keystore.clone()));
|
||||
}
|
||||
}
|
||||
if !signers.is_empty() {
|
||||
info!("Signing block must be mined by other nodes");
|
||||
}
|
||||
None
|
||||
@@ -590,7 +592,7 @@ impl Chain {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_my_domains(&self, keystore: &Option<Keystore>) -> HashMap<Bytes, (String, i64, DomainData)> {
|
||||
pub fn get_my_domains(&self, keystore: Option<&Keystore>) -> HashMap<Bytes, (String, i64, DomainData)> {
|
||||
if keystore.is_none() {
|
||||
return HashMap::new();
|
||||
}
|
||||
|
||||
+50
-8
@@ -1,4 +1,4 @@
|
||||
use crate::{Chain, Keystore, Settings};
|
||||
use crate::{Chain, Keystore, Settings, Bytes};
|
||||
#[allow(unused_imports)]
|
||||
use log::{trace, debug, info, warn, error};
|
||||
use crate::miner::MinerState;
|
||||
@@ -6,29 +6,71 @@ use crate::miner::MinerState;
|
||||
pub struct Context {
|
||||
pub app_version: String,
|
||||
pub settings: Settings,
|
||||
pub keystore: Option<Keystore>,
|
||||
pub keystores: Vec<Keystore>,
|
||||
active_key: usize,
|
||||
pub chain: Chain,
|
||||
pub miner_state: MinerState,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
/// Creating an essential context to work with
|
||||
pub fn new(app_version: String, settings: Settings, keystore: Option<Keystore>, chain: Chain) -> Context {
|
||||
pub fn new(app_version: String, settings: Settings, keystores: Vec<Keystore>, chain: Chain) -> Context {
|
||||
Context {
|
||||
app_version,
|
||||
settings,
|
||||
keystore,
|
||||
keystores,
|
||||
active_key: 0,
|
||||
chain,
|
||||
miner_state: MinerState { mining: false, full: false }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_keystore(&self) -> Option<Keystore> {
|
||||
self.keystore.clone()
|
||||
pub fn get_keystore(&self) -> Option<&Keystore> {
|
||||
self.keystores.get(self.active_key)
|
||||
}
|
||||
|
||||
pub fn set_keystore(&mut self, keystore: Option<Keystore>) {
|
||||
self.keystore = keystore;
|
||||
pub fn get_keystore_mut(&mut self) -> Option<&mut Keystore> {
|
||||
self.keystores.get_mut(self.active_key)
|
||||
}
|
||||
|
||||
pub fn get_keystores(&self) -> &Vec<Keystore> {
|
||||
&self.keystores
|
||||
}
|
||||
|
||||
pub fn has_keys(&self) -> bool {
|
||||
!self.keystores.is_empty()
|
||||
}
|
||||
|
||||
pub fn set_keystores(&mut self, keystore: Vec<Keystore>) {
|
||||
self.keystores = keystore;
|
||||
self.active_key = 0;
|
||||
}
|
||||
|
||||
pub fn add_keystore(&mut self, keystore: Keystore) {
|
||||
self.keystores.push(keystore);
|
||||
self.active_key = self.keystores.len() - 1;
|
||||
}
|
||||
|
||||
pub fn select_key_by_index(&mut self, index: usize) -> bool {
|
||||
if index < self.keystores.len() {
|
||||
self.active_key = index;
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn select_key_by_public(&mut self, public: &Bytes) -> bool {
|
||||
for (i, key) in self.keystores.iter().enumerate() {
|
||||
if key.get_public().eq(public) {
|
||||
self.active_key = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn get_active_key_index(&self) -> usize {
|
||||
self.active_key
|
||||
}
|
||||
|
||||
pub fn get_chain(&self) -> &Chain {
|
||||
|
||||
+1
-1
@@ -247,7 +247,7 @@ pub fn create_key(context: Arc<Mutex<Context>>) {
|
||||
let path = keystore.get_path().to_owned();
|
||||
let public = keystore.get_public().to_string();
|
||||
info!("Key mined successfully! Public key: {}, hash: {}", &public, &hash);
|
||||
context.set_keystore(Some(keystore));
|
||||
context.add_keystore(keystore);
|
||||
post(Event::KeyCreated { path, public, hash });
|
||||
}
|
||||
}
|
||||
|
||||
+23
-5
@@ -110,7 +110,6 @@ fn main() {
|
||||
|
||||
let settings = Settings::load(&config_name).expect(&format!("Cannot load settings from {}!", &config_name));
|
||||
debug!(target: LOG_TARGET_MAIN, "Loaded settings: {:?}", &settings);
|
||||
let keystore = Keystore::from_file(&settings.key_file, "");
|
||||
let chain: Chain = Chain::new(&settings, DB_NAME);
|
||||
if opt_matches.opt_present("b") {
|
||||
for i in 1..(chain.get_height() + 1) {
|
||||
@@ -121,7 +120,26 @@ fn main() {
|
||||
return;
|
||||
}
|
||||
let settings_copy = settings.clone();
|
||||
let context = Context::new(env!("CARGO_PKG_VERSION").to_owned(), settings, keystore, chain);
|
||||
let mut keys = Vec::new();
|
||||
if settings.key_files.len() > 0 {
|
||||
for name in &settings.key_files {
|
||||
match Keystore::from_file(name, "") {
|
||||
None => { warn!("Error loading keyfile from {}", name); }
|
||||
Some(keystore) => {
|
||||
info!("Successfully loaded keyfile {}", name);
|
||||
keys.push(keystore);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match Keystore::from_file(&settings.key_file, "") {
|
||||
None => { warn!("Error loading keyfile from {}", &settings.key_file); }
|
||||
Some(keystore) => {
|
||||
keys.push(keystore);
|
||||
}
|
||||
}
|
||||
}
|
||||
let context = Context::new(env!("CARGO_PKG_VERSION").to_owned(), settings, keys, chain);
|
||||
let context: Arc<Mutex<Context>> = Arc::new(Mutex::new(context));
|
||||
|
||||
// If we just need to generate keys
|
||||
@@ -137,7 +155,7 @@ fn main() {
|
||||
let mining_copy = Arc::clone(&mining_copy);
|
||||
let filename = filename.clone();
|
||||
thread::spawn(move || {
|
||||
if let Some(mut keystore) = context_copy.lock().unwrap().get_keystore() {
|
||||
if let Some(keystore) = context_copy.lock().unwrap().get_keystore_mut() {
|
||||
keystore.save(&filename, "");
|
||||
mining_copy.store(false, Ordering::Relaxed);
|
||||
}
|
||||
@@ -237,7 +255,7 @@ fn setup_logger(opt_matches: &Matches) {
|
||||
/// Gets own domains by current loaded keystore and writes them to log
|
||||
fn print_my_domains(context: &Arc<Mutex<Context>>) {
|
||||
let context = context.lock().unwrap();
|
||||
let domains = context.chain.get_my_domains(&context.keystore);
|
||||
let domains = context.chain.get_my_domains(context.get_keystore());
|
||||
debug!("Domains: {:?}", &domains);
|
||||
}
|
||||
|
||||
@@ -248,7 +266,7 @@ fn create_genesis_if_needed(context: &Arc<Mutex<Context>>, miner: &Arc<Mutex<Min
|
||||
let last_block = context.get_chain().last_block();
|
||||
let origin = context.settings.origin.clone();
|
||||
if origin.is_empty() && last_block.is_none() {
|
||||
if let Some(keystore) = &context.keystore {
|
||||
if let Some(keystore) = context.get_keystore() {
|
||||
// If blockchain is empty, we are going to mine a Genesis block
|
||||
let transaction = Transaction::origin(Chain::get_zones_hash(), keystore.get_public(), keystore.get_encryption_public());
|
||||
let block = Block::new(Some(transaction), keystore.get_public(), Bytes::default(), ORIGIN_DIFFICULTY);
|
||||
|
||||
+6
-6
@@ -149,13 +149,13 @@ impl Miner {
|
||||
|
||||
if !signing_waits {
|
||||
if let Ok(context) = context.lock() {
|
||||
let keystore = context.get_keystore();
|
||||
let keystores = context.get_keystores();
|
||||
// Ask the blockchain if we have to sign something
|
||||
if let Some(block) = context.chain.get_sign_block(&keystore) {
|
||||
if let Some((block, keystore)) = context.chain.get_sign_block(keystores) {
|
||||
info!("Got signing job, adding to queue");
|
||||
// We start mining sign block after some time, not everyone in the same time
|
||||
let start = Utc::now().timestamp() + (rand::random::<i64>() % BLOCK_SIGNERS_START_RANDOM);
|
||||
jobs.push(MineJob { start, block, keystore: keystore.unwrap() });
|
||||
jobs.push(MineJob { start, block, keystore });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -177,13 +177,13 @@ impl Miner {
|
||||
} else {
|
||||
// If our queue is empty
|
||||
if let Ok(context) = context.lock() {
|
||||
let keystore = context.get_keystore();
|
||||
let keystores = context.get_keystores();
|
||||
// Ask the blockchain if we have to sign something
|
||||
if let Some(block) = context.chain.get_sign_block(&keystore) {
|
||||
if let Some((block, keystore)) = context.chain.get_sign_block(keystores) {
|
||||
info!("Got signing job, adding to queue");
|
||||
// We start mining sign block after some time, not everyone in the same time
|
||||
let start = Utc::now().timestamp() + (rand::random::<i64>() % BLOCK_SIGNERS_START_RANDOM);
|
||||
jobs.push(MineJob { start, block, keystore: keystore.unwrap() });
|
||||
jobs.push(MineJob { start, block, keystore });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-2
@@ -12,7 +12,10 @@ pub struct Settings {
|
||||
#[serde(default)]
|
||||
pub origin: String,
|
||||
#[serde(default)]
|
||||
#[deprecated]
|
||||
pub key_file: String,
|
||||
#[serde(default)]
|
||||
pub key_files: Vec<String>,
|
||||
#[serde(default = "default_check_blocks")]
|
||||
pub check_blocks: u64,
|
||||
#[serde(default)]
|
||||
@@ -52,8 +55,9 @@ impl Settings {
|
||||
impl Default for Settings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
origin: String::from("00002883BB006454F795BE6902770B1A18D897B33A0AB1631F53C37C2F41F800"),
|
||||
key_file: String::from("default.key"),
|
||||
origin: String::from("0000001D2A77D63477172678502E51DE7F346061FF7EB188A2445ECA3FC0780E"),
|
||||
key_file: String::from("key1.toml"),
|
||||
key_files: Vec::new(),
|
||||
check_blocks: default_check_blocks(),
|
||||
net: Net::default(),
|
||||
dns: Default::default(),
|
||||
|
||||
+56
-7
@@ -11,7 +11,7 @@ use std::time::{Duration, Instant};
|
||||
use chrono::{DateTime, Local};
|
||||
#[allow(unused_imports)]
|
||||
use log::{debug, error, info, LevelFilter, trace, warn};
|
||||
use serde::Deserialize;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use web_view::Content;
|
||||
|
||||
use alfis::{Block, Bytes, Context, Keystore, Transaction};
|
||||
@@ -52,6 +52,7 @@ pub fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
|
||||
LoadKey => { action_load_key(&context, web_view); }
|
||||
CreateKey => { keystore::create_key(Arc::clone(&context)); }
|
||||
SaveKey => { action_save_key(&context); }
|
||||
SelectKey { index } => { action_select_key(&context, web_view, index); }
|
||||
CheckRecord { data } => { action_check_record(web_view, data); }
|
||||
CheckDomain { name } => { action_check_domain(&context, web_view, name); }
|
||||
MineDomain { name, data, signing, encryption } => {
|
||||
@@ -130,7 +131,7 @@ fn action_check_domain(context: &Arc<Mutex<Context>>, web_view: &mut WebView<()>
|
||||
}
|
||||
|
||||
fn action_save_key(context: &Arc<Mutex<Context>>) {
|
||||
if context.lock().unwrap().get_keystore().is_none() {
|
||||
if !context.lock().unwrap().has_keys() {
|
||||
return;
|
||||
}
|
||||
let result = tfd::save_file_dialog_with_filter("Save keys file", "", &["*.toml"], "Key files (*.toml)");
|
||||
@@ -141,7 +142,7 @@ fn action_save_key(context: &Arc<Mutex<Context>>) {
|
||||
new_path.push_str(".toml");
|
||||
}
|
||||
let path = new_path.clone();
|
||||
if let Some(mut keystore) = context.lock().unwrap().get_keystore() {
|
||||
if let Some(keystore) = context.lock().unwrap().get_keystore_mut() {
|
||||
let public = keystore.get_public().to_string();
|
||||
let hash = keystore.get_hash().to_string();
|
||||
keystore.save(&new_path, "");
|
||||
@@ -152,6 +153,20 @@ fn action_save_key(context: &Arc<Mutex<Context>>) {
|
||||
}
|
||||
}
|
||||
|
||||
fn action_select_key(context: &Arc<Mutex<Context>>, web_view: &mut WebView<()>, index: usize) {
|
||||
if context.lock().unwrap().select_key_by_index(index) {
|
||||
let (path, public, hash) = {
|
||||
let keystore = context.lock().unwrap().get_keystore().cloned().unwrap();
|
||||
let path = keystore.get_path().to_owned();
|
||||
let public = keystore.get_public().to_string();
|
||||
let hash = keystore.get_hash().to_string();
|
||||
(path, public, hash)
|
||||
};
|
||||
post(Event::KeyLoaded { path, public, hash });
|
||||
web_view.eval(&format!("keySelected({})", index)).expect("Error evaluating!");
|
||||
}
|
||||
}
|
||||
|
||||
fn action_load_key(context: &Arc<Mutex<Context>>, web_view: &mut WebView<()>) {
|
||||
let result = tfd::open_file_dialog("Open keys file", "", Some((&["*.key", "*.toml"], "Key files")));
|
||||
match result {
|
||||
@@ -169,7 +184,12 @@ fn action_load_key(context: &Arc<Mutex<Context>>, web_view: &mut WebView<()>) {
|
||||
let public = keystore.get_public().to_string();
|
||||
let hash = keystore.get_hash().to_string();
|
||||
post(Event::KeyLoaded { path, public, hash });
|
||||
context.lock().unwrap().set_keystore(Some(keystore));
|
||||
|
||||
if !context.lock().unwrap().select_key_by_public(&keystore.get_public()) {
|
||||
context.lock().unwrap().add_keystore(keystore);
|
||||
} else {
|
||||
warn!("This key is already loaded!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -200,6 +220,7 @@ fn action_loaded(context: &Arc<Mutex<Context>>, web_view: &mut WebView<()>) {
|
||||
let eval = match e {
|
||||
Event::KeyCreated { path, public, hash } => {
|
||||
load_domains(&mut context, &handle);
|
||||
send_keys_to_ui(&mut context, &handle);
|
||||
event_handle_luck(&handle, "Key successfully created! Don\\'t forget to save it!");
|
||||
let mut s = format!("keystoreChanged('{}', '{}', '{}');", &path, &public, &hash);
|
||||
s.push_str(" showSuccess('New key mined successfully! Save it to a safe place!')");
|
||||
@@ -208,6 +229,7 @@ fn action_loaded(context: &Arc<Mutex<Context>>, web_view: &mut WebView<()>) {
|
||||
Event::KeyLoaded { path, public, hash } |
|
||||
Event::KeySaved { path, public, hash } => {
|
||||
load_domains(&mut context, &handle);
|
||||
send_keys_to_ui(&mut context, &handle);
|
||||
format!("keystoreChanged('{}', '{}', '{}');", &path, &public, &hash)
|
||||
}
|
||||
Event::MinerStarted | Event::KeyGeneratorStarted => {
|
||||
@@ -320,6 +342,7 @@ fn action_loaded(context: &Arc<Mutex<Context>>, web_view: &mut WebView<()>) {
|
||||
if let Ok(zones) = serde_json::to_string(&zones) {
|
||||
let _ = web_view.eval(&format!("zonesChanged('{}');", &zones));
|
||||
}
|
||||
send_keys_to_ui(&c, &web_view.handle());
|
||||
event_info(web_view, "Application loaded");
|
||||
}
|
||||
|
||||
@@ -327,7 +350,7 @@ fn load_domains(context: &mut MutexGuard<Context>, handle: &Handle<()>) {
|
||||
let _ = handle.dispatch(move |web_view|{
|
||||
web_view.eval("clearMyDomains();")
|
||||
});
|
||||
let domains = context.chain.get_my_domains(&context.keystore);
|
||||
let domains = context.chain.get_my_domains(context.get_keystore());
|
||||
debug!("Domains: {:?}", &domains.values());
|
||||
for (_identity, (domain, timestamp, data)) in domains {
|
||||
let d = serde_json::to_string(&data).unwrap();
|
||||
@@ -341,11 +364,30 @@ fn load_domains(context: &mut MutexGuard<Context>, handle: &Handle<()>) {
|
||||
});
|
||||
}
|
||||
|
||||
fn send_keys_to_ui(context: &MutexGuard<Context>, handle: &Handle<()>) {
|
||||
let keys = {
|
||||
let mut keys = Vec::new();
|
||||
for key in context.get_keystores() {
|
||||
let path = key.get_path().replace("\\", "/");
|
||||
let parts: Vec<&str> = path.rsplitn(2, "/").collect();
|
||||
keys.push(KeysForJS { file_name: parts[0].to_owned(), public: key.get_public().to_string() });
|
||||
}
|
||||
keys
|
||||
};
|
||||
if !keys.is_empty() {
|
||||
let index = context.get_active_key_index();
|
||||
let _ = handle.dispatch(move |web_view| {
|
||||
let command = format!("keysChanged('{}'); keySelected({});", serde_json::to_string(&keys).unwrap(), index);
|
||||
web_view.eval(&command)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn action_create_domain(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>, web_view: &mut WebView<()>, name: String, data: String, signing: String, encryption: String) {
|
||||
debug!("Creating domain with data: {}", &data);
|
||||
let c = Arc::clone(&context);
|
||||
let context = context.lock().unwrap();
|
||||
if context.get_keystore().is_none() {
|
||||
if !context.has_keys() {
|
||||
show_warning(web_view, "You don't have keys loaded!<br>Load or mine the keys and try again.");
|
||||
let _ = web_view.eval("domainMiningUnavailable();");
|
||||
return;
|
||||
@@ -356,7 +398,7 @@ fn action_create_domain(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>,
|
||||
info!("Waiting for last full block to be signed. Try again later.");
|
||||
return;
|
||||
}
|
||||
let keystore = context.get_keystore().unwrap();
|
||||
let keystore = context.get_keystore().unwrap().clone();
|
||||
let pub_key = keystore.get_public();
|
||||
let data = match serde_json::from_str::<DomainData>(&data) {
|
||||
Ok(data) => { data }
|
||||
@@ -536,6 +578,7 @@ pub enum Cmd {
|
||||
LoadKey,
|
||||
CreateKey,
|
||||
SaveKey,
|
||||
SelectKey { index: usize },
|
||||
CheckRecord { data: String },
|
||||
CheckDomain { name: String },
|
||||
MineDomain { name: String, data: String, signing: String, encryption: String },
|
||||
@@ -571,6 +614,12 @@ impl Status {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct KeysForJS {
|
||||
file_name: String,
|
||||
public: String
|
||||
}
|
||||
|
||||
fn inline_style(s: &str) -> String {
|
||||
format!(r#"<style type="text/css">{}</style>"#, s)
|
||||
}
|
||||
|
||||
@@ -57,6 +57,24 @@
|
||||
<svg viewBox="0 0 24 24" style="width: 20px; height: 20px;"><path d="M12,17A2,2 0 0,0 14,15C14,13.89 13.1,13 12,13A2,2 0 0,0 10,15A2,2 0 0,0 12,17M18,8A2,2 0 0,1 20,10V20A2,2 0 0,1 18,22H6A2,2 0 0,1 4,20V10C4,8.89 4.9,8 6,8H7V6A5,5 0 0,1 12,1A5,5 0 0,1 17,6V8H18M12,3A3,3 0 0,0 9,6V8H15V6A3,3 0 0,0 12,3Z"></path></svg>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="control">
|
||||
<div class="dropdown" id="keys_dropdown" onclick="toggle(this, event);">
|
||||
<div class="dropdown-trigger">
|
||||
<button class="button" aria-haspopup="true" aria-controls="keys_menu">
|
||||
<span id="keys_current_name">No keys</span>
|
||||
<span class="icon is-small">
|
||||
<svg viewBox="0 0 24 24" style="width: 20px; height: 20px;"><path d="M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z"></path></svg>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="dropdown-menu" id="keys_menu" role="menu">
|
||||
<div class="dropdown-content" id="keys_links">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="buttons has-addons">
|
||||
<button class="button is-link is-light" onclick="loadKey();" title="Load keypair from file">Load key</button>
|
||||
<button class="button is-link is-light" id="save_key" onclick="saveKey();" disabled title="Save current keypair to file">Save key</button>
|
||||
|
||||
@@ -4,6 +4,8 @@ var ownerEncryption = "";
|
||||
var availableZones = [];
|
||||
var myDomains = [];
|
||||
var currentZone;
|
||||
var currentSelectedKey = -1;
|
||||
var keysLoaded = [];
|
||||
|
||||
document.addEventListener('click', function (event) {
|
||||
closeDropdowns();
|
||||
@@ -576,4 +578,50 @@ function changeZone(zone, event) {
|
||||
}
|
||||
});
|
||||
refreshZonesList();
|
||||
}
|
||||
|
||||
function refreshKeysMenu() {
|
||||
var buf = "";
|
||||
keysLoaded.forEach(function(value, index, array) {
|
||||
var file_name = value.file_name;
|
||||
if (file_name == "") {
|
||||
file_name = "[Not saved]";
|
||||
}
|
||||
var public = value.public;
|
||||
|
||||
var add_class = "";
|
||||
if (currentSelectedKey == index) {
|
||||
add_class = "is-active";
|
||||
}
|
||||
buf += "<a id=\"key-{id}\" class=\"dropdown-item {class}\" onclick=\"selectKey({index}, event);\" title=\"{title}\">{name}</a>"
|
||||
.replace("{id}", index)
|
||||
.replace("{index}", index)
|
||||
.replace("{class}", add_class)
|
||||
.replace("{title}", public)
|
||||
.replace("{name}", file_name);
|
||||
});
|
||||
var links = document.getElementById("keys_links");
|
||||
links.innerHTML = buf;
|
||||
if (currentSelectedKey >= 0) {
|
||||
var cur_name = document.getElementById("keys_current_name");
|
||||
cur_name.innerHTML = keysLoaded[currentSelectedKey].file_name;
|
||||
}
|
||||
}
|
||||
|
||||
function keysChanged(json) {
|
||||
keysLoaded = JSON.parse(json);
|
||||
refreshKeysMenu();
|
||||
}
|
||||
|
||||
function selectKey(index, event) {
|
||||
event.stopPropagation();
|
||||
closeDropdowns();
|
||||
if (currentSelectedKey != index) {
|
||||
external.invoke(JSON.stringify({cmd: 'selectKey', index: parseInt(index)}));
|
||||
}
|
||||
}
|
||||
|
||||
function keySelected(index) {
|
||||
currentSelectedKey = index;
|
||||
refreshKeysMenu();
|
||||
}
|
||||
Reference in New Issue
Block a user