Skip to content
Open
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
35 changes: 35 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Queryx Environment Variables

## Required Configuration

# USDC receivable address on Base mainnet
# Get your address from https://app.onchainden.com/
PAYMENTS_RECEIVABLE_ADDRESS=

# x402 Payment Facilitator URL
FACILITATOR_URL=https://facilitator.daydreams.systems

# Blockchain Network
# Options: base, base-sepolia
NETWORK=base

## API Keys

# Brave Search API Key
# Get free key at: https://brave.com/search/api/
BRAVE_API_KEY=

# OpenAI API Key for GPT-4o-mini synthesis
# Get key at: https://platform.openai.com/api-keys
OPENAI_API_KEY=

## Server Configuration

# Server port (default: 3000)
PORT=3000

# Cache TTL in seconds (default: 300 = 5 minutes)
CACHE_TTL_SECONDS=300

## Optional: Custom Domain
# DOMAIN=queryx.run
50 changes: 50 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
test:
name: Test
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest

- name: Install dependencies
run: bun install --frozen-lockfile

- name: Run tests
run: bun test

- name: Type check
run: bun run tsc --noEmit

build:
name: Build Docker
runs-on: ubuntu-latest
needs: test

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build Docker image
uses: docker/build-push-action@v5
with:
context: .
push: false
tags: queryx:latest
load: true
126 changes: 126 additions & 0 deletions DEPLOY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# Queryx Deployment Guide

This guide covers deploying Queryx to Railway with a single command.

## Prerequisites

- [Railway CLI](https://docs.railway.app/getting-started) installed
- Railway account
- Docker installed locally (for testing)

## Quick Deploy

### 1. Install Railway CLI

```bash
# Install via npm
npm install -g @railway/cli

# Or via brew
brew install railway
```

### 2. Login to Railway

```bash
railway login
```

This opens your browser to authenticate.

### 3. Initialize Project

```bash
cd queryx
railway init
```

Follow the prompts:
- Project name: `queryx`
- Select "Deploy from a Dockerfile"

### 4. Configure Environment Variables

Set required environment variables via Railway dashboard or CLI:

```bash
# Via CLI
railway variables set PAYMENTS_RECEIVABLE_ADDRESS=your_address
railway variables set FACILITATOR_URL=https://facilitator.daydreams.systems
railway variables set NETWORK=base
railway variables set BRAVE_API_KEY=your_brave_key
railway variables set OPENAI_API_KEY=your_openai_key
```

Or set them in the Railway dashboard under the project Settings > Variables.

### 5. Deploy

```bash
railway up
```

Your app will build via Dockerfile and deploy to Railway.

### 6. Verify Deployment

```bash
# Check status
railway status

# View logs
railway logs

# Test health endpoint
curl https://your-project-name.up.railway.app/health
```

## Custom Domain Setup

### 1. Add Custom Domain

```bash
railway domain add queryx.run
```

### 2. Configure DNS

Add a CNAME record pointing to your Railway app URL.

### 3. SSL

Railway automatically provisions SSL via Let's Encrypt.

## Local Development

### Build Docker Image Locally

```bash
docker build -t queryx .
docker run -p 3000:3000 queryx
```

### Run Smoke Test

```bash
./scripts/smoke-test.sh
```

## Troubleshooting

### Build Fails

- Ensure all required environment variables are set
- Check Dockerfile syntax
- Run `docker build` locally to debug

### Health Check Failing

- Verify PORT is set to 3000
- Check application logs: `railway logs`
- Ensure health endpoint exists at `/health`

### Deployment Issues

- Check Railway status page: https://status.railway.app
- Review deployment logs in Railway dashboard
35 changes: 35 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
FROM oven/bun:1 AS base
WORKDIR /app

# Install dependencies only when needed
FROM base AS deps
WORKDIR /app
COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile

# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .

# Production image, copy all the files and run next
FROM base AS runner
WORKDIR /app

ENV NODE_ENV="production"

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 bun

COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json
COPY --from=builder /app/src ./src

EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s \
CMD curl -f http://localhost:3000/health || exit 1

USER bun

CMD ["bun", "run", "src/index.ts"]
14 changes: 14 additions & 0 deletions railway.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"$schema": "https://railway.app/railway.schema.json",
"build": {
"builder": "DOCKERFILE",
"dockerfilePath": "Dockerfile"
},
"deploy": {
"healthcheckPath": "/health",
"healthcheckTimeout": 10,
"restartPolicyType": "ON_FAILURE",
"restartPolicyMaxRetries": 3,
"numReplicas": 1
}
}
72 changes: 72 additions & 0 deletions scripts/smoke-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/bin/bash
# Queryx Smoke Test Script
# Tests deployment health and payment endpoints

set -e

# Configuration
BASE_URL="${1:-http://localhost:3000}"
TIMEOUT=10

echo "=========================================="
echo "Queryx Smoke Test"
echo "=========================================="
echo "Testing: $BASE_URL"
echo ""

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

# Test function
test_endpoint() {
local name="$1"
local url="$2"
local expected_status="$3"

echo -n "Testing: $name... "

status=$(curl -s -o /dev/null -w "%{http_code}" --max-time $TIMEOUT "$url" || echo "000")

if [ "$status" = "$expected_status" ]; then
echo -e "${GREEN}PASS${NC} (HTTP $status)"
return 0
else
echo -e "${RED}FAIL${NC} (Expected: $expected_status, Got: $status)"
return 1
fi
}

fail_count=0

# Test 1: Health endpoint returns 200
if ! test_endpoint "Health Check" "$BASE_URL/health" "200"; then
((fail_count++))
fi

# Test 2: Search without payment returns 402
if ! test_endpoint "Search (No Payment)" "$BASE_URL/v1/search?q=test" "402"; then
((fail_count++))
fi

# Test 3: Check for x402 payment headers
echo -n "Testing: Payment Headers... "
headers=$(curl -s -I --max-time $TIMEOUT "$BASE_URL/v1/search?q=test" || true)
if echo "$headers" | grep -qi "x-payments-required"; then
echo -e "${GREEN}PASS${NC}"
else
echo -e "${RED}FAIL${NC} (Missing x-payments-required header)"
((fail_count++))
fi

echo ""
echo "=========================================="
if [ $fail_count -eq 0 ]; then
echo -e "${GREEN}All tests passed!${NC}"
exit 0
else
echo -e "${RED}$fail_count test(s) failed${NC}"
exit 1
fi