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
11 changes: 11 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.git
.github
**/.next
**/node_modules
infra
docs
.env
.env*.local
docker-compose.yml
Dockerfile
README.md
46 changes: 46 additions & 0 deletions .env-example
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# When adding additional env variables, the schema in
# "apps/web/src/env.ts" should be updated accordingly.

# Drizzle / PostgreSQL
# Matches the local database started by `docker compose up -d db`
DATABASE_URL="postgresql://postgres:password@localhost:5432/app"

# Clerk user management (https://dashboard.clerk.com -> API keys)
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=""
CLERK_SECRET_KEY=""

# Optional: server-side credential vault (1Password Connect).
# When unset, balance syncs rely on transient credentials supplied by the
# calling surface. See docs/integrations.md.
# OP_CONNECT_HOST="https://op-connect.internal:8080"
# OP_CONNECT_TOKEN=""

# PointUp Assistant provider. Selection order in the composition root:
# 1. LLM_PROVIDER=bedrock + BEDROCK_MODEL_ID -> Claude on AWS Bedrock
# 2. LLM_API_KEY -> OpenAI-compatible endpoint
# 3. (neither) -> deterministic heuristic
# LLM_PROVIDER="" # "bedrock" | "openai" (default: unset)

# Optional: OpenAI-compatible LLM for PointUp Assistant (falls back to heuristic).
# LLM_API_KEY=""
# LLM_MODEL="gpt-4o-mini"
# LLM_BASE_URL="https://api.openai.com/v1"

# Optional: Claude on AWS Bedrock via the Converse API (production assistant).
# In AWS the ECS task role supplies credentials + bedrock:InvokeModel; locally
# the standard AWS credential chain applies. BEDROCK_MODEL_ID is region-/profile-
# prefixed and MUST be verified in your account (the latest Sonnet id varies).
# LLM_PROVIDER="bedrock"
# BEDROCK_MODEL_ID="anthropic.claude-sonnet-5"
# AWS_REGION="us-east-1"

# Optional: Firecrawl for deal / award-chart scraping (falls back to stub).
# FIRECRAWL_API_KEY=""
# FIRECRAWL_BASE_URL="https://api.firecrawl.dev"

# Background worker (apps/worker) - scheduled syncs and email digests.
# MAILER: ses | smtp | console (default console = log instead of sending)
# MAILER="smtp"
# SMTP_URL="smtp://localhost:1025" # Mailpit: docker compose --profile tools up -d
# DIGEST_FROM_EMAIL="digest@pointup.local"
# DIGEST_RECIPIENT_OVERRIDE="you@example.com" # dev fallback when Clerk is not configured
66 changes: 66 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: CI

# Pull-request checks. Pushes to master run the same verification inside the
# Deploy workflow (.github/workflows/deploy.yml) before shipping to AWS.
on:
pull_request:
workflow_dispatch:

jobs:
app:
name: Lint, typecheck, test & build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm

- name: Install dependencies
run: npm ci

- name: Lint
run: npm run lint

- name: Typecheck
run: npm run typecheck

- name: Unit tests
run: npm run test

- name: Build web app
run: npm run build
env:
SKIP_ENV_VALIDATION: "1"
# Placeholder key with a valid format; the real key is provided at
# deploy time (see infra/lib/app-stack.ts).
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: pk_test_ZXhhbXBsZS5jbGVyay5hY2NvdW50cy5kZXYk

- name: Build worker bundle
run: npm run build --workspace @pointup/worker

infra:
name: Typecheck & synth infrastructure
runs-on: ubuntu-latest
defaults:
run:
working-directory: infra
steps:
- uses: actions/checkout@v4

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

- name: Install dependencies
run: npm ci

- name: Typecheck
run: npm run typecheck

- name: Synth
run: npx cdk synth --quiet
134 changes: 134 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
name: Deploy

