fix: Resolve schema and editor disconnect, add Flagship Demo Data

This commit is contained in:
cawcenter
2025-12-13 20:56:30 -05:00
parent 1d60ba6a5e
commit 2109aa6867
11 changed files with 401 additions and 22 deletions

View File

@@ -0,0 +1,45 @@
import { createDirectus, rest, authentication, deleteField, createField, deleteItems, readItems } from '@directus/sdk';
import * as dotenv from 'dotenv';
import * as path from 'path';
dotenv.config({ path: path.resolve(__dirname, '../credentials.env') });
const client = createDirectus(process.env.DIRECTUS_PUBLIC_URL!).with(authentication()).with(rest());
async function fixPagesField() {
try {
await client.login(process.env.DIRECTUS_ADMIN_EMAIL!, process.env.DIRECTUS_ADMIN_PASSWORD!);
console.log('🔧 Fixing Pages Schema...');
// 1. Delete all pages (to allow schema change)
console.log(' Deleting existing pages...');
// @ts-ignore
const pages = await client.request(readItems('pages', { limit: -1, fields: ['id'] }));
if (pages.length > 0) {
// @ts-ignore
await client.request(deleteItems('pages', pages.map(p => p.id)));
}
// 2. Delete site field
console.log(' Deleting site field...');
try {
// @ts-ignore
await client.request(deleteField('pages', 'site'));
} catch(e) { console.log(' Field might not exist.'); }
// 3. Re-create site field as UUID
console.log(' Creating site field as UUID...');
// @ts-ignore
await client.request(createField('pages', {
field: 'site',
type: 'uuid',
meta: { interface: 'select-dropdown' },
schema: { is_nullable: true }
}));
console.log('✅ Pages Schema Fixed.');
} catch (e: any) {
console.error('❌ Fix Failed:', e);
}
}
fixPagesField();

View File

@@ -0,0 +1,20 @@
import { createDirectus, rest, authentication, deleteCollection } from '@directus/sdk';
import * as dotenv from 'dotenv';
import * as path from 'path';
dotenv.config({ path: path.resolve(__dirname, '../credentials.env') });
const client = createDirectus(process.env.DIRECTUS_PUBLIC_URL!).with(authentication()).with(rest());
async function forceDelete() {
try {
await client.login(process.env.DIRECTUS_ADMIN_EMAIL!, process.env.DIRECTUS_ADMIN_PASSWORD!);
console.log('Attempting delete pages...');
// @ts-ignore
await client.request(deleteCollection('pages'));
console.log('✅ Deleted pages.');
} catch (e: any) {
console.log('❌ Delete failed:', e);
}
}
forceDelete();

View File

@@ -0,0 +1,148 @@
import { createDirectus, rest, authentication, readItems, createItem } from '@directus/sdk';
import * as dotenv from 'dotenv';
import * as path from 'path';
dotenv.config({ path: path.resolve(__dirname, '../credentials.env') });
const client = createDirectus(process.env.DIRECTUS_PUBLIC_URL!).with(authentication()).with(rest());
async function generateFlagshipDemo() {
console.log('🚀 Generating Flagship Demo Data...');
try {
await client.login(process.env.DIRECTUS_ADMIN_EMAIL!, process.env.DIRECTUS_ADMIN_PASSWORD!);
// 1. Ensure a Site Exists
// @ts-ignore
let sites = await client.request(readItems('sites', { limit: 1 }));
let siteId;
if (sites.length === 0) {
console.log(' Testing Site creating...');
// @ts-ignore
const newSite = await client.request(createItem('sites', {
name: 'Flagship Demo Site',
domain: 'demo.jumpstartscaling.com',
status: 'active'
}));
siteId = newSite.id;
} else {
console.log(' Using existing site: ' + sites[0].name);
siteId = sites[0].id;
}
// 2. Ensure an Avatar Exists
// @ts-ignore
let avatars = await client.request(readItems('avatars', { limit: 1 }));
let avatarId;
if (avatars.length === 0) {
console.log(' Creating Demo Avatar: Sarah (SEO Expert)...');
// @ts-ignore
// Note: Schema uses 'id' string key usually, but let's see. My M1 Setup used 'sarah_marketing' as key.
try {
// @ts-ignore
const newAvatar = await client.request(createItem('avatars', {
id: 'sarah_marketing',
base_name: 'Sarah',
business_niches: ['Marketing', 'SEO', 'SaaS'],
wealth_cluster: 'High'
}));
avatarId = newAvatar.id;
} catch (e) {
// Fallback if ID exists but wasn't returned in list?
avatarId = 'sarah_marketing';
}
} else {
avatarId = avatars[0].id;
}
console.log(` Site ID: ${siteId} (Type: ${typeof siteId})`);
// 3. Create 3 Flagship Landers (Pages in Launchpad)
console.log('\n--- Creating 3 Flagship Landers in Launchpad ---');
const landers = [
{
title: 'High-Converting Home',
permalink: '/',
blocks: [
{ id: 101, block_type: 'hero', block_config: { title: 'Scale Your Business 10x', subtitle: 'The ultimate growth platform for agencies.', bg: 'dark' } },
{ id: 102, block_type: 'features', block_config: { items: [{title: 'Automated SEO', desc: 'Rank #1'}, {title: 'Lead Gen', desc: 'Get more customers'}] } },
{ id: 103, block_type: 'cta', block_config: { label: 'Get Started', url: '/signup' } }
]
},
{
title: 'SEO Services Landing Page',
permalink: '/services/seo',
slug: 'seo-services',
blocks: [
{ id: 201, block_type: 'hero', block_config: { title: 'Dominate Search Results', subtitle: 'We help you own your niche.', bg: 'blue' } },
{ id: 202, block_type: 'content', block_config: { content: '<h2>Why SEO Matters</h2><p>Organic traffic is the best traffic...</p>' } }
]
},
{
title: 'SaaS Case Study',
permalink: '/case-studies/saas-growth',
slug: 'saas-case-study',
blocks: [
{ id: 301, block_type: 'hero', block_config: { title: 'How X Grew 500%', subtitle: 'A deep dive into growth hacking.', bg: 'purple' } },
{ id: 302, block_type: 'content', block_config: { content: '<p>It started with a simple idea...</p>' } }
]
}
];
for (const lander of landers) {
try {
// @ts-ignore
await client.request(createItem('pages', {
...lander,
slug: lander.permalink.replace(/^\//, '').replace(/\//g, '-') || 'home',
site: siteId, // relation ID
status: 'published'
}));
console.log(` ✅ Created Page: ${lander.title}`);
} catch (e: any) {
console.error(` ❌ Failed to create page ${lander.title}:`);
if (e.errors) console.error(JSON.stringify(e.errors, null, 2));
else console.error(e.message);
// Skip to next to try generic jobs
}
}
// 4. Create 3 Long Form Article Jobs (In Factory)
console.log('\n--- Queuing 3 Long Form Articles in Factory ---');
const articles = [
{ type: 'Topic Cluster', topic: 'Future of AI in Marketing', niche: 'Marketing' },
{ type: 'Geo Expansion', topic: 'Best Plumber in Austin', niche: 'Plumbing' },
{ type: 'Spintax Mass', topic: 'Affordable CRM Software', niche: 'SaaS' }
];
for (const art of articles) {
// @ts-ignore
await client.request(createItem('generation_jobs', {
site_id: siteId, // Schema uses 'site_id' or relation? Types say site_id, schema says campaign->site. Factory->Job link?
// Job schema has 'site_id' (legacy?) or 'site' (relation)? My setup script didn't explicitly add 'site_id' to `generation_jobs`, it existed or I added 'campaign'.
// If `generation_jobs` was existing, it probably has `site_id` field from older setup. I'll use `config` to be safe.
type: art.type,
status: 'queued',
progress: 0,
priority: 'high',
config: {
topic: art.topic,
niche: art.niche,
avatar: avatarId,
template: 'long_form_v1'
}
}));
console.log(` ✅ Queued Job: ${art.topic}`);
}
console.log('\n✅ Flagship Demo Data Generation Complete!');
} catch (error: any) {
console.error('❌ Generation Failed:', error.message, error);
}
}
generateFlagshipDemo();

View File

@@ -0,0 +1,16 @@
import { createDirectus, rest, authentication, readFields } from '@directus/sdk';
import * as dotenv from 'dotenv';
import * as path from 'path';
dotenv.config({ path: path.resolve(__dirname, '../credentials.env') });
const client = createDirectus(process.env.DIRECTUS_PUBLIC_URL!).with(authentication()).with(rest());
async function inspect() {
await client.login(process.env.DIRECTUS_ADMIN_EMAIL!, process.env.DIRECTUS_ADMIN_PASSWORD!);
// @ts-ignore
const fields = await client.request(readFields('pages'));
const blocksField = fields.find((f: any) => f.field === 'blocks');
console.log('Blocks Field:', blocksField);
}
inspect();

View File

@@ -0,0 +1,40 @@
import { createDirectus, rest, authentication, deleteCollection } from '@directus/sdk';
import * as dotenv from 'dotenv';
import * as path from 'path';
import { exec } from 'child_process';
dotenv.config({ path: path.resolve(__dirname, '../credentials.env') });
const client = createDirectus(process.env.DIRECTUS_PUBLIC_URL!).with(authentication()).with(rest());
async function resetPages() {
try {
await client.login(process.env.DIRECTUS_ADMIN_EMAIL!, process.env.DIRECTUS_ADMIN_PASSWORD!);
console.log('🗑️ Deleting pages collection...');
try {
// @ts-ignore
await client.request(deleteCollection('pages'));
console.log('✅ Deleted pages collection.');
} catch (e: any) {
console.log(' Pages collection might not exist or verify failed: ' + e.message);
}
console.log('🔄 Re-running setup schema...');
exec('npx tsx backend/scripts/setup_launchpad_schema.ts', (err, stdout, stderr) => {
if (err) console.error(stderr);
console.log(stdout);
console.log('🔄 Re-running demo generation...');
exec('npx tsx backend/scripts/generate_flagship_demo.ts', (err, stdout, stderr) => {
if (err) console.error(stderr);
console.log(stdout);
});
});
} catch (e) {
console.error(e);
}
}
resetPages();

View File

@@ -69,7 +69,7 @@ async function setupLaunchpadSchema() {
{ field: 'blocks', type: 'json', meta: { interface: 'list', note: 'JSON structure of page blocks' } }, // Using JSON for blocks primarily for flexibility
{ field: 'seo_title', type: 'string' },
{ field: 'seo_description', type: 'text' },
{ field: 'site', type: 'integer', meta: { interface: 'select-dropdown' }, schema: { is_nullable: true } } // Simplified relationship
{ field: 'site', type: 'uuid', meta: { interface: 'select-dropdown' }, schema: { is_nullable: true } } // UUID relationship
];
for (const f of pageFields) {

View File

@@ -0,0 +1,73 @@
import { createDirectus, rest, authentication, readCollections } from '@directus/sdk';
import * as dotenv from 'dotenv';
import * as path from 'path';
dotenv.config({ path: path.resolve(__dirname, '../credentials.env') });
const DIRECTUS_URL = process.env.DIRECTUS_PUBLIC_URL;
const EMAIL = process.env.DIRECTUS_ADMIN_EMAIL;
const PASSWORD = process.env.DIRECTUS_ADMIN_PASSWORD;
async function verifySystem() {
console.log('🔍 System Diagnostic Starting...\n');
// 1. Directus Connection
console.log(`[1/3] Testing Directus Connection at ${DIRECTUS_URL}...`);
if (!DIRECTUS_URL || !EMAIL || !PASSWORD) {
console.error('❌ Missing credentials in credentials.env');
return;
}
try {
const client = createDirectus(DIRECTUS_URL).with(authentication()).with(rest());
await client.login(EMAIL, PASSWORD);
console.log('✅ Directus Auth: SUCCESS');
// Check Priority 1 Collections
const requiredCollections = [
'avatars', 'avatar_variants', 'geo_clusters', 'spintax_dictionaries', 'cartesian_patterns'
];
console.log('\nChecking Priority 1 Collections:');
// @ts-ignore
const collections = await client.request(readCollections());
const collectionNames = collections.map((c: any) => c.collection);
let missing = [];
for (const req of requiredCollections) {
if (collectionNames.includes(req)) {
console.log(`${req}`);
} else {
console.log(`${req} (MISSING)`);
missing.push(req);
}
}
if (missing.length === 0) {
console.log('\n✅ All Priority 1 Collections Configured.');
} else {
console.error('\n❌ Critical Collections Missing!');
}
} catch (error: any) {
console.error('❌ Directus Connection Failed:', error.message);
}
// 2. Coolify Token (Static Check)
console.log('\n[2/3] Checking Coolify Configuration...');
if (process.env.COOLIFY_TOKEN) {
console.log('✅ COOLIFY_TOKEN found in env.');
} else {
console.log('⚠️ COOLIFY_TOKEN not found in credentials.env (Deployment checks may fail).');
}
// 3. SSH (Environment Check)
console.log('\n[3/3] Checking SSH Configuration...');
if (process.env.SSH_PRIVATE_KEY) {
console.log('✅ SSH_PRIVATE_KEY found in env.');
} else {
console.log('⚠️ SSH_PRIVATE_KEY not found in credentials.env.');
}
}
verifySystem();