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