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