initial release

This commit is contained in:
Eric Bower 2022-07-13 13:30:27 -04:00
commit 8d86bdd33d
No known key found for this signature in database
GPG key ID: 7A51A47D76D3FD02
45 changed files with 2558 additions and 0 deletions

18
html/base.layout.tmpl Normal file
View file

@ -0,0 +1,18 @@
{{define "base"}}
<!doctype html>
<html lang="en" data-theme="theme-dark">
<head>
<meta charset='utf-8'>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{{template "title" .}}</title>
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<meta name="keywords" content="pastebin, paste, copy" />
{{template "meta" .}}
<link rel="stylesheet" href="/main.css" />
</head>
<body>{{template "body" .}}</body>
</html>
{{end}}

44
html/blog.page.tmpl Normal file
View file

@ -0,0 +1,44 @@
{{template "base" .}}
{{define "title"}}{{.PageTitle}}{{end}}
{{define "meta"}}
<meta name="description" content="{{if .Header.Bio}}{{.Header.Bio}}{{else}}{{.Header.Title}}{{end}}" />
<meta property="og:type" content="website">
<meta property="og:site_name" content="{{.Site.Domain}}">
<meta property="og:url" content="{{.URL}}">
<meta property="og:title" content="{{.Header.Title}}">
{{if .Header.Bio}}<meta property="og:description" content="{{.Header.Bio}}">{{end}}
<meta property="og:image:width" content="300" />
<meta property="og:image:height" content="300" />
<meta itemprop="image" content="https://{{.Site.Domain}}/card.png" />
<meta property="og:image" content="https://{{.Site.Domain}}/card.png" />
<meta property="twitter:card" content="summary">
<meta property="twitter:url" content="{{.URL}}">
<meta property="twitter:title" content="{{.Header.Title}}">
{{if .Header.Bio}}<meta property="twitter:description" content="{{.Header.Bio}}">{{end}}
<meta name="twitter:image" content="https://{{.Site.Domain}}/card.png" />
<meta name="twitter:image:src" content="https://{{.Site.Domain}}/card.png" />
{{end}}
{{define "body"}}
<header class="text-center">
<h1 class="text-2xl font-bold">{{.Header.Title}}</h1>
<hr />
</header>
<main>
<section class="posts">
{{range .Posts}}
<article>
<div class="flex items-center">
<time datetime="{{.PublishAtISO}}" class="font-italic text-sm post-date">{{.PublishAt}}</time>
<h2 class="font-bold flex-1"><a href="{{.URL}}">{{.Title}}</a></h2>
</div>
</article>
{{end}}
</section>
</main>
{{template "footer" .}}
{{end}}

6
html/footer.partial.tmpl Normal file
View file

@ -0,0 +1,6 @@
{{define "footer"}}
<footer>
<hr />
published with <a href={{.Site.HomeURL}}>{{.Site.Domain}}</a>
</footer>
{{end}}

108
html/help.page.tmpl Normal file
View file

