Spaces:
Runtime error
Runtime error
deploy at 2024-06-29 16:29:52.130254
Browse files- Dockerfile +10 -0
- README.md +29 -4
- config.ini +5 -0
- favicon.ico +0 -0
- main.py +80 -0
- 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:
|
3 |
-
emoji: 🐨
|
4 |
colorFrom: green
|
5 |
-
colorTo:
|
6 |
sdk: docker
|
7 |
pinned: false
|
|
|
8 |
---
|
9 |
|
10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|