popeye-cli 1.7.0 → 1.9.0
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.
- package/README.md +148 -7
- package/cheatsheet.md +440 -0
- package/dist/cli/commands/db.d.ts +10 -0
- package/dist/cli/commands/db.d.ts.map +1 -0
- package/dist/cli/commands/db.js +240 -0
- package/dist/cli/commands/db.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +18 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/doctor.js +255 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/index.d.ts +3 -0
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +3 -0
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/review.d.ts +31 -0
- package/dist/cli/commands/review.d.ts.map +1 -0
- package/dist/cli/commands/review.js +156 -0
- package/dist/cli/commands/review.js.map +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +4 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/interactive.d.ts.map +1 -1
- package/dist/cli/interactive.js +218 -61
- package/dist/cli/interactive.js.map +1 -1
- package/dist/generators/admin-wizard.d.ts +25 -0
- package/dist/generators/admin-wizard.d.ts.map +1 -0
- package/dist/generators/admin-wizard.js +123 -0
- package/dist/generators/admin-wizard.js.map +1 -0
- package/dist/generators/all.d.ts.map +1 -1
- package/dist/generators/all.js +10 -3
- package/dist/generators/all.js.map +1 -1
- package/dist/generators/database.d.ts +58 -0
- package/dist/generators/database.d.ts.map +1 -0
- package/dist/generators/database.js +229 -0
- package/dist/generators/database.js.map +1 -0
- package/dist/generators/fullstack.d.ts.map +1 -1
- package/dist/generators/fullstack.js +23 -7
- package/dist/generators/fullstack.js.map +1 -1
- package/dist/generators/index.d.ts +2 -0
- package/dist/generators/index.d.ts.map +1 -1
- package/dist/generators/index.js +2 -0
- package/dist/generators/index.js.map +1 -1
- package/dist/generators/templates/admin-wizard-python.d.ts +32 -0
- package/dist/generators/templates/admin-wizard-python.d.ts.map +1 -0
- package/dist/generators/templates/admin-wizard-python.js +425 -0
- package/dist/generators/templates/admin-wizard-python.js.map +1 -0
- package/dist/generators/templates/admin-wizard-react.d.ts +48 -0
- package/dist/generators/templates/admin-wizard-react.d.ts.map +1 -0
- package/dist/generators/templates/admin-wizard-react.js +554 -0
- package/dist/generators/templates/admin-wizard-react.js.map +1 -0
- package/dist/generators/templates/database-docker.d.ts +23 -0
- package/dist/generators/templates/database-docker.d.ts.map +1 -0
- package/dist/generators/templates/database-docker.js +221 -0
- package/dist/generators/templates/database-docker.js.map +1 -0
- package/dist/generators/templates/database-python.d.ts +54 -0
- package/dist/generators/templates/database-python.d.ts.map +1 -0
- package/dist/generators/templates/database-python.js +723 -0
- package/dist/generators/templates/database-python.js.map +1 -0
- package/dist/generators/templates/database-typescript.d.ts +34 -0
- package/dist/generators/templates/database-typescript.d.ts.map +1 -0
- package/dist/generators/templates/database-typescript.js +232 -0
- package/dist/generators/templates/database-typescript.js.map +1 -0
- package/dist/generators/templates/fullstack.d.ts.map +1 -1
- package/dist/generators/templates/fullstack.js +29 -0
- package/dist/generators/templates/fullstack.js.map +1 -1
- package/dist/generators/templates/index.d.ts +5 -0
- package/dist/generators/templates/index.d.ts.map +1 -1
- package/dist/generators/templates/index.js +5 -0
- package/dist/generators/templates/index.js.map +1 -1
- package/dist/state/index.d.ts +10 -0
- package/dist/state/index.d.ts.map +1 -1
- package/dist/state/index.js +21 -0
- package/dist/state/index.js.map +1 -1
- package/dist/types/audit.d.ts +623 -0
- package/dist/types/audit.d.ts.map +1 -0
- package/dist/types/audit.js +240 -0
- package/dist/types/audit.js.map +1 -0
- package/dist/types/database-runtime.d.ts +86 -0
- package/dist/types/database-runtime.d.ts.map +1 -0
- package/dist/types/database-runtime.js +61 -0
- package/dist/types/database-runtime.js.map +1 -0
- package/dist/types/database.d.ts +85 -0
- package/dist/types/database.d.ts.map +1 -0
- package/dist/types/database.js +71 -0
- package/dist/types/database.js.map +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +4 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/workflow.d.ts +36 -0
- package/dist/types/workflow.d.ts.map +1 -1
- package/dist/types/workflow.js +7 -0
- package/dist/types/workflow.js.map +1 -1
- package/dist/workflow/audit-analyzer.d.ts +58 -0
- package/dist/workflow/audit-analyzer.d.ts.map +1 -0
- package/dist/workflow/audit-analyzer.js +420 -0
- package/dist/workflow/audit-analyzer.js.map +1 -0
- package/dist/workflow/audit-mode.d.ts +28 -0
- package/dist/workflow/audit-mode.d.ts.map +1 -0
- package/dist/workflow/audit-mode.js +169 -0
- package/dist/workflow/audit-mode.js.map +1 -0
- package/dist/workflow/audit-recovery.d.ts +61 -0
- package/dist/workflow/audit-recovery.d.ts.map +1 -0
- package/dist/workflow/audit-recovery.js +242 -0
- package/dist/workflow/audit-recovery.js.map +1 -0
- package/dist/workflow/audit-reporter.d.ts +65 -0
- package/dist/workflow/audit-reporter.d.ts.map +1 -0
- package/dist/workflow/audit-reporter.js +301 -0
- package/dist/workflow/audit-reporter.js.map +1 -0
- package/dist/workflow/audit-scanner.d.ts +87 -0
- package/dist/workflow/audit-scanner.d.ts.map +1 -0
- package/dist/workflow/audit-scanner.js +768 -0
- package/dist/workflow/audit-scanner.js.map +1 -0
- package/dist/workflow/db-setup-runner.d.ts +63 -0
- package/dist/workflow/db-setup-runner.d.ts.map +1 -0
- package/dist/workflow/db-setup-runner.js +336 -0
- package/dist/workflow/db-setup-runner.js.map +1 -0
- package/dist/workflow/db-state-machine.d.ts +30 -0
- package/dist/workflow/db-state-machine.d.ts.map +1 -0
- package/dist/workflow/db-state-machine.js +51 -0
- package/dist/workflow/db-state-machine.js.map +1 -0
- package/dist/workflow/index.d.ts +7 -0
- package/dist/workflow/index.d.ts.map +1 -1
- package/dist/workflow/index.js +7 -0
- package/dist/workflow/index.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/commands/db.ts +281 -0
- package/src/cli/commands/doctor.ts +273 -0
- package/src/cli/commands/index.ts +3 -0
- package/src/cli/commands/review.ts +187 -0
- package/src/cli/index.ts +6 -0
- package/src/cli/interactive.ts +174 -4
- package/src/generators/admin-wizard.ts +146 -0
- package/src/generators/all.ts +10 -3
- package/src/generators/database.ts +286 -0
- package/src/generators/fullstack.ts +26 -9
- package/src/generators/index.ts +12 -0
- package/src/generators/templates/admin-wizard-python.ts +431 -0
- package/src/generators/templates/admin-wizard-react.ts +560 -0
- package/src/generators/templates/database-docker.ts +227 -0
- package/src/generators/templates/database-python.ts +734 -0
- package/src/generators/templates/database-typescript.ts +238 -0
- package/src/generators/templates/fullstack.ts +29 -0
- package/src/generators/templates/index.ts +5 -0
- package/src/state/index.ts +28 -0
- package/src/types/audit.ts +294 -0
- package/src/types/database-runtime.ts +69 -0
- package/src/types/database.ts +84 -0
- package/src/types/index.ts +29 -0
- package/src/types/workflow.ts +20 -0
- package/src/workflow/audit-analyzer.ts +491 -0
- package/src/workflow/audit-mode.ts +240 -0
- package/src/workflow/audit-recovery.ts +284 -0
- package/src/workflow/audit-reporter.ts +370 -0
- package/src/workflow/audit-scanner.ts +873 -0
- package/src/workflow/db-setup-runner.ts +391 -0
- package/src/workflow/db-state-machine.ts +58 -0
- package/src/workflow/index.ts +7 -0
- package/tests/cli/commands/review.test.ts +52 -0
- package/tests/generators/admin-wizard-orchestrator.test.ts +64 -0
- package/tests/generators/admin-wizard-templates.test.ts +366 -0
- package/tests/generators/cross-phase-integration.test.ts +383 -0
- package/tests/generators/database.test.ts +456 -0
- package/tests/generators/fe-be-db-integration.test.ts +613 -0
- package/tests/types/audit.test.ts +250 -0
- package/tests/types/database-runtime.test.ts +158 -0
- package/tests/types/database.test.ts +187 -0
- package/tests/workflow/audit-analyzer.test.ts +281 -0
- package/tests/workflow/audit-mode.test.ts +114 -0
- package/tests/workflow/audit-recovery.test.ts +237 -0
- package/tests/workflow/audit-reporter.test.ts +254 -0
- package/tests/workflow/audit-scanner.test.ts +270 -0
- package/tests/workflow/db-setup-runner.test.ts +211 -0
- package/tests/workflow/db-state-machine.test.ts +117 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-wizard-python.d.ts","sourceRoot":"","sources":["../../../src/generators/templates/admin-wizard-python.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;GAIG;AACH,wBAAgB,2BAA2B,IAAI,MAAM,CA8CpD;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAW/C;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAyQjE;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAC1C,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,GAClB,MAAM,CAuER"}
|
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Admin Wizard Python backend templates
|
|
3
|
+
* Generates FastAPI middleware and admin DB routes for the setup wizard
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Generate admin auth middleware that validates X-Admin-Token header
|
|
7
|
+
*
|
|
8
|
+
* @returns Python source for admin_auth.py
|
|
9
|
+
*/
|
|
10
|
+
export function generateAdminAuthMiddleware() {
|
|
11
|
+
return `"""
|
|
12
|
+
Admin authentication middleware.
|
|
13
|
+
|
|
14
|
+
Validates the X-Admin-Token header against the ADMIN_SETUP_TOKEN
|
|
15
|
+
environment variable. Used to protect admin setup endpoints.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import logging
|
|
19
|
+
import os
|
|
20
|
+
|
|
21
|
+
from fastapi import Header, HTTPException
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
async def require_admin_token(
|
|
27
|
+
x_admin_token: str = Header(..., alias="X-Admin-Token"),
|
|
28
|
+
) -> str:
|
|
29
|
+
"""
|
|
30
|
+
FastAPI dependency that validates the admin setup token.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
x_admin_token (str): Token from X-Admin-Token header.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
str: The validated token.
|
|
37
|
+
|
|
38
|
+
Raises:
|
|
39
|
+
HTTPException: 403 if token is missing, invalid, or not configured.
|
|
40
|
+
"""
|
|
41
|
+
expected = os.getenv("ADMIN_SETUP_TOKEN", "")
|
|
42
|
+
if not expected:
|
|
43
|
+
logger.warning("ADMIN_SETUP_TOKEN is not configured")
|
|
44
|
+
raise HTTPException(
|
|
45
|
+
status_code=403,
|
|
46
|
+
detail="Admin setup token is not configured on the server.",
|
|
47
|
+
)
|
|
48
|
+
if x_admin_token != expected:
|
|
49
|
+
logger.warning("Invalid admin token attempt")
|
|
50
|
+
raise HTTPException(
|
|
51
|
+
status_code=403,
|
|
52
|
+
detail="Invalid admin token.",
|
|
53
|
+
)
|
|
54
|
+
return x_admin_token
|
|
55
|
+
`;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Generate middleware package __init__.py
|
|
59
|
+
*
|
|
60
|
+
* @returns Python source for middleware/__init__.py
|
|
61
|
+
*/
|
|
62
|
+
export function generateMiddlewareInit() {
|
|
63
|
+
return `"""
|
|
64
|
+
Middleware package.
|
|
65
|
+
|
|
66
|
+
Re-exports authentication dependencies.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
from .admin_auth import require_admin_token
|
|
70
|
+
|
|
71
|
+
__all__ = ["require_admin_token"]
|
|
72
|
+
`;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Generate admin DB routes with 4 endpoints for the setup wizard
|
|
76
|
+
*
|
|
77
|
+
* @param packageName - Python package name (snake_case)
|
|
78
|
+
* @returns Python source for routes/admin_db.py
|
|
79
|
+
*/
|
|
80
|
+
export function generateAdminDbRoutes(packageName) {
|
|
81
|
+
return `"""
|
|
82
|
+
Admin database setup routes.
|
|
83
|
+
|
|
84
|
+
Provides endpoints for the admin wizard to configure and
|
|
85
|
+
initialize the database without using the CLI.
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
import logging
|
|
89
|
+
import os
|
|
90
|
+
import subprocess
|
|
91
|
+
from pathlib import Path
|
|
92
|
+
|
|
93
|
+
from fastapi import APIRouter, Depends
|
|
94
|
+
from pydantic import BaseModel
|
|
95
|
+
|
|
96
|
+
from ${packageName}.middleware.admin_auth import require_admin_token
|
|
97
|
+
|
|
98
|
+
logger = logging.getLogger(__name__)
|
|
99
|
+
|
|
100
|
+
router = APIRouter(
|
|
101
|
+
prefix="/api/admin/db",
|
|
102
|
+
tags=["admin"],
|
|
103
|
+
dependencies=[Depends(require_admin_token)],
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class TestRequest(BaseModel):
|
|
108
|
+
"""Request body for connection test."""
|
|
109
|
+
|
|
110
|
+
database_url: str
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class ApplyRequest(BaseModel):
|
|
114
|
+
"""Request body for applying database setup."""
|
|
115
|
+
|
|
116
|
+
database_url: str
|
|
117
|
+
mode: str = "default"
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class StepResult(BaseModel):
|
|
121
|
+
"""Result of a single setup step."""
|
|
122
|
+
|
|
123
|
+
step: str
|
|
124
|
+
success: bool
|
|
125
|
+
message: str
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _read_env_file() -> dict[str, str]:
|
|
129
|
+
"""
|
|
130
|
+
Read key=value pairs from the backend .env file.
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
dict[str, str]: Parsed environment variables.
|
|
134
|
+
"""
|
|
135
|
+
env_path = Path(__file__).resolve().parents[3] / ".env"
|
|
136
|
+
pairs: dict[str, str] = {}
|
|
137
|
+
if env_path.exists():
|
|
138
|
+
for line in env_path.read_text().splitlines():
|
|
139
|
+
line = line.strip()
|
|
140
|
+
if line and not line.startswith("#") and "=" in line:
|
|
141
|
+
key, _, value = line.partition("=")
|
|
142
|
+
pairs[key.strip()] = value.strip()
|
|
143
|
+
return pairs
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _write_env_var(key: str, value: str) -> None:
|
|
147
|
+
"""
|
|
148
|
+
Write or update a key in the backend .env file.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
key (str): Environment variable name.
|
|
152
|
+
value (str): Environment variable value.
|
|
153
|
+
"""
|
|
154
|
+
env_path = Path(__file__).resolve().parents[3] / ".env"
|
|
155
|
+
lines: list[str] = []
|
|
156
|
+
found = False
|
|
157
|
+
if env_path.exists():
|
|
158
|
+
for line in env_path.read_text().splitlines():
|
|
159
|
+
stripped = line.strip()
|
|
160
|
+
if stripped.startswith(f"{key}="):
|
|
161
|
+
lines.append(f"{key}={value}")
|
|
162
|
+
found = True
|
|
163
|
+
else:
|
|
164
|
+
lines.append(line)
|
|
165
|
+
if not found:
|
|
166
|
+
lines.append(f"{key}={value}")
|
|
167
|
+
env_path.write_text("\\n".join(lines) + "\\n")
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _run_alembic_upgrade() -> StepResult:
|
|
171
|
+
"""
|
|
172
|
+
Run alembic upgrade head as a subprocess.
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
StepResult: Result of the migration step.
|
|
176
|
+
"""
|
|
177
|
+
backend_dir = Path(__file__).resolve().parents[3]
|
|
178
|
+
try:
|
|
179
|
+
result = subprocess.run(
|
|
180
|
+
["alembic", "upgrade", "head"],
|
|
181
|
+
cwd=str(backend_dir),
|
|
182
|
+
capture_output=True,
|
|
183
|
+
text=True,
|
|
184
|
+
timeout=60,
|
|
185
|
+
)
|
|
186
|
+
if result.returncode == 0:
|
|
187
|
+
return StepResult(
|
|
188
|
+
step="migrate",
|
|
189
|
+
success=True,
|
|
190
|
+
message="Migrations applied successfully.",
|
|
191
|
+
)
|
|
192
|
+
logger.error(f"Alembic failed: {result.stderr}")
|
|
193
|
+
return StepResult(
|
|
194
|
+
step="migrate",
|
|
195
|
+
success=False,
|
|
196
|
+
message=f"Migration failed: {result.stderr[:500]}",
|
|
197
|
+
)
|
|
198
|
+
except subprocess.TimeoutExpired:
|
|
199
|
+
return StepResult(
|
|
200
|
+
step="migrate",
|
|
201
|
+
success=False,
|
|
202
|
+
message="Migration timed out after 60 seconds.",
|
|
203
|
+
)
|
|
204
|
+
except FileNotFoundError:
|
|
205
|
+
return StepResult(
|
|
206
|
+
step="migrate",
|
|
207
|
+
success=False,
|
|
208
|
+
message="Alembic not found. Install with: pip install alembic",
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
@router.get("/status")
|
|
213
|
+
async def db_status():
|
|
214
|
+
"""
|
|
215
|
+
Get current database setup status.
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
dict: Status information including connectivity and migration state.
|
|
219
|
+
"""
|
|
220
|
+
import asyncpg
|
|
221
|
+
|
|
222
|
+
db_url = os.getenv("DATABASE_URL", "")
|
|
223
|
+
db_configured = bool(db_url)
|
|
224
|
+
status = "unconfigured"
|
|
225
|
+
last_error = None
|
|
226
|
+
migrations_applied = 0
|
|
227
|
+
|
|
228
|
+
if db_configured:
|
|
229
|
+
# Convert SQLAlchemy URL to asyncpg format if needed
|
|
230
|
+
connect_url = db_url.replace("postgresql+asyncpg://", "postgresql://")
|
|
231
|
+
try:
|
|
232
|
+
conn = await asyncpg.connect(connect_url)
|
|
233
|
+
try:
|
|
234
|
+
# Check alembic_version table
|
|
235
|
+
row = await conn.fetchval(
|
|
236
|
+
"SELECT COUNT(*) FROM alembic_version"
|
|
237
|
+
)
|
|
238
|
+
migrations_applied = row or 0
|
|
239
|
+
status = "ready"
|
|
240
|
+
except asyncpg.UndefinedTableError:
|
|
241
|
+
status = "configured"
|
|
242
|
+
finally:
|
|
243
|
+
await conn.close()
|
|
244
|
+
except Exception as exc:
|
|
245
|
+
logger.error(f"DB status check failed: {exc}")
|
|
246
|
+
status = "error"
|
|
247
|
+
last_error = str(exc)
|
|
248
|
+
|
|
249
|
+
return {
|
|
250
|
+
"status": status,
|
|
251
|
+
"mode": "default",
|
|
252
|
+
"lastError": last_error,
|
|
253
|
+
"migrationsApplied": migrations_applied,
|
|
254
|
+
"dbUrlConfigured": db_configured,
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
@router.post("/test")
|
|
259
|
+
async def test_connection(body: TestRequest):
|
|
260
|
+
"""
|
|
261
|
+
Test database connectivity without saving the URL.
|
|
262
|
+
|
|
263
|
+
Args:
|
|
264
|
+
body (TestRequest): Contains the database_url to test.
|
|
265
|
+
|
|
266
|
+
Returns:
|
|
267
|
+
dict: Success flag and message.
|
|
268
|
+
"""
|
|
269
|
+
import asyncpg
|
|
270
|
+
|
|
271
|
+
connect_url = body.database_url.replace(
|
|
272
|
+
"postgresql+asyncpg://", "postgresql://"
|
|
273
|
+
)
|
|
274
|
+
try:
|
|
275
|
+
conn = await asyncpg.connect(connect_url)
|
|
276
|
+
await conn.execute("SELECT 1")
|
|
277
|
+
await conn.close()
|
|
278
|
+
return {"success": True, "message": "Connection successful."}
|
|
279
|
+
except Exception as exc:
|
|
280
|
+
logger.error(f"Connection test failed: {exc}")
|
|
281
|
+
return {"success": False, "message": str(exc)}
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
@router.post("/apply")
|
|
285
|
+
async def apply_setup(body: ApplyRequest):
|
|
286
|
+
"""
|
|
287
|
+
Save DATABASE_URL to .env and run migrations.
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
body (ApplyRequest): Contains database_url and optional mode.
|
|
291
|
+
|
|
292
|
+
Returns:
|
|
293
|
+
dict: List of step results and final status.
|
|
294
|
+
"""
|
|
295
|
+
steps: list[dict] = []
|
|
296
|
+
|
|
297
|
+
# Step 1: Write DATABASE_URL to .env
|
|
298
|
+
try:
|
|
299
|
+
_write_env_var("DATABASE_URL", body.database_url)
|
|
300
|
+
os.environ["DATABASE_URL"] = body.database_url
|
|
301
|
+
steps.append({"step": "save_url", "success": True, "message": "DATABASE_URL saved."})
|
|
302
|
+
except Exception as exc:
|
|
303
|
+
steps.append({"step": "save_url", "success": False, "message": str(exc)})
|
|
304
|
+
return {"steps": steps, "status": "error"}
|
|
305
|
+
|
|
306
|
+
# Step 2: Run migrations
|
|
307
|
+
migrate_result = _run_alembic_upgrade()
|
|
308
|
+
steps.append(migrate_result.model_dump())
|
|
309
|
+
|
|
310
|
+
final_status = "ready" if migrate_result.success else "error"
|
|
311
|
+
return {"steps": steps, "status": final_status}
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
@router.post("/retry")
|
|
315
|
+
async def retry_setup():
|
|
316
|
+
"""
|
|
317
|
+
Re-run migrations using the existing DATABASE_URL from .env.
|
|
318
|
+
|
|
319
|
+
Returns:
|
|
320
|
+
dict: List of step results and final status.
|
|
321
|
+
"""
|
|
322
|
+
steps: list[dict] = []
|
|
323
|
+
|
|
324
|
+
# Read DATABASE_URL from .env
|
|
325
|
+
env_vars = _read_env_file()
|
|
326
|
+
db_url = env_vars.get("DATABASE_URL", "")
|
|
327
|
+
if not db_url:
|
|
328
|
+
return {
|
|
329
|
+
"steps": [
|
|
330
|
+
{"step": "read_env", "success": False, "message": "DATABASE_URL not found in .env"}
|
|
331
|
+
],
|
|
332
|
+
"status": "error",
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
os.environ["DATABASE_URL"] = db_url
|
|
336
|
+
steps.append({"step": "read_env", "success": True, "message": "DATABASE_URL loaded from .env."})
|
|
337
|
+
|
|
338
|
+
# Run migrations
|
|
339
|
+
migrate_result = _run_alembic_upgrade()
|
|
340
|
+
steps.append(migrate_result.model_dump())
|
|
341
|
+
|
|
342
|
+
final_status = "ready" if migrate_result.success else "error"
|
|
343
|
+
return {"steps": steps, "status": final_status}
|
|
344
|
+
`;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Generate extended FastAPI main.py that includes admin and health DB routers
|
|
348
|
+
*
|
|
349
|
+
* @param projectName - Human-readable project name
|
|
350
|
+
* @param packageName - Python package name (snake_case)
|
|
351
|
+
* @returns Python source for main.py
|
|
352
|
+
*/
|
|
353
|
+
export function generateFastAPIMainWithAdmin(projectName, packageName) {
|
|
354
|
+
return `"""
|
|
355
|
+
${projectName} Backend API
|
|
356
|
+
|
|
357
|
+
FastAPI application entry point with admin wizard and database health routes.
|
|
358
|
+
"""
|
|
359
|
+
|
|
360
|
+
import logging
|
|
361
|
+
from fastapi import FastAPI
|
|
362
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
363
|
+
|
|
364
|
+
from ${packageName}.routes.admin_db import router as admin_db_router
|
|
365
|
+
from ${packageName}.routes.health_db import router as health_db_router
|
|
366
|
+
|
|
367
|
+
# Configure logging
|
|
368
|
+
logging.basicConfig(level=logging.INFO)
|
|
369
|
+
logger = logging.getLogger(__name__)
|
|
370
|
+
|
|
371
|
+
# Create FastAPI app
|
|
372
|
+
app = FastAPI(
|
|
373
|
+
title="${projectName} API",
|
|
374
|
+
description="Backend API for ${projectName}",
|
|
375
|
+
version="1.0.0",
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
# Configure CORS
|
|
379
|
+
app.add_middleware(
|
|
380
|
+
CORSMiddleware,
|
|
381
|
+
allow_origins=["http://localhost:5173", "http://localhost:3000"],
|
|
382
|
+
allow_credentials=True,
|
|
383
|
+
allow_methods=["*"],
|
|
384
|
+
allow_headers=["*"],
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
# Include routers
|
|
388
|
+
app.include_router(admin_db_router)
|
|
389
|
+
app.include_router(health_db_router)
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
@app.get("/health")
|
|
393
|
+
async def health_check():
|
|
394
|
+
"""
|
|
395
|
+
Health check endpoint.
|
|
396
|
+
|
|
397
|
+
Returns:
|
|
398
|
+
dict: Health status.
|
|
399
|
+
"""
|
|
400
|
+
return {
|
|
401
|
+
"status": "healthy",
|
|
402
|
+
"message": "Backend is running",
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
@app.get("/")
|
|
407
|
+
async def root():
|
|
408
|
+
"""
|
|
409
|
+
Root endpoint.
|
|
410
|
+
|
|
411
|
+
Returns:
|
|
412
|
+
dict: Welcome message.
|
|
413
|
+
"""
|
|
414
|
+
return {
|
|
415
|
+
"message": "Welcome to ${projectName} API",
|
|
416
|
+
"docs": "/docs",
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
if __name__ == "__main__":
|
|
421
|
+
import uvicorn
|
|
422
|
+
uvicorn.run(app, host="0.0.0.0", port=8000)
|
|
423
|
+
`;
|
|
424
|
+
}
|
|
425
|
+
//# sourceMappingURL=admin-wizard-python.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-wizard-python.js","sourceRoot":"","sources":["../../../src/generators/templates/admin-wizard-python.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;GAIG;AACH,MAAM,UAAU,2BAA2B;IACzC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4CR,CAAC;AACF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,sBAAsB;IACpC,OAAO;;;;;;;;;CASR,CAAC;AACF,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,WAAmB;IACvD,OAAO;;;;;;;;;;;;;;;OAeF,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwPjB,CAAC;AACF,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,4BAA4B,CAC1C,WAAmB,EACnB,WAAmB;IAEnB,OAAO;EACP,WAAW;;;;;;;;;OASN,WAAW;OACX,WAAW;;;;;;;;aAQL,WAAW;mCACW,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iCAyCb,WAAW;;;;;;;;CAQ3C,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Admin Wizard React frontend templates
|
|
3
|
+
* Generates React components for the database setup wizard UI
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Generate useAdminApi custom hook for authenticated API calls
|
|
7
|
+
*
|
|
8
|
+
* @returns TypeScript source for useAdminApi.ts
|
|
9
|
+
*/
|
|
10
|
+
export declare function generateUseAdminApiHook(): string;
|
|
11
|
+
/**
|
|
12
|
+
* Generate DbStatusBanner component that polls DB status and shows setup prompt
|
|
13
|
+
*
|
|
14
|
+
* @returns TypeScript/React source for DbStatusBanner.tsx
|
|
15
|
+
*/
|
|
16
|
+
export declare function generateDbStatusBanner(): string;
|
|
17
|
+
/**
|
|
18
|
+
* Generate ConnectionForm component for testing DB URLs
|
|
19
|
+
*
|
|
20
|
+
* @returns TypeScript/React source for ConnectionForm.tsx
|
|
21
|
+
*/
|
|
22
|
+
export declare function generateConnectionForm(): string;
|
|
23
|
+
/**
|
|
24
|
+
* Generate MigrationProgress component that polls status during apply
|
|
25
|
+
*
|
|
26
|
+
* @returns TypeScript/React source for MigrationProgress.tsx
|
|
27
|
+
*/
|
|
28
|
+
export declare function generateMigrationProgress(): string;
|
|
29
|
+
/**
|
|
30
|
+
* Generate DbSetupStepper component - multi-step wizard overlay
|
|
31
|
+
*
|
|
32
|
+
* @returns TypeScript/React source for DbSetupStepper.tsx
|
|
33
|
+
*/
|
|
34
|
+
export declare function generateDbSetupStepper(): string;
|
|
35
|
+
/**
|
|
36
|
+
* Generate admin barrel export index
|
|
37
|
+
*
|
|
38
|
+
* @returns TypeScript source for admin/index.ts
|
|
39
|
+
*/
|
|
40
|
+
export declare function generateAdminIndex(): string;
|
|
41
|
+
/**
|
|
42
|
+
* Generate App.tsx that includes DbStatusBanner and DbSetupStepper
|
|
43
|
+
*
|
|
44
|
+
* @param projectName - Human-readable project name
|
|
45
|
+
* @returns TypeScript/React source for App.tsx
|
|
46
|
+
*/
|
|
47
|
+
export declare function generateAppTsxWithAdmin(projectName: string): string;
|
|
48
|
+
//# sourceMappingURL=admin-wizard-react.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-wizard-react.d.ts","sourceRoot":"","sources":["../../../src/generators/templates/admin-wizard-react.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;GAIG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAkDhD;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAiE/C;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAkF/C;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAqGlD;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CA2H/C;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAQ3C;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CA6EnE"}
|