Skip to content

kevinlarszon/ctx-directory

Repository files navigation

CTX Directory — AI Prompts & Rules Library

CTX Directory is a modern Next.js application for discovering, publishing, bookmarking, and remixing AI prompts and rules. It features categories and subcategories, creator profiles, privacy-aware visibility controls (public/private), and analytics.

This repository contains the web app. Data is powered by Supabase (Postgres, Auth, Storage), and usage analytics are optionally sourced from PostHog.

Tech Stack

  • Next.js 15 (App Router) with React 19 and TypeScript
  • Tailwind CSS v4 and Radix UI primitives
  • TanStack Query for client data fetching and caching
  • Supabase (Auth, Postgres, Storage) via @supabase/ssr and @supabase/supabase-js
  • PostHog client analytics (posthog-js) and server-side HogQL queries (optional)

Key configuration files:

  • next.config.ts — strips console.* in production
  • src/app/layout.tsx — global metadata and SEO defaults
  • src/instrumentation-client.ts — safe client-side PostHog initialization

Quick Start

Prerequisites:

  • Node.js 18+ (Node 20+ recommended)
  • pnpm (preferred) or npm/yarn
  • A Supabase project (URL + keys)
  1. Install dependencies
pnpm install
# or: npm install / yarn install
  1. Create .env.local

At minimum you need your Supabase environment and a public app URL for SEO/canonical metadata.

# Supabase (required)
NEXT_PUBLIC_SUPABASE_URL=your_supabase_project_url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key

# Server-only (optional but recommended for profile avatar uploads)
SUPABASE_SERVICE_ROLE_KEY=your_supabase_service_role_key

# App base URL used for metadata, sitemap, and server calls
NEXT_PUBLIC_APP_URL=http://localhost:3000

# PostHog (optional)
NEXT_PUBLIC_POSTHOG_KEY=phc_xxx               # client key (optional)
NEXT_PUBLIC_POSTHOG_HOST=https://us.posthog.com
# Server-side PostHog for analytics APIs (optional)
POSTHOG_PERSONAL_API_KEY=phx_xxx
POSTHOG_PROJECT_ID=12345
POSTHOG_API_HOST=https://us.posthog.com

Notes:

  • Never expose SUPABASE_SERVICE_ROLE_KEY to the browser. It is read server-side in API routes only.
  • When deployed on Vercel, VERCEL_URL is auto-populated; some server utilities fall back to it when building absolute URLs.
  1. Run the dev server
pnpm dev
# or: npm run dev / yarn dev / bun dev

Visit http://localhost:3000

Scripts

See package.json.

{
  "scripts": {
    "dev": "next dev --turbopack",
    "build": "next build --turbopack",
    "start": "next start",
    "lint": "eslint"
  }
}

Project Structure

src/
  app/                      Next.js App Router (routes, API routes, layouts)
    api/                    Server routes (REST-ish JSON)
    ...
  components/              UI components (screens, cards, forms, navigation)
  content/                 SEO/copy helpers
  contexts/                React contexts (e.g., auth, theme)
  lib/                     Shared libraries
    api/                   Client-side wrappers for API calls
    server/                Server-only helpers (e.g., rate limiting)
    supabase/              Supabase client/server factories
    types/                 Centralized shared TS types
public/                    Static assets (icons, og.png, manifest)

