feat: per-operator settings — house rules + card acceptance (multi-tenant)
Chatelet is multi-tenant: any LNbits user can host rooms. What an operator decides for all their rooms now lives in chatelet.operator_settings, keyed by user id and created lazily (m003, which also indexes bookings by guest): check-in/out times, cancellation policy, and accept_fiat. Guests see it: the public room view (both doors) gains house_rules and payment_methods, and the kind:30402 listing carries payment_methods, checkin_time and checkout_time tags so a generic Nostr client can render the right pay buttons and rules without our RPC. The check-in DM reads the room owner's rules instead of the instance row. Card is offered only when the operator opted in, the room is fiat-priced, and LNbits core has a fiat provider for that user — resolved through settings.get_fiat_providers_for_user(owner), the one seam lnbits#67's per-user Stripe credentials will plug into; chatelet never sees creds. Operator endpoints: GET/PUT /api/v1/operator (admin key → wallet user) and RPC twins chatelet_operator_get/update (AUTH_WALLET); saving re-publishes the owner's active listings. Admin UI moves the house-rule inputs into a per-operator card with the card toggle and a provider hint. The old house-rule columns on settings stay for old rows but are no longer read. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
feaaef93e3
commit
9ab52f2c69
17 changed files with 531 additions and 42 deletions
|
|
@ -30,7 +30,13 @@ from lnbits.core.models.wallets import WalletTypeInfo
|
|||
from lnbits.core.services.nostr_transport.models import NostrRpcRequest
|
||||
|
||||
from . import crud, services
|
||||
from .models import BookingRequestData, CreateBlockData, CreateRoomData, RoomStatus
|
||||
from .models import (
|
||||
BookingRequestData,
|
||||
CreateBlockData,
|
||||
CreateRoomData,
|
||||
RoomStatus,
|
||||
UpdateOperatorSettings,
|
||||
)
|
||||
|
||||
# Fields a client may patch on a room via chatelet_room_update. Identity /
|
||||
# counter fields (id, wallet, listing_event_id, created_at) are not mutable;
|
||||
|
|
@ -99,20 +105,36 @@ async def handle_block_create(auth: WalletTypeInfo, request: NostrRpcRequest) ->
|
|||
return _to_dict(block)
|
||||
|
||||
|
||||
async def handle_operator_get(auth: WalletTypeInfo, request: NostrRpcRequest) -> dict:
|
||||
ops = await crud.get_or_create_operator_settings(auth.wallet.user)
|
||||
return _to_dict(ops)
|
||||
|
||||
|
||||
async def handle_operator_update(
|
||||
auth: WalletTypeInfo, request: NostrRpcRequest
|
||||
) -> dict:
|
||||
ops = await crud.get_or_create_operator_settings(auth.wallet.user)
|
||||
for k, v in (request.body or {}).items():
|
||||
if k in UpdateOperatorSettings.__fields__:
|
||||
setattr(ops, k, v)
|
||||
return _to_dict(await crud.update_operator_settings(ops))
|
||||
|
||||
|
||||
# --- public: discovery + booking (AUTH_NONE) -------------------------------
|
||||
|
||||
|
||||
async def handle_room_list(auth: None, request: NostrRpcRequest) -> list[dict]:
|
||||
"""Active rooms only, wallet id stripped (public discovery)."""
|
||||
rooms = await crud.get_rooms()
|
||||
return [_public_room(r) for r in rooms if r.status == RoomStatus.active]
|
||||
"""Active rooms only, wallet id stripped, owner's house rules + rails
|
||||
attached (public discovery) — same view as the HTTP door."""
|
||||
rooms = [r for r in await crud.get_rooms() if r.status == RoomStatus.active]
|
||||
return await services.public_room_views(rooms)
|
||||
|
||||
|
||||
async def handle_room_get(auth: None, request: NostrRpcRequest) -> dict:
|
||||
room = await crud.get_room(_require_id(request))
|
||||
if not room or room.status != RoomStatus.active:
|
||||
raise ValueError("Room not available")
|
||||
return _public_room(room)
|
||||
return await services.public_room_view(room)
|
||||
|
||||
|
||||
async def handle_room_unavailable(auth: None, request: NostrRpcRequest) -> dict:
|
||||
|
|
@ -204,11 +226,3 @@ async def _require_owned_room(room_id: str, wallet_id: str):
|
|||
def _to_dict(obj) -> dict:
|
||||
return json.loads(obj.json())
|
||||
|
||||
|
||||
def _public_room(room) -> dict:
|
||||
# Shared with the HTTP door; strips wallet id AND checkin_instructions
|
||||
# (the latter was leaking to guests before — added after this file's
|
||||
# original public dict).
|
||||
from .models import public_room_dict
|
||||
|
||||
return public_room_dict(room)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue