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,
|
||||
"trailingComma": "all",
|
||||
"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.
|
||||
|
||||
## (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
|
||||
|
||||
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",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"format": "prettier --write '**/*.{ts,js,css,json,md}'",
|
||||
"format": "prettier --write '**/*.{ts,js,css,json,md,html}'",
|
||||
"deploy": "wrangler publish",
|
||||
"dev": "wrangler dev",
|
||||
"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 * 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 = {
|
||||
path: string
|
||||
key: string
|
||||
|
|
@ -12,6 +15,7 @@ type Bindings = {
|
|||
PLAUSIBLE_HOST?: string
|
||||
KV: KVNamespace
|
||||
kv: KVNamespace
|
||||
ENABLE_INDEX_FORM: boolean
|
||||
}
|
||||
|
||||
const url = st.runtype((v) => {
|
||||
|
|
@ -107,6 +111,10 @@ app.get('/', async (c) => {
|
|||
})),
|
||||
})
|
||||
} 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)
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -14,3 +14,6 @@ compatibility_date = "2023-03-05"
|
|||
# Remove or comment out the route line if using workers_dev
|
||||
workers_dev = false
|
||||
route = { pattern = "erisa.link/*", zone_name = "erisa.link" }
|
||||
|
||||
[vars]
|
||||
ENABLE_INDEX_FORM = true
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue