Compare commits

..

31 Commits

Author SHA1 Message Date
ca7e7cddd3 Merge branch 'devel' 2024-09-08 16:22:59 +02:00
62921c8e2a Fix wrong version number 2024-09-08 16:22:28 +02:00
94431a2aa9 Merge branch 'devel' 2024-09-08 16:21:38 +02:00
c7761e2dc8 Fix differing border color and streamline current-articles.html 2024-09-08 16:21:26 +02:00
5b1f20c5bc Merge branch 'devel' 2024-09-08 13:36:22 +02:00
013cddc157 Streamline config and make config file variable 2024-09-08 13:35:30 +02:00
523ff9d2db Restructure hub.html to be mor organized 2024-09-08 11:00:44 +02:00
84960fdd44 Implement dark mode 2024-09-08 11:00:12 +02:00
d0c566f8df Merge branch 'devel' 2024-09-01 21:14:05 +02:00
0a387c1087 Remove unnecessery fmt.Println that was used for debugging 2024-09-01 21:13:52 +02:00
38caf02b2c Upgrade all dependencies 2024-09-01 20:56:30 +02:00
435157a42f Upgrade htmx version 2024-09-01 20:52:50 +02:00
4163afb0b7 Change footer in index.html to have version number on same line as warning 2024-09-01 20:51:35 +02:00
5e586aa49a Merge branch 'devel' 2024-09-01 18:51:33 +02:00
6cc0fb40d7 Add version number to index.html 2024-09-01 18:51:14 +02:00
66b2743d3d Merge branch 'devel' 2024-09-01 18:48:26 +02:00
18617f1dbc Give autogenerated articles tags as well 2024-09-01 18:48:18 +02:00
3723b2b5e6 Merge branch 'devel' 2024-09-01 18:18:18 +02:00
79ee20a50e Allow articles to be independent of an issue and allow a title for an issue 2024-09-01 18:18:07 +02:00
e05521591b Add route for PDF uploads 2024-09-01 18:16:26 +02:00
0c9a79e24a Allow for PDF uploads from hub 2024-09-01 18:16:09 +02:00
ce788bfd50 Merge branch 'devel' 2024-09-01 12:54:12 +02:00
8147d9bef6 Small bug fix in build process 2024-09-01 12:54:05 +02:00
230a6278cc Merge branch 'devel' 2024-09-01 12:49:30 +02:00
da0d65d40b Fixed bug where no RSS feed is being generated 2024-09-01 12:27:30 +02:00
42d6e0c198 Merge branch 'devel' 2024-08-31 12:17:41 +02:00
a2d219b2c0 Don't show autogenerated articles in list for deletion 2024-08-31 12:17:30 +02:00
e1af2979af Merge branch 'devel' 2024-08-31 11:27:15 +02:00
b02a882ed7 Fixed small bug that used article.Link instead of correct path for article deletion 2024-08-31 11:27:03 +02:00
f6dedc6f10 Merge branch 'devel' 2024-08-31 01:43:53 +02:00
edb448413b Remove deleted article file when deleting article 2024-08-31 01:42:37 +02:00
21 changed files with 595 additions and 350 deletions

View File

