Handle a data export request
Two flavors:
- Shop-wide export — the owner wants everything, ever
- Per-customer export — a specific customer wants their record
Both flows are part of the data ownership commitment.
The Settings → Data → Export UI button is planned; the underlying generators are not yet wired. For now the steps below describe the manual path Kvick performs on the shop owner's behalf.
Shop-wide export
Step 1: Confirm the request
The shop owner clicks Settings → Data → Export, OR contacts Kvick. Verify it's the owner (signed in as such, or external verification).
Step 2: Generate the bundle
# From a developer machine with wrangler authenticated
mkdir export-{slug}-{date}
cd export-{slug}-{date}
# Schema dump
wrangler d1 export helm-{slug}-db --output db.sql --remote
# Per-table CSVs
# (For each table — scripted as scripts/export-csvs.sh)
wrangler d1 execute helm-{slug}-db --remote --command "SELECT * FROM customers" --json > customers.json
# Then convert to CSV via a one-liner
# Or run scripts/export-csvs.sh
# R2 objects
wrangler r2 object list helm-{slug}-assets --output json > assets-manifest.json
# Bulk download (for small buckets)
# (Or use the planned scripts/export-r2.sh which uses S3-compatible API)
# Bible schema doc
cp ../../Bible/docs-site/docs/reference/database/* ./schema/
# (When schema reference is generated)
# Add README
cat > README.md <<EOF
Helm export bundle for {shop} on {date}
========================================
- db.sql: full SQLite schema + data dump
- *.csv / *.json: per-table exports for non-SQL tools
- assets/: R2 objects (receipts, photos, audit archives)
- schema/: markdown schema description per table
- restore.sh: example restore command
Restore into a fresh SQLite environment:
sqlite3 ./helm.sqlite < db.sql
Restore into a Helm-compatible D1:
wrangler d1 import {db} ./db.sql --remote
EOF
# Tar it up
tar -czf ../export-{slug}-{date}.tar.gz .
Step 3: Deliver
Upload to the shop's R2 bucket under exports/. Generate a signed URL with 7-day expiry. Email the owner.
Step 4: Audit
Write audit_events row: action='shop.data_export', executing staff = the owner (or Kvick if Kvick performed on their behalf), summary='Generated full data export bundle'.
Per-customer export
Step 1: Confirm the request
Customer profile → "Data export" button (when built; today, manually).
Step 2: Generate
The Worker assembles for that customer:
- Profile (JSON)
- Bikes (JSON)
- Service tickets + lines (JSON)
- Transactions (JSON)
- Messages (JSON)
- Audit log entries that reference this customer (JSON, redacted PII for non-customer rows)
Renders as:
- A single PDF report (human-readable)
- A single JSON file (machine-readable)
Both bundled and emailed (or a download link).
Step 3: Audit
audit_events row: action='customer.data_export', entity_id={customer_id}.
Frequency limits
The shop may request the full bundle as often as they want; it's free. The bundle generation is rate-limited to once per 24h to avoid accidental DoS (the bundle is large and slow).
The customer may request their per-customer bundle as often as they want; per-customer generation is fast.
What's in the bundle vs what's not
In:
- All shop-owned data (customers, transactions, tickets, inventory, etc.)
- All R2 objects (receipts, photos, archives)
- Audit history for the shop's data
- Schema documentation
Not in:
- Kvick-internal logs (those are operational, not the shop's)
- Cloudflare Workers Analytics aggregates
- The bible (it's Kvick's; not shop-specific)
- Other shops' anything