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
87 changes: 87 additions & 0 deletions .github/workflows/api-contract.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
name: API contract

on:
pull_request:
paths:
- 'docs/openapi.yaml'
- 'src/routes/**'
- '.github/workflows/api-contract.yml'
push:
branches: [main]
paths:
- 'docs/openapi.yaml'

jobs:
validate-spec:
name: Validate OpenAPI spec
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Install Redocly CLI
run: npm install -g @redocly/cli@latest

- name: Lint OpenAPI spec
run: redocly lint docs/openapi.yaml --format=stylish

- name: Bundle spec (verify all $refs resolve)
run: redocly bundle docs/openapi.yaml --output /tmp/openapi-bundled.yaml

contract-tests:
name: Contract smoke tests
runs-on: ubuntu-latest
needs: validate-spec
steps:
- uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Start server
run: |
cp .env.test .env
npm run build
node dist/index.js &
# Wait up to 15 seconds for the server to accept connections
for i in $(seq 1 15); do
curl -sf http://localhost:3000/health && break
sleep 1
done
env:
NODE_ENV: test

- name: Health liveness check
run: |
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" http://localhost:3000/health)
[ "$STATUS" = "200" ] || (echo "Health check returned $STATUS" && exit 1)

- name: Validate health response shape
run: |
BODY=$(curl -sf http://localhost:3000/health)
echo "$BODY" | node -e "
const body = JSON.parse(require('fs').readFileSync('/dev/stdin','utf8'));
const required = ['status','timestamp','version','environment'];
for (const k of required) {
if (!(k in body)) { console.error('Missing field: ' + k); process.exit(1); }
}
console.log('Health response shape OK:', JSON.stringify(body));
"

- name: 401 on protected routes without token
run: |
for ROUTE in /api/portfolio /api/transactions /api/vault; do
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" http://localhost:3000$ROUTE || true)
[ "$STATUS" = "401" ] || [ "$STATUS" = "404" ] || (echo "$ROUTE returned $STATUS, expected 401" && exit 1)
echo "$ROUTE -> $STATUS OK"
done
48 changes: 48 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# NeuroWealth Backend

Express + TypeScript REST API for the NeuroWealth platform — AI-assisted portfolio management backed by Stellar smart contracts.

## API Documentation

The full OpenAPI 3.1 specification lives at [`docs/openapi.yaml`](docs/openapi.yaml).

It covers:

| Tag | Base path | Auth |
|---|---|---|
| health | `/health` | None |
| auth | `/api/auth` | None (issues JWT) |
| portfolio | `/api/portfolio` | Bearer JWT |
| transactions | `/api/transactions` | Bearer JWT |
| deposit | `/api/deposit` | Bearer JWT |
| withdraw | `/api/withdraw` | Bearer JWT |
| vault | `/api/vault` | Bearer JWT |
| admin | `/api/admin` | `X-Admin-Token` header |

### Viewing the docs locally

```bash
npx @redocly/cli preview-docs docs/openapi.yaml
```

### Updating the spec

When you add or change a route, update `docs/openapi.yaml` in the same PR. The `api-contract` CI job will lint the spec and run smoke tests automatically.

### Breaking-change policy

This API follows semantic versioning. Breaking changes (removed fields, changed response shapes, new required parameters) increment the major version and are announced at least two weeks before release.

## Development

```bash
cp .env.example .env
npm install
npm run dev
```

## Running tests

```bash
npm test
```
Loading
Loading