Compare commits

..

54 Commits

Author SHA1 Message Date
f5c1a5e8d0 Added copyright 2024-03-17 15:29:12 +01:00
40d1104a66 Set pubDate to published time and date 2024-03-17 09:41:09 +01:00
736673c33f Now everyone only sees their own rejected articles 2024-03-17 09:15:37 +01:00
1a2e18359c Added ability to view tags when rejecting and change tags when reworking articles 2024-03-17 08:46:49 +01:00
d1d3a81565 Implemented retry logic on all transactions 2024-03-15 18:37:24 +01:00
bc2302d31f Implement retry logic for UpdateAttributes 2024-03-15 15:18:02 +01:00
1b02e3fad2 Added logout 2024-03-12 20:27:39 +01:00
dc06a6e9aa Fixed dumb routing mistake 2024-03-12 19:56:22 +01:00
63370ada01 Added ability to edit user info 2024-03-11 21:08:27 +01:00
5145e1cd94 Added ability to reject and rework article 2024-03-10 15:03:46 +01:00
28eb7e236a Split up db.go into multiple files 2024-03-09 11:06:03 +01:00
4a7883f886 Also missed rss.go 2024-03-09 10:27:55 +01:00
b69a0688ef Missed main when converting to MVC 2024-03-09 10:27:04 +01:00
0e18c7a7db Changed everything to MVC 2024-03-09 10:25:20 +01:00
3ab9a8fe10 Converted RSS feed to be DB based 2024-03-09 10:12:46 +01:00
6dfd49a5cd Reachieve basic functionality 2024-03-07 20:11:28 +01:00
5455bdb12b Articles and tags are now inserted into DB correctly 2024-03-07 15:31:00 +01:00
9eaafdc712 Converted articles and tags to DB base 2024-03-06 20:53:17 +01:00
d127be4c93 Handle rollbackError with log.Fatalf() 2024-03-06 16:58:41 +01:00
34832e397f Corrected transaction for ChangePassword() 2024-03-06 16:51:08 +01:00
2c0b4878f4 Use transaction when necessary 2024-03-06 15:37:59 +01:00
f0e078c011 Load *ArticleList, *Taglist and *Channel correctly 2024-03-06 15:37:42 +01:00
a8fc28af36 Moved main.go into cmd 2024-03-05 18:26:50 +01:00
e4f085f762 Added Tags to RSS feed as categories 2024-03-05 18:20:34 +01:00
6136f83dfc Converted RSS package to git.streifling.com/jason/rss 2024-03-05 17:13:59 +01:00
21170c9cc2 A bit of cleaning up 2024-03-05 16:38:18 +01:00
6502aa7ec1 Added partial support for tags 2024-03-03 13:56:49 +01:00
e5bdc235b6 Initial sessions implementation 2024-03-03 09:16:49 +01:00
655992c8b2 Just a bit of cleaning up 2024-03-02 09:09:55 +01:00
aa7fcd6075 Created func for minimum spec for rss and article structs, thereby crushing an annoying bug that was caused by not initializing channels but waiting for messages to go through them 2024-03-02 00:28:42 +01:00
f9cc90a948 Changed articles and rss to channels 2024-03-01 21:01:38 +01:00
7f2433c30b Implemented proper User struct 2024-03-01 12:25:53 +01:00
f34efc95dd Added ability to publish articles 2024-03-01 11:30:31 +01:00
935d0a1ca4 Added article list for written but non-published articles 2024-02-27 14:10:27 +01:00
48d4d482b2 Convert title and description to plain text 2024-02-27 09:03:21 +01:00
8dae3ca21e Implemented hub 2024-02-24 15:31:33 +01:00
6f02852212 Add ability to display feed 2024-02-24 14:49:29 +01:00
4cc2110c4b Added messages and field memory for adding user 2024-02-24 13:25:32 +01:00
04cbee097c Require all fields to be filled out when creating a new user 2024-02-24 12:10:34 +01:00
93423ae606 Implemented logging to file 2024-02-24 11:41:01 +01:00
41113b24a8 Check if user already exists and bug fix 2024-02-24 10:56:12 +01:00
2247f316a3 Added ability to add user 2024-02-24 10:28:12 +01:00
9beedf9b2b Added ability to login 2024-02-24 09:54:25 +01:00
7d6f96a185 Check user credentials before adding user 2024-02-22 20:12:09 +01:00
8d47146a7c Added ability to update Passwords 2024-02-22 19:27:41 +01:00
4853184ba1 Added ability to add user 2024-02-22 18:49:51 +01:00
50895249df Changed error messages 2024-02-22 15:23:29 +01:00
6e91253908 Added HTML sanitizer 2024-02-22 15:22:45 +01:00
9bb6010319 Added initial support for MySQL databases 2024-02-18 16:37:13 +01:00
75a0af055c Handle misssed errors for encoding and decoding feeds 2024-02-18 14:31:28 +01:00
171a0dd250 Added description and a way to save and restore the RSS feed. 2024-02-18 14:01:06 +01:00
372882a252 Create RSS from HTML 2024-02-18 12:41:49 +01:00
2d0b53a254 Show HTML on website 2024-02-18 10:48:37 +01:00
2447f50bac First implementation of web based editor to HTML pipeline 2024-02-18 10:07:49 +01:00
13 changed files with 98 additions and 382 deletions

View File

@ -209,7 +209,7 @@ If you develop a new program, and you want it to be of the greatest possible use
To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found.
cpolis
Copyright (C) 2024 Jason Streifling
Copyright (C) 2024 jason
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
@ -221,7 +221,7 @@ Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode:
cpolis Copyright (C) 2024 Jason Streifling
cpolis Copyright (C) 2024 jason
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.

View File

@ -50,7 +50,6 @@ func main() {
mux.HandleFunc("GET /edit-user/", view.EditUser(db, store))
mux.HandleFunc("GET /hub/", view.ShowHub(db, store))
mux.HandleFunc("GET /logout/", view.Logout(store))
mux.HandleFunc("GET /publish-issue/", view.PublishLatestIssue(db))
mux.HandleFunc("GET /rejected-articles/", view.ShowRejectedArticles(db, store))
mux.HandleFunc("GET /rss/", view.ShowRSS(
db,
@ -58,7 +57,6 @@ func main() {
"https://distrikt-ni-st.de",
"Freiheit, Gleichheit, Brüderlichkeit, Toleranz und Humanität",
))
mux.HandleFunc("GET /this-issue/", view.ShowCurrentArticles(db))
mux.HandleFunc("GET /unpublished-articles/", view.ShowUnpublishedArticles(db))
mux.HandleFunc("GET /write-article/", view.WriteArticle(db))

View File

@ -1,10 +1,7 @@
package model
import (
"context"
"database/sql"
"fmt"
"log"
"time"
)
@ -17,64 +14,26 @@ type Article struct {
Rejected bool
ID int64
AuthorID int64
IssueID int64
}
func (db *DB) AddArticle(a *Article) (int64, error) {
var id int64
txOptions := &sql.TxOptions{Isolation: sql.LevelSerializable}
selectQuery := "SELECT id FROM issues WHERE published = false"
insertQuery := `
query := `
INSERT INTO articles
(title, description, content, published, rejected, author_id, issue_id)
VALUES (?, ?, ?, ?, ?, ?, ?)
(title, description, content, published, rejected, author_id)
VALUES (?, ?, ?, ?, ?, ?)
`
for i := 0; i < TxMaxRetries; i++ {
id, err := func() (int64, error) {
tx, err := db.BeginTx(context.Background(), txOptions)
result, err := db.Exec(query, a.Title, a.Description, a.Content,
a.Published, a.Rejected, a.AuthorID)
if err != nil {
return 0, fmt.Errorf("error starting transaction: %v", err)
}
if err = tx.QueryRow(selectQuery).Scan(&id); err != nil {
if rollbackErr := tx.Rollback(); rollbackErr != nil {
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
}
return 0, fmt.Errorf("error getting issue ID when adding article to DB: %v", err)
}
result, err := tx.Exec(insertQuery, a.Title, a.Description,
a.Content, a.Published, a.Rejected, a.AuthorID, id)
if err != nil {
if rollbackErr := tx.Rollback(); rollbackErr != nil {
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
}
return 0, fmt.Errorf("error inserting article into DB: %v", err)
}
id, err := result.LastInsertId()
if err != nil {
if rollbackErr := tx.Rollback(); rollbackErr != nil {
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
}
return 0, fmt.Errorf("error retrieving ID of added article: %v", err)
return 0, fmt.Errorf("error retrieving last ID: %v", err)
}
if err = tx.Commit(); err != nil {
return 0, fmt.Errorf("error committing transaction when adding article to DB: %v", err)
}
return id, nil
}()
if err == nil {
return id, nil
}
log.Println(err)
wait(i)
}
return 0, fmt.Errorf("error: %v unsuccessful retries for DB operation, aborting", TxMaxRetries)
}
func (db *DB) GetArticle(id int64) (*Article, error) {
@ -120,8 +79,8 @@ func (db *DB) GetCertainArticles(published, rejected bool) ([]*Article, error) {
article := new(Article)
var created []byte
if err = rows.Scan(&article.ID, &article.Title, &created,
&article.Description, &article.Content, &article.AuthorID); err != nil {
if err = rows.Scan(&article.ID, &article.Title, &created, &article.Description,
&article.Content, &article.AuthorID); err != nil {
return nil, fmt.Errorf("error scanning article row: %v", err)
}
@ -136,122 +95,3 @@ func (db *DB) GetCertainArticles(published, rejected bool) ([]*Article, error) {
return articleList, nil
}
func (db *DB) GetCurrentIssueArticles() ([]*Article, error) {
var issueID int64
txOptions := &sql.TxOptions{Isolation: sql.LevelSerializable}
issueQuery := "SELECT id FROM issues WHERE published = false"
articlesQuery := `
SELECT id, title, created, description, content, author_id
FROM articles
WHERE issue_id = ? AND published = true
`
for i := 0; i < TxMaxRetries; i++ {
id, err := func() ([]*Article, error) {
tx, err := db.BeginTx(context.Background(), txOptions)
if err != nil {
return nil, fmt.Errorf("error starting transaction: %v", err)
}
row := tx.QueryRow(issueQuery)
if err := row.Scan(&issueID); err != nil {
if rollbackErr := tx.Rollback(); rollbackErr != nil {
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
}
return nil, fmt.Errorf("error querying DB for unpublished issue: %v", err)
}
rows, err := tx.Query(articlesQuery, issueID)
if err != nil {
if rollbackErr := tx.Rollback(); rollbackErr != nil {
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
}
return nil, fmt.Errorf("error querying DB for articles of issue %v: %v", issueID, err)
}
articleList := make([]*Article, 0)
for rows.Next() {
article := new(Article)
var created []byte
if err = rows.Scan(&article.ID, &article.Title, &created,
&article.Description, &article.Content, &article.AuthorID); err != nil {
if rollbackErr := tx.Rollback(); rollbackErr != nil {
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
}
return nil, fmt.Errorf("error scanning article from issue %v: %v", issueID, err)
}
article.Created, err = time.Parse("2006-01-02 15:04:05", string(created))
if err != nil {
if rollbackErr := tx.Rollback(); rollbackErr != nil {
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
}
return nil, fmt.Errorf("error parsing created: %v", err)
}
articleList = append(articleList, article)
}
if err = tx.Commit(); err != nil {
return nil, fmt.Errorf("error committing transaction when getting articles of issue %v: %v", issueID, err)
}
return articleList, nil
}()
if err == nil {
return id, nil
}
log.Println(err)
wait(i)
}
return nil, fmt.Errorf("error: %v unsuccessful retries for DB operation, aborting", TxMaxRetries)
}
func (db *DB) AddArticleToCurrentIssue(id int64) error {
var issueID int64
txOptions := &sql.TxOptions{Isolation: sql.LevelSerializable}
selectQuery := "SELECT id FROM issues WHERE published = false"
updateQuery := "UPDATE articles SET issue_id = ? WHERE id = ?"
for i := 0; i < TxMaxRetries; i++ {
err := func() error {
tx, err := db.BeginTx(context.Background(), txOptions)
if err != nil {
return fmt.Errorf("error starting transaction: %v", err)
}
if err = tx.QueryRow(selectQuery).Scan(&issueID); err != nil {
if rollbackErr := tx.Rollback(); rollbackErr != nil {
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
}
return fmt.Errorf("error scanning row: %v", err)
}
_, err = db.Exec(updateQuery, issueID, id)
if err != nil {
if rollbackErr := tx.Rollback(); rollbackErr != nil {
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
}
return fmt.Errorf("error updating issueID for article: %v", err)
}
if err = tx.Commit(); err != nil {
return fmt.Errorf("error committing transaction when getting articles of issue %v: %v", issueID, err)
}
return nil
}()
if err == nil {
return nil
}
log.Println(err)
wait(i)
}
return fmt.Errorf("error: %v unsuccessful retries for DB operation, aborting", TxMaxRetries)
}

View File

@ -3,14 +3,12 @@ package model
import (
"fmt"
"log"
"math"
"math/rand/v2"
"time"
)
func (db *DB) WriteArticleTags(articleID int64, tagIDs []int64) error {
query := `
INSERT INTO articles_tags (article_id, tag_id)
VALUES (?, ?)
`
for i := 0; i < TxMaxRetries; i++ {
err := func() error {
tx, err := db.Begin()
@ -19,9 +17,13 @@ func (db *DB) WriteArticleTags(articleID int64, tagIDs []int64) error {
}
for _, tagID := range tagIDs {
query := `
INSERT INTO articles_tags (article_id, tag_id)
VALUES (?, ?)
`
if _, err := tx.Exec(query, articleID, tagID); err != nil {
if rollbackErr := tx.Rollback(); rollbackErr != nil {
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
log.Fatalf("error: transaction error: %v, rollback error: %v", err, rollbackErr)
}
return fmt.Errorf("error inserting into articles_tags: %v", err)
}
@ -37,7 +39,9 @@ func (db *DB) WriteArticleTags(articleID int64, tagIDs []int64) error {
}
log.Println(err)
wait(i)
waitTime := time.Duration(math.Pow(2, float64(i))) * time.Second
jitter := time.Duration(rand.IntN(1000)) * time.Millisecond
time.Sleep(waitTime + jitter)
}
return fmt.Errorf("error: %v unsuccessful retries for DB operation, aborting", TxMaxRetries)
}
@ -66,29 +70,3 @@ func (db *DB) GetArticleTags(articleID int64) ([]*Tag, error) {
return tags, nil
}
func (db *DB) UpdateArticleTags(articleID int64, tagIDs []int64) error {
query := `
`
for i := 0; i < TxMaxRetries; i++ {
err := func() error {
tx, err := db.Begin()
if err != nil {
return fmt.Errorf("error starting transaction: %v", err)
}
if err = tx.Commit(); err != nil {
return fmt.Errorf("error committing transaction: %v", err)
}
return nil
}()
if err == nil {
return nil
}
log.Println(err)
wait(i)
}
return fmt.Errorf("error: %v unsuccessful retries for DB operation, aborting", TxMaxRetries)
}

View File

@ -16,14 +16,13 @@ import (
"golang.org/x/term"
)
var TxMaxRetries = 5
var TxMaxRetries = 3
type (
DB struct{ *sql.DB }
Tx struct{ *sql.Tx }
Attribute struct {
Value any
Value interface{}
Table string
AttName string
ID int64
@ -71,15 +70,9 @@ func getCredentials() (string, string, error) {
return user, pass, nil
}
func wait(iteration int) {
waitTime := time.Duration(math.Pow(2, float64(iteration))) * 100 * time.Millisecond
jitter := time.Duration(rand.IntN(int(waitTime)/2)) * time.Millisecond
time.Sleep(waitTime + jitter)
}
func OpenDB(dbName string) (*DB, error) {
var err error
db := DB{DB: new(sql.DB)}
db := DB{DB: &sql.DB{}}
cfg := mysql.NewConfig()
cfg.DBName = dbName
@ -115,7 +108,7 @@ func (db *DB) UpdateAttributes(a ...*Attribute) error {
`, attribute.Table, attribute.AttName)
if _, err := tx.Exec(query, attribute.Value, attribute.ID); err != nil {
if rollbackErr := tx.Rollback(); rollbackErr != nil {
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
log.Fatalf("error: transaction error: %v, rollback error: %v", err, rollbackErr)
}
return fmt.Errorf("error updating %v in DB: %v", attribute.AttName, err)
}
@ -131,7 +124,9 @@ func (db *DB) UpdateAttributes(a ...*Attribute) error {
}
log.Println(err)
wait(i)
waitTime := time.Duration(math.Pow(2, float64(i))) * time.Second
jitter := time.Duration(rand.IntN(1000)) * time.Millisecond
time.Sleep(waitTime + jitter)
}
return fmt.Errorf("error: %v unsuccessful retries for DB operation, aborting", TxMaxRetries)
}
@ -148,6 +143,31 @@ func (db *DB) CountEntries(table string) (int64, error) {
return count, nil
}
func (db *DB) StartTransaction() (*Tx, error) {
tx := &Tx{Tx: new(sql.Tx)}
var err error
tx.Tx, err = db.Begin()
if err != nil {
return nil, fmt.Errorf("error starting transaction: %v", err)
}
return tx, nil
}
func (tx *Tx) CommitTransaction() error {
if err := tx.Commit(); err != nil {
return fmt.Errorf("error committing transaction: %v", err)
}
return nil
}
func (tx *Tx) RollbackTransaction() {
if err := tx.Rollback(); err != nil {
log.Fatalf("error rolling back transaction: %v", err)
}
}
func (tx *Tx) UpdateAttributes(a ...*Attribute) error {
for _, attribute := range a {
query := fmt.Sprintf(`
@ -157,7 +177,7 @@ func (tx *Tx) UpdateAttributes(a ...*Attribute) error {
`, attribute.Table, attribute.AttName)
if _, err := tx.Exec(query, attribute.Value, attribute.ID); err != nil {
if rollbackErr := tx.Rollback(); rollbackErr != nil {
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
log.Fatalf("error: transaction error: %v, rollback error: %v", err, rollbackErr)
}
return fmt.Errorf("error updating %v in DB: %v", attribute.AttName, err)
}

View File

@ -1,66 +0,0 @@
package model
import (
"context"
"database/sql"
"fmt"
"log"
)
func (db *DB) AddIssue() (int64, error) {
query := "INSERT INTO issues (published) VALUES (?)"
result, err := db.Exec(query, false)
if err != nil {
return 0, fmt.Errorf("error inserting issue into DB: %v", err)
}
id, err := result.LastInsertId()
if err != nil {
return 0, fmt.Errorf("error getting ID of added issue: %v", err)
}
return id, nil
}
func (db *DB) PublishLatestIssue() error {
var id int64
txOptions := &sql.TxOptions{Isolation: sql.LevelSerializable}
updateQuery := "UPDATE issues SET published = true WHERE published = false"
insertQuery := "INSERT INTO issues (published) VALUES (?)"
for i := 0; i < TxMaxRetries; i++ {
err := func() error {
tx, err := db.BeginTx(context.Background(), txOptions)
if err != nil {
return fmt.Errorf("error starting transaction: %v", err)
}
if _, err := tx.Exec(updateQuery, id); err != nil {
if rollbackErr := tx.Rollback(); rollbackErr != nil {
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
}
return fmt.Errorf("error publishing issue: %v", err)
}
if _, err := tx.Exec(insertQuery, false); err != nil {
if rollbackErr := tx.Rollback(); rollbackErr != nil {
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
}
return fmt.Errorf("error inserting new issue into DB: %v", err)
}
if err = tx.Commit(); err != nil {
return fmt.Errorf("error committing transaction when publishing issue: %v", err)
}
return nil
}()
if err == nil {
return nil
}
log.Println(err)
wait(i)
}
return fmt.Errorf("error: %v unsuccessful retries for DB operation, aborting", TxMaxRetries)
}

View File

@ -3,15 +3,17 @@ package model
import (
"fmt"
"log"
"math"
"math/rand/v2"
"time"
"golang.org/x/crypto/bcrypt"
)
const (
Admin = iota
Publisher
Editor
Author
Writer
)
type User struct {
@ -22,26 +24,21 @@ type User struct {
Role int
}
func (db *DB) AddUser(u *User, pass string) (int64, error) {
func (db *DB) AddUser(user *User, pass string) error {
hashedPass, err := bcrypt.GenerateFromPassword([]byte(pass), bcrypt.DefaultCost)
if err != nil {
return 0, fmt.Errorf("error creating password hash: %v", err)
return fmt.Errorf("error creating password hash: %v", err)
}
query := `
INSERT INTO users (username, password, first_name, last_name, role)
VALUES (?, ?, ?, ?, ?)
`
result, err := db.Exec(query, u.UserName, string(hashedPass), u.FirstName, u.LastName, u.Role)
if err != nil {
return 0, fmt.Errorf("error inserting new user %v into DB: %v", u.UserName, err)
}
id, err := result.LastInsertId()
if err != nil {
return 0, fmt.Errorf("error inserting user into DB: %v", err)
if _, err = db.Exec(query, user.UserName, string(hashedPass), user.FirstName, user.LastName, user.Role); err != nil {
return fmt.Errorf("error inserting user into DB: %v", err)
}
return id, nil
return nil
}
func (db *DB) GetID(userName string) (int64, bool) {
@ -90,14 +87,14 @@ func (tx *Tx) ChangePassword(id int64, oldPass, newPass string) error {
row := tx.QueryRow(getQuery, id)
if err := row.Scan(&queriedPass); err != nil {
if rollbackErr := tx.Rollback(); rollbackErr != nil {
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
log.Fatalf("error: transaction error: %v, rollback error: %v", err, rollbackErr)
}
return fmt.Errorf("error reading password from DB: %v", err)
}
if err := bcrypt.CompareHashAndPassword([]byte(queriedPass), []byte(oldPass)); err != nil {
if rollbackErr := tx.Rollback(); rollbackErr != nil {
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
log.Fatalf("error: transaction error: %v, rollback error: %v", err, rollbackErr)
}
return fmt.Errorf("incorrect password: %v", err)
}
@ -105,7 +102,7 @@ func (tx *Tx) ChangePassword(id int64, oldPass, newPass string) error {
newHashedPass, err := bcrypt.GenerateFromPassword([]byte(newPass), bcrypt.DefaultCost)
if err != nil {
if rollbackErr := tx.Rollback(); rollbackErr != nil {
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
log.Fatalf("error: transaction error: %v, rollback error: %v", err, rollbackErr)
}
return fmt.Errorf("error creating password hash: %v", err)
}
@ -117,7 +114,7 @@ func (tx *Tx) ChangePassword(id int64, oldPass, newPass string) error {
`
if _, err = tx.Exec(setQuery, string(newHashedPass), id); err != nil {
if rollbackErr := tx.Rollback(); rollbackErr != nil {
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
log.Fatalf("error: transaction error: %v, rollback error: %v", err, rollbackErr)
}
return fmt.Errorf("error updating password in DB: %v", err)
}
@ -135,8 +132,7 @@ func (db *DB) GetUser(id int64) (*User, error) {
`
row := db.QueryRow(query, id)
if err := row.Scan(&user.ID, &user.UserName, &user.FirstName,
&user.LastName, &user.Role); err != nil {
if err := row.Scan(&user.ID, &user.UserName, &user.FirstName, &user.LastName, &user.Role); err != nil {
return nil, fmt.Errorf("error reading user information: %v", err)
}
@ -165,7 +161,7 @@ func (db *DB) UpdateUserAttributes(id int64, user, first, last, oldPass, newPass
if !passwordEmpty {
if err = tx.ChangePassword(id, oldPass, newPass); err != nil {
if rollbackErr := tx.Rollback(); rollbackErr != nil {
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
log.Fatalf("error: transaction error: %v, rollback error: %v", err, rollbackErr)
}
return fmt.Errorf("error changing password: %v", err)
}
@ -177,7 +173,7 @@ func (db *DB) UpdateUserAttributes(id int64, user, first, last, oldPass, newPass
&Attribute{Table: "users", ID: id, AttName: "last_name", Value: last},
); err != nil {
if rollbackErr := tx.Rollback(); rollbackErr != nil {
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
log.Fatalf("error: transaction error: %v, rollback error: %v", err, rollbackErr)
}
return fmt.Errorf("error updating attributes in DB: %v", err)
}
@ -193,7 +189,9 @@ func (db *DB) UpdateUserAttributes(id int64, user, first, last, oldPass, newPass
}
log.Println(err)
wait(i)
waitTime := time.Duration(math.Pow(2, float64(i))) * time.Second
jitter := time.Duration(rand.IntN(1000)) * time.Millisecond
time.Sleep(waitTime + jitter)
}
return fmt.Errorf("error: %v unsuccessful retries for DB operation, aborting", TxMaxRetries)

View File

@ -272,12 +272,6 @@ func PublishArticle(db *model.DB, s *control.CookieStore) http.HandlerFunc {
template.Must(tmpl, err).ExecuteTemplate(w, "page-content", msg)
}
if err = db.AddArticleToCurrentIssue(id); err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err = db.UpdateAttributes(
&model.Attribute{Table: "articles", ID: id, AttName: "published", Value: true},
&model.Attribute{Table: "articles", ID: id, AttName: "rejected", Value: false},
@ -323,30 +317,3 @@ func RejectArticle(db *model.DB, s *control.CookieStore) http.HandlerFunc {
tmpl.ExecuteTemplate(w, "page-content", session.Values["role"])
}
}
func ShowCurrentArticles(db *model.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
articles, err := db.GetCurrentIssueArticles()
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
tmpl, err := template.ParseFiles("web/templates/current-articles.html")
template.Must(tmpl, err).ExecuteTemplate(w, "page-content", articles)
}
}
func PublishLatestIssue(db *model.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if err := db.PublishLatestIssue(); err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
tmpl, err := template.ParseFiles("web/templates/hub.html")
template.Must(tmpl, err).ExecuteTemplate(w, "page-content", nil)
}
}

View File

@ -1,7 +1,6 @@
package view
import (
"fmt"
"html/template"
"log"
"net/http"
@ -39,7 +38,6 @@ func ShowRSS(db *model.DB, title, link, desc string) http.HandlerFunc {
for _, tag := range tags {
tagNames = append(tagNames, tag.Name)
}
tagNames = append(tagNames, fmt.Sprint("Orient Express ", article.IssueID))
user, err := db.GetUser(article.AuthorID)
if err != nil {

View File

@ -86,21 +86,19 @@ func AddUser(db *model.DB, s *control.CookieStore) http.HandlerFunc {
return
}
htmlData.ID, err = db.AddUser(htmlData.User, pass)
num, err := db.CountEntries("users")
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if htmlData.ID == 1 {
if num == 0 {
if htmlData.Role != model.Admin {
htmlData.Msg = "Der erste Benutzer muss ein Administrator sein."
htmlData.Role = model.Admin
if err = db.UpdateAttributes(
&model.Attribute{Table: "users", ID: id, AttName: "role", Value: htmlData.Role},
); err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
tmpl, err := template.ParseFiles("web/templates/add-user.html")
tmpl = template.Must(tmpl, err)
tmpl.ExecuteTemplate(w, "page-content", htmlData)
return
}
@ -109,13 +107,13 @@ func AddUser(db *model.DB, s *control.CookieStore) http.HandlerFunc {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
if _, err := db.AddIssue(); err != nil {
if err := db.AddUser(htmlData.User, pass); err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
tmpl, err := template.ParseFiles("web/templates/hub.html")
template.Must(tmpl, err).ExecuteTemplate(w, "page-content", 0)

View File

@ -8,12 +8,10 @@
<input required name="first-name" placeholder="Vorname" type="text" value="{{.FirstName}}" />
<input required name="last-name" placeholder="Nachname" type="text" value="{{.LastName}}" />
<input required id="author" name="role" type="radio" value="3" {{if eq .Role 3 }}checked{{end}} />
<label for="author">Autor</label>
<input required id="editor" name="role" type="radio" value="2" {{if eq .Role 2 }}checked{{end}} />
<input required id="writer" name="role" type="radio" value="2" {{if eq .Role 2 }}checked{{end}} />
<label for="writer">Schreiber</label>
<input required id="editor" name="role" type="radio" value="1" {{if eq .Role 1 }}checked{{end}} />
<label for="editor">Redakteur</label>
<input required id="publisher" name="role" type="radio" value="1" {{if eq .Role 1 }}checked{{end}} />
<label for="publisher">Herausgeber</label>
<input required id="admin" name="role" type="radio" value="0" {{if eq .Role 0 }}checked{{end}} />
<label for="admin">Admin</label>

View File

@ -1,10 +0,0 @@
{{define "page-content"}}
{{range .}}
<div>
<h1>{{.Title}}</h1>
<p>{{.Description}}</p>
</div>
{{end}}
<button hx-get="/publish-issue/" hx-target="#page-content">Ausgabe publizieren</button>
<button hx-get="/hub/" hx-target="#page-content">Abbrechen</button>
{{end}}

View File

@ -4,13 +4,10 @@
<button hx-get="/rejected-articles/" hx-target="#page-content">Abgelehnte Artikel</button>
<button hx-get="/rss/" hx-target="#page-content">RSS Feed</button>
<button hx-get="/edit-user/" hx-target="#page-content">Benutzer bearbeiten</button>
{{if lt . 3}}
{{if lt . 2}}
<button hx-get="/unpublished-articles/" hx-target="#page-content">Unveröffentlichte Artikel</button>
<button hx-get="/create-tag/" hx-target="#page-content">Neuer Tag</button>
{{end}}
{{if lt . 2}}
<button hx-get="/this-issue/" hx-target="#page-content">Diese Ausgabe</button>
{{end}}
{{if eq . 0}}
<button hx-get="/create-user/" hx-target="#page-content">Benutzer hinzufügen</button>
{{end}}