implement basic KV binding
This commit is contained in:
parent
0547cdd0c3
commit
243a64b7b9
|
@ -13,6 +13,7 @@ default = ["console_error_panic_hook", "wee_alloc"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cfg-if = "0.1.2"
|
cfg-if = "0.1.2"
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
|
js-sys = "0.3"
|
||||||
serde = { version = "1.0", features = [ "derive" ] }
|
serde = { version = "1.0", features = [ "derive" ] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
wasm-bindgen = "0.2"
|
wasm-bindgen = "0.2"
|
||||||
|
|
45
src/blog.rs
Normal file
45
src/blog.rs
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
// Utility functions and structs for the blogging system
|
||||||
|
// Due to limitations of the Cloudflare Workers KV, we do not
|
||||||
|
// store the entire state in one record; instead, different
|
||||||
|
// parts are stroed in different records. This also increases
|
||||||
|
// efficiency, since the program won't need to load anything
|
||||||
|
// unnecessary from KV.
|
||||||
|
use crate::store;
|
||||||
|
use crate::utils::*;
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
use std::vec::Vec;
|
||||||
|
|
||||||
|
// A list of the UUIDs of all published blog posts
|
||||||
|
// This should be SORTED with the newest posts at lower indices (closer to 0)
|
||||||
|
// The user may edit this via KV UI to change ordering and such
|
||||||
|
// by default new posts are always added to the top
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct PostsList(pub Vec<String>);
|
||||||
|
|
||||||
|
impl PostsList {
|
||||||
|
pub async fn load() -> PostsList {
|
||||||
|
match store::get_obj("posts_list").await {
|
||||||
|
Ok(v) => PostsList(v),
|
||||||
|
// Don't panic on empty
|
||||||
|
// TODO: What if the user messed up when editing?
|
||||||
|
// That would cause a decode failure and all data will be gone
|
||||||
|
// if a new post is added or updated (overwriting the KV value)
|
||||||
|
// under this logic
|
||||||
|
// (if no new post is added then nothing bad would happen;
|
||||||
|
// the user would probably notice when trying to visit the blog home page)
|
||||||
|
Err(_) => PostsList(vec![])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_post(&self, uuid: &str) -> bool {
|
||||||
|
self.0.contains(&uuid.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a post to the list and then update the record in KV
|
||||||
|
// Also consumes self, as this should normally be the last action
|
||||||
|
// in an API call
|
||||||
|
pub async fn add_post(mut self, uuid: &str) -> MyResult<()> {
|
||||||
|
self.0.insert(0, uuid.into());
|
||||||
|
store::put_obj_pretty("posts_list", self.0).await
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,8 @@ extern crate lazy_static;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod utils;
|
mod utils;
|
||||||
mod router;
|
mod router;
|
||||||
|
mod store;
|
||||||
|
mod blog;
|
||||||
mod sn;
|
mod sn;
|
||||||
|
|
||||||
use cfg_if::cfg_if;
|
use cfg_if::cfg_if;
|
||||||
|
|
13
src/sn.rs
13
src/sn.rs
|
@ -1,5 +1,5 @@
|
||||||
// Interface for Standard Notes (Actions)
|
// Interface for Standard Notes (Actions)
|
||||||
use crate::CONFIG;
|
use crate::{CONFIG, blog};
|
||||||
use crate::router::Router;
|
use crate::router::Router;
|
||||||
use crate::utils::*;
|
use crate::utils::*;
|
||||||
use serde::{Serialize, Serializer};
|
use serde::{Serialize, Serializer};
|
||||||
|
@ -28,8 +28,17 @@ async fn get_actions(_req: Request, url: Url) -> MyResult<Response> {
|
||||||
let origin = url.origin();
|
let origin = url.origin();
|
||||||
let mut actions = vec![];
|
let mut actions = vec![];
|
||||||
|
|
||||||
|
// Show different options depending on whether the post already exists
|
||||||
|
let post_exists = match params.get("item_uuid") {
|
||||||
|
Some(uuid) => {
|
||||||
|
let posts = blog::PostsList::load().await;
|
||||||
|
posts.has_post(&uuid)
|
||||||
|
},
|
||||||
|
None => false
|
||||||
|
};
|
||||||
|
|
||||||
actions.push(Action {
|
actions.push(Action {
|
||||||
label: "Publish".into(),
|
label: if post_exists { "Update".into() } else { "Publish".into() },
|
||||||
url: format!("{}/post?secret={}", origin, CONFIG.secret.clone()),
|
url: format!("{}/post?secret={}", origin, CONFIG.secret.clone()),
|
||||||
verb: Verb::Post,
|
verb: Verb::Post,
|
||||||
context: Context::Item,
|
context: Context::Item,
|
||||||
|
|
39
src/store.rs
Normal file
39
src/store.rs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
// Bindings to Cloudflare Workers KV
|
||||||
|
use crate::utils::*;
|
||||||
|
use js_sys::Promise;
|
||||||
|
use serde::Serialize;
|
||||||
|
use serde::de::DeserializeOwned;
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
use wasm_bindgen_futures::JsFuture;
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
extern "C" {
|
||||||
|
#[wasm_bindgen(js_namespace = PAPRIKA, js_name = "get")]
|
||||||
|
fn kv_get(key: &str) -> Promise;
|
||||||
|
#[wasm_bindgen(js_namespace = PAPRIKA, js_name = "put")]
|
||||||
|
fn kv_put_str(key: &str, value: &str) -> Promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_str(key: &str) -> MyResult<String> {
|
||||||
|
Ok(JsFuture::from(kv_get(key)).await.internal_err()?.as_string().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_obj<T: DeserializeOwned>(key: &str) -> MyResult<T> {
|
||||||
|
let res = get_str(key).await?;
|
||||||
|
Ok(serde_json::from_str(&res).internal_err()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn put_str(key: &str, value: &str) -> MyResult<()> {
|
||||||
|
JsFuture::from(kv_put_str(key, value)).await.internal_err()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn put_obj<T: Serialize>(key: &str, value: T) -> MyResult<()> {
|
||||||
|
put_str(key, &serde_json::to_string(&value).internal_err()?).await
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some objects may be available for manual editing; thus making it pretty may be helpful
|
||||||
|
// For example, the user may want to manually edit the order in which posts appear
|
||||||
|
pub async fn put_obj_pretty<T: Serialize>(key: &str, value: T) -> MyResult<()> {
|
||||||
|
put_str(key, &serde_json::to_string_pretty(&value).internal_err()?).await
|
||||||
|
}
|
Loading…
Reference in a new issue