1
0
Fork 0
forked from aiolabs/libra

Rename Castle Accounting extension to Libra

Full identifier rename: module path lnbits.extensions.castle →
lnbits.extensions.libra, DB ext_castle → ext_libra, URL prefix
/castle/ → /libra/, manifest id castle → libra, fava ledger slug
default castle-ledger → libra-ledger, Beancount source metadata
castle-api → libra-api and link prefixes castle-{entry,tx}- →
libra-{entry,tx}-, column castle_wallet_id → libra_wallet_id, all
Python/JS/HTML identifiers (castle_ext, CastleSettings,
castle_reference, castleWalletConfigured, etc.).

Display name "Castle Accounting" → "Libra" (the scales/balance
metaphor — fits double-entry bookkeeping).

No backward compat: production hosts will be force-updated. Old
castle-prefixed Beancount metadata in existing Fava ledgers is
historical; new entries use libra-* prefixes going forward.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Padreug 2026-05-05 10:24:46 +02:00
commit c174cda48d
44 changed files with 953 additions and 953 deletions

64
crud.py
View file

@ -14,7 +14,7 @@ from .models import (
AssertionStatus,
AssignUserRole,
BalanceAssertion,
CastleSettings,
LibraSettings,
CreateAccount,
CreateAccountPermission,
CreateBalanceAssertion,
@ -32,7 +32,7 @@ from .models import (
StoredUserWalletSettings,
UpdateRole,
UserBalance,
UserCastleSettings,
UserLibraSettings,
UserEquityStatus,
UserRole,
UserWalletSettings,
@ -49,7 +49,7 @@ from .core.validation import (
validate_payment_entry,
)
db = Database("ext_castle")
db = Database("ext_libra")
# ===== CACHING =====
# Cache for account and permission lookups to reduce DB queries
@ -197,7 +197,7 @@ async def get_or_create_user_account(
Get or create a user-specific account with hierarchical naming.
This function checks if the account exists in Fava/Beancount and creates it
if it doesn't exist. The account is also registered in Castle's database for
if it doesn't exist. The account is also registered in Libra's database for
metadata tracking (permissions, descriptions, etc.).
Examples:
@ -214,7 +214,7 @@ async def get_or_create_user_account(
# Generate hierarchical account name
account_name = format_hierarchical_account_name(account_type, base_name, user_id)
# Try to find existing account with this hierarchical name in Castle DB
# Try to find existing account with this hierarchical name in Libra DB
account = await db.fetchone(
"""
SELECT * FROM accounts
@ -224,9 +224,9 @@ async def get_or_create_user_account(
Account,
)
logger.info(f"[ACCOUNT CHECK] User {user_id[:8]}, Account: {account_name}, In Castle DB: {account is not None}")
logger.info(f"[ACCOUNT CHECK] User {user_id[:8]}, Account: {account_name}, In Libra DB: {account is not None}")
# Always check/create in Fava, even if account exists in Castle DB
# Always check/create in Fava, even if account exists in Libra DB
# This ensures Beancount has the Open directive
fava_account_exists = False
if True: # Always check Fava
@ -262,23 +262,23 @@ async def get_or_create_user_account(
except Exception as e:
logger.error(f"[FAVA ERROR] Could not check/create account in Fava: {e}", exc_info=True)
# Continue anyway - account creation in Castle DB is still useful for metadata
# Continue anyway - account creation in Libra DB is still useful for metadata
# Ensure account exists in Castle DB (sync from Beancount if needed)
# Ensure account exists in Libra DB (sync from Beancount if needed)
# This uses the account sync module for consistency
if not account:
logger.info(f"[CASTLE DB] Syncing account from Beancount to Castle DB: {account_name}")
logger.info(f"[LIBRA DB] Syncing account from Beancount to Libra DB: {account_name}")
from .account_sync import sync_single_account_from_beancount
# Sync from Beancount to Castle DB
# Sync from Beancount to Libra DB
created = await sync_single_account_from_beancount(account_name)
if created:
logger.info(f"[CASTLE DB] Account synced from Beancount: {account_name}")
logger.info(f"[LIBRA DB] Account synced from Beancount: {account_name}")
else:
logger.warning(f"[CASTLE DB] Failed to sync account from Beancount: {account_name}")
logger.warning(f"[LIBRA DB] Failed to sync account from Beancount: {account_name}")
# Fetch the account from Castle DB
# Fetch the account from Libra DB
account = await db.fetchone(
"""
SELECT * FROM accounts
@ -289,9 +289,9 @@ async def get_or_create_user_account(
)
if not account:
logger.error(f"[CASTLE DB] Account still not found after sync: {account_name}")
# Fallback: create directly in Castle DB if sync failed
logger.info(f"[CASTLE DB] Creating account directly in Castle DB: {account_name}")
logger.error(f"[LIBRA DB] Account still not found after sync: {account_name}")
# Fallback: create directly in Libra DB if sync failed
logger.info(f"[LIBRA DB] Creating account directly in Libra DB: {account_name}")
try:
account = await create_account(
CreateAccount(
@ -304,7 +304,7 @@ async def get_or_create_user_account(
except Exception as e:
# Handle UNIQUE constraint error - account already exists
if "UNIQUE constraint failed" in str(e) and "accounts.name" in str(e):
logger.warning(f"[CASTLE DB] Account already exists (UNIQUE constraint), fetching by name: {account_name}")
logger.warning(f"[LIBRA DB] Account already exists (UNIQUE constraint), fetching by name: {account_name}")
# Fetch existing account by name only (ignore user_id in query)
account = await db.fetchone(
"""
@ -315,10 +315,10 @@ async def get_or_create_user_account(
Account,
)
if account:
logger.info(f"[CASTLE DB] Found existing account: {account_name} (user_id: {account.user_id})")
logger.info(f"[LIBRA DB] Found existing account: {account_name} (user_id: {account.user_id})")
# Update user_id if it's NULL or different
if account.user_id != user_id:
logger.info(f"[CASTLE DB] Updating account user_id from {account.user_id} to {user_id}")
logger.info(f"[LIBRA DB] Updating account user_id from {account.user_id} to {user_id}")
await db.execute(
"""
UPDATE accounts
@ -340,7 +340,7 @@ async def get_or_create_user_account(
# Re-raise if it's a different error
raise
else:
logger.info(f"[CASTLE DB] Account already exists in Castle DB: {account_name}")
logger.info(f"[LIBRA DB] Account already exists in Libra DB: {account_name}")
return account
@ -351,7 +351,7 @@ async def get_or_create_user_account(
# ===== JOURNAL ENTRY OPERATIONS (REMOVED) =====
#
# All journal entry operations have been moved to Fava/Beancount.
# Castle no longer maintains its own journal_entries and entry_lines tables.
# Libra no longer maintains its own journal_entries and entry_lines tables.
#
# For journal entry operations, see:
# - views_api.py: api_create_journal_entry() - writes to Fava via FavaClient
@ -375,29 +375,29 @@ async def get_or_create_user_account(
# ===== SETTINGS =====
async def create_castle_settings(
user_id: str, data: CastleSettings
) -> CastleSettings:
settings = UserCastleSettings(**data.dict(), id=user_id)
async def create_libra_settings(
user_id: str, data: LibraSettings
) -> LibraSettings:
settings = UserLibraSettings(**data.dict(), id=user_id)
await db.insert("extension_settings", settings)
return settings
async def get_castle_settings(user_id: str) -> Optional[CastleSettings]:
async def get_libra_settings(user_id: str) -> Optional[LibraSettings]:
return await db.fetchone(
"""
SELECT * FROM extension_settings
WHERE id = :user_id
""",
{"user_id": user_id},
CastleSettings,
LibraSettings,
)
async def update_castle_settings(
user_id: str, data: CastleSettings
) -> CastleSettings:
settings = UserCastleSettings(**data.dict(), id=user_id)
async def update_libra_settings(
user_id: str, data: LibraSettings
) -> LibraSettings:
settings = UserLibraSettings(**data.dict(), id=user_id)
await db.update("extension_settings", settings)
return settings