This commit is contained in:
Luke Hagar
2025-05-27 16:58:47 -05:00
parent 1b6555d7d5
commit b00c4a2da6
63 changed files with 13936 additions and 2212 deletions

View File

@@ -0,0 +1,191 @@
# SvelteKit Supabase Authentication Setup
This project uses Supabase for authentication with full server-side rendering (SSR) support through SvelteKit hooks.
## Features
- ✅ Email/Password authentication
- ✅ GitHub OAuth integration
- ✅ Server-side authentication with `hooks.server.ts`
- ✅ Protected routes with automatic redirects
- ✅ Session management across client and server
- ✅ TypeScript support with proper types
## Setup Instructions
### 1. Environment Variables
Create a `.env.local` file in your project root:
```bash
PUBLIC_SUPABASE_URL=your_supabase_project_url
PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
```
Get these values from your Supabase project dashboard under Settings > API.
### 2. Supabase Configuration
#### Enable GitHub OAuth Provider
1. Go to your Supabase project dashboard
2. Navigate to **Authentication > Providers**
3. Enable the **GitHub** provider
4. Add your GitHub OAuth credentials:
- **Client ID**: From your GitHub OAuth app
- **Client Secret**: From your GitHub OAuth app
#### GitHub OAuth App Setup
1. Go to GitHub Settings > Developer settings > OAuth Apps
2. Click "New OAuth App"
3. Fill in the details:
- **Application name**: Your app name
- **Homepage URL**: `http://localhost:5173` (for development)
- **Authorization callback URL**: `https://your-project-ref.supabase.co/auth/v1/callback`
4. Note the Client ID and Client Secret for Supabase configuration
### 3. Supabase Authentication Settings
In your Supabase project settings:
1. Go to **Authentication > Settings**
2. Add your site URLs:
- **Site URL**: `http://localhost:5173` (development) / `https://yourdomain.com` (production)
- **Redirect URLs**: Add both your local and production URLs
## Architecture Overview
### Server-Side Authentication (`hooks.server.ts`)
The authentication is handled through SvelteKit hooks:
```typescript
// src/hooks.server.ts
export const handle: Handle = sequence(supabase, authGuard);
```
#### Features:
- **`supabase` hook**: Creates server-side Supabase client with cookie management
- **`authGuard` hook**: Protects routes and handles redirects
- **Session validation**: Validates JWTs on every request
- **Cookie management**: Automatic token refresh and cookie handling
### Protected Routes
Routes are automatically protected based on path patterns:
- **`/dashboard/*`**: Requires authentication, redirects to `/auth/login` if not authenticated
- **`/auth/*`**: Redirects authenticated users to `/dashboard`
### Client-Side Integration (`+layout.svelte`)
The root layout handles client-side authentication state:
```typescript
// Syncs server-side session with client-side
$effect(() => {
session = data.session;
});
// Handles auth state changes
supabase.auth.onAuthStateChange((event, newSession) => {
if (newSession?.expires_at !== session?.expires_at) {
invalidateAll();
}
});
```
## File Structure
```
src/
├── hooks.server.ts # Server-side authentication hooks
├── app.d.ts # TypeScript definitions for Supabase
├── lib/
│ ├── index.ts # Exports (toaster)
│ └── supabaseClient.ts # Browser Supabase client
├── routes/
│ ├── +layout.svelte # Root layout with auth state
│ ├── +layout.server.ts # Server layout with session data
│ ├── auth/
│ │ ├── login/+page.svelte # Login page with GitHub OAuth
│ │ ├── signup/+page.svelte # Signup page with GitHub OAuth
│ │ └── logout/+page.server.ts # Logout action
│ └── dashboard/
│ ├── +layout.server.ts # Dashboard layout (protected)
│ └── +page.svelte # Dashboard page
```
## Usage Examples
### Login Page Features
- Email/password authentication
- GitHub OAuth login
- Form validation and error handling
- Loading states
- Automatic redirect after login
### Signup Page Features
- Email/password registration
- GitHub OAuth signup
- Email confirmation handling
- Error handling with toast notifications
### Dashboard Protection
The dashboard is automatically protected and will:
- Redirect unauthenticated users to login
- Display user information for authenticated users
- Handle session expiration gracefully
## TypeScript Support
Full TypeScript support with proper types:
```typescript
// app.d.ts
interface Locals {
supabase: SupabaseClient;
safeGetSession(): Promise<{ session: Session | null; user: User | null }>;
session: Session | null;
user: User | null;
}
```
## Development
1. Start your development server:
```bash
npm run dev
```
2. Test authentication:
- Visit `/auth/signup` to create an account
- Try GitHub OAuth login
- Visit `/dashboard` to see protected content
- Test logout functionality
## Security Features
- **JWT validation**: Server-side validation of authentication tokens
- **Automatic refresh**: Tokens are refreshed automatically
- **Secure cookies**: HTTP-only cookies for session management
- **Route protection**: Server-side route guards
- **CSRF protection**: Built into Supabase auth flow
## Troubleshooting
### Common Issues
1. **"Cannot redirect during render"**: Usually caused by trying to redirect in a `load` function without throwing the redirect
2. **OAuth callback errors**: Check your GitHub OAuth app callback URL matches Supabase settings
3. **Session not persisting**: Ensure cookies are configured correctly in `hooks.server.ts`
### Debug Tips
- Check browser Network tab for auth requests
- Verify Supabase project settings match your configuration
- Test OAuth flow in incognito mode to avoid cached sessions

392
docs/BLOG_SETUP.md Normal file
View File

@@ -0,0 +1,392 @@
# Markdown Blog Setup with MDSvex (Svelte 5 Compatible)
This project uses a static markdown-based blog system powered by MDSvex for SvelteKit. This implementation is optimized for Svelte 5 and uses component-based rendering instead of HTML content strings.
## Features
-**Markdown Support**: Write posts in markdown with full syntax support
-**Frontmatter Metadata**: YAML frontmatter for post metadata
-**Featured Posts**: Mark posts as featured for special display
-**Tags & Categories**: Organize posts with tags
-**SEO Optimized**: Automatic meta tags and Open Graph support
-**Responsive Design**: Mobile-friendly blog layout
-**Syntax Highlighting**: Code blocks with Shiki highlighting
-**Table of Contents**: Automatic TOC generation
-**Svelte 5 Compatible**: Uses component-based rendering
## File Structure
```
src/
├── lib/
│ ├── posts/ # Markdown blog posts
│ │ ├── getting-started.md
│ │ ├── advanced-features.md
│ │ └── building-integrations.md
│ ├── components/
│ │ └── MDXLayout.svelte # Layout for mdx content
│ ├── blog.ts # Blog utilities and types
│ └── blog-utils.ts # Helper functions
├── routes/
│ └── blog/
│ ├── +page.svelte # Blog index page
│ ├── +page.server.ts # Load all posts
│ └── [slug]/
│ ├── +page.svelte # Individual post page
│ └── +page.server.ts # Load single post
└── docs/
└── BLOG_SETUP.md # This documentation
```
## Creating Blog Posts
### 1. Create a New Markdown File
Create a new `.md` file in `src/lib/posts/`:
```bash
touch src/lib/posts/my-new-post.md
```
### 2. Add Frontmatter
Start your post with YAML frontmatter:
```markdown
---
title: "My Awesome Blog Post"
slug: "my-awesome-blog-post"
excerpt: "A compelling description of your blog post that will appear in listings and meta tags."
publishedAt: "2024-01-30"
author: "Your Name"
tags: ["tutorial", "sveltekit", "web-development"]
featured: true
---
# My Awesome Blog Post
Your content goes here...
```
### 3. Write Your Content
Use standard Markdown syntax for your blog post content:
```markdown
## Section Heading
This is a paragraph with **bold** and *italic* text.
### Code Examples
```javascript
const example = () => {
console.log('Hello from my blog!');
};
```
### Lists
- Item 1
- Item 2
- Item 3
### Links
Check out [SvelteKit](https://kit.svelte.dev) for more information.
```
## Frontmatter Reference
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `title` | string | ✅ | Post title displayed in listings and page title |
| `slug` | string | ✅ | URL-friendly identifier for the post |
| `excerpt` | string | ✅ | Short description for listings and meta tags |
| `publishedAt` | string | ✅ | Publication date in YYYY-MM-DD format |
| `author` | string | ✅ | Author name |
| `tags` | array | ✅ | Array of tags for categorization |
| `featured` | boolean | ❌ | Whether to feature this post (default: false) |
## Svelte 5 Integration
### Component-Based Rendering
This implementation uses Svelte 5's component system. Each markdown file is compiled to a Svelte component:
```typescript
export interface BlogPost {
title: string;
slug: string;
excerpt: string;
publishedAt: string;
author: string;
tags: string[];
featured: boolean;
component?: any; // The Svelte component for rendering
}
```
### Rendering Posts
Posts are rendered using `svelte:component`:
```svelte
<!-- In the blog post page -->
{#if post.component}
<svelte:component this={post.component} />
{:else}
<p>Content not available.</p>
{/if}
```
## Blog Utilities
### Available Functions
The blog system provides several utility functions in `src/lib/blog.ts`:
```typescript
// Get all published posts
const posts = await getAllPosts();
// Get a specific post by slug
const post = await getPostBySlug('my-post-slug');
// Get featured posts only
const featured = await getFeaturedPosts();
// Get posts with a specific tag
const tagged = await getPostsByTag('tutorial');
// Get all unique tags
const tags = await getAllTags();
```
### Helper Functions
Additional utilities in `src/lib/blog-utils.ts`:
```typescript
// Generate URL-friendly slug from title
const slug = generateSlug('My Blog Post Title');
// Create a new post template
const template = createPostTemplate('New Post', 'Author Name');
// Validate post metadata
const errors = validatePostMetadata(postData);
// Search posts
const results = searchPosts(allPosts, 'keyword');
// Get related posts
const related = getRelatedPosts(currentPost, allPosts, 3);
```
## Styling and Layout
### Custom Prose Styles
The blog uses Tailwind's typography plugin with custom styling:
```css
.prose {
@apply text-surface-900-50-token max-w-none;
}
.prose h1 {
@apply text-3xl font-bold mb-6;
}
.prose code {
@apply bg-surface-200 dark:bg-surface-700 px-1 py-0.5 rounded;
}
```
### MDX Layout Component
The `MDXLayout.svelte` component provides consistent styling for markdown content and can be customized for your design needs.
## Configuration
### MDSvex Configuration
The markdown processing is configured in `svelte.config.js`:
```javascript
const mdsvexOptions = {
extensions: ['.md'],
layout: {
_: './src/lib/components/MDXLayout.svelte'
},
remarkPlugins: [remarkUnwrapImages, remarkToc, remarkAbbr],
rehypePlugins: [rehypeSlug],
highlight: {
highlighter: async (code, lang) => {
const shiki = await import('shiki');
const highlighter = await shiki.getHighlighter({
themes: ['github-dark', 'github-light'],
langs: ['javascript', 'typescript', 'html', 'css', 'svelte', 'bash', 'json', 'yaml', 'python', 'rust', 'go']
});
const html = highlighter.codeToHtml(code, {
lang,
themes: {
light: 'github-light',
dark: 'github-dark'
}
});
return html;
}
}
};
```
### Available Plugins
- **remark-unwrap-images**: Removes paragraph wrappers around images
- **remark-toc**: Generates table of contents from headings
- **remark-abbr**: Processes abbreviation definitions
- **rehype-slug**: Adds IDs to headings for anchor links
- **shiki**: Syntax highlighting for code blocks
## SEO Features
### Automatic Meta Tags
Each blog post automatically generates:
- Page title with post title
- Meta description from excerpt
- Open Graph tags for social sharing
- Article metadata (published date, author, tags)
### Example Generated Meta Tags
```html
<title>My Blog Post | Blog</title>
<meta name="description" content="Post excerpt here" />
<meta property="og:title" content="My Blog Post" />
<meta property="og:description" content="Post excerpt here" />
<meta property="og:type" content="article" />
<meta property="article:published_time" content="2024-01-30" />
<meta property="article:author" content="Author Name" />
<meta property="article:tag" content="tutorial" />
```
## Development Workflow
### 1. Create New Post
```bash
# Create new post file
touch src/lib/posts/my-new-post.md
# Add frontmatter and content
# The post will automatically appear in the blog
```
### 2. Preview Posts
Posts are automatically available at:
- Blog index: `/blog`
- Individual post: `/blog/your-post-slug`
### 3. Manage Content
- Posts are automatically sorted by publication date
- Only posts with `publishedAt` dates in the past are shown
- Featured posts appear in a special section
## Advanced Features
### Custom Components in Markdown
You can use Svelte components in your markdown:
```markdown
<script>
import MyComponent from '$lib/components/MyComponent.svelte';
</script>
# My Post
Here's a custom component:
<MyComponent prop="value" />
```
### Dynamic Imports
Posts are loaded dynamically using Vite's `import.meta.glob()`:
```typescript
const allPostFiles = import.meta.glob('/src/lib/posts/*.md');
```
### Build-Time Processing
All markdown processing happens at build time, ensuring:
- Fast page loads
- SEO-friendly static HTML
- No runtime markdown parsing overhead
## Svelte 5 Compatibility
### Key Differences from Previous Versions
1. **Component-based rendering**: Instead of HTML strings, posts are Svelte components
2. **No server-side rendering of content**: Content is rendered client-side as components
3. **Simplified architecture**: No need to handle HTML content strings
### Migration from HTML-based Systems
If migrating from an HTML-content based blog:
1. Update blog post interface to use `component` instead of `content`
2. Replace `{@html post.content}` with `<svelte:component this={post.component} />`
3. Remove any server-side HTML rendering logic
## Deployment
The markdown blog works with any SvelteKit deployment target:
- **Static**: Pre-rendered at build time
- **Server**: Rendered on demand
- **Hybrid**: Mix of static and server rendering
All posts are processed at build time, so they work perfectly with static deployment.
## Best Practices
1. **Consistent Naming**: Use kebab-case for slugs and filenames
2. **Descriptive Excerpts**: Write compelling excerpts for better SEO
3. **Meaningful Tags**: Use consistent, meaningful tags
4. **Proper Dates**: Always use YYYY-MM-DD format for dates
5. **Image Optimization**: Optimize images before including in posts
6. **Internal Linking**: Use relative links for internal content
## Troubleshooting
### Common Issues
1. **Post not appearing**: Check `publishedAt` date is not in the future
2. **Styling issues**: Ensure prose classes are applied correctly
3. **Build errors**: Validate frontmatter YAML syntax
4. **Component not rendering**: Verify the markdown file has proper frontmatter
### Debug Mode
In development, check the browser console for any post loading errors. The system will log issues with individual posts without breaking the entire blog.
### Svelte 5 Specific Issues
- **Component rendering errors**: Check that the markdown files are properly processed by mdsvex
- **Missing syntax highlighting**: Ensure shiki is properly configured
- **Layout issues**: Verify the MDXLayout component is correctly set in svelte.config.js
---
This Svelte 5 compatible markdown blog system provides a powerful, flexible, and maintainable solution for content management while leveraging the full power of Svelte's component system!