@ -0,0 +1,108 @@
{{template "base" .}}
{{define "title"}}help -- {{.Site.Domain}}{{end}}
{{define "meta"}}
<meta name="description" content="questions and answers" />
{{end}}
{{define "body"}}
<header>
<h1 class="text-2xl">Need help?</h1>
<p>Here are some common questions on using this platform that we would like to answer.</p>
</header>
<main>
<section id="permission-denied">
<h2 class="text-xl">I get a permission denied when trying to SSH</h2>
<p>
Unfortunately SHA-2 RSA keys are <strong>not</strong> currently supported.
</p>
<p>
Unfortunately, due to a shortcoming in Gos x/crypto/ssh package, Soft Serve does
not currently support access via new SSH RSA keys: only the old SHA-1 ones will work.
Until we sort this out youll either need an SHA-1 RSA key or a key with another
algorithm, e.g. Ed25519. Not sure what type of keys you have? You can check with the
following:
</p>
<pre>$ find ~/.ssh/id_*.pub -exec ssh-keygen -l -f {} \;</pre>
<p>If youre curious about the inner workings of this problem have a look at:</p>
<ul>
<li><a href="https://github.com/golang/go/issues/37278">golang/go#37278</a></li>
<li><a href="https://go-review.googlesource.com/c/crypto/+/220037">go-review</a></li>
<li><a href="https://github.com/golang/crypto/pull/197">golang/crypto#197</a></li>
</ul>
</section>
<section id="ssh-key">
<h2 class="text-xl">Generating a new SSH key</h2>
<p>
<a href="https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent">Github reference</a>
</p>
<pre>ssh-keygen -t ed25519 -C "your_email@example.com"</pre>
<ol>
<li>When you're prompted to "Enter a file in which to save the key," press Enter. This accepts the default file location.</li>
<li>At the prompt, type a secure passphrase.</li>
</ol>
</section>
<section id="post-update">
<h2 class="text-xl">How do I update a pastebin post?</h2>
<p>
Updating a post requires that you update the source document and then run the <code>scp</code>
command again. If the filename remains the same, then the post will be updated.
</p>
</section>
<section id="post-delete">
<h2 class="text-xl">How do I delete a post?</h2>
<p>
Because <code>scp</code> does not natively support deleting files, I didn't want to bake
that behavior into my ssh server.
</p>
<p>
However, if a user wants to delete a post they can delete the contents of the file and
then upload it to our server. If the file contains 0 bytes, we will remove the post.
For example, if you want to delete <code>delete.txt</code> you could:
</p>
<pre>
cp /dev/null delete.txt
scp ./delete.txt {{.Site.Domain}}:/</pre>
<p>
Alternatively, you can go to <code>ssh {{.Site.Domain}}</code> and select "Manage posts."
Then you can highlight the post you want to delete and then press "X." It will ask for
confirmation before actually removing the post.
</p>
</section>
<section id="post-upload-single-file">
<h2 class="text-xl">When I want to publish a new post, do I have to upload all posts everytime?</h2>
<p>
Nope! Just <code>scp</code> the file you want to publish. For example, if you created
a new post called <code>taco-tuesday.md</code> then you would publish it like this:
</p>
<pre>scp ./taco-tuesday.md {{.Site.Domain}}:</pre>
</section>
<section id="pastebin-url">
<h2 class="text-xl">What is my pastebin URL?</h2>
<pre>https://{username}.{{.Site.Domain}}</pre>
</section>
<section id="multiple-accounts">
<h2 class="text-xl">Can I create multiple accounts?</h2>
<p>
Yes! You can either a) create a new keypair and use that for authentication
or b) use the same keypair and ssh into our CMS using our special username
<code>ssh new@{{.Site.Domain}}</code>.
</p>
<p>
Please note that if you use the same keypair for multiple accounts, you will need to
always specify the user when logging into our CMS.
</p>
</section>
</main>
{{template "marketing-footer" .}}
{{end}}

View file

@ -0,0 +1,12 @@
{{define "marketing-footer"}}
<footer>
<hr />
<p class="font-italic">Built and maintained by <a href="https://pico.sh">pico.sh</a>.</p>
<div>
<a href="/">home</a> |
<a href="/ops">ops</a> |
<a href="/help">help</a> |
<a href="https://git.sr.ht/~erock/pastes.sh">source</a>
</div>
</footer>
{{end}}

99
html/marketing.page.tmpl Normal file
View file

@ -0,0 +1,99 @@
{{template "base" .}}
{{define "title"}}{{.Site.Domain}} -- a pastebin for hackers{{end}}
{{define "meta"}}
<meta name="description" content="a pastebin for hackers" />
<meta property="og:type" content="website">
<meta property="og:site_name" content="{{.Site.Domain}}">
<meta property="og:url" content="https://{{.Site.Domain}}">
<meta property="og:title" content="{{.Site.Domain}}">
<meta property="og:description" content="a pastebin for hackers">
<meta name="twitter:card" content="summary" />
<meta property="twitter:url" content="https://{{.Site.Domain}}">
<meta property="twitter:title" content="{{.Site.Domain}}">
<meta property="twitter:description" content="a pastebin platform for hackers">
<meta name="twitter:image" content="https://{{.Site.Domain}}/card.png" />
<meta name="twitter:image:src" content="https://{{.Site.Domain}}/card.png" />
<meta property="og:image:width" content="300" />
<meta property="og:image:height" content="300" />
<meta itemprop="image" content="https://{{.Site.Domain}}/card.png" />
<meta property="og:image" content="https://{{.Site.Domain}}/card.png" />
{{end}}
{{define "body"}}
<header class="text-center">
<h1 class="text-2xl font-bold">{{.Site.Domain}}</h1>
<p class="text-lg">a pastebin platform for hackers</p>
<p class="text-lg"><a href="/read">discover</a> some interesting pastebins</p>
<hr />
</header>
<main>
<section>
<h2 class="text-lg font-bold">Create your account with Public-Key Cryptography</h2>
<p>We don't want your email address.</p>
<p>To get started, simply ssh into our content management system:</p>
<pre>ssh new@{{.Site.Domain}}</pre>
<div class="text-sm font-italic note">
note: <code>new</code> is a special username that will always send you to account
creation, even with multiple accounts associated with your key-pair.
</div>
<div class="text-sm font-italic note">
note: getting permission denied? <a href="/help#permission-denied">read this</a>
</div>
<p>
After that, just set a username and you're ready to start writing! When you SSH
again, use your username that you set in the CMS.
</p>
</section>
<section>
<h2 class="text-lg font-bold">Publish your pastes with one command</h2>
<p>
When your post is ready to be published, copy the file to our server with a familiar
command:
</p>
<pre>scp ~/pastes/* {{.Site.Domain}}:/</pre>
<p>We'll either create or update the posts for you.</p>
</section>
<section>
<h2 class="text-lg font-bold">Terminal workflow without installation</h2>
<p>
Since we are leveraging tools you already have on your computer
(<code>ssh</code> and <code>scp</code>), there is nothing to install.
</p>
<p>
This provides the convenience of a web app, but from inside your terminal!
</p>
</section>
<section>
<h2 class="text-lg font-bold">Features</h2>
<ul>
<li>Bring your own editor</li>
<li>You control the source files</li>
<li>Terminal workflow with no installation</li>
<li>Public-key based authentication</li>
<li>No ads, zero tracking</li>
<li>No platform lock-in</li>
<li>No javascript</li>
<li>Minimalist design</li>
<li>100% open source</li>
</ul>
</section>
<section>
<h2 class="text-lg font-bold">Roadmap</h2>
<ol>
<li>idk</li>
</ol>
</section>
</main>
{{template "marketing-footer" .}}
{{end}}

124
html/ops.page.tmpl Normal file
View file

@ -0,0 +1,124 @@
{{template "base" .}}
{{define "title"}}operations -- {{.Site.Domain}}{{end}}
{{define "meta"}}
<meta name="description" content="{{.Site.Domain}} operations" />
{{end}}
{{define "body"}}
<header>
<h1 class="text-2xl">Operations</h1>
<ul>
<li><a href="/privacy">privacy</a></li>
<li><a href="/transparency">transparency</a></li>
</ul>
</header>
<main>
<section>
<h2 class="text-xl">Purpose</h2>
<p>
{{.Site.Domain}} exists to allow people to create and share their thoughts
without the need to set up their own server or be part of a platform
that shows ads or tracks its users.
</p>
</section>
<section>
<h2 class="text-xl">Ethics</h2>
<p>We are committed to:</p>
<ul>
<li>No tracking of user or visitor behaviour.</li>
<li>Never sell any user or visitor data.</li>
<li>No ads — ever.</li>
</ul>
</section>
<section>
<h2 class="text-xl">Code of Content Publication</h2>
<p>
Content in {{.Site.Domain}} pastes is unfiltered and unmonitored. Users are free to publish any
combination of words and pixels except for: content of animosity or disparagement of an
individual or a group on account of a group characteristic such as race, color, national
origin, sex, disability, religion, or sexual orientation, which will be taken down
immediately.
</p>
<p>
If one notices something along those lines in a paste post please let us know at
<a href="mailto:{{.Site.Email}}">{{.Site.Email}}</a>.
</p>
</section>
<section>
<h2 class="text-xl">Liability</h2>
<p>
The user expressly understands and agrees that Eric Bower and Antonio Mika, the operator of this website
shall not be liable, in law or in equity, to them or to any third party for any direct,
indirect, incidental, lost profits, special, consequential, punitive or exemplary damages.
</p>
</section>
<section>
<h2 class="text-xl">Account Terms</h2>
<p>
<ul>
<li>
The user is responsible for all content posted and all actions performed with
their account.
</li>
<li>
We reserve the right to disable or delete a user's account for any reason at
any time. We have this clause because, statistically speaking, there will be
people trying to do something nefarious.
</li>
</ul>
</p>
</section>
<section>
<h2 class="text-xl">Service Availability</h2>
<p>
We provide the {{.Site.Domain}} service on an "as is" and "as available" basis. We do not offer
service-level agreements but do take uptime seriously.
</p>
</section>
<section>
<h2 class="text-xl">Contact and Support</h2>
<p>
Email us at <a href="mailto:{{.Site.Email}}">{{.Site.Email}}</a>
with any questions.
</p>
</section>
<section>
<h2 class="text-xl">Acknowledgments</h2>
<p>
{{.Site.Domain}} was inspired by <a href="https://mataroa.blog">Mataroa Blog</a>
and <a href="https://bearblog.dev/">Bear Blog</a>.
</p>
<p>
{{.Site.Domain}} is built with many open source technologies.
</p>
<p>
In particular we would like to thank:
</p>
<ul>
<li>
<span>The </span>
<a href="https://charm.sh">charm.sh</a>
<span> community</span>
</li>
<li>
<span>The </span>
<a href="https://go.dev">golang</a>
<span> community</span>
</li>
<li>
<span>The </span>
<a href="https://www.postgresql.org/">postgresql</a>
<span> community</span>
</li>
<li>
<span>The </span>
<a href="https://github.com/caddyserver/caddy">caddy</a>
<span> community</span>
</li>
</ul>
</section>
</main>
{{template "marketing-footer" .}}
{{end}}

36
html/post.page.tmpl Normal file
View file

@ -0,0 +1,36 @@
{{template "base" .}}
{{define "title"}}{{.PageTitle}}{{end}}
{{define "meta"}}
<meta property="og:type" content="website">
<meta property="og:site_name" content="{{.Site.Domain}}">
<meta property="og:url" content="{{.URL}}">
<meta property="og:title" content="{{.Title}}">
<meta property="og:image:width" content="300" />
<meta property="og:image:height" content="300" />
<meta itemprop="image" content="https://{{.Site.Domain}}/card.png" />
<meta property="og:image" content="https://{{.Site.Domain}}/card.png" />
<meta property="twitter:card" content="summary">
<meta property="twitter:url" content="{{.URL}}">
<meta property="twitter:title" content="{{.Title}}">
<meta name="twitter:image" content="https://{{.Site.Domain}}/card.png" />
<meta name="twitter:image:src" content="https://{{.Site.Domain}}/card.png" />
{{end}}
{{define "body"}}
<header>
<h1 class="text-2xl font-bold">{{.Title}}</h1>
<p class="font-bold m-0">
<time datetime="{{.PublishAtISO}}">{{.PublishAt}}</time>
<span> on </span>
<a href="{{.BlogURL}}">{{.BlogName}}</a></p>
</header>
<main>
<article>
{{.Contents}}
</article>
</main>
{{template "footer" .}}
{{end}}

52
html/privacy.page.tmpl Normal file
View file

@ -0,0 +1,52 @@
{{template "base" .}}
{{define "title"}}privacy -- {{.Site.Domain}}{{end}}
{{define "meta"}}
<meta name="description" content="{{.Site.Domain}} privacy policy" />
{{end}}
{{define "body"}}
<header>
<h1 class="text-2xl">Privacy</h1>
<p>Details on our privacy and security approach.</p>
</header>
<main>
<section>
<h2 class="text-xl">Account Data</h2>
<p>
In order to have a functional account at {{.Site.Domain}}, we need to store
your public key. That is the only piece of information we record for a user.
</p>
<p>
Because we use public-key cryptography, our security posture is a battle-tested
and proven technique for authentication.
</p>
</section>
<section>
<h2 class="text-xl">Third parties</h2>
<p>
We have a strong commitment to never share any user data with any third-parties.
</p>
</section>
<section>
<h2 class="text-xl">Service Providers</h2>
<ul>
<li>
<span>We host our server on </span>
<a href="https://digitalocean.com">digital ocean</a>
</li>
</ul>
</section>
<section>
<h2 class="text-xl">Cookies</h2>
<p>
We do not use any cookies, not even account authentication.
</p>
</section>
</main>
{{template "marketing-footer" .}}
{{end}}

35
html/read.page.tmpl Normal file
View file

@ -0,0 +1,35 @@
{{template "base" .}}
{{define "title"}}discover -- {{.Site.Domain}}{{end}}
{{define "meta"}}
<meta name="description" content="discover interesting posts" />
{{end}}
{{define "body"}}
<header class="text-center">
<h1 class="text-2xl font-bold">read</h1>
<p class="text-lg">recent pastes</p>
<hr />
</header>
<main>
<div class="my">
{{if .PrevPage}}<a href="{{.PrevPage}}">prev</a>{{else}}<span class="text-grey">prev</span>{{end}}
{{if .NextPage}}<a href="{{.NextPage}}">next</a>{{else}}<span class="text-grey">next</span>{{end}}
</div>
{{range .Posts}}
<article>
<div class="flex items-center">
<time datetime="{{.PublishAtISO}}" class="font-italic text-sm post-date">{{.PublishAt}}</time>
<div class="flex-1">
<h2 class="inline"><a href="{{.URL}}">{{.Title}}</a></h2>
<address class="text-sm inline">
<a href="{{.BlogURL}}" class="link-grey">({{.Username}})</a>
</address>
</div>
</div>
</article>
{{end}}
</main>
{{template "marketing-footer" .}}
{{end}}

1
html/rss.page.tmpl Normal file
View file

@ -0,0 +1 @@
{{.Contents}}

View file

@ -0,0 +1,57 @@
{{template "base" .}}
{{define "title"}}transparency -- {{.Site.Domain}}{{end}}
{{define "meta"}}
<meta name="description" content="full transparency of analytics and cost at {{.Site.Domain}}" />
{{end}}
{{define "body"}}
<header>
<h1 class="text-2xl">Transparency</h1>
<hr />
</header>
<main>
<section>
<h2 class="text-xl">Analytics</h2>
<p>
Here are some interesting stats on usage.
</p>
<article>
<h2 class="text-lg">Total users</h2>
<div>{{.Analytics.TotalUsers}}</div>
</article>
<article>
<h2 class="text-lg">New users in the last month</h2>
<div>{{.Analytics.UsersLastMonth}}</div>
</article>
<article>
<h2 class="text-lg">Total pastes</h2>
<div>{{.Analytics.TotalPosts}}</div>
</article>
<article>
<h2 class="text-lg">New pastes in the last month</h2>
<div>{{.Analytics.PostsLastMonth}}</div>
</article>
<article>
<h2 class="text-lg">Users with at least one paste</h2>
<div>{{.Analytics.UsersWithPost}}</div>
</article>
</section>
<section>
<h2 class="text-xl">Service maintenance costs</h2>
<ul>
<li>Server $5.00/mo</li>
<li>Domain name $3.25/mo</li>
<li>Programmer $0.00/mo</li>
</ul>
</section>
</main>
{{template "marketing-footer" .}}
{{end}}