mirror of
https://github.com/Revertron/Alfis
synced 2024-11-15 06:12:52 +00:00
Implemented web-server based on Actix-web to provide all HTML/CSS/JS for web-view.
This commit is contained in:
parent
ce6b62d2d4
commit
162962c039
@ -37,6 +37,9 @@ uuid = { version = "0.8.2", features = ["serde", "v4"] }
|
||||
mio = { version = "0.7", features = ["os-poll", "net"] }
|
||||
derive_more = "0.99" # for DNS from hermes
|
||||
lazy_static = "1.4.0"
|
||||
#tiny_http = "0.8"
|
||||
tungstenite = "0.13"
|
||||
actix-web = "3"
|
||||
|
||||
# Optional dependencies regulated by features
|
||||
web-view = { version = "0.7", features = [], optional = true }
|
||||
|
@ -23,4 +23,5 @@ pub mod dns_utils;
|
||||
pub mod settings;
|
||||
pub mod bytes;
|
||||
pub mod crypto;
|
||||
pub mod web_server;
|
||||
|
||||
|
@ -25,6 +25,7 @@ use std::io::{Seek, SeekFrom};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use alfis::keystore::create_key;
|
||||
use alfis::eventbus::register;
|
||||
use alfis::web_server::start_server;
|
||||
|
||||
#[cfg(feature = "webgui")]
|
||||
mod web_ui;
|
||||
@ -174,6 +175,11 @@ fn main() {
|
||||
let mut network = Network::new(Arc::clone(&context));
|
||||
network.start().expect("Error starting network component");
|
||||
|
||||
// Starting internal Web-server to provide HTML/CSS/JS for UI
|
||||
thread::spawn(|| {
|
||||
let _ = start_server();
|
||||
});
|
||||
|
||||
create_genesis_if_needed(&context, &miner);
|
||||
if no_gui {
|
||||
print_my_domains(&context);
|
||||
|
53
src/web_server.rs
Normal file
53
src/web_server.rs
Normal file
@ -0,0 +1,53 @@
|
||||
use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};
|
||||
|
||||
pub const CONTENT_TYPE_HTML: &str = "text/html; charset=utf-8";
|
||||
pub const CONTENT_TYPE_JS: &str = "application/javascript";
|
||||
pub const CONTENT_TYPE_CSS: &str = "text/css";
|
||||
|
||||
#[get("/")]
|
||||
async fn html() -> impl Responder {
|
||||
let body = include_str!("webview/index.html");
|
||||
HttpResponse::Ok().body(body)
|
||||
}
|
||||
|
||||
#[get("/bulma.css")]
|
||||
async fn bulma() -> impl Responder {
|
||||
let body = include_str!("webview/bulma.css");
|
||||
HttpResponse::Ok().content_type(CONTENT_TYPE_CSS).body(body)
|
||||
}
|
||||
|
||||
#[get("/styles.css")]
|
||||
async fn styles() -> impl Responder {
|
||||
let body = include_str!("webview/styles.css");
|
||||
HttpResponse::Ok().content_type(CONTENT_TYPE_CSS).body(body)
|
||||
}
|
||||
|
||||
#[get("/busy.css")]
|
||||
async fn busy() -> impl Responder {
|
||||
let body = include_str!("webview/busy.css");
|
||||
HttpResponse::Ok().content_type(CONTENT_TYPE_CSS).body(body)
|
||||
}
|
||||
|
||||
#[get("/scripts.js")]
|
||||
async fn scripts() -> impl Responder {
|
||||
let body = include_str!("webview/scripts.js");
|
||||
HttpResponse::Ok().content_type(CONTENT_TYPE_JS).body(body)
|
||||
}
|
||||
|
||||
#[post("/api")]
|
||||
async fn api(req_body: String) -> impl Responder {
|
||||
HttpResponse::Ok().body("Not implemented")
|
||||
}
|
||||
|
||||
#[actix_web::main]
|
||||
pub async fn start_server() -> std::io::Result<()> {
|
||||
HttpServer::new(|| {
|
||||
App::new()
|
||||
.service(html)
|
||||
.service(bulma)
|
||||
.service(styles)
|
||||
.service(busy)
|
||||
.service(scripts)
|
||||
.service(api)
|
||||
}).bind("127.0.0.1:4280")?.run().await
|
||||
}
|
@ -12,7 +12,6 @@ use chrono::{DateTime, Local};
|
||||
#[allow(unused_imports)]
|
||||
use log::{debug, error, info, LevelFilter, trace, warn};
|
||||
use serde::Deserialize;
|
||||
use web_view::Content;
|
||||
|
||||
use alfis::{Block, Bytes, Context, Keystore, Transaction};
|
||||
use alfis::keystore;
|
||||
@ -27,44 +26,20 @@ use Cmd::*;
|
||||
use self::web_view::{Handle, WebView};
|
||||
use alfis::crypto::CryptoBox;
|
||||
use alfis::eventbus::{register, post};
|
||||
use self::web_view::Content::Url;
|
||||
|
||||
pub fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
|
||||
let file_content = include_str!("webview/index.html");
|
||||
let mut styles = inline_style(include_str!("webview/bulma.css"));
|
||||
styles.push_str(&inline_style(include_str!("webview/styles.css")));
|
||||
styles.push_str(&inline_style(include_str!("webview/busy_indicator.css")));
|
||||
let scripts = inline_script(include_str!("webview/scripts.js"));
|
||||
|
||||
let html = Content::Html(file_content.to_owned().replace("{styles}", &styles).replace("{scripts}", &scripts));
|
||||
let title = format!("ALFIS {}", env!("CARGO_PKG_VERSION"));
|
||||
let mut interface = web_view::builder()
|
||||
.title(&title)
|
||||
.content(html)
|
||||
.content(Url("http://localhost:4280/"))
|
||||
.size(1023, 720)
|
||||
.min_size(773, 350)
|
||||
.resizable(true)
|
||||
.debug(false)
|
||||
.user_data(())
|
||||
.invoke_handler(|web_view, arg| {
|
||||
debug!("Command {}", arg);
|
||||
match serde_json::from_str(arg).unwrap() {
|
||||
Loaded => { action_loaded(&context, web_view); }
|
||||
LoadKey => { action_load_key(&context, web_view); }
|
||||
CreateKey => { keystore::create_key(Arc::clone(&context)); }
|
||||
SaveKey => { action_save_key(&context); }
|
||||
CheckRecord { data } => { action_check_record(web_view, data); }
|
||||
CheckDomain { name } => { action_check_domain(&context, web_view, name); }
|
||||
MineDomain { name, data, signing, encryption } => {
|
||||
action_create_domain(Arc::clone(&context), Arc::clone(&miner), web_view, name, data, signing, encryption);
|
||||
}
|
||||
TransferDomain { .. } => {}
|
||||
StopMining => { post(Event::ActionStopMining); }
|
||||
Open { link } => {
|
||||
if open::that(&link).is_err() {
|
||||
show_warning(web_view, "Something wrong, I can't open the link 😢");
|
||||
}
|
||||
}
|
||||
}
|
||||
handle_command(web_view, &context, &miner, arg);
|
||||
Ok(())
|
||||
})
|
||||
.build()
|
||||
@ -73,6 +48,28 @@ pub fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
|
||||
run_interface_loop(&mut interface);
|
||||
}
|
||||
|
||||
fn handle_command(web_view: &mut WebView<()>, context: &Arc<Mutex<Context>>, miner: &Arc<Mutex<Miner>>, arg: &str) {
|
||||
debug!("Command {}", arg);
|
||||
match serde_json::from_str(arg).unwrap() {
|
||||
Loaded => { action_loaded(&context, web_view); }
|
||||
LoadKey => { action_load_key(&context, web_view); }
|
||||
CreateKey => { keystore::create_key(Arc::clone(&context)); }
|
||||
SaveKey => { action_save_key(&context); }
|
||||
CheckRecord { data } => { action_check_record(web_view, data); }
|
||||
CheckDomain { name } => { action_check_domain(&context, web_view, name); }
|
||||
MineDomain { name, data, signing, encryption } => {
|
||||
action_create_domain(Arc::clone(&context), Arc::clone(&miner), web_view, name, data, signing, encryption);
|
||||
}
|
||||
TransferDomain { .. } => {}
|
||||
StopMining => { post(Event::ActionStopMining); }
|
||||
Open { link } => {
|
||||
if open::that(&link).is_err() {
|
||||
show_warning(web_view, "Something wrong, I can't open the link 😢");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Indefinitely loops through WebView steps
|
||||
fn run_interface_loop(interface: &mut WebView<()>) {
|
||||
// We use this ugly loop to lower CPU usage a lot.
|
||||
@ -570,11 +567,3 @@ impl Status {
|
||||
self.speed.iter().sum()
|
||||
}
|
||||
}
|
||||
|
||||
fn inline_style(s: &str) -> String {
|
||||
format!(r#"<style type="text/css">{}</style>"#, s)
|
||||
}
|
||||
|
||||
fn inline_script(s: &str) -> String {
|
||||
format!(r#"<script type="text/javascript">{}</script>"#, s)
|
||||
}
|
||||
|
@ -4,8 +4,10 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>ALFIS</title>
|
||||
{scripts}
|
||||
{styles}
|
||||
<script type="text/javascript" src="scripts.js"></script>
|
||||
<link rel="stylesheet" href="bulma.css">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<link rel="stylesheet" href="busy.css">
|
||||
</head>
|
||||
<body onload="onLoad();">
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
API_URL = "http://localhost:4280/api";
|
||||
|
||||
var recordsBuffer = [];
|
||||
var ownerSigning = "";
|
||||
var ownerEncryption = "";
|
||||
@ -9,6 +11,38 @@ document.addEventListener('click', function (event) {
|
||||
closeDropdowns();
|
||||
});
|
||||
|
||||
API = {}
|
||||
API.invokeJson = function(json) {
|
||||
external.invoke(json);
|
||||
}
|
||||
API.invokeCmd = function(cmd) {
|
||||
external.invoke(JSON.stringify(cmd));
|
||||
}
|
||||
API.postCmd = function(cmd) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("POST", API_URL, true);
|
||||
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||
xhr.send(JSON.stringify(cmd));
|
||||
}
|
||||
|
||||
// Defines handler for external API
|
||||
function defineExternalApi() {
|
||||
// Workaround for Arch Linux Webkit
|
||||
// https://github.com/Boscop/web-view/issues/212#issuecomment-671055663
|
||||
if (typeof window.external == 'undefined' || typeof window.external.invoke == 'undefined') {
|
||||
window.external = {
|
||||
invoke: function(x) {
|
||||
window.webkit.messageHandlers.external.postMessage(x);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function onLoad() {
|
||||
defineExternalApi();
|
||||
API.invokeCmd({cmd: 'loaded'});
|
||||
}
|
||||
|
||||
function closeDropdowns(except) {
|
||||
// Get all elements with class="dropdowns" and hide them
|
||||
var dropdowns = document.getElementsByClassName("dropdown is-active");
|
||||
@ -180,20 +214,6 @@ function editDomain(domain, event) {
|
||||
});
|
||||
}
|
||||
|
||||
function onLoad() {
|
||||
// Workaround for Arch Linux Webkit
|
||||
// https://github.com/Boscop/web-view/issues/212#issuecomment-671055663
|
||||
if (typeof window.external == 'undefined' || typeof window.external.invoke == 'undefined') {
|
||||
window.external = {
|
||||
invoke: function(x) {
|
||||
window.webkit.messageHandlers.external.postMessage(x);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
external.invoke(JSON.stringify({cmd: 'loaded'}));
|
||||
}
|
||||
|
||||
function openTab(element, tabName) {
|
||||
// Declare all variables
|
||||
var i, tabContent, tabLinks;
|
||||
@ -223,23 +243,23 @@ function toggle(element, event) {
|
||||
}
|
||||
|
||||
function open_link(link) {
|
||||
external.invoke(JSON.stringify({cmd: 'open', link: link}));
|
||||
API.invokeCmd({cmd: 'open', link: link});
|
||||
}
|
||||
|
||||
function loadKey() {
|
||||
external.invoke(JSON.stringify({cmd: 'loadKey'}));
|
||||
API.invokeCmd({cmd: 'loadKey'});
|
||||
}
|
||||
|
||||
function createKey() {
|
||||
external.invoke(JSON.stringify({cmd: 'createKey'}));
|
||||
API.invokeCmd({cmd: 'createKey'});
|
||||
}
|
||||
|
||||
function saveKey() {
|
||||
external.invoke(JSON.stringify({cmd: 'saveKey'}));
|
||||
API.invokeCmd({cmd: 'saveKey'});
|
||||
}
|
||||
|
||||
function checkRecord(data) {
|
||||
external.invoke(JSON.stringify({cmd: 'checkRecord', data: JSON.stringify(data)}));
|
||||
API.invokeCmd({cmd: 'checkRecord', data: JSON.stringify(data)});
|
||||
}
|
||||
|
||||
function recordOkay(okay) {
|
||||
@ -274,7 +294,7 @@ function createDomain() {
|
||||
data.records = recordsBuffer;
|
||||
data.contacts = getContacts();
|
||||
data = JSON.stringify(data);
|
||||
external.invoke(JSON.stringify({cmd: 'mineDomain', name: domain, data: data, signing: ownerSigning, encryption: ownerEncryption}));
|
||||
API.invokeCmd({cmd: 'mineDomain', name: domain, data: data, signing: ownerSigning, encryption: ownerEncryption});
|
||||
}
|
||||
|
||||
function getContacts() {
|
||||
@ -315,14 +335,10 @@ function domainMiningUnavailable() {
|
||||
document.getElementById("new_key_button").disabled = false;
|
||||
}
|
||||
|
||||
function sendAction(param) {
|
||||
external.invoke(JSON.stringify(param));
|
||||
}
|
||||
|
||||
function onDomainChange(element) {
|
||||
if (typeof currentZone !== 'undefined') {
|
||||
var domain = element.value + "." + currentZone.name;
|
||||
external.invoke(JSON.stringify({cmd: 'checkDomain', name: domain}));
|
||||
API.invokeCmd({cmd: 'checkDomain', name: domain});
|
||||
}
|
||||
}
|
||||
|
||||
@ -480,7 +496,7 @@ function showMiningIndicator(visible, blue) {
|
||||
|
||||
function miningIndicatorClick(element) {
|
||||
showModalDialog("Do you really want to stop mining?", function() {
|
||||
external.invoke(JSON.stringify({cmd: 'stopMining'}));
|
||||
API.invokeCmd({cmd: 'stopMining'});
|
||||
});
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user