### Install dependencies Source: https://svelteforge.dashboardpack.com/docs/getting-started Install all required project dependencies using pnpm. ```bash pnpm install ``` -------------------------------- ### Start the development server Source: https://svelteforge.dashboardpack.com/docs/getting-started Launch the SvelteKit development server to view the application locally. ```bash pnpm dev ``` -------------------------------- ### Build and Run Production Server Source: https://svelteforge.dashboardpack.com/docs/deployment Commands to compile the SvelteKit application and start the production server. ```bash # Create the production build pnpm build # Run the production server node build/index.js ``` -------------------------------- ### Manage Node.js Process with PM2 on VPS Source: https://svelteforge.dashboardpack.com/docs/deployment Install and configure PM2 to keep the SvelteKit application running on a VPS. ```bash # Example: PM2 process manager on a VPS npm install -g pm2 pm2 start build/index.js --name svelteforge pm2 save pm2 startup ``` -------------------------------- ### Launch Drizzle Studio Source: https://svelteforge.dashboardpack.com/docs/database Starts the browser-based GUI for inspecting and editing the SQLite database. ```bash pnpm db:studio ``` -------------------------------- ### Install D3 dependencies Source: https://svelteforge.dashboardpack.com/docs/analytics Install the necessary D3 modules and their corresponding TypeScript definitions for data visualization. ```bash pnpm add d3-scale d3-array d3-shape pnpm add -D @types/d3-scale @types/d3-array @types/d3-shape ``` -------------------------------- ### Dockerfile for SvelteForge Admin Source: https://svelteforge.dashboardpack.com/docs/deployment A multi-stage Dockerfile that installs dependencies, builds the SvelteKit application, and sets up the production environment with SQLite support. ```dockerfile # Stage 1: Install dependencies FROM node:20-alpine AS deps RUN apk add --no-cache python3 make g++ WORKDIR /app RUN corepack enable pnpm COPY package.json pnpm-lock.yaml ./ RUN pnpm install --frozen-lockfile # Stage 2: Build the SvelteKit application FROM node:20-alpine AS builder WORKDIR /app RUN corepack enable pnpm COPY --from=deps /app/node_modules ./node_modules COPY . . RUN pnpm build # Stage 3: Production image FROM node:20-alpine AS runner RUN apk add --no-cache python3 make g++ WORKDIR /app RUN corepack enable pnpm COPY package.json pnpm-lock.yaml ./ RUN pnpm install --frozen-lockfile --prod COPY --from=builder /app/build ./build COPY --from=builder /app/drizzle ./drizzle # Create data directory for SQLite RUN mkdir -p /app/data ENV NODE_ENV=production ENV PORT=3000 ENV HOST=0.0.0.0 ENV DATABASE_URL=/app/data/svelteforge.db EXPOSE 3000 CMD ["node", "build/index.js"] ``` -------------------------------- ### GET /sitemap.xml Source: https://svelteforge.dashboardpack.com/docs/api-reference Retrieves an auto-generated XML sitemap for SEO purposes. ```APIDOC ## GET /sitemap.xml ### Description Returns all published page URLs in standard XML sitemap format. ### Method GET ### Endpoint /sitemap.xml ``` -------------------------------- ### GET /api/search Source: https://svelteforge.dashboardpack.com/docs/routing The search endpoint powers the command palette by querying users, pages, and notifications based on a search query string. ```APIDOC ## GET /api/search ### Description Queries users, pages, and notifications in parallel based on a search term to power the command palette. ### Method GET ### Endpoint /api/search ### Parameters #### Query Parameters - **q** (string) - Required - The search query string to filter results. ``` -------------------------------- ### Search API Endpoint Request and Response Source: https://svelteforge.dashboardpack.com/docs/api-reference Demonstrates the structure of the GET /api/search request and the corresponding JSON response. ```http // Request GET /api/search?q=admin // Response { "results": [ { "title": "Admin User", "description": "admin@svelteforge.dev", "url": "/users", "category": "Users", "icon": "users" }, { "title": "Admin Dashboard Settings", "description": "Configure dashboard preferences", "url": "/content/admin-dashboard-settings", "category": "Pages", "icon": "file-text" } ] } ``` -------------------------------- ### GET /api/search Source: https://svelteforge.dashboardpack.com/docs/api-reference Performs a full-text search across users, pages, and notifications. ```APIDOC ## GET /api/search ### Description Performs a full-text search across users, pages, and notifications. Returns categorized results with icons. ### Method GET ### Endpoint /api/search ### Parameters #### Query Parameters - **q** (string) - Required - The search query string. ### Request Example GET /api/search?q=admin ### Response #### Success Response (200) - **results** (array) - A list of search result objects. #### Response Example { "results": [ { "title": "Admin User", "description": "admin@svelteforge.dev", "url": "/users", "category": "Users", "icon": "users" }, { "title": "Admin Dashboard Settings", "description": "Configure dashboard preferences", "url": "/content/admin-dashboard-settings", "category": "Pages", "icon": "file-text" } ] } ``` -------------------------------- ### Deploy to Fly.io via CLI Source: https://svelteforge.dashboardpack.com/docs/deployment Launch a new application, create a persistent volume, and deploy to Fly.io. ```bash # Fly.io deployment fly launch fly volumes create svelteforge_data --size 1 --region ord fly deploy ``` -------------------------------- ### Clone the repository Source: https://svelteforge.dashboardpack.com/docs/getting-started Download the project source code and navigate into the directory. ```bash git clone https://github.com/your-org/svelteforge-admin.git cd svelteforge-admin ``` -------------------------------- ### Deploy to Railway via CLI Source: https://svelteforge.dashboardpack.com/docs/deployment Initialize a project, configure a persistent volume, and deploy to Railway. ```bash # Railway CLI deployment railway init railway volume add --mount /app/data railway up ``` -------------------------------- ### Seed sample data Source: https://svelteforge.dashboardpack.com/docs/getting-started Populate the database with initial sample data for testing. ```bash pnpm db:seed ``` -------------------------------- ### Initialize the database Source: https://svelteforge.dashboardpack.com/docs/getting-started Push the Drizzle ORM schema to create the local SQLite database file. ```bash pnpm db:push ``` -------------------------------- ### Create Route Directory Source: https://svelteforge.dashboardpack.com/docs/routing Command to initialize a new route directory within the protected (app) layout. ```bash mkdir -p src/routes/\(app\)/reports ``` -------------------------------- ### Build and Run Commands Source: https://svelteforge.dashboardpack.com/docs/deployment Commands to build the Docker image and run the container with a persistent volume for the SQLite database. ```bash # Build the Docker image docker build -t svelteforge-admin . # Run with a persistent volume for SQLite docker run -p 3000:3000 -v ./data:/app/data svelteforge-admin ``` -------------------------------- ### Configure OAuth Providers Source: https://svelteforge.dashboardpack.com/docs/authentication Initializes Arctic OAuth clients based on environment variables. Returns null for providers missing credentials to ensure graceful degradation. ```typescript // src/lib/server/oauth.ts import * as arctic from "arctic"; import { env } from "$env/dynamic/private"; function getBaseUrl(): string { return env.ORIGIN || "http://localhost:5173"; } export const google = env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET ? new arctic.Google( env.GOOGLE_CLIENT_ID, env.GOOGLE_CLIENT_SECRET, `${getBaseUrl()}/login/google/callback` ) : null; export const github = env.GITHUB_CLIENT_ID && env.GITHUB_CLIENT_SECRET ? new arctic.GitHub( env.GITHUB_CLIENT_ID, env.GITHUB_CLIENT_SECRET, `${getBaseUrl()}/login/github/callback` ) : null; export function getEnabledProviders(): string[] { const providers: string[] = []; if (google) providers.push("google"); if (github) providers.push("github"); return providers; } ``` -------------------------------- ### Structure test files by route co-location Source: https://svelteforge.dashboardpack.com/docs/testing Demonstrates the recommended directory structure where test files are placed alongside their corresponding route files. ```text src/routes/(app)/users/ +page.svelte # UI component (Svelte 5) +page.server.ts # Server load + form actions users.test.ts # Unit tests for +page.server.ts src/routes/(app)/settings/ +page.svelte +page.server.ts settings.test.ts ``` -------------------------------- ### Add shadcn-svelte components via CLI Source: https://svelteforge.dashboardpack.com/docs/project-structure Use the shadcn-svelte CLI to add or update UI primitives in the project. Avoid manual edits to files in the src/lib/components/ui/ directory. ```bash npx shadcn-svelte@latest add button npx shadcn-svelte@latest add dialog npx shadcn-svelte@latest add dropdown-menu ``` -------------------------------- ### SvelteKit Login Form Action Source: https://svelteforge.dashboardpack.com/docs/authentication Implements a server-side login flow with input validation, password verification, and session management. ```typescript // src/routes/(auth)/login/+page.server.ts export const actions: Actions = { default: async ({ request, cookies, getClientAddress }) => { const formData = await request.formData(); const username = formData.get("username"); const password = formData.get("password"); // ... validation ... const existingUser = await db.query.users.findFirst({ where: eq(users.username, username.toLowerCase()), }); if (!existingUser) { return fail(400, { message: "Incorrect username or password" }); } const validPassword = await verify(existingUser.passwordHash, password, { memoryCost: 19456, timeCost: 2, outputLen: 32, parallelism: 1, }); if (!validPassword) { return fail(400, { message: "Incorrect username or password" }); } const token = generateSessionToken(); const session = await createSession(token, existingUser.id, { userAgent: request.headers.get("user-agent"), ipAddress: getClientAddress(), }); setSessionCookie(cookies, token, session.expiresAt); redirect(302, "/"); }, }; ``` -------------------------------- ### Configure Adapter-Node Source: https://svelteforge.dashboardpack.com/docs/deployment Configuration for the SvelteKit adapter-node in svelte.config.js to define output directories and compression settings. ```javascript // svelte.config.js import adapter from "@sveltejs/adapter-node"; export default { kit: { adapter: adapter({ // Output directory (default: "build") out: "build", // Precompress static assets with gzip and brotli precompress: false, // Environment variable for port (default: "PORT") envPrefix: "", }), }, }; ``` -------------------------------- ### Initialize Drizzle ORM with SQLite Source: https://svelteforge.dashboardpack.com/docs/database Configures the database connection using better-sqlite3 and enables WAL mode for concurrent read performance. This file must reside in src/lib/server/ to prevent client-side exposure. ```typescript // src/lib/server/db/index.ts import Database from "better-sqlite3"; import { drizzle } from "drizzle-orm/better-sqlite3"; import * as schema from "./schema.js"; const dbPath = process.env.DATABASE_URL || "svelteforge.db"; const sqlite = new Database(dbPath); sqlite.pragma("journal_mode = WAL"); export const db = drizzle(sqlite, { schema }); ``` -------------------------------- ### Implement Full Authentication Flow Test Source: https://svelteforge.dashboardpack.com/docs/testing Demonstrates a complete user journey including registration, dashboard verification, logout, and re-login. ```typescript import { test, expect } from "@playwright/test"; test("user can register, login, and access dashboard", async ({ page }) => { // Register a new account await page.goto("/register"); await page.fill('input[name="name"]', "Test User"); await page.fill('input[name="email"]', "test@example.com"); await page.fill('input[name="username"]', "testuser"); await page.fill('input[name="password"]', "password123"); await page.click('button[type="submit"]'); // Should redirect to dashboard await expect(page).toHaveURL("/"); await expect(page.locator("text=Dashboard")).toBeVisible(); // Logout await page.goto("/logout"); // Login with the same credentials await page.goto("/login"); await page.fill('input[name="username"]', "testuser"); await page.fill('input[name="password"]', "password123"); await page.click('button[type="submit"]'); // Should be back on the dashboard await expect(page).toHaveURL("/"); }); ``` -------------------------------- ### Initialize GitHub OAuth Source: https://svelteforge.dashboardpack.com/docs/api-reference Creates an authorization URL for GitHub OAuth if the provider is configured. ```typescript import { github } from "$lib/server/oauth.js"; if (github) { const url = github.createAuthorizationURL(state, { scopes: ["user:email"], }); } ``` -------------------------------- ### Configure Drizzle Kit Source: https://svelteforge.dashboardpack.com/docs/database Defines the database schema path, migration output directory, and SQLite credentials. ```typescript // drizzle.config.ts import { defineConfig } from "drizzle-kit"; export default defineConfig({ schema: "./src/lib/server/db/schema.ts", out: "./drizzle", dialect: "sqlite", dbCredentials: { url: "svelteforge.db", }, }); ``` -------------------------------- ### Backup SQLite Database Source: https://svelteforge.dashboardpack.com/docs/deployment Methods for creating consistent backups of an SQLite database running in WAL mode. ```bash # Method 1: SQLite backup command (recommended — creates a clean copy) sqlite3 svelteforge.db ".backup /backups/svelteforge-$(date +%Y%m%d).db" # Method 2: Copy all WAL files together cp svelteforge.db svelteforge.db-wal svelteforge.db-shm /backups/ ``` -------------------------------- ### Enable View Transitions Source: https://svelteforge.dashboardpack.com/docs/routing Uses the onNavigate lifecycle hook to implement smooth cross-fade animations between pages. ```typescript ``` -------------------------------- ### Implement Svelte 5 reactive patterns for user management Source: https://svelteforge.dashboardpack.com/docs/user-management Uses $state for tracking search and pagination inputs, and $derived for reactive filtering and slicing of user data. ```svelte // Svelte 5 reactive patterns in the users page let search = $state(''); let currentPage = $state(1); let pageSize = $state(10); let filteredUsers = $derived( users.filter((u) => u.name.toLowerCase().includes(search.toLowerCase()) || u.email.toLowerCase().includes(search.toLowerCase()) ) ); let paginatedUsers = $derived( filteredUsers.slice((currentPage - 1) * pageSize, currentPage * pageSize) ); ``` -------------------------------- ### Initialize Google OAuth Source: https://svelteforge.dashboardpack.com/docs/api-reference Creates an authorization URL for Google OAuth if the provider is configured. ```typescript import { google } from "$lib/server/oauth.js"; if (google) { const url = google.createAuthorizationURL(state, codeVerifier, { scopes: ["openid", "profile", "email"], }); } ``` -------------------------------- ### Reactive state with Svelte 5 runes Source: https://svelteforge.dashboardpack.com/docs Demonstrates the use of $state, $derived, and $effect runes to manage component reactivity and side effects. ```svelte ``` -------------------------------- ### Parse User-Agent String Source: https://svelteforge.dashboardpack.com/docs/api-reference Extracts browser, OS, and device information from a raw user-agent string. ```typescript import { parseUserAgent } from "$lib/utils/user-agent.js"; const info = parseUserAgent( "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" ); // => { browser: "Chrome", os: "macOS", device: "Desktop" } ``` -------------------------------- ### Update shadcn-svelte components Source: https://svelteforge.dashboardpack.com/docs/components Use the CLI to update components to the latest version while preserving theme tokens. Avoid manual edits to ensure compatibility with future updates. ```bash npx shadcn-svelte@latest add ``` -------------------------------- ### Test SvelteKit form actions Source: https://svelteforge.dashboardpack.com/docs/testing Uses Vitest or similar test runners to verify form action logic, including successful user creation and authorization enforcement for non-admin roles. ```javascript describe("create action", () => { it("creates a new user when admin submits valid data", async () => { // Arrange: create an admin user const admin = createTestUser(testDb, { role: "admin" }); const formData = createFormData({ name: "New User", email: "new@example.com", username: "newuser", password: "securepass123", role: "editor", }); // Act: call the form action const result = await actions.create({ request: createMockRequest(formData), locals: createMockLocals({ user: admin }), }) as any; // Assert: check the result expect(result.success).toBe(true); // Assert: verify database state const users = testDb.select().from(usersTable).all(); expect(users).toHaveLength(2); // admin + new user expect(users[1].email).toBe("new@example.com"); expect(users[1].role).toBe("editor"); }); it("rejects creation when user is not admin", async () => { const viewer = createTestUser(testDb, { role: "viewer" }); const formData = createFormData({ name: "Hacker", email: "hack@example.com", username: "hacker", password: "password123", role: "admin", }); const result = await actions.create({ request: createMockRequest(formData), locals: createMockLocals({ user: viewer }), }) as any; // Should return 403 failure expect(result.status).toBe(403); // Database should only have the original viewer const users = testDb.select().from(usersTable).all(); expect(users).toHaveLength(1); }); }); ``` -------------------------------- ### Define App Settings Table Source: https://svelteforge.dashboardpack.com/docs/database Key-value store schema for application configuration using the key as the primary identifier. ```typescript export const appSettings = sqliteTable("app_settings", { key: text("key").primaryKey(), value: text("value").notNull(), updatedAt: integer("updated_at", { mode: "timestamp" }) .notNull() .$defaultFn(() => new Date()), }); ``` -------------------------------- ### Create mock locals Source: https://svelteforge.dashboardpack.com/docs/testing Generates an object matching SvelteKit's App.Locals interface for use in load functions and form actions. ```typescript const locals = createMockLocals({ user: { id: "abc", role: "admin", name: "Admin" }, session: { id: "sess123", expiresAt: Date.now() + 86400000 }, }); ``` -------------------------------- ### Configure Playwright for SvelteKit Source: https://svelteforge.dashboardpack.com/docs/testing Defines the web server command and port for the production build in playwright.config.ts. ```typescript // playwright.config.ts import { defineConfig } from "@playwright/test"; export default defineConfig({ webServer: { command: "pnpm build && pnpm preview", port: 4173, }, testDir: "e2e", }); ``` -------------------------------- ### Schedule Automated Backups Source: https://svelteforge.dashboardpack.com/docs/deployment Cron job configuration for performing daily database backups at 3 AM. ```bash # Daily backup at 3 AM 0 3 * * * sqlite3 /app/data/svelteforge.db ".backup /backups/svelteforge-$(date +\%Y\%m\%d).db" ``` -------------------------------- ### Implement SvelteKit Server Handle Hook Source: https://svelteforge.dashboardpack.com/docs/authentication Handles session validation and metadata updates on every incoming request. ```typescript // src/hooks.server.ts export const handle: Handle = async ({ event, resolve }) => { const token = event.cookies.get(SESSION_COOKIE_NAME); if (!token) { event.locals.user = null; event.locals.session = null; return resolve(event); } const { session, user } = await validateSession(token); if (session) { // Refresh cookie with current expiresAt (handles auto-extension) setSessionCookie(event.cookies, token, session.expiresAt); // Update session metadata on every request const ua = event.request.headers.get("user-agent"); const ip = event.getClientAddress(); await db .update(sessions) .set({ userAgent: ua, ipAddress: ip }) .where(eq(sessions.id, session.id)); } else { deleteSessionCookie(event.cookies); } event.locals.user = user; event.locals.session = session; return resolve(event); }; ``` -------------------------------- ### Load Enabled OAuth Providers Source: https://svelteforge.dashboardpack.com/docs/authentication SvelteKit server load function that checks for existing sessions and provides the list of enabled OAuth providers to the frontend. ```typescript // src/routes/(auth)/login/+page.server.ts export const load: PageServerLoad = async ({ locals }) => { if (locals.user) redirect(302, "/"); return { enabledProviders: getEnabledProviders(), }; }; ``` -------------------------------- ### SvelteKit Route Group Structure Source: https://svelteforge.dashboardpack.com/docs/getting-started Visual representation of the project's file-based routing, where directories in parentheses organize layouts without affecting URL paths. ```text src/routes/ (app)/ # Protected routes — requires authentication dashboard/ # Main dashboard with analytics users/ # User management CRUD content/ # CMS pages analytics/ # Charts and data visualization notifications/ settings/ roles/ database/ (auth)/ # Public auth routes — login, register, OAuth callbacks login/ register/ forgot-password/ reset-password/ (public)/ # Public pages — no auth required pricing/ docs/ # Documentation (you are here) api/ # API endpoints logout/ # Server-only logout action ``` -------------------------------- ### Create in-memory test database Source: https://svelteforge.dashboardpack.com/docs/testing Initializes a fresh SQLite database in memory with the required schema for each test run. ```typescript // test-utils.ts import Database from "better-sqlite3"; import { drizzle } from "drizzle-orm/better-sqlite3"; const SCHEMA_SQL = ` CREATE TABLE users ( id TEXT PRIMARY KEY, email TEXT NOT NULL UNIQUE, username TEXT NOT NULL UNIQUE, password_hash TEXT NOT NULL, name TEXT NOT NULL, role TEXT NOT NULL DEFAULT 'viewer', avatar_url TEXT, created_at INTEGER DEFAULT (unixepoch()), updated_at INTEGER DEFAULT (unixepoch()) ); CREATE TABLE sessions ( id TEXT PRIMARY KEY, user_id TEXT NOT NULL REFERENCES users(id), expires_at INTEGER NOT NULL, user_agent TEXT, ip_address TEXT, created_at INTEGER DEFAULT (unixepoch()) ); -- ... remaining tables (pages, notifications, appSettings, etc.) `; export function createTestDb() { const sqlite = new Database(":memory:"); sqlite.pragma("journal_mode = WAL"); sqlite.exec(SCHEMA_SQL); return drizzle(sqlite); } ``` -------------------------------- ### Query Database with Drizzle ORM Source: https://svelteforge.dashboardpack.com/docs/api-reference Demonstrates SQL-like query building and relational API usage with the Drizzle ORM instance. ```typescript import { db } from "$lib/server/db/index.js"; import { users } from "$lib/server/db/schema.js"; import { eq } from "drizzle-orm"; // SQL-like query builder const allUsers = await db.select().from(users); // Relational query API const user = await db.query.users.findFirst({ where: eq(users.email, "admin@svelteforge.dev"), }); ``` -------------------------------- ### Define App Locals Types Source: https://svelteforge.dashboardpack.com/docs/routing Extends the SvelteKit App namespace to include user and session information in event.locals. ```typescript // src/app.d.ts declare global { namespace App { interface Locals { user: SessionUser | null; session: Session | null; } } } ``` -------------------------------- ### Page Status Lifecycle Source: https://svelteforge.dashboardpack.com/docs/content-management Visual representation of the page status transition flow. ```text Draft --> Published --> Archived ``` -------------------------------- ### Mock server database module Source: https://svelteforge.dashboardpack.com/docs/testing Intercepts database imports using vitest and dynamic imports to ensure tests use the in-memory instance instead of the development database. ```typescript import { describe, it, expect, vi, beforeEach } from "vitest"; import { createTestDb, createTestUser, createMockLocals } from "$lib/test-utils.js"; let testDb: ReturnType; // Step 1: Set up the mock BEFORE importing the server module vi.mock("$lib/server/db/index.js", () => ({ get db() { return testDb; }, })); // Step 2: Dynamically import the module AFTER the mock is in place const { load, actions } = await import("./+page.server.js"); describe("Users page", () => { beforeEach(() => { // Fresh database for every test testDb = createTestDb(); }); // ... tests ... }); ``` -------------------------------- ### Pattern Matching with LIKE Source: https://svelteforge.dashboardpack.com/docs/database Performs a case-insensitive search across name and email fields using SQL LIKE. ```javascript import { sql, or } from "drizzle-orm"; const pattern = `%${searchQuery}%`; const results = await db .select({ id: users.id, name: users.name }) .from(users) .where(or( sql`${users.name} LIKE ${pattern}`, sql`${users.email} LIKE ${pattern}` )) .limit(10); ``` -------------------------------- ### Create Session Source: https://svelteforge.dashboardpack.com/docs/authentication Persists a new session in the database with associated metadata. ```typescript export async function createSession( token: string, userId: string, metadata?: { userAgent?: string | null; ipAddress?: string | null } ): Promise { const sessionId = hashToken(token); const expiresAt = Date.now() + SESSION_LIFETIME_MS; // 30 days const session: Session = { id: sessionId, userId, expiresAt, userAgent: metadata?.userAgent ?? null, ipAddress: metadata?.ipAddress ?? null, createdAt: new Date(), }; await db.insert(sessions).values(session); return session; } ``` -------------------------------- ### Implement Profile Update Form Action Source: https://svelteforge.dashboardpack.com/docs/settings Handles profile updates via SvelteKit form actions, including server-side validation and email uniqueness checks using Drizzle ORM. ```javascript // Profile update form action (simplified) profile: async ({ request, locals }) => { if (!locals.user) return fail(401, { message: "Unauthorized" }); const formData = await request.formData(); const name = formData.get("name")?.toString().trim(); const email = formData.get("email")?.toString().trim().toLowerCase(); const avatarUrl = formData.get("avatarUrl")?.toString().trim() || null; // Validate required fields if (!name || !email) { return fail(400, { message: "Name and email are required" }); } // Check email uniqueness (exclude current user) const existing = await db.query.users.findFirst({ where: and(eq(users.email, email), ne(users.id, locals.user.id)), }); if (existing) { return fail(400, { message: "Email is already in use" }); } await db.update(users) .set({ name, email, avatarUrl }) .where(eq(users.id, locals.user.id)); return { success: true, message: "Profile updated" }; } ``` -------------------------------- ### Create a test user Source: https://svelteforge.dashboardpack.com/docs/testing Inserts a user into the test database with defaults that can be overridden via the second argument. ```typescript const user = createTestUser(testDb, { role: "admin", email: "admin@test.com", username: "admin", }); // Defaults if not overridden: // - id: random generated ID // - name: "Test User" // - email: "test@example.com" // - username: "testuser" // - password: hashed "password123" // - role: "viewer" ``` -------------------------------- ### Docker Compose Configuration Source: https://svelteforge.dashboardpack.com/docs/deployment Configuration for running the application with persistent volumes and environment variables. ```yaml version: "3.8" services: svelteforge: build: . ports: - "3000:3000" volumes: - ./data:/app/data environment: - DATABASE_URL=/app/data/svelteforge.db - ORIGIN=https://admin.example.com - GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID} - GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET} - GITHUB_CLIENT_ID=${GITHUB_CLIENT_ID} - GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET} restart: unless-stopped ``` -------------------------------- ### Implement PieChart with Direct Props Source: https://svelteforge.dashboardpack.com/docs/analytics Use direct props for PieChart configuration instead of passing a config object. Ensure the innerRadius and legend are set as individual attributes. ```svelte {#snippet tooltip()} {/snippet} ``` -------------------------------- ### Load content pages with author join in SvelteKit Source: https://svelteforge.dashboardpack.com/docs/content-management Uses a Drizzle ORM left join to fetch page data along with the associated author's name for the content table. ```typescript // content/+page.server.ts — load function with author join export const load: PageServerLoad = async () => { const allPages = db .select({ id: pages.id, title: pages.title, slug: pages.slug, status: pages.status, template: pages.template, authorName: users.name, createdAt: pages.createdAt, updatedAt: pages.updatedAt, publishedAt: pages.publishedAt }) .from(pages) .leftJoin(users, eq(pages.authorId, users.id)) .orderBy(desc(pages.createdAt)) .all(); return { pages: allPages }; }; ``` -------------------------------- ### Implement Auth Guard in SvelteKit Layout Source: https://svelteforge.dashboardpack.com/docs/routing Protects routes by checking authentication, enforcing maintenance mode, and pre-loading sidebar data for all child routes. ```typescript // src/routes/(app)/+layout.server.ts import { redirect, error } from "@sveltejs/kit"; import { db } from "$lib/server/db/index.js"; import { notifications, appSettings } from "$lib/server/db/schema.js"; import { eq, and, or, isNull, sql, desc } from "drizzle-orm"; import type { LayoutServerLoad } from "./$types.js"; export const load: LayoutServerLoad = async ({ locals }) => { // Auth check — redirect to login if not authenticated if (!locals.user) { redirect(302, "/login"); } // Maintenance mode — block non-admins with 503 const maintenanceSetting = await db.query.appSettings.findFirst({ where: eq(appSettings.key, "maintenanceMode"), }); if (maintenanceSetting?.value === "true" && locals.user.role !== "admin") { error(503, "The application is currently under maintenance."); } // Load sidebar data: unread count + recent notifications const userNotificationFilter = or( eq(notifications.userId, locals.user.id), isNull(notifications.userId) ); const [countResult] = await db .select({ count: sql`count(*)` }) .from(notifications) .where(and(eq(notifications.read, false), userNotificationFilter)); const recentNotifications = await db .select({ id: notifications.id, title: notifications.title, message: notifications.message, type: notifications.type, createdAt: notifications.createdAt, }) .from(notifications) .where(and(eq(notifications.read, false), userNotificationFilter)) .orderBy(desc(notifications.createdAt)) .limit(5); return { user: locals.user, unreadNotificationCount: countResult?.count ?? 0, recentNotifications, }; }; ``` -------------------------------- ### Implement Auth Guard in SvelteKit Layout Source: https://svelteforge.dashboardpack.com/docs/authentication Protects routes by checking for a user session and enforcing maintenance mode for non-admin users. ```typescript export const load: LayoutServerLoad = async ({ locals }) => { if (!locals.user) { redirect(302, "/login"); } // Check maintenance mode const maintenanceSetting = await db.query.appSettings.findFirst({ where: eq(appSettings.key, "maintenanceMode"), }); if (maintenanceSetting?.value === "true" && locals.user.role !== "admin") { error(503, "The application is currently under maintenance."); } return { user: locals.user, ... }; }; ``` -------------------------------- ### Define Server-Side Form Action Source: https://svelteforge.dashboardpack.com/docs/routing Handles form data extraction and server-side validation within a SvelteKit +page.server.ts file. ```typescript // src/routes/(app)/users/+page.server.ts import { fail } from "@sveltejs/kit"; import type { Actions } from "./$types.js"; export const actions: Actions = { create: async ({ request }) => { const formData = await request.formData(); const name = formData.get("name"); const email = formData.get("email"); const username = formData.get("username"); const password = formData.get("password"); const role = formData.get("role"); // Server-side validation if (typeof name !== "string" || name.length < 1 || name.length > 100) { return fail(400, { message: "Name is required (1-100 characters)" }); } if (typeof email !== "string" || !email.includes("@")) { return fail(400, { message: "Valid email is required" }); } // ... hash password, insert into database await db.insert(users).values({ id: generateId(10), email: email.toLowerCase(), username: username.toLowerCase(), passwordHash, name, role: role as "admin" | "editor" | "viewer", }); return { success: true }; }, }; ``` -------------------------------- ### Handling form submission with use:enhance Source: https://svelteforge.dashboardpack.com/docs/settings Use the enhance callback to manage toast notifications and update form results asynchronously. ```svelte
{ return async ({ result, update }) => { if (result.type === "success") { toast.success("Profile updated successfully"); } else if (result.type === "failure") { toast.error(result.data?.message ?? "Something went wrong"); } await update(); }; }} > ``` -------------------------------- ### Configure Vite for SSR Source: https://svelteforge.dashboardpack.com/docs/analytics Add LayerChart and svelte-ux to the ssr.noExternal array to ensure proper server-side rendering. ```typescript // vite.config.ts import { sveltekit } from '@sveltejs/kit/vite'; import tailwindcss from '@tailwindcss/vite'; import { defineConfig } from 'vitest/config'; export default defineConfig({ plugins: [tailwindcss(), sveltekit()], ssr: { noExternal: ['layerchart', 'svelte-ux'] }, test: { include: ['src/**/*.test.ts'], } }); ``` -------------------------------- ### Execute Playwright Test Commands Source: https://svelteforge.dashboardpack.com/docs/testing Common CLI commands for running the full test suite, debugging with UI mode, or targeting specific files. ```bash # Run all E2E tests pnpm test:e2e # Run with UI mode for debugging npx playwright test --ui # Run a specific test file npx playwright test e2e/auth.test.ts ``` -------------------------------- ### Implementing a tabbed interface with $state Source: https://svelteforge.dashboardpack.com/docs/settings Manage active tab state using the $state rune to conditionally render content. ```svelte {#each tabs as tab} {/each} {#if activeTab === "profile"} {:else if activeTab === "password"} {/if} ``` -------------------------------- ### Implement Password Change Form Action Source: https://svelteforge.dashboardpack.com/docs/settings Handles password verification, validation, and secure hashing before updating the database. Requires existing user session and Argon2id utility functions. ```javascript // Password change form action (simplified) password: async ({ request, locals }) => { const formData = await request.formData(); const currentPassword = formData.get("currentPassword"); const newPassword = formData.get("newPassword"); const confirmPassword = formData.get("confirmPassword"); // Validate lengths if (newPassword.length < 6 || newPassword.length > 255) { return fail(400, { message: "Password must be 6-255 characters" }); } if (newPassword !== confirmPassword) { return fail(400, { message: "Passwords do not match" }); } // Verify current password with Argon2id const user = await db.query.users.findFirst({ where: eq(users.id, locals.user.id), }); const valid = await verify(user.passwordHash, currentPassword, { memoryCost: 19456, timeCost: 2, outputLen: 32, parallelism: 1, }); if (!valid) { return fail(400, { message: "Current password is incorrect" }); } // Hash and store new password const passwordHash = await hash(newPassword, { memoryCost: 19456, timeCost: 2, outputLen: 32, parallelism: 1, }); await db.update(users) .set({ passwordHash }) .where(eq(users.id, locals.user.id)); return { success: true, message: "Password updated" }; } ``` -------------------------------- ### Create mock request Source: https://svelteforge.dashboardpack.com/docs/testing Wraps FormData into a POST Request object suitable for SvelteKit form action handlers. ```typescript const request = createMockRequest(formData); // Equivalent to: new Request("http://localhost", { method: "POST", body: formData }) ``` -------------------------------- ### Binding form data with $state Source: https://svelteforge.dashboardpack.com/docs/settings Initialize form fields with $state to enable reactive data binding. ```svelte
``` -------------------------------- ### Generate and store password reset token Source: https://svelteforge.dashboardpack.com/docs/authentication Creates a 25-character token, hashes it, and stores it in the database with a one-hour expiration. ```javascript // Generate and store hashed token const token = generateId(25); const tokenHash = /* SHA-256 hash of token */; const expiresAt = new Date(Date.now() + 60 * 60 * 1000); // 1 hour await db.insert(passwordResetTokens).values({ id: tokenId, userId: user.id, tokenHash, expiresAt, }); console.log(`[Password Reset] URL: /reset-password?token=${token}`); ``` -------------------------------- ### Implement Auth Guard in SvelteKit Layout Source: https://svelteforge.dashboardpack.com/docs/project-structure Protects routes within the (app) group by checking for user authentication and maintenance mode status. ```typescript // src/routes/(app)/+layout.server.ts export const load: LayoutServerLoad = async ({ locals }) => { if (!locals.user) { redirect(302, "/login"); } // Check maintenance mode const maintenanceSetting = await db.query.appSettings.findFirst({ where: eq(appSettings.key, "maintenanceMode"), }); if (maintenanceSetting?.value === "true" && locals.user.role !== "admin") { error(503, "The application is currently under maintenance."); } return { user: locals.user, ... }; }; ``` -------------------------------- ### Compose Tailwind classes with cn() Source: https://svelteforge.dashboardpack.com/docs/components Combines clsx and tailwind-merge to resolve conflicting Tailwind classes and handle conditional styling. Import from $lib/utils for consistent class management. ```typescript import { cn } from "$lib/utils"; // Merge base classes with conditional and override classes cn("px-4 py-2 bg-primary", isActive && "bg-primary/90", className); // tailwind-merge ensures no conflicting classes survive ``` -------------------------------- ### Responsive Grid Layout Source: https://svelteforge.dashboardpack.com/docs/theming Use these Tailwind grid utilities to create layouts that stack vertically on mobile and expand to multiple columns on larger viewports. ```html
``` -------------------------------- ### Create Svelte 5 Page Component Source: https://svelteforge.dashboardpack.com/docs/routing Displays the data returned from the server load function using Svelte 5 props. ```html