Notable files and utilities:

  • src/lib/server/rate-limit.ts — in-memory fixed-window rate limiter that emits X-RateLimit-* headers.
  • src/lib/validation/prompts.ts — shared prompt validation and limits.
  • src/lib/types/* — canonical TypeScript types for prompts, creators, categories.

Environment Variables

Client (public):

  • NEXT_PUBLIC_SUPABASE_URL
  • NEXT_PUBLIC_SUPABASE_ANON_KEY
  • NEXT_PUBLIC_APP_URL (e.g., https://ctx.directory, used for metadata and server-side fetches)
  • NEXT_PUBLIC_POSTHOG_KEY (optional)
  • NEXT_PUBLIC_POSTHOG_HOST (optional)

Server-only:

  • SUPABASE_SERVICE_ROLE_KEY — used in src/app/api/profile/avatar/route.ts for uploads that must bypass RLS.
  • POSTHOG_PERSONAL_API_KEY — required for analytics endpoints that query HogQL.
  • POSTHOG_PROJECT_ID
  • POSTHOG_API_HOST (optional, defaults to PostHog US)

Supabase Notes

  • The app assumes a Supabase schema with tables including prompts, profiles, prompt_bookmarks, categories, and subcategories. RLS is configured so public content is readable by everyone and private content is only readable by its owner.
  • Avatar uploads go to the profile-avatars storage bucket via the API route src/app/api/profile/avatar/route.ts. When SUPABASE_SERVICE_ROLE_KEY is configured, uploads bypass storage RLS while still requiring an authenticated user.

Privacy & Visibility

Visibility semantics are consistent across server and client:

  • public — visible to everyone
  • private — visible only to the author
  • all — server-side logic allows authors to see their own private items in otherwise public lists

Key API routes were updated to default to visibility=all where appropriate so creators see their private prompts in their own views.

API Reference (selected)

All endpoints are implemented under src/app/api/. Requests and responses are JSON. Many endpoints support caching headers and/or include rate limit headers.

  • GET /api/prompts

    • Query params: visibility (all|public|private, default all), type (default|rule), categoryId, subcategoryId, authorId, search, sort (recommended|newest|bookmarked), limit (max 100), cursor, ids (CSV)
    • Returns { items, nextCursor, hasMore } with keyset pagination
  • POST /api/prompts

    • Body: { title, description?, content, tags?: string[], categoryId?, subcategoryId?, visibility?: 'public'|'private', type?: 'default'|'rule', parentId? }
    • Rate limit: 10/hour per user
  • GET /api/prompts/[id] and GET /api/prompts/slug/[slug]

    • Respect visibility; private prompts are only returned to their author
  • PUT /api/prompts/[id]

    • Update prompt fields; regenerates slug when title changes
    • Rate limit: 60/hour per user
  • DELETE /api/prompts/[id]

    • Deletes a prompt (and related bookmarks)
    • Rate limit: 10/hour per user
  • POST /api/prompts/[id]/bookmark and DELETE /api/prompts/[id]/bookmark

    • Toggle bookmarks; cannot bookmark own prompt
    • Rate limit: 60/min per user
  • GET /api/prompts/bookmarks

    • Returns { promptIds } for the authenticated user
  • GET /api/prompts/popular and GET /api/prompts/newest

    • Query params: visibility, type, limit
  • GET /api/prompts/author/[authorId]

    • Query params: visibility (default all), type, limit
  • GET /api/prompts/analytics

    • Auth required; returns aggregate stats for the authenticated author (optionally accepts authorId when equal to current user)
    • Returns totals and top prompts for the creator dashboard
  • GET /api/categories

    • Query param: type (prompt|rule|both), returns { categories, subcategories }
  • GET /api/creators

    • Query params: limit, offset, includeUserId
  • GET /api/creators/[id]/metrics

    • Computes usage metrics; uses PostHog (optional) if POSTHOG_* server envs are set
    • Rate limit: 60/min per IP
  • GET /api/usernames/check/[username]

    • Validates availability and format
    • Rate limit: 30/min per IP
  • POST /api/profiles

    • Create/update your profile; validates username and URLs
  • GET /api/profiles/[id]

    • Returns public profile info
  • POST /api/profile/avatar

    • Auth required; uploads to profile-avatars bucket
    • Uses service role key when available to bypass RLS for storage operations
  • POST /api/feedback

    • Body: { topic: 'bug'|'feature'|'other', message: string, path?: string, userAgent?: string }
    • Rate limit: 30/hour per user
  • GET /api/analytics/views

    • Query params: type (default|rule), slugs (CSV)
    • Requires POSTHOG_PERSONAL_API_KEY + POSTHOG_PROJECT_ID server envs
    • Rate limit: 60/min per IP

Rate limiting is implemented via src/lib/server/rate-limit.ts and returns X-RateLimit-* headers on applicable responses.

Validation Rules

Shared in src/lib/validation/prompts.ts:

  • Title: 3–50 chars
  • Content: 10–5000 chars
  • Description: up to 300 chars
  • Max 5 tags

SEO & Analytics

  • src/app/layout.tsx sets global metadata, Open Graph/Twitter defaults, icons, and manifest.
  • src/app/sitemap.ts and src/app/robots.ts are configured via NEXT_PUBLIC_APP_URL.
  • Client analytics are initialized in src/instrumentation-client.ts (safe on the client only).
  • Server analytics endpoints require PostHog server credentials.

Deployment

Recommended: Hetzner VPS with Coolify.

  1. Set Environment Variables in your hosting platform (Coolify):

    • NEXT_PUBLIC_SUPABASE_URL
    • NEXT_PUBLIC_SUPABASE_ANON_KEY
    • SUPABASE_SERVICE_ROLE_KEY (server-only)
    • NEXT_PUBLIC_APP_URL (your public domain; e.g., https://ctx.directory)
    • Optional PostHog variables if you use analytics APIs
  2. Build and run

pnpm build && pnpm start

Troubleshooting

  • If avatar uploads fail with a 401 or 403, ensure the request is authenticated and SUPABASE_SERVICE_ROLE_KEY is set server-side. See src/app/api/profile/avatar/route.ts.
  • If analytics endpoints return 500, check POSTHOG_PERSONAL_API_KEY and POSTHOG_PROJECT_ID.
  • If categories/creators appear empty, verify your Supabase schema and RLS policies for public reads.
  • For SEO issues (wrong canonical URLs), ensure NEXT_PUBLIC_APP_URL is set correctly without a trailing slash.

License

Licensed under the MIT License. See the LICENSE file for details.

Trademarks

"CTX", "CTX Directory", and related brand assets are trademarks of their respective owner(s). Use of these brand assets is not covered by the MIT license and requires prior permission. Forks and derivatives must not imply endorsement by or affiliation with the CTX Directory project.

About

Discover, share, and remix community-made AI prompts & rules.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages