02 — Code Updates / Migration Plan¶
Summary of changes¶
Greenfield replacement. Two new repositories (adventive-admin-api and adventive-admin-ui) ship in parallel with the existing CodeIgniter admin at ~/Repositories/BitBucket/adventive-admin. Both read/write the same backing database during the transition via Cloudflare Hyperdrive (MySQL connection pooling). Operators migrate cohort-by-cohort. The CodeIgniter admin is not modified — it is decommissioned once parity is reached and a 30-day freeze window completes.
Before beginning implementation: production secrets (application/config/adventive.php) must be rotated and moved to AWS Secrets Manager or Cloudflare secrets. This is a prerequisite, not a Phase 1 task.
Target repository layout¶
adventive-admin-api/ (TypeScript / Hono on Cloudflare Workers)
├── src/
│ ├── index.ts # Hono app entrypoint + CF Access middleware
│ ├── auth/
│ │ ├── access.ts # CF-Access-Jwt-Assertion validation
│ │ └── rbac.ts # Role derivation from Access email → super-admin/billing/read-only
│ ├── routes/
│ │ ├── customers.ts # GET/PATCH /customers, /customers/:id
│ │ ├── users.ts # GET /customers/:id/users, POST /users/:id/unlock
│ │ ├── accounts.ts # Account settings, activation, cancellation
│ │ ├── campaigns.ts # GET /campaigns, /ads (per account)
│ │ ├── invoices.ts # GET /invoices → reads Stripe
│ │ ├── billing-profiles.ts # GET/PUT /billing-profiles/:account_id
│ │ ├── managed-jobs.ts # CRUD /managed-jobs
│ │ ├── overrides.ts # CRUD /overrides (R2-backed)
│ │ ├── service-levels.ts # Account plan/tier management
│ │ ├── charts.ts # GET /charts/* (account reg, revenue, ads)
│ │ ├── tools.ts # User lookup, preview lookup, ad types
│ │ └── operators.ts # Operator RBAC management
│ ├── db/
│ │ ├── client.ts # Hyperdrive MySQL client
│ │ ├── queries/
│ │ │ ├── accounts.ts # Port of Account_model queries
│ │ │ ├── campaigns.ts # Port of Campaigns_model queries
│ │ │ ├── managed-jobs.ts # Port of ManagedService_model queries
│ │ │ └── charts.ts # Port of Dashboard_model + Chart queries
│ │ └── schema.ts # Table name constants
│ ├── stripe/
│ │ └── client.ts # Stripe SDK wrapper (invoice reads, customer reads)
│ ├── r2/
│ │ └── overrides.ts # R2 operations for override file versioning
│ └── schemas/
│ └── *.ts # Zod schemas → @hono/zod-openapi
├── wrangler.toml
├── vitest.config.ts
└── test/
├── routes/
└── auth/
adventive-admin-ui/ (React + Vite on Cloudflare Pages)
├── src/
│ ├── main.tsx
│ ├── app/
│ │ ├── router.tsx # TanStack Router config
│ │ └── query-client.ts # TanStack Query config
│ ├── features/
│ │ ├── dashboard/ # Dashboard overview → replaces Dashboard.php view
│ │ ├── customers/ # Account list, detail, create, activate, cancel
│ │ ├── billing/ # Invoice list, payment, profiles, delinquent
│ │ ├── managed-jobs/ # Managed service job CRUD
│ │ ├── campaigns/ # Campaign + ad list, ad copy
│ │ ├── overrides/ # Override file versioning
│ │ └── tools/ # User lookup, ad types, benchmarks, deploy
│ ├── api/
│ │ └── generated.ts # Types generated from admin-api OpenAPI spec
│ ├── components/
│ │ ├── ui/ # shadcn/ui primitives
│ │ └── brand/ # Adventive brand overrides (colors, typography)
│ └── lib/
│ └── access.ts # Read CF Access identity from /me endpoint
├── public/
│ └── _redirects # SPA fallback for CF Pages deep links
├── vite.config.ts
└── e2e/ # Playwright
└── flows/
├── account-management.spec.ts
└── billing.spec.ts
Before / after¶
Before¶
- Monolithic CodeIgniter 3.1 app: PHP controllers render HTML views, same process handles DB access, billing rollup, invoice rendering, file I/O, and scheduled report delivery.
- Auth: JumpCloud LDAP → Duo 2FA → CI session. Currently: LDAP only (Duo disabled).
- Secrets: hardcoded in
application/config/adventive.php. - CSRF: globally disabled.
- Deploy: Bitbucket Pipelines → AWS CLI → CodeDeploy → EC2. Shared deploy-script repo (
shell). - Observability: EC2 server logs; no structured metrics or alerts.
After¶
- Three services:
admin-apiWorker (API),admin-uiPages (presentation),invoice-rendererWorker (billing — Stripe Billing Transition scope). - Auth: Cloudflare Access at the edge (JumpCloud or Google Workspace, deferred). Admin-api validates
CF-Access-Jwt-Assertionon every request. No application-managed sessions. - Secrets: Wrangler
secret/ Cloudflare environment variables. Nothing committed to source. - CSRF: eliminated — stateless JWT architecture has no session to forge.
- Deploy: GitHub Actions →
wrangler deploy(Worker) + Pages CI/CD (SPA). Single command per environment. - Observability: Workers Analytics Engine + Logpush → existing log sink.
Migration phases¶
| Phase | Scope | Gate to next |
|---|---|---|
| 0 | Pre-work: Rotate and remove hardcoded secrets from adventive.php. Move to AWS Secrets Manager. This is a prerequisite. |
Secrets cleared from source history |
| 1 | Scaffold admin-api Worker (Hono skeleton, CF Access middleware, RBAC, stub endpoints) + admin-ui Pages (Vite + shadcn/ui + TanStack Router skeleton). Deploy to preview URLs. Cloudflare Access policy provisioned for all ~10 operators. |
Preview deploys green; all operators can authenticate |
| 2 | Implement customer + account management domain (Cohort 1 workflows 1–12): account list, detail, create, activate, cancel, user lookup, unlock, settings. RBAC enforcement. Full test coverage. | Feature parity checklist for cohort 1 complete |
| 3 | Cohort 1 migration — account-management operators move to new admin. Legacy stays live. Collect feedback, iterate UI. | Cohort retention at new admin ≥ 2 weeks with no rollback |
| 4 | Implement billing read surface (Cohort 2 workflows 13–25): invoice list/detail from Stripe, billing profiles, delinquent view, revenue history, managed services. Co-develops with Stripe Billing Transition Phase B. | Billing parity checklist complete; Stripe SoR stable |
| 5 | Cohort 2 migration — billing-ops operators move to new admin. | Cohort retention ≥ 2 weeks |
| 6 | Implement campaigns, managed service jobs, override files (Cohort 3 workflows 26–31). | Parity checklist complete |
| 7 | Cohort 3 migration — remaining operators. | All operators on new admin |
| 8 | Phase 4 deferred items: tooling (deploy, Cognito sync, benchmarks), partner report evaluation. | All workflows covered or explicitly deferred |
| 9 | Freeze legacy admin (read-only mode). Start 30-day freeze window. | 30 days clean |
| 10 | Decommission CodeIgniter admin. Archive repo. | — |
File-by-file change list¶
Legacy files are not modified — they are inventoried so the replacement is feature-complete.
| Legacy file | Lines | Replaced by | Risk | Notes |
|---|---|---|---|---|
application/controllers/Auth.php |
205 | Cloudflare Access (no code equivalent) | Low | Auth moved entirely to edge; no application auth controller |
application/controllers/Dashboard.php |
42 | admin-api/src/routes/charts.ts + UI dashboard feature |
Low | Simple aggregation |
application/controllers/Account.php |
460 | admin-api/src/routes/customers.ts, accounts.ts, users.ts |
Medium | CRUD + account-service API call replicated in Worker |
application/controllers/Billing.php |
960 | admin-api/src/routes/invoices.ts, billing-profiles.ts (Stripe reads) |
High | Invoice generation retired; payment processing via Stripe; PDF stays in invoice-renderer Worker |
application/controllers/Campaign.php |
111 | admin-api/src/routes/campaigns.ts |
Medium | Ad copy curl call → internal Worker call |
application/controllers/Report.php |
445 | Phase 4 evaluation — analytics Worker or Stripe reports | High | 22 methods; do not port to admin-api |
application/controllers/Reporting.php |
997 | Cloudflare Cron Trigger Workers (separate from admin-api) | High | 25 scheduled-delivery methods → individual Cron Triggers |
application/controllers/Tools.php |
451 | admin-api/src/routes/tools.ts (partial) |
Medium | App deployment tooling may be retired; Cognito sync stays |
application/controllers/Override.php |
167 | admin-api/src/routes/overrides.ts + R2 |
Low | S3 → R2 migration; same versioning model |
application/controllers/Chart.php |
98 | admin-api/src/routes/charts.ts |
Low | 4 JSON endpoints; straightforward port |
application/controllers/ManagedServiceJob.php |
104 | admin-api/src/routes/managed-jobs.ts |
Low | CRUD; clean port |
application/controllers/Utility.php |
58 | Cloudflare Cron Trigger Workers or retired | Low | CLI-only; resetApiRequestCounts, updateLiveCampaigns |
application/controllers/api/Campaign_Controller.php |
117 | admin-api/src/routes/campaigns.ts |
Low | DataTables pagination → standard pagination |
application/controllers/api/Managedservices_Controller.php |
99 | admin-api/src/routes/managed-jobs.ts |
Low | Same |
application/models/Account_model.php |
1,321 | admin-api/src/db/queries/accounts.ts |
High | All queries must be reproduced with full parity |
application/models/Billing_model.php |
933 | admin-api/src/stripe/client.ts + residual DB queries |
High | Billing queries largely retire; Stripe reads replace most |
application/models/Campaigns_model.php |
665 | admin-api/src/db/queries/campaigns.ts |
Medium | |
application/models/Report_model.php |
5,333 | Phase 4 — analytics Worker | Critical | Do not attempt to port inline; 24 bespoke SQL functions |
application/models/ManagedService_model.php |
505 | admin-api/src/db/queries/managed-jobs.ts |
Medium | |
application/models/Stats_model.php |
1,286 | Partially retire (billing stats → Stripe); remainder in analytics Worker | High | |
application/models/Override_model.php |
280 | admin-api/src/r2/overrides.ts |
Low | S3 ops → R2 ops |
application/models/Dashboard_model.php |
121 | admin-api/src/db/queries/charts.ts |
Low | |
application/config/adventive.php |
— | Cloudflare secrets + environment variables | Critical | Secrets must be rotated before this file is removed from history |
application/libraries/Jc_auth.php |
— | Cloudflare Access (no application code) | Low | Retired entirely |
application/libraries/Mailgun.php |
— | Retained in invoice-renderer Worker |
Low | Not part of admin-api scope |
application/libraries/AmazonS3.php |
— | Cloudflare R2 SDK in overrides Worker | Low | S3 → R2 |
application/libraries/AwsCognito.php |
— | Cognito SDK in tools route | Low | Thin wrapper retained |
application/libraries/AppDeploy.php |
— | Evaluate retirement vs. thin port | Low | CodeDeploy tooling; may be superseded by wrangler/Actions |
New files (indicative)¶
| File | Purpose |
|---|---|
admin-api/src/index.ts |
Hono app entrypoint; CF Access middleware; global error handler |
admin-api/src/auth/access.ts |
Validates CF-Access-Jwt-Assertion; extracts operator email |
admin-api/src/auth/rbac.ts |
Maps operator email → RBAC role from config; enforces per-route |
admin-api/src/db/client.ts |
Hyperdrive MySQL client factory |
admin-api/openapi.json |
Generated from @hono/zod-openapi; consumed by UI type generation |
admin-ui/src/api/generated.ts |
TypeScript types generated from OpenAPI spec |
admin-ui/public/_redirects |
CF Pages SPA fallback: /* /index.html 200 |
.github/workflows/deploy-api.yml |
wrangler deploy on merge to main |
.github/workflows/deploy-ui.yml |
Pages CI/CD on merge to main |
admin-api/wrangler.toml |
Worker config: name, routes, Hyperdrive binding, R2 binding, secrets |
Removed files (end of Phase 10)¶
- Entire
~/Repositories/BitBucket/adventive-adminrepository — archived, not deleted.
Dependencies¶
Added:
| Package | Purpose |
|---|---|
hono |
HTTP framework for Workers |
@hono/zod-openapi |
OpenAPI spec generation — single source of truth for types |
zod |
Schema validation |
stripe |
Stripe SDK (Workers-compatible) |
react, react-dom |
UI framework |
@tanstack/react-query |
Server state management |
@tanstack/react-router |
Type-safe routing |
vite |
Build tool |
tailwindcss |
Utility CSS |
shadcn/ui components + @radix-ui/* |
UI component primitives |
vitest |
Unit testing |
@playwright/test |
E2E testing |
Removed (end of Phase 10): PHP 7.3 runtime, CodeIgniter 3.1, all composer dependencies.
Breaking changes¶
- Admin URL changes during transition (operators informed by cohort).
- Operator auth moves from JumpCloud LDAP → Cloudflare Access. All ~10 operators must be provisioned in the Access policy before Cohort 1 migrates.
- API consumers (any automation scripts pointing at legacy admin URLs) must update to new contract.
billing_serviceinternal API is retired as Stripe Billing Transition completes — any external callers must be identified.
Schema-freeze policy during transition¶
While both admins share the same database, no schema changes that break backward compatibility with the CodeIgniter admin may be deployed. New columns (nullable, with defaults) are acceptable. Column drops, renames, and type changes are blocked until the CodeIgniter admin is decommissioned. This policy is in effect from Phase 1 until Phase 10.
Testing strategy¶
| Layer | Tool | Scope |
|---|---|---|
| Unit — admin-api | Vitest | Route handlers, Zod schemas, RBAC logic, DB query builders |
| Unit — admin-ui | Vitest + Testing Library | Components, hooks, form validation |
| Contract | Generated OpenAPI types | UI type generation catches API drift at build time |
| Integration | admin-api against staging DB + Stripe test mode | Full request/response parity with legacy behavior |
| E2E | Playwright against Pages preview deploy | CF Access service token for automation; golden-path flows per cohort |
| Parity | Per-cohort checklist | Dogfooded alongside legacy admin before cohort migration |
| Rollback | Any cohort can return to legacy URL | No data migration risk — shared DB |