summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/channel/app.rs12
-rw-r--r--src/db/mod.rs21
-rw-r--r--src/ui.rs34
3 files changed, 52 insertions, 15 deletions
diff --git a/src/channel/app.rs b/src/channel/app.rs
index cb7ad32..7c0b107 100644
--- a/src/channel/app.rs
+++ b/src/channel/app.rs
@@ -2,7 +2,7 @@ use chrono::TimeDelta;
use itertools::Itertools;
use sqlx::sqlite::SqlitePool;
-use super::{repo::Provider as _, Channel, Id};
+use super::{repo::Provider as _, Channel, History, Id};
use crate::{
clock::DateTime,
db::NotFound,
@@ -36,6 +36,16 @@ impl<'a> Channels<'a> {
Ok(channel.as_created())
}
+ // This function is careless with respect to time, and gets you the channel as
+ // it exists in the specific moment when you call it.
+ pub async fn get(&self, channel: &Id) -> Result<Option<Channel>, sqlx::Error> {
+ let mut tx = self.db.begin().await?;
+ let channel = tx.channels().by_id(channel).await.optional()?;
+ tx.commit().await?;
+
+ Ok(channel.iter().flat_map(History::events).collect())
+ }
+
pub async fn delete(&self, channel: &Id, deleted_at: &DateTime) -> Result<(), Error> {
let mut tx = self.db.begin().await?;
diff --git a/src/db/mod.rs b/src/db/mod.rs
index b9c59ef..36d888f 100644
--- a/src/db/mod.rs
+++ b/src/db/mod.rs
@@ -97,24 +97,33 @@ pub enum Error {
pub trait NotFound {
type Ok;
+ type Error;
+
fn not_found<E, F>(self, map: F) -> Result<Self::Ok, E>
where
- E: From<sqlx::Error>,
+ E: From<Self::Error>,
F: FnOnce() -> E;
+
+ fn optional(self) -> Result<Option<Self::Ok>, Self::Error>;
}
impl<T> NotFound for Result<T, sqlx::Error> {
type Ok = T;
+ type Error = sqlx::Error;
+
+ fn optional(self) -> Result<Option<T>, sqlx::Error> {
+ match self {
+ Ok(value) => Ok(Some(value)),
+ Err(sqlx::Error::RowNotFound) => Ok(None),
+ Err(other) => Err(other),
+ }
+ }
fn not_found<E, F>(self, map: F) -> Result<T, E>
where
E: From<sqlx::Error>,
F: FnOnce() -> E,
{
- match self {
- Err(sqlx::Error::RowNotFound) => Err(map()),
- Err(other) => Err(other.into()),
- Ok(value) => Ok(value),
- }
+ self.optional()?.ok_or_else(map)
}
}
diff --git a/src/ui.rs b/src/ui.rs
index 18dd049..9654195 100644
--- a/src/ui.rs
+++ b/src/ui.rs
@@ -1,24 +1,24 @@
use axum::{
- extract::Path,
+ extract::{Path, State},
http::{header, StatusCode},
- response::{IntoResponse, Response},
+ response::{IntoResponse, Redirect, Response},
routing::get,
Router,
};
use mime_guess::Mime;
use rust_embed::EmbeddedFile;
+use crate::{app::App, channel, error::Internal, login::Login};
+
#[derive(rust_embed::Embed)]
#[folder = "hi-ui/build"]
struct Assets;
-pub fn router<S>() -> Router<S>
-where
- S: Clone + Send + Sync + 'static,
-{
+pub fn router() -> Router<App> {
Router::new()
.route("/*path", get(asset))
.route("/", get(root))
+ .route("/ch/:channel", get(channel))
}
async fn asset(Path(path): Path<String>) -> Result<Asset, NotFound<String>> {
@@ -29,8 +29,24 @@ async fn asset(Path(path): Path<String>) -> Result<Asset, NotFound<String>> {
.ok_or(NotFound(format!("not found: {path}")))
}
-async fn root() -> impl IntoResponse {
- asset(Path(String::from("index.html"))).await
+async fn root() -> Result<Asset, Internal> {
+ // "not found" in this case really is an internal error, as it should
+ // never happen. `index.html` is a known-valid path.
+ Ok(asset(Path(String::from("index.html"))).await?)
+}
+
+async fn channel(
+ State(app): State<App>,
+ login: Option<Login>,
+ Path(channel): Path<channel::Id>,
+) -> Result<impl IntoResponse, Internal> {
+ Ok(if login.is_none() {
+ Redirect::temporary("/").into_response()
+ } else if app.channels().get(&channel).await?.is_none() {
+ NotFound(root().await?).into_response()
+ } else {
+ root().await?.into_response()
+ })
}
struct Asset(Mime, EmbeddedFile);
@@ -47,6 +63,8 @@ impl IntoResponse for Asset {
}
}
+#[derive(Debug, thiserror::Error)]
+#[error("{0}")]
struct NotFound<E>(pub E);
impl<E> IntoResponse for NotFound<E>