Template
feat: Baseline added with config support
This commit is contained in:
1 parent
76934a6d76
commit
91ee0abc15
20 files changed
+1168
No files matched your search
+19
@@ -0,0 +1,19 @@
|
||||
use axum::{Router, extract::OriginalUri, routing::get};
|
||||
use tower_http::services::ServeDir;
|
||||
|
||||
use crate::models::config::Config;
|
||||
use crate::{error::AppError, routes};
|
||||
|
||||
async fn not_found(OriginalUri(uri): OriginalUri) -> AppError {
|
||||
AppError::NotFound {
|
||||
path: uri.path().to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn app() -> Router<Config> {
|
||||
let static_files = ServeDir::new("static").not_found_service(get(not_found));
|
||||
|
||||
Router::new()
|
||||
.merge(routes::router())
|
||||
.fallback_service(static_files)
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
use askama::Template;
|
||||
use axum::{
|
||||
http::StatusCode,
|
||||
response::{Html, IntoResponse},
|
||||
};
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "errors/401.html")]
|
||||
struct Error401;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "errors/404.html")]
|
||||
struct Error404 {
|
||||
path: String,
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "errors/500.html")]
|
||||
struct Error500 {
|
||||
message: Option<String>,
|
||||
}
|
||||
|
||||
pub enum AppError {
|
||||
// Unauthorized,
|
||||
NotFound { path: String },
|
||||
Internal { message: Option<String> },
|
||||
}
|
||||
|
||||
impl AppError {
|
||||
pub fn internal() -> Self {
|
||||
AppError::Internal { message: None }
|
||||
}
|
||||
|
||||
// pub fn internal_msg(msg: String) -> Self {
|
||||
// AppError::Internal { message: Some(msg.into()) }
|
||||
// }
|
||||
}
|
||||
|
||||
impl IntoResponse for AppError {
|
||||
fn into_response(self) -> axum::response::Response {
|
||||
match self {
|
||||
// AppError::Unauthorized => (
|
||||
// StatusCode::UNAUTHORIZED,
|
||||
// Html(Error401.render().unwrap())
|
||||
// ).into_response(),
|
||||
AppError::NotFound { path } => {
|
||||
let response = (
|
||||
StatusCode::NOT_FOUND,
|
||||
Html(Error404 { path }.render().unwrap()),
|
||||
);
|
||||
response.into_response()
|
||||
}
|
||||
|
||||
AppError::Internal { message } => {
|
||||
let response = (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Html(Error500 { message }.render().unwrap()),
|
||||
);
|
||||
response.into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
use crate::{models::config::Config, util::config::get_app_config};
|
||||
|
||||
mod app;
|
||||
mod error;
|
||||
mod models;
|
||||
mod routes;
|
||||
mod util;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
// Read once
|
||||
let config: Config = get_app_config().expect("Unable to read app config");
|
||||
|
||||
// Build app with shared state
|
||||
let app = app::app().with_state(config);
|
||||
|
||||
let listener = tokio::net::TcpListener::bind("0.0.0.0:7878").await.unwrap();
|
||||
|
||||
axum::serve(listener, app).await.unwrap();
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Clone, Deserialize)]
|
||||
pub struct Config {
|
||||
pub metadata: SiteMetadata,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize)]
|
||||
pub struct SiteMetadata {
|
||||
pub app_name: String,
|
||||
pub app_version: String,
|
||||
pub author: String,
|
||||
pub description: String,
|
||||
pub keywords: String,
|
||||
pub site_title: String,
|
||||
pub site_url: String,
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub mod config;
|
||||
@@ -0,0 +1,8 @@
|
||||
use crate::models::config::Config;
|
||||
use axum::Router;
|
||||
|
||||
mod root;
|
||||
|
||||
pub fn router() -> Router<Config> {
|
||||
Router::new().merge(root::router())
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
use askama::Template;
|
||||
use axum::{Router, extract::State, response::Html, routing::get};
|
||||
|
||||
use crate::{
|
||||
error::AppError,
|
||||
models::config::{Config, SiteMetadata},
|
||||
};
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "index.html")]
|
||||
struct HomePage {
|
||||
metadata: SiteMetadata,
|
||||
title: String,
|
||||
}
|
||||
|
||||
async fn home(State(config): State<Config>) -> Result<Html<String>, AppError> {
|
||||
let template = HomePage {
|
||||
metadata: config.metadata.clone(),
|
||||
title: String::from("Home"),
|
||||
};
|
||||
|
||||
template
|
||||
.render()
|
||||
.map(Html)
|
||||
.map_err(|_| AppError::internal())
|
||||
}
|
||||
|
||||
pub fn router() -> Router<Config> {
|
||||
Router::new().route("/", get(home))
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
use std::fs;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::models::config::Config;
|
||||
|
||||
// Temporary class to map from entire "Cargo.toml" file to only grab the portions under [app]
|
||||
#[derive(Deserialize)]
|
||||
struct TomlConfig {
|
||||
app: Config
|
||||
}
|
||||
|
||||
pub fn get_app_config() -> Result<Config, Box<dyn std::error::Error>> {
|
||||
let cargo_content = fs::read_to_string("Cargo.toml")?;
|
||||
let config: TomlConfig = toml::from_str(&cargo_content)?;
|
||||
|
||||
return Ok(config.app);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub mod config;
|
||||
Reference in new issue
Block a user