mirror of
https://github.com/bartvdbraak/omnidash.git
synced 2025-04-27 15:31:21 +00:00
Merge pull request #212 from bartvdbraak/unlighthouse-auth
Add Dockerfile for backend service and unlighthouse authentication
This commit is contained in:
commit
3015ae0486
16 changed files with 3087 additions and 166 deletions
10
.github/workflows/unlighthouse.yaml
vendored
10
.github/workflows/unlighthouse.yaml
vendored
|
@ -74,14 +74,14 @@ jobs:
|
|||
timeout: 360
|
||||
|
||||
- name: Install Dependencies
|
||||
run: pnpm install -g @unlighthouse/cli puppeteer
|
||||
run: pnpm install
|
||||
|
||||
- name: Run Unlighthouse
|
||||
run: |
|
||||
unlighthouse-ci \
|
||||
--site "${{ github.ref == 'refs/heads/main' && env.WEBSITE_URL || steps.vercel_preview_url.outputs.preview_url }}" \
|
||||
--reporter jsonExpanded \
|
||||
--build-static
|
||||
export SCAN_URL="${{ github.ref == 'refs/heads/main' && env.WEBSITE_URL || steps.vercel_preview_url.outputs.preview_url }}"
|
||||
export AUTH_COOKIE="$(curl "https://$SCAN_URL/auth?/login" -H "Origin: https://$SCAN_URL" -F "email=test_user" -F "password=${{ secrets.TEST_USER_PASSWORD }}" --verbose 2>&1 | awk -F'pb_auth=' '/pb_auth/{print $2;exit}' | awk -F';' '{print $1}')"
|
||||
|
||||
pnpm run unlighthouse
|
||||
|
||||
- name: Upload report to Cloudflare pages
|
||||
uses: cloudflare/wrangler-action@v3.4.1
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -8,3 +8,4 @@ node_modules
|
|||
!.env.example
|
||||
vite.config.js.timestamp-*
|
||||
vite.config.ts.timestamp-*
|
||||
.unlighthouse
|
27
backend/Dockerfile
Normal file
27
backend/Dockerfile
Normal file
|
@ -0,0 +1,27 @@
|
|||
FROM alpine:3.19.1
|
||||
|
||||
RUN addgroup -S nonroot \
|
||||
&& adduser -S nonroot -G nonroot
|
||||
|
||||
USER nonroot
|
||||
|
||||
ARG PB_VERSION=0.21.3
|
||||
|
||||
RUN apk add --no-cache \
|
||||
unzip \
|
||||
ca-certificates
|
||||
|
||||
# download and unzip PocketBase
|
||||
ADD https://github.com/pocketbase/pocketbase/releases/download/v${PB_VERSION}/pocketbase_${PB_VERSION}_linux_amd64.zip /tmp/pb.zip
|
||||
RUN unzip /tmp/pb.zip -d /pb/
|
||||
|
||||
# uncomment to copy the local pb_migrations dir into the image
|
||||
COPY ./pb_migrations /pb/pb_migrations
|
||||
|
||||
# uncomment to copy the local pb_hooks dir into the image
|
||||
# COPY ./pb_hooks /pb/pb_hooks
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
# start PocketBase
|
||||
CMD ["/pb/pocketbase", "serve", "--http=0.0.0.0:8080"]
|
|
@ -9,7 +9,8 @@
|
|||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"lint": "prettier --check . && eslint .",
|
||||
"format": "prettier --write ."
|
||||
"format": "prettier --write .",
|
||||
"unlighthouse": "unlighthouse-ci"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-auto": "^3.0.0",
|
||||
|
@ -33,6 +34,7 @@
|
|||
"tailwindcss": "^3.3.6",
|
||||
"tslib": "^2.4.1",
|
||||
"typescript": "^5.0.0",
|
||||
"unlighthouse": "^0.10.6",
|
||||
"vite": "^5.0.3",
|
||||
"zod": "^3.22.4"
|
||||
},
|
||||
|
|
3005
pnpm-lock.yaml
3005
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
@ -1,9 +1,9 @@
|
|||
export { default as Metadata } from './metadata.svelte';
|
||||
export { default as SiteFooter } from './site-footer.svelte';
|
||||
export { default as UserNav } from './user-nav.svelte';
|
||||
export { default as SiteNavBar } from './site-navbar.svelte';
|
||||
export { default as TailwindIndicator } from './tailwind-indicator.svelte';
|
||||
export { default as ModeToggle } from './mode-toggle.svelte';
|
||||
export { default as Particles } from './particles.svelte';
|
||||
|
||||
export * from './icons';
|
||||
export * from './nav';
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
export { default as MainNav } from './main-nav.svelte';
|
||||
export { default as MobileNav } from './mobile-nav.svelte';
|
|
@ -1,25 +0,0 @@
|
|||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { cn } from '$lib/utils';
|
||||
import { navConfig } from '$lib/config/nav';
|
||||
|
||||
export let authenticated = false;
|
||||
</script>
|
||||
|
||||
<div class="mr-4 hidden md:flex">
|
||||
<nav class="flex items-center space-x-6 text-sm font-medium">
|
||||
{#each navConfig.mainNav as navItem, index (navItem + index.toString())}
|
||||
{#if navItem.href && (navItem.auth == authenticated || navItem.always)}
|
||||
<a
|
||||
href={navItem.href}
|
||||
class={cn(
|
||||
'transition-colors hover:text-foreground/80',
|
||||
$page.url.pathname === navItem.href ? 'text-foreground' : 'text-foreground/60'
|
||||
)}
|
||||
>
|
||||
{navItem.title}
|
||||
</a>
|
||||
{/if}
|
||||
{/each}
|
||||
</nav>
|
||||
</div>
|
|
@ -1,23 +0,0 @@
|
|||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { cn } from '$lib/utils';
|
||||
|
||||
export let href: string;
|
||||
export let open: boolean;
|
||||
|
||||
let className: string | undefined | null = undefined;
|
||||
export { className as class };
|
||||
</script>
|
||||
|
||||
<a
|
||||
{href}
|
||||
on:click={() => (open = false)}
|
||||
class={cn(
|
||||
$page.url.pathname === href ? 'text-foreground' : 'text-foreground/60',
|
||||
'hover:text-foreground',
|
||||
className
|
||||
)}
|
||||
{...$$restProps}
|
||||
>
|
||||
<slot />
|
||||
</a>
|
|
@ -1,68 +0,0 @@
|
|||
<script lang="ts">
|
||||
import * as Sheet from '$lib/components/ui/sheet/';
|
||||
import { HamburgerMenu } from 'radix-icons-svelte';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { navConfig } from '$lib/config/nav';
|
||||
import { siteConfig } from '$lib/config/site';
|
||||
import { Icons } from '../icons';
|
||||
import MobileLink from './mobile-link.svelte';
|
||||
|
||||
let open = false;
|
||||
export let authenticated = false;
|
||||
</script>
|
||||
|
||||
<Sheet.Root bind:open>
|
||||
<Sheet.Trigger asChild let:builder>
|
||||
<Button
|
||||
builders={[builder]}
|
||||
variant="ghost"
|
||||
class="mr-2 px-0 text-base hover:bg-transparent focus-visible:bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0 md:hidden"
|
||||
>
|
||||
<HamburgerMenu class="h-5 w-5" />
|
||||
<span class="sr-only">Toggle Menu</span>
|
||||
</Button>
|
||||
</Sheet.Trigger>
|
||||
<Sheet.Content side="right" class="pr-0">
|
||||
<MobileLink href="/" class="flex items-center" bind:open>
|
||||
<span class="sr-only">Logo icon (return home)</span>
|
||||
<div class="mr-4 rounded-sm bg-gray-950 p-0.5 dark:bg-transparent">
|
||||
<Icons.logo />
|
||||
</div>
|
||||
<span class="font-mono font-bold tracking-tighter">{siteConfig.name}</span>
|
||||
</MobileLink>
|
||||
<div class="my-4 h-[calc(100vh-8rem)] overflow-auto pl-1 pt-10">
|
||||
<div class="flex flex-col space-y-3">
|
||||
{#each navConfig.mainNav as navItem, index (navItem + index.toString())}
|
||||
{#if navItem.href && (navItem.auth == authenticated || navItem.always)}
|
||||
<MobileLink href={navItem.href} bind:open class="pt-2 text-5xl font-bold">
|
||||
{navItem.title}
|
||||
</MobileLink>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
<div class="flex flex-col space-y-2">
|
||||
{#each navConfig.sidebarNav as navItem, index (index)}
|
||||
<div class="flex flex-col space-y-3 pt-6">
|
||||
<h4 class="font-medium">{navItem.title}</h4>
|
||||
{#if navItem?.items?.length}
|
||||
{#each navItem.items as item}
|
||||
{#if !item.disabled && item.href}
|
||||
<MobileLink href={item.href} bind:open class="text-muted-foreground">
|
||||
{item.title}
|
||||
{#if item.label}
|
||||
<span
|
||||
class="ml-2 rounded-md bg-[#adfa1d] px-1.5 py-0.5 text-xs leading-none text-[#000000]"
|
||||
>
|
||||
{item.label}
|
||||
</span>
|
||||
{/if}
|
||||
</MobileLink>
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</Sheet.Content>
|
||||
</Sheet.Root>
|
|
@ -5,8 +5,10 @@
|
|||
<footer class="container py-6">
|
||||
<div class="space-y-1">
|
||||
<p class="text-center text-sm text-muted-foreground">
|
||||
© {new Date().getFullYear()} — {siteConfig.name} by {siteConfig.author}. Licensed
|
||||
under GPL-3.0.
|
||||
© {new Date().getFullYear()} —
|
||||
<a href={siteConfig.links.gitHubProject} target="_blank">{siteConfig.name}</a>
|
||||
by <a href={siteConfig.links.gitHubProfile} target="_blank">{siteConfig.author}</a>. Licensed
|
||||
GPL-3.0.
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<script lang="ts">
|
||||
import { Icons, ModeToggle, MainNav, MobileNav } from '$lib/components/site';
|
||||
import { Icons, ModeToggle, UserNav } from '$lib/components/site';
|
||||
import { siteConfig } from '$lib/config/site';
|
||||
import UserNav from './nav/user-nav.svelte';
|
||||
|
||||
export let authenticated = false;
|
||||
export let user: object | null = null;
|
||||
|
@ -18,11 +17,11 @@
|
|||
</div>
|
||||
<span class="text-xl font-bold tracking-tight">{siteConfig.name}</span>
|
||||
</a>
|
||||
<MainNav {authenticated} />
|
||||
<div class="flex flex-1 items-center justify-end space-x-2 sm:space-x-4">
|
||||
<ModeToggle />
|
||||
{#if !authenticated}
|
||||
<ModeToggle />
|
||||
{/if}
|
||||
<UserNav {authenticated} {user} />
|
||||
<MobileNav {authenticated} />
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
</div>
|
||||
</DropdownMenu.Label>
|
||||
<DropdownMenu.Separator />
|
||||
<DropdownMenu.Item>Dashboards</DropdownMenu.Item>
|
||||
<DropdownMenu.Item href="/dashboard">Dashboards</DropdownMenu.Item>
|
||||
<DropdownMenu.Item>Connectors</DropdownMenu.Item>
|
||||
<DropdownMenu.Separator />
|
||||
<DropdownMenu.Label class="text-xs leading-none text-muted-foreground">
|
|
@ -1,17 +0,0 @@
|
|||
import type { NavItem, SidebarNavItem } from '$lib/types/nav';
|
||||
|
||||
interface NavConfig {
|
||||
mainNav: NavItem[];
|
||||
sidebarNav: SidebarNavItem[];
|
||||
}
|
||||
|
||||
export const navConfig: NavConfig = {
|
||||
mainNav: [
|
||||
{
|
||||
title: 'Dashboard',
|
||||
href: '/dashboard',
|
||||
auth: true
|
||||
}
|
||||
],
|
||||
sidebarNav: []
|
||||
};
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { browser } from '$app/environment';
|
||||
import { enhance } from '$app/forms';
|
||||
import { Icons } from '$lib/components/site/icons';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
|
@ -40,11 +41,17 @@
|
|||
console.error(err);
|
||||
}
|
||||
}
|
||||
let tab: string = 'login';
|
||||
|
||||
if (browser) {
|
||||
const urlSearchParams = new URLSearchParams(window.location.search);
|
||||
tab = urlSearchParams.get('tab') || 'login';
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="lg:p-8">
|
||||
<Tabs.Root
|
||||
value={form?.showLogin ? 'login' : undefined}
|
||||
value={form?.showLogin ? 'login' : tab}
|
||||
class="mx-auto flex w-full flex-col justify-center space-y-6 sm:w-[350px]"
|
||||
>
|
||||
<Tabs.List class="grid w-full grid-cols-2">
|
||||
|
@ -74,7 +81,7 @@
|
|||
<div class="grid gap-2">
|
||||
<Label for="email">Email or username</Label>
|
||||
<Input
|
||||
id="email"
|
||||
id="email-login"
|
||||
name="email"
|
||||
type="email"
|
||||
autocapitalize="none"
|
||||
|
@ -85,7 +92,7 @@
|
|||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="password">Password</Label>
|
||||
<Input id="password" name="password" type="password" disabled={isLoading} />
|
||||
<Input id="password-login" name="password" type="password" disabled={isLoading} />
|
||||
</div>
|
||||
<Button type="submit" disabled={isLoading}>
|
||||
{#if isLoading}
|
||||
|
@ -131,7 +138,7 @@
|
|||
<div class="grid gap-2">
|
||||
<Label for="email">Email</Label>
|
||||
<Input
|
||||
id="email"
|
||||
id="email-register"
|
||||
name="email"
|
||||
type="email"
|
||||
autocapitalize="none"
|
||||
|
@ -142,11 +149,16 @@
|
|||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="password">Password</Label>
|
||||
<Input id="password" name="password" type="password" disabled={isLoading} />
|
||||
<Input id="password-register" name="password" type="password" disabled={isLoading} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="password">Confirm password</Label>
|
||||
<Input id="password" name="passwordConfirm" type="password" disabled={isLoading} />
|
||||
<Input
|
||||
id="confirm-password-register"
|
||||
name="passwordConfirm"
|
||||
type="password"
|
||||
disabled={isLoading}
|
||||
/>
|
||||
</div>
|
||||
<Button type="submit" disabled={isLoading}>
|
||||
{#if isLoading}
|
||||
|
@ -165,6 +177,11 @@
|
|||
</form>
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
<p class="px-8 text-center text-xs text-muted-foreground">
|
||||
Don't have an account? <a class="text-primary underline" href="/auth?tab=register">Register</a
|
||||
>.<br />
|
||||
Forgot password? <a class="text-primary underline" href="/reset-password">Reset password.</a>
|
||||
</p>
|
||||
{#if providers.length}
|
||||
<form
|
||||
method="POST"
|
||||
|
@ -252,9 +269,4 @@
|
|||
</form>
|
||||
{/if}
|
||||
</Tabs.Root>
|
||||
|
||||
<p class="px-8 py-2 text-center text-xs text-muted-foreground">
|
||||
Don't have an account? <a class="text-primary underline" href="/register">Sign up.</a> <br />
|
||||
Forgot password? <a class="text-primary underline" href="/reset-password">Reset password.</a>
|
||||
</p>
|
||||
</div>
|
||||
|
|
18
unlighthouse.config.ts
Normal file
18
unlighthouse.config.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { defineConfig } from 'unlighthouse';
|
||||
|
||||
export default defineConfig({
|
||||
site: process.env.SCAN_URL || 'http://localhost:5173/',
|
||||
cookies: [
|
||||
{
|
||||
name: 'pb_auth',
|
||||
value: process.env.AUTH_COOKIE || '',
|
||||
domain: process.env.SCAN_URL || 'http://localhost:5173/',
|
||||
path: '/',
|
||||
sameSite: 'Lax'
|
||||
}
|
||||
],
|
||||
ci: {
|
||||
reporter: 'jsonExpanded',
|
||||
buildStatic: true
|
||||
}
|
||||
});
|
Loading…
Reference in a new issue