Make config part of bootstrap.

This commit is contained in:
Leo Vasanko
2026-02-09 18:33:29 +00:00
parent eb2a003f24
commit abdca4f648
4 changed files with 39 additions and 19 deletions
+11 -4
View File
@@ -10,6 +10,7 @@ import asyncio
import logging import logging
from paskia import authsession, db, globals from paskia import authsession, db, globals
from paskia.db.structs import Config
from paskia.util import hostutil from paskia.util import hostutil
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -30,15 +31,18 @@ def _log_reset_link(passphrase: str, message: str | None = None) -> str:
return reset_link return reset_link
async def bootstrap_system() -> None: async def bootstrap_system(config: Config | None = None) -> None:
""" """
Bootstrap the entire system with default data. Bootstrap the entire system with default data.
Uses db.bootstrap() which performs all operations in a single transaction. Uses db.bootstrap() which performs all operations in a single transaction.
The transaction log will show a single "bootstrap" action with all changes. The transaction log will show a single "bootstrap" action with all changes.
Args:
config: Configuration to store (rp_id, rp_name, origins, etc.)
""" """
# Call the single-transaction bootstrap function # Call the single-transaction bootstrap function
reset_passphrase = db.bootstrap() reset_passphrase = db.bootstrap(config=config)
# Log the reset link (this is separate from the transaction log) # Log the reset link (this is separate from the transaction log)
_log_reset_link(reset_passphrase, "✅ Bootstrap completed!") _log_reset_link(reset_passphrase, "✅ Bootstrap completed!")
@@ -89,10 +93,13 @@ async def check_admin_credentials() -> bool:
return False return False
async def bootstrap_if_needed() -> bool: async def bootstrap_if_needed(config: Config | None = None) -> bool:
""" """
Check if system needs bootstrapping and perform it if necessary. Check if system needs bootstrapping and perform it if necessary.
Args:
config: Configuration to store during bootstrap (rp_id, rp_name, origins, etc.)
Returns: Returns:
bool: True if bootstrapping was performed, False if system was already set up bool: True if bootstrapping was performed, False if system was already set up
""" """
@@ -105,7 +112,7 @@ async def bootstrap_if_needed() -> bool:
# No admin permission found, need to bootstrap # No admin permission found, need to bootstrap
# Bootstrap creates the admin user AND the reset link, so no need to check credentials after # Bootstrap creates the admin user AND the reset link, so no need to check credentials after
await bootstrap_system() await bootstrap_system(config=config)
return True return True
+8 -2
View File
@@ -109,13 +109,19 @@ class UuidResolver:
return role_data["display_name"] return role_data["display_name"]
# Check permissions # Check permissions
if "permissions" in self._previous and uuid_str in self._previous["permissions"]: if (
"permissions" in self._previous
and uuid_str in self._previous["permissions"]
):
perm_data = self._previous["permissions"][uuid_str] perm_data = self._previous["permissions"][uuid_str]
if isinstance(perm_data, dict) and "display_name" in perm_data: if isinstance(perm_data, dict) and "display_name" in perm_data:
return perm_data["display_name"] return perm_data["display_name"]
# Check credentials - look up user name # Check credentials - look up user name
if "credentials" in self._previous and uuid_str in self._previous["credentials"]: if (
"credentials" in self._previous
and uuid_str in self._previous["credentials"]
):
cred_data = self._previous["credentials"][uuid_str] cred_data = self._previous["credentials"][uuid_str]
if isinstance(cred_data, dict) and "user" in cred_data: if isinstance(cred_data, dict) and "user" in cred_data:
user_uuid = cred_data["user"] user_uuid = cred_data["user"]
+7
View File
@@ -723,6 +723,7 @@ def bootstrap(
admin_name: str = "Admin", admin_name: str = "Admin",
reset_passphrase: str | None = None, reset_passphrase: str | None = None,
reset_expiry: datetime | None = None, reset_expiry: datetime | None = None,
config: Config | None = None,
) -> str: ) -> str:
"""Bootstrap the entire system in a single transaction. """Bootstrap the entire system in a single transaction.
@@ -732,6 +733,7 @@ def bootstrap(
- Organization with Administration role - Organization with Administration role
- Admin user with Administration role - Admin user with Administration role
- Reset token for admin registration - Reset token for admin registration
- Config (if provided)
This is the only way to create a new database file. This is the only way to create a new database file.
All data is created atomically - if any step fails, nothing is written. All data is created atomically - if any step fails, nothing is written.
@@ -741,6 +743,7 @@ def bootstrap(
admin_name: Display name for the admin user (default: "Admin") admin_name: Display name for the admin user (default: "Admin")
reset_passphrase: Passphrase for the reset token (generated if not provided) reset_passphrase: Passphrase for the reset token (generated if not provided)
reset_expiry: Expiry datetime for the reset token (default: 14 days) reset_expiry: Expiry datetime for the reset token (default: 14 days)
config: Configuration to store (rp_id, rp_name, origins, etc.)
Returns: Returns:
The reset passphrase for admin registration. The reset passphrase for admin registration.
@@ -821,6 +824,10 @@ def bootstrap(
) )
_db.reset_tokens[reset_token.key] = reset_token _db.reset_tokens[reset_token.key] = reset_token
# Set config if provided
if config is not None:
_db.config = config
return reset_passphrase return reset_passphrase
+13 -13
View File
@@ -199,16 +199,14 @@ def main():
startupbox.print_startup_config(config) startupbox.print_startup_config(config)
# Build config to save (will be saved after bootstrap in async_main) # Build config to save (for bootstrap or explicit --save)
save_config = None cli_config = Config(
if args.save: rp_id=args.rp_id,
save_config = Config( rp_name=args.rp_name,
rp_id=args.rp_id, origins=args.origins,
rp_name=args.rp_name, auth_host=args.auth_host,
origins=args.origins, listen=args.listen,
auth_host=args.auth_host, )
listen=args.listen,
)
run_kwargs: dict = { run_kwargs: dict = {
"log_level": "warning", # Suppress startup messages; we use custom logging "log_level": "warning", # Suppress startup messages; we use custom logging
@@ -230,9 +228,11 @@ def main():
origins=config.origins, origins=config.origins,
bootstrap=False, bootstrap=False,
) )
await bootstrap_if_needed() # Pass config to bootstrap - it will be saved within the bootstrap transaction
if save_config is not None: await bootstrap_if_needed(config=cli_config)
await set_config(save_config) # Also save config if --save was explicitly used (even without bootstrap)
if args.save:
await set_config(cli_config)
await flush() await flush()
if len(endpoints) > 1: if len(endpoints) > 1: