Added session system
This commit is contained in:
parent
f1cb209217
commit
5f31e4bf22
35
Cargo.lock
generated
35
Cargo.lock
generated
@ -317,6 +317,29 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum-extra"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0be6ea09c9b96cb5076af0de2e383bd2bc0c18f827cf1967bdd353e0b910d733"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"axum-core",
|
||||
"bytes",
|
||||
"cookie",
|
||||
"futures-util",
|
||||
"http 1.1.0",
|
||||
"http-body 1.0.0",
|
||||
"http-body-util",
|
||||
"mime",
|
||||
"pin-project-lite",
|
||||
"serde",
|
||||
"tower",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum-macros"
|
||||
version = "0.4.1"
|
||||
@ -822,6 +845,17 @@ dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cookie"
|
||||
version = "0.18.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
"time",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.4"
|
||||
@ -5556,6 +5590,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"argon2",
|
||||
"axum",
|
||||
"axum-extra",
|
||||
"dioxus",
|
||||
"dioxus-logger",
|
||||
"once_cell",
|
||||
|
@ -16,6 +16,7 @@ once_cell = "1.19.0"
|
||||
tokio = { version = "1.38.0", features = [ "macros", "rt-multi-thread" ], optional = true }
|
||||
argon2 = { version = "0.5.3", optional = true }
|
||||
axum = { version = "0.7.5", optional = true }
|
||||
axum-extra = { version = "0.9.3", features = [ "cookie" ], optional = true}
|
||||
|
||||
# Debug
|
||||
tracing = "0.1.40"
|
||||
@ -23,5 +24,5 @@ dioxus-logger = "0.5.0"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
server = ["dioxus/axum", "axum", "surrealdb", "tokio", "argon2" ]
|
||||
server = ["dioxus/axum", "axum", "axum-extra", "surrealdb", "tokio", "argon2" ]
|
||||
web = ["dioxus/web"]
|
||||
|
@ -766,15 +766,6 @@ html {
|
||||
color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));
|
||||
}
|
||||
|
||||
.menu li > *:not(ul, .menu-title, details, .btn):active,
|
||||
.menu li > *:not(ul, .menu-title, details, .btn).active,
|
||||
.menu li > details > summary:active {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity)));
|
||||
--tw-text-opacity: 1;
|
||||
color: var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity)));
|
||||
}
|
||||
|
||||
.tab:hover {
|
||||
--tw-text-opacity: 1;
|
||||
}
|
||||
@ -783,6 +774,12 @@ html {
|
||||
--tw-text-opacity: 1;
|
||||
color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity)));
|
||||
}
|
||||
|
||||
.table tr.hover:hover,
|
||||
.table tr.hover:nth-child(even):hover {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)));
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
@ -980,18 +977,6 @@ html {
|
||||
}
|
||||
}
|
||||
|
||||
:where(.menu li:not(.menu-title, .disabled) > *:not(ul, details, .menu-title)):not(.active, .btn):hover, :where(.menu li:not(.menu-title, .disabled) > details > summary:not(.menu-title)):not(.active, .btn):hover {
|
||||
cursor: pointer;
|
||||
outline: 2px solid transparent;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
@supports (color: oklch(0% 0 0)) {
|
||||
:where(.menu li:not(.menu-title, .disabled) > *:not(ul, details, .menu-title)):not(.active, .btn):hover, :where(.menu li:not(.menu-title, .disabled) > details > summary:not(.menu-title)):not(.active, .btn):hover {
|
||||
background-color: var(--fallback-bc,oklch(var(--bc)/0.1));
|
||||
}
|
||||
}
|
||||
|
||||
.tab[disabled],
|
||||
.tab[disabled]:hover {
|
||||
cursor: not-allowed;
|
||||
@ -1066,12 +1051,6 @@ html {
|
||||
margin-inline-end: -1rem;
|
||||
}
|
||||
|
||||
.input-sm[type="number"]::-webkit-inner-spin-button {
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
margin-inline-end: -0px;
|
||||
}
|
||||
|
||||
.link {
|
||||
cursor: pointer;
|
||||
text-decoration-line: underline;
|
||||
@ -1081,59 +1060,6 @@ html {
|
||||
text-decoration-line: none;
|
||||
}
|
||||
|
||||
.menu {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-wrap: wrap;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.menu :where(li ul) {
|
||||
position: relative;
|
||||
white-space: nowrap;
|
||||
margin-inline-start: 1rem;
|
||||
padding-inline-start: 0.5rem;
|
||||
}
|
||||
|
||||
.menu :where(li:not(.menu-title) > *:not(ul, details, .menu-title, .btn)), .menu :where(li:not(.menu-title) > details > summary:not(.menu-title)) {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
align-content: flex-start;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
grid-auto-columns: minmax(auto, max-content) auto max-content;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.menu li.disabled {
|
||||
cursor: not-allowed;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
color: var(--fallback-bc,oklch(var(--bc)/0.3));
|
||||
}
|
||||
|
||||
.menu :where(li > .menu-dropdown:not(.menu-dropdown-show)) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:where(.menu li) {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
flex-direction: column;
|
||||
flex-wrap: wrap;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
:where(.menu li) .badge {
|
||||
justify-self: end;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -1147,11 +1073,6 @@ html {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.navbar-end {
|
||||
width: 50%;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
display: grid;
|
||||
align-items: flex-end;
|
||||
@ -1216,6 +1137,39 @@ input.tab:checked + .tab-content,
|
||||
display: block;
|
||||
}
|
||||
|
||||
.table {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
border-radius: var(--rounded-box, 1rem);
|
||||
text-align: left;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
|
||||
.table :where(.table-pin-rows thead tr) {
|
||||
position: sticky;
|
||||
top: 0px;
|
||||
z-index: 1;
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)));
|
||||
}
|
||||
|
||||
.table :where(.table-pin-rows tfoot tr) {
|
||||
position: sticky;
|
||||
bottom: 0px;
|
||||
z-index: 1;
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)));
|
||||
}
|
||||
|
||||
.table :where(.table-pin-cols tr th) {
|
||||
position: sticky;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)));
|
||||
}
|
||||
|
||||
.btm-nav > * .label {
|
||||
font-size: 1rem;
|
||||
line-height: 1.5rem;
|
||||
@ -1483,88 +1437,6 @@ input.tab:checked + .tab-content,
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
:where(.menu li:empty) {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity)));
|
||||
opacity: 0.1;
|
||||
margin: 0.5rem 1rem;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
.menu :where(li ul):before {
|
||||
position: absolute;
|
||||
bottom: 0.75rem;
|
||||
inset-inline-start: 0px;
|
||||
top: 0.75rem;
|
||||
width: 1px;
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity)));
|
||||
opacity: 0.1;
|
||||
content: "";
|
||||
}
|
||||
|
||||
.menu :where(li:not(.menu-title) > *:not(ul, details, .menu-title, .btn)),
|
||||
.menu :where(li:not(.menu-title) > details > summary:not(.menu-title)) {
|
||||
border-radius: var(--rounded-btn, 0.5rem);
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
text-align: start;
|
||||
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter;
|
||||
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter;
|
||||
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
|
||||
transition-duration: 200ms;
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
:where(.menu li:not(.menu-title, .disabled) > *:not(ul, details, .menu-title)):not(summary, .active, .btn).focus, :where(.menu li:not(.menu-title, .disabled) > *:not(ul, details, .menu-title)):not(summary, .active, .btn):focus, :where(.menu li:not(.menu-title, .disabled) > *:not(ul, details, .menu-title)):is(summary):not(.active, .btn):focus-visible, :where(.menu li:not(.menu-title, .disabled) > details > summary:not(.menu-title)):not(summary, .active, .btn).focus, :where(.menu li:not(.menu-title, .disabled) > details > summary:not(.menu-title)):not(summary, .active, .btn):focus, :where(.menu li:not(.menu-title, .disabled) > details > summary:not(.menu-title)):is(summary):not(.active, .btn):focus-visible {
|
||||
cursor: pointer;
|
||||
background-color: var(--fallback-bc,oklch(var(--bc)/0.1));
|
||||
--tw-text-opacity: 1;
|
||||
color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));
|
||||
outline: 2px solid transparent;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.menu li > *:not(ul, .menu-title, details, .btn):active,
|
||||
.menu li > *:not(ul, .menu-title, details, .btn).active,
|
||||
.menu li > details > summary:active {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity)));
|
||||
--tw-text-opacity: 1;
|
||||
color: var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity)));
|
||||
}
|
||||
|
||||
.menu :where(li > details > summary)::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.menu :where(li > details > summary):after,
|
||||
.menu :where(li > .menu-dropdown-toggle):after {
|
||||
justify-self: end;
|
||||
display: block;
|
||||
margin-top: -0.5rem;
|
||||
height: 0.5rem;
|
||||
width: 0.5rem;
|
||||
transform: rotate(45deg);
|
||||
transition-property: transform, margin-top;
|
||||
transition-duration: 0.3s;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
content: "";
|
||||
transform-origin: 75% 75%;
|
||||
box-shadow: 2px 2px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.menu :where(li > details[open] > summary):after,
|
||||
.menu :where(li > .menu-dropdown-toggle.menu-dropdown-show):after {
|
||||
transform: rotate(225deg);
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.mockup-browser .mockup-browser-toolbar .input {
|
||||
position: relative;
|
||||
margin-left: auto;
|
||||
@ -1798,6 +1670,45 @@ input.tab:checked + .tab-content,
|
||||
color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity)));
|
||||
}
|
||||
|
||||
:is([dir="rtl"] .table) {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.table :where(th, td) {
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
padding-top: 0.75rem;
|
||||
padding-bottom: 0.75rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.table tr.active,
|
||||
.table tr.active:nth-child(even),
|
||||
.table-zebra tbody tr:nth-child(even) {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)));
|
||||
}
|
||||
|
||||
.table :where(thead tr, tbody tr:not(:last-child),tbody tr:first-child:last-child) {
|
||||
border-bottom-width: 1px;
|
||||
--tw-border-opacity: 1;
|
||||
border-bottom-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity)));
|
||||
}
|
||||
|
||||
.table :where(thead, tfoot) {
|
||||
white-space: nowrap;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1rem;
|
||||
font-weight: 700;
|
||||
color: var(--fallback-bc,oklch(var(--bc)/0.6));
|
||||
}
|
||||
|
||||
.table :where(tfoot) {
|
||||
border-top-width: 1px;
|
||||
--tw-border-opacity: 1;
|
||||
border-top-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity)));
|
||||
}
|
||||
|
||||
@keyframes toast-pop {
|
||||
0% {
|
||||
transform: scale(0.9);
|
||||
@ -1810,44 +1721,6 @@ input.tab:checked + .tab-content,
|
||||
}
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
height: 2rem;
|
||||
min-height: 2rem;
|
||||
padding-left: 0.75rem;
|
||||
padding-right: 0.75rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.btn-square:where(.btn-sm) {
|
||||
height: 2rem;
|
||||
width: 2rem;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.btn-circle:where(.btn-sm) {
|
||||
height: 2rem;
|
||||
width: 2rem;
|
||||
border-radius: 9999px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.input-sm {
|
||||
height: 2rem;
|
||||
padding-left: 0.75rem;
|
||||
padding-right: 0.75rem;
|
||||
font-size: 0.875rem;
|
||||
line-height: 2rem;
|
||||
}
|
||||
|
||||
.menu-horizontal {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.menu-horizontal > li:not(.menu-title) > details > ul {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.tabs-md :where(.tab) {
|
||||
height: 2rem;
|
||||
font-size: 0.875rem;
|
||||
@ -1890,27 +1763,6 @@ input.tab:checked + .tab-content,
|
||||
line-height: 1.5rem;
|
||||
}
|
||||
|
||||
.menu-horizontal > li:not(.menu-title) > details > ul {
|
||||
margin-inline-start: 0px;
|
||||
margin-top: 1rem;
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
padding-inline-end: 0.5rem;
|
||||
}
|
||||
|
||||
.menu-horizontal > li > details > ul:before {
|
||||
content: none;
|
||||
}
|
||||
|
||||
:where(.menu-horizontal > li:not(.menu-title) > details > ul) {
|
||||
border-radius: var(--rounded-box, 1rem);
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)));
|
||||
--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
|
||||
--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
||||
}
|
||||
|
||||
.static {
|
||||
position: static;
|
||||
}
|
||||
@ -1928,6 +1780,10 @@ input.tab:checked + .tab-content,
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.table {
|
||||
display: table;
|
||||
}
|
||||
|
||||
.w-full {
|
||||
width: 100%;
|
||||
}
|
||||
@ -1963,16 +1819,16 @@ input.tab:checked + .tab-content,
|
||||
padding-right: 0.25rem;
|
||||
}
|
||||
|
||||
.py-6 {
|
||||
padding-top: 1.5rem;
|
||||
padding-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.py-1 {
|
||||
padding-top: 0.25rem;
|
||||
padding-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.py-6 {
|
||||
padding-top: 1.5rem;
|
||||
padding-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.pt-40 {
|
||||
padding-top: 10rem;
|
||||
}
|
||||
|
@ -7,8 +7,9 @@ use surrealdb::sql::Thing;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct User {
|
||||
email: String,
|
||||
password: String,
|
||||
pub id: String,
|
||||
pub email: String,
|
||||
pub password: Option<String>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
@ -17,10 +18,92 @@ struct Record {
|
||||
id: Thing,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Session {
|
||||
user_id: String,
|
||||
id: String,
|
||||
expires: i64,
|
||||
token: String,
|
||||
}
|
||||
|
||||
impl User {
|
||||
#[cfg(feature = "server")]
|
||||
async fn from_cookie() -> Result<Self, ServerFnError> {
|
||||
use axum_extra::extract::CookieJar;
|
||||
|
||||
let jar: CookieJar = extract().await?;
|
||||
|
||||
let session_token = jar
|
||||
.get("session_token")
|
||||
.ok_or_else(|| ServerFnError::new("Session token cookie is not set"))?;
|
||||
|
||||
let session_token = session_token.value();
|
||||
|
||||
let mut res = DB
|
||||
.query("SELECT type::string(user.id) as id, user.email as email FROM session WHERE token = $session_token")
|
||||
.bind(("session_token", session_token))
|
||||
.await?;
|
||||
|
||||
let user: Option<User> = res.take(0)?;
|
||||
|
||||
match user {
|
||||
Some(u) => {
|
||||
tracing::info!("Authorized session for {}", u.id);
|
||||
Ok(u)
|
||||
}
|
||||
None => Err(ServerFnError::ServerError(
|
||||
"Could not authorize session".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Session {
|
||||
#[cfg(feature = "server")]
|
||||
async fn create(user_id: &String) -> Result<Self, ServerFnError> {
|
||||
// Create a session
|
||||
let mut res = DB
|
||||
.query("CREATE session SET user = type::thing($user_id) RETURN type::string(id) as id, type::string(id) as user_id, time::unix(expires) as expires, token;")
|
||||
.bind(("user_id", user_id))
|
||||
.await?;
|
||||
|
||||
let session: Option<Session> = res.take(0)?;
|
||||
|
||||
match session {
|
||||
Some(s) => {
|
||||
tracing::info!("Created new session for {}", user_id);
|
||||
Ok(s)
|
||||
}
|
||||
None => Err(ServerFnError::ServerError(
|
||||
"Could not generate session".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
fn write(&self) -> Result<(), ServerFnError> {
|
||||
use axum::http::{header, HeaderValue};
|
||||
use axum_extra::extract::cookie::{Cookie, SameSite};
|
||||
|
||||
let mut cookie = Cookie::new("session_token", &self.token);
|
||||
|
||||
cookie.set_same_site(SameSite::Strict);
|
||||
|
||||
server_context()
|
||||
.response_parts_mut()
|
||||
.unwrap()
|
||||
.headers
|
||||
.insert(
|
||||
header::SET_COOKIE,
|
||||
HeaderValue::from_str(&cookie.to_string())?,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[server(Register)]
|
||||
pub async fn register(email: String, password: String) -> Result<String, ServerFnError> {
|
||||
tracing::info!("Creating new user");
|
||||
|
||||
let mut res = DB
|
||||
.query("CREATE user SET email = $email, password = crypto::argon2::generate($password)")
|
||||
.bind(("email", email))
|
||||
@ -32,6 +115,10 @@ pub async fn register(email: String, password: String) -> Result<String, ServerF
|
||||
match user {
|
||||
Some(Record { id }) => {
|
||||
tracing::info!("Created new user ({id})");
|
||||
|
||||
let session = Session::create(&id.to_raw()).await?;
|
||||
session.write()?;
|
||||
|
||||
Ok(id.to_string())
|
||||
}
|
||||
_ => Err(ServerFnError::ServerError("Could not get id".to_string())),
|
||||
@ -40,8 +127,9 @@ pub async fn register(email: String, password: String) -> Result<String, ServerF
|
||||
|
||||
#[server(Signin)]
|
||||
pub async fn signin(email: String, password: String) -> Result<User, ServerFnError> {
|
||||
// Find the user with the correct email and password
|
||||
let mut res = DB
|
||||
.query("SELECT * FROM user WHERE email = $email AND crypto::argon2::compare(password, $password)")
|
||||
.query("SELECT type::string(id) as id, email, password FROM user WHERE email = $email AND crypto::argon2::compare(password, $password)")
|
||||
.bind(("email", email))
|
||||
.bind(("password", password))
|
||||
.await?;
|
||||
@ -51,8 +139,17 @@ pub async fn signin(email: String, password: String) -> Result<User, ServerFnErr
|
||||
match user {
|
||||
Some(u) => {
|
||||
tracing::info!("User ({}) has signed in", u.email);
|
||||
|
||||
let session = Session::create(&u.id).await?;
|
||||
session.write()?;
|
||||
|
||||
Ok(u)
|
||||
}
|
||||
_ => Err(ServerFnError::ServerError("Could not get id".to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
#[server(GetUser)]
|
||||
pub async fn get_current_user() -> Result<User, ServerFnError> {
|
||||
User::from_cookie().await
|
||||
}
|
||||
|
@ -15,16 +15,30 @@ pub async fn connect() -> surrealdb::Result<()> {
|
||||
}
|
||||
|
||||
pub async fn define_schema() -> surrealdb::Result<()> {
|
||||
// Define user table
|
||||
let sql = "
|
||||
DEFINE TABLE user SCHEMAFULL;
|
||||
|
||||
DEFINE FIELD email ON TABLE user TYPE string
|
||||
ASSERT string::is::email($value);
|
||||
ASSERT string::is::email($value) VALUE string::lowercase($value);
|
||||
DEFINE FIELD password ON TABLE user TYPE string;
|
||||
DEFINE INDEX userEmailIndex ON TABLE user COLUMNS email UNIQUE;
|
||||
";
|
||||
|
||||
DB.query(sql).await?;
|
||||
|
||||
// Define session table
|
||||
let sql = "
|
||||
DEFINE TABLE session SCHEMAFULL;
|
||||
|
||||
DEFINE FIELD user ON TABLE session TYPE record;
|
||||
DEFINE FIELD token ON TABLE session TYPE string VALUE rand::string(32);
|
||||
DEFINE FIELD expires ON TABLE session TYPE datetime VALUE time::now() + 1w;
|
||||
|
||||
DEFINE INDEX sessionTokenIndex ON TABLE session COLUMNS token UNIQUE;
|
||||
";
|
||||
|
||||
DB.query(sql).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user