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## API documentation
32
33### `/create`
34
35#### Required parameters
36
37- `url`: percent-encoded URL being shortened
38
39#### Optional parameters
40
41- `name`: percent-encoded short link the `url` will be mapped to. If this is not
42 provided, a random, 4-character code will be generated instead.
43
44#### Output
45
46- `401 Unauthorized: You do not have permission to create shortlinks` if access
47 token provided in `Authorization` header does not match the configured access
48 token
49- `400 Bad Request: URL parameter is required`
50- `406 Not Acceptable: A shortened URL with this name already exists` if
51 provided name already exists in the database
52- `URL mapped to $NAME` (200 OK)
53
54### `/read`
55
56#### Required parameters
57
58- None
59
60#### Optional parameters
61
62- None
63
64#### Output
65
66- `401 Unauthorized: You do not have permission to view shortlinks` if access
67 token provided in `Authorization` header does not match the configured access
68 token
69- JSON representation of the key/value database (200 OK)
70 ```json
71 {
72 "6H1g": "https://git.sr.ht/~amolith/earl/tree/dev",
73 "N3yg": "https://secluded.site/"
74 }
75 ```
76
77### `/update`
78
79#### Required parameters:
80
81- `oldName`: percent-encoded name the URL is _currently_ referred to with
82- `name`: percent-encoded name the URL _will_ be referred to with
83- `url`: percent-encoded URL being shortened
84
85`name` and `url` are first set as a key/value pair. If `name` already exists,
86`url` is updated. If `name` does _not_ already exist, it's created and `oldName`
87is deleted. If the user is only modifying `url`, `oldName` and `name` should
88both be submitted but their values should be identical.
89
90#### Optional parameters:
91
92- None
93
94#### Output:
95
96- `401 Unauthorized: You do not have permission to create shortlinks` if access
97 token provided in `Authorization` header does not match the configured access
98 token
99- `400 Bad Request: oldName parameter is required`
100- `400 Bad Request: name parameter is required`
101- `400 Bad Request: URL parameter is required`
102- `406 Not Acceptable: A shortened URL with this name already exists` if
103 provided name already exists in the database
104- `$URL mapped to $NAME` (200 OK)
105
106### `/delete`
107
108#### Required parameters:
109
110- `name`: percent-encoded short link
111
112#### Optional parameters:
113
114- None
115
116#### Output:
117
118- `401 Unauthorized: You do not have permission to create shortlinks` if access
119 token provided in `Authorization` header does not match the configured access
120 token
121- `400 Bad Request: name parameter is required`
122- `$URL has been deleted` (200 OK)
123
124## But … why?
125
126Good question. URL shorteners are (usually) terrible and useless. Except when
127used correctly :thinkingsmart:
128
129I take a lot of hand-written notes on things and, having both a passion for and
130job in IT, my notes would be much more useful if they included links to the
131things I'm writing about. However, there's no way I'm going to hand-write a 50+
132character URL.
133
134That's where URL shorteners come in!
135
136Most shortener services I've found that are both open source and self-hosted use
137something like 6-character-long paths for the links as they're meant for use by
138multiple people; 14.8m possible URLs (your cap with 4 character paths) aren't
139enough for public services.
140
141Now six characters is _fine_ but why write six characters when you could write
142four :D
143
144And why deal with user management and a relational database when you're only
145going to have one user and a bunch of keys with values :D
146
147I was also unsatisfied with the tech stack and feature sets of the alternatives;
148they were all either a pain to set up and update or they had click counters and
149link tracking and a bunch of other crap I have no use for.
150
151So I decided to make my own that has exactly what I want and nothing more :D
152
153## Questions & Contributions
154
155Questions, comments, and patches can always be sent to my public inbox, but I'm
156also in my IRC channel/XMPP room pretty much 24/7. However, I might not see
157messages right away because I'm working on something else (or sleeping) so
158please stick around!
159
160If you're wanting to introduce a new feature and I don't feel like it fits with
161this project's goal, I encourage you to fork the repo and make whatever changes
162you like!
163
164- Email: [~amolith/public-inbox@lists.sr.ht][email]
165- IRC: [irc.nixnet.services/#secluded][irc]
166- XMPP: [secluded@muc.secluded.site][xmpp]
167
168_If you haven't used mailing lists before, please take a look at [SourceHut's
169documentation](https://man.sr.ht/lists.sr.ht/), especially the etiquette
170section._
171
172[reuse]: https://api.reuse.software/info/git.sr.ht/~amolith/earl
173[reuse-shield]: https://shields.io/reuse/compliance/git.sr.ht/~amolith/earl
174[fosspay]: https://secluded.site/donate/
175[fosspay-shield]: https://shields.io/badge/donate-fosspay-yellow
176[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
177[email]: mailto:~amolith/public-inbox@lists.sr.ht
178[irc]: irc://irc.nixnet.services/#secluded
179[xmpp]: xmpp:secluded@muc.secluded.site?join