Commit 04a1e9dc authored by Robert Czechowski's avatar Robert Czechowski
Browse files

Complete Cargo.toml, add features (closes #35), refactor config and options into config module

parent 9bd2ddd2
Pipeline #191 passed with stage
in 15 minutes and 58 seconds
[package]
name = "medal"
version = "0.1.0"
authors = ["Robert Czechowski <czechowski@bwinf.de>"]
authors = ["Robert Czechowski <czechowski@bwinf.de>", "Daniel Brüning <bruening@bwinf.de>"]
description = "A simple online contest platform"
homepage = "https://bwinf.de/jugendwettbewerb"
repository = "https://git.bwinf.de/zgtm/medal-prototype"
readme = "README.md"
license = "LGPL-3.0"
#maintenance = { status = "active-developed" }
[features]
default = ["rusqlite"]
complete = ["rusqlite", "postgres", "webbrowser"]
server = ["rusqlite", "postgres"]
desktop = ["rusqlite", "webbrowser"]
watch = ["handlebars-iron/watch"]
strict = [] # Treat warnings as a build error
[dependencies]
rusqlite = "0.14.0"
time = "0.1.40"
iron = "0.5.1"
rand = "0.5.5"
mount ="0.3"
router = "0.5"
......@@ -25,19 +34,23 @@ handlebars-iron = "0.25.1"
iron-sessionstorage = "*"
serde_yaml = "*"
structopt = "*"
reqwest = "0.6.2"
linked-hash-map = "0.5.1"
bcrypt = "0.3"
webbrowser = "0.5.1"
[dependencies.serde_json]
version = "1.0.20"
features = ["preserve_order"]
[dependencies.webbrowser]
version = "0.5.1"
optional = true
[dependencies.rusqlite]
version = "0.14.0"
optional = true
[dependencies.postgres]
version = "0.15"
features = ["with-time"]
optional = true
\ No newline at end of file
debug:
RUST_BACKTRACE=1 cargo run --features watch -- -a
RUST_BACKTRACE=1 cargo run --features 'watch' -- -a
test:
RUST_BACKTRACE=1 cargo test --features watch
RUST_BACKTRACE=1 cargo test --features 'watch complete'
release:
env OPENSSL_LIB_DIR=/usr/lib/x86_64-linux-gnu/ OPENSSL_INCLUDE_DIR=/usr/local/include OPENSSL_STATIC=yes cargo build --release
env OPENSSL_LIB_DIR=/usr/lib/x86_64-linux-gnu/ OPENSSL_INCLUDE_DIR=/usr/local/include OPENSSL_STATIC=yes cargo build --release --features 'server'
format:
cargo +nightly fmt
......
use oauth_provider;
use std::path::{Path, PathBuf};
use structopt::StructOpt;
#[derive(Serialize, Deserialize, Clone, Default, Debug)]
pub struct Config {
pub host: Option<String>,
pub port: Option<u16>,
pub self_url: Option<String>,
pub oauth_providers: Option<Vec<oauth_provider::OauthProvider>>,
pub database_file: Option<PathBuf>,
pub database_url: Option<String>,
pub template: Option<String>,
pub no_contest_scan: Option<bool>,
pub open_browser: Option<bool>,
}
#[derive(StructOpt, Debug)]
#[structopt()]
pub struct Opt {
/// Config file to use (default: 'config.json')
#[structopt(short = "c", long = "config", default_value = "config.json", parse(from_os_str))]
pub configfile: PathBuf,
/// Database file to use (default: from config file or 'medal.db')
#[structopt(short = "d", long = "database", parse(from_os_str))]
pub databasefile: Option<PathBuf>,
/// Database file to use (default: from config file or 'medal.db')
#[structopt(short = "D", long = "databaseurl")]
pub databaseurl: Option<String>,
/// Port to listen on (default: from config file or 8080)
#[structopt(short = "p", long = "port")]
pub port: Option<u16>,
/// Reset password of admin user (user_id=1)
#[structopt(short = "a", long = "reset-admin-pw")]
pub resetadminpw: bool,
/// Run medal without scanning for contests
#[structopt(short = "S", long = "no-contest-scan")]
pub nocontestscan: bool,
/// Scan for contests without starting medal
#[structopt(short = "s", long = "only-contest-scan")]
pub onlycontestscan: bool,
/// Automatically open medal in the default browser
#[structopt(short = "b", long = "browser")]
pub openbrowser: bool,
}
pub fn read_config_from_file(file: &Path) -> Config {
use std::io::Read;
println!("Reading configuration file '{}'", file.to_str().unwrap_or("<Encoding error>"));
let mut config: Config = if let Ok(mut file) = std::fs::File::open(file) {
let mut contents = String::new();
file.read_to_string(&mut contents).unwrap();
serde_json::from_str(&contents).unwrap()
} else {
println!("Configuration file '{}' not found.", file.to_str().unwrap_or("<Encoding error>"));
Default::default()
};
if let Some(ref oap) = config.oauth_providers {
println!("OAuth providers:");
for oap in oap {
println!(" * {}", oap.provider_id);
}
}
if config.host.is_none() {
config.host = Some("[::]".to_string())
}
if config.port.is_none() {
config.port = Some(8080)
}
if config.self_url.is_none() {
config.self_url = Some("http://localhost:8080".to_string())
}
if config.template.is_none() {
config.template = Some("default".to_string())
}
if config.no_contest_scan.is_none() {
config.no_contest_scan = Some(false)
}
if config.open_browser.is_none() {
config.open_browser = Some(false)
}
println!("OAuth providers will be told to redirect to {}", config.self_url.as_ref().unwrap());
config
}
#![cfg(feature = "postgres")]
extern crate bcrypt;
extern crate postgres;
......
#![cfg(feature = "rusqlite")]
extern crate bcrypt;
extern crate rusqlite;
......
......@@ -12,9 +12,11 @@ extern crate iron_sessionstorage;
extern crate mount;
extern crate params;
extern crate persistent;
#[cfg(feature = "postgres")]
extern crate postgres;
extern crate rand;
extern crate reqwest;
#[cfg(feature = "rusqlite")]
extern crate rusqlite;
extern crate serde_json;
extern crate serde_yaml;
......@@ -22,20 +24,21 @@ extern crate staticfile;
extern crate structopt;
extern crate time;
extern crate urlencoded;
#[cfg(feature = "webbrowser")]
extern crate webbrowser;
mod db_apply_migrations;
mod db_conn;
pub mod db_conn;
mod db_conn_postgres;
mod db_conn_sqlite;
mod db_objects;
pub mod config;
pub mod contestreader_yaml;
pub mod functions;
pub mod oauth_provider;
mod webfw_iron;
pub use db_conn::{MedalConnection, MedalObject};
use db_conn::{MedalConnection, MedalObject};
use functions::SetPassword; // TODO: Refactor, so we don't need to take this from there!
use db_objects::*;
......@@ -45,102 +48,10 @@ use webfw_iron::start_server;
use std::fs;
use std::path;
use std::path::{Path, PathBuf};
use std::path::Path;
use structopt::StructOpt;
#[derive(Serialize, Deserialize, Clone, Default, Debug)]
pub struct Config {
host: Option<String>,
port: Option<u16>,
self_url: Option<String>,
oauth_providers: Option<Vec<oauth_provider::OauthProvider>>,
database_file: Option<PathBuf>,
database_url: Option<String>,
template: Option<String>,
no_contest_scan: Option<bool>,
open_browser: Option<bool>,
}
fn read_config_from_file(file: &Path) -> Config {
use std::io::Read;
println!("Reading configuration file '{}'", file.to_str().unwrap_or("<Encoding error>"));
let mut config: Config = if let Ok(mut file) = fs::File::open(file) {
let mut contents = String::new();
file.read_to_string(&mut contents).unwrap();
serde_json::from_str(&contents).unwrap()
} else {
println!("Configuration file '{}' not found.", file.to_str().unwrap_or("<Encoding error>"));
Default::default()
};
if let Some(ref oap) = config.oauth_providers {
println!("OAuth providers:");
for oap in oap {
println!(" * {}", oap.provider_id);
}
}
if config.host.is_none() {
config.host = Some("[::]".to_string())
}
if config.port.is_none() {
config.port = Some(8080)
}
if config.self_url.is_none() {
config.self_url = Some("http://localhost:8080".to_string())
}
if config.template.is_none() {
config.template = Some("default".to_string())
}
if config.no_contest_scan.is_none() {
config.no_contest_scan = Some(false)
}
if config.open_browser.is_none() {
config.open_browser = Some(false)
}
println!("OAuth providers will be told to redirect to {}", config.self_url.as_ref().unwrap());
config
}
#[derive(StructOpt, Debug)]
#[structopt()]
struct Opt {
/// Config file to use (default: 'config.json')
#[structopt(short = "c", long = "config", default_value = "config.json", parse(from_os_str))]
configfile: PathBuf,
/// Database file to use (default: from config file or 'medal.db')
#[structopt(short = "d", long = "database", parse(from_os_str))]
databasefile: Option<PathBuf>,
/// Database file to use (default: from config file or 'medal.db')
#[structopt(short = "D", long = "databaseurl")]
databaseurl: Option<String>,
/// Port to listen on (default: from config file or 8080)
#[structopt(short = "p", long = "port")]
port: Option<u16>,
/// Reset password of admin user (user_id=1)
#[structopt(short = "a", long = "reset-admin-pw")]
resetadminpw: bool,
/// Run medal without scanning for contests
#[structopt(short = "S", long = "no-contest-scan")]
nocontestscan: bool,
/// Scan for contests without starting medal
#[structopt(short = "s", long = "only-contest-scan")]
onlycontestscan: bool,
/// Automatically open medal in the default browser
#[structopt(short = "b", long = "browser")]
openbrowser: bool,
}
use config::Config;
fn read_contest(p: &path::PathBuf) -> Option<Contest> {
use std::fs::File;
......@@ -245,16 +156,21 @@ fn prepare_and_start_server<C>(mut conn: C, config: Config, onlycontestscan: boo
if !onlycontestscan {
add_admin_user(&mut conn, resetadminpw);
#[cfg(feature = "webbrowser")]
let self_url = config.self_url.clone();
#[cfg(feature = "webbrowser")]
let open_browser = config.open_browser;
match start_server(conn, config) {
Ok(_) => {
println!("Server started");
#[cfg(feature = "webbrowser")]
{
if let (Some(self_url), Some(true)) = (self_url, open_browser) {
open_browser_window(&self_url);
}
}
}
Err(_) => println!("Error on server start …"),
};
......@@ -262,6 +178,7 @@ fn prepare_and_start_server<C>(mut conn: C, config: Config, onlycontestscan: boo
}
}
#[cfg(feature = "webbrowser")]
fn open_browser_window(self_url: &str) {
match webbrowser::open(&self_url) {
Ok(_) => (),
......@@ -270,10 +187,10 @@ fn open_browser_window(self_url: &str) {
}
fn main() {
let opt = Opt::from_args();
let opt = config::Opt::from_args();
//println!("{:?}", opt); // Show in different debug level?
let mut config = read_config_from_file(&opt.configfile);
let mut config = config::read_config_from_file(&opt.configfile);
if opt.databasefile.is_some() {
config.database_file = opt.databasefile;
......@@ -294,23 +211,31 @@ fn main() {
config.open_browser = Some(true)
}
if config.database_url.is_some() {
let url = config.database_url.clone().unwrap();
#[cfg(feature = "postgres")]
{
if let Some(url) = config.database_url.clone() {
print!("Using database {} … ", &url);
let conn = postgres::Connection::connect(url, postgres::TlsMode::None).unwrap();
println!("Connected");
prepare_and_start_server(conn, config, opt.onlycontestscan, opt.resetadminpw);
} else {
let path = config.database_file.clone().unwrap();
return;
}
}
#[cfg(feature = "rusqlite")]
{
if let Some(path) = config.database_file.clone() {
print!("Using database file {} … ", &path.to_str().unwrap_or("<unprintable filename>"));
let conn = rusqlite::Connection::open(path).unwrap();
println!("Connected");
prepare_and_start_server(conn, config, opt.onlycontestscan, opt.resetadminpw);
return;
}
}
println!("No database configured. Try enableing the 'rusqlite' feature during compilation.\nLeaving now.");
}
#[cfg(test)]
......@@ -338,7 +263,7 @@ mod tests {
}
}
let mut config = read_config_from_file(Path::new("thisfileshoudnotexist"));
let mut config = config::read_config_from_file(Path::new("thisfileshoudnotexist"));
config.port = Some(port);
let srvr = start_server(conn, config);
......
......@@ -29,6 +29,7 @@ use db_conn::MedalConnection;
pub use serde_json::value as json_val;
use config::Config;
use iron::typemap::Key;
static TASK_DIR: &'static str = "tasks";
......@@ -805,7 +806,7 @@ impl<C> Key for SharedDatabaseConnection<C> where C: MedalConnection + 'static
#[derive(Copy, Clone)]
pub struct SharedConfiguration;
impl Key for SharedConfiguration {
type Value = ::Config;
type Value = Config;
}
#[cfg(feature = "watch")]
......@@ -856,7 +857,7 @@ fn cookie_warning(req: &mut Request) -> IronResult<Response> {
}
}
pub fn start_server<C>(conn: C, config: ::Config) -> iron::error::HttpResult<iron::Listening>
pub fn start_server<C>(conn: C, config: Config) -> iron::error::HttpResult<iron::Listening>
where C: MedalConnection + std::marker::Send + 'static {
let router = router!(
greet: get "/" => greet_personal::<C>,
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment