1<!--
  2SPDX-FileCopyrightText: 2022 Amolith <amolith@secluded.site>
  3
  4SPDX-License-Identifier: CC0-1.0
  5-->
  6
  7# earl
  8
  9[![REUSE status][reuse-shield]][reuse]
 10[![Donate with fosspay][fosspay-shield]][fosspay]
 11![Time spent on project][wakapi-shield]
 12[![Scratch an itch badge][scratchanitchbadge]][scratchanitch]
 13
 14Personal shortlink generator
 15
 16_This project was created to [scratch an itch][scratchanitch]_
 17
 18## Features
 19
 20Links are …
 21
 22- Editable
 23- Removable
 24- Four characters long (26 uppercase letters + 26 lowercase letters + 10 numbers
 25  = 14,776,336 possible shortened URLs)
 26
 27Service has …
 28
 29- A simple API
 30- A simple web UI
 31- A simple backup procedure (database is a single directory)
 32- No user management (this is a _personal_ service after all)
 33
 34## Installation
 35
 36- Download the binary appropriate for your system or build from source with `go build .`
 37  <details>
 38    <summary>List of latest binaries</summary>
 39    <ul>
 40      <li><a href="https://earl.run/earl-darwin-amd64">earl-darwin-amd64</a></li>
 41      <li><a href="https://earl.run/earl-darwin-arm64">earl-darwin-arm64</a></li>
 42      <li><a href="https://earl.run/earl-dragonfly-amd64">earl-dragonfly-amd64</a></li>
 43      <li><a href="https://earl.run/earl-freebsd-amd64">earl-freebsd-amd64</a></li>
 44      <li><a href="https://earl.run/earl-freebsd-arm">earl-freebsd-arm</a></li>
 45      <li><a href="https://earl.run/earl-freebsd-i386">earl-freebsd-i386</a></li>
 46      <li><a href="https://earl.run/earl-illumos-amd64">earl-illumos-amd64</a></li>
 47      <li><a href="https://earl.run/earl-linux-amd64">earl-linux-amd64</a></li>
 48      <li><a href="https://earl.run/earl-linux-arm">earl-linux-arm</a></li>
 49      <li><a href="https://earl.run/earl-linux-arm64">earl-linux-arm64</a></li>
 50      <li><a href="https://earl.run/earl-linux-i386">earl-linux-i386</a></li>
 51      <li><a href="https://earl.run/earl-linux-mips">earl-linux-mips</a></li>
 52      <li><a href="https://earl.run/earl-linux-mips64">earl-linux-mips64</a></li>
 53      <li><a href="https://earl.run/earl-linux-mips64le">earl-linux-mips64le</a></li>
 54      <li><a href="https://earl.run/earl-linux-mipsle">earl-linux-mipsle</a></li>
 55      <li><a href="https://earl.run/earl-linux-ppc64">earl-linux-ppc64</a></li>
 56      <li><a href="https://earl.run/earl-linux-ppc64le">earl-linux-ppc64le</a></li>
 57      <li><a href="https://earl.run/earl-linux-riscv64">earl-linux-riscv64</a></li>
 58      <li><a href="https://earl.run/earl-linux-s390x">earl-linux-s390x</a></li>
 59      <li><a href="https://earl.run/earl-netbsd-amd64">earl-netbsd-amd64</a></li>
 60      <li><a href="https://earl.run/earl-netbsd-arm">earl-netbsd-arm</a></li>
 61      <li><a href="https://earl.run/earl-netbsd-i386">earl-netbsd-i386</a></li>
 62      <li><a href="https://earl.run/earl-openbsd-amd64">earl-openbsd-amd64</a></li>
 63      <li><a href="https://earl.run/earl-openbsd-arm">earl-openbsd-arm</a></li>
 64      <li><a href="https://earl.run/earl-openbsd-arm64">earl-openbsd-arm64</a></li>
 65      <li><a href="https://earl.run/earl-openbsd-i386">earl-openbsd-i386</a></li>
 66      <li><a href="https://earl.run/earl-plan9-amd64">earl-plan9-amd64</a></li>
 67      <li><a href="https://earl.run/earl-plan9-arm">earl-plan9-arm</a></li>
 68      <li><a href="https://earl.run/earl-plan9-i386">earl-plan9-i386</a></li>
 69      <li><a href="https://earl.run/earl-solaris-amd64">earl-solaris-amd64</a></li>
 70      <li><a href="https://earl.run/earl-windows-amd64.exe">earl-windows-amd64.exe</a></li>
 71      <li><a href="https://earl.run/earl-windows-arm64.exe">earl-windows-arm64.exe</a></li>
 72      <li><a href="https://earl.run/earl-windows-arm.exe">earl-windows-arm.exe</a></li>
 73      <li><a href="https://earl.run/earl-windows-i386.exe">earl-windows-i386.exe</a></li>
 74    </ul>
 75  </details>
 76- On Unix-based OSes (which includes macOS), mark the binary as executable with
 77  `chmod u+x path/to/binary`
 78  - I don't _think_ this is necessary with Windows.
 79- On Unix-based OSes, execute the binary with `./path/to/binary`
 80  - No clue how to execute the Windows binaries. Feel free to [send a
 81    patch](#questions-amp-contributions) with this information!
 82- Edit `./config.yaml` and modify `accessToken` to something secure; earl won't
 83  start until the access token is changed.
 84- Re-execute the binary and earl should be accessible on
 85  [localhost:1313](http://localhost:1313)!
 86- To make it accessible over the internet on a short domain, you'll need said
 87  short domain, a server, a reverse proxy, and a TLS certificate. How to
 88  obtain/configure those components is out-of-scope though.
 89  - Contributions adding a Caddyfile, an NGINX vhost, systemd/OpenRC/runit/etc.
 90    units are welcome.
 91
 92## API documentation
 93
 94### `/create`
 95
 96#### Required parameters
 97
 98- `url`: percent-encoded URL being shortened
 99
100#### Optional parameters
101
102- `name`: percent-encoded short link the `url` will be mapped to. If this is not
103  provided, a random, 4-character code will be generated instead.
104
105#### Output
106
107- `401 Unauthorized: You do not have permission to create shortlinks` if access
108  token provided in `Authorization` header does not match the configured access
109  token
110- `400 Bad Request: URL parameter is required`
111- `406 Not Acceptable: A shortened URL with this name already exists` if
112  provided name already exists in the database
113- `URL mapped to $NAME` (200 OK)
114
115### `/read`
116
117#### Required parameters
118
119- None
120
121#### Optional parameters
122
123- None
124
125#### Output
126
127- `401 Unauthorized: You do not have permission to view shortlinks` if access
128  token provided in `Authorization` header does not match the configured access
129  token
130- JSON representation of the key/value database (200 OK)
131  ```json
132  {
133    "6H1g": "https://git.sr.ht/~amolith/earl/tree/dev",
134    "N3yg": "https://secluded.site/"
135  }
136  ```
137
138### `/update`
139
140#### Required parameters:
141
142- `oldName`: percent-encoded name the URL is _currently_ referred to with
143- `name`: percent-encoded name the URL _will_ be referred to with
144- `url`: percent-encoded URL being shortened
145
146`name` and `url` are first set as a key/value pair. If `name` already exists,
147`url` is updated. If `name` does _not_ already exist, it's created and `oldName`
148is deleted. If the user is only modifying `url`, `oldName` and `name` should
149both be submitted but their values should be identical.
150
151#### Optional parameters:
152
153- None
154
155#### Output:
156
157- `401 Unauthorized: You do not have permission to create shortlinks` if access
158  token provided in `Authorization` header does not match the configured access
159  token
160- `400 Bad Request: oldName parameter is required`
161- `400 Bad Request: name parameter is required`
162- `400 Bad Request: URL parameter is required`
163- `406 Not Acceptable: A shortened URL with this name already exists` if
164  provided name already exists in the database
165- `$URL mapped to $NAME` (200 OK)
166
167### `/delete`
168
169#### Required parameters:
170
171- `name`: percent-encoded short link
172
173#### Optional parameters:
174
175- None
176
177#### Output:
178
179- `401 Unauthorized: You do not have permission to create shortlinks` if access
180  token provided in `Authorization` header does not match the configured access
181  token
182- `400 Bad Request: name parameter is required`
183- `$URL has been deleted` (200 OK)
184
185## But … why?
186
187Good question. URL shorteners are (usually) terrible and useless. Except when
188used correctly :thinkingsmart:
189
190I take a lot of hand-written notes on things and, having both a passion for and
191job in IT, my notes would be much more useful if they included links to the
192things I'm writing about. However, there's no way I'm going to hand-write a 50+
193character URL.
194
195That's where URL shorteners come in!
196
197Most shortener services I've found that are both open source and self-hosted use
198something like 6-character-long paths for the links as they're meant for use by
199multiple people; 14.8m possible URLs (your cap with 4 character paths) aren't
200enough for public services.
201
202Now six characters is _fine_ but why write six characters when you could write
203four :D
204
205And why deal with user management and a relational database when you're only
206going to have one user and a bunch of keys with values :D
207
208I was also unsatisfied with the tech stack and feature sets of the alternatives;
209they were all either a pain to set up and update or they had click counters and
210link tracking and a bunch of other crap I have no use for.
211
212So I decided to make my own that has exactly what I want and nothing more :D
213
214## Questions & Contributions
215
216Questions, comments, and patches can always be sent to my public inbox, but I'm
217also in my IRC channel/XMPP room pretty much 24/7. However, I might not see
218messages right away because I'm working on something else (or sleeping) so
219please stick around!
220
221If you're wanting to introduce a new feature and I don't feel like it fits with
222this project's goal, I encourage you to fork the repo and make whatever changes
223you like!
224
225- Email: [~amolith/public-inbox@lists.sr.ht][email]
226- IRC: [irc.nixnet.services/#secluded][irc]
227- XMPP: [secluded@muc.secluded.site][xmpp]
228
229_If you haven't used mailing lists before, please take a look at [SourceHut's
230documentation](https://man.sr.ht/lists.sr.ht/), especially the etiquette
231section._
232
233[reuse]: https://api.reuse.software/info/git.sr.ht/~amolith/earl
234[reuse-shield]: https://shields.io/reuse/compliance/git.sr.ht/~amolith/earl
235[fosspay]: https://secluded.site/donate/
236[fosspay-shield]: https://shields.io/badge/donate-fosspay-yellow
237[wakapi-shield]: https://img.shields.io/endpoint?url=https://waka.secluded.site/api/compat/shields/v1/amolith/interval:any/project:earl&color=blue&label=time%20spent
238[scratchanitchbadge]: https://img.shields.io/badge/scratchanitch-dev-FFC4B5
239[scratchanitch]: https://scratchanitch.dev
240[email]: mailto:~amolith/public-inbox@lists.sr.ht
241[irc]: irc://irc.nixnet.services/#secluded
242[xmpp]: xmpp:secluded@muc.secluded.site?join