🔐 Removes auth from frontend

This commit is contained in:
Rune Harlyk
2024-10-29 20:58:48 +01:00
parent 1c6b9f79c5
commit 84633e5707
22 changed files with 4225 additions and 4282 deletions
+10 -10
View File
@@ -1,17 +1,17 @@
<script lang="ts">
import { onDestroy } from 'svelte';
import { user, location } from '$lib/stores';
import { onDestroy } from 'svelte';
import { location } from '$lib/stores';
let source = `${$location}/api/camera/stream?access_token=${$user.bearer_token}`;
let source = `${$location}/api/camera/stream`;
onDestroy(() => (source = '#'));
onDestroy(() => (source = '#'));
</script>
<div class="w-full h-full">
<img
src={source}
class="absolute object-cover blur-3xl w-full h-full -z-10"
alt="Live stream is down"
/>
<img src={source} class="object-contain w-full h-full" alt="Live stream is down" />
<img
src={source}
class="absolute object-cover blur-3xl w-full h-full -z-10"
alt="Live stream is down"
/>
<img src={source} class="object-contain w-full h-full" alt="Live stream is down" />
</div>
-111
View File
@@ -1,111 +0,0 @@
<script lang="ts">
import logo from '$lib/assets/logo512.png';
import { PasswordInput } from '$lib/components/input';
import { user } from '$lib/stores/user';
import { notifications } from '$lib/components/toasts/notifications';
import { fade, fly } from 'svelte/transition';
import { api } from '$lib/api';
import type { JWT } from '$lib/types/models';
import { Login } from './icons';
type SignInData = {
password: string;
username: string;
};
let username = '';
let password = '';
let loginFailed = false;
let token = { access_token: '' };
async function signInUser(data: SignInData) {
const result = await api.post<JWT>('/api/signIn', data);
if (result.isErr()) {
username = '';
password = '';
notifications.error('Wrong Username or Password!', 5000);
loginFailed = true;
setTimeout(() => {
loginFailed = false;
}, 1500);
return;
}
token = result.inner;
user.init(token.access_token);
username = $user.username;
notifications.success('User ' + username + ' signed in', 5000);
}
</script>
<div class="hero from-primary/30 to-secondary/30 min-h-screen bg-gradient-to-br">
<div
class="card lg:card-side bg-base-100 face shadow-2xl {loginFailed
? 'failure border-error border-2'
: ''}"
in:fly={{ delay: 200, y: 100, duration: 500 }}
out:fade={{ duration: 200 }}
>
<figure class="bg-base-200"><img src={logo} alt="Logo" class="h-auto w-48 lg:w-64" /></figure>
<div class="card-body w-80">
<h2 class="card-title text-2xl">Login</h2>
<form class="form-control w-full max-w-xs">
<label class="label" for="user">
<span class="label-text text-md">Username</span>
</label>
<input
type="text"
class="input input-bordered w-full max-w-xs"
id="user"
bind:value={username}
/>
<label class="label" for="pwd">
<span class="label-text text-md">Password</span>
</label>
<PasswordInput id="pwd" bind:value={password} />
<div class="card-actions mt-4 justify-end">
<button
class="btn btn-primary inline-flex items-center"
on:click={() => signInUser({ username, password })}
>
<Login class="mr-2 h-5 w-5" /><span>Login</span>
</button>
</div>
</form>
</div>
</div>
</div>
<style>
.failure {
animation: shake 0.82s cubic-bezier(0.36, 0.07, 0.19, 0.97) both;
transform: translate3d(0, 0, 0);
backface-visibility: hidden;
perspective: 1000px;
}
@keyframes shake {
10%,
90% {
transform: translatex(-1px);
}
20%,
80% {
transform: translatex(2px);
}
30%,
50%,
70% {
transform: translatex(-4px);
}
40%,
60% {
transform: translatex(4px);
}
}
</style>
+171 -183
View File
@@ -1,201 +1,189 @@
<script lang="ts">
import { page } from '$app/stores';
import { user } from '$lib/stores/user';
import { createEventDispatcher } from 'svelte';
import { useFeatureFlags } from '$lib/stores/featureFlags';
import UserButton from '../menu/UserButton.svelte';
import GithubButton from '../menu/GithubButton.svelte';
import LogoButton from '../menu/LogoButton.svelte';
import MenuList from '../menu/MenuList.svelte';
import {
Connection,
Users,
Settings,
MdiController,
Devices,
Camera,
Rotate3d,
MotorOutline,
Health,
Folder,
Update,
WiFi,
Router,
AP,
Remote,
Copyright,
NTP,
Metrics
} from '$lib/components/icons';
import appEnv from 'app-env';
import { page } from '$app/stores';
import { createEventDispatcher } from 'svelte';
import { useFeatureFlags } from '$lib/stores/featureFlags';
import GithubButton from '../menu/GithubButton.svelte';
import LogoButton from '../menu/LogoButton.svelte';
import MenuList from '../menu/MenuList.svelte';
import {
Connection,
Settings,
MdiController,
Devices,
Camera,
Rotate3d,
MotorOutline,
Health,
Folder,
Update,
WiFi,
Router,
AP,
Remote,
Copyright,
NTP,
Metrics
} from '$lib/components/icons';
import appEnv from 'app-env';
const features = useFeatureFlags();
const features = useFeatureFlags();
const appName = $page.data.app_name;
const appName = $page.data.app_name;
const copyright = $page.data.copyright;
const copyright = $page.data.copyright;
const github = { href: 'https://github.com/' + $page.data.github, active: true };
const github = { href: 'https://github.com/' + $page.data.github, active: true };
type menuItem = {
title: string;
icon: ConstructorOfATypedSvelteComponent;
href?: string;
feature: boolean;
active?: boolean;
submenu?: menuItem[];
};
type menuItem = {
title: string;
icon: ConstructorOfATypedSvelteComponent;
href?: string;
feature: boolean;
active?: boolean;
submenu?: menuItem[];
};
$: menuItems = [
{
title: 'Connection',
icon: WiFi,
href: '/connection',
feature: !appEnv.VITE_USE_HOST_NAME
},
{
title: 'Controller',
icon: MdiController,
href: '/controller',
feature: true
},
{
title: 'Peripherals',
icon: Devices,
feature: true,
submenu: [
{
title: 'I2C',
icon: Connection,
href: '/peripherals/i2c',
feature: true
},
{
title: 'Camera',
icon: Camera,
href: '/peripherals/camera',
feature: $features.camera
},
{
title: 'Servo',
icon: MotorOutline,
href: '/peripherals/servo',
feature: true
},
{
title: 'IMU',
icon: Rotate3d,
href: '/peripherals/imu',
feature: $features.imu || $features.mag || $features.bmp
}
]
},
{
title: 'Connections',
icon: Remote,
feature: $features.ntp,
submenu: [
{
title: 'NTP',
icon: NTP,
href: '/connections/ntp',
feature: $features.ntp
}
]
},
{
title: 'WiFi',
icon: WiFi,
feature: true,
submenu: [
{
title: 'WiFi Station',
icon: Router,
href: '/wifi/sta',
feature: true
},
{
title: 'Access Point',
icon: AP,
href: '/wifi/ap',
feature: true
}
]
},
{
title: 'Users',
icon: Users,
href: '/user',
feature: $features.security && $user.admin
},
{
title: 'System',
icon: Settings,
feature: true,
submenu: [
{
title: 'System Status',
icon: Health,
href: '/system/status',
feature: true
},
{
title: 'File System',
icon: Folder,
href: '/system/filesystem',
feature: true
},
{
title: 'System Metrics',
icon: Metrics,
href: '/system/metrics',
feature: $features.analytics
},
{
title: 'Firmware Update',
icon: Update,
href: '/system/update',
feature:
($features.ota || $features.upload_firmware || $features.download_firmware) &&
(!$features.security || $user.admin)
}
]
}
] as menuItem[];
$: menuItems = [
{
title: 'Connection',
icon: WiFi,
href: '/connection',
feature: !appEnv.VITE_USE_HOST_NAME
},
{
title: 'Controller',
icon: MdiController,
href: '/controller',
feature: true
},
{
title: 'Peripherals',
icon: Devices,
feature: true,
submenu: [
{
title: 'I2C',
icon: Connection,
href: '/peripherals/i2c',
feature: true
},
{
title: 'Camera',
icon: Camera,
href: '/peripherals/camera',
feature: $features.camera
},
{
title: 'Servo',
icon: MotorOutline,
href: '/peripherals/servo',
feature: true
},
{
title: 'IMU',
icon: Rotate3d,
href: '/peripherals/imu',
feature: $features.imu || $features.mag || $features.bmp
}
]
},
{
title: 'Connections',
icon: Remote,
feature: $features.ntp,
submenu: [
{
title: 'NTP',
icon: NTP,
href: '/connections/ntp',
feature: $features.ntp
}
]
},
{
title: 'WiFi',
icon: WiFi,
feature: true,
submenu: [
{
title: 'WiFi Station',
icon: Router,
href: '/wifi/sta',
feature: true
},
{
title: 'Access Point',
icon: AP,
href: '/wifi/ap',
feature: true
}
]
},
{
title: 'System',
icon: Settings,
feature: true,
submenu: [
{
title: 'System Status',
icon: Health,
href: '/system/status',
feature: true
},
{
title: 'File System',
icon: Folder,
href: '/system/filesystem',
feature: true
},
{
title: 'System Metrics',
icon: Metrics,
href: '/system/metrics',
feature: $features.analytics
},
{
title: 'Firmware Update',
icon: Update,
href: '/system/update',
feature:
$features.ota || $features.upload_firmware || $features.download_firmware
}
]
}
] as menuItem[];
const dispatch = createEventDispatcher();
const dispatch = createEventDispatcher();
function setActiveMenuItem(targetTitle: string) {
menuItems.forEach(item => {
item.active = item.title === targetTitle;
item.submenu?.forEach(subItem => {
subItem.active = subItem.title === targetTitle;
});
});
menuItems = menuItems;
dispatch('menuClicked');
}
function setActiveMenuItem(targetTitle: string) {
menuItems.forEach(item => {
item.active = item.title === targetTitle;
item.submenu?.forEach(subItem => {
subItem.active = subItem.title === targetTitle;
});
});
menuItems = menuItems;
dispatch('menuClicked');
}
$: setActiveMenuItem($page.data.title);
$: setActiveMenuItem($page.data.title);
const updateMenu = (event: any) => {
setActiveMenuItem(event.details);
};
const updateMenu = (event: any) => {
setActiveMenuItem(event.details);
};
</script>
<div class="bg-base-200 text-base-content flex h-full w-80 flex-col p-4">
<LogoButton {appName} />
<LogoButton {appName} />
<MenuList {menuItems} on:select{updateMenu} class="flex-grow flex-nowrap overflow-y-auto" />
<MenuList {menuItems} on:select{updateMenu} class="flex-grow flex-nowrap overflow-y-auto" />
<UserButton />
<div class="divider my-0" />
<div class="divider my-0" />
<div class="flex items-center justify-between">
<GithubButton {github} />
<div class="flex items-center justify-end text-sm gap-2">
<Copyright class="h-4 w-4" />{copyright}
</div>
</div>
<div class="flex items-center justify-between">
<GithubButton {github} />
<div class="flex items-center justify-end text-sm gap-2">
<Copyright class="h-4 w-4" />{copyright}
</div>
</div>
</div>
@@ -1,17 +0,0 @@
<script lang="ts">
import { user } from '$lib/stores';
import { useFeatureFlags } from "$lib/stores";
import { Avatar, Logout } from '../icons';
const features = useFeatureFlags();
</script>
{#if $features.security}
<div class="flex items-center">
<Avatar class="h-8 w-8" />
<span class="flex-grow px-4 text-xl font-bold">{$user.username}</span>
<button class="btn btn-ghost" on:click={user.invalidate}>
<Logout class="h-8 w-8 rotate-180" />
</button>
</div>
{/if}
@@ -1,113 +1,112 @@
<script lang="ts">
import { page } from '$app/stores';
import { openModal, closeAllModals } from 'svelte-modals';
import { user } from '$lib/stores/user';
import { notifications } from '$lib/components/toasts/notifications';
import ConfirmDialog from '$lib/components/ConfirmDialog.svelte';
import GithubUpdateDialog from '$lib/components/GithubUpdateDialog.svelte';
import { compareVersions } from 'compare-versions';
import { onMount } from 'svelte';
import { api } from '$lib/api';
import type { GithubRelease } from '$lib/types/models';
import { useFeatureFlags } from '$lib/stores/featureFlags';
import { Cancel, CloudDown, Firmware } from '../icons';
import { page } from '$app/stores';
import { openModal, closeAllModals } from 'svelte-modals';
import { notifications } from '$lib/components/toasts/notifications';
import ConfirmDialog from '$lib/components/ConfirmDialog.svelte';
const features = useFeatureFlags();
import GithubUpdateDialog from '$lib/components/GithubUpdateDialog.svelte';
import { compareVersions } from 'compare-versions';
import { onMount } from 'svelte';
import { api } from '$lib/api';
import type { GithubRelease } from '$lib/types/models';
import { useFeatureFlags } from '$lib/stores/featureFlags';
import { Cancel, CloudDown, Firmware } from '../icons';
export let update = false;
const features = useFeatureFlags();
let firmwareVersion: string;
let firmwareDownloadLink: string;
export let update = false;
async function getGithubAPI() {
const headers = {
accept: 'application/vnd.github+json',
'X-GitHub-Api-Version': '2022-11-28'
};
const result = await api.get<GithubRelease>(
`https://api.github.com/repos/${$page.data.github}/releases/latest`,
{ headers }
);
if (result.inner.message === '404' || result.inner.message == 'Not Found') {
console.warn('Error: Could not find releases in the repository');
return;
}
if (result.isErr()) {
console.error('Error:', result.inner);
return;
}
let firmwareVersion: string;
let firmwareDownloadLink: string;
const results = result.inner;
update = false;
firmwareVersion = '';
async function getGithubAPI() {
const headers = {
accept: 'application/vnd.github+json',
'X-GitHub-Api-Version': '2022-11-28'
};
const result = await api.get<GithubRelease>(
`https://api.github.com/repos/${$page.data.github}/releases/latest`,
{ headers }
);
if (result.inner.message === '404' || result.inner.message == 'Not Found') {
console.warn('Error: Could not find releases in the repository');
return;
}
if (result.isErr()) {
console.error('Error:', result.inner);
return;
}
if (compareVersions(results.tag_name, $features.firmware_version) === 1) {
// iterate over assets and find the correct one
for (let i = 0; i < results.assets.length; i++) {
// check if the asset is of type *.bin
if (
results.assets[i].name.includes('.bin') &&
results.assets[i].name.includes($features.firmware_built_target)
) {
update = true;
firmwareVersion = results.tag_name;
firmwareDownloadLink = results.assets[i].browser_download_url;
notifications.info('Firmware update available.', 5000);
}
}
}
}
const results = result.inner;
update = false;
firmwareVersion = '';
async function postGithubDownload(url: string) {
const result = await api.post('/api/downloadUpdate', { download_url: url });
if (result.isErr()) {
console.error('Error:', result.inner);
return;
}
}
if (compareVersions(results.tag_name, $features.firmware_version) === 1) {
// iterate over assets and find the correct one
for (let i = 0; i < results.assets.length; i++) {
// check if the asset is of type *.bin
if (
results.assets[i].name.includes('.bin') &&
results.assets[i].name.includes($features.firmware_built_target)
) {
update = true;
firmwareVersion = results.tag_name;
firmwareDownloadLink = results.assets[i].browser_download_url;
notifications.info('Firmware update available.', 5000);
}
}
}
}
onMount(async () => {
if ($features.download_firmware && (!$features.security || $user.admin)) {
await getGithubAPI();
const interval = setInterval(
async () => {
await getGithubAPI();
},
60 * 60 * 1000
); // once per hour
}
});
async function postGithubDownload(url: string) {
const result = await api.post('/api/downloadUpdate', { download_url: url });
if (result.isErr()) {
console.error('Error:', result.inner);
return;
}
}
function confirmGithubUpdate(url: string) {
openModal(ConfirmDialog, {
title: 'Confirm flashing new firmware to the device',
message: 'Are you sure you want to overwrite the existing firmware with a new one?',
labels: {
cancel: { label: 'Abort', icon: Cancel },
confirm: { label: 'Update', icon: CloudDown }
},
onConfirm: () => {
postGithubDownload(url);
openModal(GithubUpdateDialog, {
onConfirm: () => closeAllModals()
});
}
});
}
onMount(async () => {
if ($features.download_firmware) {
await getGithubAPI();
const interval = setInterval(
async () => {
await getGithubAPI();
},
60 * 60 * 1000
); // once per hour
}
});
function confirmGithubUpdate(url: string) {
openModal(ConfirmDialog, {
title: 'Confirm flashing new firmware to the device',
message: 'Are you sure you want to overwrite the existing firmware with a new one?',
labels: {
cancel: { label: 'Abort', icon: Cancel },
confirm: { label: 'Update', icon: CloudDown }
},
onConfirm: () => {
postGithubDownload(url);
openModal(GithubUpdateDialog, {
onConfirm: () => closeAllModals()
});
}
});
}
</script>
{#if update}
<div class="indicator flex-none">
<button
class="btn btn-square btn-ghost h-9 w-9"
on:click={() => confirmGithubUpdate(firmwareDownloadLink)}
>
<span
class="indicator-item indicator-top indicator-center badge badge-info badge-xs top-2 scale-75 lg:top-1"
>{firmwareVersion}</span
>
<Firmware class="h-7 w-7" />
</button>
</div>
<div class="indicator flex-none">
<button
class="btn btn-square btn-ghost h-9 w-9"
on:click={() => confirmGithubUpdate(firmwareDownloadLink)}
>
<span
class="indicator-item indicator-top indicator-center badge badge-info badge-xs top-2 scale-75 lg:top-1"
>{firmwareVersion}</span
>
<Firmware class="h-7 w-7" />
</button>
</div>
{/if}