[HN Gopher] Adding comments to your static blog with Mastodon
       ___________________________________________________________________
        
       Adding comments to your static blog with Mastodon
        
       Author : ognarb
       Score  : 159 points
       Date   : 2020-12-29 14:57 UTC (8 hours ago)
        
 (HTM) web link (carlschwan.eu)
 (TXT) w3m dump (carlschwan.eu)
        
       | kordlessagain wrote:
       | I use Github Pages for my blog and just found this little gem for
       | implementing comments using issues: https://danyow.net/using-
       | github-issues-for-blog-comments/
        
         | modeless wrote:
         | Interesting. GitHub recently added a "discussions" feature. I
         | wonder if it would be more suitable for this use.
        
           | strenholme wrote:
           | I switched over from a hacked together bbPress site for
           | MaraDNS support to using GitHub discussions (which I also
           | link to for blog comments). It's one less site for me to
           | maintain.
        
       | FalconSensei wrote:
       | What I do with Hugo is:
       | 
       | - Added reddit and twitter attributes on the frontmatter
       | 
       | - After publishing and sharing to both services, I paste reddit
       | link/tweet id to the frontmatter and rebuild
       | 
       | - On the blogs footer, I just embed both posts, so people can see
       | how many replies, and also click to be redirected reply on each
       | respective service.
       | 
       | Although I would love something more automated (and preferably
       | with in-site response), at least this is a good flow for the
       | readers.
        
         | mstibbard wrote:
         | This seems like a good approach (acknowledging it limits you to
         | only comments from reddit/twitter users). How do you embed the
         | posts so it shows # of replies?
        
       | asim wrote:
       | You can embed a comments API using micro - m3o.com. Sign-up, run
       | the comments service, copy/paste some JS code and there you have
       | it.
        
       | unicornporn wrote:
       | How about https://webmention.io/ ? Fediverse-friendly and
       | Twitter-friendly.
        
         | FalconSensei wrote:
         | seems like a cool idea, but without any demo to try, it's hard
         | to go for it
        
           | unicornporn wrote:
           | It's implemented here: https://www.kickscondor.com/meat-
           | computer
        
       | dmje wrote:
       | All looked great until this: "You can use your Mastodon account
       | to reply to this post" and then you lost 99% of your possible
       | commenters
        
       | progval wrote:
       | document.getElementById('mastodon-comments-list').innerHTML =
       | data['descendants'].reduce(function(prev, reply) {
       | mastodonComment = `             ...           `;           return
       | prev + DOMPurify.sanitize(mastodonComment);         }, '');
       | 
       | Why is it implemented like this? It looks quadratic, while it's
       | trivial (and more intuitive IMO) to make it linear:
       | document.getElementById('mastodon-comments-list').innerHTML =
       | data['descendants'].map(function(reply) {           return
       | DOMPurify.sanitize(`           ...           `);
       | }).join('');
        
         | brunoluiz wrote:
         | It could be an issue for websites with huge audiences and many
         | comments, but I (guess) this wouldn't be a big performance
         | issue for most personal static websites. Probably, the audience
         | size and number of comments is not big enough to cause any
         | trouble.
         | 
         | Sometimes, a hacky solution is just enough ;)
        
           | progval wrote:
           | That's another issue I didn't want to mention to not be too
           | negative: the script relies entirely on the Mastodon instance
           | to filter out the spam.
        
         | jlokier wrote:
         | It's not quadratic if the way reduce() and JS inlining work
         | together causes this:                   return prev +
         | DOMPurify.sanitize(mastodonComment);
         | 
         | to end up behaving like this:                   reduceValue =
         | reduceValue + DOMPurify.sanitize(mastodonComment);
         | 
         | which ends up behaving like this:                   reduceValue
         | += DOMPurify.sanitize(mastodonComment);
         | 
         | which ends up being a sequence of in-place string appends.
         | 
         | In any good implementation, a sequence of repeated appends
         | takes linear time in the total length of all the appended
         | strings, like join('').
         | 
         | I've just tested this theory in Safari 14.0.2 with a JS console
         | one-liner. On this browser, both versions take linear time not
         | quadratic, and the test corresponding to the first version even
         | runs slightly faster.
         | 
         | The time difference is small though, and you're right, the
         | second version is explicitly linear time. If I had tested with
         | a much older browser (maybe IE8), I think the first version
         | would have taken quadratic time.
        
         | brabel wrote:
         | Your alternative is also pretty bad as it will generate lots of
         | intermediate Strings before joining them up... Just use a
         | forEach instead and append directly to the DOM:
         | let root = document.getElementById('mastodon-comments-list');
         | data['descendants'].forEach(reply => {             let div =
         | document.createElement('div');             div.innerHtml =
         | DOMPurify.sanitize( ... );             root.append(div);
         | });
        
           | jlokier wrote:
           | "Pretty bad" is unwarranted in my opinion. Your alternative
           | adds overhead of creating an extra div for each intermediate
           | string in the code it replaces. It's trivial overhead, and
           | may be a small number, but exactly the same applies to the
           | overhead of temporary strings - they are comparable.
           | 
           | Intermediate string length doesn't matter for time complexity
           | here. If they are short they will be fast. If they are long,
           | the time to scan and parse in DOMPurify and innerHTML will
           | dominate.
           | 
           | After building the DOM, your extra divs are processed every
           | time that section of the DOM is styled and rendered, not just
           | once. If the number of extra divs is low enough that this is
           | negligible, so is the number of intermediate strings in the
           | alternative code.
           | 
           | So I wouldn't assume your version is faster at setting up,
           | and it may be marginally slower later on repeated renderings.
           | I'd profile both versions, maybe with Browserscope (which
           | probably has a test for this particular question already).
           | 
           | However if I couldn't profile and was asked my guess at the
           | fastest version, my guess is the string-join version. I'd be
           | more concerned with whether concatenating
           | DOMPurify.sanitize() strings is guaranteed to maintain the
           | same security properties.
        
             | hombre_fatal wrote:
             | If anyone wanted to provide half useful bikeshedding, they
             | could make the "Load Comments" button disable and change to
             | "Loading..." while it's being fetched. Right now there's no
             | feedback and the user is likely to click multiple times
             | wondering if it's working on a slower connection like mine.
             | 
             | They could also probably just html-escape the few foreign
             | values rather than bringing an HTML parser to the entire
             | template.
        
           | rattray wrote:
           | Generating intermediate strings is much less expensive than
           | painting to the DOM multiple times.
        
       | failrate wrote:
       | "One of the biggest disadvantages of static site generators is
       | that they are static and can't include comments." At first, I
       | misread this as "One of the biggest advantages...", and I still
       | think that is the correct conclusion.
        
       | CarelessExpert wrote:
       | I achieve this using https://brid.gy/ (or https://fed.brid.gy/ if
       | you want your blog to appear as a first class member of the
       | Fediverse), though in my case I just syndicate out to Twitter. I
       | then collect the webmentions using https://webmention.io/.
       | 
       | The nice thing about this is I get a breadth of methods for
       | receiving comments/reactions across multiple platforms, including
       | anything that directly supports webmention (such as
       | https://micro.blog/, where my posts are also syndicated), without
       | having to do any of the platform-specific wiring myself.
        
         | andrewshadura wrote:
         | Great, thanks! I was just about to write something similar, and
         | now I think I don't need to
        
         | StavrosK wrote:
         | This looks fantastic, does it support plain RSS? My blog is a
         | static site, but I'd like it to be exposed to the fediverse.
        
           | CarelessExpert wrote:
           | Just check the site for details. The short version is you
           | need an Atom feed.
        
             | StavrosK wrote:
             | I did, looks like it needs webmentions, which needs server-
             | side code.
        
               | CarelessExpert wrote:
               | That's what webmention.io is for. It'll receive
               | webmentions on your behalf and then expose an API to pull
               | em down. You hit that API during your static site build
               | or do it dynamically with some client side JS. Either way
               | it allows you to avoid hosting any services yourself.
        
               | StavrosK wrote:
               | Oh I see, thank you.
        
         | ognarb wrote:
         | I keep hearing about webmentions in my fediverse bubble, maybe
         | one day I should take a look at it.
        
           | input_sh wrote:
           | For the unaware, they're essentially identical to WordPress'
           | Pingbacks. You configure your website so that other websites
           | let you know whenever they link to you.
        
           | CarelessExpert wrote:
           | Honestly, I set up webmentions to use brid.gy, not just for
           | the sake of using webmentions. I wanted a two-way integration
           | between my static blog and Twitter, and this solution has
           | done the job nicely.
           | 
           | That I ended up with direct webmention support was just a
           | bonus as far as I'm concerned.
        
             | FalconSensei wrote:
             | So, would brid.gy automatically post your blog post to
             | twitter? That's what you mean by two-way integration?
        
               | CarelessExpert wrote:
               | Correct. Using the right microformats, it'll auto
               | syndicate notes as tweets and full posts as tweets with a
               | summary and a link. All my site has to do is shoot out a
               | webmention to brid.gy and it does the rest.
               | 
               | Then when people react (reply, like, etc) it'll proxy
               | those interactions back as webmentions.
        
         | rattray wrote:
         | Nice! How much does this approach cost (ballpark)?
        
           | CarelessExpert wrote:
           | Other than my time to set it up? Zero. All the services I
           | mentioned are operated for free (or can be self-hosted).
        
             | criddell wrote:
             | Are the interactions valuable enough that if it weren't
             | available for free, you would be willing to pay for it each
             | month?
        
               | CarelessExpert wrote:
               | I'm not doing anything to build a readership, so my
               | content doesn't get a lot of interactions. So for me, no.
               | 
               | If I had a real twitter following, though, I might have a
               | different answer.
        
       | chrismorgan wrote:
       | The technique demonstrated here for producing the markup is bad:
       | 
       | 1. The use of DOMPurify is either unnecessary or insufficient.
       | Specifically consider reply.content, which is provided from the
       | server as HTML. (I believe it's the only one that's supposed to
       | be serialised HTML; all the other fields are text.) If the
       | backend guarantees that you get a clean DOM, DOMPurify is
       | unnecessary+. But if it's possible for a user to control the
       | markup in that field completely, then DOMPurify as configured is
       | insufficient, because although it blocks XSS and JavaScript
       | execution, it doesn't filter out remote resource loading and CSS,
       | which can be almost as harmful. Trivial example, <a
       | href=//malicious.example style=position:fixed;inset:0>pwned</a>.
       | Given the type of content, you probably want to either blacklist
       | quite a few things (e.g. the style attribute, and the img,
       | picture, source, audio and video tags), or whitelist a small set
       | of things.
       | 
       | 2. Various fields that could hypothetically contain magic
       | characters are dropped in with no escaping. If they _do_ contain
       | magic characters like  < and &, you've just messed up the display
       | and opened the way for malicious resource loading and problem  3
       | below. Even if they are supposed to be unable to contain a magic
       | character (e.g. I'm going to _guess_ that reply.account.username
       | is safe), it's probably a good idea to escape them anyway just in
       | case (perhaps an API change later makes it possible), and to
       | guard against errant copy-pasters and editors of the code that
       | don't know what they're doing. Perhaps at some point you'll add
       | or switch to reply.account.display_name, which probably _can_
       | contain  < and &.
       | 
       | 3. The markup is produced by mixing static templating with user-
       | provided input, and sanitisation is performed on the whole thing.
       | It's important when doing this sort of templating that each user-
       | provided input be escaped or sanitised _by itself in isolation_ ,
       | not as part of a whole that has been concatenated. Otherwise you
       | can mess with the DOM tree accidentally or deliberately. Suppose,
       | for example, that reply.content could contain
       | `</div></div></div><img src=//ad.example alt="Legitimate-looking
       | in-stream ad"><div class="mastodon-comment">...<div
       | class="content">...<div class="mastodon-comment-content">...`. So
       | this means:
       | 
       | * Apply attributes and text nodes to a real DOM (e.g. `img = new
       | Image(); img.src = reply.account.avatar_static;
       | avatar.append(img)`), or escape them in the HTML serialisation
       | (e.g. `<img src="${reply.account.avatar_static.replace(/&/g,
       | "&amp;").replace(/"/g, "&quot;")}">`).
       | 
       | * Do HTML sanitisation on _just the user input_ , e.g.
       | DOMPurify.sanitize(reply.content).
       | 
       | A part of my problem with the code as written is that, purely
       | from looking at the code, I can see that there _may_ well be
       | various security holes. I require knowledge of the backend also
       | before I can judge whether there _are_ security holes. Where
       | possible, it's best to write the code in such a way that you
       | _know_ that it's safe, or that it's not--try not to depend on
       | subtle things like "the _particular fields_ that we access happen
       | to be unable to contain angle brackets, ampersands and quotes",
       | because they're fragile.
       | 
       | Incidentally, it would also be more efficient to run DOMPurify
       | with the RETURN_DOM_FRAGMENT option, and append that, rather than
       | concatenating it to a string and setting innerHTML. Saves a
       | pointless serialisation/deserialisation round trip, and avoids
       | any possibility of new mXSS vulnerabilities that might be
       | discovered in the future. (I don't really understand why
       | DOMPurify defaults to emitting a string. I can't remember seeing
       | a single non-demo use of DOMPurify where the string is preferable
       | to a DOM fragment.)
       | 
       | --
       | 
       | + Though if the server sanitised arbitrary user-provided
       | HTML/MathML/SVG, I probably don't trust it as much as I trust
       | DOMPurify, for things that end up in the DOM. There are some
       | pretty crazy subtleties in things like HTML serialisation round-
       | tripping of HTML, SVG and MathML content. There's fun reading in
       | the changelogs and security patches.
        
         | ognarb wrote:
         | Thanks a lot for all this nice tips. I applied most of them to
         | my blog post.
        
       | floatingatoll wrote:
       | Does syndicating third-party content onto your blog in this way
       | expose you to legal liability for publishing that content? Are
       | deletions and takedowns synced back from Mastodon, such as when
       | someone is taken offline for breaking local law with their
       | Mastodon instance, or would their content remain published on
       | your blog?
        
         | ognarb wrote:
         | I can technically filter comments pretty easily from the js
         | script. When a comment is removed from mastodon it is also
         | removed from the blog.
        
       | llbeansandrice wrote:
       | Do people like comments on blogs? I can't find the links right
       | now but I've found a couple of people talking about how they used
       | to have comments on their blog and decided to get rid of it since
       | they would occasionally become a curation/moderation nightmare.
        
         | input_sh wrote:
         | Personally, no. They distract me from content and I often find
         | myself jumping to comments directly if I know there are some.
         | 
         | I'm from a region where they're pretty common on news sites, so
         | I've created a Firefox add-on that hides the comments from
         | about 40 local websites that I chose via Alexa ranks.
        
       ___________________________________________________________________
       (page generated 2020-12-29 23:00 UTC)