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,
|
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()
|
||||||
|
|
22
src/main.rs
22
src/main.rs
|
@ -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();
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
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 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
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>
|
<title>imgboard</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Hello world!!!</h1>
|
{{> page}}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
Loading…
Reference in a new issue