Improve config system
Add pkl to generate configs
This commit is contained in:
parent
8785186dfa
commit
b2ff632e64
47 changed files with 325 additions and 262 deletions
3
.cargo/config.toml
Normal file
3
.cargo/config.toml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[alias]
|
||||||
|
format = "+nightly fmt"
|
||||||
|
lint = "clippy --all-targets --all-features -- -D warnings"
|
2
.env
2
.env
|
@ -1 +1 @@
|
||||||
DATABASE_URL="sqlite://emgauwa-dev.sqlite"
|
DATABASE_URL="sqlite://_local/emgauwa-dev.sqlite"
|
||||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -4,8 +4,10 @@
|
||||||
/tests/testing_bak/
|
/tests/testing_bak/
|
||||||
/tests/testing_latest/
|
/tests/testing_latest/
|
||||||
|
|
||||||
|
/_local/
|
||||||
emgauwa-*.sqlite
|
emgauwa-*.sqlite
|
||||||
emgauwa-*.sqlite-*
|
emgauwa-*.sqlite-*
|
||||||
|
emgauwa-*.json
|
||||||
|
|
||||||
|
|
||||||
# Added by cargo
|
# Added by cargo
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
23
Makefile
23
Makefile
|
@ -1,24 +1,15 @@
|
||||||
|
sqlx:
|
||||||
build:
|
cargo sqlx database drop
|
||||||
cargo build
|
|
||||||
|
|
||||||
sqlx-prepare:
|
|
||||||
rm -f ./emgauwa-dev.sqlite
|
|
||||||
cargo sqlx database create
|
cargo sqlx database create
|
||||||
cargo sqlx migrate run
|
cargo sqlx migrate run
|
||||||
|
|
||||||
sqlx: sqlx-prepare
|
|
||||||
cargo sqlx prepare --workspace
|
cargo sqlx prepare --workspace
|
||||||
|
|
||||||
build-rpi:
|
build-rpi:
|
||||||
cross build --target arm-unknown-linux-gnueabihf
|
cross build --target arm-unknown-linux-gnueabihf
|
||||||
|
|
||||||
clean-db:
|
_local/emgauwa-%.json: config/emgauwa-%.pkl config/lib/%.pkl config/lib/common.pkl
|
||||||
rm ./emgauwa-*.sqlite || true
|
pkl eval -f json -o $@ $<
|
||||||
$(MAKE) sqlx-prepare
|
|
||||||
|
|
||||||
format:
|
configs:
|
||||||
cargo +nightly fmt
|
$(MAKE) _local/emgauwa-core.json
|
||||||
|
$(MAKE) _local/emgauwa-controller.json
|
||||||
lint:
|
|
||||||
cargo clippy --all-targets --all-features -- -D warnings
|
|
||||||
|
|
71
config/emgauwa-controller.pkl
Normal file
71
config/emgauwa-controller.pkl
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
amends "./lib/controller.pkl"
|
||||||
|
|
||||||
|
server {
|
||||||
|
host = "127.0.0.1"
|
||||||
|
port = 4419
|
||||||
|
}
|
||||||
|
|
||||||
|
database = "sqlite://_local/emgauwa-controller.sqlite"
|
||||||
|
|
||||||
|
permissions {
|
||||||
|
user = "emgauwa"
|
||||||
|
group = "emgauwa"
|
||||||
|
}
|
||||||
|
|
||||||
|
logging {
|
||||||
|
level = "DEBUG"
|
||||||
|
file = "stdout"
|
||||||
|
}
|
||||||
|
|
||||||
|
relays {
|
||||||
|
new {
|
||||||
|
driver = "gpio"
|
||||||
|
pin = 5
|
||||||
|
inverted = true
|
||||||
|
}
|
||||||
|
new {
|
||||||
|
driver = "gpio"
|
||||||
|
pin = 4
|
||||||
|
inverted = true
|
||||||
|
}
|
||||||
|
new {
|
||||||
|
driver = "gpio"
|
||||||
|
pin = 3
|
||||||
|
inverted = true
|
||||||
|
}
|
||||||
|
new {
|
||||||
|
driver = "gpio"
|
||||||
|
pin = 2
|
||||||
|
inverted = true
|
||||||
|
}
|
||||||
|
new {
|
||||||
|
driver = "gpio"
|
||||||
|
pin = 1
|
||||||
|
inverted = true
|
||||||
|
}
|
||||||
|
new {
|
||||||
|
driver = "gpio"
|
||||||
|
pin = 0
|
||||||
|
inverted = true
|
||||||
|
}
|
||||||
|
new {
|
||||||
|
driver = "gpio"
|
||||||
|
pin = 16
|
||||||
|
inverted = true
|
||||||
|
}
|
||||||
|
new {
|
||||||
|
driver = "gpio"
|
||||||
|
pin = 15
|
||||||
|
inverted = true
|
||||||
|
}
|
||||||
|
new {
|
||||||
|
driver = "piface"
|
||||||
|
pin = 1
|
||||||
|
inverted = false
|
||||||
|
}
|
||||||
|
new {
|
||||||
|
driver = "piface"
|
||||||
|
pin = 0
|
||||||
|
inverted = false
|
||||||
|
}
|
||||||
|
}
|
18
config/emgauwa-core.pkl
Normal file
18
config/emgauwa-core.pkl
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
amends "./lib/core.pkl"
|
||||||
|
|
||||||
|
server {
|
||||||
|
host = "127.0.0.1"
|
||||||
|
port = 4419
|
||||||
|
}
|
||||||
|
|
||||||
|
database = "sqlite://_local/emgauwa-core.sqlite"
|
||||||
|
|
||||||
|
permissions {
|
||||||
|
user = "emgauwa"
|
||||||
|
group = "emgauwa"
|
||||||
|
}
|
||||||
|
|
||||||
|
logging {
|
||||||
|
level = "DEBUG"
|
||||||
|
file = "stdout"
|
||||||
|
}
|
15
config/lib/common.pkl
Normal file
15
config/lib/common.pkl
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
class ServerConfig {
|
||||||
|
host: String
|
||||||
|
port: UInt16
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set to a user and a group to drop privileges to after binding to the port
|
||||||
|
class PermissionsConfig {
|
||||||
|
user: String
|
||||||
|
group: String
|
||||||
|
}
|
||||||
|
|
||||||
|
class LoggingConfig {
|
||||||
|
level: String
|
||||||
|
file: String
|
||||||
|
}
|
16
config/lib/controller.pkl
Normal file
16
config/lib/controller.pkl
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import "./common.pkl"
|
||||||
|
|
||||||
|
server: common.ServerConfig
|
||||||
|
|
||||||
|
database: String
|
||||||
|
|
||||||
|
permissions: common.PermissionsConfig
|
||||||
|
|
||||||
|
logging: common.LoggingConfig
|
||||||
|
|
||||||
|
class RelayConfig {
|
||||||
|
driver: "gpio" | "piface"
|
||||||
|
pin: Number
|
||||||
|
inverted: Boolean
|
||||||
|
}
|
||||||
|
relays: Listing<RelayConfig>
|
12
config/lib/core.pkl
Normal file
12
config/lib/core.pkl
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import "./common.pkl"
|
||||||
|
|
||||||
|
server: common.ServerConfig
|
||||||
|
|
||||||
|
database: String
|
||||||
|
|
||||||
|
permissions: common.PermissionsConfig
|
||||||
|
|
||||||
|
logging: common.LoggingConfig
|
||||||
|
|
||||||
|
/// Leave empty to allow all origins (will always respond with Origin and not "*")
|
||||||
|
origins: Listing<String>
|
|
@ -1,60 +0,0 @@
|
||||||
database = "sqlite://emgauwa-controller.sqlite"
|
|
||||||
name = "Emgauwa Controller"
|
|
||||||
|
|
||||||
[core]
|
|
||||||
port = 4419
|
|
||||||
host = "127.0.0.1"
|
|
||||||
|
|
||||||
[logging]
|
|
||||||
level = "DEBUG"
|
|
||||||
file = "stdout"
|
|
||||||
|
|
||||||
[[relays]]
|
|
||||||
driver = "gpio"
|
|
||||||
pin = 5
|
|
||||||
inverted = 1
|
|
||||||
|
|
||||||
[[relays]]
|
|
||||||
driver = "gpio"
|
|
||||||
pin = 4
|
|
||||||
inverted = 1
|
|
||||||
|
|
||||||
[[relays]]
|
|
||||||
driver = "gpio"
|
|
||||||
pin = 3
|
|
||||||
inverted = 1
|
|
||||||
|
|
||||||
[[relays]]
|
|
||||||
driver = "gpio"
|
|
||||||
pin = 2
|
|
||||||
inverted = 1
|
|
||||||
|
|
||||||
[[relays]]
|
|
||||||
driver = "gpio"
|
|
||||||
pin = 1
|
|
||||||
inverted = 1
|
|
||||||
|
|
||||||
[[relays]]
|
|
||||||
driver = "gpio"
|
|
||||||
pin = 0
|
|
||||||
inverted = 1
|
|
||||||
|
|
||||||
[[relays]]
|
|
||||||
driver = "gpio"
|
|
||||||
pin = 16
|
|
||||||
inverted = 1
|
|
||||||
|
|
||||||
[[relays]]
|
|
||||||
driver = "gpio"
|
|
||||||
pin = 15
|
|
||||||
inverted = 1
|
|
||||||
|
|
||||||
[[relays]]
|
|
||||||
driver = "piface"
|
|
||||||
pin = 1
|
|
||||||
inverted = 0
|
|
||||||
|
|
||||||
[[relays]]
|
|
||||||
driver = "piface"
|
|
||||||
pin = 0
|
|
||||||
inverted = 0
|
|
|
@ -4,7 +4,7 @@ use emgauwa_lib::db::{DbController, DbJunctionRelaySchedule, DbRelay, DbSchedule
|
||||||
use emgauwa_lib::errors::EmgauwaError;
|
use emgauwa_lib::errors::EmgauwaError;
|
||||||
use emgauwa_lib::models::{Controller, FromDbModel};
|
use emgauwa_lib::models::{Controller, FromDbModel};
|
||||||
use emgauwa_lib::types::ControllerUid;
|
use emgauwa_lib::types::ControllerUid;
|
||||||
use emgauwa_lib::utils::init_logging;
|
use emgauwa_lib::utils::{drop_privileges, init_logging};
|
||||||
use sqlx::pool::PoolConnection;
|
use sqlx::pool::PoolConnection;
|
||||||
use sqlx::Sqlite;
|
use sqlx::Sqlite;
|
||||||
|
|
||||||
|
@ -59,6 +59,9 @@ async fn create_this_relay(
|
||||||
#[actix::main]
|
#[actix::main]
|
||||||
async fn main() -> Result<(), std::io::Error> {
|
async fn main() -> Result<(), std::io::Error> {
|
||||||
let settings = settings::init()?;
|
let settings = settings::init()?;
|
||||||
|
|
||||||
|
drop_privileges(&settings.permissions)?;
|
||||||
|
|
||||||
init_logging(&settings.logging.level)?;
|
init_logging(&settings.logging.level)?;
|
||||||
|
|
||||||
let pool = db::init(&settings.database)
|
let pool = db::init(&settings.database)
|
||||||
|
@ -105,7 +108,7 @@ async fn main() -> Result<(), std::io::Error> {
|
||||||
|
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"ws://{}:{}/api/v1/ws/controllers",
|
"ws://{}:{}/api/v1/ws/controllers",
|
||||||
settings.core.host, settings.core.port
|
settings.server.host, settings.server.port
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,25 +1,9 @@
|
||||||
use emgauwa_lib::errors::EmgauwaError;
|
use emgauwa_lib::errors::EmgauwaError;
|
||||||
use emgauwa_lib::{constants, utils};
|
use emgauwa_lib::settings;
|
||||||
use serde_derive::Deserialize;
|
use serde_derive::Deserialize;
|
||||||
|
|
||||||
use crate::driver::Driver;
|
use crate::driver::Driver;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
|
||||||
#[serde(default)]
|
|
||||||
#[allow(unused)]
|
|
||||||
pub struct Core {
|
|
||||||
pub host: String,
|
|
||||||
pub port: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
|
||||||
#[serde(default)]
|
|
||||||
#[allow(unused)]
|
|
||||||
pub struct Logging {
|
|
||||||
pub level: String,
|
|
||||||
pub file: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
|
@ -35,9 +19,11 @@ pub struct Relay {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub core: Core,
|
pub server: settings::Server,
|
||||||
pub database: String,
|
pub database: String,
|
||||||
pub logging: Logging,
|
pub permissions: settings::Permissions,
|
||||||
|
pub logging: settings::Logging,
|
||||||
|
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub relays: Vec<Relay>,
|
pub relays: Vec<Relay>,
|
||||||
}
|
}
|
||||||
|
@ -45,9 +31,11 @@ pub struct Settings {
|
||||||
impl Default for Settings {
|
impl Default for Settings {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Settings {
|
Settings {
|
||||||
core: Core::default(),
|
server: settings::Server::default(),
|
||||||
database: String::from("sqlite://emgauwa-controller.sqlite"),
|
database: String::from("sqlite://_local/emgauwa-controller.sqlite"),
|
||||||
logging: Logging::default(),
|
permissions: settings::Permissions::default(),
|
||||||
|
logging: settings::Logging::default(),
|
||||||
|
|
||||||
name: String::from("Emgauwa Controller"),
|
name: String::from("Emgauwa Controller"),
|
||||||
relays: Vec::new(),
|
relays: Vec::new(),
|
||||||
}
|
}
|
||||||
|
@ -66,26 +54,8 @@ impl Default for Relay {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Core {
|
|
||||||
fn default() -> Self {
|
|
||||||
Core {
|
|
||||||
host: String::from("127.0.0.1"),
|
|
||||||
port: constants::DEFAULT_PORT,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Logging {
|
|
||||||
fn default() -> Self {
|
|
||||||
Logging {
|
|
||||||
level: String::from("info"),
|
|
||||||
file: String::from("stdout"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init() -> Result<Settings, EmgauwaError> {
|
pub fn init() -> Result<Settings, EmgauwaError> {
|
||||||
let mut settings: Settings = utils::load_settings("controller", "CONTROLLER")?;
|
let mut settings: Settings = settings::load("controller", "CONTROLLER")?;
|
||||||
|
|
||||||
for (num, relay) in settings.relays.iter_mut().enumerate() {
|
for (num, relay) in settings.relays.iter_mut().enumerate() {
|
||||||
if relay.number.is_none() {
|
if relay.number.is_none() {
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
port = 4419
|
|
||||||
host = "127.0.0.1"
|
|
||||||
|
|
||||||
# Set to a user and a group to drop privileges to after binding to the port
|
|
||||||
#user = "emgauwa"
|
|
||||||
#group = "emgauwa"
|
|
||||||
|
|
||||||
# Leave empty to allow all origins (will always respond with Origin and not "*")
|
|
||||||
#origins = ["http://localhost", "https://emgauwa.app"]
|
|
||||||
|
|
||||||
database = "sqlite://emgauwa-core.sqlite"
|
|
||||||
|
|
||||||
[logging]
|
|
||||||
level = "DEBUG"
|
|
||||||
file = "stdout"
|
|
|
@ -24,4 +24,3 @@ serde_derive = "1.0"
|
||||||
sqlx = { version = "0.7", features = ["sqlite", "runtime-tokio", "macros", "chrono"] }
|
sqlx = { version = "0.7", features = ["sqlite", "runtime-tokio", "macros", "chrono"] }
|
||||||
|
|
||||||
futures = "0.3.29"
|
futures = "0.3.29"
|
||||||
libc = "0.2"
|
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("cargo:rustc-env=DATABASE_URL=sqlite://emgauwa-core.sqlite")
|
println!("cargo:rustc-env=DATABASE_URL=sqlite://_local/emgauwa-core.sqlite")
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,10 +6,9 @@ use actix_web::middleware::TrailingSlash;
|
||||||
use actix_web::{middleware, web, App, HttpServer};
|
use actix_web::{middleware, web, App, HttpServer};
|
||||||
use emgauwa_lib::db::DbController;
|
use emgauwa_lib::db::DbController;
|
||||||
use emgauwa_lib::errors::EmgauwaError;
|
use emgauwa_lib::errors::EmgauwaError;
|
||||||
use emgauwa_lib::utils::init_logging;
|
use emgauwa_lib::utils::{drop_privileges, init_logging};
|
||||||
|
|
||||||
use crate::app_state::AppState;
|
use crate::app_state::AppState;
|
||||||
use crate::utils::drop_privileges;
|
|
||||||
|
|
||||||
mod app_state;
|
mod app_state;
|
||||||
mod handlers;
|
mod handlers;
|
||||||
|
@ -19,12 +18,12 @@ mod utils;
|
||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
async fn main() -> Result<(), std::io::Error> {
|
async fn main() -> Result<(), std::io::Error> {
|
||||||
let settings = settings::init()?;
|
let settings = settings::init()?;
|
||||||
|
|
||||||
|
let listener = TcpListener::bind(format!("{}:{}", settings.server.host, settings.server.port))?;
|
||||||
|
drop_privileges(&settings.permissions)?;
|
||||||
|
|
||||||
init_logging(&settings.logging.level)?;
|
init_logging(&settings.logging.level)?;
|
||||||
|
|
||||||
let listener = TcpListener::bind(format!("{}:{}", settings.host, settings.port))?;
|
|
||||||
|
|
||||||
drop_privileges(&settings)?;
|
|
||||||
|
|
||||||
let pool = emgauwa_lib::db::init(&settings.database).await?;
|
let pool = emgauwa_lib::db::init(&settings.database).await?;
|
||||||
|
|
||||||
let mut conn = pool.acquire().await.map_err(EmgauwaError::from)?;
|
let mut conn = pool.acquire().await.map_err(EmgauwaError::from)?;
|
||||||
|
@ -35,7 +34,11 @@ async fn main() -> Result<(), std::io::Error> {
|
||||||
|
|
||||||
let app_state = AppState::new(pool.clone()).start();
|
let app_state = AppState::new(pool.clone()).start();
|
||||||
|
|
||||||
log::info!("Starting server on {}:{}", settings.host, settings.port);
|
log::info!(
|
||||||
|
"Starting server on {}:{}",
|
||||||
|
settings.server.host,
|
||||||
|
settings.server.port
|
||||||
|
);
|
||||||
|
|
||||||
HttpServer::new(move || {
|
HttpServer::new(move || {
|
||||||
let cors = Cors::default().allow_any_method().allow_any_header();
|
let cors = Cors::default().allow_any_method().allow_any_header();
|
||||||
|
|
|
@ -1,57 +1,32 @@
|
||||||
use emgauwa_lib::errors::EmgauwaError;
|
use emgauwa_lib::errors::EmgauwaError;
|
||||||
use emgauwa_lib::{constants, utils};
|
use emgauwa_lib::settings;
|
||||||
use serde_derive::Deserialize;
|
use serde_derive::Deserialize;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
|
||||||
#[serde(default)]
|
|
||||||
#[allow(unused)]
|
|
||||||
pub struct Logging {
|
|
||||||
pub level: String,
|
|
||||||
pub file: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
|
pub server: settings::Server,
|
||||||
pub database: String,
|
pub database: String,
|
||||||
|
pub permissions: settings::Permissions,
|
||||||
|
pub logging: settings::Logging,
|
||||||
|
|
||||||
pub host: String,
|
|
||||||
pub port: u16,
|
|
||||||
pub origins: Vec<String>,
|
pub origins: Vec<String>,
|
||||||
|
|
||||||
pub user: String,
|
|
||||||
pub group: String,
|
|
||||||
|
|
||||||
pub logging: Logging,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Settings {
|
impl Default for Settings {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Settings {
|
Settings {
|
||||||
database: String::from("sqlite://emgauwa-core.sqlite"),
|
server: settings::Server::default(),
|
||||||
|
database: String::from("sqlite://_local/emgauwa-core.sqlite"),
|
||||||
|
permissions: settings::Permissions::default(),
|
||||||
|
logging: settings::Logging::default(),
|
||||||
|
|
||||||
host: String::from("127.0.0.1"),
|
|
||||||
port: constants::DEFAULT_PORT,
|
|
||||||
origins: Vec::new(),
|
origins: Vec::new(),
|
||||||
|
|
||||||
user: String::from(""),
|
|
||||||
group: String::from(""),
|
|
||||||
|
|
||||||
logging: Logging::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Logging {
|
|
||||||
fn default() -> Self {
|
|
||||||
Logging {
|
|
||||||
level: String::from("info"),
|
|
||||||
file: String::from("stdout"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init() -> Result<Settings, EmgauwaError> {
|
pub fn init() -> Result<Settings, EmgauwaError> {
|
||||||
utils::load_settings("core", "CORE")
|
settings::load("core", "CORE")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,3 @@
|
||||||
use std::ffi::CString;
|
|
||||||
use std::io::{Error, ErrorKind};
|
|
||||||
|
|
||||||
use crate::settings::Settings;
|
|
||||||
|
|
||||||
pub fn flatten_result<T, E>(res: Result<Result<T, E>, E>) -> Result<T, E> {
|
pub fn flatten_result<T, E>(res: Result<Result<T, E>, E>) -> Result<T, E> {
|
||||||
match res {
|
match res {
|
||||||
Ok(Ok(t)) => Ok(t),
|
Ok(Ok(t)) => Ok(t),
|
||||||
|
@ -10,64 +5,3 @@ pub fn flatten_result<T, E>(res: Result<Result<T, E>, E>) -> Result<T, E> {
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://blog.lxsang.me/post/id/28.0
|
|
||||||
pub fn drop_privileges(settings: &Settings) -> Result<(), Error> {
|
|
||||||
log::info!(
|
|
||||||
"Dropping privileges to {}:{}",
|
|
||||||
settings.user,
|
|
||||||
settings.group
|
|
||||||
);
|
|
||||||
|
|
||||||
// the group id need to be set first, because, when the user privileges drop,
|
|
||||||
// we are unable to drop the group privileges
|
|
||||||
if !settings.group.is_empty() {
|
|
||||||
drop_privileges_group(&settings.group)?;
|
|
||||||
}
|
|
||||||
if !settings.user.is_empty() {
|
|
||||||
drop_privileges_user(&settings.user)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn drop_privileges_group(group: &str) -> Result<(), Error> {
|
|
||||||
// get the gid from username
|
|
||||||
if let Ok(cstr) = CString::new(group.as_bytes()) {
|
|
||||||
let p = unsafe { libc::getgrnam(cstr.as_ptr()) };
|
|
||||||
if p.is_null() {
|
|
||||||
log::error!("Unable to getgrnam of group: {}", group);
|
|
||||||
return Err(Error::last_os_error());
|
|
||||||
}
|
|
||||||
if unsafe { libc::setgid((*p).gr_gid) } != 0 {
|
|
||||||
log::error!("Unable to setgid of group: {}", group);
|
|
||||||
return Err(Error::last_os_error());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Err(Error::new(
|
|
||||||
ErrorKind::Other,
|
|
||||||
"Cannot create CString from String (group)!",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn drop_privileges_user(user: &str) -> Result<(), Error> {
|
|
||||||
// get the uid from username
|
|
||||||
if let Ok(cstr) = CString::new(user.as_bytes()) {
|
|
||||||
let p = unsafe { libc::getpwnam(cstr.as_ptr()) };
|
|
||||||
if p.is_null() {
|
|
||||||
log::error!("Unable to getpwnam of user: {}", user);
|
|
||||||
return Err(Error::last_os_error());
|
|
||||||
}
|
|
||||||
if unsafe { libc::setuid((*p).pw_uid) } != 0 {
|
|
||||||
log::error!("Unable to setuid of user: {}", user);
|
|
||||||
return Err(Error::last_os_error());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Err(Error::new(
|
|
||||||
ErrorKind::Other,
|
|
||||||
"Cannot create CString from String (user)!",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
|
@ -25,3 +25,4 @@ sqlx = { version = "0.7", features = ["sqlite", "runtime-tokio", "macros", "chro
|
||||||
libsqlite3-sys = { version = "*", features = ["bundled"] }
|
libsqlite3-sys = { version = "*", features = ["bundled"] }
|
||||||
uuid = "1.6"
|
uuid = "1.6"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
|
libc = "0.2"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("cargo:rerun-if-changed=migrations");
|
println!("cargo:rerun-if-changed=migrations");
|
||||||
println!("cargo:rustc-env=DATABASE_URL=sqlite://emgauwa-dev.sqlite");
|
println!("cargo:rustc-env=DATABASE_URL=sqlite://_local/emgauwa-dev.sqlite");
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,5 +2,6 @@ pub mod constants;
|
||||||
pub mod db;
|
pub mod db;
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
pub mod models;
|
pub mod models;
|
||||||
|
pub mod settings;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
77
emgauwa-lib/src/settings.rs
Normal file
77
emgauwa-lib/src/settings.rs
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
use serde_derive::Deserialize;
|
||||||
|
|
||||||
|
use crate::constants;
|
||||||
|
use crate::errors::EmgauwaError;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
#[serde(default)]
|
||||||
|
#[allow(unused)]
|
||||||
|
pub struct Server {
|
||||||
|
pub host: String,
|
||||||
|
pub port: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
#[serde(default)]
|
||||||
|
#[allow(unused)]
|
||||||
|
pub struct Logging {
|
||||||
|
pub level: String,
|
||||||
|
pub file: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
#[serde(default)]
|
||||||
|
#[allow(unused)]
|
||||||
|
pub struct Permissions {
|
||||||
|
pub user: String,
|
||||||
|
pub group: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Server {
|
||||||
|
fn default() -> Self {
|
||||||
|
Server {
|
||||||
|
host: String::from("127.0.0.1"),
|
||||||
|
port: constants::DEFAULT_PORT,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Logging {
|
||||||
|
fn default() -> Self {
|
||||||
|
Logging {
|
||||||
|
level: String::from("info"),
|
||||||
|
file: String::from("stdout"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Permissions {
|
||||||
|
fn default() -> Self {
|
||||||
|
Permissions {
|
||||||
|
user: String::from(""),
|
||||||
|
group: String::from(""),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load<T>(config_name: &str, env_prefix: &str) -> Result<T, EmgauwaError>
|
||||||
|
where
|
||||||
|
for<'de> T: serde::Deserialize<'de>,
|
||||||
|
{
|
||||||
|
// TODO: Add switch to only include local config if in development mode
|
||||||
|
let dev_file =
|
||||||
|
config::File::with_name(&format!("./_local/emgauwa-{}", config_name)).required(false);
|
||||||
|
let local_file = config::File::with_name(&format!("./emgauwa-{}", config_name)).required(false);
|
||||||
|
|
||||||
|
config::Config::builder()
|
||||||
|
.add_source(dev_file)
|
||||||
|
.add_source(local_file)
|
||||||
|
.add_source(
|
||||||
|
config::Environment::with_prefix(&format!("EMGAUWA_{}", env_prefix))
|
||||||
|
.prefix_separator("__")
|
||||||
|
.separator("__"),
|
||||||
|
)
|
||||||
|
.build()?
|
||||||
|
.try_deserialize::<T>()
|
||||||
|
.map_err(EmgauwaError::from)
|
||||||
|
}
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::ffi::CString;
|
||||||
|
use std::io::{Error, ErrorKind};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use chrono::Datelike;
|
use chrono::Datelike;
|
||||||
|
@ -5,25 +7,9 @@ use log::LevelFilter;
|
||||||
use simple_logger::SimpleLogger;
|
use simple_logger::SimpleLogger;
|
||||||
|
|
||||||
use crate::errors::EmgauwaError;
|
use crate::errors::EmgauwaError;
|
||||||
|
use crate::settings::Permissions;
|
||||||
use crate::types::Weekday;
|
use crate::types::Weekday;
|
||||||
|
|
||||||
pub fn load_settings<T>(config_name: &str, env_prefix: &str) -> Result<T, EmgauwaError>
|
|
||||||
where
|
|
||||||
for<'de> T: serde::Deserialize<'de>,
|
|
||||||
{
|
|
||||||
let default_file = config::File::with_name(&format!("emgauwa-{}", config_name)).required(false);
|
|
||||||
|
|
||||||
config::Config::builder()
|
|
||||||
.add_source(default_file)
|
|
||||||
.add_source(
|
|
||||||
config::Environment::with_prefix(&format!("EMGAUWA_{}", env_prefix))
|
|
||||||
.prefix_separator("__")
|
|
||||||
.separator("__"),
|
|
||||||
)
|
|
||||||
.build()?
|
|
||||||
.try_deserialize::<T>()
|
|
||||||
.map_err(EmgauwaError::from)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init_logging(level: &str) -> Result<(), EmgauwaError> {
|
pub fn init_logging(level: &str) -> Result<(), EmgauwaError> {
|
||||||
let log_level: LevelFilter = LevelFilter::from_str(level)
|
let log_level: LevelFilter = LevelFilter::from_str(level)
|
||||||
|
@ -38,6 +24,67 @@ pub fn init_logging(level: &str) -> Result<(), EmgauwaError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://blog.lxsang.me/post/id/28.0
|
||||||
|
pub fn drop_privileges(permissions: &Permissions) -> Result<(), Error> {
|
||||||
|
log::info!(
|
||||||
|
"Dropping privileges to {}:{}",
|
||||||
|
permissions.user,
|
||||||
|
permissions.group
|
||||||
|
);
|
||||||
|
|
||||||
|
// the group id need to be set first, because, when the user privileges drop,
|
||||||
|
// we are unable to drop the group privileges
|
||||||
|
if !permissions.group.is_empty() {
|
||||||
|
drop_privileges_group(&permissions.group)?;
|
||||||
|
}
|
||||||
|
if !permissions.user.is_empty() {
|
||||||
|
drop_privileges_user(&permissions.user)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drop_privileges_group(group: &str) -> Result<(), Error> {
|
||||||
|
// get the gid from username
|
||||||
|
if let Ok(cstr) = CString::new(group.as_bytes()) {
|
||||||
|
let p = unsafe { libc::getgrnam(cstr.as_ptr()) };
|
||||||
|
if p.is_null() {
|
||||||
|
log::error!("Unable to getgrnam of group: {}", group);
|
||||||
|
return Err(Error::last_os_error());
|
||||||
|
}
|
||||||
|
if unsafe { libc::setgid((*p).gr_gid) } != 0 {
|
||||||
|
log::error!("Unable to setgid of group: {}", group);
|
||||||
|
return Err(Error::last_os_error());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(Error::new(
|
||||||
|
ErrorKind::Other,
|
||||||
|
"Cannot create CString from String (group)!",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drop_privileges_user(user: &str) -> Result<(), Error> {
|
||||||
|
// get the uid from username
|
||||||
|
if let Ok(cstr) = CString::new(user.as_bytes()) {
|
||||||
|
let p = unsafe { libc::getpwnam(cstr.as_ptr()) };
|
||||||
|
if p.is_null() {
|
||||||
|
log::error!("Unable to getpwnam of user: {}", user);
|
||||||
|
return Err(Error::last_os_error());
|
||||||
|
}
|
||||||
|
if unsafe { libc::setuid((*p).pw_uid) } != 0 {
|
||||||
|
log::error!("Unable to setuid of user: {}", user);
|
||||||
|
return Err(Error::last_os_error());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(Error::new(
|
||||||
|
ErrorKind::Other,
|
||||||
|
"Cannot create CString from String (user)!",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_weekday() -> Weekday {
|
pub fn get_weekday() -> Weekday {
|
||||||
(chrono::offset::Local::now()
|
(chrono::offset::Local::now()
|
||||||
.date_naive()
|
.date_naive()
|
||||||
|
|
Loading…
Reference in a new issue