appmanager-server 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. appmanager/__init__.py +52 -0
  2. appmanager/admin/__init__.py +3 -0
  3. appmanager/admin/app_installer.py +739 -0
  4. appmanager/admin/members.py +95 -0
  5. appmanager/admin/registry.py +372 -0
  6. appmanager/admin/routes.py +900 -0
  7. appmanager/api/__init__.py +5 -0
  8. appmanager/api/routes.py +373 -0
  9. appmanager/app_config.py +101 -0
  10. appmanager/auth/__init__.py +3 -0
  11. appmanager/auth/oauth.py +77 -0
  12. appmanager/auth/routes.py +224 -0
  13. appmanager/auth/utils.py +175 -0
  14. appmanager/bridge.py +90 -0
  15. appmanager/cli.py +1171 -0
  16. appmanager/config.py +58 -0
  17. appmanager/database.py +120 -0
  18. appmanager/extension.py +198 -0
  19. appmanager/extensions.py +225 -0
  20. appmanager/health.py +133 -0
  21. appmanager/hooks.py +187 -0
  22. appmanager/middleware.py +256 -0
  23. appmanager/models.py +307 -0
  24. appmanager/py.typed +1 -0
  25. appmanager/sdk.py +30 -0
  26. appmanager/security.py +227 -0
  27. appmanager/security_scanner.py +582 -0
  28. appmanager/signals.py +21 -0
  29. appmanager/static/css/app.css +808 -0
  30. appmanager/static/vendor/fonts/fonts.css +288 -0
  31. appmanager/static/vendor/fonts/jetbrainsmono-0.woff2 +0 -0
  32. appmanager/static/vendor/fonts/jetbrainsmono-1.woff2 +0 -0
  33. appmanager/static/vendor/fonts/jetbrainsmono-10.woff2 +0 -0
  34. appmanager/static/vendor/fonts/jetbrainsmono-11.woff2 +0 -0
  35. appmanager/static/vendor/fonts/jetbrainsmono-12.woff2 +0 -0
  36. appmanager/static/vendor/fonts/jetbrainsmono-13.woff2 +0 -0
  37. appmanager/static/vendor/fonts/jetbrainsmono-14.woff2 +0 -0
  38. appmanager/static/vendor/fonts/jetbrainsmono-15.woff2 +0 -0
  39. appmanager/static/vendor/fonts/jetbrainsmono-16.woff2 +0 -0
  40. appmanager/static/vendor/fonts/jetbrainsmono-17.woff2 +0 -0
  41. appmanager/static/vendor/fonts/jetbrainsmono-18.woff2 +0 -0
  42. appmanager/static/vendor/fonts/jetbrainsmono-19.woff2 +0 -0
  43. appmanager/static/vendor/fonts/jetbrainsmono-2.woff2 +0 -0
  44. appmanager/static/vendor/fonts/jetbrainsmono-20.woff2 +0 -0
  45. appmanager/static/vendor/fonts/jetbrainsmono-21.woff2 +0 -0
  46. appmanager/static/vendor/fonts/jetbrainsmono-22.woff2 +0 -0
  47. appmanager/static/vendor/fonts/jetbrainsmono-23.woff2 +0 -0
  48. appmanager/static/vendor/fonts/jetbrainsmono-3.woff2 +0 -0
  49. appmanager/static/vendor/fonts/jetbrainsmono-4.woff2 +0 -0
  50. appmanager/static/vendor/fonts/jetbrainsmono-5.woff2 +0 -0
  51. appmanager/static/vendor/fonts/jetbrainsmono-6.woff2 +0 -0
  52. appmanager/static/vendor/fonts/jetbrainsmono-7.woff2 +0 -0
  53. appmanager/static/vendor/fonts/jetbrainsmono-8.woff2 +0 -0
  54. appmanager/static/vendor/fonts/jetbrainsmono-9.woff2 +0 -0
  55. appmanager/static/vendor/fonts/spacegrotesk-0.woff2 +0 -0
  56. appmanager/static/vendor/fonts/spacegrotesk-1.woff2 +0 -0
  57. appmanager/static/vendor/fonts/spacegrotesk-10.woff2 +0 -0
  58. appmanager/static/vendor/fonts/spacegrotesk-11.woff2 +0 -0
  59. appmanager/static/vendor/fonts/spacegrotesk-2.woff2 +0 -0
  60. appmanager/static/vendor/fonts/spacegrotesk-3.woff2 +0 -0
  61. appmanager/static/vendor/fonts/spacegrotesk-4.woff2 +0 -0
  62. appmanager/static/vendor/fonts/spacegrotesk-5.woff2 +0 -0
  63. appmanager/static/vendor/fonts/spacegrotesk-6.woff2 +0 -0
  64. appmanager/static/vendor/fonts/spacegrotesk-7.woff2 +0 -0
  65. appmanager/static/vendor/fonts/spacegrotesk-8.woff2 +0 -0
  66. appmanager/static/vendor/fonts/spacegrotesk-9.woff2 +0 -0
  67. appmanager/static/vendor/htmx/htmx.min.js +1 -0
  68. appmanager/templates/admin/_member_drawer.html +98 -0
  69. appmanager/templates/admin/_members_table.html +108 -0
  70. appmanager/templates/admin/app_detail.html +136 -0
  71. appmanager/templates/admin/dashboard.html +902 -0
  72. appmanager/templates/admin/flairs.html +90 -0
  73. appmanager/templates/admin/permissions.html +88 -0
  74. appmanager/templates/admin/roles.html +119 -0
  75. appmanager/templates/admin/users.html +156 -0
  76. appmanager/templates/auth/login.html +58 -0
  77. appmanager/templates/auth/profile.html +44 -0
  78. appmanager/templates/base.html +76 -0
  79. appmanager/templates/errors/401.html +19 -0
  80. appmanager/templates/errors/403.html +19 -0
  81. appmanager/templates/errors/404.html +18 -0
  82. appmanager/templates/errors/500.html +19 -0
  83. appmanager/templates/index.html +54 -0
  84. appmanager_server-0.2.0.dist-info/METADATA +320 -0
  85. appmanager_server-0.2.0.dist-info/RECORD +89 -0
  86. appmanager_server-0.2.0.dist-info/WHEEL +5 -0
  87. appmanager_server-0.2.0.dist-info/entry_points.txt +5 -0
  88. appmanager_server-0.2.0.dist-info/licenses/LICENSE +21 -0
  89. appmanager_server-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,19 @@
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}401 — Authentication Required | AppManager{% endblock %}
4
+
5
+ {% block content %}
6
+ <div style="display:flex; justify-content:center; align-items:center; min-height:60vh;">
7
+ <div class="card boot-item" style="max-width:500px; text-align:center; padding:2.5rem;">
8
+ <div class="mono" style="font-size:4rem; font-weight:700; color:var(--accent); line-height:1; margin-bottom:0.5rem;">401</div>
9
+ <h1 style="font-size:1.4rem; font-weight:600; margin-bottom:1rem;">Authentication Required</h1>
10
+ <p class="text-muted" style="margin-bottom:2rem; line-height:1.6;">
11
+ You must be logged into AppManager to access this protected application or feature.
12
+ </p>
13
+ <div class="flex-center" style="justify-content:center; gap:1rem;">
14
+ <a href="/auth/login" class="btn btn-primary">Log In to Continue</a>
15
+ <a href="/" class="btn btn-secondary">Return to Home</a>
16
+ </div>
17
+ </div>
18
+ </div>
19
+ {% endblock %}
@@ -0,0 +1,19 @@
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}403 — Access Denied | AppManager{% endblock %}
4
+
5
+ {% block content %}
6
+ <div style="display:flex; justify-content:center; align-items:center; min-height:60vh;">
7
+ <div class="card boot-item" style="max-width:500px; text-align:center; padding:2.5rem;">
8
+ <div class="mono" style="font-size:4rem; font-weight:700; color:var(--danger); line-height:1; margin-bottom:0.5rem;">403</div>
9
+ <h1 style="font-size:1.4rem; font-weight:600; margin-bottom:1rem;">Access Denied</h1>
10
+ <p class="text-muted" style="margin-bottom:2rem; line-height:1.6;">
11
+ You do not have permission to access this resource or application. Please contact your administrator if you believe this is an error.
12
+ </p>
13
+ <div class="flex-center" style="justify-content:center; gap:1rem;">
14
+ <a href="/" class="btn btn-primary">Return to Home</a>
15
+ <a href="/auth/profile" class="btn btn-secondary">My Profile</a>
16
+ </div>
17
+ </div>
18
+ </div>
19
+ {% endblock %}
@@ -0,0 +1,18 @@
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}404 — Not Found | AppManager{% endblock %}
4
+
5
+ {% block content %}
6
+ <div style="display:flex; justify-content:center; align-items:center; min-height:60vh;">
7
+ <div class="card boot-item" style="max-width:500px; text-align:center; padding:2.5rem;">
8
+ <div class="mono" style="font-size:4rem; font-weight:700; color:var(--danger); line-height:1; margin-bottom:0.5rem;">404</div>
9
+ <h1 style="font-size:1.4rem; font-weight:600; margin-bottom:1rem;">Page or App Not Found</h1>
10
+ <p class="text-muted" style="margin-bottom:2rem; line-height:1.6;">
11
+ The page or application you requested could not be located. It may have been uninstalled, moved, or the URL might be mistyped.
12
+ </p>
13
+ <div class="flex-center" style="justify-content:center;">
14
+ <a href="/" class="btn btn-primary">Return to Home</a>
15
+ </div>
16
+ </div>
17
+ </div>
18
+ {% endblock %}
@@ -0,0 +1,19 @@
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}500 — Server Error | AppManager{% endblock %}
4
+
5
+ {% block content %}
6
+ <div style="display:flex; justify-content:center; align-items:center; min-height:60vh;">
7
+ <div class="card boot-item" style="max-width:520px; text-align:center; padding:2.5rem;">
8
+ <div class="mono" style="font-size:4rem; font-weight:700; color:var(--warning); line-height:1; margin-bottom:0.5rem;">500</div>
9
+ <h1 style="font-size:1.4rem; font-weight:600; margin-bottom:1rem;">Internal Server Error</h1>
10
+ <p class="text-muted" style="margin-bottom:2rem; line-height:1.6;">
11
+ AppManager encountered an unexpected condition while processing your request. Please try refreshing the page or contact system administrators if the issue persists.
12
+ </p>
13
+ <div class="flex-center" style="justify-content:center; gap:1rem;">
14
+ <a href="javascript:location.reload()" class="btn btn-primary">↻ Refresh Page</a>
15
+ <a href="/" class="btn btn-secondary">Return to Home</a>
16
+ </div>
17
+ </div>
18
+ </div>
19
+ {% endblock %}
@@ -0,0 +1,54 @@
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}Applications — AppManager{% endblock %}
4
+
5
+ {% block content %}
6
+ <div class="page-header">
7
+ <div>
8
+ <h1 class="page-title">Installed Applications</h1>
9
+ <p class="page-subtitle">Select an application below to launch it inside your session.</p>
10
+ </div>
11
+ {% if apps %}
12
+ <span class="badge badge-primary">{{ apps|length }} available</span>
13
+ {% endif %}
14
+ </div>
15
+
16
+ {{ render_slot('dashboard_widget', user=user) if render_slot is defined else '' }}
17
+
18
+ {% if apps %}
19
+ <div class="grid grid-apps">
20
+ {% for app in apps %}
21
+ <div class="app-card boot-item" style="animation-delay: {{ loop.index0 * 60 }}ms;">
22
+ <div class="app-body">
23
+ <div class="flex-between mb-1">
24
+ <span class="badge badge-primary">{{ app.source_type }}</span>
25
+ {% if app.app_type == 'extension' %}
26
+ <span class="badge badge-accent">Extension</span>
27
+ {% endif %}
28
+ </div>
29
+ <h2 class="app-title">{{ app.name }}</h2>
30
+ <p class="app-desc">{{ app.description or "Flask application hosted under AppManager wrapper." }}</p>
31
+ </div>
32
+ <div class="app-footer">
33
+ <span class="app-meta">
34
+ {% if app.app_type == 'extension' %}Extension App{% else %}/apps/{{ app.slug }}{% endif %}
35
+ </span>
36
+ {% if app.has_web_ui %}
37
+ <a href="/apps/{{ app.slug }}/" class="btn btn-primary btn-sm">Launch &rarr;</a>
38
+ {% else %}
39
+ <span class="btn btn-secondary btn-sm disabled">No UI</span>
40
+ {% endif %}
41
+ </div>
42
+ </div>
43
+ {% endfor %}
44
+ </div>
45
+ {% else %}
46
+ <div class="empty-state">
47
+ <h2>No Applications Available</h2>
48
+ <p>There are currently no sub-apps installed or you do not have permission to access any installed applications.</p>
49
+ {% if user and user.is_admin() %}
50
+ <a href="/admin" class="btn btn-primary">Go to Admin Dashboard to Install Apps</a>
51
+ {% endif %}
52
+ </div>
53
+ {% endif %}
54
+ {% endblock %}
@@ -0,0 +1,320 @@
1
+ Metadata-Version: 2.4
2
+ Name: appmanager-server
3
+ Version: 0.2.0
4
+ Summary: Multi-tenant Flask sub-application portal & dynamic WSGI dispatcher with in-process isolation, telemetry, and automated health checks.
5
+ Author: AppManager Contributors
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/r3sbarra/appmanager-server
8
+ Project-URL: Documentation, https://github.com/r3sbarra/appmanager-server#readme
9
+ Project-URL: Repository, https://github.com/r3sbarra/appmanager-server.git
10
+ Project-URL: Issues, https://github.com/r3sbarra/appmanager-server/issues
11
+ Project-URL: Changelog, https://github.com/r3sbarra/appmanager-server/blob/master/CHANGELOG.md
12
+ Keywords: flask,wsgi,multi-tenant,sub-apps,pythonanywhere,dispatcher,microfrontends,portal,appmanager
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Web Environment
15
+ Classifier: Framework :: Flask
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
25
+ Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
26
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
27
+ Requires-Python: >=3.10
28
+ Description-Content-Type: text/markdown
29
+ License-File: LICENSE
30
+ Requires-Dist: Flask>=3.0.0
31
+ Requires-Dist: Flask-SQLAlchemy>=3.1.1
32
+ Requires-Dist: blinker>=1.6.0
33
+ Requires-Dist: PyJWT>=2.8.0
34
+ Requires-Dist: requests>=2.31.0
35
+ Requires-Dist: Authlib>=1.3.0
36
+ Requires-Dist: GitPython>=3.1.41
37
+ Requires-Dist: Werkzeug>=3.0.1
38
+ Requires-Dist: python-dotenv>=1.0.0
39
+ Requires-Dist: appmanager-sdk>=0.1.0
40
+ Provides-Extra: mysql
41
+ Requires-Dist: PyMySQL>=1.1.0; extra == "mysql"
42
+ Requires-Dist: cryptography>=41.0.0; extra == "mysql"
43
+ Provides-Extra: postgres
44
+ Requires-Dist: psycopg2-binary>=2.9.0; extra == "postgres"
45
+ Provides-Extra: all
46
+ Requires-Dist: PyMySQL>=1.1.0; extra == "all"
47
+ Requires-Dist: cryptography>=41.0.0; extra == "all"
48
+ Requires-Dist: psycopg2-binary>=2.9.0; extra == "all"
49
+ Provides-Extra: dev
50
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
51
+ Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
52
+ Requires-Dist: ruff>=0.5.0; extra == "dev"
53
+ Requires-Dist: build>=1.1.0; extra == "dev"
54
+ Requires-Dist: twine>=5.0.0; extra == "dev"
55
+ Requires-Dist: PyMySQL>=1.1.0; extra == "dev"
56
+ Provides-Extra: docs
57
+ Requires-Dist: mkdocs-material>=9.5.0; extra == "docs"
58
+ Requires-Dist: mkdocstrings[python]>=0.25.0; extra == "docs"
59
+ Requires-Dist: pymdown-extensions>=10.7; extra == "docs"
60
+ Dynamic: license-file
61
+
62
+ <p align="center">
63
+ <img src="docs/assets/banner.svg" alt="AppManager Banner" width="100%">
64
+ </p>
65
+
66
+ <p align="center">
67
+ <a href="https://github.com/r3sbarra/appmanager-server/actions"><img src="https://github.com/r3sbarra/appmanager-server/actions/workflows/ci.yml/badge.svg" alt="CI Status"></a>
68
+ <a href="https://pypi.org/project/appmanager-server/"><img src="https://img.shields.io/pypi/v/appmanager-server.svg" alt="PyPI Version"></a>
69
+ <a href="https://www.python.org/downloads/"><img src="https://img.shields.io/pypi/pyversions/appmanager-server.svg" alt="Python Versions"></a>
70
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green?style=flat-square" alt="License: MIT"></a>
71
+ </p>
72
+
73
+ # AppManager Server (`appmanager-server`)
74
+
75
+ **AppManager Server** is a high-performance Python/Flask application portal and extension framework designed to dynamically host, dispatch, and manage standalone WSGI sub-applications and modular extensions under a unified host server.
76
+
77
+ Optimized specifically for **PythonAnywhere**, cloud VMs, and multi-tenant hosting environments, AppManager provides scoped in-process module isolation, a pluggable UI slot & lifecycle hook system, automated health monitoring, telemetry reporting, live developer tooling, a headless REST API, and per-app settings configuration.
78
+
79
+ ---
80
+
81
+ ## 🏛️ System Architecture
82
+
83
+ <p align="center">
84
+ <img src="docs/assets/appmanager-architecture.svg" alt="AppManager Architecture" width="100%">
85
+ </p>
86
+
87
+ ```mermaid
88
+ graph LR
89
+ Client([🌐 Client / Browser]) --> Dispatcher[⚡ DynamicAppDispatcherMiddleware]
90
+
91
+ subgraph Host [AppManager Host Portal]
92
+ Dispatcher --> Auth[🔒 Authentication & Context Forwarder]
93
+ Auth --> DB[(🗄️ SQLite / MySQL Database)]
94
+ Auth --> Hooks[🧩 Hook & Slot Registry]
95
+ Auth --> Signals[🔔 Signal Hub & Telemetry]
96
+ end
97
+
98
+ subgraph SubApps [Installed Sub-Apps & Extensions /apps/*]
99
+ Auth --> FlaskApp[🚀 Standalone Flask Sub-App]
100
+ Auth --> APIApp[🌐 RESTful API Sub-App]
101
+ Auth --> ExtPlugin[🧩 UI Slot Extension Plugin]
102
+ Auth --> HTMXApp[⚡ HTMX Reactive Dashboard]
103
+ end
104
+
105
+ FlaskApp -. SDK Telemetry & Settings .-> Host
106
+ ExtPlugin -. Injects Badges & Widgets .-> Hooks
107
+ ```
108
+
109
+ ---
110
+
111
+ ## 🚀 Key Features
112
+
113
+ - 🔌 **Standard Flask Extension (`AppManager`)**: Seamlessly embeds into existing Flask applications (`AppManager(app)`) or runs as a standalone turnkey portal.
114
+ - 🔀 **Dynamic WSGI Sub-App Dispatcher**: Dynamically intercepts `/apps/<slug>/*` requests via `DynamicAppDispatcherMiddleware` and dispatches requests to installed sub-apps on-the-fly.
115
+ - 🧩 **Pluggable Hook & UI Slot System (`appmanager.hooks`)**: Mount custom HTML badges (`user_badge`), interactive cards (`dashboard_widget`), top navigation links (`nav_item`), and assets (`head_assets`) without altering host code.
116
+ - 🧰 **Developer SDK (`appmanager.sdk` / `appmanager-sdk`)**: Fluent `AppManagerClient` offering `@require_auth(role=...)`, typed user identity header parsing, telemetry metrics, and extension key-value data storage.
117
+ - ⚙️ **Per-App Settings Configuration**: Define customizable settings schemas in `manifest.json` with live configuration from the Admin Dashboard.
118
+ - 👤 **Verified Context & Header Forwarding**: Injects verified non-spoofed user identity (`X-AppManager-User-Id`, `X-AppManager-User-Email`, `X-AppManager-User-Role`, `X-Forwarded-Prefix`) directly into sub-app request headers.
119
+ - 🛠️ **Multi-Template Scaffolding & Dev Server**: Rapidly scaffold sub-apps (`appmanager new-subapp --template [basic|api|extension|htmx|full]`) and test locally in isolation with mock auth (`appmanager dev <slug>`).
120
+ - 🩺 **Automated Health Monitoring**: Sub-app health evaluation contract (`/health` endpoint or `get_health()` callable) tracked in `AppHealthLog` with one-click admin execution.
121
+ - 📊 **In-Process Telemetry Bridge**: High-performance telemetry reporting allowing sub-apps to record events and metrics directly to the host database with zero network overhead.
122
+ - 🔒 **Granular Role & Permissions Matrix**: Full RBAC role management and per-user permission matrix controlling access to every installed sub-app.
123
+
124
+ ---
125
+
126
+ ## 📦 Quickstart & Installation
127
+
128
+ ### 1. Install via pip
129
+
130
+ ```bash
131
+ pip install appmanager-server
132
+ ```
133
+
134
+ CLI commands available: `appmanager-server`, `appmgr-server`, `appmanager`, or `appmgr`.
135
+
136
+ ### 2. Initialize and Seed
137
+
138
+ ```bash
139
+ appmanager-server init
140
+ # or: appmgr-server init / appmanager init
141
+ appmanager seed
142
+ ```
143
+
144
+ ### 3. Run Development Server
145
+
146
+ ```bash
147
+ appmanager run
148
+ ```
149
+
150
+ Navigate to `http://localhost:5000` to access the AppManager portal.
151
+
152
+ ---
153
+
154
+ ## 💻 Developer SDK & Sub-App Creation
155
+
156
+ ### Scaffolding a New Sub-App or Extension
157
+
158
+ ```bash
159
+ # Standalone Flask sub-app
160
+ appmanager new-subapp "Analytics Dashboard" --slug analytics --template basic
161
+
162
+ # RESTful JSON API sub-app
163
+ appmanager new-subapp "Payment Gateway" --slug payments --template api
164
+
165
+ # UI Slot Extension Plugin
166
+ appmanager new-subapp "User Badges" --slug user-badges --template extension
167
+
168
+ # Interactive HTMX sub-app
169
+ appmanager new-subapp "Live Monitor" --slug live-monitor --template htmx
170
+ ```
171
+
172
+ ### Local Sub-App Development Runner
173
+
174
+ Debug and test your sub-app locally with mock user authentication headers:
175
+
176
+ ```bash
177
+ appmanager dev analytics --port 5001 --email dev@example.com --role admin
178
+ ```
179
+
180
+ ---
181
+
182
+ ## 🛠️ Sub-App Specification (`manifest.json`)
183
+
184
+ To make an application deployable on AppManager, provide a `manifest.json` in the root:
185
+
186
+ ```json
187
+ {
188
+ "name": "Analytics Dashboard",
189
+ "slug": "analytics",
190
+ "version": "1.0.0",
191
+ "description": "Standardized analytics sub-application.",
192
+ "entry_point": "app:app",
193
+ "health_check_path": "/health",
194
+ "app_type": "standalone",
195
+ "has_web_ui": true,
196
+ "settings": {
197
+ "api_key": {
198
+ "type": "string",
199
+ "default": "demo-key-12345",
200
+ "description": "API key for external data ingestion."
201
+ },
202
+ "refresh_interval_sec": {
203
+ "type": "number",
204
+ "default": 60,
205
+ "description": "Dashboard polling interval in seconds."
206
+ }
207
+ },
208
+ "scheduled_tasks": [
209
+ {
210
+ "name": "daily_aggregation",
211
+ "entry_point": "tasks:run_aggregation",
212
+ "frequency": "daily"
213
+ }
214
+ ]
215
+ }
216
+ ```
217
+
218
+ ### Sub-App Implementation with `appmanager.sdk`
219
+
220
+ ```python
221
+ from flask import Flask, jsonify, request
222
+ from appmanager.sdk import AppManagerClient
223
+
224
+ app = Flask(__name__)
225
+ client = AppManagerClient("analytics")
226
+
227
+
228
+ @app.route("/")
229
+ @client.require_auth(role="user")
230
+ def index():
231
+ user = client.get_current_user(request.headers)
232
+ api_key = client.get_setting("api_key", default="demo-key")
233
+ client.report_event("dashboard_view", {"user_id": user["id"]})
234
+
235
+ return f"<h1>Welcome, {user['email']}</h1><p>Active API Key: {api_key}</p>"
236
+
237
+
238
+ @app.route("/health")
239
+ def health():
240
+ return jsonify({"status": "healthy", "app": "analytics"})
241
+
242
+
243
+ if __name__ == "__main__":
244
+ app.run(port=5001, debug=True)
245
+ ```
246
+
247
+ ---
248
+
249
+ ## 🧩 Pluggable UI Slots & Extension Hooks
250
+
251
+ Extensions can inject components into host slots:
252
+
253
+ ```python
254
+ from markupsafe import Markup
255
+ from appmanager.sdk import AppManagerClient
256
+
257
+ client = AppManagerClient("banner-extension")
258
+
259
+
260
+ def render_dashboard_widget(user=None):
261
+ return Markup('<div class="card">✨ Custom analytics summary card</div>')
262
+
263
+
264
+ # Register to the host dashboard
265
+ client.register_slot("dashboard_widget", render_dashboard_widget, priority=5)
266
+ ```
267
+
268
+ Available UI slots:
269
+ - `user_badge`: Injected next to user names across profiles, tables, and headers.
270
+ - `dashboard_widget`: Mounted on the main `/dashboard` landing view.
271
+ - `nav_item`: Injected into the top navigation header bar.
272
+ - `head_assets`: Injected into the HTML `<head>` tag.
273
+
274
+ ---
275
+
276
+ ## 🖥️ CLI Commands
277
+
278
+ | Command | Description |
279
+ | :--- | :--- |
280
+ | `appmanager init` | Bootstrap local directory with `installed_apps/` and `.env` template |
281
+ | `appmanager run` | Start the WSGI dynamic dispatcher host server |
282
+ | `appmanager dev <slug>` | Run standalone local test server with mock authentication |
283
+ | `appmanager seed` | Seed database with default starter apps, roles, and flairs |
284
+ | `appmanager new-subapp <name>` | Scaffold a sub-app with templates (`basic`, `api`, `extension`, `htmx`, `full`) |
285
+ | `appmanager validate-subapp <path>` | Validate a sub-app folder or ZIP package against manifest rules |
286
+ | `appmanager export-app <slug>` | Package an installed sub-app into a deployable ZIP archive |
287
+ | `appmanager reload-app <slug>` | Invalidate in-memory WSGI cache for zero-downtime updates |
288
+ | `appmanager hooks` | Inspect all registered UI slots, lifecycle hooks, and listeners |
289
+ | `appmanager check-health` | Run health evaluation checks across all active sub-apps |
290
+ | `appmanager run-scheduled-tasks` | Run background scheduled cron jobs and maintenance |
291
+ | `appmanager list-apps` | List all registered applications and their operational status |
292
+ | `appmanager list-users` | List all registered users and their assigned roles |
293
+ | `appmanager set-role <email>` | Elevate or update user role (`admin` or `user`) |
294
+ | `appmanager list-roles` | List all system and custom RBAC roles |
295
+ | `appmanager create-role <name>` | Create a new custom RBAC role |
296
+
297
+ ---
298
+
299
+ ## 📖 Documentation
300
+
301
+ Full documentation is available at [https://appmanager.github.io/appmanager](https://appmanager.github.io/appmanager):
302
+ - [Getting Started](https://appmanager.github.io/appmanager/getting-started/)
303
+ - [Sub-App Development Guide](https://appmanager.github.io/appmanager/sub-apps/)
304
+ - [Hook & Slot Extension Guide](https://appmanager.github.io/appmanager/sub-apps/#ui-slots--extension-hooks)
305
+ - [Configuration & Settings Reference](https://appmanager.github.io/appmanager/configuration/)
306
+ - [PythonAnywhere & WSGI Deployment](https://appmanager.github.io/appmanager/deployment/)
307
+ - [CLI Reference](https://appmanager.github.io/appmanager/cli/)
308
+ - [Python API Reference](https://appmanager.github.io/appmanager/api/)
309
+
310
+ ---
311
+
312
+ ## 🤝 Contributing
313
+
314
+ Contributions are welcome! Please check out [CONTRIBUTING.md](CONTRIBUTING.md) and our [Code of Conduct](CODE_OF_CONDUCT.md).
315
+
316
+ ---
317
+
318
+ ## 📄 License
319
+
320
+ This project is licensed under the terms of the [MIT License](LICENSE).
@@ -0,0 +1,89 @@
1
+ appmanager/__init__.py,sha256=fB9XjEp2UKAGh_0_ys9DBaT3EgtiJweSSZV3r1S_Pb4,1429
2
+ appmanager/app_config.py,sha256=28PsFjviC0E-CXgeqf1SGLnYLfolUodRB5RN6VPt3es,3291
3
+ appmanager/bridge.py,sha256=5hpvoL9W3ZUusy9gUWac-1Hk2sRRZdKOJmv0ZTjTpR0,2871
4
+ appmanager/cli.py,sha256=Y-JedYy2b-7J8WIiFW-uLHhvvjSlh8xwKLl03EH2rv4,43368
5
+ appmanager/config.py,sha256=7eP9KytNFbq41lvEhXzsF_YIeqWY1n2-bwAqe8W14wU,2251
6
+ appmanager/database.py,sha256=HEmDzoo6Rd3-c_4PKY79peQ6EViiEGKLuYvejj0lyyI,5151
7
+ appmanager/extension.py,sha256=hoftCeXZTrt1CxLGLVm4ONuBpA65QBacuW_2Qz3Pf6E,7329
8
+ appmanager/extensions.py,sha256=85eaTx0OE0RTcc2WetQWbFu7BbTLNUV5GH_fofy5n1c,7889
9
+ appmanager/health.py,sha256=-JbtlHJov3LdSR_NHnNIUE6fkSZbMziu-gz7638tdDU,4796
10
+ appmanager/hooks.py,sha256=Ad46orPZ-CS2BEBoRE6rUSP1kmgVulFZI2XYFkM9SSk,6285
11
+ appmanager/middleware.py,sha256=iXwziZ8VzDygTo7WE7bM9e5UZGrV4XKXE4YBxO6cubQ,11052
12
+ appmanager/models.py,sha256=t3Dx3uUlqD38SaFC9Cif7aNVeboRKIkfVwcR9hld4X0,11770
13
+ appmanager/py.typed,sha256=bWew9mHgMy8LqMu7RuqQXFXLBxh2CRx0dUbSx-3wE48,27
14
+ appmanager/sdk.py,sha256=BwMOwlyGf6u7ItBLRNPVM6aRhVvkjjeym7gZ7zuIQZw,577
15
+ appmanager/security.py,sha256=8a2fPY6sTULs37sT2AWz-jrEppUMQ07YXWYwMKfH1pw,8281
16
+ appmanager/security_scanner.py,sha256=w-q1BdeEz99kItyUCUZqkJX0GkuvUU7Ltk8kbKi8aB8,22012
17
+ appmanager/signals.py,sha256=dAHOhS0MNTzeVYuu7n0RtjKC7W01Rhp_xMC_UBgpwwQ,640
18
+ appmanager/admin/__init__.py,sha256=ft9m2Y85sGfmcShQm4O_8Do_UOqWExu3FTA73B3RR48,69
19
+ appmanager/admin/app_installer.py,sha256=Py4Yv2MhIhILS9DK-iPYmbEAf_1126Zh7aiXHuVTIs8,27392
20
+ appmanager/admin/members.py,sha256=R5JSH1ppkBBVTO-pYTxjXGZ5hariGU0tFbUH7njbHRs,2914
21
+ appmanager/admin/registry.py,sha256=iP8r9AaKJpafoJThIP06FYPJ6fdtEi_BpAewUCzSSBw,13911
22
+ appmanager/admin/routes.py,sha256=jfjW1Q-0-VJzOwOp620B6rJyI8YQkKrlwHudbPfOQ50,30567
23
+ appmanager/api/__init__.py,sha256=h7wQ0h6yT4M8oEaIR6yTlkp9l9QBn550iKah4iQ2NY0,142
24
+ appmanager/api/routes.py,sha256=a_SBI5LwM7d_sJB8tJCG1NFMe8sHi2N8N43JdPG5hdo,12574
25
+ appmanager/auth/__init__.py,sha256=TJE7qpIGNZzivoYzP-MxPPxBO92VvVTkouNvmdRxDOs,66
26
+ appmanager/auth/oauth.py,sha256=_6z4toSNMY3VOHBUXtQJ2dQqZ4Wy5d0aOdUAhHMG5AU,2375
27
+ appmanager/auth/routes.py,sha256=tJQWptA2FzqgZ9mIxvzS9qzkZClts2ihZx6S217Goxk,7438
28
+ appmanager/auth/utils.py,sha256=0r7eYfznnsdSyO-CTdX1PgFrAXiVjB135cP_C-XoUnw,6272
29
+ appmanager/static/css/app.css,sha256=SHfVXfuMaBBZ6oll3aOVGkNVkPtydT3kDQjESpT_6N0,30234
30
+ appmanager/static/vendor/fonts/fonts.css,sha256=yVDimL64odFqGXwctH7FRz96Y9vMKsHSqUe93VFRv08,11451
31
+ appmanager/static/vendor/fonts/jetbrainsmono-0.woff2,sha256=YiE76KeLQvHinRRS2R4viz50VXKp3ZjTlB45-gCzfXY,1640
32
+ appmanager/static/vendor/fonts/jetbrainsmono-1.woff2,sha256=4Xz9FfuWkJ1kCVAV-VggcGOgwHGR2jUS331WCnga698,8872
33
+ appmanager/static/vendor/fonts/jetbrainsmono-10.woff2,sha256=21_024PlgEJigOkzeljcV9OoN4ShsDrYCRRlFZREHVI,11624
34
+ appmanager/static/vendor/fonts/jetbrainsmono-11.woff2,sha256=g8AF1J2KalBHTHOlo2rARoB26cSinae9sUmV2AVgpb4,31432
35
+ appmanager/static/vendor/fonts/jetbrainsmono-12.woff2,sha256=YiE76KeLQvHinRRS2R4viz50VXKp3ZjTlB45-gCzfXY,1640
36
+ appmanager/static/vendor/fonts/jetbrainsmono-13.woff2,sha256=4Xz9FfuWkJ1kCVAV-VggcGOgwHGR2jUS331WCnga698,8872
37
+ appmanager/static/vendor/fonts/jetbrainsmono-14.woff2,sha256=ClV3IbH4s20_P4REJomnHKSnRDAKvLRqGVP1G_xmO2Y,6836
38
+ appmanager/static/vendor/fonts/jetbrainsmono-15.woff2,sha256=yJucwLxiYr1PjYSUtpYWAfOu-oKdCMLjY19NUB06R8I,5888
39
+ appmanager/static/vendor/fonts/jetbrainsmono-16.woff2,sha256=21_024PlgEJigOkzeljcV9OoN4ShsDrYCRRlFZREHVI,11624
40
+ appmanager/static/vendor/fonts/jetbrainsmono-17.woff2,sha256=g8AF1J2KalBHTHOlo2rARoB26cSinae9sUmV2AVgpb4,31432
41
+ appmanager/static/vendor/fonts/jetbrainsmono-18.woff2,sha256=YiE76KeLQvHinRRS2R4viz50VXKp3ZjTlB45-gCzfXY,1640
42
+ appmanager/static/vendor/fonts/jetbrainsmono-19.woff2,sha256=4Xz9FfuWkJ1kCVAV-VggcGOgwHGR2jUS331WCnga698,8872
43
+ appmanager/static/vendor/fonts/jetbrainsmono-2.woff2,sha256=ClV3IbH4s20_P4REJomnHKSnRDAKvLRqGVP1G_xmO2Y,6836
44
+ appmanager/static/vendor/fonts/jetbrainsmono-20.woff2,sha256=ClV3IbH4s20_P4REJomnHKSnRDAKvLRqGVP1G_xmO2Y,6836
45
+ appmanager/static/vendor/fonts/jetbrainsmono-21.woff2,sha256=yJucwLxiYr1PjYSUtpYWAfOu-oKdCMLjY19NUB06R8I,5888
46
+ appmanager/static/vendor/fonts/jetbrainsmono-22.woff2,sha256=21_024PlgEJigOkzeljcV9OoN4ShsDrYCRRlFZREHVI,11624
47
+ appmanager/static/vendor/fonts/jetbrainsmono-23.woff2,sha256=g8AF1J2KalBHTHOlo2rARoB26cSinae9sUmV2AVgpb4,31432
48
+ appmanager/static/vendor/fonts/jetbrainsmono-3.woff2,sha256=yJucwLxiYr1PjYSUtpYWAfOu-oKdCMLjY19NUB06R8I,5888
49
+ appmanager/static/vendor/fonts/jetbrainsmono-4.woff2,sha256=21_024PlgEJigOkzeljcV9OoN4ShsDrYCRRlFZREHVI,11624
50
+ appmanager/static/vendor/fonts/jetbrainsmono-5.woff2,sha256=g8AF1J2KalBHTHOlo2rARoB26cSinae9sUmV2AVgpb4,31432
51
+ appmanager/static/vendor/fonts/jetbrainsmono-6.woff2,sha256=YiE76KeLQvHinRRS2R4viz50VXKp3ZjTlB45-gCzfXY,1640
52
+ appmanager/static/vendor/fonts/jetbrainsmono-7.woff2,sha256=4Xz9FfuWkJ1kCVAV-VggcGOgwHGR2jUS331WCnga698,8872
53
+ appmanager/static/vendor/fonts/jetbrainsmono-8.woff2,sha256=ClV3IbH4s20_P4REJomnHKSnRDAKvLRqGVP1G_xmO2Y,6836
54
+ appmanager/static/vendor/fonts/jetbrainsmono-9.woff2,sha256=yJucwLxiYr1PjYSUtpYWAfOu-oKdCMLjY19NUB06R8I,5888
55
+ appmanager/static/vendor/fonts/spacegrotesk-0.woff2,sha256=iJXj1JglEov6UjjkyW6bQy16Pa2-XBfGCBJnOYMF3HE,6712
56
+ appmanager/static/vendor/fonts/spacegrotesk-1.woff2,sha256=lS3dtF0vlvccvzt_UQskN5r8PInqAvz4nTd7RdYsAWY,18940
57
+ appmanager/static/vendor/fonts/spacegrotesk-10.woff2,sha256=lS3dtF0vlvccvzt_UQskN5r8PInqAvz4nTd7RdYsAWY,18940
58
+ appmanager/static/vendor/fonts/spacegrotesk-11.woff2,sha256=BkCJBHb8EZirTeVx-2WN5EPE2FtmRm7AlTSoc3qxzp0,22288
59
+ appmanager/static/vendor/fonts/spacegrotesk-2.woff2,sha256=BkCJBHb8EZirTeVx-2WN5EPE2FtmRm7AlTSoc3qxzp0,22288
60
+ appmanager/static/vendor/fonts/spacegrotesk-3.woff2,sha256=iJXj1JglEov6UjjkyW6bQy16Pa2-XBfGCBJnOYMF3HE,6712
61
+ appmanager/static/vendor/fonts/spacegrotesk-4.woff2,sha256=lS3dtF0vlvccvzt_UQskN5r8PInqAvz4nTd7RdYsAWY,18940
62
+ appmanager/static/vendor/fonts/spacegrotesk-5.woff2,sha256=BkCJBHb8EZirTeVx-2WN5EPE2FtmRm7AlTSoc3qxzp0,22288
63
+ appmanager/static/vendor/fonts/spacegrotesk-6.woff2,sha256=iJXj1JglEov6UjjkyW6bQy16Pa2-XBfGCBJnOYMF3HE,6712
64
+ appmanager/static/vendor/fonts/spacegrotesk-7.woff2,sha256=lS3dtF0vlvccvzt_UQskN5r8PInqAvz4nTd7RdYsAWY,18940
65
+ appmanager/static/vendor/fonts/spacegrotesk-8.woff2,sha256=BkCJBHb8EZirTeVx-2WN5EPE2FtmRm7AlTSoc3qxzp0,22288
66
+ appmanager/static/vendor/fonts/spacegrotesk-9.woff2,sha256=iJXj1JglEov6UjjkyW6bQy16Pa2-XBfGCBJnOYMF3HE,6712
67
+ appmanager/static/vendor/htmx/htmx.min.js,sha256=RJMXreeIHpSVENthSZHhlcOgmcTHkcJNrOxV-fSipFI,48101
68
+ appmanager/templates/base.html,sha256=mlkcVJkgy9p-YRwt1SapCX-KWu9Jcr4x6tW3vGzMGGc,3202
69
+ appmanager/templates/index.html,sha256=EQtid6aCXY4lMNsmLAgeVjdyV9c3UzyaSF0C8pAh9cs,2278
70
+ appmanager/templates/admin/_member_drawer.html,sha256=6PLC4jVOpcsxUjb0jkl1wkd8eMqt-IY13m3ygAcf_ms,5345
71
+ appmanager/templates/admin/_members_table.html,sha256=7Ts-I058NVmOPWmymRA3etBoWppJGzypYnATXSKb1VA,5616
72
+ appmanager/templates/admin/app_detail.html,sha256=ZHVY7lXQmgmyUiH4tjnkorj-N7Eh4jxQhgbSRpV-JEk,7006
73
+ appmanager/templates/admin/dashboard.html,sha256=sc8BTn-zUareZ4Wh2AtLIqyzdsjvoshBspHoavxbr4A,48303
74
+ appmanager/templates/admin/flairs.html,sha256=Zg-MK_8OuAcdWJ5bEBBjmg7w-0MnkDEbJ-Hi1OCqVow,5409
75
+ appmanager/templates/admin/permissions.html,sha256=gvjd24uaRH6WR0CiG3F_PyTjAhMBTnRwoqm3OoIAq8w,3746
76
+ appmanager/templates/admin/roles.html,sha256=wFq-1YXljZd5tUX6sgjV50x2DWROif7-0Lg_XRBFUZc,6204
77
+ appmanager/templates/admin/users.html,sha256=JyqcRUp4k6Bk4skP5NOr7mCiFwBXRM6X8C6jbSPfJDY,11454
78
+ appmanager/templates/auth/login.html,sha256=t4ouI_VgUypxtLZyRE2KbAccSHJFrtYWgDv_hFA4qoI,3500
79
+ appmanager/templates/auth/profile.html,sha256=HWXuSnxkojVbnxZ4zqt1GdRd8qauT38N-BCpkyvZe-s,1962
80
+ appmanager/templates/errors/401.html,sha256=MRBkiPWki4EzUS3kPSCQSEbJgWIvM9_CO74Pn9pl39U,1004
81
+ appmanager/templates/errors/403.html,sha256=-os4ImOyx9m0xzrfcUYetvWsUHg-q5khEVUitPvq2ME,1028
82
+ appmanager/templates/errors/404.html,sha256=VPFAXI2Q-oCI4VpmLzV45tyR9FqTgqBkb5_9e4Tjhos,938
83
+ appmanager/templates/errors/500.html,sha256=1pbGuQSF3iDWRnHIAS228H_SsA5SM-YJcFsgMJN1CN4,1088
84
+ appmanager_server-0.2.0.dist-info/licenses/LICENSE,sha256=qY1OzUfb40jXi0TraUNB5gVksYQUMRPNXTJyyLd0EMo,1080
85
+ appmanager_server-0.2.0.dist-info/METADATA,sha256=fMbjTZP0xvC6ythjqdrOPz3WFyb3hB-msAuym6Yeux8,12940
86
+ appmanager_server-0.2.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
87
+ appmanager_server-0.2.0.dist-info/entry_points.txt,sha256=QVR-FLkvt0DlE2QdhMyKrwnXAcD1N3d1N_YmBPWptPQ,156
88
+ appmanager_server-0.2.0.dist-info/top_level.txt,sha256=KJpiBWALar-zQTpA9mX9cNbdRcN4NQ67m3gkcnrKkD0,11
89
+ appmanager_server-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,5 @@
1
+ [console_scripts]
2
+ appmanager = appmanager.cli:main
3
+ appmanager-server = appmanager.cli:main
4
+ appmgr = appmanager.cli:main
5
+ appmgr-server = appmanager.cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 AppManager Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ appmanager