Fork me on GitHub
#biff
<
2023-02-14
>
m.q.warnock16:02:39

I'm trying to implement a file-upload. Platypub provides an example, but it's in the context of using tinymce , and I'd like to do something more like the htmx example (https://htmx.org/examples/file-upload/ ). Just copying that example, though, I get a 403, which I [think I] understand is due to the lack of the anti-forgery token in the headers; I'm finding it difficult to figure out how to inject that, in part, I suppose, because what client-side code I have so far is typescript with webpack configured to be picky, and htmx doesn't provide types (though there's this: https://github.com/leevigraham/typescript-htmx-types/blob/main/src/@types/htmx.org/index.d.ts). There are things I've thought to try, but I'm wondering if anyone here has already solved this problem and could help me avoid the rabbit-hole I'm slipping into. It's currently looking like one of those days when the first thing on my todo-list, which was supposed to take 10m, eats the whole day and leaves me wanting to change careers 😛

pavlosmelissinos17:02:45

Longshot but which version of biff are you working with?

m.q.warnock17:02:44

I can upgrade if that helps

pavlosmelissinos17:02:41

If I'm not mistaken 0.6.0 automatically adds the anti-forgery tokens to every request

pavlosmelissinos17:02:15

In older versions you have to do something yourself, give me a moment to confirm

m.q.warnock17:02:17

oh! that would be great 🙂 i'll upgrade and find out

pavlosmelissinos17:02:43

:hx-headers (json/generate-string
                                   {:x-csrf-token anti-forgery/*anti-forgery-token*})

m.q.warnock17:02:07

in the versions I've used, biff/form adds it as a hidden input, and this file-upload is inside one, but I was figuring the multi-part/form-data changed things. oh, that easy? facepalm

pavlosmelissinos17:02:14

That's assuming your issue is indeed caused by a missing anti-forgery token

pavlosmelissinos17:02:24

If the htmx request is part of a form then you don't have to do anything, it should work out of the box (biff takes care of it), even before 0.6.0

☝️ 2
m.q.warnock17:02:35

hrmm- I guess it's something else then 😕 I've tried adding the ring-middleware for multipart uploads, but platypub doesn't use that, and I seem to be doing things essentially the same, apart from the platypub file-uploader being some javascript provided to tinymce I guess I'll try to verify the cause. any hints on how best to debug middleware stuff would be helpful

Jacob O'Bryant17:02:26

yeah, sound like the anti forgery token might not be the problem I sometimes create a wrap-wtf middleware that prints out the request + response, and then put it in various places in the middleware stack

😂 2
👍 2
Jacob O'Bryant17:02:05

I have some htmx upload code in yakread that I can share once I'm at my desk

gratitude-thank-you 2
Jacob O'Bryant20:02:13

Here's what the file input code looks like, for an image upload:

[:input {:type "file"
         :accept "image/apng, image/avif, image/gif, image/jpeg, image/png, image/svg+xml, image/webp"
         :name "image-file"
         :class '[border-gray-300
                  rounded
                  text-black]
         :hx-post "/upload-image"
         :hx-target "#image-upload"
         :hx-swap "outerHTML"
         :hx-include "closest form"
         :hx-indicator "#image-upload-indicator"
         :hx-encoding "multipart/form-data"}]

Jacob O'Bryant20:02:55

Now that I'm looking at this again, I think other inputs from the containing form are only included if you do a regular form post, or if the form element is what's triggering the htmx request. In this case, the input element itself triggers the htmx request, and I think that's why I put :hx-include "closest form" there; to make sure it included all the other params (including the csrf token)

Jacob O'Bryant20:02:40

this is the request handler by the way:

(defn upload-image [{:keys [s3/edge ad params multipart-params] :as ctx}]
  (let [image-id (random-uuid)
        file-info (get multipart-params "image-file")
        url (str edge "/" image-id)
        ctx (assoc-in ctx [:params :image] url)]
    (s3/request ctx {:method "PUT"
                     :key image-id
                     :file (:tempfile file-info)
                     :headers {"x-amz-acl" "public-read"
                               "content-type" (:content-type file-info)}})
    [:<>
     (image-upload-input {:value url})
     (preview (assoc ctx ::oob true))]))

Jacob O'Bryant20:02:42

I don't think you need to do anything with middleware

m.q.warnock21:02:20

Thanks, Jacob!!!