Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Commit

Permalink
Loading local Dapps from FS. (#1214)
Browse files Browse the repository at this point in the history
* apps list to separate module

* Preparing to support serving files from disk

* Serving files from disk

* Using dapps path from CLI

* Adding more docs
  • Loading branch information
tomusdrw authored and gavofyork committed Jun 3, 2016
1 parent 81d8daf commit bb1b8cc
Show file tree
Hide file tree
Showing 16 changed files with 657 additions and 241 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions dapps/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ parity-dapps-builtins = { git = "https://github.com/ethcore/parity-dapps-builtin
parity-dapps-wallet = { git = "https://github.com/ethcore/parity-dapps-wallet-rs.git", version = "0.5.0", optional = true }
parity-dapps-dao = { git = "https://github.com/ethcore/parity-dapps-dao-rs.git", version = "0.3.0", optional = true }
parity-dapps-makerotc = { git = "https://github.com/ethcore/parity-dapps-makerotc-rs.git", version = "0.2.0", optional = true }
mime_guess = { version = "1.6.1" }
clippy = { version = "0.0.69", optional = true}

[build-dependencies]
Expand Down
28 changes: 17 additions & 11 deletions dapps/src/api/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.

use std::sync::Arc;
use endpoint::{Endpoint, Endpoints, Handler, EndpointPath};
use endpoint::{Endpoint, Endpoints, EndpointInfo, Handler, EndpointPath};

use api::response::as_json;

pub struct RestApi {
endpoints: Arc<Endpoints>,
}

#[derive(Debug, PartialEq, Serialize)]
struct App {
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct App {
pub id: String,
pub name: String,
pub description: String,
Expand All @@ -34,6 +34,19 @@ struct App {
pub icon_url: String,
}

impl App {
fn from_info(id: &str, info: &EndpointInfo) -> Self {
App {
id: id.to_owned(),
name: info.name.to_owned(),
description: info.description.to_owned(),
version: info.version.to_owned(),
author: info.author.to_owned(),
icon_url: info.icon_url.to_owned(),
}
}
}

impl RestApi {
pub fn new(endpoints: Arc<Endpoints>) -> Box<Endpoint> {
Box::new(RestApi {
Expand All @@ -43,14 +56,7 @@ impl RestApi {

fn list_apps(&self) -> Vec<App> {
self.endpoints.iter().filter_map(|(ref k, ref e)| {
e.info().map(|ref info| App {
id: k.to_owned().clone(),
name: info.name.to_owned(),
description: info.description.to_owned(),
version: info.version.to_owned(),
author: info.author.to_owned(),
icon_url: info.icon_url.to_owned(),
})
e.info().map(|ref info| App::from_info(k, info))
}).collect()
}
}
Expand Down
1 change: 1 addition & 0 deletions dapps/src/api/mod.rs.in
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ mod api;
mod response;

pub use self::api::RestApi;
pub use self::api::App;
116 changes: 116 additions & 0 deletions dapps/src/apps/fs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.

// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.

use serde_json;
use std::io;
use std::io::Read;
use std::fs;
use std::path::PathBuf;
use page::LocalPageEndpoint;
use endpoint::{Endpoints, EndpointInfo};
use api::App;

struct LocalDapp {
id: String,
path: PathBuf,
info: EndpointInfo,
}

fn local_dapps(dapps_path: String) -> Vec<LocalDapp> {
let files = fs::read_dir(dapps_path.as_str());
if let Err(e) = files {
warn!(target: "dapps", "Unable to load local dapps from: {}. Reason: {:?}", dapps_path, e);
return vec![];
}

let files = files.expect("Check is done earlier");
files.map(|dir| {
let entry = try!(dir);
let file_type = try!(entry.file_type());

// skip files
if file_type.is_file() {
return Err(io::Error::new(io::ErrorKind::NotFound, "Not a file"));
}

// take directory name and path
entry.file_name().into_string()
.map(|name| (name, entry.path()))
.map_err(|e| {
info!(target: "dapps", "Unable to load dapp: {:?}. Reason: {:?}", entry.path(), e);
io::Error::new(io::ErrorKind::NotFound, "Invalid name")
})
})
.filter_map(|m| {
if let Err(ref e) = m {
debug!(target: "dapps", "Ignoring local dapp: {:?}", e);
}
m.ok()
})
.map(|(name, path)| {
// try to get manifest file
let info = read_manifest(&name, path.clone());
LocalDapp {
id: name,
path: path,
info: info,
}
})
.collect()
}

fn read_manifest(name: &str, mut path: PathBuf) -> EndpointInfo {
path.push("manifest.json");

fs::File::open(path.clone())
.map_err(|e| format!("{:?}", e))
.and_then(|mut f| {
// Reat file
let mut s = String::new();
try!(f.read_to_string(&mut s).map_err(|e| format!("{:?}", e)));
// Try to deserialize manifest
serde_json::from_str::<App>(&s).map_err(|e| format!("{:?}", e))
})
.map(|app| EndpointInfo {
name: app.name,
description: app.description,
version: app.version,
author: app.author,
icon_url: app.icon_url,
})
.unwrap_or_else(|e| {
warn!(target: "dapps", "Cannot read manifest file at: {:?}. Error: {:?}", path, e);

EndpointInfo {
name: name.into(),
description: name.into(),
version: "0.0.0".into(),
author: "?".into(),
icon_url: "icon.png".into(),
}
})
}

pub fn local_endpoints(dapps_path: String) -> Endpoints {
let mut pages = Endpoints::new();
for dapp in local_dapps(dapps_path) {
pages.insert(
dapp.id,
Box::new(LocalPageEndpoint::new(dapp.path, dapp.info))
);
}
pages
}
13 changes: 8 additions & 5 deletions dapps/src/apps.rs → dapps/src/apps/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ use page::PageEndpoint;
use proxypac::ProxyPac;
use parity_dapps::WebApp;

mod fs;

extern crate parity_dapps_status;
extern crate parity_dapps_builtins;


pub const DAPPS_DOMAIN : &'static str = ".parity";
pub const RPC_PATH : &'static str = "rpc";
pub const API_PATH : &'static str = "api";
Expand All @@ -36,22 +37,24 @@ pub fn utils() -> Box<Endpoint> {
Box::new(PageEndpoint::with_prefix(parity_dapps_builtins::App::default(), UTILS_PATH.to_owned()))
}

pub fn all_endpoints() -> Endpoints {
let mut pages = Endpoints::new();
pages.insert("proxy".into(), ProxyPac::boxed());

pub fn all_endpoints(dapps_path: String) -> Endpoints {
// fetch fs dapps at first to avoid overwriting builtins
let mut pages = fs::local_endpoints(dapps_path);
// Home page needs to be safe embed
// because we use Cross-Origin LocalStorage.
// TODO [ToDr] Account naming should be moved to parity.
pages.insert("home".into(), Box::new(
PageEndpoint::new_safe_to_embed(parity_dapps_builtins::App::default())
));
pages.insert("proxy".into(), ProxyPac::boxed());
insert::<parity_dapps_status::App>(&mut pages, "status");
insert::<parity_dapps_status::App>(&mut pages, "parity");

// Optional dapps
wallet_page(&mut pages);
daodapp_page(&mut pages);
makerotc_page(&mut pages);

pages
}

Expand Down
14 changes: 7 additions & 7 deletions dapps/src/endpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,17 @@ pub struct EndpointPath {
pub port: u16,
}

#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Clone)]
pub struct EndpointInfo {
pub name: &'static str,
pub description: &'static str,
pub version: &'static str,
pub author: &'static str,
pub icon_url: &'static str,
pub name: String,
pub description: String,
pub version: String,
pub author: String,
pub icon_url: String,
}

pub trait Endpoint : Send + Sync {
fn info(&self) -> Option<EndpointInfo> { None }
fn info(&self) -> Option<&EndpointInfo> { None }

fn to_handler(&self, path: EndpointPath) -> Box<server::Handler<HttpStream>>;
}
Expand Down
13 changes: 8 additions & 5 deletions dapps/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ extern crate jsonrpc_core;
extern crate jsonrpc_http_server;
extern crate parity_dapps;
extern crate ethcore_rpc;
extern crate mime_guess;

mod endpoint;
mod apps;
Expand All @@ -73,6 +74,7 @@ static DAPPS_DOMAIN : &'static str = ".parity";

/// Webapps HTTP+RPC server build.
pub struct ServerBuilder {
dapps_path: String,
handler: Arc<IoHandler>,
}

Expand All @@ -84,22 +86,23 @@ impl Extendable for ServerBuilder {

impl ServerBuilder {
/// Construct new dapps server
pub fn new() -> Self {
pub fn new(dapps_path: String) -> Self {
ServerBuilder {
dapps_path: dapps_path,
handler: Arc::new(IoHandler::new())
}
}

/// Asynchronously start server with no authentication,
/// returns result with `Server` handle on success or an error.
pub fn start_unsecure_http(&self, addr: &SocketAddr) -> Result<Server, ServerError> {
Server::start_http(addr, NoAuth, self.handler.clone())
Server::start_http(addr, NoAuth, self.handler.clone(), self.dapps_path.clone())
}

/// Asynchronously start server with `HTTP Basic Authentication`,
/// return result with `Server` handle on success or an error.
pub fn start_basic_auth_http(&self, addr: &SocketAddr, username: &str, password: &str) -> Result<Server, ServerError> {
Server::start_http(addr, HttpBasicAuth::single_user(username, password), self.handler.clone())
Server::start_http(addr, HttpBasicAuth::single_user(username, password), self.handler.clone(), self.dapps_path.clone())
}
}

Expand All @@ -110,10 +113,10 @@ pub struct Server {
}

impl Server {
fn start_http<A: Authorization + 'static>(addr: &SocketAddr, authorization: A, handler: Arc<IoHandler>) -> Result<Server, ServerError> {
fn start_http<A: Authorization + 'static>(addr: &SocketAddr, authorization: A, handler: Arc<IoHandler>, dapps_path: String) -> Result<Server, ServerError> {
let panic_handler = Arc::new(Mutex::new(None));
let authorization = Arc::new(authorization);
let endpoints = Arc::new(apps::all_endpoints());
let endpoints = Arc::new(apps::all_endpoints(dapps_path));
let special = Arc::new({
let mut special = HashMap::new();
special.insert(router::SpecialEndpoint::Rpc, rpc::rpc(handler, panic_handler.clone()));
Expand Down
Loading

0 comments on commit bb1b8cc

Please sign in to comment.