~whbboyd/russet

51ac1ea2b674517d93629f5be13ffbede2d36c99 — Will Boyd 4 months ago 2672dce main
user page
4 files changed, 48 insertions(+), 5 deletions(-)

M src/http/error.rs
M src/http/user.rs
M templates/static/styles.css.stpl
A templates/user.stpl
M src/http/error.rs => src/http/error.rs +2 -0
@@ 10,6 10,7 @@ use ulid::Ulid;
pub enum HttpError {
	BadRequest { description: String },
	Unauthenticated { redirect_to: Option<String> },
	Forbidden,
	NotFound,
	InternalError { description: String },
}


@@ 48,6 49,7 @@ impl IntoResponse for HttpError {
					) );
				return Redirect::to(&redirect).into_response();
			},
			HttpError::Forbidden => (StatusCode::FORBIDDEN, "You do not have access to this resource.".to_string()),
			HttpError::NotFound => (StatusCode::NOT_FOUND, "No resource exists at this URL.".to_string()),
			HttpError::InternalError { description } => {
				let correlation_id = Ulid::new();

M src/http/user.rs => src/http/user.rs +25 -5
@@ 4,19 4,39 @@ use crate::http::{ AppState, AuthenticatedUser };
use crate::http::error::HttpError;
use crate::model::{ UserId, UserType };
use crate::persistence::RussetPersistenceLayer;
use crate::persistence::model::User;
use sailfish::TemplateOnce;

#[derive(Clone, Debug, TemplateOnce)]
#[template(path = "user.stpl")]
pub struct UserPage<'a> {
	page_user: &'a User,
	user: Option<&'a User>,
	page_title: &'a str,
	relative_root: &'a str,
}
#[tracing::instrument]
pub async fn user_page<Persistence>(
	Path(user_id): Path<UserId>,
	Path(page_user_id): Path<UserId>,
	State(state): State<AppState<Persistence>>,
	auth_user: AuthenticatedUser<Persistence>,
) -> Result<Html<String>, HttpError>
where Persistence: RussetPersistenceLayer {
	// Authentication rules. Sysops can see all user pages. Members can see only
	// themselves.
	if auth_user.user.user_type != UserType::Sysop && auth_user.user.id != user_id {
		panic!("PERMISSION DENIED!!!1!!");
	if auth_user.user.user_type != UserType::Sysop &&
			auth_user.user.id != page_user_id {
		return Err(HttpError::Forbidden);
	}
	let user = state.domain_service.get_user(&user_id).await?;
	Ok(Html(format!("User: {}<br />ID: {:?}<br />Type: {:?}", user.name, user.id, user.user_type)))
	let page_user = state.domain_service.get_user(&page_user_id).await?;
	let page_title = format!("User - {}", page_user.name);
	Ok(Html(
		UserPage{
			page_user: &page_user,
			user: Some(&auth_user.user),
			page_title: &page_title,
			relative_root: "../",
		}
		.render_once()?
	) )
}

M templates/static/styles.css.stpl => templates/static/styles.css.stpl +4 -0
@@ 129,6 129,10 @@ button:active {
	border-color: #282;
	border-style: inset;
}
button:disabled {
	background: #444;
	border-color: #555;
}

/* Dialog form styles */
.dialog {

A templates/user.stpl => templates/user.stpl +17 -0
@@ 0,0 1,17 @@
<% include!("head.stpl"); %>
		<div style="display: flex; justify-content: center;">
			<form action="<%- relative_root %>user/<%- page_user.id.to_string() %>" method="post" class="dialog">
				<div class="inputs">
					<label for="id">Name:</label>
					<input type="text" name="name" value="<%= page_user.name %>" disabled="true" />
					<label for="id">ID:</label>
					<input type="text" name="id" value="<%= page_user.id.to_string() %>" disabled="true" />
					<label for="user_type">Type:</label>
					<input type="text" name="user_type" value="<%= format!("{:?}", page_user.user_type) %>" disabled="true" />
				</div>
				<div class="controls">
					<button disabled="true">Update</button>
				</div>
			</form>
		</div>
<% include!("foot.stpl"); %>