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:
parent
9c577c740c
commit
c174cda48d
44 changed files with 953 additions and 953 deletions
|
|
@ -8,9 +8,9 @@
|
|||
|
||||
## Summary
|
||||
|
||||
Implemented two major improvements for Castle administration:
|
||||
Implemented two major improvements for Libra administration:
|
||||
|
||||
1. **Account Synchronization** - Automatically sync accounts from Beancount → Castle DB
|
||||
1. **Account Synchronization** - Automatically sync accounts from Beancount → Libra DB
|
||||
2. **Bulk Permission Management** - Tools for managing permissions at scale
|
||||
|
||||
**Total Implementation Time**: ~4 hours
|
||||
|
|
@ -23,24 +23,24 @@ Implemented two major improvements for Castle administration:
|
|||
|
||||
### Problem Solved
|
||||
|
||||
**Before**: Accounts existed in both Beancount and Castle DB, with manual sync required.
|
||||
**After**: Automatic sync keeps Castle DB in sync with Beancount (source of truth).
|
||||
**Before**: Accounts existed in both Beancount and Libra DB, with manual sync required.
|
||||
**After**: Automatic sync keeps Libra DB in sync with Beancount (source of truth).
|
||||
|
||||
### Implementation
|
||||
|
||||
**New Module**: `castle/account_sync.py`
|
||||
**New Module**: `libra/account_sync.py`
|
||||
|
||||
**Core Functions**:
|
||||
|
||||
```python
|
||||
# 1. Full sync from Beancount to Castle
|
||||
# 1. Full sync from Beancount to Libra
|
||||
stats = await sync_accounts_from_beancount(force_full_sync=False)
|
||||
|
||||
# 2. Sync single account
|
||||
success = await sync_single_account_from_beancount("Expenses:Food")
|
||||
|
||||
# 3. Ensure account exists (recommended before granting permissions)
|
||||
exists = await ensure_account_exists_in_castle("Expenses:Marketing")
|
||||
exists = await ensure_account_exists_in_libra("Expenses:Marketing")
|
||||
|
||||
# 4. Scheduled background sync (run hourly)
|
||||
stats = await scheduled_account_sync()
|
||||
|
|
@ -77,7 +77,7 @@ stats = await scheduled_account_sync()
|
|||
|
||||
```python
|
||||
# Sync all accounts from Beancount
|
||||
from castle.account_sync import sync_accounts_from_beancount
|
||||
from libra.account_sync import sync_accounts_from_beancount
|
||||
|
||||
stats = await sync_accounts_from_beancount()
|
||||
|
||||
|
|
@ -96,11 +96,11 @@ Errors: 0
|
|||
#### Before Granting Permission (Best Practice)
|
||||
|
||||
```python
|
||||
from castle.account_sync import ensure_account_exists_in_castle
|
||||
from castle.crud import create_account_permission
|
||||
from libra.account_sync import ensure_account_exists_in_libra
|
||||
from libra.crud import create_account_permission
|
||||
|
||||
# Ensure account exists in Castle DB first
|
||||
account_exists = await ensure_account_exists_in_castle("Expenses:Marketing")
|
||||
# Ensure account exists in Libra DB first
|
||||
account_exists = await ensure_account_exists_in_libra("Expenses:Marketing")
|
||||
|
||||
if account_exists:
|
||||
# Now safe to grant permission
|
||||
|
|
@ -116,9 +116,9 @@ if account_exists:
|
|||
|
||||
```python
|
||||
# Add to your scheduler (cron, APScheduler, etc.)
|
||||
from castle.account_sync import scheduled_account_sync
|
||||
from libra.account_sync import scheduled_account_sync
|
||||
|
||||
# Run every hour to keep Castle DB in sync
|
||||
# Run every hour to keep Libra DB in sync
|
||||
scheduler.add_job(
|
||||
scheduled_account_sync,
|
||||
'interval',
|
||||
|
|
@ -142,7 +142,7 @@ Authorization: Bearer {admin_key}
|
|||
```json
|
||||
{
|
||||
"total_beancount_accounts": 150,
|
||||
"total_castle_accounts": 150,
|
||||
"total_libra_accounts": 150,
|
||||
"accounts_added": 2,
|
||||
"accounts_updated": 0,
|
||||
"accounts_skipped": 148,
|
||||
|
|
@ -152,8 +152,8 @@ Authorization: Bearer {admin_key}
|
|||
|
||||
### Benefits
|
||||
|
||||
1. **Beancount as Source of Truth**: Castle DB automatically reflects Beancount state
|
||||
2. **Reduced Manual Work**: No more manual account creation in Castle
|
||||
1. **Beancount as Source of Truth**: Libra DB automatically reflects Beancount state
|
||||
2. **Reduced Manual Work**: No more manual account creation in Libra
|
||||
3. **Prevents Permission Errors**: Cannot grant permission on non-existent account
|
||||
4. **Audit Trail**: Tracks which accounts were synced and when
|
||||
5. **Safe Operations**: Continues on errors, never deletes accounts
|
||||
|
|
@ -169,7 +169,7 @@ Authorization: Bearer {admin_key}
|
|||
|
||||
### Implementation
|
||||
|
||||
**New Module**: `castle/permission_management.py`
|
||||
**New Module**: `libra/permission_management.py`
|
||||
|
||||
**Core Functions**:
|
||||
|
||||
|
|
@ -471,19 +471,19 @@ print(f"Permission types removed: {result['permission_types_removed']}")
|
|||
# OLD: Manual permission creation (risky)
|
||||
await create_account_permission(
|
||||
user_id="alice",
|
||||
account_id="acc123", # What if account doesn't exist in Castle DB?
|
||||
account_id="acc123", # What if account doesn't exist in Libra DB?
|
||||
permission_type=PermissionType.SUBMIT_EXPENSE,
|
||||
granted_by="admin"
|
||||
)
|
||||
|
||||
# NEW: Safe permission creation with account sync
|
||||
from castle.account_sync import ensure_account_exists_in_castle
|
||||
from libra.account_sync import ensure_account_exists_in_libra
|
||||
|
||||
# Ensure account exists first
|
||||
account_exists = await ensure_account_exists_in_castle("Expenses:Marketing")
|
||||
account_exists = await ensure_account_exists_in_libra("Expenses:Marketing")
|
||||
|
||||
if account_exists:
|
||||
# Now safe - account guaranteed to be in Castle DB
|
||||
# Now safe - account guaranteed to be in Libra DB
|
||||
await create_account_permission(
|
||||
user_id="alice",
|
||||
account_id=account_id,
|
||||
|
|
@ -497,10 +497,10 @@ else:
|
|||
### Scheduler Integration
|
||||
|
||||
```python
|
||||
# Add to your Castle extension startup
|
||||
# Add to your Libra extension startup
|
||||
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||||
from castle.account_sync import scheduled_account_sync
|
||||
from castle.permission_management import cleanup_expired_permissions
|
||||
from libra.account_sync import scheduled_account_sync
|
||||
from libra.permission_management import cleanup_expired_permissions
|
||||
|
||||
scheduler = AsyncIOScheduler()
|
||||
|
||||
|
|
@ -610,7 +610,7 @@ async def test_copy_permissions():
|
|||
async def test_onboarding_workflow():
|
||||
"""Test complete onboarding workflow"""
|
||||
# 1. Sync account
|
||||
await ensure_account_exists_in_castle("Expenses:Food")
|
||||
await ensure_account_exists_in_libra("Expenses:Food")
|
||||
|
||||
# 2. Copy permissions from template user
|
||||
result = await copy_permissions(
|
||||
|
|
@ -745,19 +745,19 @@ logger.error(f"Account sync error: {error}")
|
|||
|
||||
## Migration Guide
|
||||
|
||||
### For Existing Castle Installations
|
||||
### For Existing Libra Installations
|
||||
|
||||
**Step 1: Deploy New Modules**
|
||||
```bash
|
||||
# Copy new files to Castle extension
|
||||
cp account_sync.py /path/to/castle/
|
||||
cp permission_management.py /path/to/castle/
|
||||
# Copy new files to Libra extension
|
||||
cp account_sync.py /path/to/libra/
|
||||
cp permission_management.py /path/to/libra/
|
||||
```
|
||||
|
||||
**Step 2: Initial Account Sync**
|
||||
```python
|
||||
# Run once to sync existing accounts
|
||||
from castle.account_sync import sync_accounts_from_beancount
|
||||
from libra.account_sync import sync_accounts_from_beancount
|
||||
|
||||
stats = await sync_accounts_from_beancount(force_full_sync=True)
|
||||
print(f"Synced {stats['accounts_added']} accounts")
|
||||
|
|
@ -784,14 +784,14 @@ await bulk_grant_permission(...)
|
|||
## Documentation Updates
|
||||
|
||||
**New files created**:
|
||||
- ✅ `castle/account_sync.py` (230 lines)
|
||||
- ✅ `castle/permission_management.py` (400 lines)
|
||||
- ✅ `libra/account_sync.py` (230 lines)
|
||||
- ✅ `libra/permission_management.py` (400 lines)
|
||||
- ✅ `docs/PERMISSIONS-SYSTEM.md` (full permission system docs)
|
||||
- ✅ `docs/ACCOUNT-SYNC-AND-PERMISSION-IMPROVEMENTS.md` (this file)
|
||||
|
||||
**Files to update**:
|
||||
- `castle/views_api.py` - Add new admin endpoints
|
||||
- `castle/README.md` - Document new features
|
||||
- `libra/views_api.py` - Add new admin endpoints
|
||||
- `libra/README.md` - Document new features
|
||||
- `tests/` - Add comprehensive tests
|
||||
|
||||
---
|
||||
|
|
@ -801,7 +801,7 @@ await bulk_grant_permission(...)
|
|||
### What Was Built
|
||||
|
||||
1. **Account Sync Module** (230 lines)
|
||||
- Automatic sync from Beancount → Castle DB
|
||||
- Automatic sync from Beancount → Libra DB
|
||||
- Type inference and user ID extraction
|
||||
- Background scheduling support
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue