FastAPI-fastkit 1.1.4__py3-none-any.whl → 1.2.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.
- fastapi_fastkit/__init__.py +1 -1
- fastapi_fastkit/backend/inspector.py +170 -12
- fastapi_fastkit/backend/interactive/__init__.py +51 -0
- fastapi_fastkit/backend/interactive/config_builder.py +186 -0
- fastapi_fastkit/backend/interactive/prompts.py +528 -0
- fastapi_fastkit/backend/interactive/selectors.py +241 -0
- fastapi_fastkit/backend/interactive/validators.py +163 -0
- fastapi_fastkit/backend/main.py +77 -0
- fastapi_fastkit/backend/package_managers/pdm_manager.py +6 -2
- fastapi_fastkit/backend/package_managers/pip_manager.py +6 -2
- fastapi_fastkit/backend/package_managers/poetry_manager.py +6 -2
- fastapi_fastkit/backend/package_managers/uv_manager.py +6 -2
- fastapi_fastkit/backend/project_builder/__init__.py +17 -0
- fastapi_fastkit/backend/project_builder/config_generator.py +594 -0
- fastapi_fastkit/backend/project_builder/dependency_collector.py +210 -0
- fastapi_fastkit/cli.py +218 -20
- fastapi_fastkit/core/settings.py +74 -0
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/pyproject.toml-tpl +2 -1
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/requirements.txt-tpl +1 -0
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/pyproject.toml-tpl +2 -1
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/requirements.txt-tpl +1 -0
- fastapi_fastkit/fastapi_project_template/fastapi-default/pyproject.toml-tpl +2 -1
- fastapi_fastkit/fastapi_project_template/fastapi-default/requirements.txt-tpl +1 -0
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/pyproject.toml-tpl +2 -1
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/requirements.txt-tpl +1 -0
- fastapi_fastkit/fastapi_project_template/fastapi-empty/pyproject.toml-tpl +2 -1
- fastapi_fastkit/fastapi_project_template/fastapi-empty/requirements.txt-tpl +1 -0
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/pyproject.toml-tpl +2 -1
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/requirements.txt-tpl +1 -0
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/pyproject.toml-tpl +2 -1
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/requirements.txt-tpl +1 -0
- fastapi_fastkit/fastapi_project_template/fastapi-single-module/pyproject.toml-tpl +2 -1
- fastapi_fastkit/fastapi_project_template/fastapi-single-module/requirements.txt-tpl +1 -0
- fastapi_fastkit/utils/main.py +121 -10
- {fastapi_fastkit-1.1.4.dist-info → fastapi_fastkit-1.2.0.dist-info}/METADATA +9 -6
- {fastapi_fastkit-1.1.4.dist-info → fastapi_fastkit-1.2.0.dist-info}/RECORD +39 -31
- {fastapi_fastkit-1.1.4.dist-info → fastapi_fastkit-1.2.0.dist-info}/WHEEL +1 -1
- {fastapi_fastkit-1.1.4.dist-info → fastapi_fastkit-1.2.0.dist-info}/entry_points.txt +0 -0
- {fastapi_fastkit-1.1.4.dist-info → fastapi_fastkit-1.2.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
# --------------------------------------------------------------------------
|
|
2
|
+
# Interactive prompt definitions for FastAPI-fastkit CLI
|
|
3
|
+
#
|
|
4
|
+
# Provides user-facing prompts for:
|
|
5
|
+
# - Basic project information
|
|
6
|
+
# - Template selection
|
|
7
|
+
# - Database selection
|
|
8
|
+
# - Authentication method
|
|
9
|
+
# - Additional features
|
|
10
|
+
# - Testing configuration
|
|
11
|
+
# - Deployment options
|
|
12
|
+
#
|
|
13
|
+
# @author bnbong bbbong9@gmail.com
|
|
14
|
+
# --------------------------------------------------------------------------
|
|
15
|
+
from typing import Any, Dict, List, Optional, cast
|
|
16
|
+
|
|
17
|
+
import click
|
|
18
|
+
from rich.panel import Panel
|
|
19
|
+
|
|
20
|
+
from fastapi_fastkit.utils.main import console, print_error
|
|
21
|
+
|
|
22
|
+
from .selectors import render_selection_table
|
|
23
|
+
from .validators import validate_email_format, validate_project_name
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def prompt_basic_info() -> Dict[str, str]:
|
|
27
|
+
"""
|
|
28
|
+
Prompt for basic project information.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
Dictionary with project_name, author, author_email, description
|
|
32
|
+
"""
|
|
33
|
+
console.print("\n[bold cyan]📝 Project Information[/bold cyan]")
|
|
34
|
+
console.print(
|
|
35
|
+
"[dim]Let's start with some basic information about your project[/dim]\n"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
# Project name
|
|
39
|
+
while True:
|
|
40
|
+
project_name = click.prompt("Project name", type=str)
|
|
41
|
+
is_valid, error = validate_project_name(project_name)
|
|
42
|
+
if is_valid:
|
|
43
|
+
break
|
|
44
|
+
print_error(error or "Invalid project name")
|
|
45
|
+
|
|
46
|
+
# Author
|
|
47
|
+
author = click.prompt("Author name", type=str)
|
|
48
|
+
|
|
49
|
+
# Email
|
|
50
|
+
while True:
|
|
51
|
+
author_email = click.prompt("Author email", type=str)
|
|
52
|
+
if validate_email_format(author_email):
|
|
53
|
+
break
|
|
54
|
+
print_error("Invalid email format. Please enter a valid email address.")
|
|
55
|
+
|
|
56
|
+
# Description
|
|
57
|
+
description = click.prompt("Project description", type=str)
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
"project_name": project_name,
|
|
61
|
+
"author": author,
|
|
62
|
+
"author_email": author_email,
|
|
63
|
+
"description": description,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def prompt_template_selection(settings: Any) -> Optional[str]:
|
|
68
|
+
"""
|
|
69
|
+
Prompt for base template selection.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
settings: FastkitConfig instance
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
Template name or None for empty project
|
|
76
|
+
"""
|
|
77
|
+
import os
|
|
78
|
+
|
|
79
|
+
console.print("\n[bold cyan]📦 Base Template Selection[/bold cyan]")
|
|
80
|
+
console.print("[dim]Choose a base template or start from scratch[/dim]\n")
|
|
81
|
+
|
|
82
|
+
template_dir = settings.FASTKIT_TEMPLATE_ROOT
|
|
83
|
+
excluded_dirs = ["__pycache__", "modules", "fastapi-empty"]
|
|
84
|
+
|
|
85
|
+
templates = [
|
|
86
|
+
d
|
|
87
|
+
for d in os.listdir(template_dir)
|
|
88
|
+
if os.path.isdir(os.path.join(template_dir, d)) and d not in excluded_dirs
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
# Add "Empty Project" option
|
|
92
|
+
options = {"Empty Project": "Start with minimal FastAPI setup"}
|
|
93
|
+
|
|
94
|
+
# Add available templates
|
|
95
|
+
for template in templates:
|
|
96
|
+
readme_path = os.path.join(template_dir, template, "README.md-tpl")
|
|
97
|
+
description = "No description"
|
|
98
|
+
if os.path.exists(readme_path):
|
|
99
|
+
with open(readme_path, "r") as f:
|
|
100
|
+
first_line = f.readline().strip()
|
|
101
|
+
if first_line.startswith("# "):
|
|
102
|
+
description = first_line[2:]
|
|
103
|
+
options[template] = description
|
|
104
|
+
|
|
105
|
+
render_selection_table("Available Templates", options)
|
|
106
|
+
|
|
107
|
+
choice = click.prompt(
|
|
108
|
+
"\nSelect template",
|
|
109
|
+
type=click.IntRange(1, len(options)),
|
|
110
|
+
default=1,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
selected_key = list(options.keys())[choice - 1]
|
|
114
|
+
|
|
115
|
+
return cast(
|
|
116
|
+
Optional[str], selected_key if selected_key != "Empty Project" else None
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def prompt_database_selection(settings: Any) -> Dict[str, Any]:
|
|
121
|
+
"""
|
|
122
|
+
Prompt for database selection.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
settings: FastkitConfig instance
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
Dictionary with type and packages
|
|
129
|
+
"""
|
|
130
|
+
console.print("\n[bold cyan]🗄️ Database Selection[/bold cyan]")
|
|
131
|
+
console.print("[dim]Choose your database backend[/dim]\n")
|
|
132
|
+
|
|
133
|
+
db_catalog = settings.PACKAGE_CATALOG["database"]
|
|
134
|
+
options = {
|
|
135
|
+
name: f"Packages: {', '.join(pkgs) if pkgs else 'None'}"
|
|
136
|
+
for name, pkgs in db_catalog.items()
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
render_selection_table("Database Options", options)
|
|
140
|
+
|
|
141
|
+
choice = click.prompt(
|
|
142
|
+
"\nSelect database",
|
|
143
|
+
type=click.IntRange(1, len(options)),
|
|
144
|
+
default=len(options), # Default to "None"
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
selected_type = list(options.keys())[choice - 1]
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
"type": selected_type,
|
|
151
|
+
"packages": db_catalog[selected_type],
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def prompt_authentication_selection(settings: Any) -> str:
|
|
156
|
+
"""
|
|
157
|
+
Prompt for authentication method.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
settings: FastkitConfig instance
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
Authentication type name
|
|
164
|
+
"""
|
|
165
|
+
console.print("\n[bold cyan]🔐 Authentication Selection[/bold cyan]")
|
|
166
|
+
console.print("[dim]Choose your authentication strategy[/dim]\n")
|
|
167
|
+
|
|
168
|
+
auth_catalog = settings.PACKAGE_CATALOG["authentication"]
|
|
169
|
+
options = {}
|
|
170
|
+
|
|
171
|
+
for name, pkgs in auth_catalog.items():
|
|
172
|
+
if name == "None":
|
|
173
|
+
options[name] = "No authentication"
|
|
174
|
+
elif name == "JWT":
|
|
175
|
+
options[name] = "JSON Web Tokens (recommended for APIs)"
|
|
176
|
+
elif name == "OAuth2":
|
|
177
|
+
options[name] = "OAuth2 with authlib"
|
|
178
|
+
elif name == "FastAPI-Users":
|
|
179
|
+
options[name] = "Complete user management system (includes JWT)"
|
|
180
|
+
elif name == "Session-based":
|
|
181
|
+
options[name] = "Traditional session-based authentication"
|
|
182
|
+
else:
|
|
183
|
+
options[name] = f"Packages: {', '.join(pkgs)}"
|
|
184
|
+
|
|
185
|
+
render_selection_table("Authentication Options", options)
|
|
186
|
+
|
|
187
|
+
choice = click.prompt(
|
|
188
|
+
"\nSelect authentication method",
|
|
189
|
+
type=click.IntRange(1, len(options)),
|
|
190
|
+
default=len(options), # Default to "None"
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
return cast(str, list(options.keys())[choice - 1])
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def prompt_async_tasks_selection(settings: Any) -> str:
|
|
197
|
+
"""
|
|
198
|
+
Prompt for async task queue selection.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
settings: FastkitConfig instance
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
Task queue type name
|
|
205
|
+
"""
|
|
206
|
+
console.print("\n[bold cyan]⚡ Background Tasks Selection[/bold cyan]")
|
|
207
|
+
console.print("[dim]Choose a task queue for background processing[/dim]\n")
|
|
208
|
+
|
|
209
|
+
task_catalog = settings.PACKAGE_CATALOG["async_tasks"]
|
|
210
|
+
options = {
|
|
211
|
+
name: f"Packages: {', '.join(pkgs) if pkgs else 'None'}"
|
|
212
|
+
for name, pkgs in task_catalog.items()
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
render_selection_table("Task Queue Options", options)
|
|
216
|
+
|
|
217
|
+
choice = click.prompt(
|
|
218
|
+
"\nSelect task queue",
|
|
219
|
+
type=click.IntRange(1, len(options)),
|
|
220
|
+
default=len(options), # Default to "None"
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
return cast(str, list(options.keys())[choice - 1])
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def prompt_caching_selection(settings: Any) -> str:
|
|
227
|
+
"""
|
|
228
|
+
Prompt for caching selection.
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
settings: FastkitConfig instance
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
Caching type name
|
|
235
|
+
"""
|
|
236
|
+
console.print("\n[bold cyan]💾 Caching Selection[/bold cyan]")
|
|
237
|
+
console.print("[dim]Add caching layer for better performance[/dim]\n")
|
|
238
|
+
|
|
239
|
+
cache_catalog = settings.PACKAGE_CATALOG["caching"]
|
|
240
|
+
options = {
|
|
241
|
+
name: f"Packages: {', '.join(pkgs) if pkgs else 'None'}"
|
|
242
|
+
for name, pkgs in cache_catalog.items()
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
render_selection_table("Caching Options", options)
|
|
246
|
+
|
|
247
|
+
choice = click.prompt(
|
|
248
|
+
"\nSelect caching",
|
|
249
|
+
type=click.IntRange(1, len(options)),
|
|
250
|
+
default=len(options), # Default to "None"
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
return cast(str, list(options.keys())[choice - 1])
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def prompt_monitoring_selection(settings: Any) -> str:
|
|
257
|
+
"""
|
|
258
|
+
Prompt for monitoring/logging selection.
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
settings: FastkitConfig instance
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
Monitoring type name
|
|
265
|
+
"""
|
|
266
|
+
console.print("\n[bold cyan]📊 Monitoring & Logging Selection[/bold cyan]")
|
|
267
|
+
console.print("[dim]Add monitoring and logging capabilities[/dim]\n")
|
|
268
|
+
|
|
269
|
+
monitoring_catalog = settings.PACKAGE_CATALOG["monitoring"]
|
|
270
|
+
options = {}
|
|
271
|
+
|
|
272
|
+
for name, pkgs in monitoring_catalog.items():
|
|
273
|
+
if name == "None":
|
|
274
|
+
options[name] = "No additional monitoring"
|
|
275
|
+
elif name == "Loguru":
|
|
276
|
+
options[name] = "Enhanced logging with Loguru"
|
|
277
|
+
elif name == "OpenTelemetry":
|
|
278
|
+
options[name] = "Distributed tracing and metrics"
|
|
279
|
+
elif name == "Prometheus":
|
|
280
|
+
options[name] = "Prometheus metrics instrumentation"
|
|
281
|
+
else:
|
|
282
|
+
options[name] = f"Packages: {', '.join(pkgs)}"
|
|
283
|
+
|
|
284
|
+
render_selection_table("Monitoring Options", options)
|
|
285
|
+
|
|
286
|
+
choice = click.prompt(
|
|
287
|
+
"\nSelect monitoring",
|
|
288
|
+
type=click.IntRange(1, len(options)),
|
|
289
|
+
default=len(options), # Default to "None"
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
return cast(str, list(options.keys())[choice - 1])
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def prompt_testing_selection(settings: Any) -> str:
|
|
296
|
+
"""
|
|
297
|
+
Prompt for testing framework selection.
|
|
298
|
+
|
|
299
|
+
Args:
|
|
300
|
+
settings: FastkitConfig instance
|
|
301
|
+
|
|
302
|
+
Returns:
|
|
303
|
+
Testing type name
|
|
304
|
+
"""
|
|
305
|
+
console.print("\n[bold cyan]🧪 Testing Framework Selection[/bold cyan]")
|
|
306
|
+
console.print("[dim]Choose testing tools and coverage[/dim]\n")
|
|
307
|
+
|
|
308
|
+
testing_catalog = settings.PACKAGE_CATALOG["testing"]
|
|
309
|
+
options = {}
|
|
310
|
+
|
|
311
|
+
for name, pkgs in testing_catalog.items():
|
|
312
|
+
if name == "None":
|
|
313
|
+
options[name] = "No testing framework"
|
|
314
|
+
elif name == "Basic":
|
|
315
|
+
options[name] = "pytest + httpx for API testing"
|
|
316
|
+
elif name == "Coverage":
|
|
317
|
+
options[name] = "Basic + code coverage"
|
|
318
|
+
elif name == "Advanced":
|
|
319
|
+
options[name] = "Coverage + faker + factory-boy for fixtures"
|
|
320
|
+
else:
|
|
321
|
+
options[name] = f"Packages: {', '.join(pkgs)}"
|
|
322
|
+
|
|
323
|
+
render_selection_table("Testing Options", options)
|
|
324
|
+
|
|
325
|
+
choice = click.prompt(
|
|
326
|
+
"\nSelect testing framework",
|
|
327
|
+
type=click.IntRange(1, len(options)),
|
|
328
|
+
default=2, # Default to "Basic"
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
return cast(str, list(options.keys())[choice - 1])
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def prompt_utilities_selection(settings: Any) -> List[str]:
|
|
335
|
+
"""
|
|
336
|
+
Prompt for utilities selection (multi-select).
|
|
337
|
+
|
|
338
|
+
Args:
|
|
339
|
+
settings: FastkitConfig instance
|
|
340
|
+
|
|
341
|
+
Returns:
|
|
342
|
+
List of selected utility names
|
|
343
|
+
"""
|
|
344
|
+
console.print("\n[bold cyan]🛠️ Additional Utilities[/bold cyan]")
|
|
345
|
+
console.print(
|
|
346
|
+
"[dim]Select additional utilities (comma-separated numbers, e.g., 1,3,4)[/dim]\n"
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
utilities_catalog = settings.PACKAGE_CATALOG["utilities"]
|
|
350
|
+
options = []
|
|
351
|
+
descriptions = {}
|
|
352
|
+
|
|
353
|
+
for name, pkgs in utilities_catalog.items():
|
|
354
|
+
if name == "None":
|
|
355
|
+
continue
|
|
356
|
+
options.append(name)
|
|
357
|
+
if name == "CORS":
|
|
358
|
+
descriptions[name] = "(Built-in, will configure in settings)"
|
|
359
|
+
elif name == "Rate-Limiting":
|
|
360
|
+
descriptions[name] = "API rate limiting with slowapi"
|
|
361
|
+
elif name == "Pagination":
|
|
362
|
+
descriptions[name] = "Response pagination utilities"
|
|
363
|
+
elif name == "WebSocket":
|
|
364
|
+
descriptions[name] = "(Built-in, will configure routes)"
|
|
365
|
+
else:
|
|
366
|
+
descriptions[name] = f"({', '.join(pkgs)})"
|
|
367
|
+
|
|
368
|
+
for i, option in enumerate(options, 1):
|
|
369
|
+
desc = descriptions.get(option, "")
|
|
370
|
+
console.print(f" [cyan]{i}[/cyan]. {option} {desc}")
|
|
371
|
+
|
|
372
|
+
selected_input = console.input(
|
|
373
|
+
"\n[cyan]Your choice (or press Enter to skip):[/cyan] "
|
|
374
|
+
).strip()
|
|
375
|
+
|
|
376
|
+
if not selected_input:
|
|
377
|
+
return []
|
|
378
|
+
|
|
379
|
+
try:
|
|
380
|
+
indices = [int(x.strip()) for x in selected_input.split(",") if x.strip()]
|
|
381
|
+
selected = [options[idx - 1] for idx in indices if 1 <= idx <= len(options)]
|
|
382
|
+
return selected
|
|
383
|
+
except (ValueError, IndexError):
|
|
384
|
+
console.print("[yellow]Invalid selection. Skipping utilities.[/yellow]")
|
|
385
|
+
return []
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
def prompt_deployment_options() -> List[str]:
|
|
389
|
+
"""
|
|
390
|
+
Prompt for deployment configuration.
|
|
391
|
+
|
|
392
|
+
Returns:
|
|
393
|
+
List of deployment options
|
|
394
|
+
"""
|
|
395
|
+
console.print("\n[bold cyan]🚀 Deployment Configuration[/bold cyan]")
|
|
396
|
+
console.print("[dim]Select deployment options (comma-separated numbers)[/dim]\n")
|
|
397
|
+
|
|
398
|
+
options = [
|
|
399
|
+
"Docker",
|
|
400
|
+
"docker-compose",
|
|
401
|
+
"None",
|
|
402
|
+
]
|
|
403
|
+
|
|
404
|
+
descriptions = {
|
|
405
|
+
"Docker": "Generate Dockerfile",
|
|
406
|
+
"docker-compose": "Generate docker-compose.yml (includes Docker)",
|
|
407
|
+
"None": "No deployment configuration",
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
for i, option in enumerate(options, 1):
|
|
411
|
+
desc = descriptions.get(option, "")
|
|
412
|
+
console.print(f" [cyan]{i}[/cyan]. {option} - {desc}")
|
|
413
|
+
|
|
414
|
+
choice = click.prompt(
|
|
415
|
+
"\nSelect deployment option",
|
|
416
|
+
type=click.IntRange(1, len(options)),
|
|
417
|
+
default=3, # Default to "None"
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
selected = options[choice - 1]
|
|
421
|
+
|
|
422
|
+
if selected == "None":
|
|
423
|
+
return []
|
|
424
|
+
elif selected == "docker-compose":
|
|
425
|
+
return ["Docker", "docker-compose"]
|
|
426
|
+
else:
|
|
427
|
+
return [selected]
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
def prompt_package_manager_selection(settings: Any) -> str:
|
|
431
|
+
"""
|
|
432
|
+
Prompt for package manager selection.
|
|
433
|
+
|
|
434
|
+
Args:
|
|
435
|
+
settings: FastkitConfig instance
|
|
436
|
+
|
|
437
|
+
Returns:
|
|
438
|
+
Package manager name
|
|
439
|
+
"""
|
|
440
|
+
console.print("\n[bold cyan]📦 Package Manager Selection[/bold cyan]")
|
|
441
|
+
console.print("[dim]Choose your preferred package manager[/dim]\n")
|
|
442
|
+
|
|
443
|
+
options = {}
|
|
444
|
+
for manager, config in settings.PACKAGE_MANAGER_CONFIG.items():
|
|
445
|
+
options[manager] = config["description"]
|
|
446
|
+
|
|
447
|
+
render_selection_table("Package Managers", options)
|
|
448
|
+
|
|
449
|
+
# Find default index
|
|
450
|
+
default_idx = list(options.keys()).index(settings.DEFAULT_PACKAGE_MANAGER) + 1
|
|
451
|
+
|
|
452
|
+
choice = click.prompt(
|
|
453
|
+
"\nSelect package manager",
|
|
454
|
+
type=click.IntRange(1, len(options)),
|
|
455
|
+
default=default_idx,
|
|
456
|
+
)
|
|
457
|
+
|
|
458
|
+
return cast(str, list(options.keys())[choice - 1])
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
def prompt_custom_packages() -> List[str]:
|
|
462
|
+
"""
|
|
463
|
+
Prompt for custom package names (manual entry).
|
|
464
|
+
|
|
465
|
+
Returns:
|
|
466
|
+
List of custom package names
|
|
467
|
+
"""
|
|
468
|
+
console.print("\n[bold cyan]➕ Additional Custom Packages[/bold cyan]")
|
|
469
|
+
console.print(
|
|
470
|
+
"[dim]Enter any additional packages (comma-separated) or press Enter to skip[/dim]\n"
|
|
471
|
+
)
|
|
472
|
+
|
|
473
|
+
packages_input = console.input("[cyan]Package names:[/cyan] ").strip()
|
|
474
|
+
|
|
475
|
+
if not packages_input:
|
|
476
|
+
return []
|
|
477
|
+
|
|
478
|
+
# Split by comma and clean
|
|
479
|
+
packages = [pkg.strip() for pkg in packages_input.split(",") if pkg.strip()]
|
|
480
|
+
|
|
481
|
+
return packages
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
def prompt_additional_features(settings: Any) -> Dict[str, Any]:
|
|
485
|
+
"""
|
|
486
|
+
Prompt for all additional features in sequence.
|
|
487
|
+
|
|
488
|
+
This is a convenience function that calls all feature prompts.
|
|
489
|
+
|
|
490
|
+
Args:
|
|
491
|
+
settings: FastkitConfig instance
|
|
492
|
+
|
|
493
|
+
Returns:
|
|
494
|
+
Dictionary with all feature selections
|
|
495
|
+
"""
|
|
496
|
+
features: Dict[str, Any] = {}
|
|
497
|
+
|
|
498
|
+
# Database
|
|
499
|
+
features["database"] = prompt_database_selection(settings)
|
|
500
|
+
|
|
501
|
+
# Authentication
|
|
502
|
+
features["authentication"] = prompt_authentication_selection(settings)
|
|
503
|
+
|
|
504
|
+
# Async tasks
|
|
505
|
+
features["async_tasks"] = prompt_async_tasks_selection(settings)
|
|
506
|
+
|
|
507
|
+
# Caching
|
|
508
|
+
features["caching"] = prompt_caching_selection(settings)
|
|
509
|
+
|
|
510
|
+
# Monitoring
|
|
511
|
+
features["monitoring"] = prompt_monitoring_selection(settings)
|
|
512
|
+
|
|
513
|
+
# Testing
|
|
514
|
+
features["testing"] = prompt_testing_selection(settings)
|
|
515
|
+
|
|
516
|
+
# Utilities
|
|
517
|
+
features["utilities"] = prompt_utilities_selection(settings)
|
|
518
|
+
|
|
519
|
+
# Deployment
|
|
520
|
+
features["deployment"] = prompt_deployment_options()
|
|
521
|
+
|
|
522
|
+
# Package manager
|
|
523
|
+
features["package_manager"] = prompt_package_manager_selection(settings)
|
|
524
|
+
|
|
525
|
+
# Custom packages
|
|
526
|
+
features["custom_packages"] = prompt_custom_packages()
|
|
527
|
+
|
|
528
|
+
return features
|