mirror of
https://github.com/seriousm4x/UpSnap.git
synced 2026-04-05 08:53:55 -04:00
* from now on, upsnap always requires authentification (user or admin) * users can view the dashboard and use wake/shutdown (read only) * admins can do everything (add, modify and delete) * make navbar collapse on small screens
This commit is contained in:
55
backend/migrations/1677961980_created_users.go
Normal file
55
backend/migrations/1677961980_created_users.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/pocketbase/dbx"
|
||||||
|
"github.com/pocketbase/pocketbase/daos"
|
||||||
|
m "github.com/pocketbase/pocketbase/migrations"
|
||||||
|
"github.com/pocketbase/pocketbase/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
m.Register(func(db dbx.Builder) error {
|
||||||
|
jsonData := `{
|
||||||
|
"id": "27do0wbcuyfmbmx",
|
||||||
|
"created": "2023-03-04 20:33:00.558Z",
|
||||||
|
"updated": "2023-03-04 20:33:00.558Z",
|
||||||
|
"name": "users",
|
||||||
|
"type": "auth",
|
||||||
|
"system": false,
|
||||||
|
"schema": [],
|
||||||
|
"listRule": null,
|
||||||
|
"viewRule": null,
|
||||||
|
"createRule": null,
|
||||||
|
"updateRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"options": {
|
||||||
|
"allowEmailAuth": true,
|
||||||
|
"allowOAuth2Auth": false,
|
||||||
|
"allowUsernameAuth": true,
|
||||||
|
"exceptEmailDomains": [],
|
||||||
|
"manageRule": null,
|
||||||
|
"minPasswordLength": 8,
|
||||||
|
"onlyEmailDomains": [],
|
||||||
|
"requireEmail": false
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
collection := &models.Collection{}
|
||||||
|
if err := json.Unmarshal([]byte(jsonData), &collection); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return daos.New(db).SaveCollection(collection)
|
||||||
|
}, func(db dbx.Builder) error {
|
||||||
|
dao := daos.New(db);
|
||||||
|
|
||||||
|
collection, err := dao.FindCollectionByNameOrId("27do0wbcuyfmbmx")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return dao.DeleteCollection(collection)
|
||||||
|
})
|
||||||
|
}
|
||||||
50
backend/migrations/1677962170_updated_devices.go
Normal file
50
backend/migrations/1677962170_updated_devices.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pocketbase/dbx"
|
||||||
|
"github.com/pocketbase/pocketbase/daos"
|
||||||
|
m "github.com/pocketbase/pocketbase/migrations"
|
||||||
|
"github.com/pocketbase/pocketbase/tools/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
m.Register(func(db dbx.Builder) error {
|
||||||
|
dao := daos.New(db);
|
||||||
|
|
||||||
|
collection, err := dao.FindCollectionByNameOrId("z5lghx2r3tm45n1")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
collection.ListRule = types.Pointer("@request.auth.id != \"\"")
|
||||||
|
|
||||||
|
collection.ViewRule = types.Pointer("@request.auth.id != \"\"")
|
||||||
|
|
||||||
|
collection.CreateRule = nil
|
||||||
|
|
||||||
|
collection.UpdateRule = nil
|
||||||
|
|
||||||
|
collection.DeleteRule = nil
|
||||||
|
|
||||||
|
return dao.SaveCollection(collection)
|
||||||
|
}, func(db dbx.Builder) error {
|
||||||
|
dao := daos.New(db);
|
||||||
|
|
||||||
|
collection, err := dao.FindCollectionByNameOrId("z5lghx2r3tm45n1")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
collection.ListRule = nil
|
||||||
|
|
||||||
|
collection.ViewRule = nil
|
||||||
|
|
||||||
|
collection.CreateRule = types.Pointer("")
|
||||||
|
|
||||||
|
collection.UpdateRule = types.Pointer("")
|
||||||
|
|
||||||
|
collection.DeleteRule = types.Pointer("")
|
||||||
|
|
||||||
|
return dao.SaveCollection(collection)
|
||||||
|
})
|
||||||
|
}
|
||||||
50
backend/migrations/1677962204_updated_ports.go
Normal file
50
backend/migrations/1677962204_updated_ports.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pocketbase/dbx"
|
||||||
|
"github.com/pocketbase/pocketbase/daos"
|
||||||
|
m "github.com/pocketbase/pocketbase/migrations"
|
||||||
|
"github.com/pocketbase/pocketbase/tools/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
m.Register(func(db dbx.Builder) error {
|
||||||
|
dao := daos.New(db);
|
||||||
|
|
||||||
|
collection, err := dao.FindCollectionByNameOrId("cti4l8f4mz8df3r")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
collection.ListRule = types.Pointer("@request.auth.id != \"\"")
|
||||||
|
|
||||||
|
collection.ViewRule = types.Pointer("@request.auth.id != \"\"")
|
||||||
|
|
||||||
|
collection.CreateRule = nil
|
||||||
|
|
||||||
|
collection.UpdateRule = nil
|
||||||
|
|
||||||
|
collection.DeleteRule = nil
|
||||||
|
|
||||||
|
return dao.SaveCollection(collection)
|
||||||
|
}, func(db dbx.Builder) error {
|
||||||
|
dao := daos.New(db);
|
||||||
|
|
||||||
|
collection, err := dao.FindCollectionByNameOrId("cti4l8f4mz8df3r")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
collection.ListRule = nil
|
||||||
|
|
||||||
|
collection.ViewRule = nil
|
||||||
|
|
||||||
|
collection.CreateRule = types.Pointer("")
|
||||||
|
|
||||||
|
collection.UpdateRule = types.Pointer("")
|
||||||
|
|
||||||
|
collection.DeleteRule = types.Pointer("")
|
||||||
|
|
||||||
|
return dao.SaveCollection(collection)
|
||||||
|
})
|
||||||
|
}
|
||||||
50
backend/migrations/1677962213_updated_settings.go
Normal file
50
backend/migrations/1677962213_updated_settings.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pocketbase/dbx"
|
||||||
|
"github.com/pocketbase/pocketbase/daos"
|
||||||
|
m "github.com/pocketbase/pocketbase/migrations"
|
||||||
|
"github.com/pocketbase/pocketbase/tools/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
m.Register(func(db dbx.Builder) error {
|
||||||
|
dao := daos.New(db);
|
||||||
|
|
||||||
|
collection, err := dao.FindCollectionByNameOrId("nmj3ko20gzkg8n3")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
collection.ListRule = types.Pointer("@request.auth.id != \"\"")
|
||||||
|
|
||||||
|
collection.ViewRule = types.Pointer("@request.auth.id != \"\"")
|
||||||
|
|
||||||
|
collection.CreateRule = nil
|
||||||
|
|
||||||
|
collection.UpdateRule = nil
|
||||||
|
|
||||||
|
collection.DeleteRule = nil
|
||||||
|
|
||||||
|
return dao.SaveCollection(collection)
|
||||||
|
}, func(db dbx.Builder) error {
|
||||||
|
dao := daos.New(db);
|
||||||
|
|
||||||
|
collection, err := dao.FindCollectionByNameOrId("nmj3ko20gzkg8n3")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
collection.ListRule = nil
|
||||||
|
|
||||||
|
collection.ViewRule = nil
|
||||||
|
|
||||||
|
collection.CreateRule = types.Pointer("")
|
||||||
|
|
||||||
|
collection.UpdateRule = types.Pointer("")
|
||||||
|
|
||||||
|
collection.DeleteRule = types.Pointer("")
|
||||||
|
|
||||||
|
return dao.SaveCollection(collection)
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -41,6 +41,7 @@ func StartPocketBase(distDirFS fs.FS) {
|
|||||||
Handler: HandlerWake,
|
Handler: HandlerWake,
|
||||||
Middlewares: []echo.MiddlewareFunc{
|
Middlewares: []echo.MiddlewareFunc{
|
||||||
apis.ActivityLogger(App),
|
apis.ActivityLogger(App),
|
||||||
|
apis.RequireAdminOrRecordAuth("users"),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -51,6 +52,7 @@ func StartPocketBase(distDirFS fs.FS) {
|
|||||||
Handler: HandlerShutdown,
|
Handler: HandlerShutdown,
|
||||||
Middlewares: []echo.MiddlewareFunc{
|
Middlewares: []echo.MiddlewareFunc{
|
||||||
apis.ActivityLogger(App),
|
apis.ActivityLogger(App),
|
||||||
|
apis.RequireAdminOrRecordAuth("users"),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -61,6 +63,7 @@ func StartPocketBase(distDirFS fs.FS) {
|
|||||||
Handler: HandlerScan,
|
Handler: HandlerScan,
|
||||||
Middlewares: []echo.MiddlewareFunc{
|
Middlewares: []echo.MiddlewareFunc{
|
||||||
apis.ActivityLogger(App),
|
apis.ActivityLogger(App),
|
||||||
|
apis.RequireAdminAuth(),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { dev } from '$app/environment';
|
import { dev } from '$app/environment';
|
||||||
import { parseISO, formatDistance } from 'date-fns';
|
import { parseISO, formatDistance } from 'date-fns';
|
||||||
|
import { pocketbase } from '@stores/pocketbase';
|
||||||
import {
|
import {
|
||||||
faPowerOff,
|
faPowerOff,
|
||||||
faEllipsisVertical,
|
faEllipsisVertical,
|
||||||
@@ -16,11 +17,19 @@
|
|||||||
export let now;
|
export let now;
|
||||||
|
|
||||||
function shutdown() {
|
function shutdown() {
|
||||||
fetch(`${dev ? 'http://localhost:8090' : ''}/api/upsnap/shutdown/${device.id}`);
|
fetch(`${dev ? 'http://localhost:8090' : ''}/api/upsnap/shutdown/${device.id}`, {
|
||||||
|
headers: {
|
||||||
|
Authorization: $pocketbase.authStore.baseToken
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function wake() {
|
function wake() {
|
||||||
fetch(`${dev ? 'http://localhost:8090' : ''}/api/upsnap/wake/${device.id}`);
|
fetch(`${dev ? 'http://localhost:8090' : ''}/api/upsnap/wake/${device.id}`, {
|
||||||
|
headers: {
|
||||||
|
Authorization: $pocketbase.authStore.baseToken
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
78
frontend/src/components/Login.svelte
Normal file
78
frontend/src/components/Login.svelte
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<script>
|
||||||
|
import { pocketbase, authorizedStore } from '@stores/pocketbase';
|
||||||
|
|
||||||
|
let username;
|
||||||
|
let password;
|
||||||
|
let isAdmin;
|
||||||
|
let error = {
|
||||||
|
hidden: true,
|
||||||
|
msg: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
async function login() {
|
||||||
|
if (isAdmin) {
|
||||||
|
$pocketbase.admins
|
||||||
|
.authWithPassword(username, password)
|
||||||
|
.then(() => {
|
||||||
|
authorizedStore.set(true);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
authorizedStore.set(false);
|
||||||
|
error.msg = err;
|
||||||
|
error.hidden = false;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$pocketbase
|
||||||
|
.collection('users')
|
||||||
|
.authWithPassword(username, password)
|
||||||
|
.then(() => {
|
||||||
|
authorizedStore.set(true);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
authorizedStore.set(false);
|
||||||
|
error.msg = err;
|
||||||
|
error.hidden = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="container text-dark-emphasis h-100">
|
||||||
|
<div class="row h-100 justify-content-center align-items-center">
|
||||||
|
<div class="col-md-6 col-lg-5">
|
||||||
|
<form class="w-100" on:submit|preventDefault={login}>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="username" class="form-label">Username or email address</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
id="username"
|
||||||
|
bind:value={username}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="password" class="form-label">Password</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
class="form-control"
|
||||||
|
id="password"
|
||||||
|
bind:value={password}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3 form-check">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
class="form-check-input"
|
||||||
|
id="isAdminCheck"
|
||||||
|
bind:checked={isAdmin}
|
||||||
|
/>
|
||||||
|
<label class="form-check-label" for="isAdminCheck">Admin Login</label>
|
||||||
|
</div>
|
||||||
|
<div class="callout callout-danger" class:d-none={error.hidden}>{error.msg}</div>
|
||||||
|
<button class="btn btn-secondary w-100" type="submit">Login</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -1,17 +1,44 @@
|
|||||||
<script>
|
<script>
|
||||||
|
import { onMount } from 'svelte';
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import { theme } from '@stores/theme';
|
import { theme } from '@stores/theme';
|
||||||
import { pocketbase, settings } from '@stores/pocketbase';
|
import { pocketbase, authorizedStore, settings } from '@stores/pocketbase';
|
||||||
import { faSun, faMoon, faCircleHalfStroke, faBrush } from '@fortawesome/free-solid-svg-icons';
|
import {
|
||||||
|
faSun,
|
||||||
|
faMoon,
|
||||||
|
faCircleHalfStroke,
|
||||||
|
faBrush,
|
||||||
|
faRightFromBracket
|
||||||
|
} from '@fortawesome/free-solid-svg-icons';
|
||||||
import Fa from 'svelte-fa';
|
import Fa from 'svelte-fa';
|
||||||
import { onMount } from 'svelte';
|
|
||||||
|
let userInfo = {
|
||||||
|
usernameOrEmail: '',
|
||||||
|
role: ''
|
||||||
|
};
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
$pocketbase.collection('settings').subscribe('*', function (e) {
|
$pocketbase.collection('settings').subscribe('*', function (e) {
|
||||||
settings.set(e.record);
|
settings.set(e.record);
|
||||||
document.title = $settings.website_title;
|
document.title = $settings.website_title;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if ($pocketbase.authStore.baseModel?.collectionName === 'users') {
|
||||||
|
userInfo.role = 'user';
|
||||||
|
} else {
|
||||||
|
userInfo.role = 'admin';
|
||||||
|
}
|
||||||
|
if ($pocketbase.authStore.baseModel?.username) {
|
||||||
|
userInfo.usernameOrEmail = $pocketbase.authStore.baseModel.username;
|
||||||
|
} else {
|
||||||
|
userInfo.usernameOrEmail = $pocketbase.authStore.baseModel.email;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
async function logout() {
|
||||||
|
$pocketbase.authStore.clear();
|
||||||
|
authorizedStore.set(false);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
@@ -22,7 +49,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<nav class="navbar navbar-expand">
|
<nav class="navbar navbar-expand-sm">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<a class="navbar-brand" href="/">
|
<a class="navbar-brand" href="/">
|
||||||
<img
|
<img
|
||||||
@@ -33,23 +60,36 @@
|
|||||||
class:me-2={$settings.website_title !== ''}
|
class:me-2={$settings.website_title !== ''}
|
||||||
/>{$settings.website_title ? $settings.website_title : ''}
|
/>{$settings.website_title ? $settings.website_title : ''}
|
||||||
</a>
|
</a>
|
||||||
<div class="collapse navbar-collapse" id="navbarNav">
|
<button
|
||||||
|
class="navbar-toggler border-0"
|
||||||
|
type="button"
|
||||||
|
data-bs-toggle="collapse"
|
||||||
|
data-bs-target="#navbarSupportedContent"
|
||||||
|
aria-controls="navbarSupportedContent"
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-label="Toggle navigation"
|
||||||
|
>
|
||||||
|
<span class="navbar-toggler-icon" />
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||||
<ul class="navbar-nav">
|
<ul class="navbar-nav">
|
||||||
<li class="nav-item">
|
{#if userInfo.role !== 'user'}
|
||||||
<a
|
<li class="nav-item">
|
||||||
class="nav-link"
|
<a
|
||||||
class:active={$page.url.pathname === '/' ? true : false}
|
class="nav-link"
|
||||||
href="/">Home</a
|
class:active={$page.url.pathname === '/' ? true : false}
|
||||||
>
|
href="/">Home</a
|
||||||
</li>
|
>
|
||||||
<li class="nav-item">
|
</li>
|
||||||
<a
|
<li class="nav-item me-3">
|
||||||
class="nav-link"
|
<a
|
||||||
class:active={$page.url.pathname === '/settings/' ? true : false}
|
class="nav-link"
|
||||||
href="/settings/">Settings</a
|
class:active={$page.url.pathname === '/settings/' ? true : false}
|
||||||
>
|
href="/settings/">Settings</a
|
||||||
</li>
|
>
|
||||||
<li class="nav-item dropdown ms-3">
|
</li>
|
||||||
|
{/if}
|
||||||
|
<li class="nav-item dropdown">
|
||||||
<div
|
<div
|
||||||
class="nav-link dropdown-toggle"
|
class="nav-link dropdown-toggle"
|
||||||
role="button"
|
role="button"
|
||||||
@@ -59,7 +99,7 @@
|
|||||||
<Fa icon={faBrush} class="me-2" />
|
<Fa icon={faBrush} class="me-2" />
|
||||||
Theme
|
Theme
|
||||||
</div>
|
</div>
|
||||||
<ul class="dropdown-menu border-0 p-1 shadow-sm">
|
<ul class="dropdown-menu border-0 p-1 shadow-sm mb-2">
|
||||||
<li>
|
<li>
|
||||||
<div
|
<div
|
||||||
class="dropdown-item"
|
class="dropdown-item"
|
||||||
@@ -96,6 +136,16 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<div class="ms-auto d-flex">
|
||||||
|
<button
|
||||||
|
class="text-dark-emphasis nav-link active border-0"
|
||||||
|
data-toggle="tooltip"
|
||||||
|
title="Logged in as {userInfo.role} "{userInfo.usernameOrEmail}""
|
||||||
|
on:click={() => logout()}
|
||||||
|
>
|
||||||
|
<Fa icon={faRightFromBracket} class="me-2" />Logout
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@@ -1,32 +1,19 @@
|
|||||||
<script>
|
<script>
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import { theme } from '@stores/theme';
|
|
||||||
import Navbar from '@components/Navbar.svelte';
|
import Navbar from '@components/Navbar.svelte';
|
||||||
|
import Login from '@components/Login.svelte';
|
||||||
import Transition from '@components/Transition.svelte';
|
import Transition from '@components/Transition.svelte';
|
||||||
import { pocketbase, settings, devices } from '@stores/pocketbase';
|
import { theme } from '@stores/theme';
|
||||||
|
import { pocketbase, authorizedStore } from '@stores/pocketbase';
|
||||||
|
|
||||||
let preferesDark;
|
let preferesDark;
|
||||||
|
let isAuth = false;
|
||||||
onMount(async () => {
|
|
||||||
let settingsRes = {};
|
|
||||||
settingsRes = await $pocketbase.collection('settings').getList(1, 1);
|
|
||||||
settings.set(settingsRes.items[0]);
|
|
||||||
|
|
||||||
let tempDevices = {};
|
|
||||||
const devicesRes = await $pocketbase.collection('devices').getFullList(200, {
|
|
||||||
sort: 'name',
|
|
||||||
expand: 'ports'
|
|
||||||
});
|
|
||||||
devicesRes.forEach((device) => {
|
|
||||||
tempDevices[device.id] = device;
|
|
||||||
});
|
|
||||||
devices.set(tempDevices);
|
|
||||||
});
|
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
// import bootstrap js
|
// import bootstrap js
|
||||||
import('bootstrap/js/dist/dropdown');
|
import('bootstrap/js/dist/dropdown');
|
||||||
|
import('bootstrap/js/dist/collapse');
|
||||||
|
|
||||||
// set dark mode
|
// set dark mode
|
||||||
preferesDark = window.matchMedia('(prefers-color-scheme: dark)');
|
preferesDark = window.matchMedia('(prefers-color-scheme: dark)');
|
||||||
@@ -46,6 +33,16 @@
|
|||||||
}
|
}
|
||||||
document.documentElement.setAttribute('data-bs-theme', t);
|
document.documentElement.setAttribute('data-bs-theme', t);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
authorizedStore.subscribe((state) => {
|
||||||
|
isAuth = state;
|
||||||
|
});
|
||||||
|
|
||||||
|
const pbCookie = localStorage.getItem('pocketbase_auth');
|
||||||
|
if (pbCookie) {
|
||||||
|
$pocketbase.authStore.loadFromCookie('pb_auth=' + pbCookie);
|
||||||
|
authorizedStore.set($pocketbase.authStore.isValid);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -67,11 +64,14 @@
|
|||||||
</script>
|
</script>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<Navbar />
|
{#if isAuth}
|
||||||
|
<Navbar />
|
||||||
<Transition url={$page.url}>
|
<Transition url={$page.url}>
|
||||||
<slot />
|
<slot />
|
||||||
</Transition>
|
</Transition>
|
||||||
|
{:else}
|
||||||
|
<Login />
|
||||||
|
{/if}
|
||||||
|
|
||||||
<style lang="scss" global>
|
<style lang="scss" global>
|
||||||
@import '../scss/main.scss';
|
@import '../scss/main.scss';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import DeviceCard from '@components/DeviceCard.svelte';
|
import DeviceCard from '@components/DeviceCard.svelte';
|
||||||
import { pocketbase, devices } from '@stores/pocketbase';
|
import { pocketbase, devices, settings } from '@stores/pocketbase';
|
||||||
import { sortDevices } from '../sorts';
|
import { sortDevices } from '../sorts';
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
@@ -32,6 +32,20 @@
|
|||||||
$devices[device.id].expand.ports[portIdx] = e.record;
|
$devices[device.id].expand.ports[portIdx] = e.record;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let settingsRes = {};
|
||||||
|
settingsRes = await $pocketbase.collection('settings').getList(1, 1);
|
||||||
|
settings.set(settingsRes.items[0]);
|
||||||
|
|
||||||
|
let tempDevices = {};
|
||||||
|
const devicesRes = await $pocketbase.collection('devices').getFullList(200, {
|
||||||
|
sort: 'name',
|
||||||
|
expand: 'ports'
|
||||||
|
});
|
||||||
|
devicesRes.forEach((device) => {
|
||||||
|
tempDevices[device.id] = device;
|
||||||
|
});
|
||||||
|
devices.set(tempDevices);
|
||||||
});
|
});
|
||||||
|
|
||||||
// update device date
|
// update device date
|
||||||
|
|||||||
@@ -67,7 +67,18 @@
|
|||||||
|
|
||||||
async function scanDevices() {
|
async function scanDevices() {
|
||||||
buttons.scan.state = 'waiting';
|
buttons.scan.state = 'waiting';
|
||||||
await $pocketbase.collection('settings').update($settings.id, $settings);
|
await $pocketbase
|
||||||
|
.collection('settings')
|
||||||
|
.update($settings.id, $settings)
|
||||||
|
.catch((err) => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
timeout = setTimeout(() => {
|
||||||
|
buttons.scan.error = '';
|
||||||
|
buttons.scan.state = 'none';
|
||||||
|
}, 3000);
|
||||||
|
buttons.scan.error = err;
|
||||||
|
buttons.scan.state = 'failed';
|
||||||
|
});
|
||||||
|
|
||||||
fetch(`${dev ? 'http://localhost:8090' : ''}/api/upsnap/scan`)
|
fetch(`${dev ? 'http://localhost:8090' : ''}/api/upsnap/scan`)
|
||||||
.then((res) => res.json())
|
.then((res) => res.json())
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ $dropdown-min-width: 0;
|
|||||||
@import '../../node_modules/bootstrap/scss/buttons';
|
@import '../../node_modules/bootstrap/scss/buttons';
|
||||||
@import '../../node_modules/bootstrap/scss/spinners';
|
@import '../../node_modules/bootstrap/scss/spinners';
|
||||||
@import '../../node_modules/bootstrap/scss/badge';
|
@import '../../node_modules/bootstrap/scss/badge';
|
||||||
|
@import '../../node_modules/bootstrap/scss/transitions';
|
||||||
|
|
||||||
@import '../../node_modules/bootstrap/scss/utilities/api';
|
@import '../../node_modules/bootstrap/scss/utilities/api';
|
||||||
|
|
||||||
@@ -59,6 +60,12 @@ section {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.navbar-toggler {
|
||||||
|
&:focus {
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,3 +18,4 @@ export const settings = writable({
|
|||||||
website_title: ''
|
website_title: ''
|
||||||
});
|
});
|
||||||
export const devices = writable({});
|
export const devices = writable({});
|
||||||
|
export const authorizedStore = writable(false);
|
||||||
|
|||||||
Reference in New Issue
Block a user