Files
home/Projects/kompose/docs/content/5.stacks/sexy.md
2025-10-09 17:24:27 +02:00

10 KiB
Executable File

title, description, navigation
title description navigation
Sexy - Headless CMS Runway We make content management look good!
icon
i-lucide-sparkles

"We make content management look good!" - Directus + SvelteKit

What's This All About?

This is your full-stack content management system! A headless CMS (Directus) paired with a blazing-fast SvelteKit frontend. It's like WordPress if WordPress went to design school, hit the gym, and learned to code properly. All at sexy.pivoine.art because why not make content management... sexy? 😎

The Power Couple

:icon{name="lucide:palette"} Directus API

Container: sexy_api
Image: directus/directus:11.12.0
Port: 8055
Home: https://sexy.pivoine.art/api

Directus is the headless CMS that doesn't make you cry:

  • :icon{name="lucide:bar-chart"} Database-First: Works with your existing database
  • :icon{name="lucide:ethernet-port"} Admin Panel: Beautiful UI out of the box
  • :icon{name="lucide:plug"} REST + GraphQL: Choose your flavor
  • :icon{name="lucide:image"} Asset Management: Images, videos, files - all handled
  • :icon{name="lucide:users"} User Roles: Granular permissions
  • :icon{name="lucide:refresh-cw"} Real-time: WebSocket support for live updates
  • :icon{name="lucide:palette"} Customizable: Extensions, hooks, custom fields
  • :icon{name="lucide:lock-keyhole"} Auth: Built-in user management and SSO

:icon{name="lucide:zap"} SvelteKit Frontend

Container: sexy_frontend
Image: node:22
Port: 3000
Home: https://sexy.pivoine.art

The face of your content:

  • :icon{name="lucide:rocket"} Lightning Fast: Svelte's magic compilation
  • :icon{name="lucide:target"} SEO Friendly: Server-side rendering
  • :icon{name="lucide:phone"} Responsive: Mobile-first design
  • :icon{name="lucide:palette"} Beautiful: Because sexy.pivoine.art deserves it
  • :icon{name="lucide:refresh-cw"} Real-time Updates: Live data from Directus
  • :icon{name="lucide:sparkles"} Styled: Tailwind CSS + custom design

Architecture

User Request (sexy.pivoine.art)
    ↓
