add root template

This commit is contained in:
sam 2023-10-19 02:54:47 +02:00
parent eb1bb7309a
commit 26de7b3cc4
Signed by: sam
GPG key ID: B4EF20DDE721CAA1
10 changed files with 141 additions and 21 deletions

View file

@ -2,6 +2,7 @@ CREATE TABLE users (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
username TEXT NOT NULL UNIQUE, username TEXT NOT NULL UNIQUE,
password TEXT NOT NULL, password TEXT NOT NULL,
role INTEGER NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
last_active TIMESTAMPTZ NOT NULL DEFAULT now() last_active TIMESTAMPTZ NOT NULL DEFAULT now()

View file

@ -1,26 +1,27 @@
pub mod config; pub mod config;
pub mod model; pub mod model;
pub mod pages;
pub mod state; pub mod state;
pub mod templates; pub mod templates;
use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::sync::Arc; use std::sync::Arc;
use axum::extract::State;
use axum::http::{header, StatusCode};
use axum::response::IntoResponse;
use axum::{routing::get, Router}; use axum::{routing::get, Router};
use clap::Parser; use clap::Parser;
use eyre::{Result, WrapErr}; use eyre::{Result, WrapErr};
use sqlx::migrate; use sqlx::migrate;
use sqlx::postgres::PgPoolOptions; use sqlx::postgres::PgPoolOptions;
use tower_http::trace::TraceLayer; use tower_http::trace::TraceLayer;
use tracing::{debug, error, info}; use tracing::{debug, info};
use crate::config::Config; use crate::config::Config;
use crate::state::AppState; use crate::state::AppState;
use crate::templates::handlebars; use crate::templates::handlebars;
use crate::pages::index::index;
use crate::pages::user::get_user;
#[tokio::main] #[tokio::main]
async fn main() -> Result<()> { async fn main() -> Result<()> {
dotenvy::dotenv().ok(); dotenvy::dotenv().ok();
@ -51,6 +52,7 @@ async fn main() -> Result<()> {
let app = Router::new() let app = Router::new()
.route("/", get(index)) .route("/", get(index))
.route("/users/:id", get(get_user))
.layer(TraceLayer::new_for_http()) .layer(TraceLayer::new_for_http())
.with_state(state); .with_state(state);
@ -62,15 +64,3 @@ async fn main() -> Result<()> {
.await .await
.wrap_err("running server") .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();
}

View file

@ -1,12 +1,48 @@
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use serde::Serialize; use serde::{Serialize, Serializer};
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub struct User { pub struct User {
pub id: i32, pub id: i32,
pub username: String, pub username: String,
pub password: String, pub password: String,
pub role: Role,
pub created_at: DateTime<Utc>, pub created_at: DateTime<Utc>,
pub last_active: 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
View 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
View file

@ -0,0 +1,2 @@
pub mod index;
pub mod user;

40
src/pages/user/mod.rs Normal file
View 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) }),
)
}

View file

@ -1,7 +1,14 @@
use axum::{
http::{header, StatusCode},
response::IntoResponse,
};
use eyre::{Context, Result}; use eyre::{Context, Result};
use handlebars::Handlebars; use handlebars::{Handlebars, RenderError};
use rust_embed::RustEmbed; use rust_embed::RustEmbed;
use tracing::info; use serde::Serialize;
use tracing::{error, info};
use crate::model::user::User;
#[derive(RustEmbed)] #[derive(RustEmbed)]
#[folder = "templates/"] #[folder = "templates/"]
@ -23,3 +30,28 @@ pub fn handlebars(dev_mode: bool) -> Result<Handlebars<'static>> {
Ok(hbs) 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
View file

@ -0,0 +1,5 @@
{{#*inline "page"}}
<h1>Error</h1>
<p>An internal error occurred. Sorry :&lpar;</p>
{{/inline}}
{{> root.hbs}}

5
templates/index.hbs Normal file
View file

@ -0,0 +1,5 @@
{{#*inline "page"}}
<h1>board!</h1>
<p>this will be a site eventually</p>
{{/inline}}
{{> root.hbs}}

View file

@ -6,6 +6,6 @@
<title>imgboard</title> <title>imgboard</title>
</head> </head>
<body> <body>
<h1>Hello world!!!</h1> {{> page}}
</body> </body>
</html> </html>