Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
872e78f
Add Firebase App Hosting config
aamoghS Jan 20, 2026
be5c6b7
Add Firebase App Hosting config and auth updates
aamoghS Jan 20, 2026
4147d93
Add AUTH_URL to apphosting config
aamoghS Jan 20, 2026
11ca635
Add AUTH_URL to apphosting config
aamoghS Jan 20, 2026
43f9036
meow
aamoghS Jan 20, 2026
ce1133c
Trigger App Hosting rollout
aamoghS Jan 20, 2026
755d11b
Retry App Hosting rollout
aamoghS Jan 20, 2026
4cf774c
Fix rootDirectory in apphosting.yaml
aamoghS Jan 20, 2026
a874146
Add NEXTAUTH_SECRET env var
aamoghS Jan 20, 2026
4803be1
Add custom runCommand to only start portal
aamoghS Jan 20, 2026
0f0b6cd
Fix runCommand and increase memory to 1024MiB
aamoghS Jan 20, 2026
59c5efb
meow
aamoghS Jan 20, 2026
ca2c093
meow2
aamoghS Jan 20, 2026
020b7fb
e
aamoghS Jan 20, 2026
775a6d4
meow
aamoghS Jan 20, 2026
e88f5e3
fix: remove pnpm overrides causing CI lockfile mismatch
aamoghS Jan 20, 2026
5dffd14
again
aamoghS Jan 20, 2026
b52013b
fix
aamoghS Jan 20, 2026
4921220
fix
aamoghS Jan 20, 2026
2905fb8
fix
aamoghS Jan 20, 2026
efa622f
fix4
aamoghS Jan 20, 2026
7399464
5?
aamoghS Jan 20, 2026
eb80cda
6
aamoghS Jan 20, 2026
2a0c713
updat1
aamoghS Jan 20, 2026
8ecc36b
bruh
aamoghS Jan 20, 2026
ae7e2f8
uh
aamoghS Jan 20, 2026
38308bb
chore: remove pnpm-lock.yaml from git to bypass frozen-lockfile check…
aamoghS Jan 20, 2026
c96369b
fix
aamoghS Jan 20, 2026
90db97b
chore: remove pnpm-lock.yaml fully
aamoghS Jan 20, 2026
0071160
idek
aamoghS Jan 20, 2026
689b565
finally
aamoghS Jan 20, 2026
4895b8a
dude
aamoghS Jan 20, 2026
6135ba6
idek
aamoghS Jan 20, 2026
e881867
another
aamoghS Jan 20, 2026
6dabf3c
fix: disable frozen-lockfile in .npmrc to fix CI mismatch
aamoghS Jan 20, 2026
31d495b
dude pls
aamoghS Jan 20, 2026
c7d1180
um
aamoghS Jan 20, 2026
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ build
# Misc
.DS_Store
*.pem
nul

# Debug
npm-debug.log*
Expand Down Expand Up @@ -50,3 +51,4 @@ migrations/
!.vscode/settings.json
!.vscode/extensions.json
.idea
.claude/settings.local.json
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ public-hoist-pattern[]=*typescript*
auto-install-peers=true
strict-peer-dependencies=false
shamefully-hoist=true
frozen-lockfile=false
33 changes: 33 additions & 0 deletions apphosting.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Monorepo - app is in sites/portal

scripts:
buildCommand: pnpm --filter portal build && mkdir -p sites/portal/.next/standalone/sites/portal/.next && cp -r sites/portal/.next/static sites/portal/.next/standalone/sites/portal/.next/static && (cp -r sites/portal/public sites/portal/.next/standalone/sites/portal/public || true)
runCommand: node sites/portal/.next/standalone/sites/portal/server.js

runConfig:
runtime: nodejs20
concurrency: 80
cpu: 1
memoryMiB: 1024
minInstances: 0
maxInstances: 10

env:
- variable: PORT
value: "8080"
- variable: HOSTNAME
value: "0.0.0.0"
- variable: DATABASE_URL
secret: DATABASE_URL
- variable: AUTH_SECRET
secret: AUTH_SECRET
- variable: NEXTAUTH_SECRET
secret: AUTH_SECRET
- variable: GOOGLE_CLIENT_ID
secret: AUTH_GOOGLE_ID
- variable: GOOGLE_CLIENT_SECRET
secret: AUTH_GOOGLE_SECRET
- variable: AUTH_URL
value: https://query--dsgt-website.us-east4.hosted.app
- variable: NODE_ENV
value: production
272 changes: 272 additions & 0 deletions nextstep.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
# Next Steps: Google Cloud Run Deployment

## Current Status
- Dockerfile exists at `sites/portal/Dockerfile` (multi-stage build, ready to go)
- GitHub Actions workflow exists at `.github/workflows/cloudrun-deploy.yml`
- Deploy scripts exist: `deploy-cloudrun.sh` and `deploy-cloudrun.bat`
- Firebase hosting configured to rewrite portal requests to Cloud Run

---

## Prerequisites

