fastscaff 0.1.0__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 (81) hide show
  1. fastscaff-0.1.0/PKG-INFO +155 -0
  2. fastscaff-0.1.0/README.md +130 -0
  3. fastscaff-0.1.0/fastscaff/__init__.py +1 -0
  4. fastscaff-0.1.0/fastscaff/cli.py +128 -0
  5. fastscaff-0.1.0/fastscaff/generator.py +258 -0
  6. fastscaff-0.1.0/fastscaff/templates/app/__init__.py.jinja2 +0 -0
  7. fastscaff-0.1.0/fastscaff/templates/app/api/__init__.py.jinja2 +0 -0
  8. fastscaff-0.1.0/fastscaff/templates/app/api/v1/__init__.py.jinja2 +0 -0
  9. fastscaff-0.1.0/fastscaff/templates/app/api/v1/endpoints/__init__.py.jinja2 +0 -0
  10. fastscaff-0.1.0/fastscaff/templates/app/api/v1/endpoints/auth.py.jinja2 +26 -0
  11. fastscaff-0.1.0/fastscaff/templates/app/api/v1/endpoints/health.py.jinja2 +15 -0
  12. fastscaff-0.1.0/fastscaff/templates/app/api/v1/endpoints/users.py.jinja2 +20 -0
  13. fastscaff-0.1.0/fastscaff/templates/app/api/v1/router.py.jinja2 +9 -0
  14. fastscaff-0.1.0/fastscaff/templates/app/core/__init__.py.jinja2 +5 -0
  15. fastscaff-0.1.0/fastscaff/templates/app/core/config.py.jinja2 +50 -0
  16. fastscaff-0.1.0/fastscaff/templates/app/core/database_sqlalchemy.py.jinja2 +105 -0
  17. fastscaff-0.1.0/fastscaff/templates/app/core/database_tortoise.py.jinja2 +69 -0
  18. fastscaff-0.1.0/fastscaff/templates/app/core/lifespan.py.jinja2 +38 -0
  19. fastscaff-0.1.0/fastscaff/templates/app/core/logger.py.jinja2 +157 -0
  20. fastscaff-0.1.0/fastscaff/templates/app/core/rbac.py.jinja2 +486 -0
  21. fastscaff-0.1.0/fastscaff/templates/app/core/redis.py.jinja2 +251 -0
  22. fastscaff-0.1.0/fastscaff/templates/app/core/security.py.jinja2 +69 -0
  23. fastscaff-0.1.0/fastscaff/templates/app/core/singleton.py.jinja2 +32 -0
  24. fastscaff-0.1.0/fastscaff/templates/app/exceptions/__init__.py.jinja2 +21 -0
  25. fastscaff-0.1.0/fastscaff/templates/app/exceptions/base.py.jinja2 +34 -0
  26. fastscaff-0.1.0/fastscaff/templates/app/exceptions/handlers.py.jinja2 +82 -0
  27. fastscaff-0.1.0/fastscaff/templates/app/main.py.jinja2 +61 -0
  28. fastscaff-0.1.0/fastscaff/templates/app/middleware/__init__.py.jinja2 +15 -0
  29. fastscaff-0.1.0/fastscaff/templates/app/middleware/cors.py.jinja2 +16 -0
  30. fastscaff-0.1.0/fastscaff/templates/app/middleware/jwt.py.jinja2 +102 -0
  31. fastscaff-0.1.0/fastscaff/templates/app/middleware/logging.py.jinja2 +119 -0
  32. fastscaff-0.1.0/fastscaff/templates/app/middleware/security.py.jinja2 +60 -0
  33. fastscaff-0.1.0/fastscaff/templates/app/middleware/sign.py.jinja2 +198 -0
  34. fastscaff-0.1.0/fastscaff/templates/app/middleware/tracing.py.jinja2 +87 -0
  35. fastscaff-0.1.0/fastscaff/templates/app/models/__init__.py.jinja2 +3 -0
  36. fastscaff-0.1.0/fastscaff/templates/app/models/base_sqlalchemy.py.jinja2 +23 -0
  37. fastscaff-0.1.0/fastscaff/templates/app/models/base_tortoise.py.jinja2 +13 -0
  38. fastscaff-0.1.0/fastscaff/templates/app/models/user_sqlalchemy.py.jinja2 +13 -0
  39. fastscaff-0.1.0/fastscaff/templates/app/models/user_tortoise.py.jinja2 +14 -0
  40. fastscaff-0.1.0/fastscaff/templates/app/repositories/__init__.py.jinja2 +3 -0
  41. fastscaff-0.1.0/fastscaff/templates/app/repositories/base_sqlalchemy.py.jinja2 +99 -0
  42. fastscaff-0.1.0/fastscaff/templates/app/repositories/base_tortoise.py.jinja2 +61 -0
  43. fastscaff-0.1.0/fastscaff/templates/app/repositories/user_sqlalchemy.py.jinja2 +58 -0
  44. fastscaff-0.1.0/fastscaff/templates/app/repositories/user_tortoise.py.jinja2 +45 -0
  45. fastscaff-0.1.0/fastscaff/templates/app/schemas/__init__.py.jinja2 +4 -0
  46. fastscaff-0.1.0/fastscaff/templates/app/schemas/auth.py.jinja2 +19 -0
  47. fastscaff-0.1.0/fastscaff/templates/app/schemas/base.py.jinja2 +21 -0
  48. fastscaff-0.1.0/fastscaff/templates/app/schemas/user.py.jinja2 +25 -0
  49. fastscaff-0.1.0/fastscaff/templates/app/services/__init___sqlalchemy.py.jinja2 +36 -0
  50. fastscaff-0.1.0/fastscaff/templates/app/services/__init___tortoise.py.jinja2 +36 -0
  51. fastscaff-0.1.0/fastscaff/templates/app/services/auth_sqlalchemy.py.jinja2 +45 -0
  52. fastscaff-0.1.0/fastscaff/templates/app/services/auth_tortoise.py.jinja2 +46 -0
  53. fastscaff-0.1.0/fastscaff/templates/app/services/user_sqlalchemy.py.jinja2 +34 -0
  54. fastscaff-0.1.0/fastscaff/templates/app/services/user_tortoise.py.jinja2 +35 -0
  55. fastscaff-0.1.0/fastscaff/templates/app/utils/__init__.py.jinja2 +26 -0
  56. fastscaff-0.1.0/fastscaff/templates/app/utils/auth.py.jinja2 +145 -0
  57. fastscaff-0.1.0/fastscaff/templates/app/utils/cache.py.jinja2 +529 -0
  58. fastscaff-0.1.0/fastscaff/templates/app/utils/rate_limiter.py.jinja2 +315 -0
  59. fastscaff-0.1.0/fastscaff/templates/app/utils/snowflake.py.jinja2 +71 -0
  60. fastscaff-0.1.0/fastscaff/templates/app/utils/sort_helper.py.jinja2 +77 -0
  61. fastscaff-0.1.0/fastscaff/templates/base/Dockerfile.jinja2 +30 -0
  62. fastscaff-0.1.0/fastscaff/templates/base/Makefile.jinja2 +53 -0
  63. fastscaff-0.1.0/fastscaff/templates/base/README.md.jinja2 +63 -0
  64. fastscaff-0.1.0/fastscaff/templates/base/docker-compose.yml.jinja2 +73 -0
  65. fastscaff-0.1.0/fastscaff/templates/base/env.example.jinja2 +32 -0
  66. fastscaff-0.1.0/fastscaff/templates/base/gitignore.jinja2 +59 -0
  67. fastscaff-0.1.0/fastscaff/templates/base/pre-commit-config.yaml.jinja2 +17 -0
  68. fastscaff-0.1.0/fastscaff/templates/base/pyproject.toml.jinja2 +41 -0
  69. fastscaff-0.1.0/fastscaff/templates/base/requirements.txt.jinja2 +29 -0
  70. fastscaff-0.1.0/fastscaff/templates/tests/__init__.py.jinja2 +0 -0
  71. fastscaff-0.1.0/fastscaff/templates/tests/api/__init__.py.jinja2 +0 -0
  72. fastscaff-0.1.0/fastscaff/templates/tests/api/test_health.py.jinja2 +22 -0
  73. fastscaff-0.1.0/fastscaff/templates/tests/conftest.py.jinja2 +15 -0
  74. fastscaff-0.1.0/fastscaff.egg-info/PKG-INFO +155 -0
  75. fastscaff-0.1.0/fastscaff.egg-info/SOURCES.txt +79 -0
  76. fastscaff-0.1.0/fastscaff.egg-info/dependency_links.txt +1 -0
  77. fastscaff-0.1.0/fastscaff.egg-info/entry_points.txt +2 -0
  78. fastscaff-0.1.0/fastscaff.egg-info/requires.txt +7 -0
  79. fastscaff-0.1.0/fastscaff.egg-info/top_level.txt +1 -0
  80. fastscaff-0.1.0/pyproject.toml +54 -0
  81. fastscaff-0.1.0/setup.cfg +4 -0
