Compare commits

...

26 Commits

Author SHA1 Message Date
f6dedc6f10 Merge branch 'devel' 2024-08-31 01:43:53 +02:00
edb448413b Remove deleted article file when deleting article 2024-08-31 01:42:37 +02:00
cdf0a49550 Merge branch 'devel' 2024-08-31 01:38:28 +02:00
222b791e90 Delete Ports from article links in articles.go and issues.go 2024-08-31 01:35:39 +02:00
c3c0650210 Merge branch 'devel' 2024-08-31 01:00:55 +02:00
bc58b1be44 Fixed small bug in ServeArticle where it was looking at the link to read the file 2024-08-31 01:00:39 +02:00
ab6b9b9a4f Add port to url of UploadArticleImage() 2024-08-31 00:52:10 +02:00
91ef195a56 Check if article has been published before adding it to RSS feed 2024-08-31 00:49:35 +02:00
d077f700d8 Merge branch 'devel' 2024-08-31 00:36:48 +02:00
e4589f3b84 Remove unnecessary mv from update.sh 2024-08-31 00:35:07 +02:00
cd67fe6df3 Delete unnecessary http:// from articles.go and issues.go 2024-08-31 00:22:32 +02:00
ec752b1c66 Merge branch 'devel' 2024-08-30 23:43:12 +02:00
4663cedec5 Store article content outside of DB and convert and serve on request from respective file 2024-08-30 23:43:01 +02:00
46aef4f12f Merge branch 'devel' 2024-08-25 10:51:35 +02:00
1b29e328cf Merge branch 'devel' 2024-08-25 06:38:55 +02:00
e50cb819f3 Merge branch 'devel' 2024-08-23 21:45:30 +02:00
c32e38ca10 Merge branch 'devel' 2024-08-23 20:57:11 +02:00
d7c8c7a43a Merge branch 'devel' 2024-08-18 17:31:00 +02:00
1cd3edc90c Merge branch 'devel' 2024-08-18 12:06:29 +02:00
0e768c9f61 Merge branch 'devel' 2024-08-08 21:27:07 +02:00
1fcd775cc5 Merge branch 'devel' 2024-08-08 21:14:24 +02:00
203a1ed147 Implemented EasyMDE 2024-08-08 21:13:25 +02:00
ef1914ee5c Implemented article preview 2024-08-08 21:13:25 +02:00
084b101e31 Register f.ArticlePreviewHtmlData in init() 2024-08-08 21:13:25 +02:00
b2db128aa9 Shorten lines by referencing frontend as f and backend as b 2024-08-08 21:13:25 +02:00
081e880fb6 Change structure of code tor frontend and backend one 2024-08-08 21:13:25 +02:00
10 changed files with 160 additions and 107 deletions

View File

@ -12,8 +12,10 @@ type Article struct {
Title string
Created time.Time
Description string
Content string
Link string
EncURL string
EncLength int
EncType string
Published bool
Rejected bool
ID int64
@ -27,8 +29,8 @@ func (db *DB) AddArticle(a *Article) (int64, error) {
selectQuery := "SELECT id FROM issues WHERE published = false"
insertQuery := `
INSERT INTO articles
(title, description, content, link, published, rejected, author_id, issue_id)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
(title, description, link, enc_url, enc_length, enc_type, published, rejected, author_id, issue_id)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`
for i := 0; i < TxMaxRetries; i++ {
@ -45,8 +47,8 @@ func (db *DB) AddArticle(a *Article) (int64, error) {
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.Link, a.Published, a.Rejected, a.AuthorID, id)
result, err := tx.Exec(insertQuery, a.Title, a.Description, a.Link,
a.EncURL, a.EncLength, a.EncType, 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)
@ -80,7 +82,7 @@ func (db *DB) AddArticle(a *Article) (int64, error) {
func (db *DB) GetArticle(id int64) (*Article, error) {
query := `
SELECT title, created, description, content, link, published, author_id
SELECT title, created, description, link, enc_url, enc_length, enc_type, published, author_id
FROM articles
WHERE id = ?
`
@ -91,7 +93,8 @@ func (db *DB) GetArticle(id int64) (*Article, error) {
var err error
if err := row.Scan(&article.Title, &created, &article.Description,
&article.Content, &article.Link, &article.Published, &article.AuthorID); err != nil {
&article.Link, &article.EncURL, &article.EncLength, &article.EncType,
&article.Published, &article.AuthorID); err != nil {
return nil, fmt.Errorf("error scanning article row: %v", err)
}
@ -106,7 +109,7 @@ func (db *DB) GetArticle(id int64) (*Article, error) {
func (db *DB) GetCertainArticles(published, rejected bool) ([]*Article, error) {
query := `
SELECT id, title, created, description, content, link, author_id, issue_id
SELECT id, title, created, description, link, enc_url, enc_length, enc_type, author_id, issue_id
FROM articles
WHERE published = ?
AND rejected = ?
@ -122,8 +125,8 @@ func (db *DB) GetCertainArticles(published, rejected bool) ([]*Article, error) {
var created []byte
if err = rows.Scan(&article.ID, &article.Title, &created,
&article.Description, &article.Content, &article.Link,
&article.AuthorID, &article.IssueID); err != nil {
&article.Description, &article.Link, &article.EncURL, &article.EncLength,
&article.EncType, &article.AuthorID, &article.IssueID); err != nil {
return nil, fmt.Errorf("error scanning article row: %v", err)
}
@ -144,7 +147,7 @@ func (db *DB) GetCurrentIssueArticles() ([]*Article, error) {
txOptions := &sql.TxOptions{Isolation: sql.LevelSerializable}
issueQuery := "SELECT id FROM issues WHERE published = false"
articlesQuery := `
SELECT id, title, created, description, content, link, author_id
SELECT id, title, created, description, link, enc_url, enc_length, enc_type, author_id
FROM articles
WHERE issue_id = ? AND published = true
`
@ -178,7 +181,8 @@ func (db *DB) GetCurrentIssueArticles() ([]*Article, error) {
var created []byte
if err = rows.Scan(&article.ID, &article.Title, &created,
&article.Description, &article.Content, &article.Link, &article.AuthorID); err != nil {
&article.Description, &article.Link, &article.EncURL, &article.EncLength,
&article.EncType, &article.AuthorID); err != nil {
if rollbackErr := tx.Rollback(); rollbackErr != nil {
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
}

View File

@ -9,47 +9,6 @@ import (
"git.streifling.com/jason/rss"
)
func GetChannel(db *DB, title, link, description string) (*rss.Channel, error) {
channel := &rss.Channel{
Title: title,
Link: link,
Description: description,
Items: make([]*rss.Item, 0),
}
articles, err := db.GetCertainArticles(true, false)
if err != nil {
return nil, fmt.Errorf("error fetching published articles: %v", err)
}
for _, article := range articles {
tags, err := db.GetArticleTags(article.ID)
if err != nil {
return nil, fmt.Errorf("error fetching tags for article %v: %v", article.Title, err)
}
tagNames := make([]string, 0)
for _, tag := range tags {
tagNames = append(tagNames, tag.Name)
}
user, err := db.GetUser(article.AuthorID)
if err != nil {
return nil, fmt.Errorf("error finding user %v: %v", article.AuthorID, err)
}
channel.Items = append(channel.Items, &rss.Item{
Title: article.Title,
Author: fmt.Sprint(user.FirstName, " ", user.LastName),
PubDate: article.Created.Format(time.RFC1123Z),
Description: article.Description,
Content: &rss.Content{Value: article.Content},
Categories: tagNames,
})
}
return channel, nil
}
func GenerateRSS(c *Config, db *DB) (*string, error) {
channel := &rss.Channel{
Title: c.Title,
@ -64,6 +23,10 @@ func GenerateRSS(c *Config, db *DB) (*string, error) {
}
for _, article := range articles {
if !article.Published {
continue
}
tags, err := db.GetArticleTags(article.ID)
if err != nil {
return nil, fmt.Errorf("error getting tags for articles for RSS feed: %v", err)
@ -93,15 +56,19 @@ func GenerateRSS(c *Config, db *DB) (*string, error) {
Author: fmt.Sprint(user.FirstName, " ", user.LastName),
Categories: tagNames,
Description: articleDescription,
Guid: string(article.ID),
Link: article.Link,
PubDate: article.Created.Format(time.RFC1123Z),
Title: articleTitle,
}
fmt.Println(article.Link, ": ", len(article.Link))
if article.Link == "" {
item.Link = fmt.Sprint("http://", c.Domain, "/article/serve/", article.ID, ".html")
} else {
item.Link = article.Link
if article.Title == "Autogenerated cpolis Issue Article" {
item.Enclosure = &rss.Enclosure{
Url: article.EncURL,
Lenght: article.EncLength,
Type: article.EncType,
}
}
channel.Items = append(channel.Items, item)

View File

@ -4,6 +4,7 @@ import (
"fmt"
"log"
"net/http"
"os"
"strconv"
b "streifling.com/jason/cpolis/cmd/backend"
@ -30,6 +31,25 @@ func ServeArticle(c *b.Config, db *b.DB) http.HandlerFunc {
return
}
fmt.Fprint(w, article.Content)
if !article.Published {
return
}
articleAbsName := fmt.Sprint(c.ArticleDir, "/", article.ID, ".md")
contentBytes, err := os.ReadFile(articleAbsName)
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
content, err := b.ConvertToHTML(string(contentBytes))
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
fmt.Fprint(w, content)
}
}

View File

@ -75,7 +75,6 @@ func SubmitArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
article := &b.Article{
Title: r.PostFormValue("article-title"),
Description: r.PostFormValue("article-description"),
Content: r.PostFormValue("article-content"),
Published: false,
Rejected: false,
AuthorID: session.Values["id"].(int64),
@ -88,6 +87,20 @@ func SubmitArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
return
}
articleAbsName := fmt.Sprint(c.ArticleDir, "/", article.ID, ".md")
if err = os.WriteFile(articleAbsName, []byte(r.PostFormValue("article-content")), 0644); err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
article.Link = fmt.Sprint(c.Domain, "/article/serve/", article.ID)
if err = db.UpdateAttributes(&b.Attribute{Table: "articles", ID: article.ID, AttName: "link", Value: article.Link}); err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
r.ParseForm()
tags := make([]int64, 0)
for _, tag := range r.Form["tags"] {
@ -129,10 +142,16 @@ func ResubmitArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
description := r.PostFormValue("article-description")
content := r.PostFormValue("article-content")
link := fmt.Sprint(c.ArticleDir, "/", id, ".md")
if err = os.WriteFile(link, []byte(content), 0644); err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err = db.UpdateAttributes(
&b.Attribute{Table: "articles", ID: id, AttName: "title", Value: title},
&b.Attribute{Table: "articles", ID: id, AttName: "description", Value: description},
&b.Attribute{Table: "articles", ID: id, AttName: "content", Value: content},
&b.Attribute{Table: "articles", ID: id, AttName: "rejected", Value: false},
); err != nil {
log.Println(err)
@ -260,7 +279,15 @@ func ReviewUnpublishedArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.Hand
return
}
content, err := b.ConvertToHTML(article.Content)
articleAbsName := fmt.Sprint(c.ArticleDir, "/", article.ID, ".md")
contentBytes, err := os.ReadFile(articleAbsName)
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
content, err := b.ConvertToHTML(string(contentBytes))
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
@ -290,6 +317,7 @@ func ReviewRejectedArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.Handler
type htmlData struct {
Selected map[int64]bool
Article *b.Article
Content string
Tags []*b.Tag
}
data := new(htmlData)
@ -308,6 +336,16 @@ func ReviewRejectedArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.Handler
return
}
articleAbsName := fmt.Sprint(c.ArticleDir, "/", data.Article.ID, ".md")
contentBytes, err := os.ReadFile(articleAbsName)
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
data.Content = string(contentBytes)
data.Tags, err = db.GetTagList()
if err != nil {
log.Println(err)
@ -346,27 +384,6 @@ func PublishArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
return
}
article, err := db.GetArticle(id)
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
content, err := b.ConvertToHTML(article.Content)
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
err = os.WriteFile(fmt.Sprint(c.ArticleDir, "/", article.ID, ".html"), []byte(content), 0444)
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err = db.AddArticleToCurrentIssue(id); err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
@ -555,7 +572,14 @@ func ReviewArticleForDeletion(c *b.Config, db *b.DB, s *b.CookieStore) http.Hand
return
}
content, err := b.ConvertToHTML(article.Content)
contentBytes, err := os.ReadFile(article.Link)
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
content, err := b.ConvertToHTML(string(contentBytes))
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
@ -596,6 +620,12 @@ func DeleteArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
return
}
if err = os.Remove(fmt.Sprint(c.ArticleDir, "/", id, ".md")); err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
feed, err := b.GenerateRSS(c, db)
if err != nil {
log.Println(err)

View File

@ -5,6 +5,7 @@ import (
"html/template"
"io"
"log"
"mime"
"net/http"
"os"
"path/filepath"
@ -36,10 +37,32 @@ func PublishLatestIssue(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFun
return
}
imgFileName := session.Values["issue-image"].(string)
imgAbsName := fmt.Sprint(c.PicsDir, "/", imgFileName)
imgFile, err := os.Open(imgAbsName)
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer imgFile.Close()
imgInfo, err := imgFile.Stat()
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
imgSize := imgInfo.Size()
mimeType := mime.TypeByExtension(filepath.Ext(imgAbsName))
article := &b.Article{
Title: "Autogenerated Issue Article",
Content: r.PostFormValue("issue-content"),
Link: session.Values["issue-image"].(string),
Title: "Autogenerated cpolis Issue Article",
EncURL: fmt.Sprint(c.Domain, "/image/serve/", imgFileName),
EncLength: int(imgSize),
EncType: mimeType,
Published: true,
Rejected: false,
Created: time.Now(),
@ -47,20 +70,6 @@ func PublishLatestIssue(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFun
}
fmt.Println(article.Link)
content, err := b.ConvertToHTML(article.Content)
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
err = os.WriteFile(fmt.Sprint(c.ArticleDir, "/", article.ID, ".html"), []byte(content), 0444)
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
article.ID, err = db.AddArticle(article)
if err != nil {
log.Println(err)
@ -68,6 +77,26 @@ func PublishLatestIssue(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFun
return
}
articleAbsName := fmt.Sprint(c.ArticleDir, "/", article.ID, ".md")
if err = os.WriteFile(articleAbsName, []byte(r.PostFormValue("article-content")), 0644); err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
article.Link = fmt.Sprint(c.Domain, "/article/serve/", article.ID)
if err = db.UpdateAttributes(&b.Attribute{Table: "articles", ID: article.ID, AttName: "link", Value: article.Link}); err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err = db.UpdateAttributes(&b.Attribute{Table: "articles", ID: article.ID, AttName: "link", Value: article.Link}); err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err = db.AddArticleToCurrentIssue(article.ID); err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)

View File

@ -26,6 +26,9 @@ CREATE TABLE articles (
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
description TEXT NOT NULL,
link VARCHAR(255),
enc_url VARCHAR(255),
enc_length INT,
enc_type VARCHAR(255),
published BOOL NOT NULL,
rejected BOOL NOT NULL,
author_id INT NOT NULL,

2
go.mod
View File

@ -4,7 +4,7 @@ go 1.22.0
require (
firebase.google.com/go/v4 v4.14.1
git.streifling.com/jason/rss v0.1.2
git.streifling.com/jason/rss v0.1.3
github.com/BurntSushi/toml v1.3.2
github.com/go-sql-driver/mysql v1.7.1
github.com/google/uuid v1.6.0

4
go.sum
View File

@ -15,8 +15,8 @@ cloud.google.com/go/storage v1.40.0 h1:VEpDQV5CJxFmJ6ueWNsKxcr1QAYOXEgxDa+sBbJah
cloud.google.com/go/storage v1.40.0/go.mod h1:Rrj7/hKlG87BLqDJYtwR0fbPld8uJPbQ2ucUMY7Ir0g=
firebase.google.com/go/v4 v4.14.1 h1:4qiUETaFRWoFGE1XP5VbcEdtPX93Qs+8B/7KvP2825g=
firebase.google.com/go/v4 v4.14.1/go.mod h1:fgk2XshgNDEKaioKco+AouiegSI9oTWVqRaBdTTGBoM=
git.streifling.com/jason/rss v0.1.2 h1:UB3UHJXMt5WDDh9y8n0Z6nS1XortbPXjEr7QZTdovY4=
git.streifling.com/jason/rss v0.1.2/go.mod h1:gpZF0nZbQSstMpyHD9DTAvlQEG7v4pjO5c7aIMWM4Jg=
git.streifling.com/jason/rss v0.1.3 h1:fd3j4ZtcLehapcmmroo3AP3X34gRHC4xzpfV6bDV1ZU=
git.streifling.com/jason/rss v0.1.3/go.mod h1:gpZF0nZbQSstMpyHD9DTAvlQEG7v4pjO5c7aIMWM4Jg=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=

View File

@ -43,13 +43,13 @@ chmod +x $CPOLIS_DIR/tailwindcss
$CPOLIS_DIR/tailwindcss -i $CPOLIS_DIR/web/static/css/input.css -o $CPOLIS_DIR/web/static/css/style.css
echo '\nBuilding cpolis...' >&2
go build -o $TMP_DIR/cpolis $CPOLIS_DIR/cmd/main.go
go build -o $BIN_DIR/cpolis $CPOLIS_DIR/cmd/main.go
echo '\nSetting system files up...' >&2
sudo mv $TMP_DIR/cpolis $BIN_DIR/cpolis
echo '\nSetting up system files...' >&2
sudo chown root:root $BIN_DIR/cpolis
chmod +x $BIN_DIR/cpolis
echo '\nSetting up service...' >&2
sudo mv $CPOLIS_DIR/cpolis.service $SYSTEMD_DIR
sudo chown root:root $SYSTEMD_DIR/cpolis.service
sudo systemctl daemon-reload

View File

@ -14,7 +14,7 @@
<div class="flex flex-col gap-y-1">
<label for="easyMDE">Artikel</label>
<textarea id="easyMDE">{{.Article.Content}}</textarea>
<textarea id="easyMDE">{{.Content}}</textarea>
<input id="article-content" name="article-content" type="hidden" />
</div>