Compare commits
23 Commits
Author | SHA1 | Date | |
---|---|---|---|
5dc5590da9 | |||
81c046c1b0 | |||
364112a0a4 | |||
1e9fdd2ab9 | |||
a38523e933 | |||
1fbc0ddcf6 | |||
0dd2101a08 | |||
e95871ee70 | |||
083718711d | |||
20a12c6299 | |||
200672dae2 | |||
be829e662b | |||
3d3aad88c8 | |||
0036b42d82 | |||
e4e43d1a83 | |||
59deb69e2f | |||
3aef27585a | |||
3aa8796537 | |||
737a9ec314 | |||
4b75fc61cd | |||
1ebe0380ee | |||
c439e048c1 | |||
f86f2ba146 |
@ -8,8 +8,6 @@ import (
|
||||
"git.streifling.com/jason/atom"
|
||||
)
|
||||
|
||||
type Feed struct{ *atom.Feed }
|
||||
|
||||
func GenerateAtomFeed(c *Config, db *DB) (*string, error) {
|
||||
feed := atom.NewFeed(c.Title)
|
||||
feed.ID = atom.NewID("urn:feed:1")
|
||||
@ -22,41 +20,45 @@ func GenerateAtomFeed(c *Config, db *DB) (*string, error) {
|
||||
|
||||
articles, err := db.GetCertainArticles("published", true)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting published articles for RSS feed: %v", err)
|
||||
return nil, fmt.Errorf("error getting published articles for Atom feed: %v", err)
|
||||
}
|
||||
|
||||
for _, article := range articles {
|
||||
articleTitle, err := ConvertToPlain(article.Title)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error converting title to plain text for RSS feed: %v", err)
|
||||
return nil, fmt.Errorf("error converting title to plain text for Atom feed: %v", err)
|
||||
}
|
||||
entry := atom.NewEntry(articleTitle)
|
||||
entry.ID = atom.NewID(fmt.Sprint("urn:entry:", article.ID))
|
||||
entry.Content = atom.NewContent(atom.OutOfLine, "text/hmtl", article.ContentLink)
|
||||
entry.Published = atom.NewDate(article.Created)
|
||||
|
||||
linkID := entry.AddLink(atom.NewLink(article.BannerLink))
|
||||
entry.Links[linkID].Rel = "enclosure"
|
||||
entry.Links[linkID].Type = "image/webp"
|
||||
if article.AutoGenerated {
|
||||
entry.Content = atom.NewContent(atom.InlineText, "text", "")
|
||||
} else {
|
||||
entry.Content = atom.NewContent(atom.OutOfLine, "text/hmtl", article.ContentLink)
|
||||
|
||||
articleSummary, err := ConvertToPlain(article.Summary)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error converting description to plain text for Atom feed: %v", err)
|
||||
}
|
||||
entry.Summary = atom.NewText("text", articleSummary)
|
||||
}
|
||||
|
||||
if len(article.BannerLink) > 0 {
|
||||
linkID := entry.AddLink(atom.NewLink(article.BannerLink))
|
||||
entry.Links[linkID].Rel = "enclosure"
|
||||
entry.Links[linkID].Type = "image/webp"
|
||||
}
|
||||
|
||||
user, err := db.GetUser(c, article.AuthorID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting user user info for RSS feed: %v", err)
|
||||
return nil, fmt.Errorf("error getting user user info for Atom feed: %v", err)
|
||||
}
|
||||
entry.AddAuthor(atom.NewPerson(user.FirstName + " " + user.LastName))
|
||||
|
||||
articleSummary, err := ConvertToPlain(article.Summary)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error converting description to plain text for RSS feed: %v", err)
|
||||
}
|
||||
if article.AutoGenerated {
|
||||
articleSummary = "auto generated"
|
||||
}
|
||||
entry.Summary = atom.NewText("text", articleSummary)
|
||||
|
||||
tags, err := db.GetArticleTags(article.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting tags for articles for RSS feed: %v", err)
|
||||
return nil, fmt.Errorf("error getting tags for articles for Atom feed: %v", err)
|
||||
}
|
||||
for _, tag := range tags {
|
||||
entry.AddCategory(atom.NewCategory(tag.Name))
|
||||
@ -79,7 +81,7 @@ func GenerateAtomFeed(c *Config, db *DB) (*string, error) {
|
||||
|
||||
atom, err := feed.ToXML("UTF-8")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error converting RSS feed to XML: %v", err)
|
||||
return nil, fmt.Errorf("error converting Atom feed to XML: %v", err)
|
||||
}
|
||||
|
||||
return &atom, nil
|
||||
|
@ -18,7 +18,7 @@ type Config struct {
|
||||
DBName string
|
||||
Description string
|
||||
Domain string
|
||||
AtomFeed string
|
||||
AtomFile string
|
||||
FirebaseKey string
|
||||
GOBKeyFile string
|
||||
Link string
|
||||
@ -39,7 +39,7 @@ func newConfig() *Config {
|
||||
return &Config{
|
||||
AESKeyFile: "/var/www/cpolis/aes.key",
|
||||
ArticleDir: "/var/www/cpolis/articles",
|
||||
AtomFeed: "/var/www/cpolis/cpolis.atom",
|
||||
AtomFile: "/var/www/cpolis/cpolis.atom",
|
||||
ConfigFile: "/etc/cpolis/config.toml",
|
||||
DBName: "cpolis",
|
||||
FirebaseKey: "/var/www/cpolis/serviceAccountKey.json",
|
||||
@ -52,7 +52,7 @@ func newConfig() *Config {
|
||||
PDFDir: "/var/www/cpolis/pdfs",
|
||||
PicsDir: "/var/www/cpolis/pics",
|
||||
Port: ":8080",
|
||||
Version: "v0.13.0",
|
||||
Version: "v0.13.3",
|
||||
WebDir: "/var/www/cpolis/web",
|
||||
}
|
||||
}
|
||||
@ -110,7 +110,7 @@ func (c *Config) handleCliArgs() error {
|
||||
|
||||
flag.StringVar(&c.AESKeyFile, "aes", c.AESKeyFile, "aes key file")
|
||||
flag.StringVar(&c.ArticleDir, "articles", c.ArticleDir, "articles directory")
|
||||
flag.StringVar(&c.AtomFeed, "feed", c.AtomFeed, "atom feed file")
|
||||
flag.StringVar(&c.AtomFile, "feed", c.AtomFile, "atom feed file")
|
||||
flag.StringVar(&c.ConfigFile, "config", c.ConfigFile, "config file")
|
||||
flag.StringVar(&c.DBName, "db", c.DBName, "DB name")
|
||||
flag.StringVar(&c.Description, "desc", c.Description, "channel description")
|
||||
@ -187,10 +187,10 @@ func (c *Config) setupConfig(cliConfig *Config) error {
|
||||
c.Domain = "https://" + c.Domain
|
||||
}
|
||||
|
||||
if cliConfig.AtomFeed != defaultConfig.AtomFeed {
|
||||
c.AtomFeed = cliConfig.AtomFeed
|
||||
if cliConfig.AtomFile != defaultConfig.AtomFile {
|
||||
c.AtomFile = cliConfig.AtomFile
|
||||
}
|
||||
c.AtomFeed, err = mkFile(c.AtomFeed, 0644, 0744)
|
||||
c.AtomFile, err = mkFile(c.AtomFile, 0644, 0744)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error setting up file: %v", err)
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package backend
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"image"
|
||||
"io"
|
||||
@ -43,20 +42,3 @@ func SaveImage(src io.Reader, maxHeight, maxWidth int, path string) (string, err
|
||||
|
||||
return filename, nil
|
||||
}
|
||||
|
||||
func ServeBase64Image(c *Config, filename string) (string, error) {
|
||||
file := c.PicsDir + "/" + filename
|
||||
|
||||
img, err := os.Open(file)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error opening file %v: %v", file, err)
|
||||
}
|
||||
defer img.Close()
|
||||
|
||||
imgBytes, err := io.ReadAll(img)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error turning %v into bytes: %v", file, err)
|
||||
}
|
||||
|
||||
return base64.StdEncoding.EncodeToString(imgBytes), nil
|
||||
}
|
||||
|
@ -1,103 +0,0 @@
|
||||
package backend
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"git.streifling.com/jason/rss"
|
||||
)
|
||||
|
||||
func GenerateRSS(c *Config, db *DB) (*string, error) {
|
||||
channel := &rss.Channel{
|
||||
Title: c.Title,
|
||||
Link: c.Link,
|
||||
Description: c.Description,
|
||||
Items: make([]*rss.Item, 0),
|
||||
}
|
||||
|
||||
articles, err := db.GetCertainArticles("published", true)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting published articles for RSS feed: %v", err)
|
||||
}
|
||||
|
||||
for _, article := range articles {
|
||||
tags, err := db.GetArticleTags(article.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting tags for articles for RSS feed: %v", err)
|
||||
}
|
||||
tagNames := make([]string, 0)
|
||||
for _, tag := range tags {
|
||||
tagNames = append(tagNames, tag.Name)
|
||||
}
|
||||
|
||||
if article.IsInIssue || article.AutoGenerated {
|
||||
tagNames = append(tagNames, fmt.Sprint("Orient Express ", article.IssueID))
|
||||
}
|
||||
if article.AutoGenerated {
|
||||
tagNames = append(tagNames, "autogenerated")
|
||||
}
|
||||
|
||||
user, err := db.GetUser(c, article.AuthorID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting user user info for RSS feed: %v", err)
|
||||
}
|
||||
|
||||
articleTitle, err := ConvertToPlain(article.Title)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error converting title to plain text for RSS feed: %v", err)
|
||||
}
|
||||
|
||||
articleDescription, err := ConvertToPlain(article.Summary)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error converting description to plain text for RSS feed: %v", err)
|
||||
}
|
||||
|
||||
item := &rss.Item{
|
||||
Author: user.FirstName + " " + user.LastName,
|
||||
Categories: tagNames,
|
||||
Description: articleDescription,
|
||||
Guid: string(article.ID),
|
||||
Link: article.ContentLink,
|
||||
PubDate: article.Created.Format(time.RFC1123Z),
|
||||
Title: articleTitle,
|
||||
}
|
||||
|
||||
// if article.AutoGenerated {
|
||||
// item.Enclosure = &rss.Enclosure{
|
||||
// Url: article.EncURL,
|
||||
// Lenght: article.EncLength,
|
||||
// Type: article.EncType,
|
||||
// }
|
||||
// }
|
||||
//
|
||||
channel.Items = append(channel.Items, item)
|
||||
}
|
||||
|
||||
feed := rss.NewFeed()
|
||||
feed.Channels = append(feed.Channels, channel)
|
||||
rss, err := feed.ToXML("UTF-8")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error converting RSS feed to XML: %v", err)
|
||||
}
|
||||
|
||||
return &rss, nil
|
||||
}
|
||||
|
||||
func SaveRSS(filename string, feed *string) error {
|
||||
file, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating file for RSS feed: %v", err)
|
||||
}
|
||||
defer file.Close()
|
||||
if err = file.Chmod(0644); err != nil {
|
||||
return fmt.Errorf("error setting permissions for RSS file: %v", err)
|
||||
}
|
||||
|
||||
if _, err = io.WriteString(file, *feed); err != nil {
|
||||
return fmt.Errorf("error writing to RSS file: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -14,7 +14,7 @@ func ServeAtomFeed(c *b.Config) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
absFilepath, err := filepath.Abs(c.AtomFeed)
|
||||
absFilepath, err := filepath.Abs(c.AtomFile)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
|
@ -80,7 +80,7 @@ func SubmitArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
|
||||
article := &b.Article{
|
||||
Title: r.PostFormValue("article-title"),
|
||||
BannerLink: r.PostFormValue("article-banner-url"),
|
||||
BannerLink: c.Domain + "/image/serve/" + r.PostFormValue("article-banner-url"),
|
||||
Summary: r.PostFormValue("article-summary"),
|
||||
Published: false,
|
||||
Rejected: false,
|
||||
@ -93,10 +93,6 @@ func SubmitArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
http.Error(w, "Bitte den Titel eingeben.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if len(article.BannerLink) == 0 {
|
||||
http.Error(w, "Bitte ein Titelbild einfügen.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if len(article.Summary) == 0 {
|
||||
http.Error(w, "Bitte die Beschreibung eingeben.", http.StatusBadRequest)
|
||||
return
|
||||
@ -181,6 +177,11 @@ func ResubmitArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
bannerLink := r.PostFormValue("article-banner-url")
|
||||
if len(bannerLink) != 0 {
|
||||
bannerLink = c.Domain + "/image/serve/" + bannerLink
|
||||
}
|
||||
|
||||
summary := r.PostFormValue("article-summary")
|
||||
if len(summary) == 0 {
|
||||
http.Error(w, "Bitte die Beschreibung eingeben.", http.StatusBadRequest)
|
||||
@ -202,6 +203,7 @@ func ResubmitArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
|
||||
if err = db.UpdateAttributes(
|
||||
&b.Attribute{Table: "articles", ID: id, AttName: "title", Value: title},
|
||||
&b.Attribute{Table: "articles", ID: id, AttName: "banner_link", Value: bannerLink},
|
||||
&b.Attribute{Table: "articles", ID: id, AttName: "summary", Value: summary},
|
||||
&b.Attribute{Table: "articles", ID: id, AttName: "rejected", Value: false},
|
||||
&b.Attribute{Table: "articles", ID: id, AttName: "is_in_issue", Value: r.PostFormValue("issue") == "on"},
|
||||
@ -347,13 +349,7 @@ func ReviewRejectedArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.Handler
|
||||
}
|
||||
|
||||
imgURL := strings.Split(data.Article.BannerLink, "/")
|
||||
imgFileName := imgURL[len(imgURL)-1]
|
||||
data.BannerImage, err = b.ServeBase64Image(c, imgFileName)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
data.BannerImage = imgURL[len(imgURL)-1]
|
||||
|
||||
articleAbsName := fmt.Sprint(c.ArticleDir, "/", data.Article.ID, ".md")
|
||||
content, err := os.ReadFile(articleAbsName)
|
||||
@ -469,7 +465,7 @@ func PublishArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if err = b.SaveAtomFeed(c.AtomFeed, feed); err != nil {
|
||||
if err = b.SaveAtomFeed(c.AtomFile, feed); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@ -624,13 +620,7 @@ func ReviewArticle(c *b.Config, db *b.DB, s *b.CookieStore, action, title, butto
|
||||
}
|
||||
|
||||
imgURL := strings.Split(article.BannerLink, "/")
|
||||
imgFileName := imgURL[len(imgURL)-1]
|
||||
data.BannerImage, err = b.ServeBase64Image(c, imgFileName)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
data.BannerImage = imgURL[len(imgURL)-1]
|
||||
|
||||
data.Article.Summary, err = b.ConvertToPlain(article.Summary)
|
||||
if err != nil {
|
||||
@ -704,7 +694,7 @@ func DeleteArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if err = b.SaveAtomFeed(c.AtomFeed, feed); err != nil {
|
||||
if err = b.SaveAtomFeed(c.AtomFile, feed); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@ -807,13 +797,7 @@ func EditArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
}
|
||||
|
||||
imgURL := strings.Split(data.Article.BannerLink, "/")
|
||||
imgFileName := imgURL[len(imgURL)-1]
|
||||
data.BannerImage, err = b.ServeBase64Image(c, imgFileName)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
data.BannerImage = imgURL[len(imgURL)-1]
|
||||
|
||||
content, err := os.ReadFile(fmt.Sprint(c.ArticleDir, "/", data.Article.ID, ".md"))
|
||||
if err != nil {
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"html/template"
|
||||
"log"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
|
||||
b "streifling.com/jason/cpolis/cmd/backend"
|
||||
)
|
||||
@ -17,12 +18,6 @@ func UploadImage(c *b.Config, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
if err := r.ParseMultipartForm(10 << 20); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
file, _, err := r.FormFile("article-image")
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
@ -75,20 +70,8 @@ func UploadBanner(c *b.Config, s *b.CookieStore, fileKey, htmlFile, htmlTemplate
|
||||
return
|
||||
}
|
||||
|
||||
base64Img, err := b.ServeBase64Image(c, filename)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
data := new(struct {
|
||||
BannerImage string
|
||||
URL string
|
||||
})
|
||||
|
||||
data.BannerImage = base64Img
|
||||
data.URL = c.Domain + "/image/serve/" + filename
|
||||
data := new(struct{ BannerImage string })
|
||||
data.BannerImage = filename
|
||||
|
||||
tmpl, err := template.ParseFiles(c.WebDir + "/templates/" + htmlFile)
|
||||
if err = template.Must(tmpl, err).ExecuteTemplate(w, htmlTemplate, data); err != nil {
|
||||
@ -98,3 +81,22 @@ func UploadBanner(c *b.Config, s *b.CookieStore, fileKey, htmlFile, htmlTemplate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ServeImage(c *b.Config, s *b.CookieStore) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if _, err := getSession(w, r, c, s); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
absFilepath, err := filepath.Abs(c.PicsDir)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
http.ServeFile(w, r, absFilepath+"/"+r.PathValue("img"))
|
||||
}
|
||||
}
|
||||
|
@ -41,10 +41,6 @@ func PublishLatestIssue(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFun
|
||||
http.Error(w, "Bitte den Titel eingeben.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if len(article.BannerLink) == 0 {
|
||||
http.Error(w, "Bitte ein Titelbild einfügen.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
article.ID, err = db.AddArticle(article)
|
||||
if err != nil {
|
||||
@ -97,7 +93,7 @@ func PublishLatestIssue(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFun
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if err = b.SaveAtomFeed(c.AtomFeed, feed); err != nil {
|
||||
if err = b.SaveAtomFeed(c.AtomFile, feed); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
|
@ -18,12 +18,6 @@ func UploadPDF(c *b.Config, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
if err := r.ParseMultipartForm(10 << 20); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
file, _, err := r.FormFile("pdf-upload")
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
|
@ -68,6 +68,7 @@ func main() {
|
||||
mux.HandleFunc("GET /article/write", f.WriteArticle(config, db, store))
|
||||
mux.HandleFunc("GET /atom/serve", c.ServeAtomFeed(config))
|
||||
mux.HandleFunc("GET /hub", f.ShowHub(config, db, store))
|
||||
mux.HandleFunc("GET /image/{img}", f.ServeImage(config, store))
|
||||
mux.HandleFunc("GET /image/serve/{pic}", c.ServeImage(config))
|
||||
mux.HandleFunc("GET /issue/this", f.ShowCurrentIssue(config, db, store))
|
||||
mux.HandleFunc("GET /logout", f.Logout(config, store))
|
||||
|
23
go.mod
23
go.mod
@ -3,9 +3,8 @@ module streifling.com/jason/cpolis
|
||||
go 1.23.2
|
||||
|
||||
require (
|
||||
firebase.google.com/go/v4 v4.14.1
|
||||
firebase.google.com/go/v4 v4.15.0
|
||||
git.streifling.com/jason/atom v1.0.0
|
||||
git.streifling.com/jason/rss v0.1.3
|
||||
github.com/BurntSushi/toml v1.4.0
|
||||
github.com/chai2010/webp v1.1.1
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
@ -16,19 +15,19 @@ require (
|
||||
github.com/yuin/goldmark v1.7.8
|
||||
golang.org/x/crypto v0.28.0
|
||||
golang.org/x/term v0.25.0
|
||||
google.golang.org/api v0.201.0
|
||||
google.golang.org/api v0.203.0
|
||||
)
|
||||
|
||||
require (
|
||||
cel.dev/expr v0.16.2 // indirect
|
||||
cel.dev/expr v0.18.0 // indirect
|
||||
cloud.google.com/go v0.116.0 // indirect
|
||||
cloud.google.com/go/auth v0.9.8 // indirect
|
||||
cloud.google.com/go/auth v0.9.9 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.5.2 // indirect
|
||||
cloud.google.com/go/firestore v1.17.0 // indirect
|
||||
cloud.google.com/go/iam v1.2.1 // indirect
|
||||
cloud.google.com/go/longrunning v0.6.1 // indirect
|
||||
cloud.google.com/go/monitoring v1.21.1 // indirect
|
||||
cloud.google.com/go/iam v1.2.2 // indirect
|
||||
cloud.google.com/go/longrunning v0.6.2 // indirect
|
||||
cloud.google.com/go/monitoring v1.21.2 // indirect
|
||||
cloud.google.com/go/storage v1.45.0 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.3 // indirect
|
||||
@ -70,10 +69,10 @@ require (
|
||||
golang.org/x/text v0.19.0 // indirect
|
||||
golang.org/x/time v0.7.0 // indirect
|
||||
google.golang.org/appengine/v2 v2.0.6 // indirect
|
||||
google.golang.org/genproto v0.0.0-20241015192408-796eee8c2d53 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect
|
||||
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect
|
||||
google.golang.org/grpc v1.67.1 // indirect
|
||||
google.golang.org/grpc/stats/opentelemetry v0.0.0-20241018153737-98959d9a4904 // indirect
|
||||
google.golang.org/grpc/stats/opentelemetry v0.0.0-20241025232817-cb329375b14e // indirect
|
||||
google.golang.org/protobuf v1.35.1 // indirect
|
||||
)
|
||||
|
50
go.sum
50
go.sum
@ -1,36 +1,34 @@
|
||||
cel.dev/expr v0.16.2 h1:RwRhoH17VhAu9U5CMvMhH1PDVgf0tuz9FT+24AfMLfU=
|
||||
cel.dev/expr v0.16.2/go.mod h1:gXngZQMkWJoSbE8mOzehJlXQyubn/Vg0vR9/F3W7iw8=
|
||||
cel.dev/expr v0.18.0 h1:CJ6drgk+Hf96lkLikr4rFf19WrU0BOWEihyZnI2TAzo=
|
||||
cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE=
|
||||
cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U=
|
||||
cloud.google.com/go/auth v0.9.8 h1:+CSJ0Gw9iVeSENVCKJoLHhdUykDgXSc4Qn+gu2BRtR8=
|
||||
cloud.google.com/go/auth v0.9.8/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNOr38tI=
|
||||
cloud.google.com/go/auth v0.9.9 h1:BmtbpNQozo8ZwW2t7QJjnrQtdganSdmqeIBxHxNkEZQ=
|
||||
cloud.google.com/go/auth v0.9.9/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNOr38tI=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc=
|
||||
cloud.google.com/go/compute/metadata v0.5.2 h1:UxK4uu/Tn+I3p2dYWTfiX4wva7aYlKixAHn3fyqngqo=
|
||||
cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k=
|
||||
cloud.google.com/go/firestore v1.17.0 h1:iEd1LBbkDZTFsLw3sTH50eyg4qe8eoG6CjocmEXO9aQ=
|
||||
cloud.google.com/go/firestore v1.17.0/go.mod h1:69uPx1papBsY8ZETooc71fOhoKkD70Q1DwMrtKuOT/Y=
|
||||
cloud.google.com/go/iam v1.2.1 h1:QFct02HRb7H12J/3utj0qf5tobFh9V4vR6h9eX5EBRU=
|
||||
cloud.google.com/go/iam v1.2.1/go.mod h1:3VUIJDPpwT6p/amXRC5GY8fCCh70lxPygguVtI0Z4/g=
|
||||
cloud.google.com/go/logging v1.11.0 h1:v3ktVzXMV7CwHq1MBF65wcqLMA7i+z3YxbUsoK7mOKs=
|
||||
cloud.google.com/go/logging v1.11.0/go.mod h1:5LDiJC/RxTt+fHc1LAt20R9TKiUTReDg6RuuFOZ67+A=
|
||||
cloud.google.com/go/longrunning v0.6.1 h1:lOLTFxYpr8hcRtcwWir5ITh1PAKUD/sG2lKrTSYjyMc=
|
||||
cloud.google.com/go/longrunning v0.6.1/go.mod h1:nHISoOZpBcmlwbJmiVk5oDRz0qG/ZxPynEGs1iZ79s0=
|
||||
cloud.google.com/go/monitoring v1.21.1 h1:zWtbIoBMnU5LP9A/fz8LmWMGHpk4skdfeiaa66QdFGc=
|
||||
cloud.google.com/go/monitoring v1.21.1/go.mod h1:Rj++LKrlht9uBi8+Eb530dIrzG/cU/lB8mt+lbeFK1c=
|
||||
cloud.google.com/go/iam v1.2.2 h1:ozUSofHUGf/F4tCNy/mu9tHLTaxZFLOUiKzjcgWHGIA=
|
||||
cloud.google.com/go/iam v1.2.2/go.mod h1:0Ys8ccaZHdI1dEUilwzqng/6ps2YB6vRsjIe00/+6JY=
|
||||
cloud.google.com/go/logging v1.12.0 h1:ex1igYcGFd4S/RZWOCU51StlIEuey5bjqwH9ZYjHibk=
|
||||
cloud.google.com/go/logging v1.12.0/go.mod h1:wwYBt5HlYP1InnrtYI0wtwttpVU1rifnMT7RejksUAM=
|
||||
cloud.google.com/go/longrunning v0.6.2 h1:xjDfh1pQcWPEvnfjZmwjKQEcHnpz6lHjfy7Fo0MK+hc=
|
||||
cloud.google.com/go/longrunning v0.6.2/go.mod h1:k/vIs83RN4bE3YCswdXC5PFfWVILjm3hpEUlSko4PiI=
|
||||
cloud.google.com/go/monitoring v1.21.2 h1:FChwVtClH19E7pJ+e0xUhJPGksctZNVOk2UhMmblmdU=
|
||||
cloud.google.com/go/monitoring v1.21.2/go.mod h1:hS3pXvaG8KgWTSz+dAdyzPrGUYmi2Q+WFX8g2hqVEZU=
|
||||
cloud.google.com/go/storage v1.45.0 h1:5av0QcIVj77t+44mV4gffFC/LscFRUhto6UBMB5SimM=
|
||||
cloud.google.com/go/storage v1.45.0/go.mod h1:wpPblkIuMP5jCB/E48Pz9zIo2S/zD8g+ITmxKkPCITE=
|
||||
cloud.google.com/go/trace v1.11.1 h1:UNqdP+HYYtnm6lb91aNA5JQ0X14GnxkABGlfz2PzPew=
|
||||
cloud.google.com/go/trace v1.11.1/go.mod h1:IQKNQuBzH72EGaXEodKlNJrWykGZxet2zgjtS60OtjA=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
firebase.google.com/go/v4 v4.14.1 h1:4qiUETaFRWoFGE1XP5VbcEdtPX93Qs+8B/7KvP2825g=
|
||||
firebase.google.com/go/v4 v4.14.1/go.mod h1:fgk2XshgNDEKaioKco+AouiegSI9oTWVqRaBdTTGBoM=
|
||||
firebase.google.com/go/v4 v4.15.0 h1:k27M+cHbyN1YpBI2Cf4NSjeHnnYRB9ldXwpqA5KikN0=
|
||||
firebase.google.com/go/v4 v4.15.0/go.mod h1:S/4MJqVZn1robtXkHhpRUbwOC4gdYtgsiMMJQ4x+xmQ=
|
||||
git.streifling.com/jason/atom v1.0.0 h1:E88z4S7JeT6T+WuAaJWnGwCWTx+vzSJ6giUL51MdptI=
|
||||
git.streifling.com/jason/atom v1.0.0/go.mod h1:FNTYJfatYaIOQn4OKy8y+Mtohqm3MeyEGZUu4bMtZ9E=
|
||||
git.streifling.com/jason/rss v0.1.3 h1:fd3j4ZtcLehapcmmroo3AP3X34gRHC4xzpfV6bDV1ZU=
|
||||
git.streifling.com/jason/rss v0.1.3/go.mod h1:gpZF0nZbQSstMpyHD9DTAvlQEG7v4pjO5c7aIMWM4Jg=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
||||
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
@ -227,8 +225,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.201.0 h1:+7AD9JNM3tREtawRMu8sOjSbb8VYcYXJG/2eEOmfDu0=
|
||||
google.golang.org/api v0.201.0/go.mod h1:HVY0FCHVs89xIW9fzf/pBvOEm+OolHa86G/txFezyq4=
|
||||
google.golang.org/api v0.203.0 h1:SrEeuwU3S11Wlscsn+LA1kb/Y5xT8uggJSkIhD08NAU=
|
||||
google.golang.org/api v0.203.0/go.mod h1:BuOVyCSYEPwJb3npWvDnNmFI92f3GeRnHNkETneT3SI=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine/v2 v2.0.6 h1:LvPZLGuchSBslPBp+LAhihBeGSiRh1myRoYK4NtuBIw=
|
||||
@ -236,12 +234,12 @@ google.golang.org/appengine/v2 v2.0.6/go.mod h1:WoEXGoXNfa0mLvaH5sV3ZSGXwVmy8yf7
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20241015192408-796eee8c2d53 h1:Df6WuGvthPzc+JiQ/G+m+sNX24kc0aTBqoDN/0yyykE=
|
||||
google.golang.org/genproto v0.0.0-20241015192408-796eee8c2d53/go.mod h1:fheguH3Am2dGp1LfXkrvwqC/KlFq8F0nLq3LryOMrrE=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53 h1:fVoAXEKA4+yufmbdVYv+SE73+cPZbbbe8paLsHfkK+U=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53/go.mod h1:riSXTwQ4+nqmPGtobMFyW5FqVAmIs0St6VPp4Ug7CE4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 h1:X58yt85/IXCx0Y3ZwN6sEIKZzQtDEYaBWrDvErdXrRE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
|
||||
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 h1:Q3nlH8iSQSRUwOskjbcSMcF2jiYMNiQYZ0c2KEJLKKU=
|
||||
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38/go.mod h1:xBI+tzfqGGN2JBeSebfKXFSdBpWVQ7sLW40PTupVRm4=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38 h1:2oV8dfuIkM1Ti7DwXc0BJfnwr9csz4TDXI9EmiI+Rbw=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38/go.mod h1:vuAjtvlwkDKF6L1GQ0SokiRLCGFfeBUXWr/aFFkHACc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 h1:zciRKQ4kBpFgpfC5QQCVtnnNAcLIqweL7plyZRQHVpI=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
@ -249,8 +247,8 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
|
||||
google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
|
||||
google.golang.org/grpc/stats/opentelemetry v0.0.0-20241018153737-98959d9a4904 h1:Lplo3VKrYtWeryBkDI4SZ4kJTFaWO4qUGs+xX7N2bFc=
|
||||
google.golang.org/grpc/stats/opentelemetry v0.0.0-20241018153737-98959d9a4904/go.mod h1:jzYlkSMbKypzuu6xoAEijsNVo9ZeDF1u/zCfFgsx7jg=
|
||||
google.golang.org/grpc/stats/opentelemetry v0.0.0-20241025232817-cb329375b14e h1:SoMI+r+Qsp379U9BlVzrHtqAqYP3NEv9vNhYqUaAWOg=
|
||||
google.golang.org/grpc/stats/opentelemetry v0.0.0-20241025232817-cb329375b14e/go.mod h1:jzYlkSMbKypzuu6xoAEijsNVo9ZeDF1u/zCfFgsx7jg=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
|
@ -3,10 +3,7 @@
|
||||
|
||||
<form id="edit-area" hx-encoding="multipart/form-data">
|
||||
<div class="flex flex-col gap-y-1">
|
||||
<div class="w-full" id="article-banner-container">
|
||||
<img src="data:image/webp;base64,{{.BannerImage}}" alt="Banner Image">
|
||||
<input id="article-banner-url" name="article-banner-url" type="hidden" value="{{.Article.BannerLink}}" />
|
||||
</div>
|
||||
{{template "article-banner-template" .}}
|
||||
|
||||
<div class="grid grid-cols-2 gap-4 items-center">
|
||||
<div class="flex flex-col">
|
||||
@ -17,7 +14,7 @@
|
||||
<div class="grid grid-cols-1 items-center">
|
||||
<label class="btn text-center" for="article-banner">Titelbild</label>
|
||||
<input class="hidden" id="article-banner" name="article-banner" type="file" required
|
||||
hx-post="/article/upload-banner" hx-target="#article-banner-container" />
|
||||
hx-post="/article/upload-banner" hx-swap="outerHTML" hx-target="#article-banner-container" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -92,8 +89,8 @@
|
||||
{{end}}
|
||||
|
||||
{{define "article-banner-template"}}
|
||||
<div class="w-full" id="article-banner-container">
|
||||
<img src="data:image/webp;base64,{{.BannerImage}}" alt="Banner Image">
|
||||
<input id="article-banner-url" name="article-banner-url" type="hidden" value="{{.URL}}" />
|
||||
<div id="article-banner-container">
|
||||
<img src="/image/{{.BannerImage}}" alt="">
|
||||
<input id="article-banner-url" name="article-banner-url" type="hidden" value="{{.BannerImage}}" />
|
||||
</div>
|
||||
{{end}}
|
||||
|
@ -42,7 +42,7 @@
|
||||
<p>{{.Version}} - <strong>Alpha: Drastische Änderungen und Fehler vorbehalten.</strong></p>
|
||||
</footer>
|
||||
|
||||
<script src="https://unpkg.com/htmx.org@2.0.2"></script>
|
||||
<script src="https://unpkg.com/htmx.org@2.0.3"></script>
|
||||
<script src="https://unpkg.com/easymde/dist/easymde.min.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
<div>
|
||||
<div class="w-full" id="article-banner-container">
|
||||
<img src="data:image/webp;base64,{{.BannerImage}}" alt="Banner Image">
|
||||
<img src="/image/{{.BannerImage}}" alt="Banner Image">
|
||||
</div>
|
||||
|
||||
<span>Titel</span>
|
||||
|
Reference in New Issue
Block a user