Implement client-side routing

This commit is contained in:
Kai Vogelgesang 2022-09-05 20:54:44 +02:00
parent 6f7279c049
commit c147b44b0c
Signed by: kai
GPG Key ID: 0A95D3B6E62C0879
6 changed files with 209 additions and 26 deletions

View File

@ -15,6 +15,7 @@
"sass": "^1.53.0", "sass": "^1.53.0",
"svelte": "^3.49.0", "svelte": "^3.49.0",
"svelte-check": "^2.8.0", "svelte-check": "^2.8.0",
"svelte-navigator": "^3.2.2",
"svelte-preprocess": "^4.10.7", "svelte-preprocess": "^4.10.7",
"tslib": "^2.4.0", "tslib": "^2.4.0",
"typescript": "^4.6.4", "typescript": "^4.6.4",
@ -298,6 +299,12 @@
} }
} }
}, },
"node_modules/dedent-js": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dedent-js/-/dedent-js-1.0.1.tgz",
"integrity": "sha512-OUepMozQULMLUmhxS95Vudo0jb0UchLimi3+pQ2plj61Fcy8axbP9hbiD4Sz6DPqn6XG3kfmziVfQ1rSys5AJQ==",
"dev": true
},
"node_modules/deepmerge": { "node_modules/deepmerge": {
"version": "4.2.2", "version": "4.2.2",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
@ -898,6 +905,15 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/lower-case": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
"integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
"dev": true,
"dependencies": {
"tslib": "^2.0.3"
}
},
"node_modules/magic-string": { "node_modules/magic-string": {
"version": "0.26.3", "version": "0.26.3",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.3.tgz", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.3.tgz",
@ -998,6 +1014,16 @@
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
} }
}, },
"node_modules/no-case": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
"integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
"dev": true,
"dependencies": {
"lower-case": "^2.0.2",
"tslib": "^2.0.3"
}
},
"node_modules/normalize-path": { "node_modules/normalize-path": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
@ -1028,6 +1054,16 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/pascal-case": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz",
"integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
"dev": true,
"dependencies": {
"no-case": "^3.0.4",
"tslib": "^2.0.3"
}
},
"node_modules/path-is-absolute": { "node_modules/path-is-absolute": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@ -1341,6 +1377,19 @@
"svelte": ">=3.19.0" "svelte": ">=3.19.0"
} }
}, },
"node_modules/svelte-navigator": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/svelte-navigator/-/svelte-navigator-3.2.2.tgz",
"integrity": "sha512-Xio4ohLUG1nQJ+ENNbLphXXu9L189fnI1WGg+2Q3CIMPe8Jm2ipytKQthdBs8t0mN7p3Eb03SE9hq0xZAqwQNQ==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
"svelte2tsx": "^0.1.151"
},
"peerDependencies": {
"svelte": "3.x"
}
},
"node_modules/svelte-preprocess": { "node_modules/svelte-preprocess": {
"version": "4.10.7", "version": "4.10.7",
"resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-4.10.7.tgz", "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-4.10.7.tgz",
@ -1416,6 +1465,20 @@
"sourcemap-codec": "^1.4.8" "sourcemap-codec": "^1.4.8"
} }
}, },
"node_modules/svelte2tsx": {
"version": "0.1.193",
"resolved": "https://registry.npmjs.org/svelte2tsx/-/svelte2tsx-0.1.193.tgz",
"integrity": "sha512-vzy4YQNYDnoqp2iZPnJy7kpPAY6y121L0HKrSBjU/IWW7DQ6T7RMJed2VVHFmVYm0zAGYMDl9urPc6R4DDUyhg==",
"dev": true,
"dependencies": {
"dedent-js": "^1.0.1",
"pascal-case": "^3.1.1"
},
"peerDependencies": {
"svelte": "^3.24",
"typescript": "^4.1.2"
}
},
"node_modules/to-regex-range": { "node_modules/to-regex-range": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@ -1698,6 +1761,12 @@
"ms": "2.1.2" "ms": "2.1.2"
} }
}, },
"dedent-js": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dedent-js/-/dedent-js-1.0.1.tgz",
"integrity": "sha512-OUepMozQULMLUmhxS95Vudo0jb0UchLimi3+pQ2plj61Fcy8axbP9hbiD4Sz6DPqn6XG3kfmziVfQ1rSys5AJQ==",
"dev": true
},
"deepmerge": { "deepmerge": {
"version": "4.2.2", "version": "4.2.2",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
@ -2056,6 +2125,15 @@
"integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==",
"dev": true "dev": true
}, },
"lower-case": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
"integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
"dev": true,
"requires": {
"tslib": "^2.0.3"
}
},
"magic-string": { "magic-string": {
"version": "0.26.3", "version": "0.26.3",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.3.tgz", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.3.tgz",
@ -2129,6 +2207,16 @@
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
"dev": true "dev": true
}, },
"no-case": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
"integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
"dev": true,
"requires": {
"lower-case": "^2.0.2",
"tslib": "^2.0.3"
}
},
"normalize-path": { "normalize-path": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
@ -2153,6 +2241,16 @@
"callsites": "^3.0.0" "callsites": "^3.0.0"
} }
}, },
"pascal-case": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz",
"integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
"dev": true,
"requires": {
"no-case": "^3.0.4",
"tslib": "^2.0.3"
}
},
"path-is-absolute": { "path-is-absolute": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@ -2353,6 +2451,15 @@
"dev": true, "dev": true,
"requires": {} "requires": {}
}, },
"svelte-navigator": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/svelte-navigator/-/svelte-navigator-3.2.2.tgz",
"integrity": "sha512-Xio4ohLUG1nQJ+ENNbLphXXu9L189fnI1WGg+2Q3CIMPe8Jm2ipytKQthdBs8t0mN7p3Eb03SE9hq0xZAqwQNQ==",
"dev": true,
"requires": {
"svelte2tsx": "^0.1.151"
}
},
"svelte-preprocess": { "svelte-preprocess": {
"version": "4.10.7", "version": "4.10.7",
"resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-4.10.7.tgz", "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-4.10.7.tgz",
@ -2378,6 +2485,16 @@
} }
} }
}, },
"svelte2tsx": {
"version": "0.1.193",
"resolved": "https://registry.npmjs.org/svelte2tsx/-/svelte2tsx-0.1.193.tgz",
"integrity": "sha512-vzy4YQNYDnoqp2iZPnJy7kpPAY6y121L0HKrSBjU/IWW7DQ6T7RMJed2VVHFmVYm0zAGYMDl9urPc6R4DDUyhg==",
"dev": true,
"requires": {
"dedent-js": "^1.0.1",
"pascal-case": "^3.1.1"
}
},
"to-regex-range": { "to-regex-range": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",

View File

@ -17,6 +17,7 @@
"sass": "^1.53.0", "sass": "^1.53.0",
"svelte": "^3.49.0", "svelte": "^3.49.0",
"svelte-check": "^2.8.0", "svelte-check": "^2.8.0",
"svelte-navigator": "^3.2.2",
"svelte-preprocess": "^4.10.7", "svelte-preprocess": "^4.10.7",
"tslib": "^2.4.0", "tslib": "^2.4.0",
"typescript": "^4.6.4", "typescript": "^4.6.4",

View File

@ -1,4 +1,5 @@
<script lang="ts"> <script lang="ts">
import { Router, Route } from "svelte-navigator";
import { onMount } from "svelte"; import { onMount } from "svelte";
import { user } from "./stores"; import { user } from "./stores";
@ -11,7 +12,37 @@
}); });
</script> </script>
<Navbar /> <Router>
<section class="section"> <Navbar />
<p>Hallo i bims 1 frontend</p>
</section> <Route path="/">
<section class="section">
<p>Hallo i bims 1 frontend</p>
</section>
</Route>
<Route path="/foo">foo</Route>
<Route path="/bar">bar</Route>
<Route path="/monitoring">monitoring</Route>
<Route path="/mining">mining</Route>
<Route path="/stats">stats</Route>
<Route>
<section class="section">
<div class="container has-text-centered">
<h1 class="title is-1">404</h1>
<h2 class="subtitle is-5">
The page you are trying to reach either
<strong>does not exist</strong>
or
<strong>you are not authorized</strong>
to view it.
</h2>
</div>
</section>
</Route>
</Router>

