jph00 commited on
Commit
8dd79ab
1 Parent(s): ab7a9b4

deploy at 2024-06-29 16:29:52.130254

Browse files
Files changed (6) hide show
  1. Dockerfile +10 -0
  2. README.md +29 -4
  3. config.ini +5 -0
  4. favicon.ico +0 -0
  5. main.py +80 -0
  6. requirements.txt +1 -0
Dockerfile ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10
2
+ WORKDIR /code
3
+ COPY --link --chown=1000 . .
4
+ RUN mkdir -p /tmp/cache/
5
+ RUN chmod a+rwx -R /tmp/cache/
6
+ ENV HF_HUB_CACHE=HF_HOME
7
+ RUN pip install --no-cache-dir -r requirements.txt
8
+
9
+ ENV PYTHONUNBUFFERED=1 PORT=7860
10
+ CMD ["python", "main.py"]
README.md CHANGED
@@ -1,10 +1,35 @@
1
  ---
2
- title: Fasthtml Todos
3
- emoji: 🐨
4
  colorFrom: green
5
- colorTo: blue
6
  sdk: docker
7
  pinned: false
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: FastHTML Todos
 
3
  colorFrom: green
4
+ colorTo: green
5
  sdk: docker
6
  pinned: false
7
+ license: apache-2.0
8
  ---
9
 
10
+ # FastHTML on 🤗 Spaces
11
+
12
+ Deploy a FastHTML application to [HuggingFace Spaces](https://huggingface.co/spaces) for free with one command!
13
+
14
+ ## Quickstart
15
+
16
+ 1. Create a free account on [HuggingFace](https://huggingface.co)
17
+ 2. Go to your account settings and create an access token with write access. Keep this token safe and don't share it.
18
+ 3. Set the `HF_TOKEN` environment variable to that token
19
+ 4. Install the huggingface hub client library (`pip install huggingface-hub`).
20
+ 5. Run the `deploy_hf.py` script and pass the name you want to give your space along with your token, e.g. `python deploy_hf.py fasthtml-todos <token>`.
21
+
22
+ By default this will upload a public space. You can make it private with the `--private` flag.
23
+
24
+ ## Configuration
25
+
26
+ The space will upload a backup of your database to a [HuggingFace Dataset](https://huggingface.co/datasets). By default it will be private and its name will be `<your-huggingface-id>/todos-backup`. You can change this behavior in the `config.ini` file. In not provided, a default file will be created with the contents (note that the `[DEFAULT]` line is required at the top):
27
+
28
+ ```
29
+ [DEFAULT]
30
+ dataset_id = todos-backup
31
+ db_dir = data
32
+ private_backup = True
33
+ ```
34
+
35
+ If you so choose, you can disable the automatic backups and use [persistent storage](https://huggingface.co/docs/hub/en/spaces-storage#persistent-storage-specs) instead for $5/month (USD).
config.ini ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ [DEFAULT]
2
+ dataset_id = todos-backup
3
+ db_dir = data
4
+ private_backup = True
5
+
favicon.ico ADDED
main.py ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fasthtml.common import *
2
+ from backup import upload, download
3
+
4
+ db = database("data/utodos.db")
5
+ todos,users = db.t.todos,db.t.users
6
+ if todos not in db.t:
7
+ users.create(name=str, pwd=str, pk='name')
8
+ todos.create(id=int, title=str, done=bool, name=str, pk='id')
9
+ Todo,User = todos.dataclass(),users.dataclass()
10
+
11
+ id_curr = 'current-todo'
12
+ def tid(id): return f'todo-{id}'
13
+
14
+ def lookup_user(u,p):
15
+ try: user = users[u]
16
+ except NotFoundError: user = users.insert(name=u, pwd=p)
17
+ return user.pwd==p
18
+
19
+ css = Style(':root { --pico-font-size: 100%; }')
20
+ authmw = user_pwd_auth(lookup_user, skip=[r'/favicon\.ico', r'/static/.*', r'.*\.css'])
21
+
22
+ def before(auth): todos.xtra(name=auth)
23
+
24
+ app = FastHTML(hdrs=(picolink, css), middleware=authmw, before=before)
25
+ rt = app.route
26
+
27
+ @rt("/{fname:path}.{ext:static}")
28
+ async def get(fname:str, ext:str): return FileResponse(f'{fname}.{ext}')
29
+
30
+ @patch
31
+ def __xt__(self:Todo):
32
+ show = AX(self.title, f'/todos/{self.id}', id_curr)
33
+ edit = AX('edit', f'/edit/{self.id}' , id_curr)
34
+ dt = ' (done)' if self.done else ''
35
+ return Li(show, dt, ' | ', edit, id=tid(self.id))
36
+
37
+ def mk_input(**kw): return Input(id="new-title", name="title", placeholder="New Todo", **kw)
38
+ def clr_details(): return Div(hx_swap_oob='innerHTML', id=id_curr)
39
+
40
+ @rt("/")
41
+ async def get(request, auth):
42
+ add = Form(Group(mk_input(), Button("Add")),
43
+ hx_post="/", target_id='todo-list', hx_swap="beforeend")
44
+ card = Card(Ul(*todos(), id='todo-list'),
45
+ header=add, footer=Div(id=id_curr)),
46
+ title = 'Todo list'
47
+ top = Grid(H1(f"{auth}'s {title}"), Div(A('logout', href=basic_logout(request), target="_blank"), style='text-align: right'))
48
+ return Title(title), Main(top, card, cls='container')
49
+
50
+ @rt("/todos/{id}")
51
+ async def delete(id:int):
52
+ todos.delete(id)
53
+ return clr_details()
54
+
55
+ @rt("/")
56
+ async def post(todo:Todo):
57
+ return todos.insert(todo), mk_input(hx_swap_oob='true')
58
+
59
+ @rt("/edit/{id}")
60
+ async def get(id:int):
61
+ res = Form(Group(Input(id="title"), Button("Save")),
62
+ Hidden(id="id"), Checkbox(id="done", label='Done'),
63
+ hx_put="/", target_id=tid(id), id="edit")
64
+ return fill_form(res, todos[id])
65
+
66
+ @rt("/")
67
+ async def put(todo: Todo):
68
+ return todos.upsert(todo), clr_details()
69
+
70
+ @rt("/todos/{id}")
71
+ async def get(id:int):
72
+ todo = todos[id]
73
+ btn = Button('delete', hx_delete=f'/todos/{todo.id}',
74
+ target_id=tid(todo.id), hx_swap="outerHTML")
75
+ return Div(Div(todo.title), btn)
76
+
77
+ app.on_event("startup")(download)
78
+ app.on_event("shutdown")(upload)
79
+
80
+ run_uv()
requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ python-fasthtml