Merge pull request 'setup frontend' (#5) from feature/frontend-setup into master
Reviewed-on: #5
This commit was merged in pull request #5.
This commit is contained in:
88
.github/workflows/test.yml
vendored
88
.github/workflows/test.yml
vendored
@@ -44,6 +44,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
test:
|
test:
|
||||||
|
needs: frontend-build
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
@@ -54,10 +55,19 @@ jobs:
|
|||||||
- name: Setup Rust, checkout and restore caches
|
- name: Setup Rust, checkout and restore caches
|
||||||
uses: ./.github/actions/setup-rust
|
uses: ./.github/actions/setup-rust
|
||||||
|
|
||||||
|
- name: Restore frontend build cache
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: apps/frontend/build
|
||||||
|
key: frontend-build-${{ runner.os }}-run-${{ github.run_id }}
|
||||||
|
restore-keys: |
|
||||||
|
frontend-build-${{ runner.os }}-
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: cargo test --all-features
|
run: cargo test --all-features
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
|
needs: frontend-build
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
@@ -70,8 +80,84 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
components: clippy, rustfmt
|
components: clippy, rustfmt
|
||||||
|
|
||||||
|
- name: Restore frontend build cache
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: apps/frontend/build
|
||||||
|
key: frontend-build-${{ runner.os }}-run-${{ github.run_id }}
|
||||||
|
restore-keys: |
|
||||||
|
frontend-build-${{ runner.os }}-
|
||||||
|
|
||||||
- name: Run clippy
|
- name: Run clippy
|
||||||
run: cargo clippy --all-features -- -D warnings
|
run: cargo clippy --all-features -- -D warnings
|
||||||
|
|
||||||
- name: Check code formatting
|
- name: Check code formatting
|
||||||
run: cargo fmt --all -- --check
|
run: cargo fmt --all -- --check
|
||||||
|
|
||||||
|
|
||||||
|
test-frontend:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
name: Install pnpm
|
||||||
|
with:
|
||||||
|
version: 10
|
||||||
|
run_install: false
|
||||||
|
|
||||||
|
- name: Install Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 22
|
||||||
|
cache: 'pnpm'
|
||||||
|
cache-dependency-path: apps/frontend/pnpm-lock.yaml
|
||||||
|
|
||||||
|
- name: Install frontend dependencies
|
||||||
|
run: |
|
||||||
|
cd apps/frontend
|
||||||
|
pnpm install
|
||||||
|
|
||||||
|
- name: Run frontend tests
|
||||||
|
run: |
|
||||||
|
cd apps/frontend
|
||||||
|
pnpm test
|
||||||
|
|
||||||
|
frontend-build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: 10
|
||||||
|
run_install: false
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 22
|
||||||
|
cache: 'pnpm'
|
||||||
|
cache-dependency-path: apps/frontend/pnpm-lock.yaml
|
||||||
|
|
||||||
|
- name: Install frontend dependencies
|
||||||
|
run: |
|
||||||
|
cd apps/frontend
|
||||||
|
pnpm install
|
||||||
|
|
||||||
|
- name: Build frontend
|
||||||
|
run: |
|
||||||
|
cd apps/frontend
|
||||||
|
pnpm build
|
||||||
|
|
||||||
|
- name: Cache frontend build
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: apps/frontend/build
|
||||||
|
key: frontend-build-${{ runner.os }}-run-${{ github.run_id }}
|
||||||
|
restore-keys: |
|
||||||
|
frontend-build-${{ runner.os }}-
|
||||||
|
|||||||
57
Cargo.lock
generated
57
Cargo.lock
generated
@@ -1135,6 +1135,25 @@ version = "0.3.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
|
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "h2"
|
||||||
|
version = "0.4.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386"
|
||||||
|
dependencies = [
|
||||||
|
"atomic-waker",
|
||||||
|
"bytes",
|
||||||
|
"fnv",
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
"http",
|
||||||
|
"indexmap 2.12.0",
|
||||||
|
"slab",
|
||||||
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.12.3"
|
version = "0.12.3"
|
||||||
@@ -1277,6 +1296,7 @@ dependencies = [
|
|||||||
"bytes",
|
"bytes",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"h2",
|
||||||
"http",
|
"http",
|
||||||
"http-body",
|
"http-body",
|
||||||
"httparse",
|
"httparse",
|
||||||
@@ -1488,6 +1508,25 @@ dependencies = [
|
|||||||
"icu_properties",
|
"icu_properties",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "include_dir"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd"
|
||||||
|
dependencies = [
|
||||||
|
"include_dir_macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "include_dir_macros"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "1.9.3"
|
version = "1.9.3"
|
||||||
@@ -1690,6 +1729,16 @@ version = "0.3.17"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mime_guess"
|
||||||
|
version = "2.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
|
||||||
|
dependencies = [
|
||||||
|
"mime",
|
||||||
|
"unicase",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
@@ -3696,6 +3745,12 @@ version = "0.1.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971"
|
checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicase"
|
||||||
|
version = "2.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-bidi"
|
name = "unicode-bidi"
|
||||||
version = "0.3.18"
|
version = "0.3.18"
|
||||||
@@ -4275,7 +4330,9 @@ dependencies = [
|
|||||||
"chrono",
|
"chrono",
|
||||||
"config",
|
"config",
|
||||||
"database",
|
"database",
|
||||||
|
"include_dir",
|
||||||
"migration",
|
"migration",
|
||||||
|
"mime_guess",
|
||||||
"sea-orm",
|
"sea-orm",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ edition = "2024"
|
|||||||
database = { path = "../../public/database" }
|
database = { path = "../../public/database" }
|
||||||
migration = { path = "../../public/migration" }
|
migration = { path = "../../public/migration" }
|
||||||
|
|
||||||
axum = { version = "0.8.7", features = ["form", "http1", "json", "matched-path", "original-uri", "query", "tokio", "tower-log", "tracing", "macros"]}
|
axum = { version = "0.8.7", features = ["form", "http1", "http2", "json", "matched-path", "original-uri", "query", "tokio", "tower-log", "tracing", "macros"]}
|
||||||
async-trait = { version = "0.1.89" }
|
async-trait = { version = "0.1.89" }
|
||||||
chrono = { version = "0.4.42", features = ["clock", "std", "oldtime", "wasmbind", "serde"] }
|
chrono = { version = "0.4.42", features = ["clock", "std", "oldtime", "wasmbind", "serde"] }
|
||||||
config = { version = "0.15.19", features = ["toml", "json", "yaml", "ini", "ron", "json5", "convert-case", "async"] }
|
config = { version = "0.15.19", features = ["toml", "json", "yaml", "ini", "ron", "json5", "convert-case", "async"] }
|
||||||
@@ -18,3 +18,5 @@ tracing-subscriber = { version = "0.3.20", features = ["smallvec", "fmt", "ansi"
|
|||||||
serde_json = { version = "1.0.145", features = ["std"] }
|
serde_json = { version = "1.0.145", features = ["std"] }
|
||||||
serde = { version = "1.0.228", features = ["std", "derive"] }
|
serde = { version = "1.0.228", features = ["std", "derive"] }
|
||||||
sea-orm = { workspace = true }
|
sea-orm = { workspace = true }
|
||||||
|
include_dir = { version = "0.7.4" }
|
||||||
|
mime_guess = { version = "2.0.5" }
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
mod api;
|
||||||
|
mod view;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use axum::{Extension, Router};
|
use axum::{Extension, Router};
|
||||||
@@ -25,6 +28,10 @@ pub struct AppService {
|
|||||||
pub fn get_root_router(state: impl Into<Arc<AppState>>) -> Router {
|
pub fn get_root_router(state: impl Into<Arc<AppState>>) -> Router {
|
||||||
let mut router = Router::new();
|
let mut router = Router::new();
|
||||||
|
|
||||||
|
router = router
|
||||||
|
.nest("/api", api::get_api_router())
|
||||||
|
.merge(view::get_view_router());
|
||||||
|
|
||||||
router = middlewares::apply_root_middleware(router);
|
router = middlewares::apply_root_middleware(router);
|
||||||
|
|
||||||
router = router.layer(Extension(state.into()));
|
router = router.layer(Extension(state.into()));
|
||||||
|
|||||||
11
apps/api/src/routes/api.rs
Normal file
11
apps/api/src/routes/api.rs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
use axum::{Router, response::IntoResponse, routing::any};
|
||||||
|
|
||||||
|
pub fn get_api_router() -> Router {
|
||||||
|
Router::new()
|
||||||
|
// explicit fallback for unmatched API routes
|
||||||
|
.route("/{*wildcard}", any(api_fallback_handler))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn api_fallback_handler() -> impl IntoResponse {
|
||||||
|
(axum::http::StatusCode::NOT_FOUND, "API route not found").into_response()
|
||||||
|
}
|
||||||
71
apps/api/src/routes/view.rs
Normal file
71
apps/api/src/routes/view.rs
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
use axum::{
|
||||||
|
Router,
|
||||||
|
body::Bytes,
|
||||||
|
extract::Path,
|
||||||
|
http::{StatusCode, header},
|
||||||
|
response::IntoResponse,
|
||||||
|
routing::{MethodRouter, get},
|
||||||
|
};
|
||||||
|
use include_dir::{Dir, include_dir};
|
||||||
|
use mime_guess::from_path;
|
||||||
|
|
||||||
|
static DIST_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/../frontend/build/client");
|
||||||
|
const INDEX_HTML_PATH: &str = "index.html";
|
||||||
|
const INDEX_FILE_NOT_FOUND_HTML: &str = r#"
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>404 Not Found</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>404 Not Found</h1>
|
||||||
|
<p>The requested resource was not found on this server. Possibly the frontend build is missing or corrupted.</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"#;
|
||||||
|
|
||||||
|
pub fn get_view_router() -> Router {
|
||||||
|
Router::new()
|
||||||
|
// Serve the root index.html
|
||||||
|
.route("/", get(root_view_handler))
|
||||||
|
.route(
|
||||||
|
"/{*wildcard}",
|
||||||
|
MethodRouter::new()
|
||||||
|
.get(|Path(path): Path<String>| async move { view_handler(Some(path)).await }),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn root_view_handler() -> impl IntoResponse {
|
||||||
|
view_handler(None).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn view_handler(path: Option<String>) -> impl IntoResponse {
|
||||||
|
// If path is empty, serve index.html
|
||||||
|
let incoming_path = if let Some(p) = path {
|
||||||
|
p.trim_start_matches('/').to_string()
|
||||||
|
} else {
|
||||||
|
INDEX_HTML_PATH.to_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
let path = match DIST_DIR.get_file(&incoming_path) {
|
||||||
|
Some(_) => incoming_path,
|
||||||
|
None => INDEX_HTML_PATH.to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
match DIST_DIR.get_file(&path) {
|
||||||
|
Some(file) => {
|
||||||
|
let mime = from_path(&path).first_or_octet_stream();
|
||||||
|
let body: Bytes = Bytes::copy_from_slice(file.contents());
|
||||||
|
([(header::CONTENT_TYPE, mime.as_ref())], body).into_response()
|
||||||
|
}
|
||||||
|
// This should never happen, but just in case...
|
||||||
|
None => (
|
||||||
|
StatusCode::NOT_FOUND,
|
||||||
|
[(header::CONTENT_TYPE, "text/plain")],
|
||||||
|
Bytes::from(INDEX_FILE_NOT_FOUND_HTML),
|
||||||
|
)
|
||||||
|
.into_response(),
|
||||||
|
}
|
||||||
|
}
|
||||||
4
apps/frontend/.dockerignore
Normal file
4
apps/frontend/.dockerignore
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
.react-router
|
||||||
|
build
|
||||||
|
node_modules
|
||||||
|
README.md
|
||||||
7
apps/frontend/.gitignore
vendored
Normal file
7
apps/frontend/.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
.DS_Store
|
||||||
|
.env
|
||||||
|
/node_modules/
|
||||||
|
|
||||||
|
# React Router
|
||||||
|
/.react-router/
|
||||||
|
/build/
|
||||||
15
apps/frontend/app/app.css
Normal file
15
apps/frontend/app/app.css
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
@import "tailwindcss";
|
||||||
|
|
||||||
|
@theme {
|
||||||
|
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif,
|
||||||
|
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
@apply bg-white dark:bg-gray-950;
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
color-scheme: dark;
|
||||||
|
}
|
||||||
|
}
|
||||||
59
apps/frontend/app/root.tsx
Normal file
59
apps/frontend/app/root.tsx
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { isRouteErrorResponse, Links, Meta, Outlet, Scripts, ScrollRestoration } from 'react-router';
|
||||||
|
|
||||||
|
import type { Route } from './+types/root';
|
||||||
|
import './app.css';
|
||||||
|
import { Theme } from '@radix-ui/themes';
|
||||||
|
|
||||||
|
export const links: Route.LinksFunction = () => [];
|
||||||
|
|
||||||
|
export function Layout({ children }: { children: React.ReactNode }) {
|
||||||
|
return (
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charSet="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<Meta />
|
||||||
|
<Links />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{children}
|
||||||
|
<ScrollRestoration />
|
||||||
|
<Scripts />
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function App() {
|
||||||
|
return (
|
||||||
|
<Theme>
|
||||||
|
<Outlet />
|
||||||
|
</Theme>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
|
||||||
|
let message = 'Oops!';
|
||||||
|
let details = 'An unexpected error occurred.';
|
||||||
|
let stack: string | undefined;
|
||||||
|
|
||||||
|
if (isRouteErrorResponse(error)) {
|
||||||
|
message = error.status === 404 ? '404' : 'Error';
|
||||||
|
details = error.status === 404 ? 'The requested page could not be found.' : error.statusText || details;
|
||||||
|
} else if (import.meta.env.DEV && error && error instanceof Error) {
|
||||||
|
details = error.message;
|
||||||
|
stack = error.stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main className="pt-16 p-4 container mx-auto">
|
||||||
|
<h1>{message}</h1>
|
||||||
|
<p>{details}</p>
|
||||||
|
{stack && (
|
||||||
|
<pre className="w-full p-4 overflow-x-auto">
|
||||||
|
<code>{stack}</code>
|
||||||
|
</pre>
|
||||||
|
)}
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
7
apps/frontend/app/routes.ts
Normal file
7
apps/frontend/app/routes.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { type RouteConfig, index, route } from '@react-router/dev/routes';
|
||||||
|
|
||||||
|
export default [
|
||||||
|
index('routes/home.tsx'),
|
||||||
|
// catch-all 404 route
|
||||||
|
route('*', 'routes/404.tsx'),
|
||||||
|
] satisfies RouteConfig;
|
||||||
3
apps/frontend/app/routes/404.tsx
Normal file
3
apps/frontend/app/routes/404.tsx
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export default function NotFound() {
|
||||||
|
return <h1>404 - Not Found</h1>;
|
||||||
|
}
|
||||||
10
apps/frontend/app/routes/home.tsx
Normal file
10
apps/frontend/app/routes/home.tsx
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { Text } from '@radix-ui/themes';
|
||||||
|
import type { Route } from './+types/home';
|
||||||
|
|
||||||
|
export function meta({}: Route.MetaArgs) {
|
||||||
|
return [{ title: 'YANPM' }, { name: 'description', content: 'Welcome to Yet Another Nginx Proxy Manager!' }];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
return <Text>Welcome to Yet Another Nginx Proxy Manager!</Text>;
|
||||||
|
}
|
||||||
34
apps/frontend/package.json
Normal file
34
apps/frontend/package.json
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"name": "frontend",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"build": "react-router build",
|
||||||
|
"dev": "react-router dev",
|
||||||
|
"start": "react-router-serve ./build/server/index.js",
|
||||||
|
"typecheck": "react-router typegen && tsc",
|
||||||
|
"test": "echo \"No tests specified\" && exit 0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/themes": "^3.2.1",
|
||||||
|
"@react-router/node": "^7.9.2",
|
||||||
|
"@react-router/serve": "^7.9.2",
|
||||||
|
"isbot": "^5.1.31",
|
||||||
|
"radix-ui": "^1.4.3",
|
||||||
|
"react": "^19.1.1",
|
||||||
|
"react-dom": "^19.1.1",
|
||||||
|
"react-router": "^7.9.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@react-router/dev": "^7.9.2",
|
||||||
|
"@tailwindcss/vite": "^4.1.13",
|
||||||
|
"@types/node": "^22",
|
||||||
|
"@types/react": "^19.1.13",
|
||||||
|
"@types/react-dom": "^19.1.9",
|
||||||
|
"dotenv": "^17.2.3",
|
||||||
|
"tailwindcss": "^4.1.13",
|
||||||
|
"typescript": "^5.9.2",
|
||||||
|
"vite": "^7.1.7",
|
||||||
|
"vite-tsconfig-paths": "^5.1.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
4748
apps/frontend/pnpm-lock.yaml
generated
Normal file
4748
apps/frontend/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
BIN
apps/frontend/public/favicon.ico
Normal file
BIN
apps/frontend/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
5
apps/frontend/react-router.config.ts
Normal file
5
apps/frontend/react-router.config.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import type { Config } from '@react-router/dev/config';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
ssr: false,
|
||||||
|
} satisfies Config;
|
||||||
22
apps/frontend/tsconfig.json
Normal file
22
apps/frontend/tsconfig.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"include": ["**/*", "**/.server/**/*", "**/.client/**/*", ".react-router/types/**/*"],
|
||||||
|
"compilerOptions": {
|
||||||
|
"lib": ["DOM", "DOM.Iterable", "ES2022"],
|
||||||
|
"types": ["node", "vite/client"],
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "ES2022",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"rootDirs": [".", "./.react-router/types"],
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"~/*": ["./app/*"]
|
||||||
|
},
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strict": true
|
||||||
|
}
|
||||||
|
}
|
||||||
9
apps/frontend/vite.config.ts
Normal file
9
apps/frontend/vite.config.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { reactRouter } from '@react-router/dev/vite';
|
||||||
|
import tailwindcss from '@tailwindcss/vite';
|
||||||
|
import { defineConfig } from 'vite';
|
||||||
|
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [tailwindcss(), reactRouter(), tsconfigPaths()],
|
||||||
|
appType: 'spa',
|
||||||
|
});
|
||||||
20
justfile
20
justfile
@@ -40,3 +40,23 @@ migrate *args:
|
|||||||
generate-entity:
|
generate-entity:
|
||||||
# delegate to cli
|
# delegate to cli
|
||||||
just cli db:migrate_and_generate --output-path ../../public/database/src/generated/entities
|
just cli db:migrate_and_generate --output-path ../../public/database/src/generated/entities
|
||||||
|
|
||||||
|
build-frontend:
|
||||||
|
# build frontend assets
|
||||||
|
cd apps/frontend && \
|
||||||
|
pnpm build
|
||||||
|
|
||||||
|
build-backend:
|
||||||
|
# build backend server
|
||||||
|
cd apps/api && \
|
||||||
|
cargo build --release
|
||||||
|
|
||||||
|
build: build-frontend build-backend
|
||||||
|
|
||||||
|
act *args:
|
||||||
|
if [ -n "{{args}}" ]; then \
|
||||||
|
act {{args}} --artifact-server-path /tmp/artifacts; \
|
||||||
|
else \
|
||||||
|
act; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user