Skip to content

Commit 08c0cbd

Browse files
authored
Merge pull request #25 from VectorInstitute/add_onboarding_status_webpage
Add a simple webpage to track onboarding status
2 parents 425d627 + 8e31f3d commit 08c0cbd

File tree

20 files changed

+8054
-0
lines changed

20 files changed

+8054
-0
lines changed

.github/workflows/code_checks.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,17 @@ jobs:
4646
- name: Install the project
4747
run: uv sync --all-extras --dev
4848

49+
- name: Set up Node.js
50+
uses: actions/setup-node@v4
51+
with:
52+
node-version: '20'
53+
cache: 'npm'
54+
cache-dependency-path: services/onboarding-status-web/package-lock.json
55+
56+
- name: Install Next.js dependencies
57+
run: npm ci
58+
working-directory: services/onboarding-status-web
59+
4960
- name: Install dependencies and check code
5061
run: |
5162
source .venv/bin/activate

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ wheels/
2323
# .env
2424
.env
2525

26+
# Deployment-generated URLs
27+
.onboarding-status-url
28+
.token-service-url
29+
2630
terraform.tfvars
2731
*.tfstate
2832
.terraform/

.pre-commit-config.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,16 @@ repos:
4848
- id: typos
4949
args: []
5050

51+
- repo: local
52+
hooks:
53+
- id: nextjs-lint
54+
name: Next.js Lint
55+
entry: bash -c 'cd services/onboarding-status-web && npm run lint'
56+
language: system
57+
files: '^services/onboarding-status-web'
58+
types_or: [javascript, jsx, ts, tsx]
59+
pass_filenames: false
60+
5161
ci:
5262
autofix_commit_msg: |
5363
[pre-commit.ci] Add auto fixes from pre-commit.com hooks
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
#!/bin/bash
2+
#
3+
# Deploy Onboarding Status Web Dashboard to Cloud Run
4+
#
5+
# This script builds and deploys the Next.js web dashboard that displays
6+
# real-time participant onboarding status from Firestore.
7+
#
8+
# Usage:
9+
# ./deploy_onboarding_status_web.sh [OPTIONS]
10+
#
11+
# Options:
12+
# --project PROJECT_ID GCP project ID (default: coderd)
13+
# --region REGION Cloud Run region (default: us-central1)
14+
# --service-name NAME Service name (default: onboarding-status-web)
15+
# --allow-unauthenticated Allow unauthenticated requests (default: true for dashboards)
16+
# --dry-run Show commands without executing
17+
#
18+
19+
set -euo pipefail
20+
21+
# Default configuration
22+
PROJECT_ID="${GCP_PROJECT:-coderd}"
23+
REGION="us-central1"
24+
SERVICE_NAME="onboarding-status-web"
25+
ALLOW_UNAUTHENTICATED="true" # Public dashboard by default
26+
DRY_RUN="false"
27+
FIRESTORE_DATABASE="onboarding"
28+
29+
# Colors for output
30+
RED='\033[0;31m'
31+
GREEN='\033[0;32m'
32+
YELLOW='\033[1;33m'
33+
BLUE='\033[0;34m'
34+
NC='\033[0m' # No Color
35+
36+
# Parse command line arguments
37+
while [[ $# -gt 0 ]]; do
38+
case $1 in
39+
--project)
40+
PROJECT_ID="$2"
41+
shift 2
42+
;;
43+
--region)
44+
REGION="$2"
45+
shift 2
46+
;;
47+
--service-name)
48+
SERVICE_NAME="$2"
49+
shift 2
50+
;;
51+
--allow-unauthenticated)
52+
ALLOW_UNAUTHENTICATED="true"
53+
shift
54+
;;
55+
--no-allow-unauthenticated)
56+
ALLOW_UNAUTHENTICATED="false"
57+
shift
58+
;;
59+
--dry-run)
60+
DRY_RUN="true"
61+
shift
62+
;;
63+
*)
64+
echo -e "${RED}Unknown option: $1${NC}"
65+
exit 1
66+
;;
67+
esac
68+
done
69+
70+
# Get script directory
71+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
72+
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
73+
SERVICE_DIR="${PROJECT_ROOT}/services/onboarding-status-web"
74+
75+
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
76+
echo -e "${BLUE}Onboarding Status Web Dashboard Deployment${NC}"
77+
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
78+
echo ""
79+
echo -e "${YELLOW}Configuration:${NC}"
80+
echo " Project ID: ${PROJECT_ID}"
81+
echo " Region: ${REGION}"
82+
echo " Service Name: ${SERVICE_NAME}"
83+
echo " Firestore Database: ${FIRESTORE_DATABASE}"
84+
echo " Service Directory: ${SERVICE_DIR}"
85+
echo " Allow Unauth: ${ALLOW_UNAUTHENTICATED}"
86+
echo " Dry Run: ${DRY_RUN}"
87+
echo ""
88+
89+
# Function to execute or print commands
90+
run_cmd() {
91+
if [[ "${DRY_RUN}" == "true" ]]; then
92+
echo -e "${YELLOW}[DRY RUN]${NC} $*"
93+
else
94+
echo -e "${GREEN}${NC} $*"
95+
"$@"
96+
fi
97+
}
98+
99+
# Verify service directory exists
100+
if [[ ! -d "${SERVICE_DIR}" ]]; then
101+
echo -e "${RED}✗ Service directory not found: ${SERVICE_DIR}${NC}"
102+
exit 1
103+
fi
104+
105+
echo -e "${GREEN}${NC} Service directory found"
106+
107+
# Check required files
108+
REQUIRED_FILES=("package.json" "Dockerfile" "next.config.js")
109+
for file in "${REQUIRED_FILES[@]}"; do
110+
if [[ ! -f "${SERVICE_DIR}/${file}" ]]; then
111+
echo -e "${RED}✗ Required file not found: ${file}${NC}"
112+
exit 1
113+
fi
114+
done
115+
echo -e "${GREEN}${NC} All required files present"
116+
echo ""
117+
118+
# Step 1: Set GCP project
119+
echo -e "${BLUE}Step 1: Configure GCP Project${NC}"
120+
run_cmd gcloud config set project "${PROJECT_ID}"
121+
echo ""
122+
123+
# Step 2: Enable required APIs
124+
echo -e "${BLUE}Step 2: Enable Required APIs${NC}"
125+
REQUIRED_APIS=(
126+
"run.googleapis.com"
127+
"cloudbuild.googleapis.com"
128+
"artifactregistry.googleapis.com"
129+
"firestore.googleapis.com"
130+
)
131+
132+
for api in "${REQUIRED_APIS[@]}"; do
133+
echo -e "${GREEN}${NC} Enabling ${api}..."
134+
run_cmd gcloud services enable "${api}" --project="${PROJECT_ID}"
135+
done
136+
echo ""
137+
138+
# Step 3: Build and deploy to Cloud Run
139+
echo -e "${BLUE}Step 3: Build and Deploy to Cloud Run${NC}"
140+
echo -e "${GREEN}${NC} Building container image and deploying..."
141+
142+
DEPLOY_CMD=(
143+
gcloud run deploy "${SERVICE_NAME}"
144+
--source="${SERVICE_DIR}"
145+
--platform=managed
146+
--region="${REGION}"
147+
--project="${PROJECT_ID}"
148+
--set-env-vars="GCP_PROJECT_ID=${PROJECT_ID},FIRESTORE_DATABASE_ID=${FIRESTORE_DATABASE}"
149+
--memory=1Gi
150+
--cpu=1
151+
--timeout=300s
152+
--max-instances=10
153+
--min-instances=0
154+
--concurrency=80
155+
)
156+
157+
if [[ "${ALLOW_UNAUTHENTICATED}" == "true" ]]; then
158+
DEPLOY_CMD+=(--allow-unauthenticated)
159+
echo -e "${YELLOW}Note: Allowing unauthenticated access (public dashboard)${NC}"
160+
else
161+
DEPLOY_CMD+=(--no-allow-unauthenticated)
162+
fi
163+
164+
run_cmd "${DEPLOY_CMD[@]}"
165+
echo ""
166+
167+
# Step 4: Get service URL
168+
if [[ "${DRY_RUN}" == "false" ]]; then
169+
echo -e "${BLUE}Step 4: Retrieve Service URL${NC}"
170+
SERVICE_URL=$(gcloud run services describe "${SERVICE_NAME}" \
171+
--platform=managed \
172+
--region="${REGION}" \
173+
--project="${PROJECT_ID}" \
174+
--format='value(status.url)')
175+
176+
echo -e "${GREEN}${NC} Service deployed successfully!"
177+
echo ""
178+
echo -e "${GREEN}Dashboard URL:${NC} ${SERVICE_URL}"
179+
echo ""
180+
181+
# Step 5: Configure IAM if needed
182+
if [[ "${ALLOW_UNAUTHENTICATED}" == "false" ]]; then
183+
echo -e "${BLUE}Step 5: Configure IAM Permissions${NC}"
184+
echo -e "${YELLOW}Note: Service requires authentication${NC}"
185+
echo ""
186+
echo "Grant access to specific users or service accounts:"
187+
echo ""
188+
echo -e "${BLUE}gcloud run services add-iam-policy-binding ${SERVICE_NAME} \\${NC}"
189+
echo -e "${BLUE} --region=${REGION} \\${NC}"
190+
echo -e "${BLUE} --project=${PROJECT_ID} \\${NC}"
191+
echo -e "${BLUE} --member='user:EMAIL' \\${NC}"
192+
echo -e "${BLUE} --role='roles/run.invoker'${NC}"
193+
echo ""
194+
fi
195+
196+
# Step 6: Test the service
197+
echo -e "${BLUE}Step 6: Test the Dashboard${NC}"
198+
echo "Open the dashboard in your browser:"
199+
echo ""
200+
echo -e "${BLUE}${SERVICE_URL}${NC}"
201+
echo ""
202+
echo "The dashboard will display:"
203+
echo " • Total participants"
204+
echo " • Onboarded count"
205+
echo " • Not onboarded count"
206+
echo " • Completion percentage"
207+
echo " • Real-time participant status table"
208+
echo ""
209+
echo "The dashboard auto-refreshes every 30 seconds"
210+
echo ""
211+
212+
# Save service URL to config file
213+
CONFIG_FILE="${PROJECT_ROOT}/.onboarding-status-url"
214+
echo "${SERVICE_URL}" > "${CONFIG_FILE}"
215+
echo -e "${GREEN}${NC} Dashboard URL saved to ${CONFIG_FILE}"
216+
echo ""
217+
fi
218+
219+
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
220+
echo -e "${GREEN}Deployment Complete!${NC}"
221+
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
node_modules
2+
.next
3+
.git
4+
.gitignore
5+
README.md
6+
.env.local
7+
.env.*.local
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": ["next/core-web-vitals", "next/typescript"]
3+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# dependencies
2+
/node_modules
3+
/.pnp
4+
.pnp.js
5+
6+
# testing
7+
/coverage
8+
9+
# next.js
10+
/.next/
11+
/out/
12+
13+
# production
14+
/build
15+
16+
# misc
17+
.DS_Store
18+
*.pem
19+
20+
# debug
21+
npm-debug.log*
22+
yarn-debug.log*
23+
yarn-error.log*
24+
25+
# local env files
26+
.env*.local
27+
28+
# vercel
29+
.vercel
30+
31+
# typescript
32+
*.tsbuildinfo
33+
next-env.d.ts
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Multi-stage build for Next.js on Cloud Run
2+
FROM node:20-alpine AS base
3+
4+
# Install dependencies only when needed
5+
FROM base AS deps
6+
RUN apk add --no-cache libc6-compat
7+
WORKDIR /app
8+
9+
# Copy package files
10+
COPY package.json package-lock.json* ./
11+
RUN npm ci
12+
13+
# Rebuild the source code only when needed
14+
FROM base AS builder
15+
WORKDIR /app
16+
COPY --from=deps /app/node_modules ./node_modules
17+
COPY . .
18+
19+
# Disable telemetry during build
20+
ENV NEXT_TELEMETRY_DISABLED=1
21+
22+
# Build the application
23+
RUN npm run build
24+
25+
# Production image, copy all the files and run next
26+
FROM base AS runner
27+
WORKDIR /app
28+
29+
ENV NODE_ENV=production
30+
ENV NEXT_TELEMETRY_DISABLED=1
31+
32+
# Create a non-root user for security
33+
RUN addgroup --system --gid 1001 nodejs
34+
RUN adduser --system --uid 1001 nextjs
35+
36+
# Copy public directory from builder
37+
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
38+
39+
# Set the correct permission for prerender cache
40+
RUN mkdir .next
41+
RUN chown nextjs:nodejs .next
42+
43+
# Automatically leverage output traces to reduce image size
44+
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
45+
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
46+
47+
USER nextjs
48+
49+
# Expose the port
50+
EXPOSE 8080
51+
52+
ENV PORT=8080
53+
ENV HOSTNAME="0.0.0.0"
54+
55+
# Run the application
56+
CMD ["node", "server.js"]

0 commit comments

Comments
 (0)