commit
5556cea72f
3 changed files with 114 additions and 16 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -9,3 +9,4 @@ worker/
|
|||
node_modules/
|
||||
.cargo-ok
|
||||
wrangler.prod.toml
|
||||
.mf
|
||||
25
README.md
25
README.md
|
|
@ -55,7 +55,7 @@ server: cloudflare
|
|||
..other ephemeral headers..
|
||||
```
|
||||
|
||||
To create or update a custom short URl, send a `PUT` to the intended target URL:
|
||||
To create or update a custom short URL, send a `PUT` to the intended target URL:
|
||||
|
||||
```json
|
||||
erisa@Tuturu:~$ curl -X PUT -H "Authorization: mysecret" -H "URL: https://erisa.uk" https://erisa.link/mywebsite
|
||||
|
|
@ -79,6 +79,29 @@ erisa@Tuturu:~$ curl -X DELETE -H "Authorization: mysecret" https://erisa.link/k
|
|||
}
|
||||
```
|
||||
|
||||
You can also bulk create multiple shortlinks at once by sending a `POST` to `/` with no `URL` header and with a JSON body instead:
|
||||
|
||||
```json
|
||||
erisa@Tuturu:~$ curl -X POST -H "Authorization: mysecret" https://erisa.link/ \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{ "/short1": "https://example.com", "/mywebsite": "https://erisa.uk" }'
|
||||
{
|
||||
"message": "URLs created successfully",
|
||||
"entries": [
|
||||
{
|
||||
"key": "short1",
|
||||
"shorturl": "https://erisa.link/short1",
|
||||
"longurl": "https://example.com"
|
||||
},
|
||||
{
|
||||
"key": "mywebsite",
|
||||
"shorturl": "http://erisa.link/mywebsite",
|
||||
"longurl": "https://erisa.uk"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
It is a planned feature to be able to list all URLs via a `GET` on `/` with `Authorization`.
|
||||
|
||||
For the time being you can view them from your Cloudflare Dashboard:
|
||||
|
|
|
|||
100
index.js
100
index.js
|
|
@ -19,8 +19,8 @@ async function handleRequest(event) {
|
|||
|
||||
let path = new URL(request.url).pathname
|
||||
// Trim trailing slash
|
||||
let key = path !== '/' ? path.replace(/\/$/, '') : path;
|
||||
let shorturl = new URL(request.url).origin + key
|
||||
let key = path !== '/' ? path.replace(/\/$/, '') : path
|
||||
let shorturl = new URL(key, request.url).origin
|
||||
|
||||
if (request.method == 'PUT') {
|
||||
return await putLink(
|
||||
|
|
@ -41,14 +41,77 @@ async function handleRequest(event) {
|
|||
},
|
||||
)
|
||||
}
|
||||
key = '/' + Math.random().toString(36).slice(5)
|
||||
shorturl = new URL(request.url).origin + key
|
||||
return await putLink(
|
||||
request.headers.get('Authorization'),
|
||||
shorturl,
|
||||
key,
|
||||
request.headers.get('URL'),
|
||||
)
|
||||
const body = await request.text()
|
||||
|
||||
if (!body) {
|
||||
key = '/' + Math.random().toString(36).slice(5)
|
||||
shorturl = new URL(key, request.url)
|
||||
return await putLink(
|
||||
request.headers.get('Authorization'),
|
||||
shorturl,
|
||||
key,
|
||||
request.headers.get('URL'),
|
||||
)
|
||||
} else {
|
||||
if (request.headers.get('Authorization') != secret) {
|
||||
return Response.json(
|
||||
{
|
||||
code: '401 Unauthorized',
|
||||
message: 'Unauthorized.',
|
||||
},
|
||||
{
|
||||
status: 401,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
let json
|
||||
try {
|
||||
json = JSON.parse(body)
|
||||
} catch {
|
||||
return Response.json(
|
||||
{
|
||||
code: '400 Bad Request',
|
||||
message: 'Body must be valid JSON, or none at all.',
|
||||
},
|
||||
{
|
||||
status: 400,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
const valid = validateBulkBody(json)
|
||||
if (!valid) {
|
||||
return Response.json(
|
||||
{
|
||||
code: '400 Bad Request',
|
||||
message:
|
||||
'Body must be a standard JSON object mapping keys to urls.',
|
||||
example: {
|
||||
'/short': 'https://example.com/really-really-really-long-1',
|
||||
'/other': 'https://subdomain.example.com/and-some-long-path',
|
||||
},
|
||||
},
|
||||
{ status: 400 },
|
||||
)
|
||||
}
|
||||
|
||||
for (const [key, url] of Object.entries(json)) {
|
||||
await kv.put(key, url)
|
||||
}
|
||||
|
||||
return Response.json(
|
||||
{
|
||||
message: 'URLs created successfully',
|
||||
entries: Object.entries(json).map(([key, longurl]) => ({
|
||||
key: key.slice(1),
|
||||
shorturl: new URL(key, request.url),
|
||||
longurl,
|
||||
})),
|
||||
},
|
||||
{ status: 200 },
|
||||
)
|
||||
}
|
||||
} else if (request.method == 'GET' || request.method == 'HEAD') {
|
||||
let url = await kv.get(key)
|
||||
if (url == null) {
|
||||
|
|
@ -106,7 +169,7 @@ async function handleRequest(event) {
|
|||
)
|
||||
}
|
||||
|
||||
shorturl = new URL(request.url).origin + key
|
||||
shorturl = new URL(key, request.url)
|
||||
let url = await kv.get(key)
|
||||
if (url == null) {
|
||||
return Response.json(
|
||||
|
|
@ -123,7 +186,7 @@ async function handleRequest(event) {
|
|||
return Response.json(
|
||||
{
|
||||
message: 'Short URL deleted succesfully.',
|
||||
key: key.substr(1),
|
||||
key: key.slice(1),
|
||||
shorturl: shorturl,
|
||||
longurl: url,
|
||||
},
|
||||
|
|
@ -159,6 +222,17 @@ function validateUrl(url) {
|
|||
return true
|
||||
}
|
||||
|
||||
// zod and other validation libs too
|
||||
function validateBulkBody(body) {
|
||||
// Starting `/` and no ending `/`
|
||||
const keyRe = /^\/.*?[^\/]$/
|
||||
|
||||
if (!body || typeof body !== 'object' || Array.isArray(body)) return false
|
||||
return Object.entries(body).every(
|
||||
([key, url]) => keyRe.test(key) && validateUrl(url),
|
||||
)
|
||||
}
|
||||
|
||||
async function putLink(givenSecret, shorturl, key, url) {
|
||||
if (givenSecret != secret) {
|
||||
return Response.json(
|
||||
|
|
@ -188,7 +262,7 @@ async function putLink(givenSecret, shorturl, key, url) {
|
|||
return Response.json(
|
||||
{
|
||||
message: 'URL created succesfully.',
|
||||
key: key.substr(1),
|
||||
key: key.slice(1),
|
||||
shorturl: shorturl,
|
||||
longurl: url,
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue