forked from tangled.org/core
Monorepo for Tangled — https://tangled.org

appview: pages/markup: resolve relative links

+43
appview/pages/markup/markdown.go
···
import (
"bytes"
+
"fmt"
"github.com/yuin/goldmark"
+
"github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/extension"
"github.com/yuin/goldmark/parser"
+
"github.com/yuin/goldmark/text"
+
"github.com/yuin/goldmark/util"
)
func RenderMarkdown(source string) string {
···
}
return buf.String()
}
+
+
type RelativeLinkTransformer struct {
+
User string
+
Repo string
+
Ref string
+
}
+
+
func (t *RelativeLinkTransformer) Transform(node *ast.Document, reader text.Reader, pc parser.Context) {
+
ast.Walk(node, func(n ast.Node, entering bool) (ast.WalkStatus, error) {
+
if !entering {
+
fmt.Printf("Node: %T\n", n)
+
}
+
return ast.WalkContinue, nil
+
})
+
}
+
+
func RenderMarkdownExtended(source, user, repo, ref string) string {
+
md := goldmark.New(
+
goldmark.WithExtensions(extension.GFM),
+
goldmark.WithParserOptions(parser.WithAutoHeadingID()),
+
goldmark.WithParser(
+
parser.NewParser(
+
parser.WithASTTransformers(
+
util.Prioritized(&RelativeLinkTransformer{
+
User: user,
+
Repo: repo,
+
Ref: ref,
+
}, 999),
+
),
+
),
+
),
+
)
+
+
var buf bytes.Buffer
+
if err := md.Convert([]byte(source), &buf); err != nil {
+
return source
+
}
+
return buf.String()
+
}
appview/pages/markup/readme.go appview/pages/markup/format.go
+13 -2
appview/pages/pages.go
···
OwnerHandle string
Description string
Knot string
+
Ref string
RepoAt syntax.ATURI
IsStarred bool
Stats db.RepoStats
···
ext := filepath.Ext(params.ReadmeFileName)
switch ext {
case ".md", ".markdown", ".mdown", ".mkdn", ".mkd":
-
htmlString = markup.RenderMarkdown(params.Readme)
+
htmlString = markup.RenderMarkdownExtended(
+
params.Readme,
+
params.RepoInfo.OwnerHandle,
+
params.RepoInfo.Name,
+
params.RepoInfo.Ref,
+
)
params.Raw = false
params.HTMLReadme = template.HTML(bluemonday.UGCPolicy().Sanitize(htmlString))
default:
···
if params.ShowRendered {
switch markup.GetFormat(params.Path) {
case markup.FormatMarkdown:
-
params.RenderedContents = template.HTML(markup.RenderMarkdown(params.Contents))
+
params.RenderedContents = template.HTML(markup.RenderMarkdownExtended(
+
params.Contents,
+
params.RepoInfo.OwnerHandle,
+
params.RepoInfo.Name,
+
params.RepoInfo.Ref,
+
))
}
}
+2 -2
appview/state/middleware.go
···
http.Error(w, "Forbiden", http.StatusUnauthorized)
return
}
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
http.Error(w, "malformed url", http.StatusBadRequest)
return
···
func ResolvePull(s *State) Middleware {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to fully resolve repo", err)
http.Error(w, "invalid repo url", http.StatusNotFound)
+14 -14
appview/state/pull.go
···
switch r.Method {
case http.MethodGet:
user := s.auth.GetUser(r)
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
return
···
func (s *State) RepoSinglePull(w http.ResponseWriter, r *http.Request) {
user := s.auth.GetUser(r)
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
return
···
func (s *State) RepoPullPatch(w http.ResponseWriter, r *http.Request) {
user := s.auth.GetUser(r)
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
return
···
state = db.PullMerged
}
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
return
···
func (s *State) PullComment(w http.ResponseWriter, r *http.Request) {
user := s.auth.GetUser(r)
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
return
···
func (s *State) NewPull(w http.ResponseWriter, r *http.Request) {
user := s.auth.GetUser(r)
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
return
···
func (s *State) PatchUploadFragment(w http.ResponseWriter, r *http.Request) {
user := s.auth.GetUser(r)
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
return
···
func (s *State) CompareBranchesFragment(w http.ResponseWriter, r *http.Request) {
user := s.auth.GetUser(r)
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
return
···
func (s *State) CompareForksFragment(w http.ResponseWriter, r *http.Request) {
user := s.auth.GetUser(r)
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
return
···
func (s *State) CompareForksBranchesFragment(w http.ResponseWriter, r *http.Request) {
user := s.auth.GetUser(r)
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
return
···
func (s *State) ResubmitPull(w http.ResponseWriter, r *http.Request) {
user := s.auth.GetUser(r)
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
return
···
func (s *State) MergePull(w http.ResponseWriter, r *http.Request) {
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to resolve repo:", err)
s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.")
···
func (s *State) ClosePull(w http.ResponseWriter, r *http.Request) {
user := s.auth.GetUser(r)
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("malformed middleware")
return
···
func (s *State) ReopenPull(w http.ResponseWriter, r *http.Request) {
user := s.auth.GetUser(r)
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to resolve repo", err)
s.pages.Notice(w, "pull-reopen", "Failed to reopen pull.")
+34 -39
appview/state/repo.go
···
func (s *State) RepoIndex(w http.ResponseWriter, r *http.Request) {
ref := chi.URLParam(r, "ref")
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to fully resolve repo", err)
return
···
}
func (s *State) RepoLog(w http.ResponseWriter, r *http.Request) {
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to fully resolve repo", err)
return
···
}
func (s *State) RepoDescriptionEdit(w http.ResponseWriter, r *http.Request) {
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
w.WriteHeader(http.StatusBadRequest)
···
}
func (s *State) RepoDescription(w http.ResponseWriter, r *http.Request) {
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
w.WriteHeader(http.StatusBadRequest)
···
}
func (s *State) RepoCommit(w http.ResponseWriter, r *http.Request) {
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to fully resolve repo", err)
return
···
}
func (s *State) RepoTree(w http.ResponseWriter, r *http.Request) {
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to fully resolve repo", err)
return
···
return
}
+
if result.Parent == treePath {
+
fmt.Println("here")
+
http.Redirect(w, r, fmt.Sprintf("/%s/%s/blob/%s/%s", f.OwnerHandle(), f.RepoName, ref, treePath), http.StatusPermanentRedirect)
+
return
+
}
+
user := s.auth.GetUser(r)
var breadcrumbs [][]string
···
}
func (s *State) RepoTags(w http.ResponseWriter, r *http.Request) {
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
return
···
}
func (s *State) RepoBranches(w http.ResponseWriter, r *http.Request) {
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
return
···
}
func (s *State) RepoBlob(w http.ResponseWriter, r *http.Request) {
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
return
···
}
func (s *State) RepoBlobRaw(w http.ResponseWriter, r *http.Request) {
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
return
···
}
func (s *State) AddCollaborator(w http.ResponseWriter, r *http.Request) {
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
return
···
func (s *State) DeleteRepo(w http.ResponseWriter, r *http.Request) {
user := s.auth.GetUser(r)
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
return
···
}
func (s *State) SetDefaultBranch(w http.ResponseWriter, r *http.Request) {
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
return
···
}
func (s *State) RepoSettings(w http.ResponseWriter, r *http.Request) {
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
return
···
}
}
-
resp, err = us.DefaultBranch(f.OwnerDid(), f.RepoName)
+
result, err := us.DefaultBranch(f.OwnerDid(), f.RepoName)
if err != nil {
log.Println("failed to reach knotserver", err)
} else {
-
defer resp.Body.Close()
-
-
body, err := io.ReadAll(resp.Body)
-
if err != nil {
-
log.Printf("Error reading response body: %v", err)
-
} else {
-
var result types.RepoDefaultBranchResponse
-
err = json.Unmarshal(body, &result)
-
if err != nil {
-
log.Println("failed to parse response:", err)
-
} else {
-
defaultBranch = result.Branch
-
}
-
}
+
defaultBranch = result.Branch
}
}
···
Knot string
OwnerId identity.Identity
RepoName string
+
Ref string
RepoAt syntax.ATURI
Description string
AddedAt string
···
Description: f.Description,
IsStarred: isStarred,
Knot: knot,
+
Ref: f.Ref,
Roles: RolesInRepo(s, u, f),
Stats: db.RepoStats{
StarCount: starCount,
···
func (s *State) RepoSingleIssue(w http.ResponseWriter, r *http.Request) {
user := s.auth.GetUser(r)
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
return
···
func (s *State) CloseIssue(w http.ResponseWriter, r *http.Request) {
user := s.auth.GetUser(r)
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
return
···
func (s *State) ReopenIssue(w http.ResponseWriter, r *http.Request) {
user := s.auth.GetUser(r)
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
return
···
func (s *State) NewIssueComment(w http.ResponseWriter, r *http.Request) {
user := s.auth.GetUser(r)
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
return
···
func (s *State) IssueComment(w http.ResponseWriter, r *http.Request) {
user := s.auth.GetUser(r)
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
return
···
func (s *State) EditIssueComment(w http.ResponseWriter, r *http.Request) {
user := s.auth.GetUser(r)
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
return
···
func (s *State) DeleteIssueComment(w http.ResponseWriter, r *http.Request) {
user := s.auth.GetUser(r)
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
return
···
user := s.auth.GetUser(r)
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
return
···
func (s *State) NewIssue(w http.ResponseWriter, r *http.Request) {
user := s.auth.GetUser(r)
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Println("failed to get repo and knot", err)
return
···
func (s *State) ForkRepo(w http.ResponseWriter, r *http.Request) {
user := s.auth.GetUser(r)
-
f, err := fullyResolvedRepo(r)
+
f, err := s.fullyResolvedRepo(r)
if err != nil {
log.Printf("failed to resolve source repo: %v", err)
return
+19 -2
appview/state/repo_util.go
···
"tangled.sh/tangled.sh/core/appview/pages"
)
-
func fullyResolvedRepo(r *http.Request) (*FullyResolvedRepo, error) {
+
func (s *State) fullyResolvedRepo(r *http.Request) (*FullyResolvedRepo, error) {
repoName := chi.URLParam(r, "repo")
+
ref := chi.URLParam(r, "ref")
+
knot, ok := r.Context().Value("knot").(string)
if !ok {
log.Println("malformed middleware")
···
return nil, fmt.Errorf("malformed middleware")
}
+
if ref == "" {
+
us, err := NewUnsignedClient(knot, s.config.Dev)
+
if err != nil {
+
return nil, fmt.Errorf("failed to setup new signed client: %w", err)
+
}
+
+
def, err := us.DefaultBranch(id.DID.String(), repoName)
+
if err != nil {
+
return nil, fmt.Errorf("failed to get default branch: %w", err)
+
}
+
+
ref = def.Branch
+
}
+
parsedRepoAt, err := syntax.ParseATURI(repoAt)
if err != nil {
log.Println("malformed repo at-uri")
-
return nil, fmt.Errorf("malformed middleware")
+
return nil, fmt.Errorf("malformed middleware: %w", err)
}
// pass through values from the middleware
···
RepoAt: parsedRepoAt,
Description: description,
AddedAt: addedAt,
+
Ref: ref,
}, nil
}
+13 -2
appview/state/signer.go
···
return us.client.Do(req)
}
-
func (us *UnsignedClient) DefaultBranch(ownerDid, repoName string) (*http.Response, error) {
+
func (us *UnsignedClient) DefaultBranch(ownerDid, repoName string) (*types.RepoDefaultBranchResponse, error) {
const (
Method = "GET"
)
···
return nil, err
}
-
return us.client.Do(req)
+
resp, err := us.client.Do(req)
+
if err != nil {
+
return nil, err
+
}
+
defer resp.Body.Close()
+
+
var branchResponse types.RepoDefaultBranchResponse
+
if err := json.NewDecoder(resp.Body).Decode(&branchResponse); err != nil {
+
return nil, err
+
}
+
+
return &branchResponse, nil
}
func (us *UnsignedClient) Capabilities() (*types.Capabilities, error) {
+1 -1
go.mod
···
github.com/resend/resend-go/v2 v2.15.0
github.com/sethvargo/go-envconfig v1.1.0
github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e
-
github.com/yuin/goldmark v1.4.13
+
github.com/yuin/goldmark v1.7.8
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028
)
+2 -1
go.sum
···
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-
github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+
github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
+
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b h1:CzigHMRySiX3drau9C6Q5CAbNIApmLdat5jPMqChvDA=
gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b/go.mod h1:/y/V339mxv2sZmYYR64O07VuCpdNZqCTwO8ZcouTMI8=
gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 h1:qwDnMxjkyLmAFgcfgTnfJrmYKWhHnci3GjDqcZp1M3Q=