#!/usr/bin/env node /** * Directus Collection Setup Script * This script creates collections for our custom database tables in Directus */ const axios = require('axios'); const DIRECTUS_URL = 'http://localhost:8055'; const ADMIN_EMAIL = 'admin@assetmanagement.com'; const ADMIN_PASSWORD = 'AssetAdmin2024!'; let authToken = null; // Create axios instance const api = axios.create({ baseURL: DIRECTUS_URL, timeout: 10000, }); // Add auth token to requests api.interceptors.request.use((config) => { if (authToken) { config.headers.Authorization = `Bearer ${authToken}`; } return config; }); async function authenticate() { try { console.log('šŸ”‘ Authenticating with Directus...'); const response = await api.post('/auth/login', { email: ADMIN_EMAIL, password: ADMIN_PASSWORD, }); authToken = response.data.data.access_token; console.log('āœ… Authentication successful'); return authToken; } catch (error) { console.error('āŒ Authentication failed:', error.response?.data || error.message); throw error; } } async function createCollection(collection) { try { console.log(`šŸ“ Creating collection: ${collection.collection}`); // Check if collection already exists try { await api.get(`/collections/${collection.collection}`); console.log(`ā„¹ļø Collection ${collection.collection} already exists, skipping...`); return; } catch (error) { // Collection doesn't exist, create it } await api.post('/collections', collection); console.log(`āœ… Collection ${collection.collection} created successfully`); } catch (error) { console.error(`āŒ Failed to create collection ${collection.collection}:`, error.response?.data || error.message); } } async function setupCollections() { console.log('šŸš€ Setting up Directus collections...\n'); // Define our collections based on the database schema const collections = [ { collection: 'organizations', meta: { accountability: 'all', archive_app_filter: true, archive_field: null, archive_value: null, collapse: 'open', collection: 'organizations', color: '#6644FF', display_template: '{{ name }}', group: null, hidden: false, icon: 'business', item_duplication_fields: null, note: 'SaaS tenant organizations', preview_url: null, singleton: false, sort: 1, sort_field: null, translations: null, unarchive_value: null, versioning: false }, schema: null }, { collection: 'subscription_plans', meta: { accountability: 'all', archive_app_filter: true, archive_field: null, archive_value: null, collapse: 'open', collection: 'subscription_plans', color: '#00C897', display_template: '{{ display_name }}', group: null, hidden: false, icon: 'paid', item_duplication_fields: null, note: 'Available subscription plans', preview_url: null, singleton: false, sort: 2, sort_field: null, translations: null, unarchive_value: null, versioning: false }, schema: null }, { collection: 'asset_categories', meta: { accountability: 'all', archive_app_filter: true, archive_field: null, archive_value: null, collapse: 'open', collection: 'asset_categories', color: '#FF6B35', display_template: '{{ name }}', group: null, hidden: false, icon: 'category', item_duplication_fields: null, note: 'Asset categorization', preview_url: null, singleton: false, sort: 3, sort_field: null, translations: null, unarchive_value: null, versioning: false }, schema: null }, { collection: 'locations', meta: { accountability: 'all', archive_app_filter: true, archive_field: null, archive_value: null, collapse: 'open', collection: 'locations', color: '#4CAF50', display_template: '{{ name }}', group: null, hidden: false, icon: 'place', item_duplication_fields: null, note: 'Asset locations and facilities', preview_url: null, singleton: false, sort: 4, sort_field: null, translations: null, unarchive_value: null, versioning: false }, schema: null }, { collection: 'vendors', meta: { accountability: 'all', archive_app_filter: true, archive_field: null, archive_value: null, collapse: 'open', collection: 'vendors', color: '#9C27B0', display_template: '{{ name }}', group: null, hidden: false, icon: 'store', item_duplication_fields: null, note: 'Vendors and suppliers', preview_url: null, singleton: false, sort: 5, sort_field: null, translations: null, unarchive_value: null, versioning: false }, schema: null }, { collection: 'assets', meta: { accountability: 'all', archive_app_filter: true, archive_field: null, archive_value: null, collapse: 'open', collection: 'assets', color: '#0078D4', display_template: '{{ name }} ({{ asset_identifier }})', group: null, hidden: false, icon: 'inventory', item_duplication_fields: null, note: 'Main asset registry', preview_url: null, singleton: false, sort: 6, sort_field: null, translations: null, unarchive_value: null, versioning: false }, schema: null }, { collection: 'asset_components', meta: { accountability: 'all', archive_app_filter: true, archive_field: null, archive_value: null, collapse: 'open', collection: 'asset_components', color: '#FF9800', display_template: '{{ name }}', group: null, hidden: false, icon: 'construction', item_duplication_fields: null, note: 'Asset components and parts', preview_url: null, singleton: false, sort: 7, sort_field: null, translations: null, unarchive_value: null, versioning: false }, schema: null }, { collection: 'asset_qr_codes', meta: { accountability: 'all', archive_app_filter: true, archive_field: null, archive_value: null, collapse: 'open', collection: 'asset_qr_codes', color: '#795548', display_template: 'QR Code for {{ asset_id }}', group: null, hidden: false, icon: 'qr_code', item_duplication_fields: null, note: 'QR codes for assets', preview_url: null, singleton: false, sort: 8, sort_field: null, translations: null, unarchive_value: null, versioning: false }, schema: null }, { collection: 'work_order_types', meta: { accountability: 'all', archive_app_filter: true, archive_field: null, archive_value: null, collapse: 'open', collection: 'work_order_types', color: '#E91E63', display_template: '{{ name }}', group: null, hidden: false, icon: 'assignment', item_duplication_fields: null, note: 'Work order categories', preview_url: null, singleton: false, sort: 9, sort_field: null, translations: null, unarchive_value: null, versioning: false }, schema: null }, { collection: 'work_orders', meta: { accountability: 'all', archive_app_filter: true, archive_field: null, archive_value: null, collapse: 'open', collection: 'work_orders', color: '#2196F3', display_template: '{{ title }} ({{ work_order_number }})', group: null, hidden: false, icon: 'build', item_duplication_fields: null, note: 'Work orders and maintenance requests', preview_url: null, singleton: false, sort: 10, sort_field: null, translations: null, unarchive_value: null, versioning: false }, schema: null }, { collection: 'asset_reminders', meta: { accountability: 'all', archive_app_filter: true, archive_field: null, archive_value: null, collapse: 'open', collection: 'asset_reminders', color: '#FF5722', display_template: '{{ title }}', group: null, hidden: false, icon: 'notifications', item_duplication_fields: null, note: 'Maintenance reminders and alerts', preview_url: null, singleton: false, sort: 11, sort_field: null, translations: null, unarchive_value: null, versioning: false }, schema: null } ]; try { await authenticate(); console.log('\nšŸ“ Creating collections...\n'); for (const collection of collections) { await createCollection(collection); } console.log('\nšŸŽ‰ Collection setup completed successfully!'); console.log('\nšŸ“‹ Next steps:'); console.log('1. Visit http://localhost:8055/admin to configure field permissions'); console.log('2. Test the frontend connection'); console.log('3. Create some test assets\n'); } catch (error) { console.error('\nāŒ Setup failed:', error.message); process.exit(1); } } // Run the setup setupCollections().catch(console.error);