diff --git a/cmd/web/web.go b/cmd/web/web.go index 9ecf60c..665162c 100644 --- a/cmd/web/web.go +++ b/cmd/web/web.go @@ -1,68 +1,23 @@ package main import ( - "fmt" - "gitlab.com/kbr4/svevijesti/internal/database" - "html/template" - "io/ioutil" + "gitlab.com/kbr4/svevijesti/internal/server" + "log" "net/http" - "path/filepath" - "strings" "time" ) -var tPath = "./web/tpl/" -var dPath = "./web/data/" - -var templateDirs = []string{"./web/tpl", "./web/data"} -var templates *template.Template - -func getTemplates() (templates *template.Template, err error) { - var allFiles []string - for _, dir := range templateDirs { - files2, _ := ioutil.ReadDir(dir) - for _, file := range files2 { - filename := file.Name() - if strings.HasSuffix(filename, ".html") { - filePath := filepath.Join(dir, filename) - fmt.Println("Template found: ", filePath) - allFiles = append(allFiles, filePath) - } - } - } - - templates, err = template.New("").ParseFiles(allFiles...) - return -} - -func init() { - templates, _ = getTemplates() -} - -func rootHandler(wr http.ResponseWriter, req *http.Request) { - title := "Pocetna" - store, err := database.Connect() - if err != nil { - http.Error(wr, err.Error(), http.StatusInternalServerError) - } - - articles, err := database.ArticlesForDay(store, time.Now()) - if err != nil { - http.Error(wr, err.Error(), http.StatusInternalServerError) - } - - data := map[string]interface{}{ - "title": title, - "articles": articles, - } - - err = templates.ExecuteTemplate(wr, "homeHTML", data) - if err != nil { - http.Error(wr, err.Error(), http.StatusInternalServerError) - } -} - func main() { - http.HandleFunc("/", rootHandler) - http.ListenAndServe(":8080", nil) + r := server.CreateRoutes() + http.Handle("/", r) + + srv := &http.Server{ + Handler: r, + Addr: "0.0.0.0:8080", + // Good practice: enforce timeouts for servers you create! + WriteTimeout: 15 * time.Second, + ReadTimeout: 15 * time.Second, + } + + log.Fatal(srv.ListenAndServe()) } diff --git a/go.mod b/go.mod index 4eac5ae..a5b4e5d 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/gocolly/colly v1.2.0 // indirect github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect github.com/golang/protobuf v1.3.1 // indirect + github.com/gorilla/mux v1.8.0 // indirect github.com/gosimple/slug v1.12.0 // indirect github.com/gosimple/unidecode v1.0.1 // indirect github.com/kennygrant/sanitize v1.2.4 // indirect diff --git a/go.sum b/go.sum index f092c10..b68be6c 100644 --- a/go.sum +++ b/go.sum @@ -19,6 +19,8 @@ github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18h github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gosimple/slug v1.12.0 h1:xzuhj7G7cGtd34NXnW/yF0l+AGNfWqwgh/IXgFy7dnc= github.com/gosimple/slug v1.12.0/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ= github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o= diff --git a/internal/database/articles.go b/internal/database/articles.go index 65c6df2..64e0dab 100644 --- a/internal/database/articles.go +++ b/internal/database/articles.go @@ -4,7 +4,9 @@ import ( "fmt" _ "github.com/lib/pq" "gitlab.com/kbr4/svevijesti/internal/model" + "html/template" "math" + "strings" "time" ) @@ -86,3 +88,45 @@ func ArticlesForDay(store *Store, day time.Time) (articles []model.DisplayArticl return result, nil } + +func ArticleByID(store *Store, ID int, slug string) (article model.DisplayArticle, err error) { + + result := model.DisplayArticle{} + query, err := store.Prepare(` + select id,title, content, slug, original_url, source_id, created_at from articles where id = $1 and slug = $2; + `) + if err != nil { + return result, err + } + + row := query.QueryRow(ID, slug) + if err != nil { + return result, err + } + + r := model.DisplayArticle{} + content := "" + err = row.Scan(&r.ID, &r.Title, &content, &r.Slug, &r.OriginalUrl, &r.SourceId, &r.CreatedAt) + if err != nil { + return result, err + } + + ago := time.Now().Sub(r.CreatedAt) + hours := ago.Hours() + + r.Content = template.HTML(strings.Replace(content, "\n", "
\n", -1)) + + if hours < 1 { + r.FormatedCreatedAt = fmt.Sprintf("Prije %d minuta.", int(math.Floor(ago.Minutes()))) + + } else if hours > 24 { + r.FormatedCreatedAt = r.CreatedAt.Format("01.02.2006. 15:04:05") + } else { + r.FormatedCreatedAt = fmt.Sprintf("Prije %d sati.", int(math.Floor(hours))) + } + r.SourceName = model.SourceName(r.SourceId) + + result = r + + return result, nil +} diff --git a/internal/model/model.go b/internal/model/model.go index 29aa6c7..d9211fb 100644 --- a/internal/model/model.go +++ b/internal/model/model.go @@ -1,6 +1,9 @@ package model -import "time" +import ( + "html/template" + "time" +) type ScrapedArticle struct { Title string @@ -13,7 +16,7 @@ type ScrapedArticle struct { type DisplayArticle struct { ID int Title string - Content string + Content template.HTML Slug string OriginalUrl string SourceId int diff --git a/internal/server/articles.go b/internal/server/articles.go new file mode 100644 index 0000000..c28f897 --- /dev/null +++ b/internal/server/articles.go @@ -0,0 +1,61 @@ +package server + +import ( + "github.com/gorilla/mux" + "gitlab.com/kbr4/svevijesti/internal/database" + "net/http" + "strconv" + "time" +) + +func rootHandler(wr http.ResponseWriter, req *http.Request) { + title := "Pocetna" + store, err := database.Connect() + if err != nil { + http.Error(wr, err.Error(), http.StatusInternalServerError) + } + + articles, err := database.ArticlesForDay(store, time.Now()) + if err != nil { + http.Error(wr, err.Error(), http.StatusInternalServerError) + } + + data := map[string]interface{}{ + "title": title, + "articles": articles, + } + + err = templates.ExecuteTemplate(wr, "homeHTML", data) + if err != nil { + http.Error(wr, err.Error(), http.StatusInternalServerError) + } +} + +func articleHandler(wr http.ResponseWriter, req *http.Request) { + title := "Pocetna" + store, err := database.Connect() + if err != nil { + http.Error(wr, err.Error(), http.StatusInternalServerError) + } + + vars := mux.Vars(req) + articleID, err := strconv.Atoi(vars["id"]) + if err != nil { + articleID = -1 + } + articleSlug := vars["slug"] + article, err := database.ArticleByID(store, articleID, articleSlug) + if err != nil { + http.Error(wr, err.Error(), http.StatusNotFound) + } + + data := map[string]interface{}{ + "title": title, + "article": article, + } + + err = templates.ExecuteTemplate(wr, "articleHTML", data) + if err != nil { + http.Error(wr, err.Error(), http.StatusInternalServerError) + } +} diff --git a/internal/server/server.go b/internal/server/server.go index abb4e43..90cb0c8 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -1 +1,45 @@ package server + +import ( + "fmt" + "github.com/gorilla/mux" + "html/template" + "io/ioutil" + "path/filepath" + "strings" +) + +var tPath = "./web/tpl/" +var dPath = "./web/data/" + +var templateDirs = []string{"./web/tpl", "./web/data"} +var templates *template.Template + +func getTemplates() (templates *template.Template, err error) { + var allFiles []string + for _, dir := range templateDirs { + files2, _ := ioutil.ReadDir(dir) + for _, file := range files2 { + filename := file.Name() + if strings.HasSuffix(filename, ".html") { + filePath := filepath.Join(dir, filename) + fmt.Println("Template found: ", filePath) + allFiles = append(allFiles, filePath) + } + } + } + + templates, err = template.New("").ParseFiles(allFiles...) + return +} + +func init() { + templates, _ = getTemplates() +} + +func CreateRoutes() *mux.Router { + r := mux.NewRouter() + r.HandleFunc("/", rootHandler) + r.HandleFunc("/{id:[0-9]+}/{slug}", articleHandler) + return r +} diff --git a/web/data/footer.html b/web/data/footer.html index 80b8f37..a34300b 100644 --- a/web/data/footer.html +++ b/web/data/footer.html @@ -1,6 +1,6 @@ {{define "footerHTML"}} {{end}} diff --git a/web/data/head.html b/web/data/head.html index 8c66df4..bf43e1e 100644 --- a/web/data/head.html +++ b/web/data/head.html @@ -20,8 +20,20 @@ max-width: 98vw; } + h1#title { + margin-block-end: 0; + font-size: 1.7em; + } + .timestamp { font-size: 0.8em; + color: gray; + } + + .single_timestamp { + font-size: 0.77em; + margin-bottom: 0.7em; + color: gray; } #logo { diff --git a/web/data/single_article.html b/web/data/single_article.html new file mode 100644 index 0000000..5c30b30 --- /dev/null +++ b/web/data/single_article.html @@ -0,0 +1,11 @@ +{{define "singleArticleHTML"}} +{{with .article }} +
+

{{.Title}}

+
{{.SourceName}} - {{ .FormatedCreatedAt }}
+
+ {{.Content}} +
+

+{{end}} +{{end}} diff --git a/web/tpl/article.html b/web/tpl/article.html new file mode 100644 index 0000000..6400b9e --- /dev/null +++ b/web/tpl/article.html @@ -0,0 +1,12 @@ +{{define "articleHTML"}} + +{{template "headHTML" .}} + +{{template "headerHTML" .}} + +{{template "singleArticleHTML" .}} + +{{template "footerHTML" .}} + + +{{end}}