Content & editorial
Content Migration Tools
Indicative price, not an offer: $50–150/mo for self-serve tools; or $1,000–3,000 one-time professional service
Generated from docs/plan/paid/content-migration-tools/PLAN.md in the AstroBaaS repository. Nothing described below is implemented — it is the written plan for it.
Content Migration Tools enable merchants to bulk-import content from WordPress, Medium, Ghost, Substack, or other platforms via OAuth-authenticated API connectors. A merchant connects their source platform, maps fields (title, body, author, publish date, tags), previews the import, and runs the migration in a background job. This is a paid plugin requiring external API credentials, per-platform support commitment, and data-integrity expertise.
The problem
Merchants moving from WordPress or Medium have 500+ posts that would take weeks to manually re-enter. Without migration tooling, they are forced to hire expensive agencies ($1,000–5,000+) or they abandon their archive entirely and start fresh.
What it does
- OAuth2 connector for WordPress, Medium, Ghost, Substack, and CSV upload fallback
- Fetch posts/articles from source platform, paginated to avoid memory issues
- Interactive field mapper: source (WordPress title) ↔ target (AstroBaaS collection.title field)
- Preview import results: show 5-10 sampled records with mapped fields before commit
- Run migration as background job, reporting progress (50 of 500 posts imported)
- Detect and handle duplicates: skip by source URL or title hash, or merge with existing
- Handle media: download embedded images to asset store, rewrite URLs in content body
- Preserve metadata: author name, publish date, tags, canonical URL (if applicable)
- Rollback incomplete migrations: mark imported records with migration_id, deletable in batch
- Migration audit log: source count, target count, skipped, errors, final status
What it deliberately does NOT do
Each boundary carries its reason. A boundary without a reason gets crossed by the next person who reads this.
- Automated author mapping (source author → AstroBaaS staff user) — requires manual staff ownership assignment; merchant must review and approve
- Smart content rewrite (fixing broken image links, relative URLs) — out of scope; merchant is responsible for post-import QA
- Continuous sync (live sync after initial migration) — scope is one-time import; continuous sync is a separate feature requiring webhooks
Data model
New plugin-owned collection: Migration (id, source_platform, source_url, status, importedAt, recordsTotal, recordsImported, recordsSkipped, errors). Content records tagged with migration_id for batch rollback. No core schema migration.
API
- POST /api/migrations/oauth/authorize/:platform — start OAuth flow for source platform
- POST /api/migrations/oauth/callback — OAuth redirect endpoint
- GET /api/migrations/:id/preview — fetch sample records with field mapping applied
- POST /api/migrations/:id/run — start background migration job
- GET /api/migrations/:id/status — poll progress (imported, skipped, errored counts)
- DELETE /api/migrations/:id/rollback — delete all records where migration_id matches
Every route added here must also appear in src/pages/openapi.json.ts — a test
fails the build if it does not.
Admin
Migrations page lists recent imports (source, status, date, record count). New migration form prompts for source platform, shows OAuth login flow, displays field mapper interface (drag-to-map or dropdown selector per field), preview pane, and Run button. Progress view shows import count, ETA, error log. Rollback button on completed migration.
The seam — why this is paid
Core owns: collection storage, asset store, audit log. Paid plugin owns: OAuth connectors, field mapping UI, media download and URL rewriting, duplicate detection, background job orchestration.
Paid owns platform integrations (WordPress, Medium, Ghost, Substack APIs) and data-mapping logic. Migration is not core CMS functionality; it is integration work requiring external credentials and per-platform support.
Dependencies
- core-asset-store (image download and storage)
- core-audit-log (migration tracking)
- core-webhook-system (optional: sync source updates post-migration)
Acceptance checks
Each of these must be able to fail. Before claiming this is done, break the code deliberately and watch each one go red.
- OAuth login for WordPress succeeds and fetches first 10 posts from source blog
- Field mapper can map source.title to target collection.name field
- Preview shows 5 sampled records with mapped values before import runs
- Background job imports 500 posts without blocking admin UI
- Embedded image in source post is downloaded, stored in asset store, and URL rewritten in content body
- Duplicate detection skips posts where source_url already exists in target collection
- Migration rollback deletes all 500 imported records when migration_id matches
- Error log records source posts that failed (e.g., 403 permission denied) without halting migration
Risks
Unhandled OAuth errors (revoked token, rate limit) leaves migration in limbo. Memory exhaustion if fetching 10,000 posts without pagination. Duplicate detection by URL fails if source platform rewrites URLs post-import. Image URL rewriting corrupts body text if regex too greedy. Rollback deletes unrelated records if migration_id not scoped correctly (use UUID, not sequential ID).
Commercial context
| Suggested price | $50–150/mo for self-serve tools; or $1,000–3,000 one-time professional service |
| Rival anchor | WordPress migration services ($1,000–5,000+ agency); Shopify migration ($500–2,000 service); no standard SaaS product |
The anchor is what the nearest equivalent charges on Shopify or Magento today. It is context for a pricing decision, not the decision.
Generated from the commerce plan. See docs/COMMERCE-PLAN.md for the full
catalogue and ../../AI-GUIDE.md for how to work on this repository.