forgeapi 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. fastapi_forge/__init__.py +15 -0
  2. fastapi_forge/cli.py +125 -0
  3. fastapi_forge/commands/__init__.py +5 -0
  4. fastapi_forge/commands/create.py +107 -0
  5. fastapi_forge/generator.py +300 -0
  6. fastapi_forge/models.py +187 -0
  7. fastapi_forge/prompts.py +364 -0
  8. fastapi_forge/templates/base/.dockerignore.jinja +20 -0
  9. fastapi_forge/templates/base/.github/workflows/ci.yml.jinja +155 -0
  10. fastapi_forge/templates/base/.gitignore.jinja +68 -0
  11. fastapi_forge/templates/base/Dockerfile.jinja +69 -0
  12. fastapi_forge/templates/base/README.md.jinja +141 -0
  13. fastapi_forge/templates/base/alembic/README.md.jinja +43 -0
  14. fastapi_forge/templates/base/alembic/env.py.jinja +93 -0
  15. fastapi_forge/templates/base/alembic/script.py.mako.jinja +26 -0
  16. fastapi_forge/templates/base/alembic/versions/.gitkeep.jinja +1 -0
  17. fastapi_forge/templates/base/alembic.ini.jinja +70 -0
  18. fastapi_forge/templates/base/app/__init__.py.jinja +5 -0
  19. fastapi_forge/templates/base/app/api/__init__.py.jinja +3 -0
  20. fastapi_forge/templates/base/app/api/auth_api.py.jinja +72 -0
  21. fastapi_forge/templates/base/app/api/health_api.py.jinja +41 -0
  22. fastapi_forge/templates/base/app/config/__init__.py.jinja +31 -0
  23. fastapi_forge/templates/base/app/config/base.py.jinja +52 -0
  24. fastapi_forge/templates/base/app/config/env.py.jinja +75 -0
  25. fastapi_forge/templates/base/app/core/__init__.py.jinja +3 -0
  26. fastapi_forge/templates/base/app/core/auth.py.jinja +96 -0
  27. fastapi_forge/templates/base/app/core/config.py.jinja +56 -0
  28. fastapi_forge/templates/base/app/core/database.py.jinja +68 -0
  29. fastapi_forge/templates/base/app/core/deps.py.jinja +55 -0
  30. fastapi_forge/templates/base/app/core/redis.py.jinja +41 -0
  31. fastapi_forge/templates/base/app/daos/__init__.py.jinja +3 -0
  32. fastapi_forge/templates/base/app/exceptions/__init__.py.jinja +7 -0
  33. fastapi_forge/templates/base/app/exceptions/exception.py.jinja +34 -0
  34. fastapi_forge/templates/base/app/exceptions/handler.py.jinja +56 -0
  35. fastapi_forge/templates/base/app/main.py.jinja +24 -0
  36. fastapi_forge/templates/base/app/models/__init__.py.jinja +7 -0
  37. fastapi_forge/templates/base/app/models/base.py.jinja +13 -0
  38. fastapi_forge/templates/base/app/schemas/__init__.py.jinja +3 -0
  39. fastapi_forge/templates/base/app/schemas/api_schema.py.jinja +20 -0
  40. fastapi_forge/templates/base/app/schemas/auth_schema.py.jinja +21 -0
  41. fastapi_forge/templates/base/app/server.py.jinja +99 -0
  42. fastapi_forge/templates/base/app/services/__init__.py.jinja +3 -0
  43. fastapi_forge/templates/base/app/utils/__init__.py.jinja +3 -0
  44. fastapi_forge/templates/base/app/utils/log.py.jinja +21 -0
  45. fastapi_forge/templates/base/config.yaml.jinja +71 -0
  46. fastapi_forge/templates/base/docker-compose.yml.jinja +117 -0
  47. fastapi_forge/templates/base/pyproject.toml.jinja +86 -0
  48. fastapi_forge/templates/base/pytest.ini.jinja +10 -0
  49. fastapi_forge/templates/base/ruff.toml.jinja +33 -0
  50. fastapi_forge/templates/base/tests/__init__.py.jinja +3 -0
  51. fastapi_forge/templates/base/tests/conftest.py.jinja +31 -0
  52. fastapi_forge/templates/base/tests/test_health.py.jinja +24 -0
  53. forgeapi-0.1.0.dist-info/METADATA +182 -0
  54. forgeapi-0.1.0.dist-info/RECORD +57 -0
  55. forgeapi-0.1.0.dist-info/WHEEL +4 -0
  56. forgeapi-0.1.0.dist-info/entry_points.txt +2 -0
  57. forgeapi-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,24 @@
