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