Compare commits
4 Commits
dbfd33f55e
...
bf23d6a9cf
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf23d6a9cf | ||
|
|
0271f84948 | ||
|
|
0e3e6c54de | ||
|
|
352eff2691 |
@@ -70,17 +70,19 @@ func oneItem(ctx context.Context, feed feeds.Feed, item feeds.Item) error {
|
|||||||
Item: item,
|
Item: item,
|
||||||
}
|
}
|
||||||
|
|
||||||
method, err := render(feed.Version.WebhookMethod, arg)
|
wmethod, wurl, wbody := feed.Webhook()
|
||||||
|
|
||||||
|
method, err := render(wmethod, arg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
wurl, err := render(feed.Version.WebhookURL, arg)
|
wurl, err = render(wurl, arg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
body, err := render(feed.Version.WebhookBody, arg)
|
body, err := render(wbody, arg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,9 +29,9 @@ func TestOne(t *testing.T) {
|
|||||||
},
|
},
|
||||||
"feeds": func(t *testing.T) context.Context {
|
"feeds": func(t *testing.T) context.Context {
|
||||||
gets := []string{}
|
gets := []string{}
|
||||||
sURL := "http://localhost:10000/"
|
sURL := "http://localhost:10001/"
|
||||||
s := &http.Server{
|
s := &http.Server{
|
||||||
Addr: ":10000",
|
Addr: ":10001",
|
||||||
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
gets = append(gets, r.URL.String())
|
gets = append(gets, r.URL.String())
|
||||||
rb, _ := io.ReadAll(r.Body)
|
rb, _ := io.ReadAll(r.Body)
|
||||||
@@ -43,6 +43,7 @@ func TestOne(t *testing.T) {
|
|||||||
case "10":
|
case "10":
|
||||||
case "11":
|
case "11":
|
||||||
default:
|
default:
|
||||||
|
t.Logf("%s => 404", r.URL)
|
||||||
http.NotFound(w, r)
|
http.NotFound(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,12 +24,12 @@ func (h Handler) feeds(w http.ResponseWriter, r *http.Request) error {
|
|||||||
func (h Handler) feedsPost(ctx context.Context, form url.Values) error {
|
func (h Handler) feedsPost(ctx context.Context, form url.Values) error {
|
||||||
var req feeds.Version
|
var req feeds.Version
|
||||||
for k, v := range map[string]*string{
|
for k, v := range map[string]*string{
|
||||||
"URL": &req.URL,
|
|
||||||
"Cron": &req.Cron,
|
"Cron": &req.Cron,
|
||||||
"Pattern": &req.Pattern,
|
"Pattern": &req.Pattern,
|
||||||
|
"URL": &req.URL,
|
||||||
|
"WebhookBody": &req.WebhookBody,
|
||||||
"WebhookMethod": &req.WebhookMethod,
|
"WebhookMethod": &req.WebhookMethod,
|
||||||
"WebhookURL": &req.WebhookURL,
|
"WebhookURL": &req.WebhookURL,
|
||||||
"WebhookBody": &req.WebhookBody,
|
|
||||||
} {
|
} {
|
||||||
if *v = form.Get(k); *v == "" {
|
if *v = form.Get(k); *v == "" {
|
||||||
return fmt.Errorf("no ?%s in %s", k, form.Encode())
|
return fmt.Errorf("no ?%s in %s", k, form.Encode())
|
||||||
|
|||||||
38
src/cmd/server/handler/testdata/index.tmpl
vendored
38
src/cmd/server/handler/testdata/index.tmpl
vendored
@@ -1,11 +1,6 @@
|
|||||||
<html>
|
<html>
|
||||||
<header>
|
<header>
|
||||||
<link rel="stylesheet" href="/experimental/ui/dark.css">
|
<link rel="stylesheet" href="/experimental/ui/dark.css">
|
||||||
<script>
|
|
||||||
fill(elem) {
|
|
||||||
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</header>
|
</header>
|
||||||
<body>
|
<body>
|
||||||
<h2>Feeds</h2>
|
<h2>Feeds</h2>
|
||||||
@@ -13,24 +8,37 @@
|
|||||||
<div>
|
<div>
|
||||||
{{ range feeds }}
|
{{ range feeds }}
|
||||||
<div>
|
<div>
|
||||||
<h4>(<button onclick="fill(this)">{{ namespan "entry.id" .Entry.ID}}</button>) {{ namespan "version.url" .Version.URL }}</h4>
|
<h3><code><a href="?edit={{.Entry.ID}}">{{ .Version.URL }}</a></code></h3>
|
||||||
<div>{{ .Version.Created }} (last {{ .Execution.Executed }})</div>
|
<div>@<code>{{ .Version.Cron }}</code> ~<code>{{ .Version.Pattern }}</code></div>
|
||||||
<div>@{{ .Version.Cron }} ~"{{ .Version.Pattern }}"</div>
|
<div><code>{{ .Version.WebhookMethod }} {{ .Version.WebhookURL }} | {{ .Version.WebhookBody }}</code></div>
|
||||||
<div>{{ .Version.WebhookMethod }} {{ .Version.WebhookURL }} | {{ .Version.WebhookBody }}</div>
|
<div>(last run {{ ago .Execution.Executed }} ago)</div>
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br>
|
<br><hr><br>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h3>New</h3>
|
<h3>
|
||||||
|
{{ if eq "" .editing.ID }}
|
||||||
|
New
|
||||||
|
{{ else }}
|
||||||
|
Update <code><a target="_blank" href="{{ .editing_url }}">{{ .editing.URL }}</a></code> (<a href="?">clear</a>)
|
||||||
|
{{ end }}
|
||||||
|
</h3>
|
||||||
<form method="POST" action="/v1/feeds">
|
<form method="POST" action="/v1/feeds">
|
||||||
{{ range feedsVersionFields }}
|
{{ range $k, $v := .editing }}
|
||||||
{{ if ne . "Created" }}
|
{{ if not (in $k "Created" "Deleted" "Updated" "ID") }}
|
||||||
<div>
|
<div>
|
||||||
<label for="{{ . }}">{{ . }}</label>
|
<label for="{{ $k }}">
|
||||||
<input name="{{ . }}" type="text" />
|
{{ $k }}
|
||||||
|
{{- if eq $k "URL" }}
|
||||||
|
(hint: nyaa://?q=show)
|
||||||
|
{{ else if eq $k "WebhookURL" }}
|
||||||
|
(hint: vpntor:///outdir)
|
||||||
|
{{ end }}
|
||||||
|
</label>
|
||||||
|
<input name="{{ $k }}" type="text" value="{{ $v }}"/>
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ package handler
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"show-rss/src/feeds"
|
"show-rss/src/feeds"
|
||||||
"slices"
|
"slices"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var dir = func() string {
|
var dir = func() string {
|
||||||
@@ -30,16 +30,27 @@ func (h Handler) ui(w http.ResponseWriter, r *http.Request) error {
|
|||||||
func (h Handler) uiIndex(w http.ResponseWriter, r *http.Request) error {
|
func (h Handler) uiIndex(w http.ResponseWriter, r *http.Request) error {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
|
|
||||||
|
var editing struct {
|
||||||
|
feeds.Entry `json:",inline"`
|
||||||
|
feeds.Version `json:",inline"`
|
||||||
|
}
|
||||||
|
all := []feeds.Feed{}
|
||||||
|
if err := feeds.ForEach(ctx, func(f feeds.Feed) error {
|
||||||
|
all = append(all, f)
|
||||||
|
if f.Entry.ID == r.URL.Query().Get("edit") {
|
||||||
|
editing.Entry = f.Entry
|
||||||
|
editing.Version = f.Version
|
||||||
|
}
|
||||||
|
return ctx.Err()
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
b, _ := os.ReadFile(path.Join(dir, "index.tmpl"))
|
b, _ := os.ReadFile(path.Join(dir, "index.tmpl"))
|
||||||
|
|
||||||
tmpl := template.New(r.URL.Path).Funcs(template.FuncMap{
|
tmpl := template.New(r.URL.Path).Funcs(template.FuncMap{
|
||||||
"feeds": func() ([]feeds.Feed, error) {
|
"feeds": func() []feeds.Feed {
|
||||||
all := []feeds.Feed{}
|
return all
|
||||||
err := feeds.ForEach(ctx, func(f feeds.Feed) error {
|
|
||||||
all = append(all, f)
|
|
||||||
return ctx.Err()
|
|
||||||
})
|
|
||||||
return all, err
|
|
||||||
},
|
},
|
||||||
"feedsVersionFields": func() []string {
|
"feedsVersionFields": func() []string {
|
||||||
b, _ := json.Marshal(feeds.Version{})
|
b, _ := json.Marshal(feeds.Version{})
|
||||||
@@ -52,8 +63,11 @@ func (h Handler) uiIndex(w http.ResponseWriter, r *http.Request) error {
|
|||||||
slices.Sort(ks)
|
slices.Sort(ks)
|
||||||
return ks
|
return ks
|
||||||
},
|
},
|
||||||
"namespan": func(k string, v any) string {
|
"in": func(k string, v ...string) bool {
|
||||||
return fmt.Sprintf("<span name=%q>%s</span>", k, v)
|
return slices.Contains(v, k)
|
||||||
|
},
|
||||||
|
"ago": func(t time.Time) time.Duration {
|
||||||
|
return time.Since(t)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -62,5 +76,16 @@ func (h Handler) uiIndex(w http.ResponseWriter, r *http.Request) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return tmpl.Execute(w, nil)
|
args := map[string]any{}
|
||||||
|
|
||||||
|
{
|
||||||
|
b, _ := json.Marshal(editing)
|
||||||
|
var m map[string]any
|
||||||
|
json.Unmarshal(b, &m)
|
||||||
|
args["editing"] = m
|
||||||
|
|
||||||
|
args["editing_url"], _ = editing.FetchURL()
|
||||||
|
}
|
||||||
|
|
||||||
|
return tmpl.Execute(w, args)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,18 +5,12 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"show-rss/src/cmd/server/handler"
|
"show-rss/src/cmd/server/handler"
|
||||||
"strconv"
|
"show-rss/src/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Main(ctx context.Context) error {
|
func Main(ctx context.Context) error {
|
||||||
port, _ := strconv.Atoi(os.Getenv("PORT"))
|
return Run(ctx, fmt.Sprintf(":%d", server.Port))
|
||||||
if port == 0 {
|
|
||||||
port = 10000
|
|
||||||
}
|
|
||||||
|
|
||||||
return Run(ctx, fmt.Sprintf(":%d", port))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Run(ctx context.Context, listen string) error {
|
func Run(ctx context.Context, listen string) error {
|
||||||
|
|||||||
@@ -2,8 +2,11 @@ package feeds
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"net/url"
|
||||||
"show-rss/src/db"
|
"show-rss/src/db"
|
||||||
|
"show-rss/src/server"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
@@ -230,3 +233,17 @@ func initDB(ctx context.Context) error {
|
|||||||
)`,
|
)`,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (feed Feed) Webhook() (string, string, string) {
|
||||||
|
u, _ := url.Parse(feed.Version.WebhookURL)
|
||||||
|
switch u.Scheme {
|
||||||
|
case "vpntor":
|
||||||
|
return "POST", fmt.Sprintf("http://localhost:%d/v1/vpntor", server.Port), fmt.Sprintf(`{
|
||||||
|
"Magnet": "{{ .Item.Link }}",
|
||||||
|
"Dir": %q,
|
||||||
|
"URL": "https://vpntor.int.bel.blue/transmission/rpc"
|
||||||
|
}`, u.Path)
|
||||||
|
default:
|
||||||
|
return feed.Version.WebhookMethod, feed.Version.WebhookURL, feed.Version.WebhookBody
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -132,14 +132,18 @@ func proxyFetch(ctx context.Context, u string) (string, error) {
|
|||||||
b, _ := io.ReadAll(resp.Body)
|
b, _ := io.ReadAll(resp.Body)
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return "", fmt.Errorf("failed fetch: (%d) %s", resp.StatusCode, b)
|
return "", fmt.Errorf("failed fetch %s %s: (%d) %s", req.Method, req.URL.String(), resp.StatusCode, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
return string(b), nil
|
return string(b), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (feed Feed) FetchURL() (*url.URL, error) {
|
func (feed Feed) FetchURL() (*url.URL, error) {
|
||||||
u, err := url.Parse(feed.Version.URL)
|
return feed.Version.FetchURL()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (version Version) FetchURL() (*url.URL, error) {
|
||||||
|
u, err := url.Parse(version.URL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -148,7 +152,7 @@ func (feed Feed) FetchURL() (*url.URL, error) {
|
|||||||
case "nyaa": // `nyaa://?q=A B` to `https://nyaa.si/?page=rss&q=A%20B&c=0_0&f=0`
|
case "nyaa": // `nyaa://?q=A B` to `https://nyaa.si/?page=rss&q=A%20B&c=0_0&f=0`
|
||||||
q := u.Query()
|
q := u.Query()
|
||||||
if q.Get("q") == "" {
|
if q.Get("q") == "" {
|
||||||
return nil, fmt.Errorf("invalid nyaa:// (%s): no ?q", feed.Version.URL)
|
return nil, fmt.Errorf("invalid nyaa:// (%s): no ?q", version.URL)
|
||||||
}
|
}
|
||||||
|
|
||||||
q.Set("page", "rss")
|
q.Set("page", "rss")
|
||||||
|
|||||||
14
src/server/config.go
Normal file
14
src/server/config.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Port = func() int {
|
||||||
|
port, _ := strconv.Atoi(os.Getenv("PORT"))
|
||||||
|
if port == 0 {
|
||||||
|
port = 10000
|
||||||
|
}
|
||||||
|
return port
|
||||||
|
}()
|
||||||
Reference in New Issue
Block a user