vibetuner 2.14.1__py3-none-any.whl → 2.26.9__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.

Files changed (52) hide show
  1. vibetuner/cli/__init__.py +65 -3
  2. vibetuner/cli/run.py +2 -2
  3. vibetuner/config.py +19 -9
  4. vibetuner/context.py +3 -0
  5. vibetuner/frontend/__init__.py +27 -5
  6. vibetuner/frontend/lifespan.py +18 -7
  7. vibetuner/frontend/middleware.py +2 -6
  8. vibetuner/frontend/routes/debug.py +1 -1
  9. vibetuner/frontend/routes/user.py +1 -1
  10. vibetuner/frontend/templates.py +1 -3
  11. vibetuner/mongo.py +18 -3
  12. vibetuner/paths.py +31 -10
  13. vibetuner/tasks/__init__.py +0 -2
  14. vibetuner/tasks/lifespan.py +28 -0
  15. vibetuner/tasks/worker.py +2 -5
  16. vibetuner/templates/email/{default/magic_link.html.jinja → magic_link.html.jinja} +2 -1
  17. vibetuner/templates/frontend/base/favicons.html.jinja +1 -1
  18. vibetuner/templates/frontend/base/skeleton.html.jinja +5 -2
  19. vibetuner/templates/frontend/debug/collections.html.jinja +2 -0
  20. vibetuner/templates/frontend/debug/components/debug_nav.html.jinja +6 -6
  21. vibetuner/templates/frontend/debug/index.html.jinja +6 -4
  22. vibetuner/templates/frontend/debug/info.html.jinja +2 -0
  23. vibetuner/templates/frontend/debug/users.html.jinja +4 -2
  24. vibetuner/templates/frontend/debug/version.html.jinja +2 -0
  25. vibetuner/templates/frontend/email_sent.html.jinja +2 -1
  26. vibetuner/templates/frontend/index.html.jinja +1 -0
  27. vibetuner/templates/frontend/login.html.jinja +8 -3
  28. vibetuner/templates/frontend/user/edit.html.jinja +3 -2
  29. vibetuner/templates/frontend/user/profile.html.jinja +2 -1
  30. vibetuner/versioning.py +6 -2
  31. {vibetuner-2.14.1.dist-info → vibetuner-2.26.9.dist-info}/METADATA +23 -20
  32. vibetuner-2.26.9.dist-info/RECORD +71 -0
  33. {vibetuner-2.14.1.dist-info → vibetuner-2.26.9.dist-info}/WHEEL +1 -1
  34. vibetuner/frontend/AGENTS.md +0 -113
  35. vibetuner/frontend/CLAUDE.md +0 -113
  36. vibetuner/frontend/context.py +0 -10
  37. vibetuner/models/AGENTS.md +0 -165
  38. vibetuner/models/CLAUDE.md +0 -165
  39. vibetuner/services/AGENTS.md +0 -104
  40. vibetuner/services/CLAUDE.md +0 -104
  41. vibetuner/tasks/AGENTS.md +0 -98
  42. vibetuner/tasks/CLAUDE.md +0 -98
  43. vibetuner/tasks/context.py +0 -34
  44. vibetuner/templates/email/AGENTS.md +0 -48
  45. vibetuner/templates/email/CLAUDE.md +0 -48
  46. vibetuner/templates/frontend/AGENTS.md +0 -74
  47. vibetuner/templates/frontend/CLAUDE.md +0 -74
  48. vibetuner/templates/markdown/AGENTS.md +0 -29
  49. vibetuner/templates/markdown/CLAUDE.md +0 -29
  50. vibetuner-2.14.1.dist-info/RECORD +0 -86
  51. /vibetuner/templates/email/{default/magic_link.txt.jinja → magic_link.txt.jinja} +0 -0
  52. {vibetuner-2.14.1.dist-info → vibetuner-2.26.9.dist-info}/entry_points.txt +0 -0
vibetuner/cli/__init__.py CHANGED
@@ -1,16 +1,20 @@
1
1
  # ABOUTME: Core CLI setup with AsyncTyper wrapper and base configuration
2
2
  # ABOUTME: Provides main CLI entry point and logging configuration
3
+ import importlib.metadata
3
4
  import inspect
4
5
  from functools import partial, wraps
5
6
  from importlib import import_module
