Compare commits
	
		
			30 Commits
		
	
	
		
			v0.9.1
			...
			bc4d8fa37e
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| bc4d8fa37e | |||
| 1368593c75 | |||
| e4bef7006c | |||
| afa1b65563 | |||
| a9bef63174 | |||
| 4d944ef65a | |||
| d2b21e7405 | |||
| 4bd255a7c4 | |||
| 2743899b65 | |||
| 065ffcdc30 | |||
| 38ef7b80d5 | |||
| e3c192359f | |||
| c777a77824 | |||
| 46532e4c85 | |||
| c183043dac | |||
| c722135a56 | |||
| 391b3bf157 | |||
| 0aa479763d | |||
| ff0e229f03 | |||
| 8ef6ff729e | |||
| e4624b8705 | |||
| 887fa863bc | |||
| 4592bdf970 | |||
| dadd610b2d | |||
| 74d71cfb6a | |||
| 4004bcb8f0 | |||
| 9e0182ed03 | |||
| e554174c28 | |||
| 8597f1b849 | |||
| b04e0e5e81 | 
| @@ -14,6 +14,7 @@ args_bin = [ | ||||
|     "-log tmp/cpolis.log", | ||||
|     "-pdfs tmp/pdfs", | ||||
|     "-pics tmp/pics", | ||||
|     "-port 8080", | ||||
|     "-rss tmp/orientexpress_alle.rss", | ||||
|     "-title 'Freimaurer Distrikt Niedersachsen und Sachsen-Anhalt'", | ||||
|     "-web web", | ||||
|   | ||||
| @@ -9,18 +9,19 @@ import ( | ||||
| ) | ||||
|  | ||||
| type Article struct { | ||||
| 	Title         string | ||||
| 	Created       time.Time | ||||
| 	Title         string | ||||
| 	Description   string | ||||
| 	Link          string | ||||
| 	EncURL        string | ||||
| 	EncLength     int | ||||
| 	EncType       string | ||||
| 	Published     bool | ||||
| 	Rejected      bool | ||||
| 	ID            int64 | ||||
| 	AuthorID      int64 | ||||
| 	IssueID       int64 | ||||
| 	EditedID      int64 | ||||
| 	EncLength     int | ||||
| 	Published     bool | ||||
| 	Rejected      bool | ||||
| 	IsInIssue     bool | ||||
| 	AutoGenerated bool | ||||
| } | ||||
| @@ -31,8 +32,9 @@ func (db *DB) AddArticle(a *Article) (int64, error) { | ||||
| 	selectQuery := "SELECT id FROM issues WHERE published = false" | ||||
| 	insertQuery := ` | ||||
|     INSERT INTO articles | ||||
|         (title, description, link, enc_url, enc_length, enc_type, published, rejected, author_id, issue_id, is_in_issue, auto_generated) | ||||
|     VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) | ||||
|         (title, description, link, enc_url, enc_length, enc_type, published, | ||||
|         rejected, author_id, issue_id, edited_id, is_in_issue, auto_generated) | ||||
|     VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) | ||||
|     ` | ||||
|  | ||||
| 	for i := 0; i < TxMaxRetries; i++ { | ||||
| @@ -51,7 +53,7 @@ func (db *DB) AddArticle(a *Article) (int64, error) { | ||||
|  | ||||
| 			result, err := tx.Exec(insertQuery, a.Title, a.Description, a.Link, | ||||
| 				a.EncURL, a.EncLength, a.EncType, a.Published, a.Rejected, a.AuthorID, id, | ||||
| 				a.IsInIssue, a.AutoGenerated) | ||||
| 				a.EditedID, a.IsInIssue, a.AutoGenerated) | ||||
| 			if err != nil { | ||||
| 				if rollbackErr := tx.Rollback(); rollbackErr != nil { | ||||
| 					log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr) | ||||
| @@ -85,7 +87,9 @@ func (db *DB) AddArticle(a *Article) (int64, error) { | ||||
|  | ||||
| func (db *DB) GetArticle(id int64) (*Article, error) { | ||||
| 	query := ` | ||||
|     SELECT title, created, description, link, enc_url, enc_length, enc_type, published, author_id, issue_id, is_in_issue, auto_generated | ||||
|     SELECT | ||||
|         title, created, description, link, enc_url, enc_length, enc_type, | ||||
|         published, author_id, issue_id, edited_id, is_in_issue, auto_generated | ||||
|     FROM articles | ||||
|     WHERE id = ? | ||||
|     ` | ||||
| @@ -97,7 +101,7 @@ func (db *DB) GetArticle(id int64) (*Article, error) { | ||||
|  | ||||
| 	if err := row.Scan(&article.Title, &created, &article.Description, | ||||
| 		&article.Link, &article.EncURL, &article.EncLength, &article.EncType, | ||||
| 		&article.Published, &article.AuthorID, &article.IssueID, | ||||
| 		&article.Published, &article.AuthorID, &article.IssueID, &article.EditedID, | ||||
| 		&article.IsInIssue, &article.AutoGenerated); err != nil { | ||||
| 		return nil, fmt.Errorf("error scanning article row: %v", err) | ||||
| 	} | ||||
| @@ -111,14 +115,15 @@ func (db *DB) GetArticle(id int64) (*Article, error) { | ||||
| 	return article, nil | ||||
| } | ||||
|  | ||||
| func (db *DB) GetCertainArticles(published, rejected bool) ([]*Article, error) { | ||||
| 	query := ` | ||||
|     SELECT id, title, created, description, link, enc_url, enc_length, enc_type, author_id, issue_id, is_in_issue, auto_generated | ||||
| 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 | ||||
|     FROM articles | ||||
|     WHERE published = ? | ||||
|     AND rejected = ? | ||||
|     ` | ||||
| 	rows, err := db.Query(query, published, rejected) | ||||
|     WHERE %s = ? | ||||
|     `, attribute) | ||||
| 	rows, err := db.Query(query, value) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("error querying articles: %v", err) | ||||
| 	} | ||||
| @@ -130,12 +135,11 @@ func (db *DB) GetCertainArticles(published, rejected bool) ([]*Article, error) { | ||||
|  | ||||
| 		if err = rows.Scan(&article.ID, &article.Title, &created, | ||||
| 			&article.Description, &article.Link, &article.EncURL, &article.EncLength, | ||||
| 			&article.EncType, &article.AuthorID, &article.IssueID, | ||||
| 			&article.IsInIssue, &article.AutoGenerated); err != nil { | ||||
| 			&article.EncType, &article.AuthorID, &article.IssueID, &article.Published, | ||||
| 			&article.Rejected, &article.IsInIssue, &article.AutoGenerated); err != nil { | ||||
| 			return nil, fmt.Errorf("error scanning article row: %v", err) | ||||
| 		} | ||||
|  | ||||
| 		article.Published = false | ||||
| 		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) | ||||
| @@ -152,7 +156,9 @@ 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, link, enc_url, enc_length, enc_type, author_id, auto_generated | ||||
|     SELECT | ||||
|         id, title, created, description, link, enc_url, enc_length, enc_type, | ||||
|         author_id, auto_generated | ||||
|     FROM articles | ||||
|     WHERE issue_id = ? AND published = true AND is_in_issue = true | ||||
|     ` | ||||
| @@ -269,14 +275,13 @@ func (db *DB) AddArticleToCurrentIssue(id int64) error { | ||||
|  | ||||
| func (db *DB) DeleteArticle(id int64) error { | ||||
| 	articlesTagsQuery := "DELETE FROM articles_tags WHERE article_id = ?" | ||||
| 	articlesQuery := "DELETE FROM articles WHERE id = ?" | ||||
|  | ||||
| 	_, err := db.Exec(articlesTagsQuery, id) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("error deleting article %v from DB: %v", id, err) | ||||
| 	} | ||||
|  | ||||
| 	articlesQuery := "DELETE FROM articles WHERE id = ?" | ||||
|  | ||||
| 	_, err = db.Exec(articlesQuery, id) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("error deleting article %v from DB: %v", id, err) | ||||
|   | ||||
| @@ -38,8 +38,8 @@ func newConfig() *Config { | ||||
| 		KeyFile:     "/var/www/cpolis/cpolis.key", | ||||
| 		LogFile:     "/var/log/cpolis.log", | ||||
| 		PDFDir:      "/var/www/cpolis/pdfs", | ||||
| 		Port:        ":8080", | ||||
| 		PicsDir:     "/var/www/cpolis/pics", | ||||
| 		Port:        ":8080", | ||||
| 		RSSFile:     "/var/www/cpolis/cpolis.rss", | ||||
| 		WebDir:      "/var/www/cpolis/web", | ||||
| 	} | ||||
| @@ -113,7 +113,10 @@ func (c *Config) handleCliArgs() error { | ||||
| 	flag.IntVar(&port, "port", port, "port") | ||||
| 	flag.Parse() | ||||
|  | ||||
| 	c.Port = fmt.Sprint(":", port) | ||||
| 	if port != 0 { | ||||
| 		c.Port = fmt.Sprint(":", port) | ||||
| 	} | ||||
|  | ||||
| 	c.ConfigFile, err = mkFile(c.ConfigFile, 0600, 0700) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("error setting up file: %v", err) | ||||
|   | ||||
							
								
								
									
										28
									
								
								cmd/backend/files.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								cmd/backend/files.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| package backend | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"os" | ||||
| ) | ||||
|  | ||||
| func CopyFile(src, dst string) error { | ||||
| 	srcFile, err := os.Open(src) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("error opening source file: %v", err) | ||||
| 	} | ||||
| 	defer srcFile.Close() | ||||
|  | ||||
| 	dstFile, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("error opening destination file: %v", err) | ||||
| 	} | ||||
| 	defer dstFile.Close() | ||||
|  | ||||
| 	_, err = io.Copy(dstFile, srcFile) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("error copying file: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	return dstFile.Sync() | ||||
| } | ||||
| @@ -8,9 +8,7 @@ import ( | ||||
| 	"google.golang.org/api/option" | ||||
| ) | ||||
|  | ||||
| type Client struct { | ||||
| 	*auth.Client | ||||
| } | ||||
| type Client struct{ *auth.Client } | ||||
|  | ||||
| func NewClient(c *Config) (*Client, error) { | ||||
| 	var err error | ||||
|   | ||||
| @@ -17,7 +17,7 @@ func GenerateRSS(c *Config, db *DB) (*string, error) { | ||||
| 		Items:       make([]*rss.Item, 0), | ||||
| 	} | ||||
|  | ||||
| 	articles, err := db.GetCertainArticles(true, false) | ||||
| 	articles, err := db.GetCertainArticles("published", true) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("error getting published articles for RSS feed: %v", err) | ||||
| 	} | ||||
|   | ||||
| @@ -10,9 +10,10 @@ import ( | ||||
| 	"github.com/gorilla/sessions" | ||||
| ) | ||||
|  | ||||
| type CookieStore struct { | ||||
| 	sessions.CookieStore | ||||
| } | ||||
| type ( | ||||
| 	CookieStore struct{ sessions.CookieStore } | ||||
| 	Session     struct{ sessions.Session } | ||||
| ) | ||||
|  | ||||
| func NewKey() ([]byte, error) { | ||||
| 	key := make([]byte, 32) | ||||
|   | ||||
| @@ -22,6 +22,17 @@ const ( | ||||
| 	PreviewMode | ||||
| ) | ||||
|  | ||||
| type EditorHTMLData struct { | ||||
| 	Selected     map[int64]bool | ||||
| 	Content      string | ||||
| 	Action       string | ||||
| 	ActionTitle  string | ||||
| 	ActionButton string | ||||
| 	HTMLContent  template.HTML | ||||
| 	Article      *b.Article | ||||
| 	Tags         []*b.Tag | ||||
| } | ||||
|  | ||||
| func WriteArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc { | ||||
| 	return func(w http.ResponseWriter, r *http.Request) { | ||||
| 		session, err := getSession(w, r, c, s) | ||||
| @@ -29,22 +40,13 @@ func WriteArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc { | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		type editorHTMLData struct { | ||||
| 			Title       string | ||||
| 			Description string | ||||
| 			Content     string | ||||
| 			HTMLContent template.HTML | ||||
| 			Tags        []*b.Tag | ||||
| 			Mode        int | ||||
| 		} | ||||
| 		data := &EditorHTMLData{Action: "submit"} | ||||
|  | ||||
| 		var data editorHTMLData | ||||
| 		if session.Values["article"] == nil { | ||||
| 			data = editorHTMLData{} | ||||
| 			data = &EditorHTMLData{Article: new(b.Article)} | ||||
| 		} else { | ||||
| 			data = session.Values["article"].(editorHTMLData) | ||||
| 			data = session.Values["article"].(*EditorHTMLData) | ||||
| 		} | ||||
| 		data.Mode = EditMode | ||||
|  | ||||
| 		data.Tags, err = db.GetTagList() | ||||
| 		if err != nil { | ||||
| @@ -185,22 +187,41 @@ func ResubmitArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func ShowUnpublishedArticles(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc { | ||||
| func ShowUnpublishedUnrejectedAndPublishedRejectedArticles(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc { | ||||
| 	return func(w http.ResponseWriter, r *http.Request) { | ||||
| 		if _, err := getSession(w, r, c, s); err != nil { | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		unpublishedArticles, err := db.GetCertainArticles(false, false) | ||||
| 		rejectedArticles, err := db.GetCertainArticles("rejected", true) | ||||
| 		if err != nil { | ||||
| 			log.Println(err) | ||||
| 			http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		articles := make([]*b.Article, 0) | ||||
| 		for _, article := range rejectedArticles { | ||||
| 			if article.Published { | ||||
| 				articles = append(articles, article) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		unpublishedArticles, err := db.GetCertainArticles("published", false) | ||||
| 		if err != nil { | ||||
| 			log.Println(err) | ||||
| 			http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		for _, article := range unpublishedArticles { | ||||
| 			if !article.Rejected { | ||||
| 				articles = append(articles, article) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		tmpl, err := template.ParseFiles(c.WebDir + "/templates/unpublished-articles.html") | ||||
| 		tmpl = template.Must(tmpl, err) | ||||
| 		tmpl.ExecuteTemplate(w, "page-content", unpublishedArticles) | ||||
| 		template.Must(tmpl, err).ExecuteTemplate(w, "page-content", articles) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -211,13 +232,12 @@ func ShowRejectedArticles(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerF | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		type htmlData struct { | ||||
| 		data := new(struct { | ||||
| 			MyIDs            map[int64]bool | ||||
| 			RejectedArticles []*b.Article | ||||
| 		} | ||||
| 		data := new(htmlData) | ||||
| 		}) | ||||
|  | ||||
| 		data.RejectedArticles, err = db.GetCertainArticles(false, true) | ||||
| 		data.RejectedArticles, err = db.GetCertainArticles("rejected", true) | ||||
| 		if err != nil { | ||||
| 			log.Println(err) | ||||
| 			http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| @@ -237,74 +257,12 @@ func ShowRejectedArticles(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerF | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func ReviewUnpublishedArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc { | ||||
| 	return func(w http.ResponseWriter, r *http.Request) { | ||||
| 		if _, err := getSession(w, r, c, s); err != nil { | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		data := new(struct { | ||||
| 			Article *b.Article | ||||
| 			Content template.HTML | ||||
| 			Tags    []*b.Tag | ||||
| 		}) | ||||
|  | ||||
| 		id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) | ||||
| 		if err != nil { | ||||
| 			log.Println(err) | ||||
| 			http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		data.Article, err = db.GetArticle(id) | ||||
| 		if err != nil { | ||||
| 			log.Println(err) | ||||
| 			http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 			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 | ||||
| 		} | ||||
|  | ||||
| 		content, err := b.ConvertToHTML(string(contentBytes)) | ||||
| 		if err != nil { | ||||
| 			log.Println(err) | ||||
| 			http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 			return | ||||
| 		} | ||||
| 		data.Content = template.HTML(content) | ||||
|  | ||||
| 		data.Tags, err = db.GetArticleTags(data.Article.ID) | ||||
| 		if err != nil { | ||||
| 			log.Println(err) | ||||
| 			http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		tmpl, err := template.ParseFiles(c.WebDir + "/templates/to-be-published.html") | ||||
| 		tmpl = template.Must(tmpl, err) | ||||
| 		tmpl.ExecuteTemplate(w, "page-content", data) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func ReviewRejectedArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc { | ||||
| 	return func(w http.ResponseWriter, r *http.Request) { | ||||
| 		if _, err := getSession(w, r, c, s); err != nil { | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		data := new(struct { | ||||
| 			Selected map[int64]bool | ||||
| 			Article  *b.Article | ||||
| 			Content  string | ||||
| 			Tags     []*b.Tag | ||||
| 		}) | ||||
|  | ||||
| 		id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) | ||||
| 		if err != nil { | ||||
| 			log.Println(err) | ||||
| @@ -312,6 +270,7 @@ func ReviewRejectedArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.Handler | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		data := new(EditorHTMLData) | ||||
| 		data.Article, err = db.GetArticle(id) | ||||
| 		if err != nil { | ||||
| 			log.Println(err) | ||||
| @@ -320,14 +279,13 @@ func ReviewRejectedArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.Handler | ||||
| 		} | ||||
|  | ||||
| 		articleAbsName := fmt.Sprint(c.ArticleDir, "/", data.Article.ID, ".md") | ||||
| 		contentBytes, err := os.ReadFile(articleAbsName) | ||||
| 		content, err := os.ReadFile(articleAbsName) | ||||
| 		if err != nil { | ||||
| 			log.Println(err) | ||||
| 			http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		data.Content = string(contentBytes) | ||||
| 		data.Content = string(content) | ||||
|  | ||||
| 		data.Tags, err = db.GetTagList() | ||||
| 		if err != nil { | ||||
| @@ -347,7 +305,9 @@ func ReviewRejectedArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.Handler | ||||
| 			data.Selected[tag.ID] = true | ||||
| 		} | ||||
|  | ||||
| 		tmpl, err := template.ParseFiles(c.WebDir + "/templates/rework-article.html") | ||||
| 		data.Action = fmt.Sprint("resubmit/", data.Article.ID) | ||||
|  | ||||
| 		tmpl, err := template.ParseFiles(c.WebDir + "/templates/editor.html") | ||||
| 		tmpl = template.Must(tmpl, err) | ||||
| 		tmpl.ExecuteTemplate(w, "page-content", data) | ||||
| 	} | ||||
| @@ -390,6 +350,36 @@ func PublishArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc { | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		if article.EditedID != 0 { | ||||
| 			oldArticle, err := db.GetArticle(article.EditedID) | ||||
| 			if err != nil { | ||||
| 				log.Println(err) | ||||
| 				http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
| 			if err = db.DeleteArticle(oldArticle.ID); err != nil { | ||||
| 				log.Println(err) | ||||
| 				http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
| 			if err = os.Remove(fmt.Sprint(c.ArticleDir, "/", oldArticle.ID, ".md")); err != nil { | ||||
| 				log.Println(err) | ||||
| 				http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
| 			if err = db.UpdateAttributes( | ||||
| 				&b.Attribute{Table: "articles", ID: id, AttName: "link", Value: fmt.Sprint(c.Domain, "/article/serve/", article.ID)}, | ||||
| 				&b.Attribute{Table: "articles", ID: id, AttName: "edited_id", Value: 0}, | ||||
| 			); 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) | ||||
| @@ -422,9 +412,7 @@ func RejectArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc { | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		if err = db.UpdateAttributes( | ||||
| 			&b.Attribute{Table: "articles", ID: id, AttName: "rejected", Value: true}, | ||||
| 		); err != nil { | ||||
| 		if err = db.UpdateAttributes(&b.Attribute{Table: "articles", ID: id, AttName: "rejected", Value: true}); err != nil { | ||||
| 			log.Println(err) | ||||
| 			http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 			return | ||||
| @@ -498,71 +486,75 @@ func UploadArticleImage(c *b.Config, s *b.CookieStore) http.HandlerFunc { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func ShowPublishedArticles(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc { | ||||
| func ShowPublishedArticles(c *b.Config, db *b.DB, s *b.CookieStore, action string) http.HandlerFunc { | ||||
| 	return func(w http.ResponseWriter, r *http.Request) { | ||||
| 		if _, err := getSession(w, r, c, s); err != nil { | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		publishedArticles, err := db.GetCertainArticles(true, false) | ||||
| 		data := new(struct { | ||||
| 			Action   string | ||||
| 			Articles []*b.Article | ||||
| 		}) | ||||
| 		data.Action = action | ||||
|  | ||||
| 		publishedArticles, err := db.GetCertainArticles("published", true) | ||||
| 		if err != nil { | ||||
| 			log.Println(err) | ||||
| 			http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		filteredArticles := make([]*b.Article, 0) | ||||
| 		for _, article := range publishedArticles { | ||||
| 			if !article.AutoGenerated { | ||||
| 				filteredArticles = append(filteredArticles, article) | ||||
| 				data.Articles = append(data.Articles, article) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		tmpl, err := template.ParseFiles(c.WebDir + "/templates/published-articles.html") | ||||
| 		tmpl = template.Must(tmpl, err) | ||||
| 		tmpl.ExecuteTemplate(w, "page-content", filteredArticles) | ||||
| 		tmpl.ExecuteTemplate(w, "page-content", data) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func ReviewArticleForDeletion(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc { | ||||
| func ReviewArticle(c *b.Config, db *b.DB, s *b.CookieStore, action, title, button string) http.HandlerFunc { | ||||
| 	return func(w http.ResponseWriter, r *http.Request) { | ||||
| 		if _, err := getSession(w, r, c, s); err != nil { | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		type htmlData struct { | ||||
| 			Title       string | ||||
| 			Description string | ||||
| 			Content     template.HTML | ||||
| 			Tags        []*b.Tag | ||||
| 			ID          int64 | ||||
| 		} | ||||
|  | ||||
| 		var err error | ||||
| 		data := new(htmlData) | ||||
|  | ||||
| 		data.ID, err = strconv.ParseInt(r.PathValue("id"), 10, 64) | ||||
| 		id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) | ||||
| 		if err != nil { | ||||
| 			log.Println(err) | ||||
| 			http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		article, err := db.GetArticle(data.ID) | ||||
| 		article, err := db.GetArticle(id) | ||||
| 		if err != nil { | ||||
| 			log.Println(err) | ||||
| 			http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		data.Title, err = b.ConvertToPlain(article.Title) | ||||
| 		data := &EditorHTMLData{ | ||||
| 			Article: &b.Article{ | ||||
| 				ID:        id, | ||||
| 				IsInIssue: article.IsInIssue, | ||||
| 			}, | ||||
| 			Action:       action, | ||||
| 			ActionTitle:  title, | ||||
| 			ActionButton: button, | ||||
| 		} | ||||
|  | ||||
| 		data.Article.Title, err = b.ConvertToPlain(article.Title) | ||||
| 		if err != nil { | ||||
| 			log.Println(err) | ||||
| 			http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		data.Description, err = b.ConvertToPlain(article.Description) | ||||
| 		data.Article.Description, err = b.ConvertToPlain(article.Description) | ||||
| 		if err != nil { | ||||
| 			log.Println(err) | ||||
| 			http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| @@ -570,31 +562,29 @@ func ReviewArticleForDeletion(c *b.Config, db *b.DB, s *b.CookieStore) http.Hand | ||||
| 		} | ||||
|  | ||||
| 		articleAbsName := fmt.Sprint(c.ArticleDir, "/", article.ID, ".md") | ||||
| 		contentBytes, err := os.ReadFile(articleAbsName) | ||||
| 		content, err := os.ReadFile(articleAbsName) | ||||
| 		if err != nil { | ||||
| 			log.Println(err) | ||||
| 			http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 			return | ||||
| 		} | ||||
| 		data.Content, err = b.ConvertToHTML(string(content)) | ||||
| 		if err != nil { | ||||
| 			log.Println(err) | ||||
| 			http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 			return | ||||
| 		} | ||||
| 		data.HTMLContent = template.HTML(data.Content) | ||||
|  | ||||
| 		data.Tags, err = db.GetArticleTags(id) | ||||
| 		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 | ||||
| 		} | ||||
| 		data.Content = template.HTML(content) | ||||
|  | ||||
| 		data.Tags, err = db.GetArticleTags(data.ID) | ||||
| 		if err != nil { | ||||
| 			log.Println(err) | ||||
| 			http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		tmpl, err := template.ParseFiles(c.WebDir + "/templates/to-be-deleted.html") | ||||
| 		tmpl = template.Must(tmpl, err) | ||||
| 		tmpl.ExecuteTemplate(w, "page-content", data) | ||||
| 		tmpl, err := template.ParseFiles(c.WebDir + "/templates/review-article.html") | ||||
| 		template.Must(tmpl, err).ExecuteTemplate(w, "page-content", data) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -641,3 +631,110 @@ func DeleteArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc { | ||||
| 		tmpl.ExecuteTemplate(w, "page-content", session.Values["role"].(int)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func AllowEditArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc { | ||||
| 	return func(w http.ResponseWriter, r *http.Request) { | ||||
| 		session, err := getSession(w, r, c, s) | ||||
| 		if err != nil { | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		oldID, err := strconv.ParseInt(r.PathValue("id"), 10, 64) | ||||
| 		if err != nil { | ||||
| 			log.Println(err) | ||||
| 			http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 			return | ||||
| 		} | ||||
| 		fmt.Println(oldID) | ||||
|  | ||||
| 		oldArticle, err := db.GetArticle(oldID) | ||||
| 		if err != nil { | ||||
| 			log.Println(err) | ||||
| 			http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		newArticle := oldArticle | ||||
| 		newArticle.Published = false | ||||
| 		newArticle.Rejected = true | ||||
| 		newArticle.EditedID = oldArticle.ID | ||||
|  | ||||
| 		newID, err := db.AddArticle(newArticle) | ||||
| 		if err != nil { | ||||
| 			log.Println(err) | ||||
| 			http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		if err = db.UpdateAttributes(&b.Attribute{Table: "articles", ID: oldID, AttName: "edited_id", Value: newID}); err != nil { | ||||
| 			log.Println(err) | ||||
| 			http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		if err = b.CopyFile(fmt.Sprint(c.ArticleDir, "/", oldID, ".md"), fmt.Sprint(c.ArticleDir, "/", newID, ".md")); err != nil { | ||||
| 			log.Println(err) | ||||
| 			http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html") | ||||
| 		tmpl = template.Must(tmpl, err) | ||||
| 		tmpl.ExecuteTemplate(w, "page-content", session.Values["role"].(int)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func EditArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc { | ||||
| 	return func(w http.ResponseWriter, r *http.Request) { | ||||
| 		if _, err := getSession(w, r, c, s); err != nil { | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) | ||||
| 		if err != nil { | ||||
| 			log.Println(err) | ||||
| 			http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		data := new(EditorHTMLData) | ||||
|  | ||||
| 		data.Article, err = db.GetArticle(id) | ||||
| 		if err != nil { | ||||
| 			log.Println(err) | ||||
| 			http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		content, err := os.ReadFile(fmt.Sprint(c.ArticleDir, "/", data.Article.ID, ".md")) | ||||
| 		if err != nil { | ||||
| 			log.Println(err) | ||||
| 			http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 			return | ||||
| 		} | ||||
| 		data.Content = string(content) | ||||
|  | ||||
| 		data.Tags, err = db.GetArticleTags(data.Article.ID) | ||||
| 		if err != nil { | ||||
| 			log.Println(err) | ||||
| 			http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		selectedTags, err := db.GetArticleTags(id) | ||||
| 		if err != nil { | ||||
| 			log.Println(err) | ||||
| 			http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 			return | ||||
| 		} | ||||
| 		data.Selected = make(map[int64]bool) | ||||
| 		for _, tag := range selectedTags { | ||||
| 			data.Selected[tag.ID] = true | ||||
| 		} | ||||
|  | ||||
| 		data.Action = fmt.Sprint("save/", data.Article.ID) | ||||
|  | ||||
| 		tmpl, err := template.ParseFiles(c.WebDir + "/templates/editor.html") | ||||
| 		template.Must(tmpl, err).ExecuteTemplate(w, "page-content", data) | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -30,6 +30,14 @@ func PublishLatestIssue(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFun | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		title := r.PostFormValue("issue-title") | ||||
| 		if len(title) == 0 { | ||||
| 			err = fmt.Errorf("error: no title for issue specified") | ||||
| 			log.Println(err) | ||||
| 			http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		if session.Values["issue-image"] == nil { | ||||
| 			err := "error: Image required" | ||||
| 			log.Println(err) | ||||
| @@ -55,14 +63,11 @@ func PublishLatestIssue(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFun | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		imgSize := imgInfo.Size() | ||||
| 		mimeType := mime.TypeByExtension(filepath.Ext(imgAbsName)) | ||||
|  | ||||
| 		article := &b.Article{ | ||||
| 			Title:         r.PostFormValue("issue-title"), | ||||
| 			Title:         title, | ||||
| 			EncURL:        fmt.Sprint(c.Domain, "/image/serve/", imgFileName), | ||||
| 			EncLength:     int(imgSize), | ||||
| 			EncType:       mimeType, | ||||
| 			EncLength:     int(imgInfo.Size()), | ||||
| 			EncType:       mime.TypeByExtension(filepath.Ext(imgAbsName)), | ||||
| 			Published:     true, | ||||
| 			Rejected:      false, | ||||
| 			Created:       time.Now(), | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| package frontend | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"html/template" | ||||
| 	"log" | ||||
| @@ -26,6 +27,26 @@ func saveSession(w http.ResponseWriter, r *http.Request, s *b.CookieStore, u *b. | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // getSession is used for verifying that the user is logged in and returns their session and an error. | ||||
| func getSession(w http.ResponseWriter, r *http.Request, c *b.Config, s *b.CookieStore) (*b.Session, error) { | ||||
| 	msg := "Keine gültige Session. Bitte erneut anmelden." | ||||
| 	tmpl, tmplErr := template.ParseFiles(c.WebDir+"/templates/index.html", c.WebDir+"/templates/login.html") | ||||
|  | ||||
| 	tmpSession, err := s.Get(r, "cookie") | ||||
| 	if err != nil { | ||||
| 		template.Must(tmpl, tmplErr).ExecuteTemplate(w, "page-content", msg) | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	session := &b.Session{Session: *tmpSession} | ||||
| 	if session.IsNew { | ||||
| 		template.Must(tmpl, tmplErr).ExecuteTemplate(w, "page-content", msg) | ||||
| 		return session, errors.New("error: no existing session") | ||||
| 	} | ||||
|  | ||||
| 	return session, nil | ||||
| } | ||||
|  | ||||
| func HomePage(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc { | ||||
| 	return func(w http.ResponseWriter, r *http.Request) { | ||||
| 		numRows, err := db.CountEntries("users") | ||||
|   | ||||
| @@ -277,12 +277,12 @@ func ShowAllUsers(c *b.Config, db *b.DB, s *b.CookieStore, action string) http.H | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		type htmlData struct { | ||||
| 		data := new(struct { | ||||
| 			Users  map[int64]*b.User | ||||
| 			Action string | ||||
| 		} | ||||
| 		}) | ||||
|  | ||||
| 		data := &htmlData{Action: action} | ||||
| 		data.Action = action | ||||
| 		data.Users, err = db.GetAllUsers() | ||||
| 		if err != nil { | ||||
| 			log.Println(err) | ||||
|   | ||||
| @@ -1,29 +0,0 @@ | ||||
| package frontend | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"html/template" | ||||
| 	"net/http" | ||||
|  | ||||
| 	"github.com/gorilla/sessions" | ||||
| 	b "streifling.com/jason/cpolis/cmd/backend" | ||||
| ) | ||||
|  | ||||
| // getSession is used for verifying that the user is logged in and returns their session and an error. | ||||
| func getSession(w http.ResponseWriter, r *http.Request, c *b.Config, s *b.CookieStore) (*sessions.Session, error) { | ||||
| 	msg := "Keine gültige Session. Bitte erneut anmelden." | ||||
| 	tmpl, tmplErr := template.ParseFiles(c.WebDir+"/templates/index.html", c.WebDir+"/templates/login.html") | ||||
|  | ||||
| 	session, err := s.Get(r, "cookie") | ||||
| 	if err != nil { | ||||
| 		template.Must(tmpl, tmplErr).ExecuteTemplate(w, "page-content", msg) | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	if session.IsNew { | ||||
| 		template.Must(tmpl, tmplErr).ExecuteTemplate(w, "page-content", msg) | ||||
| 		return session, errors.New("error: no existing session") | ||||
| 	} | ||||
|  | ||||
| 	return session, nil | ||||
| } | ||||
							
								
								
									
										12
									
								
								cmd/main.go
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								cmd/main.go
									
									
									
									
									
								
							| @@ -49,15 +49,19 @@ func main() { | ||||
| 		http.FileServer(http.Dir(config.WebDir+"/static/")))) | ||||
| 	mux.HandleFunc("/", f.HomePage(config, db, store)) | ||||
|  | ||||
| 	mux.HandleFunc("GET /article/all-published", f.ShowPublishedArticles(config, db, store)) | ||||
| 	mux.HandleFunc("GET /article/allow-edit/{id}", f.AllowEditArticle(config, db, store)) | ||||
| 	mux.HandleFunc("GET /article/all-published/review-edit", f.ShowPublishedArticles(config, db, store, "review-edit")) | ||||
| 	mux.HandleFunc("GET /article/all-published/delete", f.ShowPublishedArticles(config, db, store, "review-delete")) | ||||
| 	mux.HandleFunc("GET /article/all-rejected", f.ShowRejectedArticles(config, db, store)) | ||||
| 	mux.HandleFunc("GET /article/all-unpublished", f.ShowUnpublishedArticles(config, db, store)) | ||||
| 	mux.HandleFunc("GET /article/all-unpublished-unrejected-and-published-rejected", f.ShowUnpublishedUnrejectedAndPublishedRejectedArticles(config, db, store)) | ||||
| 	mux.HandleFunc("GET /article/delete/{id}", f.DeleteArticle(config, db, store)) | ||||
| 	mux.HandleFunc("GET /article/edit/{id}", f.EditArticle(config, db, store)) | ||||
| 	mux.HandleFunc("GET /article/publish/{id}", f.PublishArticle(config, db, store)) | ||||
| 	mux.HandleFunc("GET /article/reject/{id}", f.RejectArticle(config, db, store)) | ||||
| 	mux.HandleFunc("GET /article/review-deletion/{id}", f.ReviewArticleForDeletion(config, db, store)) | ||||
| 	mux.HandleFunc("GET /article/review-delete/{id}", f.ReviewArticle(config, db, store, "delete", "Artikel löschen", "Löschen")) | ||||
| 	mux.HandleFunc("GET /article/review-edit/{id}", f.ReviewArticle(config, db, store, "allow-edit", "Artikel bearbeiten", "Bearbeiten erlauben")) | ||||
| 	mux.HandleFunc("GET /article/review-rejected/{id}", f.ReviewRejectedArticle(config, db, store)) | ||||
| 	mux.HandleFunc("GET /article/review-unpublished/{id}", f.ReviewUnpublishedArticle(config, db, store)) | ||||
| 	mux.HandleFunc("GET /article/review-unpublished/{id}", f.ReviewArticle(config, db, store, "publish", "Artikel veröffentlichen", "Veröffentlichen")) | ||||
| 	mux.HandleFunc("GET /article/serve/{id}", c.ServeArticle(config, db)) | ||||
| 	mux.HandleFunc("GET /article/write", f.WriteArticle(config, db, store)) | ||||
| 	mux.HandleFunc("GET /hub", f.ShowHub(config, db, store)) | ||||
|   | ||||
| @@ -33,6 +33,7 @@ CREATE TABLE articles ( | ||||
|     rejected        BOOL            NOT NULL, | ||||
|     author_id       INT             NOT NULL, | ||||
|     issue_id        INT             NOT NULL, | ||||
|     edited_id       INT, | ||||
|     is_in_issue     BOOL            NOT NULL, | ||||
|     auto_generated  BOOL            NOT NULL, | ||||
|     PRIMARY KEY (id), | ||||
|   | ||||
							
								
								
									
										81
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										81
									
								
								go.mod
									
									
									
									
									
								
							| @@ -1,62 +1,59 @@ | ||||
| module streifling.com/jason/cpolis | ||||
|  | ||||
| go 1.23 | ||||
|  | ||||
| toolchain go1.23.0 | ||||
| go 1.22.0 | ||||
|  | ||||
| require ( | ||||
| 	firebase.google.com/go/v4 v4.14.1 | ||||
| 	git.streifling.com/jason/rss v0.1.3 | ||||
| 	github.com/BurntSushi/toml v1.4.0 | ||||
| 	github.com/go-sql-driver/mysql v1.8.1 | ||||
| 	github.com/BurntSushi/toml v1.3.2 | ||||
| 	github.com/go-sql-driver/mysql v1.7.1 | ||||
| 	github.com/google/uuid v1.6.0 | ||||
| 	github.com/gorilla/sessions v1.4.0 | ||||
| 	github.com/microcosm-cc/bluemonday v1.0.27 | ||||
| 	github.com/yuin/goldmark v1.7.4 | ||||
| 	golang.org/x/crypto v0.26.0 | ||||
| 	golang.org/x/term v0.23.0 | ||||
| 	google.golang.org/api v0.195.0 | ||||
| 	github.com/gorilla/sessions v1.2.2 | ||||
| 	github.com/microcosm-cc/bluemonday v1.0.26 | ||||
| 	github.com/yuin/goldmark v1.7.0 | ||||
| 	golang.org/x/crypto v0.21.0 | ||||
| 	golang.org/x/term v0.18.0 | ||||
| 	google.golang.org/api v0.170.0 | ||||
| ) | ||||
|  | ||||
| require ( | ||||
| 	cloud.google.com/go v0.115.1 // indirect | ||||
| 	cloud.google.com/go/auth v0.9.2 // indirect | ||||
| 	cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect | ||||
| 	cloud.google.com/go/compute/metadata v0.5.0 // indirect | ||||
| 	cloud.google.com/go/firestore v1.16.0 // indirect | ||||
| 	cloud.google.com/go/iam v1.2.0 // indirect | ||||
| 	cloud.google.com/go/longrunning v0.6.0 // indirect | ||||
| 	cloud.google.com/go/storage v1.43.0 // indirect | ||||
| 	filippo.io/edwards25519 v1.1.0 // indirect | ||||
| 	cloud.google.com/go v0.112.1 // indirect | ||||
| 	cloud.google.com/go/compute v1.24.0 // indirect | ||||
| 	cloud.google.com/go/compute/metadata v0.2.3 // indirect | ||||
| 	cloud.google.com/go/firestore v1.15.0 // indirect | ||||
| 	cloud.google.com/go/iam v1.1.7 // indirect | ||||
| 	cloud.google.com/go/longrunning v0.5.5 // indirect | ||||
| 	cloud.google.com/go/storage v1.40.0 // indirect | ||||
| 	github.com/MicahParks/keyfunc v1.9.0 // indirect | ||||
| 	github.com/aymerick/douceur v0.2.0 // indirect | ||||
| 	github.com/felixge/httpsnoop v1.0.4 // indirect | ||||
| 	github.com/go-logr/logr v1.4.2 // indirect | ||||
| 	github.com/go-logr/logr v1.4.1 // indirect | ||||
| 	github.com/go-logr/stdr v1.2.2 // indirect | ||||
| 	github.com/golang-jwt/jwt/v4 v4.5.0 // indirect | ||||
| 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect | ||||
| 	github.com/golang/protobuf v1.5.4 // indirect | ||||
| 	github.com/google/s2a-go v0.1.8 // indirect | ||||
| 	github.com/googleapis/enterprise-certificate-proxy v0.3.3 // indirect | ||||
| 	github.com/googleapis/gax-go/v2 v2.13.0 // indirect | ||||
| 	github.com/gorilla/css v1.0.1 // indirect | ||||
| 	github.com/google/s2a-go v0.1.7 // indirect | ||||
| 	github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect | ||||
| 	github.com/googleapis/gax-go/v2 v2.12.3 // indirect | ||||
| 	github.com/gorilla/css v1.0.0 // indirect | ||||
| 	github.com/gorilla/securecookie v1.1.2 // indirect | ||||
| 	go.opencensus.io v0.24.0 // indirect | ||||
| 	go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect | ||||
| 	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect | ||||
| 	go.opentelemetry.io/otel v1.29.0 // indirect | ||||
| 	go.opentelemetry.io/otel/metric v1.29.0 // indirect | ||||
| 	go.opentelemetry.io/otel/trace v1.29.0 // indirect | ||||
| 	golang.org/x/net v0.28.0 // indirect | ||||
| 	golang.org/x/oauth2 v0.22.0 // indirect | ||||
| 	golang.org/x/sync v0.8.0 // indirect | ||||
| 	golang.org/x/sys v0.24.0 // indirect | ||||
| 	golang.org/x/text v0.17.0 // indirect | ||||
| 	golang.org/x/time v0.6.0 // indirect | ||||
| 	google.golang.org/appengine/v2 v2.0.6 // indirect | ||||
| 	google.golang.org/genproto v0.0.0-20240827150818-7e3bb234dfed // indirect | ||||
| 	google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed // indirect | ||||
| 	google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed // indirect | ||||
| 	google.golang.org/grpc v1.66.0 // indirect | ||||
| 	google.golang.org/protobuf v1.34.2 // indirect | ||||
| 	go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect | ||||
| 	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect | ||||
| 	go.opentelemetry.io/otel v1.24.0 // indirect | ||||
| 	go.opentelemetry.io/otel/metric v1.24.0 // indirect | ||||
| 	go.opentelemetry.io/otel/trace v1.24.0 // indirect | ||||
| 	golang.org/x/net v0.23.0 // indirect | ||||
| 	golang.org/x/oauth2 v0.18.0 // indirect | ||||
| 	golang.org/x/sync v0.6.0 // indirect | ||||
| 	golang.org/x/sys v0.18.0 // indirect | ||||
| 	golang.org/x/text v0.14.0 // indirect | ||||
| 	golang.org/x/time v0.5.0 // indirect | ||||
| 	google.golang.org/appengine v1.6.8 // indirect | ||||
| 	google.golang.org/appengine/v2 v2.0.2 // indirect | ||||
| 	google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect | ||||
| 	google.golang.org/genproto/googleapis/api v0.0.0-20240314234333-6e1732d8331c // indirect | ||||
| 	google.golang.org/genproto/googleapis/rpc v0.0.0-20240311132316-a219d84964c2 // indirect | ||||
| 	google.golang.org/grpc v1.62.1 // indirect | ||||
| 	google.golang.org/protobuf v1.33.0 // indirect | ||||
| ) | ||||
|   | ||||
							
								
								
									
										173
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										173
									
								
								go.sum
									
									
									
									
									
								
							| @@ -1,29 +1,25 @@ | ||||
| cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | ||||
| cloud.google.com/go v0.115.1 h1:Jo0SM9cQnSkYfp44+v+NQXHpcHqlnRJk2qxh6yvxxxQ= | ||||
| cloud.google.com/go v0.115.1/go.mod h1:DuujITeaufu3gL68/lOFIirVNJwQeyf5UXyi+Wbgknc= | ||||
| cloud.google.com/go/auth v0.9.2 h1:I+Rq388FYU8QdbVB1IiPd+6KNdrqtAPE/asiKHShBLM= | ||||
| cloud.google.com/go/auth v0.9.2/go.mod h1:7z6VY+7h3KUdRov5F1i8NDP5ZzWKYmEPO842BgCsmTk= | ||||
| cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY= | ||||
| cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc= | ||||
| cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= | ||||
| cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= | ||||
| cloud.google.com/go/firestore v1.16.0 h1:YwmDHcyrxVRErWcgxunzEaZxtNbc8QoFYA/JOEwDPgc= | ||||
| cloud.google.com/go/firestore v1.16.0/go.mod h1:+22v/7p+WNBSQwdSwP57vz47aZiY+HrDkrOsJNhk7rg= | ||||
| cloud.google.com/go/iam v1.2.0 h1:kZKMKVNk/IsSSc/udOb83K0hL/Yh/Gcqpz+oAkoIFN8= | ||||
| cloud.google.com/go/iam v1.2.0/go.mod h1:zITGuWgsLZxd8OwAlX+eMFgZDXzBm7icj1PVTYG766Q= | ||||
| cloud.google.com/go/longrunning v0.6.0 h1:mM1ZmaNsQsnb+5n1DNPeL0KwQd9jQRqSqSDEkBZr+aI= | ||||
| cloud.google.com/go/longrunning v0.6.0/go.mod h1:uHzSZqW89h7/pasCWNYdUpwGz3PcVWhrWupreVPYLts= | ||||
| cloud.google.com/go/storage v1.43.0 h1:CcxnSohZwizt4LCzQHWvBf1/kvtHUn7gk9QERXPyXFs= | ||||
| cloud.google.com/go/storage v1.43.0/go.mod h1:ajvxEa7WmZS1PxvKRq4bq0tFT3vMd502JwstCcYv0Q0= | ||||
| filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= | ||||
| filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= | ||||
| cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM= | ||||
| cloud.google.com/go v0.112.1/go.mod h1:+Vbu+Y1UU+I1rjmzeMOb/8RfkKJK2Gyxi1X6jJCZLo4= | ||||
| cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg= | ||||
| cloud.google.com/go/compute v1.24.0/go.mod h1:kw1/T+h/+tK2LJK0wiPPx1intgdAM3j/g3hFDlscY40= | ||||
| cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= | ||||
| cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= | ||||
| cloud.google.com/go/firestore v1.15.0 h1:/k8ppuWOtNuDHt2tsRV42yI21uaGnKDEQnRFeBpbFF8= | ||||
| cloud.google.com/go/firestore v1.15.0/go.mod h1:GWOxFXcv8GZUtYpWHw/w6IuYNux/BtmeVTMmjrm4yhk= | ||||
| cloud.google.com/go/iam v1.1.7 h1:z4VHOhwKLF/+UYXAJDFwGtNF0b6gjsW1Pk9Ml0U/IoM= | ||||
| cloud.google.com/go/iam v1.1.7/go.mod h1:J4PMPg8TtyurAUvSmPj8FF3EDgY1SPRZxcUGrn7WXGA= | ||||
| cloud.google.com/go/longrunning v0.5.5 h1:GOE6pZFdSrTb4KAiKnXsJBtlE6mEyaW44oKyMILWnOg= | ||||
| cloud.google.com/go/longrunning v0.5.5/go.mod h1:WV2LAxD8/rg5Z1cNW6FJ/ZpX4E4VnDnoTk0yawPBB7s= | ||||
| cloud.google.com/go/storage v1.40.0 h1:VEpDQV5CJxFmJ6ueWNsKxcr1QAYOXEgxDa+sBbJahPw= | ||||
| 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.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.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= | ||||
| github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= | ||||
| github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= | ||||
| github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= | ||||
| github.com/MicahParks/keyfunc v1.9.0 h1:lhKd5xrFHLNOWrDc4Tyb/Q1AJ4LCzQ48GVJyVIID3+o= | ||||
| github.com/MicahParks/keyfunc v1.9.0/go.mod h1:IdnCilugA0O/99dW+/MkvlyrsX8+L8+x95xuVNtM5jw= | ||||
| github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= | ||||
| @@ -41,12 +37,12 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 | ||||
| github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= | ||||
| github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= | ||||
| github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= | ||||
| github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= | ||||
| github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= | ||||
| github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= | ||||
| github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= | ||||
| github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= | ||||
| github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= | ||||
| github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= | ||||
| github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= | ||||
| github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= | ||||
| github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= | ||||
| github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= | ||||
| github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= | ||||
| github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= | ||||
| @@ -56,6 +52,7 @@ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l | ||||
| github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= | ||||
| github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= | ||||
| github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||
| github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||
| github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||
| github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= | ||||
| github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= | ||||
| @@ -65,6 +62,7 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq | ||||
| github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= | ||||
| github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= | ||||
| github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= | ||||
| github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= | ||||
| github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= | ||||
| github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= | ||||
| github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= | ||||
| @@ -78,25 +76,25 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= | ||||
| github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | ||||
| github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= | ||||
| github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= | ||||
| github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc= | ||||
| github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0= | ||||
| github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= | ||||
| github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= | ||||
| github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= | ||||
| github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= | ||||
| github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= | ||||
| github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= | ||||
| github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||||
| github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= | ||||
| github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||||
| github.com/googleapis/enterprise-certificate-proxy v0.3.3 h1:QRje2j5GZimBzlbhGA2V2QlGNgL8G6e+wGo/+/2bWI0= | ||||
| github.com/googleapis/enterprise-certificate-proxy v0.3.3/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= | ||||
| github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s= | ||||
| github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A= | ||||
| github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= | ||||
| github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= | ||||
| github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= | ||||
| github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= | ||||
| github.com/googleapis/gax-go/v2 v2.12.3 h1:5/zPPDvw8Q1SuXjrqrZslrqT7dL/uJT2CQii/cLCKqA= | ||||
| github.com/googleapis/gax-go/v2 v2.12.3/go.mod h1:AKloxT6GtNbaLm8QTNSidHUVsHYcBHwWRvkNFJUQcS4= | ||||
| github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= | ||||
| github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= | ||||
| github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= | ||||
| github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= | ||||
| github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ= | ||||
| github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik= | ||||
| github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk= | ||||
| github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA= | ||||
| github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY= | ||||
| github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ= | ||||
| github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58= | ||||
| github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs= | ||||
| github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||||
| github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||||
| github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= | ||||
| @@ -106,30 +104,30 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE | ||||
| github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||||
| github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= | ||||
| github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= | ||||
| github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= | ||||
| github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= | ||||
| github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= | ||||
| github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= | ||||
| github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= | ||||
| github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg= | ||||
| github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= | ||||
| github.com/yuin/goldmark v1.7.0 h1:EfOIvIMZIzHdB/R/zVrikYLPPwJlfMcNczJFMs1m6sA= | ||||
| github.com/yuin/goldmark v1.7.0/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= | ||||
| go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= | ||||
| go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= | ||||
| go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc= | ||||
| go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI= | ||||
| go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= | ||||
| go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= | ||||
| go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= | ||||
| go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= | ||||
| go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc= | ||||
| go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8= | ||||
| go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= | ||||
| go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= | ||||
| go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= | ||||
| go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= | ||||
| go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= | ||||
| go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= | ||||
| go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= | ||||
| go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= | ||||
| go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= | ||||
| go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= | ||||
| go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= | ||||
| go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= | ||||
| go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw= | ||||
| go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc= | ||||
| go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= | ||||
| go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= | ||||
| golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||||
| golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||||
| golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= | ||||
| golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= | ||||
| golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= | ||||
| golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= | ||||
| golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= | ||||
| golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | ||||
| golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= | ||||
| golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= | ||||
| @@ -143,18 +141,19 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn | ||||
| golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= | ||||
| golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= | ||||
| golang.org/x/net v0.0.0-20220708220712-1185a9018129/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= | ||||
| golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= | ||||
| golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= | ||||
| golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= | ||||
| golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= | ||||
| golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= | ||||
| golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= | ||||
| golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= | ||||
| golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= | ||||
| golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= | ||||
| golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= | ||||
| golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= | ||||
| golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= | ||||
| golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= | ||||
| golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= | ||||
| golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| @@ -163,20 +162,20 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w | ||||
| golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= | ||||
| golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||||
| golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= | ||||
| golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||||
| golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | ||||
| golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= | ||||
| golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= | ||||
| golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= | ||||
| golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= | ||||
| golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= | ||||
| golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
| golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||
| golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= | ||||
| golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= | ||||
| golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= | ||||
| golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= | ||||
| golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= | ||||
| golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= | ||||
| golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= | ||||
| golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= | ||||
| golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= | ||||
| golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= | ||||
| golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||||
| golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||||
| golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= | ||||
| @@ -186,28 +185,32 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn | ||||
| golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= | ||||
| golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| google.golang.org/api v0.195.0 h1:Ude4N8FvTKnnQJHU48RFI40jOBgIrL8Zqr3/QeST6yU= | ||||
| google.golang.org/api v0.195.0/go.mod h1:DOGRWuv3P8TU8Lnz7uQc4hyNqrBpMtD9ppW3wBJurgc= | ||||
| golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= | ||||
| golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= | ||||
| google.golang.org/api v0.170.0 h1:zMaruDePM88zxZBG+NG8+reALO2rfLhe/JShitLyT48= | ||||
| google.golang.org/api v0.170.0/go.mod h1:/xql9M2btF85xac/VAm4PsLMTLVGUOpq4BE9R8jyNy8= | ||||
| google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= | ||||
| google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= | ||||
| google.golang.org/appengine/v2 v2.0.6 h1:LvPZLGuchSBslPBp+LAhihBeGSiRh1myRoYK4NtuBIw= | ||||
| google.golang.org/appengine/v2 v2.0.6/go.mod h1:WoEXGoXNfa0mLvaH5sV3ZSGXwVmy8yf7Z1JKf3J3wLI= | ||||
| google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= | ||||
| google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= | ||||
| google.golang.org/appengine/v2 v2.0.2 h1:MSqyWy2shDLwG7chbwBJ5uMyw6SNqJzhJHNDwYB0Akk= | ||||
| google.golang.org/appengine/v2 v2.0.2/go.mod h1:PkgRUWz4o1XOvbqtWTkBtCitEJ5Tp4HoVEdMMYQR/8E= | ||||
| google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= | ||||
| google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= | ||||
| google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= | ||||
| google.golang.org/genproto v0.0.0-20240827150818-7e3bb234dfed h1:4C4dbrVFtfIp3GXJdMX1Sj25mahfn5DywOo65/2ISQ8= | ||||
| google.golang.org/genproto v0.0.0-20240827150818-7e3bb234dfed/go.mod h1:ICjniACoWvcDz8c8bOsHVKuuSGDJy1z5M4G0DM3HzTc= | ||||
| google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed h1:3RgNmBoI9MZhsj3QxC+AP/qQhNwpCLOvYDYYsFrhFt0= | ||||
| google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo= | ||||
| google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed h1:J6izYgfBXAI3xTKLgxzTmUltdYaLsuBxFCgDHWJ/eXg= | ||||
| google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= | ||||
| google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y= | ||||
| google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s= | ||||
| google.golang.org/genproto/googleapis/api v0.0.0-20240314234333-6e1732d8331c h1:kaI7oewGK5YnVwj+Y+EJBO/YN1ht8iTL9XkFHtVZLsc= | ||||
| google.golang.org/genproto/googleapis/api v0.0.0-20240314234333-6e1732d8331c/go.mod h1:VQW3tUculP/D4B+xVCo+VgSq8As6wA9ZjHl//pmk+6s= | ||||
| google.golang.org/genproto/googleapis/rpc v0.0.0-20240311132316-a219d84964c2 h1:9IZDv+/GcI6u+a4jRFRLxQs0RUCfavGfoOgEW6jpkI0= | ||||
| google.golang.org/genproto/googleapis/rpc v0.0.0-20240311132316-a219d84964c2/go.mod h1:UCOku4NytXMJuLQE5VuqA5lX3PcHCBo8pxNyvkf4xBs= | ||||
| google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= | ||||
| google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= | ||||
| google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= | ||||
| google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= | ||||
| google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= | ||||
| google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c= | ||||
| google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= | ||||
| google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= | ||||
| google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= | ||||
| google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= | ||||
| google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= | ||||
| google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= | ||||
| @@ -218,9 +221,9 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 | ||||
| google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | ||||
| google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= | ||||
| google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= | ||||
| google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= | ||||
| google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= | ||||
| google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= | ||||
| google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= | ||||
| google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= | ||||
| google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= | ||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||
| gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||||
|   | ||||
| @@ -19,8 +19,16 @@ textarea { | ||||
|     @apply bg-slate-50 dark:bg-slate-950 border border-slate-200 dark:border-slate-800 h-32 rounded-md; | ||||
| } | ||||
|  | ||||
| .btn-area-1 { | ||||
|     @apply grid grid-cols-1 gap-x-4 gap-y-1 mt-4; | ||||
| } | ||||
|  | ||||
| .btn-area { | ||||
|     @apply flex gap-4 mt-4; | ||||
|     @apply grid grid-cols-1 md:grid-cols-2 gap-x-4 gap-y-1 mt-4; | ||||
| } | ||||
|  | ||||
| .btn-area-3 { | ||||
|     @apply grid grid-cols-1 md:grid-cols-3 gap-x-4 gap-y-1 mt-4; | ||||
| } | ||||
|  | ||||
| .btn { | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
| <h2>Neuer Tag</h2> | ||||
|  | ||||
| <form> | ||||
|     <input required name="tag" placeholder="Tag eingeben" type="text" /> | ||||
|     <input class="w-full" name="tag" placeholder="Tag eingeben" required type="text" /> | ||||
|     <div class="btn-area"> | ||||
|         <input class="action-btn" type="submit" value="Anlegen" hx-post="/tag/add" hx-target="#page-content" /> | ||||
|         <button class="btn" hx-get="/hub" hx-target="#page-content">Abbrechen</button> | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
| <h2>Neuer Benutzer</h2> | ||||
|  | ||||
| <form> | ||||
|     <div class="grid grid-cols-3 gap-4"> | ||||
|     <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4"> | ||||
|         <div> | ||||
|             <label for="username">Benutzername</label> | ||||
|             <input class="w-full" required name="username" type="text" value="{{.UserName}}" /> | ||||
| @@ -25,7 +25,7 @@ | ||||
|         </div> | ||||
|     </div> | ||||
|  | ||||
|     <div class="flex gap-4"> | ||||
|     <div class="flex flex-wrap gap-4"> | ||||
|         <div> | ||||
|             <input required id="author" name="role" type="radio" value="3" {{if eq .Role 3 }}checked{{end}} /> | ||||
|             <label for="author">Autor</label> | ||||
|   | ||||
| @@ -16,9 +16,9 @@ | ||||
|         </div> | ||||
|  | ||||
|         <div> | ||||
|             <h3>Cover</h3> | ||||
|             <h3>Titelseite</h3> | ||||
|             <div class="grid grid-cols-2 gap-4 items-center"> | ||||
|                 <input class="h-full" name="issue-title" placeholder="Titel" type="text" /> | ||||
|                 <input class="h-full" name="issue-title" placeholder="Titel" required type="text" /> | ||||
|                 <label class="btn text-center" for="image-upload">Bild hochladen</label> | ||||
|                 <input class="hidden" id="image-upload" name="issue-image" type="file" required | ||||
|                     hx-post="/issue/upload-image" /> | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
| <h2>Profil bearbeiten</h2> | ||||
|  | ||||
| <form> | ||||
|     <div class="grid grid-cols-3 gap-4"> | ||||
|     <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4"> | ||||
|         <div> | ||||
|             <label for="username">Benutzername</label> | ||||
|             <input class="w-full" name="username" type="text" value="{{.UserName}}" /> | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
| <h2>Profil von {{.FirstName}} {{.LastName}} bearbeiten</h2> | ||||
|  | ||||
| <form> | ||||
|     <div class="grid grid-cols-3 gap-4"> | ||||
|     <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4"> | ||||
|         <div> | ||||
|             <label for="username">Benutzername</label> | ||||
|             <input class="w-full" name="username" type="text" value="{{.UserName}}" /> | ||||
| @@ -29,7 +29,7 @@ | ||||
|         </div> | ||||
|     </div> | ||||
|  | ||||
|     <div class="flex gap-4"> | ||||
|     <div class="flex flex-wrap gap-4"> | ||||
|         <div> | ||||
|             <input required id="author" name="role" type="radio" value="3" {{if eq .Role 3 }}checked{{end}} /> | ||||
|             <label for="author">Autor</label> | ||||
|   | ||||
| @@ -4,39 +4,41 @@ | ||||
| <form id="edit-area"> | ||||
|     <div class="flex flex-col gap-y-1"> | ||||
|         <label for="article-title">Titel</label> | ||||
|         <input name="article-title" type="text" value="{{.Title}}" /> | ||||
|         <input name="article-title" type="text" value="{{.Article.Title}}" /> | ||||
|     </div> | ||||
|  | ||||
|     <div class="flex flex-col gap-y-1"> | ||||
|         <label for="article-description">Beschreibung</label> | ||||
|         <textarea name="article-description">{{.Description}}</textarea> | ||||
|         <textarea name="article-description">{{.Article.Description}}</textarea> | ||||
|     </div> | ||||
|  | ||||
|     <div class="flex flex-col gap-y-1"> | ||||
|         <label for="easyMDE">Artikel</label> | ||||
|         <textarea id="easyMDE">{{.Content}}</textarea> | ||||
|         <input id="article-content" name="article-content" type="hidden" /> | ||||
|         <input id="article-content" name="article-content" type="hidden" value="{{.Content}}" /> | ||||
|     </div> | ||||
|  | ||||
|     <div> | ||||
|         <span>Tags</span> | ||||
|         <div class="flex flex-wrap gap-x-4"> | ||||
|             <div> | ||||
|                 <input id="issue" name="issue" type="checkbox" /> | ||||
|                 <input id="issue" name="issue" type="checkbox" {{if .Article.IsInIssue}}checked{{end}} /> | ||||
|                 <label for="issue">Orient Express</label> | ||||
|             </div> | ||||
|  | ||||
|             {{range .Tags}} | ||||
|             <div> | ||||
|                 <input id="{{.Name}}" name="tags" type="checkbox" value="{{.ID}}" /> | ||||
|                 <label for="{{.Name}}">{{.Name}}</label> | ||||
|                 <input id="tag-{{.Name}}" name="tags" type="checkbox" value="{{.ID}}" {{if index $.Selected | ||||
|                     .ID}}checked{{end}} /> | ||||
|                 <label for="tag-{{.Name}}">{{.Name}}</label> | ||||
|             </div> | ||||
|             {{end}} | ||||
|         </div> | ||||
|     </div> | ||||
|  | ||||
|     <div class="btn-area"> | ||||
|         <input class="action-btn" type="submit" value="Senden" hx-post="/article/submit" hx-target="#page-content" /> | ||||
|         <input class="action-btn" type="submit" value="Senden" hx-post="/article/{{.Action}}" | ||||
|             hx-target="#page-content" /> | ||||
|         <button class="btn" hx-get="/hub" hx-target="#page-content">Abbrechen</button> | ||||
|     </div> | ||||
| </form> | ||||
|   | ||||
| @@ -25,7 +25,7 @@ | ||||
|         </div> | ||||
|     </div> | ||||
|  | ||||
|     <div class="btn-area"> | ||||
|     <div class="btn-area-1"> | ||||
|         <input class="action-btn" type="submit" value="Anlegen" hx-post="/user/add-first" hx-target="#page-content" /> | ||||
|     </div> | ||||
| </form> | ||||
|   | ||||
| @@ -5,15 +5,15 @@ | ||||
|     {{if lt . 4}} | ||||
|     <div class="mb-3"> | ||||
|         <h2>Artikel</h2> | ||||
|         <div class="grid grid-cols-2 gap-x-4 gap-y-2"> | ||||
|         <div class="grid grid-cols-1 md:grid-cols-2 gap-x-4 gap-y-2"> | ||||
|             <button class="btn" hx-get="/article/write" hx-target="#page-content">Artikel schreiben</button> | ||||
|             <button class="btn" hx-get="/article/all-rejected" hx-target="#page-content">Abgelehnte Artikel</button> | ||||
|             {{if lt . 3}}<button class="btn" hx-get="/article/all-unpublished" hx-target="#page-content"> | ||||
|                 Unveröffentlichte Artikel | ||||
|             </button>{{end}} | ||||
|             {{if lt . 2}}<button class="btn" hx-get="/article/all-published" hx-target="#page-content"> | ||||
|                 Artikel löschen | ||||
|             </button>{{end}} | ||||
|             <button class="btn" hx-get="/article/all-rejected" hx-target="#page-content">Artikel bearbeiten</button> | ||||
|             {{if lt . 3}}<button class="btn" hx-get="/article/all-unpublished-unrejected-and-published-rejected" | ||||
|                 hx-target="#page-content">Artikel veröffentlichen</button>{{end}} | ||||
|             {{if lt . 2}}<button class="btn" hx-get="/article/all-published/delete" hx-target="#page-content">Artikel | ||||
|                 löschen</button>{{end}} | ||||
|             {{if lt . 2}}<button class="btn" hx-get="/article/all-published/review-edit" | ||||
|                 hx-target="#page-content">Artikel bearbeiten lassen</button>{{end}} | ||||
|             {{if lt . 3}}<button class="btn" hx-get="/tag/create" hx-target="#page-content">Neuer Tag</button>{{end}} | ||||
|         </div> | ||||
|     </div> | ||||
| @@ -22,7 +22,7 @@ | ||||
|     {{if lt . 2}} | ||||
|     <div class="mb-3"> | ||||
|         <h2>Ausgabe</h2> | ||||
|         <div class="grid grid-cols-2 gap-x-4 gap-y-2"> | ||||
|         <div class="grid grid-cols-1 md:grid-cols-2 gap-x-4 gap-y-2"> | ||||
|             <button class="btn" hx-get="/issue/this" hx-target="#page-content">Diese Ausgabe</button> | ||||
|             <form class="flex" hx-encoding="multipart/form-data"> | ||||
|                 <label class="btn text-center" for="pdf-upload">PDF hochladen</label> | ||||
| @@ -36,17 +36,14 @@ | ||||
|     {{if lt . 4}} | ||||
|     <div class="mb-3"> | ||||
|         <h2>Benutzer</h2> | ||||
|         <div class="grid grid-cols-2 gap-x-4 gap-y-2"> | ||||
|         <div class="grid grid-cols-1 md:grid-cols-2 gap-x-4 gap-y-2"> | ||||
|             <button class="btn" hx-get="/user/edit/self" hx-target="#page-content">Mein Profil bearbeiten</button> | ||||
|             {{if eq . 0}}<button class="btn" hx-get="/user/create" hx-target="#page-content"> | ||||
|                 Benutzer hinzufügen | ||||
|             </button>{{end}} | ||||
|             {{if eq . 0}}<button class="btn" hx-get="/user/show-all/edit" hx-target="#page-content"> | ||||
|                 Benutzer bearbeiten | ||||
|             </button>{{end}} | ||||
|             {{if eq . 0}}<button class="btn" hx-get="/user/show-all/delete" hx-target="#page-content"> | ||||
|                 Benutzer löschen | ||||
|             </button>{{end}} | ||||
|             {{if eq . 0}}<button class="btn" hx-get="/user/create" hx-target="#page-content">Benutzer | ||||
|                 hinzufügen</button>{{end}} | ||||
|             {{if eq . 0}}<button class="btn" hx-get="/user/show-all/edit" hx-target="#page-content">Benutzer | ||||
|                 bearbeiten</button>{{end}} | ||||
|             {{if eq . 0}}<button class="btn" hx-get="/user/show-all/delete" hx-target="#page-content">Benutzer | ||||
|                 löschen</button>{{end}} | ||||
|         </div> | ||||
|     </div> | ||||
|     {{end}} | ||||
|   | ||||
| @@ -10,8 +10,8 @@ | ||||
|     <link rel="stylesheet" href="https://unpkg.com/easymde/dist/easymde.min.css"> | ||||
| </head> | ||||
|  | ||||
| <body style="width: 800px;" | ||||
|     class="bg-slate-50 dark:bg-slate-950 flex flex-col justify-between min-h-screen mx-auto text-slate-900 dark:text-slate-100"> | ||||
| <body | ||||
|     class="bg-slate-50 dark:bg-slate-950 container flex flex-col justify-between max-w-screen-lg min-h-screen mx-auto text-slate-900 dark:text-slate-100"> | ||||
|     <header class="my-8"> | ||||
|         <h1 class="font-bold text-4xl text-center">Orient Editor</h1> | ||||
|  | ||||
| @@ -34,13 +34,8 @@ | ||||
|     </main> | ||||
|  | ||||
|     <footer class="text-center text-gray-500 my-8"> | ||||
|         <p> | ||||
|             © 2024 Jason Streifling. Alle Rechte vorbehalten. | ||||
|         </p> | ||||
|         <p> | ||||
|             v0.9.1 - <strong>Hinweis:</strong> Diese Software befindet sich noch in der Entwicklung und kann Fehler | ||||
|             enthalten. | ||||
|         </p> | ||||
|         <p>© 2024 Jason Streifling. Alle Rechte vorbehalten.</p> | ||||
|         <p>v0.11.1 - <strong>Alpha: Drastische Änderungen und Fehler vorbehalten.</strong></p> | ||||
|     </footer> | ||||
|  | ||||
|     <script src="https://unpkg.com/htmx.org@2.0.2"></script> | ||||
|   | ||||
| @@ -2,11 +2,13 @@ | ||||
| <h2>Anmeldung</h2> | ||||
|  | ||||
| <form> | ||||
|     <div class="btn-area"> | ||||
|     <div class="grid grid-cols-1 md:grid-cols-2 gap-4 my-1"> | ||||
|         <input class="w-full" name="username" placeholder="Benutzername" type="text" /> | ||||
|         <input class="w-full" name="password" placeholder="Passwort" type="password" /> | ||||
|     </div> | ||||
|  | ||||
|     <input class="action-btn" type="submit" value="Anmelden" hx-post="/login" hx-target="#page-content" /> | ||||
|     <div class="mt-2"> | ||||
|         <input class="action-btn" type="submit" value="Anmelden" hx-post="/login" hx-target="#page-content" /> | ||||
|     </div> | ||||
| </form> | ||||
| {{end}} | ||||
|   | ||||
| @@ -2,12 +2,13 @@ | ||||
| <h2>Artikel löschen</h2> | ||||
|  | ||||
| <div class="flex flex-col gap-4"> | ||||
|     {{range .}} | ||||
|     <button class="btn" hx-get="/article/review-deletion/{{.ID}}" hx-target="#page-content"> | ||||
|     {{range .Articles}} | ||||
|     <button class="btn" hx-get="/article/{{$.Action}}/{{.ID}}" hx-target="#page-content"> | ||||
|         <h1 class="font-bold text-2xl">{{.Title}}</h1> | ||||
|         <p>{{.Description}}</p> | ||||
|     </button> | ||||
|     {{end}} | ||||
|  | ||||
|     <button class="action-btn" hx-get="/hub" hx-target="#page-content">Zurück</button> | ||||
| </div> | ||||
| {{end}} | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| {{define "page-content"}} | ||||
| <h2>Abgelehnte Artikel</h2> | ||||
| <h2>Artikel bearbeiten</h2> | ||||
|  | ||||
| <div class="flex flex-col gap-4"> | ||||
|     {{range .RejectedArticles}} | ||||
|   | ||||
							
								
								
									
										50
									
								
								web/templates/review-article.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								web/templates/review-article.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | ||||
| {{define "page-content"}} | ||||
| <h2>{{.ActionTitle}}</h2> | ||||
|  | ||||
| <div> | ||||
|     <span>Titel</span> | ||||
|     <div class="border border-slate-200 dark:border-slate-800 mb-3 px-2 py-2 rounded-md w-full"> | ||||
|         {{.Article.Title}} | ||||
|     </div> | ||||
|  | ||||
|     <span>Beschreibung</span> | ||||
|     <div class="border border-slate-200 dark:border-slate-800 mb-3 px-2 py-2 rounded-md w-full"> | ||||
|         {{.Article.Description}} | ||||
|     </div> | ||||
|  | ||||
|     <span>Artikel</span> | ||||
|     <div class="border border-slate-200 dark:border-slate-800 mb-3 px-2 py-2 rounded-md w-full"> | ||||
|         <div class="prose text-slate-900 dark:text-slate-100"> | ||||
|             {{.HTMLContent}} | ||||
|         </div> | ||||
|     </div> | ||||
|  | ||||
|     <span>Tags</span> | ||||
|     <div class="border border-slate-200 dark:border-slate-800 mb-3 px-2 py-2 rounded-md w-full"> | ||||
|         {{if .Article.IsInIssue}} | ||||
|         <span>Orient Express</span> | ||||
|         <br> | ||||
|         {{end}} | ||||
|         {{range .Tags}} | ||||
|         <span>{{.Name}}</span> | ||||
|         <br> | ||||
|         {{end}} | ||||
|     </div> | ||||
|  | ||||
|     {{if eq .Action "publish"}} | ||||
|     <div class="btn-area-3"> | ||||
|         <input class="action-btn" type="submit" value="{{.ActionButton}}" hx-get="/article/{{.Action}}/{{.Article.ID}}" | ||||
|             hx-target="#page-content" /> | ||||
|         <input class="btn" type="submit" value="Ablehnen" hx-get="/article/reject/{{.Article.ID}}" | ||||
|             hx-target="#page-content" /> | ||||
|         <button class="btn" hx-get="/hub" hx-target="#page-content">Abbrechen</button> | ||||
|     </div> | ||||
|     {{else}} | ||||
|     <div class="btn-area"> | ||||
|         <input class="action-btn" type="submit" value="{{.ActionButton}}" hx-get="/article/{{.Action}}/{{.Article.ID}}" | ||||
|             hx-target="#page-content" /> | ||||
|         <button class="btn" hx-get="/hub" hx-target="#page-content">Abbrechen</button> | ||||
|     </div> | ||||
|     {{end}} | ||||
| </div> | ||||
| {{end}} | ||||
| @@ -1,78 +0,0 @@ | ||||
| {{define "page-content"}} | ||||
| <h2>Editor</h2> | ||||
|  | ||||
| <form> | ||||
|     <div class="flex flex-col gap-y-1"> | ||||
|         <label for="article-title">Titel</label> | ||||
|         <input name="article-title" type="text" value="{{.Article.Title}}" /> | ||||
|     </div> | ||||
|  | ||||
|     <div class="flex flex-col gap-y-1"> | ||||
|         <label for="article-description">Beschreibung</label> | ||||
|         <textarea name="article-description">{{.Article.Description}}</textarea> | ||||
|     </div> | ||||
|  | ||||
|     <div class="flex flex-col gap-y-1"> | ||||
|         <label for="easyMDE">Artikel</label> | ||||
|         <textarea id="easyMDE">{{.Content}}</textarea> | ||||
|         <input id="article-content" name="article-content" type="hidden" /> | ||||
|     </div> | ||||
|  | ||||
|     <div> | ||||
|         <span>Tags</span> | ||||
|         <div class="flex flex-wrap gap-x-4"> | ||||
|             <div> | ||||
|                 <input id="issue" name="issue" type="checkbox" {{if .Article.IsInIssue}}checked{{end}} /> | ||||
|                 <label for="issue">Orient Express</label> | ||||
|             </div> | ||||
|  | ||||
|             {{range .Tags}} | ||||
|             <div> | ||||
|                 <input id="tag-{{.Name}}" name="tags" type="checkbox" value="{{.ID}}" {{if index $.Selected | ||||
|                     .ID}}checked{{end}} /> | ||||
|                 <label for="tag-{{.Name}}">{{.Name}}</label> | ||||
|             </div> | ||||
|             {{end}} | ||||
|         </div> | ||||
|     </div> | ||||
|  | ||||
|     <div class="btn-area"> | ||||
|         <input class="action-btn" type="submit" value="Senden" hx-post="/article/resubmit/{{.Article.ID}}" | ||||
|             hx-target="#page-content" /> | ||||
|         <button class="btn" hx-get="/hub" hx-target="#page-content">Zurück</button> | ||||
|     </div> | ||||
| </form> | ||||
|  | ||||
| <script> | ||||
|     document.getElementById('article-content').value = easyMDE.value(); | ||||
|  | ||||
|     var easyMDE = new EasyMDE({ | ||||
|         element: document.getElementById('easyMDE'), | ||||
|         hideIcons: ['image'], | ||||
|         imageTexts: {sbInit: ''}, | ||||
|         showIcons: ["code", "table", "upload-image"], | ||||
|         uploadImage: true, | ||||
|  | ||||
|         imageUploadFunction: function (file, onSuccess, onError) { | ||||
|             var formData = new FormData(); | ||||
|             formData.append('article-image', file); | ||||
|  | ||||
|             fetch('/article/upload-image', { | ||||
|                 method: 'POST', | ||||
|                 body: formData | ||||
|             }) | ||||
|                 .then(response => response.json()) | ||||
|                 .then(data => { | ||||
|                     onSuccess(data); | ||||
|                 }) | ||||
|                 .catch(error => { | ||||
|                     onError(error); | ||||
|                 }); | ||||
|         }, | ||||
|     }); | ||||
|  | ||||
|     easyMDE.codemirror.on("change", () => { | ||||
|         document.getElementById('article-content').value = easyMDE.value(); | ||||
|     }); | ||||
| </script> | ||||
| {{end}} | ||||
| @@ -1,36 +0,0 @@ | ||||
| {{define "page-content"}} | ||||
| <h2>Artikel löschen</h2> | ||||
|  | ||||
| <div> | ||||
|     <span>Titel</span> | ||||
|     <div class="bg-white border border-slate-200 dark:border-slate-800 mb-3 px-2 py-2 rounded-md w-full"> | ||||
|         {{.Title}} | ||||
|     </div> | ||||
|  | ||||
|     <span>Beschreibung</span> | ||||
|     <div class="bg-white border border-slate-200 dark:border-slate-800 mb-3 px-2 py-2 rounded-md w-full"> | ||||
|         {{.Description}} | ||||
|     </div> | ||||
|  | ||||
|     <span>Artikel</span> | ||||
|     <div class="bg-white border border-slate-200 dark:border-slate-800 mb-3 px-2 py-2 rounded-md w-full"> | ||||
|         <div class="prose"> | ||||
|             {{.Content}} | ||||
|         </div> | ||||
|     </div> | ||||
|  | ||||
|     <span>Tags</span> | ||||
|     <div class="bg-white border border-slate-200 dark:border-slate-800 mb-3 px-2 py-2 rounded-md w-full"> | ||||
|         {{range .Tags}} | ||||
|         {{.Name}} | ||||
|         <br> | ||||
|         {{end}} | ||||
|     </div> | ||||
|  | ||||
|     <div class="btn-area"> | ||||
|         <input class="action-btn" type="submit" value="Löschen" hx-get="/article/delete/{{.ID}}" | ||||
|             hx-target="#page-content" /> | ||||
|         <button class="btn" hx-get="/hub" hx-target="#page-content">Zurück</button> | ||||
|     </div> | ||||
| </div> | ||||
| {{end}} | ||||
| @@ -1,42 +0,0 @@ | ||||
| {{define "page-content"}} | ||||
| <h2>Artikel veröffentlichen</h2> | ||||
|  | ||||
| <div> | ||||
|     <span>Titel</span> | ||||
|     <div class="bg-white border border-slate-200 dark:border-slate-800 mb-3 px-2 py-2 rounded-md w-full"> | ||||
|         {{.Article.Title}} | ||||
|     </div> | ||||
|  | ||||
|     <span>Beschreibung</span> | ||||
|     <div class="bg-white border border-slate-200 dark:border-slate-800 mb-3 px-2 py-2 rounded-md w-full"> | ||||
|         {{.Article.Description}} | ||||
|     </div> | ||||
|  | ||||
|     <span>Artikel</span> | ||||
|     <div class="bg-white border border-slate-200 dark:border-slate-800 mb-3 px-2 py-2 rounded-md w-full"> | ||||
|         <div class="prose"> | ||||
|             {{.Content}} | ||||
|         </div> | ||||
|     </div> | ||||
|  | ||||
|     <span>Tags</span> | ||||
|     <div class="bg-white border border-slate-200 dark:border-slate-800 mb-3 px-2 py-2 rounded-md w-full"> | ||||
|         {{if .Article.IsInIssue}} | ||||
|         <span>Orient Express</span> | ||||
|         <br> | ||||
|         {{end}} | ||||
|         {{range .Tags}} | ||||
|         <span>{{.Name}}</span> | ||||
|         <br> | ||||
|         {{end}} | ||||
|     </div> | ||||
|  | ||||
|     <div class="btn-area"> | ||||
|         <input class="action-btn" type="submit" value="Veröffentlichen" hx-get="/article/publish/{{.Article.ID}}" | ||||
|             hx-target="#page-content" /> | ||||
|         <input class="btn" type="submit" value="Ablehnen" hx-get="/article/reject/{{.Article.ID}}" | ||||
|             hx-target="#page-content" /> | ||||
|         <button class="btn" hx-get="/hub" hx-target="#page-content">Zurück</button> | ||||
|     </div> | ||||
| </div> | ||||
| {{end}} | ||||
| @@ -1,5 +1,5 @@ | ||||
| {{define "page-content"}} | ||||
| <h2>Unveröffentlichte Artikel</h2> | ||||
| <h2>Artikel veröffentlichen</h2> | ||||
|  | ||||
| <div class="flex flex-col gap-4"> | ||||
|     {{range .}} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user