diff --git a/src/assets/templates/article.html b/src/assets/templates/article.html index 99f81d6..83126db 100644 --- a/src/assets/templates/article.html +++ b/src/assets/templates/article.html @@ -14,7 +14,9 @@ {{end}}

{{ .Summary }}

- Link +
+ Details +
{{ end }} diff --git a/src/assets/templates/articlePage.html b/src/assets/templates/articlePage.html new file mode 100644 index 0000000..40ba206 --- /dev/null +++ b/src/assets/templates/articlePage.html @@ -0,0 +1,27 @@ +{{ define "content" }} + +
+ +
+
+
+
+
{{ .ArticlePageVM.Title }}
+
+ +
+

Datum{{ .ArticlePageVM.PublishDate }}

+

Quelle{{ .ArticlePageVM.ShortSource }}

+

TLDR{{ .ArticlePageVM.AiSummary }}

+

Inhalt{{ .ArticlePageVM.Content }}

+ +
+
+
+ +
+{{ end }} diff --git a/src/assets/templates/layout.html b/src/assets/templates/layout.html index dc0105f..d048bb4 100644 --- a/src/assets/templates/layout.html +++ b/src/assets/templates/layout.html @@ -20,7 +20,7 @@
crowsnest
@@ -47,7 +47,7 @@ diff --git a/src/internal/app/Article.go b/src/internal/app/Article.go new file mode 100644 index 0000000..028c666 --- /dev/null +++ b/src/internal/app/Article.go @@ -0,0 +1,42 @@ +package app + +import ( + "html/template" + "net/http" + "strconv" +) + +// Enpoint that returns a list of articles given search terms in the post +// request of a search form. Uses the content template. +func (app *App) Article(w http.ResponseWriter, req *http.Request) { + // get id + id, err := strconv.ParseUint(req.PathValue("id"), 10, 64) + if err != nil { + http.NotFound(w, req) + return + } + + // get articles + article, err := app.articles.ById(int(id)) + if err != nil { + // treat as no result + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // render template + t := template.Must(template.ParseFiles( + "assets/templates/articlePage.html", + "assets/templates/layout.html", + )) + + data := map[string]interface{}{ + "SelectedNavItemArticle": false, + "ArticlePageVM": article.PageViewModel(), + } + err = t.ExecuteTemplate(w, "base", data) + if err != nil { + http.Error(w, "Failed to render template", http.StatusInternalServerError) + return + } +} diff --git a/src/internal/app/app.go b/src/internal/app/app.go index 7dec87b..3facd2e 100644 --- a/src/internal/app/app.go +++ b/src/internal/app/app.go @@ -24,6 +24,8 @@ func (app *App) Routes() http.Handler { mux.Handle("GET /page/{id}", http.HandlerFunc(app.Index)) mux.Handle("POST /up/search", http.HandlerFunc(app.UpSearch)) + mux.Handle("GET /article/{id}", http.HandlerFunc(app.Article)) + // serve files from the "static" directory mux.Handle("GET /static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./assets/static")))) diff --git a/src/internal/model/article.go b/src/internal/model/article.go index 2170468..8bd9232 100644 --- a/src/internal/model/article.go +++ b/src/internal/model/article.go @@ -7,7 +7,7 @@ import ( // TODO docstring type Article struct { - Identifier int + Id int SourceUrl string PublishDate time.Time FetchDate time.Time @@ -18,14 +18,23 @@ type Article struct { // TODO docstring type ArticleViewModel struct { + Id int Title string PublishDate string - SourceUrl string ShortSource string Summary string AiSummarized bool } +type ArticlePageViewModel struct { + SourceUrl string + ShortSource string + PublishDate string + Title string + Content string + AiSummary string +} + // TODO docstring func (a *Article) ViewModel() *ArticleViewModel { summary := a.AiSummary @@ -44,11 +53,33 @@ func (a *Article) ViewModel() *ArticleViewModel { } return &ArticleViewModel{ + Id: a.Id, Title: a.Title, PublishDate: a.PublishDate.Local().Format("02.01.2006"), - SourceUrl: a.SourceUrl, ShortSource: short_url, Summary: summary, AiSummarized: a.AiSummary != "", } } + +func (a *Article) PageViewModel() *ArticlePageViewModel { + summary := a.AiSummary + if summary == "" { + summary = "N/A" + } + + short_url := "" + parsedURL, err := url.Parse(a.SourceUrl) + if err == nil { + short_url = parsedURL.Hostname() + } + + return &ArticlePageViewModel{ + SourceUrl: a.SourceUrl, + ShortSource: short_url, + Title: a.Title, + PublishDate: a.PublishDate.Local().Format("02.01.2006 15:04"), + Content: a.Content, + AiSummary: summary, + } +} diff --git a/src/internal/model/database/articles.go b/src/internal/model/database/articles.go index 3dd161d..5048974 100644 --- a/src/internal/model/database/articles.go +++ b/src/internal/model/database/articles.go @@ -27,7 +27,7 @@ func (m *ArticleModel) All(limit int, offset int) ([]model.Article, error) { articles := []model.Article{} for rows.Next() { a := model.Article{} - err := rows.Scan(&a.Identifier, &a.Title, &a.SourceUrl, &a.Content, &a.PublishDate, &a.FetchDate, &a.AiSummary) + err := rows.Scan(&a.Id, &a.Title, &a.SourceUrl, &a.Content, &a.PublishDate, &a.FetchDate, &a.AiSummary) if err != nil { return nil, err } @@ -78,7 +78,7 @@ func (m *ArticleModel) Search(query string) ([]model.Article, error) { articles := []model.Article{} for rows.Next() { a := model.Article{} - err := rows.Scan(&a.Identifier, &a.Title, &a.SourceUrl, &a.Content, &a.PublishDate, &a.FetchDate) + err := rows.Scan(&a.Id, &a.Title, &a.SourceUrl, &a.Content, &a.PublishDate, &a.FetchDate) if err != nil { return nil, err } @@ -92,6 +92,25 @@ func (m *ArticleModel) Search(query string) ([]model.Article, error) { return articles, nil } +// Will return an article given an id. This may fail if the connection to the +// database fails or there is no aritcle with the given id. +func (m *ArticleModel) ById(id int) (*model.Article, error) { + stmt := ` + SELECT a.id, a.title, a.sourceurl, a.content, a.publishdate, a.fetchDate + FROM articles a + WHERE a.id = $1 + ` + + rows := m.DB.QueryRow(stmt, id) + + a := &model.Article{} + if err := rows.Scan(&a.Id, &a.Title, &a.SourceUrl, &a.Content, &a.PublishDate, &a.FetchDate); err != nil { + return nil, err + } + + return a, nil +} + // Inserts a new article into the database. The id attribute of the given // article will be ignored. May throw an error if the execution of the database // query fails. @@ -110,6 +129,6 @@ func (m *ArticleModel) Update(a *model.Article) error { SET title = $1, sourceUrl = $2, content = $4, publishDate = $5, fetchDate = $6, aisummary = $7 WHERE id = $8 ` - _, err := m.DB.Exec(stmt, a.Title, a.SourceUrl, a.Content, a.PublishDate, a.FetchDate, a.AiSummary, a.Identifier) + _, err := m.DB.Exec(stmt, a.Title, a.SourceUrl, a.Content, a.PublishDate, a.FetchDate, a.AiSummary, a.Id) return err }