feat: support for sub path hosting

This commit is contained in:
Maxi Quoß
2025-09-04 23:12:50 +02:00
parent 75969b037e
commit 7c640ac32e
8 changed files with 41 additions and 25 deletions

View File

@@ -116,6 +116,21 @@ upsnap.example.com {
}
```
### Run in sub path
You can run UpSnap on a different path than `/`, e.g. `/upsnap-sub-path/`. To do this in caddy, set the following:
```
http://localhost:8091 {
handle /upsnap-sub-path/* {
uri strip_prefix /upsnap-sub-path
reverse_proxy localhost:8090
}
}
```
Paths must end with a trailing `/`.
## 🐧 Install from the [AUR](https://aur.archlinux.org/packages/upsnap-bin)
```bash

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { resolve } from '$app/paths';
import { page } from '$app/stores';
import { asset, resolve } from '$app/paths';
import { page } from '$app/state';
import { m } from '$lib/paraglide/messages';
import { backendUrl, permission, pocketbase } from '$lib/stores/pocketbase';
import { settingsPub } from '$lib/stores/settings';
@@ -111,7 +111,7 @@
</div>
{/if}
<li>
<a href={resolve('/')} class="px-4 py-2" class:active={$page.url.pathname === '/'}
<a href={resolve('/')} class="px-4 py-2" class:active={page.url.pathname === resolve('/')}
><Fa icon={faHome} />{m.home_page_title()}</a
>
</li>
@@ -120,7 +120,7 @@
<a
href={resolve('/users')}
class="px-4 py-2"
class:active={$page.url.pathname.startsWith('/users')}
class:active={page.url.pathname.startsWith(resolve('/users'))}
><Fa icon={faUsersGear} />{m.users_page_title()}</a
>
</li>
@@ -128,7 +128,7 @@
<a
href={resolve('/settings/')}
class="px-4 py-2"
class:active={$page.url.pathname.startsWith('/settings')}
class:active={page.url.pathname.startsWith(resolve('/settings'))}
><Fa icon={faCog} />{m.settings_page_title()}</a
>
</li>
@@ -173,7 +173,7 @@
<img
src={$settingsPub?.id && $settingsPub?.favicon
? `${backendUrl}api/files/settings_public/${$settingsPub?.id}/${$settingsPub?.favicon}`
: '/gopher.svg'}
: asset('/gopher.svg')}
alt={$settingsPub?.website_title ? $settingsPub?.website_title : 'UpSnap'}
width="45"
height="45"
@@ -186,7 +186,7 @@
{/if}
<ul class="menu menu-horizontal h-full gap-1 px-1">
<li class="h-full">
<a href={resolve('/')} class="p-2" class:menu-active={$page.url.pathname === '/'}
<a href={resolve('/')} class="p-2" class:menu-active={page.url.pathname === resolve('/')}
><Fa icon={faHome} />{m.home_page_title()}</a
>
</li>
@@ -195,7 +195,7 @@
<a
href={resolve('/users')}
class="p-2"
class:menu-active={$page.url.pathname.startsWith('/users')}
class:menu-active={page.url.pathname.startsWith(resolve('/users'))}
><Fa icon={faUsersGear} />{m.users_page_title()}</a
>
</li>
@@ -203,7 +203,7 @@
<a
href={resolve('/settings/')}
class="p-2"
class:menu-active={$page.url.pathname.startsWith('/settings')}
class:menu-active={page.url.pathname.startsWith(resolve('/settings'))}
><Fa icon={faCog} />{m.settings_page_title()}</a
>
</li>
@@ -254,7 +254,7 @@
<div class="dropdown dropdown-end">
<label tabindex="-1" class="avatar btn btn-circle btn-ghost" for="avatar">
<div class="w-10 rounded-full" id="avatar">
<img src="/avatars/avatar{avatar}.svg" alt="Avatar {avatar}" />
<img src={asset(`/avatars/avatar${avatar}.svg`)} alt="Avatar {avatar}" />
</div>
</label>
<ul

View File

@@ -1,9 +1,10 @@
import { resolve } from '$app/paths';
import type { Permission } from '$lib/types/permission';
import PocketBase from 'pocketbase';
import { writable } from 'svelte/store';
// set backend url based on environment
export const backendUrl = import.meta.env.DEV ? 'http://127.0.0.1:8090/' : '/';
export const backendUrl = import.meta.env.DEV ? 'http://127.0.0.1:8090/' : resolve('/');
// connect to backend
const pb = new PocketBase(backendUrl);

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { resolve } from '$app/paths';
import { asset, resolve } from '$app/paths';
import { page } from '$app/state';
import Navbar from '$lib/components/Navbar.svelte';
import Transition from '$lib/components/Transition.svelte';
@@ -90,7 +90,7 @@
rel="shortcut icon"
href={$settingsPub?.id && $settingsPub?.favicon
? `${backendUrl}api/files/settings_public/${$settingsPub?.id}/${$settingsPub?.favicon}`
: '/gopher.svg'}
: asset('/gopher.svg')}
/>
{#if $settingsPub === undefined}
<title>UpSnap</title>
@@ -99,7 +99,7 @@
{/if}
</svelte:head>
{#if authIsValid && !page.url.pathname.startsWith('/welcome')}
{#if authIsValid && !page.url.pathname.startsWith(resolve('/welcome'))}
<Navbar />
{/if}

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { resolve } from '$app/paths';
import { asset, resolve } from '$app/paths';
import { m } from '$lib/paraglide/messages';
import type { Locale } from '$lib/paraglide/runtime';
import { getLocale, locales, setLocale } from '$lib/paraglide/runtime';
@@ -145,7 +145,7 @@
<div class="w-16 rounded-full">
{#if $pocketbase.authStore.record?.id}
<img
src="/avatars/avatar{newAvatar ?? $pocketbase.authStore.record?.avatar}.svg"
src={asset(`/avatars/avatar${newAvatar ?? $pocketbase.authStore.record?.avatar}.svg`)}
alt="Avatar {newAvatar ?? $pocketbase.authStore.record?.avatar}"
/>
{/if}
@@ -192,7 +192,7 @@
role="none"
>
{#if $pocketbase.authStore.record?.id}
<img src="/avatars/avatar{i}.svg" alt="{m.account_avatar_title()} {i}" />
<img src={asset(`/avatars/avatar${i}.svg`)} alt="{m.account_avatar_title()} {i}" />
{/if}
</div>
</div>

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { resolve } from '$app/paths';
import { page } from '$app/stores';
import { page } from '$app/state';
import DeviceForm from '$lib/components/DeviceForm.svelte';
import NetworkScan from '$lib/components/NetworkScan.svelte';
import { m } from '$lib/paraglide/messages';
@@ -49,7 +49,7 @@
$effect(() => {
if (Object.hasOwn($permission, 'create')) {
if (!$pocketbase.authStore.isSuperuser && !$permission.create) {
toast(m.toasts_no_permission({ url: $page.url.pathname }), {
toast(m.toasts_no_permission({ url: page.url.pathname }), {
icon: '⛔'
});
goto(resolve('/'));

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { resolve } from '$app/paths';
import { asset, resolve } from '$app/paths';
import { page } from '$app/state';
import { PUBLIC_VERSION } from '$env/static/public';
import PageLoading from '$lib/components/PageLoading.svelte';
@@ -194,7 +194,7 @@ second (059, optional)
class="h-36"
src={$settingsPub.favicon !== ''
? `${backendUrl}api/files/settings_public/${$settingsPub.id}/${$settingsPub.favicon}`
: '/gopher.svg'}
: asset('/gopher.svg')}
alt="Favicon preview"
bind:this={faviconPreview}
/>

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { resolve } from '$app/paths';
import { page } from '$app/stores';
import { asset, resolve } from '$app/paths';
import { page } from '$app/state';
import PageLoading from '$lib/components/PageLoading.svelte';
import { m } from '$lib/paraglide/messages';
import { pocketbase } from '$lib/stores/pocketbase';
@@ -37,7 +37,7 @@
onMount(() => {
if (!$pocketbase.authStore.isSuperuser) {
toast(m.toasts_no_permission({ url: $page.url.pathname }), {
toast(m.toasts_no_permission({ url: page.url.pathname }), {
icon: '⛔'
});
goto(resolve('/'));
@@ -197,7 +197,7 @@
<h2 class="card-title">
<label tabindex="-1" class="avatar" for="avatar{index}">
<div class="w-10 rounded-full" id="avatar{index}">
<img src="/avatars/avatar{user.avatar}.svg" alt="Avatar {user.avatar}" />
<img src={asset(`/avatars/avatar${user.avatar}.svg`)} alt="Avatar {user.avatar}" />
</div>
</label>
{user.username}