11"""Simplest example."""
22
3+ import asyncio
4+ import datetime
35import os
46from contextlib import asynccontextmanager
57from pathlib import Path
8+ from typing import Annotated
69
7- from fastapi import FastAPI
10+ from fastapi import Depends , FastAPI , Request , WebSocket
11+ from fastapi .responses import HTMLResponse , JSONResponse
812from nc_py_api import NextcloudApp
9- from nc_py_api .ex_app import AppAPIAuthMiddleware , LogLvl , run_app , set_handlers
13+ from nc_py_api .ex_app import AppAPIAuthMiddleware , LogLvl , nc_app , run_app , set_handlers
1014
1115
1216@asynccontextmanager
@@ -18,11 +22,113 @@ async def lifespan(app: FastAPI):
1822APP = FastAPI (lifespan = lifespan )
1923APP .add_middleware (AppAPIAuthMiddleware ) # set global AppAPI authentication middleware
2024
25+ # Build the WebSocket URL dynamically using the NextcloudApp configuration.
26+ WS_URL = NextcloudApp ().app_cfg .endpoint + "/exapps/app-skeleton-python/ws"
27+
28+ # HTML content served at the root URL.
29+ # This page opens a WebSocket connection, displays incoming messages,
30+ # and allows you to send messages back to the server.
31+ HTML = f"""
32+ <!DOCTYPE html>
33+ <html>
34+ <head>
35+ <title>FastAPI WebSocket Demo</title>
36+ </head>
37+ <body>
38+ <h1>FastAPI WebSocket Demo</h1>
39+ <p>Type a message and click "Send", or simply watch the server send cool updates!</p>
40+ <input type="text" id="messageText" placeholder="Enter message here...">
41+ <button onclick="sendMessage()">Send</button>
42+ <ul id="messages"></ul>
43+ <script>
44+ // Create a WebSocket connection using the dynamic URL.
45+ var ws = new WebSocket("{ WS_URL } ");
46+
47+ // When a message is received from the server, add it to the list.
48+ ws.onmessage = function(event) {{
49+ var messages = document.getElementById('messages');
50+ var message = document.createElement('li');
51+ message.textContent = event.data;
52+ messages.appendChild(message);
53+ }};
54+
55+ // Function to send a message to the server.
56+ function sendMessage() {{
57+ var input = document.getElementById("messageText");
58+ ws.send(input.value);
59+ input.value = '';
60+ }}
61+ </script>
62+ </body>
63+ </html>
64+ """
65+
66+
67+ @APP .get ("/" )
68+ async def get ():
69+ # WebSockets works only in Nextcloud 32 when `HaRP` is used instead of `DSP`
70+ return HTMLResponse (HTML )
71+
72+
73+ @APP .get ("/public" )
74+ async def public_get (request : Request ):
75+ print (f"public_get: { request .headers } " , flush = True )
76+ return "Public page!"
77+
78+
79+ @APP .get ("/user" )
80+ async def user_get (request : Request , status : int = 200 ):
81+ print (f"user_get: { request .headers } " , flush = True )
82+ return JSONResponse (content = "Page for the registered users only!" , status_code = status )
83+
84+
85+ @APP .get ("/admin" )
86+ async def admin_get (request : Request ):
87+ print (f"admin_get: { request .headers } " , flush = True )
88+ return "Admin page!"
89+
90+
91+ @APP .websocket ("/ws" )
92+ async def websocket_endpoint (
93+ websocket : WebSocket ,
94+ nc : Annotated [NextcloudApp , Depends (nc_app )],
95+ ):
96+ # WebSockets works only in Nextcloud 32 when `HaRP` is used instead of `DSP`
97+ print (nc .user ) # if you need user_id that initiated WebSocket connection
98+ print (f"websocket_endpoint: { websocket .headers } " , flush = True )
99+ await websocket .accept ()
100+
101+ # This background task sends a periodic message (the current time) every 2 seconds.
102+ async def send_periodic_messages ():
103+ while True :
104+ try :
105+ message = f"Server time: { datetime .datetime .now ().strftime ('%Y-%m-%d %H:%M:%S' )} "
106+ await websocket .send_text (message )
107+ await asyncio .sleep (2 )
108+ except Exception as exc :
109+ NextcloudApp ().log (LogLvl .ERROR , str (exc ))
110+ break
111+
112+ # Start the periodic sender in the background.
113+ periodic_task = asyncio .create_task (send_periodic_messages ())
114+
115+ try :
116+ # Continuously listen for messages from the client.
117+ while True :
118+ data = await websocket .receive_text ()
119+ # Echo the received message back to the client.
120+ await websocket .send_text (f"Echo: { data } " )
121+ except Exception as e :
122+ NextcloudApp ().log (LogLvl .ERROR , str (e ))
123+ finally :
124+ # Cancel the periodic message task when the connection is closed.
125+ periodic_task .cancel ()
126+
21127
22128def enabled_handler (enabled : bool , nc : NextcloudApp ) -> str :
23129 # This will be called each time application is `enabled` or `disabled`
24130 # NOTE: `user` is unavailable on this step, so all NC API calls that require it will fail as unauthorized.
25- print (f"enabled={ enabled } " )
131+ print (f"enabled={ enabled } " , flush = True )
26132 if enabled :
27133 nc .log (LogLvl .WARNING , f"Hello from { nc .app_cfg .app_name } :)" )
28134 else :
0 commit comments