fastapi-spawn 0.4.21__tar.gz → 0.4.23__tar.gz

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 (79) hide show
  1. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/PKG-INFO +8 -1
  2. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/README.md +7 -0
  3. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/__init__.py +1 -1
  4. fastapi_spawn-0.4.23/fastapi_spawn/templates/app/api/v1/health/router.py.j2 +85 -0
  5. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/api/v1/uploads/router.py.j2 +2 -1
  6. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/core/config.py.j2 +7 -1
  7. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/db/session.py.j2 +5 -0
  8. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/main.py.j2 +19 -0
  9. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/base/env_example.j2 +2 -2
  10. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/base/pyproject.toml.j2 +2 -2
  11. fastapi_spawn-0.4.23/fastapi_spawn/templates/docker/docker-compose.yml.j2 +272 -0
  12. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/pyproject.toml +1 -1
  13. fastapi_spawn-0.4.21/fastapi_spawn/templates/app/api/v1/health/router.py.j2 +0 -40
  14. fastapi_spawn-0.4.21/fastapi_spawn/templates/docker/docker-compose.yml.j2 +0 -111
  15. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/.gitignore +0 -0
  16. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/LICENSE +0 -0
  17. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/cli.py +0 -0
  18. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/config.py +0 -0
  19. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/constants.py +0 -0
  20. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/generator.py +0 -0
  21. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/interactive.py +0 -0
  22. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/alembic/alembic.ini.j2 +0 -0
  23. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/alembic/env.py.j2 +0 -0
  24. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/__init__.py.j2 +0 -0
  25. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/admin/setup.py.j2 +0 -0
  26. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/api/deps.py.j2 +0 -0
  27. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/api/graphql.py.j2 +0 -0
  28. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/api/v1/auth/router.py.j2 +0 -0
  29. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/api/v1/auth/sso.py.j2 +0 -0
  30. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/api/v1/pagination/router.py.j2 +0 -0
  31. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/api/v1/payments/router.py.j2 +0 -0
  32. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/api/v1/permissions/router.py.j2 +0 -0
  33. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/api/v1/router.py.j2 +0 -0
  34. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/api/v1/streaming/router.py.j2 +0 -0
  35. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/api/v1/ws/router.py.j2 +0 -0
  36. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/core/ai.py.j2 +0 -0
  37. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/core/cache.py.j2 +0 -0
  38. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/core/email.py.j2 +0 -0
  39. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/core/exceptions.py.j2 +0 -0
  40. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/core/logger.py.j2 +0 -0
  41. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/core/logging.py.j2 +0 -0
  42. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/core/monitoring.py.j2 +0 -0
  43. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/core/notifications.py.j2 +0 -0
  44. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/core/ocr.py.j2 +0 -0
  45. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/core/permissions.py.j2 +0 -0
  46. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/core/search.py.j2 +0 -0
  47. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/core/security.py.j2 +0 -0
  48. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/core/storage.py.j2 +0 -0
  49. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/core/vector_db.py.j2 +0 -0
  50. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/core/ws_manager.py.j2 +0 -0
  51. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/frontend/index.html.j2 +0 -0
  52. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/middleware/__init__.py.j2 +0 -0
  53. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/middleware/rate_limit.py.j2 +0 -0
  54. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/middleware/request_logger.py.j2 +0 -0
  55. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/app/middleware/response_format.py.j2 +0 -0
  56. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/base/Makefile.j2 +0 -0
  57. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/base/README.md.j2 +0 -0
  58. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/base/env.j2 +0 -0
  59. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/base/gitignore.j2 +0 -0
  60. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/base/pre_commit.j2 +0 -0
  61. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/ci/github/publish.yml.j2 +0 -0
  62. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/ci/github/tests.yml.j2 +0 -0
  63. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/ci/gitlab/gitlab-ci.yml.j2 +0 -0
  64. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/db/seed.py.j2 +0 -0
  65. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/docker/Dockerfile.j2 +0 -0
  66. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/docker/dockerignore.j2 +0 -0
  67. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/infra/docker/docker-compose.prod.yml.j2 +0 -0
  68. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/infra/helm/Chart.yaml.j2 +0 -0
  69. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/infra/helm/values.yaml.j2 +0 -0
  70. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/infra/terraform/main.tf.j2 +0 -0
  71. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/infra/terraform/variables.tf.j2 +0 -0
  72. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/root/main.py.j2 +0 -0
  73. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/tasks/arq_worker.py.j2 +0 -0
  74. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/tasks/celery_app.py.j2 +0 -0
  75. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/tasks/sample_tasks.py.j2 +0 -0
  76. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/tests/conftest.py.j2 +0 -0
  77. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/templates/tests/test_health.py.j2 +0 -0
  78. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/utils.py +0 -0
  79. {fastapi_spawn-0.4.21 → fastapi_spawn-0.4.23}/fastapi_spawn/validators.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-spawn
