wwwlayermeshusa/seed.mjs
2026-03-10 09:54:58 +01:00

501 lines
18 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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
// sectionBackground: 'white' | 'dark'
// cartProductType: 'hardware' | 'vps' | 'service'
{
blockType: 'lmProductDetail',
eyebrow: 'Hardware',
productName: 'VF Enhet',
description: 'Purpose-built edge compute hardware for virtuell fiber deployment. Plug the VF Enhet into your location, connect it to the Layer & Mesh network, and get instant private infrastructure — no data center required. Each unit runs a full mesh node with encrypted backhaul, local compute, and automatic failover.',
ctaText: 'Add to Cart',
ctaLink: '/contact',
secondaryCtaText: 'Contact Sales',
secondaryCtaLink: '/contact',
enableCart: true,
cartProductName: 'VF Enhet',
cartPrice: 149,
cartProductType: 'hardware',
sectionBackground: 'dark',
},
// Tech Properties — maxRows: 4!
// categoryColor: 'white' | 'dark'
// valueColor: 'yellow' | 'white' | 'dark'
// sectionBackground: 'dark' | 'white' | 'gray' | 'yellow'
{
blockType: 'lmTechProperties',
sectionBackground: 'dark',
categoryColor: 'white',
valueColor: 'yellow',
properties: [
{ category: 'Processor', value: 'ARM Cortex-A76 Quad-Core' },
{ category: 'Memory', value: '8 GB DDR4' },
{ category: 'Storage', value: '256 GB NVMe SSD' },
{ category: 'Network', value: '2.5 GbE + Wi-Fi 6' },
],
},
// Second Tech Properties block for remaining specs
{
blockType: 'lmTechProperties',
sectionBackground: 'dark',
categoryColor: 'white',
valueColor: 'white',
properties: [
{ category: 'Power', value: '12W idle / 25W peak' },
{ category: 'Dimensions', value: '145 × 100 × 40 mm' },
{ category: 'Connectivity', value: 'Encrypted mesh backhaul' },
{ category: 'Management', value: 'Remote dashboard included' },
],
},
// FAQ — answer is richText
// theme: 'gray' | 'light' | 'dark'
{
blockType: 'lmFaq',
heading: 'Frequently Asked Questions',
theme: 'dark',
items: [
{
question: 'What is the VF Enhet?',
answer: richText(
'A compact hardware node that connects your location directly to the Layer & Mesh network. It provides edge compute, encrypted connectivity, and mesh routing — all in a box smaller than a paperback book.',
),
},
{
question: 'Do I need special wiring or a server room?',
answer: richText(
'No. The VF Enhet plugs into any standard ethernet connection and power outlet. It runs silently at 12W idle — about the same as a phone charger.',
),
},
{
question: 'How is this different from a VPS?',
answer: richText(
'A VPS runs in our cloud. The VF Enhet runs at your location. Both connect to the same Layer & Mesh network, but the Enhet gives you physical edge compute with lower latency.',
),
},
{
question: 'Can I cancel?',
answer: richText(
'Yes. Month-to-month with no long-term commitment. Give us 30 days notice and we will arrange return of the hardware.',
),
},
],
},
// 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 57 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)
})