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

452 lines
10 KiB
Markdown

---
title: Sexy - Headless CMS Runway
description: "We make content management look good!"
navigation:
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
- 🎛️ **Admin Panel**: Beautiful UI out of the box
- :icon{name="lucide:plug"} **REST + GraphQL**: Choose your flavor
- 🖼️ **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
- 📱 **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**:
```bash
DB_CLIENT=pg # PostgreSQL ftw!
DB_HOST=postgres
DB_DATABASE=directus
```
**Cache** (Redis for speed):
```bash
CACHE_ENABLED=true
CACHE_STORE=redis
REDIS=redis://redis:6379
```
**WebSockets** (Real-time magic):
```bash
WEBSOCKETS_ENABLED=true
```
**CORS** (Frontend can talk to API):
```bash
CORS_ENABLED=true
CORS_ORIGIN=https://sexy.pivoine.art
SESSION_COOKIE_DOMAIN=sexy.pivoine.art
```
**Extensions** (Custom functionality):
```bash
EXTENSIONS_PATH=./extensions
DIRECTUS_BUNDLE=/var/www/sexy.pivoine.art/packages/bundle
```
### Frontend Setup
Running from `/var/www/sexy.pivoine.art`:
```bash
# Built SvelteKit app
node build/index.js
```
## First Time Setup :icon{name="lucide:rocket"}
### 1. Create Database
```bash
docker exec data_postgres createdb -U your_db_user directus
```
### 2. Start the Stack
```bash
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 🎛️
### 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)
- 🔢 Numbers (Integer, Float, Decimal)
- 📅 Dates (Date, DateTime, Time)
- ✅ Booleans & Toggles
- :icon{name="lucide:palette"} JSON & Code
- :icon{name="lucide:link"} Relations (O2M, M2O, M2M)
- 🖼️ Files & Images
- 📍 Geolocation
## API Usage :icon{name="lucide:plug"}
### REST API
**Get All Posts**:
```bash
curl https://sexy.pivoine.art/api/items/posts
```
**Get Single Post**:
```bash
curl https://sexy.pivoine.art/api/items/posts/1
```
**Create Post** (Auth required):
```bash
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**:
```bash
curl "https://sexy.pivoine.art/api/items/posts?filter[status][_eq]=published&sort=-date"
```
### GraphQL
```graphql
query {
posts {
id
title
content
author {
first_name
last_name
}
}
}
```
### Authentication
**Login**:
```bash
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
```javascript
// 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
};
}
```
```svelte
<!-- 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:
```html
<img
src="https://sexy.pivoine.art/api/assets/{id}?width=800&quality=80"
alt="Featured"
>
```
## Real-Time Updates :icon{name="lucide:refresh-cw"}
### WebSocket Connection
```javascript
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{name="lucide:wrench"}
### Custom Hooks
```javascript
// 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
```javascript
// 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{name="lucide:rocket"}
### Caching Strategy
```javascript
// 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{name="lucide:lock"}
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{name="lucide:sparkles"}
- :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
- 📱 **Modern**: Built with latest tech
- 🆓 **Open Source**: Free forever
- :icon{name="lucide:dumbbell"} **Production Ready**: Powers serious sites
## Resources
- [Directus Documentation](https://docs.directus.io/)
- [SvelteKit Docs](https://kit.svelte.dev/docs)
- [Directus Extensions](https://docs.directus.io/extensions/)
- [GraphQL Guide](https://graphql.org/learn/)
---
*"Content management should feel like art, not work."* - Sexy Philosophy :icon{name="lucide:sparkles"}:icon{name="lucide:sparkles"}