3
- Version: 0.4.21
3
+ Version: 0.4.23
4
4
  Summary: A powerful CLI tool to scaffold production-ready FastAPI projects with flexible database, auth, broker, and deployment options.
5
5
  Project-URL: Homepage, https://github.com/Bishwajitgarai/fastapi-spawn
6
6
  Project-URL: Documentation, https://github.com/Bishwajitgarai/fastapi-spawn#readme
@@ -587,6 +587,13 @@ $ fastapi-spawn add websockets
587
587
  | `beanie` | mongodb |
588
588
  | `none` | any |
589
589
 
590
+ ## Recent Updates (v0.4.22)
591
+
592
+ - **Ollama Integration**: Updated default model to `tinyllama` for faster testing.
593
+ - **Supabase Fixes**: Fixed missing environment variables in configuration templates.
594
+ - **Frontend & Admin Mounted**: Automatically mounts the static frontend at `/` and the `sqladmin` dashboard at `/admin` (if enabled).
595
+ - **Service Health Checks**: Added connection checks for Elasticsearch, OpenSearch, Kafka, Vespa, Ollama, DuckDB, Chroma, and Supabase in the generated `/health/services` endpoint.
596
+
590
597
  ---
591
598
 
592
599
  ## Contributing
@@ -546,6 +546,13 @@ $ fastapi-spawn add websockets
546
546
  | `beanie` | mongodb |
547
547
  | `none` | any |
548
548
 
549
+ ## Recent Updates (v0.4.22)
550
+
551
+ - **Ollama Integration**: Updated default model to `tinyllama` for faster testing.
552
+ - **Supabase Fixes**: Fixed missing environment variables in configuration templates.
553
+ - **Frontend & Admin Mounted**: Automatically mounts the static frontend at `/` and the `sqladmin` dashboard at `/admin` (if enabled).
554
+ - **Service Health Checks**: Added connection checks for Elasticsearch, OpenSearch, Kafka, Vespa, Ollama, DuckDB, Chroma, and Supabase in the generated `/health/services` endpoint.
555
+
549
556
  ---
550
557
 
551
558
  ## Contributing
@@ -1,6 +1,6 @@
1
1
  """fastapi-spawn — Production-ready FastAPI project scaffolding CLI."""
2
2
 
3
- __version__ = "0.4.21"
3
+ __version__ = "0.4.22"
4
4
  __author__ = "Bishwajit Garai"
5
5
  __email__ = "bishwajitgarai@gmail.com"
6
6
  __license__ = "MIT"
