Initial paw❤️print repo with SaaS architecture docs
README, multi-tenant SaaS architecture (database-per-tenant), automated onboarding flow (signup → 60 seconds → live site), and template extraction plan from AHCR codebase. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,274 @@
|
||||
# Automated Onboarding
|
||||
|
||||
## Flow
|
||||
|
||||
```
|
||||
Rescue signs up at pawprint.app
|
||||
│
|
||||
▼
|
||||
┌─────────────────────┐
|
||||
│ Signup Form │
|
||||
│ - Org name │
|
||||
│ - Admin email │
|
||||
│ - Admin name │
|
||||
│ - Subdomain pick │
|
||||
│ - Password │
|
||||
└─────────┬───────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────┐
|
||||
│ Validate │
|
||||
│ - Subdomain avail? │
|
||||
│ - Email unique? │
|
||||
│ - Spam check │
|
||||
└─────────┬───────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────┐
|
||||
│ Provision │ ← Automated script
|
||||
│ 1. Create DB │
|
||||
│ 2. Run migrations │
|
||||
│ 3. Seed admin user │
|
||||
│ 4. Generate .env │
|
||||
│ 5. Create service │
|
||||
│ 6. Add Caddy route │
|
||||
│ 7. Start app │
|
||||
└─────────┬───────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────┐
|
||||
│ Welcome Email │
|
||||
│ - Login URL │
|
||||
│ - Temp password │
|
||||
│ - Getting started │
|
||||
└─────────┬───────────┘
|
||||
│
|
||||
▼
|
||||
Rescue is live at
|
||||
{slug}.pawprint.app
|
||||
(~60 seconds total)
|
||||
```
|
||||
|
||||
## Provisioning Script
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# provision-tenant.sh <slug> <org_name> <admin_email> <admin_name>
|
||||
|
||||
set -e
|
||||
|
||||
SLUG=$1
|
||||
ORG_NAME=$2
|
||||
ADMIN_EMAIL=$3
|
||||
ADMIN_NAME=$4
|
||||
DB_NAME="pp_${SLUG}"
|
||||
PORT=$(next_available_port) # Function to find next open port
|
||||
APP_DIR="/var/www/pawprint/${SLUG}"
|
||||
SESSION_SECRET=$(openssl rand -hex 32)
|
||||
TEMP_PASSWORD=$(openssl rand -hex 4)
|
||||
|
||||
echo "=== Provisioning ${SLUG} ==="
|
||||
|
||||
# 1. Create database
|
||||
echo "Creating database ${DB_NAME}..."
|
||||
mysql -e "CREATE DATABASE IF NOT EXISTS ${DB_NAME} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
|
||||
mysql -e "CREATE USER IF NOT EXISTS 'pp_${SLUG}'@'localhost' IDENTIFIED BY '$(openssl rand -hex 16)';"
|
||||
mysql -e "GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO 'pp_${SLUG}'@'localhost';"
|
||||
mysql -e "FLUSH PRIVILEGES;"
|
||||
|
||||
# 2. Clone app (or symlink to shared build)
|
||||
echo "Setting up app directory..."
|
||||
mkdir -p ${APP_DIR}
|
||||
cp -r /var/www/pawprint/_template/* ${APP_DIR}/
|
||||
# Or: ln -s /var/www/pawprint/_shared_build/build ${APP_DIR}/build
|
||||
|
||||
# 3. Generate .env
|
||||
echo "Generating .env..."
|
||||
cat > ${APP_DIR}/.env << EOF
|
||||
DATABASE_URL=mysql://pp_${SLUG}:${DB_PASS}@localhost:3306/${DB_NAME}
|
||||
SESSION_SECRET=${SESSION_SECRET}
|
||||
UPLOAD_DIR=${APP_DIR}/uploads
|
||||
PUBLIC_SITE_URL=https://${SLUG}.pawprint.app
|
||||
ORG_NAME=${ORG_NAME}
|
||||
PORT=${PORT}
|
||||
EOF
|
||||
|
||||
# 4. Run migrations
|
||||
echo "Running migrations..."
|
||||
cd ${APP_DIR}
|
||||
npx drizzle-kit push
|
||||
|
||||
# 5. Seed admin user
|
||||
echo "Seeding admin user..."
|
||||
node scripts/seed-tenant.js \
|
||||
--email "${ADMIN_EMAIL}" \
|
||||
--name "${ADMIN_NAME}" \
|
||||
--password "${TEMP_PASSWORD}" \
|
||||
--orgName "${ORG_NAME}"
|
||||
|
||||
# 6. Create systemd service
|
||||
echo "Creating systemd service..."
|
||||
cat > /etc/systemd/system/pawprint-${SLUG}.service << EOF
|
||||
[Unit]
|
||||
Description=PawPrint - ${ORG_NAME}
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=www-data
|
||||
WorkingDirectory=${APP_DIR}
|
||||
ExecStart=/usr/bin/node build
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
Environment=NODE_ENV=production
|
||||
EnvironmentFile=${APP_DIR}/.env
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable pawprint-${SLUG}
|
||||
systemctl start pawprint-${SLUG}
|
||||
|
||||
# 7. Add Caddy route
|
||||
echo "Configuring Caddy..."
|
||||
cat >> /etc/caddy/sites/pawprint-tenants.caddy << EOF
|
||||
|
||||
${SLUG}.pawprint.app {
|
||||
reverse_proxy localhost:${PORT}
|
||||
file_server /uploads/* {
|
||||
root ${APP_DIR}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
systemctl reload caddy
|
||||
|
||||
# 8. Register in admin DB
|
||||
mysql pawprint_admin -e "INSERT INTO tenants (name, slug, subdomain, db_name, port, admin_email, admin_name, status, trial_ends_at) VALUES ('${ORG_NAME}', '${SLUG}', '${SLUG}.pawprint.app', '${DB_NAME}', ${PORT}, '${ADMIN_EMAIL}', '${ADMIN_NAME}', 'active', DATE_ADD(NOW(), INTERVAL 14 DAY));"
|
||||
|
||||
# 9. Send welcome email
|
||||
echo "Sending welcome email..."
|
||||
node scripts/send-welcome.js \
|
||||
--email "${ADMIN_EMAIL}" \
|
||||
--name "${ADMIN_NAME}" \
|
||||
--orgName "${ORG_NAME}" \
|
||||
--url "https://${SLUG}.pawprint.app" \
|
||||
--password "${TEMP_PASSWORD}"
|
||||
|
||||
echo "=== ${SLUG}.pawprint.app is LIVE ==="
|
||||
```
|
||||
|
||||
## What the Rescue Gets
|
||||
|
||||
After ~60 seconds of provisioning:
|
||||
|
||||
1. **Live site** at `{slug}.pawprint.app` with SSL
|
||||
2. **Admin login** with temp password (forced change on first login)
|
||||
3. **Welcome email** with login URL, getting started guide
|
||||
4. **Empty but functional** — ready to add pets, customize content, upload photos
|
||||
5. **14-day free trial** — full features, no credit card required
|
||||
|
||||
## Setup Wizard (First Login)
|
||||
|
||||
When the admin first logs in, they see a setup wizard instead of the dashboard:
|
||||
|
||||
### Step 1: Organization Info
|
||||
- Logo upload
|
||||
- Organization name (pre-filled from signup)
|
||||
- Tagline / mission statement
|
||||
- Location (city, state)
|
||||
- Contact email, phone
|
||||
- Social links (Facebook, Instagram)
|
||||
|
||||
### Step 2: Branding
|
||||
- Primary color (default: teal)
|
||||
- Accent color
|
||||
- Font preference (2-3 options)
|
||||
- Preview of how the site looks
|
||||
|
||||
### Step 3: Application Forms
|
||||
- Toggle which application types to enable (adoption, foster, volunteer, surrender)
|
||||
- Customize adoption fee default
|
||||
- Edit agreement items (pre-populated with sensible defaults)
|
||||
|
||||
### Step 4: Integrations (Optional)
|
||||
- Stripe keys (for accepting payments)
|
||||
- Email SMTP (or use PawPrint's shared sender)
|
||||
- Facebook page connection
|
||||
- Petfinder widget (org ID)
|
||||
|
||||
### Step 5: Import Data (Optional)
|
||||
- Upload CSV of existing pets
|
||||
- Map columns to PawPrint fields
|
||||
- Preview and confirm import
|
||||
|
||||
### Done!
|
||||
- Redirect to dashboard
|
||||
- Checklist of "next steps" (add first pet, upload logo, share your site)
|
||||
|
||||
## Deprovisioning
|
||||
|
||||
When a tenant cancels or trial expires:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# deprovision-tenant.sh <slug>
|
||||
|
||||
SLUG=$1
|
||||
|
||||
# 1. Stop service
|
||||
systemctl stop pawprint-${SLUG}
|
||||
systemctl disable pawprint-${SLUG}
|
||||
rm /etc/systemd/system/pawprint-${SLUG}.service
|
||||
|
||||
# 2. Remove Caddy route
|
||||
# (sed out the block from tenants.caddy)
|
||||
systemctl reload caddy
|
||||
|
||||
# 3. Archive data (keep for 30 days)
|
||||
mysqldump pp_${SLUG} > /backups/tenants/${SLUG}-$(date +%Y%m%d).sql
|
||||
tar -czf /backups/tenants/${SLUG}-uploads-$(date +%Y%m%d).tar.gz /var/www/pawprint/${SLUG}/uploads/
|
||||
|
||||
# 4. Mark as deleted in admin DB
|
||||
mysql pawprint_admin -e "UPDATE tenants SET status = 'deleted' WHERE slug = '${SLUG}';"
|
||||
|
||||
# 5. Schedule hard delete after 30 days
|
||||
at now + 30 days << EOF
|
||||
mysql -e "DROP DATABASE IF EXISTS pp_${SLUG};"
|
||||
mysql -e "DROP USER IF EXISTS 'pp_${SLUG}'@'localhost';"
|
||||
rm -rf /var/www/pawprint/${SLUG}
|
||||
rm /backups/tenants/${SLUG}-*.sql
|
||||
rm /backups/tenants/${SLUG}-*.tar.gz
|
||||
EOF
|
||||
|
||||
echo "=== ${SLUG} deprovisioned, data retained for 30 days ==="
|
||||
```
|
||||
|
||||
## Scaling Considerations
|
||||
|
||||
### 0-50 Tenants
|
||||
- Single VPS ($15-20/mo)
|
||||
- Everything on one box
|
||||
- Manual monitoring
|
||||
|
||||
### 50-200 Tenants
|
||||
- Separate DB server
|
||||
- Shared build (symlink, not copy)
|
||||
- Automated health checks
|
||||
- Consider container-per-tenant (Docker)
|
||||
|
||||
### 200+ Tenants
|
||||
- Multiple app servers
|
||||
- Managed database (PlanetScale, Aiven)
|
||||
- Cloudflare R2 for images
|
||||
- Kubernetes or Docker Swarm
|
||||
- Dedicated ops/monitoring (Grafana, alerts)
|
||||
|
||||
### Revenue at Scale
|
||||
| Tenants | Monthly Revenue | Server Costs | Margin |
|
||||
|---------|----------------|--------------|--------|
|
||||
| 10 | $190 | $15 | $175 |
|
||||
| 50 | $950 | $30 | $920 |
|
||||
| 100 | $1,900 | $60 | $1,840 |
|
||||
| 500 | $9,500 | $200 | $9,300 |
|
||||
+211
@@ -0,0 +1,211 @@
|
||||
# PawPrint SaaS Architecture
|
||||
|
||||
## Multi-Tenant Strategy
|
||||
|
||||
### Phase 1: Database-per-Tenant (MVP)
|
||||
|
||||
Each rescue gets their own database on shared infrastructure. The app code is identical across tenants — only the database connection and subdomain differ.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ Caddy Reverse Proxy │
|
||||
│ *.pawprint.app → route by subdomain │
|
||||
├──────────┬──────────┬──────────┬────────────┤
|
||||
│ almosthome│ happypaws│ furever │ ... │
|
||||
│ :3001 │ :3002 │ :3003 │ │
|
||||
├──────────┼──────────┼──────────┼────────────┤
|
||||
│ DB: ahcr │ DB: hpaws│ DB: furev│ │
|
||||
└──────────┴──────────┴──────────┴────────────┘
|
||||
│ MariaDB Server │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Why this approach:**
|
||||
- Zero code changes to the rescue app itself
|
||||
- Complete data isolation between tenants (no org_id bugs)
|
||||
- Each tenant can be backed up, migrated, or deleted independently
|
||||
- Can scale horizontally by adding more VPS nodes
|
||||
- Simple to reason about — each rescue is just "another install"
|
||||
|
||||
**How it works:**
|
||||
1. Tenant signs up at `pawprint.app`
|
||||
2. Provisioning script creates: database, .env file, systemd service, Caddy route
|
||||
3. App boots on a unique port, connects to its own DB
|
||||
4. Caddy routes `{slug}.pawprint.app` → `localhost:{port}`
|
||||
|
||||
### Phase 2: Shared Database (Scale)
|
||||
|
||||
When we hit 500+ tenants and per-DB overhead matters:
|
||||
- Add `orgId` column to every table
|
||||
- Single app instance, single DB
|
||||
- Row-level security via middleware
|
||||
- **Only do this when Phase 1 becomes painful**
|
||||
|
||||
## Infrastructure
|
||||
|
||||
### Single Server (0-100 tenants)
|
||||
|
||||
One beefy VPS handles everything:
|
||||
- **Hetzner CX31** ($15/mo): 4 vCPU, 8GB RAM, 80GB SSD
|
||||
- MariaDB: all tenant databases
|
||||
- Node.js: one process per tenant (low memory with Node adapter)
|
||||
- Caddy: wildcard SSL via Let's Encrypt
|
||||
- Each tenant uses ~50MB RAM idle, ~200MB under load
|
||||
|
||||
100 tenants × 50MB = 5GB RAM — fits on an 8GB server with headroom.
|
||||
|
||||
### Multi Server (100+ tenants)
|
||||
|
||||
- Separate DB server (managed MariaDB or dedicated VPS)
|
||||
- Multiple app servers behind a load balancer
|
||||
- Shared file storage (Cloudflare R2 or mounted volume)
|
||||
- Caddy on each app server or a dedicated proxy
|
||||
|
||||
### Image Storage
|
||||
|
||||
| Scale | Strategy | Cost |
|
||||
|-------|----------|------|
|
||||
| 0-50 tenants | Local disk | $0 (included in VPS) |
|
||||
| 50-200 tenants | Cloudflare R2 | ~$1-5/mo (zero egress) |
|
||||
| 200+ tenants | R2 + CDN | ~$5-20/mo |
|
||||
|
||||
Average rescue: ~300MB of images. 100 rescues = 30GB = $0.45/mo on R2.
|
||||
|
||||
## Tenant Management
|
||||
|
||||
### Admin Dashboard (`admin.pawprint.app`)
|
||||
|
||||
The PawPrint operator (you) gets a meta-dashboard:
|
||||
|
||||
- **Tenants list** — name, subdomain, plan, status, created date, last active
|
||||
- **Provisioning** — create new tenant (runs automation)
|
||||
- **Billing** — Stripe subscription status per tenant
|
||||
- **Health** — systemd service status, DB size, disk usage
|
||||
- **Maintenance** — run migrations across all tenants, restart services
|
||||
|
||||
### Database Schema (Meta)
|
||||
|
||||
The admin dashboard has its own database (`pawprint_admin`):
|
||||
|
||||
```sql
|
||||
CREATE TABLE tenants (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
name VARCHAR(255) NOT NULL, -- "Almost Home Canine Rescue"
|
||||
slug VARCHAR(100) NOT NULL UNIQUE, -- "almosthome"
|
||||
subdomain VARCHAR(100) NOT NULL UNIQUE, -- "almosthome.pawprint.app"
|
||||
custom_domain VARCHAR(255), -- "almosthomecaninerescue.com"
|
||||
db_name VARCHAR(100) NOT NULL, -- "pp_almosthome"
|
||||
port INT NOT NULL, -- 3001
|
||||
admin_email VARCHAR(255) NOT NULL,
|
||||
admin_name VARCHAR(255) NOT NULL,
|
||||
plan ENUM('free_trial', 'cloud', 'self_hosted') DEFAULT 'free_trial',
|
||||
stripe_customer_id VARCHAR(255),
|
||||
stripe_subscription_id VARCHAR(255),
|
||||
status ENUM('provisioning', 'active', 'suspended', 'deleted') DEFAULT 'provisioning',
|
||||
trial_ends_at TIMESTAMP,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE tenant_events (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
tenant_id INT NOT NULL REFERENCES tenants(id),
|
||||
event VARCHAR(50) NOT NULL, -- "provisioned", "migrated", "suspended", etc.
|
||||
details JSON,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
```
|
||||
|
||||
## Billing
|
||||
|
||||
### Stripe Integration
|
||||
|
||||
- **Product:** PawPrint Cloud ($19/mo)
|
||||
- **Trial:** 14 days free (no card required)
|
||||
- **Webhook events:**
|
||||
- `customer.subscription.created` → activate tenant
|
||||
- `customer.subscription.deleted` → suspend tenant (grace period)
|
||||
- `invoice.payment_failed` → send warning email, suspend after 3 failures
|
||||
- **Dunning:** Stripe handles retry logic
|
||||
|
||||
### Lifecycle
|
||||
|
||||
```
|
||||
Signup → Trial (14 days) → Active ($19/mo) → ...
|
||||
→ Expired → Suspended (7 days) → Deleted
|
||||
```
|
||||
|
||||
Suspended tenants:
|
||||
- App still running but shows "account suspended" page
|
||||
- Data preserved for 30 days
|
||||
- Reactivate by paying
|
||||
|
||||
## Custom Domains
|
||||
|
||||
Tenants can optionally use their own domain:
|
||||
|
||||
1. Tenant sets custom domain in their settings
|
||||
2. They add a CNAME record: `www.theirrescue.com → almosthome.pawprint.app`
|
||||
3. Caddy auto-provisions SSL via ACME
|
||||
4. We add the domain to the Caddy config
|
||||
|
||||
```
|
||||
theirrescue.com {
|
||||
reverse_proxy localhost:3001
|
||||
}
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
- Each tenant's DB user should only have access to their own database
|
||||
- .env files are per-tenant with unique SESSION_SECRET
|
||||
- Admin dashboard requires separate auth (not tenant auth)
|
||||
- Tenant data is never mixed — complete isolation
|
||||
- Backups are per-tenant (can restore one without affecting others)
|
||||
- Rate limiting is per-tenant (one rescue getting hammered doesn't affect others)
|
||||
|
||||
## Migration Path
|
||||
|
||||
### From WordPress
|
||||
1. Export pets from WP (CSV or WP REST API)
|
||||
2. Map fields to PawPrint schema
|
||||
3. Import via PawPrint's CSV import tool
|
||||
4. Download and re-upload photos
|
||||
|
||||
### From Shelterluv / RescueGroups
|
||||
1. Export data as CSV
|
||||
2. Map to PawPrint schema
|
||||
3. Import tool handles the rest
|
||||
|
||||
### From Petfinder
|
||||
1. If they have API access: use REST API to pull pets
|
||||
2. If not: manual CSV export or widget scraping
|
||||
3. Note: Petfinder killed their API in 2025, so most rescues can't use it
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### Updating the App
|
||||
|
||||
When we push updates to the PawPrint app:
|
||||
|
||||
```bash
|
||||
# On the server
|
||||
for tenant in $(cat /etc/pawprint/tenants.list); do
|
||||
cd /var/www/pawprint/$tenant
|
||||
git pull
|
||||
npm install
|
||||
npm run build
|
||||
systemctl restart pawprint-$tenant
|
||||
done
|
||||
```
|
||||
|
||||
Or better: blue-green deploys per tenant. Build once, symlink to each tenant.
|
||||
|
||||
### Running Migrations
|
||||
|
||||
```bash
|
||||
for tenant in $(cat /etc/pawprint/tenants.list); do
|
||||
DB_NAME=$(grep DATABASE_URL /var/www/pawprint/$tenant/.env | cut -d/ -f4)
|
||||
mysql $DB_NAME < migrations/0009_new_feature.sql
|
||||
done
|
||||
```
|
||||
@@ -0,0 +1,136 @@
|
||||
# Template Extraction Plan
|
||||
|
||||
## What to Pull from AHCR
|
||||
|
||||
The AHCR codebase (`almosthomecaninerescue.com-2026/ahcr`) is the source. We extract and genericize.
|
||||
|
||||
### Copy As-Is (Core Framework)
|
||||
- SvelteKit config, vite config, tsconfig
|
||||
- Drizzle ORM setup and schema (strip AHCR-specific data)
|
||||
- Auth system (sessions, Argon2, roles)
|
||||
- Rate limiting
|
||||
- Audit logging
|
||||
- File upload handler
|
||||
- Email system (Nodemailer)
|
||||
- Stripe integration
|
||||
- Bot protection (tracking, robots.txt, honeypot)
|
||||
|
||||
### Genericize (Replace AHCR-Specific)
|
||||
|
||||
| AHCR-Specific | PawPrint Generic |
|
||||
|---|---|
|
||||
| "Almost Home Canine Rescue" | `ORG_NAME` env var |
|
||||
| Sioux Falls, SD references | `ORG_LOCATION` env var |
|
||||
| SD17 Petfinder org ID | `PETFINDER_ORG_ID` env var (optional) |
|
||||
| Teal color scheme (#0d9488) | Configurable via setup wizard, stored in site_settings |
|
||||
| AHCR logo / images | Placeholder logo, upload your own |
|
||||
| 16 adoption agreement items | Configurable JSON in site_settings |
|
||||
| Foster application agreements | Configurable JSON |
|
||||
| info@almosthomecaninerescue.com | `SMTP_FROM` env var |
|
||||
| GiveButter donation link | Generic donate page with configurable URL |
|
||||
| Facebook app ID / secrets | Per-tenant config |
|
||||
| Specific intake sources | Seed with common defaults, editable in admin |
|
||||
| Content blocks (home, about, etc.) | Seed with placeholder content |
|
||||
|
||||
### Remove (AHCR Data)
|
||||
- database_dump.sql
|
||||
- decoded_lewodi.php (malware artifact)
|
||||
- CSV import data and scripts (AHCR-specific)
|
||||
- Petfinder scraper (API is dead)
|
||||
- docs/csv-import-data-issues.md
|
||||
- docs/deleted-junk-pets.md
|
||||
- All pet data, user data, etc.
|
||||
|
||||
### Add (New for Template)
|
||||
- Setup wizard (first-login onboarding)
|
||||
- `org_config` table or expand site_settings for org-level config
|
||||
- Configurable color theme (CSS custom properties)
|
||||
- Configurable application form questions
|
||||
- Seed script with placeholder data (example pets, example events)
|
||||
- Docker Compose file
|
||||
- Landing page / marketing site (separate from rescue site)
|
||||
- Health check endpoint (`/api/health`)
|
||||
- Tenant provisioning scripts
|
||||
|
||||
## File-by-File Extraction Checklist
|
||||
|
||||
### Config Files
|
||||
- [ ] package.json — update name, description, remove AHCR references
|
||||
- [ ] svelte.config.js — keep as-is
|
||||
- [ ] vite.config.ts — keep as-is
|
||||
- [ ] tsconfig.json — keep as-is
|
||||
- [ ] drizzle.config.ts — keep as-is
|
||||
- [ ] .env.example — genericize all values
|
||||
- [ ] .gitignore — keep, add uploads/
|
||||
|
||||
### Schema
|
||||
- [ ] src/lib/server/schema.ts — keep all tables, add org_config if needed
|
||||
- [ ] drizzle/ migrations — regenerate clean from schema
|
||||
|
||||
### Auth & Roles
|
||||
- [ ] src/lib/roles.ts — keep all roles
|
||||
- [ ] src/lib/server/auth.ts — keep as-is
|
||||
- [ ] src/lib/server/auth-utils.ts — keep as-is
|
||||
- [ ] src/hooks.server.ts — keep, genericize any hardcoded strings
|
||||
|
||||
### Server Utilities
|
||||
- [ ] src/lib/server/db.ts — keep as-is
|
||||
- [ ] src/lib/server/email.ts — genericize from address, org name in templates
|
||||
- [ ] src/lib/server/stripe.ts — keep as-is
|
||||
- [ ] src/lib/server/upload.ts — keep as-is
|
||||
- [ ] src/lib/server/audit.ts — keep as-is
|
||||
- [ ] src/lib/server/track.ts — keep as-is
|
||||
- [ ] src/lib/server/rate-limit.ts — keep as-is
|
||||
- [ ] src/lib/server/content.ts — keep as-is
|
||||
- [ ] src/lib/server/facebook.ts — keep as-is
|
||||
- [ ] src/lib/server/contract.ts — genericize org name in contract template
|
||||
- [ ] src/lib/server/slugify.ts — keep as-is
|
||||
|
||||
### Routes — Keep & Genericize
|
||||
- [ ] All admin routes — keep, genericize any AHCR text
|
||||
- [ ] All public routes — keep, replace hardcoded content with CMS blocks
|
||||
- [ ] Foster portal — keep as-is
|
||||
- [ ] API routes — keep as-is
|
||||
- [ ] robots.txt — keep dynamic version
|
||||
- [ ] sitemap.xml — keep, genericize
|
||||
|
||||
### Routes — Remove
|
||||
- [ ] src/lib/server/csv-import.ts — AHCR-specific
|
||||
- [ ] src/lib/server/run-import.ts — AHCR-specific
|
||||
- [ ] src/routes/admin/import/ — replace with generic CSV import
|
||||
|
||||
### Components
|
||||
- [ ] All UI components — keep as-is
|
||||
- [ ] Header/Footer — genericize org name, links
|
||||
- [ ] Replace any hardcoded "Almost Home" text
|
||||
|
||||
### Static Assets
|
||||
- [ ] Remove AHCR logos and images
|
||||
- [ ] Add placeholder logo
|
||||
- [ ] Keep favicon structure (replace with paw icon)
|
||||
|
||||
### New Files to Create
|
||||
- [ ] scripts/provision-tenant.sh
|
||||
- [ ] scripts/deprovision-tenant.sh
|
||||
- [ ] scripts/seed-tenant.js
|
||||
- [ ] scripts/send-welcome.js
|
||||
- [ ] scripts/migrate-all.sh
|
||||
- [ ] docker-compose.yml
|
||||
- [ ] Dockerfile
|
||||
- [ ] Setup wizard route (/setup)
|
||||
- [ ] Health check endpoint (/api/health)
|
||||
- [ ] Landing/marketing page (separate or at /)
|
||||
|
||||
## Estimated Effort
|
||||
|
||||
| Task | Time |
|
||||
|------|------|
|
||||
| Extract and genericize core app | 4-6 hours |
|
||||
| Setup wizard | 2-3 hours |
|
||||
| Docker Compose | 1 hour |
|
||||
| Provisioning scripts | 2-3 hours |
|
||||
| Admin meta-dashboard | 4-6 hours |
|
||||
| Landing page | 2-3 hours |
|
||||
| Stripe billing integration | 2-3 hours |
|
||||
| Testing & polish | 3-4 hours |
|
||||
| **Total** | **~20-30 hours** |
|
||||
Reference in New Issue
Block a user