Reports

{#each data.stats as stat}

{stat.status}

{stat.count}

{/each}
``` -------------------------------- ### Unit test SvelteKit load functions Source: https://svelteforge.dashboardpack.com/docs/testing Uses a mock event object with locals to test load function behavior and data return shapes. ```typescript describe("load function", () => { it("returns user list for admin", async () => { // Seed the test database const admin = createTestUser(testDb, { role: "admin" }); createTestUser(testDb, { email: "viewer@test.com", username: "viewer", role: "viewer", }); // Call the load function const result = await load({ locals: createMockLocals({ user: admin }), url: new URL("http://localhost/users"), }) as any; // Assert returned data shape expect(result.users).toHaveLength(2); expect(result.users[0]).toHaveProperty("id"); expect(result.users[0]).toHaveProperty("email"); expect(result.users[0]).toHaveProperty("role"); // Password hashes should never be returned expect(result.users[0]).not.toHaveProperty("passwordHash"); }); it("restricts data for non-admin users", async () => { const viewer = createTestUser(testDb, { role: "viewer" }); const result = await load({ locals: createMockLocals({ user: viewer }), url: new URL("http://localhost/users"), }) as any; // Viewers may see limited data or get redirected // depending on the page's authorization logic }); }); ``` -------------------------------- ### Load users from database Source: https://svelteforge.dashboardpack.com/docs/user-management Fetches all users from the Drizzle ORM users table, ordered by creation date. ```typescript // users/+page.server.ts — load function export const load: PageServerLoad = async () => { const allUsers = db.select().from(users).orderBy(desc(users.createdAt)).all(); return { users: allUsers }; }; ```