quantum-framework 0.18.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (333) hide show
  1. quantum/__init__.py +1 -0
  2. quantum/cli/__init__.py +2 -0
  3. quantum/cli/admin.py +131 -0
  4. quantum/cli/check.py +223 -0
  5. quantum/cli/commands/__init__.py +15 -0
  6. quantum/cli/commands/build.py +414 -0
  7. quantum/cli/commands/dev.py +185 -0
  8. quantum/cli/commands/docs.py +329 -0
  9. quantum/cli/commands/lint.py +523 -0
  10. quantum/cli/commands/migrate.py +548 -0
  11. quantum/cli/commands/new.py +622 -0
  12. quantum/cli/commands/serve.py +190 -0
  13. quantum/cli/commands/test.py +193 -0
  14. quantum/cli/deploy.py +810 -0
  15. quantum/cli/hot_reload.py +951 -0
  16. quantum/cli/jobs.py +356 -0
  17. quantum/cli/mq.py +582 -0
  18. quantum/cli/pkg.py +390 -0
  19. quantum/cli/runner.py +565 -0
  20. quantum/cli/server_process.py +159 -0
  21. quantum/cli/utils.py +334 -0
  22. quantum/compiler/__init__.py +30 -0
  23. quantum/compiler/base_generator.py +367 -0
  24. quantum/compiler/cli.py +295 -0
  25. quantum/compiler/expression_transformer.py +444 -0
  26. quantum/compiler/javascript/__init__.py +10 -0
  27. quantum/compiler/javascript/generator.py +659 -0
  28. quantum/compiler/optimizer.py +270 -0
  29. quantum/compiler/python/__init__.py +10 -0
  30. quantum/compiler/python/generator.py +883 -0
  31. quantum/compiler/python/runtime.py +863 -0
  32. quantum/compiler/transpiler.py +330 -0
  33. quantum/core/__init__.py +3 -0
  34. quantum/core/ast_nodes.py +2619 -0
  35. quantum/core/config_env.py +54 -0
  36. quantum/core/docs_links.py +8 -0
  37. quantum/core/expression_diagnostics.py +87 -0
  38. quantum/core/expression_stdlib.py +208 -0
  39. quantum/core/expressions.py +677 -0
  40. quantum/core/features/agents/src/__init__.py +30 -0
  41. quantum/core/features/agents/src/ast_node.py +540 -0
  42. quantum/core/features/conditionals/src/__init__.py +8 -0
  43. quantum/core/features/conditionals/src/ast_node.py +68 -0
  44. quantum/core/features/data_import/src/__init__.py +0 -0
  45. quantum/core/features/data_import/src/ast_node.py +292 -0
  46. quantum/core/features/data_import/src/runtime.py +533 -0
  47. quantum/core/features/dump/src/__init__.py +12 -0
  48. quantum/core/features/dump/src/ast_node.py +106 -0
  49. quantum/core/features/dump/src/parser.py +61 -0
  50. quantum/core/features/dump/src/runtime.py +246 -0
  51. quantum/core/features/functions/src/__init__.py +8 -0
  52. quantum/core/features/functions/src/ast_node.py +149 -0
  53. quantum/core/features/game_engine_2d/src/__init__.py +19 -0
  54. quantum/core/features/game_engine_2d/src/ast_nodes.py +1719 -0
  55. quantum/core/features/game_engine_2d/src/parser.py +983 -0
  56. quantum/core/features/invocation/src/__init__.py +0 -0
  57. quantum/core/features/invocation/src/ast_node.py +147 -0
  58. quantum/core/features/invocation/src/runtime.py +332 -0
  59. quantum/core/features/knowledge_base/src/__init__.py +6 -0
  60. quantum/core/features/knowledge_base/src/ast_node.py +113 -0
  61. quantum/core/features/knowledge_base/src/parser.py +82 -0
  62. quantum/core/features/logging/src/__init__.py +12 -0
  63. quantum/core/features/logging/src/ast_node.py +111 -0
  64. quantum/core/features/logging/src/parser.py +50 -0
  65. quantum/core/features/logging/src/runtime.py +190 -0
  66. quantum/core/features/loops/src/__init__.py +8 -0
  67. quantum/core/features/loops/src/ast_node.py +60 -0
  68. quantum/core/features/query/src/__init__.py +0 -0
  69. quantum/core/features/query/src/query_validators.py +20 -0
  70. quantum/core/features/state_management/src/__init__.py +11 -0
  71. quantum/core/features/state_management/src/ast_node.py +121 -0
  72. quantum/core/features/terminal_engine/src/__init__.py +21 -0
  73. quantum/core/features/terminal_engine/src/ast_nodes.py +560 -0
  74. quantum/core/features/terminal_engine/src/parser.py +361 -0
  75. quantum/core/features/testing_engine/src/__init__.py +41 -0
  76. quantum/core/features/testing_engine/src/ast_nodes.py +1212 -0
  77. quantum/core/features/testing_engine/src/parser.py +604 -0
  78. quantum/core/features/theming/src/__init__.py +48 -0
  79. quantum/core/features/theming/src/ast_node.py +137 -0
  80. quantum/core/features/theming/src/presets.py +405 -0
  81. quantum/core/features/ui_engine/src/__init__.py +20 -0
  82. quantum/core/features/ui_engine/src/ast_nodes.py +1913 -0
  83. quantum/core/features/ui_engine/src/parser.py +1192 -0
  84. quantum/core/features/websocket/src/__init__.py +24 -0
  85. quantum/core/features/websocket/src/ast_node.py +247 -0
  86. quantum/core/guards.py +53 -0
  87. quantum/core/html_compat.py +299 -0
  88. quantum/core/parser.py +1551 -0
  89. quantum/core/parser_registry.py +213 -0
  90. quantum/core/parsers/__init__.py +76 -0
  91. quantum/core/parsers/ai/__init__.py +12 -0
  92. quantum/core/parsers/ai/agent_parser.py +106 -0
  93. quantum/core/parsers/ai/knowledge_parser.py +86 -0
  94. quantum/core/parsers/ai/llm_parser.py +78 -0
  95. quantum/core/parsers/ai/team_parser.py +88 -0
  96. quantum/core/parsers/base.py +322 -0
  97. quantum/core/parsers/composition/__init__.py +10 -0
  98. quantum/core/parsers/composition/import_parser.py +50 -0
  99. quantum/core/parsers/composition/slot_parser.py +48 -0
  100. quantum/core/parsers/control_flow/__init__.py +11 -0
  101. quantum/core/parsers/control_flow/if_parser.py +71 -0
  102. quantum/core/parsers/control_flow/loop_parser.py +105 -0
  103. quantum/core/parsers/control_flow/set_parser.py +93 -0
  104. quantum/core/parsers/data/__init__.py +12 -0
  105. quantum/core/parsers/data/data_parser.py +229 -0
  106. quantum/core/parsers/data/invoke_parser.py +133 -0
  107. quantum/core/parsers/data/query_parser.py +213 -0
  108. quantum/core/parsers/data/transaction_parser.py +86 -0
  109. quantum/core/parsers/events/__init__.py +9 -0
  110. quantum/core/parsers/events/dispatch_event_parser.py +44 -0
  111. quantum/core/parsers/forms/__init__.py +11 -0
  112. quantum/core/parsers/forms/action_parser.py +79 -0
  113. quantum/core/parsers/forms/flash_parser.py +34 -0
  114. quantum/core/parsers/forms/redirect_parser.py +33 -0
  115. quantum/core/parsers/functions/__init__.py +11 -0
  116. quantum/core/parsers/functions/function_parser.py +98 -0
  117. quantum/core/parsers/functions/param_parser.py +73 -0
  118. quantum/core/parsers/functions/return_parser.py +31 -0
  119. quantum/core/parsers/html/__init__.py +10 -0
  120. quantum/core/parsers/html/component_call_parser.py +130 -0
  121. quantum/core/parsers/html/html_parser.py +115 -0
  122. quantum/core/parsers/jobs/__init__.py +11 -0
  123. quantum/core/parsers/jobs/job_parser.py +71 -0
  124. quantum/core/parsers/jobs/schedule_parser.py +62 -0
  125. quantum/core/parsers/jobs/thread_parser.py +57 -0
  126. quantum/core/parsers/messaging/__init__.py +17 -0
  127. quantum/core/parsers/messaging/message_ack_parser.py +30 -0
  128. quantum/core/parsers/messaging/message_nack_parser.py +30 -0
  129. quantum/core/parsers/messaging/message_parser.py +114 -0
  130. quantum/core/parsers/messaging/queue_parser.py +61 -0
  131. quantum/core/parsers/messaging/websocket_parser.py +121 -0
  132. quantum/core/parsers/routing/__init__.py +9 -0
  133. quantum/core/parsers/routing/route_parser.py +41 -0
  134. quantum/core/parsers/scripting/__init__.py +12 -0
  135. quantum/core/parsers/scripting/pyclass_parser.py +62 -0
  136. quantum/core/parsers/scripting/pydecorator_parser.py +68 -0
  137. quantum/core/parsers/scripting/pyimport_parser.py +52 -0
  138. quantum/core/parsers/scripting/python_parser.py +49 -0
  139. quantum/core/parsers/services/__init__.py +12 -0
  140. quantum/core/parsers/services/dump_parser.py +52 -0
  141. quantum/core/parsers/services/file_parser.py +48 -0
  142. quantum/core/parsers/services/log_parser.py +46 -0
  143. quantum/core/parsers/services/mail_parser.py +65 -0
  144. quantum/core/tiers.py +164 -0
  145. quantum/core/xml_lines.py +81 -0
  146. quantum/packages/__init__.py +28 -0
  147. quantum/packages/manager.py +413 -0
  148. quantum/packages/manifest.py +351 -0
  149. quantum/packages/registry.py +399 -0
  150. quantum/packages/resolver.py +336 -0
  151. quantum/plugins/__init__.py +33 -0
  152. quantum/plugins/hooks.py +329 -0
  153. quantum/plugins/loader.py +479 -0
  154. quantum/plugins/manifest.py +336 -0
  155. quantum/plugins/registry.py +371 -0
  156. quantum/runtime/__init__.py +25 -0
  157. quantum/runtime/action_handler.py +532 -0
  158. quantum/runtime/adapters/__init__.py +88 -0
  159. quantum/runtime/adapters/memory_adapter.py +690 -0
  160. quantum/runtime/adapters/rabbitmq_adapter.py +715 -0
  161. quantum/runtime/adapters/redis_adapter.py +582 -0
  162. quantum/runtime/adapters/sqlite_adapter.py +414 -0
  163. quantum/runtime/agent_service.py +1133 -0
  164. quantum/runtime/ast_cache.py +506 -0
  165. quantum/runtime/auth_service.py +267 -0
  166. quantum/runtime/component.py +1048 -0
  167. quantum/runtime/component_composer.py +133 -0
  168. quantum/runtime/component_resolver.py +174 -0
  169. quantum/runtime/conditions.py +54 -0
  170. quantum/runtime/database_service.py +595 -0
  171. quantum/runtime/db_schema.py +100 -0
  172. quantum/runtime/dev_panel.py +178 -0
  173. quantum/runtime/email_service.py +162 -0
  174. quantum/runtime/error_handler.py +295 -0
  175. quantum/runtime/execution_context.py +283 -0
  176. quantum/runtime/executor_registry.py +178 -0
  177. quantum/runtime/executors/__init__.py +71 -0
  178. quantum/runtime/executors/ai/__init__.py +12 -0
  179. quantum/runtime/executors/ai/agent_executor.py +217 -0
  180. quantum/runtime/executors/ai/knowledge_executor.py +114 -0
  181. quantum/runtime/executors/ai/llm_executor.py +153 -0
  182. quantum/runtime/executors/ai/team_executor.py +171 -0
  183. quantum/runtime/executors/base.py +264 -0
  184. quantum/runtime/executors/control_flow/__init__.py +12 -0
  185. quantum/runtime/executors/control_flow/if_executor.py +98 -0
  186. quantum/runtime/executors/control_flow/loop_executor.py +308 -0
  187. quantum/runtime/executors/control_flow/redirect_executor.py +43 -0
  188. quantum/runtime/executors/control_flow/set_executor.py +464 -0
  189. quantum/runtime/executors/data/__init__.py +12 -0
  190. quantum/runtime/executors/data/data_executor.py +156 -0
  191. quantum/runtime/executors/data/invoke_executor.py +228 -0
  192. quantum/runtime/executors/data/query_executor.py +290 -0
  193. quantum/runtime/executors/data/transaction_executor.py +91 -0
  194. quantum/runtime/executors/jobs/__init__.py +11 -0
  195. quantum/runtime/executors/jobs/job_executor.py +190 -0
  196. quantum/runtime/executors/jobs/schedule_executor.py +132 -0
  197. quantum/runtime/executors/jobs/thread_executor.py +127 -0
  198. quantum/runtime/executors/messaging/__init__.py +17 -0
  199. quantum/runtime/executors/messaging/message_ack_executor.py +51 -0
  200. quantum/runtime/executors/messaging/message_executor.py +174 -0
  201. quantum/runtime/executors/messaging/queue_executor.py +103 -0
  202. quantum/runtime/executors/messaging/websocket_executor.py +197 -0
  203. quantum/runtime/executors/scripting/__init__.py +11 -0
  204. quantum/runtime/executors/scripting/pyclass_executor.py +90 -0
  205. quantum/runtime/executors/scripting/pyimport_executor.py +81 -0
  206. quantum/runtime/executors/scripting/python_executor.py +249 -0
  207. quantum/runtime/executors/services/__init__.py +12 -0
  208. quantum/runtime/executors/services/dump_executor.py +72 -0
  209. quantum/runtime/executors/services/file_executor.py +89 -0
  210. quantum/runtime/executors/services/log_executor.py +77 -0
  211. quantum/runtime/executors/services/mail_executor.py +81 -0
  212. quantum/runtime/expression_cache.py +498 -0
  213. quantum/runtime/file_upload_service.py +326 -0
  214. quantum/runtime/function_registry.py +118 -0
  215. quantum/runtime/game_builder.py +166 -0
  216. quantum/runtime/game_code_generator.py +2371 -0
  217. quantum/runtime/game_templates.py +2006 -0
  218. quantum/runtime/godot_code_generator.py +4681 -0
  219. quantum/runtime/godot_templates.py +1449 -0
  220. quantum/runtime/job_executor.py +1599 -0
  221. quantum/runtime/knowledge_service.py +546 -0
  222. quantum/runtime/llm_cache.py +100 -0
  223. quantum/runtime/llm_providers.py +704 -0
  224. quantum/runtime/llm_service.py +287 -0
  225. quantum/runtime/local_server.py +28 -0
  226. quantum/runtime/logging_setup.py +141 -0
  227. quantum/runtime/message_broker.py +364 -0
  228. quantum/runtime/message_queue_service.py +571 -0
  229. quantum/runtime/param_validation.py +218 -0
  230. quantum/runtime/pypy_compat.py +317 -0
  231. quantum/runtime/python_bridge.py +698 -0
  232. quantum/runtime/query_validators.py +304 -0
  233. quantum/runtime/renderer.py +734 -0
  234. quantum/runtime/service_container.py +470 -0
  235. quantum/runtime/table_params.py +163 -0
  236. quantum/runtime/terminal_builder.py +76 -0
  237. quantum/runtime/terminal_code_generator.py +607 -0
  238. quantum/runtime/terminal_templates.py +243 -0
  239. quantum/runtime/testing_builder.py +77 -0
  240. quantum/runtime/testing_code_generator.py +833 -0
  241. quantum/runtime/testing_templates.py +85 -0
  242. quantum/runtime/ui_builder.py +222 -0
  243. quantum/runtime/ui_console.py +405 -0
  244. quantum/runtime/ui_desktop.py +56 -0
  245. quantum/runtime/ui_form_rules.py +193 -0
  246. quantum/runtime/ui_html_adapter.py +2567 -0
  247. quantum/runtime/ui_html_templates.py +2106 -0
  248. quantum/runtime/ui_mobile_adapter.py +1832 -0
  249. quantum/runtime/ui_mobile_templates.py +1003 -0
  250. quantum/runtime/ui_page_renderer.py +392 -0
  251. quantum/runtime/ui_pager.py +73 -0
  252. quantum/runtime/ui_table_edit.py +139 -0
  253. quantum/runtime/ui_textual_adapter.py +1866 -0
  254. quantum/runtime/ui_textual_templates.py +45 -0
  255. quantum/runtime/ui_tokens.py +465 -0
  256. quantum/runtime/ui_validator.py +365 -0
  257. quantum/runtime/ui_view.py +221 -0
  258. quantum/runtime/validators.py +256 -0
  259. quantum/runtime/web_server.py +2131 -0
  260. quantum/runtime/websocket_adapter.py +501 -0
  261. quantum/runtime/websocket_service.py +585 -0
  262. quantum/runtime/websocket_transport.py +289 -0
  263. quantum/runtime/wsgi.py +101 -0
  264. quantum/services.py +109 -0
  265. quantum/utils/__init__.py +1 -0
  266. quantum_admin/__init__.py +4 -0
  267. quantum_admin/assets/quantum-admin-connectors.js +123 -0
  268. quantum_admin/assets/quantum-admin.css +2058 -0
  269. quantum_admin/assets/quantum-source-highlight.js +106 -0
  270. quantum_admin/backend/__init__.py +4 -0
  271. quantum_admin/backend/audit_service.py +346 -0
  272. quantum_admin/backend/auth_service.py +394 -0
  273. quantum_admin/backend/component_discovery.py +766 -0
  274. quantum_admin/backend/config_generator.py +586 -0
  275. quantum_admin/backend/connector_service.py +1016 -0
  276. quantum_admin/backend/crud.py +557 -0
  277. quantum_admin/backend/database.py +226 -0
  278. quantum_admin/backend/db_setup_service.py +193 -0
  279. quantum_admin/backend/deploy_service.py +698 -0
  280. quantum_admin/backend/docker_service.py +422 -0
  281. quantum_admin/backend/environment_service.py +418 -0
  282. quantum_admin/backend/git_service.py +370 -0
  283. quantum_admin/backend/health_service.py +390 -0
  284. quantum_admin/backend/job_service.py +347 -0
  285. quantum_admin/backend/log_service.py +331 -0
  286. quantum_admin/backend/main.py +14426 -0
  287. quantum_admin/backend/models.py +986 -0
  288. quantum_admin/backend/pipeline_service.py +812 -0
  289. quantum_admin/backend/resource_manager.py +1118 -0
  290. quantum_admin/backend/schemas.py +419 -0
  291. quantum_admin/backend/secret_manager.py +277 -0
  292. quantum_admin/backend/settings_service.py +488 -0
  293. quantum_admin/backend/template_service.py +710 -0
  294. quantum_admin/backend/test_execution_service.py +364 -0
  295. quantum_admin/backend/test_generator.py +782 -0
  296. quantum_admin/backend/webhook_service.py +454 -0
  297. quantum_admin/backend/websocket_manager.py +360 -0
  298. quantum_admin/components/admin/_layout/AdminShell.q +121 -0
  299. quantum_admin/components/admin/agents.q +107 -0
  300. quantum_admin/components/admin/app/[name].q +558 -0
  301. quantum_admin/components/admin/applications.q +160 -0
  302. quantum_admin/components/admin/component/[...path].q +205 -0
  303. quantum_admin/components/admin/components.q +59 -0
  304. quantum_admin/components/admin/connectors.q +283 -0
  305. quantum_admin/components/admin/dashboard.q +86 -0
  306. quantum_admin/components/admin/database.q +95 -0
  307. quantum_admin/components/admin/features.q +69 -0
  308. quantum_admin/components/admin/index.q +12 -0
  309. quantum_admin/components/admin/jobs.q +75 -0
  310. quantum_admin/components/admin/login.q +65 -0
  311. quantum_admin/components/admin/logout.q +11 -0
  312. quantum_admin/components/admin/settings.q +174 -0
  313. quantum_admin/components/admin/source.q +45 -0
  314. quantum_admin/components/admin/tests.q +73 -0
  315. quantum_admin/run.py +13 -0
  316. quantum_admin/services/__init__.py +16 -0
  317. quantum_admin/services/_base.py +84 -0
  318. quantum_admin/services/_component_test_generator.py +205 -0
  319. quantum_admin/services/_yaml_editor.py +82 -0
  320. quantum_admin/services/apps.py +330 -0
  321. quantum_admin/services/auth.py +61 -0
  322. quantum_admin/services/components.py +210 -0
  323. quantum_admin/services/connectors.py +164 -0
  324. quantum_admin/services/projects.py +189 -0
  325. quantum_admin/services/repository.py +223 -0
  326. quantum_admin/services/settings.py +150 -0
  327. quantum_admin/services/yaml_import.py +118 -0
  328. quantum_framework-0.18.0.dist-info/METADATA +281 -0
  329. quantum_framework-0.18.0.dist-info/RECORD +333 -0
  330. quantum_framework-0.18.0.dist-info/WHEEL +5 -0
  331. quantum_framework-0.18.0.dist-info/entry_points.txt +2 -0
  332. quantum_framework-0.18.0.dist-info/licenses/LICENSE +21 -0
  333. quantum_framework-0.18.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,586 @@
