Compare commits
5 Commits
99ab34c76d
...
f0a7cad87a
| Author | SHA1 | Date | |
|---|---|---|---|
| f0a7cad87a | |||
| 4639b2d19c | |||
| 2dafceb304 | |||
| 22d7b98794 | |||
| f0ba6ac28f |
@@ -26,6 +26,7 @@ const groups: {
|
|||||||
{metric: "api_error_400", data_type: "number"},
|
{metric: "api_error_400", data_type: "number"},
|
||||||
{metric: "api_error_500", data_type: "number"},
|
{metric: "api_error_500", data_type: "number"},
|
||||||
{metric: "api_panic", data_type: "number"},
|
{metric: "api_panic", data_type: "number"},
|
||||||
|
{metric: "api_rate_limited", data_type: "number"},
|
||||||
{metric: "api_user_register", data_type: "number"},
|
{metric: "api_user_register", data_type: "number"},
|
||||||
{metric: "api_user_login", data_type: "number"},
|
{metric: "api_user_login", data_type: "number"},
|
||||||
{metric: "api_user_delete", data_type: "number"},
|
{metric: "api_user_delete", data_type: "number"},
|
||||||
@@ -37,6 +38,14 @@ const groups: {
|
|||||||
{metric: "filesystem_write_size", data_type: "bytes"},
|
{metric: "filesystem_write_size", data_type: "bytes"},
|
||||||
{metric: "filesystem_read", data_type: "number"},
|
{metric: "filesystem_read", data_type: "number"},
|
||||||
{metric: "filesystem_read_size", data_type: "bytes"},
|
{metric: "filesystem_read_size", data_type: "bytes"},
|
||||||
|
{metric: "filesystem_stat", data_type: "number"},
|
||||||
|
{metric: "filesystem_mkdir", data_type: "number"},
|
||||||
|
{metric: "filesystem_readdir", data_type: "number"},
|
||||||
|
{metric: "filesystem_update", data_type: "number"},
|
||||||
|
{metric: "filesystem_rename", data_type: "number"},
|
||||||
|
{metric: "filesystem_remove", data_type: "number"},
|
||||||
|
{metric: "filesystem_remove_all", data_type: "number"},
|
||||||
|
{metric: "filesystem_trash", data_type: "number"},
|
||||||
{metric: "filesystem_index_update", data_type: "number"},
|
{metric: "filesystem_index_update", data_type: "number"},
|
||||||
{metric: "filesystem_index_update_duration", data_type: "duration"},
|
{metric: "filesystem_index_update_duration", data_type: "duration"},
|
||||||
{
|
{
|
||||||
@@ -45,11 +54,13 @@ const groups: {
|
|||||||
agg_divisor: "filesystem_index_update",
|
agg_divisor: "filesystem_index_update",
|
||||||
data_type: "duration",
|
data_type: "duration",
|
||||||
},
|
},
|
||||||
|
{metric: "filesystem_index_walk_child", data_type: "number"},
|
||||||
]
|
]
|
||||||
}, {
|
}, {
|
||||||
title: "Task scheduler", expanded: false,
|
title: "Task scheduler", expanded: false,
|
||||||
graphs: [
|
graphs: [
|
||||||
{metric: "scheduler_filesystem_remove_orphan", data_type: "number"},
|
{metric: "scheduler_filesystem_remove_orphan", data_type: "number"},
|
||||||
|
{metric: "scheduler_filesystem_dangling_child", data_type: "number"},
|
||||||
{metric: "scheduler_timeseries_delete", data_type: "number"},
|
{metric: "scheduler_timeseries_delete", data_type: "number"},
|
||||||
],
|
],
|
||||||
}, {
|
}, {
|
||||||
@@ -62,6 +73,9 @@ const groups: {
|
|||||||
{metric: "database_query_rows_avg", agg_base: "database_query_rows", agg_divisor: "database_query", data_type: "number"},
|
{metric: "database_query_rows_avg", agg_base: "database_query_rows", agg_divisor: "database_query", data_type: "number"},
|
||||||
{metric: "database_query_retries", data_type: "number"},
|
{metric: "database_query_retries", data_type: "number"},
|
||||||
{metric: "database_query_error", data_type: "number"},
|
{metric: "database_query_error", data_type: "number"},
|
||||||
|
{metric: "database_walk_cache_hit", data_type: "number"},
|
||||||
|
{metric: "database_walk_cache_miss", data_type: "number"},
|
||||||
|
{metric: "database_walk_cache_stale", data_type: "number"},
|
||||||
],
|
],
|
||||||
}, {
|
}, {
|
||||||
title: "Pixelstore", expanded: false,
|
title: "Pixelstore", expanded: false,
|
||||||
@@ -148,7 +162,7 @@ const load_metrics = async (window: number, interval: number) => {
|
|||||||
metrics.timestamps[idx] = formatDate(val, true, true, true)
|
metrics.timestamps[idx] = formatDate(val, true, true, true)
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
alert(error)
|
alert(JSON.stringify(error))
|
||||||
} finally {
|
} finally {
|
||||||
loading_finish()
|
loading_finish()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ let {
|
|||||||
class:node_selected={child.fm_selected}
|
class:node_selected={child.fm_selected}
|
||||||
class:hidden={child.name.startsWith(".") && !show_hidden}
|
class:hidden={child.name.startsWith(".") && !show_hidden}
|
||||||
>
|
>
|
||||||
<img src={fs_node_icon(child, 64, 64)} class="node_icon" class:large_icons alt="icon"/>
|
<img src={fs_node_icon(child, 64, 64)} class="node_icon" class:large_icons loading="lazy" alt="icon"/>
|
||||||
<div class="node_name">
|
<div class="node_name">
|
||||||
{child.name}
|
{child.name}
|
||||||
</div>
|
</div>
|
||||||
@@ -69,6 +69,8 @@ let {
|
|||||||
/* backdrop-filter: blur(4px); */
|
/* backdrop-filter: blur(4px); */
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
|
content-visibility: auto;
|
||||||
|
contain-intrinsic-size: auto 2.4em;
|
||||||
}
|
}
|
||||||
.node:hover:not(.node_selected) {
|
.node:hover:not(.node_selected) {
|
||||||
background: var(--input_hover_background);
|
background: var(--input_hover_background);
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
|
<script lang="ts" module>
|
||||||
|
// Scroll offset per directory path. The file manager is destroyed when a file
|
||||||
|
// is opened, so the positions are kept here to survive the remount
|
||||||
|
const scroll_positions = new Map<string, number>()
|
||||||
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { fs_rename, fs_trash, FSNode } from "lib/FilesystemAPI.svelte"
|
import { fs_rename, fs_trash, FSNode } from "lib/FilesystemAPI.svelte"
|
||||||
import { onMount } from "svelte"
|
|
||||||
import CreateDirectory from "./CreateDirectory.svelte"
|
import CreateDirectory from "./CreateDirectory.svelte"
|
||||||
import ListView from "./ListView.svelte"
|
import ListView from "./ListView.svelte"
|
||||||
import GalleryView from "./GalleryView.svelte"
|
import GalleryView from "./GalleryView.svelte"
|
||||||
@@ -22,7 +27,7 @@ let {
|
|||||||
upload_widget,
|
upload_widget,
|
||||||
edit_window = $bindable(),
|
edit_window = $bindable(),
|
||||||
search_bar = $bindable(),
|
search_bar = $bindable(),
|
||||||
directory_view = $bindable(""),
|
directory_view = $bindable(localStorage.getItem("directory_view") || "list"),
|
||||||
children
|
children
|
||||||
}: {
|
}: {
|
||||||
nav: FSNavigator;
|
nav: FSNavigator;
|
||||||
@@ -33,12 +38,15 @@ let {
|
|||||||
children?: import('svelte').Snippet;
|
children?: import('svelte').Snippet;
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
let large_icons = $state(false)
|
let large_icons = $state(localStorage.getItem("large_icons") === "true")
|
||||||
let uploader!: FsUploadWidget
|
let uploader!: FsUploadWidget
|
||||||
let mode = $state("viewing")
|
let mode = $state("viewing")
|
||||||
let creating_dir = $state(false)
|
let creating_dir = $state(false)
|
||||||
let show_hidden = $state(false)
|
let show_hidden = $state(false)
|
||||||
let file_menu!: FileMenu
|
let file_menu!: FileMenu
|
||||||
|
$effect(() => {
|
||||||
|
window.scrollTo(0, scroll_positions.get($nav.base.path) ?? 0)
|
||||||
|
})
|
||||||
|
|
||||||
export const upload = (files: File[]) => {
|
export const upload = (files: File[]) => {
|
||||||
return uploader.upload_files(files)
|
return uploader.upload_files(files)
|
||||||
@@ -270,18 +278,13 @@ const move_here = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
if(typeof Storage !== "undefined") {
|
|
||||||
directory_view = localStorage.getItem("directory_view")
|
|
||||||
large_icons = localStorage.getItem("large_icons") === "true"
|
|
||||||
}
|
|
||||||
if (directory_view === "" || directory_view === null) {
|
|
||||||
directory_view = "list"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:window onkeydown={keypress} onkeyup={keypress} />
|
<svelte:window
|
||||||
|
onkeydown={keypress}
|
||||||
|
onkeyup={keypress}
|
||||||
|
onscroll={() => scroll_positions.set(nav.base.path, window.scrollY)}
|
||||||
|
/>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="container"
|
class="container"
|
||||||
@@ -407,9 +410,8 @@ onMount(() => {
|
|||||||
<style>
|
<style>
|
||||||
.container {
|
.container {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
overflow: auto;
|
|
||||||
display: block;
|
display: block;
|
||||||
height: 100%; /* Used for drop target */
|
min-height: 100%; /* Used for drop target */
|
||||||
}
|
}
|
||||||
.width_container {
|
.width_container {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
|
|||||||
@@ -27,11 +27,13 @@ let {
|
|||||||
class:large_icons
|
class:large_icons
|
||||||
title={child.name}
|
title={child.name}
|
||||||
>
|
>
|
||||||
<div
|
<img
|
||||||
class="node_icon"
|
class="node_icon"
|
||||||
class:cover={fs_node_type(child) === "image" || fs_node_type(child) === "video"}
|
class:cover={fs_node_type(child) === "image" || fs_node_type(child) === "video"}
|
||||||
style='background-image: url("{fs_node_icon(child, 256, 256)}");'>
|
src={fs_node_icon(child, 256, 256)}
|
||||||
</div>
|
loading="lazy"
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
<div class="node_name">
|
<div class="node_name">
|
||||||
{child.name}
|
{child.name}
|
||||||
</div>
|
</div>
|
||||||
@@ -51,6 +53,10 @@ let {
|
|||||||
aspect-ratio: 1;
|
aspect-ratio: 1;
|
||||||
width: 150px;
|
width: 150px;
|
||||||
height: 150px;
|
height: 150px;
|
||||||
|
/* Skips style, layout and paint for nodes which are off-screen. The
|
||||||
|
intrinsic size keeps the scrollbar accurate while they're skipped */
|
||||||
|
content-visibility: auto;
|
||||||
|
contain-intrinsic-size: auto 150px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: var(--body_background);
|
background: var(--body_background);
|
||||||
/* backdrop-filter: blur(4px); */
|
/* backdrop-filter: blur(4px); */
|
||||||
@@ -65,6 +71,7 @@ let {
|
|||||||
.file.large_icons {
|
.file.large_icons {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
height: 200px;
|
height: 200px;
|
||||||
|
contain-intrinsic-size: auto 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* On very small screens we switch to grid layout */
|
/* On very small screens we switch to grid layout */
|
||||||
@@ -93,13 +100,13 @@ let {
|
|||||||
}
|
}
|
||||||
.node_icon {
|
.node_icon {
|
||||||
flex: 1 1 0;
|
flex: 1 1 0;
|
||||||
|
min-height: 0;
|
||||||
|
width: 100%;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background-position: center;
|
object-fit: contain;
|
||||||
background-size: contain;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
}
|
}
|
||||||
.node_icon.cover {
|
.node_icon.cover {
|
||||||
background-size: cover;
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
.node_name {
|
.node_name {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ onMount(load_settings)
|
|||||||
class:hidden={child.name.startsWith(".") && !show_hidden}
|
class:hidden={child.name.startsWith(".") && !show_hidden}
|
||||||
>
|
>
|
||||||
<td>
|
<td>
|
||||||
<img src={fs_node_icon(child, 64, 64)} class="node_icon" class:large_icons alt="icon"/>
|
<img src={fs_node_icon(child, 64, 64)} class="node_icon" class:large_icons loading="lazy" alt="icon"/>
|
||||||
</td>
|
</td>
|
||||||
<td class="node_name">
|
<td class="node_name">
|
||||||
<a class="title_link" href={"/d"+fs_encode_path(child.path)}>
|
<a class="title_link" href={"/d"+fs_encode_path(child.path)}>
|
||||||
@@ -133,7 +133,7 @@ onMount(load_settings)
|
|||||||
{/if}
|
{/if}
|
||||||
</td>
|
</td>
|
||||||
<td class="node_size hide_small" class:hidden={!config.id}>
|
<td class="node_size hide_small" class:hidden={!config.id}>
|
||||||
<CopyButton text={$nav.base.id} onclick={prevent} />
|
<CopyButton text={child.id} onclick={prevent} />
|
||||||
<a href="/d/{child.id}" onclick={prevent}>{child.id}</a>
|
<a href="/d/{child.id}" onclick={prevent}>{child.id}</a>
|
||||||
</td>
|
</td>
|
||||||
<td class="node_icons">
|
<td class="node_icons">
|
||||||
@@ -187,7 +187,6 @@ onMount(load_settings)
|
|||||||
background: var(--body_background);
|
background: var(--body_background);
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
backdrop-filter: blur(4px);
|
backdrop-filter: blur(4px);
|
||||||
max-width: 1200px;
|
|
||||||
margin: auto; /* center */
|
margin: auto; /* center */
|
||||||
}
|
}
|
||||||
td {
|
td {
|
||||||
|
|||||||
@@ -14,6 +14,54 @@ let playing = $state(false)
|
|||||||
let media_session = false
|
let media_session = false
|
||||||
let siblings: FSNode[] = $state([])
|
let siblings: FSNode[] = $state([])
|
||||||
|
|
||||||
|
// The player source is managed manually instead of with a src attribute so
|
||||||
|
// that change_track can swap it without svelte reloading it afterwards
|
||||||
|
let current_src = ""
|
||||||
|
$effect(() => {
|
||||||
|
const url = fs_path_url($nav.base.path)
|
||||||
|
if (url !== current_src) {
|
||||||
|
current_src = url
|
||||||
|
player.src = url
|
||||||
|
player.play().catch(() => {})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Preloads the next track into the browser cache so that switching to it
|
||||||
|
// doesn't have to wait for the network. Files are served with a one year cache
|
||||||
|
// lifetime, so the player picks the data straight out of the cache
|
||||||
|
const preloader = new Audio()
|
||||||
|
preloader.preload = "auto"
|
||||||
|
$effect(() => {
|
||||||
|
const index = siblings.findIndex(s => s.path === $nav.base.path)
|
||||||
|
const next = siblings[index + 1]
|
||||||
|
if (index !== -1 && next !== undefined && next.type === "file" && !nav.shuffle) {
|
||||||
|
preloader.src = fs_path_url(next.path)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Switching tracks starts playback of the new file immediately and only then
|
||||||
|
// navigates to it. Firefox on Android freezes a background tab as soon as it
|
||||||
|
// stops playing audio, so anything waiting on a network request after the
|
||||||
|
// ended event would never run
|
||||||
|
const change_track = (offset: number) => {
|
||||||
|
const index = siblings.findIndex(s => s.path === $nav.base.path)
|
||||||
|
if (index === -1) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const next = nav.shuffle ?
|
||||||
|
siblings[Math.floor(Math.random() * siblings.length)] :
|
||||||
|
siblings[index + offset]
|
||||||
|
if (next === undefined || next.type === "dir") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
current_src = fs_path_url(next.path)
|
||||||
|
player.src = current_src
|
||||||
|
player.play().catch(() => {})
|
||||||
|
nav.navigate(next.path, true)
|
||||||
|
}
|
||||||
|
|
||||||
export const toggle_playback = () => playing ? player.pause() : player.play()
|
export const toggle_playback = () => playing ? player.pause() : player.play()
|
||||||
export const toggle_mute = () => player.muted = !player.muted
|
export const toggle_mute = () => player.muted = !player.muted
|
||||||
|
|
||||||
@@ -29,24 +77,29 @@ export const seek = (delta: number) => {
|
|||||||
|
|
||||||
var background_div: HTMLDivElement
|
var background_div: HTMLDivElement
|
||||||
export const update = async () => {
|
export const update = async () => {
|
||||||
|
siblings = await nav.get_siblings()
|
||||||
|
|
||||||
|
let cover = ""
|
||||||
|
for (const sib of siblings) {
|
||||||
|
const lower = sib.name.toLowerCase()
|
||||||
|
if (lower === "cover.jpg" || lower === "cover.png" || lower === "cover.webp") {
|
||||||
|
cover = fs_path_url(sib.path)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always assigned, so that the cover of the previous album is cleared when
|
||||||
|
// the new one doesn't have one
|
||||||
|
background_div.style.backgroundImage = cover === "" ? "" : `url("${cover}")`
|
||||||
|
|
||||||
if (media_session) {
|
if (media_session) {
|
||||||
navigator.mediaSession.metadata = new MediaMetadata({
|
navigator.mediaSession.metadata = new MediaMetadata({
|
||||||
title: nav.base.name,
|
title: nav.base.name,
|
||||||
artist: "Nova",
|
artist: "Nova",
|
||||||
album: "unknown",
|
album: "unknown",
|
||||||
|
artwork: cover === "" ? [] : [{ src: cover }],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
siblings = await nav.get_siblings()
|
|
||||||
|
|
||||||
for(const sib of siblings) {
|
|
||||||
const lower = sib.name.toLowerCase()
|
|
||||||
if (lower === "cover.jpg" || lower === "cover.png" || lower === "cover.webp") {
|
|
||||||
console.debug("Found album cover image", sib)
|
|
||||||
background_div.style.backgroundImage = `url("/api/filesystem/${sib.path}")`
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
@@ -55,8 +108,8 @@ onMount(() => {
|
|||||||
navigator.mediaSession.setActionHandler('play', () => player.play());
|
navigator.mediaSession.setActionHandler('play', () => player.play());
|
||||||
navigator.mediaSession.setActionHandler('pause', () => player.pause());
|
navigator.mediaSession.setActionHandler('pause', () => player.pause());
|
||||||
navigator.mediaSession.setActionHandler('stop', () => player.pause());
|
navigator.mediaSession.setActionHandler('stop', () => player.pause());
|
||||||
navigator.mediaSession.setActionHandler('previoustrack', () => nav.open_sibling(-1));
|
navigator.mediaSession.setActionHandler('previoustrack', () => change_track(-1));
|
||||||
navigator.mediaSession.setActionHandler('nexttrack', () => nav.open_sibling(1));
|
navigator.mediaSession.setActionHandler('nexttrack', () => change_track(1));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
@@ -68,16 +121,14 @@ onMount(() => {
|
|||||||
<audio
|
<audio
|
||||||
bind:this={player}
|
bind:this={player}
|
||||||
class="player"
|
class="player"
|
||||||
src={fs_path_url($nav.base.path)}
|
|
||||||
autoplay
|
|
||||||
controls
|
controls
|
||||||
onpause={() => playing = false}
|
onpause={() => playing = false}
|
||||||
onplay={() => playing = true}
|
onplay={() => playing = true}
|
||||||
onended={() => nav.open_sibling(1)}>
|
onended={() => change_track(1)}>
|
||||||
<track kind="captions"/>
|
<track kind="captions"/>
|
||||||
</audio>
|
</audio>
|
||||||
<div style="text-align: center;">
|
<div style="text-align: center;">
|
||||||
<button onclick={() => nav.open_sibling(-1)}><i class="icon">skip_previous</i></button>
|
<button onclick={() => change_track(-1)}><i class="icon">skip_previous</i></button>
|
||||||
<button onclick={() => seek(-10)}><i class="icon">replay_10</i></button>
|
<button onclick={() => seek(-10)}><i class="icon">replay_10</i></button>
|
||||||
<button onclick={toggle_playback}>
|
<button onclick={toggle_playback}>
|
||||||
{#if playing}
|
{#if playing}
|
||||||
@@ -87,7 +138,7 @@ onMount(() => {
|
|||||||
{/if}
|
{/if}
|
||||||
</button>
|
</button>
|
||||||
<button onclick={() => seek(10)}><i class="icon">forward_10</i></button>
|
<button onclick={() => seek(10)}><i class="icon">forward_10</i></button>
|
||||||
<button onclick={() => nav.open_sibling(1)}><i class="icon">skip_next</i></button>
|
<button onclick={() => change_track(1)}><i class="icon">skip_next</i></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2>Tracklist</h2>
|
<h2>Tracklist</h2>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { swipe_nav } from "lib/SwipeNavigate";
|
import { swipe_nav } from "lib/SwipeNavigate";
|
||||||
import { fs_path_url } from "lib/FilesystemAPI.svelte";
|
import { fs_path_url, fs_node_type, type FSNode } from "lib/FilesystemAPI.svelte";
|
||||||
import type { FSNavigator } from "filesystem/FSNavigator";
|
import type { FSNavigator } from "filesystem/FSNavigator";
|
||||||
import { loading_finish, loading_start } from "lib/Loading";
|
import { loading_finish, loading_start } from "lib/Loading";
|
||||||
|
|
||||||
@@ -56,11 +56,22 @@ export const update = async () => {
|
|||||||
if (siblings[i].name === nav.base.name) {
|
if (siblings[i].name === nav.base.name) {
|
||||||
swipe_prev = i > 0
|
swipe_prev = i > 0
|
||||||
swipe_next = i < siblings.length-1
|
swipe_next = i < siblings.length-1
|
||||||
|
preload(siblings[i-1])
|
||||||
|
preload(siblings[i+1])
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Preloads a neighbouring image into the browser cache so that swiping to it
|
||||||
|
// doesn't have to wait for the network. Files are served with a one year cache
|
||||||
|
// lifetime, so the <img> element picks the data straight out of the cache
|
||||||
|
const preload = (node: FSNode | undefined) => {
|
||||||
|
if (node !== undefined && fs_node_type(node) === "image") {
|
||||||
|
new Image().src = fs_path_url(node.path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const on_load = () => loading_finish()
|
const on_load = () => loading_finish()
|
||||||
|
|
||||||
const clamp = (val: number, min: number, max: number) => Math.min(Math.max(val, min), max)
|
const clamp = (val: number, min: number, max: number) => Math.min(Math.max(val, min), max)
|
||||||
|
|||||||
@@ -171,7 +171,6 @@ export const logout_user = async (redirect_path: string) => {
|
|||||||
{ method: "DELETE" },
|
{ method: "DELETE" },
|
||||||
))
|
))
|
||||||
|
|
||||||
// document.cookie = "pd_auth_key=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
|
|
||||||
window.location.pathname = redirect_path
|
window.location.pathname = redirect_path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
package webcontroller
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"fornaxian.tech/log"
|
|
||||||
"github.com/julienschmidt/httprouter"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (wc *WebController) serveLogout(
|
|
||||||
w http.ResponseWriter,
|
|
||||||
r *http.Request,
|
|
||||||
p httprouter.Params,
|
|
||||||
) {
|
|
||||||
if key, err := wc.getAPIKey(r); err == nil {
|
|
||||||
var api = wc.api.Login(key)
|
|
||||||
if err = api.DeleteUserSession(key); err != nil {
|
|
||||||
log.Warn("logout failed for session '%s': %s", key, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
||||||
}
|
|
||||||
@@ -256,7 +256,7 @@ func (wc *WebController) serveFile(path string) httprouter.Handle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (wc *WebController) getAPIKey(r *http.Request) (key string, err error) {
|
func (wc *WebController) getAPIKey(r *http.Request) (key string, err error) {
|
||||||
if cookie, err := r.Cookie("pd_auth_key"); err == nil {
|
if cookie, err := r.Cookie("nova_auth_key"); err == nil {
|
||||||
if len(cookie.Value) == 36 {
|
if len(cookie.Value) == 36 {
|
||||||
return cookie.Value, nil
|
return cookie.Value, nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user