Añade configuración y conexión a la base de datos

This commit is contained in:
Manuel Cillero 2022-03-10 00:10:48 +01:00
parent b6dd473578
commit 76785af4dc
13 changed files with 210 additions and 83 deletions

View file

@ -29,6 +29,7 @@ once_cell = "1.9.0"
figlet-rs = "0.1.3"
config_rs = { package = "config", version = "0.11.0", features = ["toml"] }
url = "2.2.2"
tracing = "0.1"
tracing-appender = "0.2"
@ -47,9 +48,20 @@ downcast-rs = "1.2.0"
serde = { version = "1.0", features = ["derive"] }
[dependencies.sea-orm]
version = "0.6"
features = ["macros", "debug-print", "runtime-async-std-native-tls"]
default-features = false
[build-dependencies]
actix-web-static-files = "3.0.5"
[features]
default = ["sea-orm/sqlx-mysql"]
mysql = ["sea-orm/sqlx-mysql"]
postgres = ["sea-orm/sqlx-postgres"]
sqlite = ["sea-orm/sqlx-sqlite"]
[lib]
name = "pagetop"

View file

@ -23,6 +23,20 @@ prefix = "tracing.log"
# Presentación de las trazas: "Json", "Full", "Compact" o "Pretty".
format = "Json"
[database]
# Ajustes para conectar con la base de datos.
# El tipo de base de datos es una característica de compilación.
# Nombre (mysql/postgres) o referencia (sqlite) de la base de datos.
db_name = "dbname"
# Usuario y contraseña (mysql/postgres).
db_user = "dbuser"
db_pass = "dbpass"
# Servidor (mysql/postgres) y puerto predeterminado (0 para 3306 ó 5432).
db_host = "localhost"
db_port = 0
# Número máximo de conexiones habilitadas.
max_pool_size = 5
[webserver]
# Configuración del servidor web.
bind_address = "localhost"

View file

