640 lines
88 KiB
XML
640 lines
88 KiB
XML
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
|
||
<channel>
|
||
<title>Blog on AlipourIm journeys</title>
|
||
<link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/blog/</link>
|
||
<description>Recent content in Blog on AlipourIm journeys</description>
|
||
<generator>Hugo -- 0.146.0</generator>
|
||
<language>en</language>
|
||
<lastBuildDate>Thu, 23 Oct 2025 19:31:12 +0200</lastBuildDate>
|
||
<atom:link href="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/blog/index.xml" rel="self" type="application/rss+xml" />
|
||
<item>
|
||
<title>New features for my blog! Adding Comments + RSS to Hugo PaperMod (Giscus and Isso FTW)</title>
|
||
<link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/comments-rss-papermod/</link>
|
||
<pubDate>Thu, 23 Oct 2025 19:31:12 +0200</pubDate>
|
||
<guid>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/comments-rss-papermod/</guid>
|
||
<description>A friendly, copy-pasteable guide to wiring up Giscus comments (GitHub Discussions) and Isso comments + RSS in Hugo PaperMod.</description>
|
||
<content:encoded><![CDATA[<blockquote>
|
||
<p>Short story: I wanted comments and a proper RSS feed on my Hugo + PaperMod site.<br>
|
||
Longer story: I spent 30 minutes poking around, and now you don’t have to. Here’s exactly what I did.</p></blockquote>
|
||
<hr>
|
||
<p>I’ve been wanting to add comment section to my blog for a while.
|
||
There are a few problems.</p>
|
||
<ul>
|
||
<li>I don’t want to get attacked by bots or random people for no reason.</li>
|
||
<li>I don’t want to allow weird things to happen.</li>
|
||
<li>I don’t want to have a database for it.</li>
|
||
</ul>
|
||
<p>So… I decided to take a path which is not necesserially the best.
|
||
It requires a third party to act on your behalf, but it is not as scary as it sounds.
|
||
According to Github:</p>
|
||
<blockquote>
|
||
<p><strong>What the app can do</strong>
|
||
The app’s rights are limited to the repo(s) where the site owner installed it and to the permissions it requested (basically: Discussions read/write + metadata). It can’t touch a commenter’s private repos or do things outside those scopes. GitHub’s docs also say a GitHub App can only do what both you and the app have permission to do.</p></blockquote>
|
||
<blockquote>
|
||
<p><strong>What “Act on your behalf” means</strong>
|
||
That scary wording is GitHub’s. The giscus maintainer explains it means “post comments under your username,” and shows that the app requests Discussions read/write + metadata when installed.</p></blockquote>
|
||
<blockquote>
|
||
<p><strong>Do commenters have alternatives?</strong>
|
||
Yes. Anyone can open the linked GitHub Discussion and comment directly on GitHub, bypassing the app’s OAuth popup. The README explicitly mentions this.</p></blockquote>
|
||
<blockquote>
|
||
<p><strong>Privacy & storage</strong>
|
||
No tracking/ads; comments live in GitHub Discussions. giscus is open-source and has a published privacy policy. You can also revoke the app any time in your GitHub settings.</p></blockquote>
|
||
<p>With the notice out of the way let’s dive right in!</p>
|
||
<hr>
|
||
<h2 id="why-i-chose-giscus-instead-of-disqus">Why I chose Giscus (instead of Disqus)</h2>
|
||
<ul>
|
||
<li><strong>No ads or trackers</strong> (my page weight thanks me).</li>
|
||
<li><strong>Uses GitHub Discussions</strong> (portable, transparent, easy to search).</li>
|
||
<li><strong>Readers log in with GitHub</strong> (no mystery auth popups).</li>
|
||
<li>In theory you can directly go to the repo’s discussion section and comment there!</li>
|
||
</ul>
|
||
<hr>
|
||
<h2 id="what-you-need-for-your-site-not-per-user">What you need (for your site, not per user)</h2>
|
||
<ul>
|
||
<li>A Hugo site using <strong>PaperMod</strong> ✅</li>
|
||
<li>A GitHub account ✅</li>
|
||
<li>A <strong>public</strong> repository for the discussion threads (I called mine <code>blog-comments</code>) with <strong>Discussions</strong> enabled ✅</li>
|
||
<li>Giscus app installed on that repo ✅</li>
|
||
</ul>
|
||
<p>If you like to keep your main site repo clean, use a separate one, e.g. <code>github-username/blog-comments</code>.</p>
|
||
<hr>
|
||
<h2 id="1-turn-on-comments-with-giscus">1) Turn on comments with Giscus</h2>
|
||
<p>PaperMod renders a <code>comments.html</code> partial when <code>params.comments = true</code>.<br>
|
||
So we enable it in config and create the partial.</p>
|
||
<h3 id="11-enable-comments-globally">1.1 Enable comments globally</h3>
|
||
<p><code>hugo.toml</code> (snippet)</p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-toml" data-lang="toml"><span style="display:flex;"><span>[<span style="color:#a6e22e">params</span>]
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">comments</span> = <span style="color:#66d9ef">true</span>
|
||
</span></span></code></pre></div><p>You can still opt out per post with <code>comments = false</code> in front matter.</p>
|
||
<h3 id="12-create-the-comments-partial">1.2 Create the comments partial</h3>
|
||
<p>Create this file <strong>in your site</strong> (not inside the theme):</p>
|
||
<pre tabindex="0"><code>layouts/partials/comments.html
|
||
</code></pre><p>Paste the Giscus embed (swap the placeholders with yours from giscus.app):</p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span><<span style="color:#f92672">section</span> <span style="color:#a6e22e">id</span><span style="color:#f92672">=</span><span style="color:#e6db74">"comments"</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">"giscus"</span>>
|
||
</span></span><span style="display:flex;"><span> <<span style="color:#f92672">script</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">src</span><span style="color:#f92672">=</span><span style="color:#e6db74">"https://giscus.app/client.js"</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">data-repo</span><span style="color:#f92672">=</span><span style="color:#e6db74">"yourname/blog-comments"</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">data-repo-id</span><span style="color:#f92672">=</span><span style="color:#e6db74">"YOUR_REPO_ID"</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">data-category</span><span style="color:#f92672">=</span><span style="color:#e6db74">"Comments"</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">data-category-id</span><span style="color:#f92672">=</span><span style="color:#e6db74">"YOUR_CATEGORY_ID"</span> <span style="color:#960050;background-color:#1e0010"><!</span><span style="color:#a6e22e">--</span> <span style="color:#a6e22e">Get</span> <span style="color:#a6e22e">them</span> <span style="color:#a6e22e">from</span> <span style="color:#a6e22e">Giscus</span><span style="color:#960050;background-color:#1e0010">'</span><span style="color:#a6e22e">s</span> <span style="color:#a6e22e">page</span><span style="color:#960050;background-color:#1e0010">!</span> <span style="color:#a6e22e">--</span>>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">data</span><span style="color:#f92672">-</span><span style="color:#a6e22e">mapping</span><span style="color:#f92672">=</span><span style="color:#e6db74">"pathname"</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">data</span><span style="color:#f92672">-</span><span style="color:#a6e22e">strict</span><span style="color:#f92672">=</span><span style="color:#e6db74">"0"</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">data</span><span style="color:#f92672">-</span><span style="color:#a6e22e">reactions</span><span style="color:#f92672">-</span><span style="color:#a6e22e">enabled</span><span style="color:#f92672">=</span><span style="color:#e6db74">"1"</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">data</span><span style="color:#f92672">-</span><span style="color:#a6e22e">emit</span><span style="color:#f92672">-</span><span style="color:#a6e22e">metadata</span><span style="color:#f92672">=</span><span style="color:#e6db74">"0"</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">data</span><span style="color:#f92672">-</span><span style="color:#a6e22e">input</span><span style="color:#f92672">-</span><span style="color:#a6e22e">position</span><span style="color:#f92672">=</span><span style="color:#e6db74">"bottom"</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">data</span><span style="color:#f92672">-</span><span style="color:#a6e22e">theme</span><span style="color:#f92672">=</span><span style="color:#e6db74">"preferred_color_scheme"</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">data</span><span style="color:#f92672">-</span><span style="color:#a6e22e">lang</span><span style="color:#f92672">=</span><span style="color:#e6db74">"en"</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">data</span><span style="color:#f92672">-</span><span style="color:#a6e22e">loading</span><span style="color:#f92672">=</span><span style="color:#e6db74">"lazy"</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">crossorigin</span><span style="color:#f92672">=</span><span style="color:#e6db74">"anonymous"</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">async</span><span style="color:#f92672">></span>
|
||
</span></span><span style="display:flex;"><span> </<span style="color:#f92672">script</span>>
|
||
</span></span><span style="display:flex;"><span> <<span style="color:#f92672">noscript</span>>Enable JavaScript to view comments powered by Giscus.</<span style="color:#f92672">noscript</span>>
|
||
</span></span><span style="display:flex;"><span></<span style="color:#f92672">section</span>>
|
||
</span></span></code></pre></div><p><strong>Notes</strong></p>
|
||
<ul>
|
||
<li>You’ll get <code>repo-id</code> and <code>category-id</code> from <strong>giscus.app</strong> after picking your repo + category.</li>
|
||
<li><code>data-mapping="pathname"</code> works great for static sites.</li>
|
||
<li>Prefer a <strong>public</strong> repo so visitors can read discussions without friction.</li>
|
||
<li>Want a different language? Change <code>data-lang</code> (e.g., <code>de</code>, <code>fr</code>).</li>
|
||
</ul>
|
||
<hr>
|
||
<h2 id="2-enable-rss-home--sections">2) Enable RSS (home + sections)</h2>
|
||
<p>Hugo already knows how to generate feeds; we just tell it where.</p>
|
||
<p><code>hugo.toml</code></p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-toml" data-lang="toml"><span style="display:flex;"><span><span style="color:#a6e22e">baseURL</span> = <span style="color:#e6db74">"https://example.com/"</span> <span style="color:#75715e"># set your production URL</span>
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span>[<span style="color:#a6e22e">outputs</span>]
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">home</span> = [<span style="color:#e6db74">"HTML"</span>, <span style="color:#e6db74">"RSS"</span>, <span style="color:#e6db74">"JSON"</span>] <span style="color:#75715e"># JSON is optional (e.g., for on-site search)</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">section</span> = [<span style="color:#e6db74">"HTML"</span>, <span style="color:#e6db74">"RSS"</span>]
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">taxonomy</span> = [<span style="color:#e6db74">"HTML"</span>, <span style="color:#e6db74">"RSS"</span>]
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">term</span> = [<span style="color:#e6db74">"HTML"</span>, <span style="color:#e6db74">"RSS"</span>]
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span>[<span style="color:#a6e22e">services</span>.<span style="color:#a6e22e">rss</span>]
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">limit</span> = <span style="color:#ae81ff">-1</span> <span style="color:#75715e"># -1 = no cap; or set e.g. 20</span>
|
||
</span></span></code></pre></div><p><strong>Feed URLs</strong></p>
|
||
<ul>
|
||
<li>Home feed: <code>/index.xml</code> → <code>https://example.com/index.xml</code></li>
|
||
<li>Section feeds: <code>/posts/index.xml</code>, <code>/notes/index.xml</code>, etc. (folders are lowercase)</li>
|
||
</ul>
|
||
<blockquote>
|
||
<p>If you’re using a <code>.onion</code> address or staging domain, just set that in <code>baseURL</code>.<br>
|
||
Locally, Hugo serves feeds at <code>http://localhost:1313/index.xml</code>.</p></blockquote>
|
||
<hr>
|
||
<h2 id="3-show-an-rss-button-on-the-home-page">3) Show an RSS button on the home page</h2>
|
||
<p>PaperMod shows a nice RSS icon on <strong>section/taxonomy</strong> lists by default (when enabled), but not on the home page. Two options:</p>
|
||
<h3 id="option-a--use-the-home-social-icons-row">Option A — Use the home “social icons” row</h3>
|
||
<p>This appears if you enable <strong>Home-Info</strong> or <strong>Profile</strong> mode:</p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-toml" data-lang="toml"><span style="display:flex;"><span>[<span style="color:#a6e22e">params</span>.<span style="color:#a6e22e">homeInfoParams</span>]
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">Title</span> = <span style="color:#e6db74">"Hello, internet!"</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">Content</span> = <span style="color:#e6db74">"Notes, experiments, and occasional rabbit holes."</span>
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span>[[<span style="color:#a6e22e">params</span>.<span style="color:#a6e22e">socialIcons</span>]]
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">name</span> = <span style="color:#e6db74">"rss"</span> <span style="color:#75715e"># lowercase matters</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">url</span> = <span style="color:#e6db74">"/index.xml"</span>
|
||
</span></span></code></pre></div><h3 id="option-b--add-an-icon-in-the-footer-theme-safe">Option B — Add an icon in the footer (theme-safe)</h3>
|
||
<p>Create:</p>
|
||
<pre tabindex="0"><code>layouts/partials/extend_footer.html
|
||
</code></pre><p>Paste:</p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span><<span style="color:#f92672">a</span> <span style="color:#a6e22e">href</span><span style="color:#f92672">=</span><span style="color:#e6db74">"/index.xml"</span> <span style="color:#a6e22e">rel</span><span style="color:#f92672">=</span><span style="color:#e6db74">"alternate"</span> <span style="color:#a6e22e">type</span><span style="color:#f92672">=</span><span style="color:#e6db74">"application/rss+xml"</span> <span style="color:#a6e22e">title</span><span style="color:#f92672">=</span><span style="color:#e6db74">"RSS"</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">"rss-link"</span>>
|
||
</span></span><span style="display:flex;"><span> {{ partial "svg.html" (dict "name" "rss") }}
|
||
</span></span><span style="display:flex;"><span> <<span style="color:#f92672">span</span>>RSS</<span style="color:#f92672">span</span>>
|
||
</span></span><span style="display:flex;"><span></<span style="color:#f92672">a</span>>
|
||
</span></span></code></pre></div><p>Make it a sensible size:</p>
|
||
<p>Create:</p>
|
||
<pre tabindex="0"><code>assets/css/extended/rss.css
|
||
</code></pre><p>Paste:</p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-css" data-lang="css"><span style="display:flex;"><span><span style="color:#75715e">/* Small & aligned RSS icon in the footer */</span>
|
||
</span></span><span style="display:flex;"><span>.<span style="color:#a6e22e">rss-link</span> { <span style="color:#66d9ef">display</span>: <span style="color:#66d9ef">inline-flex</span>; <span style="color:#66d9ef">align-items</span>: <span style="color:#66d9ef">center</span>; <span style="color:#66d9ef">gap</span>: <span style="color:#ae81ff">.35</span><span style="color:#66d9ef">rem</span>; }
|
||
</span></span><span style="display:flex;"><span>.<span style="color:#a6e22e">rss-link</span> <span style="color:#f92672">svg</span> { <span style="color:#66d9ef">margin-left</span>: <span style="color:#ae81ff">10</span><span style="color:#66d9ef">px</span>; <span style="color:#66d9ef">width</span>: <span style="color:#ae81ff">18</span><span style="color:#66d9ef">px</span>; <span style="color:#66d9ef">height</span>: <span style="color:#ae81ff">18</span><span style="color:#66d9ef">px</span>; } <span style="color:#75715e">/* a bit of left margin for good measure!*/</span>
|
||
</span></span><span style="display:flex;"><span>.<span style="color:#a6e22e">rss-link</span> <span style="color:#f92672">span</span> { <span style="color:#66d9ef">font-size</span>: <span style="color:#ae81ff">.95</span><span style="color:#66d9ef">rem</span>; }
|
||
</span></span></code></pre></div><p>PaperMod auto-bundles anything under <code>assets/css/extended/</code>, so no extra imports needed.</p>
|
||
<hr>
|
||
<h2 id="4-verify-everything">4) Verify everything</h2>
|
||
<p>Build or run dev:</p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>hugo <span style="color:#75715e"># outputs to ./public</span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#75715e"># or</span>
|
||
</span></span><span style="display:flex;"><span>hugo server <span style="color:#75715e"># serves at http://localhost:1313</span>
|
||
</span></span></code></pre></div><p>Check feeds:</p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># Home feed:</span>
|
||
</span></span><span style="display:flex;"><span>curl -s http://localhost:1313/index.xml | head
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#75715e"># A section feed (adjust path to your section name):</span>
|
||
</span></span><span style="display:flex;"><span>curl -s http://localhost:1313/posts/index.xml | head
|
||
</span></span></code></pre></div><p>Check comments:</p>
|
||
<ol>
|
||
<li>Open any post locally.</li>
|
||
<li>Scroll to the bottom — Giscus should appear.</li>
|
||
<li>Try a reaction or comment (you’ll be prompted to sign in with GitHub).</li>
|
||
</ol>
|
||
<hr>
|
||
<h2 id="5-per-post-controls-handy-later">5) Per-post controls (handy later)</h2>
|
||
<p>Turn comments off for a single post:</p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-toml" data-lang="toml"><span style="display:flex;"><span><span style="color:#75715e"># in that post’s front matter</span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">comments</span> = <span style="color:#66d9ef">false</span>
|
||
</span></span></code></pre></div><p>Full content in RSS (global):</p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-toml" data-lang="toml"><span style="display:flex;"><span>[<span style="color:#a6e22e">params</span>]
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">ShowFullTextinRSS</span> = <span style="color:#66d9ef">true</span>
|
||
</span></span></code></pre></div><p>Show RSS buttons on section/taxonomy lists:</p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-toml" data-lang="toml"><span style="display:flex;"><span>[<span style="color:#a6e22e">params</span>]
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">ShowRssButtonInSectionTermList</span> = <span style="color:#66d9ef">true</span>
|
||
</span></span></code></pre></div><hr>
|
||
<h2 id="6-troubleshooting-aka-what-i-bumped-into">6) Troubleshooting (aka “what I bumped into”)</h2>
|
||
<ul>
|
||
<li>
|
||
<p><strong>Home page has no RSS icon</strong><br>
|
||
That’s expected; either enable Home-Info/Profile mode + <code>socialIcons</code>, or use the footer partial.</p>
|
||
</li>
|
||
<li>
|
||
<p><strong>Section feed 404</strong><br>
|
||
Folder names are lowercase (<code>content/posts/</code>, not <code>content/Posts/</code>).<br>
|
||
The URL mirrors that: <code>/posts/index.xml</code>.</p>
|
||
</li>
|
||
<li>
|
||
<p><strong>Giscus doesn’t create a discussion</strong><br>
|
||
Double-check the four attributes: <code>data-repo</code>, <code>data-repo-id</code>, <code>data-category</code>, <code>data-category-id</code>.<br>
|
||
Make sure Discussions is enabled and the Giscus app is installed for the repo.</p>
|
||
</li>
|
||
<li>
|
||
<p><strong>Icon is comically large</strong><br>
|
||
Keep the CSS above; 16–20px usually looks right.</p>
|
||
</li>
|
||
</ul>
|
||
<hr>
|
||
<h2 id="7-bonus-fast-setup-via-github-cli-optional-i-didnt-use-it-but-i-shouldve-perhaps">7) Bonus: fast setup via GitHub CLI (optional, I didn’t use it, but I should’ve perhaps)</h2>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># Create the public comments repo (adjust names)</span>
|
||
</span></span><span style="display:flex;"><span>gh repo create yourname/blog-comments --public -d <span style="color:#e6db74">"Blog comment threads"</span> --confirm
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Enable Discussions for it</span>
|
||
</span></span><span style="display:flex;"><span>gh repo edit yourname/blog-comments --enable-discussions
|
||
</span></span></code></pre></div><p>Then visit <strong>giscus.app</strong>, select the repo + category (e.g. “Comments”), and copy the generated IDs into <code>layouts/partials/comments.html</code>.</p>
|
||
<hr>
|
||
<h3 id="final-config-snapshot-condensed">Final config snapshot (condensed)</h3>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-toml" data-lang="toml"><span style="display:flex;"><span><span style="color:#a6e22e">baseURL</span> = <span style="color:#e6db74">"https://example.com/"</span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">theme</span> = <span style="color:#e6db74">"PaperMod"</span>
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span>[<span style="color:#a6e22e">outputs</span>]
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">home</span> = [<span style="color:#e6db74">"HTML"</span>, <span style="color:#e6db74">"RSS"</span>, <span style="color:#e6db74">"JSON"</span>]
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">section</span> = [<span style="color:#e6db74">"HTML"</span>, <span style="color:#e6db74">"RSS"</span>]
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">taxonomy</span> = [<span style="color:#e6db74">"HTML"</span>, <span style="color:#e6db74">"RSS"</span>]
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">term</span> = [<span style="color:#e6db74">"HTML"</span>, <span style="color:#e6db74">"RSS"</span>]
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span>[<span style="color:#a6e22e">services</span>.<span style="color:#a6e22e">rss</span>]
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">limit</span> = <span style="color:#ae81ff">-1</span>
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span>[<span style="color:#a6e22e">params</span>]
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">comments</span> = <span style="color:#66d9ef">true</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">ShowRssButtonInSectionTermList</span> = <span style="color:#66d9ef">true</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">ShowFullTextinRSS</span> = <span style="color:#66d9ef">true</span>
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Optional if using Home-Info/Profile mode:</span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#75715e"># [[params.socialIcons]]</span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#75715e"># name = "rss"</span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#75715e"># url = "/index.xml"</span>
|
||
</span></span></code></pre></div><hr>
|
||
<h1 id="appendix--why-i-added-isso-how-i-wired-it-and-what-i-fixed">Appendix — Why I added Isso, how I wired it, and what I fixed</h1>
|
||
<h2 id="the-story-why-isso--why-i-still-keep-giscus">The story (why Isso + why I still keep Giscus)</h2>
|
||
<p>I wanted comments that:</p>
|
||
<ul>
|
||
<li>allow <strong>anonymous</strong> replies (no GitHub account required),</li>
|
||
<li>are <strong>lightweight</strong> and <strong>self-hosted</strong>, and</li>
|
||
<li>can work on my <strong>Tor <code>.onion</code> mirror</strong>.</li>
|
||
</ul>
|
||
<p><strong>Isso</strong> checks those boxes: it’s a tiny Python app with SQLite storage, no trackers, and a simple embed. At the same time, I like the developer-friendly workflow of <strong>Giscus</strong> (comments are GitHub Discussions), so I kept both and added a little switcher UI: <strong>“Anonymous (Isso)”</strong> or <strong>“GitHub (Giscus)”</strong>.</p>
|
||
<p>Result: readers can pick privacy/anon (Isso) or identity/notifications (Giscus).</p>
|
||
<h2 id="how-i-run-it-high-level">How I run it (high-level)</h2>
|
||
<ul>
|
||
<li><strong>Site:</strong> Hugo + PaperMod</li>
|
||
<li><strong>Isso:</strong> listening on <code>127.0.0.1:8080</code>, stored locally (SQLite)</li>
|
||
<li><strong>Nginx:</strong> reverse-proxies <strong><code>/isso/</code></strong> to Isso on both the clearnet and the <code>.onion</code> vhost</li>
|
||
<li><strong>Hugo partial:</strong> renders the two panels (Isso & Giscus) and a switcher; Giscus is <strong>lazy-loaded</strong> and <strong>auto-rethemed</strong> with the site theme</li>
|
||
</ul>
|
||
<blockquote>
|
||
<p>I’m deliberately keeping Isso’s own config tiny in this appendix; the important part for my setup is the <strong>Nginx proxying</strong> and the <strong>Hugo partial</strong>.</p></blockquote>
|
||
<h2 id="hugo-partial-switcher--embeds">Hugo partial: switcher + embeds</h2>
|
||
<p>Save as <code>layouts/partials/comments.html</code> and include it in your single layout (e.g. <code>layouts/_default/single.html</code>) with <code>{{ partial "comments.html" . }}</code>.</p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span><span style="color:#75715e"><!-- comments.html --></span>
|
||
</span></span><span style="display:flex;"><span><<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">"comments-switch"</span> <span style="color:#a6e22e">style</span><span style="color:#f92672">=</span><span style="color:#e6db74">"display:flex;gap:.5rem;margin:.5rem 0 1rem;"</span>>
|
||
</span></span><span style="display:flex;"><span> <<span style="color:#f92672">button</span> <span style="color:#a6e22e">id</span><span style="color:#f92672">=</span><span style="color:#e6db74">"btn-isso"</span> <span style="color:#a6e22e">type</span><span style="color:#f92672">=</span><span style="color:#e6db74">"button"</span> <span style="color:#a6e22e">aria-pressed</span><span style="color:#f92672">=</span><span style="color:#e6db74">"true"</span>>Anonymous (Isso)</<span style="color:#f92672">button</span>>
|
||
</span></span><span style="display:flex;"><span> <<span style="color:#f92672">button</span> <span style="color:#a6e22e">id</span><span style="color:#f92672">=</span><span style="color:#e6db74">"btn-giscus"</span> <span style="color:#a6e22e">type</span><span style="color:#f92672">=</span><span style="color:#e6db74">"button"</span> <span style="color:#a6e22e">aria-pressed</span><span style="color:#f92672">=</span><span style="color:#e6db74">"false"</span>>GitHub (Giscus)</<span style="color:#f92672">button</span>>
|
||
</span></span><span style="display:flex;"><span> <<span style="color:#f92672">a</span> <span style="color:#a6e22e">href</span><span style="color:#f92672">=</span><span style="color:#e6db74">"https://github.com/AlipourIm/blog-comments/discussions/categories/comments"</span> <span style="color:#a6e22e">target</span><span style="color:#f92672">=</span><span style="color:#e6db74">"_blank"</span> <span style="color:#a6e22e">rel</span><span style="color:#f92672">=</span><span style="color:#e6db74">"noopener"</span>>Open on GitHub ↗</<span style="color:#f92672">a</span>>
|
||
</span></span><span style="display:flex;"><span></<span style="color:#f92672">div</span>>
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#75715e"><!-- Isso panel (default) --></span>
|
||
</span></span><span style="display:flex;"><span><<span style="color:#f92672">div</span> <span style="color:#a6e22e">id</span><span style="color:#f92672">=</span><span style="color:#e6db74">"panel-isso"</span>>
|
||
</span></span><span style="display:flex;"><span> <<span style="color:#f92672">section</span> <span style="color:#a6e22e">id</span><span style="color:#f92672">=</span><span style="color:#e6db74">"isso-thread"</span>></<span style="color:#f92672">section</span>>
|
||
</span></span><span style="display:flex;"><span> <<span style="color:#f92672">script</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">src</span><span style="color:#f92672">=</span><span style="color:#e6db74">"/isso/js/embed.min.js"</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">data-isso</span><span style="color:#f92672">=</span><span style="color:#e6db74">"/isso"</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">data-isso-css</span><span style="color:#f92672">=</span><span style="color:#e6db74">"true"</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">data-isso-lang</span><span style="color:#f92672">=</span><span style="color:#e6db74">"en"</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">data-isso-max-comments-nested</span><span style="color:#f92672">=</span><span style="color:#e6db74">"5"</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">data-isso-sorting</span><span style="color:#f92672">=</span><span style="color:#e6db74">"newest"</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">async</span>>
|
||
</span></span><span style="display:flex;"><span> </<span style="color:#f92672">script</span>>
|
||
</span></span><span style="display:flex;"><span> <<span style="color:#f92672">small</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">"isso-powered"</span> <span style="color:#a6e22e">style</span><span style="color:#f92672">=</span><span style="color:#e6db74">"display:block;margin:.5rem 0;color:var(--secondary,#888)"</span>>
|
||
</span></span><span style="display:flex;"><span> Comments powered by <<span style="color:#f92672">a</span> <span style="color:#a6e22e">href</span><span style="color:#f92672">=</span><span style="color:#e6db74">"https://isso-comments.de"</span> <span style="color:#a6e22e">target</span><span style="color:#f92672">=</span><span style="color:#e6db74">"_blank"</span> <span style="color:#a6e22e">rel</span><span style="color:#f92672">=</span><span style="color:#e6db74">"noopener"</span>>Isso</<span style="color:#f92672">a</span>>
|
||
</span></span><span style="display:flex;"><span> </<span style="color:#f92672">small</span>>
|
||
</span></span><span style="display:flex;"><span></<span style="color:#f92672">div</span>>
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#75715e"><!-- Giscus panel (lazy-loaded on click) --></span>
|
||
</span></span><span style="display:flex;"><span><<span style="color:#f92672">div</span> <span style="color:#a6e22e">id</span><span style="color:#f92672">=</span><span style="color:#e6db74">"panel-giscus"</span> <span style="color:#a6e22e">style</span><span style="color:#f92672">=</span><span style="color:#e6db74">"display:none"</span>>
|
||
</span></span><span style="display:flex;"><span> <<span style="color:#f92672">section</span> <span style="color:#a6e22e">id</span><span style="color:#f92672">=</span><span style="color:#e6db74">"giscus-thread"</span>></<span style="color:#f92672">section</span>>
|
||
</span></span><span style="display:flex;"><span></<span style="color:#f92672">div</span>>
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><<span style="color:#f92672">script</span>>
|
||
</span></span><span style="display:flex;"><span>(<span style="color:#66d9ef">function</span> () {
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">panelIsso</span> <span style="color:#f92672">=</span> document.<span style="color:#a6e22e">getElementById</span>(<span style="color:#e6db74">'panel-isso'</span>);
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">panelGisc</span> <span style="color:#f92672">=</span> document.<span style="color:#a6e22e">getElementById</span>(<span style="color:#e6db74">'panel-giscus'</span>);
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">issoBtn</span> <span style="color:#f92672">=</span> document.<span style="color:#a6e22e">getElementById</span>(<span style="color:#e6db74">'btn-isso'</span>);
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">giscBtn</span> <span style="color:#f92672">=</span> document.<span style="color:#a6e22e">getElementById</span>(<span style="color:#e6db74">'btn-giscus'</span>);
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">let</span> <span style="color:#a6e22e">gLoaded</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">false</span>;
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">isDark</span>() {
|
||
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// 1) explicit PaperMod preference
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">pref</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">localStorage</span>.<span style="color:#a6e22e">getItem</span>(<span style="color:#e6db74">'pref-theme'</span>);
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">pref</span> <span style="color:#f92672">===</span> <span style="color:#e6db74">'dark'</span>) <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">true</span>;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">pref</span> <span style="color:#f92672">===</span> <span style="color:#e6db74">'light'</span>) <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">false</span>;
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// 2) page state (classes / data-theme)
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">H</span> <span style="color:#f92672">=</span> document.<span style="color:#a6e22e">documentElement</span>, <span style="color:#a6e22e">B</span> <span style="color:#f92672">=</span> document.<span style="color:#a6e22e">body</span>;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">hasDark</span> <span style="color:#f92672">=</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">H</span>.<span style="color:#a6e22e">classList</span>.<span style="color:#a6e22e">contains</span>(<span style="color:#e6db74">'dark'</span>) <span style="color:#f92672">||</span> <span style="color:#a6e22e">B</span>.<span style="color:#a6e22e">classList</span>.<span style="color:#a6e22e">contains</span>(<span style="color:#e6db74">'dark'</span>) <span style="color:#f92672">||</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">H</span>.<span style="color:#a6e22e">dataset</span>.<span style="color:#a6e22e">theme</span> <span style="color:#f92672">===</span> <span style="color:#e6db74">'dark'</span> <span style="color:#f92672">||</span> <span style="color:#a6e22e">B</span>.<span style="color:#a6e22e">dataset</span>.<span style="color:#a6e22e">theme</span> <span style="color:#f92672">===</span> <span style="color:#e6db74">'dark'</span> <span style="color:#f92672">||</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">H</span>.<span style="color:#a6e22e">classList</span>.<span style="color:#a6e22e">contains</span>(<span style="color:#e6db74">'theme-dark'</span>) <span style="color:#f92672">||</span> <span style="color:#a6e22e">B</span>.<span style="color:#a6e22e">classList</span>.<span style="color:#a6e22e">contains</span>(<span style="color:#e6db74">'theme-dark'</span>);
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">hasDark</span>) <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">true</span>;
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// 3) OS preference
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">return</span> window.<span style="color:#a6e22e">matchMedia</span> <span style="color:#f92672">&&</span> window.<span style="color:#a6e22e">matchMedia</span>(<span style="color:#e6db74">'(prefers-color-scheme: dark)'</span>).<span style="color:#a6e22e">matches</span>;
|
||
</span></span><span style="display:flex;"><span> }
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">gTheme</span> <span style="color:#f92672">=</span> () => (<span style="color:#a6e22e">isDark</span>() <span style="color:#f92672">?</span> <span style="color:#e6db74">'dark_dimmed'</span> <span style="color:#f92672">:</span> <span style="color:#e6db74">'noborder_light'</span>);
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">loadGiscus</span>() {
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">gLoaded</span>) <span style="color:#66d9ef">return</span>;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">s</span> <span style="color:#f92672">=</span> document.<span style="color:#a6e22e">createElement</span>(<span style="color:#e6db74">'script'</span>);
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">src</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">'https://giscus.app/client.js'</span>;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">s</span>.<span style="color:#66d9ef">async</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">crossOrigin</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">'anonymous'</span>;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">setAttribute</span>(<span style="color:#e6db74">'data-repo'</span>,<span style="color:#e6db74">'AlipourIm/blog-comments'</span>);
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">setAttribute</span>(<span style="color:#e6db74">'data-repo-id'</span>,<span style="color:#e6db74">'R_kgDOQGARyA'</span>);
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">setAttribute</span>(<span style="color:#e6db74">'data-category'</span>,<span style="color:#e6db74">'Comments'</span>);
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">setAttribute</span>(<span style="color:#e6db74">'data-category-id'</span>,<span style="color:#e6db74">'DIC_kwDOQGARyM4Cw3x-'</span>);
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">setAttribute</span>(<span style="color:#e6db74">'data-mapping'</span>,<span style="color:#e6db74">'pathname'</span>);
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">setAttribute</span>(<span style="color:#e6db74">'data-strict'</span>,<span style="color:#e6db74">'0'</span>);
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">setAttribute</span>(<span style="color:#e6db74">'data-reactions-enabled'</span>,<span style="color:#e6db74">'1'</span>);
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">setAttribute</span>(<span style="color:#e6db74">'data-emit-metadata'</span>,<span style="color:#e6db74">'0'</span>);
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">setAttribute</span>(<span style="color:#e6db74">'data-input-position'</span>,<span style="color:#e6db74">'bottom'</span>);
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">setAttribute</span>(<span style="color:#e6db74">'data-lang'</span>,<span style="color:#e6db74">'en'</span>);
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">setAttribute</span>(<span style="color:#e6db74">'data-theme'</span>, <span style="color:#a6e22e">gTheme</span>()); <span style="color:#75715e">// initial theme
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> document.<span style="color:#a6e22e">getElementById</span>(<span style="color:#e6db74">'giscus-thread'</span>).<span style="color:#a6e22e">appendChild</span>(<span style="color:#a6e22e">s</span>);
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Retheme once iframe exists
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">reTheme</span> <span style="color:#f92672">=</span> () => {
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">iframe</span> <span style="color:#f92672">=</span> document.<span style="color:#a6e22e">querySelector</span>(<span style="color:#e6db74">'iframe.giscus-frame'</span>);
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span><span style="color:#a6e22e">iframe</span>) <span style="color:#66d9ef">return</span>;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">iframe</span>.<span style="color:#a6e22e">contentWindow</span><span style="color:#f92672">?</span>.<span style="color:#a6e22e">postMessage</span>(
|
||
</span></span><span style="display:flex;"><span> { <span style="color:#a6e22e">giscus</span><span style="color:#f92672">:</span> { <span style="color:#a6e22e">setConfig</span><span style="color:#f92672">:</span> { <span style="color:#a6e22e">theme</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">gTheme</span>() } } },
|
||
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">'https://giscus.app'</span>
|
||
</span></span><span style="display:flex;"><span> );
|
||
</span></span><span style="display:flex;"><span> };
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// PaperMod toggle button
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> document.<span style="color:#a6e22e">getElementById</span>(<span style="color:#e6db74">'theme-toggle'</span>)<span style="color:#f92672">?</span>.<span style="color:#a6e22e">addEventListener</span>(<span style="color:#e6db74">'click'</span>, () =>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">setTimeout</span>(<span style="color:#a6e22e">reTheme</span>, <span style="color:#ae81ff">0</span>)
|
||
</span></span><span style="display:flex;"><span> );
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Also react to attribute changes
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">mo</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">MutationObserver</span>(<span style="color:#a6e22e">reTheme</span>);
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">mo</span>.<span style="color:#a6e22e">observe</span>(document.<span style="color:#a6e22e">documentElement</span>, { <span style="color:#a6e22e">attributes</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span>, <span style="color:#a6e22e">attributeFilter</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">'class'</span>,<span style="color:#e6db74">'data-theme'</span>] });
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">mo</span>.<span style="color:#a6e22e">observe</span>(document.<span style="color:#a6e22e">body</span>, { <span style="color:#a6e22e">attributes</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span>, <span style="color:#a6e22e">attributeFilter</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">'class'</span>,<span style="color:#e6db74">'data-theme'</span>] });
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">gLoaded</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
|
||
</span></span><span style="display:flex;"><span> }
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">show</span>(<span style="color:#a6e22e">which</span>) {
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">showIsso</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">which</span> <span style="color:#f92672">===</span> <span style="color:#e6db74">'isso'</span>;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">panelIsso</span>.<span style="color:#a6e22e">style</span>.<span style="color:#a6e22e">display</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">showIsso</span> <span style="color:#f92672">?</span> <span style="color:#e6db74">'block'</span> <span style="color:#f92672">:</span> <span style="color:#e6db74">'none'</span>;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">panelGisc</span>.<span style="color:#a6e22e">style</span>.<span style="color:#a6e22e">display</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">showIsso</span> <span style="color:#f92672">?</span> <span style="color:#e6db74">'none'</span> <span style="color:#f92672">:</span> <span style="color:#e6db74">'block'</span>;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">issoBtn</span><span style="color:#f92672">?</span>.<span style="color:#a6e22e">setAttribute</span>(<span style="color:#e6db74">'aria-pressed'</span>, <span style="color:#a6e22e">showIsso</span>);
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">giscBtn</span><span style="color:#f92672">?</span>.<span style="color:#a6e22e">setAttribute</span>(<span style="color:#e6db74">'aria-pressed'</span>, <span style="color:#f92672">!</span><span style="color:#a6e22e">showIsso</span>);
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span><span style="color:#a6e22e">showIsso</span>) <span style="color:#a6e22e">loadGiscus</span>();
|
||
</span></span><span style="display:flex;"><span> }
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">issoBtn</span><span style="color:#f92672">?</span>.<span style="color:#a6e22e">addEventListener</span>(<span style="color:#e6db74">'click'</span>, <span style="color:#a6e22e">e</span> => { <span style="color:#a6e22e">e</span>.<span style="color:#a6e22e">preventDefault</span>(); <span style="color:#a6e22e">show</span>(<span style="color:#e6db74">'isso'</span>); });
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">giscBtn</span><span style="color:#f92672">?</span>.<span style="color:#a6e22e">addEventListener</span>(<span style="color:#e6db74">'click'</span>, <span style="color:#a6e22e">e</span> => { <span style="color:#a6e22e">e</span>.<span style="color:#a6e22e">preventDefault</span>(); <span style="color:#a6e22e">show</span>(<span style="color:#e6db74">'giscus'</span>); });
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// default
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#a6e22e">show</span>(<span style="color:#e6db74">'isso'</span>);
|
||
</span></span><span style="display:flex;"><span>})();
|
||
</span></span><span style="display:flex;"><span></<span style="color:#f92672">script</span>>
|
||
</span></span></code></pre></div><h2 id="nginx-proxy-isso-on-both-sites-and-avoid-the-404-onion-trap">Nginx: proxy Isso on <strong>both</strong> sites (and avoid the 404-onion trap)</h2>
|
||
<p>The bug I hit: my <code>.onion</code> server had a regex static block that <strong>won over</strong> the plain <code>/isso/</code> location, so <code>/isso/js/embed.min.js</code> returned <strong>404</strong>.</p>
|
||
<p><strong>Fix:</strong> make <code>/isso/</code> a <code>^~</code> prefix location so it beats regex, and place it <strong>above</strong> the static block.</p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#75715e"># /etc/nginx/sites-enabled/onion-blog (same idea for the clearnet vhost)
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">server</span> {
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> 127.0.0.1:<span style="color:#ae81ff">3301</span> <span style="color:#e6db74">default_server</span>;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion</span> 127.0.0.1 <span style="color:#e6db74">localhost</span>;
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">Referrer-Policy</span> <span style="color:#e6db74">no-referrer</span> <span style="color:#e6db74">always</span>;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">X-Content-Type-Options</span> <span style="color:#e6db74">nosniff</span> <span style="color:#e6db74">always</span>;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">X-Frame-Options</span> <span style="color:#e6db74">SAMEORIGIN</span> <span style="color:#e6db74">always</span>;
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">root</span> <span style="color:#e6db74">/srv/hugo/mysite/public-onion</span>;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">index</span> <span style="color:#e6db74">index.html</span>;
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">location</span> <span style="color:#e6db74">/</span> { <span style="color:#f92672">try_files</span> $uri $uri/ <span style="color:#e6db74">/index.html</span>; }
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># /isso -> /isso/
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">location</span> = <span style="color:#e6db74">/isso</span> { <span style="color:#f92672">return</span> <span style="color:#ae81ff">301</span> <span style="color:#e6db74">/isso/</span>; }
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># IMPORTANT: this must beat regex locations below
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">location</span> <span style="color:#e6db74">^~</span> <span style="color:#e6db74">/isso/</span> {
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_pass</span> <span style="color:#e6db74">http://127.0.0.1:8080/</span>; <span style="color:#75715e"># Isso container/port
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">proxy_http_version</span> <span style="color:#ae81ff">1</span><span style="color:#e6db74">.1</span>;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Host</span> $host;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-Host</span> $host;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-Proto</span> $scheme;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-For</span> $proxy_add_x_forwarded_for;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_redirect</span> <span style="color:#66d9ef">off</span>;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_read_timeout</span> <span style="color:#e6db74">60s</span>;
|
||
</span></span><span style="display:flex;"><span> }
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># static
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">location</span> ~<span style="color:#e6db74">*</span> <span style="color:#e6db74">\\.(css|js|ico|png|jpg|jpeg|gif|svg|webp|txt|xml)</span>$ {
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">access_log</span> <span style="color:#66d9ef">off</span>;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">Cache-Control</span> <span style="color:#e6db74">"public,</span> <span style="color:#e6db74">max-age=31536000,</span> <span style="color:#e6db74">immutable"</span>;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">try_files</span> $uri =<span style="color:#ae81ff">404</span>;
|
||
</span></span><span style="display:flex;"><span> }
|
||
</span></span><span style="display:flex;"><span>}
|
||
</span></span></code></pre></div><p>Reload and smoke test:</p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo nginx -t <span style="color:#f92672">&&</span> sudo systemctl reload nginx
|
||
</span></span><span style="display:flex;"><span>curl -I http://<your-onion-host>/isso/js/embed.min.js <span style="color:#75715e"># expect 200</span>
|
||
</span></span></code></pre></div><blockquote>
|
||
<p>If you ever point <code>data-isso</code> to a <strong>different origin</strong>, add CORS headers on that vhost. For same-origin (<code>data-isso="/isso"</code>), you don’t need CORS.</p></blockquote>
|
||
<h2 id="small-css-tweaks-one-color-across-lightdark--spacing">Small CSS tweaks (one color across light/dark + spacing)</h2>
|
||
<p>I wanted the same blue for <strong>reply / edit / delete</strong> links and for <strong>Submit / Preview</strong> buttons, in both themes, and a little spacing:</p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-css" data-lang="css"><span style="display:flex;"><span><span style="color:#75715e">/* put this in your site CSS */</span>
|
||
</span></span><span style="display:flex;"><span>:<span style="color:#a6e22e">root</span> { --btn-blue: <span style="color:#ae81ff">#2563eb</span>; } <span style="color:#75715e">/* adjust to taste */</span>
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#75715e">/* Isso form buttons */</span>
|
||
</span></span><span style="display:flex;"><span>.<span style="color:#a6e22e">isso-post-action</span> <span style="color:#f92672">input</span><span style="color:#f92672">[</span><span style="color:#f92672">type</span><span style="color:#f92672">=</span><span style="color:#e6db74">"submit"</span><span style="color:#f92672">],</span>
|
||
</span></span><span style="display:flex;"><span>.<span style="color:#a6e22e">isso-post-action</span> <span style="color:#f92672">input</span><span style="color:#f92672">[</span><span style="color:#f92672">name</span><span style="color:#f92672">=</span><span style="color:#e6db74">"preview"</span><span style="color:#f92672">]</span> {
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">background</span>: <span style="color:#a6e22e">var</span>(<span style="color:#f92672">--</span>btn<span style="color:#f92672">-</span><span style="color:#66d9ef">blue</span>) <span style="color:#75715e">!important</span>;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">color</span>: <span style="color:#ae81ff">#fff</span> <span style="color:#75715e">!important</span>;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">border</span>: <span style="color:#66d9ef">none</span>;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">padding</span>: <span style="color:#ae81ff">.5</span><span style="color:#66d9ef">rem</span> <span style="color:#ae81ff">.75</span><span style="color:#66d9ef">rem</span>;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">border-radius</span>: <span style="color:#ae81ff">.4</span><span style="color:#66d9ef">rem</span>;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">cursor</span>: <span style="color:#66d9ef">pointer</span>;
|
||
</span></span><span style="display:flex;"><span>}
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#75715e">/* link actions under comments */</span>
|
||
</span></span><span style="display:flex;"><span>.<span style="color:#a6e22e">isso-comment-footer</span> <span style="color:#f92672">a</span> {
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">color</span>: <span style="color:#a6e22e">var</span>(<span style="color:#f92672">--</span>btn<span style="color:#f92672">-</span><span style="color:#66d9ef">blue</span>) <span style="color:#75715e">!important</span>;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">margin-right</span>: <span style="color:#ae81ff">.5</span><span style="color:#66d9ef">rem</span>; <span style="color:#75715e">/* gives "edit" and "delete" some breathing room */</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">text-decoration</span>: <span style="color:#66d9ef">none</span>;
|
||
</span></span><span style="display:flex;"><span>}
|
||
</span></span><span style="display:flex;"><span>.<span style="color:#a6e22e">isso-comment-footer</span> <span style="color:#f92672">a</span>:<span style="color:#a6e22e">hover</span> { <span style="color:#66d9ef">text-decoration</span>: <span style="color:#66d9ef">underline</span>; }
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#75715e">/* keep name/email/website stacked, slightly larger */</span>
|
||
</span></span><span style="display:flex;"><span>.<span style="color:#a6e22e">isso-auth-section</span> .<span style="color:#a6e22e">isso-input-wrapper</span> {
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">display</span>: <span style="color:#66d9ef">block</span>;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">margin</span>: <span style="color:#ae81ff">.35</span><span style="color:#66d9ef">rem</span> <span style="color:#ae81ff">0</span>;
|
||
</span></span><span style="display:flex;"><span>}
|
||
</span></span><span style="display:flex;"><span>.<span style="color:#a6e22e">isso-auth-section</span> <span style="color:#f92672">label</span> { <span style="color:#66d9ef">font-size</span>: <span style="color:#ae81ff">.95</span><span style="color:#66d9ef">rem</span>; }
|
||
</span></span><span style="display:flex;"><span>.<span style="color:#a6e22e">isso-auth-section</span> <span style="color:#f92672">input</span><span style="color:#f92672">[</span><span style="color:#f92672">type</span><span style="color:#f92672">=</span><span style="color:#e6db74">"text"</span><span style="color:#f92672">],</span>
|
||
</span></span><span style="display:flex;"><span>.<span style="color:#a6e22e">isso-auth-section</span> <span style="color:#f92672">input</span><span style="color:#f92672">[</span><span style="color:#f92672">type</span><span style="color:#f92672">=</span><span style="color:#e6db74">"email"</span><span style="color:#f92672">],</span>
|
||
</span></span><span style="display:flex;"><span>.<span style="color:#a6e22e">isso-auth-section</span> <span style="color:#f92672">input</span><span style="color:#f92672">[</span><span style="color:#f92672">type</span><span style="color:#f92672">=</span><span style="color:#e6db74">"url"</span><span style="color:#f92672">]</span> {
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">width</span>: <span style="color:#ae81ff">100</span><span style="color:#66d9ef">%</span>;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">font-size</span>: <span style="color:#ae81ff">1</span><span style="color:#66d9ef">rem</span>;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">padding</span>: <span style="color:#ae81ff">.5</span><span style="color:#66d9ef">rem</span> <span style="color:#ae81ff">.6</span><span style="color:#66d9ef">rem</span>;
|
||
</span></span><span style="display:flex;"><span>}
|
||
</span></span></code></pre></div><h2 id="what-changed-the-fixes-i-actually-made">What changed (the fixes I actually made)</h2>
|
||
<ol>
|
||
<li>
|
||
<p><strong>Theme correctness for Giscus.</strong><br>
|
||
PaperMod doesn’t add <code>.light</code>—it only toggles <code>.dark</code> and stores <code>pref-theme</code>. I now:</p>
|
||
<ul>
|
||
<li>read <code>localStorage.pref-theme</code> first,</li>
|
||
<li>fall back to class/data-theme, then</li>
|
||
<li>fall back to OS preference.<br>
|
||
I also re-theme the Giscus iframe on every toggle via <code>postMessage</code>.</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<p><strong>Isso over Tor.</strong><br>
|
||
On the <code>.onion</code> vhost, I moved the proxy to a <code>^~ /isso/</code> block so it beats the static regex. That fixed the <code>404</code> on <code>/isso/js/embed.min.js</code>.</p>
|
||
</li>
|
||
<li>
|
||
<p><strong>UI polish.</strong></p>
|
||
<ul>
|
||
<li>Unified button/link color across themes,</li>
|
||
<li>ensured “edit”/“delete” aren’t touching,</li>
|
||
<li>kept the author/email/website inputs <strong>stacked</strong>, and</li>
|
||
<li>left <strong>Submit</strong>/<strong>Preview</strong> styled and grouped.</li>
|
||
</ul>
|
||
</li>
|
||
</ol>
|
||
<h2 id="sanity-check">Sanity check</h2>
|
||
<ul>
|
||
<li>Toggle the theme: Giscus switches between <code>noborder_light</code> and <code>dark_dimmed</code> without reload.</li>
|
||
<li>Open DevTools → Network on onion: <code>/isso/js/embed.min.js</code> loads with <strong>200</strong>.</li>
|
||
<li>Switcher buttons swap panels instantly; Giscus only loads when clicked.</li>
|
||
</ul>
|
||
<h2 id="8-final-notes">8) Final notes!</h2>
|
||
<p>I could not fully fix the style of Isso.
|
||
CSS is not easy… drat!</p>
|
||
<p>I don’t think anyone actually reads the blog, let alone comment on it.
|
||
But it felt cool to have a comment section and also RSS for it.
|
||
I had a lot of not so fun debugging experience, and will probably work on the styling to make it look right, but for now I’m happy with it! ^^</p>
|
||
<h1 id="admin-note--delete-all-isso-comments-for-a-single-post">Admin note — Delete all <strong>Isso</strong> comments for a single post</h1>
|
||
<p>This note is for future me: how to remove every comment tied to one post in <strong>Isso</strong>.<br>
|
||
Tested with SQLite-backed Isso (default). Works whether you serve via clearnet or <code>.onion</code> — because this operates directly on the database.</p>
|
||
<hr>
|
||
<h2 id="tldr-host-with-sqlite">TL;DR (host with SQLite)</h2>
|
||
<blockquote>
|
||
<p>Replace the placeholders for <code>DB</code> and <code>POST_URI</code> first.</p></blockquote>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># 0) Variables — edit these two lines for your setup</span>
|
||
</span></span><span style="display:flex;"><span>DB<span style="color:#f92672">=</span><span style="color:#e6db74">"/var/lib/isso/comments.db"</span> <span style="color:#75715e"># path to your Isso SQLite DB (check isso.conf: [general] dbpath)</span>
|
||
</span></span><span style="display:flex;"><span>POST_URI<span style="color:#f92672">=</span><span style="color:#e6db74">"/posts/skin_routine/"</span> <span style="color:#75715e"># the post's URI as stored by Isso (often just the path)</span>
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 1) Backup (always do this!)</span>
|
||
</span></span><span style="display:flex;"><span>sqlite3 <span style="color:#e6db74">"</span>$DB<span style="color:#e6db74">"</span> <span style="color:#e6db74">".backup '</span><span style="color:#66d9ef">$(</span>dirname <span style="color:#e6db74">"</span>$DB<span style="color:#e6db74">"</span><span style="color:#66d9ef">)</span><span style="color:#e6db74">/comments.</span><span style="color:#66d9ef">$(</span>date +%F-%H%M%S<span style="color:#66d9ef">)</span><span style="color:#e6db74">.backup.sqlite3'"</span>
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 2) Inspect: find the thread row and preview the comments</span>
|
||
</span></span><span style="display:flex;"><span>sqlite3 <span style="color:#e6db74">"</span>$DB<span style="color:#e6db74">"</span> <span style="color:#e6db74">"
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">.headers on
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">.mode column
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">SELECT id, uri FROM threads WHERE uri LIKE '%' || '</span>$POST_URI<span style="color:#e6db74">' || '%';
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">SELECT id, tid, created, author, text
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">FROM comments
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">WHERE tid IN (SELECT id FROM threads WHERE uri LIKE '%' || '</span>$POST_URI<span style="color:#e6db74">' || '%')
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">ORDER BY created DESC
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">LIMIT 25;
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">"</span>
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 3) Delete all comments for that post (thread)</span>
|
||
</span></span><span style="display:flex;"><span>sqlite3 <span style="color:#e6db74">"</span>$DB<span style="color:#e6db74">"</span> <span style="color:#e6db74">"
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">DELETE FROM comments
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">WHERE tid IN (SELECT id FROM threads WHERE uri LIKE '%' || '</span>$POST_URI<span style="color:#e6db74">' || '%');
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">"</span>
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 4) (Optional) Remove the now-empty thread row too</span>
|
||
</span></span><span style="display:flex;"><span>sqlite3 <span style="color:#e6db74">"</span>$DB<span style="color:#e6db74">"</span> <span style="color:#e6db74">"
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">DELETE FROM threads WHERE uri LIKE '%' || '</span>$POST_URI<span style="color:#e6db74">' || '%';
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">VACUUM;
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">"</span>
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 5) Done. Isso usually picks this up automatically.</span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#75715e"># If you're containerized and want to be safe:</span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#75715e"># docker restart <isso_container_name></span>
|
||
</span></span></code></pre></div><hr>
|
||
<h2 id="if-running-isso-in-docker">If running Isso in Docker</h2>
|
||
<p>Open a shell in the container, then run the same steps:</p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker exec -it <isso_container_name> /bin/sh
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Inside the container:</span>
|
||
</span></span><span style="display:flex;"><span>DB<span style="color:#f92672">=</span><span style="color:#e6db74">"/db/comments.db"</span> <span style="color:#75715e"># or /data/comments.db, check your image/volume mapping</span>
|
||
</span></span><span style="display:flex;"><span>POST_URI<span style="color:#f92672">=</span><span style="color:#e6db74">"/posts/skin_routine/"</span>
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span>sqlite3 <span style="color:#e6db74">"</span>$DB<span style="color:#e6db74">"</span> <span style="color:#e6db74">".backup '/db/comments.</span><span style="color:#66d9ef">$(</span>date +%F-%H%M%S<span style="color:#66d9ef">)</span><span style="color:#e6db74">.backup.sqlite3'"</span>
|
||
</span></span><span style="display:flex;"><span>sqlite3 <span style="color:#e6db74">"</span>$DB<span style="color:#e6db74">"</span> <span style="color:#e6db74">"SELECT id, uri FROM threads WHERE uri LIKE '%' || '</span>$POST_URI<span style="color:#e6db74">' || '%';"</span>
|
||
</span></span><span style="display:flex;"><span>sqlite3 <span style="color:#e6db74">"</span>$DB<span style="color:#e6db74">"</span> <span style="color:#e6db74">"DELETE FROM comments WHERE tid IN (SELECT id FROM threads WHERE uri LIKE '%' || '</span>$POST_URI<span style="color:#e6db74">' || '%');"</span>
|
||
</span></span><span style="display:flex;"><span>sqlite3 <span style="color:#e6db74">"</span>$DB<span style="color:#e6db74">"</span> <span style="color:#e6db74">"DELETE FROM threads WHERE uri LIKE '%' || '</span>$POST_URI<span style="color:#e6db74">' || '%'; VACUUM;"</span>
|
||
</span></span><span style="display:flex;"><span>exit
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Optionally restart the container:</span>
|
||
</span></span><span style="display:flex;"><span>docker restart <isso_container_name>
|
||
</span></span></code></pre></div><hr>
|
||
<h2 id="notes--gotchas">Notes & gotchas</h2>
|
||
<ul>
|
||
<li><strong>Find the real DB path</strong>: In <code>isso.conf</code> (often <code>/etc/isso/isso.conf</code> or within your container at <code>/config/isso.conf</code>), look for:
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ini" data-lang="ini"><span style="display:flex;"><span><span style="color:#66d9ef">[general]</span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">dbpath</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">/var/lib/isso/comments.db</span>
|
||
</span></span></code></pre></div></li>
|
||
<li><strong>What goes in <code>POST_URI</code>?</strong> Isso stores a <code>uri</code> string per thread. With the default embed config, it’s usually the <strong>path</strong> (<code>/posts/<slug>/</code>). If you’re unsure, list recent threads:
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sqlite3 <span style="color:#e6db74">"</span>$DB<span style="color:#e6db74">"</span> <span style="color:#e6db74">".headers on"</span> <span style="color:#e6db74">".mode column"</span> <span style="color:#e6db74">"SELECT id, uri FROM threads ORDER BY id DESC LIMIT 50;"</span>
|
||
</span></span></code></pre></div></li>
|
||
<li><strong>Clearnet vs <code>.onion</code></strong>: If you load the same post under multiple origins <em>and</em> your embed setup stores full origins, you may have <strong>multiple thread rows</strong>. The <code>LIKE</code> query above deliberately matches any uri containing your <code>POST_URI</code> to catch those.</li>
|
||
<li><strong>Rollback</strong>: To undo, stop Isso (optional), replace the DB with your backup (<code>comments.YYYY-MM-DD-HHMMSS.backup.sqlite3</code>), and start Isso again.</li>
|
||
<li><strong>HTTP API option</strong>: Isso supports <code>DELETE /<comment_id></code> if admin endpoints/auth are enabled. For bulk removal it’s simpler and safer to use SQLite directly.</li>
|
||
<li><strong>Safety</strong>: Always run a backup first, and <strong>preview with the SELECT</strong> before you run the DELETE.</li>
|
||
</ul>
|
||
<hr>
|
||
<h2 id="one-liners-for-the-impatient">One-liners for the impatient</h2>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>DB<span style="color:#f92672">=</span><span style="color:#e6db74">"/var/lib/isso/comments.db"</span>; POST_URI<span style="color:#f92672">=</span><span style="color:#e6db74">"/posts/skin_routine/"</span>
|
||
</span></span><span style="display:flex;"><span>sqlite3 <span style="color:#e6db74">"</span>$DB<span style="color:#e6db74">"</span> <span style="color:#e6db74">".backup '</span><span style="color:#66d9ef">$(</span>dirname <span style="color:#e6db74">"</span>$DB<span style="color:#e6db74">"</span><span style="color:#66d9ef">)</span><span style="color:#e6db74">/comments.</span><span style="color:#66d9ef">$(</span>date +%F-%H%M%S<span style="color:#66d9ef">)</span><span style="color:#e6db74">.backup.sqlite3'"</span>
|
||
</span></span><span style="display:flex;"><span>sqlite3 <span style="color:#e6db74">"</span>$DB<span style="color:#e6db74">"</span> <span style="color:#e6db74">"DELETE FROM comments WHERE tid IN (SELECT id FROM threads WHERE uri LIKE '%' || '</span>$POST_URI<span style="color:#e6db74">' || '%');"</span>
|
||
</span></span><span style="display:flex;"><span>sqlite3 <span style="color:#e6db74">"</span>$DB<span style="color:#e6db74">"</span> <span style="color:#e6db74">"DELETE FROM threads WHERE uri LIKE '%' || '</span>$POST_URI<span style="color:#e6db74">' || '%'; VACUUM;"</span>
|
||
</span></span></code></pre></div><hr>
|
||
<p><strong>Isso container: quick checks & fixes for future me! (thank past me later ^^)</strong></p>
|
||
<p><strong>Find the container</strong></p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker ps -a --format <span style="color:#e6db74">'table {{.ID}} {{.Names}} {{.Status}} {{.Ports}}'</span> | grep -i isso
|
||
</span></span></code></pre></div><p><strong>Inspect basic state</strong></p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker inspect -f <span style="color:#e6db74">'Name={{.Name}} Running={{.State.Running}} Status={{.State.Status}} StartedAt={{.State.StartedAt}} FinishedAt={{.State.FinishedAt}}'</span> isso
|
||
</span></span></code></pre></div><p><strong>Logs</strong></p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker logs --tail<span style="color:#f92672">=</span><span style="color:#ae81ff">200</span> isso
|
||
</span></span><span style="display:flex;"><span>docker logs -f isso
|
||
</span></span></code></pre></div><p><strong>Ports & reachability</strong></p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker port isso
|
||
</span></span><span style="display:flex;"><span>sudo ss -ltnp | grep <span style="color:#e6db74">':8080 '</span>
|
||
</span></span><span style="display:flex;"><span>curl -i http://127.0.0.1:8080/
|
||
</span></span></code></pre></div><p><strong>Restart policy (keep it running)</strong></p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker inspect -f <span style="color:#e6db74">'{{.HostConfig.RestartPolicy.Name}}'</span> isso
|
||
</span></span><span style="display:flex;"><span>docker update --restart unless-stopped isso
|
||
</span></span></code></pre></div><p><strong>Start/stop the container</strong></p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker start isso
|
||
</span></span><span style="display:flex;"><span>docker stop isso
|
||
</span></span></code></pre></div><p><strong>Who/what stopped it (events)</strong></p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker events --since 24h --filter container<span style="color:#f92672">=</span>isso
|
||
</span></span></code></pre></div><p><strong>Common config issue: duplicate options/sections</strong>
|
||
If logs show DuplicateOptionError or DuplicateSectionError, fix <code>/config/isso.cfg</code> (remove duplicate <code>[server]</code> blocks or duplicate <code>public-endpoint</code>).</p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>nl -ba /config/isso.cfg | sed -n <span style="color:#e6db74">'1,120p'</span>
|
||
</span></span><span style="display:flex;"><span>grep -n <span style="color:#e6db74">'^\[server\]'</span> /config/isso.cfg
|
||
</span></span><span style="display:flex;"><span>grep -n <span style="color:#e6db74">'^public-endpoint'</span> /config/isso.cfg
|
||
</span></span></code></pre></div><p><strong>Using Docker Compose</strong> <em>(run these in the directory with your compose file)</em></p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker compose ps
|
||
</span></span><span style="display:flex;"><span>docker compose logs --tail<span style="color:#f92672">=</span><span style="color:#ae81ff">200</span> isso
|
||
</span></span><span style="display:flex;"><span>docker compose up -d isso
|
||
</span></span></code></pre></div><p><strong>If the container doesn’t exist (create/recreate)</strong></p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker image ls | grep -i isso
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span>docker run -d --name isso -p 8080:8080 -v /srv/isso/config:/config -v /srv/isso/db:/db --restart unless-stopped ghcr.io/posativ/isso:latest
|
||
</span></span></code></pre></div><p><strong>Optional: exclude from auto-updaters (like Watchtower)</strong></p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker update --label-add com.centurylinklabs.watchtower.enable<span style="color:#f92672">=</span>false isso
|
||
</span></span></code></pre></div><p><em>This note is intentionally self-contained so I can paste it at the end of the blog post as a maintainer-only appendix.</em></p>
|
||
<hr>
|
||
<div class="signature-block" style="margin-top:1rem">
|
||
<p><strong>Downloads:</strong>
|
||
<a href="/sources/posts/new_features.md">Markdown</a> ·
|
||
<a href="/sources/posts/new_features.md.asc">Signature (.asc)</a>
|
||
</p><details class="signature">
|
||
<summary>View OpenPGP signature</summary>
|
||
<pre style="font-size:0.85em; overflow-x:auto; padding:0.75rem;">-----BEGIN PGP SIGNATURE-----
|
||
|
||
iQIzBAABCgAdFiEEVaKl3oR5K6zGyu4/tYgoUOBMjSoFAmpkbuMACgkQtYgoUOBM
|
||
jSr2VxAAgdHpf+4EIx2ASmSB+MeAHp7s1+2EPhmn96QuhiQO9Dr1e9250LbF/R8S
|
||
A0zcN8mmyOtKv2rediU6HsGy6ddwdTAtpQDRvMw2Xuk2pBUZaG5LuvlNgSse9zVB
|
||
dcG3GVLuMSRgNmyolhFoSWn46aa+3wZGrzYZQVUt8op+2fVp36agq2YoB4zeGKSm
|
||
Ri43rO/kTingD0bclOA+SMbHpKQOcLyHwDlVrqIjVXcJ/kKLcm1G3Z5owo+gB6jY
|
||
EMjxYiqyf5Zcbt4q/WzojbHLffjXoMzOgZ1sDOIObVQni9atgV49X4oIZ3+xSXSH
|
||
W7ZbH6sg9LlrU8djddBXUKB0i72IVZ/4F5icVFUcK+szOmASy8fFP7gCOcCRATtb
|
||
eNIFtiAlRXI0CqJdeq5fE9b+LX8lRpNnX229tg7GFgddzYUmFkKAsoJ9EUzUvUHm
|
||
Xzqlm9DKy9LG/CeOxo473fIo4YCT2fcmMnt9nCZW4iDKOVl1nCupkTn5qsfNdpQM
|
||
KycaNwsLBHPZWV+jDon8NEzYQk07n1Q9rlEWdL0egvn2S+pYnvLZbfi5dRhe+ciC
|
||
qcEW/I1NZZRdU7MUEzhWvhbWsZtTkm6OevXjnqACv91DIQv7tNrKwZuASnpFeDRa
|
||
GE0QYRrsaI3vCLR3cUmBAFsvZdwgzH0RQLb8w21XBW/BoFvixbA=
|
||
=9k+S
|
||
-----END PGP SIGNATURE-----
|
||
</pre>
|
||
</details><p><em>Verify locally:</em></p>
|
||
<pre style="font-size:0.85em; overflow-x:auto; padding:0.75rem;">torsocks curl -fSLO http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/sources/posts/new_features.md
|
||
torsocks curl -fSLO http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/sources/posts/new_features.md.asc
|
||
gpg --verify new_features.md.asc new_features.md
|
||
</pre>
|
||
</div>
|
||
|
||
|
||
]]></content:encoded>
|
||
</item>
|
||
</channel>
|
||
</rss>
|