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
1 change: 1 addition & 0 deletions .github/workflows/docker-build-push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ jobs:
--build-arg VITE_SUPABASE_URL=${{ secrets.VITE_SUPABASE_URL }} \
--build-arg VITE_SUPABASE_ANON_KEY=${{ secrets.VITE_SUPABASE_ANON_KEY }} \
--build-arg VITE_API_URL=${{ secrets.VITE_API_URL }} \
--build-arg VITE_GCP_FUNCTION_BASE_URL=https://${{ env.REGION }}-${{ env.PROJECT_ID }}.cloudfunctions.net \
-t ${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.REPOSITORY }}/${{ env.IMAGE }}:${{ github.sha }} webapp/
docker tag ${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.REPOSITORY }}/${{ env.IMAGE }}:${{ github.sha }} \
${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.REPOSITORY }}/${{ env.IMAGE }}:latest
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
local-dev

dist/

static_assets/
scripts/
tmp/
Expand Down
6 changes: 6 additions & 0 deletions _infra/cloud-run.tf
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ resource "google_cloud_run_service" "webapp" {
containers {
image = "${var.region}-docker.pkg.dev/${var.project_id}/${var.repository_name}/${var.image_name}:latest"

# Add environment variables for the webapp
env {
name = "VITE_GCP_FUNCTION_BASE_URL"
value = "https://${var.region}-${var.project_id}.cloudfunctions.net"
}

resources {
limits = {
cpu = "1000m"
Expand Down
73 changes: 73 additions & 0 deletions _infra/cloud_functions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ resource "google_service_account" "coach_file_uploader" {
project = var.project_id
}

resource "google_service_account" "coach_avatar_generator" {
account_id = "coach-avatar-generator"
display_name = "Service Account for Coach Avatar Generator Function"
project = var.project_id
}

# Create conversation storage bucket
resource "google_storage_bucket" "conversation_storage" {
Expand Down Expand Up @@ -134,6 +139,19 @@ resource "google_project_iam_member" "coach_file_uploader_roles" {
member = "serviceAccount:${google_service_account.coach_file_uploader.email}"
}

resource "google_project_iam_member" "coach_avatar_generator_roles" {
for_each = toset([
"roles/cloudfunctions.invoker",
"roles/storage.objectUser",
"roles/logging.logWriter",
"roles/iam.serviceAccountTokenCreator"
])

project = var.project_id
role = each.key
member = "serviceAccount:${google_service_account.coach_avatar_generator.email}"
}

# Allow the coach file uploader service account to create tokens for itself
resource "google_service_account_iam_member" "coach_file_uploader_self_token_creator" {
service_account_id = google_service_account.coach_file_uploader.name
Expand Down Expand Up @@ -173,13 +191,26 @@ resource "google_storage_bucket_iam_member" "coach_file_uploader_bucket_access"
member = "serviceAccount:${google_service_account.coach_file_uploader.email}"
}

resource "google_storage_bucket_iam_member" "coach_avatar_generator_content_bucket_access" {
bucket = google_storage_bucket.coach_content_bucket.name
role = "roles/storage.objectUser"
member = "serviceAccount:${google_service_account.coach_avatar_generator.email}"
}

# Grant the motivational images function access to the image bucket
resource "google_storage_bucket_iam_member" "motivational_images_bucket_access" {
bucket = "${var.project_id}-image-bucket"
role = "roles/storage.objectUser"
member = "serviceAccount:${google_service_account.motivational_images.email}"
}

# Grant the avatar generator function access to the image bucket
resource "google_storage_bucket_iam_member" "coach_avatar_generator_image_bucket_access" {
bucket = "${var.project_id}-image-bucket"
role = "roles/storage.objectUser"
member = "serviceAccount:${google_service_account.coach_avatar_generator.email}"
}

resource "google_project_iam_member" "motivational_images_roles" {
for_each = toset([
"roles/cloudfunctions.invoker",
Expand Down Expand Up @@ -272,6 +303,12 @@ data "archive_file" "coach_file_uploader_zip" {
excludes = ["node_modules"]
}

data "archive_file" "coach_avatar_generator_zip" {
type = "zip"
source_dir = "${path.root}/../functions/coach-avatar-generator"
output_path = "${path.root}/tmp/coach-avatar-generator.zip"
excludes = ["node_modules"]
}

# Upload the function sources to Cloud Storage
resource "google_storage_bucket_object" "motivational_images_source" {
Expand Down Expand Up @@ -341,6 +378,12 @@ resource "google_storage_bucket_object" "coach_file_uploader_source" {
source = data.archive_file.coach_file_uploader_zip.output_path
}

resource "google_storage_bucket_object" "coach_avatar_generator_source" {
name = "coach-avatar-generator-${data.archive_file.coach_avatar_generator_zip.output_md5}.zip"
bucket = google_storage_bucket.function_bucket.name
source = data.archive_file.coach_avatar_generator_zip.output_path
}

# Deploy Cloud Functions using the module
module "motivation_function" {
source = "./modules/cloud_function"
Expand Down Expand Up @@ -589,6 +632,29 @@ module "coach_file_uploader_function" {
]
}

module "coach_avatar_generator_function" {
source = "./modules/cloud_function"

name = "coach-avatar-generator"
description = "Function to generate professional avatars from coach selfies"
region = var.region
bucket_name = google_storage_bucket.function_bucket.name
source_object = google_storage_bucket_object.coach_avatar_generator_source.name
entry_point = "generateCoachAvatar"
memory = "1Gi"
timeout = 540 # 9 minutes for AI image generation
service_account_email = google_service_account.coach_avatar_generator.email

environment_variables = {
PROJECT_ID = var.project_id
SUPABASE_URL = var.supabase_url
SUPABASE_SERVICE_ROLE_KEY = var.supabase_service_role_key
REPLICATE_API_TOKEN = var.replicate_api_key
ALLOWED_ORIGINS = var.allowed_origins
}
depends_on = [google_storage_bucket_object.coach_avatar_generator_source]
}

resource "google_cloud_run_service_iam_member" "process_sms_invoker" {
location = module.process_sms_function.function.location
service = module.process_sms_function.function.name
Expand Down Expand Up @@ -651,4 +717,11 @@ resource "google_cloud_run_service_iam_member" "coach_file_uploader_invoker" {
service = module.coach_file_uploader_function.function.name
role = "roles/run.invoker"
member = "allUsers"
}

resource "google_cloud_run_service_iam_member" "coach_avatar_generator_invoker" {
location = module.coach_avatar_generator_function.function.location
service = module.coach_avatar_generator_function.function.name
role = "roles/run.invoker"
member = "allUsers"
}
8 changes: 5 additions & 3 deletions _infra/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ output "coach_file_uploader_url" {
value = module.coach_file_uploader_function.url
}

output "coach_avatar_generator_url" {
description = "URL of the coach avatar generator function"
value = module.coach_avatar_generator_function.url
}

output "conversation_bucket_name" {
description = "Name of the conversation storage bucket"
value = google_storage_bucket.conversation_storage.name
Expand All @@ -125,9 +130,6 @@ output "webapp_environment_variables" {
description = "Environment variables needed for the webapp"
value = {
VITE_GCP_FUNCTION_BASE_URL = "https://${var.region}-${var.project_id}.cloudfunctions.net"
VITE_COACH_CONTENT_PROCESSOR_URL = module.coach_content_processor_function.url
VITE_COACH_RESPONSE_GENERATOR_URL = module.coach_response_generator_function.url
VITE_COACH_FILE_UPLOADER_URL = module.coach_file_uploader_function.url
}
sensitive = false
}
Loading