Add listing functionality (#13)

* Ignore dist directory when formatting

* Add list endpoint (`/`)

* Document list endpoint
This commit is contained in:
Kot 2023-04-14 15:08:04 -07:00 committed by GitHub
parent 038f80db03
commit 9e34fb15e6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 76 additions and 12 deletions

1
.prettierignore Normal file
View file

@ -0,0 +1 @@
dist/

View file

@ -102,10 +102,33 @@ erisa@Tuturu:~$ curl -X POST -H "Authorization: mysecret" https://erisa.link/ \
}
```
It is a planned feature to be able to list all URLs via a `GET` on `/` with `Authorization`.
You can list all URLs by sending a `GET` to `/` (with the `Authorization` header set to your secret, of course).
For the time being you can view them from your Cloudflare Dashboard:
Cloudflare Dashboard -> Workers -> KV -> View on the namespace.
```json
kot@Starry:~$ curl -H "Authorization: mysecret" "https://erisa.link/?prefix=%2F&limit=1"
{
"list_complete": false,
"cursor": "AAAAAJhOXekucRAqut7Xs7Q2f09GCZyStWBfONvq6u5JP05Bg-z5FM5gf7krRaDrsvyxqfDuvFWUHIZp2n9OZ7Au92h-x68xwg8-bwerIoPd7fesG5w-ZB6f6oXopZHNXDCscmVUQ0OIaDEOx_6pruyEcCKfD3WpOstj6lO_sYJG_zQKdBgmYvLoMFQpK-cK7t8mCLWQA2t351xc9sJ08SM0JniY73t7bOdSxF3ADVTV6ihMSti0Z6svhpknfCn9VHjT",
"links": [
{
"key": "/0031qr7q7"
},
{
"key": "/00ybqita"
},
{
"key": "/02ji9wlg"
}
]
}
```
The endpoint is paginated by default (1000/page). Just send `cursor` in the query string to access the next page.
You can set `limit` in the query string to `0` to retrieve all URLs.
You can also view URLs from your Cloudflare Dashboard:
`Cloudflare Dashboard -> Workers -> KV -> View` on the namespace.
## Plausible Analytics

View file

@ -1,4 +1,4 @@
import { Context, Hono } from 'hono'
import { Context as HonoContext, Env as HonoEnv, Hono } from 'hono'
import * as st from 'simple-runtypes'
type Variables = {
@ -9,6 +9,7 @@ type Variables = {
type Bindings = {
WORKERLINKS_SECRET: string
PLAUSIBLE_HOST?: string
KV: KVNamespace
kv: KVNamespace
}
@ -31,7 +32,18 @@ const bulkValidator = st.dictionary(
url,
)
const app = new Hono<{ Variables: Variables; Bindings: Bindings }>()
const checkAuth = (c: Context) =>
c.req.headers.get('Authorization') === c.env.WORKERLINKS_SECRET
const unauthorized = (c: Context) =>
c.json({ code: '401 Unauthorized', message: 'Unauthorized' }, 401)
type Env = {
Bindings: Bindings
Variables: Variables
}
type Context = HonoContext<Env>
const app = new Hono<Env>()
// store the path, key and short url for reference in requeests
// e.g. c.get('key')
@ -57,20 +69,48 @@ app.use('*', async (c, next) => {
// handle auth
app.use('*', async (c, next) => {
c.res.headers.set('Vary', 'Authorization')
if (c.env.WORKERLINKS_SECRET === undefined) {
return c.text('Secret is not defined. Please add WORKERLINKS_SECRET.')
}
if (
!['GET', 'HEAD'].includes(c.req.method) &&
c.req.headers.get('Authorization') !== c.env.WORKERLINKS_SECRET
) {
return c.json({ code: '401 Unauthorized', message: 'Unauthorized' }, 401)
if (!['GET', 'HEAD'].includes(c.req.method) && !checkAuth(c)) {
return unauthorized(c)
}
await next()
})
// retrieve list of keys
app.get('/', async (c) => {
if (c.req.header('Authorization')) {
if (!checkAuth(c)) {
return unauthorized(c)
}
let { prefix, cursor, limit: limitStr } = c.req.query()
prefix = prefix ? decodeURIComponent(prefix) : ''
cursor = cursor ? decodeURIComponent(cursor) : ''
let limit = limitStr ? parseInt(decodeURIComponent(limitStr)) : 1000
let { keys, ...list } = await c.env.KV.list({
limit,
prefix,
cursor,
})
return c.json({
...list,
links: keys.map((key) => ({
key: key.name,
})),
})
} else {
return handleGetHead(c)
}
})
// retrieve key
app.get('*', handleGetHead)
@ -84,7 +124,7 @@ async function handleGetHead(c: Context) {
if (urlResult == null) {
return c.json(
{ code: '404 Not Found', message: ' Key does not exist.' },
{ code: '404 Not Found', message: 'Key does not exist.' },
404,
)
} else {
@ -108,7 +148,7 @@ app.delete('*', async (c) => {
if (urlResult == null) {
return c.json(
{ code: '404 Not Found', message: ' Key does not exist.' },
{ code: '404 Not Found', message: 'Key does not exist.' },
404,
)
} else {