1
+ """
2
+ Config Generator - Generate configuration files for projects
3
+
4
+ Generates:
5
+ - .env files
6
+ - docker-compose.yml
7
+ - nginx.conf
8
+ - systemd service files
9
+ """
10
+
11
+ import os
12
+ import json
13
+ import logging
14
+ from typing import Dict, List, Optional, Any
15
+ from datetime import datetime
16
+ from sqlalchemy.orm import Session
17
+
18
+ try:
19
+ from .models import Project, Environment, Datasource, PortAllocation, Secret, ServiceRegistry
20
+ from .resource_manager import get_resource_manager
21
+ except ImportError:
22
+ from models import Project, Environment, Datasource, PortAllocation, Secret, ServiceRegistry
23
+ from resource_manager import get_resource_manager
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+
28
+ class ConfigGenerator:
29
+ """Generate configuration files for projects"""
30
+
31
+ def __init__(self, db: Session):
32
+ self.db = db
33
+ self.resource_manager = get_resource_manager(db)
34
+
35
+ def generate_env_file(
36
+ self,
37
+ project_id: int,
38
+ environment_id: int = None,
39
+ include_secrets: bool = True
40
+ ) -> str:
41
+ """
42
+ Generate a .env file content for a project
43
+
44
+ Args:
45
+ project_id: Project ID
46
+ environment_id: Optional environment ID for environment-specific vars
47
+ include_secrets: Include decrypted secret values
48
+
49
+ Returns:
50
+ String content of .env file
51
+ """
52
+ project = self.db.query(Project).filter(Project.id == project_id).first()
53
+ if not project:
54
+ raise ValueError(f"Project {project_id} not found")
55
+
56
+ lines = [
57
+ f"# Generated by Quantum Admin - {datetime.now().isoformat()}",
58
+ f"# Project: {project.name}",
59
+ ""
60
+ ]
61
+
62
+ # Get environment if specified
63
+ environment = None
64
+ if environment_id:
65
+ environment = self.db.query(Environment).filter(
66
+ Environment.id == environment_id
67
+ ).first()
68
+ if environment:
69
+ lines.append(f"# Environment: {environment.name}")
70
+ lines.append("")
71
+
72
+ # Add project-level config
73
+ lines.append("# Application")
74
+ lines.append(f"APP_NAME={project.name}")
75
+ lines.append(f"APP_ENV={'production' if environment and environment.name == 'production' else 'development'}")
76
+ lines.append("")
77
+
78
+ # Get port allocations
79
+ ports = self.db.query(PortAllocation).filter(
80
+ PortAllocation.project_id == project_id,
81
+ PortAllocation.status == 'allocated'
82
+ )
83
+ if environment_id:
84
+ ports = ports.filter(PortAllocation.environment_id == environment_id)
85
+ ports = ports.all()
86
+
87
+ if ports:
88
+ lines.append("# Ports")
89
+ for port in ports:
90
+ var_name = f"{port.service_name.upper().replace('-', '_')}_PORT"
91
+ lines.append(f"{var_name}={port.port}")
92
+ lines.append("")
93
+
94
+ # Get secrets
95
+ if include_secrets:
96
+ secrets = self.resource_manager.secrets.list_secrets(
97
+ project_id=project_id,
98
+ environment_id=environment_id
99
+ )
100
+ if secrets:
101
+ lines.append("# Secrets")
102
+ for secret in secrets:
103
+ value = self.resource_manager.secrets.get(
104
+ secret.key,
105
+ project_id=project_id,
106
+ environment_id=environment_id
107
+ )
108
+ if value:
109
+ lines.append(f"{secret.key}={value}")
110
+ lines.append("")
111
+
112
+ # Get datasources
113
+ datasources = self.db.query(Datasource).filter(
114
+ Datasource.project_id == project_id
115
+ ).all()
116
+
117
+ if datasources:
118
+ lines.append("# Databases")
119
+ for ds in datasources:
120
+ prefix = ds.name.upper().replace('-', '_')
121
+ lines.append(f"{prefix}_HOST={ds.host or 'localhost'}")
122
+ lines.append(f"{prefix}_PORT={ds.port or 5432}")
123
+ lines.append(f"{prefix}_NAME={ds.database_name or ds.name}")
124
+ if ds.username:
125
+ lines.append(f"{prefix}_USER={ds.username}")
126
+ # Don't include encrypted password - use secrets instead
127
+ lines.append("")
128
+
129
+ # Get services
130
+ services = self.resource_manager.services.list_services(
131
+ project_id=project_id,
132
+ environment_id=environment_id
133
+ )
134
+
135
+ if services:
136
+ lines.append("# Services")
137
+ for svc in services:
138
+ prefix = svc.name.upper().replace('-', '_')
139
+ lines.append(f"{prefix}_URL={svc.protocol}://{svc.host}:{svc.port}")
140
+ lines.append("")
141
+
142
+ return "\n".join(lines)
143
+
144
+ def generate_docker_compose(
145
+ self,
146
+ project_id: int,
147
+ environment_id: int = None,
148
+ include_databases: bool = True,
149
+ include_cache: bool = True
150
+ ) -> str:
151
+ """
152
+ Generate a docker-compose.yml content for a project
153
+
154
+ Args:
155
+ project_id: Project ID
156
+ environment_id: Optional environment ID
157
+ include_databases: Include database containers
158
+ include_cache: Include Redis cache
159
+
160
+ Returns:
161
+ String content of docker-compose.yml
162
+ """
163
+ project = self.db.query(Project).filter(Project.id == project_id).first()
164
+ if not project:
165
+ raise ValueError(f"Project {project_id} not found")
166
+
167
+ # Get environment for naming
168
+ env_name = "dev"
169
+ environment = None
170
+ if environment_id:
171
+ environment = self.db.query(Environment).filter(
172
+ Environment.id == environment_id
173
+ ).first()
174
+ if environment:
175
+ env_name = environment.name
176
+
177
+ # Get port allocations
178
+ ports = self.db.query(PortAllocation).filter(
179
+ PortAllocation.project_id == project_id,
180
+ PortAllocation.status == 'allocated'
181
+ )
182
+ if environment_id:
183
+ ports = ports.filter(PortAllocation.environment_id == environment_id)
184
+ ports_list = ports.all()
185
+
186
+ # Find app port
187
+ app_port = 8000
188
+ for p in ports_list:
189
+ if p.port_type == 'app':
190
+ app_port = p.port
191
+ break
192
+
193
+ # Build compose content
194
+ compose = {
195
+ "version": "3.8",
196
+ "services": {},
197
+ "networks": {
198
+ "quantum-network": {
199
+ "driver": "bridge"
200
+ }
201
+ },
202
+ "volumes": {}
203
+ }
204
+
205
+ # Main app service
206
+ service_name = f"{project.name.lower().replace(' ', '-')}-app"
207
+ compose["services"][service_name] = {
208
+ "build": ".",
209
+ "container_name": f"{project.name.lower()}-{env_name}",
210
+ "ports": [f"{app_port}:8000"],
211
+ "environment": [
212
+ f"APP_NAME={project.name}",
213
+ f"APP_ENV={env_name}",
214
+ ],
215
+ "networks": ["quantum-network"],
216
+ "restart": "unless-stopped"
217
+ }
218
+
219
+ # Get datasources
220
+ if include_databases:
221
+ datasources = self.db.query(Datasource).filter(
222
+ Datasource.project_id == project_id
223
+ ).all()
224
+
225
+ for ds in datasources:
226
+ ds_name = ds.name.lower().replace(' ', '-')
227
+ ds_port = ds.port or self._get_default_db_port(ds.type)
228
+
229
+ if ds.type == 'postgres':
230
+ compose["services"][f"{ds_name}-db"] = {
231
+ "image": "postgres:15-alpine",
232
+ "container_name": f"{ds_name}-{env_name}",
233
+ "environment": [
234
+ f"POSTGRES_DB={ds.database_name or ds.name}",
235
+ f"POSTGRES_USER={ds.username or 'quantum'}",
236
+ "POSTGRES_PASSWORD=${DB_PASSWORD:-quantum123}"
237
+ ],
238
+ "ports": [f"{ds_port}:5432"],
239
+ "volumes": [f"{ds_name}-data:/var/lib/postgresql/data"],
240
+ "networks": ["quantum-network"],
241
+ "restart": "unless-stopped"
242
+ }
243
+ compose["volumes"][f"{ds_name}-data"] = None
244
+
245
+ elif ds.type == 'mysql':
246
+ compose["services"][f"{ds_name}-db"] = {
247
+ "image": "mysql:8",
248
+ "container_name": f"{ds_name}-{env_name}",
249
+ "environment": [
250
+ f"MYSQL_DATABASE={ds.database_name or ds.name}",
251
+ f"MYSQL_USER={ds.username or 'quantum'}",
252
+ "MYSQL_PASSWORD=${DB_PASSWORD:-quantum123}",
253
+ "MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD:-root123}"
254
+ ],
255
+ "ports": [f"{ds_port}:3306"],
256
+ "volumes": [f"{ds_name}-data:/var/lib/mysql"],
257
+ "networks": ["quantum-network"],
258
+ "restart": "unless-stopped"
259
+ }
260
+ compose["volumes"][f"{ds_name}-data"] = None
261
+
262
+ elif ds.type == 'mongodb':
263
+ compose["services"][f"{ds_name}-db"] = {
264
+ "image": "mongo:6",
265
+ "container_name": f"{ds_name}-{env_name}",
266
+ "environment": [
267
+ f"MONGO_INITDB_DATABASE={ds.database_name or ds.name}",
268
+ ],
269
+ "ports": [f"{ds_port}:27017"],
270
+ "volumes": [f"{ds_name}-data:/data/db"],
271
+ "networks": ["quantum-network"],
272
+ "restart": "unless-stopped"
273
+ }
274
+ compose["volumes"][f"{ds_name}-data"] = None
275
+
276
+ # Add Redis cache
277
+ if include_cache:
278
+ cache_port = 6379
279
+ for p in ports_list:
280
+ if p.port_type == 'cache':
281
+ cache_port = p.port
282
+ break
283
+
284
+ compose["services"]["redis"] = {
285
+ "image": "redis:7-alpine",
286
+ "container_name": f"{project.name.lower()}-redis-{env_name}",
287
+ "ports": [f"{cache_port}:6379"],
288
+ "networks": ["quantum-network"],
289
+ "restart": "unless-stopped"
290
+ }
291
+
292
+ # Convert to YAML format
293
+ return self._dict_to_yaml(compose)
294
+
295
+ def generate_nginx_conf(
296
+ self,
297
+ project_id: int,
298
+ environment_id: int = None,
299
+ domain: str = None,
300
+ ssl: bool = True
301
+ ) -> str:
302
+ """
303
+ Generate nginx configuration for a project
304
+
305
+ Args:
306
+ project_id: Project ID
307
+ environment_id: Optional environment ID
308
+ domain: Domain name (defaults to localhost)
309
+ ssl: Enable SSL configuration
310
+
311
+ Returns:
312
+ String content of nginx.conf
313
+ """
314
+ project = self.db.query(Project).filter(Project.id == project_id).first()
315
+ if not project:
316
+ raise ValueError(f"Project {project_id} not found")
317
+
318
+ # Get environment
319
+ env_name = "dev"
320
+ environment = None
321
+ if environment_id:
322
+ environment = self.db.query(Environment).filter(
323
+ Environment.id == environment_id
324
+ ).first()
325
+ if environment:
326
+ env_name = environment.name
327
+
328
+ # Get app port
329
+ ports = self.db.query(PortAllocation).filter(
330
+ PortAllocation.project_id == project_id,
331
+ PortAllocation.port_type == 'app',
332
+ PortAllocation.status == 'allocated'
333
+ )
334
+ if environment_id:
335
+ ports = ports.filter(PortAllocation.environment_id == environment_id)
336
+ port_alloc = ports.first()
337
+ app_port = port_alloc.port if port_alloc else 8000
338
+
339
+ # Determine domain
340
+ if not domain:
341
+ if environment and hasattr(environment, 'health_url') and environment.health_url:
342
+ # Extract domain from health URL
343
+ from urllib.parse import urlparse
344
+ parsed = urlparse(environment.health_url)
345
+ domain = parsed.netloc.split(':')[0]
346
+ else:
347
+ domain = "localhost"
348
+
349
+ upstream_name = f"{project.name.lower().replace(' ', '_')}_upstream"
350
+
351
+ conf_lines = [
352
+ f"# Nginx configuration for {project.name} ({env_name})",
353
+ f"# Generated by Quantum Admin - {datetime.now().isoformat()}",
354
+ "",
355
+ f"upstream {upstream_name} {{",
356
+ f" server 127.0.0.1:{app_port};",
357
+ " keepalive 32;",
358
+ "}",
359
+ "",
360
+ ]
361
+
362
+ # HTTP to HTTPS redirect if SSL
363
+ if ssl:
364
+ conf_lines.extend([
365
+ "server {",
366
+ " listen 80;",
367
+ f" server_name {domain};",
368
+ "",
369
+ " location / {",
370
+ " return 301 https://$host$request_uri;",
371
+ " }",
372
+ "}",
373
+ "",
374
+ ])
375
+
376
+ # Main server block
377
+ if ssl:
378
+ conf_lines.extend([
379
+ "server {",
380
+ " listen 443 ssl http2;",
381
+ f" server_name {domain};",
382
+ "",
383
+ f" ssl_certificate /etc/letsencrypt/live/{domain}/fullchain.pem;",
384
+ f" ssl_certificate_key /etc/letsencrypt/live/{domain}/privkey.pem;",
385
+ " ssl_protocols TLSv1.2 TLSv1.3;",
386
+ " ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;",
387
+ " ssl_prefer_server_ciphers off;",
388
+ "",
389
+ ])
390
+ else:
391
+ conf_lines.extend([
392
+ "server {",
393
+ " listen 80;",
394
+ f" server_name {domain};",
395
+ "",
396
+ ])
397
+
398
+ conf_lines.extend([
399
+ " # Logging",
400
+ f" access_log /var/log/nginx/{project.name.lower()}-access.log;",
401
+ f" error_log /var/log/nginx/{project.name.lower()}-error.log;",
402
+ "",
403
+ " # Security headers",
404
+ " add_header X-Frame-Options \"SAMEORIGIN\" always;",
405
+ " add_header X-Content-Type-Options \"nosniff\" always;",
406
+ " add_header X-XSS-Protection \"1; mode=block\" always;",
407
+ "",
408
+ " # Gzip compression",
409
+ " gzip on;",
410
+ " gzip_types text/plain text/css application/json application/javascript text/xml application/xml;",
411
+ "",
412
+ " # Static files",
413
+ " location /static/ {",
414
+ " alias /var/www/{project.name.lower()}/static/;",
415
+ " expires 30d;",
416
+ " add_header Cache-Control \"public, immutable\";",
417
+ " }",
418
+ "",
419
+ " # Proxy to application",
420
+ " location / {",
421
+ f" proxy_pass http://{upstream_name};",
422
+ " proxy_http_version 1.1;",
423
+ " proxy_set_header Host $host;",
424
+ " proxy_set_header X-Real-IP $remote_addr;",
425
+ " proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;",
426
+ " proxy_set_header X-Forwarded-Proto $scheme;",
427
+ " proxy_set_header Connection \"\";",
428
+ "",
429
+ " # Timeouts",
430
+ " proxy_connect_timeout 60s;",
431
+ " proxy_send_timeout 60s;",
432
+ " proxy_read_timeout 60s;",
433
+ " }",
434
+ "",
435
+ " # WebSocket support",
436
+ " location /ws/ {",
437
+ f" proxy_pass http://{upstream_name};",
438
+ " proxy_http_version 1.1;",
439
+ " proxy_set_header Upgrade $http_upgrade;",
440
+ " proxy_set_header Connection \"upgrade\";",
441
+ " proxy_set_header Host $host;",
442
+ " proxy_read_timeout 86400;",
443
+ " }",
444
+ "",
445
+ " # Health check endpoint",
446
+ " location /health {",
447
+ f" proxy_pass http://{upstream_name}/health;",
448
+ " access_log off;",
449
+ " }",
450
+ "}",
451
+ ])
452
+
453
+ return "\n".join(conf_lines)
454
+
455
+ def generate_systemd_service(
456
+ self,
457
+ project_id: int,
458
+ environment_id: int = None,
459
+ working_dir: str = None,
460
+ python_path: str = None
461
+ ) -> str:
462
+ """
463
+ Generate systemd service file for a project
464
+
465
+ Args:
466
+ project_id: Project ID
467
+ environment_id: Optional environment ID
468
+ working_dir: Working directory (defaults to /var/www/{project})
469
+ python_path: Python executable path
470
+
471
+ Returns:
472
+ String content of systemd service file
473
+ """
474
+ project = self.db.query(Project).filter(Project.id == project_id).first()
475
+ if not project:
476
+ raise ValueError(f"Project {project_id} not found")
477
+
478
+ env_name = "dev"
479
+ if environment_id:
480
+ environment = self.db.query(Environment).filter(
481
+ Environment.id == environment_id
482
+ ).first()
483
+ if environment:
484
+ env_name = environment.name
485
+
486
+ project_slug = project.name.lower().replace(' ', '-')
487
+ if not working_dir:
488
+ working_dir = f"/var/www/{project_slug}"
489
+ if not python_path:
490
+ python_path = f"{working_dir}/venv/bin/python"
491
+
492
+ # Get app port
493
+ ports = self.db.query(PortAllocation).filter(
494
+ PortAllocation.project_id == project_id,
495
+ PortAllocation.port_type == 'app',
496
+ PortAllocation.status == 'allocated'
497
+ )
498
+ if environment_id:
499
+ ports = ports.filter(PortAllocation.environment_id == environment_id)
500
+ port_alloc = ports.first()
501
+ app_port = port_alloc.port if port_alloc else 8000
502
+
503
+ service_lines = [
504
+ f"# Systemd service for {project.name} ({env_name})",
505
+ f"# Generated by Quantum Admin - {datetime.now().isoformat()}",
506
+ "",
507
+ "[Unit]",
508
+ f"Description={project.name} Quantum Application ({env_name})",
509
+ "After=network.target postgresql.service redis.service",
510
+ "Wants=postgresql.service redis.service",
511
+ "",
512
+ "[Service]",
513
+ "Type=simple",
514
+ f"User=www-data",
515
+ f"Group=www-data",
516
+ f"WorkingDirectory={working_dir}",
517
+ f"Environment=APP_ENV={env_name}",
518
+ f"EnvironmentFile={working_dir}/.env",
519
+ f"ExecStart={python_path} -m uvicorn main:app --host 0.0.0.0 --port {app_port}",
520
+ "Restart=always",
521
+ "RestartSec=5",
522
+ "StandardOutput=journal",
523
+ "StandardError=journal",
524
+ "",
525
+ "# Security",
526
+ "NoNewPrivileges=true",
527
+ "ProtectSystem=strict",
528
+ f"ReadWritePaths={working_dir}",
529
+ "PrivateTmp=true",
530
+ "",
531
+ "[Install]",
532
+ "WantedBy=multi-user.target",
533
+ ]
534
+
535
+ return "\n".join(service_lines)
536
+
537
+ def _get_default_db_port(self, db_type: str) -> int:
538
+ """Get default port for database type"""
539
+ defaults = {
540
+ 'postgres': 5432,
541
+ 'mysql': 3306,
542
+ 'mongodb': 27017,
543
+ 'redis': 6379,
544
+ }
545
+ return defaults.get(db_type, 5432)
546
+
547
+ def _dict_to_yaml(self, data: Dict, indent: int = 0) -> str:
548
+ """Simple dict to YAML converter"""
549
+ lines = []
550
+ prefix = " " * indent
551
+
552
+ for key, value in data.items():
553
+ if value is None:
554
+ lines.append(f"{prefix}{key}:")
555
+ elif isinstance(value, dict):
556
+ lines.append(f"{prefix}{key}:")
557
+ lines.append(self._dict_to_yaml(value, indent + 1))
558
+ elif isinstance(value, list):
559
+ lines.append(f"{prefix}{key}:")
560
+ for item in value:
561
+ if isinstance(item, dict):
562
+ lines.append(f"{prefix} -")
563
+ for k, v in item.items():
564
+ lines.append(f"{prefix} {k}: {v}")
565
+ else:
566
+ lines.append(f"{prefix} - {item}")
567
+ else:
568
+ lines.append(f"{prefix}{key}: {value}")
569
+
570
+ return "\n".join(lines)
571
+
572
+
573
+ # Singleton instance
574
+ _config_generator = None
575
+
576
+
577
+ def get_config_generator(db: Session = None) -> ConfigGenerator:
578
+ """Get config generator instance"""
579
+ if db:
580
+ return ConfigGenerator(db)
581
+
582
+ global _config_generator
583
+ if _config_generator is None:
584
+ from .database import SessionLocal
585
+ _config_generator = ConfigGenerator(SessionLocal())
586
+ return _config_generator