forked from Ivasoft/opds-proxy
feat: search
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -16,7 +15,6 @@ type KepubConverter struct {
|
||||
|
||||
func (kc *KepubConverter) Available() bool {
|
||||
kc.availableOnce.Do(func() {
|
||||
fmt.Println("TEST")
|
||||
path, err := exec.LookPath("kepubify")
|
||||
kc.available = err == nil && path != ""
|
||||
})
|
||||
|
||||
@@ -2,30 +2,7 @@
|
||||
|
||||
<nav class="navigation">
|
||||
{{if .Search }}
|
||||
<div class="search">
|
||||
<a
|
||||
href="{{.Search}}"
|
||||
tabindex="-1"
|
||||
hx-get="?q={{.Search}}&partial=search.html"
|
||||
>
|
||||
<svg
|
||||
width="30"
|
||||
height="30"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="size-6"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
{{template "search" .}}
|
||||
{{end}}
|
||||
|
||||
<div class="nav-controls">
|
||||
@@ -45,11 +22,13 @@
|
||||
{{end}}
|
||||
<div class="info">
|
||||
<p>{{.Title}}</p>
|
||||
{{if empty .Author | not }}
|
||||
<p>{{.Author}}</p>
|
||||
{{end}}
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
|
||||
{{end}}
|
||||
{{end}}
|
||||
@@ -23,6 +23,14 @@ func parse(file ...string) *template.Template {
|
||||
return template.Must(
|
||||
template.New("layout.html").
|
||||
Funcs(sprig.FuncMap()).
|
||||
Funcs(template.FuncMap{
|
||||
"getKey": func(key string, d map[string]interface{}) interface{} {
|
||||
if val, ok := d[key]; ok {
|
||||
return val
|
||||
}
|
||||
return ""
|
||||
}},
|
||||
).
|
||||
ParseFS(files, file...),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>{{block "title" .}}Kobo OPDS Proxy{{end}}</title>
|
||||
<link rel="stylesheet" href="/static/style.css" />
|
||||
<script
|
||||
src="https://unpkg.com/htmx.org@2.0.0"
|
||||
integrity="sha384-wS5l5IKJBvK6sPTKa2WZ1js3d947pvWXbPJ1OmWfEuxLgeHcEbjUUA5i9V5ZkpCw"
|
||||
crossorigin="anonymous"
|
||||
></script>
|
||||
</head>
|
||||
<body>
|
||||
<main id="container">{{block "content" .}}{{end}}</main>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>{{block "title" .}}Kobo OPDS Proxy{{end}}</title>
|
||||
<link rel="stylesheet" href="/static/style.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main id="container">{{block "content" .}}{{end}}</main>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,28 +1,23 @@
|
||||
{{define "title"}}Feed Log In{{end}}
|
||||
{{ define "content" }}
|
||||
<main>
|
||||
<h1>{{index (urlParse .ReturnURL) "query" | trimPrefix "q=" }}</h1>
|
||||
<p>Log in to access this feed</p>
|
||||
<div id="content">
|
||||
<form method="post">
|
||||
<input type="text" name="username" placeholder="Username" />
|
||||
<input type="password" name="password" placeholder="Password" />
|
||||
<button type="submit">Log In</button>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
<h1>{{urlParse .ReturnURL | getKey "query" | trimPrefix "q=" | urlParse | getKey "host" }}</h1>
|
||||
<p>Log in to access this feed.</p>
|
||||
<div id="content">
|
||||
<form method="post">
|
||||
<input type="text" name="username" placeholder="Username" />
|
||||
<input type="password" name="password" placeholder="Password" />
|
||||
<button type="submit">Log In</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
main {
|
||||
margin-top: 2rem;
|
||||
h1,
|
||||
p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
form {
|
||||
margin: 0 auto;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
input {
|
||||
@@ -35,7 +30,6 @@
|
||||
}
|
||||
|
||||
button {
|
||||
font-style: normal;
|
||||
padding: 0.8rem 1rem;
|
||||
margin: 1rem auto;
|
||||
display: block;
|
||||
@@ -43,6 +37,7 @@
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 2px;
|
||||
width: 268px;
|
||||
}
|
||||
</style>
|
||||
{{ end }}
|
||||
@@ -1,16 +1,23 @@
|
||||
{{define "search-partial"}}
|
||||
{{define "search"}}
|
||||
|
||||
<form method="get">
|
||||
<form method="get" id="search-form">
|
||||
<input type="hidden" name="q" value="{{.Search}}" />
|
||||
<input type="search" placeholder="Search" name="search" />
|
||||
<label for="search">
|
||||
<svg width="30" height="30" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class="size-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z" />
|
||||
</svg>
|
||||
<input tabindex="-1" type="search" id="search" placeholder="Search" name="search" />
|
||||
</label>
|
||||
|
||||
<script>
|
||||
const searchInput = document.querySelector("input[name=search]");
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
if (searchParams.has("search") && !!searchInput) {
|
||||
if (searchParams.has("search")) {
|
||||
searchInput.value = searchParams.get("search");
|
||||
}
|
||||
</script>
|
||||
</form>
|
||||
|
||||
{{end}}
|
||||
{{end}}
|
||||
@@ -8,41 +8,47 @@
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
#container {
|
||||
padding: 1rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#container > ul {
|
||||
#container>ul {
|
||||
margin-top: 75px;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
#container > ul {
|
||||
#container>ul {
|
||||
margin-top: 125px;
|
||||
}
|
||||
}
|
||||
|
||||
#container > ul > li {
|
||||
#container>ul>li {
|
||||
padding: 1rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#container > ul > li > a > img {
|
||||
#container>ul>li>a>img {
|
||||
display: inline-block;
|
||||
height: 75px;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
#container > ul > li > a > .info {
|
||||
|
||||
#container>ul>li>a>.info {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
white-space: normal;
|
||||
max-width: calc(100% - 3rem - 75px);
|
||||
}
|
||||
|
||||
.info > p:not(:first-child) {
|
||||
font-size: 1rem;
|
||||
.info>p:first-child {
|
||||
font-weight: 500;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
a {
|
||||
@@ -51,11 +57,11 @@ a {
|
||||
color: black;
|
||||
}
|
||||
|
||||
#container > ul > li a {
|
||||
#container>ul>li a {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#container > ul > li:not(:last-child) {
|
||||
#container>ul>li:not(:last-child) {
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
@@ -69,12 +75,13 @@ a {
|
||||
|
||||
display: block;
|
||||
|
||||
height: 64px;
|
||||
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.search {
|
||||
width: 100px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.search:focus {
|
||||
@@ -82,19 +89,45 @@ a {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.search > a {
|
||||
#search-form {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#search-form>label>input {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
font-size: 1rem;
|
||||
border: none;
|
||||
border-right: 1px solid rgba(0, 0, 0, 0.5);
|
||||
padding-left: 3rem;
|
||||
}
|
||||
|
||||
#search-form>label {
|
||||
position: relative;
|
||||
}
|
||||
#search-form>label>svg {
|
||||
position: absolute;
|
||||
top: 25%;
|
||||
left: 0.5rem;
|
||||
height: 1rem;
|
||||
}
|
||||
|
||||
.search>form {
|
||||
padding-top: 1rem;
|
||||
padding-left: 1rem;
|
||||
display: block;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.nav-controls {
|
||||
float: right;
|
||||
vertical-align: center;
|
||||
}
|
||||
|
||||
.nav-controls > a {
|
||||
.nav-controls>a {
|
||||
padding: 1rem 0.5rem;
|
||||
height: 100%;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
@@ -102,23 +135,42 @@ a {
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
*, *::before, *::after {
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
line-height: 1.5;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
/* -webkit-font-smoothing: antialiased; */
|
||||
}
|
||||
img, picture, video, canvas, svg {
|
||||
|
||||
img,
|
||||
picture,
|
||||
video,
|
||||
canvas,
|
||||
svg {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
}
|
||||
input, button, textarea, select {
|
||||
|
||||
input,
|
||||
button,
|
||||
textarea,
|
||||
select {
|
||||
font: inherit;
|
||||
}
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
|
||||
p,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
@@ -95,7 +95,6 @@ func handleFeed(outputDir string) http.HandlerFunc {
|
||||
|
||||
searchTerm := r.URL.Query().Get("search")
|
||||
if searchTerm != "" {
|
||||
fmt.Println("Search term", searchTerm)
|
||||
queryURL = replaceSearchPlaceHolder(queryURL, searchTerm)
|
||||
}
|
||||
|
||||
@@ -213,6 +212,7 @@ func handleAuth() http.HandlerFunc {
|
||||
|
||||
http.SetCookie(w, cookie)
|
||||
http.Redirect(w, r, returnUrl, http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
|
||||
Reference in New Issue
Block a user