Skip to content
Closed
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
7 changes: 1 addition & 6 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,15 @@

# This would be set to the production domain with an env var on deployment

# used by Traefik to transmit traffic and aqcuire TLS certificates

DOMAIN=localhost

# To test the local Traefik config

# DOMAIN=localhost.tiangolo.com

# Environment: "development", "testing", "staging", "production"

ENVIRONMENT=development

PROJECT_NAME="Kaapi"
STACK_NAME=Kaapi
API_VERSION=0.5.0

#Backend
SECRET_KEY=changethis
Expand Down
20 changes: 20 additions & 0 deletions .github/ISSUE_TEMPLATE/enhancement_request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
name: Enhancement request
about: Suggest an improvement to existing codebase
title: ''
labels: ''
assignees: ''

---

**Describe the current behavior**
A clear description of how it currently works and what the limitations are.

**Describe the enhancement you'd like**
A clear and concise description of the improvement you want to see.

**Why is this enhancement needed?**
Explain the benefits (e.g., performance, usability, maintainability, scalability).

**Additional context**
Add any other context, metrics, screenshots, or examples about the enhancement here.
2 changes: 1 addition & 1 deletion .github/workflows/cd-production.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Deploy AI Platform to ECS Production
name: Deploy Kaapi to ECS Production

on:
push:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/cd-staging.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Deploy AI Platform to ECS
name: Deploy Kaapi to ECS

on:
push:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/continuous_integration.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: AI Platform CI
name: Kaapi CI

on:
push:
Expand All @@ -22,7 +22,7 @@ jobs:

strategy:
matrix:
python-version: ["3.11.7"]
python-version: ["3.12"]
redis-version: [6]

steps:
Expand Down
51 changes: 49 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ fastapi run --reload app/main.py
# Run pre-commit hooks
uv run pre-commit run --all-files

# Generate database migration
alembic revision --autogenerate -m 'Description'
# Generate database migration (rev-id should be latest existing revision ID + 1)
alembic revision --autogenerate -m "Description" --rev-id 040

# Seed database with test data
uv run python -m app.seed_data.seed_data
Expand Down Expand Up @@ -90,3 +90,50 @@ The application uses different environment files:

- Python 3.11+ with type hints
- Pre-commit hooks for linting and formatting

## Coding Conventions

### Type Hints

Always add type hints to all function parameters and return values.

### Logging Format

Prefix all log messages with the function name in square brackets.

```python
logger.info(f"[function_name] Message {mask_string(sensitive_value)}")
```

### Database Column Comments

Use sa_column_kwargs["comment"] to describe database columns, especially when the purpose isn’t obvious. This helps non-developers understand column purposes directly from the database schema:

```python
field_name: int = Field(
foreign_key="table.id",
nullable=False,
ondelete="CASCADE",
sa_column_kwargs={"comment": "What this column represents"}
)
```

Prioritize comments for:
- Columns with non-obvious purposes
- Status/type fields (document valid values)
- JSON/metadata columns (describe expected structure)
- Foreign keys (clarify the relationship)

### Endpoint Documentation

Load Swagger descriptions from external markdown files instead of inline strings:

```python
@router.post(
"/endpoint",
description=load_description("domain/action.md"),
response_model=APIResponse[ResponseModel],
)
```

Store documentation files in `backend/app/api/docs/<domain>/<action>.md`
23 changes: 14 additions & 9 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# Contributing to Tech4Dev AI Platform
# Contributing to Kaapi

Thank you for considering contributing to **Tech4Dev AI Platform**! We welcome contributions of all kinds, including bug reports, feature requests, documentation improvements, and code contributions.
Thank you for considering contributing to **Kaapi**! We welcome contributions of all kinds, including bug reports, feature requests, documentation improvements, and code contributions.

---

## 📌 Getting Started
To contribute successfully, you must first set up the project on your local machine. Please follow the instructions outlined in the project's README to configure the repository and begin your contributions.

Before you proceed, **make sure to check the repository's [README](https://github.com/ProjectTech4DevAI/ai-platform/blob/main/backend/README.md) for a comprehensive overview of the project's backend and detailed setup guidelines.**
Before you proceed, **make sure to check the repository's [README](https://github.com/ProjectTech4DevAI/kaapi-backend/blob/main/backend/README.md) for a comprehensive overview of the project's backend and detailed setup guidelines.**

---