# Continuous deployment to AWS on every push to master:
# verify -> cdk deploy (web + worker images, full stack) -> run DB migrations
#
# One-time setup (see README "Continuous deployment"):
# 1. npx cdk deploy GithubOidc -c githubRepo=<owner/repo> (in infra/)
# 2. Repo secret AWS_DEPLOY_ROLE_ARN = the emitted role ARN
# 3. Repo secret CLERK_PUBLISHABLE_KEY = pk_live_... (inlined at build time)
# 4. Optional repo variables: AWS_REGION (default us-east-1),
# DIGEST_FROM_EMAIL (verified SES sender for weekly digests)

on:
push:
branches: [master]
workflow_dispatch:

permissions:
id-token: write # OIDC federation - no long-lived AWS keys
contents: read

concurrency:
group: deploy-production
cancel-in-progress: false

jobs:
check-config:
name: Check deploy configuration
runs-on: ubuntu-latest
outputs:
configured: ${{ steps.check.outputs.configured }}
steps:
- id: check
env:
ROLE_ARN: ${{ secrets.AWS_DEPLOY_ROLE_ARN }}
run: |
if [ -n "$ROLE_ARN" ]; then
echo "configured=true" >> "$GITHUB_OUTPUT"
else
echo "configured=false" >> "$GITHUB_OUTPUT"
echo "::notice::AWS_DEPLOY_ROLE_ARN secret not set - skipping deployment."
fi

verify:
name: Lint, typecheck, test & build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm

- name: Install dependencies
run: npm ci

- name: Lint
run: npm run lint

- name: Typecheck
run: npm run typecheck

- name: Unit tests
run: npm run test

- name: Build web app
run: npm run build
env:
SKIP_ENV_VALIDATION: "1"
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: pk_test_ZXhhbXBsZS5jbGVyay5hY2NvdW50cy5kZXYk

- name: Build worker bundle
run: npm run build --workspace @pointup/worker

deploy:
name: Deploy to AWS
runs-on: ubuntu-latest
needs: [check-config, verify]
if: needs.check-config.outputs.configured == 'true'
environment: production
steps:
- uses: actions/checkout@v4

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

- name: Install infra dependencies
working-directory: infra
run: npm ci

- name: Configure AWS credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_DEPLOY_ROLE_ARN }}
aws-region: ${{ vars.AWS_REGION || 'us-east-1' }}

- name: CDK deploy
working-directory: infra
env:
CLERK_PUBLISHABLE_KEY: ${{ secrets.CLERK_PUBLISHABLE_KEY }}
DIGEST_FROM_EMAIL: ${{ vars.DIGEST_FROM_EMAIL }}
run: |
npx cdk deploy TemplateApp \
--require-approval never \
--outputs-file cdk-outputs.json

- name: Run database migrations
working-directory: infra
run: |
CLUSTER=$(jq -r '.TemplateApp.ClusterArn' cdk-outputs.json)
TASK_DEF=$(jq -r '.TemplateApp.MigrationTaskDefinitionArn' cdk-outputs.json)
SUBNETS=$(jq -r '.TemplateApp.MigrationSubnetIds' cdk-outputs.json)
SG=$(jq -r '.TemplateApp.MigrationSecurityGroupId' cdk-outputs.json)

echo "Starting migration task..."
TASK_ARN=$(aws ecs run-task \
--cluster "$CLUSTER" \
--task-definition "$TASK_DEF" \
--launch-type FARGATE \
--network-configuration "awsvpcConfiguration={subnets=[$SUBNETS],securityGroups=[$SG],assignPublicIp=DISABLED}" \
--started-by "github-actions-${GITHUB_RUN_ID}" \
--query 'tasks[0].taskArn' --output text)
echo "Migration task: $TASK_ARN"

aws ecs wait tasks-stopped --cluster "$CLUSTER" --tasks "$TASK_ARN"

EXIT_CODE=$(aws ecs describe-tasks --cluster "$CLUSTER" --tasks "$TASK_ARN" \
--query 'tasks[0].containers[?name==`Migrate`] | [0].exitCode' --output text)
echo "Migration exit code: $EXIT_CODE"
[ "$EXIT_CODE" = "0" ]
Loading
Loading