Modifica la definición de apps como un módulo más
This commit is contained in:
parent
61813a44e5
commit
5c65642ec0
13 changed files with 384 additions and 137 deletions
|
|
@ -1,24 +1,31 @@
|
||||||
use pagetop::prelude::*;
|
use pagetop::prelude::*;
|
||||||
|
|
||||||
|
pub_const_handler!(APP_DRUST);
|
||||||
|
|
||||||
struct Drust;
|
struct Drust;
|
||||||
|
|
||||||
impl AppTrait for Drust {
|
impl ModuleTrait for Drust {
|
||||||
fn enable_modules(&self) -> Vec<ModuleStaticRef> {
|
fn handler(&self) -> Handler {
|
||||||
|
APP_DRUST
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dependencies(&self) -> Vec<ModuleStaticRef> {
|
||||||
vec![
|
vec![
|
||||||
&pagetop_admin::Admin,
|
&pagetop_admin::Admin,
|
||||||
&pagetop_user::User,
|
&pagetop_user::User,
|
||||||
&pagetop_node::Node,
|
&pagetop_node::Node,
|
||||||
|
&pagetop::base::module::homepage::DefaultHomePage,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn disable_modules(&self) -> Vec<ModuleStaticRef> {
|
fn uninstall_modules(&self) -> Vec<ModuleStaticRef> {
|
||||||
vec![
|
vec![
|
||||||
// &pagetop_node::Node,
|
// &pagetop_node::Node
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
async fn main() -> std::io::Result<()> {
|
async fn main() -> std::io::Result<()> {
|
||||||
Application::prepare(Drust).await?.run()?.await
|
Application::prepare(&Drust).await?.run()?.await
|
||||||
}
|
}
|
||||||
|
|
|
||||||
19
pagetop-mdbook/Cargo.toml
Normal file
19
pagetop-mdbook/Cargo.toml
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
[package]
|
||||||
|
name = "pagetop-mdbook"
|
||||||
|
version = "0.0.1"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
authors = [
|
||||||
|
"Manuel Cillero <manuel@cillero.es>"
|
||||||
|
]
|
||||||
|
description = """\
|
||||||
|
...\
|
||||||
|
"""
|
||||||
|
homepage = "https://suitepro.cillero.es/projects/pagetop"
|
||||||
|
repository = "https://gitlab.com/manuelcillero/pagetop"
|
||||||
|
license = "Apache-2.0 or MIT"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
pagetop = { path = "../pagetop" }
|
||||||
|
static-files = "0.2.3"
|
||||||
|
maud = { git = "https://github.com/lambda-fairy/maud", rev = "e6787cd6" }
|
||||||
191
pagetop-mdbook/src/lib.rs
Normal file
191
pagetop-mdbook/src/lib.rs
Normal file
|
|
@ -0,0 +1,191 @@
|
||||||
|
use pagetop::prelude::*;
|
||||||
|
|
||||||
|
pub type BookMapResources = std::collections::HashMap<&'static str, static_files::Resource>;
|
||||||
|
|
||||||
|
pub_const_handler!(MODULE_MDBOOK);
|
||||||
|
|
||||||
|
pub struct MdBook;
|
||||||
|
|
||||||
|
impl ModuleTrait for MdBook {
|
||||||
|
fn handler(&self) -> Handler {
|
||||||
|
MODULE_MDBOOK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MdBook {
|
||||||
|
pub fn configure_service_mdbook(
|
||||||
|
cfg: &mut app::web::ServiceConfig,
|
||||||
|
mdbook_path: &'static str,
|
||||||
|
mdbook_map: &'static BookMapResources,
|
||||||
|
) {
|
||||||
|
let path = mdbook_path.trim_end_matches('/');
|
||||||
|
cfg.service(
|
||||||
|
app::web::scope(path)
|
||||||
|
.route(
|
||||||
|
"{tail:.*html$}",
|
||||||
|
app::web::get().to(move |request: app::HttpRequest| {
|
||||||
|
mdbook_page(request, path, mdbook_map)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"{tail:.*$}",
|
||||||
|
app::web::get().to(move |request: app::HttpRequest| {
|
||||||
|
mdbook_resource(request, path, mdbook_map)
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn mdbook_page(
|
||||||
|
request: app::HttpRequest,
|
||||||
|
mdbook_path: &'static str,
|
||||||
|
mdbook_map: &'static BookMapResources,
|
||||||
|
) -> ResultPage<Markup, FatalError> {
|
||||||
|
let path_len = mdbook_path.len() + 1;
|
||||||
|
if let Some(content) = mdbook_map.get(&request.path()[path_len..]) {
|
||||||
|
if let Ok(html) = std::str::from_utf8(content.data) {
|
||||||
|
let _lang = extract("Lang", html);
|
||||||
|
let title = match extract("Title", html) {
|
||||||
|
Some(title) => title,
|
||||||
|
_ => "Documentación",
|
||||||
|
};
|
||||||
|
let _print = matches!(extract("Print", html), Some("enabled"));
|
||||||
|
let _mathjax = matches!(extract("MathJax", html), Some("supported"));
|
||||||
|
let beginning = {
|
||||||
|
let separator = "<!-- mdBook -->";
|
||||||
|
match html.find(separator) {
|
||||||
|
Some(pos) => pos + separator.len(),
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Page::new()
|
||||||
|
.with_title(title)
|
||||||
|
.with_context(PageOp::AddMetadata("theme-color", "#ffffff"))
|
||||||
|
.with_context(PageOp::AddStyleSheet(StyleSheet::located(
|
||||||
|
"/doc/css/variables.css",
|
||||||
|
)))
|
||||||
|
.with_context(PageOp::AddStyleSheet(StyleSheet::located(
|
||||||
|
"/doc/css/general.css",
|
||||||
|
)))
|
||||||
|
.with_context(PageOp::AddStyleSheet(StyleSheet::located(
|
||||||
|
"/doc/css/chrome.css",
|
||||||
|
)))
|
||||||
|
.with_context(PageOp::AddStyleSheet(
|
||||||
|
StyleSheet::located("/doc/css/print.css").for_media(TargetMedia::Print),
|
||||||
|
))
|
||||||
|
.with_context(PageOp::AddStyleSheet(StyleSheet::located(
|
||||||
|
"/doc/FontAwesome/css/font-awesome.css",
|
||||||
|
)))
|
||||||
|
.with_context(PageOp::AddStyleSheet(StyleSheet::located(
|
||||||
|
"/doc/fonts/fonts.css",
|
||||||
|
)))
|
||||||
|
.with_context(PageOp::AddStyleSheet(StyleSheet::located(
|
||||||
|
"/doc/highlight.css",
|
||||||
|
)))
|
||||||
|
.with_context(PageOp::AddStyleSheet(StyleSheet::located(
|
||||||
|
"/doc/tomorrow-night.css",
|
||||||
|
)))
|
||||||
|
.with_context(PageOp::AddStyleSheet(StyleSheet::located(
|
||||||
|
"/doc/ayu-highlight.css",
|
||||||
|
)))
|
||||||
|
.add_to(
|
||||||
|
"region-content",
|
||||||
|
Container::new()
|
||||||
|
.with_id("mdbook")
|
||||||
|
.with_component(Html::with(html! { (PreEscaped(&html[beginning..])) })),
|
||||||
|
)
|
||||||
|
.render()
|
||||||
|
} else {
|
||||||
|
Err(FatalError::NotFound)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(FatalError::NotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn mdbook_resource(
|
||||||
|
request: app::HttpRequest,
|
||||||
|
mdbook_path: &'static str,
|
||||||
|
mdbook_map: &'static BookMapResources,
|
||||||
|
) -> app::HttpResponse {
|
||||||
|
let path_len = mdbook_path.len() + 1;
|
||||||
|
// From https://github.com/kilork/actix-web-static-files/blob/master/src/resource_files.rs, see
|
||||||
|
// functions respond_to(), any_match() and none_match().
|
||||||
|
if let Some(file) = &mdbook_map.get(&request.path()[path_len..]) {
|
||||||
|
let etag = Some(app::http::header::EntityTag::new_strong(format!(
|
||||||
|
"{:x}:{:x}",
|
||||||
|
file.data.len(),
|
||||||
|
file.modified
|
||||||
|
)));
|
||||||
|
|
||||||
|
let precondition_failed = !any_match(etag.as_ref(), &request);
|
||||||
|
|
||||||
|
let not_modified = !none_match(etag.as_ref(), &request);
|
||||||
|
|
||||||
|
let mut resp = app::HttpResponse::build(app::http::StatusCode::OK);
|
||||||
|
resp.insert_header((app::http::header::CONTENT_TYPE, file.mime_type));
|
||||||
|
|
||||||
|
if let Some(etag) = etag {
|
||||||
|
resp.insert_header(app::http::header::ETag(etag));
|
||||||
|
}
|
||||||
|
|
||||||
|
if precondition_failed {
|
||||||
|
return FatalError::PreconditionFailed.error_response();
|
||||||
|
} else if not_modified {
|
||||||
|
return FatalError::NotModified.error_response();
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.body(file.data)
|
||||||
|
} else {
|
||||||
|
FatalError::NotFound.error_response()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if `request` has no `If-Match` header or one which matches `etag`.
|
||||||
|
fn any_match(etag: Option<&app::http::header::EntityTag>, request: &app::HttpRequest) -> bool {
|
||||||
|
match request.get_header::<app::http::header::IfMatch>() {
|
||||||
|
None | Some(app::http::header::IfMatch::Any) => true,
|
||||||
|
Some(app::http::header::IfMatch::Items(ref items)) => {
|
||||||
|
if let Some(some_etag) = etag {
|
||||||
|
for item in items {
|
||||||
|
if item.strong_eq(some_etag) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if `request` doesn't have an `If-None-Match` header matching `req`.
|
||||||
|
fn none_match(etag: Option<&app::http::header::EntityTag>, request: &app::HttpRequest) -> bool {
|
||||||
|
match request.get_header::<app::http::header::IfNoneMatch>() {
|
||||||
|
Some(app::http::header::IfNoneMatch::Any) => false,
|
||||||
|
Some(app::http::header::IfNoneMatch::Items(ref items)) => {
|
||||||
|
if let Some(some_etag) = etag {
|
||||||
|
for item in items {
|
||||||
|
if item.weak_eq(some_etag) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
None => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract(attr: &'static str, from: &'static str) -> Option<&'static str> {
|
||||||
|
let search = concat_string!("<!-- ", attr, ":");
|
||||||
|
if let Some(ini) = from.find(&search) {
|
||||||
|
let ini = ini + search.len() + 1;
|
||||||
|
if let Some(end) = from[ini..].find("-->").map(|i| i + ini) {
|
||||||
|
let end = end - 1;
|
||||||
|
return Some(&from[ini..end]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
@ -13,9 +13,6 @@ pub mod locale;
|
||||||
#[cfg(feature = "database")]
|
#[cfg(feature = "database")]
|
||||||
pub mod db;
|
pub mod db;
|
||||||
|
|
||||||
mod definition;
|
|
||||||
pub use definition::AppTrait;
|
|
||||||
|
|
||||||
pub mod application;
|
pub mod application;
|
||||||
|
|
||||||
pub mod fatal_error;
|
pub mod fatal_error;
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
use super::{fatal_error::FatalError, AppTrait};
|
use super::fatal_error::FatalError;
|
||||||
use crate::config::SETTINGS;
|
use crate::config::SETTINGS;
|
||||||
|
use crate::core::module::ModuleStaticRef;
|
||||||
use crate::core::{module, theme};
|
use crate::core::{module, theme};
|
||||||
use crate::html::Markup;
|
use crate::html::Markup;
|
||||||
use crate::response::page::ResultPage;
|
use crate::response::page::ResultPage;
|
||||||
use crate::{base, trace, LazyStatic};
|
use crate::LazyStatic;
|
||||||
|
|
||||||
use actix_web::dev::Server;
|
use actix_web::dev::Server;
|
||||||
|
|
||||||
use std::io::Error;
|
use std::io::Error;
|
||||||
|
|
||||||
pub struct Application {
|
pub struct Application {
|
||||||
|
|
@ -13,7 +15,7 @@ pub struct Application {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Application {
|
impl Application {
|
||||||
pub async fn prepare(app: impl AppTrait) -> Result<Self, Error> {
|
pub async fn prepare(app: ModuleStaticRef) -> Result<Self, Error> {
|
||||||
// Rótulo de presentación.
|
// Rótulo de presentación.
|
||||||
super::banner::print_on_startup();
|
super::banner::print_on_startup();
|
||||||
|
|
||||||
|
|
@ -23,40 +25,25 @@ impl Application {
|
||||||
// Valida el identificador de idioma.
|
// Valida el identificador de idioma.
|
||||||
LazyStatic::force(&super::locale::LANGID);
|
LazyStatic::force(&super::locale::LANGID);
|
||||||
|
|
||||||
// Conecta con la base de datos (opcional).
|
|
||||||
#[cfg(feature = "database")]
|
#[cfg(feature = "database")]
|
||||||
|
// Conecta con la base de datos.
|
||||||
LazyStatic::force(&super::db::DBCONN);
|
LazyStatic::force(&super::db::DBCONN);
|
||||||
|
|
||||||
// Deshabilita los módulos indicados por la aplicación.
|
// Registra los módulos de la aplicación.
|
||||||
module::all::disable_modules(app.disable_modules());
|
module::all::register_modules(app);
|
||||||
// Habilita los módulos predeterminados.
|
|
||||||
module::all::enable_modules(vec![&base::module::homepage::DefaultHomePage]);
|
|
||||||
// Habilita los módulos de la aplicación.
|
|
||||||
module::all::enable_modules(app.enable_modules());
|
|
||||||
|
|
||||||
// Registra los temas predeterminados.
|
// Registra los temas de los módulos.
|
||||||
theme::all::register_themes(vec![
|
module::all::register_themes();
|
||||||
&base::theme::aliner::Aliner,
|
|
||||||
&base::theme::minimal::Minimal,
|
|
||||||
&base::theme::bootsier::Bootsier,
|
|
||||||
&base::theme::bulmix::Bulmix,
|
|
||||||
]);
|
|
||||||
// Registra los temas de la aplicación.
|
|
||||||
theme::all::register_themes(app.themes());
|
|
||||||
|
|
||||||
// Registra las acciones de todos los módulos.
|
// Registra acciones de los módulos.
|
||||||
module::all::register_actions();
|
module::all::register_actions();
|
||||||
|
|
||||||
// Ejecuta actualizaciones pendientes de la base de datos (opcional).
|
// Inicializa los módulos.
|
||||||
#[cfg(feature = "database")]
|
|
||||||
module::all::run_migrations();
|
|
||||||
|
|
||||||
// Inicializa los módulos que lo requieran.
|
|
||||||
module::all::init_modules();
|
module::all::init_modules();
|
||||||
|
|
||||||
// Ejecuta la función de inicio de la aplicación.
|
#[cfg(feature = "database")]
|
||||||
trace::info!("Calling application bootstrap");
|
// Ejecuta actualizaciones pendientes de la base de datos.
|
||||||
app.bootstrap();
|
module::all::run_migrations();
|
||||||
|
|
||||||
// Prepara el servidor web.
|
// Prepara el servidor web.
|
||||||
let server = super::HttpServer::new(move || {
|
let server = super::HttpServer::new(move || {
|
||||||
|
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
use crate::core::module::ModuleStaticRef;
|
|
||||||
use crate::core::theme::ThemeStaticRef;
|
|
||||||
|
|
||||||
pub trait AppTrait: Send + Sync {
|
|
||||||
fn bootstrap(&self) {}
|
|
||||||
|
|
||||||
fn enable_modules(&self) -> Vec<ModuleStaticRef> {
|
|
||||||
vec![]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn disable_modules(&self) -> Vec<ModuleStaticRef> {
|
|
||||||
vec![]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn themes(&self) -> Vec<ThemeStaticRef> {
|
|
||||||
vec![]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use super::ModuleStaticRef;
|
use super::ModuleStaticRef;
|
||||||
use crate::core::hook::add_action;
|
use crate::core::hook::add_action;
|
||||||
|
use crate::core::theme;
|
||||||
use crate::{app, trace, LazyStatic};
|
use crate::{app, trace, LazyStatic};
|
||||||
|
|
||||||
#[cfg(feature = "database")]
|
#[cfg(feature = "database")]
|
||||||
|
|
@ -7,47 +8,42 @@ use crate::{db::*, run_now};
|
||||||
|
|
||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
|
|
||||||
// DISABLED MODULES ********************************************************************************
|
// REGISTER MODULES ********************************************************************************
|
||||||
|
|
||||||
static DISABLED_MODULES: LazyStatic<RwLock<Vec<ModuleStaticRef>>> =
|
|
||||||
LazyStatic::new(|| RwLock::new(Vec::new()));
|
|
||||||
|
|
||||||
pub fn disable_modules(modules: Vec<ModuleStaticRef>) {
|
|
||||||
let mut disabled_modules = DISABLED_MODULES.write().unwrap();
|
|
||||||
for module in modules {
|
|
||||||
if !disabled_modules
|
|
||||||
.iter()
|
|
||||||
.any(|m| m.handler() == module.handler())
|
|
||||||
{
|
|
||||||
trace::debug!("Disabling the \"{}\" module", module.single_name());
|
|
||||||
disabled_modules.push(module);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ENABLED MODULES *********************************************************************************
|
|
||||||
|
|
||||||
static ENABLED_MODULES: LazyStatic<RwLock<Vec<ModuleStaticRef>>> =
|
static ENABLED_MODULES: LazyStatic<RwLock<Vec<ModuleStaticRef>>> =
|
||||||
LazyStatic::new(|| RwLock::new(Vec::new()));
|
LazyStatic::new(|| RwLock::new(Vec::new()));
|
||||||
|
|
||||||
pub fn enable_modules(modules: Vec<ModuleStaticRef>) {
|
static DISCARDED_MODULES: LazyStatic<RwLock<Vec<ModuleStaticRef>>> =
|
||||||
for module in modules {
|
LazyStatic::new(|| RwLock::new(Vec::new()));
|
||||||
let mut list: Vec<ModuleStaticRef> = Vec::new();
|
|
||||||
add_to_enabled(&mut list, module);
|
pub fn register_modules(app: ModuleStaticRef) {
|
||||||
list.reverse();
|
// Revisa los módulos a deshabilitar.
|
||||||
ENABLED_MODULES.write().unwrap().append(&mut list);
|
let mut list: Vec<ModuleStaticRef> = Vec::new();
|
||||||
|
add_to_discarded(&mut list, app);
|
||||||
|
DISCARDED_MODULES.write().unwrap().append(&mut list);
|
||||||
|
|
||||||
|
// Habilita los módulos de la aplicación.
|
||||||
|
let mut list: Vec<ModuleStaticRef> = Vec::new();
|
||||||
|
add_to_enabled(&mut list, app);
|
||||||
|
list.reverse();
|
||||||
|
ENABLED_MODULES.write().unwrap().append(&mut list);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_to_discarded(list: &mut Vec<ModuleStaticRef>, module: ModuleStaticRef) {
|
||||||
|
for u in module.uninstall_modules().iter() {
|
||||||
|
if !list.iter().any(|m| m.handler() == u.handler()) {
|
||||||
|
list.push(*u);
|
||||||
|
trace::debug!("Module \"{}\" discarded", u.single_name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for d in module.dependencies().iter() {
|
||||||
|
add_to_discarded(list, *d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_to_enabled(list: &mut Vec<ModuleStaticRef>, module: ModuleStaticRef) {
|
fn add_to_enabled(list: &mut Vec<ModuleStaticRef>, module: ModuleStaticRef) {
|
||||||
if !ENABLED_MODULES
|
if !list.iter().any(|m| m.handler() == module.handler()) {
|
||||||
.read()
|
if DISCARDED_MODULES
|
||||||
.unwrap()
|
|
||||||
.iter()
|
|
||||||
.any(|m| m.handler() == module.handler())
|
|
||||||
&& !list.iter().any(|m| m.handler() == module.handler())
|
|
||||||
{
|
|
||||||
if DISABLED_MODULES
|
|
||||||
.read()
|
.read()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -58,7 +54,6 @@ fn add_to_enabled(list: &mut Vec<ModuleStaticRef>, module: ModuleStaticRef) {
|
||||||
module.single_name()
|
module.single_name()
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
trace::debug!("Enabling the \"{}\" module", module.single_name());
|
|
||||||
list.push(module);
|
list.push(module);
|
||||||
|
|
||||||
let mut dependencies = module.dependencies();
|
let mut dependencies = module.dependencies();
|
||||||
|
|
@ -66,23 +61,21 @@ fn add_to_enabled(list: &mut Vec<ModuleStaticRef>, module: ModuleStaticRef) {
|
||||||
for d in dependencies.iter() {
|
for d in dependencies.iter() {
|
||||||
add_to_enabled(list, *d);
|
add_to_enabled(list, *d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trace::debug!("Enabling \"{}\" module", module.single_name());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CONFIGURE MODULES *******************************************************************************
|
// REGISTER THEMES *********************************************************************************
|
||||||
|
|
||||||
pub fn init_modules() {
|
pub fn register_themes() {
|
||||||
for m in ENABLED_MODULES.read().unwrap().iter() {
|
for m in ENABLED_MODULES.read().unwrap().iter() {
|
||||||
m.init_module();
|
theme::all::register_themes(m.themes());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn configure_services(cfg: &mut app::web::ServiceConfig) {
|
// REGISTER ACTIONS ********************************************************************************
|
||||||
for m in ENABLED_MODULES.read().unwrap().iter() {
|
|
||||||
m.configure_service(cfg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn register_actions() {
|
pub fn register_actions() {
|
||||||
for m in ENABLED_MODULES.read().unwrap().iter() {
|
for m in ENABLED_MODULES.read().unwrap().iter() {
|
||||||
|
|
@ -92,23 +85,10 @@ pub fn register_actions() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "database")]
|
// RUN MIGRATIONS **********************************************************************************
|
||||||
pub(crate) fn run_migrations() {
|
|
||||||
run_now({
|
|
||||||
struct Migrator;
|
|
||||||
impl MigratorTrait for Migrator {
|
|
||||||
fn migrations() -> Vec<MigrationItem> {
|
|
||||||
let mut migrations = vec![];
|
|
||||||
for m in DISABLED_MODULES.read().unwrap().iter() {
|
|
||||||
migrations.append(&mut m.migrations());
|
|
||||||
}
|
|
||||||
migrations
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Migrator::down(&app::db::DBCONN, None)
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
|
#[cfg(feature = "database")]
|
||||||
|
pub fn run_migrations() {
|
||||||
run_now({
|
run_now({
|
||||||
struct Migrator;
|
struct Migrator;
|
||||||
impl MigratorTrait for Migrator {
|
impl MigratorTrait for Migrator {
|
||||||
|
|
@ -123,4 +103,36 @@ pub(crate) fn run_migrations() {
|
||||||
Migrator::up(&app::db::DBCONN, None)
|
Migrator::up(&app::db::DBCONN, None)
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
run_now({
|
||||||
|
struct Migrator;
|
||||||
|
impl MigratorTrait for Migrator {
|
||||||
|
fn migrations() -> Vec<MigrationItem> {
|
||||||
|
let mut migrations = vec![];
|
||||||
|
for m in DISCARDED_MODULES.read().unwrap().iter() {
|
||||||
|
migrations.append(&mut m.migrations());
|
||||||
|
}
|
||||||
|
migrations
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Migrator::down(&app::db::DBCONN, None)
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// INIT MODULES ************************************************************************************
|
||||||
|
|
||||||
|
pub fn init_modules() {
|
||||||
|
trace::info!("Calling application bootstrap");
|
||||||
|
for m in ENABLED_MODULES.read().unwrap().iter() {
|
||||||
|
m.init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CONFIGURE SERVICES ******************************************************************************
|
||||||
|
|
||||||
|
pub fn configure_services(cfg: &mut app::web::ServiceConfig) {
|
||||||
|
for m in ENABLED_MODULES.read().unwrap().iter() {
|
||||||
|
m.configure_service(cfg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::app;
|
use crate::app;
|
||||||
use crate::core::hook::HookAction;
|
use crate::core::hook::HookAction;
|
||||||
|
use crate::core::theme::ThemeStaticRef;
|
||||||
use crate::util::{single_type_name, Handler};
|
use crate::util::{single_type_name, Handler};
|
||||||
|
|
||||||
#[cfg(feature = "database")]
|
#[cfg(feature = "database")]
|
||||||
|
|
@ -27,10 +28,13 @@ pub trait ModuleTrait: BaseModule + Send + Sync {
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_module(&self) {}
|
fn uninstall_modules(&self) -> Vec<ModuleStaticRef> {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(unused_variables)]
|
fn themes(&self) -> Vec<ThemeStaticRef> {
|
||||||
fn configure_service(&self, cfg: &mut app::web::ServiceConfig) {}
|
vec![]
|
||||||
|
}
|
||||||
|
|
||||||
fn actions(&self) -> Vec<HookAction> {
|
fn actions(&self) -> Vec<HookAction> {
|
||||||
vec![]
|
vec![]
|
||||||
|
|
@ -41,6 +45,11 @@ pub trait ModuleTrait: BaseModule + Send + Sync {
|
||||||
fn migrations(&self) -> Vec<MigrationItem> {
|
fn migrations(&self) -> Vec<MigrationItem> {
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn init(&self) {}
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
fn configure_service(&self, cfg: &mut app::web::ServiceConfig) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<M: ?Sized + ModuleTrait> BaseModule for M {
|
impl<M: ?Sized + ModuleTrait> BaseModule for M {
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,19 @@
|
||||||
use super::ThemeStaticRef;
|
use super::ThemeStaticRef;
|
||||||
use crate::{app, theme_static_files, trace, LazyStatic};
|
use crate::{app, base, theme_static_files, trace, LazyStatic};
|
||||||
|
|
||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/theme.rs"));
|
include!(concat!(env!("OUT_DIR"), "/theme.rs"));
|
||||||
|
|
||||||
// Temas registrados.
|
// Temas registrados.
|
||||||
static THEMES: LazyStatic<RwLock<Vec<ThemeStaticRef>>> =
|
static THEMES: LazyStatic<RwLock<Vec<ThemeStaticRef>>> = LazyStatic::new(|| {
|
||||||
LazyStatic::new(|| RwLock::new(Vec::new()));
|
RwLock::new(vec![
|
||||||
|
&base::theme::aliner::Aliner,
|
||||||
|
&base::theme::minimal::Minimal,
|
||||||
|
&base::theme::bootsier::Bootsier,
|
||||||
|
&base::theme::bulmix::Bulmix,
|
||||||
|
])
|
||||||
|
});
|
||||||
|
|
||||||
pub fn register_themes(themes: Vec<ThemeStaticRef>) {
|
pub fn register_themes(themes: Vec<ThemeStaticRef>) {
|
||||||
let mut registered_themes = THEMES.write().unwrap();
|
let mut registered_themes = THEMES.write().unwrap();
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,13 @@ pub use crate::{db, db::*, migration_item, pub_migration};
|
||||||
pub use crate::app;
|
pub use crate::app;
|
||||||
pub use crate::app::application::Application;
|
pub use crate::app::application::Application;
|
||||||
pub use crate::app::fatal_error::FatalError;
|
pub use crate::app::fatal_error::FatalError;
|
||||||
pub use crate::app::{AppTrait, HttpMessage};
|
pub use crate::app::HttpMessage;
|
||||||
|
|
||||||
pub use crate::core::{component::*, hook::*, module::*, theme::*};
|
pub use crate::core::{component::*, hook::*, module::*, theme::*};
|
||||||
|
|
||||||
pub use crate::{hook_action, hook_before_render_component};
|
pub use crate::{hook_action, hook_before_render_component};
|
||||||
|
|
||||||
pub use crate::response::page::*;
|
pub use crate::response::page::*;
|
||||||
|
pub use crate::response::ResponseError;
|
||||||
|
|
||||||
pub use crate::base::component::*;
|
pub use crate::base::component::*;
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ path = "../pagetop"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-web = "4.1.0"
|
actix-web = "4.1.0"
|
||||||
static-files = "0.2.3"
|
static-files = "0.2.3"
|
||||||
|
#pagetop-mdbook = { path = "../pagetop-mdbook" }
|
||||||
maud = { git = "https://github.com/lambda-fairy/maud", rev = "e6787cd6" }
|
maud = { git = "https://github.com/lambda-fairy/maud", rev = "e6787cd6" }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,25 @@
|
||||||
use pagetop::prelude::*;
|
use pagetop::prelude::*;
|
||||||
|
|
||||||
|
pub_const_handler!(APP_PAGETOP_WEBSITE);
|
||||||
|
|
||||||
mod mdbook;
|
mod mdbook;
|
||||||
|
|
||||||
struct PageTopWebSite;
|
struct PageTopWebSite;
|
||||||
|
|
||||||
impl AppTrait for PageTopWebSite {
|
impl ModuleTrait for PageTopWebSite {
|
||||||
fn enable_modules(&self) -> Vec<ModuleStaticRef> {
|
fn handler(&self) -> Handler {
|
||||||
vec![&mdbook::MdBook]
|
APP_PAGETOP_WEBSITE
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dependencies(&self) -> Vec<ModuleStaticRef> {
|
||||||
|
vec![
|
||||||
|
&mdbook::MdBook,
|
||||||
|
&pagetop::base::module::homepage::DefaultHomePage,
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
async fn main() -> std::io::Result<()> {
|
async fn main() -> std::io::Result<()> {
|
||||||
Application::prepare(PageTopWebSite).await?.run()?.await
|
Application::prepare(&PageTopWebSite).await?.run()?.await
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,12 @@ use static_files::Resource;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub_const_handler!(MODULE_MDBOOK);
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/mdbook.rs"));
|
include!(concat!(env!("OUT_DIR"), "/mdbook.rs"));
|
||||||
|
|
||||||
static MDBOOK: LazyStatic<HashMap<&'static str, Resource>> = LazyStatic::new(generate);
|
static MDBOOK: LazyStatic<HashMap<&'static str, Resource>> = LazyStatic::new(generate);
|
||||||
|
|
||||||
pub_const_handler!(MODULE_MDBOOK);
|
|
||||||
|
|
||||||
pub struct MdBook;
|
pub struct MdBook;
|
||||||
|
|
||||||
impl ModuleTrait for MdBook {
|
impl ModuleTrait for MdBook {
|
||||||
|
|
@ -19,19 +19,41 @@ impl ModuleTrait for MdBook {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn configure_service(&self, cfg: &mut app::web::ServiceConfig) {
|
fn configure_service(&self, cfg: &mut app::web::ServiceConfig) {
|
||||||
cfg.service(
|
configure_mdbook_service(cfg, "/doc/", &MDBOOK);
|
||||||
app::web::scope("/doc")
|
|
||||||
.route("{tail:.*html$}", app::web::get().to(mdbook_page))
|
|
||||||
.route("{tail:.*$}", app::web::get().to(mdbook_resource)),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn mdbook_page(request: app::HttpRequest) -> ResultPage<Markup, FatalError> {
|
fn configure_mdbook_service(
|
||||||
// Remove initial "/doc/" from path:
|
cfg: &mut app::web::ServiceConfig,
|
||||||
let path = &request.path()[5..];
|
url_root: &'static str,
|
||||||
|
book: &'static HashMap<&'static str, Resource>,
|
||||||
|
) {
|
||||||
|
let url = url_root.trim_end_matches('/');
|
||||||
|
let url_len = url.len() + 1;
|
||||||
|
cfg.service(
|
||||||
|
app::web::scope(url)
|
||||||
|
.route(
|
||||||
|
"{tail:.*html$}",
|
||||||
|
app::web::get()
|
||||||
|
.to(move |request: app::HttpRequest| mdbook_page(request, url_len, book)),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"{tail:.*$}",
|
||||||
|
app::web::get()
|
||||||
|
.to(move |request: app::HttpRequest| mdbook_resource(request, url_len, book)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(content) = MDBOOK.get(path) {
|
async fn mdbook_page(
|
||||||
|
request: app::HttpRequest,
|
||||||
|
v: usize,
|
||||||
|
book: &HashMap<&'static str, Resource>,
|
||||||
|
) -> ResultPage<Markup, FatalError> {
|
||||||
|
// Remove initial "/doc/" from path:
|
||||||
|
let path = &request.path()[v..];
|
||||||
|
|
||||||
|
if let Some(content) = book.get(path) {
|
||||||
if let Ok(html) = std::str::from_utf8(content.data) {
|
if let Ok(html) = std::str::from_utf8(content.data) {
|
||||||
let _lang = extract("Lang", html);
|
let _lang = extract("Lang", html);
|
||||||
let title = match extract("Title", html) {
|
let title = match extract("Title", html) {
|
||||||
|
|
@ -93,13 +115,17 @@ async fn mdbook_page(request: app::HttpRequest) -> ResultPage<Markup, FatalError
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn mdbook_resource(request: app::HttpRequest) -> app::HttpResponse {
|
async fn mdbook_resource(
|
||||||
|
request: app::HttpRequest,
|
||||||
|
v: usize,
|
||||||
|
book: &HashMap<&'static str, Resource>,
|
||||||
|
) -> app::HttpResponse {
|
||||||
// Remove initial "/doc/" from path:
|
// Remove initial "/doc/" from path:
|
||||||
let path = &request.path()[5..];
|
let path = &request.path()[v..];
|
||||||
|
|
||||||
// From https://github.com/kilork/actix-web-static-files/blob/master/src/resource_files.rs, see
|
// From https://github.com/kilork/actix-web-static-files/blob/master/src/resource_files.rs, see
|
||||||
// functions respond_to(), any_match() and none_match().
|
// functions respond_to(), any_match() and none_match().
|
||||||
if let Some(file) = &MDBOOK.get(path) {
|
if let Some(file) = &book.get(path) {
|
||||||
let etag = Some(app::http::header::EntityTag::new_strong(format!(
|
let etag = Some(app::http::header::EntityTag::new_strong(format!(
|
||||||
"{:x}:{:x}",
|
"{:x}:{:x}",
|
||||||
file.data.len(),
|
file.data.len(),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue