feat: visual polish & UX — fade-in, theme toggle, scroll-to-top, footer, cursor, badge
- Add FadeIn component (IntersectionObserver scroll-reveal + load animation) - Add dark/light theme toggle (useSyncExternalStore + localStorage) - Add scroll-to-top button (appears after 300px scroll) - Add configurable footer with social links (gallery.yaml) - Add crosshair cursor on photo grid items - Add 'X photos' count badge on subpage cover images - Add light theme color palette in tokens.css
This commit is contained in:
45
lib/env.ts
Normal file
45
lib/env.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Zod-validated environment variables.
|
||||
* Parsed once at startup — all env access should go through this module.
|
||||
*/
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
const envSchema = z.object({
|
||||
/** Base URL of your Immich instance, e.g. https://immich.example.com */
|
||||
IMMICH_API_URL: z
|
||||
.string()
|
||||
.url('IMMICH_API_URL must be a valid URL')
|
||||
.transform((url) => url.replace(/\/+$/, '')),
|
||||
|
||||
/** Immich API key (from Admin → API Keys) */
|
||||
IMMICH_API_KEY: z.string().min(1, 'IMMICH_API_KEY is required'),
|
||||
|
||||
/** Public site title shown in the header */
|
||||
SITE_TITLE: z.string().default('Gallery'),
|
||||
|
||||
/** Subtitle displayed below the title */
|
||||
SITE_SUBTITLE: z.string().default(''),
|
||||
|
||||
/** Cache TTL in seconds */
|
||||
CACHE_TTL: z.coerce.number().int().min(0).default(300),
|
||||
|
||||
/** Rate limit: max requests per minute per IP */
|
||||
RATE_LIMIT_RPM: z.coerce.number().int().min(1).default(120),
|
||||
});
|
||||
|
||||
export type Env = z.infer<typeof envSchema>;
|
||||
|
||||
function parseEnv(): Env {
|
||||
const result = envSchema.safeParse(process.env);
|
||||
if (!result.success) {
|
||||
const formatted = result.error.issues
|
||||
.map((issue) => ` ✗ ${issue.path.join('.')}: ${issue.message}`)
|
||||
.join('\n');
|
||||
throw new Error(`\n❌ Environment validation failed:\n${formatted}\n`);
|
||||
}
|
||||
return result.data;
|
||||
}
|
||||
|
||||
/** Validated, typed environment variables. */
|
||||
export const env = parseEnv();
|
||||
Reference in New Issue
Block a user