2
0
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:
Revertron 2021-05-11 19:57:25 +02:00
parent ce6b62d2d4
commit 162962c039
8 changed files with 134 additions and 64 deletions

View File

@ -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 }

View File

@ -23,4 +23,5 @@ pub mod dns_utils;
pub mod settings;
pub mod bytes;
pub mod crypto;
pub mod web_server;

View File

@ -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
View 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
}

View File

@ -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)
}

View File

@ -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();">

View File

@ -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'});
});
}