create web interface
This commit is contained in:
parent
e25999832c
commit
794e1ef668
|
@ -3,6 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
"git.32bit.cafe/yequari/rss-gen/rss"
|
"git.32bit.cafe/yequari/rss-gen/rss"
|
||||||
)
|
)
|
||||||
|
@ -16,7 +17,10 @@ func main() {
|
||||||
if *siteUrl == "" || *siteTitle == "" || *siteDesc == "" {
|
if *siteUrl == "" || *siteTitle == "" || *siteDesc == "" {
|
||||||
flag.PrintDefaults()
|
flag.PrintDefaults()
|
||||||
}
|
}
|
||||||
|
feed, err := rss.GenerateRss(*siteUrl, *siteTitle, *siteDesc, flag.Args()...)
|
||||||
fmt.Println(rss.GenerateRss(*siteUrl, *siteTitle, *siteDesc, flag.Args()...))
|
if err != nil {
|
||||||
|
os.Stderr.WriteString(err.Error())
|
||||||
|
}
|
||||||
|
fmt.Println(feed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,38 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "net/http"
|
import (
|
||||||
|
"html"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.32bit.cafe/yequari/rss-gen/rss"
|
||||||
|
)
|
||||||
|
|
||||||
func (app *application) home(w http.ResponseWriter, r *http.Request) {
|
func (app *application) home(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.URL.Path != "/" {
|
||||||
|
http.NotFound(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
app.render(w, http.StatusOK, "home.tmpl.html", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *application) generateRss(w http.ResponseWriter, r *http.Request) {
|
func (app *application) generateRss(w http.ResponseWriter, r *http.Request) {
|
||||||
|
q := r.URL.Query()
|
||||||
|
siteName := q.Get("site-name")
|
||||||
|
siteUrl := q.Get("site-url")
|
||||||
|
siteDesc := q.Get("site-description")
|
||||||
|
pageUrls := q.Get("page-urls")
|
||||||
|
pages := strings.Split(pageUrls, "\n")
|
||||||
|
for i := range pages {
|
||||||
|
pages[i] = strings.TrimSpace(pages[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
feed, err := rss.GenerateRss(siteUrl, siteName, siteDesc, pages...)
|
||||||
|
if err != nil {
|
||||||
|
app.infoLog.Printf("Error generating feed: %s\n", err.Error())
|
||||||
|
}
|
||||||
|
for _, line := range strings.Split(feed, "\n") {
|
||||||
|
w.Write([]byte(html.EscapeString(line) + "\n"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"path"
|
||||||
|
"runtime/debug"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (app *application) serverError(w http.ResponseWriter, err error) {
|
||||||
|
trace := fmt.Sprintf("%s\n%s", err.Error(), debug.Stack())
|
||||||
|
app.errorLog.Print(trace)
|
||||||
|
|
||||||
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *application) clientError(w http.ResponseWriter, status int) {
|
||||||
|
http.Error(w, http.StatusText(status), status)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *application) notFound(w http.ResponseWriter) {
|
||||||
|
app.clientError(w, http.StatusNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *application) cleanUrl(url string) string {
|
||||||
|
s := strings.TrimPrefix(url, "http://")
|
||||||
|
s = strings.TrimPrefix(s, "https://")
|
||||||
|
s = path.Base(path.Clean(s))
|
||||||
|
s = "http://" + s
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *application) render(w http.ResponseWriter, status int, page string, data *templateData) {
|
||||||
|
ts, ok := app.templateCache[page]
|
||||||
|
if !ok {
|
||||||
|
err := fmt.Errorf("the template %s does not exist", page)
|
||||||
|
app.serverError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// a buffer to attempt to write the template to
|
||||||
|
// before writing it to the ResponseWriter w
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
|
||||||
|
err := ts.ExecuteTemplate(buf, "base", data)
|
||||||
|
if err != nil {
|
||||||
|
app.serverError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(status)
|
||||||
|
|
||||||
|
buf.WriteTo(w)
|
||||||
|
}
|
|
@ -5,11 +5,13 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"text/template"
|
||||||
)
|
)
|
||||||
|
|
||||||
type application struct {
|
type application struct {
|
||||||
errorLog *log.Logger
|
errorLog *log.Logger
|
||||||
infoLog *log.Logger
|
infoLog *log.Logger
|
||||||
|
templateCache map[string]*template.Template
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -20,9 +22,15 @@ func main() {
|
||||||
infoLog := log.New(os.Stdout, "INFO\t", log.Ldate|log.Ltime)
|
infoLog := log.New(os.Stdout, "INFO\t", log.Ldate|log.Ltime)
|
||||||
errorLog := log.New(os.Stderr, "ERROR\t", log.Ldate|log.Ltime|log.Lshortfile)
|
errorLog := log.New(os.Stderr, "ERROR\t", log.Ldate|log.Ltime|log.Lshortfile)
|
||||||
|
|
||||||
|
templateCache, err := newTemplateCache()
|
||||||
|
if err != nil {
|
||||||
|
errorLog.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
app := &application {
|
app := &application {
|
||||||
errorLog: errorLog,
|
errorLog: errorLog,
|
||||||
infoLog: infoLog,
|
infoLog: infoLog,
|
||||||
|
templateCache: templateCache,
|
||||||
}
|
}
|
||||||
|
|
||||||
srv := &http.Server {
|
srv := &http.Server {
|
||||||
|
@ -32,6 +40,6 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
infoLog.Printf("Starting server on %s", *addr)
|
infoLog.Printf("Starting server on %s", *addr)
|
||||||
err := srv.ListenAndServe()
|
err = srv.ListenAndServe()
|
||||||
errorLog.Fatal(err)
|
errorLog.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"text/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
type templateData struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTemplateCache() (map[string]*template.Template, error) {
|
||||||
|
cache := map[string]*template.Template{}
|
||||||
|
|
||||||
|
pages, err := filepath.Glob("./ui/html/pages/*.tmpl.html")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, page := range pages {
|
||||||
|
name := filepath.Base(page)
|
||||||
|
|
||||||
|
ts, err := template.ParseFiles("./ui/html/base.tmpl.html")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse all partials into the template set
|
||||||
|
ts, err = ts.ParseGlob("./ui/html/partials/*.tmpl.html")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse the page template last
|
||||||
|
ts, err = ts.ParseFiles(page)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cache[name] = ts
|
||||||
|
}
|
||||||
|
|
||||||
|
return cache, nil
|
||||||
|
}
|
|
@ -17,8 +17,7 @@ const feedfmt = `<?xml version="1.0" encoding="utf-8"?>
|
||||||
<description>%s</description>
|
<description>%s</description>
|
||||||
%s
|
%s
|
||||||
</channel>
|
</channel>
|
||||||
</rss>
|
</rss>`
|
||||||
`
|
|
||||||
|
|
||||||
const itemfmt = `<item>
|
const itemfmt = `<item>
|
||||||
<title>Content Title</title>
|
<title>Content Title</title>
|
||||||
|
@ -26,7 +25,7 @@ const itemfmt = `<item>
|
||||||
<guid>%s</guid>
|
<guid>%s</guid>
|
||||||
<pubDate>%s</pubDate>
|
<pubDate>%s</pubDate>
|
||||||
<description><![CDATA[%s]]></description>
|
<description><![CDATA[%s]]></description>
|
||||||
</item>`;
|
</item>`
|
||||||
|
|
||||||
func fetchPage(url string) (string, error) {
|
func fetchPage(url string) (string, error) {
|
||||||
resp, err := http.Get(url)
|
resp, err := http.Get(url)
|
||||||
|
@ -102,6 +101,7 @@ func GenerateRss(siteUrl, siteTitle, siteDesc string, pageUrls ...string) (strin
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
for _, u := range pageUrls {
|
for _, u := range pageUrls {
|
||||||
|
var err error
|
||||||
page, err := fetchPage(u)
|
page, err := fetchPage(u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
{{define "base"}}
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<link rel="stylesheet" href="/static/css/main.css">
|
||||||
|
<title>RSS Generator</title>
|
||||||
|
<script src="/static/js/htmx.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
{{template "main" .}}
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
{{end}}
|
|
@ -0,0 +1,35 @@
|
||||||
|
{{define "main"}}
|
||||||
|
<h1>yequari's RSS Feed Generator</h1>
|
||||||
|
<!--<form action="/generate" method="get" id="generate-form">-->
|
||||||
|
<form id="generate-form">
|
||||||
|
<p>
|
||||||
|
<label>
|
||||||
|
Site Name:
|
||||||
|
<input name="site-name" />
|
||||||
|
</label>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<label>
|
||||||
|
Site URL:
|
||||||
|
<input name="site-url" />
|
||||||
|
</label>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<label>
|
||||||
|
Site Description:
|
||||||
|
<input name="site-description" />
|
||||||
|
</label>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<label>
|
||||||
|
Pages to Include:
|
||||||
|
<textarea name="page-urls" cols="40" rows="6">List of URLs, one per line</textarea>
|
||||||
|
</label>
|
||||||
|
</p>
|
||||||
|
<button id="generate-button" hx-get="/generate" hx-include="#generate-form" hx-params="*" hx-target="#output">Generate</button>
|
||||||
|
</form>
|
||||||
|
<div class="output-container">
|
||||||
|
<code id="output">
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
|
@ -0,0 +1,3 @@
|
||||||
|
#output {
|
||||||
|
white-space: pre;
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue