Quick start
Run the app, paste a website URL, and let the server build a reusable customer workspace. The first crawl creates the database rows, corporate identity context, campaigns, and live previews.
- Install dependencies with npm install, then start the Express server with npm start.
- Open http://localhost:3000 or the configured reverse-proxy host.
- Paste a domain. press2pixel resolves redirects, extracts brand signals, crawls content, groups campaigns, and stores the result.
- Refreshes keep the same customer, selected templates, captions, and design settings through SQLite and localStorage.
npm install
npm start
# open http://localhost:3000
What the app builds
The product is a bulk social media asset generator. It turns posts, pages, products, events, listings, and other structured content into branded image sets and matching captions.
- Campaign-ready PNG graphics across practical social ratios.
- Lazy AI captions for Twitter/X, Instagram, and LinkedIn, stored per content row.
- ZIP exports and fullscreen signage playlists.
- Universal templates plus customer-locked signature templates that only appear for matching hosts.
Website ingestion pipeline
Crawling happens on the server so the browser stays presentational and avoids CORS problems. The crawler tries structured sources first, then supplements them with sitemap-driven page extraction.
- resolveCanonical follows http/https redirects and normalizes the customer host.
- extractCI collects logo, brand color, fonts, language, hero image, social handles, and contact signals.
- WordPress REST and Shopify JSON are read when available.
- sitemap.xml, robots Sitemap entries, llms.txt, JSON-LD, Open Graph, and rendered SPA HTML fill the gaps.
- Media candidates are scored so hero, cover, and page-matching images beat icons, thumbnails, and generic logos.
POST /api/crawl/:customerUrl
-> resolveCanonical()
-> extractCI()
-> wpScrape() / shopifyScrape()
-> discoverUrls()
-> extractFromPage()
-> groupItems()
-> persist
API surface
The frontend talks to a compact Express API. Customer, content, campaign, sync, auth, captions, provider status, and config routes are all served from the Node app.
- POST /api/crawl/:customerUrl runs the full sync pipeline.
- GET /api/customers, /api/content/:customerUrl, and /api/campaigns/:customerUrl hydrate the workspace.
- POST /api/captions/generate writes generated caption JSON back to content.captions.
- GET /api/events streams sync updates over Server-Sent Events.
- Auth routes are available when AUTH_ENABLED is true.
GET /api/customers
POST /api/crawl/:customerUrl
GET /api/content/:customerUrl
GET /api/campaigns/:customerUrl
POST /api/captions/generate
GET /api/events
Content model and storage
Every discovered item is normalized before it reaches the UI. A stable id_unique key deduplicates content across syncs, while raw_json keeps enough source detail for media repair and diagnostics.
- customers store url, display name, corporate identity JSON, preset JSON, owner, and last sync time.
- content stores title, excerpt, imageUrl, date, type, source_url, raw_json, campaign_id, and captions.
- campaigns store AI or heuristic groups per customer.
- crawl_cache keeps fetched pages for a six-hour TTL, reducing repeated network work.
- sync_log records started/completed times, status, item counts, and errors.
content {
id_unique,
customer_url,
type,
title,
excerpt,
imageUrl,
source_url,
campaign_id,
captions
}
AI routing, grouping, and captions
AI runs through local Ollama or command-line providers rather than direct browser calls. Each task has its own provider chain, model settings, fallback behavior, and provenance.
- Campaign grouping is local-first through Ollama, with a deterministic taxonomy fallback when the model is unavailable.
- Caption generation uses a server route, saves provider/model metadata, and reuses stored captions on export.
- Template and vision generation can use Gemini, Claude, Codex, MLX, or Ollama depending on the configured task chain.
- Rate-limit state and token usage are tracked so the app can skip temporarily unavailable providers.
CAPTION_PROVIDERS=claude,gemini
GROUPING_PROVIDERS=ollama,gemini,claude
OLLAMA_URL=http://localhost:11434
OLLAMA_MODEL=gemma4:26b
Templates and design controls
Templates are JSON files rendered by the frontend. The app keeps template markup separate from app logic and uses a strict root token contract so every design responds to the same controls.
- New templates live under assets/json/templates and are activated through index.json.
- Template variables cover title, excerpt, image, brand colors, typography, spacing, shadows, surfaces, and export ratio behavior.
- Font sizes use em units so separate headline and body sliders can scale typography reliably.
- Image fit and position are bound through object-fit and object-position controls.
- Brand templates use customerHosts to appear only for the matching customer domain.
font-size:100%;--p2p-headline-mul:{{headlineScale}};--p2p-text-mul:{{textScale}}
Database, backups, and sync
The app uses a small database facade. SQLite is the default, while MariaDB/MySQL can be selected through DB_DRIVER without changing the rest of the codebase.
- scripts/db/index.js exposes run, all, get, and migrate for every module.
- Migrations are idempotent and create the active driver schema on boot.
- npm run db:backup exports JSONL table snapshots and can import them later.
- npm run db:sync copies data between SQLite and MariaDB/MySQL.
- node-cron runs background sync hourly and skips recently synced customers.
DB_DRIVER=sqlite
# or
DB_DRIVER=mariadb
DB_HOST=127.0.0.1
DB_NAME=press2pixel
Deploy with Docker or reverse proxy
The app can run directly with Node or through Docker Compose. A production proxy should send every app request to Node because the server owns API routes and view composition.
- docker compose up -d starts the app plus an Ollama sidecar.
- BYO Ollama deployments can point OLLAMA_URL at the host or another container.
- Nginx and Apache examples proxy all app routes to http://127.0.0.1:3000.
- The production image intentionally excludes Playwright/Chromium and cloud CLIs unless a custom image adds them.
- The public landing page is a static build and does not depend on the app server.
docker compose up -d
docker compose exec ollama ollama pull gemma4:26b
Auth, privacy, and safety notes
Authentication is available but opt-in in the current app docs. Once the application is exposed publicly, auth, HTTPS, rate limiting, and careful crawl limits should be enabled together.
- AUTH_ENABLED=true activates the login gate and session-protected API routes.
- Passwords use scrypt and sessions are stored as opaque HttpOnly-cookie tokens.
- OAuth buttons appear only when provider client IDs and secrets are configured.
- Open registration should be replaced with approval or invite codes before broad public use.
- Public deploy should enforce HTTPS and rate-limit crawl, AI, login, and registration routes.
AUTH_ENABLED=true
ADMIN_EMAIL=info@example.com
ADMIN_PASSWORD=change-me
Quality gates and roadmap
The app already has a strict template audit and a practical open-source backlog. The next quality work is mostly about public-deploy hardening, tests, modularity, and higher-volume brand polish.
- npm test runs the template audit and validates active template JSON, renderer tokens, image controls, conditionals, and customer host coverage.
- High-priority deploy work includes auth defaults, rate limiting, HTTPS enforcement, secure headers, and cleanup of legacy scripts.
- Product roadmap items include desktop packaging, template editing, per-item image picking, stock photo integration, and stronger visual audits across more ratios.
- UI roadmap items include richer empty states, toasts, keyboard shortcuts, bulk customer workflows, and mobile verification.
npm run test:unit
npm run test:audit
npm test