mirror of
https://github.com/waynesutton/markdown-site.git
synced 2026-01-12 04:09:14 +00:00
fix: post mutation
This commit is contained in:
@@ -383,6 +383,7 @@ export const syncPosts = internalMutation({
|
||||
blogFeatured: v.optional(v.boolean()),
|
||||
newsletter: v.optional(v.boolean()),
|
||||
contactForm: v.optional(v.boolean()),
|
||||
unlisted: v.optional(v.boolean()),
|
||||
}),
|
||||
),
|
||||
},
|
||||
@@ -433,6 +434,7 @@ export const syncPosts = internalMutation({
|
||||
blogFeatured: post.blogFeatured,
|
||||
newsletter: post.newsletter,
|
||||
contactForm: post.contactForm,
|
||||
unlisted: post.unlisted,
|
||||
lastSyncedAt: now,
|
||||
});
|
||||
updated++;
|
||||
@@ -487,6 +489,7 @@ export const syncPostsPublic = mutation({
|
||||
blogFeatured: v.optional(v.boolean()),
|
||||
newsletter: v.optional(v.boolean()),
|
||||
contactForm: v.optional(v.boolean()),
|
||||
unlisted: v.optional(v.boolean()),
|
||||
}),
|
||||
),
|
||||
},
|
||||
@@ -537,6 +540,7 @@ export const syncPostsPublic = mutation({
|
||||
blogFeatured: post.blogFeatured,
|
||||
newsletter: post.newsletter,
|
||||
contactForm: post.contactForm,
|
||||
unlisted: post.unlisted,
|
||||
lastSyncedAt: now,
|
||||
});
|
||||
updated++;
|
||||
|
||||
@@ -27,14 +27,26 @@ Released December 30, 2025
|
||||
- Automated CLAUDE.md updates via sync-discovery-files.ts
|
||||
- CLAUDE.md status comment updated during `npm run sync:discovery`
|
||||
- Includes current site name, post count, page count, and last updated timestamp
|
||||
- Unlisted posts feature
|
||||
- New `unlisted` frontmatter field for blog posts
|
||||
- Set `unlisted: true` to hide posts from listings while keeping them accessible via direct link
|
||||
- Unlisted posts are excluded from: blog listings, featured sections, tag pages, search results, and related posts
|
||||
- Posts remain accessible via direct URL (e.g., `/blog/post-slug`)
|
||||
- Useful for draft posts, private content, or posts you want to share via direct link only
|
||||
|
||||
**Technical details:**
|
||||
|
||||
- New file: `CLAUDE.md` in project root
|
||||
- New directory: `.claude/skills/` with three markdown files
|
||||
- Updated: `scripts/sync-discovery-files.ts` to update CLAUDE.md alongside AGENTS.md and llms.txt
|
||||
- Updated: `convex/schema.ts` - Added `unlisted` optional boolean field to posts table
|
||||
- Updated: `convex/posts.ts` - All listing queries filter out unlisted posts
|
||||
- Updated: `convex/search.ts` - Search excludes unlisted posts from results
|
||||
- Updated: `scripts/sync-posts.ts` - Added `unlisted` to interfaces and parsing logic
|
||||
- Updated: `src/pages/Write.tsx` - Added `unlisted` to POST_FIELDS frontmatter reference
|
||||
- Updated documentation: `.claude/skills/frontmatter.md`, `content/pages/docs.md`, `content/blog/setup-guide.md`, `files.md`
|
||||
|
||||
Updated files: `CLAUDE.md`, `.claude/skills/frontmatter.md`, `.claude/skills/convex.md`, `.claude/skills/sync.md`, `scripts/sync-discovery-files.ts`, `files.md`, `changelog.md`, `TASK.md`
|
||||
Updated files: `CLAUDE.md`, `.claude/skills/frontmatter.md`, `.claude/skills/convex.md`, `.claude/skills/sync.md`, `scripts/sync-discovery-files.ts`, `convex/schema.ts`, `convex/posts.ts`, `convex/search.ts`, `scripts/sync-posts.ts`, `src/pages/Write.tsx`, `files.md`, `changelog.md`, `content/pages/changelog-page.md`
|
||||
|
||||
## v2.0.0
|
||||
|
||||
|
||||
@@ -125,6 +125,7 @@ Content here...
|
||||
| `blogFeatured` | No | Show as featured on blog page (first becomes hero, rest in 2-column row) |
|
||||
| `newsletter` | No | Override newsletter signup display (`true` to show, `false` to hide) |
|
||||
| `contactForm` | No | Enable contact form on this post |
|
||||
| `unlisted` | No | Hide from listings but allow direct access via slug. Set `true` to hide from blog listings, featured sections, tag pages, search results, and related posts. Post remains accessible via direct link. |
|
||||
| `showImageAtTop` | No | Set `true` to display the `image` field at the top of the post above the header (default: `false`) |
|
||||
|
||||
### Static pages
|
||||
@@ -169,6 +170,8 @@ Content here...
|
||||
|
||||
**Hide pages from navigation:** Set `showInNav: false` to keep a page published and accessible via direct URL, but hidden from the navigation menu. Pages with `showInNav: false` remain searchable and available via API endpoints. Useful for pages you want to link directly but not show in the main nav.
|
||||
|
||||
**Unlisted posts:** Set `unlisted: true` to hide a blog post from all listings while keeping it accessible via direct link. Unlisted posts are excluded from: blog listings (`/blog` page), featured sections (homepage), tag pages (`/tags/[tag]`), search results (Command+K), and related posts. The post remains accessible via direct URL (e.g., `/blog/post-slug`). Useful for draft posts, private content, or posts you want to share via direct link only. Note: `unlisted` only works for blog posts, not pages.
|
||||
|
||||
**Show image at top:** Add `showImageAtTop: true` to display the `image` field at the top of the post/page above the header. Default behavior: if `showImageAtTop` is not set or `false`, image only used for Open Graph previews and featured card thumbnails.
|
||||
|
||||
**Image lightbox:** Images in blog posts and pages automatically open in a full-screen lightbox when clicked (if enabled in `siteConfig.imageLightbox.enabled`). This allows readers to view images at full size. The lightbox can be closed by clicking outside the image, pressing Escape, or clicking the close button.
|
||||
|
||||
190
public/raw/how-i-added-workos-with-cursor.md
Normal file
190
public/raw/how-i-added-workos-with-cursor.md
Normal file
@@ -0,0 +1,190 @@
|
||||
# How I added WorkOS to my Convex app with Cursor
|
||||
|
||||
> A timeline of adding WorkOS AuthKit authentication to my markdown blog dashboard using Cursor, prompt engineering, and vibe coding. From PRD import to published feature.
|
||||
|
||||
---
|
||||
Type: post
|
||||
Date: 2025-12-30
|
||||
Reading time: 8 min read
|
||||
Tags: cursor, workos, convex, prompt-engineering, ai-coding
|
||||
---
|
||||
|
||||
# How I added WorkOS to my Convex app with Cursor
|
||||
|
||||
I added WorkOS AuthKit authentication to my markdown blog dashboard using Cursor, prompt engineering, and what I call vibe coding. Here's the timeline from start to published.
|
||||
|
||||
## The goal
|
||||
|
||||
Add optional WorkOS authentication to the `/dashboard` page. The dashboard should work with or without WorkOS configured. When authentication is enabled, users log in before accessing the dashboard.
|
||||
|
||||
## Timeline: start to published
|
||||
|
||||
### Step 1: Updated docs and cursor rules
|
||||
|
||||
I started by updating `content/pages/docs.md` with a dashboard section explaining how it works. Then I updated my Cursor rules to include WorkOS documentation patterns and Convex AuthKit integration guidelines.
|
||||
|
||||
The docs update gave me a clear picture of what I wanted to build. The cursor rules helped Cursor understand the patterns I use for authentication in Convex apps.
|
||||
|
||||
### Step 2: Imported PRD from Claude
|
||||
|
||||
I had a conversation with Claude about adding WorkOS to a Convex app. Claude generated a step-by-step PRD that covered:
|
||||
|
||||
- WorkOS account setup
|
||||
- Environment variable configuration
|
||||
- Convex auth configuration
|
||||
- React component structure
|
||||
- Route handling
|
||||
|
||||
I imported this PRD into my project at `prds/workos-authkit-dashboard-guide.md`. It became the blueprint for the entire feature.
|
||||
|
||||
### Step 3: Created a plan in Cursor
|
||||
|
||||
I opened Cursor and asked it to create a plan based on the PRD. Cursor analyzed the PRD and generated a structured plan file at `.cursor/plans/workos_setup_9603c983.plan.md`.
|
||||
|
||||
The plan broke down the work into specific tasks:
|
||||
|
||||
- Create Callback.tsx
|
||||
- Add callback route to App.tsx
|
||||
- Update Dashboard.tsx with auth protection
|
||||
- Test the authentication flow
|
||||
|
||||
Each task had a clear status and description. This gave me a roadmap I could follow step by step.
|
||||
|
||||
### Step 4: Updated environment variables
|
||||
|
||||
I added WorkOS environment variables to `.env.local`:
|
||||
|
||||
```env
|
||||
VITE_WORKOS_CLIENT_ID=client_01XXXXXXXXXXXXXXXXX
|
||||
VITE_WORKOS_REDIRECT_URI=http://localhost:5173/callback
|
||||
```
|
||||
|
||||
I also added `WORKOS_CLIENT_ID` to my Convex environment variables through the Convex dashboard. These variables connect the frontend and backend to WorkOS.
|
||||
|
||||
### Step 5: Cursor and Opus built the app
|
||||
|
||||
I started implementing the plan with Cursor. I asked it to create the Callback component first. Cursor generated `src/pages/Callback.tsx` with proper WorkOS auth handling.
|
||||
|
||||
Then I asked Cursor to update `src/App.tsx` to add the callback route. It added the route correctly, matching the existing route patterns.
|
||||
|
||||
For the Dashboard component, I asked Cursor to add authentication protection. It wrapped the dashboard content with `Authenticated`, `Unauthenticated`, and `AuthLoading` components from Convex React.
|
||||
|
||||
### Step 6: Debugged routes for dashboard page
|
||||
|
||||
The dashboard needed to work in two modes:
|
||||
|
||||
1. With WorkOS configured and `requireAuth: true` - requires login
|
||||
2. Without WorkOS or with `requireAuth: false` - open access
|
||||
|
||||
I asked Cursor to implement conditional authentication. It created a utility function `isWorkOSConfigured()` that checks if WorkOS environment variables are set.
|
||||
|
||||
The Dashboard component now checks:
|
||||
|
||||
- If dashboard is disabled, show disabled message
|
||||
- If auth is required but WorkOS isn't configured, show setup instructions
|
||||
- If WorkOS isn't configured and auth isn't required, show dashboard directly
|
||||
- If WorkOS is configured, use the auth flow
|
||||
|
||||
This conditional logic ensures the dashboard works whether WorkOS is set up or not.
|
||||
|
||||
### Step 7: Frontmatter integration
|
||||
|
||||
I wanted the dashboard authentication to be configurable via `siteConfig.ts`. I added a `dashboard` configuration object:
|
||||
|
||||
```typescript
|
||||
dashboard: {
|
||||
enabled: true,
|
||||
requireAuth: false, // Set to true to require WorkOS authentication
|
||||
},
|
||||
```
|
||||
|
||||
Cursor helped me integrate this configuration into the Dashboard component. The component reads `siteConfig.dashboard.enabled` and `siteConfig.dashboard.requireAuth` to determine behavior.
|
||||
|
||||
### Step 8: Published and documented
|
||||
|
||||
After testing locally, I:
|
||||
|
||||
1. Deployed the changes to production
|
||||
2. Updated `changelog.md` with the new feature
|
||||
3. Updated `content/pages/changelog-page.md` with release notes
|
||||
4. Created a blog post: "How to setup WorkOS"
|
||||
5. Updated `files.md` with new file descriptions
|
||||
|
||||
The feature went live on December 29, 2025 as part of v1.45.0.
|
||||
|
||||
## What I learned about prompt engineering
|
||||
|
||||
### Start with documentation
|
||||
|
||||
Updating docs first helped me clarify what I wanted to build. When I asked Cursor to implement features, it had context from the docs to understand the patterns.
|
||||
|
||||
### Use PRDs as blueprints
|
||||
|
||||
The PRD from Claude became the single source of truth. Every implementation step referenced the PRD. This kept the work focused and prevented scope creep.
|
||||
|
||||
### Break work into small tasks
|
||||
|
||||
The plan file broke the work into specific, actionable tasks. Each task was small enough that Cursor could complete it in one go. This made progress visible and debugging easier.
|
||||
|
||||
### Iterate on prompts
|
||||
|
||||
When Cursor didn't get something right, I refined my prompts. Instead of "add authentication," I said "add WorkOS authentication that works with or without WorkOS configured, checking siteConfig.dashboard.requireAuth."
|
||||
|
||||
Specific prompts led to better results.
|
||||
|
||||
### Trust but verify
|
||||
|
||||
Cursor generated working code, but I tested each change. The conditional authentication logic needed manual verification to ensure it handled all cases correctly.
|
||||
|
||||
## The vibe coding experience
|
||||
|
||||
Vibe coding means working with AI tools in a flow state. You describe what you want, the AI generates code, you test it, and you iterate.
|
||||
|
||||
With Cursor, this felt natural:
|
||||
|
||||
1. I described the feature
|
||||
2. Cursor generated the code
|
||||
3. I tested it locally
|
||||
4. I asked for refinements
|
||||
5. We repeated until it worked
|
||||
|
||||
The back-and-forth felt like pair programming with a very fast partner who never gets tired.
|
||||
|
||||
## Key files created
|
||||
|
||||
- `src/pages/Callback.tsx` - Handles OAuth callback
|
||||
- `src/utils/workos.ts` - WorkOS configuration utility
|
||||
- `convex/auth.config.ts` - Convex auth configuration
|
||||
- `prds/workos-authkit-dashboard-guide.md` - Step-by-step PRD
|
||||
- `.cursor/plans/workos_setup_9603c983.plan.md` - Implementation plan
|
||||
|
||||
## Key files modified
|
||||
|
||||
- `src/main.tsx` - Added conditional WorkOS providers
|
||||
- `src/App.tsx` - Added callback route handling
|
||||
- `src/pages/Dashboard.tsx` - Added optional authentication
|
||||
- `src/config/siteConfig.ts` - Added dashboard configuration
|
||||
- `content/pages/docs.md` - Added dashboard documentation
|
||||
|
||||
## Result
|
||||
|
||||
The dashboard now supports optional WorkOS authentication. Users can:
|
||||
|
||||
- Use the dashboard without WorkOS (open access)
|
||||
- Enable WorkOS authentication via `siteConfig.dashboard.requireAuth`
|
||||
- See setup instructions if auth is required but WorkOS isn't configured
|
||||
|
||||
The implementation is clean, type-safe, and follows Convex best practices. It works whether WorkOS is configured or not.
|
||||
|
||||
## Takeaways
|
||||
|
||||
1. **Documentation first** - Writing docs clarifies requirements
|
||||
2. **PRDs as blueprints** - Import PRDs to guide implementation
|
||||
3. **Small tasks** - Break work into specific, actionable items
|
||||
4. **Specific prompts** - Detailed prompts produce better code
|
||||
5. **Test everything** - Verify AI-generated code works correctly
|
||||
6. **Iterate quickly** - Use AI tools to move fast and refine
|
||||
|
||||
Adding WorkOS to my Convex app took a few hours from start to published. Most of that time was testing and refining. The actual coding was fast thanks to Cursor and good prompt engineering.
|
||||
|
||||
The [How to setup WorkOS](https://www.markdown.fast/how-to-setup-workos) is the setup guide covers everything you need to get started
|
||||
@@ -2,8 +2,10 @@
|
||||
|
||||
This is the homepage index of all published content.
|
||||
|
||||
## Blog Posts (17)
|
||||
## Blog Posts (18)
|
||||
|
||||
- **[How I added WorkOS to my Convex app with Cursor](/raw/how-i-added-workos-with-cursor.md)** - A timeline of adding WorkOS AuthKit authentication to my markdown blog dashboard using Cursor, prompt engineering, and vibe coding. From PRD import to published feature.
|
||||
- Date: 2025-12-30 | Reading time: 8 min read | Tags: cursor, workos, convex, prompt-engineering, ai-coding
|
||||
- **[How to setup WorkOS with Markdown Sync](/raw/how-to-setup-workos.md)** - Step-by-step guide to configure WorkOS AuthKit authentication for your markdown blog dashboard. WorkOS is optional and can be enabled in siteConfig.ts.
|
||||
- Date: 2025-12-29 | Reading time: 10 min read | Tags: workos, authentication, tutorial, dashboard
|
||||
- **[How to use the Markdown sync dashboard](/raw/how-to-use-the-markdown-sync-dashboard.md)** - Learn how to use the dashboard at /dashboard to manage content, configure your site, and sync markdown files without leaving your browser.
|
||||
@@ -51,6 +53,6 @@ This is the homepage index of all published content.
|
||||
|
||||
---
|
||||
|
||||
**Total Content:** 17 posts, 7 pages
|
||||
**Total Content:** 18 posts, 7 pages
|
||||
|
||||
All content is available as raw markdown files at `/raw/{slug}.md`
|
||||
|
||||
@@ -345,6 +345,7 @@ Your markdown content here...
|
||||
| `authorName` | No | Author display name shown next to date |
|
||||
| `authorImage` | No | Round author avatar image URL |
|
||||
| `rightSidebar` | No | Enable right sidebar with CopyPageDropdown (opt-in, requires explicit `true`) |
|
||||
| `unlisted` | No | Hide from listings but allow direct access via slug. Set `true` to hide from blog listings, featured sections, tag pages, search results, and related posts. Post remains accessible via direct link. |
|
||||
|
||||
### How Frontmatter Works
|
||||
|
||||
|
||||
Reference in New Issue
Block a user