1
+ """
2
+ Application Entry Point
3
+
4
+ This module provides the main entry point for running the application.
5
+ """
6
+
7
+ import uvicorn
8
+
9
+ from app.core.config import get_config
10
+
11
+
12
+ def main():
13
+ """Run the application with uvicorn."""
14
+ config = get_config()
15
+ uvicorn.run(
16
+ config.app.uvicorn,
17
+ host=config.app.host,
18
+ port=config.app.port,
19
+ reload=config.app.reload,
20
+ )
21
+
22
+
23
+ if __name__ == "__main__":
24
+ main()
@@ -0,0 +1,7 @@
1
+ """
2
+ SQLAlchemy Models Module
3
+ """
4
+
5
+ from app.models.base import Base
6
+
7
+ __all__ = ["Base"]
@@ -0,0 +1,13 @@
1
+ """
2
+ Base Model
3
+
4
+ This module defines the SQLAlchemy declarative base for all models.
5
+ """
6
+
7
+ from sqlalchemy.orm import DeclarativeBase
8
+
9
+
10
+ class Base(DeclarativeBase):
11
+ """SQLAlchemy declarative base class."""
12
+
13
+ pass
@@ -0,0 +1,3 @@
1
+ """
2
+ Pydantic Schemas Module
3
+ """
@@ -0,0 +1,20 @@
1
+ """
2
+ API Schemas
3
+
4
+ This module defines common API response schemas.
5
+ """
6
+
7
+ from pydantic import BaseModel, Field
8
+
9
+
10
+ class MessageResponse(BaseModel):
11
+ """Generic message response schema."""
12
+
13
+ message: str = Field(..., description="Response message")
14
+
15
+
16
+ class HealthResponse(BaseModel):
17
+ """Health check response schema."""
18
+
19
+ status: str = Field(..., description="Health status")
20
+ version: str = Field(..., description="Application version")
@@ -0,0 +1,21 @@
1
+ """
2
+ Authentication Schemas
3
+
4
+ This module defines Pydantic schemas for authentication.
5
+ """
6
+
7
+ from pydantic import BaseModel, Field
8
+
9
+
10
+ class TokenResponse(BaseModel):
11
+ """JWT token response schema."""
12
+
13
+ access_token: str = Field(..., description="JWT access token")
14
+ token_type: str = Field(default="bearer", description="Token type")
15
+
16
+
17
+ class LoginRequest(BaseModel):
18
+ """Login request schema."""
19
+
20
+ email: str = Field(..., description="User email")
21
+ password: str = Field(..., description="User password")
@@ -0,0 +1,99 @@
1
+ """
2
+ FastAPI Application Server
3
+
4
+ This module configures and creates the FastAPI application instance.
5
+ """
6
+
7
+ from contextlib import asynccontextmanager
8
+
9
+ from fastapi import FastAPI
10
+
11
+ from app.api.health_api import healthAPI
12
+ {% if use_auth %}
13
+ from app.api.auth_api import authAPI
14
+ {% endif %}
15
+ from app.core.config import get_config
16
+ {% if use_database %}
17
+ from app.core.database import DatabaseUtil
18
+ {% endif %}
19
+ {% if use_redis %}
20
+ from app.core.redis import RedisUtil
21
+ {% endif %}
22
+ from app.exceptions.handler import register_exception_handlers
23
+ from app.utils.log import log
24
+
25
+ config = get_config()
26
+
27
+
28
+ async def init_services(app: FastAPI):
29
+ """
30
+ Initialize application services.
31
+
32
+ Args:
33
+ app: FastAPI application instance
34
+ """
35
+ app.state.config = config
36
+ {% if use_database %}
37
+ app.state.db_engine = await DatabaseUtil.init_engine(config.db)
38
+ {% endif %}
39
+ {% if use_redis %}
40
+ app.state.redis = await RedisUtil.init_redis(config.redis)
41
+ {% endif %}
42
+
43
+
44
+ async def close_services(app: FastAPI):
45
+ """
46
+ Close application services.
47
+
48
+ Args:
49
+ app: FastAPI application instance
50
+ """
51
+ {% if use_database %}
52
+ await DatabaseUtil.close_engine(app.state.db_engine)
53
+ {% endif %}
54
+ {% if use_redis %}
55
+ await RedisUtil.close_redis(app.state.redis)
56
+ {% endif %}
57
+ pass
58
+
59
+
60
+ @asynccontextmanager
61
+ async def lifespan(app: FastAPI):
62
+ """
63
+ Application lifespan manager.
64
+
65
+ Args:
66
+ app: FastAPI application instance
67
+ """
68
+ # Startup
69
+ await init_services(app)
70
+ log.info(f"{config.app.name} started successfully")
71
+
72
+ yield
73
+
74
+ # Shutdown
75
+ await close_services(app)
76
+ log.info(f"{config.app.name} shutdown complete")
77
+
78
+
79
+ # Create FastAPI application
80
+ app = FastAPI(
81
+ title=config.app.name,
82
+ description=config.app.description,
83
+ version=config.app.version,
84
+ lifespan=lifespan,
85
+ )
86
+
87
+ # Register exception handlers
88
+ register_exception_handlers(app)
89
+
90
+ # Register routers
91
+ API_ROUTES = [
92
+ {"router": healthAPI, "tags": ["Health"]},
93
+ {% if use_auth %}
94
+ {"router": authAPI, "tags": ["Authentication"]},
95
+ {% endif %}
96
+ ]
97
+
98
+ for route in API_ROUTES:
99
+ app.include_router(router=route["router"], tags=route["tags"])
@@ -0,0 +1,3 @@
1
+ """
2
+ Services Module
3
+ """
@@ -0,0 +1,3 @@
1
+ """
2
+ Utilities Module
3
+ """
@@ -0,0 +1,21 @@
1
+ """
2
+ Logging Utilities
3
+
4
+ This module provides logging configuration and utilities.
5
+ """
6
+
7
+ import logging
8
+ import sys
9
+
10
+ # Configure logging
11
+ logging.basicConfig(
12
+ level=logging.INFO,
13
+ format="%(asctime)s - %(levelname)s - %(message)s",
14
+ datefmt="%Y-%m-%d %H:%M:%S",
15
+ handlers=[
16
+ logging.StreamHandler(sys.stdout),
17
+ ],
18
+ )
19
+
20
+ # Create logger instance
21
+ log = logging.getLogger("{{ project_slug }}")
@@ -0,0 +1,71 @@
1
+ env: dev
2
+
3
+ # Development environment
4
+ dev:
5
+ app:
6
+ name: {{ project_name }} (dev)
7
+ description: {{ project_description }}
8
+ api: /api
9
+ host: 0.0.0.0
10
+ port: 8000
11
+ uvicorn: app.server:app
12
+ version: 0.1.0
13
+ reload: true
14
+
15
+ {% if use_database %}
16
+ db:
17
+ {% if use_postgres %}
18
+ host: localhost
19
+ port: 5432
20
+ user: postgres
21
+ password: postgres
22
+ database: {{ project_slug }}
23
+ driver_name: postgresql+asyncpg
24
+ {% elif use_mysql %}
25
+ host: localhost
26
+ port: 3306
27
+ user: root
28
+ password: root
29
+ database: {{ project_slug }}
30
+ driver_name: mysql+aiomysql
31
+ {% elif use_sqlite %}
32
+ host: ""
33
+ port: 0
34
+ user: ""
35
+ password: ""
36
+ database: {{ project_slug }}.db
37
+ driver_name: sqlite+aiosqlite
38
+ {% endif %}
39
+ echo: true
40
+ pool_size: 5
41
+ max_overflow: 10
42
+ pool_recycle: 3600
43
+ pool_timeout: 30
44
+ {% endif %}
45
+
46
+ {% if use_redis %}
47
+ redis:
48
+ host: localhost
49
+ port: 6379
50
+ db: 0
51
+ password: ""
52
+ {% endif %}
53
+
54
+ {% if use_auth %}
55
+ jwt:
56
+ secret_key: your-secret-key-change-in-production
57
+ algorithm: HS256
58
+ expire_minutes: 1440
59
+ {% endif %}
60
+
61
+ # Production environment
62
+ prod:
63
+ app:
64
+ name: {{ project_name }}
65
+ reload: false
66
+
67
+ {% if use_database %}
68
+ db:
69
+ echo: false
70
+ pool_size: 20
71
+ {% endif %}
@@ -0,0 +1,117 @@
1
+ services:
2
+ app:
3
+ build:
4
+ context: .
5
+ dockerfile: Dockerfile
6
+ ports:
7
+ - "8000:8000"
8
+ environment:
9
+ - PYTHONPATH=/app
10
+ {% if use_database or use_redis %}
11
+ depends_on:
12
+ {% if "postgres" in docker_services %}
13
+ - postgres
14
+ {% endif %}
15
+ {% if "mysql" in docker_services %}
16
+ - mysql
17
+ {% endif %}
18
+ {% if "redis" in docker_services %}
19
+ - redis
20
+ {% endif %}
21
+ {% endif %}
22
+ volumes:
23
+ - .:/app
24
+ command: python -m app.main
25
+ {% if use_database or use_redis %}
26
+ networks:
27
+ - app-network
28
+ {% endif %}
29
+
30
+ {% if "postgres" in docker_services %}
31
+ postgres:
32
+ image: postgres:16-alpine
33
+ environment:
34
+ POSTGRES_USER: postgres
35
+ POSTGRES_PASSWORD: postgres
36
+ POSTGRES_DB: {{ project_slug }}
37
+ ports:
38
+ - "5432:5432"
39
+ volumes:
40
+ - postgres_data:/var/lib/postgresql/data
41
+ networks:
42
+ - app-network
43
+ {% endif %}
44
+
45
+ {% if "mysql" in docker_services %}
46
+ mysql:
47
+ image: mysql:8
48
+ environment:
49
+ MYSQL_ROOT_PASSWORD: root
50
+ MYSQL_DATABASE: {{ project_slug }}
51
+ ports:
52
+ - "3306:3306"
53
+ volumes:
54
+ - mysql_data:/var/lib/mysql
55
+ networks:
56
+ - app-network
57
+ {% endif %}
58
+
59
+ {% if "redis" in docker_services %}
60
+ redis:
61
+ image: redis:alpine
62
+ ports:
63
+ - "6379:6379"
64
+ networks:
65
+ - app-network
66
+ {% endif %}
67
+
68
+ {% if "rabbitmq" in docker_services %}
69
+ rabbitmq:
70
+ image: rabbitmq:3-management
71
+ ports:
72
+ - "5672:5672"
73
+ - "15672:15672"
74
+ environment:
75
+ RABBITMQ_DEFAULT_USER: admin
76
+ RABBITMQ_DEFAULT_PASS: admin
77
+ volumes:
78
+ - rabbitmq_data:/var/lib/rabbitmq
79
+ networks:
80
+ - app-network
81
+ {% endif %}
82
+
83
+ {% if "minio" in docker_services %}
84
+ minio:
85
+ image: minio/minio
86
+ command: server /data --console-address ":9001"
87
+ ports:
88
+ - "9000:9000"
89
+ - "9001:9001"
90
+ environment:
91
+ MINIO_ROOT_USER: minioadmin
92
+ MINIO_ROOT_PASSWORD: minioadmin
93
+ volumes:
94
+ - minio_data:/data
95
+ networks:
96
+ - app-network
97
+ {% endif %}
98
+
99
+ {% if use_database or use_redis %}
100
+ volumes:
101
+ {% if "postgres" in docker_services %}
102
+ postgres_data:
103
+ {% endif %}
104
+ {% if "mysql" in docker_services %}
105
+ mysql_data:
106
+ {% endif %}
107
+ {% if "rabbitmq" in docker_services %}
108
+ rabbitmq_data:
109
+ {% endif %}
110
+ {% if "minio" in docker_services %}
111
+ minio_data:
112
+ {% endif %}
113
+
114
+ networks:
115
+ app-network:
116
+ driver: bridge
117
+ {% endif %}
@@ -0,0 +1,86 @@
1
+ [project]
2
+ name = "{{ project_slug }}"
3
+ version = "0.1.0"
4
+ description = "{{ project_description }}"
5
+ authors = [
6
+ { name = "{{ author_name }}", email = "{{ author_email }}" }
7
+ ]
8
+ readme = "README.md"
9
+ requires-python = ">={{ python_version }}"
10
+
11
+ dependencies = [
12
+ "fastapi[standard]>=0.115.0",
13
+ "uvicorn>=0.34.0",
14
+ "pyyaml>=6.0.0",
15
+ "pydantic>=2.0.0",
16
+ "pydantic-settings>=2.0.0",
17
+ {% if use_database %}
18
+ "sqlalchemy[asyncio]>=2.0.0",
19
+ {% endif %}
20
+ {% if use_postgres %}
21
+ "asyncpg>=0.29.0",
22
+ "psycopg[binary]>=3.2.0",
23
+ {% elif use_mysql %}
24
+ "aiomysql>=0.2.0",
25
+ {% elif use_sqlite %}
26
+ "aiosqlite>=0.20.0",
27
+ {% endif %}
28
+ {% if use_alembic %}
29
+ "alembic>=1.15.0",
30
+ {% endif %}
31
+ {% if use_redis %}
32
+ "redis>=5.2.0",
33
+ {% endif %}
34
+ {% if use_auth %}
35
+ "pyjwt>=2.10.0",
36
+ "passlib[bcrypt]>=1.7.4",
37
+ "bcrypt==4.0.1",
38
+ {% endif %}
39
+ ]
40
+
41
+ [dependency-groups]
42
+ dev = [
43
+ "pytest>=8.3.0",
44
+ "pytest-asyncio>=0.26.0",
45
+ {% if use_ruff %}
46
+ "ruff>=0.11.0",
47
+ {% endif %}
48
+ {% if use_redis %}
49
+ "fakeredis>=2.27.0",
50
+ {% endif %}
51
+ {% if use_postgres %}
52
+ "pytest-postgresql>=4.1.0",
53
+ {% endif %}
54
+ ]
55
+
56
+ [project.scripts]
57
+ start = "app.main:main"
58
+
59
+ [build-system]
60
+ requires = ["hatchling"]
61
+ build-backend = "hatchling.build"
62
+
63
+ [tool.hatch.build.targets.wheel]
64
+ packages = ["app"]
65
+ {% if use_ruff %}
66
+
67
+ [tool.ruff]
68
+ line-length = 88
69
+ indent-width = 4
70
+ target-version = "py{{ python_version | replace('.', '') }}"
71
+
72
+ [tool.ruff.lint]
73
+ select = ["E", "W", "F", "I", "B", "C4", "UP"]
74
+ ignore = ["E501"]
75
+
76
+ [tool.ruff.format]
77
+ quote-style = "double"
78
+ indent-style = "space"
79
+ {% endif %}
80
+
81
+ [tool.pytest.ini_options]
82
+ testpaths = ["tests"]
83
+ python_files = ["test_*.py"]
84
+ python_functions = ["test_*"]
85
+ asyncio_mode = "auto"
86
+ asyncio_default_fixture_loop_scope = "function"
@@ -0,0 +1,10 @@
1
+ [pytest]
2
+ testpaths = tests
3
+ python_files = test_*.py
4
+ python_classes = Test*
5
+ python_functions = test_*
6
+ asyncio_mode = auto
7
+ addopts = -v --tb=short
8
+ filterwarnings =
9
+ ignore::DeprecationWarning
10
+ ignore::PendingDeprecationWarning
@@ -0,0 +1,33 @@
1
+ # Ruff Configuration
2
+ # https://docs.astral.sh/ruff/
3
+
4
+ line-length = 120
5
+ target-version = "py310"
6
+
7
+ [lint]
8
+ select = [
9
+ "E", # pycodestyle errors
10
+ "W", # pycodestyle warnings
11
+ "F", # pyflakes
12
+ "I", # isort
13
+ "B", # flake8-bugbear
14
+ "C4", # flake8-comprehensions
15
+ "UP", # pyupgrade
16
+ "ARG", # flake8-unused-arguments
17
+ "SIM", # flake8-simplify
18
+ ]
19
+ ignore = [
20
+ "E501", # line too long (handled by formatter)
21
+ "B008", # do not perform function calls in argument defaults
22
+ "B904", # raise without from inside except
23
+ "ARG001", # unused function argument
24
+ ]
25
+
26
+ [lint.isort]
27
+ known-first-party = ["app"]
28
+ force-single-line = false
29
+ lines-after-imports = 2
30
+
31
+ [lint.per-file-ignores]
32
+ "tests/*" = ["ARG001", "S101"]
33
+ "alembic/*" = ["ARG001"]
@@ -0,0 +1,3 @@
1
+ """
2
+ Test Suite
3
+ """
@@ -0,0 +1,31 @@
1
+ """
2
+ Pytest Configuration and Fixtures
3
+
4
+ This module provides test fixtures and configuration for pytest.
5
+ """
6
+
7
+ import pytest
8
+ from fastapi.testclient import TestClient
9
+ {% if use_redis %}
10
+ import fakeredis.aioredis
11
+ {% endif %}
12
+
13
+ from app.server import app
14
+
15
+
16
+ @pytest.fixture
17
+ def client():
18
+ """Create a test client for the FastAPI application."""
19
+ with TestClient(app) as test_client:
20
+ yield test_client
21
+
22
+
23
+ {% if use_redis %}
24
+ @pytest.fixture
25
+ async def fake_redis():
26
+ """Create a fake Redis instance for testing."""
27
+ fake_redis = fakeredis.aioredis.FakeRedis(decode_responses=True)
28
+ yield fake_redis
29
+ await fake_redis.flushall()
30
+ await fake_redis.aclose()
31
+ {% endif %}
@@ -0,0 +1,24 @@
1
+ """
2
+ Health API Tests
3
+
4
+ This module contains tests for the health check endpoints.
5
+ """
6
+
7
+
8
+ def test_health_check(client):
9
+ """Test the health check endpoint returns healthy status."""
10
+ response = client.get("/api/health")
11
+ assert response.status_code == 200
12
+
13
+ data = response.json()
14
+ assert data["status"] == "healthy"
15
+ assert "version" in data
16
+
17
+
18
+ def test_readiness_check(client):
19
+ """Test the readiness check endpoint returns ready status."""
20
+ response = client.get("/api/health/ready")
21
+ assert response.status_code == 200
22
+
23
+ data = response.json()
24
+ assert data["status"] == "ready"