Files
portfolio/scripts/setup-directus-projects.js
denshooter e431ff50fc feat: Add Directus setup scripts for collections, fields, and relations
- Created setup-directus-collections.js to automate the creation of tech stack collections, fields, and relations in Directus.
- Created setup-directus-hobbies.js for setting up hobbies collection with translations.
- Created setup-directus-projects.js for establishing projects collection with comprehensive fields and translations.
- Added setup-tech-stack-directus.js to populate tech_stack_items with predefined data.
2026-01-23 02:53:31 +01:00

504 lines
12 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env node
/**
* Directus Projects Collection Setup via REST API
*
* Erstellt die komplette Projects Collection mit allen Feldern und Translations
*
* Usage:
* node scripts/setup-directus-projects.js
*/
const fetch = require('node-fetch');
require('dotenv').config();
const DIRECTUS_URL = process.env.DIRECTUS_URL || 'https://cms.dk0.dev';
const DIRECTUS_TOKEN = process.env.DIRECTUS_STATIC_TOKEN;
if (!DIRECTUS_TOKEN) {
console.error('❌ Error: DIRECTUS_STATIC_TOKEN not found in .env');
process.exit(1);
}
console.log(`🔗 Connecting to: ${DIRECTUS_URL}`);
async function directusRequest(endpoint, method = 'GET', body = null) {
const url = `${DIRECTUS_URL}/${endpoint}`;
const options = {
method,
headers: {
'Authorization': `Bearer ${DIRECTUS_TOKEN}`,
'Content-Type': 'application/json'
}
};
if (body) {
options.body = JSON.stringify(body);
}
try {
const response = await fetch(url, options);
const text = await response.text();
if (!response.ok) {
if (text.includes('already exists') || text.includes('RECORD_NOT_UNIQUE')) {
console.log(` ⚠️ Already exists, skipping...`);
return { data: null, alreadyExists: true };
}
throw new Error(`HTTP ${response.status}: ${text}`);
}
return text ? JSON.parse(text) : {};
} catch (error) {
console.error(`❌ Error calling ${method} ${endpoint}:`, error.message);
throw error;
}
}
async function createProjectsCollections() {
console.log('\n📦 Creating Projects Collections...\n');
// 1. Create projects collection
console.log('1⃣ Creating projects...');
try {
await directusRequest('collections', 'POST', {
collection: 'projects',
meta: {
icon: 'folder',
display_template: '{{title}}',
hidden: false,
singleton: false,
translations: [
{ language: 'en-US', translation: 'Projects' },
{ language: 'de-DE', translation: 'Projekte' }
],
sort_field: 'sort'
},
schema: {
name: 'projects'
}
});
console.log(' ✅ Collection created');
} catch (error) {
console.log(' ⚠️ Collection might already exist');
}
// 2. Create projects_translations collection
console.log('\n2⃣ Creating projects_translations...');
try {
await directusRequest('collections', 'POST', {
collection: 'projects_translations',
meta: {
hidden: true,
icon: 'import_export'
},
schema: {
name: 'projects_translations'
}
});
console.log(' ✅ Collection created');
} catch (error) {
console.log(' ⚠️ Collection might already exist');
}
}
async function createProjectFields() {
console.log('\n🔧 Creating Project Fields...\n');
const projectFields = [
{
field: 'status',
type: 'string',
meta: {
interface: 'select-dropdown',
options: {
choices: [
{ text: 'Published', value: 'published' },
{ text: 'Draft', value: 'draft' },
{ text: 'Archived', value: 'archived' }
]
}
},
schema: { default_value: 'draft', is_nullable: false }
},
{
field: 'sort',
type: 'integer',
meta: { interface: 'input', hidden: true },
schema: {}
},
{
field: 'slug',
type: 'string',
meta: {
interface: 'input',
note: 'URL-friendly identifier (e.g. my-portfolio-website)',
required: true
},
schema: { is_unique: true, is_nullable: false }
},
{
field: 'featured',
type: 'boolean',
meta: {
interface: 'boolean',
note: 'Show on homepage'
},
schema: { default_value: false }
},
{
field: 'category',
type: 'string',
meta: {
interface: 'select-dropdown',
options: {
choices: [
{ text: 'Web Application', value: 'Web Application' },
{ text: 'Mobile App', value: 'Mobile App' },
{ text: 'Backend Development', value: 'Backend Development' },
{ text: 'DevOps', value: 'DevOps' },
{ text: 'AI/ML', value: 'AI/ML' },
{ text: 'Other', value: 'Other' }
]
}
},
schema: { default_value: 'Web Application' }
},
{
field: 'difficulty',
type: 'string',
meta: {
interface: 'select-dropdown',
options: {
choices: [
{ text: 'Beginner', value: 'BEGINNER' },
{ text: 'Intermediate', value: 'INTERMEDIATE' },
{ text: 'Advanced', value: 'ADVANCED' },
{ text: 'Expert', value: 'EXPERT' }
]
}
},
schema: { default_value: 'INTERMEDIATE' }
},
{
field: 'date',
type: 'string',
meta: {
interface: 'input',
note: 'Project date (e.g. "2024" or "2023-2024")'
},
schema: {}
},
{
field: 'time_to_complete',
type: 'string',
meta: {
interface: 'input',
note: 'e.g. "4-6 weeks"',
placeholder: '4-6 weeks'
},
schema: {}
},
{
field: 'github',
type: 'string',
meta: {
interface: 'input',
note: 'GitHub repository URL',
placeholder: 'https://github.com/...'
},
schema: {}
},
{
field: 'live',
type: 'string',
meta: {
interface: 'input',
note: 'Live demo URL',
placeholder: 'https://...'
},
schema: {}
},
{
field: 'image_url',
type: 'string',
meta: {
interface: 'input',
note: 'Main project image URL'
},
schema: {}
},
{
field: 'demo_video',
type: 'string',
meta: {
interface: 'input',
note: 'Demo video URL (YouTube, Vimeo, etc.)'
},
schema: {}
},
{
field: 'color_scheme',
type: 'string',
meta: {
interface: 'input',
note: 'e.g. "Dark theme with blue accents"'
},
schema: { default_value: 'Dark' }
},
{
field: 'accessibility',
type: 'boolean',
meta: {
interface: 'boolean',
note: 'Is the project accessible?'
},
schema: { default_value: true }
},
{
field: 'tags',
type: 'json',
meta: {
interface: 'tags',
note: 'Technology tags (e.g. React, Node.js, Docker)'
},
schema: {}
},
{
field: 'technologies',
type: 'json',
meta: {
interface: 'tags',
note: 'Detailed tech stack'
},
schema: {}
},
{
field: 'challenges',
type: 'json',
meta: {
interface: 'list',
note: 'Challenges faced during development'
},
schema: {}
},
{
field: 'lessons_learned',
type: 'json',
meta: {
interface: 'list',
note: 'What you learned from this project'
},
schema: {}
},
{
field: 'future_improvements',
type: 'json',
meta: {
interface: 'list',
note: 'Planned improvements'
},
schema: {}
},
{
field: 'screenshots',
type: 'json',
meta: {
interface: 'list',
note: 'Array of screenshot URLs'
},
schema: {}
},
{
field: 'performance',
type: 'json',
meta: {
interface: 'input-code',
options: {
language: 'json'
},
note: 'Performance metrics (lighthouse, bundle size, load time)'
},
schema: {}
},
{
field: 'date_created',
type: 'timestamp',
meta: {
special: ['date-created'],
interface: 'datetime',
readonly: true,
hidden: true
},
schema: {}
},
{
field: 'date_updated',
type: 'timestamp',
meta: {
special: ['date-updated'],
interface: 'datetime',
readonly: true,
hidden: true
},
schema: {}
},
{
field: 'translations',
type: 'alias',
meta: {
special: ['translations'],
interface: 'translations',
options: { languageField: 'languages_code' }
}
}
];
console.log('Adding fields to projects:');
for (const field of projectFields) {
try {
await directusRequest('fields/projects', 'POST', field);
console.log(`${field.field}`);
} catch (error) {
console.log(` ⚠️ ${field.field} (might already exist)`);
}
}
// Translation fields
console.log('\nAdding fields to projects_translations:');
const translationFields = [
{
field: 'projects_id',
type: 'uuid',
meta: { hidden: true },
schema: {}
},
{
field: 'languages_code',
type: 'string',
meta: { interface: 'select-dropdown-m2o' },
schema: {}
},
{
field: 'title',
type: 'string',
meta: {
interface: 'input',
note: 'Project title',
required: true
},
schema: { is_nullable: false }
},
{
field: 'description',
type: 'text',
meta: {
interface: 'input-multiline',
note: 'Short description (1-2 sentences)'
},
schema: {}
},
{
field: 'content',
type: 'text',
meta: {
interface: 'input-rich-text-md',
note: 'Full project content (Markdown)'
},
schema: {}
},
{
field: 'meta_description',
type: 'string',
meta: {
interface: 'input',
note: 'SEO meta description'
},
schema: {}
},
{
field: 'keywords',
type: 'string',
meta: {
interface: 'input',
note: 'SEO keywords (comma separated)'
},
schema: {}
}
];
for (const field of translationFields) {
try {
await directusRequest('fields/projects_translations', 'POST', field);
console.log(`${field.field}`);
} catch (error) {
console.log(` ⚠️ ${field.field} (might already exist)`);
}
}
}
async function createProjectRelations() {
console.log('\n🔗 Creating Relations...\n');
const relations = [
{
collection: 'projects_translations',
field: 'projects_id',
related_collection: 'projects',
meta: {
one_field: 'translations',
sort_field: null,
one_deselect_action: 'delete'
},
schema: { on_delete: 'CASCADE' }
},
{
collection: 'projects_translations',
field: 'languages_code',
related_collection: 'languages',
meta: {
one_field: null,
sort_field: null,
one_deselect_action: 'nullify'
},
schema: { on_delete: 'SET NULL' }
}
];
for (let i = 0; i < relations.length; i++) {
try {
await directusRequest('relations', 'POST', relations[i]);
console.log(` ✅ Relation ${i + 1}/${relations.length}`);
} catch (error) {
console.log(` ⚠️ Relation ${i + 1}/${relations.length} (might already exist)`);
}
}
}
async function main() {
console.log('\n╔════════════════════════════════════════╗');
console.log('║ Directus Projects Setup via API ║');
console.log('╚════════════════════════════════════════╝\n');
try {
await createProjectsCollections();
await createProjectFields();
await createProjectRelations();
console.log('\n╔════════════════════════════════════════╗');
console.log('║ ✅ Setup Complete! ║');
console.log('╚════════════════════════════════════════╝\n');
console.log('🎉 Projects Collection ist bereit!\n');
console.log('Nächste Schritte:');
console.log(' 1. Besuche: https://cms.dk0.dev/admin/content/projects');
console.log(' 2. Führe aus: node scripts/migrate-projects-to-directus.js');
console.log(' 3. Verifiziere die Daten im Directus Admin Panel\n');
} catch (error) {
console.error('\n❌ Setup failed:', error);
process.exit(1);
}
}
main();