feat: bulk create on POST / with a body
This commit is contained in:
parent
cf1c7edc9c
commit
a753e8d0a0
2 changed files with 76 additions and 13 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -9,3 +9,4 @@ worker/
|
||||||
node_modules/
|
node_modules/
|
||||||
.cargo-ok
|
.cargo-ok
|
||||||
wrangler.prod.toml
|
wrangler.prod.toml
|
||||||
|
.mf
|
||||||
88
index.js
88
index.js
|
|
@ -19,8 +19,8 @@ async function handleRequest(event) {
|
||||||
|
|
||||||
let path = new URL(request.url).pathname
|
let path = new URL(request.url).pathname
|
||||||
// Trim trailing slash
|
// Trim trailing slash
|
||||||
let key = path !== '/' ? path.replace(/\/$/, '') : path;
|
let key = path !== '/' ? path.replace(/\/$/, '') : path
|
||||||
let shorturl = new URL(request.url).origin + key
|
let shorturl = new URL(key, request.url).origin
|
||||||
|
|
||||||
if (request.method == 'PUT') {
|
if (request.method == 'PUT') {
|
||||||
return await putLink(
|
return await putLink(
|
||||||
|
|
@ -41,14 +41,65 @@ async function handleRequest(event) {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
key = '/' + Math.random().toString(36).slice(5)
|
const body = await request.text()
|
||||||
shorturl = new URL(request.url).origin + key
|
|
||||||
return await putLink(
|
if (!body) {
|
||||||
request.headers.get('Authorization'),
|
key = '/' + Math.random().toString(36).slice(5)
|
||||||
shorturl,
|
shorturl = new URL(key, request.url)
|
||||||
key,
|
return await putLink(
|
||||||
request.headers.get('URL'),
|
request.headers.get('Authorization'),
|
||||||
)
|
shorturl,
|
||||||
|
key,
|
||||||
|
request.headers.get('URL'),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
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') {
|
} else if (request.method == 'GET' || request.method == 'HEAD') {
|
||||||
let url = await kv.get(key)
|
let url = await kv.get(key)
|
||||||
if (url == null) {
|
if (url == null) {
|
||||||
|
|
@ -99,7 +150,7 @@ async function handleRequest(event) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
shorturl = new URL(request.url).origin + key
|
shorturl = new URL(key, request.url)
|
||||||
let url = await kv.get(key)
|
let url = await kv.get(key)
|
||||||
if (url == null) {
|
if (url == null) {
|
||||||
return Response.json(
|
return Response.json(
|
||||||
|
|
@ -116,7 +167,7 @@ async function handleRequest(event) {
|
||||||
return Response.json(
|
return Response.json(
|
||||||
{
|
{
|
||||||
message: 'Short URL deleted succesfully.',
|
message: 'Short URL deleted succesfully.',
|
||||||
key: key.substr(1),
|
key: key.slice(1),
|
||||||
shorturl: shorturl,
|
shorturl: shorturl,
|
||||||
longurl: url,
|
longurl: url,
|
||||||
},
|
},
|
||||||
|
|
@ -152,6 +203,17 @@ function validateUrl(url) {
|
||||||
return true
|
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) {
|
async function putLink(givenSecret, shorturl, key, url) {
|
||||||
if (givenSecret != secret) {
|
if (givenSecret != secret) {
|
||||||
return Response.json(
|
return Response.json(
|
||||||
|
|
@ -181,7 +243,7 @@ async function putLink(givenSecret, shorturl, key, url) {
|
||||||
return Response.json(
|
return Response.json(
|
||||||
{
|
{
|
||||||
message: 'URL created succesfully.',
|
message: 'URL created succesfully.',
|
||||||
key: key.substr(1),
|
key: key.slice(1),
|
||||||
shorturl: shorturl,
|
shorturl: shorturl,
|
||||||
longurl: url,
|
longurl: url,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue