diff --git a/enlace/base.py b/enlace/base.py index fac9aca..6adde84 100644 --- a/enlace/base.py +++ b/enlace/base.py @@ -153,6 +153,15 @@ class AppConfig(BaseModel): "Empty list means any authenticated user allowed." ), ) + user_store: bool = Field( + default=False, + description=( + "Offer this app a per-user store to signed-in visitors even when its " + "access is not protected:user (e.g. a public app with optional sign-in). " + "Consumed by enlace_auth, like ``access``; enlace itself does not " + "interpret it." + ), + ) display_name: str = "" provenance: dict[str, str] = Field(default_factory=dict) diff --git a/enlace/discover.py b/enlace/discover.py index 829e708..a8b6c8c 100644 --- a/enlace/discover.py +++ b/enlace/discover.py @@ -367,6 +367,7 @@ def _load_toml(path: Path) -> dict: "access": "access", "shared_password_env": "shared_password_env", "allowed_users": "allowed_users", + "user_store": "user_store", "display_name": "display_name", # `title` is an alias for `display_name`. If an app.toml sets BOTH, the # later key in this map wins (dict-insertion order in _overlay_toml_fields) diff --git a/enlace/tests/test_discover.py b/enlace/tests/test_discover.py index 765782f..92af9f0 100644 --- a/enlace/tests/test_discover.py +++ b/enlace/tests/test_discover.py @@ -107,6 +107,21 @@ def test_discover_app_toml_override(single_app_dir): assert app.provenance["route_prefix"] == "override: app.toml" +def test_discover_user_store_flag(single_app_dir, tmp_apps_dir): + """``user_store = true`` reaches AppConfig (asgi and frontend-only); default off.""" + discoverer = _make_discoverer() + assert discoverer.discover(single_app_dir)[0].user_store is False + toml = 'access = "public"\nuser_store = true\n' + (single_app_dir / "foo" / "app.toml").write_text(toml) + static = tmp_apps_dir / "site" + (static / "frontend").mkdir(parents=True) + (static / "frontend" / "index.html").write_text("

hi

") + (static / "app.toml").write_text(toml + 'frontend_dir = "frontend"\n') + apps = {a.name: a for a in discoverer.discover(tmp_apps_dir)} + assert apps["foo"].user_store is True + assert apps["site"].user_store is True + + def test_discover_conflict_detection(tmp_apps_dir): """Two apps resolving to the same route trigger a conflict.""" for name in ["app_a", "app_b"]: