diff --git a/cmd/backend/articles.go b/cmd/backend/articles.go index 36a14d7..fc47ec7 100644 --- a/cmd/backend/articles.go +++ b/cmd/backend/articles.go @@ -12,8 +12,10 @@ type Article struct { Title string Created time.Time Description string - Content string Link string + EncURL string + EncLength int + EncType string Published bool Rejected bool ID int64 @@ -27,8 +29,8 @@ func (db *DB) AddArticle(a *Article) (int64, error) { selectQuery := "SELECT id FROM issues WHERE published = false" insertQuery := ` INSERT INTO articles - (title, description, content, link, published, rejected, author_id, issue_id) - VALUES (?, ?, ?, ?, ?, ?, ?, ?) + (title, description, link, enc_url, enc_length, enc_type, published, rejected, author_id, issue_id) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ` for i := 0; i < TxMaxRetries; i++ { @@ -45,8 +47,8 @@ func (db *DB) AddArticle(a *Article) (int64, error) { return 0, fmt.Errorf("error getting issue ID when adding article to DB: %v", err) } - result, err := tx.Exec(insertQuery, a.Title, a.Description, - a.Content, a.Link, a.Published, a.Rejected, a.AuthorID, id) + result, err := tx.Exec(insertQuery, a.Title, a.Description, a.Link, + a.EncURL, a.EncLength, a.EncType, a.Published, a.Rejected, a.AuthorID, id) if err != nil { if rollbackErr := tx.Rollback(); rollbackErr != nil { log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr) @@ -80,7 +82,7 @@ func (db *DB) AddArticle(a *Article) (int64, error) { func (db *DB) GetArticle(id int64) (*Article, error) { query := ` - SELECT title, created, description, content, link, published, author_id + SELECT title, created, description, link, enc_url, enc_length, enc_type, published, author_id FROM articles WHERE id = ? ` @@ -91,7 +93,8 @@ func (db *DB) GetArticle(id int64) (*Article, error) { var err error if err := row.Scan(&article.Title, &created, &article.Description, - &article.Content, &article.Link, &article.Published, &article.AuthorID); err != nil { + &article.Link, &article.EncURL, &article.EncLength, &article.EncType, + &article.Published, &article.AuthorID); err != nil { return nil, fmt.Errorf("error scanning article row: %v", err) } @@ -106,7 +109,7 @@ func (db *DB) GetArticle(id int64) (*Article, error) { func (db *DB) GetCertainArticles(published, rejected bool) ([]*Article, error) { query := ` - SELECT id, title, created, description, content, link, author_id, issue_id + SELECT id, title, created, description, link, enc_url, enc_length, enc_type, author_id, issue_id FROM articles WHERE published = ? AND rejected = ? @@ -122,8 +125,8 @@ func (db *DB) GetCertainArticles(published, rejected bool) ([]*Article, error) { var created []byte if err = rows.Scan(&article.ID, &article.Title, &created, - &article.Description, &article.Content, &article.Link, - &article.AuthorID, &article.IssueID); err != nil { + &article.Description, &article.Link, &article.EncURL, &article.EncLength, + &article.EncType, &article.AuthorID, &article.IssueID); err != nil { return nil, fmt.Errorf("error scanning article row: %v", err) } @@ -144,7 +147,7 @@ func (db *DB) GetCurrentIssueArticles() ([]*Article, error) { txOptions := &sql.TxOptions{Isolation: sql.LevelSerializable} issueQuery := "SELECT id FROM issues WHERE published = false" articlesQuery := ` - SELECT id, title, created, description, content, link, author_id + SELECT id, title, created, description, link, enc_url, enc_length, enc_type, author_id FROM articles WHERE issue_id = ? AND published = true ` @@ -178,7 +181,8 @@ func (db *DB) GetCurrentIssueArticles() ([]*Article, error) { var created []byte if err = rows.Scan(&article.ID, &article.Title, &created, - &article.Description, &article.Content, &article.Link, &article.AuthorID); err != nil { + &article.Description, &article.Link, &article.EncURL, &article.EncLength, + &article.EncType, &article.AuthorID); err != nil { if rollbackErr := tx.Rollback(); rollbackErr != nil { log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr) } diff --git a/cmd/backend/rss.go b/cmd/backend/rss.go index 7981fb2..35927d2 100644 --- a/cmd/backend/rss.go +++ b/cmd/backend/rss.go @@ -9,47 +9,6 @@ import ( "git.streifling.com/jason/rss" ) -func GetChannel(db *DB, title, link, description string) (*rss.Channel, error) { - channel := &rss.Channel{ - Title: title, - Link: link, - Description: description, - Items: make([]*rss.Item, 0), - } - - articles, err := db.GetCertainArticles(true, false) - if err != nil { - return nil, fmt.Errorf("error fetching published articles: %v", err) - } - - for _, article := range articles { - tags, err := db.GetArticleTags(article.ID) - if err != nil { - return nil, fmt.Errorf("error fetching tags for article %v: %v", article.Title, err) - } - tagNames := make([]string, 0) - for _, tag := range tags { - tagNames = append(tagNames, tag.Name) - } - - user, err := db.GetUser(article.AuthorID) - if err != nil { - return nil, fmt.Errorf("error finding user %v: %v", article.AuthorID, err) - } - - channel.Items = append(channel.Items, &rss.Item{ - Title: article.Title, - Author: fmt.Sprint(user.FirstName, " ", user.LastName), - PubDate: article.Created.Format(time.RFC1123Z), - Description: article.Description, - Content: &rss.Content{Value: article.Content}, - Categories: tagNames, - }) - } - - return channel, nil -} - func GenerateRSS(c *Config, db *DB) (*string, error) { channel := &rss.Channel{ Title: c.Title, @@ -93,15 +52,19 @@ func GenerateRSS(c *Config, db *DB) (*string, error) { Author: fmt.Sprint(user.FirstName, " ", user.LastName), Categories: tagNames, Description: articleDescription, + Guid: string(article.ID), + Link: article.Link, PubDate: article.Created.Format(time.RFC1123Z), Title: articleTitle, } fmt.Println(article.Link, ": ", len(article.Link)) - if article.Link == "" { - item.Link = fmt.Sprint("http://", c.Domain, "/article/serve/", article.ID, ".html") - } else { - item.Link = article.Link + if article.Title == "Autogenerated cpolis Issue Article" { + item.Enclosure = &rss.Enclosure{ + Url: article.EncURL, + Lenght: article.EncLength, + Type: article.EncType, + } } channel.Items = append(channel.Items, item) diff --git a/cmd/calls/articles.go b/cmd/calls/articles.go index 363dcbc..0c327c0 100644 --- a/cmd/calls/articles.go +++ b/cmd/calls/articles.go @@ -4,6 +4,7 @@ import ( "fmt" "log" "net/http" + "os" "strconv" b "streifling.com/jason/cpolis/cmd/backend" @@ -30,6 +31,24 @@ func ServeArticle(c *b.Config, db *b.DB) http.HandlerFunc { return } - fmt.Fprint(w, article.Content) + if !article.Published { + return + } + + contentBytes, err := os.ReadFile(article.Link) + if err != nil { + log.Println(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + content, err := b.ConvertToHTML(string(contentBytes)) + if err != nil { + log.Println(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + fmt.Fprint(w, content) } } diff --git a/cmd/frontend/articles.go b/cmd/frontend/articles.go index fa0c61e..7b34023 100644 --- a/cmd/frontend/articles.go +++ b/cmd/frontend/articles.go @@ -75,7 +75,6 @@ func SubmitArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc { article := &b.Article{ Title: r.PostFormValue("article-title"), Description: r.PostFormValue("article-description"), - Content: r.PostFormValue("article-content"), Published: false, Rejected: false, AuthorID: session.Values["id"].(int64), @@ -88,6 +87,20 @@ func SubmitArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc { return } + articleAbsName := fmt.Sprint(c.ArticleDir, "/", article.ID, ".md") + if err = os.WriteFile(articleAbsName, []byte(r.PostFormValue("article-content")), 0644); err != nil { + log.Println(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + article.Link = fmt.Sprint("http://", c.Domain, c.Port, "/article/serve/", article.ID) + if err = db.UpdateAttributes(&b.Attribute{Table: "articles", ID: article.ID, AttName: "link", Value: article.Link}); err != nil { + log.Println(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + r.ParseForm() tags := make([]int64, 0) for _, tag := range r.Form["tags"] { @@ -129,10 +142,16 @@ func ResubmitArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc { description := r.PostFormValue("article-description") content := r.PostFormValue("article-content") + link := fmt.Sprint(c.ArticleDir, "/", id, ".md") + if err = os.WriteFile(link, []byte(content), 0644); err != nil { + log.Println(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if err = db.UpdateAttributes( &b.Attribute{Table: "articles", ID: id, AttName: "title", Value: title}, &b.Attribute{Table: "articles", ID: id, AttName: "description", Value: description}, - &b.Attribute{Table: "articles", ID: id, AttName: "content", Value: content}, &b.Attribute{Table: "articles", ID: id, AttName: "rejected", Value: false}, ); err != nil { log.Println(err) @@ -260,7 +279,15 @@ func ReviewUnpublishedArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.Hand return } - content, err := b.ConvertToHTML(article.Content) + articleAbsName := fmt.Sprint(c.ArticleDir, "/", article.ID, ".md") + contentBytes, err := os.ReadFile(articleAbsName) + if err != nil { + log.Println(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + content, err := b.ConvertToHTML(string(contentBytes)) if err != nil { log.Println(err) http.Error(w, err.Error(), http.StatusInternalServerError) @@ -290,6 +317,7 @@ func ReviewRejectedArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.Handler type htmlData struct { Selected map[int64]bool Article *b.Article + Content string Tags []*b.Tag } data := new(htmlData) @@ -308,6 +336,16 @@ func ReviewRejectedArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.Handler return } + articleAbsName := fmt.Sprint(c.ArticleDir, "/", data.Article.ID, ".md") + contentBytes, err := os.ReadFile(articleAbsName) + if err != nil { + log.Println(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + data.Content = string(contentBytes) + data.Tags, err = db.GetTagList() if err != nil { log.Println(err) @@ -346,27 +384,6 @@ func PublishArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc { return } - article, err := db.GetArticle(id) - if err != nil { - log.Println(err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - content, err := b.ConvertToHTML(article.Content) - if err != nil { - log.Println(err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - err = os.WriteFile(fmt.Sprint(c.ArticleDir, "/", article.ID, ".html"), []byte(content), 0444) - if err != nil { - log.Println(err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if err = db.AddArticleToCurrentIssue(id); err != nil { log.Println(err) http.Error(w, err.Error(), http.StatusInternalServerError) @@ -555,7 +572,14 @@ func ReviewArticleForDeletion(c *b.Config, db *b.DB, s *b.CookieStore) http.Hand return } - content, err := b.ConvertToHTML(article.Content) + contentBytes, err := os.ReadFile(article.Link) + if err != nil { + log.Println(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + content, err := b.ConvertToHTML(string(contentBytes)) if err != nil { log.Println(err) http.Error(w, err.Error(), http.StatusInternalServerError) diff --git a/cmd/frontend/issues.go b/cmd/frontend/issues.go index 187352e..b1c7258 100644 --- a/cmd/frontend/issues.go +++ b/cmd/frontend/issues.go @@ -5,6 +5,7 @@ import ( "html/template" "io" "log" + "mime" "net/http" "os" "path/filepath" @@ -36,10 +37,32 @@ func PublishLatestIssue(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFun return } + imgFileName := session.Values["issue-image"].(string) + imgAbsName := fmt.Sprint(c.PicsDir, "/", imgFileName) + + imgFile, err := os.Open(imgAbsName) + if err != nil { + log.Println(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + defer imgFile.Close() + + imgInfo, err := imgFile.Stat() + if err != nil { + log.Println(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + imgSize := imgInfo.Size() + mimeType := mime.TypeByExtension(filepath.Ext(imgAbsName)) + article := &b.Article{ - Title: "Autogenerated Issue Article", - Content: r.PostFormValue("issue-content"), - Link: session.Values["issue-image"].(string), + Title: "Autogenerated cpolis Issue Article", + EncURL: fmt.Sprint("http://", c.Domain, c.Port, "/image/serve/", imgFileName), + EncLength: int(imgSize), + EncType: mimeType, Published: true, Rejected: false, Created: time.Now(), @@ -47,20 +70,6 @@ func PublishLatestIssue(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFun } fmt.Println(article.Link) - content, err := b.ConvertToHTML(article.Content) - if err != nil { - log.Println(err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - err = os.WriteFile(fmt.Sprint(c.ArticleDir, "/", article.ID, ".html"), []byte(content), 0444) - if err != nil { - log.Println(err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - article.ID, err = db.AddArticle(article) if err != nil { log.Println(err) @@ -68,6 +77,26 @@ func PublishLatestIssue(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFun return } + articleAbsName := fmt.Sprint(c.ArticleDir, "/", article.ID, ".md") + if err = os.WriteFile(articleAbsName, []byte(r.PostFormValue("article-content")), 0644); err != nil { + log.Println(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + article.Link = fmt.Sprint("http://", c.Domain, c.Port, "/article/serve/", article.ID) + if err = db.UpdateAttributes(&b.Attribute{Table: "articles", ID: article.ID, AttName: "link", Value: article.Link}); err != nil { + log.Println(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + if err = db.UpdateAttributes(&b.Attribute{Table: "articles", ID: article.ID, AttName: "link", Value: article.Link}); err != nil { + log.Println(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if err = db.AddArticleToCurrentIssue(article.ID); err != nil { log.Println(err) http.Error(w, err.Error(), http.StatusInternalServerError) diff --git a/create_db.sql b/create_db.sql index 436475b..23eb491 100644 --- a/create_db.sql +++ b/create_db.sql @@ -26,6 +26,9 @@ CREATE TABLE articles ( created TIMESTAMP DEFAULT CURRENT_TIMESTAMP, description TEXT NOT NULL, link VARCHAR(255), + enc_url VARCHAR(255), + enc_length INT, + enc_type VARCHAR(255), published BOOL NOT NULL, rejected BOOL NOT NULL, author_id INT NOT NULL, diff --git a/go.mod b/go.mod index f50229c..c4f0419 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.22.0 require ( firebase.google.com/go/v4 v4.14.1 - git.streifling.com/jason/rss v0.1.2 + git.streifling.com/jason/rss v0.1.3 github.com/BurntSushi/toml v1.3.2 github.com/go-sql-driver/mysql v1.7.1 github.com/google/uuid v1.6.0 diff --git a/go.sum b/go.sum index d2d9396..2ded0a3 100644 --- a/go.sum +++ b/go.sum @@ -15,8 +15,8 @@ cloud.google.com/go/storage v1.40.0 h1:VEpDQV5CJxFmJ6ueWNsKxcr1QAYOXEgxDa+sBbJah cloud.google.com/go/storage v1.40.0/go.mod h1:Rrj7/hKlG87BLqDJYtwR0fbPld8uJPbQ2ucUMY7Ir0g= 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= -git.streifling.com/jason/rss v0.1.2 h1:UB3UHJXMt5WDDh9y8n0Z6nS1XortbPXjEr7QZTdovY4= -git.streifling.com/jason/rss v0.1.2/go.mod h1:gpZF0nZbQSstMpyHD9DTAvlQEG7v4pjO5c7aIMWM4Jg= +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.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= diff --git a/web/templates/rework-article.html b/web/templates/rework-article.html index 72dfa3a..5029144 100644 --- a/web/templates/rework-article.html +++ b/web/templates/rework-article.html @@ -14,7 +14,7 @@