diff --git a/deployment/cloud-run/service.yaml b/deployment/cloud-run/service.yaml index c715cba..7afdff5 100644 --- a/deployment/cloud-run/service.yaml +++ b/deployment/cloud-run/service.yaml @@ -44,7 +44,7 @@ spec: - name: LOG_LEVEL value: "INFO" - name: CORS_ORIGINS - value: "*" + value: "['*']" - name: CORS_METHODS value: "GET,POST,PUT,DELETE,OPTIONS" - name: CORS_HEADERS diff --git a/deployment/terraform/.terraform.tfstate.lock.info b/deployment/terraform/.terraform.tfstate.lock.info new file mode 100644 index 0000000..f1ec83a --- /dev/null +++ b/deployment/terraform/.terraform.tfstate.lock.info @@ -0,0 +1 @@ +{"ID":"5f252024-676f-4920-f829-cb47c30360a4","Operation":"OperationTypeApply","Info":"","Who":"DESKTOP\\habal@Desktop","Version":"1.10.1","Created":"2025-05-24T20:52:18.4005055Z","Path":"terraform.tfstate"} \ No newline at end of file diff --git a/deployment/terraform/main.tf b/deployment/terraform/main.tf index 3442f23..6be2a5a 100644 --- a/deployment/terraform/main.tf +++ b/deployment/terraform/main.tf @@ -115,10 +115,46 @@ resource "google_cloud_run_service" "sereact" { value = var.qdrant_api_key } + env { + name = "QDRANT_HTTPS" + value = "false" + } + + env { + name = "QDRANT_PREFER_GRPC" + value = "false" + } + env { name = "LOG_LEVEL" value = "INFO" } + + # CORS Configuration - These were missing! + env { + name = "CORS_ORIGINS" + value = "['*']" + } + + env { + name = "CORS_METHODS" + value = "GET,POST,PUT,DELETE,OPTIONS" + } + + env { + name = "CORS_HEADERS" + value = "Content-Type,Authorization,X-Requested-With" + } + + env { + name = "CORS_EXPOSE_HEADERS" + value = "Content-Length,Content-Range" + } + + env { + name = "CORS_MAX_AGE" + value = "3600" + } } } diff --git a/deployment/terraform/terraform.tfstate b/deployment/terraform/terraform.tfstate index 98a27cd..654ebe7 100644 --- a/deployment/terraform/terraform.tfstate +++ b/deployment/terraform/terraform.tfstate @@ -1,7 +1,7 @@ { "version": 4, "terraform_version": "1.10.1", - "serial": 260, + "serial": 269, "lineage": "a183cd95-f987-8698-c6dd-84e933c394a5", "outputs": { "cloud_run_qdrant_host": { @@ -136,7 +136,7 @@ "effective_annotations": { "run.googleapis.com/ingress": "all", "run.googleapis.com/ingress-status": "all", - "run.googleapis.com/operation-id": "5c612fa4-f8c2-4757-88a1-c0c2fe67d0ad", + "run.googleapis.com/operation-id": "a36b6733-8ef6-4151-a609-3f89a8666953", "run.googleapis.com/urls": "[\"https://sereact-761163285547.us-central1.run.app\",\"https://sereact-p64zpdtkta-uc.a.run.app\"]", "serving.knative.dev/creator": "johnpccd3@gmail.com", "serving.knative.dev/lastModifier": "johnpccd3@gmail.com" @@ -145,10 +145,10 @@ "cloud.googleapis.com/location": "us-central1", "goog-terraform-provisioned": "true" }, - "generation": 1, - "labels": null, + "generation": 4, + "labels": {}, "namespace": "gen-lang-client-0424120530", - "resource_version": "AAY15bM0evU", + "resource_version": "AAY159zQkzU", "self_link": "/apis/serving.knative.dev/v1/namespaces/761163285547/services/sereact", "terraform_labels": { "goog-terraform-provisioned": "true" @@ -162,27 +162,27 @@ { "conditions": [ { - "message": "", - "reason": "", - "status": "True", + "message": "Revision 'sereact-00004-45z' is not ready and cannot serve traffic. The user-provided container failed the configured startup probe checks. Logs for this revision might contain more information.\n\nLogs URL: https://console.cloud.google.com/logs/viewer?project=gen-lang-client-0424120530\u0026resource=cloud_run_revision/service_name/sereact/revision_name/sereact-00004-45z\u0026advancedFilter=resource.type%3D%22cloud_run_revision%22%0Aresource.labels.service_name%3D%22sereact%22%0Aresource.labels.revision_name%3D%22sereact-00004-45z%22 \nFor more troubleshooting guidance, see https://cloud.google.com/run/docs/troubleshooting#container-failed-to-start", + "reason": "HealthCheckContainerError", + "status": "False", "type": "Ready" }, { - "message": "", + "message": "The user-provided container failed the configured startup probe checks. Logs for this revision might contain more information.\n\nLogs URL: https://console.cloud.google.com/logs/viewer?project=gen-lang-client-0424120530\u0026resource=cloud_run_revision/service_name/sereact/revision_name/sereact-00003-2fs\u0026advancedFilter=resource.type%3D%22cloud_run_revision%22%0Aresource.labels.service_name%3D%22sereact%22%0Aresource.labels.revision_name%3D%22sereact-00003-2fs%22 \nFor more troubleshooting guidance, see https://cloud.google.com/run/docs/troubleshooting#container-failed-to-start", "reason": "", "status": "True", "type": "ConfigurationsReady" }, { - "message": "", - "reason": "", - "status": "True", + "message": "Revision 'sereact-00004-45z' is not ready and cannot serve traffic. The user-provided container failed the configured startup probe checks. Logs for this revision might contain more information.\n\nLogs URL: https://console.cloud.google.com/logs/viewer?project=gen-lang-client-0424120530\u0026resource=cloud_run_revision/service_name/sereact/revision_name/sereact-00004-45z\u0026advancedFilter=resource.type%3D%22cloud_run_revision%22%0Aresource.labels.service_name%3D%22sereact%22%0Aresource.labels.revision_name%3D%22sereact-00004-45z%22 \nFor more troubleshooting guidance, see https://cloud.google.com/run/docs/troubleshooting#container-failed-to-start", + "reason": "HealthCheckContainerError", + "status": "False", "type": "RoutesReady" } ], - "latest_created_revision_name": "sereact-00001-rsq", + "latest_created_revision_name": "sereact-00004-45z", "latest_ready_revision_name": "sereact-00001-rsq", - "observed_generation": 1, + "observed_generation": 4, "traffic": [ { "latest_revision": true, @@ -204,7 +204,7 @@ }, "generation": 0, "labels": { - "run.googleapis.com/startupProbeType": "Default" + "run.googleapis.com/startupProbeType": "Custom" }, "name": "", "namespace": "", @@ -218,9 +218,34 @@ "container_concurrency": 80, "containers": [ { - "args": null, - "command": null, + "args": [], + "command": [], "env": [ + { + "name": "CORS_EXPOSE_HEADERS", + "value": "Content-Length,Content-Range", + "value_from": [] + }, + { + "name": "CORS_HEADERS", + "value": "Content-Type,Authorization,X-Requested-With", + "value_from": [] + }, + { + "name": "CORS_MAX_AGE", + "value": "3600", + "value_from": [] + }, + { + "name": "CORS_METHODS", + "value": "GET,POST,PUT,DELETE,OPTIONS", + "value_from": [] + }, + { + "name": "CORS_ORIGINS", + "value": "['*']", + "value_from": [] + }, { "name": "FIRESTORE_DATABASE_NAME", "value": "sereact-imagedb", @@ -251,11 +276,21 @@ "value": "34.71.6.1", "value_from": [] }, + { + "name": "QDRANT_HTTPS", + "value": "false", + "value_from": [] + }, { "name": "QDRANT_PORT", "value": "6333", "value_from": [] }, + { + "name": "QDRANT_PREFER_GRPC", + "value": "false", + "value_from": [] + }, { "name": "VECTOR_DB_ENVIRONMENT", "value": "gcp-starter", @@ -284,29 +319,31 @@ "cpu": "1", "memory": "1Gi" }, - "requests": null + "requests": {} } ], "startup_probe": [ { - "failure_threshold": 1, + "failure_threshold": 6, "grpc": [], - "http_get": [], - "initial_delay_seconds": 0, - "period_seconds": 240, - "tcp_socket": [ + "http_get": [ { + "http_headers": [], + "path": "/health", "port": 8000 } ], - "timeout_seconds": 240 + "initial_delay_seconds": 30, + "period_seconds": 10, + "tcp_socket": [], + "timeout_seconds": 5 } ], "volume_mounts": [], "working_dir": "" } ], - "node_selector": null, + "node_selector": {}, "service_account_name": "761163285547-compute@developer.gserviceaccount.com", "serving_state": "", "timeout_seconds": 300, @@ -407,13 +444,6 @@ } ] }, - { - "mode": "managed", - "type": "google_compute_address", - "name": "vector_db_static_ip", - "provider": "provider[\"registry.terraform.io/hashicorp/google\"]", - "instances": [] - }, { "mode": "managed", "type": "google_compute_firewall", @@ -620,6 +650,12 @@ "zone": "us-central1-a" }, "sensitive_attributes": [ + [ + { + "type": "get_attr", + "value": "metadata_startup_script" + } + ], [ { "type": "get_attr", @@ -653,12 +689,6 @@ "type": "get_attr", "value": "disk_encryption_key_rsa" } - ], - [ - { - "type": "get_attr", - "value": "metadata_startup_script" - } ] ], "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjoxMjAwMDAwMDAwMDAwLCJkZWxldGUiOjEyMDAwMDAwMDAwMDAsInVwZGF0ZSI6MTIwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiNiJ9", @@ -686,8 +716,8 @@ "database_edition": "STANDARD", "delete_protection_state": "DELETE_PROTECTION_DISABLED", "deletion_policy": "ABANDON", - "earliest_version_time": "2025-05-24T17:16:21.411338Z", - "etag": "IM/W7rHbvI0DMLa2lbPIvI0D", + "earliest_version_time": "2025-05-24T19:50:39.508594Z", + "etag": "ILqLvPD9vI0DMLa2lbPIvI0D", "id": "projects/gen-lang-client-0424120530/databases/sereact-imagedb", "key_prefix": "", "location_id": "us-central1", @@ -701,7 +731,10 @@ "version_retention_period": "3600s" }, "sensitive_attributes": [], - "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjoxMjAwMDAwMDAwMDAwLCJkZWxldGUiOjEyMDAwMDAwMDAwMDAsInVwZGF0ZSI6MTIwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMCJ9" + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjoxMjAwMDAwMDAwMDAwLCJkZWxldGUiOjEyMDAwMDAwMDAwMDAsInVwZGF0ZSI6MTIwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMCJ9", + "dependencies": [ + "google_project_service.services" + ] } ] }, diff --git a/deployment/terraform/terraform.tfstate.backup b/deployment/terraform/terraform.tfstate.backup index 0f12ea7..c7d47b1 100644 --- a/deployment/terraform/terraform.tfstate.backup +++ b/deployment/terraform/terraform.tfstate.backup @@ -1,7 +1,7 @@ { "version": 4, "terraform_version": "1.10.1", - "serial": 259, + "serial": 268, "lineage": "a183cd95-f987-8698-c6dd-84e933c394a5", "outputs": { "cloud_run_qdrant_host": { @@ -41,6 +41,10 @@ } ] }, + "firestore_database_id": { + "value": "projects/gen-lang-client-0424120530/databases/sereact-imagedb", + "type": "string" + }, "pubsub_dlq_topic_name": { "value": "image-processing-topic-dlq", "type": "string" @@ -132,7 +136,7 @@ "effective_annotations": { "run.googleapis.com/ingress": "all", "run.googleapis.com/ingress-status": "all", - "run.googleapis.com/operation-id": "5c612fa4-f8c2-4757-88a1-c0c2fe67d0ad", + "run.googleapis.com/operation-id": "25c8aa08-3810-4d26-82c7-5fa64300fdf9", "run.googleapis.com/urls": "[\"https://sereact-761163285547.us-central1.run.app\",\"https://sereact-p64zpdtkta-uc.a.run.app\"]", "serving.knative.dev/creator": "johnpccd3@gmail.com", "serving.knative.dev/lastModifier": "johnpccd3@gmail.com" @@ -141,10 +145,10 @@ "cloud.googleapis.com/location": "us-central1", "goog-terraform-provisioned": "true" }, - "generation": 1, - "labels": null, + "generation": 3, + "labels": {}, "namespace": "gen-lang-client-0424120530", - "resource_version": "AAY15bM0evU", + "resource_version": "AAY15dY3+48", "self_link": "/apis/serving.knative.dev/v1/namespaces/761163285547/services/sereact", "terraform_labels": { "goog-terraform-provisioned": "true" @@ -158,27 +162,27 @@ { "conditions": [ { - "message": "", - "reason": "", - "status": "True", + "message": "Revision 'sereact-00003-2fs' is not ready and cannot serve traffic. The user-provided container failed the configured startup probe checks. Logs for this revision might contain more information.\n\nLogs URL: https://console.cloud.google.com/logs/viewer?project=gen-lang-client-0424120530\u0026resource=cloud_run_revision/service_name/sereact/revision_name/sereact-00003-2fs\u0026advancedFilter=resource.type%3D%22cloud_run_revision%22%0Aresource.labels.service_name%3D%22sereact%22%0Aresource.labels.revision_name%3D%22sereact-00003-2fs%22 \nFor more troubleshooting guidance, see https://cloud.google.com/run/docs/troubleshooting#container-failed-to-start", + "reason": "HealthCheckContainerError", + "status": "False", "type": "Ready" }, { - "message": "", + "message": "The user-provided container failed the configured startup probe checks. Logs for this revision might contain more information.\n\nLogs URL: https://console.cloud.google.com/logs/viewer?project=gen-lang-client-0424120530\u0026resource=cloud_run_revision/service_name/sereact/revision_name/sereact-00003-2fs\u0026advancedFilter=resource.type%3D%22cloud_run_revision%22%0Aresource.labels.service_name%3D%22sereact%22%0Aresource.labels.revision_name%3D%22sereact-00003-2fs%22 \nFor more troubleshooting guidance, see https://cloud.google.com/run/docs/troubleshooting#container-failed-to-start", "reason": "", "status": "True", "type": "ConfigurationsReady" }, { - "message": "", - "reason": "", - "status": "True", + "message": "Revision 'sereact-00003-2fs' is not ready and cannot serve traffic. The user-provided container failed the configured startup probe checks. Logs for this revision might contain more information.\n\nLogs URL: https://console.cloud.google.com/logs/viewer?project=gen-lang-client-0424120530\u0026resource=cloud_run_revision/service_name/sereact/revision_name/sereact-00003-2fs\u0026advancedFilter=resource.type%3D%22cloud_run_revision%22%0Aresource.labels.service_name%3D%22sereact%22%0Aresource.labels.revision_name%3D%22sereact-00003-2fs%22 \nFor more troubleshooting guidance, see https://cloud.google.com/run/docs/troubleshooting#container-failed-to-start", + "reason": "HealthCheckContainerError", + "status": "False", "type": "RoutesReady" } ], - "latest_created_revision_name": "sereact-00001-rsq", + "latest_created_revision_name": "sereact-00003-2fs", "latest_ready_revision_name": "sereact-00001-rsq", - "observed_generation": 1, + "observed_generation": 3, "traffic": [ { "latest_revision": true, @@ -200,7 +204,7 @@ }, "generation": 0, "labels": { - "run.googleapis.com/startupProbeType": "Default" + "run.googleapis.com/startupProbeType": "Custom" }, "name": "", "namespace": "", @@ -214,9 +218,34 @@ "container_concurrency": 80, "containers": [ { - "args": null, - "command": null, + "args": [], + "command": [], "env": [ + { + "name": "CORS_EXPOSE_HEADERS", + "value": "Content-Length,Content-Range", + "value_from": [] + }, + { + "name": "CORS_HEADERS", + "value": "Content-Type,Authorization,X-Requested-With", + "value_from": [] + }, + { + "name": "CORS_MAX_AGE", + "value": "3600", + "value_from": [] + }, + { + "name": "CORS_METHODS", + "value": "GET,POST,PUT,DELETE,OPTIONS", + "value_from": [] + }, + { + "name": "CORS_ORIGINS", + "value": "['*']", + "value_from": [] + }, { "name": "FIRESTORE_DATABASE_NAME", "value": "sereact-imagedb", @@ -247,11 +276,21 @@ "value": "34.71.6.1", "value_from": [] }, + { + "name": "QDRANT_HTTPS", + "value": "false", + "value_from": [] + }, { "name": "QDRANT_PORT", "value": "6333", "value_from": [] }, + { + "name": "QDRANT_PREFER_GRPC", + "value": "false", + "value_from": [] + }, { "name": "VECTOR_DB_ENVIRONMENT", "value": "gcp-starter", @@ -280,29 +319,31 @@ "cpu": "1", "memory": "1Gi" }, - "requests": null + "requests": {} } ], "startup_probe": [ { - "failure_threshold": 1, + "failure_threshold": 6, "grpc": [], - "http_get": [], - "initial_delay_seconds": 0, - "period_seconds": 240, - "tcp_socket": [ + "http_get": [ { + "http_headers": [], + "path": "/health", "port": 8000 } ], - "timeout_seconds": 240 + "initial_delay_seconds": 30, + "period_seconds": 10, + "tcp_socket": [], + "timeout_seconds": 5 } ], "volume_mounts": [], "working_dir": "" } ], - "node_selector": null, + "node_selector": {}, "service_account_name": "761163285547-compute@developer.gserviceaccount.com", "serving_state": "", "timeout_seconds": 300, @@ -666,6 +707,44 @@ } ] }, + { + "mode": "managed", + "type": "google_firestore_database", + "name": "database", + "provider": "provider[\"registry.terraform.io/hashicorp/google\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "app_engine_integration_mode": "DISABLED", + "cmek_config": [], + "concurrency_mode": "PESSIMISTIC", + "create_time": "", + "database_edition": "STANDARD", + "delete_protection_state": "DELETE_PROTECTION_DISABLED", + "deletion_policy": "ABANDON", + "earliest_version_time": "2025-05-24T19:50:07.050995Z", + "etag": "IKWD/+D9vI0DMLa2lbPIvI0D", + "id": "projects/gen-lang-client-0424120530/databases/sereact-imagedb", + "key_prefix": "", + "location_id": "us-central1", + "name": "sereact-imagedb", + "point_in_time_recovery_enablement": "POINT_IN_TIME_RECOVERY_DISABLED", + "project": "gen-lang-client-0424120530", + "timeouts": null, + "type": "FIRESTORE_NATIVE", + "uid": "30634721-c8c7-4673-b605-c1a91dff3eab", + "update_time": "", + "version_retention_period": "3600s" + }, + "sensitive_attributes": [], + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjoxMjAwMDAwMDAwMDAwLCJkZWxldGUiOjEyMDAwMDAwMDAwMDAsInVwZGF0ZSI6MTIwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMCJ9", + "dependencies": [ + "google_project_service.services" + ] + } + ] + }, { "mode": "managed", "type": "google_project_service", diff --git a/main.py b/main.py index 18feb08..4eca656 100644 --- a/main.py +++ b/main.py @@ -68,8 +68,10 @@ app.add_middleware( CORSMiddleware, allow_origins=settings.CORS_ORIGINS, allow_credentials=True, - allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"], - allow_headers=["Content-Type", "Authorization", "X-API-Key"], + allow_methods=settings.CORS_METHODS, + allow_headers=settings.CORS_HEADERS, + expose_headers=settings.CORS_EXPOSE_HEADERS, + max_age=settings.CORS_MAX_AGE, ) # Include API routers diff --git a/src/config/config.py b/src/config/config.py index a866a54..d9342a8 100644 --- a/src/config/config.py +++ b/src/config/config.py @@ -12,14 +12,62 @@ class Settings(BaseSettings): ENVIRONMENT: str = "development" # CORS settings - CORS_ORIGINS: List[str] = ["http://localhost:3000", "http://localhost:8000", "http://127.0.0.1:8000", "http://127.0.0.1:3000", "https://localhost:3000", "https://localhost:8000"] + CORS_ORIGINS: List[str] = ["*"] + CORS_METHODS: List[str] = ["GET", "POST", "PUT", "DELETE", "OPTIONS"] + CORS_HEADERS: List[str] = ["Content-Type", "Authorization", "X-Requested-With"] + CORS_EXPOSE_HEADERS: List[str] = ["Content-Length", "Content-Range"] + CORS_MAX_AGE: int = 3600 @field_validator("CORS_ORIGINS", mode="before") def assemble_cors_origins(cls, v): + # Read from environment variable first + env_origins = os.getenv("CORS_ORIGINS") + if env_origins: + if env_origins.startswith("[") and env_origins.endswith("]"): + # Handle list format like "['*']" + import ast + try: + return ast.literal_eval(env_origins) + except: + pass + # Handle comma-separated format + return [i.strip().strip("'\"") for i in env_origins.split(",")] + + # Fallback to default if no env var if isinstance(v, str) and not v.startswith("["): return [i.strip() for i in v.split(",")] + elif not v: # If empty list, use defaults + return ["http://localhost:3000", "http://localhost:8000", "http://127.0.0.1:8000", "http://127.0.0.1:3000", "https://localhost:3000", "https://localhost:8000"] return v + @field_validator("CORS_METHODS", mode="before") + def assemble_cors_methods(cls, v): + env_methods = os.getenv("CORS_METHODS") + if env_methods: + return [i.strip() for i in env_methods.split(",")] + return v or ["GET", "POST", "PUT", "DELETE", "OPTIONS"] + + @field_validator("CORS_HEADERS", mode="before") + def assemble_cors_headers(cls, v): + env_headers = os.getenv("CORS_HEADERS") + if env_headers: + return [i.strip() for i in env_headers.split(",")] + return v or ["Content-Type", "Authorization", "X-Requested-With"] + + @field_validator("CORS_EXPOSE_HEADERS", mode="before") + def assemble_cors_expose_headers(cls, v): + env_expose = os.getenv("CORS_EXPOSE_HEADERS") + if env_expose: + return [i.strip() for i in env_expose.split(",")] + return v or ["Content-Length", "Content-Range"] + + @field_validator("CORS_MAX_AGE", mode="before") + def assemble_cors_max_age(cls, v): + env_max_age = os.getenv("CORS_MAX_AGE") + if env_max_age: + return int(env_max_age) + return v or 3600 + # Firestore settings FIRESTORE_PROJECT_ID: str = os.getenv("FIRESTORE_PROJECT_ID", "") FIRESTORE_DATABASE_NAME: str = os.getenv("FIRESTORE_DATABASE_NAME", "sereact-db")