Converted articles and tags to DB base

This commit is contained in:
Jason Streifling 2024-03-06 20:53:17 +01:00
parent 052d36b01b
commit 582f25bec7
6 changed files with 154 additions and 245 deletions

View File

@ -1,206 +1,16 @@
package data package data
import ( import (
"encoding/gob"
"fmt"
"os"
"sync"
"time" "time"
"github.com/google/uuid"
) )
type Article struct { type Article struct {
Title string Title string
Author string Created time.Time
Created time.Time Desc string
Desc string Content string
Content string Tags []string
Tags []string Published bool
UUID uuid.UUID ID int64
AuthorID int64 AuthorID int64
}
// TODO: setCh
type ArticleList struct {
addCh chan *Article
delCh chan uuid.UUID
getCh chan []Article
retCh chan *Article
articles []*Article
wg sync.WaitGroup
}
// TODO: setCh
type TagList struct {
addCh chan string
getCh chan []string
tags []string
wg sync.WaitGroup
}
func initArticleList() *ArticleList {
return &ArticleList{
addCh: make(chan *Article),
delCh: make(chan uuid.UUID),
getCh: make(chan []Article),
retCh: make(chan *Article),
}
}
func initTagList() *TagList {
return &TagList{
addCh: make(chan string),
getCh: make(chan []string),
}
}
func (al *ArticleList) start() {
al.wg.Done()
for {
select {
case article := <-al.addCh:
al.articles = append(al.articles, article)
case uuid := <-al.delCh:
for i, article := range al.articles {
if article.UUID == uuid {
al.articles = append(al.articles[:i], al.articles[i+1:]...)
al.retCh <- article
}
}
case al.getCh <- func() []Article {
var list []Article
for _, article := range al.articles {
list = append(list, *article)
}
return list
}():
}
}
}
func (tl *TagList) start() {
tl.wg.Done()
for {
select {
case tag := <-tl.addCh:
tl.tags = append(tl.tags, tag)
case tl.getCh <- tl.tags:
}
}
}
func NewArticleList() *ArticleList {
list := initArticleList()
list.articles = make([]*Article, 0)
list.wg.Add(1)
go list.start()
list.wg.Wait()
return list
}
func (al *ArticleList) Add(a *Article) {
al.addCh <- a
}
func (al *ArticleList) Release(uuid uuid.UUID) (*Article, bool) {
al.delCh <- uuid
article := <-al.retCh
if article == nil {
return nil, false
}
return article, true
}
func (al *ArticleList) Get() []Article {
return <-al.getCh
}
func (al *ArticleList) Save(filename string) error {
file, err := os.Create(filename)
if err != nil {
return fmt.Errorf("error creating key file: %v", err)
}
defer file.Close()
articles := al.Get()
if err = gob.NewEncoder(file).Encode(articles); err != nil {
return fmt.Errorf("error ecoding key: %v", err)
}
return nil
}
func LoadArticleList(filename string) (*ArticleList, error) {
file, err := os.Open(filename)
if err != nil {
return nil, fmt.Errorf("error opening key file: %v", err)
}
articleList := initArticleList()
if err = gob.NewDecoder(file).Decode(&articleList.articles); err != nil {
return nil, fmt.Errorf("error decoding key: %v", err)
}
articleList.wg.Add(1)
go articleList.start()
articleList.wg.Wait()
return articleList, nil
}
func NewTagList() *TagList {
list := initTagList()
list.tags = make([]string, 0)
list.wg.Add(1)
go list.start()
list.wg.Wait()
return list
}
func (tl *TagList) Add(tag string) {
tl.addCh <- tag
}
func (tl *TagList) Get() []string {
return <-tl.getCh
}
func (tl *TagList) Save(filename string) error {
file, err := os.Create(filename)
if err != nil {
return fmt.Errorf("error creating key file: %v", err)
}
defer file.Close()
tags := tl.Get()
if err = gob.NewEncoder(file).Encode(tags); err != nil {
return fmt.Errorf("error ecoding key: %v", err)
}
return nil
}
func LoadTagList(filename string) (*TagList, error) {
file, err := os.Open(filename)
if err != nil {
return nil, fmt.Errorf("error opening key file: %v", err)
}
defer file.Close()
tagList := initTagList()
if err = gob.NewDecoder(file).Decode(&tagList.tags); err != nil {
return nil, fmt.Errorf("error decoding key: %v", err)
}
tagList.wg.Add(1)
go tagList.start()
tagList.wg.Wait()
return tagList, nil
} }

View File

@ -171,3 +171,97 @@ func (db *DB) GetUser(id int64) (*User, error) {
return user, nil return user, nil
} }
func (db *DB) AddTag(tagName string) error {
query := "INSERT INTO tags name VALUES ?"
if _, err := db.Exec(query, tagName); err != nil {
return fmt.Errorf("error inserting tag into DB: %v", err)
}
return nil
}
func (db *DB) GetTagList() ([]Tag, error) {
query := "SELECT id, name FROM tags"
rows, err := db.Query(query)
if err != nil {
return nil, fmt.Errorf("error querying tags: %v", err)
}
var tagList []Tag
for rows.Next() {
var tag Tag
if err = rows.Scan(&tag.ID, &tag.Name); err != nil {
return nil, fmt.Errorf("error scanning tag row: %v", err)
}
tagList = append(tagList, tag)
}
return tagList, nil
}
func (db *DB) AddArticle(a *Article) error {
query := `
INSERT INTO articles
(title, description, content, published, author_id)
VALUES
(?, ?, ?, ?)
`
if _, err := db.Exec(query, a.Title, a.Desc, a.Content, a.Published, a.AuthorID); err != nil {
return fmt.Errorf("error inserting article into DB: %v", err)
}
return nil
}
func (db *DB) GetArticle(id int64) (*Article, error) {
query := `
SELECT title, created, description, content, published, author_id
FROM articles
WHERE id = ?
`
row := db.QueryRow(query, id)
article := new(Article)
if err := row.Scan(&article.Title, &article.Created, &article.Desc,
&article.Content, &article.Published, &article.AuthorID); err != nil {
return nil, fmt.Errorf("error scanning article row: %v", err)
}
return article, nil
}
func (db *DB) GetUnpublishedArticles() ([]Article, error) {
query := `
SELECT id, title, created, description, content, published, author_id
FROM articles
WHERE published = ?
`
rows, err := db.Query(query, false)
if err != nil {
return nil, fmt.Errorf("error querying articles: %v", err)
}
var articleList []Article
for rows.Next() {
var article Article
if err = rows.Scan(&article.ID, &article.Title, &article.Created,
&article.Desc, &article.Content, &article.Published,
&article.AuthorID); err != nil {
return nil, fmt.Errorf("error scanning article row: %v", err)
}
articleList = append(articleList, article)
}
return articleList, nil
}
func (db *DB) UpdateArticle(id int64, attribute string, val interface{}) error {
query := `
UPDATE articles
SET ? = ?
WHERE id = ?
`
if _, err := db.Exec(query, attribute, val, id); err != nil {
return fmt.Errorf("error updating article in DB: %v", err)
}
return nil
}

6
cmd/data/tags.go Normal file
View File

@ -0,0 +1,6 @@
package data
type Tag struct {
ID int64
Name string
}

View File

@ -47,16 +47,6 @@ func main() {
} }
store := data.NewCookieStore(key) store := data.NewCookieStore(key)
articleList, err := data.LoadArticleList("tmp/unpublished-articles.gob")
if err != nil {
articleList = data.NewArticleList()
}
tagList, err := data.LoadTagList("tmp/tags.gob")
if err != nil {
tagList = data.NewTagList()
}
mux := http.NewServeMux() mux := http.NewServeMux()
mux.Handle("/web/static/", http.StripPrefix("/web/static/", http.FileServer(http.Dir("web/static/")))) mux.Handle("/web/static/", http.StripPrefix("/web/static/", http.FileServer(http.Dir("web/static/"))))
mux.HandleFunc("/", ui.HomePage(db, store)) mux.HandleFunc("/", ui.HomePage(db, store))
@ -65,15 +55,15 @@ func main() {
mux.HandleFunc("GET /create-user/", ui.CreateUser) mux.HandleFunc("GET /create-user/", ui.CreateUser)
mux.HandleFunc("GET /hub/", ui.ShowHub(store)) mux.HandleFunc("GET /hub/", ui.ShowHub(store))
mux.HandleFunc("GET /rss/", ui.ShowRSS(feed)) mux.HandleFunc("GET /rss/", ui.ShowRSS(feed))
mux.HandleFunc("GET /unpublished-articles/", ui.ShowUnpublishedArticles(articleList)) mux.HandleFunc("GET /unpublished-articles/", ui.ShowUnpublishedArticles(db))
mux.HandleFunc("GET /write-article/", ui.WriteArticle(tagList)) mux.HandleFunc("GET /write-article/", ui.WriteArticle(db))
mux.HandleFunc("POST /add-tag/", ui.AddTag(tagList, store)) mux.HandleFunc("POST /add-tag/", ui.AddTag(db, store))
mux.HandleFunc("POST /add-user/", ui.AddUser(db, store)) mux.HandleFunc("POST /add-user/", ui.AddUser(db, store))
mux.HandleFunc("POST /finish-article/", ui.FinishArticle(articleList, store)) mux.HandleFunc("POST /finish-article/", ui.FinishArticle(db, store))
mux.HandleFunc("POST /login/", ui.Login(db, store)) mux.HandleFunc("POST /login/", ui.Login(db, store))
mux.HandleFunc("POST /review-article/", ui.ReviewArticle(articleList, store)) mux.HandleFunc("POST /review-article/", ui.ReviewArticle(db, store))
mux.HandleFunc("POST /publish-article/", ui.PublishArticle(feed, articleList, store)) mux.HandleFunc("POST /publish-article/", ui.PublishArticle(db, feed, store))
log.Fatalln(http.ListenAndServe(":8080", mux)) log.Fatalln(http.ListenAndServe(":8080", mux))
} }

View File

