chore: hygiene sweep — dead code, role race, user lookup, stale files
One pass over the LOW-tier review items plus two folded issues: - Delete validate_journal_entry (dead since the Fava migration; it validated the pre-string-amount model) with its exports, unused crud imports, and tests. Beancount validates entries now. - Migration m006: UNIQUE index on user_roles(user_id, role_id) after deduping; assign_user_role inserts with ON CONFLICT DO NOTHING and returns the existing assignment — closes the auto-assign check-then-act race on concurrent logins. - Extract _get_username_from_user_id (110 lines in views_api, fresh LNbits Database per call inside per-row hot paths) into user_lookup.py with one shared core-DB handle, a 60s TTL cache and a batch get_usernames API (review #18). - Receivable-entry responses report CLEARED, matching the flag the formatter actually writes; PENDING misled the UI (libra-#35). - Replace the remaining print() calls in tasks.py with logger. - get_all_accounts derives valid roots from account_utils.ACCOUNT_TYPE_ROOTS instead of a hardcoded tuple, and the no-op per-test rate-limit reset is gone (libra-#54). - Delete migrations_old.py.bak, MIGRATION_SQUASH_SUMMARY.md, docs/PHASE*_COMPLETE.md and the rendered .html; .gitignore data/ (it holds the runtime .lnbits_auth_key secret). - Track docs/CODE-REVIEW-2026-06.md with finding statuses updated for the PR #55-#59 + chore/hygiene series. - CLAUDE.md notes LNbits pins Pydantic v1: keep .dict(), don't "modernize" to .model_dump(). Note: format_payment_entry's is_payable docstring (flagged in review follow-up) turned out to be consistent with the body — no change. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
c0d371036b
commit
ec6cac51f0
19 changed files with 454 additions and 2942 deletions
124
views_api.py
124
views_api.py
|
|
@ -15,6 +15,7 @@ from lnbits.utils.exchange_rates import allowed_currencies, fiat_amount_as_satos
|
|||
|
||||
from .account_utils import VALID_ACCOUNT_PREFIXES, validate_account_name
|
||||
from .beancount_format import fiat_rate_metadata
|
||||
from .user_lookup import get_username
|
||||
from .crud import (
|
||||
approve_manual_payment_request,
|
||||
check_balance_assertion,
|
||||
|
|
@ -643,7 +644,7 @@ async def api_get_user_entries(
|
|||
break
|
||||
|
||||
# Look up actual username using helper function
|
||||
username = await _get_username_from_user_id(user_id_match) if user_id_match else None
|
||||
username = await get_username(user_id_match) if user_id_match else None
|
||||
|
||||
entry_data = {
|
||||
"id": entry_id or e.get("entry_hash", "unknown"),
|
||||
|
|
@ -684,119 +685,6 @@ async def api_get_user_entries(
|
|||
}
|
||||
|
||||
|
||||
async def _get_username_from_user_id(user_id: str) -> str:
|
||||
"""
|
||||
Helper function to get username from user_id, handling various formats.
|
||||
|
||||
Supports:
|
||||
- Full UUID with dashes (36 chars): "375ec158-686c-4a21-b44d-a51cc90ef07d"
|
||||
- Dashless UUID (32 chars): "375ec158686c4a21b44da51cc90ef07d"
|
||||
- Partial ID (8 chars from account names): "375ec158"
|
||||
|
||||
Returns username or formatted fallback.
|
||||
"""
|
||||
from lnbits.core.crud.users import get_user
|
||||
|
||||
logger.debug(f"[USERNAME] Called with: '{user_id}' (len={len(user_id) if user_id else 0})")
|
||||
|
||||
if not user_id:
|
||||
return None
|
||||
|
||||
# Case 1: Already in standard UUID format (36 chars with dashes)
|
||||
if len(user_id) == 36 and user_id.count('-') == 4:
|
||||
logger.debug(f"[USERNAME] Case 1: Full UUID format")
|
||||
user = await get_user(user_id)
|
||||
result = user.username if user and user.username else f"User-{user_id[:8]}"
|
||||
logger.debug(f"[USERNAME] Case 1 result: '{result}'")
|
||||
return result
|
||||
|
||||
# Case 2: Dashless 32-char UUID - lookup via Libra user settings, fallback to LNbits
|
||||
elif len(user_id) == 32 and '-' not in user_id:
|
||||
logger.debug(f"[USERNAME] Case 2: Dashless UUID format - looking up in Libra user settings")
|
||||
try:
|
||||
# Convert dashless to dashed format
|
||||
user_id_with_dashes = f"{user_id[0:8]}-{user_id[8:12]}-{user_id[12:16]}-{user_id[16:20]}-{user_id[20:32]}"
|
||||
logger.debug(f"[USERNAME] Converted to dashed format: {user_id_with_dashes}")
|
||||
|
||||
# Try Libra settings first
|
||||
user_settings = await get_all_user_wallet_settings()
|
||||
for setting in user_settings:
|
||||
if setting.id == user_id_with_dashes:
|
||||
logger.debug(f"[USERNAME] Found matching user in Libra settings")
|
||||
user = await get_user(setting.id)
|
||||
result = user.username if user and user.username else f"User-{user_id[:8]}"
|
||||
logger.debug(f"[USERNAME] Case 2 result (from Libra): '{result}'")
|
||||
return result
|
||||
|
||||
# Not in Libra settings - try LNbits database directly
|
||||
logger.debug(f"[USERNAME] Not in Libra settings, querying LNbits database directly")
|
||||
from lnbits.db import Database
|
||||
db = Database("database")
|
||||
async with db.connect() as conn:
|
||||
row = await conn.fetchone(
|
||||
"SELECT id, username FROM accounts WHERE id = :user_id LIMIT 1",
|
||||
{"user_id": user_id_with_dashes}
|
||||
)
|
||||
logger.debug(f"[USERNAME] Database query result: {row}")
|
||||
if row and row["username"]:
|
||||
result = row["username"]
|
||||
logger.debug(f"[USERNAME] Case 2 result (from LNbits DB): '{result}'")
|
||||
return result
|
||||
|
||||
# User doesn't exist anywhere
|
||||
logger.debug(f"[USERNAME] User not found in LNbits database either")
|
||||
result = f"User-{user_id[:8]}"
|
||||
logger.debug(f"[USERNAME] Case 2 result (not found): '{result}'")
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error looking up user by dashless UUID {user_id}: {e}")
|
||||
result = f"User-{user_id[:8]}"
|
||||
return result
|
||||
|
||||
# Case 3: Partial ID (8 chars from account name) - lookup via Libra user settings
|
||||
elif len(user_id) == 8:
|
||||
logger.debug(f"[USERNAME] Case 3: Partial ID format - looking up in Libra user settings")
|
||||
try:
|
||||
# Get all Libra users (which have full user_ids)
|
||||
user_settings = await get_all_user_wallet_settings()
|
||||
|
||||
# Find matching user by first 8 chars
|
||||
for setting in user_settings:
|
||||
if setting.id.startswith(user_id):
|
||||
logger.debug(f"[USERNAME] Found full user_id: {setting.id}")
|
||||
# Now get username from LNbits with full ID
|
||||
user = await get_user(setting.id)
|
||||
result = user.username if user and user.username else f"User-{user_id}"
|
||||
logger.debug(f"[USERNAME] Case 3 result (found): '{result}'")
|
||||
return result
|
||||
|
||||
# No matching user found in Libra settings
|
||||
logger.debug(f"[USERNAME] No matching user found in Libra settings")
|
||||
result = f"User-{user_id}"
|
||||
logger.debug(f"[USERNAME] Case 3 result (not found): '{result}'")
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error looking up user by partial ID {user_id}: {e}")
|
||||
result = f"User-{user_id}"
|
||||
return result
|
||||
|
||||
# Case 4: Unknown format - try as-is and fall back
|
||||
else:
|
||||
logger.debug(f"[USERNAME] Case 4: Unknown format - trying as-is")
|
||||
try:
|
||||
user = await get_user(user_id)
|
||||
result = user.username if user and user.username else f"User-{user_id[:8]}"
|
||||
logger.debug(f"[USERNAME] Case 4 result: '{result}'")
|
||||
return result
|
||||
except Exception as e:
|
||||
logger.debug(f"[USERNAME] Case 4 exception: {e}")
|
||||
result = f"User-{user_id[:8]}"
|
||||
logger.debug(f"[USERNAME] Case 4 fallback result: '{result}'")
|
||||
return result
|
||||
|
||||
|
||||
@libra_api_router.get("/api/v1/entries/pending")
|
||||
async def api_get_pending_entries(
|
||||
auth: AuthContext = Depends(require_super_user),
|
||||
|
|
@ -844,7 +732,7 @@ async def api_get_pending_entries(
|
|||
break
|
||||
|
||||
# Look up username using helper function
|
||||
username = await _get_username_from_user_id(user_id) if user_id else None
|
||||
username = await get_username(user_id) if user_id else None
|
||||
|
||||
# Extract amount from postings (sum of absolute values / 2)
|
||||
amount_sats = 0
|
||||
|
|
@ -1453,7 +1341,9 @@ async def api_create_receivable_entry(
|
|||
created_by=auth.user_id,
|
||||
created_at=datetime.now(),
|
||||
reference=data.reference,
|
||||
flag=JournalEntryFlag.PENDING,
|
||||
# Receivables are written cleared (format_receivable_entry uses
|
||||
# flag="*") — reporting PENDING here misled the UI (libra-#35).
|
||||
flag=JournalEntryFlag.CLEARED,
|
||||
meta=entry_meta,
|
||||
lines=[
|
||||
EntryLine(
|
||||
|
|
@ -1682,7 +1572,7 @@ async def api_get_all_balances(
|
|||
# Enrich with username information using helper function
|
||||
result = []
|
||||
for balance in balances:
|
||||
username = await _get_username_from_user_id(balance["user_id"])
|
||||
username = await get_username(balance["user_id"])
|
||||
|
||||
result.append({
|
||||
"user_id": balance["user_id"],
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue