1
1
"""Simplest example."""
2
2
3
+ import asyncio
4
+ import datetime
3
5
import os
4
6
from contextlib import asynccontextmanager
5
7
from pathlib import Path
8
+ from typing import Annotated
6
9
7
- from fastapi import FastAPI
10
+ from fastapi import Depends , FastAPI , Request , WebSocket
11
+ from fastapi .responses import HTMLResponse
8
12
from 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
10
14
11
15
12
16
@asynccontextmanager
@@ -18,11 +22,113 @@ async def lifespan(app: FastAPI):
18
22
APP = FastAPI (lifespan = lifespan )
19
23
APP .add_middleware (AppAPIAuthMiddleware ) # set global AppAPI authentication middleware
20
24
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 ):
81
+ print (f"user_get: { request .headers } " , flush = True )
82
+ return "Page for the registered users only!"
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
+
21
127
22
128
def enabled_handler (enabled : bool , nc : NextcloudApp ) -> str :
23
129
# This will be called each time application is `enabled` or `disabled`
24
130
# 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 )
26
132
if enabled :
27
133
nc .log (LogLvl .WARNING , f"Hello from { nc .app_cfg .app_name } :)" )
28
134
else :
0 commit comments