### 1. Install Required Tools
- [ ] **Google Cloud SDK (gcloud)**: https://cloud.google.com/sdk/docs/install
- [ ] **Docker Desktop**: https://docs.docker.com/get-docker/
- [ ] **Firebase CLI**: `npm install -g firebase-tools`

### 2. Google Cloud Project Setup
- [ ] Create or select GCP project (current config expects: `dsgt-website`)
- [ ] Enable these APIs in Google Cloud Console:
- Cloud Run API
- Container Registry API
- Secret Manager API (for secrets)
- Cloud SQL Admin API (if using Cloud SQL)

```bash
gcloud services enable run.googleapis.com containerregistry.googleapis.com secretmanager.googleapis.com
```

### 3. Authenticate
```bash
gcloud auth login
gcloud config set project dsgt-website
gcloud auth configure-docker
```

---

## Database Setup (PostgreSQL)

### Option A: Cloud SQL (Recommended for Production)
- [ ] Create Cloud SQL PostgreSQL instance in GCP Console
- [ ] Create database named `portal_db` (or whatever)
- [ ] Create user with password
- [ ] Enable Cloud SQL Auth Proxy or configure public IP with SSL
- [ ] Get connection string format:
```
postgresql://USER:PASSWORD@/DATABASE?host=/cloudsql/PROJECT:REGION:INSTANCE
```

### Option B: External PostgreSQL (Supabase, Neon, Railway, etc.)
- [ ] Create PostgreSQL database on your provider
- [ ] Get connection string with SSL enabled
- [ ] Whitelist Cloud Run IPs (or allow all if no IP restrictions)

---

## Secrets Setup in Google Secret Manager

Create these secrets in GCP Secret Manager:

```bash
# Database URL
echo -n "postgresql://user:pass@host:5432/dbname" | \
gcloud secrets create DATABASE_URL --data-file=-

# NextAuth Secret (generate a random string)
echo -n "your-super-secret-auth-key-min-32-chars" | \
gcloud secrets create AUTH_SECRET --data-file=-

# Google OAuth (from Google Cloud Console > APIs & Services > Credentials)
echo -n "your-google-client-id.apps.googleusercontent.com" | \
gcloud secrets create AUTH_GOOGLE_ID --data-file=-

echo -n "your-google-client-secret" | \
gcloud secrets create AUTH_GOOGLE_SECRET --data-file=-
```

---

## First Manual Deployment

### Step 1: Build and Push Docker Image
```bash
# From the repo root
docker build -t gcr.io/dsgt-website/portal -f sites/portal/Dockerfile .
docker push gcr.io/dsgt-website/portal
```

### Step 2: Deploy to Cloud Run
```bash
gcloud run deploy portal \
--image gcr.io/dsgt-website/portal:latest \
--platform managed \
--region us-central1 \
--allow-unauthenticated \
--memory 1Gi \
--cpu 1 \
--min-instances 0 \
--max-instances 10 \
--port 8080 \
--timeout 60s \
--set-env-vars "NODE_ENV=production" \
--set-secrets "DATABASE_URL=DATABASE_URL:latest,AUTH_SECRET=AUTH_SECRET:latest,AUTH_GOOGLE_ID=AUTH_GOOGLE_ID:latest,AUTH_GOOGLE_SECRET=AUTH_GOOGLE_SECRET:latest"
```

### Step 3: Get the Service URL
```bash
gcloud run services describe portal --region us-central1 --format 'value(status.url)'
```

This will output something like: `https://portal-xxxxx-uc.a.run.app`

### Step 4: Update Auth URLs
Go to Cloud Run console and set these env vars:
- `AUTH_URL` = `https://dsgt-portal.web.app` (your Firebase hosting URL)
- `NEXT_PUBLIC_APP_URL` = `https://dsgt-portal.web.app`

### Step 5: Update Google OAuth Redirect URIs
In Google Cloud Console > APIs & Services > Credentials > Your OAuth Client:
- Add authorized redirect URI: `https://dsgt-portal.web.app/api/auth/callback/google`
- Add authorized redirect URI: `https://portal-xxxxx-uc.a.run.app/api/auth/callback/google`

---

## Firebase Hosting Setup (Routes traffic to Cloud Run)

### Step 1: Login and set project
```bash
firebase login
firebase use dsgt-website
```

### Step 2: Deploy hosting configuration
```bash
firebase deploy --only hosting:portal
```

This deploys the rewrite rules in `firebase.json` that route all `dsgt-portal.web.app/*` traffic to your Cloud Run service.

---

## GitHub Actions Setup (CI/CD)

### Required GitHub Secrets
Add these in GitHub repo > Settings > Secrets and variables > Actions:

| Secret Name | Value |
|-------------|-------|
| `GCP_PROJECT_ID` | `dsgt-website` |
| `GCP_SA_KEY` | Service account JSON key (see below) |

