1
+ from fasthtml .common import *
2
+ from fasthtml .jupyter import *
3
+ from functools import partial
4
+ from dataclasses import dataclass
5
+ from hmac import compare_digest
6
+ from monsterui .all import *
7
+
8
+ db = database (':memory:' )
9
+
10
+ class User : name :str ; pwd :str
11
+
12
+ class Todo :
13
+ id :int ; title :str ; done :bool ; name :str ; details :str ; priority :int
14
+
15
+ db .users = db .create (User , transform = True , pk = 'name' )
16
+ db .todos = db .create (Todo , transform = True )
17
+
18
+
19
+ def user_auth_before (req , sess ):
20
+ auth = req .scope ['auth' ] = sess .get ('auth' , None )
21
+ if not auth : return login_redir
22
+ db .todos .xtra (name = auth )
23
+
24
+ beforeware = Beforeware (
25
+ user_auth_before ,
26
+ skip = [r'/favicon\.ico' , r'/static/.*' , r'.*\.css' , r'.*\.js' , '/login' ]
27
+ )
28
+
29
+
30
+ app , rt = fast_app (hdrs = Theme .blue .headers (),before = beforeware )
31
+
32
+ login_redir = Redirect ('/login' )
33
+
34
+
35
+ @rt
36
+ def index (auth ):
37
+ top = Grid (Div (A ('logout' , href = logout ), style = 'text-align: right' ))
38
+ new_inp = Input (id = "new-title" , name = "title" , placeholder = "New Todo" )
39
+ add = Form (Group (new_inp , Button ("Add" )),
40
+ hx_post = "/" , target_id = 'todo-list' , hx_swap = "afterbegin" )
41
+ frm = Form (* db .todos (order_by = 'priority' ),
42
+ id = 'todo-list' , cls = 'sortable' , hx_post = "/reorder" , hx_trigger = "end" )
43
+
44
+ card = Card (Ul (frm ), header = add , footer = Div (id = 'current-todo' ))
45
+ return Titled (f"{ auth } 's Todo list" , Container (top , card ))
46
+
47
+
48
+ @rt ('/login' )
49
+ def get ():
50
+ frm = Form (
51
+ LabelInput ("Name" , name = 'name' ),
52
+ LabelInput ("Password" , name = 'pwd' ),
53
+ Button ('login' ),
54
+ action = '/login' , method = 'post' )
55
+ return Titled ("Login" , frm , cls = ContainerT .sm )
56
+
57
+ @dataclass
58
+ class Login : name :str ; pwd :str
59
+
60
+ @rt ("/login" )
61
+ def post (login :Login , sess ):
62
+ if not login .name or not login .pwd : return login_redir
63
+ try : u = db .users [login .name ]
64
+ except NotFoundError : u = db .users .insert (login )
65
+ if not compare_digest (u .pwd .encode ("utf-8" ), login .pwd .encode ("utf-8" )): return login_redir
66
+ sess ['auth' ] = u .name
67
+ return Redirect ('/' )
68
+
69
+ @app .get ("/logout" )
70
+ def logout (sess ):
71
+ del sess ['auth' ]
72
+ return login_redir
73
+
74
+ serve ()
0 commit comments