Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions bookmarkActionHandlers.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package gobookmarks

import (
"errors"
"fmt"
"github.com/gorilla/sessions"
"golang.org/x/oauth2"
Expand Down Expand Up @@ -30,7 +31,25 @@ func BookmarksEditSaveAction(w http.ResponseWriter, r *http.Request) error {
return fmt.Errorf("bookmark modified concurrently")
}

repoName := RepoName
if r.PostFormValue("repoName") != "" {
repoName = r.PostFormValue("repoName")
}
if r.PostFormValue("createRepo") == "1" {
if err := ActiveProvider.CreateRepo(r.Context(), login, token, repoName); err != nil {
return renderCreateRepoPrompt(w, r, repoName, text, branch, ref, sha, err)
}
RepoName = repoName
if err := CreateBookmarks(r.Context(), login, token, branch, text); err != nil {
return fmt.Errorf("createBookmark error: %w", err)
}
return nil
}

if err := UpdateBookmarks(r.Context(), login, token, ref, branch, text, curSha); err != nil {
if errors.Is(err, ErrRepoNotFound) {
return renderCreateRepoPrompt(w, r, repoName, text, branch, ref, sha, nil)
}
return fmt.Errorf("updateBookmark error: %w", err)
}
return nil
Expand Down Expand Up @@ -90,3 +109,29 @@ func CategoryEditSaveAction(w http.ResponseWriter, r *http.Request) error {
}
return nil
}