@ -4,10 +4,10 @@ import (
"html/template" "html/template"
"log" "log"
"net/http" "net/http"
"strconv"
"time" "time"
"git.streifling.com/jason/rss" "git.streifling.com/jason/rss"
"github.com/google/uuid"
"streifling.com/jason/cpolis/cmd/data" "streifling.com/jason/cpolis/cmd/data"
) )
@ -25,14 +25,21 @@ func ShowHub(s *data.CookieStore) http.HandlerFunc {
} }
} }
func WriteArticle(tl *data.TagList) http.HandlerFunc { func WriteArticle(db *data.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
tags, err := db.GetTagList()
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
tmpl, err := template.ParseFiles("web/templates/editor.html") tmpl, err := template.ParseFiles("web/templates/editor.html")
template.Must(tmpl, err).ExecuteTemplate(w, "page-content", tl.Get()) template.Must(tmpl, err).ExecuteTemplate(w, "page-content", tags)
} }
} }
func FinishArticle(al *data.ArticleList, s *data.CookieStore) http.HandlerFunc { func FinishArticle(db *data.DB, s *data.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
article := new(data.Article) article := new(data.Article)
var err error var err error
@ -68,13 +75,8 @@ func FinishArticle(al *data.ArticleList, s *data.CookieStore) http.HandlerFunc {
template.Must(tmpl, err).ExecuteTemplate(w, "page-content", msg) template.Must(tmpl, err).ExecuteTemplate(w, "page-content", msg)
} }
article.UUID = uuid.New()
article.Author = session.Values["name"].(string)
article.Created = time.Now()
article.AuthorID = session.Values["id"].(int64) article.AuthorID = session.Values["id"].(int64)
db.AddArticle(article)
al.Add(article)
al.Save("tmp/unpublished-articles.gob")
tmpl, err := template.ParseFiles("web/templates/hub.html") tmpl, err := template.ParseFiles("web/templates/hub.html")
tmpl = template.Must(tmpl, err) tmpl = template.Must(tmpl, err)
@ -82,28 +84,33 @@ func FinishArticle(al *data.ArticleList, s *data.CookieStore) http.HandlerFunc {
} }
} }
func ShowUnpublishedArticles(al *data.ArticleList) http.HandlerFunc { func ShowUnpublishedArticles(db *data.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
articles, err := db.GetUnpublishedArticles()
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
tmpl, err := template.ParseFiles("web/templates/unpublished-articles.html") tmpl, err := template.ParseFiles("web/templates/unpublished-articles.html")
template.Must(tmpl, err).ExecuteTemplate(w, "page-content", al.Get()) template.Must(tmpl, err).ExecuteTemplate(w, "page-content", articles)
} }
} }
func ReviewArticle(al *data.ArticleList, s *data.CookieStore) http.HandlerFunc { func ReviewArticle(db *data.DB, s *data.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
uuid, err := uuid.Parse(r.PostFormValue("uuid")) id, err := strconv.ParseInt(r.PostFormValue("id"), 10, 64)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
for _, article := range al.Get() { article, err := db.GetArticle(id)
if article.UUID == uuid { if err != nil {
tmpl, err := template.ParseFiles("web/templates/to-be-published.html") tmpl, err := template.ParseFiles("web/templates/to-be-published.html")
template.Must(tmpl, err).ExecuteTemplate(w, "page-content", article) template.Must(tmpl, err).ExecuteTemplate(w, "page-content", article)
return return
}
} }
session, err := s.Get(r, "cookie") session, err := s.Get(r, "cookie")
@ -119,21 +126,24 @@ func ReviewArticle(al *data.ArticleList, s *data.CookieStore) http.HandlerFunc {
} }
} }
func PublishArticle(c *data.Channel, al *data.ArticleList, s *data.CookieStore) http.HandlerFunc { func PublishArticle(db *data.DB, c *data.Channel, s *data.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
uuid, err := uuid.Parse(r.PostFormValue("uuid")) id, err := strconv.ParseInt(r.PostFormValue("id"), 10, 64)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
article, ok := al.Release(uuid) if err = db.UpdateArticle(id, "published", true); err != nil {
if !ok { log.Println(err)
// TODO: Warnung anzeigen http.Error(w, err.Error(), http.StatusInternalServerError)
// msg = "Alle Felder müssen ausgefüllt werden." return
// tmpl, err := template.ParseFiles("web/templates/add-user.html") }
// template.Must(tmpl, err).ExecuteTemplate(w, "page-content", msg) article, err := db.GetArticle(id)
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }

View File

@ -12,10 +12,9 @@ func CreateTag(w http.ResponseWriter, r *http.Request) {
template.Must(tmpl, err).ExecuteTemplate(w, "page-content", nil) template.Must(tmpl, err).ExecuteTemplate(w, "page-content", nil)
} }
func AddTag(tl *data.TagList, s *data.CookieStore) http.HandlerFunc { func AddTag(db *data.DB, s *data.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
tl.Add(r.PostFormValue("tag")) db.AddTag(r.PostFormValue("tag"))
tl.Save("tmp/tags.gob")
session, err := s.Get(r, "cookie") session, err := s.Get(r, "cookie")
if err != nil { if err != nil {