Skip to content

Commit 9b730e3

Browse files
authored
Merge pull request #5 from hackdays-io/feature/env-management
Configure environment variables management
2 parents 3d7ee9f + 7a7a2fc commit 9b730e3

File tree

13 files changed

+555
-17
lines changed

13 files changed

+555
-17
lines changed

.github/workflows/backend-ci.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,43 @@ jobs:
4141
- name: Lint with flake8
4242
run: flake8 . --count --max-complexity=10 --max-line-length=120 --statistics
4343

44+
- name: Create test environment file
45+
run: |
46+
mkdir -p /tmp
47+
echo "DATABASE_URL=postgresql://postgres:postgres@localhost:5432/test_db" > .env.test
48+
echo "DATABASE_TEST_URL=postgresql://postgres:postgres@localhost:5432/test_db" >> .env.test
49+
echo "SECRET_KEY=test-secret-key-for-ci" >> .env.test
50+
echo "AUTH0_DOMAIN=example.auth0.com" >> .env.test
51+
echo "AUTH0_CLIENT_ID=test-client-id" >> .env.test
52+
echo "AUTH0_CLIENT_SECRET=test-client-secret" >> .env.test
53+
echo "AUTH0_AUDIENCE=https://api.example.com" >> .env.test
54+
echo "OPENAI_API_KEY=sk-test-key" >> .env.test
55+
56+
- name: Check environment variables
57+
env:
58+
CI: "true"
59+
TESTING: "True"
60+
DATABASE_URL: "postgresql://postgres:postgres@localhost:5432/test_db"
61+
DATABASE_TEST_URL: "postgresql://postgres:postgres@localhost:5432/test_db"
62+
SECRET_KEY: "test-secret-key-for-ci"
63+
AUTH0_DOMAIN: "example.auth0.com"
64+
AUTH0_CLIENT_ID: "test-client-id"
65+
AUTH0_CLIENT_SECRET: "test-client-secret"
66+
AUTH0_AUDIENCE: "https://api.example.com"
67+
OPENAI_API_KEY: "sk-test-key"
68+
run: python scripts/check_env.py --env-file .env.test --no-exit
69+
4470
- name: Run tests with pytest
71+
env:
72+
TESTING: "True"
73+
DATABASE_URL: "postgresql://postgres:postgres@localhost:5432/test_db"
74+
DATABASE_TEST_URL: "postgresql://postgres:postgres@localhost:5432/test_db"
75+
SECRET_KEY: "test-secret-key-for-ci-environment"
76+
AUTH0_DOMAIN: "example.auth0.com"
77+
AUTH0_CLIENT_ID: "test-client-id"
78+
AUTH0_CLIENT_SECRET: "test-client-secret"
79+
AUTH0_AUDIENCE: "https://api.example.com"
80+
OPENAI_API_KEY: "sk-test-key"
4581
run: pytest --cov=app --cov-report=xml
4682

4783
- name: Upload coverage to Codecov

.github/workflows/frontend-ci.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,28 @@ jobs:
3939
- name: Type check
4040
run: npm run typecheck || npm run check:types || npm run tsc
4141

42+
- name: Create test environment file
43+
run: |
44+
mkdir -p /tmp
45+
echo "VITE_API_URL=http://localhost:8000/api/v1" > .env.test
46+
echo "VITE_AUTH0_DOMAIN=example.auth0.com" >> .env.test
47+
echo "VITE_AUTH0_CLIENT_ID=test-client-id" >> .env.test
48+
echo "VITE_AUTH0_AUDIENCE=https://api.example.com" >> .env.test
49+
50+
- name: Install dotenv for env checking
51+
run: npm install dotenv --no-save
52+
53+
- name: Check environment variables
54+
run: node scripts/check-env.js .env.test
55+
4256
- name: Build
57+
env:
58+
CI: "true"
59+
VITE_API_URL: "http://localhost:8000/api/v1"
60+
VITE_AUTH0_DOMAIN: "example.auth0.com"
61+
VITE_AUTH0_CLIENT_ID: "test-client-id"
62+
VITE_AUTH0_AUDIENCE: "https://api.example.com"
63+
VITE_DEV_MODE: "false"
4364
run: npm run build
4465

4566
- name: Test

README.md

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# TeamInsight - Contribution Analytics Platform
1+
# Toban Contribution Viewer
22

33
TeamInsight is an AI-powered analytics platform designed to extract, analyze, and visualize team contributions across digital workspaces. The platform connects to Slack, GitHub, and Notion via their APIs to collect activity data, processes it using AI to identify meaningful contributions, and presents actionable insights through an intuitive dashboard.
44

@@ -43,9 +43,9 @@ TeamInsight is an AI-powered analytics platform designed to extract, analyze, an
4343

4444
### Prerequisites
4545

46-
- Python 3.8+
47-
- Node.js 16+
48-
- PostgreSQL
46+
- Python 3.12+
47+
- Node.js 18+
48+
- PostgreSQL 13+
4949
- API keys for:
5050
- Slack
5151
- GitHub
@@ -104,6 +104,38 @@ TeamInsight is an AI-powered analytics platform designed to extract, analyze, an
104104
npm run dev
105105
```
106106

107+
## Environment Variables Management
108+
109+
The project uses a structured approach to environment variables management to ensure proper configuration across environments.
110+
111+
### Backend Environment Variables
112+
113+
Backend environment variables are managed through:
114+
115+
1. **Configuration Definition**: All environment variables are defined in `app/config.py` using Pydantic for validation
116+
2. **Environment Validation**: The application validates required variables at startup and logs warnings if any are missing
117+
3. **Testing Utility**: A utility (`app/core/env_test.py`) is provided to check environment configurations
118+
4. **Command-line Verification**: The `scripts/check_env.py` script can be used to verify environment variables before deployment
119+
120+
Required backend environment variables:
121+
- `DATABASE_URL`: PostgreSQL connection string
122+
- `SECRET_KEY`: Application secret key for security
123+
- `AUTH0_DOMAIN`, `AUTH0_CLIENT_ID`, `AUTH0_CLIENT_SECRET`, `AUTH0_AUDIENCE`: Auth0 authentication settings
124+
- `OPENAI_API_KEY`: For AI-powered analysis
125+
126+
### Frontend Environment Variables
127+
128+
Frontend environment variables are managed through:
129+
130+
1. **Centralized Configuration**: All environment variables are accessed through the `src/config/env.ts` module
131+
2. **Validation at Runtime**: The application validates required variables during initialization
132+
3. **Build-time Verification**: The `npm run check-env` script verifies environment variables during build
133+
4. **Typed Access**: Strongly-typed access to environment variables with proper error handling
134+
135+
Required frontend environment variables:
136+
- `VITE_API_URL`: URL to the backend API
137+
- `VITE_AUTH0_DOMAIN`, `VITE_AUTH0_CLIENT_ID`, `VITE_AUTH0_AUDIENCE`: Auth0 authentication settings
138+
107139
## Contributing
108140

109141
Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests.

backend/app/config.py

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,72 @@
1+
import os
12
from functools import lru_cache
3+
from typing import Any, List, Optional
24

5+
from pydantic import PostgresDsn, SecretStr, validator
36
from pydantic_settings import BaseSettings
47

58

69
class Settings(BaseSettings):
10+
# API Settings
711
PROJECT_NAME: str = "Toban Contribution Viewer API"
812
PROJECT_DESCRIPTION: str = "API for tracking and visualizing contributions across various platforms"
913
PROJECT_VERSION: str = "0.1.0"
10-
14+
API_PREFIX: str = "/api/v1"
15+
DEBUG: bool = False
16+
ALLOWED_HOSTS: List[str] = ["localhost", "127.0.0.1"]
17+
18+
# Secret Keys
19+
SECRET_KEY: str
20+
21+
# Database Settings
22+
DATABASE_URL: PostgresDsn
23+
DATABASE_TEST_URL: Optional[PostgresDsn] = None
24+
25+
# Authentication Settings
26+
AUTH0_DOMAIN: str
27+
AUTH0_CLIENT_ID: str
28+
AUTH0_CLIENT_SECRET: SecretStr
29+
AUTH0_AUDIENCE: str
30+
31+
# Third-Party API Keys
32+
OPENAI_API_KEY: SecretStr
33+
SLACK_CLIENT_ID: Optional[str] = None
34+
SLACK_CLIENT_SECRET: Optional[SecretStr] = None
35+
SLACK_SIGNING_SECRET: Optional[SecretStr] = None
36+
GITHUB_CLIENT_ID: Optional[str] = None
37+
GITHUB_CLIENT_SECRET: Optional[SecretStr] = None
38+
NOTION_API_KEY: Optional[SecretStr] = None
39+
40+
# Feature Flags
41+
ENABLE_SLACK_INTEGRATION: bool = True
42+
ENABLE_GITHUB_INTEGRATION: bool = True
43+
ENABLE_NOTION_INTEGRATION: bool = True
44+
45+
# Logging
46+
LOG_LEVEL: str = "INFO"
47+
48+
# Validators
49+
@validator("DATABASE_URL", pre=True)
50+
def validate_database_url(cls, v: Optional[str]) -> Any:
51+
if os.environ.get("TESTING") == "True":
52+
# Use test database during testing
53+
test_url = os.environ.get("DATABASE_TEST_URL")
54+
return test_url if test_url else v
55+
return v
56+
1157
class Config:
1258
env_file = ".env"
59+
case_sensitive = True
60+
env_file_encoding = "utf-8"
1361

1462

1563
@lru_cache()
1664
def get_settings() -> Settings:
65+
"""
66+
Get application settings from environment variables.
67+
68+
Using lru_cache to avoid reloading settings for each request.
69+
"""
1770
return Settings()
1871

1972

backend/app/core/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Core package initialization

backend/app/core/env_test.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
"""
2+
Utility for testing environment variable configurations.
3+
This module helps verify that all required environment variables are present
4+
and properly formatted before the application starts.
5+
"""
6+
7+
import os
8+
import sys
9+
from typing import Dict, List, Optional
10+
11+
from pydantic import ValidationError
12+
13+
from app.config import Settings
14+
15+
16+
def test_env_vars(env_file: Optional[str] = None) -> Dict[str, List[str]]:
17+
"""
18+
Test environment variables configuration.
19+
20+
Args:
21+
env_file: Optional path to an environment file to test.
22+
23+
Returns:
24+
Dict with 'missing' and 'invalid' lists of environment variables.
25+
"""
26+
result = {
27+
"missing": [],
28+
"invalid": [],
29+
}
30+
31+
# If env_file is provided, read environment variables from it
32+
if env_file and os.path.exists(env_file):
33+
env_vars = {}
34+
with open(env_file, "r") as f:
35+
for line in f:
36+
line = line.strip()
37+
if not line or line.startswith("#"):
38+
continue
39+
key, value = line.split("=", 1)
40+
env_vars[key] = value
41+
else:
42+
env_vars = os.environ.copy()
43+
44+
# Try to create settings from environment variables
45+
try:
46+
Settings(**env_vars)
47+
except ValidationError as e:
48+
for error in e.errors():
49+
field = error["loc"][0]
50+
if "missing" in error["msg"]:
51+
result["missing"].append(field)
52+
else:
53+
result["invalid"].append(field)
54+
55+
return result
56+
57+
58+
def check_env(env_file: Optional[str] = None, exit_on_error: bool = True) -> bool:
59+
"""
60+
Check environment variables and optionally exit if any are missing or invalid.
61+
62+
Args:
63+
env_file: Optional path to an environment file to test.
64+
exit_on_error: Whether to exit the application if any variables are missing/invalid.
65+
66+
Returns:
67+
True if all environment variables are valid, False otherwise.
68+
"""
69+
result = test_env_vars(env_file)
70+
71+
if result["missing"] or result["invalid"]:
72+
print("Environment variable configuration errors:")
73+
74+
if result["missing"]:
75+
print("Missing variables:")
76+
for var in result["missing"]:
77+
print(f" - {var}")
78+
79+
if result["invalid"]:
80+
print("Invalid variables:")
81+
for var in result["invalid"]:
82+
print(f" - {var}")
83+
84+
if exit_on_error:
85+
print("Exiting due to environment configuration errors.")
86+
sys.exit(1)
87+
88+
return False
89+
90+
return True

backend/app/main.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,47 @@
1+
import logging
2+
13
from fastapi import FastAPI
4+
from fastapi.middleware.cors import CORSMiddleware
25

36
from app.config import settings
7+
from app.core.env_test import check_env
8+
9+
# Configure logging
10+
logging.basicConfig(
11+
level=getattr(logging, settings.LOG_LEVEL),
12+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
13+
)
14+
logger = logging.getLogger(__name__)
415

16+
# Check environment variables on startup
17+
if not check_env(exit_on_error=False):
18+
logger.warning("Application started with environment configuration issues")
19+
20+
# Create FastAPI application
521
app = FastAPI(
622
title=settings.PROJECT_NAME,
723
description=settings.PROJECT_DESCRIPTION,
824
version=settings.PROJECT_VERSION,
25+
docs_url="/docs" if settings.DEBUG else None,
26+
redoc_url="/redoc" if settings.DEBUG else None,
27+
openapi_url="/openapi.json" if settings.DEBUG else None,
928
)
1029

30+
# Configure CORS
31+
app.add_middleware(
32+
CORSMiddleware,
33+
allow_origins=[str(origin) for origin in settings.ALLOWED_HOSTS],
34+
allow_credentials=True,
35+
allow_methods=["*"],
36+
allow_headers=["*"],
37+
)
1138

39+
# Root endpoint
1240
@app.get("/")
1341
async def root():
14-
return {"message": "Welcome to Toban Contribution Viewer API"}
42+
return {"message": "Welcome to Toban Contribution Viewer API"}
43+
44+
# Health check endpoint
45+
@app.get("/health")
46+
async def health_check():
47+
return {"status": "ok"}

backend/scripts/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Scripts package initialization

0 commit comments

Comments
 (0)