Compare commits
No commits in common. "d521782f19a12159a237ebf4936871af484dab54" and "7027b1ca6ac2877e34e54cede72b2bbf13efef3c" have entirely different histories.
d521782f19
...
7027b1ca6a
5 changed files with 4 additions and 101 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"id": "chatelet",
|
"id": "chatelet",
|
||||||
"version": "0.3.0",
|
"version": "0.2.0",
|
||||||
"name": "Chatelet",
|
"name": "Chatelet",
|
||||||
"repo": "https://git.atitlan.io/aiolabs/chatelet",
|
"repo": "https://git.atitlan.io/aiolabs/chatelet",
|
||||||
"short_description": "Nostr-native room rentals (Airbnb-style) for LNbits",
|
"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.
|
arbiter of "is this range open" — Nostr events are requests, not locks.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from enum import Enum
|
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):
|
class AvailabilityQuery(BaseModel):
|
||||||
room_id: str
|
room_id: str
|
||||||
check_in: str # YYYY-MM-DD inclusive
|
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:
|
def _public_room(room) -> dict:
|
||||||
# Shared with the HTTP door; strips wallet id AND checkin_instructions
|
d = _to_dict(room)
|
||||||
# (the latter was leaking to guests before — added after this file's
|
d.pop("wallet", None) # wallet id is operator-internal, not for guests
|
||||||
# original public dict).
|
return d
|
||||||
from .models import public_room_dict
|
|
||||||
|
|
||||||
return public_room_dict(room)
|
|
||||||
|
|
|
||||||
22
views_api.py
22
views_api.py
|
|
@ -24,7 +24,6 @@ from .models import (
|
||||||
CreateRoomData,
|
CreateRoomData,
|
||||||
Room,
|
Room,
|
||||||
RoomStatus,
|
RoomStatus,
|
||||||
public_room_dict,
|
|
||||||
)
|
)
|
||||||
from .nostr import service as nostr
|
from .nostr import service as nostr
|
||||||
|
|
||||||
|
|
@ -190,27 +189,6 @@ async def api_update_settings(
|
||||||
return await crud.update_settings(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) --------------------------------------------
|
# --- availability (public read) --------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue