Initial commit: Spark Platform with Cartesian SEO Engine
This commit is contained in:
26
directus/Dockerfile
Normal file
26
directus/Dockerfile
Normal file
@@ -0,0 +1,26 @@
|
||||
FROM directus/directus:11.1.0
|
||||
|
||||
USER root
|
||||
|
||||
# Install additional dependencies for image generation
|
||||
RUN apk add --no-cache \
|
||||
python3 \
|
||||
make \
|
||||
g++ \
|
||||
cairo-dev \
|
||||
jpeg-dev \
|
||||
pango-dev \
|
||||
giflib-dev \
|
||||
pixman-dev \
|
||||
pangomm-dev \
|
||||
libjpeg-turbo-dev \
|
||||
freetype-dev
|
||||
|
||||
USER node
|
||||
|
||||
# Copy extensions and scripts
|
||||
COPY --chown=node:node ./extensions /directus/extensions
|
||||
COPY --chown=node:node ./scripts /directus/scripts
|
||||
COPY --chown=node:node ./template /directus/template
|
||||
|
||||
WORKDIR /directus
|
||||
2
directus/extensions/.gitkeep
Normal file
2
directus/extensions/.gitkeep
Normal file
@@ -0,0 +1,2 @@
|
||||
# Placeholder for Directus extensions
|
||||
# Custom extensions go here
|
||||
14
directus/package.json
Normal file
14
directus/package.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "spark-platform-directus",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"import": "node scripts/import_template.js",
|
||||
"load-locations": "node scripts/load_locations.js",
|
||||
"generate-headlines": "node scripts/generate_headlines.js",
|
||||
"assemble-article": "node scripts/assemble_article.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@directus/sdk": "^17.0.0",
|
||||
"dotenv": "^16.3.1"
|
||||
}
|
||||
}
|
||||
79
directus/scripts/import_template.js
Normal file
79
directus/scripts/import_template.js
Normal file
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Spark Platform - Directus Schema Import Script
|
||||
*
|
||||
* This script imports the collections, fields, and relations from the template
|
||||
* into a fresh Directus instance.
|
||||
*
|
||||
* Usage: node scripts/import_template.js
|
||||
*/
|
||||
|
||||
require('dotenv').config();
|
||||
const { createDirectus, rest, staticToken, schemaApply, createCollection, createField, createRelation } = require('@directus/sdk');
|
||||
const collections = require('../template/src/collections.json');
|
||||
const fields = require('../template/src/fields.json');
|
||||
const relations = require('../template/src/relations.json');
|
||||
|
||||
const DIRECTUS_URL = process.env.DIRECTUS_URL || 'http://localhost:8055';
|
||||
const DIRECTUS_TOKEN = process.env.DIRECTUS_ADMIN_TOKEN;
|
||||
|
||||
if (!DIRECTUS_TOKEN) {
|
||||
console.error('❌ DIRECTUS_ADMIN_TOKEN is required');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const directus = createDirectus(DIRECTUS_URL).with(rest()).with(staticToken(DIRECTUS_TOKEN));
|
||||
|
||||
async function importSchema() {
|
||||
console.log('🚀 Starting Spark Platform schema import...\n');
|
||||
|
||||
// Create collections
|
||||
console.log('📦 Creating collections...');
|
||||
for (const collection of collections) {
|
||||
try {
|
||||
await directus.request(createCollection(collection));
|
||||
console.log(` ✅ ${collection.collection}`);
|
||||
} catch (err) {
|
||||
if (err.message?.includes('already exists')) {
|
||||
console.log(` ⏭️ ${collection.collection} (exists)`);
|
||||
} else {
|
||||
console.log(` ❌ ${collection.collection}: ${err.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create fields
|
||||
console.log('\n📝 Creating fields...');
|
||||
for (const [collectionName, collectionFields] of Object.entries(fields)) {
|
||||
for (const field of collectionFields) {
|
||||
try {
|
||||
await directus.request(createField(collectionName, field));
|
||||
console.log(` ✅ ${collectionName}.${field.field}`);
|
||||
} catch (err) {
|
||||
if (err.message?.includes('already exists')) {
|
||||
console.log(` ⏭️ ${collectionName}.${field.field} (exists)`);
|
||||
} else {
|
||||
console.log(` ❌ ${collectionName}.${field.field}: ${err.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create relations
|
||||
console.log('\n🔗 Creating relations...');
|
||||
for (const relation of relations) {
|
||||
try {
|
||||
await directus.request(createRelation(relation));
|
||||
console.log(` ✅ ${relation.collection}.${relation.field} → ${relation.related_collection}`);
|
||||
} catch (err) {
|
||||
if (err.message?.includes('already exists')) {
|
||||
console.log(` ⏭️ ${relation.collection}.${relation.field} (exists)`);
|
||||
} else {
|
||||
console.log(` ❌ ${relation.collection}.${relation.field}: ${err.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n✨ Schema import complete!');
|
||||
}
|
||||
|
||||
importSchema().catch(console.error);
|
||||
179
directus/scripts/load_locations.js
Normal file
179
directus/scripts/load_locations.js
Normal file
@@ -0,0 +1,179 @@
|
||||
/**
|
||||
* Spark Platform - US Location Data Loader
|
||||
*
|
||||
* This script loads US states, counties, and top cities into Directus.
|
||||
*
|
||||
* Usage: node scripts/load_locations.js
|
||||
*/
|
||||
|
||||
require('dotenv').config();
|
||||
const { createDirectus, rest, staticToken, createItem, readItems } = require('@directus/sdk');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const DIRECTUS_URL = process.env.DIRECTUS_URL || 'http://localhost:8055';
|
||||
const DIRECTUS_TOKEN = process.env.DIRECTUS_ADMIN_TOKEN;
|
||||
|
||||
if (!DIRECTUS_TOKEN) {
|
||||
console.error('❌ DIRECTUS_ADMIN_TOKEN is required');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const directus = createDirectus(DIRECTUS_URL).with(rest()).with(staticToken(DIRECTUS_TOKEN));
|
||||
|
||||
// US States data
|
||||
const US_STATES = [
|
||||
{ name: 'Alabama', code: 'AL' },
|
||||
{ name: 'Alaska', code: 'AK' },
|
||||
{ name: 'Arizona', code: 'AZ' },
|
||||
{ name: 'Arkansas', code: 'AR' },
|
||||
{ name: 'California', code: 'CA' },
|
||||
{ name: 'Colorado', code: 'CO' },
|
||||
{ name: 'Connecticut', code: 'CT' },
|
||||
{ name: 'Delaware', code: 'DE' },
|
||||
{ name: 'Florida', code: 'FL' },
|
||||
{ name: 'Georgia', code: 'GA' },
|
||||
{ name: 'Hawaii', code: 'HI' },
|
||||
{ name: 'Idaho', code: 'ID' },
|
||||
{ name: 'Illinois', code: 'IL' },
|
||||
{ name: 'Indiana', code: 'IN' },
|
||||
{ name: 'Iowa', code: 'IA' },
|
||||
{ name: 'Kansas', code: 'KS' },
|
||||
{ name: 'Kentucky', code: 'KY' },
|
||||
{ name: 'Louisiana', code: 'LA' },
|
||||
{ name: 'Maine', code: 'ME' },
|
||||
{ name: 'Maryland', code: 'MD' },
|
||||
{ name: 'Massachusetts', code: 'MA' },
|
||||
{ name: 'Michigan', code: 'MI' },
|
||||
{ name: 'Minnesota', code: 'MN' },
|
||||
{ name: 'Mississippi', code: 'MS' },
|
||||
{ name: 'Missouri', code: 'MO' },
|
||||
{ name: 'Montana', code: 'MT' },
|
||||
{ name: 'Nebraska', code: 'NE' },
|
||||
{ name: 'Nevada', code: 'NV' },
|
||||
{ name: 'New Hampshire', code: 'NH' },
|
||||
{ name: 'New Jersey', code: 'NJ' },
|
||||
{ name: 'New Mexico', code: 'NM' },
|
||||
{ name: 'New York', code: 'NY' },
|
||||
{ name: 'North Carolina', code: 'NC' },
|
||||
{ name: 'North Dakota', code: 'ND' },
|
||||
{ name: 'Ohio', code: 'OH' },
|
||||
{ name: 'Oklahoma', code: 'OK' },
|
||||
{ name: 'Oregon', code: 'OR' },
|
||||
{ name: 'Pennsylvania', code: 'PA' },
|
||||
{ name: 'Rhode Island', code: 'RI' },
|
||||
{ name: 'South Carolina', code: 'SC' },
|
||||
{ name: 'South Dakota', code: 'SD' },
|
||||
{ name: 'Tennessee', code: 'TN' },
|
||||
{ name: 'Texas', code: 'TX' },
|
||||
{ name: 'Utah', code: 'UT' },
|
||||
{ name: 'Vermont', code: 'VT' },
|
||||
{ name: 'Virginia', code: 'VA' },
|
||||
{ name: 'Washington', code: 'WA' },
|
||||
{ name: 'West Virginia', code: 'WV' },
|
||||
{ name: 'Wisconsin', code: 'WI' },
|
||||
{ name: 'Wyoming', code: 'WY' },
|
||||
{ name: 'District of Columbia', code: 'DC' }
|
||||
];
|
||||
|
||||
async function loadLocations() {
|
||||
console.log('🚀 Loading US location data...\n');
|
||||
|
||||
// Check if data already loaded
|
||||
const existingStates = await directus.request(
|
||||
readItems('locations_states', { limit: 1 })
|
||||
);
|
||||
|
||||
if (existingStates.length > 0) {
|
||||
console.log('📊 Location data already loaded. Skipping...');
|
||||
return;
|
||||
}
|
||||
|
||||
// Load states
|
||||
console.log('🗺️ Loading states...');
|
||||
const stateMap = new Map();
|
||||
|
||||
for (const state of US_STATES) {
|
||||
try {
|
||||
const result = await directus.request(
|
||||
createItem('locations_states', {
|
||||
name: state.name,
|
||||
code: state.code,
|
||||
country_code: 'US'
|
||||
})
|
||||
);
|
||||
stateMap.set(state.code, result.id);
|
||||
console.log(` ✅ ${state.name} (${state.code})`);
|
||||
} catch (err) {
|
||||
console.log(` ❌ ${state.name}: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we have the full locations.json file
|
||||
const locationsFile = path.join(__dirname, '../template/src/locations.json');
|
||||
|
||||
if (fs.existsSync(locationsFile)) {
|
||||
console.log('\n📦 Loading counties and cities from locations.json...');
|
||||
const locations = JSON.parse(fs.readFileSync(locationsFile, 'utf8'));
|
||||
|
||||
// Load counties
|
||||
const countyMap = new Map();
|
||||
console.log(` Loading ${locations.counties?.length || 0} counties...`);
|
||||
|
||||
for (const county of (locations.counties || [])) {
|
||||
const stateId = stateMap.get(county.state_code);
|
||||
if (!stateId) continue;
|
||||
|
||||
try {
|
||||
const result = await directus.request(
|
||||
createItem('locations_counties', {
|
||||
name: county.name,
|
||||
state: stateId,
|
||||
fips_code: county.fips_code,
|
||||
population: county.population
|
||||
})
|
||||
);
|
||||
countyMap.set(county.fips_code, result.id);
|
||||
} catch (err) {
|
||||
// Silently continue on duplicate
|
||||
}
|
||||
}
|
||||
console.log(` ✅ Counties loaded`);
|
||||
|
||||
// Load cities
|
||||
console.log(` Loading cities (top 50 per county)...`);
|
||||
|
||||
let cityCount = 0;
|
||||
for (const city of (locations.cities || [])) {
|
||||
const countyId = countyMap.get(city.county_fips);
|
||||
const stateId = stateMap.get(city.state_code);
|
||||
if (!countyId || !stateId) continue;
|
||||
|
||||
try {
|
||||
await directus.request(
|
||||
createItem('locations_cities', {
|
||||
name: city.name,
|
||||
county: countyId,
|
||||
state: stateId,
|
||||
lat: city.lat,
|
||||
lng: city.lng,
|
||||
population: city.population,
|
||||
postal_code: city.postal_code,
|
||||
ranking: city.ranking
|
||||
})
|
||||
);
|
||||
cityCount++;
|
||||
} catch (err) {
|
||||
// Silently continue on duplicate
|
||||
}
|
||||
}
|
||||
console.log(` ✅ ${cityCount} cities loaded`);
|
||||
} else {
|
||||
console.log('\n⚠️ Full locations.json not found. Only states loaded.');
|
||||
console.log(' Download full US location data from GeoNames and run this script again.');
|
||||
}
|
||||
|
||||
console.log('\n✨ Location data import complete!');
|
||||
}
|
||||
|
||||
loadLocations().catch(console.error);
|
||||
243
directus/template/src/collections.json
Normal file
243
directus/template/src/collections.json
Normal file
@@ -0,0 +1,243 @@
|
||||
[
|
||||
{
|
||||
"collection": "sites",
|
||||
"meta": {
|
||||
"icon": "domain",
|
||||
"note": "Multi-tenant sites/domains",
|
||||
"singleton": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "pages",
|
||||
"meta": {
|
||||
"icon": "article",
|
||||
"note": "Site pages with block builder",
|
||||
"singleton": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "posts",
|
||||
"meta": {
|
||||
"icon": "edit_note",
|
||||
"note": "Blog posts and articles",
|
||||
"singleton": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "globals",
|
||||
"meta": {
|
||||
"icon": "settings",
|
||||
"note": "Site-wide settings and branding",
|
||||
"singleton": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "navigation",
|
||||
"meta": {
|
||||
"icon": "menu",
|
||||
"note": "Site navigation menus",
|
||||
"singleton": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "authors",
|
||||
"meta": {
|
||||
"icon": "person",
|
||||
"note": "Content authors",
|
||||
"singleton": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "leads",
|
||||
"meta": {
|
||||
"icon": "contact_mail",
|
||||
"note": "Captured leads from forms",
|
||||
"singleton": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "newsletter_subscribers",
|
||||
"meta": {
|
||||
"icon": "email",
|
||||
"note": "Newsletter subscribers",
|
||||
"singleton": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "forms",
|
||||
"meta": {
|
||||
"icon": "dynamic_form",
|
||||
"note": "Custom form definitions",
|
||||
"singleton": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "form_submissions",
|
||||
"meta": {
|
||||
"icon": "inbox",
|
||||
"note": "Form submission records",
|
||||
"singleton": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "campaign_masters",
|
||||
"meta": {
|
||||
"icon": "campaign",
|
||||
"note": "SEO content campaigns",
|
||||
"singleton": false,
|
||||
"group": "seo_engine"
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "headline_inventory",
|
||||
"meta": {
|
||||
"icon": "title",
|
||||
"note": "Generated headlines from spintax",
|
||||
"singleton": false,
|
||||
"group": "seo_engine"
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "content_fragments",
|
||||
"meta": {
|
||||
"icon": "extension",
|
||||
"note": "Modular content blocks for article assembly",
|
||||
"singleton": false,
|
||||
"group": "seo_engine"
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "generated_articles",
|
||||
"meta": {
|
||||
"icon": "auto_awesome",
|
||||
"note": "AI-assembled SEO articles",
|
||||
"singleton": false,
|
||||
"group": "seo_engine"
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "image_templates",
|
||||
"meta": {
|
||||
"icon": "image",
|
||||
"note": "SVG templates for feature images",
|
||||
"singleton": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "locations_states",
|
||||
"meta": {
|
||||
"icon": "map",
|
||||
"note": "US States",
|
||||
"singleton": false,
|
||||
"group": "locations"
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "locations_counties",
|
||||
"meta": {
|
||||
"icon": "location_city",
|
||||
"note": "US Counties",
|
||||
"singleton": false,
|
||||
"group": "locations"
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "locations_cities",
|
||||
"meta": {
|
||||
"icon": "place",
|
||||
"note": "US Cities (top 50 per county)",
|
||||
"singleton": false,
|
||||
"group": "locations"
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "block_hero",
|
||||
"meta": {
|
||||
"icon": "view_carousel",
|
||||
"note": "Hero section block",
|
||||
"singleton": false,
|
||||
"group": "blocks"
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "block_richtext",
|
||||
"meta": {
|
||||
"icon": "subject",
|
||||
"note": "Rich text content block",
|
||||
"singleton": false,
|
||||
"group": "blocks"
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "block_columns",
|
||||
"meta": {
|
||||
"icon": "view_column",
|
||||
"note": "Multi-column layout block",
|
||||
"singleton": false,
|
||||
"group": "blocks"
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "block_media",
|
||||
"meta": {
|
||||
"icon": "perm_media",
|
||||
"note": "Image/video block",
|
||||
"singleton": false,
|
||||
"group": "blocks"
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "block_steps",
|
||||
"meta": {
|
||||
"icon": "format_list_numbered",
|
||||
"note": "Numbered steps/process block",
|
||||
"singleton": false,
|
||||
"group": "blocks"
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "block_quote",
|
||||
"meta": {
|
||||
"icon": "format_quote",
|
||||
"note": "Quote/testimonial block",
|
||||
"singleton": false,
|
||||
"group": "blocks"
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "block_gallery",
|
||||
"meta": {
|
||||
"icon": "photo_library",
|
||||
"note": "Image gallery block",
|
||||
"singleton": false,
|
||||
"group": "blocks"
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "block_faq",
|
||||
"meta": {
|
||||
"icon": "help",
|
||||
"note": "FAQ accordion block",
|
||||
"singleton": false,
|
||||
"group": "blocks"
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "block_form",
|
||||
"meta": {
|
||||
"icon": "contact_page",
|
||||
"note": "Lead capture form block",
|
||||
"singleton": false,
|
||||
"group": "blocks"
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "block_posts",
|
||||
"meta": {
|
||||
"icon": "library_books",
|
||||
"note": "Posts listing block",
|
||||
"singleton": false,
|
||||
"group": "blocks"
|
||||
}
|
||||
}
|
||||
]
|
||||
1349
directus/template/src/fields.json
Normal file
1349
directus/template/src/fields.json
Normal file
File diff suppressed because it is too large
Load Diff
189
directus/template/src/relations.json
Normal file
189
directus/template/src/relations.json
Normal file
@@ -0,0 +1,189 @@
|
||||
[
|
||||
{
|
||||
"collection": "pages",
|
||||
"field": "site",
|
||||
"related_collection": "sites",
|
||||
"meta": {
|
||||
"many_collection": "pages",
|
||||
"many_field": "site",
|
||||
"one_collection": "sites",
|
||||
"one_field": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "posts",
|
||||
"field": "site",
|
||||
"related_collection": "sites",
|
||||
"meta": {
|
||||
"many_collection": "posts",
|
||||
"many_field": "site",
|
||||
"one_collection": "sites",
|
||||
"one_field": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "posts",
|
||||
"field": "author",
|
||||
"related_collection": "authors",
|
||||
"meta": {
|
||||
"many_collection": "posts",
|
||||
"many_field": "author",
|
||||
"one_collection": "authors",
|
||||
"one_field": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "globals",
|
||||
"field": "site",
|
||||
"related_collection": "sites",
|
||||
"meta": {
|
||||
"many_collection": "globals",
|
||||
"many_field": "site",
|
||||
"one_collection": "sites",
|
||||
"one_field": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "navigation",
|
||||
"field": "site",
|
||||
"related_collection": "sites",
|
||||
"meta": {
|
||||
"many_collection": "navigation",
|
||||
"many_field": "site",
|
||||
"one_collection": "sites",
|
||||
"one_field": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "navigation",
|
||||
"field": "parent",
|
||||
"related_collection": "navigation",
|
||||
"meta": {
|
||||
"many_collection": "navigation",
|
||||
"many_field": "parent",
|
||||
"one_collection": "navigation",
|
||||
"one_field": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "leads",
|
||||
"field": "site",
|
||||
"related_collection": "sites",
|
||||
"meta": {
|
||||
"many_collection": "leads",
|
||||
"many_field": "site",
|
||||
"one_collection": "sites",
|
||||
"one_field": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "campaign_masters",
|
||||
"field": "site",
|
||||
"related_collection": "sites",
|
||||
"meta": {
|
||||
"many_collection": "campaign_masters",
|
||||
"many_field": "site",
|
||||
"one_collection": "sites",
|
||||
"one_field": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "headline_inventory",
|
||||
"field": "campaign",
|
||||
"related_collection": "campaign_masters",
|
||||
"meta": {
|
||||
"many_collection": "headline_inventory",
|
||||
"many_field": "campaign",
|
||||
"one_collection": "campaign_masters",
|
||||
"one_field": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "headline_inventory",
|
||||
"field": "used_on_article",
|
||||
"related_collection": "generated_articles",
|
||||
"meta": {
|
||||
"many_collection": "headline_inventory",
|
||||
"many_field": "used_on_article",
|
||||
"one_collection": "generated_articles",
|
||||
"one_field": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "content_fragments",
|
||||
"field": "campaign",
|
||||
"related_collection": "campaign_masters",
|
||||
"meta": {
|
||||
"many_collection": "content_fragments",
|
||||
"many_field": "campaign",
|
||||
"one_collection": "campaign_masters",
|
||||
"one_field": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "generated_articles",
|
||||
"field": "site",
|
||||
"related_collection": "sites",
|
||||
"meta": {
|
||||
"many_collection": "generated_articles",
|
||||
"many_field": "site",
|
||||
"one_collection": "sites",
|
||||
"one_field": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "generated_articles",
|
||||
"field": "campaign",
|
||||
"related_collection": "campaign_masters",
|
||||
"meta": {
|
||||
"many_collection": "generated_articles",
|
||||
"many_field": "campaign",
|
||||
"one_collection": "campaign_masters",
|
||||
"one_field": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "locations_counties",
|
||||
"field": "state",
|
||||
"related_collection": "locations_states",
|
||||
"meta": {
|
||||
"many_collection": "locations_counties",
|
||||
"many_field": "state",
|
||||
"one_collection": "locations_states",
|
||||
"one_field": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "locations_cities",
|
||||
"field": "county",
|
||||
"related_collection": "locations_counties",
|
||||
"meta": {
|
||||
"many_collection": "locations_cities",
|
||||
"many_field": "county",
|
||||
"one_collection": "locations_counties",
|
||||
"one_field": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "locations_cities",
|
||||
"field": "state",
|
||||
"related_collection": "locations_states",
|
||||
"meta": {
|
||||
"many_collection": "locations_cities",
|
||||
"many_field": "state",
|
||||
"one_collection": "locations_states",
|
||||
"one_field": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"collection": "image_templates",
|
||||
"field": "site",
|
||||
"related_collection": "sites",
|
||||
"meta": {
|
||||
"many_collection": "image_templates",
|
||||
"many_field": "site",
|
||||
"one_collection": "sites",
|
||||
"one_field": null
|
||||
}
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user