fix: enable auth nav, turnstile deployment, country dropdown, profile routes
- Add PUBLIC_TURNSTILE_SITE_KEY as Docker build arg and Woodpecker CI arg - Uncomment auth nav in Header and MobileNav (login/logout/profile links) - Move ThemeToggle from header to footer - Expand country dropdown from DACH-only to all European countries - Replace profile route redirect with requireAuth guard - Set cookie secure flag based on environment (secure in prod) - Add error handling to admin markets page (403 instead of 500)
This commit is contained in:
@@ -29,6 +29,7 @@ steps:
|
||||
from_secret: registry_password
|
||||
build_args:
|
||||
- PUBLIC_API_BASE_URL=https://api.marktvogt.de
|
||||
- PUBLIC_TURNSTILE_SITE_KEY=0x4AAAAAAACjLCV-78Q1loTPz
|
||||
when:
|
||||
- event: push
|
||||
branch: main
|
||||
|
||||
@@ -20,6 +20,9 @@ COPY . .
|
||||
ARG PUBLIC_API_BASE_URL=https://api.marktvogt.de
|
||||
ENV PUBLIC_API_BASE_URL=$PUBLIC_API_BASE_URL
|
||||
|
||||
ARG PUBLIC_TURNSTILE_SITE_KEY=1x00000000000000000000AA
|
||||
ENV PUBLIC_TURNSTILE_SITE_KEY=$PUBLIC_TURNSTILE_SITE_KEY
|
||||
|
||||
RUN bun run build
|
||||
|
||||
# ─────────────────────────────────────────────
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { dev } from '$app/environment';
|
||||
import type { Cookies } from '@sveltejs/kit';
|
||||
import type { AuthData } from '$lib/api/types.js';
|
||||
|
||||
const COOKIE_OPTS = {
|
||||
path: '/',
|
||||
httpOnly: true,
|
||||
secure: false, // TODO: set to true in production
|
||||
secure: !dev,
|
||||
sameSite: 'lax' as const
|
||||
};
|
||||
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
<script lang="ts">
|
||||
import ThemeToggle from '$lib/components/ui/ThemeToggle.svelte';
|
||||
</script>
|
||||
|
||||
<footer class="border-primary-800 bg-primary-950 border-t">
|
||||
<div class="mx-auto max-w-7xl px-4 py-8 sm:px-6 lg:px-8">
|
||||
<div class="flex flex-col items-center justify-between gap-4 sm:flex-row">
|
||||
<p class="text-primary-400 text-sm">© {new Date().getFullYear()} Marktvogt</p>
|
||||
<nav class="flex gap-6">
|
||||
<a href="/impressum" class="text-primary-400 hover:text-primary-200 text-sm">Impressum</a>
|
||||
<a href="/datenschutz" class="text-primary-400 hover:text-primary-200 text-sm"
|
||||
>Datenschutz</a
|
||||
>
|
||||
</nav>
|
||||
<div class="flex items-center gap-6">
|
||||
<nav class="flex gap-6">
|
||||
<a href="/impressum" class="text-primary-400 hover:text-primary-200 text-sm">Impressum</a>
|
||||
<a href="/datenschutz" class="text-primary-400 hover:text-primary-200 text-sm"
|
||||
>Datenschutz</a
|
||||
>
|
||||
</nav>
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script lang="ts">
|
||||
import type { ProfileData } from '$lib/api/types.js';
|
||||
import MobileNav from './MobileNav.svelte';
|
||||
import ThemeToggle from '$lib/components/ui/ThemeToggle.svelte';
|
||||
|
||||
interface Props {
|
||||
user: ProfileData | null;
|
||||
@@ -45,27 +44,25 @@
|
||||
Admin
|
||||
</a>
|
||||
{/if}
|
||||
<!-- TODO: re-enable auth nav when login/signup is ready
|
||||
{#if user}
|
||||
<a href="/profile" class="text-sm font-medium text-primary-200 hover:text-white">Profil</a>
|
||||
<a href="/profile" class="text-primary-200 text-sm font-medium hover:text-white">
|
||||
Profil
|
||||
</a>
|
||||
<form method="POST" action="/auth/abmelden">
|
||||
<button type="submit" class="text-sm font-medium text-primary-200 hover:text-white">
|
||||
<button type="submit" class="text-primary-200 text-sm font-medium hover:text-white">
|
||||
Abmelden
|
||||
</button>
|
||||
</form>
|
||||
<span class="text-sm text-primary-300">{user.display_name}</span>
|
||||
<span class="text-primary-300 text-sm">{user.display_name}</span>
|
||||
{:else}
|
||||
<a href="/auth/anmelden" class="text-sm font-medium text-accent-300 hover:text-accent-200">
|
||||
<a href="/auth/anmelden" class="text-accent-300 hover:text-accent-200 text-sm font-medium">
|
||||
Anmelden
|
||||
</a>
|
||||
{/if}
|
||||
-->
|
||||
<ThemeToggle />
|
||||
</nav>
|
||||
|
||||
<!-- Mobile: theme toggle + menu button -->
|
||||
<!-- Mobile: menu button -->
|
||||
<div class="flex items-center gap-2 md:hidden">
|
||||
<ThemeToggle />
|
||||
<button
|
||||
type="button"
|
||||
class="text-primary-300 rounded-md p-2 hover:text-white"
|
||||
|
||||
@@ -30,22 +30,28 @@
|
||||
Admin
|
||||
</a>
|
||||
{/if}
|
||||
<!-- TODO: re-enable auth nav when login/signup is ready
|
||||
{#if user}
|
||||
<a href="/profile" class="text-sm font-medium text-primary-200 hover:text-white" onclick={onclose}>
|
||||
<a
|
||||
href="/profile"
|
||||
class="text-primary-200 text-sm font-medium hover:text-white"
|
||||
onclick={onclose}
|
||||
>
|
||||
Profil
|
||||
</a>
|
||||
<form method="POST" action="/auth/abmelden">
|
||||
<button type="submit" class="text-sm font-medium text-primary-200 hover:text-white">
|
||||
<button type="submit" class="text-primary-200 text-sm font-medium hover:text-white">
|
||||
Abmelden
|
||||
</button>
|
||||
</form>
|
||||
<span class="text-sm text-primary-300">{user.display_name}</span>
|
||||
<span class="text-primary-300 text-sm">{user.display_name}</span>
|
||||
{:else}
|
||||
<a href="/auth/anmelden" class="text-sm font-medium text-accent-300 hover:text-accent-200" onclick={onclose}>
|
||||
<a
|
||||
href="/auth/anmelden"
|
||||
class="text-accent-300 hover:text-accent-200 text-sm font-medium"
|
||||
onclick={onclose}
|
||||
>
|
||||
Anmelden
|
||||
</a>
|
||||
{/if}
|
||||
-->
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { serverFetch } from '$lib/api/client.server.js';
|
||||
import type { AdminMarketSummary, PaginationMeta } from '$lib/api/types.js';
|
||||
import { ApiClientError } from '$lib/api/client.js';
|
||||
import { buildSearchQuery } from '$lib/api/client.js';
|
||||
import type { AdminMarketSummary, PaginationMeta } from '$lib/api/types.js';
|
||||
import type { PageServerLoad } from './$types.js';
|
||||
|
||||
export const load: PageServerLoad = async ({ url, cookies }) => {
|
||||
@@ -9,11 +11,19 @@ export const load: PageServerLoad = async ({ url, cookies }) => {
|
||||
const page = url.searchParams.get('page') ?? '1';
|
||||
|
||||
const query = buildSearchQuery({ status, q, page, per_page: '20' });
|
||||
const res = await serverFetch<AdminMarketSummary[]>(`/admin/markets?${query}`, cookies);
|
||||
|
||||
return {
|
||||
markets: res.data,
|
||||
meta: res.meta as PaginationMeta,
|
||||
filters: { status, q }
|
||||
};
|
||||
try {
|
||||
const res = await serverFetch<AdminMarketSummary[]>(`/admin/markets?${query}`, cookies);
|
||||
|
||||
return {
|
||||
markets: res.data,
|
||||
meta: res.meta as PaginationMeta,
|
||||
filters: { status, q }
|
||||
};
|
||||
} catch (err) {
|
||||
if (err instanceof ApiClientError) {
|
||||
error(err.status, err.message);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -117,12 +117,52 @@
|
||||
class="bg-vellum focus:border-primary-500 focus:ring-primary-500 w-full rounded-lg border border-stone-300 px-3 py-2
|
||||
text-sm shadow-sm focus:ring-2 focus:outline-none
|
||||
dark:border-stone-600 dark:bg-stone-800"
|
||||
value={form?.country ?? 'DE'}
|
||||
>
|
||||
<option value="DE" selected={form?.country !== 'AT' && form?.country !== 'CH'}>
|
||||
Deutschland
|
||||
</option>
|
||||
<option value="AT" selected={form?.country === 'AT'}>Österreich</option>
|
||||
<option value="CH" selected={form?.country === 'CH'}>Schweiz</option>
|
||||
<option value="DE">Deutschland</option>
|
||||
<option value="AT">Österreich</option>
|
||||
<option value="CH">Schweiz</option>
|
||||
<option disabled>──────────</option>
|
||||
<option value="AL">Albanien</option>
|
||||
<option value="AD">Andorra</option>
|
||||
<option value="BE">Belgien</option>
|
||||
<option value="BA">Bosnien und Herzegowina</option>
|
||||
<option value="BG">Bulgarien</option>
|
||||
<option value="DK">Dänemark</option>
|
||||
<option value="EE">Estland</option>
|
||||
<option value="FI">Finnland</option>
|
||||
<option value="FR">Frankreich</option>
|
||||
<option value="GR">Griechenland</option>
|
||||
<option value="IE">Irland</option>
|
||||
<option value="IS">Island</option>
|
||||
<option value="IT">Italien</option>
|
||||
<option value="XK">Kosovo</option>
|
||||
<option value="HR">Kroatien</option>
|
||||
<option value="LV">Lettland</option>
|
||||
<option value="LI">Liechtenstein</option>
|
||||
<option value="LT">Litauen</option>
|
||||
<option value="LU">Luxemburg</option>
|
||||
<option value="MT">Malta</option>
|
||||
<option value="MD">Moldawien</option>
|
||||
<option value="MC">Monaco</option>
|
||||
<option value="ME">Montenegro</option>
|
||||
<option value="NL">Niederlande</option>
|
||||
<option value="MK">Nordmazedonien</option>
|
||||
<option value="NO">Norwegen</option>
|
||||
<option value="PL">Polen</option>
|
||||
<option value="PT">Portugal</option>
|
||||
<option value="RO">Rumänien</option>
|
||||
<option value="SM">San Marino</option>
|
||||
<option value="SE">Schweden</option>
|
||||
<option value="RS">Serbien</option>
|
||||
<option value="SK">Slowakei</option>
|
||||
<option value="SI">Slowenien</option>
|
||||
<option value="ES">Spanien</option>
|
||||
<option value="CZ">Tschechien</option>
|
||||
<option value="UA">Ukraine</option>
|
||||
<option value="HU">Ungarn</option>
|
||||
<option value="VA">Vatikanstadt</option>
|
||||
<option value="GB">Vereinigtes Königreich</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// TODO: remove this redirect when login/signup is re-enabled
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import { requireAuth } from '$lib/auth/guard.js';
|
||||
import type { LayoutServerLoad } from './$types';
|
||||
|
||||
export const load: LayoutServerLoad = () => {
|
||||
redirect(302, '/');
|
||||
export const load: LayoutServerLoad = (event) => {
|
||||
requireAuth(event);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user