aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJordan <me@jordan.im>2021-03-14 13:43:03 -0700
committerJordan <me@jordan.im>2021-03-14 13:43:03 -0700
commitf03d77f73be72a934a99541b30a9663cc99a1733 (patch)
tree5065869c40492ebb917e825b6bcf0075b5117a17
parentc0271039935407c232270cef74cd2f559f77a64e (diff)
downloadcrane-f03d77f73be72a934a99541b30a9663cc99a1733.tar.gz
crane-f03d77f73be72a934a99541b30a9663cc99a1733.zip
r/w map locks; concurrent safety
-rw-r--r--crane.go39
-rw-r--r--http.go22
-rw-r--r--templates/admin-edit.html10
-rw-r--r--templates/admin.html6
-rw-r--r--templates/index.html4
-rw-r--r--templates/list.html2
6 files changed, 67 insertions, 16 deletions
diff --git a/crane.go b/crane.go
index 32e9a5f..bd5bb8a 100644
--- a/crane.go
+++ b/crane.go
@@ -17,6 +17,7 @@ import (
"os"
"path/filepath"
"strings"
+ "sync"
"golang.org/x/net/publicsuffix"
)
@@ -64,12 +65,13 @@ type Paper struct {
}
type Papers struct {
+ sync.RWMutex
List map[string]map[string]*Paper
Path string
}
type Resp struct {
- Papers map[string]map[string]*Paper
+ Papers Papers
Status string
LastPaperDL string
LastUsedCategory string
@@ -121,6 +123,7 @@ func getPaperFileNameFromResp(resp *http.Response) string {
func (papers *Papers) getUniqueName(category string, name string) string {
newName := name
ext := 2
+ papers.RLock()
for {
key := filepath.Join(category, newName+".pdf")
if _, exists := papers.List[category][key]; exists != true {
@@ -130,6 +133,7 @@ func (papers *Papers) getUniqueName(category string, name string) string {
ext++
}
}
+ papers.RUnlock()
return newName
}
@@ -142,6 +146,9 @@ func (papers *Papers) findPapersWalk(path string, info os.FileInfo,
return nil
}
+ papers.Lock()
+ defer papers.Unlock()
+
// derive category name (e.g. Mathematics) from directory name; used as key
var category string
if i, _ := os.Stat(path); i.IsDir() {
@@ -246,10 +253,12 @@ func (papers *Papers) NewPaperFromDOI(doi []byte, category string) (*Paper,
// if not matching, check if DOIs match (genuine duplicate)
if name != uniqueName {
key := filepath.Join(category, name+".pdf")
+ papers.RLock()
if meta.DOI == papers.List[category][key].Meta.DOI {
return nil, fmt.Errorf("paper %q with DOI %q already downloaded",
name, string(doi))
}
+ papers.RUnlock()
}
paper.PaperName = uniqueName
@@ -276,8 +285,11 @@ func (papers *Papers) NewPaperFromDOI(doi []byte, category string) (*Paper,
return nil, err
}
paper.Meta = *meta
+
+ papers.Lock()
papers.List[category][filepath.Join(category,
paper.PaperName+".pdf")] = &paper
+ papers.Unlock()
return &paper, nil
}
@@ -315,8 +327,10 @@ func (papers *Papers) NewPaperFromDirectLink(resp *http.Response, meta *Meta,
if err := renameFile(tmpPDF.Name(), paper.PaperPath); err != nil {
return nil, err
}
+ papers.Lock()
papers.List[category][filepath.Join(category,
paper.PaperName+".pdf")] = &paper
+ papers.Unlock()
return &paper, nil
}
@@ -325,6 +339,7 @@ func (papers *Papers) NewPaperFromDirectLink(resp *http.Response, meta *Meta,
func (papers *Papers) DeletePaper(paper string) error {
// check if the category in which the paper is said to belong
// exists
+ papers.RLock()
category := filepath.Dir(paper)
if _, exists := papers.List[category]; exists != true {
return fmt.Errorf("category %q does not exist\n",
@@ -336,9 +351,11 @@ func (papers *Papers) DeletePaper(paper string) error {
return fmt.Errorf("paper %q does not exist in category %q\n", paper,
category)
}
+ papers.RUnlock()
// paper and category exists and the paper belongs to the provided
// category; remove it and its XML metadata
+ papers.Lock()
if err := os.Remove(papers.List[category][paper].PaperPath); err != nil {
return err
}
@@ -353,31 +370,40 @@ func (papers *Papers) DeletePaper(paper string) error {
}
}
delete(papers.List[category], paper)
+ papers.Unlock()
return nil
}
// DeleteCategory deletes a category and its contents from the filesystem and
// the papers.List set
func (papers *Papers) DeleteCategory(category string) error {
+ papers.RLock()
if _, exists := papers.List[category]; exists != true {
return fmt.Errorf("category %q does not exist in the set\n", category)
}
+ papers.RUnlock()
+
+ papers.Lock()
if err := os.RemoveAll(filepath.Join(papers.Path, category)); err != nil {
return err
}
+
// remove subcategories (nested directories) which exist under the primary
for key, _ := range papers.List {
if strings.HasPrefix(key, category+"/") {
delete(papers.List, key)
}
}
+
delete(papers.List, category)
+ papers.Unlock()
return nil
}
// MovePaper moves a paper to the destination category on the filesystem and
// the papers.List set
func (papers *Papers) MovePaper(paper string, category string) error {
+ papers.RLock()
prevCategory := filepath.Dir(paper)
if _, exists := papers.List[prevCategory]; exists != true {
return fmt.Errorf("category %q does not exist\n", prevCategory)
@@ -393,6 +419,9 @@ func (papers *Papers) MovePaper(paper string, category string) error {
return fmt.Errorf("paper %q exists in destination category %q\n",
paper, category)
}
+ papers.RUnlock()
+
+ papers.Lock()
paperDest := filepath.Join(filepath.Join(papers.Path, category),
papers.List[prevCategory][paper].PaperName+".pdf")
if err := os.Rename(papers.List[prevCategory][paper].PaperPath, paperDest);
@@ -422,12 +451,15 @@ func (papers *Papers) MovePaper(paper string, category string) error {
}
}
delete(papers.List[prevCategory], paper)
+
+ papers.Unlock()
return nil
}
// RenameCategory renames a category on the filesystem and the paper.List set
func (papers *Papers) RenameCategory(oldCategory string,
newCategory string) error {
+ papers.RLock()
if _, exists := papers.List[oldCategory]; exists != true {
return fmt.Errorf("category %q does not exist in the set\n", oldCategory)
}
@@ -438,6 +470,9 @@ func (papers *Papers) RenameCategory(oldCategory string,
filepath.Join(papers.Path, newCategory)); err != nil {
return err
}
+ papers.RUnlock()
+
+ papers.Lock()
papers.List[newCategory] = make(map[string]*Paper)
for k, v := range papers.List[oldCategory] {
pPaperPath := filepath.Join(papers.Path, filepath.Join(newCategory,
@@ -453,6 +488,8 @@ func (papers *Papers) RenameCategory(oldCategory string,
}
}
delete(papers.List, oldCategory)
+
+ papers.Unlock()
return nil
}
diff --git a/http.go b/http.go
index fcfd38f..ad42c2b 100644
--- a/http.go
+++ b/http.go
@@ -20,9 +20,12 @@ func (papers *Papers) IndexHandler(w http.ResponseWriter, r *http.Request) {
filepath.Join(templateDir, "index.html"),
filepath.Join(templateDir, "list.html"),
)
+ papers.RLock()
res := Resp{
- Papers: papers.List,
+ Papers: *papers,
}
+ papers.RUnlock()
+
t.Execute(w, &res)
}
@@ -33,9 +36,12 @@ func (papers *Papers) AdminHandler(w http.ResponseWriter, r *http.Request) {
filepath.Join(templateDir, "layout.html"),
filepath.Join(templateDir, "list.html"),
)
+ papers.RLock()
res := Resp{
- Papers: papers.List,
+ Papers: *papers,
}
+ papers.RUnlock()
+
if user != "" && pass != "" {
username, password, ok := r.BasicAuth()
if ok && user == username && pass == password {
@@ -58,9 +64,12 @@ func (papers *Papers) EditHandler(w http.ResponseWriter, r *http.Request) {
filepath.Join(templateDir, "layout.html"),
filepath.Join(templateDir, "list.html"),
)
+ papers.RLock()
res := Resp{
- Papers: papers.List,
+ Papers: *papers,
}
+ papers.RUnlock()
+
if user != "" && pass != "" {
username, password, ok := r.BasicAuth()
if !ok || user != username || pass != password {
@@ -152,7 +161,8 @@ func (papers *Papers) AddHandler(w http.ResponseWriter, r *http.Request) {
// sanitize input; we use the category to build the path used to save
// papers
nc = strings.Trim(strings.Replace(nc, "..", "", -1), "/.")
- res := Resp{Papers: papers.List}
+
+ res := Resp{}
// paper download, both required fields populated
if len(strings.TrimSpace(p)) > 0 && len(strings.TrimSpace(c)) > 0 {
@@ -194,6 +204,10 @@ func (papers *Papers) AddHandler(w http.ResponseWriter, r *http.Request) {
res.Status = fmt.Sprintf("category %q added successfully", nc)
}
}
+ papers.RLock()
+ res.Papers = *papers
+ papers.RUnlock()
+
t.Execute(w, &res)
}
diff --git a/templates/admin-edit.html b/templates/admin-edit.html
index 57a9aaa..7b30294 100644
--- a/templates/admin-edit.html
+++ b/templates/admin-edit.html
@@ -3,13 +3,13 @@
<p>{{ .Status }}</p>
<table id='header'>
<tr>
- {{ $categoryCount := len .Papers }}
+ {{ $categoryCount := len .Papers.List }}
{{ if gt $categoryCount 0 }}
<td class='inpt'>
<form method='post' action='/admin/edit/'>
<input type="text" id="rename-category" name="rename-to" placeholder="Mathematics">
<select class="sel" name="rename-category" id="category">
- {{ range $category, $papers := .Papers }}
+ {{ range $category, $papers := .Papers.List }}
<option value="{{ $category }}">{{ $category }}</option>
{{ end }}
</select>
@@ -20,7 +20,7 @@
</tr>
</table>
<div class="cat">
- <span>{{ range $category, $paper := .Papers }}[<a href="#{{ $category }}">{{ $category }}</a>] {{ end }}</span>
+ <span>{{ range $category, $paper := .Papers.List }}[<a href="#{{ $category }}">{{ $category }}</a>] {{ end }}</span>
</div>
<table class='tabs'>
<tr>
@@ -36,7 +36,7 @@
<option value="delete">Delete</option>
</optgroup>
<optgroup label="Move To">
- {{ range $category, $papers := .Papers }}
+ {{ range $category, $papers := .Papers.List }}
<option value="move-{{ $category }}">{{ $category }}</option>
{{ end }}
</optgroup>
@@ -44,7 +44,7 @@
<input class="btn" type="submit" value="Save" />
</div>
<div class='papers nowrap list'>
-{{ range $category, $papers := .Papers }}
+{{ range $category, $papers := .Papers.List }}
{{ $paperCount := len $papers }}
<div class="papersection">
<input type="checkbox" id="{{ $category }}" name="category" value="{{ $category }}">
diff --git a/templates/admin.html b/templates/admin.html
index 22b0e0a..05cf04f 100644
--- a/templates/admin.html
+++ b/templates/admin.html
@@ -14,7 +14,7 @@
</form>
</td>
</tr>
- {{ $categoryCount := len .Papers }}
+ {{ $categoryCount := len .Papers.List }}
{{ if gt $categoryCount 0 }}
<tr>
<td class='inpt'>
@@ -25,7 +25,7 @@
{{ if $lastUsedCategory }}
<option value="{{ .LastUsedCategory }}">{{ $lastUsedCategory }}</option>
{{ end }}
- {{ range $category, $papers := .Papers }}
+ {{ range $category, $papers := .Papers.List }}
{{ if ne $category $lastUsedCategory }}
<option value="{{ $category }}">{{ $category }}</option>
{{ end }}
@@ -39,7 +39,7 @@
</table>
{{ if gt $categoryCount 0 }}
<div class="cat">
- <span>{{ range $category, $paper := .Papers }}[<a href="#{{ $category }}">{{ $category }}</a>] {{ end }}</span>
+ <span>{{ range $category, $paper := .Papers.List }}[<a href="#{{ $category }}">{{ $category }}</a>] {{ end }}</span>
</div>
<table class='tabs'>
<tr>
diff --git a/templates/index.html b/templates/index.html
index 30023cd..6397671 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -2,10 +2,10 @@
{{ define "content" }}
<table id='header'>
</table>
-{{ $categoryCount := len .Papers }}
+{{ $categoryCount := len .Papers.List }}
{{ if gt $categoryCount 0 }}
<div class="cat">
- <span>{{ range $category, $paper := .Papers }}[<a href="#{{ $category }}">{{ $category }}</a>] {{ end }}</span>
+ <span>{{ range $category, $paper := .Papers.List }}[<a href="#{{ $category }}">{{ $category }}</a>] {{ end }}</span>
</div>
<table class='tabs'>
<td><a class='active' href='/admin/'>Manage</a></td>
diff --git a/templates/list.html b/templates/list.html
index 9e139b9..a08efd6 100644
--- a/templates/list.html
+++ b/templates/list.html
@@ -1,6 +1,6 @@
{{ define "list" }}
<div class='papers nowrap list'>
-{{ range $category, $papers := .Papers }}
+{{ range $category, $papers := .Papers.List }}
{{ $paperCount := len $papers }}
{{ if ge $paperCount 1 }}
<div class="papersection">