create web interface

This commit is contained in:
yequari 2024-01-22 13:43:46 -07:00
parent e25999832c
commit 794e1ef668
11 changed files with 202 additions and 7 deletions

View File

@ -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)
} }

View File

@ -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"))
}
} }

56
cmd/web/helpers.go Normal file
View File

@ -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)
}

View File

@ -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)
} }

43
cmd/web/templates.go Normal file
View File

@ -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
}

View File

@ -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

16
ui/html/base.tmpl.html Normal file
View File

@ -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}}

View File

@ -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}}

View File

3
ui/static/css/main.css Normal file
View File

@ -0,0 +1,3 @@
#output {
white-space: pre;
}

1
ui/static/js/htmx.min.js vendored Normal file

File diff suppressed because one or more lines are too long