Bookmarklets & CORS
This commit is contained in:
44
main.go
44
main.go
@@ -72,6 +72,7 @@ func NewShortLinks(db *sql.DB, domainAliases map[string]string, writableDomains
|
|||||||
sl.mux.HandleFunc("GET /{short}", sl.serveShort)
|
sl.mux.HandleFunc("GET /{short}", sl.serveShort)
|
||||||
sl.mux.HandleFunc("POST /{$}", sl.serveSet)
|
sl.mux.HandleFunc("POST /{$}", sl.serveSet)
|
||||||
sl.mux.HandleFunc("QUERY /{$}", sl.serveSuggest)
|
sl.mux.HandleFunc("QUERY /{$}", sl.serveSuggest)
|
||||||
|
sl.mux.HandleFunc("OPTIONS /{$}", sl.serveOptions)
|
||||||
|
|
||||||
return sl, nil
|
return sl, nil
|
||||||
}
|
}
|
||||||
@@ -81,9 +82,9 @@ func (sl *ShortLinks) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (sl *ShortLinks) serveRoot(w http.ResponseWriter, r *http.Request) {
|
func (sl *ShortLinks) serveRoot(w http.ResponseWriter, r *http.Request) {
|
||||||
err := sl.parseForm(r)
|
err := sl.initRequest(w, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendError(w, http.StatusBadRequest, "parse form: %s", err)
|
sendError(w, http.StatusBadRequest, "init request: %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,9 +109,9 @@ func (sl *ShortLinks) serveRoot(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (sl *ShortLinks) serveRootWithPath(w http.ResponseWriter, r *http.Request, path string) {
|
func (sl *ShortLinks) serveRootWithPath(w http.ResponseWriter, r *http.Request, path string) {
|
||||||
err := sl.parseForm(r)
|
err := sl.initRequest(w, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendError(w, http.StatusBadRequest, "parse form: %s", err)
|
sendError(w, http.StatusBadRequest, "init request: %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,9 +151,9 @@ func (sl *ShortLinks) serveShort(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (sl *ShortLinks) serveSet(w http.ResponseWriter, r *http.Request) {
|
func (sl *ShortLinks) serveSet(w http.ResponseWriter, r *http.Request) {
|
||||||
err := sl.parseForm(r)
|
err := sl.initRequest(w, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendError(w, http.StatusBadRequest, "parse form: %s", err)
|
sendError(w, http.StatusBadRequest, "init request: %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,9 +195,9 @@ func (sl *ShortLinks) serveSet(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (sl *ShortLinks) serveSuggest(w http.ResponseWriter, r *http.Request) {
|
func (sl *ShortLinks) serveSuggest(w http.ResponseWriter, r *http.Request) {
|
||||||
err := sl.parseForm(r)
|
err := sl.initRequest(w, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendError(w, http.StatusBadRequest, "parse form: %s", err)
|
sendError(w, http.StatusBadRequest, "init request: %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,7 +242,8 @@ func (sl *ShortLinks) serveHelp(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := sl.help.Execute(w, map[string]any{
|
err := sl.help.Execute(w, map[string]any{
|
||||||
"host": r.Host,
|
"writeHost": r.Host,
|
||||||
|
"readHost": sl.getDomain(r.Host),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendError(w, http.StatusInternalServerError, "error executing template: %s", err)
|
sendError(w, http.StatusInternalServerError, "error executing template: %s", err)
|
||||||
@@ -249,6 +251,16 @@ func (sl *ShortLinks) serveHelp(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sl *ShortLinks) serveOptions(w http.ResponseWriter, r *http.Request) {
|
||||||
|
err := sl.initRequest(w, r)
|
||||||
|
if err != nil {
|
||||||
|
sendError(w, http.StatusBadRequest, "init request: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
}
|
||||||
|
|
||||||
func (sl *ShortLinks) genShort(domain string) (string, error) {
|
func (sl *ShortLinks) genShort(domain string) (string, error) {
|
||||||
for chars := 3; chars <= 10; chars++ {
|
for chars := 3; chars <= 10; chars++ {
|
||||||
b := make([]byte, chars)
|
b := make([]byte, chars)
|
||||||
@@ -295,7 +307,13 @@ func (sl *ShortLinks) getLong(short, domain string) (string, error) {
|
|||||||
return long, nil
|
return long, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sl *ShortLinks) parseForm(r *http.Request) error {
|
func (sl *ShortLinks) initRequest(w http.ResponseWriter, r *http.Request) error {
|
||||||
|
log.Printf("%s %s %s %s %s %#v", r.RemoteAddr, r.Method, r.Host, sl.getDomain(r.Host), r.URL, r.Form)
|
||||||
|
|
||||||
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||||
|
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, QUERY, OPTIONS")
|
||||||
|
w.Header().Set("Access-Control-Allow-Headers", "*")
|
||||||
|
|
||||||
defer r.Body.Close()
|
defer r.Body.Close()
|
||||||
|
|
||||||
err := r.ParseForm()
|
err := r.ParseForm()
|
||||||
@@ -325,15 +343,9 @@ func (sl *ShortLinks) parseForm(r *http.Request) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sl.logRequest(r)
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sl *ShortLinks) logRequest(r *http.Request) {
|
|
||||||
log.Printf("%s %s %s %s %s %#v", r.RemoteAddr, r.Method, r.Host, sl.getDomain(r.Host), r.URL, r.Form)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
port := os.Getenv("PORT")
|
port := os.Getenv("PORT")
|
||||||
if port == "" {
|
if port == "" {
|
||||||
|
|||||||
@@ -26,6 +26,11 @@ br + sl-tag[pill] {
|
|||||||
sl-tag::part(base) {
|
sl-tag::part(base) {
|
||||||
user-select: text;
|
user-select: text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--sl-color-primary-900);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMTIwMHB0IiBoZWlnaHQ9IjEyMDBwdCIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIDAgMTIwMCAxMjAwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogPHBhdGggZD0ibTExNC42NiAxMDE0LjdjLTE5LjU0NyAxOS41LTE5LjU0NyA1MS4xODggMCA3MC42ODggMTkuNSAxOS41NDcgNTEuMTg4IDE5LjU0NyA3MC42ODggMGwyNjQuNjYtMjY0LjY2djE3OS4yOWMwIDI3LjYwOSAyMi40MDYgNTAuMDE2IDUwLjAxNiA1MC4wMTZzNDkuOTY5LTIyLjQwNiA0OS45NjktNTAuMDE2di0zMDBjMC0yNy42MDktMjIuMzU5LTQ5Ljk2OS00OS45NjktNDkuOTY5aC0zMDBjLTI3LjYwOSAwLTUwLjAxNiAyMi4zNTktNTAuMDE2IDQ5Ljk2OXMyMi40MDYgNTAuMDE2IDUwLjAxNiA1MC4wMTZoMTc5LjI5eiIgZmlsbD0iI2ZmM2NlZSIvPgogPHBhdGggZD0ibTY5OS45OCAxNTBjMjcuNjA5IDAgNTAuMDE2IDIyLjQwNiA1MC4wMTYgNTAuMDE2djE3OS4yOWwyNjQuNjYtMjY0LjY2YzE5LjUtMTkuNTQ3IDUxLjE4OC0xOS41NDcgNzAuNjg4IDAgMTkuNTQ3IDE5LjUgMTkuNTQ3IDUxLjE4OCAwIDcwLjY4OGwtMjY0LjY2IDI2NC42NmgxNzkuMjljMjcuNjA5IDAgNTAuMDE2IDIyLjQwNiA1MC4wMTYgNTAuMDE2cy0yMi40MDYgNDkuOTY5LTUwLjAxNiA0OS45NjloLTMwMGMtMjcuNjA5IDAtNDkuOTY5LTIyLjM1OS00OS45NjktNDkuOTY5di0zMDBjMC0yNy42MDkgMjIuMzU5LTUwLjAxNiA0OS45NjktNTAuMDE2eiIgZmlsbD0iI2ZmM2NlZSIvPgo8L3N2Zz4K" />
|
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMTIwMHB0IiBoZWlnaHQ9IjEyMDBwdCIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIDAgMTIwMCAxMjAwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogPHBhdGggZD0ibTExNC42NiAxMDE0LjdjLTE5LjU0NyAxOS41LTE5LjU0NyA1MS4xODggMCA3MC42ODggMTkuNSAxOS41NDcgNTEuMTg4IDE5LjU0NyA3MC42ODggMGwyNjQuNjYtMjY0LjY2djE3OS4yOWMwIDI3LjYwOSAyMi40MDYgNTAuMDE2IDUwLjAxNiA1MC4wMTZzNDkuOTY5LTIyLjQwNiA0OS45NjktNTAuMDE2di0zMDBjMC0yNy42MDktMjIuMzU5LTQ5Ljk2OS00OS45NjktNDkuOTY5aC0zMDBjLTI3LjYwOSAwLTUwLjAxNiAyMi4zNTktNTAuMDE2IDQ5Ljk2OXMyMi40MDYgNTAuMDE2IDUwLjAxNiA1MC4wMTZoMTc5LjI5eiIgZmlsbD0iI2ZmM2NlZSIvPgogPHBhdGggZD0ibTY5OS45OCAxNTBjMjcuNjA5IDAgNTAuMDE2IDIyLjQwNiA1MC4wMTYgNTAuMDE2djE3OS4yOWwyNjQuNjYtMjY0LjY2YzE5LjUtMTkuNTQ3IDUxLjE4OC0xOS41NDcgNzAuNjg4IDAgMTkuNTQ3IDE5LjUgMTkuNTQ3IDUxLjE4OCAwIDcwLjY4OGwtMjY0LjY2IDI2NC42NmgxNzkuMjljMjcuNjA5IDAgNTAuMDE2IDIyLjQwNiA1MC4wMTYgNTAuMDE2cy0yMi40MDYgNDkuOTY5LTUwLjAxNiA0OS45NjloLTMwMGMtMjcuNjA5IDAtNDkuOTY5LTIyLjM1OS00OS45NjktNDkuOTY5di0zMDBjMC0yNy42MDkgMjIuMzU5LTUwLjAxNiA0OS45NjktNTAuMDE2eiIgZmlsbD0iI2ZmM2NlZSIvPgo8L3N2Zz4K" />
|
||||||
@@ -41,8 +46,6 @@ sl-tag::part(base) {
|
|||||||
onload="document.documentElement.classList.add('sl-theme-dark');"
|
onload="document.documentElement.classList.add('sl-theme-dark');"
|
||||||
/>
|
/>
|
||||||
<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.18.0/cdn/shoelace-autoloader.js"></script>
|
<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.18.0/cdn/shoelace-autoloader.js"></script>
|
||||||
<script>
|
|
||||||
</script>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div style="width: min(500px, calc(100vw - 10px))">
|
<div style="width: min(500px, calc(100vw - 10px))">
|
||||||
@@ -50,6 +53,7 @@ sl-tag::part(base) {
|
|||||||
<sl-tab slot="nav" panel="share">Share</sl-tab>
|
<sl-tab slot="nav" panel="share">Share</sl-tab>
|
||||||
<sl-tab slot="nav" panel="copy">Copy</sl-tab>
|
<sl-tab slot="nav" panel="copy">Copy</sl-tab>
|
||||||
<sl-tab slot="nav" panel="custom">Custom</sl-tab>
|
<sl-tab slot="nav" panel="custom">Custom</sl-tab>
|
||||||
|
<sl-tab slot="nav" panel="browser">Browser</sl-tab>
|
||||||
|
|
||||||
<sl-tab-panel name="share">
|
<sl-tab-panel name="share">
|
||||||
<sl-card class="card-basic">
|
<sl-card class="card-basic">
|
||||||
@@ -66,7 +70,7 @@ sl-tag::part(base) {
|
|||||||
|
|
||||||
<sl-card class="card-basic">
|
<sl-card class="card-basic">
|
||||||
Get contents of
|
Get contents of
|
||||||
<sl-tag size="small" pill variant="primary">https://{{ .host }}/</sl-tag>
|
<sl-tag size="small" pill variant="primary">https://{{ .writeHost }}/</sl-tag>
|
||||||
|
|
||||||
<sl-divider></sl-divider>
|
<sl-divider></sl-divider>
|
||||||
|
|
||||||
@@ -120,7 +124,7 @@ sl-tag::part(base) {
|
|||||||
|
|
||||||
<sl-card class="card-basic">
|
<sl-card class="card-basic">
|
||||||
Get contents of
|
Get contents of
|
||||||
<sl-tag size="small" pill variant="primary">https://{{ .host }}/</sl-tag>
|
<sl-tag size="small" pill variant="primary">https://{{ .writeHost }}/</sl-tag>
|
||||||
|
|
||||||
<sl-divider></sl-divider>
|
<sl-divider></sl-divider>
|
||||||
|
|
||||||
@@ -181,7 +185,7 @@ sl-tag::part(base) {
|
|||||||
|
|
||||||
<sl-card class="card-basic">
|
<sl-card class="card-basic">
|
||||||
Combine
|
Combine
|
||||||
<sl-tag size="small" pill variant="primary">https://{{ .host }}/?long=</sl-tag>
|
<sl-tag size="small" pill variant="primary">https://{{ .writeHost }}/?long=</sl-tag>
|
||||||
<sl-tag size="small" pill variant="primary">URL Encoded Text</sl-tag>
|
<sl-tag size="small" pill variant="primary">URL Encoded Text</sl-tag>
|
||||||
<sl-tag size="small" pill variant="neutral">+</sl-tag>
|
<sl-tag size="small" pill variant="neutral">+</sl-tag>
|
||||||
with
|
with
|
||||||
@@ -194,6 +198,47 @@ sl-tag::part(base) {
|
|||||||
<sl-tag size="small" pill variant="primary">Combined Text</sl-tag>
|
<sl-tag size="small" pill variant="primary">Combined Text</sl-tag>
|
||||||
</sl-card>
|
</sl-card>
|
||||||
</sl-tab-panel>
|
</sl-tab-panel>
|
||||||
|
|
||||||
|
<sl-tab-panel name="browser">
|
||||||
|
<sl-card class="card-basic">
|
||||||
|
Drag links to bookmark bar:
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<sl-tag pill variant="primary">
|
||||||
|
<a href="javascript:(async function() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('{{ .writeHost }}', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ long: location.href }),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(response.statusText);
|
||||||
|
}
|
||||||
|
|
||||||
|
await navigator.clipboard.writeText((await response.json()).url);
|
||||||
|
} catch (err) {
|
||||||
|
alert(err);
|
||||||
|
}
|
||||||
|
})();">{{ .readHost }} (Copy)</a>
|
||||||
|
</sl-tag>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<sl-tag pill variant="primary">
|
||||||
|
<a href="javascript:(async function() {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.set('long', location.href);
|
||||||
|
|
||||||
|
window.open(`https://{{ .writeHost }}/?${params.toString()}`);
|
||||||
|
})();">{{ .readHost }} (Custom)</a>
|
||||||
|
</sl-tag>
|
||||||
|
</sl-card>
|
||||||
|
</sl-tab-panel>
|
||||||
</sl-tab-group>
|
</sl-tab-group>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Reference in New Issue
Block a user