feat: extension lifecycle hooks (__init__.py)

- restaurant_start spawns three permanent tasks:
    1. invoice listener (LNBits payment settlement)
    2. NostrClient bootstrap (after 10s grace for nostrclient ext)
    3. Nostr sync loop (after 15s)
- restaurant_stop cancels tasks and closes the WS.
- Module-level nostr_client = None when nostrclient unavailable;
  publishing helpers no-op gracefully in that case.
This commit is contained in:
Padreug 2026-04-29 23:34:57 +02:00
commit 3b046276f6

89
__init__.py Normal file
View file

@ -0,0 +1,89 @@
import asyncio
from fastapi import APIRouter
from loguru import logger
from .crud import db
from .tasks import wait_for_paid_invoices
from .views import restaurant_generic_router
from .views_api import restaurant_api_router
restaurant_ext: APIRouter = APIRouter(prefix="/restaurant", tags=["Restaurant"])
restaurant_ext.include_router(restaurant_generic_router)
restaurant_ext.include_router(restaurant_api_router)
restaurant_static_files = [
{
"path": "/restaurant/static",
"name": "restaurant_static",
}
]
scheduled_tasks: list[asyncio.Task] = []
# Module-level NostrClient — None when nostrclient extension is unavailable.
# Populated by the lifecycle task below.
nostr_client = None
def restaurant_stop():
for task in scheduled_tasks:
try:
task.cancel()
except Exception as ex:
logger.warning(ex)
global nostr_client
if nostr_client:
asyncio.get_event_loop().create_task(nostr_client.stop())
def restaurant_start():
from lnbits.tasks import create_permanent_unique_task
# Invoice listener — settles orders on payment, kicks off print jobs.
task1 = create_permanent_unique_task("ext_restaurant", wait_for_paid_invoices)
scheduled_tasks.append(task1)
async def _start_nostr_client():
global nostr_client
await asyncio.sleep(10) # Wait for nostrclient to be ready
try:
from .nostr.nostr_client import NostrClient
nostr_client = NostrClient()
logger.info("[RESTAURANT] Starting NostrClient for menu + order sync")
await nostr_client.run_forever()
except Exception as e:
logger.warning(f"[RESTAURANT] NostrClient failed to start: {e}")
logger.info("[RESTAURANT] Restaurant will work without Nostr layer")
task2 = create_permanent_unique_task("ext_restaurant_nostr", _start_nostr_client)
scheduled_tasks.append(task2)
async def _sync_nostr_events():
global nostr_client
await asyncio.sleep(15)
if not nostr_client:
logger.info("[RESTAURANT] No NostrClient, skipping Nostr sync")
return
try:
from .nostr_sync import wait_for_nostr_events
await wait_for_nostr_events(nostr_client)
except Exception as e:
logger.error(f"[RESTAURANT] Nostr sync task failed: {e}")
task3 = create_permanent_unique_task(
"ext_restaurant_nostr_sync", _sync_nostr_events
)
scheduled_tasks.append(task3)
__all__ = [
"db",
"restaurant_ext",
"restaurant_start",
"restaurant_static_files",
"restaurant_stop",
]