Add support for Plausible Analytics (#4)

* Added a dirty way to POST Plausible

* Added some documentation

* Made the documentation slightly better

* Wrapped fetch in waitUntil

* Apply suggestions from code review
This commit is contained in:
aura 2022-12-20 17:21:42 -05:00 committed by GitHub
parent 15adcb9bd7
commit 45472515e0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 57 additions and 30 deletions

View file

@ -12,13 +12,14 @@ Wrangler 2 is required now, but should be handled automatically for `yarn` comma
To run manual wrangler commands, try `npx wrangler`.
Simple steps:
- `yarn install`
- `yarn addsecret`
- `yarn deploy`
## Deploy (More involved)
To deploy to your Cloudflare Workers account, edit the relevant entries in `wrangler.toml`, add a secret with `wrangler secret put WORKERLINKS_SECRET` and use `wrangler publish`.
To deploy to your Cloudflare Workers account, edit the relevant entries in `wrangler.toml`, add a secret with `wrangler secret put WORKERLINKS_SECRET` and use `wrangler publish`.
## Debugging
@ -83,6 +84,10 @@ It is a planned feature to be able to list all URLs via a `GET` on `/` with `Aut
For the time being you can view them from your Cloudflare Dashboard:
Cloudflare Dashboard -> Workers -> KV -> View on the namespace.
## Plausible Analytics
To get statistics for your short URLs with Plausible Analytics, define a `PLAUSIBLE_HOST` secret set to the URL of your Plausible instance. For example, `https://plausible.io/`.
## Security
This code is relatively simple but still, if you find any security issues that can be exploited publicly, please reach out to me via email: `erisa (at) erisa.uk` with any relevant details.

View file

@ -1,14 +1,15 @@
let secret
addEventListener('fetch', (event) => {
event.respondWith(handleRequest(event.request))
event.respondWith(handleRequest(event))
})
/**
* Respond to the request
* @param {Request} request
* @param {Event} event
*/
async function handleRequest(request) {
async function handleRequest(event) {
const { request } = event
// Set this in your worker's environment. wrangler.toml or cloudflare dashboard
if (WORKERLINKS_SECRET === undefined) {
return new Response('Secret is not defined. Please add WORKERLINKS_SECRET.')
@ -31,11 +32,11 @@ async function handleRequest(request) {
return Response.json(
{
code: '405 Method Not Allowed',
message: 'POST not valid for individual keys. Did you mean PUT?'
message: 'POST not valid for individual keys. Did you mean PUT?',
},
{
status: 405
}
status: 405,
},
)
}
key = '/' + Math.random().toString(36).slice(5)
@ -52,13 +53,34 @@ async function handleRequest(request) {
return Response.json(
{
code: '404 Not Found',
message: 'Key does not exist or has not propagated.'
message: 'Key does not exist or has not propagated.',
},
{
status: 404
}
status: 404,
},
)
} else {
// PLAUSIBLE_HOST should be the full URL to your Plausible Analytics instance
// e.g. https://plausible.io/
if (PLAUSIBLE_HOST !== undefined) {
const url = PLAUSIBLE_HOST + 'api/event'
const headers = new Headers()
headers.append('User-Agent', request.headers.get('User-Agent'))
headers.append(
'X-Forwarded-For',
request.headers.get('X-Forwarded-For'),
)
headers.append('Content-Type', 'application/json')
const data = {
name: 'pageview',
url: request.url,
domain: new URL(request.url).hostname,
}
event.waitUntil(
fetch(url, { method: 'POST', headers, body: JSON.stringify(data) }),
)
}
return new Response(null, { status: 302, headers: { Location: url } })
}
} else if (request.method == 'DELETE') {
@ -66,11 +88,11 @@ async function handleRequest(request) {
return Response.json(
{
code: '401 Unauthorized',
message: 'Unauthorized.'
message: 'Unauthorized.',
},
{
status: 401
}
status: 401,
},
)
}
@ -80,11 +102,11 @@ async function handleRequest(request) {
return Response.json(
{
code: '404 Not Found',
message: 'Key does not exist or has not propagated.'
message: 'Key does not exist or has not propagated.',
},
{
status: 404,
}
},
)
} else {
await kv.delete(key)
@ -93,11 +115,11 @@ async function handleRequest(request) {
message: 'Short URL deleted succesfully.',
key: key.substr(1),
shorturl: shorturl,
longurl: url
longurl: url,
},
{
status: 200
}
status: 200,
},
)
}
}
@ -106,11 +128,11 @@ async function handleRequest(request) {
{
code: '405 Method Not Allowed',
message:
'Unsupported method. Please use one of GET, PUT, POST, DELETE, HEAD.'
'Unsupported method. Please use one of GET, PUT, POST, DELETE, HEAD.',
},
{
status: 405
}
status: 405,
},
)
}
@ -132,11 +154,11 @@ async function putLink(givenSecret, shorturl, key, url) {
return Response.json(
{
code: '401 Unauthorized',
message: 'Unauthorized.'
message: 'Unauthorized.',
},
{
status: 401
}
status: 401,
},
)
}
@ -144,11 +166,11 @@ async function putLink(givenSecret, shorturl, key, url) {
return Response.json(
{
code: '400 Bad Request',
message: "No valid URL given. Please set a 'URL' header."
message: "No valid URL given. Please set a 'URL' header.",
},
{
status: 400
}
status: 400,
},
)
}
@ -158,10 +180,10 @@ async function putLink(givenSecret, shorturl, key, url) {
message: 'URL created succesfully.',
key: key.substr(1),
shorturl: shorturl,
longurl: url
longurl: url,
},
{
status: 200
}
status: 200,
},
)
}