Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions config.example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ log_level: info # Logging level: fatal, error, warn, info, debug, trace, silent
# Login flow configuration
login:
mode: username_password # username_password (default) or username_only or scanner_only or username_or_scanner
# If the login help image must be localised this the second configuration must be chosen.
# If the image is languistically neutral the first cofiguration is enough
# login_help_image: '/branding/login-help.png' # optional; supports /absolute/path or https:// URL
# login_help_image: # optional
# en: '/branding/login-help-en.png' # supports /absolute/path or https:// URL
# de: '/branding/login-help-de.png' # supports /absolute/path or https:// URL
# validation:
# implementation: campus_id # empty/missing means scanner values are used directly for login
# campus_id:
Expand Down
16 changes: 13 additions & 3 deletions src/lib/components/LoginModal.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,28 @@
import { CircleX } from '@lucide/svelte';
import { clientLogger } from '$lib/client/logger';
import { m } from '$lib/paraglide/messages';
import { getLocale, type Locale } from "$lib/paraglide/runtime";

type LoginMode = 'username_password' | 'username_only' | 'scanner_only' | 'username_or_scanner';

interface Props {
onSuccess: () => void;
onCancel?: () => void;
loginMode?: LoginMode;
loginHelpImage?: string;
loginHelpImage?: Record<Locale, string> | string;
}

let { onSuccess, onCancel, loginMode = 'username_password', loginHelpImage }: Props = $props();

const locale = getLocale().toUpperCase();
const helpImage = $derived.by(() => {
if (!loginHelpImage) return undefined;
if (typeof loginHelpImage === "string") {
return loginHelpImage;
}
return loginHelpImage[getLocale()];
});

const requiresPassword = $derived(loginMode === 'username_password');
const hasCameraToggle = $derived(loginMode === 'username_or_scanner');
const scannerOnlyMode = $derived(loginMode === 'scanner_only');
Expand Down Expand Up @@ -211,10 +221,10 @@
class="modal-box max-w-4xl rounded-3xl bg-base-100/95 text-base-content shadow-2xl ring-1 ring-base-300/70"
>
<div class="flex flex-col gap-6 md:flex-row md:items-stretch">
{#if loginHelpImage}
{#if helpImage}
<div class="md:w-72 md:shrink-0">
<img
src={loginHelpImage}
src={helpImage}
alt={m.login_required()}
class="h-full w-full rounded-2xl object-cover shadow-xl"
/>
Expand Down
17 changes: 15 additions & 2 deletions src/lib/server/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import fs from 'fs';
import path from 'path';
import YAML from 'yaml';
import { type LogLevel, parseLogLevel } from '$lib/logger/levels';
import { type Locale } from "$lib/paraglide/runtime";

export type LoginMode =
'username_password' | 'username_only' | 'scanner_only' | 'username_or_scanner';
Expand Down Expand Up @@ -35,7 +36,7 @@ export interface MiddlewareInstanceConfig {

export interface LoginConfig {
mode?: LoginMode;
login_help_image?: string;
login_help_image?: Record<Locale, string> | string;
validation?: LoginValidationConfig;
}

Expand Down Expand Up @@ -284,6 +285,14 @@ function validateConfigData(data: LMSConfig, requireTaggingFormats: boolean): vo
if (!data.middleware_instances || !Array.isArray(data.middleware_instances)) {
throw new Error('Invalid configuration: middleware_instances must be an array');
}

// if (data.login?.login_help_image) {
// for (const [locale, source] of Object.entries(data.login.login_help_image)) {
// if (validateSource(source) === undefined) {
// throw new Error('Invalid configuration: login_help_image must be a path');
// }
// }
// }
}

function normalizeConfigData(data: LMSConfig, requireTaggingFormats: boolean): LMSConfig {
Expand All @@ -294,7 +303,8 @@ function normalizeConfigData(data: LMSConfig, requireTaggingFormats: boolean): L
data.log_level = parseLogLevel(data.log_level, 'info');
data.login = {
mode: parsedLoginMode,
login_help_image: validateSource(data.login?.login_help_image),
// login_help_image: data.login?.login_help_image,
login_help_image: data.login?.login_help_image,
validation: parseLoginValidationConfig(data.login)
};
data.gate = parseGateConfig(data);
Expand Down Expand Up @@ -388,6 +398,9 @@ log_level: info # Logging level: fatal, error, warn, info, debug, trace, silent
login:
mode: username_password # username_password (default) or username_only or scanner_only or username_or_scanner
# login_help_image: '/branding/login-help.png' # optional; supports /absolute/path or https:// URL
# login_help_image: # optional
# de: '/branding/login-help-de.png supports /absolute/path or https:// URL
# en: '/branding/login-help-en.png supports /absolute/path or https:// URL
# validation:
# implementation: campus_id # empty/missing means scanner values are used directly for login
# campus_id:
Expand Down
5 changes: 3 additions & 2 deletions src/routes/checkout/account/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@ import { getLms } from '$lib/server/lms/resolve';
import { clearAuthCookie, getAuthUserFromCookies } from '$lib/server/auth-cookies';
import { getConfig, type LoginMode } from '$lib/server/config';
import { logger } from '$lib/server/logger';
import { type Locale } from "$lib/paraglide/runtime";

export const load = (async ({ cookies }) => {
const lms = getLms();
const userId = getAuthUserFromCookies(cookies);
let loginMode: LoginMode = 'username_password';
let loginHelpImage: string | undefined;
let loginHelpImage: Record<Locale, string> | string | undefined;

try {
const config = getConfig();
loginMode = config.login?.mode ?? 'username_password';
loginHelpImage = config.login?.login_help_image;
loginHelpImage = config.login?.login_help_image;
} catch (error) {
logger.error({ err: error }, 'Failed to load login config; defaulting to username/password');
}
Expand Down
5 changes: 3 additions & 2 deletions src/routes/checkout/borrow/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@ import { getLms } from '$lib/server/lms/resolve';
import { clearAuthCookie, getAuthUserFromCookies } from '$lib/server/auth-cookies';
import { getConfig, type LoginMode } from '$lib/server/config';
import { logger } from '$lib/server/logger';
import { type Locale } from "$lib/paraglide/runtime";

export const load = (async ({ cookies }) => {
const lms = getLms();
const userId = getAuthUserFromCookies(cookies);
let loginMode: LoginMode = 'username_password';
let loginHelpImage: string | undefined;
let loginHelpImage: Record<Locale, string> | string | undefined;

try {
const config = getConfig();
loginMode = config.login?.mode ?? 'username_password';
loginHelpImage = config.login?.login_help_image;
loginHelpImage = config.login?.login_help_image;
} catch (error) {
logger.error({ err: error }, 'Failed to load login config; defaulting to username/password');
}
Expand Down