Traefik
    ├─ /api/* → Directus API (Backend)
    └─ /* → SvelteKit (Frontend)

The magic:

  • Frontend requests /api/items/posts
  • Traefik strips /api prefix
  • Routes to Directus
  • Returns JSON
  • Frontend renders beautifully

Configuration Breakdown

Directus Environment

Database:

DB_CLIENT=pg  # PostgreSQL ftw!
DB_HOST=postgres
DB_DATABASE=directus

Cache (Redis for speed):

CACHE_ENABLED=true
CACHE_STORE=redis
REDIS=redis://redis:6379

WebSockets (Real-time magic):

WEBSOCKETS_ENABLED=true

CORS (Frontend can talk to API):

CORS_ENABLED=true
CORS_ORIGIN=https://sexy.pivoine.art
SESSION_COOKIE_DOMAIN=sexy.pivoine.art

Extensions (Custom functionality):

EXTENSIONS_PATH=./extensions
DIRECTUS_BUNDLE=/var/www/sexy.pivoine.art/packages/bundle

Frontend Setup

Running from /var/www/sexy.pivoine.art:

# Built SvelteKit app
node build/index.js

First Time Setup :icon

1. Create Database

docker exec data_postgres createdb -U your_db_user directus

2. Start the Stack

docker compose up -d

3. Access Directus Admin

URL: https://sexy.pivoine.art/api/admin
Email: Your ADMIN_EMAIL
Password: Your ADMIN_PASSWORD

4. Create Your First Collection

  1. Go to Settings → Data Model

  2. Click "Create Collection"

  3. Name it (e.g., "posts")

  4. Add Fields:

    • Title (String)
    • Content (WYSIWYG)
    • Author (Many-to-One User)
    • Published Date (DateTime)
    • Featured Image (Image)
  5. Save and Create Item!

Using the Admin Panel :icon

Content Management

Create Items:

  • Navigate to your collection
  • Click "+" button
  • Fill in fields
  • Save as draft or publish

Media Library:

  • Upload images, videos, PDFs
  • Organize in folders
  • Generate thumbnails automatically
  • Serve optimized versions

User Management:

  • Create editors, authors, admins
  • Set granular permissions
  • SSO integration available

Data Model

Field Types:

  • :icon{name="lucide:file-text"} Text (String, Text, Markdown) -- :icon{name="lucide:arrow-up-0-1"} Numbers (Integer, Float, Decimal)
  • :icon{name="lucide:calendar"} Dates (Date, DateTime, Time)
  • :icon{name="lucide:check"} Booleans & Toggles
  • :icon{name="lucide:palette"} JSON & Code
  • :icon{name="lucide:link"} Relations (O2M, M2O, M2M)
  • :icon{name="lucide:image"} Files & Images -- :icon{name="lucide:map-pin"} Geolocation

API Usage :icon

REST API

Get All Posts:

curl https://sexy.pivoine.art/api/items/posts

Get Single Post:

curl https://sexy.pivoine.art/api/items/posts/1

Create Post (Auth required):

curl -X POST https://sexy.pivoine.art/api/items/posts \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "My First Post",
    "content": "Hello World!"
  }'

Filter & Sort:

curl "https://sexy.pivoine.art/api/items/posts?filter[status][_eq]=published&sort=-date"

GraphQL

query {
  posts {
    id
    title
    content
    author {
      first_name
      last_name
    }
  }
}

Authentication

Login:

curl -X POST https://sexy.pivoine.art/api/auth/login \
  -d '{"email": "user@example.com", "password": "secret"}'

Returns access token for authenticated requests.

Frontend Integration

Fetching in SvelteKit

// src/routes/blog/+page.js
export async function load({ fetch }) {
  const res = await fetch('https://sexy.pivoine.art/api/items/posts');
  const { data } = await res.json();
  
  return {
    posts: data
  };
}
<!-- src/routes/blog/+page.svelte -->
<script>
  export let data;
</script>

{#each data.posts as post}
  <article>
    <h2>{post.title}</h2>
    <p>{post.content}</p>
  </article>
{/each}

Image Optimization

Directus automatically generates thumbnails:

<img 
  src="https://sexy.pivoine.art/api/assets/{id}?width=800&quality=80"
  alt="Featured"
>

Real-Time Updates :icon

WebSocket Connection

import { createDirectus, realtime } from '@directus/sdk';

const client = createDirectus('https://sexy.pivoine.art/api')
  .with(realtime());

client.subscribe('posts', {
  event: 'create',
  query: {
    fields: ['*']
  }
}, (message) => {
  console.log('New post!', message);
});

Extensions & Customization :icon

Custom Hooks

// extensions/hooks/notify-on-publish/index.js
export default ({ filter }) => {
  filter('posts.items.create', async (payload) => {
    // Send notification when post created
    await sendNotification(payload);
    return payload;
  });
};

Custom Endpoints

// extensions/endpoints/stats/index.js
export default (router) => {
  router.get('/', async (req, res) => {
    const stats = await calculateStats();
    res.json(stats);
  });
};

Custom Panels

Create custom admin panels with Vue.js!

Volumes & Data

Uploads Directory

./uploads → /directus/uploads

All uploaded files stored here.

Extensions Bundle

/var/www/sexy.pivoine.art/packages/bundle

Custom extensions and functionality.

Ports & Networking

Service Internal Port External Access
Directus API 8055 /api/* via Traefik
Frontend 3000 /* via Traefik

Content Workflows

Blog Post Workflow

  1. Draft: Writer creates post
  2. Review: Editor reviews content
  3. Approve: Admin approves
  4. Schedule: Set publish date
  5. Publish: Goes live automatically

User Permissions

  • Admin: Full access
  • Editor: Edit content, manage media
  • Author: Create own posts
  • Public: Read published content

Performance Optimization :icon

Caching Strategy

// Redis cache for API responses
CACHE_AUTO_PURGE=true  // Auto-clear on changes
CACHE_TTL=300          // 5 minutes

Image Optimization

  • Automatic WebP conversion
  • Lazy loading
  • Responsive images
  • CDN-ready URLs

Database Queries

  • Indexed fields
  • Query result caching
  • Connection pooling

Security Best Practices :icon

  1. Change Default Password: First thing!
  2. API Access Tokens: Use tokens, not passwords
  3. CORS Configuration: Only allow your domain
  4. Rate Limiting: Protect against abuse
  5. File Upload Validation: Check file types
  6. Regular Backups: Both database and uploads

Troubleshooting

Q: Can't access admin panel?
A: Check ADMIN_EMAIL and ADMIN_PASSWORD in .env

Q: API returns 401?
A: Need authentication token for private collections

Q: Images not loading?
A: Check uploads volume is mounted correctly

Q: Frontend can't fetch API?
A: Verify CORS settings and PUBLIC_URL

Q: Real-time not working?
A: Check WEBSOCKETS_ENABLED=true and wss:// connection

Common Use Cases

Blog Platform

  • Posts, authors, categories
  • Comments system
  • SEO optimization
  • RSS feed

E-commerce

  • Products catalog
  • Inventory management
  • Order processing
  • Customer data

Portfolio Site

  • Project showcase
  • Case studies
  • Client testimonials
  • Contact forms

Documentation

  • Articles & guides
  • Search functionality
  • Version control
  • Multi-language support

Why This Stack is Sexy :icon

  • :icon{name="lucide:sparkles"} Developer Experience: Joy to work with
  • :icon{name="lucide:rocket"} Performance: Fast out of the box
  • :icon{name="lucide:palette"} Design: Beautiful admin interface
  • :icon{name="lucide:wrench"} Flexibility: Customize everything
  • :icon{name="lucide:phone"} Modern: Built with latest tech
  • :icon{name="lucide:smile"} Open Source: Free forever
  • :icon{name="lucide:dumbbell"} Production Ready: Powers serious sites

Resources


"Content management should feel like art, not work." - Sexy Philosophy :icon{name="lucide:sparkles"}:icon{name="lucide:sparkles"}