feat: session token WIP

This commit is contained in:
Fu Zi Xiang 2023-10-09 17:07:16 +08:00
parent c71665e57b
commit 827b79e15c
No known key found for this signature in database
7 changed files with 1276 additions and 143 deletions

1092
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -79,6 +79,7 @@ gotrue-entity = { path = "libs/gotrue-entity" }
infra = { path = "libs/infra" }
shared_entity = { path = "libs/shared-entity", features = ["cloud"] }
itertools = "0.11"
axum_session = "0.7.0"
[dev-dependencies]
@ -109,7 +110,8 @@ members = [
"libs/infra",
"libs/shared-entity",
"libs/gotrue",
"libs/gotrue-entity"
"libs/gotrue-entity",
"admin_frontend",
]
[profile.release]

View File

@ -15,6 +15,7 @@ tokio = { version = "1.32.0", features = ["rt-multi-thread", "macros"] }
tower = "0.4.13"
tower-http = { version = "0.4.4", features = ["fs"]}
tracing = "0.1.37"
serde = { version = "1.0.188", features = ["derive"] }
[package.metadata.leptos]
# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name

View File

@ -0,0 +1,27 @@
CREATE TABLE IF NOT EXISTS users (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
password TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS user_permissions (
user_id INTEGER NOT NULL,
token TEXT NOT NULL
);
-- INSERT INTO users (id, anonymous, username, password)
-- SELECT 0, true, 'Guest', ''
-- ON CONFLICT(id) DO UPDATE SET
-- anonymous = EXCLUDED.anonymous,
-- username = EXCLUDED.username;
CREATE TABLE IF NOT EXISTS todos (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
title TEXT NOT NULL,
completed BOOLEAN,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
-- FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE
);

View File

@ -1,8 +1,16 @@
use axum::{routing::post, Router};
use leptos::{server, ServerFnError};
pub fn api_router() -> Router {
Router::new().route("/test", post(test_handler))
Router::new().route("/test", post(test_handler))
}
pub async fn test_handler() -> String {
"Test from api".to_string()
"Test from api".to_string()
}
#[server(Foo)]
pub async fn foo() -> Result<(), ServerFnError> {
println!("Foo!");
Ok(())
}

View File

@ -1,100 +1,129 @@
use leptos::{component, view, IntoView};
use crate::api::{foo, Foo};
use leptos::{component, create_resource, create_server_action, provide_context, view, IntoView};
use leptos_axum::handle_server_fns_with_context;
use leptos_router::{Route, Router, Routes};
#[component]
pub fn App() -> impl IntoView {
view! {
<Router>
<Routes>
<Route path="/" view=Home/>
<Route path="/admin" view=Home/>
<Route path="/admin/settings" view=Settings/>
<Route path="/admin/users" view=Users/>
</Routes>
</Router>
}
view! {
<Router>
<Routes>
<Route path="/" view=Home/>
<Route path="/admin" view=Home/>
<Route path="/admin/settings" view=Settings/>
<Route path="/admin/users" view=Users/>
</Routes>
</Router>
}
}
#[component]
pub fn Users() -> impl IntoView {
view! {
<script
src="https://unpkg.com/htmx.org@1.9.6"
integrity="sha384-FhXw7b6AlE/jyjlZH5iHa/tTe9EpJ1Y55RjcgPbjeWMskSxZt1v9qkxLJWNJaGni"
crossorigin="anonymous"
></script>
}
view! {
<script
src="https://unpkg.com/htmx.org@1.9.6"
integrity="sha384-FhXw7b6AlE/jyjlZH5iHa/tTe9EpJ1Y55RjcgPbjeWMskSxZt1v9qkxLJWNJaGni"
crossorigin="anonymous"
></script>
}
}
#[component]
pub fn Home() -> impl IntoView {
view! {
<script
src="https://unpkg.com/htmx.org@1.9.6"
integrity="sha384-FhXw7b6AlE/jyjlZH5iHa/tTe9EpJ1Y55RjcgPbjeWMskSxZt1v9qkxLJWNJaGni"
crossorigin="anonymous"
></script>
// let a = tokio::spawn(async move {
// println!("Hello from tokio!");
// tokio::time::sleep(std::time::Duration::from_secs(1)).await;
// println!("Hello from tokio again!");
// });
// let b = tokio::runtime::Runtime::new().unwrap();
// b.block_on(a).unwrap();
<body>
<h1>Admin Login</h1>
// let action = create_server_action::<Foo>();
// // create_resource(source, fetcher)
// let bar = create_resource(
// move || {
// (
// // action.version().get(),
// // action.version().post(),
// // action.version().put(),
// // action.version().delete(),
// )
// },
// move |_| foo(),
// );
// println!("bar: {:?}", bar);
// handle_server_fns_with_context(path, headers, raw_query, additional_context, req)
<form>
<table>
<tr>
<td>
<label for="email">Email:</label>
</td>
<td>
<input type="text" id="email" name="email" required/>
</td>
view! {
<script
src="https://unpkg.com/htmx.org@1.9.6"
integrity="sha384-FhXw7b6AlE/jyjlZH5iHa/tTe9EpJ1Y55RjcgPbjeWMskSxZt1v9qkxLJWNJaGni"
crossorigin="anonymous"
></script>
</tr>
<tr>
<td>
<label for="password">Password:</label>
</td>
<td>
<input type="password" id="password" name="password" required/>
</td>
<body>
<h1>Admin Login</h1>
</tr>
</table>
<button hx-post="/token?grant_type=password"
hx-target="#response">
Submit
</button>
</form>
<div id="response"></div>
</body>
}
let todos = create_resource(
move || (add_todo.version().get(), delete_todo.version().get()),
move |_| get_todos(),
);
<form>
<table>
<tr>
<td>
<label for="email">Email:</label>
</td>
<td>
<input type="text" id="email" name="email" required/>
</td>
</tr>
<tr>
<td>
<label for="password">Password:</label>
</td>
<td>
<input type="password" id="password" name="password" required/>
</td>
</tr>
</table>
<button hx-get="/settings" hx-target="#response">
Submit
</button>
</form>
<div id="response"></div>
</body>
}
}
#[component]
pub fn Settings() -> impl IntoView {
view! {
<script
src="https://unpkg.com/htmx.org@1.9.6"
integrity="sha384-FhXw7b6AlE/jyjlZH5iHa/tTe9EpJ1Y55RjcgPbjeWMskSxZt1v9qkxLJWNJaGni"
crossorigin="anonymous"
></script>
<h1>Settings Page</h1>
<button
hx-post="https://test.appflowy.cloud/settings"
hx-trigger="click"
hx-swap="innerHTML"
hx-target="#content"
mustache-template="foo"
>
>
Click Me!
</button>
view! {
<script
src="https://unpkg.com/htmx.org@1.9.6"
integrity="sha384-FhXw7b6AlE/jyjlZH5iHa/tTe9EpJ1Y55RjcgPbjeWMskSxZt1v9qkxLJWNJaGni"
crossorigin="anonymous"
></script>
<h1>Settings Page</h1>
<button
hx-post="https://test.appflowy.cloud/settings"
hx-trigger="click"
hx-swap="innerHTML"
hx-target="#content"
mustache-template="foo"
>
>
Click Me!
</button>
<p id="content">Start</p>
<template id="foo"></template>
<p id="content">Start</p>
<template id="foo"></template>
<nav>
<h2>"Navigation"</h2>
<a href="/">"/home"</a>
</nav>
}
<nav>
<h2>"Navigation"</h2>
<a href="/">"/home"</a>
</nav>
}
}

View File

@ -5,12 +5,16 @@ use app::App;
use axum::response::Response as AxumResponse;
use axum::Router;
use axum::{
body::{boxed, Body, BoxBody},
extract::State,
http::{Request, Response, StatusCode, Uri},
response::IntoResponse,
body::{boxed, Body, BoxBody},
extract::State,
http::{Request, Response, StatusCode, Uri},
response::IntoResponse,
};
// use axum_session::{SessionConfig, SessionLayer, SessionStore};
// use axum_session_auth::{AuthConfig, AuthSessionLayer, SessionSqlitePool};
use leptos::LeptosOptions;
// use sqlx::{sqlite::SqlitePoolOptions, SqlitePool};
use leptos::{component, get_configuration, view, IntoView};
use leptos_axum::generate_route_list;
use leptos_axum::LeptosRoutes;
@ -19,53 +23,69 @@ use tower_http::services::ServeDir;
#[tokio::main]
async fn main() {
let conf = get_configuration(Some("Cargo.toml")).await.unwrap();
let leptos_options = conf.leptos_options;
let routes = generate_route_list(App);
let conf = get_configuration(Some("Cargo.toml")).await.unwrap();
let leptos_options = conf.leptos_options;
let routes = generate_route_list(App);
let app = Router::new()
.nest_service("/api", api::api_router())
.leptos_routes(&leptos_options, routes, App)
.fallback(file_and_error_handler)
.with_state(leptos_options);
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
// let pool = SqlitePoolOptions::new()
// .connect("sqlite:Todos.db")
// .await
// .expect("Could not make pool.");
// let session_config = SessionConfig::default().with_table_name("axum_sessions");
// let auth_config = AuthConfig::<i64>::default();
// let session_store =
// SessionStore::<SessionSqlitePool>::new(Some(pool.clone().into()), session_config);
// session_store.initiate().await.unwrap();
// sqlx::migrate!()
// .run(&pool)
// .await
// .expect("could not run SQLx migrations");
let app = Router::new()
.nest_service("/api", api::api_router())
.leptos_routes(&leptos_options, routes, App)
.fallback(file_and_error_handler)
.with_state(leptos_options);
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}
#[component]
pub fn NotFound() -> impl IntoView {
view! { <h1>Not Found</h1> }
view! { <h1>Not Found</h1> }
}
pub async fn file_and_error_handler(
uri: Uri,
State(options): State<LeptosOptions>,
req: Request<Body>,
uri: Uri,
State(options): State<LeptosOptions>,
req: Request<Body>,
) -> AxumResponse {
let root = options.site_root.clone();
let res = get_static_file(uri.clone(), &root).await.unwrap();
let root = options.site_root.clone();
let res = get_static_file(uri.clone(), &root).await.unwrap();
if res.status() == StatusCode::OK {
res.into_response()
} else {
let handler = leptos_axum::render_app_to_stream(options.to_owned(), NotFound);
let resp = handler(req).await.into_response();
resp
}
if res.status() == StatusCode::OK {
res.into_response()
} else {
let handler = leptos_axum::render_app_to_stream(options.to_owned(), NotFound);
let resp = handler(req).await.into_response();
resp
}
}
async fn get_static_file(uri: Uri, root: &str) -> Result<Response<BoxBody>, (StatusCode, String)> {
let req = Request::builder()
.uri(uri.clone())
.body(Body::empty())
.unwrap();
match ServeDir::new(root).oneshot(req).await {
Ok(res) => Ok(res.map(boxed)),
Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {err}"),
)),
}
let req = Request::builder()
.uri(uri.clone())
.body(Body::empty())
.unwrap();
match ServeDir::new(root).oneshot(req).await {
Ok(res) => Ok(res.map(boxed)),
Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {err}"),
)),
}
}