Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions .github/workflows/migrations.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: Database Migrations

on:
pull_request:
paths:
- 'backend/prisma/**'
- 'backend/migrations/**'
- '.github/workflows/migrations.yml'
push:
branches: [main, dev]
paths:
- 'backend/prisma/**'
- 'backend/migrations/**'

concurrency:
group: migrations-${{ github.ref }}
cancel-in-progress: true

jobs:
validate-migrations:
runs-on: ubuntu-latest
timeout-minutes: 10
defaults:
run:
working-directory: backend

services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: agenticpay
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U postgres -d agenticpay"
--health-interval 5s
--health-timeout 5s
--health-retries 10

env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/agenticpay

steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
cache-dependency-path: backend/package-lock.json

- name: Install dependencies
run: npm ci

- name: Generate Prisma client
run: npm run db:generate

- name: Apply migrations
run: npm run db:migrate

- name: Check for migration conflicts / schema drift
run: npx tsx migrations/runner.ts check

- name: Migration status
run: npm run db:migrate:status
60 changes: 60 additions & 0 deletions .github/workflows/sdk-docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: SDK Documentation

on:
pull_request:
paths:
- 'packages/sdk/**'
- 'packages/types/**'
- '.github/workflows/sdk-docs.yml'
push:
branches: [main, dev]
paths:
- 'packages/sdk/**'
- 'packages/types/**'

jobs:
typedoc:
runs-on: ubuntu-latest
strategy:
matrix:
package: [sdk, types]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
cache-dependency-path: packages/${{ matrix.package }}/package-lock.json

- name: Install
working-directory: packages/${{ matrix.package }}
run: npm ci 2>/dev/null || npm install

- name: Build
working-directory: packages/${{ matrix.package }}
run: npm run build

- name: Generate TypeDoc
working-directory: packages/${{ matrix.package }}
run: npm run docs

- name: Upload docs artifact
uses: actions/upload-artifact@v4
with:
name: typedoc-${{ matrix.package }}
path: packages/${{ matrix.package }}/docs/api

sdk-examples:
runs-on: ubuntu-latest
defaults:
run:
working-directory: packages/sdk
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm install
- run: npm run build
- name: Compile examples
run: npx tsc --noEmit examples/*.ts --module nodenext --moduleResolution nodenext --target ES2022
16 changes: 8 additions & 8 deletions WEBHOOK_VERIFICATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
Implement webhook signature verification for all incoming webhooks to prevent spoofed callbacks from payment providers, webhooks, and other services.

## Acceptance Criteria
- [ ] HMAC-SHA256 verification
- [ ] Per-provider secrets
- [ ] Timestamp verification (replay protection)
- [ ] Signature validation on all incoming
- [ ] Failed webhook queuing
- [ ] Manual retry
- [ ] Secret rotation
- [ ] Verification logs
- [x] HMAC-SHA256 verification
- [x] Per-provider secrets (with optional `keyId` for rotation)
- [x] Timestamp verification (replay protection) + event-id dedup
- [x] Signature validation on all incoming (`/webhooks/*`, Stripe SDK on `/webhooks/stripe`)
- [x] Failed webhook queuing
- [x] Manual retry
- [x] Secret rotation
- [x] Verification logs (structured Pino + `/api/v1/webhooks/audit`)

## Technical Scope
- Files: backend/webhooks/verification
Expand Down
2 changes: 2 additions & 0 deletions backend/.env.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
NODE_ENV=development
PORT=3001
LOG_LEVEL=info
LOG_LEVELS=webhooks:debug
CORS_ALLOWED_ORIGINS=http://localhost:3000
JOBS_ENABLED=true
QUEUE_ENABLED=true
Expand Down
1 change: 1 addition & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ node_modules/
dist/
.env
*.log
.migration-state.json
backend/benchmarks/results.json
30 changes: 30 additions & 0 deletions backend/migrations/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Database Migrations (#410)

Prisma Migrate with versioned SQL under `prisma/migrations/`.

## Commands

| Command | Description |
|---------|-------------|
| `npm run db:migrate` | Apply pending migrations (`prisma migrate deploy`) |
| `npm run db:migrate:status` | Show migration status |
| `npm run db:migrate:check` | CI — detect schema drift / conflicts |
| `npm run db:rollback` | Dev — reset DB and re-apply (destructive) |
| `npm run db:rollback:one` | Dev — run `down.sql` for latest migration |
| `npm run db:seed` | Seed development data |

## Creating migrations

```bash
npx tsx migrations/runner.ts create-migration add_feature_name
```

Review generated SQL, add optional `down.sql` for reversible rollbacks.

## Deployment

`scripts/deploy.sh` runs `db:generate` + `db:migrate` before starting the backend (unless `--skip-migrations`).

## CI

`.github/workflows/migrations.yml` applies migrations against Postgres and runs `db:migrate:check`.
60 changes: 59 additions & 1 deletion backend/migrations/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,62 @@ const commands: Record<string, () => void> = {
run('npx', ['tsx', 'migrations/seed.ts']);
console.log('[migrate] ✅ Seed complete.');
},

/** CI: fail if schema drift or pending migrations would conflict */
check() {
console.log('[migrate] Validating migration history vs schema…');
npx(['prisma', 'migrate', 'status']);
npx([
'prisma',
'migrate',
'diff',
'--from-migrations',
'prisma/migrations',
'--to-schema-datamodel',
'prisma/schema.prisma',
'--exit-code',
]);
console.log('[migrate] ✅ No migration conflicts detected.');
},

/** Dev: roll back one migration using down.sql when present */
'rollback-one'() {
if (process.env.NODE_ENV === 'production') {
console.error('rollback-one is blocked in production.');
process.exit(1);
}
const migrations = getAvailableMigrations();
const latest = migrations[migrations.length - 1];
if (!latest) {
console.error('No migrations to roll back.');
process.exit(1);
}
const downPath = join(MIGRATIONS_DIR, latest, 'down.sql');
if (!existsSync(downPath)) {
console.error(`No down.sql for ${latest}. Use rollback (reset) or add down.sql.`);
process.exit(1);
}
console.log(`[migrate] Applying down migration: ${latest}`);
const sql = readFileSync(downPath, 'utf-8');
if (!process.env.DATABASE_URL) {
console.error('DATABASE_URL required for rollback-one');
process.exit(1);
}
const result = spawnSync('npx', ['prisma', 'db', 'execute', '--stdin'], {
cwd: ROOT,
input: sql,
stdio: ['pipe', 'inherit', 'inherit'],
shell: false,
});
if (result.status !== 0) {
throw new Error('down.sql execution failed');
}
const state = readState();
state.migrations = migrations.slice(0, -1);
state.appliedAt = new Date().toISOString();
writeState(state);
console.log('[migrate] ✅ rollback-one complete.');
},
};

const command = process.argv[2];
Expand All @@ -131,7 +187,9 @@ Usage: npx tsx migrations/runner.ts <command>
Commands:
deploy Apply all pending migrations (safe for CI/CD)
status Show current migration status
rollback Roll back to previous migration (dev only)
rollback Roll back to previous migration (dev only, destructive reset)
rollback-one Apply down.sql for latest migration (dev only)
check CI validation — detect schema/migration drift
reset Reset database and re-run all migrations (dev only)
generate Regenerate Prisma client from schema
create-migration Create a new migration: create-migration <name>
Expand Down
Loading
Loading