wasm-cli 0.14.0__tar.gz → 0.14.2__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 (146) hide show
  1. {wasm_cli-0.14.0/src/wasm_cli.egg-info → wasm_cli-0.14.2}/PKG-INFO +1 -1
  2. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/pyproject.toml +1 -1
  3. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/setup.py +1 -1
  4. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/__init__.py +1 -1
  5. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/cli/commands/backup.py +18 -10
  6. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/cli/commands/store.py +20 -4
  7. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/cli/commands/webapp.py +71 -13
  8. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/cli/parser.py +27 -3
  9. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/completions/_wasm +31 -7
  10. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/completions/wasm.bash +25 -1
  11. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/completions/wasm.fish +24 -1
  12. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/core/__init__.py +2 -0
  13. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/core/logger.py +2 -0
  14. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/core/store.py +26 -0
  15. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/core/utils.py +18 -3
  16. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/deployers/__init__.py +2 -0
  17. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/deployers/base.py +3 -3
  18. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/deployers/helpers/__init__.py +4 -0
  19. wasm_cli-0.14.2/src/wasm/deployers/helpers/turbo.py +337 -0
  20. wasm_cli-0.14.2/src/wasm/deployers/helpers/workspace.py +423 -0
  21. wasm_cli-0.14.2/src/wasm/deployers/monorepo.py +1243 -0
  22. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/deployers/registry.py +2 -0
  23. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/managers/backup_manager.py +108 -14
  24. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/managers/service_manager.py +91 -45
  25. wasm_cli-0.14.2/src/wasm/templates/nginx/monorepo.conf.j2 +144 -0
  26. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/api/apps.py +6 -4
  27. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/api/backups.py +37 -17
  28. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/api/services.py +31 -25
  29. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/websockets/router.py +3 -1
  30. {wasm_cli-0.14.0 → wasm_cli-0.14.2/src/wasm_cli.egg-info}/PKG-INFO +1 -1
  31. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm_cli.egg-info/SOURCES.txt +4 -0
  32. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/LICENSE +0 -0
  33. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/MANIFEST.in +0 -0
  34. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/README.md +0 -0
  35. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/docs/MONITOR.md +0 -0
  36. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/docs/OBS_SETUP.md +0 -0
  37. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/docs/assets/logo.png +0 -0
  38. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/docs/assets/logo_bg.png +0 -0
  39. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/man/wasm.1 +0 -0
  40. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/setup.cfg +0 -0
  41. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/__main__.py +0 -0
  42. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/cli/__init__.py +0 -0
  43. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/cli/commands/__init__.py +0 -0
  44. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/cli/commands/cert.py +0 -0
  45. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/cli/commands/config.py +0 -0
  46. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/cli/commands/db.py +0 -0
  47. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/cli/commands/health.py +0 -0
  48. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/cli/commands/monitor.py +0 -0
  49. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/cli/commands/service.py +0 -0
  50. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/cli/commands/setup.py +0 -0
  51. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/cli/commands/site.py +0 -0
  52. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/cli/commands/version.py +0 -0
  53. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/cli/commands/web.py +0 -0
  54. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/cli/interactive.py +0 -0
  55. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/completions/__init__.py +0 -0
  56. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/core/config.py +0 -0
  57. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/core/dependencies.py +0 -0
  58. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/core/exceptions.py +0 -0
  59. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/core/update_checker.py +0 -0
  60. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/deployers/helpers/package_manager.py +0 -0
  61. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/deployers/helpers/path_resolver.py +0 -0
  62. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/deployers/helpers/prisma.py +0 -0
  63. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/deployers/nextjs.py +0 -0
  64. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/deployers/nodejs.py +0 -0
  65. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/deployers/python.py +0 -0
  66. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/deployers/static.py +0 -0
  67. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/deployers/vite.py +0 -0
  68. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/main.py +0 -0
  69. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/managers/__init__.py +0 -0
  70. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/managers/apache_manager.py +0 -0
  71. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/managers/base_manager.py +0 -0
  72. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/managers/cert_manager.py +0 -0
  73. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/managers/database/__init__.py +0 -0
  74. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/managers/database/base.py +0 -0
  75. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/managers/database/mongodb.py +0 -0
  76. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/managers/database/mysql.py +0 -0
  77. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/managers/database/postgres.py +0 -0
  78. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/managers/database/redis.py +0 -0
  79. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/managers/database/registry.py +0 -0
  80. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/managers/nginx_manager.py +0 -0
  81. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/managers/source_manager.py +0 -0
  82. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/monitor/__init__.py +0 -0
  83. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/monitor/ai_analyzer.py +0 -0
  84. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/monitor/email_notifier.py +0 -0
  85. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/monitor/process_monitor.py +0 -0
  86. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/monitor/threat_store.py +0 -0
  87. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/templates/__init__.py +0 -0
  88. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/templates/apache/proxy.conf.j2 +0 -0
  89. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/templates/apache/static.conf.j2 +0 -0
  90. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/templates/nginx/proxy.conf.j2 +0 -0
  91. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/templates/nginx/static.conf.j2 +0 -0
  92. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/templates/systemd/app.service.j2 +0 -0
  93. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/validators/__init__.py +0 -0
  94. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/validators/domain.py +0 -0
  95. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/validators/port.py +0 -0
  96. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/validators/source.py +0 -0
  97. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/validators/ssh.py +0 -0
  98. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/__init__.py +0 -0
  99. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/api/__init__.py +0 -0
  100. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/api/auth.py +0 -0
  101. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/api/certs.py +0 -0
  102. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/api/config.py +0 -0
  103. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/api/databases.py +0 -0
  104. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/api/jobs.py +0 -0
  105. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/api/monitor.py +0 -0
  106. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/api/router.py +0 -0
  107. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/api/sites.py +0 -0
  108. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/api/system.py +0 -0
  109. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/auth.py +0 -0
  110. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/jobs.py +0 -0
  111. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/server.py +0 -0
  112. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/static/css/main.css +0 -0
  113. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/static/index.html +0 -0
  114. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/static/js/components/cards.js +0 -0
  115. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/static/js/components/jobs.js +0 -0
  116. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/static/js/components/metrics.js +0 -0
  117. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/static/js/components/skeleton.js +0 -0
  118. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/static/js/core/api.js +0 -0
  119. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/static/js/core/dialogs.js +0 -0
  120. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/static/js/core/notifications.js +0 -0
  121. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/static/js/core/router.js +0 -0
  122. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/static/js/core/search.js +0 -0
  123. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/static/js/core/shortcuts.js +0 -0
  124. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/static/js/core/theme.js +0 -0
  125. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/static/js/core/ui.js +0 -0
  126. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/static/js/core/websocket.js +0 -0
  127. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/static/js/main.js +0 -0
  128. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/static/js/pages/apps.js +0 -0
  129. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/static/js/pages/backups.js +0 -0
  130. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/static/js/pages/certs.js +0 -0
  131. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/static/js/pages/config.js +0 -0
  132. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/static/js/pages/dashboard.js +0 -0
  133. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/static/js/pages/databases.js +0 -0
  134. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/static/js/pages/jobs.js +0 -0
  135. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/static/js/pages/logs.js +0 -0
  136. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/static/js/pages/monitor.js +0 -0
  137. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/static/js/pages/services.js +0 -0
  138. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/static/js/pages/sites.js +0 -0
  139. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/static/login.html +0 -0
  140. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/static/logo.png +0 -0
  141. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/static/logo.webp +0 -0
  142. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm/web/websockets/__init__.py +0 -0
  143. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm_cli.egg-info/dependency_links.txt +0 -0
  144. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm_cli.egg-info/entry_points.txt +0 -0
  145. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm_cli.egg-info/requires.txt +0 -0
  146. {wasm_cli-0.14.0 → wasm_cli-0.14.2}/src/wasm_cli.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wasm-cli
