mirror of
https://github.com/LeNei/axum-sqlx-template.git
synced 2026-02-13 22:56:19 +00:00
base inertia setup
This commit is contained in:
103
Cargo.lock
generated
103
Cargo.lock
generated
@@ -141,12 +141,29 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum-inertia"
|
||||
version = "0.8.1"
|
||||
source = "git+https://github.com/LeNei/axum-inertia#6cfa00876604eff51701df87f0bea36d49fcdd8e"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"hex",
|
||||
"http",
|
||||
"hyper",
|
||||
"indoc",
|
||||
"maud",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum-sqlx-template"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
"axum-inertia",
|
||||
"chrono",
|
||||
"config",
|
||||
"http",
|
||||
@@ -160,7 +177,7 @@ dependencies = [
|
||||
"tower-http",
|
||||
"tracing",
|
||||
"tracing-bunyan-formatter",
|
||||
"tracing-log 0.1.4",
|
||||
"tracing-log 0.2.0",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
@@ -696,6 +713,12 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-range-header"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c"
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.10.1"
|
||||
@@ -916,6 +939,12 @@ dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indoc"
|
||||
version = "2.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.15"
|
||||
@@ -1006,6 +1035,28 @@ version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
|
||||
|
||||
[[package]]
|
||||
name = "maud"
|
||||
version = "0.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8156733e27020ea5c684db5beac5d1d611e1272ab17901a49466294b84fc217e"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"maud_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "maud_macros"
|
||||
version = "0.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7261b00f3952f617899bc012e3dbd56e4f0110a038175929fa5d18e5a19913ca"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"proc-macro2-diagnostics",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "md-5"
|
||||
version = "0.10.6"
|
||||
@@ -1028,6 +1079,16 @@ version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.5"
|
||||
@@ -1254,6 +1315,18 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2-diagnostics"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
@@ -2029,6 +2102,19 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower"
|
||||
version = "0.5.2"
|
||||
@@ -2053,9 +2139,18 @@ checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"http-range-header",
|
||||
"httpdate",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
@@ -2170,6 +2265,12 @@ version = "1.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.18"
|
||||
|
||||
@@ -32,8 +32,9 @@ log = "0.4"
|
||||
tracing = "0.1.19"
|
||||
tracing-subscriber = { version = "0.3", features = ["registry", "env-filter"] }
|
||||
tracing-bunyan-formatter = "0.3"
|
||||
tracing-log = "0.1.1"
|
||||
tracing-log = "0.2.0"
|
||||
axum-inertia = { git = "https://github.com/LeNei/axum-inertia" }
|
||||
axum = { version = "0.8.1", features = ["tracing"] }
|
||||
tower-http = { version = "0.6.2", features = ["trace", "cors"] }
|
||||
tower-http = { version = "0.6.2", features = ["trace", "cors", "fs"] }
|
||||
http = "1.3.1"
|
||||
anyhow = "1.0"
|
||||
|
||||
@@ -8,3 +8,4 @@ database:
|
||||
password: "postgres"
|
||||
database_name: "postgres"
|
||||
require_ssl: false
|
||||
is_dev: false
|
||||
|
||||
@@ -3,3 +3,4 @@ application:
|
||||
base_url: "http://127.0.0.1"
|
||||
database:
|
||||
require_ssl: false
|
||||
is_dev: true
|
||||
|
||||
24
frontend/.gitignore
vendored
Normal file
24
frontend/.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
54
frontend/README.md
Normal file
54
frontend/README.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# React + TypeScript + Vite
|
||||
|
||||
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
||||
|
||||
Currently, two official plugins are available:
|
||||
|
||||
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
|
||||
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
||||
|
||||
## Expanding the ESLint configuration
|
||||
|
||||
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
|
||||
|
||||
```js
|
||||
export default tseslint.config({
|
||||
extends: [
|
||||
// Remove ...tseslint.configs.recommended and replace with this
|
||||
...tseslint.configs.recommendedTypeChecked,
|
||||
// Alternatively, use this for stricter rules
|
||||
...tseslint.configs.strictTypeChecked,
|
||||
// Optionally, add this for stylistic rules
|
||||
...tseslint.configs.stylisticTypeChecked,
|
||||
],
|
||||
languageOptions: {
|
||||
// other options...
|
||||
parserOptions: {
|
||||
project: ['./tsconfig.node.json', './tsconfig.app.json'],
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
|
||||
|
||||
```js
|
||||
// eslint.config.js
|
||||
import reactX from 'eslint-plugin-react-x'
|
||||
import reactDom from 'eslint-plugin-react-dom'
|
||||
|
||||
export default tseslint.config({
|
||||
plugins: {
|
||||
// Add the react-x and react-dom plugins
|
||||
'react-x': reactX,
|
||||
'react-dom': reactDom,
|
||||
},
|
||||
rules: {
|
||||
// other rules...
|
||||
// Enable its recommended typescript rules
|
||||
...reactX.configs['recommended-typescript'].rules,
|
||||
...reactDom.configs.recommended.rules,
|
||||
},
|
||||
})
|
||||
```
|
||||
28
frontend/eslint.config.js
Normal file
28
frontend/eslint.config.js
Normal file
@@ -0,0 +1,28 @@
|
||||
import js from '@eslint/js'
|
||||
import globals from 'globals'
|
||||
import reactHooks from 'eslint-plugin-react-hooks'
|
||||
import reactRefresh from 'eslint-plugin-react-refresh'
|
||||
import tseslint from 'typescript-eslint'
|
||||
|
||||
export default tseslint.config(
|
||||
{ ignores: ['dist'] },
|
||||
{
|
||||
extends: [js.configs.recommended, ...tseslint.configs.recommended],
|
||||
files: ['**/*.{ts,tsx}'],
|
||||
languageOptions: {
|
||||
ecmaVersion: 2020,
|
||||
globals: globals.browser,
|
||||
},
|
||||
plugins: {
|
||||
'react-hooks': reactHooks,
|
||||
'react-refresh': reactRefresh,
|
||||
},
|
||||
rules: {
|
||||
...reactHooks.configs.recommended.rules,
|
||||
'react-refresh/only-export-components': [
|
||||
'warn',
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
},
|
||||
},
|
||||
)
|
||||
13
frontend/index.html
Normal file
13
frontend/index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + React + TS</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
4201
frontend/package-lock.json
generated
Normal file
4201
frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
34
frontend/package.json
Normal file
34
frontend/package.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "frontend",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc -b && vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@inertiajs/react": "^2.0.5",
|
||||
"@tailwindcss/vite": "^4.0.17",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"tailwindcss": "^4.0.17",
|
||||
"vite-plugin-compression2": "^1.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.21.0",
|
||||
"@types/node": "^22.13.14",
|
||||
"@types/react": "^19.0.10",
|
||||
"@types/react-dom": "^19.0.4",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"eslint": "^9.21.0",
|
||||
"eslint-plugin-react-hooks": "^5.1.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.19",
|
||||
"globals": "^15.15.0",
|
||||
"typescript": "~5.7.2",
|
||||
"typescript-eslint": "^8.24.1",
|
||||
"vite": "^6.2.0"
|
||||
}
|
||||
}
|
||||
1
frontend/public/vite.svg
Normal file
1
frontend/public/vite.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
5
frontend/src/Pages/Home.tsx
Normal file
5
frontend/src/Pages/Home.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
"use client";
|
||||
|
||||
export default function Home() {
|
||||
return <div className="text-red-500">Home</div>;
|
||||
}
|
||||
21
frontend/src/index.css
Normal file
21
frontend/src/index.css
Normal file
@@ -0,0 +1,21 @@
|
||||
@import "tailwindcss";
|
||||
@custom-variant dark (&:where(.dark, .dark *));
|
||||
|
||||
html {
|
||||
@apply h-full;
|
||||
}
|
||||
body,
|
||||
#app {
|
||||
@apply h-full bg-white dark:bg-zinc-900;
|
||||
}
|
||||
|
||||
input::-webkit-outer-spin-button,
|
||||
input::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Firefox */
|
||||
input[type="number"] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
13
frontend/src/main.tsx
Normal file
13
frontend/src/main.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { createInertiaApp } from "@inertiajs/react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import "./index.css";
|
||||
|
||||
createInertiaApp({
|
||||
resolve: async (name) => {
|
||||
const pages = import.meta.glob("./Pages/**/*.tsx");
|
||||
return await pages[`./Pages/${name}.tsx`]();
|
||||
},
|
||||
setup({ el, App, props }) {
|
||||
createRoot(el).render(<App {...props} />);
|
||||
},
|
||||
});
|
||||
1
frontend/src/vite-env.d.ts
vendored
Normal file
1
frontend/src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
26
frontend/tsconfig.app.json
Normal file
26
frontend/tsconfig.app.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
7
frontend/tsconfig.json
Normal file
7
frontend/tsconfig.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "./tsconfig.app.json" },
|
||||
{ "path": "./tsconfig.node.json" }
|
||||
]
|
||||
}
|
||||
24
frontend/tsconfig.node.json
Normal file
24
frontend/tsconfig.node.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2023"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
29
frontend/vite.config.ts
Normal file
29
frontend/vite.config.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { defineConfig } from "vite";
|
||||
import path from "path";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
import viteCompression from "vite-plugin-compression2";
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
react(),
|
||||
tailwindcss(),
|
||||
viteCompression({
|
||||
include: /\.(js|css|html)$/,
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": path.resolve(__dirname, "./src"),
|
||||
},
|
||||
},
|
||||
build: {
|
||||
// generiert .vite/manifest.json in outDir
|
||||
manifest: true,
|
||||
rollupOptions: {
|
||||
// Überschreibe den Standard-.html-Einstieg
|
||||
input: path.resolve(__dirname, "./src/main.tsx"),
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -1,28 +1,22 @@
|
||||
use axum::{Router, routing::get};
|
||||
use http::{Method, StatusCode};
|
||||
use sqlx::PgPool;
|
||||
use tower_http::cors::{Any, CorsLayer};
|
||||
use tower_http::trace::TraceLayer;
|
||||
|
||||
use crate::config::ApiContext;
|
||||
|
||||
#[tracing::instrument(name = "Ping")]
|
||||
async fn ping() -> StatusCode {
|
||||
StatusCode::OK
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ApiContext {
|
||||
pub db: PgPool,
|
||||
}
|
||||
|
||||
pub fn build_routes(api_context: ApiContext) -> Router {
|
||||
pub fn routes(api_context: ApiContext) -> Router {
|
||||
let cors = CorsLayer::new()
|
||||
// allow `GET` and `POST` when accessing the resource
|
||||
.allow_methods([Method::GET, Method::POST])
|
||||
// allow requests from any origin
|
||||
.allow_origin(Any);
|
||||
Router::new()
|
||||
.route("/", get(ping))
|
||||
.layer(TraceLayer::new_for_http())
|
||||
.route("/ping", get(ping))
|
||||
.layer(cors)
|
||||
.with_state(api_context)
|
||||
}
|
||||
17
src/config/inertia.rs
Normal file
17
src/config/inertia.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
use axum_inertia::{InertiaConfig, vite};
|
||||
|
||||
pub fn get_inertia_config(is_dev: bool) -> InertiaConfig {
|
||||
if !is_dev {
|
||||
vite::Production::new("frontend/dist/.vite/manifest.json", "src/main.tsx")
|
||||
.unwrap()
|
||||
.lang("de")
|
||||
.into_config()
|
||||
} else {
|
||||
vite::Development::default()
|
||||
.port(5173)
|
||||
.main("src/main.tsx")
|
||||
.lang("de")
|
||||
.react() // call if using react
|
||||
.into_config()
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,20 @@
|
||||
pub mod app;
|
||||
pub mod database;
|
||||
pub mod inertia;
|
||||
pub mod logging;
|
||||
|
||||
use app::ApplicationSettings;
|
||||
use axum::extract::FromRef;
|
||||
use axum_inertia::InertiaConfig;
|
||||
use database::DatabaseSettings;
|
||||
use serde::Deserialize;
|
||||
use sqlx::PgPool;
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
pub struct Settings {
|
||||
pub database: DatabaseSettings,
|
||||
pub application: ApplicationSettings,
|
||||
pub is_dev: bool,
|
||||
}
|
||||
|
||||
pub fn get_configuration() -> Result<Settings, config::ConfigError> {
|
||||
@@ -74,3 +79,15 @@ impl TryFrom<String> for Environment {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ApiContext {
|
||||
pub db: PgPool,
|
||||
pub inertia: InertiaConfig,
|
||||
}
|
||||
|
||||
impl FromRef<ApiContext> for InertiaConfig {
|
||||
fn from_ref(app_state: &ApiContext) -> InertiaConfig {
|
||||
app_state.inertia.clone()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod api;
|
||||
pub mod config;
|
||||
pub mod routes;
|
||||
pub mod pages;
|
||||
pub mod startup;
|
||||
|
||||
22
src/pages/mod.rs
Normal file
22
src/pages/mod.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
use axum::{Router, response::IntoResponse, routing::get};
|
||||
use axum_inertia::Inertia;
|
||||
use http::Method;
|
||||
use serde_json::json;
|
||||
use tower_http::cors::CorsLayer;
|
||||
|
||||
use crate::config::ApiContext;
|
||||
|
||||
#[tracing::instrument(name = "Home Page", skip(i))]
|
||||
async fn home(i: Inertia) -> impl IntoResponse {
|
||||
i.render("Home", json!({}))
|
||||
}
|
||||
|
||||
pub fn routes(api_context: ApiContext) -> Router {
|
||||
let cors = CorsLayer::new()
|
||||
// allow `GET` and `POST` when accessing the resource
|
||||
.allow_methods([Method::GET, Method::POST, Method::PUT, Method::DELETE]);
|
||||
Router::new()
|
||||
.route("/", get(home))
|
||||
.layer(cors)
|
||||
.with_state(api_context)
|
||||
}
|
||||
@@ -1,21 +1,49 @@
|
||||
use crate::config::Settings;
|
||||
use crate::routes::{build_routes, ApiContext};
|
||||
use crate::api::routes as api_routes;
|
||||
use crate::config::inertia::get_inertia_config;
|
||||
use crate::config::{ApiContext, Settings};
|
||||
use crate::pages::routes as page_routes;
|
||||
use tower_http::services::ServeDir;
|
||||
|
||||
use anyhow::Context;
|
||||
use axum::Router;
|
||||
use tokio::net::TcpListener;
|
||||
use tower_http::trace::TraceLayer;
|
||||
|
||||
pub async fn build(settings: Settings) -> anyhow::Result<()> {
|
||||
let api_context = ApiContext {
|
||||
db: settings.database.get_connection_pool(),
|
||||
inertia: get_inertia_config(settings.is_dev),
|
||||
};
|
||||
let api_router = build_routes(api_context);
|
||||
|
||||
tracing::info!("Creating router...");
|
||||
let mut router = Router::new()
|
||||
.merge(page_routes(api_context.clone()))
|
||||
.nest("/api", api_routes(api_context))
|
||||
.layer(TraceLayer::new_for_http());
|
||||
|
||||
if !settings.is_dev {
|
||||
// Serve static assets in production from the frontend/dist/assets directory instead of Vite
|
||||
let service = ServeDir::new("frontend/dist/assets").precompressed_gzip();
|
||||
router = router.nest_service("/assets", service);
|
||||
}
|
||||
|
||||
let public_service = match settings.is_dev {
|
||||
true => ServeDir::new("frontend/public"),
|
||||
false => ServeDir::new("frontend/dist"),
|
||||
};
|
||||
router = router.fallback_service(public_service);
|
||||
|
||||
let address = format!(
|
||||
"{}:{}",
|
||||
settings.application.host, settings.application.port
|
||||
);
|
||||
let listener = TcpListener::bind(address).await.context("Failed to bind to port")?;
|
||||
|
||||
run(api_router, listener).await
|
||||
tracing::info!("Binding to address: {}", address);
|
||||
let listener = TcpListener::bind(address)
|
||||
.await
|
||||
.context("Failed to bind to port")?;
|
||||
|
||||
run(router, listener).await
|
||||
}
|
||||
|
||||
async fn run(router: Router, listener: TcpListener) -> anyhow::Result<()> {
|
||||
|
||||
Reference in New Issue
Block a user