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"
|
||||
"fmt"
|
||||
_ "github.com/lib/pq"
|
||||
"gitlab.com/kbr4/svevijesti/internal/model"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -24,36 +23,3 @@ func Connect() (*Store, error) {
|
||||
db, err := sql.Open("postgres", psqlInfo)
|
||||
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
|
||||
|
||||
import "time"
|
||||
|
||||
type ScrapedArticle struct {
|
||||
Title string
|
||||
Content string
|
||||
@@ -8,6 +10,26 @@ type ScrapedArticle struct {
|
||||
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 (
|
||||
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