-
Notifications
You must be signed in to change notification settings - Fork 4
/
server.py
126 lines (109 loc) · 4.05 KB
/
server.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import asyncio
import os
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.middleware.authentication import AuthenticationMiddleware
from starlette.middleware.cors import CORSMiddleware
from starlette.responses import Response, StreamingResponse
from starlette.routing import Route
from jmap.api import api, CAPABILITIES, JSONResponse
from user import BasicAuthBackend
BASEURL = os.getenv('BASEURL', 'http://127.0.0.1:8888')
async def event_stream(request, types, closeafter, ping):
while True:
if await request.is_disconnected():
break
if ping:
yield 'event: ping\ndata: {"interval":%d}\n\n' % ping
await asyncio.sleep(ping or 10)
async def event(request):
try:
types = request.query_params['types'].split(',')
except KeyError:
types = ['*']
# return Response('types param required', status_code=400)
try:
closeafter = request.query_params['closeafter']
except KeyError:
closeafter = ''
# return Response('closeafter param required', status_code=400)
try:
ping = int(request.query_params['ping'])
except (KeyError, ValueError):
ping = 5
# return Response('ping param required', status_code=400)
return StreamingResponse(
event_stream(request, types, closeafter, ping),
200,
media_type='text/event-stream',
)
async def upload(request):
user = request['user']
try:
accountId = request.path_params['accountId']
account = user.accounts[accountId]
except KeyError:
return Response('No access to this accountId', 403)
res = account.upload(request.stream(), request.headers['content-type'])
return JSONResponse(res)
async def download(request):
user = request['user']
try:
accountId = request.path_params['accountId']
account = user.accounts[accountId]
except KeyError:
return Response('No access to this accountId', 403)
blobId = request.path_params['blobId']
try:
body = account.download(blobId)
except Exception as e:
return Response(str(e), 404)
name = request.path_params['name']
headers = {
'content-disposition': 'attachment; name=' + name
}
if 'type' in request.query_params:
headers['content-type'] = request.query_params['type']
return Response(body, 200, headers=headers)
async def well_known_jmap(request):
res = {
"capabilities": {name: module.capability
for name, module in CAPABILITIES.items()},
"username": request.user.username,
"accounts": {
account.id: {
"name": account.name,
"isPersonal": account.is_personal,
"isArchiveUser": False,
"accountCapabilities": account.capabilities,
"isReadOnly": False
} for account in request.user.accounts.values()
},
"primaryAccounts": {
"urn:ietf:params:jmap:mail": request.user.username,
"urn:ietf:params:jmap:submission": request.user.username,
"urn:ietf:params:jmap:vacationresponse": request.user.username,
},
"state": "0",
"apiUrl": BASEURL + "/api/",
"downloadUrl": BASEURL + "/download/{accountId}/{blobId}/{name}?type={type}",
"uploadUrl": BASEURL + "/upload/{accountId}/",
"eventSourceUrl": BASEURL + "/event/"#?types={types}&closeafter={closeafter}&ping={ping}",
}
return JSONResponse(res)
routes = [
Route('/api/', api, methods=["POST", "GET"]),
Route('/event/', event),
Route('/upload/{accountId}', upload, methods=["POST"]),
Route('/download/{accountId}/{blobId}/{name}', download),
Route('/.well-known/jmap', well_known_jmap),
]
middleware = [
Middleware(CORSMiddleware, allow_origins=['*'], allow_headers=['authorization'], allow_methods=['*']),
Middleware(AuthenticationMiddleware, backend=BasicAuthBackend()),
]
app = Starlette(
debug=True,
routes=routes,
middleware=middleware,
)