Initial index support
This commit is contained in:
@ -386,12 +386,23 @@ func (db *DB) GetAllArticles() ([]*Article, error) {
|
||||
return articles, nil
|
||||
}
|
||||
|
||||
func WriteArticleToFile(c *Config, articleUUID uuid.UUID, content []byte) error {
|
||||
articlePath := filepath.Join(c.ArticleDir, fmt.Sprint(articleUUID, ".md"))
|
||||
func (a *Article) WriteContentToFile(c *Config, content []byte) error {
|
||||
articlePath := filepath.Join(c.ArticleDir, fmt.Sprint(a.UUID, ".md"))
|
||||
|
||||
if err := os.WriteFile(articlePath, content, 0644); err != nil {
|
||||
return fmt.Errorf("error writing article %v to file: %v", articleUUID, err)
|
||||
return fmt.Errorf("error writing article %v to file: %v", a.UUID, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Article) ReadContent(c *Config) (string, error) {
|
||||
articlePath := filepath.Join(c.ArticleDir, fmt.Sprint(a.UUID, ".md"))
|
||||
|
||||
content, err := os.ReadFile(articlePath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error reading file %v: %v", articlePath, err)
|
||||
}
|
||||
|
||||
return string(content), nil
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ type Config struct {
|
||||
Domain string
|
||||
FirebaseKey string
|
||||
ImgDir string
|
||||
IndexDir string
|
||||
Link string
|
||||
LogFile string
|
||||
PDFDir string
|
||||
@ -48,6 +49,7 @@ func newConfig() *Config {
|
||||
DBUser: "cpolis",
|
||||
FirebaseKey: "/etc/cpolis/serviceAccountKey.json",
|
||||
ImgDir: "/var/www/cpolis/images",
|
||||
IndexDir: "/var/www/cpolis/index",
|
||||
LogFile: "/var/log/cpolis.log",
|
||||
MaxBannerHeight: 1080,
|
||||
MaxBannerWidth: 1920,
|
||||
@ -119,6 +121,7 @@ func (c *Config) handleCliArgs() error {
|
||||
flag.StringVar(&c.Domain, "domain", c.Domain, "domain name")
|
||||
flag.StringVar(&c.FirebaseKey, "firebase", c.FirebaseKey, "Firebase service account key file")
|
||||
flag.StringVar(&c.ImgDir, "images", c.ImgDir, "images directory")
|
||||
flag.StringVar(&c.IndexDir, "index", c.IndexDir, "index directory")
|
||||
flag.StringVar(&c.Link, "link", c.Link, "channel Link")
|
||||
flag.StringVar(&c.LogFile, "log", c.LogFile, "log file")
|
||||
flag.StringVar(&c.PDFDir, "pdfs", c.PDFDir, "pdf directory")
|
||||
@ -245,6 +248,18 @@ func (c *Config) setupConfig(cliConfig *Config) error {
|
||||
return fmt.Errorf("error setting up directory: %v", err)
|
||||
}
|
||||
|
||||
if cliConfig.IndexDir != defaultConfig.IndexDir {
|
||||
c.IndexDir = cliConfig.IndexDir
|
||||
}
|
||||
c.IndexDir, err = filepath.Abs(c.IndexDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error setting absolute filepath for IndexDir: %v", err)
|
||||
}
|
||||
c.IndexDir, err = mkDir(c.IndexDir, 0700)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error setting up file: %v", err)
|
||||
}
|
||||
|
||||
if cliConfig.Link != defaultConfig.Link {
|
||||
c.Link = cliConfig.Link
|
||||
}
|
||||
|
136
cmd/backend/index.go
Normal file
136
cmd/backend/index.go
Normal file
@ -0,0 +1,136 @@
|
||||
package backend
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/blevesearch/bleve"
|
||||
)
|
||||
|
||||
type (
|
||||
Index struct {
|
||||
index bleve.Index
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
Result struct{ *bleve.SearchResult }
|
||||
)
|
||||
|
||||
func (i *Index) isIndexed(filename string, errChan chan<- error) bool {
|
||||
i.mu.RLock()
|
||||
defer i.mu.RUnlock()
|
||||
|
||||
query := bleve.NewDocIDQuery([]string{filename})
|
||||
request := bleve.NewSearchRequest(query)
|
||||
result, err := i.index.Search(request)
|
||||
if err != nil {
|
||||
errChan <- fmt.Errorf("error searching for file %v in index: %v", filename, err)
|
||||
return false
|
||||
}
|
||||
|
||||
return len(result.Hits) > 0
|
||||
}
|
||||
|
||||
func (i *Index) indexAllArticles(c *Config, db *DB) error {
|
||||
articles, err := db.GetAllArticles()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting all articles from DB: %v", err)
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
errChan := make(chan error, len(articles))
|
||||
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(errChan)
|
||||
}()
|
||||
|
||||
for _, article := range articles {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
filename := article.UUID.String()
|
||||
content, err := article.ReadContent(c)
|
||||
if err != nil {
|
||||
errChan <- fmt.Errorf("error reading content of article %v: %v", filename, err)
|
||||
return
|
||||
}
|
||||
|
||||
if !i.isIndexed(filename, errChan) {
|
||||
if err := i.Add(filename, content); err != nil {
|
||||
errChan <- fmt.Errorf("error adding file %v to index: %v", filename, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
errChan <- nil
|
||||
}()
|
||||
}
|
||||
if err = <-errChan; err != nil {
|
||||
return fmt.Errorf("error(s) indexing all articles: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func InitIndex(c *Config) (*Index, error) {
|
||||
var err error
|
||||
index := new(Index)
|
||||
|
||||
index.index, err = bleve.Open(c.IndexDir)
|
||||
if err != nil {
|
||||
mapping := bleve.NewIndexMapping()
|
||||
index.index, err = bleve.New(c.IndexDir, mapping)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating new index file: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return index, err
|
||||
}
|
||||
|
||||
func (i *Index) Add(filename, content string) error {
|
||||
i.mu.Lock()
|
||||
defer i.mu.Unlock()
|
||||
|
||||
doc := map[string]string{
|
||||
"name": filename,
|
||||
"body": content,
|
||||
}
|
||||
|
||||
if err := i.index.Index(filename, doc); err != nil {
|
||||
return fmt.Errorf("error indexing file %v: %v", filename, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Index) Query(q string) (*Result, error) {
|
||||
i.mu.RLock()
|
||||
defer i.mu.RUnlock()
|
||||
|
||||
query := bleve.NewQueryStringQuery(q)
|
||||
request := bleve.NewSearchRequest(query)
|
||||
result, err := i.index.Search(request)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error querying index for \"%v\": %v", q, err)
|
||||
}
|
||||
|
||||
if len(result.Hits) < 1 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &Result{result}, err
|
||||
}
|
||||
|
||||
func (i *Index) Delete(filename string) error {
|
||||
i.mu.Lock()
|
||||
defer i.mu.Unlock()
|
||||
|
||||
if err := i.index.Delete(filename); err != nil {
|
||||
return fmt.Errorf("error deleting file %v from index: %v", filename, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -167,7 +167,7 @@ func SubmitArticle(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFun
|
||||
http.Error(w, "Bitte den Artikel eingeben.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if err := b.WriteArticleToFile(c, article.UUID, content); err != nil {
|
||||
if err := article.WriteContentToFile(c, content); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@ -291,7 +291,7 @@ func ResubmitArticle(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerF
|
||||
return
|
||||
}
|
||||
|
||||
if err = b.WriteArticleToFile(c, article.UUID, []byte(content)); err != nil {
|
||||
if err = article.WriteContentToFile(c, []byte(content)); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@ -535,7 +535,7 @@ func ReviewRejectedArticle(c *b.Config, db *b.DB, s map[string]*Session) http.Ha
|
||||
}
|
||||
}
|
||||
|
||||
func PublishArticle(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc {
|
||||
func PublishArticle(c *b.Config, db *b.DB, s map[string]*Session, i *b.Index) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
session, err := ManageSession(w, r, c, s)
|
||||
if err != nil {
|
||||
@ -573,6 +573,19 @@ func PublishArticle(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFu
|
||||
return
|
||||
}
|
||||
|
||||
content, err := article.ReadContent(c)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if err = i.Add(article.UUID.String(), content); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if article.EditedID != 0 {
|
||||
oldArticle, err := db.GetArticle(article.EditedID)
|
||||
if err != nil {
|
||||
@ -587,7 +600,14 @@ func PublishArticle(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFu
|
||||
return
|
||||
}
|
||||
|
||||
if err = os.Remove(filepath.Join(c.ArticleDir, fmt.Sprint(oldArticle.UUID, ".md"))); err != nil {
|
||||
uuid := oldArticle.UUID.String()
|
||||
if err = i.Delete(uuid); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if err = os.Remove(filepath.Join(c.ArticleDir, uuid+".md")); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
|
@ -33,6 +33,11 @@ func main() {
|
||||
sessions, sessionExpiryChan := f.StartSessions()
|
||||
defer close(sessionExpiryChan)
|
||||
|
||||
index, err := b.InitIndex(config)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
go func(c *b.Config, db *b.DB) {
|
||||
for {
|
||||
if err = b.CleanUpImages(c, db); err != nil {
|
||||
@ -54,7 +59,7 @@ func main() {
|
||||
mux.HandleFunc("GET /article/all-unpublished-unrejected-and-published-rejected", f.ShowUnpublishedUnrejectedAndPublishedRejectedArticles(config, db, sessions))
|
||||
mux.HandleFunc("GET /article/delete/{id}", f.DeleteArticle(config, db, sessions))
|
||||
mux.HandleFunc("GET /article/edit/{id}", f.EditArticle(config, db, sessions))
|
||||
mux.HandleFunc("GET /article/publish/{id}", f.PublishArticle(config, db, sessions))
|
||||
mux.HandleFunc("GET /article/publish/{id}", f.PublishArticle(config, db, sessions, index))
|
||||
mux.HandleFunc("GET /article/reject/{id}", f.RejectArticle(config, db, sessions))
|
||||
mux.HandleFunc("GET /article/review-delete/{id}", f.ReviewArticle(config, db, sessions, "delete", "Artikel löschen", "Löschen"))
|
||||
mux.HandleFunc("GET /article/review-edit/{id}", f.ReviewArticle(config, db, sessions, "allow-edit", "Artikel bearbeiten", "Bearbeiten erlauben"))
|
||||
|
Reference in New Issue
Block a user