Compare commits
No commits in common. "main" and "v0.2.0" have entirely different histories.
5 changed files with 4 additions and 101 deletions
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"id": "chatelet",
|
||||
"version": "0.3.0",
|
||||
"version": "0.2.0",
|
||||
"name": "Chatelet",
|
||||
"repo": "https://git.atitlan.io/aiolabs/chatelet",
|
||||
"short_description": "Nostr-native room rentals (Airbnb-style) for LNbits",
|
||||
|
|
|
|||
12
models.py
12
models.py
|
|
@ -17,7 +17,6 @@ Design notes carried into the field definitions:
|
|||
arbiter of "is this range open" — Nostr events are requests, not locks.
|
||||
"""
|
||||
|
||||
import json
|
||||
from datetime import datetime, timezone
|
||||
from enum import Enum
|
||||
|
||||
|
|
@ -195,17 +194,6 @@ class Block(BaseModel):
|
|||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def public_room_dict(room: Room) -> dict:
|
||||
"""A Room as public JSON for guests — strips operator-private fields: the
|
||||
wallet id, and the check-in instructions (address/gate code, delivered only
|
||||
in the encrypted post-payment DM). Shared by the HTTP and Nostr-RPC guest
|
||||
doors so neither can leak them."""
|
||||
d = json.loads(room.json())
|
||||
d.pop("wallet", None)
|
||||
d.pop("checkin_instructions", None)
|
||||
return d
|
||||
|
||||
|
||||
class AvailabilityQuery(BaseModel):
|
||||
room_id: str
|
||||
check_in: str # YYYY-MM-DD inclusive
|
||||
|
|
|
|||
|
|
@ -1,60 +0,0 @@
|
|||
"""Public guest discovery endpoints + the operator-private field strip
|
||||
(privacy: check-in instructions must never reach a guest)."""
|
||||
|
||||
import asyncio
|
||||
|
||||
import pytest
|
||||
from fastapi import HTTPException
|
||||
|
||||
from .. import crud, views_api
|
||||
from ..models import RoomStatus, public_room_dict
|
||||
from .conftest import make_room
|
||||
|
||||
|
||||
def test_public_room_dict_strips_private_fields():
|
||||
room = make_room(wallet="w1")
|
||||
room.checkin_instructions = "gate code 4213, door on the left"
|
||||
d = public_room_dict(room)
|
||||
assert "wallet" not in d # operator-internal
|
||||
assert "checkin_instructions" not in d # private, DM-only
|
||||
assert d["title"] == "Tower Room" # public fields survive
|
||||
assert d["price_amount"] == 100
|
||||
|
||||
|
||||
def test_public_rooms_lists_active_only_and_stripped(monkeypatch):
|
||||
active = make_room("a", status=RoomStatus.active)
|
||||
active.checkin_instructions = "secret"
|
||||
inactive = make_room("b", status=RoomStatus.inactive)
|
||||
|
||||
async def gr():
|
||||
return [active, inactive]
|
||||
|
||||
monkeypatch.setattr(crud, "get_rooms", gr)
|
||||
out = asyncio.run(views_api.api_public_rooms())
|
||||
assert [r["id"] for r in out] == ["a"] # inactive hidden from guests
|
||||
assert "checkin_instructions" not in out[0]
|
||||
assert "wallet" not in out[0]
|
||||
|
||||
|
||||
def test_public_room_404_when_inactive(monkeypatch):
|
||||
async def gr(_):
|
||||
return make_room(status=RoomStatus.inactive)
|
||||
|
||||
monkeypatch.setattr(crud, "get_room", gr)
|
||||
with pytest.raises(HTTPException) as e:
|
||||
asyncio.run(views_api.api_public_room("x"))
|
||||
assert e.value.status_code == 404
|
||||
|
||||
|
||||
def test_public_room_returns_stripped_when_active(monkeypatch):
|
||||
room = make_room("a", status=RoomStatus.active)
|
||||
room.checkin_instructions = "gate"
|
||||
|
||||
async def gr(_):
|
||||
return room
|
||||
|
||||
monkeypatch.setattr(crud, "get_room", gr)
|
||||
out = asyncio.run(views_api.api_public_room("a"))
|
||||
assert out["id"] == "a"
|
||||
assert "checkin_instructions" not in out
|
||||
assert "wallet" not in out
|
||||
|
|
@ -196,9 +196,6 @@ def _to_dict(obj) -> dict:
|
|||
|
||||
|
||||
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)
|
||||
d = _to_dict(room)
|
||||
d.pop("wallet", None) # wallet id is operator-internal, not for guests
|
||||
return d
|
||||
|
|
|
|||
22
views_api.py
22
views_api.py
|
|
@ -24,7 +24,6 @@ from .models import (
|
|||
CreateRoomData,
|
||||
Room,
|
||||
RoomStatus,
|
||||
public_room_dict,
|
||||
)
|
||||
from .nostr import service as nostr
|
||||
|
||||
|
|
@ -190,27 +189,6 @@ async def api_update_settings(
|
|||
return await crud.update_settings(settings)
|
||||
|
||||
|
||||
# --- public guest discovery (no auth) --------------------------------------
|
||||
|
||||
|
||||
@chatelet_api_router.get("/api/v1/public/rooms")
|
||||
async def api_public_rooms() -> list[dict]:
|
||||
"""Active rooms for guest browsing — operator-private fields stripped."""
|
||||
return [
|
||||
public_room_dict(r)
|
||||
for r in await crud.get_rooms()
|
||||
if r.status == RoomStatus.active
|
||||
]
|
||||
|
||||
|
||||
@chatelet_api_router.get("/api/v1/public/rooms/{room_id}")
|
||||
async def api_public_room(room_id: str) -> dict:
|
||||
room = await crud.get_room(room_id)
|
||||
if not room or room.status != RoomStatus.active:
|
||||
raise HTTPException(404, "Room not available")
|
||||
return public_room_dict(room)
|
||||
|
||||
|
||||
# --- availability (public read) --------------------------------------------
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue