diff --git a/.github/workflows/backend-ci.yml b/.github/workflows/backend-ci.yml index 72ad4a3..c4282b8 100644 --- a/.github/workflows/backend-ci.yml +++ b/.github/workflows/backend-ci.yml @@ -1,5 +1,5 @@ # Copyright (c) 2025 CityLens Contributors -# Licensed under the MIT License +# Licensed under the GNU General Public License v3.0 (GPL-3.0) name: Backend CI diff --git a/.github/workflows/mobile-ci.yml b/.github/workflows/mobile-ci.yml index 7cc7df0..1c5c117 100644 --- a/.github/workflows/mobile-ci.yml +++ b/.github/workflows/mobile-ci.yml @@ -1,5 +1,5 @@ # Copyright (c) 2025 CityLens Contributors -# Licensed under the MIT License +# Licensed under the GNU General Public License v3.0 (GPL-3.0) name: Mobile App CI @@ -43,7 +43,7 @@ jobs: run: flutter build apk --release - name: Upload APK - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: citylens-app path: ./mobile-app/build/app/outputs/flutter-apk/app-release.apk diff --git a/.github/workflows/web-ci.yml b/.github/workflows/web-ci.yml index 9cb426b..7552324 100644 --- a/.github/workflows/web-ci.yml +++ b/.github/workflows/web-ci.yml @@ -1,5 +1,5 @@ # Copyright (c) 2025 CityLens Contributors -# Licensed under the MIT License +# Licensed under the GNU General Public License v3.0 (GPL-3.0) name: Web Dashboard CI @@ -40,7 +40,7 @@ jobs: run: npm run build - name: Upload build artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: web-dashboard-build path: ./web-dashboard/dist diff --git a/backend/app/core/config.py b/backend/app/core/config.py index eaaf0b9..d7efbdf 100644 --- a/backend/app/core/config.py +++ b/backend/app/core/config.py @@ -1,12 +1,13 @@ # Copyright (c) 2025 CityLens Contributors # Licensed under the GNU General Public License v3.0 (GPL-3.0) +import json from typing import List, Union, Optional -from pydantic import AnyHttpUrl, validator, ConfigDict -from pydantic_settings import BaseSettings +from pydantic import AnyHttpUrl, field_validator +from pydantic_settings import BaseSettings, SettingsConfigDict class Settings(BaseSettings): - model_config = ConfigDict( + model_config = SettingsConfigDict( case_sensitive=True, env_file=".env", extra='ignore' # Ignore extra fields from .env @@ -24,6 +25,23 @@ class Settings(BaseSettings): # CORS BACKEND_CORS_ORIGINS: List[Union[str, AnyHttpUrl]] = ["http://localhost:3000", "http://localhost:8000", "*"] + + @field_validator('BACKEND_CORS_ORIGINS', mode='before') + @classmethod + def parse_cors_origins(cls, v): + """Allow comma-separated string or JSON array for CORS origins""" + if v is None or v == "": + return [] + if isinstance(v, str): + # If user provides JSON array string, try to parse + if v.strip().startswith('['): + try: + return json.loads(v) + except json.JSONDecodeError: + pass + # Fallback: comma-separated list + return [origin.strip() for origin in v.split(',') if origin.strip()] + return v # Database (PostgreSQL with PostGIS) POSTGRES_SERVER: str = "db" @@ -54,6 +72,7 @@ class Settings(BaseSettings): OPENWEATHER_API_KEY: Optional[str] = None TOMTOM_API_KEY: Optional[str] = None AQICN_API_KEY: Optional[str] = None # WAQI API token from https://aqicn.org/api/ + GEMINI_API_KEY: Optional[str] = None # Google Gemini API key for AI chat @property def REDIS_URL(self) -> str: diff --git a/docker-compose.yml b/docker-compose.yml index 54293ee..6343ab3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -85,6 +85,7 @@ services: env_file: - ./backend/.env environment: + - BACKEND_CORS_ORIGINS=["*"] - POSTGRES_SERVER=postgres - POSTGRES_USER=citylens - POSTGRES_PASSWORD=citylens_password