W6W6YBQ6VLEPWLG6GG5VJIJ3LGDISF42HAEC5KV3G5CAMCHIHXAQC
use surrealdb::sql::Uuid;
use ulid::Ulid;
pub fn ulid_to_suuid(ulid: Ulid) -> Uuid {
let uuid: uuid::Uuid = ulid.into();
uuid.into()
}
ulid::Ulid::from_string(&self.id.to_string())
.map(|ulid| ulid.timestamp_ms() % 999999)
.unwrap_or(3033132)
let ulid: Ulid = match Ulid::from_string(&self.id.id.to_raw()) {
Ok(ulid) => ulid,
Err(_) => return DEFAULT_CODE,
};
ulid.timestamp_ms() % MAX_CODE
let users: Vec<User> = db.create("user").content(signup).await?;
dbg!(&users);
assert!(users.len() == 1);
let Some(user) = users.into_iter().next() else {
let id = ulid::Ulid::new();
let user: Option<User> =
db.create(("user", ulid_to_suuid(id))).content::<SignupSafe>(signup.into()).await?;
let Some(user) = user else {
#V2
DEFINE TABLE user SCHEMAFULL;
DEFINE TABLE permission SCHEMAFULL;
DEFINE TABLE granted SCHEMAFULL;
USE NAMESPACE noteboek;
USE DATABASE noteboek;
DEFINE TABLE user SCHEMAFULL;
DEFINE TABLE permission SCHEMAFULL;
DEFINE TABLE granted SCHEMAFULL;
/**/q
q
USE NAMESPACE noteboek;
USE DATABASE noteboek;
DEFINE TABLE user SCHEMAFULL;
DEFINE FIELD email ON TABLE user TYPE string ASSERT string::is::email($value);
DEFINE FIELD username ON TABLE user TYPE string;
DEFINE FIELD code ON TABLE user TYPE int DEFAULT rand::int(0, 9999999);
DEFINE FIELD password ON TABLE user TYPE string;
DEFINE INDEX unique_id ON TABLE user COLUMNS username, code UNIQUE;
DEFINE TABLE permission SCHEMAFULL;
DEFINE FIELD id ON TABLE permission TYPE string;
DEFINE FIELD aspect ON TABLE permission TYPE string;
DEFINE TABLE granted SCHEMAFULL;
DEFINE FIELD in ON TABLE granted TYPE record<user>;
DEFINE FIELD out ON TABLE granted TYPE record<permission>;
DEFINE FIELD granted ON TABLE granted TYPE datetime DEFAULT time::now();
DEFINE INDEX unique_permissions ON TABLE granted COLUMNS in, out UNIQUE;
USE NAMESPACE noteboek;
USE DATABASE noteboek;
DEFINE TABLE user SCHEMAFULL;
DEFINE FIELD email ON TABLE user TYPE string ASSERT string::is::email($value);
DEFINE FIELD username ON TABLE user TYPE string;
USE NAMESPACE noteboek;
USE DATABASE noteboek;
DEFINE TABLE user SCHEMAFULL;
DEFINE FIELD email ON TABLE user TYPE string ASSERT string::is::email($value);
DEFINE FIELD username ON TABLE user TYPE string;
DEFINE FIELD code ON TABLE user TYPE int DEFAULT rand::ulid();
DEFINE FIELD password ON TABLE user TYPE string;
DEFINE INDEX unique_id ON TABLE user COLUMNS username, code UNIQUE;
DEFINE TABLE permission SCHEMAFULL;
DEFINE FIELD id ON TABLE permission TYPE string;
DEFINE FIELD aspect ON TABLE permission TYPE string;
DEFINE TABLE granted SCHEMAFULL;
DEFINE FIELD in ON TABLE granted TYPE record<user>;
DEFINE FIELD out ON TABLE granted TYPE record<permission>;
DEFINE FIELD granted ON TABLE granted TYPE datetime DEFAULT time::now();
DEFINE INDEX unique_permissions ON TABLE granted COLUMNS in, out UNIQUE;
USE NAMESPACE noteboek;
USE DATABASE noteboek;
DEFINE TABLE user SCHEMAFULL;
DEFINE FIELD email ON TABLE user TYPE string ASSERT string::is::email($value);
DEFINE FIELD username ON TABLE user TYPE string;
DEFINE FIELD id ON TABLE user TYPE string DEFAULT rand::ulid();
DEFINE FIELD password ON TABLE user TYPE string;
DEFINE INDEX unique_id ON TABLE user COLUMNS username, id UNIQUE;
DEFINE TABLE permission SCHEMAFULL;
DEFINE FIELD id ON TABLE permission TYPE string;
DEFINE FIELD aspect ON TABLE permission TYPE string;
DEFINE TABLE granted SCHEMAFULL;
DEFINE FIELD in ON TABLE granted TYPE record<user>;
DEFINE FIELD out ON TABLE granted TYPE record<permission>;
DEFINE FIELD granted ON TABLE granted TYPE datetime DEFAULT time::now();
DEFINE INDEX unique_permissions ON TABLE granted COLUMNS in, out UNIQUE;
USE NAMESPACE noteboek;
USE DATABASE noteboek;
DEFINE TABLE user SCHEMAFULL;
DEFINE FIELD email ON TABLE user TYPE string ASSERT string::is::email($value);
DEFINE FIELD username ON TABLE user TYPE string;
DEFINE FIELD id ON TABLE user TYPE string DEFAULT rand::ulid();
DEFINE FIELD password ON TABLE user TYPE string;
DEFINE INDEX unique_id ON TABLE user COLUMNS username, id UNIQUE;
DEFINE TABLE permission SCHEMAFULL;
DEFINE FIELD id ON TABLE permission TYPE string;
DEFINE FIELD aspect ON TABLE permission TYPE string;
DEFINE TABLE granted SCHEMAFULL;
DEFINE FIELD in ON TABLE granted TYPE record<user>;
DEFINE FIELD out ON TABLE granted TYPE record<permission>;
DEFINE FIELD granted ON TABLE granted TYPE datetime DEFAULT time::now();
DEFINE INDEX unique_permissions ON TABLE granted COLUMNS in, out UNIQUE;
USE NAMESPACE noteboek;
USE DATABASE noteboek;
DEFINE TABLE user SCHEMAFULL;
DEFINE FIELD email ON TABLE user TYPE string ASSERT string::is::email($value);
DEFINE FIELD username ON TABLE user TYPE string;
DEFINE FIELD id ON TABLE user TYPE string DEFAULT rand::ulid();
DEFINE FIELD password ON TABLE user TYPE string;
DEFINE INDEX unique_id ON TABLE user COLUMNS username, id UNIQUE;
DEFINE TABLE permission SCHEMAFULL;
DEFINE FIELD id ON TABLE permission TYPE string;
DEFINE FIELD aspect ON TABLE permission TYPE string;
DEFINE TABLE granted SCHEMAFULL;
DEFINE FIELD in ON TABLE granted TYPE record<user>;
DEFINE FIELD out ON TABLE granted TYPE record<permission>;
DEFINE FIELD granted ON TABLE granted TYPE datetime DEFAULT time::now();
DEFINE INDEX unique_permissions ON TABLE granted COLUMNS in, out UNIQUE;
INFO FOR TABLE user
DROP
DELETE user.code;
REMOVE FIELD code ON user;
INFO FOR TABLE user
history.txt
async fn create_new_user(
signup: &Signup,
) -> Result<std::collections::HashMap<String, String>, String> {
Ok(gloo_net::http::Request::post("/api/v1/signup")
.json(signup)
.map_err(|e| format!("JSON creation error: {e}"))?
.send()
.await
.map_err(|e| format!("Send error: {e}"))?
.json()
.await
.map_err(|e| format!("JSON read error: {e}"))?)
async fn create_new_user(signup: &Signup) -> Result<gloo_net::http::Response, gloo_net::Error> {
Ok(gloo_net::http::Request::post("/api/v1/signup").json(signup)?.send().await?)
let ret = create_new_user(&signup).await;
if ret.is_ok() {
user_status.refetch()
}
ret
let resp = create_new_user(&signup).await.map_err(|err| error::ApiError {
code: error::ErrorCode::JsSendError,
message: err.to_string(),
})?;
let resp_parsed: Result<(), error::ApiError> = resp.json().await.map_err(|err| {
error::ApiError { code: error::ErrorCode::JsReadError, message: err.to_string() }
})?;
resp_parsed
mview! {
[user_status.with(|user| match user {
Some(Ok(status)) => match status {
UserStatus::Anonymous => "not logged in".to_string(),
UserStatus::LoggedIn { username, code } => format!("logged in as {username}#{code}")
}.into_view(),
Some(Err(e)) =>{
let e = format!("{}", e);
mview! {
div class="text-error bg-slate-500" { [e.clone()] }
}.into_view()
},
None => "Loading...".into_view()
})]
Form action="/api/v1/login" method="POST" {
input type="email" name="email";
input type="password" name="password";
button type="submit" {
"Login"
let login_view = move || {
if user_status.with(|user| {
user.as_ref().is_some_and(|status| {
status.as_ref().is_ok_and(|status| status == &UserStatus::Anonymous)
})
}) {
mview! {
Form action="/api/v1/login" method="POST" class="join join-vertical my-2" {
label for="email" class="label" {
span class="label-text" { "Email" }
}
input type="email" id="email" name="email" class="input input-bordered w-full max-w-xs";
label for="password" class="label" {
span class="label-text" { "Password" }
}
input type="password" id="password" name="password" class="input input-bordered w-full max-w-xs";
button type="submit" on:submit={move |_| user_status.refetch()} {
"Login"
}
h2 { "Signup" }
Form action="" method="POST" {
input type="username" name="username" prop:value=[username.get()] on:input={move |ev| set_username.set(event_target_value(&ev))};
input type="email" name="email" prop:value=[email.get()] on:input={move |ev| set_email.set(event_target_value(&ev))};
input type="password" name="password" prop:value=[password.get()] on:input={move |ev| set_password.set(event_target_value(&ev))};
button type="submit" on:click={move |ev| {
ev.prevent_default();
signup.dispatch((username.get(), email.get(), password.get()));
}} {
"Login"
.into_view()
} else {
mview! {}.into_view()
}
};
let signup_view = move || {
mview! {
Form action="" method="POST" {
input type="username" name="username" prop:value=[username.get()] on:input={move |ev| set_username.set(event_target_value(&ev))};
input type="email" name="email" prop:value=[email.get()] on:input={move |ev| set_email.set(event_target_value(&ev))};
input type="password" name="password" prop:value=[password.get()] on:input={move |ev| set_password.set(event_target_value(&ev))};
button type="submit" on:click={move |ev| {
ev.prevent_default();
signup.dispatch((username.get(), email.get(), password.get()));
}} {
"Register"
}
p { f["{:?}", signup.value().get()] }
}.into_view()
};
mview! {
div class="text-center" {
[user_status.with(|user| match user {
Some(Ok(status)) => match status {
UserStatus::Anonymous => "not logged in".to_string(),
UserStatus::LoggedIn { username, code } => format!("logged in as {username}#{code}")
}.into_view(),
Some(Err(e)) =>{
let e = format!("{}", e);
mview! {
div class="text-error bg-slate-500" { [e.clone()] }
}.into_view()
},
None => "Loading...".into_view()
})]
use core::fmt;
use serde::Deserialize;
#[derive(Deserialize, Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
#[serde(rename_all = "kebab-case")]
pub(crate) enum ErrorCode {
// Frontend specific
JsSendError,
JsReadError,
// Backend
InvalidCredentials,
}
impl fmt::Display for ErrorCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::JsSendError => write!(f, "error sending json"),
Self::JsReadError => write!(f, "error reading json"),
Self::InvalidCredentials => write!(f, "invalid credentials"),
}
}
}
#[derive(Deserialize, Clone, Debug)]
pub(crate) struct ApiError {
pub(crate) code: ErrorCode,
pub(crate) message: String,
}
impl fmt::Display for ApiError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {}", self.code, self.message)
}
}