diff --git a/Cargo.toml b/Cargo.toml index d3160c4..7dcde98 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,17 +1,22 @@ [package] name = "lucid" -description = "High performance and distributed KV ledger." +description = "A Fast, Secure and Distributed KV store with a HTTP API." version = "0.1.0" authors = ["Clint.Network "] edition = "2018" -keywords = ["kv", "distributed", "ledger", "kv-store", "key-value"] +keywords = ["kv", "distributed", "ledger", "kv-store", "key-value", "distributed key-value"] repository = "https://github.com/clintnetwork/lucid" license = "https://github.com/clintnetwork/lucid/blob/master/LICENSE.md" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[badges] +travis-ci = { repository = "clintnetwork/lucid", branch = "master" } [dependencies] -clap = "*" nickel = "*" serde = "*" -serde_json = "*" \ No newline at end of file +serde_json = "*" +rustflake = "*" + +[dependencies.clap] +version = "*" +features = ["yaml"] \ No newline at end of file diff --git a/README.md b/README.md index 66490c9..ffaafa3 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,22 @@ # About Lucid -An High performance and distributed KV ledger. Written in Rust +An High performance and distributed KV store accessible through a HTTP API. Written in Rust. 🦀 + +[![Build Status](https://travis-ci.com/clintnetwork/lucid.svg?branch=developement)](https://travis-ci.com/clintnetwork/lucid) +[![Made with Rust](https://img.shields.io/badge/Made%20With-Rust-dea584)](https://www.rust-lang.org/) +[![Clint.Network](https://img.shields.io/badge/Powered%20by-Clint.Network-blue.svg)](https://twitter.com/clint_network) ## Introduction -Provide an high performance and a distributed key-value-store accessible via an REST API is the mission of Lucid. +Lucid is currently in an embryonic state but we wish to achieve a fast, secure and distributed key-value store accessible through a HTTP API, we also want to propose persistence, encryption, websocket streaming, replication and a lots of features. + +## Some Use Cases -Lucid is currently in developement and we plan to implement some logics, like authentication, ACLs, encryption on the fly, and data streaming through API (websocket). +- Private Keys Storing (for a wallet by example) +- IoT: collect and save statistics data +- A distributed cache for an application +- Service Discovery +- Distributed Configuration ## Command Line Interface @@ -40,10 +50,10 @@ SUBCOMMANDS: ## Web Interface (UI) -Lucid want to propose an humain interface to manage objects, clusters and configuration. +Lucid want to propose an web UI to manage data, issue tokens, organize nodes and configure instances. ## About the Author Lucid is powered by [Clint.Network](https://twitter.com/clint_network) and published under the [MIT License](LICENSE.md). -If you want to make a little donation (or bigger), use this Bitcoin address: 3NhdjiGrpzH5geVrDHa173EuXxnAVhghtZ +If you want to make a little donation, use this Bitcoin address: 3NhdjiGrpzH5geVrDHa173EuXxnAVhghtZ or my [Patreon](https://www.patreon.com/clintnetwork). diff --git a/_config.yml b/_config.yml deleted file mode 100644 index 2f7efbe..0000000 --- a/_config.yml +++ /dev/null @@ -1 +0,0 @@ -theme: jekyll-theme-minimal \ No newline at end of file diff --git a/assets/logo.png b/assets/logo.png new file mode 100644 index 0000000..addeb2b Binary files /dev/null and b/assets/logo.png differ diff --git a/scripts/build.ps1 b/scripts/build.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/src/cli.yml b/src/cli.yml new file mode 100644 index 0000000..0b9d3aa --- /dev/null +++ b/src/cli.yml @@ -0,0 +1,183 @@ +name: Lucid +author: Written in Rust by Clint.Network (twitter.com/clint_network) +subcommands: + - cli: + about: Spawn to the command line interface + - init: + about: Initialize the Lucid cluster and generate configuration file + - members: + about: Manage members of the cluster + - server: + about: Run a new Lucid server instance + - settings: + about: Manage Lucid configuration file + - store: + about: Play with the KV store (get/set) + - tokens: + about: Manage JWT Tokens (issue, revoke etc.) + - server: + about: Run a new Lucid server instance + args: + - help: + help: Prints help information + short: h + long: help + # index: 1 + # required: true + takes_value: false + - scopt: + short: B + multiple: true + help: example subcommand option + takes_value: true + - scpos1: + help: example subcommand positional + index: 1 +args: + - help: + help: Prints help information + short: h + long: help + takes_value: false + - version: + help: Prints version information + short: v + long: version + takes_value: false + + +# - pos: +# help: example positional argument from yaml +# index: 1 +# # A list of possible values can be defined as a list +# possible_values: +# - fast +# - slow +# - flag: +# help: demo flag argument +# short: F +# multiple: true +# global: true +# # Conflicts, mutual overrides, and requirements can all be defined as a +# # list, where the key is the name of the other argument +# conflicts_with: +# - opt +# requires: +# - pos +# - mode: +# long: mode +# help: shows an option with specific values +# # possible_values can also be defined in this list format +# possible_values: [ vi, emacs ] +# takes_value: true +# - mvals: +# long: mult-vals +# help: demos an option which has two named values +# # value names can be described in a list, where the help will be shown +# # --mult-vals +# value_names: +# - one +# - two +# - minvals: +# long: min-vals +# multiple: true +# help: you must supply at least two values to satisfy me +# min_values: 2 +# - maxvals: +# long: max-vals +# multiple: true +# help: you can only supply a max of 3 values for me! +# max_values: 3 + +# AppSettings can be defined as a list and are **not** ascii case sensitive +# settings: +# - ArgRequiredElseHelp + +# # All Args must be defined in the 'args:' list where the name of the arg, is the +# # key to a Hash object +# args: +# # The name of this argument, is 'opt' which will be used to access the value +# # later in your Rust code +# - opt: +# help: example option argument from yaml +# short: o +# long: option +# multiple: true +# takes_value: true +# - pos: +# help: example positional argument from yaml +# index: 1 +# # A list of possible values can be defined as a list +# possible_values: +# - fast +# - slow +# - flag: +# help: demo flag argument +# short: F +# multiple: true +# global: true +# # Conflicts, mutual overrides, and requirements can all be defined as a +# # list, where the key is the name of the other argument +# conflicts_with: +# - opt +# requires: +# - pos +# - mode: +# long: mode +# help: shows an option with specific values +# # possible_values can also be defined in this list format +# possible_values: [ vi, emacs ] +# takes_value: true +# - mvals: +# long: mult-vals +# help: demos an option which has two named values +# # value names can be described in a list, where the help will be shown +# # --mult-vals +# value_names: +# - one +# - two +# - minvals: +# long: min-vals +# multiple: true +# help: you must supply at least two values to satisfy me +# min_values: 2 +# - maxvals: +# long: max-vals +# multiple: true +# help: you can only supply a max of 3 values for me! +# max_values: 3 + +# # All subcommands must be listed in the 'subcommand:' object, where the key to +# # the list is the name of the subcommand, and all settings for that command are +# # are part of a Hash object +# subcommands: +# # The name of this subcommand will be 'subcmd' which can be accessed in your +# # Rust code later +# - subcmd: +# about: demos subcommands from yaml +# version: "0.1" +# author: Kevin K. +# # Subcommand args are exactly like App args +# args: +# - scopt: +# short: B +# multiple: true +# help: example subcommand option +# takes_value: true +# - scpos1: +# help: example subcommand positional +# index: 1 + +# # ArgGroups are supported as well, and must be sepcified in the 'groups:' +# # object of this file +# groups: +# # the name of the ArgGoup is specified here +# - min-max-vals: +# # All args and groups that are a part of this group are set here +# args: +# - minvals +# - maxvals +# # setting conflicts is done the same manner as setting 'args:' +# # +# # to make this group required, you could set 'required: true' but for +# # this example we won't do that. \ No newline at end of file diff --git a/src/crossplatform.rs b/src/crossplatform.rs index 87ecd34..9ee6ce3 100644 --- a/src/crossplatform.rs +++ b/src/crossplatform.rs @@ -1,16 +1,9 @@ #[cfg(target_os = "linux")] fn get_binary() -> &'static str { - "lucid" + "./lucid" } -// And this function only gets compiled if the target OS is *not* linux #[cfg(not(target_os = "linux"))] fn get_binary() -> &'static str { "lucid.exe" -} - -// if cfg!(target_os = "linux") { -// println!("Yes. It's definitely linux!"); -// } else { -// println!("Yes. It's definitely *not* linux!"); -// } \ No newline at end of file +} \ No newline at end of file diff --git a/src/lucid.rs b/src/lucid.rs index aaba9cf..7c994f7 100644 --- a/src/lucid.rs +++ b/src/lucid.rs @@ -1,175 +1,80 @@ -extern crate clap; +mod server; + +use clap::{App}; -use std::io::Write; -use nickel::status::StatusCode::NotFound; -use nickel::*; -use clap::{App, SubCommand}; - include!("crossplatform.rs"); +include!("utils.rs"); -pub struct Lucid; +pub struct Lucid { + sever_instance: server::Server, +} -impl Lucid -{ - pub fn init() -> Lucid - { - Lucid { } +impl Lucid { + pub fn new() -> Lucid { + Lucid { + sever_instance: server::Server::new(), + } } - pub fn commandline(&self) -> Lucid - { - let mut commands = App::new("High performance and distributed KV ledger.") - .bin_name(get_binary()) - // .version("0.1.0") - .author("Written in Rust by Clint.Network") - .subcommand(SubCommand::with_name("monitor") - .about("controls testing features")) - .subcommand(SubCommand::with_name("server") - .about("Run an instance as server")) - .subcommand(SubCommand::with_name("auth") - .about("Manage credentials of the instance")) - .subcommand(SubCommand::with_name("members") - .about("Manage members of the cluster")) - .subcommand(SubCommand::with_name("cli") - .about("Spawn to the command line interface")) - .subcommand(SubCommand::with_name("settings") - .about("Configure the instance")); + pub fn banner(&self) -> () { + println!("{}", r###" + ██╗ ██╗ ██╗ ██████╗██╗██████╗ ██╗ ██╗██╗ ██╗ + ██║ ██║ ██║██╔════╝██║██╔══██╗ ██║ ██╔╝██║ ██║ + ██║ ██║ ██║██║ ██║██║ ██║ ██╔═██╗ ╚██╗ ██╔╝ + ██████╗╚██████╔╝╚██████╗██║██████╔╝ ██║ ██╗ ╚████╔╝ + ╚═════╝ ╚═════╝ ╚═════╝╚═╝╚═════╝ ╚═╝ ╚═╝ ╚═══╝ + "###); + } - match commands.get_matches_from_safe_borrow(::std::env::args_os()) + pub fn init(&self) -> Result<(), String> { + let yaml = load_yaml!("cli.yml"); + let mut commands = App::from_yaml(yaml) + .name(crate_description!()) + .bin_name(get_binary()); + + match self.parse_cli(&mut commands) { - Ok(content) => { - if content.subcommand_matches("server").is_some() - { - let mut server = Nickel::new(); - // server.options() + Some(_) => { }, + None => { + self.banner(); + commands.print_help().unwrap(); + println!("\n"); + } + }; + + Ok(()) + } - // server.post("/mdr", handler: H) - // server.get("/mdr", middleware!("This is the /bar handler")); - server.get("/mdr", middleware! { |request| - format!("OK OK OK") - }); + pub fn parse_cli(&self, commands: &mut App) -> Option<()> + { + if let Ok(cli) = commands.get_matches_from_safe_borrow(::std::env::args_os()) { + if cli.is_present("version") { + println!("Lucid Version {}", crate_version!()); + return Some(()); + } + else if cli.is_present("help") { + return None; + } - fn custom_404<'a>(err: &mut NickelError, _req: &mut Request) -> Action { - if let Some(ref mut res) = err.stream { - if res.status() == NotFound { - res.write_all(b"404 Not Found").expect("Unable to write in the stream"); - // res.headers().append_raw("salut", b"dsds".to_vec()); - // let _ = res.write_all(format!("{:#?}", res.headers()).to_string().as_bytes()); - return Halt(()) - } + match cli.subcommand_name() { + Some("server") => { + match self.sever_instance.run() + { + Ok(_) => { }, + Err(e) => { + println!("Unable to launch Lucid server.\n{}", e); } - Continue(()) } - - let custom_handler: fn(&mut NickelError, &mut Request) -> Action = custom_404; - server.handle_error(custom_handler); - server.listen("127.0.0.1:7210").unwrap(); - } - else - { - commands.print_help().unwrap(); + return Some(()); + }, + None => { + return None; + }, + _ => { + return None; } - }, - Err(_) => { - commands.print_help().unwrap(); } } - Lucid { } - } - - pub fn banner(&self) -> Lucid - { - let lucid_banner = r###" _ _ _ ___ ___ ___ _ - | | | | | |/ __|_ _| \| | - | |_| |_| | (__ | || |) |_| - |____\___/ \___|___|___/(_) -"###; - println!("{}", lucid_banner); - Lucid { } + return None; } - - // pub fn listen(&self) -> Lucid - // { - // Lucid { } - // } -} - -// pub fn banner() -> String -// { -// "lol".to_string() -// } - -// pub trait Lucid -// { -// fn summarize() -> &'static str; -// } - -// impl Lucid -// { -// fn summarize() -> &'static str -// { -// println!("ok"); -// "alut" -// } -// } - -// extern crate clap; -// use clap::{App, SubCommand}; - -// #[cfg(target_os = "linux")] -// fn get_binary() -> &'static str { -// "lucid" -// } - -// // And this function only gets compiled if the target OS is *not* linux -// #[cfg(not(target_os = "linux"))] -// fn get_binary() -> &'static str { -// "lucid.exe" -// } - -// fn main() -// { -// let lucid_banner = r###" _ _ _ ___ ___ ___ _ -// | | | | | |/ __|_ _| \| | -// | |_| |_| | (__ | || |) |_| -// |____\___/ \___|___|___/(_) -// "###; -// println!("{}", lucid_banner); - -// let mut commands = App::new("High performance and distributed KV ledger.") -// .bin_name(get_binary()) -// // .version("0.1.0") -// .author("Written in Rust by Clint.Network") -// .subcommand(SubCommand::with_name("monitor") -// .about("controls testing features")) -// .subcommand(SubCommand::with_name("server") -// .about("Run an instance as server")) -// .subcommand(SubCommand::with_name("auth") -// .about("Manage credentials of the instance")) -// .subcommand(SubCommand::with_name("members") -// .about("Manage members of the cluster")) -// .subcommand(SubCommand::with_name("settings") -// .about("Configure the instance")) -// ; -// // .about("controls testing features") -// // .version("1.3") -// // .author("Someone E. ") -// // .arg_from_usage("-d, --debug 'Print debug information'")); - -// match commands.get_matches_from_safe_borrow(::std::env::args_os()) -// { -// Ok(content) => { -// if content.subcommand_matches("test").is_some() -// { -// println!("yeah"); -// } -// else -// { -// commands.print_help().unwrap(); -// } -// }, -// Err(_) => { -// commands.print_help().unwrap(); -// } -// } -// } \ No newline at end of file +} \ No newline at end of file diff --git a/src/lucid/cli.rs b/src/lucid/cli.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/lucid/server.rs b/src/lucid/server.rs new file mode 100644 index 0000000..c4942fd --- /dev/null +++ b/src/lucid/server.rs @@ -0,0 +1,78 @@ +use std::io::Write; +use std::collections::HashMap; +use nickel::status::StatusCode::NotFound; +// use nickel::{Nickel, HttpRouter, Request, Response, MiddlewareResult, NickelError, Action, Halt, Continue}; +use nickel::*; + +fn logger<'a, D>(request: &mut Request, response: Response<'a, D>) -> MiddlewareResult<'a, D> { + println!("logging request: {:?}", request.origin.uri); + response.next_middleware() +} + +fn handler_error_404<'a>(err: &mut NickelError, _request: &mut Request) -> Action { + if let Some(ref mut res) = err.stream { + if res.status() == NotFound { + // TODO: display vuejs error page + res.write_all(b"404 Not Found").expect("Unable to write in the stream"); + return Halt(()) + } + } + Continue(()) +} + +fn handler_vuejs<'a> (_: &mut Request, res: Response<'a>) -> MiddlewareResult<'a> { + let mut data = HashMap::<&str, &str>::new(); + data.insert("name", "Alex"); + res.render("webui/dist/index.tpl", &data) +} + +pub struct Server { + port: i32, +} + +impl Server { + pub fn new() -> Server { + Server { + port: 7221 + } + } + + fn router_webui(&self) -> nickel::Router { + let mut router = Nickel::router(); + router.get("/", handler_vuejs); + router + } + + fn router_api(&self) -> nickel::Router { + let mut router = Nickel::router(); + router.get("/api/**", middleware!("You call API")); + router + } + + pub fn run(&self) -> Result> { + // TODO: move into struct + let mut daemon = Nickel::new(); + + daemon.utilize(logger); + + daemon.utilize(StaticFilesHandler::new("assets/")); + daemon.utilize(StaticFilesHandler::new("webui/dist")); + + daemon.utilize(self.router_api()); + daemon.utilize(self.router_webui()); + + let custom_handler: fn(&mut NickelError, &mut Request) -> Action = handler_error_404; + daemon.handle_error(custom_handler); + + // TODO: Implement HTTPS (https://github.com/nickel-org/nickel.rs/blob/master/examples/https.rs) + daemon.listen(self.where_to_bind()) + // daemon.listen(("0.00.0", self.where_to_bind())) + } + + fn where_to_bind(&self) -> String + { + // env::var("PORT").unwrap_or("6767".to_string()).parse().unwrap() + // TODO: implement configuration + return format!("127.0.0.1:{}", self.port); + } +} diff --git a/src/main.rs b/src/main.rs index 1f088e1..7ad7e28 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,14 +4,14 @@ * Date: 28/07/2019 */ -#[macro_use] extern crate nickel; +#[macro_use] +extern crate clap; mod lucid; use lucid::Lucid; -fn main() +fn main() -> Result<(), String> { - Lucid::init() - .banner() - .commandline(); -} \ No newline at end of file + let lucid = Lucid::new(); + lucid.init() +} diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..0a87659 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,12 @@ +// use rustflake::Snowflake; + +// fn generate_snowflake() -> i64 { +// let mut snowflake = Snowflake::default(); +// snowflake.generate() +// } + +// fn generate_snowflake(datacenter_id: i64) -> i64 { +// let mut snowflake = Snowflake::default(); +// snowflake.datacenter_id(datacenter_id); +// snowflake.generate() +// } \ No newline at end of file diff --git a/webui/public/index.html b/webui/public/index.html index ffa8954..87159de 100644 --- a/webui/public/index.html +++ b/webui/public/index.html @@ -12,6 +12,5 @@ We're sorry but lucid doesn't work properly without JavaScript enabled. Please enable it to continue.
- diff --git a/webui/src/App.vue b/webui/src/App.vue index fcc5662..a1cd1b2 100644 --- a/webui/src/App.vue +++ b/webui/src/App.vue @@ -1,17 +1,13 @@ diff --git a/webui/src/components/HelloWorld.vue b/webui/src/components/HelloWorld.vue deleted file mode 100644 index 879051a..0000000 --- a/webui/src/components/HelloWorld.vue +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - diff --git a/webui/vue.config.js b/webui/vue.config.js index 9e31def..a7791b1 100644 --- a/webui/vue.config.js +++ b/webui/vue.config.js @@ -12,5 +12,4 @@ module.exports = { }, // assetsDir: '../../assets/', indexPath: 'index.tpl' - // outputDir: '../main/resources/', } \ No newline at end of file