First version of web UI
This commit is contained in:
68
cmd/web/web.go
Normal file
68
cmd/web/web.go
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"gitlab.com/kbr4/svevijesti/internal/database"
|
||||||
|
"html/template"
|
||||||
|
"io/ioutil"
|
||||||
|
"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)
|
||||||
|
}
|
||||||
88
internal/database/articles.go
Normal file
88
internal/database/articles.go
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
_ "github.com/lib/pq"
|
||||||
|
"gitlab.com/kbr4/svevijesti/internal/model"
|
||||||
|
"math"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InsertArticle(store *Store, article model.ScrapedArticle) (err error) {
|
||||||
|
query := `
|
||||||
|
INSERT INTO articles
|
||||||
|
(title, content, slug, original_url, source_id)
|
||||||
|
VALUES
|
||||||
|
($1,$2,$3,$4,$5);`
|
||||||
|
|
||||||
|
_, err = store.Exec(query, article.Title, article.Content, article.Slug, article.OriginalUrl, article.SourceId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsSaved(store *Store, url string) bool {
|
||||||
|
|
||||||
|
exists := false
|
||||||
|
query, err := store.Prepare(`
|
||||||
|
select exists(select 1 from articles where original_url = $1);
|
||||||
|
`)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
row := query.QueryRow(url)
|
||||||
|
err = row.Scan(&exists)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return exists
|
||||||
|
}
|
||||||
|
|
||||||
|
func ArticlesForDay(store *Store, day time.Time) (articles []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 created_at > $1 and created_at < $2 and LENGTH(content) > 10 order by id desc;
|
||||||
|
`)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tomorrow := day.AddDate(0, 0, 1)
|
||||||
|
todayDate := day.Format("2006-01-02")
|
||||||
|
tomorrowDate := tomorrow.Format("2006-01-02")
|
||||||
|
|
||||||
|
rows, err := query.Query(todayDate, tomorrowDate)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
r := model.DisplayArticle{}
|
||||||
|
err = rows.Scan(&r.ID, &r.Title, &r.Content, &r.Slug, &r.OriginalUrl, &r.SourceId, &r.CreatedAt)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ago := time.Now().Sub(r.CreatedAt)
|
||||||
|
hours := ago.Hours()
|
||||||
|
|
||||||
|
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 = append(result, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
_ "github.com/lib/pq"
|
_ "github.com/lib/pq"
|
||||||
"gitlab.com/kbr4/svevijesti/internal/model"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -24,36 +23,3 @@ func Connect() (*Store, error) {
|
|||||||
db, err := sql.Open("postgres", psqlInfo)
|
db, err := sql.Open("postgres", psqlInfo)
|
||||||
return db, err
|
return db, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func InsertArticle(store *Store, article model.ScrapedArticle) (err error) {
|
|
||||||
query := `
|
|
||||||
INSERT INTO articles
|
|
||||||
(title, content, slug, original_url, source_id)
|
|
||||||
VALUES
|
|
||||||
($1,$2,$3,$4,$5);`
|
|
||||||
|
|
||||||
_, err = store.Exec(query, article.Title, article.Content, article.Slug, article.OriginalUrl, article.SourceId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsSaved(store *Store, url string) bool {
|
|
||||||
|
|
||||||
exists := false
|
|
||||||
query, err := store.Prepare(`
|
|
||||||
select exists(select 1 from articles where original_url = $1);
|
|
||||||
`)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
row := query.QueryRow(url)
|
|
||||||
err = row.Scan(&exists)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return exists
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
type ScrapedArticle struct {
|
type ScrapedArticle struct {
|
||||||
Title string
|
Title string
|
||||||
Content string
|
Content string
|
||||||
@@ -8,6 +10,26 @@ type ScrapedArticle struct {
|
|||||||
SourceId int
|
SourceId int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DisplayArticle struct {
|
||||||
|
ID int
|
||||||
|
Title string
|
||||||
|
Content string
|
||||||
|
Slug string
|
||||||
|
OriginalUrl string
|
||||||
|
SourceId int
|
||||||
|
CreatedAt time.Time
|
||||||
|
FormatedCreatedAt string
|
||||||
|
SourceName string
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
KlixSource = 1
|
KlixSource = 1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func SourceName(sourceId int) string {
|
||||||
|
switch sourceId {
|
||||||
|
case KlixSource:
|
||||||
|
return "klix"
|
||||||
|
}
|
||||||
|
return "starenovine"
|
||||||
|
}
|
||||||
|
|||||||
16
web/data/articles.html
Normal file
16
web/data/articles.html
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{{define "articlesHTML"}}
|
||||||
|
<ol reversed>
|
||||||
|
{{range .articles}}
|
||||||
|
<li>
|
||||||
|
<div class="article_content">
|
||||||
|
<a href="/{{.ID}}/{{.Slug}}">
|
||||||
|
{{.Title}}</a></div>
|
||||||
|
<div class="timestamp">{{.SourceName}} - {{ .FormatedCreatedAt }}</div>
|
||||||
|
</li>
|
||||||
|
<br><br>
|
||||||
|
{{else}}
|
||||||
|
Nema članaka za izabrani datum.
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
</ol>
|
||||||
|
{{end}}
|
||||||
6
web/data/footer.html
Normal file
6
web/data/footer.html
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{{define "footerHTML"}}
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
© StareNovine
|
||||||
|
</footer>
|
||||||
|
{{end}}
|
||||||
44
web/data/head.html
Normal file
44
web/data/head.html
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
{{define "headHTML"}}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<meta property="og:site_name" content="svevijesti">
|
||||||
|
<meta name="twitter:card" content="preview">
|
||||||
|
<meta property="og:title" content="stare novine: bosna i hercegovina, klix, avaz, faktor, blijesak, srpskainfo">
|
||||||
|
<meta name="description" content="stare novine omogucavaju citanje svih vijesti iz bosne i hercegovine, hrvatske, srbije, crne gore, kosova na bosanskom, crnogorskom, hrvatskom, srpskom jeziku na svim uredjajima koliko god stari bili">
|
||||||
|
<meta property="og:url" content="https://www.starenovine.com">
|
||||||
|
<title>{{.title}} - stare novine</title>
|
||||||
|
<link rel="canonical" href="https://www.starenovine.com/">
|
||||||
|
<style type="text/css">
|
||||||
|
body {
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 1.5em;
|
||||||
|
width: 90%;
|
||||||
|
max-width: 98vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timestamp {
|
||||||
|
font-size: 0.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#logo {
|
||||||
|
font-size: 2vw;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre.article_content {
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width: 98vw;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
{{end}}
|
||||||
18
web/data/header.html
Normal file
18
web/data/header.html
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{{define "headerHTML"}}
|
||||||
|
<header>
|
||||||
|
<pre id="logo">
|
||||||
|
_____ ______ ____ ____ ___ ____ ___ __ __ ____ ____ ___
|
||||||
|
/ ___/| | / || \ / _]| \ / \ | | || || \ / _]
|
||||||
|
( \_ | || o || D ) / [_ | _ || || | | | | | _ | / [_
|
||||||
|
\__ ||_| |_|| || / | _]| | || O || | | | | | | || _]
|
||||||
|
/ \ | | | | _ || \ | [_ | | || || : | | | | | || [_
|
||||||
|
\ | | | | | || . \| || | || | \ / | | | | || |
|
||||||
|
\___| |__| |__|__||__|\_||_____||__|__| \___/ \_/ |____||__|__||_____|
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
<br>
|
||||||
|
<nav>
|
||||||
|
<a href="/">Početna</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
{{end}}
|
||||||
12
web/tpl/home.html
Normal file
12
web/tpl/home.html
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{{define "homeHTML"}}
|
||||||
|
|
||||||
|
{{template "headHTML" .}}
|
||||||
|
<body>
|
||||||
|
{{template "headerHTML" .}}
|
||||||
|
|
||||||
|
{{template "articlesHTML" .}}
|
||||||
|
|
||||||
|
{{template "footerHTML" .}}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
{{end}}
|
||||||
Reference in New Issue
Block a user