mirror of
https://github.com/kjuulh/bitebuds.git
synced 2024-11-08 11:01:47 +01:00
feat: with local store
This commit is contained in:
parent
e16127ecca
commit
762c792c05
64
Cargo.lock
generated
64
Cargo.lock
generated
@ -403,6 +403,15 @@ version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257"
|
||||
|
||||
[[package]]
|
||||
name = "domain"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"serde",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "drain_filter_polyfill"
|
||||
version = "0.1.3"
|
||||
@ -441,6 +450,16 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "eyre"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb"
|
||||
dependencies = [
|
||||
"indenter",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
@ -773,6 +792,12 @@ dependencies = [
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indenter"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.2"
|
||||
@ -1496,9 +1521,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.93"
|
||||
version = "1.0.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76"
|
||||
checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@ -1526,6 +1551,33 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_yaml"
|
||||
version = "0.9.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f82e6c8c047aa50a7328632d067bcae6ef38772a79e28daf32f735e0e4f3dd10"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
"unsafe-libyaml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "services"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"domain",
|
||||
"eyre",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"tokio",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.6"
|
||||
@ -1603,6 +1655,7 @@ dependencies = [
|
||||
"chrono",
|
||||
"console_error_panic_hook",
|
||||
"console_log",
|
||||
"domain",
|
||||
"lazy_static",
|
||||
"leptos",
|
||||
"leptos_axum",
|
||||
@ -1610,6 +1663,7 @@ dependencies = [
|
||||
"leptos_router",
|
||||
"log",
|
||||
"serde",
|
||||
"services",
|
||||
"simple_logger",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
@ -1942,6 +1996,12 @@ version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||
|
||||
[[package]]
|
||||
name = "unsafe-libyaml"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad2024452afd3874bf539695e04af6732ba06517424dbf958fdb16a01f3bef6c"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.3.1"
|
||||
|
@ -1,3 +1,7 @@
|
||||
[workspace]
|
||||
members = [".", "crates/services", "crates/domain"]
|
||||
|
||||
|
||||
[package]
|
||||
name = "ssr_modes_axum"
|
||||
version = "0.1.0"
|
||||
@ -27,6 +31,9 @@ wasm-bindgen = "0.2"
|
||||
chrono = { version = "0.4.23", features = ["serde"] }
|
||||
uuid = { version = "1.3.0", features = ["v4", "wasm-bindgen", "js", "serde"] }
|
||||
|
||||
domain = { path = "crates/domain" }
|
||||
services = { path = "crates/services", optional = true }
|
||||
|
||||
[features]
|
||||
hydrate = ["leptos/hydrate", "leptos_meta/hydrate", "leptos_router/hydrate"]
|
||||
ssr = [
|
||||
@ -38,6 +45,7 @@ ssr = [
|
||||
"leptos_meta/ssr",
|
||||
"leptos_router/ssr",
|
||||
"dep:leptos_axum",
|
||||
"dep:services",
|
||||
]
|
||||
|
||||
[package.metadata.leptos]
|
||||
|
13
articles/events/2023-03-06-gammeldags-oksesteg.md
Normal file
13
articles/events/2023-03-06-gammeldags-oksesteg.md
Normal file
@ -0,0 +1,13 @@
|
||||
---
|
||||
coverImage:
|
||||
url: "https://cdn-rdb.arla.com/Files/arla-dk/2010638351/0606cf14-3972-4abb-b2c8-faa3249de170.jpg?crop=(0,482,0,-117)&w=1269&h=715&mode=crop&ak=6826258c&hm=f35b5bfe"
|
||||
alt: billede af oksesteg
|
||||
name: Gammeldags oksesteg
|
||||
description: |
|
||||
God gammeldags oksesteg med en intens og fyldig brun sauce. Gammeldags oksesteg
|
||||
er rigtig simremad som gør de fleste glade. Så server en gammeldags oksesteg for
|
||||
din gæster... både de unge og de gamle.
|
||||
time: 2023-03-06
|
||||
---
|
||||
|
||||
Some article
|
14
crates/domain/Cargo.toml
Normal file
14
crates/domain/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[package]
|
||||
name = "domain"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
chrono = { version = "0.4.23", features = ["serde"] }
|
||||
serde = { version = "1.0.152", features = ["derive"] }
|
||||
uuid = { version = "1.3.0", features = ["v4", "wasm-bindgen", "js", "serde"] }
|
@ -25,7 +25,7 @@ pub struct Event {
|
||||
pub cover_image: Option<Image>,
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
pub time: chrono::DateTime<chrono::Utc>,
|
||||
pub time: chrono::NaiveDate,
|
||||
pub recipe_id: Option<uuid::Uuid>,
|
||||
pub images: Vec<Image>,
|
||||
pub metadata: Option<Metadata>,
|
||||
@ -37,7 +37,7 @@ pub struct EventOverview {
|
||||
pub cover_image: Option<Image>,
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
pub time: chrono::DateTime<chrono::Utc>,
|
||||
pub time: chrono::NaiveDate,
|
||||
}
|
||||
|
||||
impl From<Event> for EventOverview {
|
2
crates/services/.gitignore
vendored
Normal file
2
crates/services/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/target
|
||||
/Cargo.lock
|
19
crates/services/Cargo.toml
Normal file
19
crates/services/Cargo.toml
Normal file
@ -0,0 +1,19 @@
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[package]
|
||||
name = "services"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
chrono = { version = "0.4.23", features = ["serde"] }
|
||||
domain = { path = "../domain" }
|
||||
eyre = "0.6.8"
|
||||
serde = { version = "1.0.152", features = ["derive"] }
|
||||
serde_json = "1.0.94"
|
||||
serde_yaml = "0.9.19"
|
||||
tokio = { version = "1.26.0", features = ["full"] }
|
||||
uuid = { version = "1.3.0", features = ["v4", "serde"] }
|
143
crates/services/src/lib.rs
Normal file
143
crates/services/src/lib.rs
Normal file
@ -0,0 +1,143 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use domain::{Event, Image, Metadata};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub struct EventStore {
|
||||
pub path: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct RawImage {
|
||||
pub url: String,
|
||||
pub alt: String,
|
||||
pub metadata: Option<Metadata>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct RawEvent {
|
||||
pub cover_image: Option<RawImage>,
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
#[serde(with = "short_time_stamp")]
|
||||
pub time: chrono::NaiveDate,
|
||||
pub recipe_id: Option<uuid::Uuid>,
|
||||
//pub images: Vec<RawImage>,
|
||||
pub metadata: Option<Metadata>,
|
||||
#[serde(skip)]
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
mod short_time_stamp {
|
||||
use chrono::{DateTime, NaiveDate, TimeZone, Utc};
|
||||
use serde::{self, Deserialize, Deserializer, Serializer};
|
||||
|
||||
const FORMAT: &'static str = "%Y-%m-%d";
|
||||
|
||||
pub fn serialize<S>(date: &NaiveDate, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let s = format!("{}", date.format(FORMAT));
|
||||
serializer.serialize_str(&s)
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<NaiveDate, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
NaiveDate::parse_from_str(&s, FORMAT).map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RawEvent> for Event {
|
||||
fn from(value: RawEvent) -> Self {
|
||||
Self {
|
||||
id: uuid::Uuid::new_v4(),
|
||||
cover_image: value.cover_image.map(|ci| ci.into()),
|
||||
name: value.name,
|
||||
description: value.description,
|
||||
time: value.time,
|
||||
recipe_id: value.recipe_id,
|
||||
images: vec![],
|
||||
metadata: value.metadata,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RawImage> for Image {
|
||||
fn from(value: RawImage) -> Self {
|
||||
Self {
|
||||
id: uuid::Uuid::new_v4(),
|
||||
url: value.url,
|
||||
alt: value.alt,
|
||||
metadata: value.metadata,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EventStore {
|
||||
pub fn new(path: PathBuf) -> Self {
|
||||
Self { path }
|
||||
}
|
||||
|
||||
pub async fn get_upcoming_events(&self) -> eyre::Result<Vec<Event>> {
|
||||
let mut event_path = self.path.clone();
|
||||
event_path.push("events");
|
||||
let mut dir = tokio::fs::read_dir(event_path).await?;
|
||||
|
||||
let mut events = vec![];
|
||||
|
||||
while let Ok(Some(entry)) = dir.next_entry().await {
|
||||
let metadata = entry.metadata().await?;
|
||||
if metadata.is_file() {
|
||||
let file = tokio::fs::read(entry.path()).await?;
|
||||
let content = std::str::from_utf8(&file)?;
|
||||
if content.starts_with("---\n") {
|
||||
let after_marker = &content[4..];
|
||||
if let Some(marker_end) = after_marker.find("---\n") {
|
||||
let raw_front_matter = &content[4..marker_end + 4];
|
||||
let mut raw_event: RawEvent = serde_yaml::from_str(raw_front_matter)?;
|
||||
raw_event.content = content[marker_end + 4..].to_string();
|
||||
|
||||
events.push(raw_event.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(events)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for EventStore {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
path: PathBuf::from("articles"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use domain::Event;
|
||||
|
||||
use crate::RawEvent;
|
||||
|
||||
#[test]
|
||||
fn can_parse_event() {
|
||||
let raw = r#"coverImage:
|
||||
url: "https://cdn-rdb.arla.com/Files/arla-dk/2010638351/0606cf14-3972-4abb-b2c8-faa3249de170.jpg?crop=(0,482,0,-117)&w=1269&h=715&mode=crop&ak=6826258c&hm=f35b5bfe"
|
||||
alt: billede af oksesteg
|
||||
name: Gammeldags oksesteg
|
||||
description: |
|
||||
God gammeldags oksesteg med en intens og fyldig brun sauce. Gammeldags oksesteg
|
||||
er rigtig simremad som gør de fleste glade. Så server en gammeldags oksesteg for
|
||||
din gæster... både de unge og de gamle.
|
||||
time: 2023-03-06"#;
|
||||
|
||||
let raw_event: RawEvent = serde_yaml::from_str(raw).unwrap();
|
||||
let _: Event = raw_event.into();
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@ use lazy_static::lazy_static;
|
||||
use leptos::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::models::{Event, EventOverview, Image};
|
||||
use domain::{Event, EventOverview, Image};
|
||||
|
||||
lazy_static! {
|
||||
static ref EVENTS: Vec<Event> = vec![
|
||||
@ -18,7 +18,8 @@ lazy_static! {
|
||||
description: Some("Lorem ipsum dolor sit amet, qui minim labore adipisicing minim sint cillum sint consectetur cupidatat.".into()),
|
||||
time: chrono::Utc::now()
|
||||
.checked_add_days(chrono::Days::new(1))
|
||||
.unwrap(),
|
||||
.unwrap()
|
||||
.date_naive(),
|
||||
recipe_id: None,
|
||||
images: vec![],
|
||||
metadata: None,
|
||||
@ -35,7 +36,8 @@ lazy_static! {
|
||||
description: Some("Lorem ipsum dolor sit amet, officia excepteur ex fugiat reprehenderit enim labore culpa sint ad nisi Lorem pariatur mollit ex esse exercitation amet. Nisi anim cupidatat excepteur officia. Reprehenderit nostrud nostrud ipsum Lorem est aliquip amet voluptate voluptate dolor minim nulla est proident. Nostrud officia pariatur ut officia. Sit irure elit esse ea nulla sunt ex occaecat reprehenderit commodo officia dolor Lorem duis laboris cupidatat officia voluptate. Culpa proident adipisicing id nulla nisi laboris ex in Lorem sunt duis officia eiusmod. Aliqua reprehenderit commodo ex non excepteur duis sunt velit enim. Voluptate laboris sint cupidatat ullamco ut ea consectetur et est culpa et culpa duis.".into()),
|
||||
time: chrono::Utc::now()
|
||||
.checked_add_days(chrono::Days::new(4))
|
||||
.unwrap(),
|
||||
.unwrap()
|
||||
.date_naive(),
|
||||
recipe_id: None,
|
||||
images: vec![],
|
||||
metadata: None,
|
||||
@ -52,7 +54,8 @@ lazy_static! {
|
||||
description: Some("description".into()),
|
||||
time: chrono::Utc::now()
|
||||
.checked_sub_days(chrono::Days::new(2))
|
||||
.unwrap(),
|
||||
.unwrap()
|
||||
.date_naive(),
|
||||
recipe_id: None,
|
||||
images: vec![],
|
||||
metadata: None,
|
||||
@ -71,15 +74,19 @@ pub async fn get_upcoming_events() -> Result<UpcomingEventsOverview, ServerFnErr
|
||||
get_upcoming_events_fn().await
|
||||
}
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
async fn get_upcoming_events_fn() -> Result<UpcomingEventsOverview, ServerFnError> {
|
||||
let current_time = chrono::Utc::now();
|
||||
|
||||
let mut events: Vec<EventOverview> = EVENTS
|
||||
let es = services::EventStore::default();
|
||||
|
||||
let events = es
|
||||
.get_upcoming_events()
|
||||
.await
|
||||
.map_err(|e| ServerFnError::ServerError(e.to_string()))?
|
||||
.iter()
|
||||
.filter(|data| data.time > current_time)
|
||||
.map(|data| data.clone().into())
|
||||
.collect();
|
||||
events.sort_by(|a, b| a.time.cmp(&b.time));
|
||||
|
||||
Ok(UpcomingEventsOverview { events })
|
||||
}
|
||||
@ -91,6 +98,7 @@ pub async fn get_full_event(event_id: uuid::Uuid) -> Result<Option<Event>, Serve
|
||||
get_full_event_fn(event_id).await
|
||||
}
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
async fn get_full_event_fn(event_id: uuid::Uuid) -> Result<Option<Event>, ServerFnError> {
|
||||
let event = EVENTS
|
||||
.iter()
|
||||
|
28
src/app.rs
28
src/app.rs
@ -10,20 +10,20 @@ pub fn App(cx: Scope) -> impl IntoView {
|
||||
provide_meta_context(cx);
|
||||
|
||||
view! { cx,
|
||||
<Stylesheet id="leptos" href="/pkg/ssr_modes.css" />
|
||||
<Title text="Bitebuds" />
|
||||
<Stylesheet id="leptos" href="/pkg/ssr_modes.css" />
|
||||
<Title text="Bitebuds" />
|
||||
|
||||
<Router>
|
||||
<div class="app grid lg:grid-cols-[25%,50%,25%] sm:grid-cols-[10%,80%,10%] grid-cols-[5%,90%,5%]">
|
||||
<main class="main col-start-2">
|
||||
<div class="pt-4">
|
||||
<h1 class="font-semibold text-xl tracking-wide">"Bitebuds"</h1>
|
||||
<Routes>
|
||||
<Route path="" view=|cx| view! { cx, <HomePage /> }/>
|
||||
</Routes>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</Router>
|
||||
<Router>
|
||||
<div class="app grid lg:grid-cols-[25%,50%,25%] sm:grid-cols-[10%,80%,10%] grid-cols-[5%,90%,5%]">
|
||||
<main class="main col-start-2">
|
||||
<div class="pt-4">
|
||||
<h1 class="font-semibold text-xl tracking-wide">"Bitebuds"</h1>
|
||||
<Routes>
|
||||
<Route path="" view=|cx| view! { cx, <HomePage /> }/>
|
||||
</Routes>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</Router>
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use chrono::Datelike;
|
||||
use leptos::*;
|
||||
|
||||
use crate::api::events::*;
|
||||
use crate::models::{EventOverview, Image};
|
||||
use domain::{EventOverview, Image};
|
||||
|
||||
#[component]
|
||||
pub fn Day(
|
||||
|
@ -4,7 +4,6 @@ pub mod api;
|
||||
pub mod app;
|
||||
mod components;
|
||||
pub mod fallback;
|
||||
mod models;
|
||||
mod pages;
|
||||
use cfg_if::cfg_if;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user