Backend API Next.js dengan integrasi Gemini AI untuk Jekyll Studio webapp. API ini menyediakan endpoints lengkap untuk membuat, mengelola, dan men-deploy Jekyll sites menggunakan AI.
Terinspirasi dari "Firebase Studio, tapi untuk Jekyll" membangun fondasi backend yang solid dan canggih untuk melakukan persis seperti Firebase Studio.
-
Backend Terkelola (Managed Backend): Sama seperti Firebase yang menyediakan backend siap pakai, proyek ini juga menyediakan API yang mengabstraksi semua kerumitan. Pengguna frontend tidak perlu tahu cara install Ruby, menjalankan
jekyll build
, atau mengelola Docker. Mereka tinggal klik satu tombol di UI. -
Fungsi Inti Yang Sudah Siap:
- "Authentication/Database" (Manajemen Situs): Punya sistem untuk membuat, melihat, dan menghapus "proyek" atau situs.
- "Hosting" (Build & Serve): Punya endpoint untuk men-deploy (
jekyll serve
) dan membangun (jekyll build
) situs. - "Cloud Functions" (AI & Kustomisasi): Menggunakan kekuatan AI dari Gemini sebagai "fungsi cloud" untuk men-generate seluruh struktur situs dari sebuah prompt, sesuatu yang sangat modern.
- "Storage" (File Management): Punya API untuk eksplorasi, membaca, dan menulis file di dalam setiap situs Jekyll.
- β Gemini AI Integration - Generate Jekyll sites dari natural language prompts
- β Jekyll Container Management - Automated Jekyll site creation dan building
- β Real-time Updates - WebSocket untuk live updates dan file changes
- β File Management - CRUD operations untuk Jekyll files
- β Security - Rate limiting, CORS, input validation
- β Multi-site Support - Handle multiple Jekyll sites simultaneously
- β Live Preview - Development server dengan live reload
- Node.js 18+
- Docker & Docker Compose
- Gemini AI API Key
git clone https://github.com/daffadevhosting/jekyll-studio-api.git
cd jekyll-studio-api
npm install
cp .env.example .env
# Edit .env dengan your API keys
npm run dev
# Start complete stack
docker-compose -f docker-compose.api.yml up -d
# Start only API + Jekyll
docker-compose -f docker-compose.api.yml up api jekyll redis
jekyll-studio-api/
βββ pages/api/ # Next.js API routes
β βββ sites/ # Site management endpoints
β βββ ai/ # AI generation endpoints
β βββ websocket.ts # WebSocket server
βββ lib/ # Core libraries
β βββ gemini.ts # Gemini AI service
β βββ jekyll-manager.ts # Jekyll operations manager
β βββ utils.ts # Utility functions
βββ middleware/ # Security middleware
βββ docker/ # Docker configurations
βββ projects/ # Generated Jekyll sites
βββ templates/ # Jekyll templates
POST /api/sites/create
Content-Type: application/json
{
"name": "my-blog",
"prompt": "Create a personal tech blog with dark theme and syntax highlighting"
}
GET /api/sites
GET /api/sites/[id]
DELETE /api/sites/[id]
POST /api/sites/[id]/build
POST /api/sites/[id]/serve
Content-Type: application/json
{
"port": 4001 // optional
}
DELETE /api/sites/[id]/serve
GET /api/sites/[id]/files?path=/
GET /api/sites/[id]/files?path=_config.yml
PUT /api/sites/[id]/files
Content-Type: application/json
{
"filePath": "_posts/2024-01-01-hello-world.md",
"content": "---\nlayout: post\ntitle: Hello World\n---\n\nContent here..."
}
POST /api/ai/generate
Content-Type: application/json
{
"type": "site",
"prompt": "Create a portfolio website for a web developer"
}
POST /api/ai/generate
Content-Type: application/json
{
"type": "component",
"prompt": "Create a hero section layout",
"context": {
"componentType": "layout"
}
}
POST /api/ai/generate
Content-Type: application/json
{
"type": "styles",
"prompt": "Modern dark theme with purple accents"
}
POST /api/ai/generate
Content-Type: application/json
{
"type": "improve",
"prompt": "Make this more engaging",
"context": {
"content": "existing content here",
"improvements": "add more personality and examples"
}
}
const ws = new WebSocket('ws://localhost:8080');
ws.on('message', (data) => {
const event = JSON.parse(data);
console.log('Event:', event.type, event.data);
});
sites
- Initial sites listsiteStatusChanged
- Site status updatesfileChanged
- File change notificationssiteBuilt
- Build completion notifications
- API Calls: 100 requests/minute
- AI Generation: 10 requests/minute
- Site Creation: 5 sites/5 minutes
- XSS protection
- SQL injection prevention
- File path traversal protection
- Site name validation
- Configurable allowed origins
- Secure headers
- Credential support
docker-compose -f docker-compose.api.yml up -d
# With HTTPS and database
docker-compose -f docker-compose.api.yml --profile production --profile persistence up -d
- API: Next.js application
- Jekyll: Jekyll container for site operations
- Redis: Caching and session storage
- Nginx: Reverse proxy (production)
- PostgreSQL: Data persistence (optional)
- FileBrowser: File management UI (optional)
GEMINI_API_KEY=your_gemini_api_key
NEXTAUTH_SECRET=your_secure_secret
# Database
DATABASE_URL=postgresql://user:pass@localhost:5432/jekyll_studio
# Redis
REDIS_URL=redis://localhost:6379
# Security
ALLOWED_ORIGINS=http://localhost:3000,https://yourdomain.com
RATE_LIMIT_WINDOW_MS=60000
RATE_LIMIT_MAX_REQUESTS=100
# Email (notifications)
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
[email protected]
SMTP_PASS=your_app_password
- Default: API + Jekyll + Redis
- Production: + Nginx with SSL
- Persistence: + PostgreSQL database
- Tools: + FileBrowser for file management
// Create new site
async function createSite(prompt) {
const response = await fetch('/api/sites/create', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: 'my-site',
prompt: prompt
})
});
return response.json();
}
// Watch site status
const ws = new WebSocket('ws://localhost:8080');
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'siteStatusChanged') {
updateSiteStatus(data.data);
}
};
// composables/useSites.js
import { ref, onMounted } from 'vue'
export function useSites() {
const sites = ref([])
const loading = ref(false)
const createSite = async (siteData) => {
loading.value = true
try {
const response = await $fetch('/api/sites/create', {
method: 'POST',
body: siteData
})
sites.value.push(response.site)
return response
} finally {
loading.value = false
}
}
const fetchSites = async () => {
const response = await $fetch('/api/sites')
sites.value = response.sites
}
onMounted(fetchSites)
return { sites, createSite, fetchSites, loading }
}
# Create site
curl -X POST http://localhost:3000/api/sites/create \
-H "Content-Type: application/json" \
-d '{"name": "blog", "prompt": "Create a minimalist blog"}'
# Build site
curl -X POST http://localhost:3000/api/sites/{id}/build
# Update file
curl -X PUT http://localhost:3000/api/sites/{id}/files \
-H "Content-Type: application/json" \
-d '{"filePath": "index.md", "content": "# Hello World"}'
import requests
import json
class JekyllStudioClient:
def __init__(self, base_url="http://localhost:3000"):
self.base_url = base_url
def create_site(self, name, prompt):
response = requests.post(
f"{self.base_url}/api/sites/create",
json={"name": name, "prompt": prompt}
)
return response.json()
def build_site(self, site_id):
response = requests.post(
f"{self.base_url}/api/sites/{site_id}/build"
)
return response.json()
def serve_site(self, site_id, port=None):
data = {"port": port} if port else {}
response = requests.post(
f"{self.base_url}/api/sites/{site_id}/serve",
json=data
)
return response.json()
# Usage
client = JekyllStudioClient()
site = client.create_site("my-blog", "Create a tech blog with dark theme")
build_result = client.build_site(site['site']['id'])
serve_result = client.serve_site(site['site']['id'])
npm run test
npm run test:integration
Import the Postman collection:
curl -o jekyll-studio.postman_collection.json \
https://raw.githubusercontent.com/your-repo/postman-collection.json
# Install k6
npm install -g k6
# Run load tests
k6 run tests/load-test.js
GET /api/health
Response:
{
"status": "healthy",
"timestamp": "2024-01-01T00:00:00Z",
"services": {
"jekyll": "running",
"redis": "connected",
"database": "connected"
}
}
# View API logs
docker-compose logs -f api
# View Jekyll container logs
docker-compose logs -f jekyll
# View all logs
docker-compose logs -f
GET /api/metrics
# Production environment
cp .env.example .env.production
# Configure production values
docker build -t jekyll-studio-api:latest -f Dockerfile.api .
# Start production stack
docker-compose -f docker-compose.api.yml --profile production up -d
# Generate SSL certificates (Let's Encrypt)
docker run --rm -v ./nginx/ssl:/etc/letsencrypt \
certbot/certbot certonly --webroot \
-w /var/www/certbot -d yourdomain.com
docker-compose exec postgres psql -U jekyll_user -d jekyll_studio -f /app/migrations/init.sql
# Backup PostgreSQL
docker-compose exec postgres pg_dump -U jekyll_user jekyll_studio > backup.sql
# Restore
docker-compose exec -T postgres psql -U jekyll_user jekyll_studio < backup.sql
# Backup all sites
tar -czf sites-backup.tar.gz projects/
# Restore
tar -xzf sites-backup.tar.gz
Error: GEMINI_API_KEY is required
Solution: Set your Gemini API key in .env
Error: Permission denied accessing /var/run/docker.sock
Solution: Add user to docker group
sudo usermod -aG docker $USER
Error: Port 3000 is already in use
Solution: Change port in .env
or stop conflicting service
Check logs:
docker-compose logs jekyll
# Enable debug logging
DEBUG=jekyll-studio:* npm run dev
# Clone repository
git clone https://github.com/daffadevhosting/jekyll-studio-api.git
cd jekyll-studio-api
# Install dependencies
npm install
# Start development environment
npm run dev
# Run tests
npm test
- ESLint configuration
- Prettier formatting
- TypeScript strict mode
- Fork repository
- Create feature branch
- Add tests for new features
- Ensure all tests pass
- Update documentation
- Submit pull request
CC0 1.0 Universal License - see LICENSE file for details.
- Frontend Repository: Jekyll Studio Frontend
- Docker Images: Docker Hub
- Documentation: Full Documentation
- Issues: GitHub Issues