func renderCreateRepoPrompt(w http.ResponseWriter, r *http.Request, repoName, text, branch, ref, sha string, err error) error {
data := struct {
*CoreData
RepoName string
Text string
Branch string
Ref string
Sha string
Error string
}{
CoreData: r.Context().Value(ContextValues("coreData")).(*CoreData),
RepoName: repoName,
Text: text,
Branch: branch,
Ref: ref,
Sha: sha,
}
if err != nil {
data.Error = err.Error()
}
if tplErr := GetCompiledTemplates(NewFuncs(r)).ExecuteTemplate(w, "createRepo.gohtml", data); tplErr != nil {
return fmt.Errorf("template: %w", tplErr)
}
return ErrHandled
}
6 changes: 6 additions & 0 deletions cmd/gobookmarks/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"crypto/x509/pkix"
"encoding/json"
"encoding/pem"
"errors"
"flag"
"fmt"
. "github.com/arran4/gobookmarks"
Expand Down Expand Up @@ -136,6 +137,7 @@ func main() {

UseCssColumns = cfg.CssColumns
Namespace = cfg.Namespace
RepoName = GetBookmarksRepoName()
SiteTitle = cfg.Title
if cfg.GitServer != "" {
GitServer = cfg.GitServer
Expand Down Expand Up @@ -206,6 +208,7 @@ func main() {

log.Printf("gobookmarks: %s, commit %s, built at %s", version, commit, date)
SetVersion(version, commit, date)
RepoName = GetBookmarksRepoName()
log.Printf("Redirect URL configured to: %s", redirectUrl)
log.Println("Server started on http://localhost:8080")
log.Println("Server started on https://localhost:8443")
Expand Down Expand Up @@ -342,6 +345,9 @@ func runHandlerChain(chain ...any) func(http.ResponseWriter, *http.Request) {
each(w, r)
case func(http.ResponseWriter, *http.Request) error:
if err := each(w, r); err != nil {
if errors.Is(err, ErrHandled) {
return
}
type ErrorData struct {
*CoreData
Error string
Expand Down
9 changes: 9 additions & 0 deletions data_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,15 @@ func TestExecuteTemplates(t *testing.T) {
*CoreData
Error string
}{baseData.CoreData, "boom"}},
{"createRepo", "createRepo.gohtml", struct {
*CoreData
RepoName string
Text string
Branch string
Ref string
Sha string
Error string
}{baseData.CoreData, "MyBookmarks", "text", "main", "ref", "sha", ""}},
}

for _, tt := range pages {
Expand Down
10 changes: 10 additions & 0 deletions errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package gobookmarks

import "errors"

// ErrRepoNotFound indicates that the bookmarks repository does not exist.
var ErrRepoNotFound = errors.New("repository not found")

// ErrHandled is returned by handlers when they have already written
// a response and no further handlers should run.
var ErrHandled = errors.New("handled")
5 changes: 3 additions & 2 deletions provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ type Provider interface {
GetCommits(ctx context.Context, user string, token *oauth2.Token) ([]*Commit, error)
GetBookmarks(ctx context.Context, user, ref string, token *oauth2.Token) (string, string, error)
UpdateBookmarks(ctx context.Context, user string, token *oauth2.Token, sourceRef, branch, text, expectSHA string) error
CreateBookmarks(ctx context.Context, user string, token *oauth2.Token, branch, text string) error
DefaultServer() string
CreateBookmarks(ctx context.Context, user string, token *oauth2.Token, branch, text string) error
CreateRepo(ctx context.Context, user string, token *oauth2.Token, name string) error
DefaultServer() string
}

var providers = map[string]Provider{}
Expand Down
38 changes: 14 additions & 24 deletions provider_github.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,35 +141,31 @@ func (p GitHubProvider) GetBookmarks(ctx context.Context, user, ref string, toke

var commitAuthor = &github.CommitAuthor{Name: SP("Gobookmarks"), Email: SP("Gobookmarks@arran.net.au")}

func (p GitHubProvider) getDefaultBranch(ctx context.Context, user string, client *github.Client, branch string) (string, bool, error) {
func (p GitHubProvider) getDefaultBranch(ctx context.Context, user string, client *github.Client, branch string) (string, error) {
rep, resp, err := client.Repositories.Get(ctx, user, RepoName)
created := false
if resp != nil && resp.StatusCode == 404 {
rep, err = p.createRepo(ctx, user, client)
if err != nil {
log.Printf("github createRepo: %v", err)
return "", created, err
}
created = true
return "", ErrRepoNotFound
}
if err != nil {
log.Printf("github getDefaultBranch: %v", err)
return "", created, fmt.Errorf("Repositories.Get: %w", err)
return "", fmt.Errorf("Repositories.Get: %w", err)
}
if rep.DefaultBranch != nil {
branch = *rep.DefaultBranch
} else {
branch = "main"
}
return branch, created, nil
return branch, nil
}

func (p GitHubProvider) createRepo(ctx context.Context, user string, client *github.Client) (*github.Repository, error) {
func (p GitHubProvider) CreateRepo(ctx context.Context, user string, token *oauth2.Token, name string) error {
client := p.client(ctx, token)
RepoName = name
rep := &github.Repository{Name: &RepoName, Description: SP("Personal bookmarks"), Private: BP(true)}
rep, _, err := client.Repositories.Create(ctx, "", rep)
if err != nil {
log.Printf("github createRepo: %v", err)
return nil, fmt.Errorf("Repositories.Create: %w", err)
return fmt.Errorf("Repositories.Create: %w", err)
}
_, _, err = client.Repositories.CreateFile(ctx, user, RepoName, "readme.md", &github.RepositoryContentFileOptions{
Message: SP("Auto create from web"),
Expand All @@ -180,9 +176,10 @@ See . https://github.com/arran4/gobookmarks `),
})
if err != nil {
log.Printf("github createRepo readme: %v", err)
return nil, fmt.Errorf("CreateReadme: %w", err)
return fmt.Errorf("CreateReadme: %w", err)
}
return rep, nil
_ = rep
return nil
}

func (p GitHubProvider) createRef(ctx context.Context, user string, client *github.Client, sourceRef, branchRef string) error {
Expand All @@ -204,7 +201,7 @@ func (p GitHubProvider) createRef(ctx context.Context, user string, client *gith

func (p GitHubProvider) UpdateBookmarks(ctx context.Context, user string, token *oauth2.Token, sourceRef, branch, text, expectSHA string) error {
client := p.client(ctx, token)
defaultBranch, created, err := p.getDefaultBranch(ctx, user, client, branch)
defaultBranch, err := p.getDefaultBranch(ctx, user, client, branch)
if err != nil {
return err
}
Expand All @@ -215,9 +212,6 @@ func (p GitHubProvider) UpdateBookmarks(ctx context.Context, user string, token
if sourceRef == "" {
sourceRef = branchRef
}
if created {
return p.CreateBookmarks(ctx, user, token, branch, text)
}
_, grefResp, err := client.Git.GetRef(ctx, user, RepoName, branchRef)
if err != nil && grefResp.StatusCode != 404 {
log.Printf("github UpdateBookmarks getRef: %v", err)
Expand All @@ -231,11 +225,7 @@ func (p GitHubProvider) UpdateBookmarks(ctx context.Context, user string, token
}
contents, _, resp, err := client.Repositories.GetContents(ctx, user, RepoName, "bookmarks.txt", &github.RepositoryContentGetOptions{Ref: branchRef})
if resp != nil && resp.StatusCode == 404 {
if _, err := p.createRepo(ctx, user, client); err != nil {
log.Printf("github UpdateBookmarks create repo: %v", err)
return fmt.Errorf("CreateRepo: %w", err)
}
return p.CreateBookmarks(ctx, user, token, branch, text)
return ErrRepoNotFound
}
if err != nil {
log.Printf("github UpdateBookmarks get contents: %v", err)
Expand Down Expand Up @@ -266,7 +256,7 @@ func (p GitHubProvider) CreateBookmarks(ctx context.Context, user string, token
client := p.client(ctx, token)
if branch == "" {
var err error
branch, _, err = p.getDefaultBranch(ctx, user, client, branch)
branch, err = p.getDefaultBranch(ctx, user, client, branch)
if err != nil {
log.Printf("github CreateBookmarks default branch: %v", err)
return err
Expand Down
25 changes: 24 additions & 1 deletion provider_gitlab.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,9 @@ func (GitLabProvider) UpdateBookmarks(ctx context.Context, user string, token *o
}
_, _, err = c.RepositoryFiles.UpdateFile(user+"/"+RepoName, "bookmarks.txt", opt)
if err != nil {
if respErr, ok := err.(*gitlab.ErrorResponse); ok && respErr.Response != nil && respErr.Response.StatusCode == http.StatusNotFound {
return ErrRepoNotFound
}
log.Printf("gitlab UpdateBookmarks: %v", err)
return err
}
Expand All @@ -188,7 +191,27 @@ func (GitLabProvider) CreateBookmarks(ctx context.Context, user string, token *o
}
_, _, err = c.RepositoryFiles.CreateFile(user+"/"+RepoName, "bookmarks.txt", opt)
if err != nil {
log.Printf("gitlab CreateBookmarks: %v", err)
if respErr, ok := err.(*gitlab.ErrorResponse); ok && respErr.Response != nil && respErr.Response.StatusCode == http.StatusNotFound {
return ErrRepoNotFound
}
if err != nil {
log.Printf("gitlab CreateBookmarks: %v", err)
}
}
return err
}

func (p GitLabProvider) CreateRepo(ctx context.Context, user string, token *oauth2.Token, name string) error {
c, err := GitLabProvider{}.client(token)
if err != nil {
return err
}
RepoName = name
_, _, err = c.Projects.CreateProject(&gitlab.CreateProjectOptions{
Name: gitlab.String(RepoName),
Description: gitlab.String("Personal bookmarks"),
Visibility: gitlab.Visibility(gitlab.PrivateVisibility),
InitializeWithReadme: gitlab.Ptr(true),
})
return err
}
11 changes: 9 additions & 2 deletions repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,16 @@ package gobookmarks

var RepoName = GetBookmarksRepoName()

// GetBookmarksRepoName returns the repository name based on the current
// configuration and build mode. When running a development build the name is
// suffixed with "-dev". The Namespace value is appended if supplied.
func GetBookmarksRepoName() string {
name := "MyBookmarks"
if version == "dev" {
name += "-dev"
}
if Namespace != "" {
return "MyBookmarks-" + Namespace
name += "-" + Namespace
}
return "MyBookmarks"
return name
}
24 changes: 24 additions & 0 deletions templates/createRepo.gohtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{{ template "head" $ }}
{{ if $.Error }}
<p style="color: #FF0000">Error: {{ $.Error }}</p>
{{ end }}
<form method="post" action="/edit">
<input type="hidden" name="task" value="Save" />
<input type="hidden" name="text" value="{{ $.Text }}" />
<input type="hidden" name="branch" value="{{ $.Branch }}" />
<input type="hidden" name="ref" value="{{ $.Ref }}" />
<input type="hidden" name="sha" value="{{ $.Sha }}" />
<input type="hidden" name="createRepo" value="1" />
Repository Name: <input type="text" name="repoName" value="{{ $.RepoName }}" /><br/>
<input type="submit" value="Create Repo and Retry" />
</form>
<form method="post" action="/edit">
<input type="hidden" name="task" value="Save" />
<input type="hidden" name="text" value="{{ $.Text }}" />
<input type="hidden" name="branch" value="{{ $.Branch }}" />
<input type="hidden" name="ref" value="{{ $.Ref }}" />
<input type="hidden" name="sha" value="{{ $.Sha }}" />
<input type="submit" value="Retry" />
</form>
{{ template "tail" $ }}

Loading