@@ -0,0 +1,155 @@
1
+ Metadata-Version: 2.4
2
+ Name: fastscaff
3
+ Version: 0.1.0
4
+ Summary: FastAPI project scaffolding tool
5
+ Author: FastScaff
6
+ License: MIT
7
+ Keywords: fastapi,scaffold,cli,project-generator
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Environment :: Console
10
+ Classifier: Framework :: FastAPI
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Requires-Python: >=3.10
18
+ Description-Content-Type: text/markdown
19
+ Requires-Dist: typer>=0.9.0
20
+ Requires-Dist: rich>=13.0.0
21
+ Requires-Dist: jinja2>=3.1.0
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
24
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
25
+
26
+ # FastScaff
27
+
28
+ FastAPI project scaffolding tool - quickly create standardized FastAPI project structures.
29
+
30
+ [中文文档](README_CN.md)
31
+
32
+ ## Features
33
+
34
+ - One-click creation of standardized FastAPI project structure
35
+ - Multiple ORM support (Tortoise ORM / SQLAlchemy)
36
+ - Built-in JWT authentication
37
+ - Structured logging with structlog
38
+ - Unified exception handling
39
+ - Docker deployment support
40
+ - Testing framework integration
41
+ - SQLite by default (zero configuration)
42
+
43
+ ## Installation
44
+
45
+ ```bash
46
+ # Install from source
47
+ cd FastScaff
48
+ pip install -e .
49
+
50
+ # Or install dependencies directly
51
+ pip install -r requirements.txt
52
+ ```
53
+
54
+ ## Quick Start
55
+
56
+ ### Create a New Project
57
+
58
+ ```bash
59
+ # Using SQLAlchemy (with SQLite by default)
60
+ fastscaff new myproject --orm sqlalchemy
61
+
62
+ # Using Tortoise ORM
63
+ fastscaff new myproject --orm tortoise
64
+
65
+ # Specify output directory
66
+ fastscaff new myproject --output /path/to/dir
67
+ ```
68
+
69
+ ### Generated Project Structure
70
+
71
+ ```
72
+ myproject/
73
+ ├── app/
74
+ │ ├── main.py
75
+ │ ├── core/
76
+ │ │ ├── config.py
77
+ │ │ ├── database.py
78
+ │ │ ├── logger.py
79
+ │ │ ├── lifespan.py
80
+ │ │ ├── security.py
81
+ │ │ └── redis.py
82
+ │ ├── api/
83
+ │ │ └── v1/
84
+ │ │ ├── router.py
85
+ │ │ ├── deps.py
86
+ │ │ └── endpoints/
87
+ │ ├── models/
88
+ │ ├── schemas/
89
+ │ ├── repositories/
90
+ │ ├── services/
91
+ │ ├── middleware/
92
+ │ ├── exceptions/
93
+ │ └── utils/
94
+ ├── tests/
95
+ ├── .env.example
96
+ ├── Dockerfile
97
+ ├── docker-compose.yml
98
+ ├── Makefile
99
+ └── requirements.txt
100
+ ```
101
+
102
+ ### Run the Generated Project
103
+
104
+ ```bash
105
+ cd myproject
106
+ pip install -r requirements.txt
107
+ make dev
108
+ ```
109
+
110
+ The project runs out of the box with SQLite - no configuration needed.
111
+
112
+ ## CLI Reference
113
+
114
+ ```bash
115
+ # Show help
116
+ fastscaff --help
117
+
118
+ # Create new project
119
+ fastscaff new <project_name>
120
+ --orm ORM choice: tortoise or sqlalchemy (default: tortoise)
121
+ --output Output directory (default: current directory)
122
+ --force Force overwrite existing directory
123
+ --with-rbac Enable RBAC with Casbin
124
+
125
+ # Show version
126
+ fastscaff version
127
+ ```
128
+
129
+ ## Project Layers
130
+
131
+ | Layer | Directory | Responsibility |
132
+ |-------|-----------|----------------|
133
+ | API | `api/` | HTTP request handling, parameter validation |
134
+ | Service | `services/` | Business logic, transaction management |
135
+ | Repository | `repositories/` | Data access, CRUD operations |
136
+ | Model | `models/` | Database table definitions |
137
+ | Schema | `schemas/` | Request/response data structures |
138
+
139
+ ## Development
140
+
141
+ ```bash
142
+ # Install dev dependencies
143
+ pip install -e ".[dev]"
144
+
145
+ # Run tests
146
+ pytest
147
+
148
+ # Code formatting
149
+ ruff check --fix .
150
+ ruff format .
151
+ ```
152
+
153
+ ## License
154
+
155
+ MIT
@@ -0,0 +1,130 @@
1
+ # FastScaff
2
+
3
+ FastAPI project scaffolding tool - quickly create standardized FastAPI project structures.
4
+
5
+ [中文文档](README_CN.md)
6
+
7
+ ## Features
8
+
9
+ - One-click creation of standardized FastAPI project structure
10
+ - Multiple ORM support (Tortoise ORM / SQLAlchemy)
11
+ - Built-in JWT authentication
12
+ - Structured logging with structlog
13
+ - Unified exception handling
14
+ - Docker deployment support
15
+ - Testing framework integration
16
+ - SQLite by default (zero configuration)
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ # Install from source
22
+ cd FastScaff
23
+ pip install -e .
24
+
25
+ # Or install dependencies directly
26
+ pip install -r requirements.txt
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ ### Create a New Project
32
+
33
+ ```bash
34
+ # Using SQLAlchemy (with SQLite by default)
35
+ fastscaff new myproject --orm sqlalchemy
36
+
37
+ # Using Tortoise ORM
38
+ fastscaff new myproject --orm tortoise
39
+
40
+ # Specify output directory
41
+ fastscaff new myproject --output /path/to/dir
42
+ ```
43
+
44
+ ### Generated Project Structure
45
+
46
+ ```
47
+ myproject/
48
+ ├── app/
49
+ │ ├── main.py
50
+ │ ├── core/
51
+ │ │ ├── config.py
52
+ │ │ ├── database.py
53
+ │ │ ├── logger.py
54
+ │ │ ├── lifespan.py
55
+ │ │ ├── security.py
56
+ │ │ └── redis.py
57
+ │ ├── api/
58
+ │ │ └── v1/
59
+ │ │ ├── router.py
60
+ │ │ ├── deps.py
61
+ │ │ └── endpoints/
62
+ │ ├── models/
63
+ │ ├── schemas/
64
+ │ ├── repositories/
65
+ │ ├── services/
66
+ │ ├── middleware/
67
+ │ ├── exceptions/
68
+ │ └── utils/
69
+ ├── tests/
70
+ ├── .env.example
71
+ ├── Dockerfile
72
+ ├── docker-compose.yml
73
+ ├── Makefile
74
+ └── requirements.txt
75
+ ```
76
+
77
+ ### Run the Generated Project
78
+
79
+ ```bash
80
+ cd myproject
81
+ pip install -r requirements.txt
82
+ make dev
83
+ ```
84
+
85
+ The project runs out of the box with SQLite - no configuration needed.
86
+
87
+ ## CLI Reference
88
+
89
+ ```bash
90
+ # Show help
91
+ fastscaff --help
92
+
93
+ # Create new project
94
+ fastscaff new <project_name>
95
+ --orm ORM choice: tortoise or sqlalchemy (default: tortoise)
96
+ --output Output directory (default: current directory)
97
+ --force Force overwrite existing directory
98
+ --with-rbac Enable RBAC with Casbin
99
+
100
+ # Show version
101
+ fastscaff version
102
+ ```
103
+
104
+ ## Project Layers
105
+
106
+ | Layer | Directory | Responsibility |
107
+ |-------|-----------|----------------|
108
+ | API | `api/` | HTTP request handling, parameter validation |
109
+ | Service | `services/` | Business logic, transaction management |
110
+ | Repository | `repositories/` | Data access, CRUD operations |
111
+ | Model | `models/` | Database table definitions |
112
+ | Schema | `schemas/` | Request/response data structures |
113
+
114
+ ## Development
115
+
116
+ ```bash
117
+ # Install dev dependencies
118
+ pip install -e ".[dev]"
119
+
120
+ # Run tests
121
+ pytest
122
+
123
+ # Code formatting
124
+ ruff check --fix .
125
+ ruff format .
126
+ ```
127
+
128
+ ## License
129
+
130
+ MIT
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
@@ -0,0 +1,128 @@
1
+ from pathlib import Path
2
+ from typing import Optional
3
+
4
+ import typer
5
+ from rich.console import Console
6
+ from rich.panel import Panel
7
+
8
+ from fastscaff import __version__
9
+ from fastscaff.generator import ProjectGenerator
10
+
11
+ app = typer.Typer(
12
+ name="fastscaff",
13
+ help="FastAPI project scaffolding tool",
14
+ add_completion=False,
15
+ )
16
+ console = Console()
17
+
18
+
19
+ def _version_callback(value: bool) -> None:
20
+ if value:
21
+ console.print(f"FastScaff version: {__version__}")
22
+ raise typer.Exit()
23
+
24
+
25
+ @app.callback()
26
+ def main(
27
+ _version: bool = typer.Option(
28
+ None,
29
+ "--version",
30
+ "-v",
31
+ help="Show version",
32
+ callback=_version_callback,
33
+ is_eager=True,
34
+ ),
35
+ ) -> None:
36
+ pass
37
+
38
+
39
+ @app.command()
40
+ def new(
41
+ project_name: str = typer.Argument(..., help="Project name"),
42
+ orm: str = typer.Option(
43
+ "tortoise",
44
+ "--orm",
45
+ "-o",
46
+ help="ORM choice: tortoise or sqlalchemy",
47
+ ),
48
+ output: Optional[Path] = typer.Option(
49
+ None,
50
+ "--output",
51
+ "-d",
52
+ help="Output directory",
53
+ ),
54
+ with_rbac: bool = typer.Option(
55
+ False,
56
+ "--with-rbac",
57
+ help="Include Casbin RBAC support",
58
+ ),
59
+ force: bool = typer.Option(
60
+ False,
61
+ "--force",
62
+ "-f",
63
+ help="Overwrite existing directory",
64
+ ),
65
+ ) -> None:
66
+ if orm not in ("tortoise", "sqlalchemy"):
67
+ console.print(f"[red]Error: ORM must be 'tortoise' or 'sqlalchemy', got '{orm}'[/red]")
68
+ raise typer.Exit(1)
69
+
70
+ if not project_name.replace("_", "").replace("-", "").isalnum():
71
+ console.print(
72
+ "[red]Error: Project name can only contain alphanumeric, underscores and hyphens[/red]"
73
+ )
74
+ raise typer.Exit(1)
75
+
76
+ output_path = output or Path.cwd()
77
+ project_path = output_path / project_name
78
+
79
+ if project_path.exists() and not force:
80
+ console.print(
81
+ f"[red]Error: Directory '{project_path}' already exists. Use --force to overwrite[/red]"
82
+ )
83
+ raise typer.Exit(1)
84
+
85
+ features = []
86
+ if with_rbac:
87
+ features.append("RBAC (Casbin)")
88
+
89
+ console.print(Panel.fit(
90
+ f"[bold green]Creating project[/bold green]\n\n"
91
+ f"Name: [cyan]{project_name}[/cyan]\n"
92
+ f"ORM: [cyan]{orm}[/cyan]\n"
93
+ f"Features: [cyan]{', '.join(features) if features else 'None'}[/cyan]\n"
94
+ f"Path: [cyan]{project_path}[/cyan]",
95
+ title="FastScaff",
96
+ border_style="blue",
97
+ ))
98
+
99
+ try:
100
+ generator = ProjectGenerator(
101
+ project_name=project_name,
102
+ orm=orm,
103
+ output_path=project_path,
104
+ with_rbac=with_rbac,
105
+ )
106
+ generator.generate()
107
+
108
+ console.print("\n[bold green]Project created successfully.[/bold green]\n")
109
+ console.print(Panel.fit(
110
+ f"cd {project_name}\n"
111
+ f"pip install -r requirements.txt\n"
112
+ f"make dev",
113
+ title="Next steps",
114
+ border_style="green",
115
+ ))
116
+
117
+ except Exception as e:
118
+ console.print(f"[red]Error: {e}[/red]")
119
+ raise typer.Exit(1) from None
120
+
121
+
122
+ @app.command()
123
+ def version() -> None:
124
+ console.print(f"FastScaff version: {__version__}")
125
+
126
+
127
+ if __name__ == "__main__":
128
+ app()
@@ -0,0 +1,258 @@
1
+ from pathlib import Path
2
+ from typing import Any
3
+
4
+ from jinja2 import Environment, FileSystemLoader
5
+ from rich.console import Console
6
+ from rich.progress import Progress, SpinnerColumn, TextColumn
7
+
8
+ console = Console()
9
+ TEMPLATES_DIR = Path(__file__).parent / "templates"
10
+
11
+
12
+ class ProjectGenerator:
13
+
14
+ def __init__(
15
+ self,
16
+ project_name: str,
17
+ orm: str,
18
+ output_path: Path,
19
+ with_rbac: bool = False,
20
+ ) -> None:
21
+ self.project_name = project_name
22
+ self.orm = orm
23
+ self.output_path = output_path
24
+ self.with_rbac = with_rbac
25
+
26
+ self.env = Environment(
27
+ loader=FileSystemLoader(str(TEMPLATES_DIR)),
28
+ keep_trailing_newline=True,
29
+ )
30
+
31
+ self.context: dict[str, Any] = {
32
+ "project_name": project_name,
33
+ "project_name_snake": project_name.replace("-", "_"),
34
+ "orm": orm,
35
+ "is_tortoise": orm == "tortoise",
36
+ "is_sqlalchemy": orm == "sqlalchemy",
37
+ "with_rbac": with_rbac,
38
+ }
39
+
40
+ def generate(self) -> None:
41
+ with Progress(
42
+ SpinnerColumn(),
43
+ TextColumn("[progress.description]{task.description}"),
44
+ console=console,
45
+ ) as progress:
46
+ task = progress.add_task("Generating project...", total=None)
47
+
48
+ self.output_path.mkdir(parents=True, exist_ok=True)
49
+
50
+ progress.update(task, description="Generating base files...")
51
+ self._generate_base_files()
52
+
53
+ progress.update(task, description="Generating application code...")
54
+ self._generate_app_structure()
55
+
56
+ progress.update(task, description="Generating tests...")
57
+ self._generate_tests()
58
+
59
+ progress.update(task, description="Done")
60
+
61
+ def _generate_base_files(self) -> None:
62
+ base_files = [
63
+ ("base/env.example.jinja2", ".env.example"),
64
+ ("base/gitignore.jinja2", ".gitignore"),
65
+ ("base/pre-commit-config.yaml.jinja2", ".pre-commit-config.yaml"),
66
+ ("base/Dockerfile.jinja2", "Dockerfile"),
67
+ ("base/docker-compose.yml.jinja2", "docker-compose.yml"),
68
+ ("base/Makefile.jinja2", "Makefile"),
69
+ ("base/pyproject.toml.jinja2", "pyproject.toml"),
70
+ ("base/requirements.txt.jinja2", "requirements.txt"),
71
+ ("base/README.md.jinja2", "README.md"),
72
+ ]
73
+
74
+ for template_path, output_name in base_files:
75
+ self._render_template(template_path, output_name)
76
+
77
+ def _generate_app_structure(self) -> None:
78
+ app_dir = self.output_path / "app"
79
+ app_dir.mkdir(exist_ok=True)
80
+
81
+ self._render_template("app/__init__.py.jinja2", "app/__init__.py")
82
+ self._render_template("app/main.py.jinja2", "app/main.py")
83
+
84
+ self._generate_core()
85
+ self._generate_api()
86
+ self._generate_models()
87
+ self._generate_schemas()
88
+ self._generate_repositories()
89
+ self._generate_services()
90
+ self._generate_middleware()
91
+ self._generate_exceptions()
92
+ self._generate_utils()
93
+
94
+ def _generate_core(self) -> None:
95
+ core_dir = self.output_path / "app" / "core"
96
+ core_dir.mkdir(exist_ok=True)
97
+
98
+ core_files = [
99
+ ("app/core/__init__.py.jinja2", "app/core/__init__.py"),
100
+ ("app/core/config.py.jinja2", "app/core/config.py"),
101
+ ("app/core/logger.py.jinja2", "app/core/logger.py"),
102
+ ("app/core/lifespan.py.jinja2", "app/core/lifespan.py"),
103
+ ("app/core/security.py.jinja2", "app/core/security.py"),
104
+ ("app/core/singleton.py.jinja2", "app/core/singleton.py"),
105
+ ("app/core/redis.py.jinja2", "app/core/redis.py"),
106
+ ]
107
+
108
+ for template_path, output_name in core_files:
109
+ self._render_template(template_path, output_name)
110
+
111
+ if self.orm == "tortoise":
112
+ self._render_template("app/core/database_tortoise.py.jinja2", "app/core/database.py")
113
+ else:
114
+ self._render_template("app/core/database_sqlalchemy.py.jinja2", "app/core/database.py")
115
+
116
+ if self.with_rbac:
117
+ self._render_template("app/core/rbac.py.jinja2", "app/core/rbac.py")
118
+
119
+ def _generate_api(self) -> None:
120
+ api_v1_dir = self.output_path / "app" / "api" / "v1" / "endpoints"
121
+ api_v1_dir.mkdir(parents=True, exist_ok=True)
122
+
123
+ api_files = [
124
+ ("app/api/__init__.py.jinja2", "app/api/__init__.py"),
125
+ ("app/api/v1/__init__.py.jinja2", "app/api/v1/__init__.py"),
126
+ ("app/api/v1/router.py.jinja2", "app/api/v1/router.py"),
127
+ ("app/api/v1/endpoints/__init__.py.jinja2", "app/api/v1/endpoints/__init__.py"),
128
+ ("app/api/v1/endpoints/health.py.jinja2", "app/api/v1/endpoints/health.py"),
129
+ ("app/api/v1/endpoints/auth.py.jinja2", "app/api/v1/endpoints/auth.py"),
130
+ ("app/api/v1/endpoints/users.py.jinja2", "app/api/v1/endpoints/users.py"),
131
+ ]
132
+
133
+ for template_path, output_name in api_files:
134
+ self._render_template(template_path, output_name)
135
+
136
+ def _generate_models(self) -> None:
137
+ models_dir = self.output_path / "app" / "models"
138
+ models_dir.mkdir(exist_ok=True)
139
+
140
+ self._render_template("app/models/__init__.py.jinja2", "app/models/__init__.py")
141
+
142
+ if self.orm == "tortoise":
143
+ self._render_template("app/models/base_tortoise.py.jinja2", "app/models/base.py")
144
+ self._render_template("app/models/user_tortoise.py.jinja2", "app/models/user.py")
145
+ else:
146
+ self._render_template("app/models/base_sqlalchemy.py.jinja2", "app/models/base.py")
147
+ self._render_template("app/models/user_sqlalchemy.py.jinja2", "app/models/user.py")
148
+
149
+ def _generate_schemas(self) -> None:
150
+ schemas_dir = self.output_path / "app" / "schemas"
151
+ schemas_dir.mkdir(exist_ok=True)
152
+
153
+ schema_files = [
154
+ ("app/schemas/__init__.py.jinja2", "app/schemas/__init__.py"),
155
+ ("app/schemas/base.py.jinja2", "app/schemas/base.py"),
156
+ ("app/schemas/auth.py.jinja2", "app/schemas/auth.py"),
157
+ ("app/schemas/user.py.jinja2", "app/schemas/user.py"),
158
+ ]
159
+
160
+ for template_path, output_name in schema_files:
161
+ self._render_template(template_path, output_name)
162
+
163
+ def _generate_repositories(self) -> None:
164
+ repo_dir = self.output_path / "app" / "repositories"
165
+ repo_dir.mkdir(exist_ok=True)
166
+
167
+ self._render_template("app/repositories/__init__.py.jinja2", "app/repositories/__init__.py")
168
+
169
+ if self.orm == "tortoise":
170
+ self._render_template("app/repositories/base_tortoise.py.jinja2", "app/repositories/base.py")
171
+ self._render_template("app/repositories/user_tortoise.py.jinja2", "app/repositories/user.py")
172
+ else:
173
+ self._render_template("app/repositories/base_sqlalchemy.py.jinja2", "app/repositories/base.py")
174
+ self._render_template("app/repositories/user_sqlalchemy.py.jinja2", "app/repositories/user.py")
175
+
176
+ def _generate_services(self) -> None:
177
+ services_dir = self.output_path / "app" / "services"
178
+ services_dir.mkdir(exist_ok=True)
179
+
180
+ if self.orm == "tortoise":
181
+ self._render_template("app/services/__init___tortoise.py.jinja2", "app/services/__init__.py")
182
+ self._render_template("app/services/auth_tortoise.py.jinja2", "app/services/auth.py")
183
+ self._render_template("app/services/user_tortoise.py.jinja2", "app/services/user.py")
184
+ else:
185
+ self._render_template("app/services/__init___sqlalchemy.py.jinja2", "app/services/__init__.py")
186
+ self._render_template("app/services/auth_sqlalchemy.py.jinja2", "app/services/auth.py")
187
+ self._render_template("app/services/user_sqlalchemy.py.jinja2", "app/services/user.py")
188
+
189
+ def _generate_middleware(self) -> None:
190
+ middleware_dir = self.output_path / "app" / "middleware"
191
+ middleware_dir.mkdir(exist_ok=True)
192
+
193
+ middleware_files = [
194
+ ("app/middleware/__init__.py.jinja2", "app/middleware/__init__.py"),
195
+ ("app/middleware/logging.py.jinja2", "app/middleware/logging.py"),
196
+ ("app/middleware/cors.py.jinja2", "app/middleware/cors.py"),
197
+ ("app/middleware/security.py.jinja2", "app/middleware/security.py"),
198
+ ("app/middleware/jwt.py.jinja2", "app/middleware/jwt.py"),
199
+ ("app/middleware/sign.py.jinja2", "app/middleware/sign.py"),
200
+ ("app/middleware/tracing.py.jinja2", "app/middleware/tracing.py"),
201
+ ]
202
+
203
+ for template_path, output_name in middleware_files:
204
+ self._render_template(template_path, output_name)
205
+
206
+ def _generate_exceptions(self) -> None:
207
+ exceptions_dir = self.output_path / "app" / "exceptions"
208
+ exceptions_dir.mkdir(exist_ok=True)
209
+
210
+ exception_files = [
211
+ ("app/exceptions/__init__.py.jinja2", "app/exceptions/__init__.py"),
212
+ ("app/exceptions/base.py.jinja2", "app/exceptions/base.py"),
213
+ ("app/exceptions/handlers.py.jinja2", "app/exceptions/handlers.py"),
214
+ ]
215
+
216
+ for template_path, output_name in exception_files:
217
+ self._render_template(template_path, output_name)
218
+
219
+ def _generate_utils(self) -> None:
220
+ utils_dir = self.output_path / "app" / "utils"
221
+ utils_dir.mkdir(exist_ok=True)
222
+
223
+ utils_files = [
224
+ ("app/utils/__init__.py.jinja2", "app/utils/__init__.py"),
225
+ ("app/utils/snowflake.py.jinja2", "app/utils/snowflake.py"),
226
+ ("app/utils/sort_helper.py.jinja2", "app/utils/sort_helper.py"),
227
+ ("app/utils/rate_limiter.py.jinja2", "app/utils/rate_limiter.py"),
228
+ ("app/utils/auth.py.jinja2", "app/utils/auth.py"),
229
+ ("app/utils/cache.py.jinja2", "app/utils/cache.py"),
230
+ ]
231
+
232
+ for template_path, output_name in utils_files:
233
+ self._render_template(template_path, output_name)
234
+
235
+ def _generate_tests(self) -> None:
236
+ tests_dir = self.output_path / "tests"
237
+ tests_dir.mkdir(exist_ok=True)
238
+
239
+ api_tests_dir = tests_dir / "api"
240
+ api_tests_dir.mkdir(exist_ok=True)
241
+
242
+ test_files = [
243
+ ("tests/__init__.py.jinja2", "tests/__init__.py"),
244
+ ("tests/conftest.py.jinja2", "tests/conftest.py"),
245
+ ("tests/api/__init__.py.jinja2", "tests/api/__init__.py"),
246
+ ("tests/api/test_health.py.jinja2", "tests/api/test_health.py"),
247
+ ]
248
+
249
+ for template_path, output_name in test_files:
250
+ self._render_template(template_path, output_name)
251
+
252
+ def _render_template(self, template_path: str, output_name: str) -> None:
253
+ template = self.env.get_template(template_path)
254
+ content = template.render(**self.context)
255
+
256
+ output_file = self.output_path / output_name
257
+ output_file.parent.mkdir(parents=True, exist_ok=True)
258
+ output_file.write_text(content, encoding="utf-8")