7
+ from pathlib import Path
8
+ from typing import Annotated
6
9
 
7
10
  import asyncer
8
11
  import typer
9
12
  from rich.console import Console
13
+ from rich.table import Table
10
14
 
11
15
  from vibetuner.cli.run import run_app
12
16
  from vibetuner.cli.scaffold import scaffold_app
13
- from vibetuner.logging import LogLevel, setup_logging
17
+ from vibetuner.logging import LogLevel, logger, setup_logging
14
18
 
15
19
 
16
20
  console = Console()
@@ -69,11 +73,69 @@ def callback(log_level: LogLevel | None = LOG_LEVEL_OPTION) -> None:
69
73
  setup_logging(level=log_level)
70
74
 
71
75
 
76
+ @app.command()
77
+ def version(
78
+ show_app: bool = typer.Option(
79
+ False,
80
+ "--app",
81
+ "-a",
82
+ help="Show app settings version even if not in a project directory",
83
+ ),
84
+ ) -> None:
85
+ """Show version information."""
86
+ try:
87
+ # Get vibetuner package version
88
+ vibetuner_version = importlib.metadata.version("vibetuner")
89
+ except importlib.metadata.PackageNotFoundError:
90
+ vibetuner_version = "unknown"
91
+
92
+ # Create table for nice display
93
+ table = Table(title="Version Information")
94
+ table.add_column("Component", style="cyan", no_wrap=True)
95
+ table.add_column("Version", style="green", no_wrap=True)
96
+
97
+ # Always show vibetuner package version
98
+ table.add_row("vibetuner package", vibetuner_version)
99
+
100
+ # Show app version if requested or if in a project
101
+ try:
102
+ from vibetuner.config import CoreConfiguration
103
+
104
+ settings = CoreConfiguration()
105
+ table.add_row(f"{settings.project.project_name} settings", settings.version)
106
+ except Exception:
107
+ if show_app:
108
+ table.add_row("app settings", "not in project directory")
109
+ # else: don't show app version if not in project and not requested
110
+
111
+ console.print(table)
112
+
113
+
114
+ @app.command()
115
+ def core_template_symlink(
116
+ target: Annotated[
117
+ Path,
118
+ typer.Argument(
119
+ help="Path where the 'core' symlink should be created or updated",
120
+ ),
121
+ ],
122
+ ) -> None:
123
+ """Create or update a 'core' symlink to the package templates directory."""
124
+ from vibetuner.paths import create_core_templates_symlink
125
+
126
+ create_core_templates_symlink(target)
127
+
128
+
72
129
  app.add_typer(run_app, name="run")
73
130
  app.add_typer(scaffold_app, name="scaffold")
74
131
 
75
132
  try:
76
133
  import_module("app.cli")
77
- except (ImportError, ModuleNotFoundError):
134
+ except ModuleNotFoundError:
135
+ # Silent pass for missing app.cli module (expected in some projects)
78
136
  pass
79
- # Cache buster
137
+ except ImportError as e:
138
+ # Log warning for any import error (including syntax errors, missing dependencies, etc.)
139
+ logger.warning(
140
+ f"Failed to import app.cli: {e}. User CLI commands will not be available."
141
+ )
vibetuner/cli/run.py CHANGED
@@ -47,7 +47,7 @@ def dev(
47
47
 
48
48
  # Call streaq programmatically
49
49
  streaq_main(
50
- worker_path="app.tasks.worker.worker",
50
+ worker_path="vibetuner.tasks.worker.worker",
51
51
  workers=1,
52
52
  reload=True,
53
53
  verbose=True,
@@ -123,7 +123,7 @@ def prod(
123
123
 
124
124
  # Call streaq programmatically
125
125
  streaq_main(
126
- worker_path="app.tasks.worker.worker",
126
+ worker_path="vibetuner.tasks.worker.worker",
127
127
  workers=workers_count,
128
128
  reload=False,
129
129
  verbose=False,
vibetuner/config.py CHANGED
@@ -17,6 +17,8 @@ from pydantic import (
17
17
  from pydantic_extra_types.language_code import LanguageAlpha2
18
18
  from pydantic_settings import BaseSettings, SettingsConfigDict
19
19
 
20
+ from vibetuner.logging import logger
21
+
20
22
  from .paths import config_vars as config_vars_path
21
23
  from .versioning import version
22
24
 
@@ -32,12 +34,16 @@ def _load_project_config() -> "ProjectConfiguration":
32
34
  )
33
35
  if not config_vars_path.exists():
34
36
  return ProjectConfiguration()
35
- return ProjectConfiguration(
36
- **yaml.safe_load(config_vars_path.read_text(encoding="utf-8"))
37
- )
37
+
38
+ yaml_data = yaml.safe_load(config_vars_path.read_text(encoding="utf-8"))
39
+ return ProjectConfiguration(**yaml_data)
38
40
 
39
41
 
40
42
  class ProjectConfiguration(BaseSettings):
43
+ @classmethod
44
+ def from_project_config(cls) -> "ProjectConfiguration":
45
+ return _load_project_config()
46
+
41
47
  project_slug: str = "default_project"
42
48
  project_name: str = "default_project"
43
49
 
@@ -47,9 +53,6 @@ class ProjectConfiguration(BaseSettings):
47
53
  supported_languages: set[LanguageAlpha2] | None = None
48
54
  default_language: LanguageAlpha2 = LanguageAlpha2("en")
49
55
 
50
- mongodb_url: MongoDsn | None = None
51
- redis_url: RedisDsn | None = None
52
-
53
56
  # AWS Parameters
54
57
  aws_default_region: str = "eu-central-1"
55
58
 
@@ -90,16 +93,20 @@ class ProjectConfiguration(BaseSettings):
90
93
  )
91
94
  return f"© {year_part}{f' {self.company_name}' if self.company_name else ''}"
92
95
 
93
- model_config = SettingsConfigDict(extra="ignore")
96
+ model_config = SettingsConfigDict(case_sensitive=False, extra="ignore")
94
97
 
95
98
 
96
99
  class CoreConfiguration(BaseSettings):
97
- project: ProjectConfiguration
100
+ project: ProjectConfiguration = ProjectConfiguration.from_project_config()
98
101
 
99
102
  debug: bool = False
100
103
  version: str = version
101
104
  session_key: SecretStr = SecretStr("ct-!secret-must-change-me")
102
105
 
106
+ # Database and Cache URLs
107
+ mongodb_url: MongoDsn = MongoDsn("mongodb://localhost:27017")
108
+ redis_url: RedisDsn = RedisDsn("redis://localhost:6379")
109
+
103
110
  aws_access_key_id: SecretStr | None = None
104
111
  aws_secret_access_key: SecretStr | None = None
105
112
 
@@ -130,4 +137,7 @@ class CoreConfiguration(BaseSettings):
130
137
  )
131
138
 
132
139
 
133
- settings = CoreConfiguration(project=_load_project_config())
140
+ settings = CoreConfiguration()
141
+
142
+
143
+ logger.info("Configuration loaded for project: {}", settings.project.project_name)
vibetuner/context.py CHANGED
@@ -23,3 +23,6 @@ class Context(BaseModel):
23
23
  fqdn: str | None = settings.project.fqdn
24
24
 
25
25
  model_config = {"arbitrary_types_allowed": True}
26
+
27
+
28
+ ctx = Context()
@@ -4,10 +4,12 @@ from fastapi import APIRouter, Depends as Depends, FastAPI, Request
4
4
  from fastapi.responses import HTMLResponse, RedirectResponse
5
5
  from fastapi.staticfiles import StaticFiles
6
6
 
7
+ import vibetuner.frontend.lifespan as lifespan_module
7
8
  from vibetuner import paths
9
+ from vibetuner.logging import logger
8
10
 
9
11
  from .deps import LangDep as LangDep, MagicCookieDep as MagicCookieDep
10
- from .lifespan import ctx, lifespan
12
+ from .lifespan import ctx
11
13
  from .middleware import middlewares
12
14
  from .routes import auth, debug, health, language, meta, user
13
15
  from .templates import render_template
@@ -21,15 +23,35 @@ def register_router(router: APIRouter) -> None:
21
23
 
22
24
 
23
25
  try:
24
- import app.frontend.oauth as _app_oauth # noqa: F401 # type: ignore[unresolved-import]
25
- import app.frontend.routes as _app_routes # noqa: F401 # type: ignore[unresolved-import]
26
+ import app.frontend.oauth as _app_oauth # type: ignore[unresolved-import] # noqa: F401
27
+ import app.frontend.routes as _app_routes # type: ignore[unresolved-import] # noqa: F401
26
28
 
27
29
  # Register OAuth routes after providers are registered
28
30
  from .routes.auth import register_oauth_routes
29
31
 
30
32
  register_oauth_routes()
31
- except (ImportError, ModuleNotFoundError):
33
+ except ModuleNotFoundError:
34
+ # Silent pass for missing app.frontend.oauth or app.frontend.routes modules (expected in some projects)
32
35
  pass
36
+ except ImportError as e:
37
+ # Log warning for any import error (including syntax errors, missing dependencies, etc.)
38
+ logger.warning(
39
+ f"Failed to import app.frontend.oauth or app.frontend.routes: {e}. OAuth and custom routes will not be available."
40
+ )
41
+
42
+ try:
43
+ from app.frontend.middleware import (
44
+ middlewares as app_middlewares, # type: ignore[unresolved-import]
45
+ )
46
+
47
+ middlewares.extend(app_middlewares)
48
+ except ModuleNotFoundError:
49
+ pass
50
+ except ImportError as e:
51
+ # Log warning for any import error (including syntax errors, missing dependencies, etc.)
52
+ logger.warning(
53
+ f"Failed to import app.frontend.middleware: {e}. Additional middlewares will not be available."
54
+ )
33
55
 
34
56
 
35
57
  dependencies: list[Any] = [
@@ -38,7 +60,7 @@ dependencies: list[Any] = [
38
60
 
39
61
  app = FastAPI(
40
62
  debug=ctx.DEBUG,
41
- lifespan=lifespan,
63
+ lifespan=lifespan_module.lifespan,
42
64
  docs_url=None,
43
65
  redoc_url=None,
44
66
  openapi_url=None,
@@ -1,26 +1,37 @@
1
1
  from contextlib import asynccontextmanager
2
+ from typing import AsyncGenerator
2
3
 
3
4
  from fastapi import FastAPI
4
5
 
6
+ from vibetuner.context import ctx
7
+ from vibetuner.logging import logger
5
8
  from vibetuner.mongo import init_models
6
9
 
7
- from .context import ctx
8
10
  from .hotreload import hotreload
9
11
 
10
12
 
11
13
  @asynccontextmanager
12
- async def lifespan(app: FastAPI):
14
+ async def base_lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
15
+ logger.info("Vibetuner frontend starting")
13
16
  if ctx.DEBUG:
14
17
  await hotreload.startup()
15
18
 
16
19
  await init_models()
17
- # Add below anything that should happen before startup
18
20
 
19
- # Until here
20
21
  yield
21
22
 
22
- # Add below anything that should happen before shutdown
23
-
24
- # Until here
23
+ logger.info("Vibetuner frontend stopping")
25
24
  if ctx.DEBUG:
26
25
  await hotreload.shutdown()
26
+ logger.info("Vibetuner frontend stopped")
27
+
28
+
29
+ try:
30
+ from app.frontend.lifespan import lifespan # ty: ignore
31
+ except ModuleNotFoundError:
32
+ # Silent pass for missing app.frontend.lifespan module (expected in some projects)
33
+ lifespan = base_lifespan
34
+ except ImportError as e:
35
+ # Log warning for any import error (including syntax errors, missing dependencies, etc.)
36
+ logger.warning(f"Failed to import app.frontend.lifespan: {e}. Using base lifespan.")
37
+ lifespan = base_lifespan
@@ -13,12 +13,12 @@ from starlette_babel import (
13
13
  LocaleMiddleware,
14
14
  get_translator,
15
15
  )
16
- from starlette_htmx.middleware import HtmxMiddleware # type: ignore[import-untyped]
16
+ from starlette_htmx.middleware import HtmxMiddleware
17
17
 
18
18
  from vibetuner.config import settings
19
+ from vibetuner.context import ctx
19
20
  from vibetuner.paths import locales as locales_path
20
21
 
21
- from .context import ctx
22
22
  from .oauth import WebUser
23
23
 
24
24
 
@@ -126,7 +126,6 @@ class AuthBackend(AuthenticationBackend):
126
126
  return None
127
127
 
128
128
 
129
- # Until this line
130
129
  middlewares: list[Middleware] = [
131
130
  Middleware(TrustedHostMiddleware),
132
131
  Middleware(ForwardedProtocolMiddleware),
@@ -145,7 +144,4 @@ middlewares: list[Middleware] = [
145
144
  ),
146
145
  Middleware(AdjustLangCookieMiddleware),
147
146
  Middleware(AuthenticationMiddleware, backend=AuthBackend()),
148
- # Add your middleware below this line
149
147
  ]
150
-
151
- # EOF
@@ -10,10 +10,10 @@ from fastapi.responses import (
10
10
  RedirectResponse,
11
11
  )
12
12
 
13
+ from vibetuner.context import ctx
13
14
  from vibetuner.models import UserModel
14
15
  from vibetuner.models.registry import get_all_models
15
16
 
16
- from ..context import ctx
17
17
  from ..deps import MAGIC_COOKIE_NAME
18
18
  from ..templates import render_template
19
19
 
@@ -3,9 +3,9 @@ from fastapi.responses import HTMLResponse, RedirectResponse
3
3
  from pydantic_extra_types.language_code import LanguageAlpha2
4
4
  from starlette.authentication import requires
5
5
 
6
+ from vibetuner.context import ctx
6
7
  from vibetuner.models import UserModel
7
8
 
8
- from ..context import ctx
9
9
  from ..templates import render_template
10
10
 
11
11
 
@@ -7,7 +7,7 @@ from starlette.responses import HTMLResponse
7
7
  from starlette_babel import gettext_lazy as _, gettext_lazy as ngettext
8
8
  from starlette_babel.contrib.jinja import configure_jinja_env
9
9
 
10
- from vibetuner.context import Context
10
+ from vibetuner.context import ctx as data_ctx
11
11
  from vibetuner.paths import frontend_templates
12
12
  from vibetuner.templates import render_static_template
13
13
  from vibetuner.time import age_in_timedelta
@@ -19,8 +19,6 @@ __all__ = [
19
19
  "render_static_template",
20
20
  ]
21
21
 
22
- data_ctx = Context()
23
-
24
22
 
25
23
  def timeago(dt):
26
24
  """Converts a datetime object to a human-readable string representing the time elapsed since the given datetime.
vibetuner/mongo.py CHANGED
@@ -1,15 +1,30 @@
1
+ from importlib import import_module
2
+
1
3
  from beanie import init_beanie
2
4
  from pymongo import AsyncMongoClient
3
5
 
4
- from .config import settings
5
- from .models.registry import get_all_models
6
+ from vibetuner.config import settings
7
+ from vibetuner.logging import logger
8
+ from vibetuner.models.registry import get_all_models
6
9
 
7
10
 
8
11
  async def init_models() -> None:
9
12
  """Initialize MongoDB connection and register all Beanie models."""
10
13
 
14
+ # Try to import user models to trigger their registration
15
+ try:
16
+ import_module("app.models")
17
+ except ModuleNotFoundError:
18
+ # Silent pass for missing app.models module (expected in some projects)
19
+ pass
20
+ except ImportError as e:
21
+ # Log warning for any import error (including syntax errors, missing dependencies, etc.)
22
+ logger.warning(
23
+ f"Failed to import app.models: {e}. User models will not be registered."
24
+ )
25
+
11
26
  client: AsyncMongoClient = AsyncMongoClient(
12
- host=str(settings.project.mongodb_url),
27
+ host=str(settings.mongodb_url),
13
28
  compressors=["zstd"],
14
29
  )
15
30
 
vibetuner/paths.py CHANGED
@@ -5,6 +5,8 @@ from typing import Self
5
5
  from pydantic import computed_field, model_validator
6
6
  from pydantic_settings import BaseSettings, SettingsConfigDict
7
7
 
8
+ from vibetuner.logging import logger
9
+
8
10
 
9
11
  # Package-relative paths (for bundled templates in the vibetuner package)
10
12
  _package_files = files("vibetuner")
@@ -22,6 +24,35 @@ def _get_package_templates_path() -> Path:
22
24
  ) from None
23
25
 
24
26
 
27
+ def create_core_templates_symlink(target: Path) -> None:
28
+ """Create or update a 'core' symlink pointing to the package templates directory."""
29
+
30
+ try:
31
+ source = _get_package_templates_path().resolve()
32
+ except RuntimeError as e:
33
+ logger.error(f"Cannot create symlink: {e}")
34
+ return
35
+
36
+ # Case 1: target does not exist → create symlink
37
+ if not target.exists():
38
+ target.symlink_to(source, target_is_directory=True)
39
+ logger.info(f"Created symlink '{target}' → '{source}'")
40
+ return
41
+
42
+ # Case 2: exists but is not a symlink → error
43
+ if not target.is_symlink():
44
+ logger.error(f"Cannot create symlink: '{target}' exists and is not a symlink.")
45
+ raise FileExistsError(
46
+ f"Cannot create symlink: '{target}' exists and is not a symlink."
47
+ )
48
+
49
+ # Case 3: is a symlink but points somewhere else → update it
50
+ if target.resolve() != source:
51
+ target.unlink()
52
+ target.symlink_to(source, target_is_directory=True)
53
+ logger.info(f"Updated symlink '{target}' → '{source}'")
54
+
55
+
25
56
  # Package templates always available
26
57
  package_templates = _get_package_templates_path()
27
58
  core_templates = package_templates # Alias for backwards compatibility
@@ -158,10 +189,6 @@ class PathSettings(BaseSettings):
158
189
  paths.append(package_templates / "markdown")
159
190
  return paths
160
191
 
161
- def set_root(self, project_root: Path) -> None:
162
- """Explicitly set project root (overrides auto-detection)."""
163
- self.root = project_root
164
-
165
192
  def to_template_path_list(self, path: Path) -> list[Path]:
166
193
  """Convert path to list with fallback."""
167
194
  return [path, path / self.fallback_path]
@@ -191,12 +218,6 @@ class PathSettings(BaseSettings):
191
218
  _settings = PathSettings()
192
219
 
193
220
 
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)
198
-
199
-
200
221
  def to_template_path_list(path: Path) -> list[Path]:
201
222
  """Convert path to list with fallback."""
202
223
  return _settings.to_template_path_list(path)
@@ -1,2 +0,0 @@
1
- # Import all your tasks here with (noqa: F401)
2
- # from . import x_tasks
@@ -0,0 +1,28 @@
1
+ from contextlib import asynccontextmanager
2
+ from typing import AsyncGenerator
3
+
4
+ from vibetuner.context import Context, ctx
5
+ from vibetuner.logging import logger
6
+ from vibetuner.mongo import init_models
7
+
8
+
9
+ @asynccontextmanager
10
+ async def base_lifespan() -> AsyncGenerator[Context, None]:
11
+ logger.info("Vibetuner task worker starting")
12
+
13
+ await init_models()
14
+
15
+ yield ctx
16
+
17
+ logger.info("Vibetuner task worker stopping")
18
+
19
+
20
+ try:
21
+ from app.tasks.lifespan import lifespan # ty: ignore
22
+ except ModuleNotFoundError:
23
+ # Silent pass for missing app.tasks.lifespan module (expected in some projects)
24
+ lifespan = base_lifespan
25
+ except ImportError as e:
26
+ # Log warning for any import error (including syntax errors, missing dependencies, etc.)
27
+ logger.warning(f"Failed to import app.tasks.lifespan: {e}. Using base lifespan.")
28
+ lifespan = base_lifespan
vibetuner/tasks/worker.py CHANGED
@@ -1,11 +1,11 @@
1
1
  from streaq import Worker
2
2
 
3
3
  from vibetuner.config import settings
4
- from vibetuner.tasks.context import lifespan
4
+ from vibetuner.tasks.lifespan import lifespan
5
5
 
6
6
 
7
7
  worker = Worker(
8
- redis_url=str(settings.project.redis_url),
8
+ redis_url=str(settings.redis_url),
9
9
  queue_name=(
10
10
  settings.project.project_slug
11
11
  if not settings.debug
@@ -13,6 +13,3 @@ worker = Worker(
13
13
  ),
14
14
  lifespan=lifespan,
15
15
  )
16
-
17
- # Register tasks
18
- # use something like from . import task_module_name // noqa: E402, F401
@@ -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("site_webmanifest").path }}" />
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("debug_index") }}"
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("debug_info") }}"
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("debug_collections") }}"
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("debug_users") }}"
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("debug_version") }}"
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("health_ping") }}"
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">