Merging new cli options into testing to ensure cli opts are working in
pipelines
This commit is contained in:
@@ -4,6 +4,7 @@ use crate::db_types::{UBigInt, VarChar};
|
||||
|
||||
use crate::routes;
|
||||
|
||||
pub const BCRYPT_COST: u32 = 14;
|
||||
pub enum AuthReason {
|
||||
Good, //passed regular check
|
||||
OpenAuth, // route does not require auth
|
||||
@@ -56,3 +57,15 @@ pub async fn wall_entry(path: &str, pool: &Pool, params: &mut serde_json::Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_secret() -> String {
|
||||
/*
|
||||
* Generates a url-safe-plaintext secret for our db
|
||||
* */
|
||||
use getrandom::getrandom;
|
||||
use base64::{encode_config, URL_SAFE};
|
||||
|
||||
let mut buf: Vec<u8> = vec![0;64];
|
||||
getrandom(&mut buf);
|
||||
encode_config(buf,URL_SAFE)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
extern crate chrono;
|
||||
extern crate clap;
|
||||
extern crate dotenv;
|
||||
extern crate getrandom;
|
||||
extern crate bcrypt;
|
||||
extern crate base64;
|
||||
extern crate serde;
|
||||
|
||||
use std::net::SocketAddr;
|
||||
use std::convert::Infallible; // our main dispatcher basically never fails hence why we use this
|
||||
use std::env;
|
||||
use std::env::{self, set_var};
|
||||
|
||||
use tokio;
|
||||
use hyper::{
|
||||
@@ -16,7 +19,9 @@ use hyper::{
|
||||
service::{make_service_fn, service_fn}
|
||||
};
|
||||
use mysql_async::Pool;
|
||||
|
||||
use dotenv::dotenv;
|
||||
use clap::{Arg, App};
|
||||
|
||||
mod auth;
|
||||
use auth::AuthReason;
|
||||
@@ -24,12 +29,18 @@ use auth::AuthReason;
|
||||
mod routes;
|
||||
mod invites;
|
||||
mod channels;
|
||||
|
||||
mod members;
|
||||
|
||||
mod messages;
|
||||
mod http_params;
|
||||
mod perms;
|
||||
mod db_types;
|
||||
|
||||
const NO_ERR: u16 = 0;
|
||||
const CONFIG_ERR: u16 = 1;
|
||||
const SHUTDOWN_ERR: u16 = 2;
|
||||
|
||||
async fn route_dispatcher(pool: &Pool, resp: &mut Response<Body>, meth: &Method, path: &str, params: serde_json::Value) {
|
||||
// At some point we should have some way of hiding this obnoxious complexity
|
||||
use routes::resolve_dynamic_route;
|
||||
@@ -109,33 +120,96 @@ async fn shutdown_signal() {
|
||||
.await
|
||||
.expect("Failed to capture ctrl-c signal");
|
||||
}
|
||||
|
||||
async fn start_server(ecode: u16) -> u16 {
|
||||
println!("Servering on localhost:8888");
|
||||
let addr = SocketAddr::from(([127,0,0,1], 8888));
|
||||
let service = make_service_fn(|_conn| async {
|
||||
Ok::<_, Infallible>(service_fn(main_responder))
|
||||
});
|
||||
let server = Server::bind(&addr).serve(service);
|
||||
let graceful_shutdown = server.with_graceful_shutdown(shutdown_signal());
|
||||
|
||||
if let Err(e) = graceful_shutdown.await {
|
||||
eprintln!("Server shutdown error: {}", e);
|
||||
return ecode | SHUTDOWN_ERR;
|
||||
}
|
||||
else {
|
||||
return ecode
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), u16>{
|
||||
const NO_ERR: u16 = 0;
|
||||
const CONFIG_ERR: u16 = 1;
|
||||
const SHUTDOWN_ERR: u16 = 2;
|
||||
|
||||
let mut main_ret: u16 = 0;
|
||||
// setting up environment variables
|
||||
let d_result = dotenv();
|
||||
|
||||
// check for a database_url before the override we get from the cmd line
|
||||
if let Err(_d) = d_result {
|
||||
// we may be on a pipeline/prod environment so .env may not be there
|
||||
if let Err(_e) = env::var("DATABASE_URL") {
|
||||
main_ret |= CONFIG_ERR;
|
||||
}
|
||||
}
|
||||
if main_ret == NO_ERR {
|
||||
println!("Servering on localhost:8888");
|
||||
let addr = SocketAddr::from(([127,0,0,1], 8888));
|
||||
let service = make_service_fn(|_conn| async {
|
||||
Ok::<_, Infallible>(service_fn(main_responder))
|
||||
});
|
||||
let server = Server::bind(&addr).serve(service);
|
||||
let graceful_shutdown = server.with_graceful_shutdown(shutdown_signal());
|
||||
|
||||
if let Err(e) = graceful_shutdown.await {
|
||||
main_ret |= SHUTDOWN_ERR;
|
||||
eprintln!("Server shutdown error: {}", e);
|
||||
let args = App::new("Freechat Server")
|
||||
.version("0.1")
|
||||
.author("shockrah")
|
||||
.about("Decentralized chat system")
|
||||
.arg(Arg::with_name("db-url")
|
||||
.short("d")
|
||||
.long("db-url")
|
||||
.value_name("DATABASE URL")
|
||||
.help("Sets the DATABASE URL via an environment variable")
|
||||
.takes_value(true))
|
||||
.arg(Arg::with_name("create-owner")
|
||||
.short("c")
|
||||
.long("create-owner")
|
||||
.value_name("Owner")
|
||||
.help("Creates an account with full permissions in the SQL database."))
|
||||
.arg(Arg::with_name("server")
|
||||
.short("s")
|
||||
.long("server")
|
||||
.help("Starts the API server"))
|
||||
.get_matches();
|
||||
|
||||
|
||||
if args.args.len() == 0 {
|
||||
println!("Freechat Server 0.1
|
||||
shockrah
|
||||
Decentralized chat system
|
||||
|
||||
USAGE:
|
||||
freechat-server [FLAGS] [OPTIONS]
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-s, --server Starts the API server
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-c, --create-owner <Owner> Creates an account with full permissions in the SQL database.
|
||||
-d, --db-url <DATABASE URL> Sets the DATABASE URL via an environment variable");
|
||||
}
|
||||
|
||||
if let Some(db_url) = args.value_of("db-url") {
|
||||
set_var("DATABASE_URL", db_url);
|
||||
}
|
||||
|
||||
if let Some(owner_name) = args.value_of("create-owner") {
|
||||
let p = Pool::new(&env::var("DATABASE_URL").unwrap());
|
||||
println!("Creating owner {{ {} }}...", owner_name);
|
||||
if let Ok(owner) = members::insert_new_member(&p, owner_name.to_string(), std::u64::MAX).await {
|
||||
println!("{}", serde_json::to_string(&owner).unwrap());
|
||||
}
|
||||
p.disconnect();
|
||||
}
|
||||
|
||||
if args.is_present("server") {
|
||||
if main_ret == NO_ERR {
|
||||
main_ret = start_server(main_ret).await;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,26 +1,21 @@
|
||||
use chrono::Utc;
|
||||
use hyper::{Body, Response, StatusCode};
|
||||
use hyper::header::{HeaderName, HeaderValue};
|
||||
use mysql_async::{Conn, Pool};
|
||||
use mysql_async::{Conn, Pool, error::Error as MySqlError};
|
||||
use mysql_async::prelude::{params, Queryable};
|
||||
use mysql_async::error::Error as SQLError;
|
||||
use serde_json::Value;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::db_types::{UBigInt, BigInt, Integer, VarChar};
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct Member {
|
||||
id: UBigInt,
|
||||
secret: VarChar,
|
||||
name: VarChar,
|
||||
joindate: BigInt,
|
||||
status: Integer,
|
||||
permissions: UBigInt,
|
||||
}
|
||||
|
||||
impl Member {
|
||||
pub fn to_json(&self) -> String {
|
||||
unimplemented!()
|
||||
}
|
||||
pub id: UBigInt,
|
||||
pub secret: VarChar,
|
||||
pub name: VarChar,
|
||||
pub joindate: BigInt,
|
||||
pub status: Integer,
|
||||
pub permissions: UBigInt,
|
||||
}
|
||||
|
||||
struct InsertableMember<'n> {
|
||||
@@ -40,32 +35,39 @@ impl<'n> InsertableMember<'n> {
|
||||
}
|
||||
}
|
||||
|
||||
async fn insert_new_member<'nm>(conn: Conn, mem: InsertableMember<'nm>)
|
||||
-> Result<Member, SQLError> {
|
||||
// standard insertion with no checks, those should be done before we get here
|
||||
// in theory only 500's should happen here assuming we did everything else right
|
||||
let conn = conn.batch_exec(
|
||||
"INSERT INTO `members` () ",
|
||||
|
||||
pub async fn insert_new_member(p: &Pool, name: VarChar, perms: u64) -> Result<Member, MySqlError> {
|
||||
use crate::auth::generate_secret;
|
||||
|
||||
let conn: Conn = p.get_conn().await?;
|
||||
let secret: String = generate_secret();
|
||||
let now: BigInt = Utc::now().timestamp();
|
||||
|
||||
let conn = conn.drop_exec(
|
||||
"INSERT INTO members(secret, name, joindate, status, permissions)
|
||||
VALUES(:secret, :name, :joindate, :status, :permissions)",
|
||||
mysql_async::params!{
|
||||
"name" => mem.name,
|
||||
"permissions" => mem.permissions,
|
||||
/*"badges" => mem.permissions*/ // not used yet as we have no serializer
|
||||
"secret" => secret.clone(),
|
||||
"name" => name.clone(),
|
||||
"joindate" => now,
|
||||
"status" => 0,
|
||||
"permissions" => perms
|
||||
}).await?;
|
||||
|
||||
type MemberRow = (UBigInt, VarChar, VarChar, BigInt, Integer, UBigInt);
|
||||
let (conn, row): (Conn, Option<MemberRow>) = conn.first_exec(
|
||||
"SELECT * from `members` WHERE `secret` = :secret ",
|
||||
// now pull back the user from our db and return that row
|
||||
let db_row_result: (Conn, Option<UBigInt>) = conn.first_exec(
|
||||
"SELECT id FROM members WHERE secret = :secret",
|
||||
params!{
|
||||
"secret" => mem.name
|
||||
"secret" => secret.clone()
|
||||
}).await?;
|
||||
|
||||
Ok(Member {
|
||||
id: 0,
|
||||
secret: "secret".into(),
|
||||
name: "String".into(),
|
||||
joindate: 123,
|
||||
status: 69,
|
||||
permissions: 0,
|
||||
id: db_row_result.1.unwrap(), // if we made it this far this shouldn't fail (i hope)
|
||||
secret: secret,
|
||||
name: name,
|
||||
joindate: now,
|
||||
status: 0,
|
||||
permissions: perms
|
||||
})
|
||||
}
|
||||
|
||||
@@ -73,25 +75,24 @@ async fn general_new_user(p: &Pool, resp: &mut Response<Body>, params: Value) {
|
||||
/*
|
||||
* @name: string => desired default name
|
||||
*/
|
||||
use crate::perms;
|
||||
let default_name = serde_json::json!("NewUser");
|
||||
let name = params.get("name")
|
||||
.unwrap_or(&default_name)
|
||||
.as_str().unwrap_or("NewUser");
|
||||
|
||||
let pre_mem = InsertableMember::new(name);
|
||||
if let Ok(conn) = p.get_conn().await {
|
||||
match insert_new_member(conn, pre_mem).await {
|
||||
Ok(new_member) => {
|
||||
*resp.status_mut() = StatusCode::OK;
|
||||
let json_hdr_name = HeaderName::from_static("Content-Type");
|
||||
let json_hdr_val = HeaderValue::from_static("application/json");
|
||||
resp.headers_mut().insert(json_hdr_name, json_hdr_val);
|
||||
*resp.body_mut() = Body::from(new_member.to_json());
|
||||
},
|
||||
Err(_) => {
|
||||
*resp.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
|
||||
*resp.body_mut() = Body::from("Could not process input");
|
||||
}
|
||||
match insert_new_member(p, name.to_string(), perms::GENERAL_NEW).await {
|
||||
Ok(new_member) => {
|
||||
*resp.status_mut() = StatusCode::OK;
|
||||
let json_hdr_name = HeaderName::from_static("Content-Type");
|
||||
let json_hdr_val = HeaderValue::from_static("application/json");
|
||||
resp.headers_mut().insert(json_hdr_name, json_hdr_val);
|
||||
*resp.body_mut() = Body::from(serde_json::to_string(&new_member).unwrap());
|
||||
},
|
||||
Err(_) => {
|
||||
*resp.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
|
||||
*resp.body_mut() = Body::from("Could not process input");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
pub const JOIN_VOICE:u64 = 1;
|
||||
pub const SEND_MESSAGES:u64 = 2;
|
||||
pub const SEND_MESSAGES:u64 = 2;
|
||||
|
||||
pub const GENERAL_NEW: u64 = JOIN_VOICE | SEND_MESSAGES;
|
||||
|
||||
Reference in New Issue
Block a user