View File

@ -1,4 +1,7 @@
<script lang="ts"> <script lang="ts">
import Navlink from "./Navlink.svelte";
import { Link } from "svelte-navigator";
import logo from "./assets/turtle.png"; import logo from "./assets/turtle.png";
import { user } from "./stores"; import { user } from "./stores";
@ -8,12 +11,10 @@
<nav class="navbar"> <nav class="navbar">
<div class="navbar-brand"> <div class="navbar-brand">
<a class="navbar-item" href="/"> <Link class="navbar-item" to="/">
<span class="icon-text"> <img class="image is-24x24 mr-1" src={logo} alt="logo" />
<img class="image is-24x24" src={logo} alt="logo" /> <span><strong>Control Panel</strong></span>
<span><strong>Control Panel</strong></span> </Link>
</span>
</a>
<!-- svelte-ignore a11y-missing-attribute --> <!-- svelte-ignore a11y-missing-attribute -->
<a <a
role="button" role="button"
@ -29,10 +30,13 @@
</div> </div>
<div class="navbar-menu {menu_open ? 'is-active' : ''}"> <div class="navbar-menu {menu_open ? 'is-active' : ''}">
<div class="navbar-start"> <div class="navbar-start">
<a class="navbar-item" href="/foo"> Foo </a> <Navlink target="/foo" text="Foo" icon="fas fa-hippo" />
<Navlink target="/bar" text="Bar" icon="fas fa-otter" />
<a class="navbar-item" href="/bar"> Bar </a> <Navlink target="/monitoring" text="Monitoring" icon="fas fa-binoculars" />
<Navlink target="/mining" text="Mining" icon="fas fa-person-digging" />
<Navlink target="/stats" text="Statistics" icon="fas fa-chart-line" />
</div> </div>
<div class="navbar-end"> <div class="navbar-end">
{#if $user !== null} {#if $user !== null}
<div class="navbar-item has-dropdown is-hoverable"> <div class="navbar-item has-dropdown is-hoverable">

View File

@ -0,0 +1,23 @@
<script lang="ts">
import { Link, useLocation } from "svelte-navigator";
const location = useLocation();
export let target: string;
export let icon: string;
export let text: string;
</script>
<Link
to={target}
class="navbar-item is-tab {$location.pathname === target
? 'is-active'
: ''}"
>
<span class="icon-text">
<span class="icon">
<i class={icon} />
</span>
<span>{text}</span>
</span>
</Link>