@ -5,8 +5,10 @@ tmp_dir = "tmp"
[build] [build]
args_bin = [ args_bin = [
"-articles tmp/articles", "-articles tmp/articles",
"-config tmp/config.toml",
"-desc 'Freiheit, Gleichheit, Brüderlichkeit, Toleranz und Humanität'", "-desc 'Freiheit, Gleichheit, Brüderlichkeit, Toleranz und Humanität'",
"-domain localhost", "-domain localhost",
"-firebase tmp/firebase.json",
"-key tmp/key.gob", "-key tmp/key.gob",
"-link https://distrikt-ni-st.de", "-link https://distrikt-ni-st.de",
"-log tmp/cpolis.log", "-log tmp/cpolis.log",

View File

@ -9,18 +9,20 @@ import (
) )
type Article struct { type Article struct {
Title string Title string
Created time.Time Created time.Time
Description string Description string
Link string Link string
EncURL string EncURL string
EncLength int EncLength int
EncType string EncType string
Published bool Published bool
Rejected bool Rejected bool
ID int64 ID int64
AuthorID int64 AuthorID int64
IssueID int64 IssueID int64
IsInIssue bool
AutoGenerated bool
} }
func (db *DB) AddArticle(a *Article) (int64, error) { func (db *DB) AddArticle(a *Article) (int64, error) {
@ -29,8 +31,8 @@ func (db *DB) AddArticle(a *Article) (int64, error) {
selectQuery := "SELECT id FROM issues WHERE published = false" selectQuery := "SELECT id FROM issues WHERE published = false"
insertQuery := ` insertQuery := `
INSERT INTO articles INSERT INTO articles
(title, description, link, enc_url, enc_length, enc_type, published, rejected, author_id, issue_id) (title, description, link, enc_url, enc_length, enc_type, published, rejected, author_id, issue_id, is_in_issue, auto_generated)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
` `
for i := 0; i < TxMaxRetries; i++ { for i := 0; i < TxMaxRetries; i++ {
@ -48,7 +50,8 @@ func (db *DB) AddArticle(a *Article) (int64, error) {
} }
result, err := tx.Exec(insertQuery, a.Title, a.Description, a.Link, 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.EncURL, a.EncLength, a.EncType, a.Published, a.Rejected, a.AuthorID, id,
a.IsInIssue, a.AutoGenerated)
if err != nil { if err != nil {
if rollbackErr := tx.Rollback(); rollbackErr != nil { if rollbackErr := tx.Rollback(); rollbackErr != nil {
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr) log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
@ -82,7 +85,7 @@ func (db *DB) AddArticle(a *Article) (int64, error) {
func (db *DB) GetArticle(id int64) (*Article, error) { func (db *DB) GetArticle(id int64) (*Article, error) {
query := ` query := `
SELECT title, created, description, link, enc_url, enc_length, enc_type, published, author_id SELECT title, created, description, link, enc_url, enc_length, enc_type, published, author_id, issue_id, is_in_issue, auto_generated
FROM articles FROM articles
WHERE id = ? WHERE id = ?
` `
@ -94,7 +97,8 @@ func (db *DB) GetArticle(id int64) (*Article, error) {
if err := row.Scan(&article.Title, &created, &article.Description, if err := row.Scan(&article.Title, &created, &article.Description,
&article.Link, &article.EncURL, &article.EncLength, &article.EncType, &article.Link, &article.EncURL, &article.EncLength, &article.EncType,
&article.Published, &article.AuthorID); err != nil { &article.Published, &article.AuthorID, &article.IssueID,
&article.IsInIssue, &article.AutoGenerated); err != nil {
return nil, fmt.Errorf("error scanning article row: %v", err) return nil, fmt.Errorf("error scanning article row: %v", err)
} }
@ -109,7 +113,7 @@ func (db *DB) GetArticle(id int64) (*Article, error) {
func (db *DB) GetCertainArticles(published, rejected bool) ([]*Article, error) { func (db *DB) GetCertainArticles(published, rejected bool) ([]*Article, error) {
query := ` query := `
SELECT id, title, created, description, link, enc_url, enc_length, enc_type, author_id, issue_id SELECT id, title, created, description, link, enc_url, enc_length, enc_type, author_id, issue_id, is_in_issue, auto_generated
FROM articles FROM articles
WHERE published = ? WHERE published = ?
AND rejected = ? AND rejected = ?
@ -126,7 +130,8 @@ func (db *DB) GetCertainArticles(published, rejected bool) ([]*Article, error) {
if err = rows.Scan(&article.ID, &article.Title, &created, if err = rows.Scan(&article.ID, &article.Title, &created,
&article.Description, &article.Link, &article.EncURL, &article.EncLength, &article.Description, &article.Link, &article.EncURL, &article.EncLength,
&article.EncType, &article.AuthorID, &article.IssueID); err != nil { &article.EncType, &article.AuthorID, &article.IssueID,
&article.IsInIssue, &article.AutoGenerated); err != nil {
return nil, fmt.Errorf("error scanning article row: %v", err) return nil, fmt.Errorf("error scanning article row: %v", err)
} }
@ -147,9 +152,9 @@ func (db *DB) GetCurrentIssueArticles() ([]*Article, error) {
txOptions := &sql.TxOptions{Isolation: sql.LevelSerializable} txOptions := &sql.TxOptions{Isolation: sql.LevelSerializable}
issueQuery := "SELECT id FROM issues WHERE published = false" issueQuery := "SELECT id FROM issues WHERE published = false"
articlesQuery := ` articlesQuery := `
SELECT id, title, created, description, link, enc_url, enc_length, enc_type, author_id SELECT id, title, created, description, link, enc_url, enc_length, enc_type, author_id, auto_generated
FROM articles FROM articles
WHERE issue_id = ? AND published = true WHERE issue_id = ? AND published = true AND is_in_issue = true
` `
for i := 0; i < TxMaxRetries; i++ { for i := 0; i < TxMaxRetries; i++ {
@ -182,7 +187,7 @@ func (db *DB) GetCurrentIssueArticles() ([]*Article, error) {
if err = rows.Scan(&article.ID, &article.Title, &created, if err = rows.Scan(&article.ID, &article.Title, &created,
&article.Description, &article.Link, &article.EncURL, &article.EncLength, &article.Description, &article.Link, &article.EncURL, &article.EncLength,
&article.EncType, &article.AuthorID); err != nil { &article.EncType, &article.AuthorID, &article.AutoGenerated); err != nil {
if rollbackErr := tx.Rollback(); rollbackErr != nil { if rollbackErr := tx.Rollback(); rollbackErr != nil {
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr) log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
} }

View File

@ -3,6 +3,7 @@ package backend
import ( import (
"flag" "flag"
"fmt" "fmt"
"io/fs"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@ -12,6 +13,7 @@ import (
type Config struct { type Config struct {
ArticleDir string ArticleDir string
ConfigFile string
DBName string DBName string
Description string Description string
Domain string Domain string
@ -30,56 +32,72 @@ type Config struct {
func newConfig() *Config { func newConfig() *Config {
return &Config{ return &Config{
ArticleDir: "/var/www/cpolis/articles", ArticleDir: "/var/www/cpolis/articles",
ConfigFile: "/etc/cpolis/config.toml",
DBName: "cpolis", DBName: "cpolis",
FirebaseKey: "/var/www/cpolis/serviceAccountKey.json", FirebaseKey: "/var/www/cpolis/serviceAccountKey.json",
KeyFile: "/var/www/cpolis/cpolis.key", KeyFile: "/var/www/cpolis/cpolis.key",
LogFile: "/var/log/cpolis.log", LogFile: "/var/log/cpolis.log",
PDFDir: "/var/www/cpolis/pdfs", PDFDir: "/var/www/cpolis/pdfs",
Port: ":8080",
PicsDir: "/var/www/cpolis/pics", PicsDir: "/var/www/cpolis/pics",
RSSFile: "/var/www/cpolis/cpolis.rss", RSSFile: "/var/www/cpolis/cpolis.rss",
WebDir: "/var/www/cpolis/web", WebDir: "/var/www/cpolis/web",
} }
} }
func (c *Config) readFile() error { func mkDir(path string, perm fs.FileMode) (string, error) {
cfgFile, err := filepath.Abs(os.Getenv("HOME") + "/.config/cpolis/config.toml") var err error
stringSlice := strings.Split(path, "/")
name := stringSlice[len(stringSlice)-1]
path, err = filepath.Abs(path)
if err != nil { if err != nil {
return fmt.Errorf("error getting absolute path for config file: %v", err) return "", fmt.Errorf("error finding absolute path for %v directory: %v", name, err)
}
if err = os.MkdirAll(path, perm); err != nil {
return "", fmt.Errorf("error creating %v directory: %v", name, err)
} }
_, err = os.Stat(cfgFile) return path, nil
if os.IsNotExist(err) { }
fileStrings := strings.Split(cfgFile, "/")
dir := strings.Join(fileStrings[0:len(fileStrings)-1], "/") func mkFile(path string, filePerm, dirPerm fs.FileMode) (string, error) {
if err = os.MkdirAll(dir, 0755); err != nil { var err error
return fmt.Errorf("error creating config directory: %v", err)
path, err = filepath.Abs(path)
if err != nil {
return "", fmt.Errorf("error finding absolute path for %v: %v", path, err)
}
stringSlice := strings.Split(path, "/")
_, err = os.Stat(path)
if os.IsNotExist(err) {
dir := strings.Join(stringSlice[:len(stringSlice)-1], "/")
if err = os.MkdirAll(dir, dirPerm); err != nil {
return "", fmt.Errorf("error creating %v: %v", dir, err)
} }
fileName := fileStrings[len(fileStrings)-1] fileName := stringSlice[len(stringSlice)-1]
file, err := os.Create(dir + "/" + fileName) file, err := os.Create(dir + "/" + fileName)
if err != nil { if err != nil {
return fmt.Errorf("error creating config file: %v", err) return "", fmt.Errorf("error creating %v: %v", fileName, err)
} }
defer file.Close() defer file.Close()
if err = file.Chmod(0644); err != nil { if err = file.Chmod(filePerm); err != nil {
return fmt.Errorf("error setting permissions for config file: %v", err) return "", fmt.Errorf("error setting permissions for %v: %v", fileName, err)
}
} else {
_, err = toml.DecodeFile(cfgFile, c)
if err != nil {
return fmt.Errorf("error reading config file: %v", err)
} }
} }
return nil return path, nil
} }
func (c *Config) handleCliArgs() error { func (c *Config) handleCliArgs() error {
var port int
var err error var err error
port := 8080
flag.StringVar(&c.ArticleDir, "articles", c.ArticleDir, "articles directory") flag.StringVar(&c.ArticleDir, "articles", c.ArticleDir, "articles directory")
flag.StringVar(&c.ConfigFile, "config", c.ConfigFile, "config file")
flag.StringVar(&c.DBName, "db", c.DBName, "DB name") flag.StringVar(&c.DBName, "db", c.DBName, "DB name")
flag.StringVar(&c.Description, "desc", c.Description, "channel description") flag.StringVar(&c.Description, "desc", c.Description, "channel description")
flag.StringVar(&c.Domain, "domain", c.Domain, "domain name") flag.StringVar(&c.Domain, "domain", c.Domain, "domain name")
@ -95,58 +113,114 @@ func (c *Config) handleCliArgs() error {
flag.IntVar(&port, "port", port, "port") flag.IntVar(&port, "port", port, "port")
flag.Parse() flag.Parse()
c.ArticleDir, err = filepath.Abs(c.ArticleDir)
if err != nil {
return fmt.Errorf("error finding absolute path for articles directory: %v", err)
}
if err = os.MkdirAll(c.ArticleDir, 0755); err != nil {
return fmt.Errorf("error creating articles directory: %v", err)
}
c.FirebaseKey, err = filepath.Abs(c.FirebaseKey)
if err != nil {
return fmt.Errorf("error finding absolute path for Firebase service account key file: %v", err)
}
c.KeyFile, err = filepath.Abs(c.KeyFile)
if err != nil {
return fmt.Errorf("error finding absolute path for key file: %v", err)
}
c.LogFile, err = filepath.Abs(c.LogFile)
if err != nil {
return fmt.Errorf("error finding absolute path for log file: %v", err)
}
c.PDFDir, err = filepath.Abs(c.PDFDir)
if err != nil {
return fmt.Errorf("error finding absolute path for pdfs directory: %v", err)
}
if err = os.MkdirAll(c.PDFDir, 0755); err != nil {
return fmt.Errorf("error creating pdfs directory: %v", err)
}
c.PicsDir, err = filepath.Abs(c.PicsDir)
if err != nil {
return fmt.Errorf("error finding absolute path for pics directory: %v", err)
}
if err = os.MkdirAll(c.PicsDir, 0755); err != nil {
return fmt.Errorf("error creating pics directory: %v", err)
}
c.Port = fmt.Sprint(":", port) c.Port = fmt.Sprint(":", port)
c.ConfigFile, err = mkFile(c.ConfigFile, 0600, 0700)
c.RSSFile, err = filepath.Abs(c.RSSFile)
if err != nil { if err != nil {
return fmt.Errorf("error finding absolute path for RSS file: %v", err) return fmt.Errorf("error setting up file: %v", err)
} }
c.WebDir, err = filepath.Abs(c.WebDir) return nil
}
func (c *Config) handleFile(configFile string) error {
_, err := toml.DecodeFile(configFile, c)
if err != nil { if err != nil {
return fmt.Errorf("error finding absolute path for web directory: %v", err) return fmt.Errorf("error reading config file: %v", err)
} }
if err = os.MkdirAll(c.WebDir, 0755); err != nil {
return fmt.Errorf("error creating web directory: %v", err) return nil
}
func (c *Config) setupConfig(cliConfig *Config) error {
var err error
defaultConfig := newConfig()
if cliConfig.ArticleDir != defaultConfig.ArticleDir {
c.ArticleDir = cliConfig.ArticleDir
}
c.ArticleDir, err = mkDir(c.ArticleDir, 0700)
if err != nil {
return fmt.Errorf("error setting up directory: %v", err)
}
if cliConfig.DBName != defaultConfig.DBName {
c.DBName = cliConfig.DBName
}
if cliConfig.Description != defaultConfig.Description {
c.Description = cliConfig.Description
}
if cliConfig.Domain != defaultConfig.Domain {
c.Domain = cliConfig.Domain
}
if cliConfig.FirebaseKey != defaultConfig.FirebaseKey {
c.FirebaseKey = cliConfig.FirebaseKey
}
c.FirebaseKey, err = mkFile(c.FirebaseKey, 0600, 0700)
if err != nil {
return fmt.Errorf("error setting up file: %v", err)
}
if cliConfig.KeyFile != defaultConfig.KeyFile {
c.KeyFile = cliConfig.KeyFile
}
c.KeyFile, err = mkFile(c.KeyFile, 0600, 0700)
if err != nil {
return fmt.Errorf("error setting up file: %v", err)
}
if cliConfig.Link != defaultConfig.Link {
c.Link = cliConfig.Link
}
if cliConfig.LogFile != defaultConfig.LogFile {
c.LogFile = cliConfig.LogFile
}
c.LogFile, err = mkFile(c.LogFile, 0600, 0700)
if err != nil {
return fmt.Errorf("error setting up file: %v", err)
}
if cliConfig.PDFDir != defaultConfig.PDFDir {
c.PDFDir = cliConfig.PDFDir
}
c.PDFDir, err = mkDir(c.PDFDir, 0700)
if err != nil {
return fmt.Errorf("error setting up directory: %v", err)
}
if cliConfig.PicsDir != defaultConfig.PicsDir {
c.PicsDir = cliConfig.PicsDir
}
c.PicsDir, err = mkDir(c.PicsDir, 0700)
if err != nil {
return fmt.Errorf("error setting up directory: %v", err)
}
if cliConfig.Port != defaultConfig.Port {
c.Port = cliConfig.Port
}
if cliConfig.RSSFile != defaultConfig.RSSFile {
c.RSSFile = cliConfig.RSSFile
}
c.RSSFile, err = mkFile(c.RSSFile, 0600, 0700)
if err != nil {
return fmt.Errorf("error setting up file: %v", err)
}
if cliConfig.Title != defaultConfig.Title {
c.Title = cliConfig.Title
}
if cliConfig.WebDir != defaultConfig.WebDir {
c.WebDir = cliConfig.WebDir
}
c.WebDir, err = mkDir(c.WebDir, 0700)
if err != nil {
return fmt.Errorf("error setting up directory: %v", err)
} }
return nil return nil
@ -154,13 +228,19 @@ func (c *Config) handleCliArgs() error {
func HandleConfig() (*Config, error) { func HandleConfig() (*Config, error) {
config := newConfig() config := newConfig()
cliConfig := newConfig()
if err := config.readFile(); err != nil { if err := cliConfig.handleCliArgs(); err != nil {
return nil, fmt.Errorf("error reading config file: %v", err) return nil, fmt.Errorf("error handling cli arguments: %v", err)
} }
if err := config.handleCliArgs(); err != nil { err := config.handleFile(cliConfig.ConfigFile)
return nil, fmt.Errorf("error handling cli arguments: %v", err) if err != nil {
return nil, fmt.Errorf("error reading configuration file: %v", err)
}
if err = config.setupConfig(cliConfig); err != nil {
return nil, fmt.Errorf("error setting up files: %v", err)
} }
return config, nil return config, nil

View File

@ -23,10 +23,6 @@ func GenerateRSS(c *Config, db *DB) (*string, error) {
} }
for _, article := range articles { for _, article := range articles {
if !article.Published {
continue
}
tags, err := db.GetArticleTags(article.ID) tags, err := db.GetArticleTags(article.ID)
if err != nil { if err != nil {
return nil, fmt.Errorf("error getting tags for articles for RSS feed: %v", err) return nil, fmt.Errorf("error getting tags for articles for RSS feed: %v", err)
@ -35,7 +31,13 @@ func GenerateRSS(c *Config, db *DB) (*string, error) {
for _, tag := range tags { for _, tag := range tags {
tagNames = append(tagNames, tag.Name) tagNames = append(tagNames, tag.Name)
} }
tagNames = append(tagNames, fmt.Sprint("Orient Express ", article.IssueID))
if article.IsInIssue || article.AutoGenerated {
tagNames = append(tagNames, fmt.Sprint("Orient Express ", article.IssueID))
}
if article.AutoGenerated {
tagNames = append(tagNames, "autogenerated")
}
user, err := db.GetUser(article.AuthorID) user, err := db.GetUser(article.AuthorID)
if err != nil { if err != nil {
@ -61,9 +63,8 @@ func GenerateRSS(c *Config, db *DB) (*string, error) {
PubDate: article.Created.Format(time.RFC1123Z), PubDate: article.Created.Format(time.RFC1123Z),
Title: articleTitle, Title: articleTitle,
} }
fmt.Println(article.Link, ": ", len(article.Link))
if article.Title == "Autogenerated cpolis Issue Article" { if article.AutoGenerated {
item.Enclosure = &rss.Enclosure{ item.Enclosure = &rss.Enclosure{
Url: article.EncURL, Url: article.EncURL,
Lenght: article.EncLength, Lenght: article.EncLength,

View File

@ -73,11 +73,13 @@ func SubmitArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
} }
article := &b.Article{ article := &b.Article{
Title: r.PostFormValue("article-title"), Title: r.PostFormValue("article-title"),
Description: r.PostFormValue("article-description"), Description: r.PostFormValue("article-description"),
Published: false, Published: false,
Rejected: false, Rejected: false,
AuthorID: session.Values["id"].(int64), AuthorID: session.Values["id"].(int64),
IsInIssue: r.PostFormValue("issue") == "on",
AutoGenerated: false,
} }
article.ID, err = db.AddArticle(article) article.ID, err = db.AddArticle(article)
@ -153,6 +155,7 @@ func ResubmitArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
&b.Attribute{Table: "articles", ID: id, AttName: "title", Value: title}, &b.Attribute{Table: "articles", ID: id, AttName: "title", Value: title},
&b.Attribute{Table: "articles", ID: id, AttName: "description", Value: description}, &b.Attribute{Table: "articles", ID: id, AttName: "description", Value: description},
&b.Attribute{Table: "articles", ID: id, AttName: "rejected", Value: false}, &b.Attribute{Table: "articles", ID: id, AttName: "rejected", Value: false},
&b.Attribute{Table: "articles", ID: id, AttName: "is_in_issue", Value: r.PostFormValue("issue") == "on"},
); err != nil { ); err != nil {
log.Println(err) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
@ -240,46 +243,27 @@ func ReviewUnpublishedArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.Hand
return return
} }
type htmlData struct { data := new(struct {
Title string Article *b.Article
Description string Content template.HTML
Content template.HTML Tags []*b.Tag
Tags []*b.Tag })
ID int64
}
var err error id, err := strconv.ParseInt(r.PathValue("id"), 10, 64)
data := new(htmlData)
data.ID, err = strconv.ParseInt(r.PathValue("id"), 10, 64)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
article, err := db.GetArticle(data.ID) data.Article, err = db.GetArticle(id)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
data.Title, err = b.ConvertToPlain(article.Title) articleAbsName := fmt.Sprint(c.ArticleDir, "/", data.Article.ID, ".md")
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
data.Description, err = b.ConvertToPlain(article.Description)
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
articleAbsName := fmt.Sprint(c.ArticleDir, "/", article.ID, ".md")
contentBytes, err := os.ReadFile(articleAbsName) contentBytes, err := os.ReadFile(articleAbsName)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
@ -295,7 +279,7 @@ func ReviewUnpublishedArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.Hand
} }
data.Content = template.HTML(content) data.Content = template.HTML(content)
data.Tags, err = db.GetArticleTags(data.ID) data.Tags, err = db.GetArticleTags(data.Article.ID)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
@ -314,13 +298,12 @@ func ReviewRejectedArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.Handler
return return
} }
type htmlData struct { data := new(struct {
Selected map[int64]bool Selected map[int64]bool
Article *b.Article Article *b.Article
Content string Content string
Tags []*b.Tag Tags []*b.Tag
} })
data := new(htmlData)
id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) id, err := strconv.ParseInt(r.PathValue("id"), 10, 64)
if err != nil { if err != nil {
@ -378,13 +361,20 @@ func PublishArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
} }
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.StatusBadRequest)
return
}
article, err := db.GetArticle(id)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
if err = db.AddArticleToCurrentIssue(id); err != nil { if err = db.AddArticleToCurrentIssue(article.ID); err != nil {
log.Println(err) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
@ -521,9 +511,16 @@ func ShowPublishedArticles(c *b.Config, db *b.DB, s *b.CookieStore) http.Handler
return return
} }
filteredArticles := make([]*b.Article, 0)
for _, article := range publishedArticles {
if !article.AutoGenerated {
filteredArticles = append(filteredArticles, article)
}
}
tmpl, err := template.ParseFiles(c.WebDir + "/templates/published-articles.html") tmpl, err := template.ParseFiles(c.WebDir + "/templates/published-articles.html")
tmpl = template.Must(tmpl, err) tmpl = template.Must(tmpl, err)
tmpl.ExecuteTemplate(w, "page-content", publishedArticles) tmpl.ExecuteTemplate(w, "page-content", filteredArticles)
} }
} }
@ -572,7 +569,8 @@ func ReviewArticleForDeletion(c *b.Config, db *b.DB, s *b.CookieStore) http.Hand
return return
} }
contentBytes, err := os.ReadFile(article.Link) articleAbsName := fmt.Sprint(c.ArticleDir, "/", article.ID, ".md")
contentBytes, err := os.ReadFile(articleAbsName)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
@ -620,6 +618,12 @@ func DeleteArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
return return
} }
if err = os.Remove(fmt.Sprint(c.ArticleDir, "/", id, ".md")); err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
feed, err := b.GenerateRSS(c, db) feed, err := b.GenerateRSS(c, db)
if err != nil { if err != nil {
log.Println(err) log.Println(err)

View File

@ -59,16 +59,16 @@ func PublishLatestIssue(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFun
mimeType := mime.TypeByExtension(filepath.Ext(imgAbsName)) mimeType := mime.TypeByExtension(filepath.Ext(imgAbsName))
article := &b.Article{ article := &b.Article{
Title: "Autogenerated cpolis Issue Article", Title: r.PostFormValue("issue-title"),
EncURL: fmt.Sprint(c.Domain, "/image/serve/", imgFileName), EncURL: fmt.Sprint(c.Domain, "/image/serve/", imgFileName),
EncLength: int(imgSize), EncLength: int(imgSize),
EncType: mimeType, EncType: mimeType,
Published: true, Published: true,
Rejected: false, Rejected: false,
Created: time.Now(), Created: time.Now(),
AuthorID: session.Values["id"].(int64), AuthorID: session.Values["id"].(int64),
AutoGenerated: true,
} }
fmt.Println(article.Link)
article.ID, err = db.AddArticle(article) article.ID, err = db.AddArticle(article)
if err != nil { if err != nil {

59
cmd/frontend/pdf.go Normal file
View File

@ -0,0 +1,59 @@
package frontend
import (
"fmt"
"io"
"log"
"net/http"
"os"
"path/filepath"
"github.com/google/uuid"
b "streifling.com/jason/cpolis/cmd/backend"
)
func UploadPDF(c *b.Config, s *b.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if _, err := getSession(w, r, c, s); err != nil {
return
}
if err := r.ParseMultipartForm(10 << 20); err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
file, _, err := r.FormFile("pdf-upload")
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer file.Close()
filename := fmt.Sprint(uuid.New(), ".pdf")
absFilepath, err := filepath.Abs(fmt.Sprint(c.PDFDir, "/", filename))
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
pdf, err := os.Create(absFilepath)
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer pdf.Close()
if _, err = io.Copy(pdf, file); err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
}
}

View File

@ -81,6 +81,7 @@ func main() {
mux.HandleFunc("POST /issue/publish", f.PublishLatestIssue(config, db, store)) mux.HandleFunc("POST /issue/publish", f.PublishLatestIssue(config, db, store))
mux.HandleFunc("POST /issue/upload-image", f.UploadIssueImage(config, store)) mux.HandleFunc("POST /issue/upload-image", f.UploadIssueImage(config, store))
mux.HandleFunc("POST /login", f.Login(config, db, store)) mux.HandleFunc("POST /login", f.Login(config, db, store))
mux.HandleFunc("POST /pdf/upload", f.UploadPDF(config, store))
mux.HandleFunc("POST /tag/add", f.AddTag(config, db, store)) mux.HandleFunc("POST /tag/add", f.AddTag(config, db, store))
mux.HandleFunc("POST /user/add", f.AddUser(config, db, store)) mux.HandleFunc("POST /user/add", f.AddUser(config, db, store))
mux.HandleFunc("POST /user/add-first", f.AddFirstUser(config, db, store)) mux.HandleFunc("POST /user/add-first", f.AddFirstUser(config, db, store))

View File

@ -21,18 +21,20 @@ CREATE TABLE issues (
); );
CREATE TABLE articles ( CREATE TABLE articles (
id INT AUTO_INCREMENT, id INT AUTO_INCREMENT,
title VARCHAR(255) NOT NULL, title VARCHAR(255) NOT NULL,
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
description TEXT NOT NULL, description TEXT NOT NULL,
link VARCHAR(255), link VARCHAR(255),
enc_url VARCHAR(255), enc_url VARCHAR(255),
enc_length INT, enc_length INT,
enc_type VARCHAR(255), enc_type VARCHAR(255),
published BOOL NOT NULL, published BOOL NOT NULL,
rejected BOOL NOT NULL, rejected BOOL NOT NULL,
author_id INT NOT NULL, author_id INT NOT NULL,
issue_id INT NOT NULL, issue_id INT NOT NULL,
is_in_issue BOOL NOT NULL,
auto_generated BOOL NOT NULL,
PRIMARY KEY (id), PRIMARY KEY (id),
FOREIGN KEY (author_id) REFERENCES users (id), FOREIGN KEY (author_id) REFERENCES users (id),
FOREIGN KEY (issue_id) REFERENCES issues (id) FOREIGN KEY (issue_id) REFERENCES issues (id)

81
go.mod
View File

@ -1,59 +1,62 @@
module streifling.com/jason/cpolis module streifling.com/jason/cpolis
go 1.22.0 go 1.23
toolchain go1.23.0
require ( require (
firebase.google.com/go/v4 v4.14.1 firebase.google.com/go/v4 v4.14.1
git.streifling.com/jason/rss v0.1.3 git.streifling.com/jason/rss v0.1.3
github.com/BurntSushi/toml v1.3.2 github.com/BurntSushi/toml v1.4.0
github.com/go-sql-driver/mysql v1.7.1 github.com/go-sql-driver/mysql v1.8.1
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/gorilla/sessions v1.2.2 github.com/gorilla/sessions v1.4.0
github.com/microcosm-cc/bluemonday v1.0.26 github.com/microcosm-cc/bluemonday v1.0.27
github.com/yuin/goldmark v1.7.0 github.com/yuin/goldmark v1.7.4
golang.org/x/crypto v0.21.0 golang.org/x/crypto v0.26.0
golang.org/x/term v0.18.0 golang.org/x/term v0.23.0
google.golang.org/api v0.170.0 google.golang.org/api v0.195.0
) )
require ( require (
cloud.google.com/go v0.112.1 // indirect cloud.google.com/go v0.115.1 // indirect
cloud.google.com/go/compute v1.24.0 // indirect cloud.google.com/go/auth v0.9.2 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect
cloud.google.com/go/firestore v1.15.0 // indirect cloud.google.com/go/compute/metadata v0.5.0 // indirect
cloud.google.com/go/iam v1.1.7 // indirect cloud.google.com/go/firestore v1.16.0 // indirect
cloud.google.com/go/longrunning v0.5.5 // indirect cloud.google.com/go/iam v1.2.0 // indirect
cloud.google.com/go/storage v1.40.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
github.com/MicahParks/keyfunc v1.9.0 // indirect github.com/MicahParks/keyfunc v1.9.0 // indirect
github.com/aymerick/douceur v0.2.0 // indirect github.com/aymerick/douceur v0.2.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect github.com/golang/protobuf v1.5.4 // indirect
github.com/google/s2a-go v0.1.7 // indirect github.com/google/s2a-go v0.1.8 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.3 // indirect
github.com/googleapis/gax-go/v2 v2.12.3 // indirect github.com/googleapis/gax-go/v2 v2.13.0 // indirect
github.com/gorilla/css v1.0.0 // indirect github.com/gorilla/css v1.0.1 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect github.com/gorilla/securecookie v1.1.2 // indirect
go.opencensus.io v0.24.0 // indirect go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.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.49.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
go.opentelemetry.io/otel v1.24.0 // indirect go.opentelemetry.io/otel v1.29.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect go.opentelemetry.io/otel/metric v1.29.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect go.opentelemetry.io/otel/trace v1.29.0 // indirect
golang.org/x/net v0.23.0 // indirect golang.org/x/net v0.28.0 // indirect
golang.org/x/oauth2 v0.18.0 // indirect golang.org/x/oauth2 v0.22.0 // indirect
golang.org/x/sync v0.6.0 // indirect golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.18.0 // indirect golang.org/x/sys v0.24.0 // indirect
golang.org/x/text v0.14.0 // indirect golang.org/x/text v0.17.0 // indirect
golang.org/x/time v0.5.0 // indirect golang.org/x/time v0.6.0 // indirect
google.golang.org/appengine v1.6.8 // indirect google.golang.org/appengine/v2 v2.0.6 // indirect
google.golang.org/appengine/v2 v2.0.2 // indirect google.golang.org/genproto v0.0.0-20240827150818-7e3bb234dfed // indirect
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240314234333-6e1732d8331c // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240311132316-a219d84964c2 // indirect google.golang.org/grpc v1.66.0 // indirect
google.golang.org/grpc v1.62.1 // indirect google.golang.org/protobuf v1.34.2 // indirect
google.golang.org/protobuf v1.33.0 // indirect
) )

173
go.sum
View File

@ -1,25 +1,29 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM= cloud.google.com/go v0.115.1 h1:Jo0SM9cQnSkYfp44+v+NQXHpcHqlnRJk2qxh6yvxxxQ=
cloud.google.com/go v0.112.1/go.mod h1:+Vbu+Y1UU+I1rjmzeMOb/8RfkKJK2Gyxi1X6jJCZLo4= cloud.google.com/go v0.115.1/go.mod h1:DuujITeaufu3gL68/lOFIirVNJwQeyf5UXyi+Wbgknc=
cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg= cloud.google.com/go/auth v0.9.2 h1:I+Rq388FYU8QdbVB1IiPd+6KNdrqtAPE/asiKHShBLM=
cloud.google.com/go/compute v1.24.0/go.mod h1:kw1/T+h/+tK2LJK0wiPPx1intgdAM3j/g3hFDlscY40= cloud.google.com/go/auth v0.9.2/go.mod h1:7z6VY+7h3KUdRov5F1i8NDP5ZzWKYmEPO842BgCsmTk=
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY=
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc=
cloud.google.com/go/firestore v1.15.0 h1:/k8ppuWOtNuDHt2tsRV42yI21uaGnKDEQnRFeBpbFF8= cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY=
cloud.google.com/go/firestore v1.15.0/go.mod h1:GWOxFXcv8GZUtYpWHw/w6IuYNux/BtmeVTMmjrm4yhk= cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
cloud.google.com/go/iam v1.1.7 h1:z4VHOhwKLF/+UYXAJDFwGtNF0b6gjsW1Pk9Ml0U/IoM= cloud.google.com/go/firestore v1.16.0 h1:YwmDHcyrxVRErWcgxunzEaZxtNbc8QoFYA/JOEwDPgc=
cloud.google.com/go/iam v1.1.7/go.mod h1:J4PMPg8TtyurAUvSmPj8FF3EDgY1SPRZxcUGrn7WXGA= cloud.google.com/go/firestore v1.16.0/go.mod h1:+22v/7p+WNBSQwdSwP57vz47aZiY+HrDkrOsJNhk7rg=
cloud.google.com/go/longrunning v0.5.5 h1:GOE6pZFdSrTb4KAiKnXsJBtlE6mEyaW44oKyMILWnOg= cloud.google.com/go/iam v1.2.0 h1:kZKMKVNk/IsSSc/udOb83K0hL/Yh/Gcqpz+oAkoIFN8=
cloud.google.com/go/longrunning v0.5.5/go.mod h1:WV2LAxD8/rg5Z1cNW6FJ/ZpX4E4VnDnoTk0yawPBB7s= cloud.google.com/go/iam v1.2.0/go.mod h1:zITGuWgsLZxd8OwAlX+eMFgZDXzBm7icj1PVTYG766Q=
cloud.google.com/go/storage v1.40.0 h1:VEpDQV5CJxFmJ6ueWNsKxcr1QAYOXEgxDa+sBbJahPw= cloud.google.com/go/longrunning v0.6.0 h1:mM1ZmaNsQsnb+5n1DNPeL0KwQd9jQRqSqSDEkBZr+aI=
cloud.google.com/go/storage v1.40.0/go.mod h1:Rrj7/hKlG87BLqDJYtwR0fbPld8uJPbQ2ucUMY7Ir0g= 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=
firebase.google.com/go/v4 v4.14.1 h1:4qiUETaFRWoFGE1XP5VbcEdtPX93Qs+8B/7KvP2825g= 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= 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 h1:fd3j4ZtcLehapcmmroo3AP3X34gRHC4xzpfV6bDV1ZU=
git.streifling.com/jason/rss v0.1.3/go.mod h1:gpZF0nZbQSstMpyHD9DTAvlQEG7v4pjO5c7aIMWM4Jg= 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 v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/MicahParks/keyfunc v1.9.0 h1:lhKd5xrFHLNOWrDc4Tyb/Q1AJ4LCzQ48GVJyVIID3+o= 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/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= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
@ -37,12 +41,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 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= 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.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= 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-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= 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 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
@ -52,7 +56,6 @@ 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/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/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.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.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/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
@ -62,7 +65,6 @@ 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.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 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.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 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@ -76,25 +78,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/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 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc=
github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0=
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM=
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= github.com/googleapis/enterprise-certificate-proxy v0.3.3 h1:QRje2j5GZimBzlbhGA2V2QlGNgL8G6e+wGo/+/2bWI0=
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/enterprise-certificate-proxy v0.3.3/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
github.com/googleapis/gax-go/v2 v2.12.3 h1:5/zPPDvw8Q1SuXjrqrZslrqT7dL/uJT2CQii/cLCKqA= github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s=
github.com/googleapis/gax-go/v2 v2.12.3/go.mod h1:AKloxT6GtNbaLm8QTNSidHUVsHYcBHwWRvkNFJUQcS4= github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A=
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY= github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ=
github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ= github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=
github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58= github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs= github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 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/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= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
@ -104,30 +106,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.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.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.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.7.0 h1:EfOIvIMZIzHdB/R/zVrikYLPPwJlfMcNczJFMs1m6sA= github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg=
github.com/yuin/goldmark v1.7.0/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
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.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= 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.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= 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.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8=
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc=
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8=
go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw= go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE=
go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc= go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg=
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 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-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.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 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-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@ -141,19 +143,18 @@ 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-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-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-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.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA=
golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 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-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-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.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.8.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-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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -162,20 +163,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-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-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.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.24.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-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.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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.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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.6.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-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-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@ -185,32 +186,28 @@ 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/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-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= google.golang.org/api v0.195.0 h1:Ude4N8FvTKnnQJHU48RFI40jOBgIrL8Zqr3/QeST6yU=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= google.golang.org/api v0.195.0/go.mod h1:DOGRWuv3P8TU8Lnz7uQc4hyNqrBpMtD9ppW3wBJurgc=
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.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 v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine/v2 v2.0.6 h1:LvPZLGuchSBslPBp+LAhihBeGSiRh1myRoYK4NtuBIw=
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/appengine/v2 v2.0.6/go.mod h1:WoEXGoXNfa0mLvaH5sV3ZSGXwVmy8yf7Z1JKf3J3wLI=
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-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-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-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y= google.golang.org/genproto v0.0.0-20240827150818-7e3bb234dfed h1:4C4dbrVFtfIp3GXJdMX1Sj25mahfn5DywOo65/2ISQ8=
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s= google.golang.org/genproto v0.0.0-20240827150818-7e3bb234dfed/go.mod h1:ICjniACoWvcDz8c8bOsHVKuuSGDJy1z5M4G0DM3HzTc=
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-20240827150818-7e3bb234dfed h1:3RgNmBoI9MZhsj3QxC+AP/qQhNwpCLOvYDYYsFrhFt0=
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/api v0.0.0-20240827150818-7e3bb234dfed/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240311132316-a219d84964c2 h1:9IZDv+/GcI6u+a4jRFRLxQs0RUCfavGfoOgEW6jpkI0= google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed h1:J6izYgfBXAI3xTKLgxzTmUltdYaLsuBxFCgDHWJ/eXg=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240311132316-a219d84964c2/go.mod h1:UCOku4NytXMJuLQE5VuqA5lX3PcHCBo8pxNyvkf4xBs= google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 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.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 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.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c=
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 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-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@ -221,9 +218,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.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.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.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 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.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View File

@ -7,4 +7,5 @@ module.exports = {
plugins: [ plugins: [
require('@tailwindcss/typography') require('@tailwindcss/typography')
], ],
darkMode: 'selector',
} }

View File

@ -43,7 +43,9 @@ chmod +x $CPOLIS_DIR/tailwindcss
$CPOLIS_DIR/tailwindcss -i $CPOLIS_DIR/web/static/css/input.css -o $CPOLIS_DIR/web/static/css/style.css $CPOLIS_DIR/tailwindcss -i $CPOLIS_DIR/web/static/css/input.css -o $CPOLIS_DIR/web/static/css/style.css
echo '\nBuilding cpolis...' >&2 echo '\nBuilding cpolis...' >&2
go build -o $BIN_DIR/cpolis $CPOLIS_DIR/cmd/main.go cd $CPOLIS_DIR
go build -o $BIN_DIR/cpolis cmd/main.go
cd
echo '\nSetting up system files...' >&2 echo '\nSetting up system files...' >&2
sudo chown root:root $BIN_DIR/cpolis sudo chown root:root $BIN_DIR/cpolis

View File

@ -2,30 +2,21 @@
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;
body {
width: 800px;
@apply mx-auto text-slate-900;
}
h2 { h2 {
@apply font-bold mb-2 text-2xl; @apply font-bold mb-2 text-2xl;
} }
form { h3 {
@apply flex flex-col gap-y-3; @apply font-bold mb-2 text-xl;
}
input[type="file"] {
@apply border rounded-md w-full;
} }
input[type="password"], input[type="password"],
input[type="text"] { input[type="text"] {
@apply border h-8 rounded-md; @apply bg-slate-50 dark:bg-slate-950 border border-slate-200 dark:border-slate-800 h-8 rounded-md;
} }
textarea { textarea {
@apply border h-32 rounded-md; @apply bg-slate-50 dark:bg-slate-950 border border-slate-200 dark:border-slate-800 h-32 rounded-md;
} }
.btn-area { .btn-area {
@ -33,9 +24,41 @@ textarea {
} }
.btn { .btn {
@apply bg-slate-200 border my-2 px-3 py-2 rounded-md w-full hover:bg-slate-100; @apply bg-slate-200 dark:bg-slate-800 hover:bg-slate-100 dark:hover:bg-slate-900 border border-slate-200 dark:border-slate-800 my-2 px-3 py-2 rounded-md w-full;
} }
.action-btn { .action-btn {
@apply bg-slate-800 border my-2 px-3 py-2 rounded-md text-slate-50 w-full hover:bg-slate-700; @apply bg-slate-800 dark:bg-slate-200 hover:bg-slate-700 dark:hover:bg-slate-300 my-2 px-3 py-2 rounded-md text-slate-50 dark:text-slate-950 w-full;
}
.EasyMDEContainer .CodeMirror {
@apply bg-slate-50 dark:bg-slate-950 border-slate-200 dark:border-slate-800 text-slate-900 dark:text-slate-100
}
.EasyMDEContainer .cm-s-easymde .CodeMirror-cursor {
@apply border-slate-900 dark:border-slate-100
}
.EasyMDEContainer .editor-toolbar > * {
@apply text-slate-900 dark:text-slate-100
}
.EasyMDEContainer .editor-toolbar > .active, .editor-toolbar > button:hover, .editor-preview pre, .cm-s-easymde .cm-comment {
@apply bg-slate-100 dark:bg-slate-900
}
.EasyMDEContainer .CodeMirror-fullscreen {
@apply bg-slate-50 dark:bg-slate-950
}
.editor-toolbar {
@apply border border-slate-200 dark:border-slate-800
}
.editor-toolbar.fullscreen {
@apply bg-slate-50 dark:bg-slate-950
}
.editor-preview {
@apply bg-slate-50 dark:bg-slate-950
} }

View File

@ -7,7 +7,7 @@
<h3>Aktuelle Artikel</h3> <h3>Aktuelle Artikel</h3>
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
{{range .}} {{range .}}
<div class="border px-2 py-1 rounded-md"> <div class="border border-slate-200 dark:border-slate-800 px-2 py-1 rounded-md">
<h1 class="font-bold text-2xl">{{.Title}}</h1> <h1 class="font-bold text-2xl">{{.Title}}</h1>
<p>{{.Description}}</p> <p>{{.Description}}</p>
</div> </div>
@ -17,7 +17,12 @@
<div> <div>
<h3>Cover</h3> <h3>Cover</h3>
<input id="image-upload" name="issue-image" type="file" required hx-post="/issue/upload-image" /> <div class="grid grid-cols-2 gap-4 items-center">
<input class="h-full" name="issue-title" placeholder="Titel" 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" />
</div>
</div> </div>
<div> <div>

View File

@ -21,6 +21,11 @@
<div> <div>
<span>Tags</span> <span>Tags</span>
<div class="flex flex-wrap gap-x-4"> <div class="flex flex-wrap gap-x-4">
<div>
<input id="issue" name="issue" type="checkbox" />
<label for="issue">Orient Express</label>
</div>
{{range .Tags}} {{range .Tags}}
<div> <div>
<input id="{{.Name}}" name="tags" type="checkbox" value="{{.ID}}" /> <input id="{{.Name}}" name="tags" type="checkbox" value="{{.ID}}" />

View File

@ -4,44 +4,49 @@
{{if lt . 4}} {{if lt . 4}}
<div class="mb-3"> <div class="mb-3">
<h2>Autor</h2> <h2>Artikel</h2>
<div class="grid grid-cols-2 gap-x-4 gap-y-2"> <div class="grid 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/write" hx-target="#page-content">Artikel schreiben</button>
<button class="btn" hx-get="/article/all-rejected" hx-target="#page-content">Abgelehnte Artikel</button> <button class="btn" hx-get="/article/all-rejected" hx-target="#page-content">Abgelehnte Artikel</button>
<button class="btn" hx-get="/user/edit/self" hx-target="#page-content">Profil bearbeiten</button> {{if lt . 3}}<button class="btn" hx-get="/article/all-unpublished" hx-target="#page-content">
</div>
</div>
{{end}}
{{if lt . 3}}
<div class="mb-3">
<h2>Redakteur</h2>
<div class="grid grid-cols-2 gap-4">
<button class="btn" hx-get="/article/all-unpublished" hx-target="#page-content">
Unveröffentlichte Artikel Unveröffentlichte Artikel
</button> </button>{{end}}
<button class="btn" hx-get="/tag/create" hx-target="#page-content">Neuer Tag</button> {{if lt . 2}}<button class="btn" hx-get="/article/all-published" hx-target="#page-content">
Artikel löschen
</button>{{end}}
{{if lt . 3}}<button class="btn" hx-get="/tag/create" hx-target="#page-content">Neuer Tag</button>{{end}}
</div> </div>
</div> </div>
{{end}} {{end}}
{{if lt . 2}} {{if lt . 2}}
<div class="mb-3"> <div class="mb-3">
<h2>Herausgeber</h2> <h2>Ausgabe</h2>
<div class="grid grid-cols-2 gap-4">
<button class="btn" hx-get="/issue/this" hx-target="#page-content">Diese Ausgabe</button>
<button class="btn" hx-get="/article/all-published" hx-target="#page-content">Artikel löschen</button>
</div>
</div>
{{end}}
{{if eq . 0}}
<div class="mb-3">
<h2>Administrator</h2>
<div class="grid grid-cols-2 gap-x-4 gap-y-2"> <div class="grid grid-cols-2 gap-x-4 gap-y-2">
<button class="btn" hx-get="/user/create" hx-target="#page-content">Benutzer hinzufügen</button> <button class="btn" hx-get="/issue/this" hx-target="#page-content">Diese Ausgabe</button>
<button class="btn" hx-get="/user/show-all/edit" hx-target="#page-content">Benutzer bearbeiten</button> <form class="flex" hx-encoding="multipart/form-data">
<button class="btn" hx-get="/user/show-all/delete" hx-target="#page-content">Benutzer löschen</button> <label class="btn text-center" for="pdf-upload">PDF hochladen</label>
<input accept=".pdf" class="hidden" id="pdf-upload" name="pdf-upload" type="file"
hx-post="/pdf/upload" />
</form>
</div>
{{end}}
</div>
{{if lt . 4}}
<div class="mb-3">
<h2>Benutzer</h2>
<div class="grid 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}}
</div> </div>
</div> </div>
{{end}} {{end}}

View File

@ -5,13 +5,26 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>Orient Editor</title> <title>Orient Editor</title>
<link href="/web/static/css/style.css" rel="stylesheet"> <link href="/web/static/css/style.css" rel="stylesheet">
<link rel="stylesheet" href="https://unpkg.com/easymde/dist/easymde.min.css"> <link rel="stylesheet" href="https://unpkg.com/easymde/dist/easymde.min.css">
</head> </head>
<body class="flex flex-col justify-between min-h-screen bg-slate-50"> <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">
<header class="my-8"> <header class="my-8">
<h1 class="font-bold text-4xl text-center">Orient Editor</h1> <h1 class="font-bold text-4xl text-center">Orient Editor</h1>
<div class="ml-4">
<label class="cursor-pointer flex items-center relative" for="theme-toggle">
<div class="bg-slate-200 dark:bg-slate-800 block h-6 w-12 rounded-full"></div>
<div
class="absolute bg-slate-800 dark:bg-slate-50 dot left-1 top-1 h-4 w-4 rounded-full transform transition">
</div>
<span class="ml-2">Dunkel</span>
</label>
<input type="checkbox" id="theme-toggle" class="sr-only">
</div>
</header> </header>
<main class="mx-4"> <main class="mx-4">
@ -20,17 +33,44 @@
</div> </div>
</main> </main>
<footer class="my-8"> <footer class="text-center text-gray-500 my-8">
<p class="text-center text-gray-500 dark:text-gray-400"> <p>
&copy; 2024 Jason Streifling. Alle Rechte vorbehalten. &copy; 2024 Jason Streifling. Alle Rechte vorbehalten.
</p> </p>
<p class="text-center text-gray-500 dark:text-gray-400"> <p>
<strong>Hinweis:</strong> Diese Software befindet sich noch in der Entwicklung und kann Fehler enthalten. v0.9.1 - <strong>Hinweis:</strong> Diese Software befindet sich noch in der Entwicklung und kann Fehler
enthalten.
</p> </p>
</footer> </footer>
<script src="https://unpkg.com/htmx.org@2.0.1"></script> <script src="https://unpkg.com/htmx.org@2.0.2"></script>
<script src="https://unpkg.com/easymde/dist/easymde.min.js"></script> <script src="https://unpkg.com/easymde/dist/easymde.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
const toggleSwitch = document.getElementById('theme-toggle');
const dot = document.querySelector('.dot');
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark')
toggleSwitch.checked = true;
dot.classList.add('translate-x-6');
} else {
document.documentElement.classList.remove('dark')
}
toggleSwitch.addEventListener('change', () => {
if (toggleSwitch.checked) {
document.documentElement.classList.add('dark');
localStorage.theme = 'dark';
dot.classList.add('translate-x-6');
} else {
document.documentElement.classList.remove('dark');
localStorage.theme = 'light';
dot.classList.remove('translate-x-6');
}
});
});
</script>
</body> </body>
</html> </html>

View File

@ -21,6 +21,11 @@
<div> <div>
<span>Tags</span> <span>Tags</span>
<div class="flex flex-wrap gap-x-4"> <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}} {{range .Tags}}
<div> <div>
<input id="tag-{{.Name}}" name="tags" type="checkbox" value="{{.ID}}" {{if index $.Selected <input id="tag-{{.Name}}" name="tags" type="checkbox" value="{{.ID}}" {{if index $.Selected

View File

@ -3,24 +3,24 @@
<div> <div>
<span>Titel</span> <span>Titel</span>
<div class="bg-white border mb-3 px-2 py-2 rounded-md w-full"> <div class="bg-white border border-slate-200 dark:border-slate-800 mb-3 px-2 py-2 rounded-md w-full">
{{.Title}} {{.Title}}
</div> </div>
<span>Beschreibung</span> <span>Beschreibung</span>
<div class="bg-white border mb-3 px-2 py-2 rounded-md w-full"> <div class="bg-white border border-slate-200 dark:border-slate-800 mb-3 px-2 py-2 rounded-md w-full">
{{.Description}} {{.Description}}
</div> </div>
<span>Artikel</span> <span>Artikel</span>
<div class="bg-white border mb-3 px-2 py-2 rounded-md w-full"> <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"> <div class="prose">
{{.Content}} {{.Content}}
</div> </div>
</div> </div>
<span>Tags</span> <span>Tags</span>
<div class="bg-white border mb-3 px-2 py-2 rounded-md w-full"> <div class="bg-white border border-slate-200 dark:border-slate-800 mb-3 px-2 py-2 rounded-md w-full">
{{range .Tags}} {{range .Tags}}
{{.Name}} {{.Name}}
<br> <br>

View File

@ -3,34 +3,39 @@
<div> <div>
<span>Titel</span> <span>Titel</span>
<div class="bg-white border mb-3 px-2 py-2 rounded-md w-full"> <div class="bg-white border border-slate-200 dark:border-slate-800 mb-3 px-2 py-2 rounded-md w-full">
{{.Title}} {{.Article.Title}}
</div> </div>
<span>Beschreibung</span> <span>Beschreibung</span>
<div class="bg-white border mb-3 px-2 py-2 rounded-md w-full"> <div class="bg-white border border-slate-200 dark:border-slate-800 mb-3 px-2 py-2 rounded-md w-full">
{{.Description}} {{.Article.Description}}
</div> </div>
<span>Artikel</span> <span>Artikel</span>
<div class="bg-white border mb-3 px-2 py-2 rounded-md w-full"> <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"> <div class="prose">
{{.Content}} {{.Content}}
</div> </div>
</div> </div>
<span>Tags</span> <span>Tags</span>
<div class="bg-white border mb-3 px-2 py-2 rounded-md w-full"> <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}} {{range .Tags}}
{{.Name}} <span>{{.Name}}</span>
<br> <br>
{{end}} {{end}}
</div> </div>
<div class="btn-area"> <div class="btn-area">
<input class="action-btn" type="submit" value="Veröffentlichen" hx-get="/article/publish/{{.ID}}" <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" /> hx-target="#page-content" />
<input class="btn" type="submit" value="Ablehnen" hx-get="/article/reject/{{.ID}}" hx-target="#page-content" />
<button class="btn" hx-get="/hub" hx-target="#page-content">Zurück</button> <button class="btn" hx-get="/hub" hx-target="#page-content">Zurück</button>
</div> </div>
</div> </div>