feat: add admin frontend project
This commit is contained in:
parent
9918a6fe43
commit
bd83127519
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,21 @@
|
||||||
|
[package]
|
||||||
|
name = "admin_frontend"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1.0.75"
|
||||||
|
axum = "0.6.20"
|
||||||
|
leptos = "0.5.0"
|
||||||
|
leptos_axum = "0.5.0"
|
||||||
|
leptos_router = "0.5.0"
|
||||||
|
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"
|
||||||
|
|
||||||
|
[package.metadata.leptos]
|
||||||
|
# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name
|
||||||
|
output-name = "admin_frontend"
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
FROM lukemathwalker/cargo-chef:latest-rust-1.72.1 as chef
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
RUN apt update && apt install lld clang -y
|
||||||
|
|
||||||
|
FROM chef as planner
|
||||||
|
COPY . .
|
||||||
|
# Compute a lock-like file for our project
|
||||||
|
RUN cargo chef prepare --recipe-path recipe.json
|
||||||
|
|
||||||
|
FROM chef as builder
|
||||||
|
COPY --from=planner /app/recipe.json recipe.json
|
||||||
|
# Build our project dependencies
|
||||||
|
RUN cargo chef cook --release --recipe-path recipe.json
|
||||||
|
COPY . .
|
||||||
|
# Build the project
|
||||||
|
RUN cargo build --release --bin admin_frontend
|
||||||
|
|
||||||
|
FROM debian AS runtime
|
||||||
|
WORKDIR /app
|
||||||
|
RUN apt-get update -y \
|
||||||
|
&& apt-get install -y --no-install-recommends libc6 \
|
||||||
|
# Clean up
|
||||||
|
&& apt-get autoremove -y \
|
||||||
|
&& apt-get clean -y \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY --from=builder /app/target/release/admin_frontend /usr/local/bin/admin_frontend
|
||||||
|
COPY Cargo.toml /app/Cargo.toml
|
||||||
|
ENV RUST_BACKTRACE 1
|
||||||
|
CMD ["admin_frontend"]
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Admin Frontend
|
||||||
|
- Start the whole stack: `docker compose up -d`
|
||||||
|
- Go to [web server](localhost)
|
||||||
|
- Quick rebuild only frontend after editing `docker compose up -d --no-deps --build admin_frontend`
|
||||||
|
- You might need to add `--force-recreate` for non build changes to take effect
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
use axum::{routing::post, Router};
|
||||||
|
|
||||||
|
pub fn api_router() -> Router {
|
||||||
|
Router::new().route("/test", post(test_handler))
|
||||||
|
}
|
||||||
|
pub async fn test_handler() -> String {
|
||||||
|
"Test from api".to_string()
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
use leptos::{component, view, IntoView};
|
||||||
|
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>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>Admin Login</h1>
|
||||||
|
|
||||||
|
<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-post="/token?grant_type=password"
|
||||||
|
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>
|
||||||
|
|
||||||
|
<p id="content">Start</p>
|
||||||
|
<template id="foo"></template>
|
||||||
|
|
||||||
|
<nav>
|
||||||
|
<h2>"Navigation"</h2>
|
||||||
|
<a href="/">"/home"</a>
|
||||||
|
</nav>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
mod api;
|
||||||
|
mod app;
|
||||||
|
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
use leptos::LeptosOptions;
|
||||||
|
use leptos::{component, get_configuration, view, IntoView};
|
||||||
|
use leptos_axum::generate_route_list;
|
||||||
|
use leptos_axum::LeptosRoutes;
|
||||||
|
use tower::util::ServiceExt;
|
||||||
|
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 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> }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn file_and_error_handler(
|
||||||
|
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();
|
||||||
|
|
||||||
|
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}"),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ services:
|
||||||
depends_on:
|
depends_on:
|
||||||
- appflowy_cloud
|
- appflowy_cloud
|
||||||
- gotrue
|
- gotrue
|
||||||
|
- admin_frontend
|
||||||
volumes:
|
volumes:
|
||||||
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
|
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
|
||||||
- ./nginx/ssl/certificate.crt:/etc/nginx/ssl/certificate.crt
|
- ./nginx/ssl/certificate.crt:/etc/nginx/ssl/certificate.crt
|
||||||
|
|
@ -92,3 +93,13 @@ services:
|
||||||
- gotrue
|
- gotrue
|
||||||
ports:
|
ports:
|
||||||
- 8000:8000
|
- 8000:8000
|
||||||
|
|
||||||
|
admin_frontend:
|
||||||
|
restart: on-failure
|
||||||
|
build:
|
||||||
|
context: ./admin_frontend
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
depends_on:
|
||||||
|
- gotrue
|
||||||
|
ports:
|
||||||
|
- 3000:3000
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ http {
|
||||||
listen 443 ssl;
|
listen 443 ssl;
|
||||||
|
|
||||||
# GoTrue
|
# GoTrue
|
||||||
location ~ ^/(verify|authorize|callback|settings|user) {
|
location ~ ^/(verify|authorize|callback|settings|user|token|admin) {
|
||||||
proxy_pass http://gotrue:9999;
|
proxy_pass http://gotrue:9999;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -34,9 +34,14 @@ http {
|
||||||
}
|
}
|
||||||
|
|
||||||
# AppFlowy-Cloud
|
# AppFlowy-Cloud
|
||||||
location / {
|
location /api {
|
||||||
proxy_pass http://appflowy_cloud:8000;
|
proxy_pass http://appflowy_cloud:8000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Admin Frontend
|
||||||
|
location / {
|
||||||
|
proxy_pass http://admin_frontend:3000;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue