cpolis/cmd/backend/articles.go

292 lines
8.7 KiB
Go
Raw Normal View History

package backend
2024-03-09 11:06:03 +01:00
import (
2024-03-28 07:00:37 +01:00
"context"
"database/sql"
2024-03-09 11:06:03 +01:00
"fmt"
2024-03-28 07:00:37 +01:00
"log"
2024-03-09 11:06:03 +01:00
"time"
)
2024-03-11 21:08:27 +01:00
type Article struct {
Created time.Time
2024-09-10 20:08:13 +02:00
Title string
Description string
Link string
EncURL string
EncType string
ID int64
AuthorID int64
IssueID int64
2024-09-28 12:17:03 +02:00
EditedID int64
2024-09-10 20:08:13 +02:00
EncLength int
Published bool
Rejected bool
IsInIssue bool
AutoGenerated bool
2024-03-11 21:08:27 +01:00
}
2024-03-09 11:06:03 +01:00
func (db *DB) AddArticle(a *Article) (int64, error) {
2024-03-28 07:00:37 +01:00
var id int64
txOptions := &sql.TxOptions{Isolation: sql.LevelSerializable}
selectQuery := "SELECT id FROM issues WHERE published = false"
insertQuery := `
2024-03-09 11:06:03 +01:00
INSERT INTO articles
2024-09-28 12:17:03 +02:00
(title, description, link, enc_url, enc_length, enc_type, published,
rejected, author_id, issue_id, edited_id, is_in_issue, auto_generated)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2024-03-09 11:06:03 +01:00
`
2024-03-28 07:00:37 +01:00
for i := 0; i < TxMaxRetries; i++ {
id, err := func() (int64, error) {
tx, err := db.BeginTx(context.Background(), txOptions)
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.Link,
a.EncURL, a.EncLength, a.EncType, a.Published, a.Rejected, a.AuthorID, id,
2024-09-28 12:17:03 +02:00
a.EditedID, a.IsInIssue, a.AutoGenerated)
2024-03-28 07:00:37 +01:00
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)
}
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)
2024-03-09 11:06:03 +01:00
}
2024-03-28 07:00:37 +01:00
return 0, fmt.Errorf("error: %v unsuccessful retries for DB operation, aborting", TxMaxRetries)
2024-03-09 11:06:03 +01:00
}
func (db *DB) GetArticle(id int64) (*Article, error) {
query := `
2024-09-28 12:17:03 +02:00
SELECT
title, created, description, link, enc_url, enc_length, enc_type,
published, author_id, issue_id, edited_id, is_in_issue, auto_generated
2024-03-09 11:06:03 +01:00
FROM articles
WHERE id = ?
`
row := db.QueryRow(query, id)
article := new(Article)
var created []byte
var err error
if err := row.Scan(&article.Title, &created, &article.Description,
&article.Link, &article.EncURL, &article.EncLength, &article.EncType,
2024-09-28 12:17:03 +02:00
&article.Published, &article.AuthorID, &article.IssueID, &article.EditedID,
&article.IsInIssue, &article.AutoGenerated); err != nil {
2024-03-09 11:06:03 +01:00
return nil, fmt.Errorf("error scanning article row: %v", err)
}
article.ID = id
article.Created, err = time.Parse("2006-01-02 15:04:05", string(created))
if err != nil {
return nil, fmt.Errorf("error parsing created: %v", err)
}
return article, nil
}
2024-09-28 12:17:03 +02:00
func (db *DB) GetCertainArticles(attribute string, value bool) ([]*Article, error) {
query := fmt.Sprintf(`
SELECT
id, title, created, description, link, enc_url, enc_length, enc_type,
author_id, issue_id, published, rejected, is_in_issue, auto_generated
2024-03-09 11:06:03 +01:00
FROM articles
2024-09-28 12:17:03 +02:00
WHERE %s = ?
`, attribute)
rows, err := db.Query(query, value)
2024-03-09 11:06:03 +01:00
if err != nil {
return nil, fmt.Errorf("error querying articles: %v", err)
}
articleList := make([]*Article, 0)
for rows.Next() {
article := new(Article)
var created []byte
2024-03-28 07:00:37 +01:00
if err = rows.Scan(&article.ID, &article.Title, &created,
&article.Description, &article.Link, &article.EncURL, &article.EncLength,
2024-09-28 12:17:03 +02:00
&article.EncType, &article.AuthorID, &article.IssueID, &article.Published,
&article.Rejected, &article.IsInIssue, &article.AutoGenerated); err != nil {
2024-03-09 11:06:03 +01:00
return nil, fmt.Errorf("error scanning article row: %v", err)
}
article.Created, err = time.Parse("2006-01-02 15:04:05", string(created))
if err != nil {
return nil, fmt.Errorf("error parsing created: %v", err)
}
articleList = append(articleList, article)
}
return articleList, nil
}
2024-03-28 07:00:37 +01:00
func (db *DB) GetCurrentIssueArticles() ([]*Article, error) {
var issueID int64
txOptions := &sql.TxOptions{Isolation: sql.LevelSerializable}
issueQuery := "SELECT id FROM issues WHERE published = false"
articlesQuery := `
2024-09-28 12:17:03 +02:00
SELECT
id, title, created, description, link, enc_url, enc_length, enc_type,
author_id, auto_generated
2024-03-28 07:00:37 +01:00
FROM articles
WHERE issue_id = ? AND published = true AND is_in_issue = true
2024-03-28 07:00:37 +01:00
`
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.Link, &article.EncURL, &article.EncLength,
&article.EncType, &article.AuthorID, &article.AutoGenerated); err != nil {
2024-03-28 07:00:37 +01:00
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)
}
2024-08-25 06:35:15 +02:00
func (db *DB) DeleteArticle(id int64) error {
articlesTagsQuery := "DELETE FROM articles_tags WHERE article_id = ?"
2024-09-13 05:12:57 +02:00
articlesQuery := "DELETE FROM articles WHERE id = ?"
2024-08-25 06:35:15 +02:00
_, err := db.Exec(articlesTagsQuery, id)
if err != nil {
return fmt.Errorf("error deleting article %v from DB: %v", id, err)
}
_, err = db.Exec(articlesQuery, id)
if err != nil {
return fmt.Errorf("error deleting article %v from DB: %v", id, err)
}
return nil
}