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
19 changes: 18 additions & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ concurrency:
cancel-in-progress: false

permissions:
contents: read
contents: write
id-token: write
packages: write
pull-requests: write
Expand Down Expand Up @@ -205,3 +205,20 @@ jobs:
-auto-approve \
-var-file=${{ steps.paths.outputs.TFVARS }} \
-input=false

- name: Update GitHub Repo Description with Frontend URL (prod only)
if: github.ref == 'refs/heads/main'
run: |
cd terraform-infra
FRONTEND_URL=$(terraform output -raw app_engine_url 2>/dev/null || echo "")
if [ -n "$FRONTEND_URL" ]; then
echo "Updating GitHub repo description with URL: $FRONTEND_URL"
gh api repos/${{ github.repository }} \
--method PATCH \
-f homepage="$FRONTEND_URL" \
-f description="Guidon - Serverless Discord Bot for collaborative drawing | Frontend: $FRONTEND_URL"
else
echo "Frontend URL not found in Terraform outputs, skipping update"
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
18 changes: 2 additions & 16 deletions services/canvas-service/shared/auth_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,14 @@ def get_service_url_from_request(request: Request) -> Optional[str]:
request: Flask request object

Returns:
Full service URL including function path for Cloud Functions
(e.g., https://region-project.cloudfunctions.net/function-name)
or Cloud Run URL (e.g., https://service-name-xxx.a.run.app)
Full service URL (e.g., https://service-name-xxx.a.run.app) or None
"""
host = request.headers.get('Host')
if host:
scheme = request.headers.get('X-Forwarded-Proto', 'https')
if ':' in host:
host = host.split(':')[0]

base_url = f"{scheme}://{host}"

# For Cloud Functions Gen2, include the function name (first path segment)
# Request path like: /function-name/api/endpoint
# We want: https://domain.cloudfunctions.net/function-name
if 'cloudfunctions.net' in host and request.path:
path_parts = request.path.strip('/').split('/')
if path_parts and path_parts[0]:
# First segment is the function name
base_url = f"{base_url}/{path_parts[0]}"

return base_url
return f"{scheme}://{host}"
return None


Expand Down
18 changes: 2 additions & 16 deletions services/shared/auth_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,14 @@ def get_service_url_from_request(request: Request) -> Optional[str]:
request: Flask request object

Returns:
Full service URL including function path for Cloud Functions
(e.g., https://region-project.cloudfunctions.net/function-name)
or Cloud Run URL (e.g., https://service-name-xxx.a.run.app)
Full service URL (e.g., https://service-name-xxx.a.run.app) or None
"""
host = request.headers.get('Host')
if host:
scheme = request.headers.get('X-Forwarded-Proto', 'https')
if ':' in host:
host = host.split(':')[0]

base_url = f"{scheme}://{host}"

# For Cloud Functions Gen2, include the function name (first path segment)
# Request path like: /function-name/api/endpoint
# We want: https://domain.cloudfunctions.net/function-name
if 'cloudfunctions.net' in host and request.path:
path_parts = request.path.strip('/').split('/')
if path_parts and path_parts[0]:
# First segment is the function name
base_url = f"{base_url}/{path_parts[0]}"

return base_url
return f"{scheme}://{host}"
return None


Expand Down
18 changes: 2 additions & 16 deletions services/user-manager/shared/auth_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,14 @@ def get_service_url_from_request(request: Request) -> Optional[str]:
request: Flask request object

Returns:
Full service URL including function path for Cloud Functions
(e.g., https://region-project.cloudfunctions.net/function-name)
or Cloud Run URL (e.g., https://service-name-xxx.a.run.app)
Full service URL (e.g., https://service-name-xxx.a.run.app) or None
"""
host = request.headers.get('Host')
if host:
scheme = request.headers.get('X-Forwarded-Proto', 'https')
if ':' in host:
host = host.split(':')[0]

base_url = f"{scheme}://{host}"

# For Cloud Functions Gen2, include the function name (first path segment)
# Request path like: /function-name/api/endpoint
# We want: https://domain.cloudfunctions.net/function-name
if 'cloudfunctions.net' in host and request.path:
path_parts = request.path.strip('/').split('/')
if path_parts and path_parts[0]:
# First segment is the function name
base_url = f"{base_url}/{path_parts[0]}"

return base_url
return f"{scheme}://{host}"
return None


Expand Down
33 changes: 33 additions & 0 deletions terraform-infra/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,15 @@ resource "google_secret_manager_secret_iam_member" "gcs_bucket_access" {
depends_on = [module.service_accounts]
}

# Permissions IAM pour le service account cloud-functions sur le bucket canvas-snapshots
resource "google_storage_bucket_iam_member" "canvas_bucket_admin" {
bucket = module.gcs_buckets["canvas-snapshots"].bucket_name
role = "roles/storage.objectAdmin"
member = module.service_accounts["cloud-functions"].member

depends_on = [module.gcs_buckets, module.service_accounts]
}

# ============================================================================
# PHASE 1 : Secrets basés sur l'API Gateway (valeurs temporaires)
# ============================================================================
Expand Down Expand Up @@ -562,4 +571,28 @@ resource "google_secret_manager_secret_iam_member" "gcp_project_id_access" {
member = module.service_accounts["cloud-functions"].member

depends_on = [module.service_accounts]
}

# ========================================
# OUTPUTS
# ========================================

output "api_gateway_url" {
description = "URL de l'API Gateway"
value = module.api_gateway.gateway_url
}

output "app_engine_url" {
description = "URL du frontend App Engine"
value = module.app_engine.app_url
}

output "project_id" {
description = "ID du projet GCP"
value = var.project_id
}

output "region" {
description = "Région GCP"
value = var.region
}