### Create Service Account for CI/CD
```bash
# Create service account
gcloud iam service-accounts create github-actions \
--display-name="GitHub Actions"

# Grant roles
gcloud projects add-iam-policy-binding dsgt-website \
--member="serviceAccount:github-actions@dsgt-website.iam.gserviceaccount.com" \
--role="roles/run.admin"

gcloud projects add-iam-policy-binding dsgt-website \
--member="serviceAccount:github-actions@dsgt-website.iam.gserviceaccount.com" \
--role="roles/storage.admin"

gcloud projects add-iam-policy-binding dsgt-website \
--member="serviceAccount:github-actions@dsgt-website.iam.gserviceaccount.com" \
--role="roles/iam.serviceAccountUser"

# Create and download key
gcloud iam service-accounts keys create github-sa-key.json \
--iam-account=github-actions@dsgt-website.iam.gserviceaccount.com

# Copy the contents of github-sa-key.json to GCP_SA_KEY secret
cat github-sa-key.json
```

---

## Database Migration

After Cloud Run is deployed, run migrations:

```bash
# Set DATABASE_URL locally to your production database
export DATABASE_URL="postgresql://..."

# Push schema to database
cd packages/db
pnpm migrate:push
```

Or connect to Cloud Run and run migrations there (advanced).

---

## Quick Deploy Script

Once everything is set up, you can use the existing script:

```bash
# Unix/Mac/WSL
./deploy-cloudrun.sh

# Windows (PowerShell with Docker)
# Use the manual steps above or WSL
```

---

## Checklist Summary

### One-Time Setup
- [ ] GCP project created and APIs enabled
- [ ] gcloud CLI installed and authenticated
- [ ] Docker installed
- [ ] PostgreSQL database created (Cloud SQL or external)
- [ ] Secrets created in Secret Manager
- [ ] Google OAuth credentials configured with correct redirect URIs
- [ ] Service account created for GitHub Actions
- [ ] GitHub secrets configured

### Each Deployment
- [ ] Build passes locally: `pnpm build`
- [ ] Push to main branch (triggers GitHub Actions)
- [ ] OR run `./deploy-cloudrun.sh` manually
- [ ] Verify at Cloud Run URL
- [ ] Verify at Firebase hosting URL (dsgt-portal.web.app)

---

## Useful Commands

```bash
# Check Cloud Run logs
gcloud run services logs read portal --region us-central1

# Stream logs in real-time
gcloud run services logs tail portal --region us-central1

# List Cloud Run services
gcloud run services list

# Delete a service (if needed)
gcloud run services delete portal --region us-central1

# Check secret versions
gcloud secrets versions list DATABASE_URL
```

---

## Troubleshooting

### "Container failed to start"
- Check Cloud Run logs for actual error
- Verify DATABASE_URL is correct and accessible
- Make sure secrets are properly mounted

### "NEXT_AUTH_URL mismatch"
- Set AUTH_URL env var to match your domain
- Update OAuth redirect URIs in Google Console

### Build fails in Docker
- Test locally first: `docker build -t test -f sites/portal/Dockerfile .`
- Check pnpm-lock.yaml is up to date

### Database connection refused
- Whitelist Cloud Run egress IPs (or use Cloud SQL connector)
- Check if SSL is required by your DB provider
31 changes: 17 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
"name": "query",
"private": true,
"version": "1.0.0",
"packageManager": "pnpm@10.26.1",
"packageManager": "pnpm@10.28.1",
"engines": {
"node": ">=20.16.0",
"node": ">=20.16.0 <23",
"pnpm": ">=9"
},
"scripts": {
Expand All @@ -22,6 +22,16 @@
"format:fix": "turbo run format -- --write",
"typecheck": "turbo run typecheck"
},
"dependencies": {
"@tanstack/react-router": "^1.141.2",
"autoprefixer": "^10.4.22",
"chart.js": "^4.5.1",
"minimatch": "^10.1.1",
"postcss": "^8.5.6",
"next": "15.5.9",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"devDependencies": {
"@query/eslint-config": "workspace:*",
"@query/prettier-config": "workspace:*",
Expand All @@ -33,28 +43,21 @@
"@trpc/react-query": "^11.7.2",
"@trpc/server": "^11.8.0",
"@turbo/gen": "^2.1.3",
"@types/node": "^22.10.1",
"@types/react": "^18.3.1",
"@types/react-dom": "^18.3.1",
"prettier": "^3.3.3",
"tsx": "^4.21.0",
"turbo": "^2.6.3",
"typescript": "^5.6.3",
"zod": "^3.23.8"
},
"dependencies": {
"@tanstack/react-router": "^1.141.2",
"autoprefixer": "^10.4.22",
"chart.js": "^4.5.1",
"minimatch": "^10.1.1",
"postcss": "^8.5.6"
},
"prettier": "@query/prettier-config",
"pnpm": {
"overrides": {
"next": "15.5.9",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"@types/react": "^18.3.1",
"@types/react-dom": "^18.3.1",
"@types/node": "^22.10.1"
"react-dom": "^18.3.1"
}
}
}
}
Loading
Loading