Expand All @@ -17,10 +17,18 @@ Before you proceed, **make sure to check the repository's [README](https://githu
1. Click the **Fork** button on the top right of this repository.
2. Clone your forked repository:
```
git clone https://github.com/{username}/ai-platform.git
cd ai-platform
git clone https://github.com/{username}/kaapi-backend.git
cd kaapi-backend
```

### Check for Existing Issues
Before you start working on a contribution:
1. **Check if an issue exists** for the bug or feature you want to work on in the [Issues](https://github.com/ProjectTech4DevAI/kaapi-backend/issues) section.
2. **If no issue exists**, create one first using the templates present:
- For bugs: Use the bug report template
- For enhancements: Use the enhancement request template
- For features: Create a feature request issue

### Create a Branch
• Always work in a new branch based on main.
• For branch name, follow this convention: ``type/one-line-description``
Expand All @@ -30,12 +38,9 @@ cd ai-platform
- enhancement
- bugfix
- feature


```
git checkout -b type/one-line-description
```

### Make and Test Changes
1. Adhere to the project's established coding style for consistency.
2. Make sure the code adheres to best practices.
Expand All @@ -60,7 +65,7 @@ git commit -m "one liner for the commit"
• For PR name, follow this convention:
``Module Name: One liner of changes``

• Don't forget to link the PR to the issue if you are solving one.
• Don't forget to link the PR to the issue.

• Push your changes to GitHub:
```
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# AI Platform
# Kaapi

[![License: AGPL v3](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0)
![](https://github.com/ProjectTech4DevAI/ai-platform/workflows/Continuous%20Integration/badge.svg)
[![Code coverage badge](https://img.shields.io/codecov/c/github/ProjectTech4DevAI/ai-platform/staging.svg)](https://codecov.io/gh/ProjectTech4DevAI/ai-platform/branch/staging)
![GitHub issues](https://img.shields.io/github/issues-raw/ProjectTech4DevAI/ai-platform)
[![codebeat badge](https://codebeat.co/badges/dd951390-5f51-4c98-bddc-0b618bdb43fd)](https://codebeat.co/projects/github-com-ProjectTech4DevAI/ai-platform-staging)
[![Commits](https://img.shields.io/github/commit-activity/m/ProjectTech4DevAI/ai-platform)](https://img.shields.io/github/commit-activity/m/ProjectTech4DevAI/ai-platform)
![](https://github.com/ProjectTech4DevAI/kaapi-backend/workflows/Continuous%20Integration/badge.svg)
[![Code coverage badge](https://img.shields.io/codecov/c/github/ProjectTech4DevAI/kaapi-backend/staging.svg)](https://codecov.io/gh/ProjectTech4DevAI/kaapi-backend/branch/staging)
![GitHub issues](https://img.shields.io/github/issues-raw/ProjectTech4DevAI/kaapi-backend)
[![codebeat badge](https://codebeat.co/badges/dd951390-5f51-4c98-bddc-0b618bdb43fd)](https://codebeat.co/projects/github-com-ProjectTech4DevAI/kaapi-backend-staging)
[![Commits](https://img.shields.io/github/commit-activity/m/ProjectTech4DevAI/kaapi-backend)](https://img.shields.io/github/commit-activity/m/ProjectTech4DevAI/kaapi-backend)

## Pre-requisites

Expand Down
30 changes: 24 additions & 6 deletions backend/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# FastAPI Project - Backend
# Kaapi - Backend

## Requirements

Expand Down Expand Up @@ -27,13 +27,15 @@ $ source .venv/bin/activate

Make sure your editor is using the correct Python virtual environment, with the interpreter at `backend/.venv/bin/python`.

Modify or add SQLModel models for data and SQL tables in `./backend/app/models.py`, API endpoints in `./backend/app/api/`, CRUD (Create, Read, Update, Delete) utils in `./backend/app/crud.py`.
Modify or add SQLModel models for data and SQL tables in `./backend/app/models/`, API endpoints in `./backend/app/api/`, CRUD (Create, Read, Update, Delete) utils in `./backend/app/crud/`.

## VS Code
## Seed Database (Optional)

There are already configurations in place to run the backend through the VS Code debugger, so that you can use breakpoints, pause and explore variables, etc.
For local development, seed the database with baseline data:

The setup is also already configured so you can run the tests through the VS Code Python tests tab.
```console
$ uv run python -m app.seed_data.seed_data
```

## Docker Compose Override

Expand Down Expand Up @@ -91,6 +93,22 @@ Nevertheless, if it doesn't detect a change but a syntax error, it will just sto

## Backend tests

### Setup Test Environment

Before running tests, create a test environment configuration:

1. Copy the test environment template:
```console
$ cp .env.test.example .env.test
```

2. Update `.env.test` with test-specific settings:
- Set `ENVIRONMENT=testing`
- Configure a separate test database (recommended). Using a separate
database prevents tests from affecting your development data.

### Run Tests

To test the backend run:

```console
Expand Down Expand Up @@ -133,7 +151,7 @@ Make sure you create a "revision" of your models and that you "upgrade" your dat
$ docker compose exec backend bash
```

* Alembic is already configured to import your SQLModel models from `./backend/app/models.py`.
* Alembic is already configured to import your SQLModel models from `./backend/app/models/`.

* After changing a model (for example, adding a column), inside the container, create a revision, e.g.:

Expand Down
109 changes: 0 additions & 109 deletions backend/app/api/deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
AuthContext,
TokenPayload,
User,
UserOrganization,
UserProjectOrg,
)


Expand All @@ -37,113 +35,6 @@ def get_db() -> Generator[Session, None, None]:
TokenDep = Annotated[str, Depends(reusable_oauth2)]


def get_current_user(
session: SessionDep,
token: TokenDep,
api_key: Annotated[str, Depends(api_key_header)],
) -> User:
"""Authenticate user via API Key first, fallback to JWT token. Returns only User."""

if api_key:
api_key_record = api_key_manager.verify(session, api_key)
if not api_key_record:
raise HTTPException(status_code=401, detail="Invalid API Key")

if not api_key_record.user.is_active:
raise HTTPException(status_code=403, detail="Inactive user")

return api_key_record.user # Return only User object

elif token:
try:
payload = jwt.decode(
token, settings.SECRET_KEY, algorithms=[security.ALGORITHM]
)
token_data = TokenPayload(**payload)
except (InvalidTokenError, ValidationError):
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Could not validate credentials",
)

user = session.get(User, token_data.sub)
if not user:
raise HTTPException(status_code=404, detail="User not found")
if not user.is_active:
raise HTTPException(status_code=403, detail="Inactive user")

return user # Return only User object

raise HTTPException(status_code=401, detail="Invalid Authorization format")


CurrentUser = Annotated[User, Depends(get_current_user)]


def get_current_user_org(
current_user: CurrentUser, session: SessionDep, request: Request
) -> UserOrganization:
"""Extend `User` with organization_id if available, otherwise return UserOrganization without it."""

organization_id = None
api_key = request.headers.get("X-API-KEY")
if api_key:
api_key_record = api_key_manager.verify(session, api_key)
if api_key_record:
validate_organization(session, api_key_record.organization.id)
organization_id = api_key_record.organization.id

return UserOrganization(
**current_user.model_dump(), organization_id=organization_id
)


CurrentUserOrg = Annotated[UserOrganization, Depends(get_current_user_org)]


def get_current_user_org_project(
current_user: CurrentUser, session: SessionDep, request: Request
) -> UserProjectOrg:
api_key = request.headers.get("X-API-KEY")
organization_id = None
project_id = None

if api_key:
api_key_record = api_key_manager.verify(session, api_key)
if api_key_record:
validate_organization(session, api_key_record.organization.id)
organization_id = api_key_record.organization.id
project_id = api_key_record.project.id

else:
raise HTTPException(status_code=401, detail="Invalid API Key")

return UserProjectOrg(
**current_user.model_dump(),
organization_id=organization_id,
project_id=project_id,
)


CurrentUserOrgProject = Annotated[UserProjectOrg, Depends(get_current_user_org_project)]


def get_current_active_superuser(current_user: CurrentUser) -> User:
if not current_user.is_superuser:
raise HTTPException(
status_code=403, detail="The user doesn't have enough privileges"
)
return current_user


def get_current_active_superuser_org(current_user: CurrentUserOrg) -> User:
if not current_user.is_superuser:
raise HTTPException(
status_code=403, detail="The user doesn't have enough privileges"
)
return current_user


def get_auth_context(
session: SessionDep,
token: TokenDep,
Expand Down
3 changes: 3 additions & 0 deletions backend/app/api/docs/api_keys/create.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Create a new API key for programmatic access to the platform.

The raw API key is returned **only once during creation**. Store it securely as it cannot be retrieved again. Only the key prefix will be visible in subsequent requests for security reasons.
3 changes: 3 additions & 0 deletions backend/app/api/docs/api_keys/delete.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Delete an API key by its ID.

Permanently revokes the API key. Any requests using this key will fail immediately after deletion.
Loading