vibetuner 2.7.0__py3-none-any.whl → 2.18.1__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.
Potentially problematic release.
This version of vibetuner might be problematic. Click here for more details.
- vibetuner/cli/__init__.py +13 -2
- vibetuner/cli/run.py +0 -1
- vibetuner/cli/scaffold.py +187 -0
- vibetuner/config.py +27 -11
- vibetuner/context.py +3 -0
- vibetuner/frontend/__init__.py +7 -2
- vibetuner/frontend/lifespan.py +12 -7
- vibetuner/frontend/middleware.py +3 -3
- vibetuner/frontend/routes/auth.py +19 -13
- vibetuner/frontend/routes/debug.py +1 -1
- vibetuner/frontend/routes/health.py +4 -0
- vibetuner/frontend/routes/user.py +1 -1
- vibetuner/mongo.py +1 -1
- vibetuner/paths.py +197 -80
- vibetuner/tasks/worker.py +1 -1
- vibetuner/templates/email/{default/magic_link.html.jinja → magic_link.html.jinja} +2 -1
- vibetuner/templates/frontend/base/favicons.html.jinja +1 -1
- vibetuner/templates/frontend/base/skeleton.html.jinja +5 -2
- vibetuner/templates/frontend/debug/collections.html.jinja +2 -0
- vibetuner/templates/frontend/debug/components/debug_nav.html.jinja +6 -6
- vibetuner/templates/frontend/debug/index.html.jinja +6 -4
- vibetuner/templates/frontend/debug/info.html.jinja +2 -0
- vibetuner/templates/frontend/debug/users.html.jinja +4 -2
- vibetuner/templates/frontend/debug/version.html.jinja +2 -0
- vibetuner/templates/frontend/email_sent.html.jinja +2 -1
- vibetuner/templates/frontend/index.html.jinja +1 -0
- vibetuner/templates/frontend/login.html.jinja +8 -3
- vibetuner/templates/frontend/user/edit.html.jinja +3 -2
- vibetuner/templates/frontend/user/profile.html.jinja +2 -1
- vibetuner/templates.py +9 -15
- vibetuner/versioning.py +1 -1
- vibetuner-2.18.1.dist-info/METADATA +241 -0
- vibetuner-2.18.1.dist-info/RECORD +72 -0
- {vibetuner-2.7.0.dist-info → vibetuner-2.18.1.dist-info}/WHEEL +1 -1
- vibetuner-2.18.1.dist-info/entry_points.txt +3 -0
- vibetuner/frontend/AGENTS.md +0 -113
- vibetuner/frontend/CLAUDE.md +0 -113
- vibetuner/models/AGENTS.md +0 -165
- vibetuner/models/CLAUDE.md +0 -165
- vibetuner/services/AGENTS.md +0 -104
- vibetuner/services/CLAUDE.md +0 -104
- vibetuner/tasks/AGENTS.md +0 -98
- vibetuner/tasks/CLAUDE.md +0 -98
- vibetuner/templates/email/AGENTS.md +0 -48
- vibetuner/templates/email/CLAUDE.md +0 -48
- vibetuner/templates/frontend/AGENTS.md +0 -74
- vibetuner/templates/frontend/CLAUDE.md +0 -74
- vibetuner/templates/markdown/AGENTS.md +0 -29
- vibetuner/templates/markdown/CLAUDE.md +0 -29
- vibetuner-2.7.0.dist-info/METADATA +0 -48
- vibetuner-2.7.0.dist-info/RECORD +0 -84
- /vibetuner/templates/email/{default/magic_link.txt.jinja → magic_link.txt.jinja} +0 -0
vibetuner/paths.py
CHANGED
|
@@ -1,112 +1,229 @@
|
|
|
1
1
|
from importlib.resources import files
|
|
2
2
|
from pathlib import Path
|
|
3
|
+
from typing import Self
|
|
4
|
+
|
|
5
|
+
from pydantic import computed_field, model_validator
|
|
6
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
7
|
+
|
|
3
8
|
|
|
4
9
|
# Package-relative paths (for bundled templates in the vibetuner package)
|
|
5
10
|
_package_files = files("vibetuner")
|
|
6
11
|
_package_templates_traversable = _package_files / "templates"
|
|
7
12
|
|
|
8
|
-
|
|
13
|
+
|
|
9
14
|
def _get_package_templates_path() -> Path:
|
|
10
15
|
"""Get package templates path, works for both installed and editable installs."""
|
|
11
|
-
# For most cases, we can convert directly to Path
|
|
12
|
-
# For zip files, importlib.resources handles extraction automatically
|
|
13
16
|
try:
|
|
14
17
|
return Path(str(_package_templates_traversable))
|
|
15
18
|
except (TypeError, ValueError):
|
|
16
|
-
# If we can't convert to Path, we're in a zip or similar
|
|
17
|
-
# In this case, we'll need to use as_file() context manager when accessing
|
|
18
|
-
# For now, raise an error - we can enhance this later if needed
|
|
19
19
|
raise RuntimeError(
|
|
20
20
|
"Package templates are in a non-filesystem location. "
|
|
21
21
|
"This is not yet supported."
|
|
22
|
-
)
|
|
22
|
+
) from None
|
|
23
23
|
|
|
24
24
|
|
|
25
|
+
# Package templates always available
|
|
25
26
|
package_templates = _get_package_templates_path()
|
|
27
|
+
core_templates = package_templates # Alias for backwards compatibility
|
|
26
28
|
|
|
27
|
-
# Project root (set at runtime by the application using vibetuner)
|
|
28
|
-
# When None, only package templates are available
|
|
29
|
-
root: Path | None = None
|
|
30
|
-
fallback_path = "defaults"
|
|
31
29
|
|
|
30
|
+
class PathSettings(BaseSettings):
|
|
31
|
+
"""Path settings with lazy auto-detection of project root."""
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
model_config = SettingsConfigDict(
|
|
34
|
+
case_sensitive=False,
|
|
35
|
+
extra="ignore",
|
|
36
|
+
validate_default=True,
|
|
37
|
+
)
|
|
35
38
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
root: Path | None = None
|
|
40
|
+
fallback_path: str = "defaults"
|
|
41
|
+
|
|
42
|
+
@model_validator(mode="after")
|
|
43
|
+
def detect_project_root(self) -> Self:
|
|
44
|
+
"""Auto-detect project root if not explicitly set."""
|
|
45
|
+
if self.root is None:
|
|
46
|
+
detected = self._find_project_root()
|
|
47
|
+
if detected is not None:
|
|
48
|
+
self.root = detected
|
|
49
|
+
return self
|
|
50
|
+
|
|
51
|
+
@staticmethod
|
|
52
|
+
def _find_project_root() -> Path | None:
|
|
53
|
+
"""Find project root by searching for marker files."""
|
|
54
|
+
markers = [".copier-answers.yml", "pyproject.toml", ".git"]
|
|
55
|
+
current = Path.cwd()
|
|
56
|
+
|
|
57
|
+
for parent in [current, *current.parents]:
|
|
58
|
+
if any((parent / marker).exists() for marker in markers):
|
|
59
|
+
return parent
|
|
60
|
+
|
|
61
|
+
return None
|
|
62
|
+
|
|
63
|
+
# Project-specific paths (only available if root is set)
|
|
64
|
+
@computed_field
|
|
65
|
+
@property
|
|
66
|
+
def templates(self) -> Path | None:
|
|
67
|
+
"""Project templates directory."""
|
|
68
|
+
return self.root / "templates" if self.root else None
|
|
69
|
+
|
|
70
|
+
@computed_field
|
|
71
|
+
@property
|
|
72
|
+
def app_templates(self) -> Path | None:
|
|
73
|
+
"""Deprecated: use templates instead."""
|
|
74
|
+
return self.templates
|
|
75
|
+
|
|
76
|
+
@computed_field
|
|
77
|
+
@property
|
|
78
|
+
def locales(self) -> Path | None:
|
|
79
|
+
"""Project locales directory."""
|
|
80
|
+
return self.root / "locales" if self.root else None
|
|
81
|
+
|
|
82
|
+
@computed_field
|
|
83
|
+
@property
|
|
84
|
+
def config_vars(self) -> Path | None:
|
|
85
|
+
"""Copier answers file."""
|
|
86
|
+
return self.root / ".copier-answers.yml" if self.root else None
|
|
87
|
+
|
|
88
|
+
@computed_field
|
|
89
|
+
@property
|
|
90
|
+
def assets(self) -> Path | None:
|
|
91
|
+
"""Project assets directory."""
|
|
92
|
+
return self.root / "assets" if self.root else None
|
|
93
|
+
|
|
94
|
+
@computed_field
|
|
95
|
+
@property
|
|
96
|
+
def statics(self) -> Path | None:
|
|
97
|
+
"""Project static assets directory."""
|
|
98
|
+
return self.root / "assets" / "statics" if self.root else None
|
|
99
|
+
|
|
100
|
+
@computed_field
|
|
101
|
+
@property
|
|
102
|
+
def css(self) -> Path | None:
|
|
103
|
+
"""Project CSS directory."""
|
|
104
|
+
return self.root / "assets" / "statics" / "css" if self.root else None
|
|
105
|
+
|
|
106
|
+
@computed_field
|
|
107
|
+
@property
|
|
108
|
+
def js(self) -> Path | None:
|
|
109
|
+
"""Project JavaScript directory."""
|
|
110
|
+
return self.root / "assets" / "statics" / "js" if self.root else None
|
|
111
|
+
|
|
112
|
+
@computed_field
|
|
113
|
+
@property
|
|
114
|
+
def favicons(self) -> Path | None:
|
|
115
|
+
"""Project favicons directory."""
|
|
116
|
+
return self.root / "assets" / "statics" / "favicons" if self.root else None
|
|
117
|
+
|
|
118
|
+
@computed_field
|
|
119
|
+
@property
|
|
120
|
+
def img(self) -> Path | None:
|
|
121
|
+
"""Project images directory."""
|
|
122
|
+
return self.root / "assets" / "statics" / "img" if self.root else None
|
|
123
|
+
|
|
124
|
+
# Template paths (always return a list, project + package)
|
|
125
|
+
@computed_field
|
|
126
|
+
@property
|
|
127
|
+
def frontend_templates(self) -> list[Path]:
|
|
128
|
+
"""Frontend template search paths (project overrides, then package)."""
|
|
129
|
+
paths = []
|
|
130
|
+
if self.root:
|
|
131
|
+
project_path = self.root / "templates" / "frontend"
|
|
132
|
+
if project_path.exists():
|
|
133
|
+
paths.append(project_path)
|
|
134
|
+
paths.append(package_templates / "frontend")
|
|
135
|
+
return paths
|
|
136
|
+
|
|
137
|
+
@computed_field
|
|
138
|
+
@property
|
|
139
|
+
def email_templates(self) -> list[Path]:
|
|
140
|
+
"""Email template search paths (project overrides, then package)."""
|
|
141
|
+
paths = []
|
|
142
|
+
if self.root:
|
|
143
|
+
project_path = self.root / "templates" / "email"
|
|
144
|
+
if project_path.exists():
|
|
145
|
+
paths.append(project_path)
|
|
146
|
+
paths.append(package_templates / "email")
|
|
147
|
+
return paths
|
|
148
|
+
|
|
149
|
+
@computed_field
|
|
150
|
+
@property
|
|
151
|
+
def markdown_templates(self) -> list[Path]:
|
|
152
|
+
"""Markdown template search paths (project overrides, then package)."""
|
|
153
|
+
paths = []
|
|
154
|
+
if self.root:
|
|
155
|
+
project_path = self.root / "templates" / "markdown"
|
|
156
|
+
if project_path.exists():
|
|
157
|
+
paths.append(project_path)
|
|
158
|
+
paths.append(package_templates / "markdown")
|
|
159
|
+
return paths
|
|
160
|
+
|
|
161
|
+
def set_root(self, project_root: Path) -> None:
|
|
162
|
+
"""Explicitly set project root (overrides auto-detection)."""
|
|
163
|
+
self.root = project_root
|
|
164
|
+
|
|
165
|
+
def to_template_path_list(self, path: Path) -> list[Path]:
|
|
166
|
+
"""Convert path to list with fallback."""
|
|
167
|
+
return [path, path / self.fallback_path]
|
|
168
|
+
|
|
169
|
+
def fallback_static_default(self, static_type: str, file_name: str) -> Path:
|
|
170
|
+
"""Return a fallback path for a static file."""
|
|
171
|
+
if self.statics is None:
|
|
172
|
+
raise RuntimeError(
|
|
173
|
+
"Project root not detected. Cannot access static assets."
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
paths_to_check = [
|
|
177
|
+
self.statics / static_type / file_name,
|
|
178
|
+
self.statics / self.fallback_path / static_type / file_name,
|
|
179
|
+
]
|
|
180
|
+
|
|
181
|
+
for path in paths_to_check:
|
|
182
|
+
if path.exists():
|
|
183
|
+
return path
|
|
184
|
+
|
|
185
|
+
raise FileNotFoundError(
|
|
186
|
+
f"Could not find {file_name} in any of the fallback paths: {paths_to_check}"
|
|
187
|
+
)
|
|
42
188
|
|
|
43
|
-
root = project_root
|
|
44
189
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
app_templates = templates # Deprecated: projects now use templates/ directly
|
|
48
|
-
locales = root / "locales"
|
|
49
|
-
config_vars = root / ".copier-answers.yml"
|
|
190
|
+
# Global settings instance with lazy auto-detection
|
|
191
|
+
_settings = PathSettings()
|
|
50
192
|
|
|
51
|
-
# Update asset paths
|
|
52
|
-
assets = root / "assets"
|
|
53
|
-
statics = assets / "statics"
|
|
54
|
-
css = statics / "css"
|
|
55
|
-
js = statics / "js"
|
|
56
|
-
favicons = statics / "favicons"
|
|
57
|
-
img = statics / "img"
|
|
58
193
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
194
|
+
# Backwards-compatible module-level API
|
|
195
|
+
def set_project_root(project_root: Path) -> None:
|
|
196
|
+
"""Set the project root directory explicitly."""
|
|
197
|
+
_settings.set_root(project_root)
|
|
63
198
|
|
|
64
199
|
|
|
65
200
|
def to_template_path_list(path: Path) -> list[Path]:
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
path / fallback_path,
|
|
69
|
-
]
|
|
201
|
+
"""Convert path to list with fallback."""
|
|
202
|
+
return _settings.to_template_path_list(path)
|
|
70
203
|
|
|
71
204
|
|
|
72
205
|
def fallback_static_default(static_type: str, file_name: str) -> Path:
|
|
73
|
-
"""Return a fallback path for a file."""
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
frontend_templates = [package_templates / "frontend"]
|
|
98
|
-
email_templates = [package_templates / "email"]
|
|
99
|
-
markdown_templates = [package_templates / "markdown"]
|
|
100
|
-
|
|
101
|
-
# Project-specific paths - will be None until set_project_root() is called
|
|
102
|
-
# These get updated by set_project_root()
|
|
103
|
-
templates: Path | None = None
|
|
104
|
-
app_templates: Path | None = None
|
|
105
|
-
locales: Path | None = None
|
|
106
|
-
config_vars: Path | None = None
|
|
107
|
-
assets: Path | None = None
|
|
108
|
-
statics: Path | None = None
|
|
109
|
-
css: Path | None = None
|
|
110
|
-
js: Path | None = None
|
|
111
|
-
favicons: Path | None = None
|
|
112
|
-
img: Path | None = None
|
|
206
|
+
"""Return a fallback path for a static file."""
|
|
207
|
+
return _settings.fallback_static_default(static_type, file_name)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
# Expose settings instance for direct access
|
|
211
|
+
paths = _settings
|
|
212
|
+
|
|
213
|
+
# Module-level variables that delegate to settings (backwards compatibility)
|
|
214
|
+
# Access like: from vibetuner.paths import frontend_templates
|
|
215
|
+
# Or better: from vibetuner.paths import paths; paths.frontend_templates
|
|
216
|
+
root = _settings.root
|
|
217
|
+
templates = _settings.templates
|
|
218
|
+
app_templates = _settings.app_templates
|
|
219
|
+
locales = _settings.locales
|
|
220
|
+
config_vars = _settings.config_vars
|
|
221
|
+
assets = _settings.assets
|
|
222
|
+
statics = _settings.statics
|
|
223
|
+
css = _settings.css
|
|
224
|
+
js = _settings.js
|
|
225
|
+
favicons = _settings.favicons
|
|
226
|
+
img = _settings.img
|
|
227
|
+
frontend_templates = _settings.frontend_templates
|
|
228
|
+
email_templates = _settings.email_templates
|
|
229
|
+
markdown_templates = _settings.markdown_templates
|
vibetuner/tasks/worker.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
{# djlint:off #}
|
|
1
2
|
<html>
|
|
2
3
|
<body>
|
|
3
|
-
<h2>Sign in to {{ project_name }}</h2>
|
|
4
4
|
<p>Click the link below to sign in to your account:</p>
|
|
5
5
|
<p>
|
|
6
6
|
<a href="{{ login_url }}"
|
|
@@ -14,3 +14,4 @@
|
|
|
14
14
|
<p>If you didn't request this, you can safely ignore this email.</p>
|
|
15
15
|
</body>
|
|
16
16
|
</html>
|
|
17
|
+
{# djlint:on #}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<link rel="manifest" href="{{ url_for(
|
|
1
|
+
<link rel="manifest" href="{{ url_for('site_webmanifest').path }}" />
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<meta name="description" content="{{ meta_description | default(
|
|
7
|
-
<meta name="keywords" content="{{ meta_keywords | default(
|
|
6
|
+
<meta name="description" content="{{ meta_description | default('') }}" />
|
|
7
|
+
<meta name="keywords" content="{{ meta_keywords | default('') }}" />
|
|
8
8
|
<meta name="color-scheme" content="light" />
|
|
9
9
|
<title>
|
|
10
10
|
{% block title %}
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
{% endif %}
|
|
18
18
|
{% include "base/favicons.html.jinja" %}
|
|
19
19
|
{% include "base/opengraph.html.jinja" %}
|
|
20
|
+
|
|
20
21
|
{% block head %}
|
|
21
22
|
{% endblock head %}
|
|
22
23
|
</head>
|
|
@@ -26,6 +27,7 @@
|
|
|
26
27
|
{% block header %}
|
|
27
28
|
{% if not SKIP_HEADER %}
|
|
28
29
|
{% include "base/header.html.jinja" %}
|
|
30
|
+
|
|
29
31
|
{% endif %}
|
|
30
32
|
{% endblock header %}
|
|
31
33
|
{% block body %}
|
|
@@ -33,6 +35,7 @@
|
|
|
33
35
|
{% block footer %}
|
|
34
36
|
{% if not SKIP_FOOTER %}
|
|
35
37
|
{% include "base/footer.html.jinja" %}
|
|
38
|
+
|
|
36
39
|
{% endif %}
|
|
37
40
|
{% endblock footer %}
|
|
38
41
|
{% if DEBUG %}{{ hotreload.script(url_for('hot-reload') ) | safe }}{% endif %}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
{% extends "base/skeleton.html.jinja" %}
|
|
2
|
+
|
|
2
3
|
{% block title %}
|
|
3
4
|
Database Collections Schema - Debug
|
|
4
5
|
{% endblock title %}
|
|
@@ -99,5 +100,6 @@
|
|
|
99
100
|
</div>
|
|
100
101
|
<!-- Debug Navigation Footer -->
|
|
101
102
|
{% include "debug/components/debug_nav.html.jinja" %}
|
|
103
|
+
|
|
102
104
|
</div>
|
|
103
105
|
{% endblock body %}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<!-- Debug Navigation Component -->
|
|
2
2
|
<nav class="mt-8 pt-6 border-t border-base-200">
|
|
3
3
|
<div class="flex flex-wrap gap-4">
|
|
4
|
-
<a href="{{ url_for(
|
|
4
|
+
<a href="{{ url_for('debug_index') }}"
|
|
5
5
|
class="btn btn-outline btn-primary gap-2">
|
|
6
6
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
7
7
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2H5a2 2 0 00-2-2z">
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
</svg>
|
|
12
12
|
Debug Home
|
|
13
13
|
</a>
|
|
14
|
-
<a href="{{ url_for(
|
|
14
|
+
<a href="{{ url_for('debug_info') }}"
|
|
15
15
|
class="btn btn-outline btn-primary gap-2">
|
|
16
16
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
17
17
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z">
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
</svg>
|
|
20
20
|
System Info
|
|
21
21
|
</a>
|
|
22
|
-
<a href="{{ url_for(
|
|
22
|
+
<a href="{{ url_for('debug_collections') }}"
|
|
23
23
|
class="btn btn-outline btn-primary gap-2">
|
|
24
24
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
25
25
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4">
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
</svg>
|
|
28
28
|
Collections
|
|
29
29
|
</a>
|
|
30
|
-
<a href="{{ url_for(
|
|
30
|
+
<a href="{{ url_for('debug_users') }}"
|
|
31
31
|
class="btn btn-outline btn-warning gap-2">
|
|
32
32
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
33
33
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z">
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
</svg>
|
|
36
36
|
User Impersonation
|
|
37
37
|
</a>
|
|
38
|
-
<a href="{{ url_for(
|
|
38
|
+
<a href="{{ url_for('debug_version') }}"
|
|
39
39
|
class="btn btn-outline btn-primary gap-2">
|
|
40
40
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
41
41
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z">
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
</svg>
|
|
44
44
|
Version
|
|
45
45
|
</a>
|
|
46
|
-
<a href="{{ url_for(
|
|
46
|
+
<a href="{{ url_for('health_ping') }}"
|
|
47
47
|
class="btn btn-outline btn-success gap-2">
|
|
48
48
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
49
49
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z">
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
{% extends "base/skeleton.html.jinja" %}
|
|
2
|
+
|
|
2
3
|
{% block title %}
|
|
3
4
|
Debug Dashboard
|
|
4
5
|
{% endblock title %}
|
|
@@ -23,7 +24,7 @@
|
|
|
23
24
|
</h2>
|
|
24
25
|
<p class="text-base-content/70 mb-4">View application configuration, cookies, and runtime information</p>
|
|
25
26
|
<div class="card-actions justify-end">
|
|
26
|
-
<a href="{{ url_for(
|
|
27
|
+
<a href="{{ url_for('debug_info') }}" class="btn btn-primary btn-sm">View Details</a>
|
|
27
28
|
</div>
|
|
28
29
|
</div>
|
|
29
30
|
</div>
|
|
@@ -39,7 +40,7 @@
|
|
|
39
40
|
</h2>
|
|
40
41
|
<p class="text-base-content/70 mb-4">Explore MongoDB collections and their field schemas</p>
|
|
41
42
|
<div class="card-actions justify-end">
|
|
42
|
-
<a href="{{ url_for(
|
|
43
|
+
<a href="{{ url_for('debug_collections') }}"
|
|
43
44
|
class="btn btn-primary btn-sm">View Schemas</a>
|
|
44
45
|
</div>
|
|
45
46
|
</div>
|
|
@@ -56,7 +57,7 @@
|
|
|
56
57
|
</h2>
|
|
57
58
|
<p class="text-base-content/70 mb-4">Check application version and build information</p>
|
|
58
59
|
<div class="card-actions justify-end">
|
|
59
|
-
<a href="{{ url_for(
|
|
60
|
+
<a href="{{ url_for('debug_version') }}" class="btn btn-primary btn-sm">View Version</a>
|
|
60
61
|
</div>
|
|
61
62
|
</div>
|
|
62
63
|
</div>
|
|
@@ -72,12 +73,13 @@
|
|
|
72
73
|
</h2>
|
|
73
74
|
<p class="text-base-content/70 mb-4">Verify application health and API endpoint status</p>
|
|
74
75
|
<div class="card-actions justify-end">
|
|
75
|
-
<a href="{{ url_for(
|
|
76
|
+
<a href="{{ url_for('health_ping') }}" class="btn btn-success btn-sm">Check Health</a>
|
|
76
77
|
</div>
|
|
77
78
|
</div>
|
|
78
79
|
</div>
|
|
79
80
|
</div>
|
|
80
81
|
<!-- Debug Navigation Footer -->
|
|
81
82
|
{% include "debug/components/debug_nav.html.jinja" %}
|
|
83
|
+
|
|
82
84
|
</div>
|
|
83
85
|
{% endblock body %}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
{% extends "base/skeleton.html.jinja" %}
|
|
2
|
+
|
|
2
3
|
{% block title %}
|
|
3
4
|
System Information - Debug
|
|
4
5
|
{% endblock title %}
|
|
@@ -252,5 +253,6 @@
|
|
|
252
253
|
</div>
|
|
253
254
|
<!-- Debug Navigation Footer -->
|
|
254
255
|
{% include "debug/components/debug_nav.html.jinja" %}
|
|
256
|
+
|
|
255
257
|
</div>
|
|
256
258
|
{% endblock body %}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
{% extends "base/skeleton.html.jinja" %}
|
|
2
|
+
|
|
2
3
|
{% block title %}
|
|
3
4
|
Debug - User Impersonation
|
|
4
5
|
{% endblock title %}
|
|
@@ -36,7 +37,7 @@
|
|
|
36
37
|
User ID: <code>{{ current_user_id }}</code>
|
|
37
38
|
</p>
|
|
38
39
|
<div class="card-actions justify-end">
|
|
39
|
-
<form method="post" action="
|
|
40
|
+
<form method="post" action="{{ url_for('debug.stop_impersonation') }}">
|
|
40
41
|
<button type="submit" class="btn btn-secondary btn-sm">Stop Impersonation</button>
|
|
41
42
|
</form>
|
|
42
43
|
</div>
|
|
@@ -105,7 +106,7 @@
|
|
|
105
106
|
<td class="text-center">
|
|
106
107
|
{% if current_user_id != user.id|string %}
|
|
107
108
|
<form method="post"
|
|
108
|
-
action="
|
|
109
|
+
action="{{ url_for('debug.impersonate_user', user_id=user.id) }}"
|
|
109
110
|
class="inline">
|
|
110
111
|
<button type="submit" class="btn btn-outline btn-sm">Impersonate</button>
|
|
111
112
|
</form>
|
|
@@ -133,5 +134,6 @@
|
|
|
133
134
|
</div>
|
|
134
135
|
<!-- Debug Navigation Footer -->
|
|
135
136
|
{% include "debug/components/debug_nav.html.jinja" %}
|
|
137
|
+
|
|
136
138
|
</div>
|
|
137
139
|
{% endblock body %}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
{% extends "base/skeleton.html.jinja" %}
|
|
2
|
+
|
|
2
3
|
{% block title %}
|
|
3
4
|
Version Information - Debug
|
|
4
5
|
{% endblock title %}
|
|
@@ -49,5 +50,6 @@
|
|
|
49
50
|
</div>
|
|
50
51
|
<!-- Debug Navigation Footer -->
|
|
51
52
|
{% include "debug/components/debug_nav.html.jinja" %}
|
|
53
|
+
|
|
52
54
|
</div>
|
|
53
55
|
{% endblock body %}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
{% extends "base/skeleton.html.jinja" %}
|
|
2
|
+
|
|
2
3
|
{% block title %}
|
|
3
4
|
{{ _("Check Your Email") }}
|
|
4
5
|
{% endblock title %}
|
|
@@ -64,7 +65,7 @@
|
|
|
64
65
|
{{ _("Try Again") }}
|
|
65
66
|
</a>
|
|
66
67
|
<!-- Back to Home -->
|
|
67
|
-
<a href="{{ url_for(
|
|
68
|
+
<a href="{{ url_for('homepage') }}"
|
|
68
69
|
class="btn btn-ghost btn-block gap-2 text-base-content/70 hover:text-base-content transition-all duration-300">
|
|
69
70
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
70
71
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
{% set BODY_CLASS = "min-h-screen bg-gradient-to-br from-slate-50 via-blue-50 to-indigo-100 flex flex-col justify-between" %}
|
|
2
2
|
{% extends "base/skeleton.html.jinja" %}
|
|
3
|
+
|
|
3
4
|
{% block body %}
|
|
4
5
|
<!-- Main Content -->
|
|
5
6
|
<div class="flex-1 flex items-center justify-center px-6">
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
{% extends "base/skeleton.html.jinja" %}
|
|
2
|
+
|
|
2
3
|
{% block title %}
|
|
3
4
|
{{ _("Sign In") }}
|
|
4
5
|
{% endblock title %}
|
|
@@ -15,7 +16,11 @@
|
|
|
15
16
|
{% if has_oauth and providers %}
|
|
16
17
|
<div class="space-y-3 mb-6">
|
|
17
18
|
{% for provider in providers %}
|
|
18
|
-
|
|
19
|
+
{% set login_url = url_for('login_with_' + provider) %}
|
|
20
|
+
{% if next %}
|
|
21
|
+
{% set login_url = login_url ~ '?next=' ~ next %}
|
|
22
|
+
{% endif %}
|
|
23
|
+
<a href="{{ login_url }}"
|
|
19
24
|
class="btn btn-outline btn-block justify-start gap-3 hover:btn-primary group transition-all duration-300 hover:scale-[1.02]">
|
|
20
25
|
<span>{{ _("Continue with %(provider)s") | format(provider=provider.title()) }}</span>
|
|
21
26
|
</a>
|
|
@@ -29,7 +34,7 @@
|
|
|
29
34
|
<div class="bg-base-200/50 rounded-2xl p-6 border border-base-300/50">
|
|
30
35
|
<form method="post"
|
|
31
36
|
class="space-y-4"
|
|
32
|
-
action="{{ url_for(
|
|
37
|
+
action="{{ url_for('send_magic_link') }}">
|
|
33
38
|
{% if next %}<input type="hidden" name="next" value="{{ next }}" />{% endif %}
|
|
34
39
|
<div class="form-control">
|
|
35
40
|
<label class="label" for="email">
|
|
@@ -40,7 +45,7 @@
|
|
|
40
45
|
id="email"
|
|
41
46
|
name="email"
|
|
42
47
|
class="input input-bordered w-full pr-12 focus:input-primary transition-all duration-300"
|
|
43
|
-
placeholder="{{ _(
|
|
48
|
+
placeholder="{{ _('Enter your email address') }}"
|
|
44
49
|
required
|
|
45
50
|
autocomplete="email" />
|
|
46
51
|
<div class="absolute inset-y-0 right-0 flex items-center pr-3">
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
{% extends "base/skeleton.html.jinja" %}
|
|
2
|
+
|
|
2
3
|
{% block title %}
|
|
3
4
|
{{ _("Edit Profile") }}
|
|
4
5
|
{% endblock title %}
|
|
@@ -13,7 +14,7 @@
|
|
|
13
14
|
</div>
|
|
14
15
|
<!-- Edit Form -->
|
|
15
16
|
<div class="bg-white shadow-lg rounded-lg p-6">
|
|
16
|
-
<form method="post" action="{{ url_for(
|
|
17
|
+
<form method="post" action="{{ url_for('user_edit_submit') }}">
|
|
17
18
|
<div class="space-y-6">
|
|
18
19
|
<!-- Name Field -->
|
|
19
20
|
<div>
|
|
@@ -55,7 +56,7 @@
|
|
|
55
56
|
</div>
|
|
56
57
|
<!-- Form Actions -->
|
|
57
58
|
<div class="flex justify-between items-center mt-8 pt-6 border-t border-gray-200">
|
|
58
|
-
<a href="{{ url_for(
|
|
59
|
+
<a href="{{ url_for('user_profile') }}" class="btn btn-ghost">{{ _("Cancel") }}</a>
|
|
59
60
|
<button type="submit" class="btn btn-primary">{{ _("Save Changes") }}</button>
|
|
60
61
|
</div>
|
|
61
62
|
</form>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
{% extends "base/skeleton.html.jinja" %}
|
|
2
|
+
|
|
2
3
|
{% block title %}
|
|
3
4
|
{{ _("User Profile") }}
|
|
4
5
|
{% endblock title %}
|
|
@@ -108,7 +109,7 @@
|
|
|
108
109
|
hx-get="/user/edit"
|
|
109
110
|
hx-target="#main-content"
|
|
110
111
|
hx-swap="innerHTML">{{ _("Edit Profile") }}</button>
|
|
111
|
-
<a href="{{ url_for(
|
|
112
|
+
<a href="{{ url_for('auth_logout') }}" class="btn btn-outline btn-error">{{ _("Sign Out") }}</a>
|
|
112
113
|
</div>
|
|
113
114
|
</div>
|
|
114
115
|
</div>
|