fix login & add sveltestrap

This commit is contained in:
ThePetrovich 2025-06-26 19:15:33 +08:00
parent a822fb1e36
commit 527d4417ff
15 changed files with 373 additions and 191 deletions

View file

@ -1,110 +1,130 @@
<script>
import { goto } from '$app/navigation';
import { page } from '$app/stores';
import { checkAuthenticated, logout } from '$lib/auth';
import {
Dropdown,
DropdownItem,
DropdownMenu,
DropdownToggle,
Nav,
NavItem,
NavLink,
Navbar,
NavbarBrand
} from '@sveltestrap/sveltestrap';
// Check if user is authenticated (using localStorage token)
let isAuthenticated = false;
// This should be reactive to changes in auth status
$: isAuthenticated = typeof window !== 'undefined' ? !!localStorage.getItem('accessToken') : false;
$: if (typeof window !== 'undefined') {
Promise.resolve(checkAuthenticated()).then(result => {
isAuthenticated = result;
});
} else {
isAuthenticated = false;
}
function handleLogout() {
// Clear authentication tokens
localStorage.removeItem('accessToken');
localStorage.removeItem('refreshToken');
try {
logout();
} catch (error) {
console.error('Logout failed:', error);
}
// Update auth status
isAuthenticated = false;
// Redirect to login page
goto('/login');
goto('/');
}
</script>
<nav class="navbar navbar-expand-lg navbar-light bg-light fixed-top custom-navbar">
<div class="container-fluid white-bg">
<a class="navbar-brand" href="/">
<img src="/logo.svg" alt="Logo" height="36" />
</a>
<button
class="navbar-toggler"
type="button"
data-bs-toggle="collapse"
data-bs-target="#navbarNav"
aria-controls="navbarNav"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link nav-full-height {window.location.pathname === '/predict' ? 'active' : ''}" href="/predict">Прогнозирование</a>
</li>
<li class="nav-item">
<a class="nav-link nav-full-height {window.location.pathname === '/track' ? 'active' : ''}" href="/track">Слежение</a>
</li>
</ul>
<ul class="navbar-nav">
{#if isAuthenticated}
<li class="nav-item">
<button
class="nav-link nav-full-height"
on:click={handleLogout}
style="cursor: pointer;"
>
Logout
</button>
</li>
{:else}
<li class="nav-item">
<a class="nav-link nav-full-height {$page.url.pathname === '/login' ? 'active' : ''}" href="/login">Login</a>
</li>
{/if}
</ul>
</div>
</div>
</nav>
<!-- <Navbar color="dark" dark expand="md">
<Nav navbar>
<NavItem>
<NavLink href="/components/">Inactive Link</NavLink>
</NavItem>
<Dropdown nav setActiveFromChild>
<DropdownToggle nav class="nav-link" caret>
Dropdown
</DropdownToggle>
<DropdownMenu>
<DropdownItem href="#" active>
Lancelot
</DropdownItem>
<DropdownItem href="#">Link</DropdownItem>
<DropdownItem href="#">Secret</DropdownItem>
<DropdownItem href="#">Chimp</DropdownItem>
</DropdownMenu>
</Dropdown>
</Nav>
</Navbar> -->
<Navbar color="light" light expand="lg" fixed="top" class="custom-navbar">
<Nav class="me-auto mb-2 mb-lg-0" navbar>
<NavbarBrand href="/" class="nav-full-height">
<img src="/logo.svg" alt="Logo" height="36" class="d-inline-block align-text-top" />
</NavbarBrand>
<NavItem>
<NavLink
href="/predict"
class="nav-full-height"
active={$page.url.pathname === '/predict'}
>
Прогнозирование
</NavLink>
</NavItem>
<NavItem>
<NavLink
href="/track"
class="nav-full-height"
active={$page.url.pathname === '/track'}
>
Слежение
</NavLink>
</NavItem>
</Nav>
<Nav navbar>
{#if isAuthenticated}
<Dropdown nav inNavbar>
<DropdownToggle nav caret class="nav-full-height">
Account
</DropdownToggle>
<DropdownMenu end>
<DropdownItem href="/user/account">
Account Settings
</DropdownItem>
<DropdownItem href="/user/templates">
Saved Templates
</DropdownItem>
<DropdownItem href="/user/predictions">
Prediction History
</DropdownItem>
<DropdownItem href="/user/flights">
Flight History
</DropdownItem>
<DropdownItem divider />
<DropdownItem on:click={handleLogout}>
Logout
</DropdownItem>
</DropdownMenu>
</Dropdown>
{:else}
<NavItem>
<NavLink
href="/login"
class="nav-full-height"
active={$page.url.pathname === '/login'}
>
Login
</NavLink>
</NavItem>
{/if}
</Nav>
</Navbar>
<style>
.custom-navbar {
height: 44px;
padding-top: 0rem;
padding-bottom: 0rem;
z-index: 1000;
border: none;
}
.nav-link {
color: inherit;
padding-left: 1rem !important;
padding-right: 1rem !important;
padding-top: 12px;
background-color: var(--bs-light);
margin-right: 1px;
}
.nav-link:hover {
color: var(--bs-light);
background-color: var(--bs-primary);
}
.nav-link.active {
color: var(--bs-light);
background-color: var(--bs-primary);
}
.nav-full-height {
display: flex;
align-items: center;
height: 100%;
}
.white-bg {
background-color: #fff !important;
}
.navbar-brand {
margin-right: 1em;
}
</style>

View file

@ -1,18 +1,12 @@
<script>
import { goto } from '$app/navigation';
import { login } from '$lib/auth';
let username = '';
let password = '';
let error = '';
let isLoading = false;
async function getCsrfToken() {
const response = await fetch('http://127.0.0.1:8000/api/session/', {
credentials: 'include'
});
return response.headers.get('X-CSRFToken');
}
async function handleLogin() {
if (!username || !password) {
error = 'Please enter both username and password';
@ -26,37 +20,15 @@
// login request
try {
const response = await fetch('http://127.0.0.1:8000/api/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': await getCsrfToken(),
},
body: JSON.stringify({
username: username,
password: password
}),
credentials: 'include' // For session/cookie based auth
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.message || 'Login failed');
}
const data = await response.json();
// Store token if using JWT
if (data.access) {
localStorage.setItem('accessToken', data.access);
if (data.refresh) {
localStorage.setItem('refreshToken', data.refresh);
}
}
await login(username, password);
goto('/'); // Redirect after successful login
} catch (err) {
error = err.message || 'Invalid credentials';
if (err instanceof Error) {
error = err.message || 'Invalid credentials';
} else {
error = 'Invalid credentials';
}
} finally {
isLoading = false;
}

View file

@ -1,6 +1,9 @@
<script>
<script lang="ts">
import { onMount } from 'svelte';
import * as L from 'leaflet';
import type { Map as LeafletMap } from 'leaflet';
import type { Marker } from 'leaflet';
import type { LatLng } from 'leaflet';
import 'leaflet/dist/leaflet.css';
import VelocityLayer from './velocity.svelte';
import { distHaversine } from '../lib/mathutil.ts';
@ -8,10 +11,7 @@
import { latestPredictionParsed } from '../lib/prediction.ts';
import { latestTelemetryParsed } from '../lib/telemetry.ts';
/**
* @type {L.Map}
*/
let map;
let map: typeof LeafletMap;
let mouseLat = 0;
let mouseLng = 0;
let inputLat = '56.3576';
@ -26,10 +26,7 @@
},
data: null, // здесь будут ваши данные
};
/**
* @type {null}
*/
let marker = null;
let marker: typeof Marker | null = null;
export { mouseLat, mouseLng, inputLat, inputLng, updateMapPosition };
@ -78,7 +75,7 @@
const response = await fetch('src/routes/testVelo.json');
velocityOptions.data = await response.json();
map.on('mousemove', (e) => {
map.on('mousemove', (e: any) => {
mouseLat = e.latlng.lat.toFixed(6);
mouseLng = e.latlng.lng.toFixed(6);
});
@ -97,12 +94,12 @@
});
});
const plotPrediction = (prediction) => {
const plotPrediction = (prediction: any) => {
console.log("Flight data parsed, creating map plot...");
// Clear existing map items
if (marker) {
map.eachLayer((layer) => {
map.eachLayer((layer: any) => {
if (layer instanceof L.Marker || layer instanceof L.Polyline) {
map.removeLayer(layer);
}
@ -163,12 +160,12 @@
map.setView(launch.latlng, 8);
};
const plotTelemetryTrack = (telemetry) => {
const plotTelemetryTrack = (telemetry: any) => {
console.log("Telemetry data parsed, creating map plot...");
// Clear existing map items
if (marker) {
map.eachLayer((layer) => {
map.eachLayer((layer: any) => {
if (layer instanceof L.Marker || layer instanceof L.Polyline) {
map.removeLayer(layer);
}
@ -203,7 +200,7 @@
// }
// Add telemetry markers to the map
telemetry.datapoints.forEach((point) => {
telemetry.datapoints.forEach((point: any) => {
const telemetryMarker = L.marker([point.latitude, point.longitude], {
title: `Telemetry (${point.latitude.toFixed(4)}, ${point.longitude.toFixed(4)}) at ${point.datetime}`,
icon: telemetryIcon,

View file

@ -0,0 +1,12 @@
<script lang="ts">
import Navbar from '../../Navbar.svelte';
</script>
<main>
<Navbar />
<div class="container">
<h1>User Account</h1>
<p>Manage your account settings here.</p>
<!-- Add account management components or links here -->
</div>
</main>

View file

@ -0,0 +1 @@
export const ssr =false;

View file

@ -0,0 +1 @@
export const ssr =false;

View file

@ -0,0 +1 @@
export const ssr =false;

View file

@ -0,0 +1 @@
export const ssr = false;