3
- Version: 0.14.0
3
+ Version: 0.14.2
4
4
  Summary: Web App System Management - Deploy and manage web applications on Linux servers
5
5
  Author: Yago López Prado
6
6
  Author-email: Yago López Prado <yago.lopez.adeje@gmail.com>
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "wasm-cli"
7
- version = "0.14.0"
7
+ version = "0.14.2"
8
8
  description = "Web App System Management - Deploy and manage web applications on Linux servers"
9
9
  readme = "README.md"
10
10
  license = { text = "WASM-NCSAL" }
@@ -9,7 +9,7 @@ from setuptools import setup, find_packages
9
9
 
10
10
  setup(
11
11
  name="wasm-cli",
12
- version="0.14.0",
12
+ version="0.14.2",
13
13
  description="Web App System Management - Deploy and manage web applications on Linux servers",
14
14
  author="Yago López Prado",
15
15
  author_email="yago.lopez.adeje@gmail.com",
@@ -8,7 +8,7 @@ WASM - Web App System Management
8
8
  A robust CLI tool for deploying and managing web applications on Linux servers.
9
9
  """
10
10
 
11
- __version__ = "0.14.0"
11
+ __version__ = "0.14.2"
12
12
  __author__ = "Yago López Prado"
13
13
  __license__ = "WASM-NCSAL"
14
14
 
@@ -105,6 +105,7 @@ def _backup_create(args: Namespace, verbose: bool) -> int:
105
105
  include_env = not getattr(args, "no_env", False)
106
106
  include_node_modules = getattr(args, "include_node_modules", False)
107
107
  include_build = getattr(args, "include_build", False)
108
+ include_databases = getattr(args, "include_databases", False)
108
109
  tags = getattr(args, "tags", None)
109
110
 
110
111
  if not domain:
@@ -127,6 +128,7 @@ def _backup_create(args: Namespace, verbose: bool) -> int:
127
128
  include_env=include_env,
128
129
  include_node_modules=include_node_modules,
129
130
  include_build=include_build,
131
+ include_databases=include_databases,
130
132
  tags=tag_list,
131
133
  )
132
134
 
@@ -135,7 +137,13 @@ def _backup_create(args: Namespace, verbose: bool) -> int:
135
137
  logger.info(f" Size: {metadata.size_human}")
136
138
  if metadata.git_commit:
137
139
  logger.info(f" Commit: {metadata.git_commit} ({metadata.git_branch})")
138
-
140
+ if metadata.database_backups:
141
+ db_count = len(metadata.database_backups)
142
+ logger.info(f" Databases: {db_count} backed up")
143
+ for db_info in metadata.database_backups:
144
+ size_mb = db_info.get("size_bytes", 0) / (1024 * 1024)
145
+ logger.info(f" - {db_info['engine']}/{db_info['name']} ({size_mb:.1f} MB)")
146
+
139
147
  return 0
140
148
 
141
149
  except BackupError as e:
@@ -195,7 +203,7 @@ def _backup_list(args: Namespace, verbose: bool) -> int:
195
203
  by_domain[backup.domain].append(backup)
196
204
 
197
205
  for dom, dom_backups in by_domain.items():
198
- logger.info(f"\n📦 {dom}")
206
+ logger.info(f"\n[{dom}]")
199
207
  _print_backup_table(dom_backups, logger, indent=True)
200
208
 
201
209
  return 0
@@ -226,7 +234,7 @@ def _print_backup_table(backups, logger, indent: bool = False):
226
234
  desc_str = f" - {backup.description}"
227
235
 
228
236
  logger.info(
229
- f"{prefix} {backup.id}: {backup.size_human}, "
237
+ f"{prefix}- {backup.id}: {backup.size_human}, "
230
238
  f"{backup.age}{commit_str}{tags_str}{desc_str}"
231
239
  )
232
240
 
@@ -359,17 +367,17 @@ def _backup_verify(args: Namespace, verbose: bool) -> int:
359
367
 
360
368
  if result["valid"]:
361
369
  logger.success("Backup is valid")
362
- if result.get("checksum_verified"):
363
- logger.info(" Checksum verified")
364
- if result.get("archive_valid"):
365
- logger.info(f" Archive valid ({result.get('file_count', '?')} files)")
370
+ if result.get("checksum_ok"):
371
+ logger.info(" [OK] Checksum verified")
372
+ if result.get("files_ok"):
373
+ logger.info(f" [OK] Archive valid ({result.get('file_count', '?')} files)")
366
374
  else:
367
375
  logger.error("Backup is invalid")
368
376
  for err in result["errors"]:
369
- logger.error(f" {err}")
370
-
377
+ logger.error(f" [ERROR] {err}")
378
+
371
379
  for warn in result.get("warnings", []):
372
- logger.warning(f" {warn}")
380
+ logger.warning(f" [WARN] {warn}")
373
381
 
374
382
  return 0 if result["valid"] else 1
375
383
 
@@ -313,13 +313,29 @@ def _store_import(args: Namespace, verbose: bool) -> int:
313
313
  else:
314
314
  logger.step(2, 3, "No Apache sites found")
315
315
 
316
- # 3. Import from systemd services (wasm-* prefix)
316
+ # 3. Import from systemd services (both legacy wasm-* and new format)
317
317
  logger.step(3, 3, "Scanning systemd services")
318
318
  if SYSTEMD_DIR.exists():
319
- for service_file in SYSTEMD_DIR.glob("wasm-*.service"):
319
+ # Find all potential WASM service files
320
+ service_files = list(SYSTEMD_DIR.glob("wasm-*.service"))
321
+ # Also check for services matching domain pattern (new format)
322
+ for sf in SYSTEMD_DIR.glob("*.service"):
323
+ name = sf.stem
324
+ # Skip if already found as wasm-* or if it's a system service
325
+ if name.startswith("wasm-") or not "-" in name:
326
+ continue
327
+ # Check if it looks like a domain-based name (has hyphen, no @ or other special chars)
328
+ if "@" not in name and name.count("-") >= 1:
329
+ service_files.append(sf)
330
+
331
+ for service_file in service_files:
320
332
  # Extract app name from service file name
321
- service_name = service_file.stem # wasm-example-com
322
- app_name = service_name[5:] # example-com (remove wasm- prefix)
333
+ service_name = service_file.stem
334
+ # Handle both legacy (wasm-example-com) and new format (example-com)
335
+ if service_name.startswith("wasm-"):
336
+ app_name = service_name[5:] # Remove wasm- prefix
337
+ else:
338
+ app_name = service_name
323
339
 
324
340
  if store.get_service(app_name):
325
341
  continue
@@ -202,10 +202,14 @@ def _handle_create(args: Namespace) -> int:
202
202
  logger.key_value("Package Manager", package_manager)
203
203
  logger.key_value("SSL", "Yes" if not args.no_ssl else "No")
204
204
  logger.blank()
205
-
205
+
206
+ # Handle monorepo deployments specially
207
+ if app_type == "monorepo":
208
+ return _handle_monorepo_create(args, domain, env_vars, logger)
209
+
206
210
  # Get deployer
207
211
  deployer = get_deployer(app_type, verbose=args.verbose)
208
-
212
+
209
213
  # Configure deployer
210
214
  deployer.configure(
211
215
  domain=domain,
@@ -217,10 +221,51 @@ def _handle_create(args: Namespace) -> int:
217
221
  env_vars=env_vars,
218
222
  package_manager=package_manager,
219
223
  )
220
-
224
+
221
225
  # Run deployment
222
226
  deployer.deploy()
223
-
227
+
228
+ return 0
229
+
230
+
231
+ def _handle_monorepo_create(args: Namespace, domain: str, env_vars: Dict, logger: Logger) -> int:
232
+ """Handle monorepo deployment specially."""
233
+ from wasm.deployers.monorepo import MonorepoDeployer
234
+
235
+ # Parse subdomain overrides
236
+ subdomain_overrides = {}
237
+ if getattr(args, "subdomains", None):
238
+ for mapping in args.subdomains:
239
+ if ":" in mapping:
240
+ app_name, subdomain = mapping.split(":", 1)
241
+ subdomain_overrides[app_name] = subdomain
242
+ else:
243
+ logger.warning(f"Invalid subdomain mapping: {mapping} (expected app:subdomain)")
244
+
245
+ # Get workspace filter
246
+ workspace_filter = getattr(args, "workspaces", None)
247
+
248
+ # Get skip_database flag
249
+ skip_database = getattr(args, "no_database", False)
250
+
251
+ # Create and configure deployer
252
+ deployer = MonorepoDeployer(verbose=args.verbose)
253
+
254
+ deployer.configure(
255
+ domain=domain,
256
+ source=args.source,
257
+ webserver=args.webserver,
258
+ ssl=not args.no_ssl,
259
+ branch=args.branch,
260
+ env_vars=env_vars,
261
+ subdomain_overrides=subdomain_overrides,
262
+ workspace_filter=workspace_filter,
263
+ skip_database=skip_database,
264
+ )
265
+
266
+ # Run deployment
267
+ deployer.deploy()
268
+
224
269
  return 0
225
270
 
226
271
 
@@ -457,7 +502,7 @@ def _handle_start(args: Namespace) -> int:
457
502
  def _handle_update(args: Namespace) -> int:
458
503
  """
459
504
  Handle webapp update command with zero-downtime strategy.
460
-
505
+
461
506
  Strategy:
462
507
  1. Create pre-update backup (for rollback)
463
508
  2. Pull/fetch new code
@@ -468,11 +513,24 @@ def _handle_update(args: Namespace) -> int:
468
513
  """
469
514
  logger = Logger(verbose=args.verbose)
470
515
  config = Config()
471
-
516
+
517
+ from wasm.core.store import get_store
518
+ store = get_store()
519
+
472
520
  domain = validate_domain(args.domain)
473
- app_name = domain_to_app_name(domain)
474
- app_path = config.apps_directory / app_name
475
-
521
+
522
+ # Consultar BD primero para obtener el app_path real
523
+ app = store.get_app(domain)
524
+
525
+ if app and app.app_path:
526
+ # Usar el path almacenado en la BD (soporta apps legacy con prefijo wasm-)
527
+ app_path = Path(app.app_path)
528
+ app_name = app_path.name
529
+ else:
530
+ # Fallback para apps no registradas en BD
531
+ app_name = domain_to_app_name(domain)
532
+ app_path = config.apps_directory / app_name
533
+
476
534
  if not app_path.exists():
477
535
  raise WASMError(f"Application not found: {domain}")
478
536
 
@@ -639,7 +697,7 @@ def _handle_delete(args: Namespace) -> int:
639
697
  try:
640
698
  status = service_manager.status(app_name)
641
699
  if status.get("exists"):
642
- logger.key_value("Stop and remove service", f"wasm-{app_name}")
700
+ logger.key_value("Stop and remove service", app_name)
643
701
  except Exception:
644
702
  pass
645
703
 
@@ -734,9 +792,9 @@ def _handle_logs(args: Namespace) -> int:
734
792
  domain = validate_domain(args.domain)
735
793
  app_name = domain_to_app_name(domain)
736
794
 
737
- # Get service name (ServiceManager adds prefix internally)
738
- service_name = f"wasm-{app_name}"
739
-
795
+ # Get resolved service name (handles both legacy wasm-* and new format)
796
+ service_name = service_manager._resolve_service_name(app_name)
797
+
740
798
  if args.follow:
741
799
  # Use journalctl directly for follow mode
742
800
  import subprocess
@@ -36,7 +36,7 @@ def create_parser() -> argparse.ArgumentParser:
36
36
  CLI tool for deploying and managing web applications on Linux servers.
37
37
  Automates: Git clone, build, systemd, Nginx/Apache, SSL (Let's Encrypt).
38
38
 
39
- Supported app types: nextjs, nodejs, vite, python, static
39
+ Supported app types: nextjs, nodejs, vite, python, static, monorepo
40
40
 
41
41
  QUICK START:
42
42
  wasm setup init Initialize directories (first time, requires sudo)
@@ -179,7 +179,7 @@ def _add_webapp_commands(subparsers) -> None:
179
179
  )
180
180
  create.add_argument(
181
181
  "--type", "-t",
182
- choices=["nextjs", "nodejs", "vite", "python", "static", "auto"],
182
+ choices=["nextjs", "nodejs", "vite", "python", "static", "monorepo", "auto"],
183
183
  default="auto",
184
184
  help="Application type (default: auto-detect)",
185
185
  )
@@ -213,7 +213,26 @@ def _add_webapp_commands(subparsers) -> None:
213
213
  default="auto",
214
214
  help="Package manager to use (default: auto-detect)",
215
215
  )
216
-
216
+
217
+ # Monorepo-specific options
218
+ create.add_argument(
219
+ "--subdomains",
220
+ nargs="+",
221
+ metavar="APP:SUBDOMAIN",
222
+ help="Subdomain mapping for monorepo apps (e.g., erp-backend:api web-gateway:app)",
223
+ )
224
+ create.add_argument(
225
+ "--workspaces",
226
+ nargs="+",
227
+ metavar="NAME",
228
+ help="Specific workspace apps to deploy (default: all)",
229
+ )
230
+ create.add_argument(
231
+ "--no-database",
232
+ action="store_true",
233
+ help="Skip database provisioning for monorepo",
234
+ )
235
+
217
236
  # list (alias: ls)
218
237
  subparsers.add_parser(
219
238
  "list",
@@ -908,6 +927,11 @@ def _add_backup_parser(subparsers) -> None:
908
927
  action="store_true",
909
928
  help="Include build artifacts (.next, dist, build)",
910
929
  )
930
+ create.add_argument(
931
+ "--include-databases", "--include-db",
932
+ action="store_true",
933
+ help="Include associated database dumps in backup",
934
+ )
911
935
  create.add_argument(
912
936
  "--tags", "-t",
913
937
  help="Comma-separated tags for the backup",
@@ -14,14 +14,38 @@
14
14
 
15
15
  # Helper functions
16
16
  _wasm_apps() {
17
- local apps_dir="/var/www/apps"
18
17
  local apps=()
19
- if [[ -d "$apps_dir" ]]; then
20
- for app in "$apps_dir"/*/; do
21
- if [[ -d "$app" ]]; then
22
- apps+=("$(basename "$app")")
23
- fi
24
- done 2>/dev/null
18
+
19
+ # Try to get domains from SQLite store (fast and accurate)
20
+ local db_path="/var/lib/wasm/wasm.db"
21
+ if [[ ! -f "$db_path" ]]; then
22
+ db_path="$HOME/.local/share/wasm/wasm.db"
23
+ fi
24
+
25
+ if [[ -f "$db_path" ]] && command -v sqlite3 &>/dev/null; then
26
+ apps=(${(f)"$(sqlite3 "$db_path" "SELECT domain FROM apps" 2>/dev/null)"})
27
+ else
28
+ # Fallback: list app directories and convert names to domains
29
+ local apps_dir="/var/www/apps"
30
+ if [[ -d "$apps_dir" ]]; then
31
+ for app in "$apps_dir"/*/; do
32
+ if [[ -d "$app" ]]; then
33
+ local name
34
+ name=$(basename "$app")
35
+ # Convert directory name to domain format
36
+ # Legacy: wasm-example-com -> example.com
37
+ # New: example-com -> example.com
38
+ if [[ "$name" == wasm-* ]]; then
39
+ apps+=("${${name#wasm-}//\-/.}")
40
+ elif [[ "$name" == *-* ]]; then
41
+ # New format: convert hyphens to dots
42
+ apps+=("${name//\-/.}")
43
+ else
44
+ apps+=("$name")
45
+ fi
46
+ fi
47
+ done 2>/dev/null
48
+ fi
25
49
  fi
26
50
  _describe -t apps 'deployed applications' apps
27
51
  }
@@ -16,11 +16,35 @@
16
16
 
17
17
  # Helper function to get list of deployed apps (domains)
18
18
  _wasm_get_apps() {
19
+ # Try to get domains from SQLite store (fast and accurate)
20
+ local db_path="/var/lib/wasm/wasm.db"
21
+ if [[ ! -f "$db_path" ]]; then
22
+ db_path="$HOME/.local/share/wasm/wasm.db"
23
+ fi
24
+
25
+ if [[ -f "$db_path" ]] && command -v sqlite3 &>/dev/null; then
26
+ sqlite3 "$db_path" "SELECT domain FROM apps" 2>/dev/null
27
+ return
28
+ fi
29
+
30
+ # Fallback: list app directories and convert names to domains
19
31
  local apps_dir="/var/www/apps"
20
32
  if [[ -d "$apps_dir" ]]; then
21
33
  for app in "$apps_dir"/*/; do
22
34
  if [[ -d "$app" ]]; then
23
- basename "$app"
35
+ local name
36
+ name=$(basename "$app")
37
+ # Convert directory name to domain format
38
+ # Legacy: wasm-example-com -> example.com
39
+ # New: example-com -> example.com
40
+ if [[ "$name" == wasm-* ]]; then
41
+ echo "${name#wasm-}" | tr '-' '.'
42
+ elif [[ "$name" == *-* ]]; then
43
+ # New format: convert hyphens to dots
44
+ echo "$name" | tr '-' '.'
45
+ else
46
+ echo "$name"
47
+ fi
24
48
  fi
25
49
  done 2>/dev/null
26
50
  fi
@@ -14,11 +14,34 @@ complete -c wasm -f
14
14
 
15
15
  # Helper functions
16
16
  function __wasm_get_apps
17
+ # Try to get domains from SQLite store (fast and accurate)
18
+ set -l db_path "/var/lib/wasm/wasm.db"
19
+ if not test -f "$db_path"
20
+ set db_path "$HOME/.local/share/wasm/wasm.db"
21
+ end
22
+
23
+ if test -f "$db_path"; and command -v sqlite3 >/dev/null
24
+ sqlite3 "$db_path" "SELECT domain FROM apps" 2>/dev/null
25
+ return
26
+ end
27
+
28
+ # Fallback: list app directories and convert names to domains
17
29
  set -l apps_dir "/var/www/apps"
18
30
  if test -d "$apps_dir"
19
31
  for app in $apps_dir/*/
20
32
  if test -d "$app"
21
- basename "$app"
33
+ set -l name (basename "$app")
34
+ # Convert directory name to domain format
35
+ # Legacy: wasm-example-com -> example.com
36
+ # New: example-com -> example.com
37
+ if string match -q 'wasm-*' "$name"
38
+ string replace 'wasm-' '' "$name" | string replace -a '-' '.'
39
+ else if string match -q '*-*' "$name"
40
+ # New format: convert hyphens to dots
41
+ string replace -a '-' '.' "$name"
42
+ else
43
+ echo "$name"
44
+ end
22
45
  end
23
46
  end 2>/dev/null
24
47
  end
@@ -11,6 +11,7 @@ from wasm.core.store import (
11
11
  Service,
12
12
  Database,
13
13
  DatabaseUser,
14
+ MonorepoWorkspace,
14
15
  AppType,
15
16
  AppStatus,
16
17
  WebServer,
@@ -28,6 +29,7 @@ __all__ = [
28
29
  "Service",
29
30
  "Database",
30
31
  "DatabaseUser",
32
+ "MonorepoWorkspace",
31
33
  "AppType",
32
34
  "AppStatus",
33
35
  "WebServer",
@@ -63,6 +63,8 @@ class Icons:
63
63
  CLOCK = "⏱"
64
64
  FOLDER = "📁"
65
65
  FILE = "📄"
66
+ DATABASE = "🗄️"
67
+ SEARCH = "🔍"
66
68
 
67
69
 
68
70
  class LogLevel(Enum):
@@ -35,6 +35,7 @@ class AppType(str, Enum):
35
35
  PYTHON = "python"
36
36
  VITE = "vite"
37
37
  STATIC = "static"
38
+ MONOREPO = "monorepo"
38
39
  UNKNOWN = "unknown"
39
40
 
40
41
 
@@ -211,6 +212,31 @@ class DatabaseUser:
211
212
  return cls(**dict(row))
212
213
 
213
214
 
215
+ @dataclass
216
+ class MonorepoWorkspace:
217
+ """
218
+ Configuration for a workspace app within a monorepo.
219
+
220
+ Used by MonorepoDeployer to track individual apps in a Turborepo/pnpm
221
+ workspace monorepo.
222
+ """
223
+ name: str = ""
224
+ path: str = ""
225
+ app_type: str = AppType.UNKNOWN.value
226
+ subdomain: str = ""
227
+ port: int = 3000
228
+ start_command: Optional[str] = None
229
+ health_check: str = "/"
230
+ env_vars: Dict[str, str] = field(default_factory=dict)
231
+
232
+ def to_dict(self) -> Dict[str, Any]:
233
+ """Convert to dictionary."""
234
+ d = asdict(self)
235
+ if isinstance(d.get('env_vars'), dict):
236
+ d['env_vars'] = json.dumps(d['env_vars'])
237
+ return d
238
+
239
+
214
240
  # Schema version for migrations
215
241
  SCHEMA_VERSION = 1
216
242
 
@@ -219,12 +219,27 @@ def sanitize_name(name: str) -> str:
219
219
  def domain_to_app_name(domain: str) -> str:
220
220
  """
221
221
  Convert a domain to an application name.
222
-
222
+
223
223
  Args:
224
224
  domain: Domain name (e.g., "myapp.example.com").
225
-
225
+
226
+ Returns:
227
+ Application name (e.g., "myapp-example-com").
228
+ """
229
+ return sanitize_name(domain)
230
+
231
+
232
+ def legacy_app_name(domain: str) -> str:
233
+ """
234
+ Get legacy app name format (with wasm- prefix).
235
+
236
+ Used for backwards compatibility with apps created before v0.14.1.
237
+
238
+ Args:
239
+ domain: Domain name (e.g., "myapp.example.com").
240
+
226
241
  Returns:
227
- Application name (e.g., "wasm-myapp-example-com").
242
+ Legacy application name (e.g., "wasm-myapp-example-com").
228
243
  """
229
244
  return f"wasm-{sanitize_name(domain)}"
230
245
 
@@ -2,10 +2,12 @@
2
2
 
3
3
  from wasm.deployers.base import BaseDeployer
4
4
  from wasm.deployers.registry import DeployerRegistry, get_deployer, detect_app_type
5
+ from wasm.deployers.monorepo import MonorepoDeployer
5
6
 
6
7
  __all__ = [
7
8
  "BaseDeployer",
8
9
  "DeployerRegistry",
9
10
  "get_deployer",
10
11
  "detect_app_type",
12
+ "MonorepoDeployer",
11
13
  ]
@@ -899,9 +899,9 @@ class BaseDeployer(ABC):
899
899
  app = self.store.get_app(self.domain)
900
900
  app_id = app.id if app else None
901
901
 
902
- # Service name without prefix (store handles that)
902
+ # Service name (no prefix for new format)
903
903
  service_name = self.app_name
904
- service_file = SYSTEMD_DIR / f"wasm-{self.app_name}.service"
904
+ service_file = SYSTEMD_DIR / f"{self.app_name}.service"
905
905
 
906
906
  existing_service = self.store.get_service(service_name)
907
907
 
@@ -1142,7 +1142,7 @@ class BaseDeployer(ABC):
1142
1142
  # Basic info
1143
1143
  protocol = "https" if ssl_obtained else "http"
1144
1144
  self.logger.key_value("URL", f"{protocol}://{self.domain}")
1145
- self.logger.key_value("Service", f"wasm-{self.app_name}")
1145
+ self.logger.key_value("Service", self.app_name)
1146
1146
  self.logger.key_value("Port", str(self.port))
1147
1147
  self.logger.key_value("App Path", str(self.app_path))
1148
1148
 
@@ -12,9 +12,13 @@ to improve maintainability and testability.
12
12
  from wasm.deployers.helpers.package_manager import PackageManagerHelper
13
13
  from wasm.deployers.helpers.path_resolver import PathResolver
14
14
  from wasm.deployers.helpers.prisma import PrismaHelper
15
+ from wasm.deployers.helpers.workspace import WorkspaceHelper
16
+ from wasm.deployers.helpers.turbo import TurboHelper
15
17
 
16
18
  __all__ = [
17
19
  "PackageManagerHelper",
18
20
  "PathResolver",
19
21
  "PrismaHelper",
22
+ "WorkspaceHelper",
23
+ "TurboHelper",
20
24
  ]