#!/usr/bin/env node /* * LM USA — Seed Script * * Usage: * PAYLOAD_URL=http://localhost:3000 PAYLOAD_API_KEY=your-key node seed.mjs * * Creates: * - Contact form * - Home page / * - Virtuell Fiber /virtuell-fiber * - VPS Server /vps * - Contact /contact * - Header navigation * - Footer */ const PAYLOAD_URL = process.env.PAYLOAD_URL || 'http://localhost:3000' const API_KEY = process.env.PAYLOAD_API_KEY if (!API_KEY) { console.error('❌ Missing PAYLOAD_API_KEY environment variable') process.exit(1) } const headers = { 'Content-Type': 'application/json', Authorization: `users API-Key ${API_KEY}`, } // ─── Helpers ────────────────────────────────────────────────────────────────── function richText(text) { const paragraphs = text.split('\n').filter(Boolean) return { root: { type: 'root', children: paragraphs.map((p) => ({ type: 'paragraph', children: [ { type: 'text', text: p, format: 0, detail: 0, mode: 'normal', style: '', version: 1 }, ], direction: 'ltr', format: '', indent: 0, version: 1, })), direction: 'ltr', format: '', indent: 0, version: 1, }, } } async function apiPost(collection, data) { const res = await fetch(`${PAYLOAD_URL}/api/${collection}`, { method: 'POST', headers, body: JSON.stringify(data), }) if (!res.ok) { const err = await res.text() throw new Error(`POST /api/${collection} failed (${res.status}): ${err}`) } return res.json() } async function apiGet(collection, query = '') { const res = await fetch(`${PAYLOAD_URL}/api/${collection}${query}`, { headers }) if (!res.ok) { const err = await res.text() throw new Error(`GET /api/${collection} failed (${res.status}): ${err}`) } return res.json() } async function apiUpdateGlobal(slug, data) { const res = await fetch(`${PAYLOAD_URL}/api/globals/${slug}`, { method: 'POST', headers, body: JSON.stringify(data), }) if (!res.ok) { const err = await res.text() throw new Error(`POST /api/globals/${slug} failed (${res.status}): ${err}`) } return res.json() } async function createPageIfNotExists(slug, data) { const existing = await apiGet('pages', `?where[slug][equals]=${slug}&limit=1`) if (existing.docs?.length > 0) { console.log(` ⏭ Page "${slug}" already exists — skipping`) return existing.docs[0] } const result = await apiPost('pages', data) return result.doc } // ─── Main ───────────────────────────────────────────────────────────────────── async function seed() { console.log(`🌱 Seeding LM USA database at ${PAYLOAD_URL}...`) // ─── 1. Contact Form ─────────────────────────────────────────────────────── console.log(' → Creating contact form...') let contactForm const existingForms = await apiGet('forms', '?where[title][equals]=Contact Form&limit=1') if (existingForms.docs?.length > 0) { console.log(' ⏭ Contact form already exists — skipping') contactForm = existingForms.docs[0] } else { const result = await apiPost('forms', { title: 'Contact Form', fields: [ { name: 'name', label: 'Name', blockType: 'text', required: true, width: 50 }, { name: 'company', label: 'Company', blockType: 'text', required: false, width: 50 }, { name: 'email', label: 'Email', blockType: 'email', required: true, width: 50 }, { name: 'phone', label: 'Phone', blockType: 'text', required: false, width: 50 }, { name: 'message', label: 'Message', blockType: 'textarea', required: true }, ], submitButtonLabel: 'Send Message', confirmationType: 'message', confirmationMessage: richText( "Thanks for reaching out. We'll get back to you within 24 hours.", ), }) contactForm = result.doc } // ─── 2. Home Page ────────────────────────────────────────────────────────── console.log(' → Creating Home page...') await createPageIfNotExists('index', { title: 'Home', slug: 'index', _status: 'published', publishedAt: new Date().toISOString(), layout: [ // Hero // theme: 'light' | 'dark' // textColor: 'auto' | 'white' | 'dark' { blockType: 'lmHero', heading: 'Infrastructure Without Borders', subheading: 'Layer & Mesh USA', body: 'Edge compute, virtual fiber, and managed servers — built for businesses that need sovereign, reliable infrastructure. Configure your setup and get started in minutes.', ctaText: 'Explore Products', ctaLink: '#products', secondaryCtaText: 'Contact Us', secondaryCtaLink: '/contact', theme: 'dark', textColor: 'auto', }, // Link Cards // cardStyle: 'outlined' | 'dark' | 'gray' | 'yellow' // sectionBackground: 'white' | 'gray' | 'dark' // title is textarea (supports \n) { blockType: 'lmLinkCards', heading: 'Choose Your Infrastructure', description: 'Two paths to managed IT — hardware at your location or virtual in our cloud.', columns: '2', cardStyle: 'outlined', sectionBackground: 'dark', anchorId: 'products', cards: [ { title: 'Virtuell Fiber\nEdge compute hardware deployed at your location. Plug in, connect to the mesh — instant private infrastructure.', linkLabel: 'Explore VF Enhet', linkUrl: '/virtuell-fiber', }, { title: 'VPS Servers\nCloud-hosted virtual servers on Layer & Mesh infrastructure. Full root access, instant provisioning, sovereign data.', linkLabel: 'Configure a VPS', linkUrl: '/vps', }, ], }, // Statistics // numberColor: 'gradient' | 'yellow' | 'mint' | 'dark' | 'white' // sectionBackground: 'white' | 'dark' | 'gray' { blockType: 'lmStatistics', heading: 'Built for Reliability', sectionBackground: 'dark', numberColor: 'gradient', stats: [ { number: '99.99%', label: 'Uptime SLA' }, { number: '<5ms', label: 'Edge Latency' }, { number: '24/7', label: 'Support' }, { number: '100%', label: 'Swedish Sovereign' }, ], }, // Swedish CTA // sectionBackground: 'dark' | 'elevated' | 'gray' { blockType: 'lmSwedishCta', headingText: 'Söker du Fiber Direkt i Sverige?', subText: 'Visit our Swedish site for local fiber and hosting services.', ctaLabel: 'fiberdirekt.se', ctaUrl: 'https://fiberdirekt.se', sectionBackground: 'dark', }, // Bottom CTA // sectionBackground: 'yellow' | 'dark' | 'gray' | 'white' // alignment: 'center' | 'left' // size: 'small' | 'medium' | 'large' { blockType: 'lmCtaBanner', heading: 'Ready to get started?', subheading: 'Configure your infrastructure or talk to our team — no commitment required.', ctaText: 'Contact Us', ctaLink: '/contact', secondaryCtaText: 'View VPS Pricing', secondaryCtaLink: '/vps', sectionBackground: 'yellow', alignment: 'center', size: 'medium', }, ], }) // ─── 3. Virtuell Fiber Page ──────────────────────────────────────────────── console.log(' → Creating Virtuell Fiber page...') await createPageIfNotExists('virtuell-fiber', { title: 'Virtuell Fiber', slug: 'virtuell-fiber', _status: 'published', publishedAt: new Date().toISOString(), layout: [ // Product Detail — eyebrow as breadcrumb, specs in richText body (right col) // sectionBackground: 'white' | 'dark' // cartProductType: 'hardware' | 'vps' | 'service' { blockType: 'lmProductDetail', eyebrow: 'Platform / Network / Virtual Fiber', productName: 'VF Standard', description: 'Virtual Fiber is not a VPN. It creates an encrypted, transparent Layer 2 network, built on top of any available internet connection, that behaves exactly like a private fiber link between your locations. The VF Standard unit ships pre-configured and ready. Connect it to your existing broadband, fiber, or 4G connection and it joins the Layer & Mesh mesh automatically. Your devices get full Layer 2 transport to every other point on the network — with VLAN, QinQ, and Full MTU support — without a single line of configuration. No fixed IP address needed. No IT team required on site. Works as a permanent connection or deployed temporarily. Contract terms start at one day.', body: richText('$90 / month\nShipping additional. No setup fee. Cancel anytime.\nPerformance\nThroughput: Up to 1,000 Mbit/s\nTransport: Encrypted Layer 2 over any internet\nRedundancy: Automatic failover via 4G or secondary ISP\nHardware\nForm factor: 1U rack-mounted\nCooling: Active (fan)\nWAN: 2 × 1GbE RJ-45\nLAN: 2 × 1GbE RJ-45\nFeatures\nPlug and play — pre-configured at dispatch\nVLAN · QinQ · Full MTU\nNo public IP address required\nWorld-class Layer 2 encryption'), ctaText: 'Order Now — $90/month', ctaLink: '/contact', secondaryCtaText: 'Download Datasheet', secondaryCtaLink: '#', enableCart: true, cartProductName: 'VF Standard', cartPrice: 90, cartProductType: 'hardware', sectionBackground: 'dark', }, // Service Cards — "What Virtual Fiber Makes Possible" // cardSurface: 'outlined-teal' | 'dark' | 'elevated' | 'teal' | 'light' | 'cyan' // sectionBackground: 'dark' | 'gray' | 'white' | 'teal' // iconSlug: 'backup' | 'colocation' | 'disaster-recovery' | 'connectivity' | 'connectivity-grid' | 'storage' | 'virtual-server' { blockType: 'lmServiceCards', heading: 'What Virtual Fiber Makes Possible', columns: '3', cardSurface: 'outlined-teal', sectionBackground: 'dark', cards: [ { eyebrow: 'Reach', title: 'Access Resources From Anywhere', description: 'Your team in New York reaches your Stockholm servers the same way they reach the printer down the hall — over a private Layer 2 network, without a VPN client or public IP.', iconSlug: 'connectivity', }, { eyebrow: 'Simplicity', title: 'No Configuration Required', description: 'Units arrive ready. Connect to any internet access — fiber, broadband, 4G — and the device joins the mesh automatically. Dual transport paths for redundancy are supported out of the box.', iconSlug: 'connectivity-grid', }, { eyebrow: 'Security', title: 'World-Class Layer 2 Encryption', description: 'All traffic is encrypted using state-of-the-art technology. Secure channels are established over public networks without any configuration — a clear replacement for traditional VPN.', iconSlug: 'storage', }, ], }, // USP Checklist — "VF Standard at a Glance" // checkColor: 'dark' | 'yellow' | 'gray' // sectionBackground: 'white' | 'gray' | 'dark' // textColor: 'dark' | 'white' { blockType: 'lmUspChecklist', heading: 'VF Standard at a Glance', sectionBackground: 'dark', textColor: 'white', checkColor: 'dark', items: [ { text: 'Micro PC portable unit, ships pre-configured, online in minutes' }, { text: 'Up to 1,000 Mbit/s Layer 2 encrypted transport, not a VPN, a dedicated private network' }, { text: '2 WAN + 2 LAN ports (RJ-45, 1GbE), or failover over 4G/5G or secondary ISP' }, { text: 'Full VLAN, QinQ, and MTU support, works with all Layer & Mesh services natively' }, { text: 'No public IP address needed, works over any available internet connection' }, { text: '$90/month subscription, no long-term commitment required, shipping additional' }, ], }, // FAQ — matching VF-specific questions from screenshot // theme: 'gray' | 'light' | 'dark' { blockType: 'lmFaq', heading: 'Virtual Fiber Questions', theme: 'dark', items: [ { question: "What's the difference between Virtual Fiber and a VPN?", answer: richText( 'Virtual Fiber creates a true Layer 2 network — your devices see each other as if they were on the same physical switch. A VPN tunnels Layer 3 traffic and requires client software, configuration, and a public IP. Virtual Fiber needs none of that.', ), }, { question: 'What internet connection does the VF Standard need?', answer: richText( 'Any internet connection works — broadband, fiber, 4G/5G, or even satellite. The VF Standard automatically establishes encrypted transport over whatever is available. For redundancy, connect a second WAN port to a backup ISP or 4G modem.', ), }, { question: 'Does it need any configuration on site?', answer: richText( 'No. Units are fully configured before dispatch. Connect the WAN port to your internet access and the LAN ports to your network — the unit joins the Layer & Mesh mesh automatically. There is nothing to configure.', ), }, { question: 'Can I connect multiple offices?', answer: richText( 'Yes. Each office gets a VF Standard unit. All units on your account see each other over a single flat Layer 2 network. Add or remove locations at any time — no reconfiguration needed.', ), }, { question: 'What are the contract terms?', answer: richText( 'Month-to-month with no long-term commitment. Contract terms start at one day. Give us 30 days notice and we will arrange return of the hardware. Shipping is additional.', ), }, ], }, // CTA { blockType: 'lmCtaBanner', heading: 'Need a custom deployment?', subheading: 'Talk to our team about multi-unit rollouts, custom configurations, or enterprise pricing.', ctaText: 'Contact Us', ctaLink: '/contact', sectionBackground: 'yellow', alignment: 'left', size: 'small', }, ], }) // ─── 4. VPS Server Page ──────────────────────────────────────────────────── console.log(' → Creating VPS page...') await createPageIfNotExists('vps', { title: 'VPS Servers', slug: 'vps', _status: 'published', publishedAt: new Date().toISOString(), layout: [ // Alternate Hero // sectionBackground: 'white' | 'dark' | 'gray' { blockType: 'lmAlternateHero', heading: 'Virtual Private Servers', description: 'Cloud-hosted servers on Layer & Mesh infrastructure. Full root access, instant provisioning, and Swedish-sovereign data. Configure your resources below — pricing updates in real-time.', primaryCtaText: 'Configure Below', primaryCtaLink: '#calculator', secondaryCtaText: 'Contact Sales', secondaryCtaLink: '/contact', sectionBackground: 'dark', }, // VPS Calculator // sectionBackground: 'dark' | 'gray' | 'teal' // currency: 'usd' | 'sek' | 'eur' { blockType: 'lmVpsCalculator', heading: 'Estimate your cost', orderCtaText: 'Add to Cart', contactCtaText: 'Questions?', contactCtaLink: '/contact', currency: 'usd', sectionBackground: 'dark', pricingCpuPerCore: 12, pricingRamPerGb: 5, pricingSsdPerGb: 0.08, pricingHddPerGb: 0.02, pricingWindowsLicense: 25, anchorId: 'calculator', }, // USP Table — description is richText // checkColor: 'dark' | 'yellow' | 'gray' // textColor: 'dark' | 'white' // sectionBackground: 'white' | 'gray' | 'dark' { blockType: 'lmUspTable', heading: "What's Included", sectionBackground: 'dark', checkColor: 'dark', textColor: 'white', rows: [ { title: 'Full Root Access', description: richText('SSH, VNC, or console — full control of your server.'), }, { title: 'Unmetered Bandwidth', description: richText('No traffic caps or overage fees.'), }, { title: 'NVMe Storage', description: richText('Enterprise-grade SSDs for maximum I/O.'), }, { title: 'Instant Provisioning', description: richText('Your server is ready in under 60 seconds.'), }, { title: '99.99% Uptime SLA', description: richText('Guaranteed availability with automatic failover.'), }, { title: 'Swedish Sovereignty', description: richText('Data stored and processed in Sweden.'), }, ], }, // CTA { blockType: 'lmCtaBanner', heading: 'Need dedicated hardware?', subheading: 'Check out the VF Enhet — edge compute deployed at your location.', ctaText: 'Learn About VF Enhet', ctaLink: '/virtuell-fiber', secondaryCtaText: 'Contact Sales', secondaryCtaLink: '/contact', sectionBackground: 'yellow', alignment: 'left', size: 'small', }, ], }) // ─── 5. Contact Page ─────────────────────────────────────────────────────── console.log(' → Creating Contact page...') await createPageIfNotExists('contact', { title: 'Contact', slug: 'contact', _status: 'published', publishedAt: new Date().toISOString(), layout: [ // Contact Form // sectionBackground: 'white' | 'gray' | 'dark' | 'navyGradient' // layout: 'standard' | 'withImage' | 'card' { blockType: 'lmContactForm', heading: 'Get in Touch', description: 'Tell us about your infrastructure needs — our team will get back to you within 24 hours.', form: contactForm.id, submitText: 'Send Message', sectionBackground: 'dark', layout: 'card', }, // FAQ { blockType: 'lmFaq', heading: 'Common Questions', theme: 'dark', items: [ { question: 'Where are you located?', answer: richText( 'Layer & Mesh operates infrastructure in Sweden with support available globally. Our US operations serve North American customers.', ), }, { question: 'How quickly can I get started?', answer: richText( 'VPS servers are provisioned instantly. VF Enhet hardware typically ships within 5–7 business days.', ), }, { question: 'Do you offer enterprise pricing?', answer: richText( 'Yes. Contact us for volume pricing, custom SLAs, and dedicated account management.', ), }, ], }, ], }) // ─── 6. Header Navigation ───────────────────────────────────────────────── // Header navItems are flat: { label, type, url } directly on each item console.log(' → Setting up header...') await apiUpdateGlobal('header', { logoLink: { type: 'custom', url: '/' }, navItems: [ { label: 'Virtuell Fiber', type: 'custom', url: '/virtuell-fiber' }, { label: 'VPS Servers', type: 'custom', url: '/vps' }, { label: 'Contact', type: 'custom', url: '/contact' }, ], }) // ─── 7. Footer ──────────────────────────────────────────────────────────── // Footer navItems use link() field: { link: { type, label, url } } console.log(' → Setting up footer...') await apiUpdateGlobal('footer', { logoLink: { type: 'custom', url: '/' }, navItems: [ { link: { type: 'custom', label: 'Virtuell Fiber', url: '/virtuell-fiber' } }, { link: { type: 'custom', label: 'VPS Servers', url: '/vps' } }, { link: { type: 'custom', label: 'Contact', url: '/contact' } }, { link: { type: 'custom', label: 'Fiber Direkt (Sweden)', url: 'https://fiberdirekt.se', newTab: true } }, ], }) console.log('') console.log('✅ Seed complete!') console.log(' Pages: /, /virtuell-fiber, /vps, /contact') } seed().catch((err) => { console.error('❌ Seed failed:', err.message || err) process.exit(1) })