View File

@ -1,10 +1,7 @@
import json import json
from fastapi import FastAPI, Request from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates from fastapi.responses import HTMLResponse
from starlette.config import Config from jinja2 import Environment, FileSystemLoader, select_autoescape
from starlette.middleware.sessions import SessionMiddleware
from starlette.responses import HTMLResponse, RedirectResponse
from authlib.integrations.starlette_client import OAuth, OAuthError
from .settings import settings from .settings import settings
from .user import user_auth from .user import user_auth
@ -13,8 +10,6 @@ app = FastAPI()
app.mount("/user/", user_auth) app.mount("/user/", user_auth)
templates = Jinja2Templates(directory="templates")
manifest = dict() manifest = dict()
if not settings.dev_mode: if not settings.dev_mode:
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
@ -23,12 +18,24 @@ if not settings.dev_mode:
manifest = json.load(f) manifest = json.load(f)
@app.get("/") j2env = Environment(
async def index(request: Request): loader=FileSystemLoader("templates"),
return templates.TemplateResponse( autoescape=select_autoescape(),
"index.html", )
{"request": request, "dev_mode": settings.dev_mode, "manifest": manifest}, index = j2env.get_template("index.html").render(
) dev_mode=settings.dev_mode,
manifest=manifest,
)
@app.middleware("http")
async def index_catch_all(request: Request, call_next):
response = await call_next(request)
if response.status_code == 404:
return HTMLResponse(index)
return response
if not settings.dev_mode: if not settings.dev_mode: