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
36 changes: 36 additions & 0 deletions .github/workflows/backend-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,43 @@ jobs:
- name: Lint with flake8
run: flake8 . --count --max-complexity=10 --max-line-length=120 --statistics

- name: Create test environment file
run: |
mkdir -p /tmp
echo "DATABASE_URL=postgresql://postgres:postgres@localhost:5432/test_db" > .env.test
echo "DATABASE_TEST_URL=postgresql://postgres:postgres@localhost:5432/test_db" >> .env.test
echo "SECRET_KEY=test-secret-key-for-ci" >> .env.test
echo "AUTH0_DOMAIN=example.auth0.com" >> .env.test
echo "AUTH0_CLIENT_ID=test-client-id" >> .env.test
echo "AUTH0_CLIENT_SECRET=test-client-secret" >> .env.test
echo "AUTH0_AUDIENCE=https://api.example.com" >> .env.test
echo "OPENAI_API_KEY=sk-test-key" >> .env.test

- name: Check environment variables
env:
CI: "true"
TESTING: "True"
DATABASE_URL: "postgresql://postgres:postgres@localhost:5432/test_db"
DATABASE_TEST_URL: "postgresql://postgres:postgres@localhost:5432/test_db"
SECRET_KEY: "test-secret-key-for-ci"
AUTH0_DOMAIN: "example.auth0.com"
AUTH0_CLIENT_ID: "test-client-id"
AUTH0_CLIENT_SECRET: "test-client-secret"
AUTH0_AUDIENCE: "https://api.example.com"
OPENAI_API_KEY: "sk-test-key"
run: python scripts/check_env.py --env-file .env.test --no-exit

- name: Run tests with pytest
env:
TESTING: "True"
DATABASE_URL: "postgresql://postgres:postgres@localhost:5432/test_db"
DATABASE_TEST_URL: "postgresql://postgres:postgres@localhost:5432/test_db"
SECRET_KEY: "test-secret-key-for-ci-environment"
AUTH0_DOMAIN: "example.auth0.com"
AUTH0_CLIENT_ID: "test-client-id"
AUTH0_CLIENT_SECRET: "test-client-secret"
AUTH0_AUDIENCE: "https://api.example.com"
OPENAI_API_KEY: "sk-test-key"
run: pytest --cov=app --cov-report=xml

- name: Upload coverage to Codecov
Expand Down
21 changes: 21 additions & 0 deletions .github/workflows/frontend-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,28 @@ jobs:
- name: Type check
run: npm run typecheck || npm run check:types || npm run tsc

- name: Create test environment file
run: |
mkdir -p /tmp
echo "VITE_API_URL=http://localhost:8000/api/v1" > .env.test
echo "VITE_AUTH0_DOMAIN=example.auth0.com" >> .env.test
echo "VITE_AUTH0_CLIENT_ID=test-client-id" >> .env.test
echo "VITE_AUTH0_AUDIENCE=https://api.example.com" >> .env.test

- name: Install dotenv for env checking
run: npm install dotenv --no-save

- name: Check environment variables
run: node scripts/check-env.js .env.test

- name: Build
env:
CI: "true"
VITE_API_URL: "http://localhost:8000/api/v1"
VITE_AUTH0_DOMAIN: "example.auth0.com"
VITE_AUTH0_CLIENT_ID: "test-client-id"
VITE_AUTH0_AUDIENCE: "https://api.example.com"
VITE_DEV_MODE: "false"
run: npm run build

- name: Test
Expand Down
40 changes: 36 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# TeamInsight - Contribution Analytics Platform
# Toban Contribution Viewer

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.

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

### Prerequisites

- Python 3.8+
- Node.js 16+
- PostgreSQL
- Python 3.12+
- Node.js 18+
- PostgreSQL 13+
- API keys for:
- Slack
- GitHub
Expand Down Expand Up @@ -104,6 +104,38 @@ TeamInsight is an AI-powered analytics platform designed to extract, analyze, an
npm run dev
```

## Environment Variables Management

The project uses a structured approach to environment variables management to ensure proper configuration across environments.

### Backend Environment Variables

Backend environment variables are managed through:

1. **Configuration Definition**: All environment variables are defined in `app/config.py` using Pydantic for validation
2. **Environment Validation**: The application validates required variables at startup and logs warnings if any are missing
3. **Testing Utility**: A utility (`app/core/env_test.py`) is provided to check environment configurations
4. **Command-line Verification**: The `scripts/check_env.py` script can be used to verify environment variables before deployment

Required backend environment variables:
- `DATABASE_URL`: PostgreSQL connection string
- `SECRET_KEY`: Application secret key for security
- `AUTH0_DOMAIN`, `AUTH0_CLIENT_ID`, `AUTH0_CLIENT_SECRET`, `AUTH0_AUDIENCE`: Auth0 authentication settings
- `OPENAI_API_KEY`: For AI-powered analysis

### Frontend Environment Variables

Frontend environment variables are managed through:

1. **Centralized Configuration**: All environment variables are accessed through the `src/config/env.ts` module
2. **Validation at Runtime**: The application validates required variables during initialization
3. **Build-time Verification**: The `npm run check-env` script verifies environment variables during build
4. **Typed Access**: Strongly-typed access to environment variables with proper error handling

Required frontend environment variables:
- `VITE_API_URL`: URL to the backend API
- `VITE_AUTH0_DOMAIN`, `VITE_AUTH0_CLIENT_ID`, `VITE_AUTH0_AUDIENCE`: Auth0 authentication settings

## Contributing

Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests.
Expand Down
55 changes: 54 additions & 1 deletion backend/app/config.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,72 @@
import os
from functools import lru_cache
from typing import Any, List, Optional

from pydantic import PostgresDsn, SecretStr, validator
from pydantic_settings import BaseSettings


class Settings(BaseSettings):
# API Settings
PROJECT_NAME: str = "Toban Contribution Viewer API"
PROJECT_DESCRIPTION: str = "API for tracking and visualizing contributions across various platforms"
PROJECT_VERSION: str = "0.1.0"

API_PREFIX: str = "/api/v1"
DEBUG: bool = False
ALLOWED_HOSTS: List[str] = ["localhost", "127.0.0.1"]

# Secret Keys
SECRET_KEY: str

# Database Settings
DATABASE_URL: PostgresDsn
DATABASE_TEST_URL: Optional[PostgresDsn] = None

# Authentication Settings
AUTH0_DOMAIN: str
AUTH0_CLIENT_ID: str
AUTH0_CLIENT_SECRET: SecretStr
AUTH0_AUDIENCE: str

# Third-Party API Keys
OPENAI_API_KEY: SecretStr
SLACK_CLIENT_ID: Optional[str] = None
SLACK_CLIENT_SECRET: Optional[SecretStr] = None
SLACK_SIGNING_SECRET: Optional[SecretStr] = None
GITHUB_CLIENT_ID: Optional[str] = None
GITHUB_CLIENT_SECRET: Optional[SecretStr] = None
NOTION_API_KEY: Optional[SecretStr] = None

# Feature Flags
ENABLE_SLACK_INTEGRATION: bool = True
ENABLE_GITHUB_INTEGRATION: bool = True
ENABLE_NOTION_INTEGRATION: bool = True

# Logging
LOG_LEVEL: str = "INFO"

# Validators
@validator("DATABASE_URL", pre=True)
def validate_database_url(cls, v: Optional[str]) -> Any:
if os.environ.get("TESTING") == "True":
# Use test database during testing
test_url = os.environ.get("DATABASE_TEST_URL")
return test_url if test_url else v
return v

class Config:
env_file = ".env"
case_sensitive = True
env_file_encoding = "utf-8"


@lru_cache()
def get_settings() -> Settings:
"""
Get application settings from environment variables.

Using lru_cache to avoid reloading settings for each request.
"""
return Settings()


Expand Down
1 change: 1 addition & 0 deletions backend/app/core/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Core package initialization
90 changes: 90 additions & 0 deletions backend/app/core/env_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
"""
Utility for testing environment variable configurations.
This module helps verify that all required environment variables are present
and properly formatted before the application starts.
"""

import os
import sys
from typing import Dict, List, Optional

from pydantic import ValidationError

from app.config import Settings


def test_env_vars(env_file: Optional[str] = None) -> Dict[str, List[str]]:
"""
Test environment variables configuration.

Args:
env_file: Optional path to an environment file to test.

Returns:
Dict with 'missing' and 'invalid' lists of environment variables.
"""
result = {
"missing": [],
"invalid": [],
}

# If env_file is provided, read environment variables from it
if env_file and os.path.exists(env_file):
env_vars = {}
with open(env_file, "r") as f:
for line in f:
line = line.strip()
if not line or line.startswith("#"):
continue
key, value = line.split("=", 1)
env_vars[key] = value
else:
env_vars = os.environ.copy()

# Try to create settings from environment variables
try:
Settings(**env_vars)
except ValidationError as e:
for error in e.errors():
field = error["loc"][0]
if "missing" in error["msg"]:
result["missing"].append(field)
else:
result["invalid"].append(field)

return result


def check_env(env_file: Optional[str] = None, exit_on_error: bool = True) -> bool:
"""
Check environment variables and optionally exit if any are missing or invalid.

Args:
env_file: Optional path to an environment file to test.
exit_on_error: Whether to exit the application if any variables are missing/invalid.

Returns:
True if all environment variables are valid, False otherwise.
"""
result = test_env_vars(env_file)

if result["missing"] or result["invalid"]:
print("Environment variable configuration errors:")

if result["missing"]:
print("Missing variables:")
for var in result["missing"]:
print(f" - {var}")

if result["invalid"]:
print("Invalid variables:")
for var in result["invalid"]:
print(f" - {var}")

if exit_on_error:
print("Exiting due to environment configuration errors.")
sys.exit(1)

return False

return True
35 changes: 34 additions & 1 deletion backend/app/main.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,47 @@
import logging

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

from app.config import settings
from app.core.env_test import check_env

# Configure logging
logging.basicConfig(
level=getattr(logging, settings.LOG_LEVEL),
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
logger = logging.getLogger(__name__)

# Check environment variables on startup
if not check_env(exit_on_error=False):
logger.warning("Application started with environment configuration issues")

# Create FastAPI application
app = FastAPI(
title=settings.PROJECT_NAME,
description=settings.PROJECT_DESCRIPTION,
version=settings.PROJECT_VERSION,
docs_url="/docs" if settings.DEBUG else None,
redoc_url="/redoc" if settings.DEBUG else None,
openapi_url="/openapi.json" if settings.DEBUG else None,
)

# Configure CORS
app.add_middleware(
CORSMiddleware,
allow_origins=[str(origin) for origin in settings.ALLOWED_HOSTS],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)

# Root endpoint
@app.get("/")
async def root():
return {"message": "Welcome to Toban Contribution Viewer API"}
return {"message": "Welcome to Toban Contribution Viewer API"}

# Health check endpoint
@app.get("/health")
async def health_check():
return {"status": "ok"}
1 change: 1 addition & 0 deletions backend/scripts/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Scripts package initialization
Loading