@@ -0,0 +1,85 @@
1
+ """Health check endpoints — /health, /readiness, /liveness."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import time
6
+
7
+ from fastapi import APIRouter
8
+ from pydantic import BaseModel
9
+
10
+ router = APIRouter(prefix="/health")
11
+
12
+ _start_time = time.time()
13
+
14
+
15
+ class HealthResponse(BaseModel):
16
+ status: str
17
+ uptime_seconds: float
18
+ version: str = "0.1.0"
19
+ service: str = "{{ project_name }}"
20
+
21
+
22
+ @router.get("", response_model=HealthResponse, summary="Basic health check")
23
+ async def health() -> HealthResponse:
24
+ """Returns service status and uptime."""
25
+ return HealthResponse(
26
+ status="ok",
27
+ uptime_seconds=round(time.time() - _start_time, 2),
28
+ )
29
+
30
+
31
+ @router.get("/services", summary="Check external services status")
32
+ async def check_services() -> dict:
33
+ """Checks connection to external services."""
34
+ results = {}
35
+
36
+ {% if vector_db == "elasticsearch" %}
37
+ try:
38
+ import urllib.request
39
+ from app.core.config import settings
40
+ response = urllib.request.urlopen(settings.elasticsearch_url, timeout=5)
41
+ if response.status == 200:
42
+ results["elasticsearch"] = "connected"
43
+ else:
44
+ results["elasticsearch"] = f"failed with status {response.status}"
45
+ except Exception as e:
46
+ results["elasticsearch"] = f"error: {e}"
47
+ {% endif %}
48
+
49
+ {% if vector_db == "opensearch" %}
50
+ try:
51
+ import urllib.request
52
+ from app.core.config import settings
53
+ response = urllib.request.urlopen(settings.opensearch_url, timeout=5)
54
+ if response.status == 200:
55
+ results["opensearch"] = "connected"
56
+ else:
57
+ results["opensearch"] = f"failed with status {response.status}"
58
+ except Exception as e:
59
+ results["opensearch"] = f"error: {e}"
60
+ {% endif %}
61
+
62
+ {% if broker == "kafka" %}
63
+ try:
64
+ from app.core.kafka import kafka_producer
65
+ if kafka_producer.producer:
66
+ results["kafka"] = "connected"
67
+ else:
68
+ results["kafka"] = "not_started"
69
+ except Exception as e:
70
+ results["kafka"] = f"error: {e}"
71
+ {% endif %}
72
+
73
+ return results
74
+
75
+
76
+ @router.get("/readiness", summary="Readiness probe (Kubernetes)")
77
+ async def readiness() -> dict:
78
+ """Indicates whether the service is ready to accept traffic."""
79
+ return {"status": "ready"}
80
+
81
+
82
+ @router.get("/liveness", summary="Liveness probe (Kubernetes)")
83
+ async def liveness() -> dict:
84
+ """Indicates whether the service process is alive."""
85
+ return {"status": "alive"}
@@ -2,6 +2,7 @@ from fastapi import APIRouter, UploadFile, File, HTTPException
2
2
  import shutil
3
3
  import os
4
4
  import uuid
5
+ from app.core.config import settings
5
6
 
6
7
  router = APIRouter(
7
8
  prefix="/uploads",
@@ -25,7 +26,7 @@ async def upload_file(file: UploadFile = File(...)):
25
26
  safe_filename = f"{uuid.uuid4().hex}{ext}"
26
27
 
27
28
  # Save locally as an example
28
- upload_dir = "media"
29
+ upload_dir = settings.LOCAL_STORAGE_DIR
29
30
  os.makedirs(upload_dir, exist_ok=True)
30
31
  file_path = os.path.join(upload_dir, safe_filename)
31
32
 
@@ -49,6 +49,7 @@ class Settings(BaseSettings):
49
49
  LOG_LEVEL: str = "INFO"
50
50
  LOG_DIR: str = "logs"
51
51
  LOG_BACKUP_DAYS: int = 30
52
+ LOCAL_STORAGE_DIR: str = "media"
52
53
 
53
54
  {% if db == "postgresql" %}
54
55
  # ── PostgreSQL ─────────────────────────────────────────────────────────
@@ -178,13 +179,18 @@ class Settings(BaseSettings):
178
179
  # ── Ollama (local) ─────────────────────────────────────────────────────
179
180
  OLLAMA_HOST: str = "localhost"
180
181
  OLLAMA_PORT: str = "11434"
181
- OLLAMA_MODEL: str = "llama3"
182
+ OLLAMA_MODEL: str = "tinyllama"
182
183
 
183
184
  @property
184
185
  def ollama_url(self) -> str:
185
186
  return f"http://{self.OLLAMA_HOST}:{self.OLLAMA_PORT}"
186
187
 
187
188
  {% endif %}
189
+ {% if db == "supabase" or vector_db == "supabase" %}
190
+ # ── Supabase ───────────────────────────────────────────────────────────
191
+ SUPABASE_URL: str = ""
192
+ SUPABASE_KEY: str = ""
193
+ {% endif %}
188
194
  {% if has_sentry %}
189
195
  # ── Sentry ─────────────────────────────────────────────────────────────
190
196
  SENTRY_DSN: str = ""
@@ -72,6 +72,11 @@ from beanie import init_beanie
72
72
 
73
73
  from app.core.config import settings
74
74
 
75
+ # Workaround for Beanie/Motor compatibility issue on Python 3.12
76
+ if not hasattr(motor.motor_asyncio.AsyncIOMotorClient, "append_metadata"):
77
+ def append_metadata(self, metadata):
78
+ pass
79
+ motor.motor_asyncio.AsyncIOMotorClient.append_metadata = append_metadata
75
80
 
76
81
  async def init_db() -> None:
77
82
  client = motor.motor_asyncio.AsyncIOMotorClient(settings.mongodb_url)
@@ -66,6 +66,25 @@ def create_application() -> FastAPI:
66
66
  application.include_router(graphql_router, prefix="/graphql")
67
67
  {% endif %}
68
68
 
69
+ {% if "admin" in extras %}
70
+ # Mount Admin
71
+ try:
72
+ from app.admin.setup import setup_admin
73
+ from app.db.session import engine
74
+ setup_admin(application, engine)
75
+ except Exception as e:
76
+ import logging
77
+ logging.warning(f"Failed to setup admin: {e}")
78
+ {% endif %}
79
+
80
+ # Mount StaticFiles for frontend (must be last)
81
+ from fastapi.staticfiles import StaticFiles
82
+ import os
83
+ current_dir = os.path.dirname(__file__)
84
+ frontend_path = os.path.join(current_dir, "frontend")
85
+ if os.path.exists(frontend_path):
86
+ application.mount("/", StaticFiles(directory=frontend_path, html=True), name="frontend")
87
+
69
88
  return application
70
89
 
71
90
 
@@ -174,7 +174,7 @@ GEMINI_MODEL=gemini-1.5-pro
174
174
  # Ollama — local LLM, no API key (active)
175
175
  OLLAMA_HOST=localhost
176
176
  OLLAMA_PORT=11434
177
- OLLAMA_MODEL=llama3
177
+ OLLAMA_MODEL=tinyllama
178
178
  {% elif ai == "langchain" %}
179
179
  # LangChain (active) — uses OpenAI backend by default
180
180
  OPENAI_API_KEY=sk-placeholder
@@ -192,7 +192,7 @@ OPENAI_BASE_URL=
192
192
  # OpenAI: OPENAI_API_KEY=sk-placeholder | OPENAI_MODEL=gpt-4o | OPENAI_EMBEDDING_MODEL=text-embedding-3-small | OPENAI_BASE_URL=
193
193
  # Anthropic: ANTHROPIC_API_KEY=sk-ant-placeholder | ANTHROPIC_MODEL=claude-3-5-sonnet-20241022
194
194
  # Gemini: GEMINI_API_KEY=CHANGE_ME | GEMINI_MODEL=gemini-1.5-pro
195
- # Ollama: OLLAMA_HOST=localhost | OLLAMA_PORT=11434 | OLLAMA_MODEL=llama3 (no API key)
195
+ # Ollama: OLLAMA_HOST=localhost | OLLAMA_PORT=11434 | OLLAMA_MODEL=tinyllama (no API key)
196
196
  # LangChain: uses OPENAI_* vars above
197
197
  # LlamaIndex: uses OPENAI_* vars above
198
198
  {% endif %}
@@ -42,8 +42,8 @@ dependencies = [
42
42
  "aerich>=0.7.2",
43
43
  {% endif %}
44
44
  {% if has_mongo and orm == "beanie" %}
45
- "beanie>=1.25.0",
46
- "motor>=3.4.0",
45
+ "beanie>=1.27.0",
46
+ "motor>=3.3.0",
47
47
  {% endif %}
48
48
 
49
49
  {% if auth == "jwt" or auth == "oauth2" %}
@@ -0,0 +1,272 @@
1
+ services:
2
+ app:
3
+ build: .
4
+ ports:
5
+ - "8000:8000"
6
+ env_file:
7
+ - .env
8
+ {% if (has_relational_db and db != "sqlite") or has_mongo or broker == "redis" or cache == "redis" or broker == "rabbitmq" %}
9
+ depends_on:
10
+ {% if has_relational_db and db == "postgresql" %}
11
+ postgres:
12
+ condition: service_healthy
13
+ {% endif %}
14
+ {% if has_relational_db and db == "mysql" %}
15
+ mysql:
16
+ condition: service_healthy
17
+ {% endif %}
18
+ {% if has_mongo %}
19
+ mongodb:
20
+ condition: service_healthy
21
+ {% endif %}
22
+ {% if broker == "redis" or cache == "redis" %}
23
+ redis:
24
+ condition: service_healthy
25
+ {% endif %}
26
+ {% if broker == "rabbitmq" %}
27
+ rabbitmq:
28
+ condition: service_healthy
29
+ {% endif %}
30
+ {% endif %}
31
+ volumes:
32
+ - .:/app
33
+ {% if (has_relational_db and db != "sqlite") or has_mongo or broker == "redis" or cache == "redis" or broker == "rabbitmq" or vector_db == "qdrant" or vector_db == "elasticsearch" or vector_db == "opensearch" or ai == "ollama" %}
34
+ environment:
35
+ {% if has_relational_db and db == "postgresql" %}
36
+ - POSTGRES_HOST=postgres
37
+ {% endif %}
38
+ {% if has_relational_db and db == "mysql" %}
39
+ - MYSQL_HOST=mysql
40
+ {% endif %}
41
+ {% if has_mongo %}
42
+ - MONGODB_HOST=mongodb
43
+ {% endif %}
44
+ {% if broker == "redis" or cache == "redis" %}
45
+ - REDIS_HOST=redis
46
+ {% endif %}
47
+ {% if broker == "rabbitmq" %}
48
+ - RABBITMQ_HOST=rabbitmq
49
+ {% endif %}
50
+ {% if vector_db == "elasticsearch" %}
51
+ - ELASTICSEARCH_HOST=elasticsearch
52
+ {% endif %}
53
+ {% if vector_db == "opensearch" %}
54
+ - OPENSEARCH_HOST=opensearch
55
+ {% endif %}
56
+ {% if vector_db == "qdrant" %}
57
+ - QDRANT_HOST=qdrant
58
+ {% endif %}
59
+ {% if ai == "ollama" %}
60
+ - OLLAMA_HOST=ollama
61
+ {% endif %}
62
+ {% endif %}
63
+ restart: unless-stopped
64
+
65
+ {% if has_broker and broker != "kafka" %}
66
+ worker:
67
+ build: .
68
+ command: celery -A tasks.celery_app worker --loglevel=info --concurrency=2
69
+ env_file:
70
+ - .env
71
+ depends_on:
72
+ - app
73
+ environment:
74
+ {% if has_relational_db and db == "postgresql" %}
75
+ - POSTGRES_HOST=postgres
76
+ {% endif %}
77
+ {% if has_relational_db and db == "mysql" %}
78
+ - MYSQL_HOST=mysql
79
+ {% endif %}
80
+ {% if broker == "redis" or cache == "redis" %}
81
+ - REDIS_HOST=redis
82
+ {% endif %}
83
+ {% if broker == "rabbitmq" %}
84
+ - RABBITMQ_HOST=rabbitmq
85
+ {% endif %}
86
+ restart: unless-stopped
87
+ {% endif %}
88
+
89
+ {% if has_relational_db and db == "postgresql" %}
90
+ postgres:
91
+ image: postgres:16-alpine
92
+ environment:
93
+ POSTGRES_DB: {{ slug }}_db
94
+ POSTGRES_USER: postgres
95
+ POSTGRES_PASSWORD: postgres
96
+ ports:
97
+ - "5432:5432"
98
+ volumes:
99
+ - postgres_data:/var/lib/postgresql/data
100
+ healthcheck:
101
+ test: ["CMD-SHELL", "pg_isready -U postgres -d {{ slug }}_db"]
102
+ interval: 10s
103
+ timeout: 5s
104
+ retries: 5
105
+ {% endif %}
106
+
107
+ {% if has_relational_db and db == "mysql" %}
108
+ mysql:
109
+ image: mysql:8
110
+ environment:
111
+ MYSQL_DATABASE: {{ slug }}_db
112
+ MYSQL_ROOT_PASSWORD: ${MYSQL_PASSWORD:-mysql}
113
+ ports:
114
+ - "3306:3306"
115
+ volumes:
116
+ - mysql_data:/var/lib/mysql
117
+ healthcheck:
118
+ test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
119
+ interval: 10s
120
+ timeout: 5s
121
+ retries: 5
122
+ {% endif %}
123
+
124
+ {% if has_mongo %}
125
+ mongodb:
126
+ image: mongo:7
127
+ ports:
128
+ - "27017:27017"
129
+ environment:
130
+ - MONGO_INITDB_ROOT_USERNAME=${MONGODB_USER:-mongo}
131
+ - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_PASSWORD:-mongo}
132
+ volumes:
133
+ - mongo_data:/data/db
134
+ healthcheck:
135
+ test: ["CMD-SHELL", "mongosh --eval 'db.runCommand({ping: 1})' --quiet"]
136
+ interval: 10s
137
+ timeout: 5s
138
+ retries: 5
139
+ {% endif %}
140
+
141
+ {% if broker == "redis" or cache == "redis" %}
142
+ redis:
143
+ image: redis:7-alpine
144
+ ports:
145
+ - "6379:6379"
146
+ volumes:
147
+ - redis_data:/data
148
+ healthcheck:
149
+ test: ["CMD", "redis-cli", "ping"]
150
+ interval: 10s
151
+ timeout: 3s
152
+ retries: 3
153
+ {% endif %}
154
+
155
+ {% if broker == "rabbitmq" %}
156
+ rabbitmq:
157
+ image: rabbitmq:3-management-alpine
158
+ ports:
159
+ - "5672:5672"
160
+ - "15672:15672"
161
+ environment:
162
+ RABBITMQ_DEFAULT_USER: ${RABBITMQ_USER:-guest}
163
+ RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASSWORD:-guest}
164
+ healthcheck:
165
+ test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"]
166
+ interval: 10s
167
+ timeout: 5s
168
+ retries: 5
169
+ {% endif %}
170
+
171
+ {% if vector_db == "qdrant" %}
172
+ qdrant:
173
+ image: qdrant/qdrant:latest
174
+ ports:
175
+ - "6333:6333"
176
+ volumes:
177
+ - qdrant_data:/qdrant/storage
178
+ {% endif %}
179
+
180
+ {% if vector_db == "elasticsearch" %}
181
+ elasticsearch:
182
+ image: docker.elastic.co/elasticsearch/elasticsearch:8.12.0
183
+ environment:
184
+ - discovery.type=single-node
185
+ - xpack.security.enabled=false
186
+ - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
187
+ ports:
188
+ - "9200:9200"
189
+ volumes:
190
+ - es_data:/usr/share/elasticsearch/data
191
+ {% endif %}
192
+
193
+ {% if vector_db == "opensearch" %}
194
+ opensearch:
195
+ image: opensearchproject/opensearch:2.11.0
196
+ environment:
197
+ - discovery.type=single-node
198
+ - plugins.security.disabled=true
199
+ - OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m
200
+ ports:
201
+ - "9200:9200"
202
+ volumes:
203
+ - opensearch_data:/usr/share/opensearch/data
204
+ {% endif %}
205
+
206
+ {% if ai == "ollama" %}
207
+ ollama:
208
+ image: ollama/ollama:latest
209
+ ports:
210
+ - "11434:11434"
211
+ volumes:
212
+ - ollama_data:/root/.ollama
213
+ {% endif %}
214
+
215
+ {% if broker == "kafka" %}
216
+ kafka:
217
+ image: confluentinc/cp-kafka:7.5.0
218
+ ports:
219
+ - "9092:9092"
220
+ environment:
221
+ - KAFKA_NODE_ID=1
222
+ - KAFKA_PROCESS_ROLES=broker,controller
223
+ - KAFKA_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093
224
+ - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092
225
+ - KAFKA_CONTROLLER_LISTENER_NAMES=CONTROLLER
226
+ - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
227
+ - KAFKA_CONTROLLER_QUORUM_VOTERS=1@localhost:9093
228
+ - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1
229
+ - KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=1
230
+ - KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=1
231
+ - CLUSTER_ID=MkU3OEVBNTcwNTJENDM2Qk
232
+ {% endif %}
233
+
234
+ {% if vector_db == "vespa" %}
235
+ vespa:
236
+ image: vespaengine/vespa
237
+ ports:
238
+ - "8080:8080"
239
+ volumes:
240
+ - vespa_data:/opt/vespa/var
241
+ {% endif %}
242
+
243
+ {% if (has_relational_db and db != "sqlite") or has_mongo or broker == "redis" or cache == "redis" or vector_db == "qdrant" or vector_db == "elasticsearch" or vector_db == "opensearch" or ai == "ollama" or vector_db == "vespa" %}
244
+ volumes:
245
+ {% if has_relational_db and db == "postgresql" %}
246
+ postgres_data:
247
+ {% endif %}
248
+ {% if has_relational_db and db == "mysql" %}
249
+ mysql_data:
250
+ {% endif %}
251
+ {% if has_mongo %}
252
+ mongo_data:
253
+ {% endif %}
254
+ {% if broker == "redis" or cache == "redis" %}
255
+ redis_data:
256
+ {% endif %}
257
+ {% if vector_db == "qdrant" %}
258
+ qdrant_data:
259
+ {% endif %}
260
+ {% if vector_db == "elasticsearch" %}
261
+ es_data:
262
+ {% endif %}
263
+ {% if vector_db == "opensearch" %}
264
+ opensearch_data:
265
+ {% endif %}
266
+ {% if ai == "ollama" %}
267
+ ollama_data:
268
+ {% endif %}
269
+ {% if vector_db == "vespa" %}
270
+ vespa_data:
271
+ {% endif %}
272
+ {% endif %}
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "fastapi-spawn"
7
- version = "0.4.21"
7
+ version = "0.4.23"
8
8
  description = "A powerful CLI tool to scaffold production-ready FastAPI projects with flexible database, auth, broker, and deployment options."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -1,40 +0,0 @@
1
- """Health check endpoints — /health, /readiness, /liveness."""
2
-
3
- from __future__ import annotations
4
-
5
- import time
6
-
7
- from fastapi import APIRouter
8
- from pydantic import BaseModel
9
-
10
- router = APIRouter(prefix="/health")
11
-
12
- _start_time = time.time()
13
-
14
-
15
- class HealthResponse(BaseModel):
16
- status: str
17
- uptime_seconds: float
18
- version: str = "0.1.0"
19
- service: str = "{{ project_name }}"
20
-
21
-
22
- @router.get("", response_model=HealthResponse, summary="Basic health check")
23
- async def health() -> HealthResponse:
24
- """Returns service status and uptime."""
25
- return HealthResponse(
26
- status="ok",
27
- uptime_seconds=round(time.time() - _start_time, 2),
28
- )
29
-
30
-
31
- @router.get("/readiness", summary="Readiness probe (Kubernetes)")
32
- async def readiness() -> dict:
33
- """Indicates whether the service is ready to accept traffic."""
34
- return {"status": "ready"}
35
-
36
-
37
- @router.get("/liveness", summary="Liveness probe (Kubernetes)")
38
- async def liveness() -> dict:
39
- """Indicates whether the service process is alive."""
40
- return {"status": "alive"}
@@ -1,111 +0,0 @@
1
- services:
2
- app:
3
- build: .
4
- ports:
5
- - "8000:8000"
6
- env_file:
7
- - .env
8
- depends_on:
9
- {% if has_relational_db and db == "postgresql" %}
10
- - postgres
11
- {% endif %}
12
- {% if has_mongo %}
13
- - mongodb
14
- {% endif %}
15
- {% if broker == "redis" or cache == "redis" %}
16
- - redis
17
- {% endif %}
18
- {% if broker == "rabbitmq" %}
19
- - rabbitmq
20
- {% endif %}
21
- volumes:
22
- - .:/app
23
- environment:
24
- {% if has_relational_db and db == "postgresql" %}
25
- - POSTGRES_HOST=postgres
26
- {% endif %}
27
- {% if broker == "redis" or cache == "redis" %}
28
- - REDIS_HOST=redis
29
- {% endif %}
30
- restart: unless-stopped
31
-
32
- {% if has_broker and broker != "kafka" %}
33
- worker:
34
- build: .
35
- command: celery -A tasks.celery_app worker --loglevel=info --concurrency=2
36
- env_file:
37
- - .env
38
- depends_on:
39
- - app
40
- environment:
41
- {% if has_relational_db and db == "postgresql" %}
42
- - POSTGRES_HOST=postgres
43
- {% endif %}
44
- {% if broker == "redis" or cache == "redis" %}
45
- - REDIS_HOST=redis
46
- {% endif %}
47
- restart: unless-stopped
48
- {% endif %}
49
-
50
- {% if has_relational_db and db == "postgresql" %}
51
- postgres:
52
- image: postgres:16-alpine
53
- environment:
54
- POSTGRES_DB: {{ slug }}_db
55
- POSTGRES_USER: postgres
56
- POSTGRES_PASSWORD: postgres
57
- ports:
58
- - "5432:5432"
59
- volumes:
60
- - postgres_data:/var/lib/postgresql/data
61
- healthcheck:
62
- test: ["CMD-SHELL", "pg_isready -U user -d {{ slug }}_db"]
63
- interval: 10s
64
- timeout: 5s
65
- retries: 5
66
- {% endif %}
67
-
68
- {% if has_mongo %}
69
- mongodb:
70
- image: mongo:7
71
- ports:
72
- - "27017:27017"
73
- volumes:
74
- - mongo_data:/data/db
75
- {% endif %}
76
-
77
- {% if broker == "redis" or cache == "redis" %}
78
- redis:
79
- image: redis:7-alpine
80
- ports:
81
- - "6379:6379"
82
- volumes:
83
- - redis_data:/data
84
- healthcheck:
85
- test: ["CMD", "redis-cli", "ping"]
86
- interval: 10s
87
- timeout: 3s
88
- retries: 3
89
- {% endif %}
90
-
91
- {% if broker == "rabbitmq" %}
92
- rabbitmq:
93
- image: rabbitmq:3-management-alpine
94
- ports:
95
- - "5672:5672"
96
- - "15672:15672"
97
- environment:
98
- RABBITMQ_DEFAULT_USER: guest
99
- RABBITMQ_DEFAULT_PASS: guest
100
- {% endif %}
101
-
102
- volumes:
103
- {% if has_relational_db and db == "postgresql" %}
104
- postgres_data:
105
- {% endif %}
106
- {% if has_mongo %}
107
- mongo_data:
108
- {% endif %}
109
- {% if broker == "redis" or cache == "redis" %}
110
- redis_data:
111
- {% endif %}
File without changes