@ -1,8 +1,9 @@
use crate::prelude::*;
use super::l;
pub async fn summary() -> server::Result<Markup> {
let top_menu = Menu::prepare()
.add(MenuItem::label("Opción 1"))
.add(MenuItem::label(l("module_fullname").as_str()))
.add(MenuItem::link("Opción 2", "https://www.google.es"))
.add(MenuItem::link_blank("Opción 3", "https://www.google.es"))
.add(MenuItem::submenu("Submenú 1", Menu::prepare()

View file

@ -87,6 +87,16 @@ pub struct Log {
pub format : String,
}
#[derive(Debug, Deserialize)]
pub struct Database {
pub db_name : String,
pub db_user : String,
pub db_pass : String,
pub db_host : String,
pub db_port : u16,
pub max_pool_size : u32,
}
#[derive(Debug, Deserialize)]
pub struct Webserver {
pub bind_address : String,
@ -97,6 +107,7 @@ pub struct Webserver {
pub struct Settings {
pub app : App,
pub log : Log,
pub database : Database,
pub webserver : Webserver,
}
@ -121,6 +132,14 @@ Ajustes globales y valores predeterminados para las secciones *\[app\]*,
"log.prefix" => "tracing.log",
"log.format" => "json",
// [database]
"database.db_name" => "dbname",
"database.db_user" => "dbuser",
"database.db_pass" => "dbpass",
"database.db_host" => "localhost",
"database.db_port" => 0,
"database.max_pool_size" => 5,
// [webserver]
"webserver.bind_address" => "localhost",
"webserver.bind_port" => 8088

138
src/core/server/app.rs Normal file
View file

@ -0,0 +1,138 @@
use crate::{Lazy, base, locale, trace};
use crate::config::SETTINGS;
use crate::core::{Server, global, server};
use crate::core::theme::register_theme;
use crate::core::module::register_module;
use std::io::Error;
use actix_web::middleware::normalize::{NormalizePath, TrailingSlash};
pub struct Application {
server: Server,
}
impl Application {
pub async fn build(bootstrap: Option<fn()>) -> Result<Self, Error> {
// Imprime rótulo (opcional) de bienvenida.
if SETTINGS.app.startup_banner.to_lowercase() != "off" {
let figfont = figlet_rs::FIGfont::from_content(
match SETTINGS.app.startup_banner.to_lowercase().as_str() {
"slant" => include_str!("figfonts/slant.flf"),
"small" => include_str!("figfonts/small.flf"),
"speed" => include_str!("figfonts/speed.flf"),
"starwars" => include_str!("figfonts/starwars.flf"),
_ => {
println!(
"FIGfont \"{}\" not found for banner. {}. {}.",
SETTINGS.app.startup_banner,
"Using \"Small\"",
"Check the settings file",
);
include_str!("figfonts/small.flf")
}
}
).unwrap();
println!("\n{} {}\n\n Powered by PageTop {}\n",
figfont.convert(&SETTINGS.app.name).unwrap(),
&SETTINGS.app.description,
env!("CARGO_PKG_VERSION")
);
}
// Inicia registro de trazas y eventos.
Lazy::force(&server::tracing::TRACING);
// Valida el identificador de idioma.
Lazy::force(&locale::LANGID);
// Inicializa la conexión con la base de datos.
trace::info!(
"Connecting to database \"{}\" with a pool of {} connections.",
&SETTINGS.database.db_name,
&SETTINGS.database.max_pool_size
);
#[cfg(any(feature = "default", feature = "mysql"))]
let db_uri = format!(
"mysql://{}/{}",
&SETTINGS.database.db_host,
&SETTINGS.database.db_name
);
#[cfg(feature = "postgres")]
let db_uri = format!(
"postgres://{}/{}",
&SETTINGS.database.db_host,
&SETTINGS.database.db_name
);
#[cfg(feature = "sqlite")]
let db_uri = format!("sqlite://{}", &SETTINGS.database.db_name);
let mut uri = url::Url::parse(&db_uri).unwrap();
// https://github.com/launchbadge/sqlx/issues/1624
#[cfg(not(feature = "sqlite"))]
uri.set_username(&SETTINGS.database.db_user.as_str()).unwrap();
#[cfg(not(feature = "sqlite"))]
uri.set_password(Some(&SETTINGS.database.db_pass.as_str())).unwrap();
#[cfg(not(feature = "sqlite"))]
if SETTINGS.database.db_port != 0 {
uri.set_port(Some(SETTINGS.database.db_port)).unwrap();
}
let mut db_options = sea_orm::ConnectOptions::new(uri.to_string());
db_options.max_connections(SETTINGS.database.max_pool_size);
let mut db_conn = server::dbconn::DBCONN.write().unwrap();
*db_conn = Some(
sea_orm::Database::connect::<sea_orm::ConnectOptions>(
db_options.into()
)
.await
.expect("Failed to connect to database")
);
// Registra los temas predefinidos.
register_theme(&base::theme::aliner::AlinerTheme);
register_theme(&base::theme::minimal::MinimalTheme);
register_theme(&base::theme::bootsier::BootsierTheme);
// Registra los módulos predeterminados.
register_module(&base::module::admin::AdminModule);
register_module(&base::module::user::UserModule);
// Ejecuta la función de inicio de la aplicación.
if bootstrap != None {
trace::debug!("Calling application bootstrap");
let _ = &(bootstrap.unwrap())();
}
// Registra el módulo para la página de inicio de PageTop.
// Al ser el último, puede sobrecargarse con la función de inicio.
register_module(&base::module::homepage::HomepageModule);
// Prepara el servidor web.
let server = server::HttpServer::new(|| {
server::App::new()
.wrap(tracing_actix_web::TracingLogger)
.wrap(NormalizePath::new(TrailingSlash::Trim))
.configure(&global::themes)
.configure(&global::modules)
})
.bind(format!("{}:{}",
&SETTINGS.webserver.bind_address,
&SETTINGS.webserver.bind_port
))?
.run();
Ok(Self { server })
}
pub fn run(self) -> Result<Server, Error> {
Ok(self.server)
}
}

View file

@ -0,0 +1,8 @@
use crate::Lazy;
use crate::database::DatabaseConnection;
use std::sync::RwLock;
pub static DBCONN: Lazy<RwLock<Option<DatabaseConnection>>> = Lazy::new(|| {
RwLock::new(None)
});

View file

@ -1,76 +0,0 @@
use crate::{Lazy, base, locale, trace};
use crate::config::SETTINGS;
use crate::core::{Server, global, server};
use crate::core::theme::register_theme;
use crate::core::module::register_module;
use actix_web::middleware::normalize;
pub fn run(bootstrap: Option<fn()>) -> Result<Server, std::io::Error> {
// Imprime el rótulo (opcional) de bienvenida.
if SETTINGS.app.startup_banner.to_lowercase() != "off" {
let figfont = figlet_rs::FIGfont::from_content(
match SETTINGS.app.startup_banner.to_lowercase().as_str() {
"slant" => include_str!("figfonts/slant.flf"),
"small" => include_str!("figfonts/small.flf"),
"speed" => include_str!("figfonts/speed.flf"),
"starwars" => include_str!("figfonts/starwars.flf"),
_ => {
println!(
"FIGfont \"{}\" not found for banner. {}. {}.",
SETTINGS.app.startup_banner,
"Using \"Small\"",
"Check the settings file",
);
include_str!("figfonts/small.flf")
}
}
).unwrap();
println!("\n{} {}\n\n Powered by PageTop {}\n",
figfont.convert(&SETTINGS.app.name).unwrap(),
&SETTINGS.app.description,
env!("CARGO_PKG_VERSION")
);
}
// Inicia registro de trazas y eventos.
Lazy::force(&server::tracing::TRACING);
// Asigna identificador de idioma.
Lazy::force(&locale::LANGID);
// Registra los temas predefinidos.
register_theme(&base::theme::aliner::AlinerTheme);
register_theme(&base::theme::minimal::MinimalTheme);
register_theme(&base::theme::bootsier::BootsierTheme);
// Registra los módulos predeterminados.
register_module(&base::module::admin::AdminModule);
register_module(&base::module::user::UserModule);
// Ejecuta la función de inicio de la aplicación.
if bootstrap != None {
trace::debug!("Calling application bootstrap");
let _ = &(bootstrap.unwrap())();
}
// Registra el módulo para la página de inicio de PageTop.
// Al ser el último, puede sobrecargarse en la función de arranque.
register_module(&base::module::homepage::HomepageModule);
// Inicializa el servidor web.
let server = server::HttpServer::new(|| {
server::App::new()
.wrap(tracing_actix_web::TracingLogger)
.wrap(normalize::NormalizePath::new(normalize::TrailingSlash::Trim))
.configure(&global::themes)
.configure(&global::modules)
})
.bind(format!("{}:{}",
&SETTINGS.webserver.bind_address,
&SETTINGS.webserver.bind_port
))?
.run();
Ok(server)
}

View file

@ -4,5 +4,7 @@ pub use actix_web::{
mod tracing;
mod main;
pub use main::run;
mod dbconn;
mod app;
pub use app::Application;

1
src/database.rs Normal file
View file

@ -0,0 +1 @@
pub use sea_orm::DatabaseConnection;

View file

@ -10,8 +10,11 @@ pub use once_cell::sync::Lazy;
pub mod config; // Gestión de la configuración.
pub mod trace; // Registro de trazas y eventos de la aplicación.
pub mod locale; // Localización.
pub mod database; // Acceso a la base de datos.
pub mod core; // Servidor web y sistemas para Temas, Módulos y Respuestas.
pub mod base; // Temas, Módulos y Componentes base.
pub mod util; // Macros y funciones útiles.
pub mod prelude; // Re-exporta recursos comunes.
pub use crate::core::server::Application;

View file

@ -44,12 +44,12 @@ macro_rules! localize {
}
#[allow(dead_code)]
pub fn l(key: &str) -> String {
fn l(key: &str) -> String {
LOCALES.lookup(&LANGID, key)
}
#[allow(dead_code)]
pub fn t(
fn t(
key: &str,
args: &std::collections::HashMap<String, FluentValue>
) -> String {
@ -57,7 +57,7 @@ macro_rules! localize {
}
#[allow(dead_code)]
pub fn e(
fn e(
key: &str,
args: &std::collections::HashMap<String, FluentValue>
) -> crate::core::theme::PreEscaped<String> {

View file

@ -1,4 +1,8 @@
#[actix_web::main]
async fn main() -> std::io::Result<()> {
pagetop::core::server::run(None)?.await
// ```
// let app = pagetop::Application::build(None).await?;
// app.run()?.await
// ```
pagetop::Application::build(None).await?.run()?.await
}

View file

@ -6,6 +6,7 @@ pub use crate::util;
pub use crate::config::SETTINGS;
pub use crate::trace;
pub use crate::localize;
pub use crate::database;
pub use crate::core::theme::*;
pub use crate::core::module::*;