#!/usr/bin/env node /** * setup-tables.js — Creates a new "Digital Hub" base in NocoDB * with all 12 tables, fields, and links. * Run once: node setup-tables.js */ require('dotenv').config({ path: __dirname + '/.env' }); const NOCODB_URL = process.env.NOCODB_URL || 'http://localhost:8090'; const NOCODB_TOKEN = process.env.NOCODB_TOKEN; const fs = require('fs'); const path = require('path'); async function request(method, url, body) { const opts = { method, headers: { 'xc-token': NOCODB_TOKEN, 'Content-Type': 'application/json' }, }; if (body) opts.body = JSON.stringify(body); const res = await fetch(url, opts); if (!res.ok) { const text = await res.text(); throw new Error(`${method} ${url} → ${res.status}: ${text}`); } const text = await res.text(); return text ? JSON.parse(text) : {}; } async function createBase() { console.log('Creating "Digital Hub" base...'); const data = await request('POST', `${NOCODB_URL}/api/v2/meta/bases/`, { title: 'Digital Hub', type: 'database', }); console.log(` Base created: ${data.id}`); return data.id; } async function createTable(baseId, title, columns) { console.log(` Creating table: ${title}`); const data = await request('POST', `${NOCODB_URL}/api/v2/meta/bases/${baseId}/tables`, { title, columns, }); console.log(` → ${data.id}`); return data; } async function createLinkColumn(tableId, title, relatedTableId, type = 'hm') { console.log(` Linking: ${title}`); await request('POST', `${NOCODB_URL}/api/v2/meta/tables/${tableId}/columns`, { title, uidt: 'Links', parentId: tableId, childId: relatedTableId, type, }); } // Field type helpers const text = (title) => ({ title, uidt: 'SingleLineText' }); const longText = (title) => ({ title, uidt: 'LongText' }); const email = (title) => ({ title, uidt: 'Email' }); const num = (title) => ({ title, uidt: 'Number' }); const decimal = (title) => ({ title, uidt: 'Decimal' }); const checkbox = (title) => ({ title, uidt: 'Checkbox' }); const date = (title) => ({ title, uidt: 'Date' }); const dateTime = (title) => ({ title, uidt: 'DateTime' }); const singleSelect = (title, options) => ({ title, uidt: 'SingleSelect', dtxp: options.map(o => `'${o}'`).join(','), }); async function main() { try { // 1. Create base const baseId = await createBase(); // 2. Create tables (without links first) const users = await createTable(baseId, 'Users', [ text('name'), email('email'), singleSelect('role', ['superadmin', 'manager', 'contributor']), text('team_role'), longText('brands'), text('phone'), text('avatar'), checkbox('tutorial_completed'), ]); const brands = await createTable(baseId, 'Brands', [ text('name'), text('name_ar'), num('priority'), text('color'), text('icon'), text('category'), text('logo'), ]); const campaigns = await createTable(baseId, 'Campaigns', [ text('name'), longText('description'), date('start_date'), date('end_date'), singleSelect('status', ['planning', 'active', 'paused', 'completed', 'cancelled']), text('color'), decimal('budget'), longText('goals'), longText('platforms'), decimal('budget_spent'), decimal('revenue'), num('impressions'), num('clicks'), num('conversions'), decimal('cost_per_click'), longText('notes'), num('brand_id'), num('created_by_user_id'), ]); const campaignTracks = await createTable(baseId, 'CampaignTracks', [ text('name'), singleSelect('type', ['organic_social', 'paid_social', 'paid_search', 'email', 'seo', 'influencer', 'event', 'other']), text('platform'), decimal('budget_allocated'), decimal('budget_spent'), decimal('revenue'), num('impressions'), num('clicks'), num('conversions'), longText('notes'), singleSelect('status', ['planned', 'active', 'paused', 'completed']), num('campaign_id'), ]); const campaignAssignments = await createTable(baseId, 'CampaignAssignments', [ dateTime('assigned_at'), num('campaign_id'), num('member_id'), num('assigner_id'), ]); const projects = await createTable(baseId, 'Projects', [ text('name'), longText('description'), singleSelect('status', ['active', 'paused', 'completed', 'cancelled']), singleSelect('priority', ['low', 'medium', 'high', 'urgent']), date('start_date'), date('due_date'), num('brand_id'), num('owner_id'), num('created_by_user_id'), ]); const tasks = await createTable(baseId, 'Tasks', [ text('title'), longText('description'), singleSelect('status', ['todo', 'in_progress', 'done']), singleSelect('priority', ['low', 'medium', 'high', 'urgent']), date('start_date'), date('due_date'), checkbox('is_personal'), dateTime('completed_at'), num('project_id'), num('assigned_to_id'), num('created_by_user_id'), ]); const posts = await createTable(baseId, 'Posts', [ text('title'), longText('description'), singleSelect('status', ['draft', 'in_review', 'approved', 'scheduled', 'published', 'rejected']), text('platform'), longText('platforms'), text('content_type'), dateTime('scheduled_date'), dateTime('published_date'), longText('notes'), longText('publication_links'), num('brand_id'), num('assigned_to_id'), num('campaign_id'), num('track_id'), num('created_by_user_id'), ]); const assets = await createTable(baseId, 'Assets', [ text('filename'), text('original_name'), text('mime_type'), num('size'), longText('tags'), text('folder'), num('brand_id'), num('campaign_id'), num('uploader_id'), ]); const postAttachments = await createTable(baseId, 'PostAttachments', [ text('filename'), text('original_name'), text('mime_type'), num('size'), text('url'), num('post_id'), ]); const comments = await createTable(baseId, 'Comments', [ text('entity_type'), num('entity_id'), longText('content'), num('user_id'), ]); const budgetEntries = await createTable(baseId, 'BudgetEntries', [ text('label'), decimal('amount'), text('source'), text('category'), date('date_received'), longText('notes'), num('campaign_id'), ]); // 3. All relationships are now handled via plain Number FK columns // (brand_id, owner_id, campaign_id, etc.) defined directly in each table above. // No NocoDB link columns are needed. // 4. Save base ID to .env const envPath = path.join(__dirname, '.env'); let envContent = fs.readFileSync(envPath, 'utf8'); envContent = envContent.replace(/^NOCODB_BASE_ID=.*$/m, `NOCODB_BASE_ID=${baseId}`); fs.writeFileSync(envPath, envContent); console.log(`\nāœ… Done! Base ID ${baseId} saved to .env`); console.log('Tables created:'); const tables = [users, brands, campaigns, campaignTracks, campaignAssignments, projects, tasks, posts, assets, postAttachments, comments, budgetEntries]; for (const t of tables) { console.log(` ${t.title}: ${t.id}`); } } catch (err) { console.error('Setup failed:', err); process.exit(1); } } main();