File size: 4,393 Bytes
d6eb753
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5210aac
 
d6eb753
 
 
 
 
5210aac
d6eb753
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5210aac
d6eb753
5210aac
87030ce
d6eb753
 
 
 
 
87030ce
d6eb753
 
 
 
 
 
 
 
 
 
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
"""PW app β€” heavy library files β†’ Hugging Face dataset.

Why this Space exists
---------------------
Library files used to go through the Supabase Edge Function `hf-upload`. That
can never work: Supabase Edge Functions cap **CPU time at 2 seconds**, and
@huggingface/hub hashes + encodes the whole payload in-process. Measured on the
live project: a 12-byte file succeeded, but 256 KB / 2 MB / 10 MB / 45 MB all
died with WORKER_RESOURCE_LIMIT in ~0.9 s. Every real upload silently fell back
to Supabase storage, which is itself hard-capped at 50 MB on the free plan.

So the bytes have to skip Supabase entirely. Gradio streams uploads to disk
(free CPU Space = 16 GB RAM / 50 GB disk), so 250 MB is comfortable here.
The HF write token stays a Space secret and never reaches a browser.

Space secrets (Settings β†’ Variables and secrets):
  HF_TOKEN         = hf_...  (WRITE access to the dataset)
  HF_DATASET_REPO  = kacapower/Directory   (must be a PUBLIC dataset, else the
                     returned resolve/ links 401 for everyone)
  PW_UPLOAD_KEY    = any random string, must match PW_UPLOAD_KEY in js/config.js

API (api_name="push"): [file, original_name, key] -> public download URL.

Note on the key: it ships inside browser JS, so it stops drive-by abuse of a
public endpoint β€” it is NOT real authentication. Same trust model as the
anon-key endpoints the app already uses.
"""

import os
import re
import time
import uuid
from collections import deque

import gradio as gr
from huggingface_hub import HfApi

MAX_BYTES = 250 * 1024 * 1024          # matches the library cap in resources.html
RATE_MAX = 12                          # uploads per IP ...
RATE_WINDOW = 600                      # ... per 10 minutes

TOKEN = os.environ.get("HF_TOKEN")
DATASET = os.environ.get("HF_DATASET_REPO", "kacapower/Directory")
UPLOAD_KEY = os.environ.get("PW_UPLOAD_KEY", "")

api = HfApi(token=TOKEN)
_hits: dict[str, deque] = {}


def _rate_ok(ip):
    now = time.time()
    q = _hits.setdefault(ip, deque())
    while q and now - q[0] > RATE_WINDOW:
        q.popleft()
    if len(q) >= RATE_MAX:
        return False
    q.append(now)
    return True


def _ext(name):
    """Same sanitising rule the old hf-upload edge function used."""
    raw = (name or "file.bin").rsplit(".", 1)[-1].lower()
    return re.sub(r"[^a-z0-9]", "", raw)[:8] or "bin"


def push(file_path, original_name, key, request: gr.Request):
    """Upload one file to the dataset, return its permanent public URL."""
    if not TOKEN:
        raise gr.Error("Space is not configured: HF_TOKEN secret is missing.")
    if UPLOAD_KEY and key != UPLOAD_KEY:
        raise gr.Error("Rejected: bad upload key.")

    ip = getattr(getattr(request, "client", None), "host", "?") if request else "?"
    if not _rate_ok(ip):
        raise gr.Error("Too many uploads from this device β€” try again in a few minutes.")

    if not file_path or not os.path.exists(file_path):
        raise gr.Error("No file received.")

    size = os.path.getsize(file_path)
    if size == 0:
        raise gr.Error("Empty file.")
    if size > MAX_BYTES:
        raise gr.Error(f"File is {size / 1048576:.1f} MB β€” the limit is 250 MB.")

    path_in_repo = f"files/{int(time.time() * 1000)}_{uuid.uuid4().hex[:8]}.{_ext(original_name)}"
    api.upload_file(
        path_or_fileobj=file_path,
        path_in_repo=path_in_repo,
        repo_id=DATASET,
        repo_type="dataset",
        commit_message=f"pw-app upload: {path_in_repo}",
    )
    return f"https://huggingface.co/datasets/{DATASET}/resolve/main/{path_in_repo}"


with gr.Blocks() as demo:
    gr.Markdown("## PW Library β€” heavy file uploader")
    gr.Markdown(
        f"Pushes files up to **250 MB** into the `{DATASET}` dataset and returns a "
        "permanent public download link. Used by the DoubtShare library page."
    )
    with gr.Row():
        with gr.Column():
            f_in = gr.File(label="File", type="filepath")
            f_name = gr.Textbox(label="Original filename", value="file.bin")
            f_key = gr.Textbox(label="Upload key", type="password")
            f_btn = gr.Button("Upload to dataset", variant="primary")
        f_out = gr.Textbox(label="Public URL", show_copy_button=True)
    f_btn.click(fn=push, inputs=[f_in, f_name, f_key], outputs=f_out, api_name="push")

demo.queue(max_size=20).launch(max_file_size="250mb")