Simple web UI for shortlink creation (#15)
* add HTML interface (inlined into typescript) - with CSS and autocomplete hand-holding * Some changes (listed below) - Make UI optional and disabled by default - Lint it in line with other code - Import UI from a bundled HTML file - Add UI to README - Add support for random URL generation - Show the URL in the output --------- Co-authored-by: Erisa A <erisa@erisa.uk>
This commit is contained in:
parent
ad4ba3f6f0
commit
1bef54163c
7 changed files with 137 additions and 2 deletions
10
.prettierrc
10
.prettierrc
|
|
@ -3,5 +3,13 @@
|
||||||
"semi": false,
|
"semi": false,
|
||||||
"trailingComma": "all",
|
"trailingComma": "all",
|
||||||
"useTabs": true,
|
"useTabs": true,
|
||||||
"printWidth": 80
|
"printWidth": 80,
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": "src/*.html",
|
||||||
|
"options": {
|
||||||
|
"printWidth": 140
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,12 @@ For debuggging, you can use `yarn dev` (Which runs `wrangler dev --local`). To c
|
||||||
|
|
||||||
You can also debug on the edge with `wrangler dev`, though you will need to first configure a prepview namespace in `wrangler.toml` and add the `WORKERLINKS_SECRET` secret to the Worker.
|
You can also debug on the edge with `wrangler dev`, though you will need to first configure a prepview namespace in `wrangler.toml` and add the `WORKERLINKS_SECRET` secret to the Worker.
|
||||||
|
|
||||||
|
## (Optional) User Interface
|
||||||
|
|
||||||
|
If `ENABLE_INDEX_FORM` is enabled in `wrangler.toml`, an optional UI form is available when visiting the Worker in a browser, allowing easy creation of links:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Once deployed, interacting with the API should be rather simple. It's based on headers, specifically with the `Authorization` and `URL` headers.
|
Once deployed, interacting with the API should be rather simple. It's based on headers, specifically with the `Authorization` and `URL` headers.
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
"author": "Erisa A",
|
"author": "Erisa A",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"format": "prettier --write '**/*.{ts,js,css,json,md}'",
|
"format": "prettier --write '**/*.{ts,js,css,json,md,html}'",
|
||||||
"deploy": "wrangler publish",
|
"deploy": "wrangler publish",
|
||||||
"dev": "wrangler dev",
|
"dev": "wrangler dev",
|
||||||
"addsecret": "wrangler secret put WORKERLINKS_SECRET",
|
"addsecret": "wrangler secret put WORKERLINKS_SECRET",
|
||||||
|
|
|
||||||
106
src/form.html
Normal file
106
src/form.html
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>Shorten a URL</title>
|
||||||
|
<meta content="width=device-width,initial-scale=1" name="viewport" />
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width: 480px;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button[type='submit'] {
|
||||||
|
background-color: green;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 1.5em;
|
||||||
|
padding: 10px 20px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 75%;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='submit'] {
|
||||||
|
background-color: #4caf50;
|
||||||
|
border: none;
|
||||||
|
color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 16px;
|
||||||
|
margin-top: 16px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='submit']:hover {
|
||||||
|
background-color: #3e8e41;
|
||||||
|
}
|
||||||
|
|
||||||
|
#response {
|
||||||
|
margin-top: 16px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<form id="myForm" onsubmit="submitForm(event)">
|
||||||
|
<label for="value">Long URL:</label>
|
||||||
|
<input autocomplete="one-time-code" id="value" name="value" placeholder="https://google.com/search?q=how+does+google" /><br /><br />
|
||||||
|
<label for="key">Short Key for URL: (optional)</label>
|
||||||
|
<input autocomplete="one-time-code" id="key" name="key" placeholder="metagoogle" /><br /><br />
|
||||||
|
<label for="password">Password:</label>
|
||||||
|
<input autocomplete="password" id="password" name="password" type="password" placeholder="mysecret" /><br /><br />
|
||||||
|
<button type="submit">Submit</button><br /><br />
|
||||||
|
<div id="response"></div>
|
||||||
|
<br />
|
||||||
|
<span id="url"></span><a id="urlLink"></a>
|
||||||
|
</form>
|
||||||
|
<script>
|
||||||
|
function submitForm(event) {
|
||||||
|
event.preventDefault()
|
||||||
|
const key = document.getElementById('key').value
|
||||||
|
const value = document.getElementById('value').value
|
||||||
|
const password = document.getElementById('password').value
|
||||||
|
const xhr = new XMLHttpRequest()
|
||||||
|
xhr.open(key === '' ? 'POST' : 'PUT', window.location.href + key, true)
|
||||||
|
xhr.setRequestHeader('Url', value)
|
||||||
|
xhr.setRequestHeader('Authorization', password)
|
||||||
|
xhr.responseType = 'json'
|
||||||
|
xhr.onload = function () {
|
||||||
|
const response = document.getElementById('response')
|
||||||
|
response.textContent = 'Response: ' + xhr.response.message
|
||||||
|
const urlText = document.getElementById('url')
|
||||||
|
const urlLink = document.getElementById('urlLink')
|
||||||
|
if (xhr.response.shorturl) {
|
||||||
|
urlText.textContent = 'URL: '
|
||||||
|
urlLink.textContent = xhr.response.shorturl
|
||||||
|
urlLink.href = xhr.response.shorturl
|
||||||
|
} else {
|
||||||
|
urlText.textContent = ''
|
||||||
|
urlLink.textContent = ''
|
||||||
|
urlLink.href = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xhr.send()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
4
src/html.d.ts
vendored
Normal file
4
src/html.d.ts
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
declare module '*.html' {
|
||||||
|
const value: string
|
||||||
|
export default value
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
import { Context as HonoContext, Env as HonoEnv, Hono } from 'hono'
|
import { Context as HonoContext, Env as HonoEnv, Hono } from 'hono'
|
||||||
import * as st from 'simple-runtypes'
|
import * as st from 'simple-runtypes'
|
||||||
|
|
||||||
|
// html.d.ts tells typescript that this is a normal thing to do
|
||||||
|
import creationPageHtml from './form.html'
|
||||||
|
|
||||||
type Variables = {
|
type Variables = {
|
||||||
path: string
|
path: string
|
||||||
key: string
|
key: string
|
||||||
|
|
@ -12,6 +15,7 @@ type Bindings = {
|
||||||
PLAUSIBLE_HOST?: string
|
PLAUSIBLE_HOST?: string
|
||||||
KV: KVNamespace
|
KV: KVNamespace
|
||||||
kv: KVNamespace
|
kv: KVNamespace
|
||||||
|
ENABLE_INDEX_FORM: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = st.runtype((v) => {
|
const url = st.runtype((v) => {
|
||||||
|
|
@ -107,6 +111,10 @@ app.get('/', async (c) => {
|
||||||
})),
|
})),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
const urlResult = await c.env.KV.get(c.get('key'))
|
||||||
|
if (urlResult == null && c.env.ENABLE_INDEX_FORM) {
|
||||||
|
return c.html(creationPageHtml)
|
||||||
|
}
|
||||||
return handleGetHead(c)
|
return handleGetHead(c)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -14,3 +14,6 @@ compatibility_date = "2023-03-05"
|
||||||
# Remove or comment out the route line if using workers_dev
|
# Remove or comment out the route line if using workers_dev
|
||||||
workers_dev = false
|
workers_dev = false
|
||||||
route = { pattern = "erisa.link/*", zone_name = "erisa.link" }
|
route = { pattern = "erisa.link/*", zone_name = "erisa.link" }
|
||||||
|
|
||||||
|
[vars]
|
||||||
|
ENABLE_INDEX_FORM = true
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue