Detalji clanaka
This commit is contained in:
@@ -1,68 +1,23 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"gitlab.com/kbr4/svevijesti/internal/server"
|
||||||
"gitlab.com/kbr4/svevijesti/internal/database"
|
"log"
|
||||||
"html/template"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"time"
|
"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() {
|
func main() {
|
||||||
http.HandleFunc("/", rootHandler)
|
r := server.CreateRoutes()
|
||||||
http.ListenAndServe(":8080", nil)
|
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())
|
||||||
}
|
}
|
||||||
|
|||||||
1
go.mod
1
go.mod
@@ -12,6 +12,7 @@ require (
|
|||||||
github.com/gocolly/colly v1.2.0 // indirect
|
github.com/gocolly/colly v1.2.0 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
|
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
|
||||||
github.com/golang/protobuf v1.3.1 // 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/slug v1.12.0 // indirect
|
||||||
github.com/gosimple/unidecode v1.0.1 // indirect
|
github.com/gosimple/unidecode v1.0.1 // indirect
|
||||||
github.com/kennygrant/sanitize v1.2.4 // indirect
|
github.com/kennygrant/sanitize v1.2.4 // indirect
|
||||||
|
|||||||
2
go.sum
2
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/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 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
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 h1:xzuhj7G7cGtd34NXnW/yF0l+AGNfWqwgh/IXgFy7dnc=
|
||||||
github.com/gosimple/slug v1.12.0/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ=
|
github.com/gosimple/slug v1.12.0/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ=
|
||||||
github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o=
|
github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o=
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
_ "github.com/lib/pq"
|
_ "github.com/lib/pq"
|
||||||
"gitlab.com/kbr4/svevijesti/internal/model"
|
"gitlab.com/kbr4/svevijesti/internal/model"
|
||||||
|
"html/template"
|
||||||
"math"
|
"math"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -86,3 +88,45 @@ func ArticlesForDay(store *Store, day time.Time) (articles []model.DisplayArticl
|
|||||||
|
|
||||||
return result, nil
|
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", "<br>\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
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"html/template"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
type ScrapedArticle struct {
|
type ScrapedArticle struct {
|
||||||
Title string
|
Title string
|
||||||
@@ -13,7 +16,7 @@ type ScrapedArticle struct {
|
|||||||
type DisplayArticle struct {
|
type DisplayArticle struct {
|
||||||
ID int
|
ID int
|
||||||
Title string
|
Title string
|
||||||
Content string
|
Content template.HTML
|
||||||
Slug string
|
Slug string
|
||||||
OriginalUrl string
|
OriginalUrl string
|
||||||
SourceId int
|
SourceId int
|
||||||
|
|||||||
61
internal/server/articles.go
Normal file
61
internal/server/articles.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1 +1,45 @@
|
|||||||
package server
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{{define "footerHTML"}}
|
{{define "footerHTML"}}
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
© StareNovine
|
SN
|
||||||
</footer>
|
</footer>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -20,8 +20,20 @@
|
|||||||
max-width: 98vw;
|
max-width: 98vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h1#title {
|
||||||
|
margin-block-end: 0;
|
||||||
|
font-size: 1.7em;
|
||||||
|
}
|
||||||
|
|
||||||
.timestamp {
|
.timestamp {
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
|
color: gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single_timestamp {
|
||||||
|
font-size: 0.77em;
|
||||||
|
margin-bottom: 0.7em;
|
||||||
|
color: gray;
|
||||||
}
|
}
|
||||||
|
|
||||||
#logo {
|
#logo {
|
||||||
|
|||||||
11
web/data/single_article.html
Normal file
11
web/data/single_article.html
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{{define "singleArticleHTML"}}
|
||||||
|
{{with .article }}
|
||||||
|
<div class="article_content">
|
||||||
|
<h1 id="title">{{.Title}}</h1>
|
||||||
|
<div class="single_timestamp">{{.SourceName}} - {{ .FormatedCreatedAt }}</div>
|
||||||
|
<div class="article_body">
|
||||||
|
{{.Content}}
|
||||||
|
</div>
|
||||||
|
<br><br>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
12
web/tpl/article.html
Normal file
12
web/tpl/article.html
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{{define "articleHTML"}}
|
||||||
|
|
||||||
|
{{template "headHTML" .}}
|
||||||
|
<body>
|
||||||
|
{{template "headerHTML" .}}
|
||||||
|
|
||||||
|
{{template "singleArticleHTML" .}}
|
||||||
|
|
||||||
|
{{template "footerHTML" .}}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
{{end}}
|
||||||
Reference in New Issue
Block a user