Note: This post was researched and written by Claude (the AI assistant I’ve been building this site with). I asked it to dig through a backup of my old website, map out how everything worked, and write up a migration plan. What follows is its report.
I went through the backup of this site’s previous incarnation — a proprietary PHP “Website Builder” platform — to understand exactly what was there and figure out how to port it cleanly to Astro. What follows is a full map of the old codebase and a phased migration plan.
The Old Stack: What Was Actually Running
The old site was generated by a hosted website builder and published as a set of PHP files. Under the hood:
- Platform: Proprietary “Website Builder” (hosted, drag-and-drop)
- Entry point:
public_html/index.php→ delegates toncsitebuilder/index.php - Routing: A central dispatcher parses the URL, maps it to a hashed page ID, and
include()s the matching PHP template - Data: Page configs and content stored partly in PHP arrays inside
index.php, partly in a SQLite database (dat/project.db), and partly as pre-rendered HTML inside each page’s PHP template - Assets: 9 page-specific CSS bundles + 1 common bundle, and 9 page-specific JS bundles + 1 common bundle — all pre-compiled, not intended to be edited by hand
The template files have hash-based filenames (e.g., a18a5e5577780091a934e00d6ba0010d.php), which makes them completely opaque at a glance. Here’s the full map.
File Map: What Every PHP File Does
Page Templates (hash-named .php files)
| File (short hash) | URL alias | Page |
|---|---|---|
...0091...0d.php | / | Home — “Thinker, Tinkerer, Artist” intro with three content sections |
...01d2...05.php | /Contacts/ | Contacts — Contact form, 13 fields, emails to [email protected] |
...02b5...81.php | /Tech/ | Tech Projects — Portfolio cards: Trading, Software, Photography, Video, Hardware, AI |
...0300...46.php | /AI-Adventures/ | AI Adventures (Blog) — Blog listing page, 6 posts per page, links to individual posts |
...047a...e7.php | /Art/ | Art Projects — Portfolio cards: Rock/Metal, Design, Photography, Video, Admin, Company |
...0553...fd.php | /Gift-ideas/ | Gift Ideas (Store) — E-commerce listing, 11 items, PayPal checkout, marked noindex |
...0016...4e.php | /Projects/ | Projects — Slideshow gallery with geo-tagged images, powered by PhotoSwipe |
...6700...6e.php | (internal) | Custom 404 page — bare header/footer, no content |
...7102...cb.php | (internal) | Maintenance page — “This page is currently down for maintenance” |
Core Engine Files
| File | Purpose |
|---|---|
index.php | Central router + dispatcher. Parses URL, loads page template, handles form submissions, applies SEO meta, manages sessions |
functions.inc.php | ~50KB of utility functions: URL parsing, form handling, SEO/canonical URL generation, comment rendering, session helpers, localization |
pd.json | Plugin feature-flags config — which modules are enabled (Blog, Store, Forms, Gallery, PayPal, etc.) |
sitemap.txt | XML sitemap with 20 URLs: 7 pages + 2 blog posts + 11 store items |
class.json.php | JSON encoding utility for older PHP versions |
ga.php | Google Analytics injection helper |
GatewayPaypal.php | PayPal v2 payment gateway handler |
polyfill.php | HTML5 compatibility polyfills |
main_BuyNow.php | ”Buy Now” button handler for store checkout |
Data & Database
| File | Purpose |
|---|---|
dat/project.db | SQLite 3 database — full site project data, page layouts, plugin configs |
dat/backend_tasks.json | Build task log — records when pages were last published and which plugins were active |
Asset Directories
| Directory | Contents |
|---|---|
css/ | common-bundle.css + 9 page-specific CSS bundles |
css/font-awesome/ | Font Awesome icon assets |
css/flag-icon-css/ | Country flag icons (used in the contact form’s country dropdown) |
css/fonts/ | Bootstrap Glyphicons (eot, svg, ttf, woff, woff2) |
js/ | common-bundle.js + 9 page-specific JS bundles |
js/photoswipe/ | PhotoSwipe — the open-source JS gallery library powering the Projects slideshow |
gallery/ | Original uploaded images |
gallery_gen/ | Auto-generated thumbnails and WebP-optimized versions |
gateways/ | Full PayPal PHP SDK |
phpmailer/ | PHPMailer library for form email delivery |
phpseclib/ | PHP security library |
tcpdf/ | PDF generation library |
src/ | Core PHP modules: BlogModule, StoreModule, FormModule, BlogElement, StoreElement |
Note: Google Fonts (Roboto, Raleway, Ubuntu, Josefin Sans) were loaded from Google’s CDN at runtime — they are not self-hosted in this backup.
What the Old Site Actually Had (Content Inventory)
Pages in the main nav:
- Home — tagline, three-section intro
- Projects — slideshow gallery portfolio
- AI Adventures — blog (2 posts at time of backup)
Additional pages (accessible by URL, not in the main nav):
- Contacts — contact form
- Tech Projects — portfolio cards
- Art Projects — portfolio cards
- Gift Ideas — store with 11 gift items (PayPal-enabled, but
noindex’d)
Blog posts:
- “Punk Jazz, by ChatGPT” (2023-02-16)
- “How ChatGPT Rewards Lies” (2023-02-15)
Store items (Gift Ideas page), by actual title:
- A Copy of Your Favorite Record
- A Modern Swiss Army Knife
- Ashley’s Book of Knots
- Tiny Plastic Robots
- Powerful T-shirts
- Guitar Strings for my Ukulele
- Donate to Guitar Repairs
- Donate to my 4Runner Project
- ProCo Rat Distortion Pedal
- Tetsuo Sakurai Gentle Hearts Tour 2004
- EHX Op-Amp Big Muff Fuzz Pedal
Forms:
- Contact form (13 fields)
- Store inquiry form (13 fields)
- Billing form (13 fields, for PayPal checkout)
The Migration Plan: PHP → Astro
The goal isn’t just to replicate the old site — it’s to own it. That means every page is a file you can open and read, every post is plain Markdown, and there’s no build platform that obfuscates everything behind hash-named files.
Guiding Principles
- Content lives in Markdown. Blog posts, project descriptions, and store items should all be plain
.mdfiles, not locked in a database. - One component per concern. Header, Footer, Nav, and layout sections should each be their own
.astrocomponent — easy to find, easy to edit. - Static output first. No PHP, no server-side rendering needed for a portfolio site. Astro’s static build is faster and simpler to host.
- Pages mirror the old URL structure. Existing URLs like
/Projects/and/AI-Adventures/should resolve to the same content (or redirect properly).
Phase 1 — Pages (already partially done)
Map each old PHP page to an Astro page file:
| Old URL | Old PHP file | Astro target |
|---|---|---|
/ | ...0091...0d.php | src/pages/index.astro |
/Projects/ | ...0016...4e.php | src/pages/projects.astro |
/AI-Adventures/ or /blog/ | ...0300...46.php | src/pages/blog/index.astro |
/Contacts/ | ...01d2...05.php | src/pages/contact.astro (new) |
/Tech/ | ...02b5...81.php | Merge into projects.astro or add src/pages/tech.astro |
/Art/ | ...047a...e7.php | Merge into projects.astro or add src/pages/art.astro |
/Gift-ideas/ | ...0553...fd.php | src/pages/gifts.astro (optional, later) |
Action items:
- Rebuild the Home page content (three-section intro: Thinker, Tinkerer, Artist)
- Create a Contact page with a static form (use Netlify Forms, Formspree, or similar — no PHP needed)
- Decide whether Tech and Art pages get merged into a unified Projects page or stay separate
Phase 2 — Projects Content Collection
The old Projects/Tech/Art pages were just hardcoded HTML cards. The better approach in Astro is a content collection:
src/content/projects/
automated-trading.md
software-engineering.md
hardware-development.md
ai-research.md
music.md
photography.md
video.md
Each file gets frontmatter like:
---
title: "Automated Trading"
category: "tech" # or "art"
description: "..."
image: "/images/projects/trading.jpg"
featured: true
---
Then src/pages/projects.astro queries the collection and renders the cards dynamically — add or remove a project by editing a Markdown file.
Phase 3 — Blog Migration
Both old blog posts already exist as .md files in this repo. Going forward:
- All posts live in
src/content/blog/ - Frontmatter schema is validated via Zod (already set up in
src/content/config.ts) - The URL structure is
/blog/<slug>/rather than/AI-Adventures/<slug>/— add a redirect if needed
To preserve old URLs, add Astro redirects in astro.config.mjs:
redirects: {
'/AI-Adventures/[...slug]': '/blog/[...slug]',
}
Phase 4 — Contact Form (No PHP)
The old contact form relied on PHP + PHPMailer. Replacements for a static site:
| Option | Pros | Cons |
|---|---|---|
| Formspree | Easy setup, free tier, spam filtering | Monthly submission limits |
| Netlify Forms | Zero config if hosted on Netlify | Netlify-only |
| EmailJS | Works from any host, free tier | Client-side (email visible in source) |
| Web3Forms | Free, no account needed for setup | Newer, smaller community |
Recommended: Formspree or Netlify Forms — drop in the action attribute, done.
Fields to replicate from the old form: Name, Email, “How did you find me?”, “Your Question?”, Message. The rest (Country, City, Address, VAT number) can be skipped unless actually needed.
Phase 5 — Gallery / Images
The old Projects page used PhotoSwipe for its slideshow gallery, with geo-tagged images. In Astro:
- Move images to
public/images/projects/ - PhotoSwipe can be used again as a lightweight client-side component (it’s framework-agnostic), or swap for a simpler pure-CSS approach
- Drop the geo-tagging unless you want to add a map embed via Google Maps or Leaflet later
Phase 6 — Store / Gift Ideas (Optional, Later)
The Gift Ideas page had PayPal integration. Options for later:
| Option | When to use |
|---|---|
| Snipcart | Easiest drop-in, works with static sites, small monthly fee |
| Stripe Payment Links | No-code Stripe checkout, no server needed |
| Headless Shopify + Astro SSR | Full e-commerce control, requires switching to server output |
For now, the Gift Ideas page can be a placeholder or skipped entirely.
Phase 7 — Fonts & Styles
The old site loaded Google Fonts (Roboto, Raleway, Ubuntu, Josefin Sans) from Google’s CDN at runtime — they were not self-hosted. In Astro, the options are:
- Self-host fonts using
@fontsourcepackages (install via npm, no external requests, better privacy) - Or keep Google Fonts
<link>tags inBaseLayout.astroif external requests are acceptable
The current site uses Tailwind CSS — keep that. The brand color #ff5d00 is already configured as brand in tailwind.config.mjs.
File Structure Target
Here’s what the fully migrated Astro site should look like:
src/
components/
Header.astro
Footer.astro
Nav.astro
ProjectCard.astro ← new
GallerySlideshow.astro ← new
content/
config.ts ← add 'projects' collection here
blog/
punk-jazz-by-chatgpt.md
how-chatgpt-rewards-lies.md
...
projects/
automated-trading.md
software-engineering.md
...
layouts/
BaseLayout.astro
pages/
index.astro ← Home
projects.astro ← Projects gallery + content collection
contact.astro ← Contact form (static)
blog/
index.astro ← Blog listing
[...slug].astro ← Individual posts
styles/
global.css
public/
images/
projects/ ← Migrated gallery images
favicon.svg
Quick-Start Checklist
Here’s the recommended order of operations:
- Rebuild Home page content (Thinker / Tinkerer / Artist sections)
- Create
src/pages/contact.astrowith a static form (Formspree or Netlify Forms) - Create
src/content/projects/collection with frontmatter schema inconfig.ts - Add project
.mdfiles for each old Tech/Art card - Update
src/pages/projects.astroto query the collection and render cards - Move gallery images to
public/images/projects/ - Add a gallery/slideshow component (PhotoSwipe or similar)
- Add URL redirects for old
/AI-Adventures/paths - Decide on fonts: self-host via
@fontsourceor keep Google Fonts - Revisit Gift Ideas / store page last (optional)
The old site did a lot of heavy lifting invisibly — routing, form handling, gallery generation, store checkout — all buried in obfuscated, hash-named PHP files. The Astro version trades that magic for clarity: every page is a file you can open and read, every post is plain Markdown, and there’s no build platform you don’t control. That’s the goal.