add root template
This commit is contained in:
		
							parent
							
								
									eb1bb7309a
								
							
						
					
					
						commit
						26de7b3cc4
					
				
					 10 changed files with 141 additions and 21 deletions
				
			
		| 
						 | 
				
			
			@ -2,6 +2,7 @@ CREATE TABLE users (
 | 
			
		|||
    id       SERIAL PRIMARY KEY,
 | 
			
		||||
    username TEXT NOT NULL UNIQUE,
 | 
			
		||||
    password TEXT NOT NULL,
 | 
			
		||||
    role     INTEGER NOT NULL,
 | 
			
		||||
 | 
			
		||||
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
 | 
			
		||||
    last_active TIMESTAMPTZ NOT NULL DEFAULT now()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										22
									
								
								src/main.rs
									
										
									
									
									
								
							
							
						
						
									
										22
									
								
								src/main.rs
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,26 +1,27 @@
 | 
			
		|||
pub mod config;
 | 
			
		||||
pub mod model;
 | 
			
		||||
pub mod pages;
 | 
			
		||||
pub mod state;
 | 
			
		||||
pub mod templates;
 | 
			
		||||
 | 
			
		||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
 | 
			
		||||
use std::sync::Arc;
 | 
			
		||||
 | 
			
		||||
use axum::extract::State;
 | 
			
		||||
use axum::http::{header, StatusCode};
 | 
			
		||||
use axum::response::IntoResponse;
 | 
			
		||||
use axum::{routing::get, Router};
 | 
			
		||||
use clap::Parser;
 | 
			
		||||
use eyre::{Result, WrapErr};
 | 
			
		||||
use sqlx::migrate;
 | 
			
		||||
use sqlx::postgres::PgPoolOptions;
 | 
			
		||||
use tower_http::trace::TraceLayer;
 | 
			
		||||
use tracing::{debug, error, info};
 | 
			
		||||
use tracing::{debug, info};
 | 
			
		||||
 | 
			
		||||
use crate::config::Config;
 | 
			
		||||
use crate::state::AppState;
 | 
			
		||||
use crate::templates::handlebars;
 | 
			
		||||
 | 
			
		||||
use crate::pages::index::index;
 | 
			
		||||
use crate::pages::user::get_user;
 | 
			
		||||
 | 
			
		||||
#[tokio::main]
 | 
			
		||||
async fn main() -> Result<()> {
 | 
			
		||||
    dotenvy::dotenv().ok();
 | 
			
		||||
| 
						 | 
				
			
			@ -51,6 +52,7 @@ async fn main() -> Result<()> {
 | 
			
		|||
 | 
			
		||||
    let app = Router::new()
 | 
			
		||||
        .route("/", get(index))
 | 
			
		||||
        .route("/users/:id", get(get_user))
 | 
			
		||||
        .layer(TraceLayer::new_for_http())
 | 
			
		||||
        .with_state(state);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -62,15 +64,3 @@ async fn main() -> Result<()> {
 | 
			
		|||
        .await
 | 
			
		||||
        .wrap_err("running server")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn index(State(state): State<Arc<AppState>>) -> impl IntoResponse {
 | 
			
		||||
    let out = match state.hbs.render("root.hbs", &16) {
 | 
			
		||||
        Ok(s) => s,
 | 
			
		||||
        Err(err) => {
 | 
			
		||||
            error!("Rendering index page: {}", err);
 | 
			
		||||
            return (StatusCode::INTERNAL_SERVER_ERROR, "Internal server error").into_response();
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return ([(header::CONTENT_TYPE, "text/html")], out).into_response();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,12 +1,48 @@
 | 
			
		|||
use chrono::{DateTime, Utc};
 | 
			
		||||
use serde::Serialize;
 | 
			
		||||
use serde::{Serialize, Serializer};
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Serialize)]
 | 
			
		||||
pub struct User {
 | 
			
		||||
    pub id: i32,
 | 
			
		||||
    pub username: String,
 | 
			
		||||
    pub password: String,
 | 
			
		||||
    pub role: Role,
 | 
			
		||||
 | 
			
		||||
    pub created_at: DateTime<Utc>,
 | 
			
		||||
    pub last_active: DateTime<Utc>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(sqlx::Type, Debug)]
 | 
			
		||||
#[repr(i32)]
 | 
			
		||||
pub enum Role {
 | 
			
		||||
    Viewer = 1,
 | 
			
		||||
    Editor = 2,
 | 
			
		||||
    Manager = 3,
 | 
			
		||||
    Admin = 4,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Serialize for Role {
 | 
			
		||||
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
 | 
			
		||||
    where
 | 
			
		||||
        S: Serializer,
 | 
			
		||||
    {
 | 
			
		||||
        serializer.serialize_str(match self {
 | 
			
		||||
            Role::Viewer => "viwer",
 | 
			
		||||
            Role::Editor => "editor",
 | 
			
		||||
            Role::Manager => "manager",
 | 
			
		||||
            Role::Admin => "admin",
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<i32> for Role {
 | 
			
		||||
    fn from(value: i32) -> Self {
 | 
			
		||||
        match value {
 | 
			
		||||
            1 => Self::Viewer,
 | 
			
		||||
            2 => Self::Editor,
 | 
			
		||||
            3 => Self::Manager,
 | 
			
		||||
            4 => Self::Admin,
 | 
			
		||||
            _ => unreachable!("Role should never be outside 1-4")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										9
									
								
								src/pages/index.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/pages/index.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
use std::sync::Arc;
 | 
			
		||||
 | 
			
		||||
use axum::{extract::State, response::IntoResponse};
 | 
			
		||||
 | 
			
		||||
use crate::{state::AppState, templates::{Page, PageData}};
 | 
			
		||||
 | 
			
		||||
pub async fn index(State(state): State<Arc<AppState>>) -> impl IntoResponse {
 | 
			
		||||
    Page::new(state.hbs.render::<PageData>("index.hbs", &Default::default()))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										2
									
								
								src/pages/mod.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								src/pages/mod.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,2 @@
 | 
			
		|||
pub mod index;
 | 
			
		||||
pub mod user;
 | 
			
		||||
							
								
								
									
										40
									
								
								src/pages/user/mod.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/pages/user/mod.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,40 @@
 | 
			
		|||
use std::sync::Arc;
 | 
			
		||||
 | 
			
		||||
use axum::{
 | 
			
		||||
    extract::{Path, State},
 | 
			
		||||
    response::IntoResponse,
 | 
			
		||||
};
 | 
			
		||||
use sqlx::query_as;
 | 
			
		||||
use tracing::error;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    model::user::User,
 | 
			
		||||
    state::AppState,
 | 
			
		||||
    templates::{Page, PageData},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
pub async fn get_user(
 | 
			
		||||
    State(state): State<Arc<AppState>>,
 | 
			
		||||
    Path(user_id): Path<i32>,
 | 
			
		||||
) -> impl IntoResponse {
 | 
			
		||||
    let user = match query_as!(User, r#"SELECT * FROM users WHERE id = $1"#, user_id)
 | 
			
		||||
        .fetch_one(&state.pool)
 | 
			
		||||
        .await
 | 
			
		||||
    {
 | 
			
		||||
        Ok(user) => user,
 | 
			
		||||
        Err(why) => {
 | 
			
		||||
            error!("Getting user: {}", why);
 | 
			
		||||
            return Page::new(
 | 
			
		||||
                state
 | 
			
		||||
                    .hbs
 | 
			
		||||
                    .render::<PageData>("error.hbs", &Default::default()),
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    Page::new(
 | 
			
		||||
        state
 | 
			
		||||
            .hbs
 | 
			
		||||
            .render("error.hbs", &PageData { user: Some(user) }),
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,7 +1,14 @@
 | 
			
		|||
use axum::{
 | 
			
		||||
    http::{header, StatusCode},
 | 
			
		||||
    response::IntoResponse,
 | 
			
		||||
};
 | 
			
		||||
use eyre::{Context, Result};
 | 
			
		||||
use handlebars::Handlebars;
 | 
			
		||||
use handlebars::{Handlebars, RenderError};
 | 
			
		||||
use rust_embed::RustEmbed;
 | 
			
		||||
use tracing::info;
 | 
			
		||||
use serde::Serialize;
 | 
			
		||||
use tracing::{error, info};
 | 
			
		||||
 | 
			
		||||
use crate::model::user::User;
 | 
			
		||||
 | 
			
		||||
#[derive(RustEmbed)]
 | 
			
		||||
#[folder = "templates/"]
 | 
			
		||||
| 
						 | 
				
			
			@ -23,3 +30,28 @@ pub fn handlebars(dev_mode: bool) -> Result<Handlebars<'static>> {
 | 
			
		|||
 | 
			
		||||
    Ok(hbs)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct Page(Result<String, RenderError>);
 | 
			
		||||
 | 
			
		||||
impl Page {
 | 
			
		||||
    pub fn new(res: Result<String, RenderError>) -> Self {
 | 
			
		||||
        Self(res)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl IntoResponse for Page {
 | 
			
		||||
    fn into_response(self) -> axum::response::Response {
 | 
			
		||||
        match self.0 {
 | 
			
		||||
            Ok(s) => ([(header::CONTENT_TYPE, "text/html; charset=utf-8")], s).into_response(),
 | 
			
		||||
            Err(why) => {
 | 
			
		||||
                error!("Error rendering page: {}", why);
 | 
			
		||||
                (StatusCode::INTERNAL_SERVER_ERROR, "Internal server error").into_response()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Default, Serialize)]
 | 
			
		||||
pub struct PageData {
 | 
			
		||||
    pub user: Option<User>,
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										5
									
								
								templates/error.hbs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								templates/error.hbs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
{{#*inline "page"}}
 | 
			
		||||
<h1>Error</h1>
 | 
			
		||||
<p>An internal error occurred. Sorry :(</p>
 | 
			
		||||
{{/inline}}
 | 
			
		||||
{{> root.hbs}}
 | 
			
		||||
							
								
								
									
										5
									
								
								templates/index.hbs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								templates/index.hbs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
{{#*inline "page"}}
 | 
			
		||||
<h1>board!</h1>
 | 
			
		||||
<p>this will be a site eventually</p>
 | 
			
		||||
{{/inline}}
 | 
			
		||||
{{> root.hbs}}
 | 
			
		||||
| 
						 | 
				
			
			@ -6,6 +6,6 @@
 | 
			
		|||
    <title>imgboard</title>
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
    <h1>Hello world!!!</h1>
 | 
			
		||||
    {{> page}}
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue