Monorepo for Tangled tangled.org

appview/pages/markup: smart commit autolink renderer #1003

merged opened by boltless.me targeting master from sl/uupvwnkzxzom
Labels

None yet.

assignee

None yet.

Participants 3
AT URI
at://did:plc:xasnlahkri4ewmbuzly2rlc5/sh.tangled.repo.pull/3mcufej76qu22
+15 -15
Interdiff #2 โ†’ #3
+1 -1
appview/config/config.go
··· 29 return !c.Dev 30 } 31 32 - func (c *CoreConfig) Url() string { 33 if c.UseTLS() { 34 return "https://" + c.AppviewHost 35 }
··· 29 return !c.Dev 30 } 31 32 + func (c *CoreConfig) BaseUrl() string { 33 if c.UseTLS() { 34 return "https://" + c.AppviewHost 35 }
+1 -1
appview/oauth/oauth.go
··· 37 38 func New(config *config.Config, ph posthog.Client, db *db.DB, enforcer *rbac.Enforcer, res *idresolver.Resolver, logger *slog.Logger) (*OAuth, error) { 39 var oauthConfig oauth.ClientConfig 40 - clientUri := config.Core.Url() 41 callbackUri := clientUri + "/oauth/callback" 42 if config.Core.Dev { 43 oauthConfig = oauth.NewLocalhostConfig(callbackUri, []string{"atproto", "transition:generic"})
··· 37 38 func New(config *config.Config, ph posthog.Client, db *db.DB, enforcer *rbac.Enforcer, res *idresolver.Resolver, logger *slog.Logger) (*OAuth, error) { 39 var oauthConfig oauth.ClientConfig 40 + clientUri := config.Core.BaseUrl() 41 callbackUri := clientUri + "/oauth/callback" 42 if config.Core.Dev { 43 oauthConfig = oauth.NewLocalhostConfig(callbackUri, []string{"atproto", "transition:generic"})
appview/pages/markup/extension/tangledlink.go

This file has not been changed.

appview/pages/markup/markdown.go

This file has not been changed.

appview/pages/markup/markdown_test.go

This file has not been changed.

appview/pages/markup/reference_link.go

This file has not been changed.

appview/pages/pages.go

This file has not been changed.

+1 -1
appview/repo/archive.go
··· 66 if link := resp.Header.Get("Link"); link != "" { 67 if resolvedRef, err := extractImmutableLink(link); err == nil { 68 newLink := fmt.Sprintf("<%s/%s/archive/%s.tar.gz>; rel=\"immutable\"", 69 - rp.config.Core.Url(), f.DidSlashRepo(), resolvedRef) 70 w.Header().Set("Link", newLink) 71 } 72 }
··· 66 if link := resp.Header.Get("Link"); link != "" { 67 if resolvedRef, err := extractImmutableLink(link); err == nil { 68 newLink := fmt.Sprintf("<%s/%s/archive/%s.tar.gz>; rel=\"immutable\"", 69 + rp.config.Core.BaseUrl(), f.DidSlashRepo(), resolvedRef) 70 w.Header().Set("Link", newLink) 71 } 72 }
+4 -4
appview/repo/feed.go
··· 37 38 feed := &feeds.Feed{ 39 Title: fmt.Sprintf("activity feed for @%s", ownerSlashRepo), 40 - Link: &feeds.Link{Href: fmt.Sprintf("%s/%s", rp.config.Core.Url(), ownerSlashRepo), Type: "text/html", Rel: "alternate"}, 41 Items: make([]*feeds.Item, 0), 42 Updated: time.UnixMilli(0), 43 } ··· 86 mainItem := &feeds.Item{ 87 Title: fmt.Sprintf("[PR #%d] %s", pull.PullId, pull.Title), 88 Description: description, 89 - Link: &feeds.Link{Href: fmt.Sprintf("%s/%s/pulls/%d", rp.config.Core.Url(), ownerSlashRepo, pull.PullId)}, 90 Created: pull.Created, 91 Author: &feeds.Author{Name: fmt.Sprintf("@%s", owner.Handle)}, 92 } ··· 100 roundItem := &feeds.Item{ 101 Title: fmt.Sprintf("[PR #%d] %s (round #%d)", pull.PullId, pull.Title, round.RoundNumber), 102 Description: fmt.Sprintf("@%s submitted changes (at round #%d) on PR #%d in @%s", owner.Handle, round.RoundNumber, pull.PullId, ownerSlashRepo), 103 - Link: &feeds.Link{Href: fmt.Sprintf("%s/%s/pulls/%d/round/%d/", rp.config.Core.Url(), ownerSlashRepo, pull.PullId, round.RoundNumber)}, 104 Created: round.Created, 105 Author: &feeds.Author{Name: fmt.Sprintf("@%s", owner.Handle)}, 106 } ··· 124 return &feeds.Item{ 125 Title: fmt.Sprintf("[Issue #%d] %s", issue.IssueId, issue.Title), 126 Description: fmt.Sprintf("@%s %s issue #%d in @%s", owner.Handle, state, issue.IssueId, ownerSlashRepo), 127 - Link: &feeds.Link{Href: fmt.Sprintf("%s/%s/issues/%d", rp.config.Core.Url(), ownerSlashRepo, issue.IssueId)}, 128 Created: issue.Created, 129 Author: &feeds.Author{Name: fmt.Sprintf("@%s", owner.Handle)}, 130 }, nil
··· 37 38 feed := &feeds.Feed{ 39 Title: fmt.Sprintf("activity feed for @%s", ownerSlashRepo), 40 + Link: &feeds.Link{Href: fmt.Sprintf("%s/%s", rp.config.Core.BaseUrl(), ownerSlashRepo), Type: "text/html", Rel: "alternate"}, 41 Items: make([]*feeds.Item, 0), 42 Updated: time.UnixMilli(0), 43 } ··· 86 mainItem := &feeds.Item{ 87 Title: fmt.Sprintf("[PR #%d] %s", pull.PullId, pull.Title), 88 Description: description, 89 + Link: &feeds.Link{Href: fmt.Sprintf("%s/%s/pulls/%d", rp.config.Core.BaseUrl(), ownerSlashRepo, pull.PullId)}, 90 Created: pull.Created, 91 Author: &feeds.Author{Name: fmt.Sprintf("@%s", owner.Handle)}, 92 } ··· 100 roundItem := &feeds.Item{ 101 Title: fmt.Sprintf("[PR #%d] %s (round #%d)", pull.PullId, pull.Title, round.RoundNumber), 102 Description: fmt.Sprintf("@%s submitted changes (at round #%d) on PR #%d in @%s", owner.Handle, round.RoundNumber, pull.PullId, ownerSlashRepo), 103 + Link: &feeds.Link{Href: fmt.Sprintf("%s/%s/pulls/%d/round/%d/", rp.config.Core.BaseUrl(), ownerSlashRepo, pull.PullId, round.RoundNumber)}, 104 Created: round.Created, 105 Author: &feeds.Author{Name: fmt.Sprintf("@%s", owner.Handle)}, 106 } ··· 124 return &feeds.Item{ 125 Title: fmt.Sprintf("[Issue #%d] %s", issue.IssueId, issue.Title), 126 Description: fmt.Sprintf("@%s %s issue #%d in @%s", owner.Handle, state, issue.IssueId, ownerSlashRepo), 127 + Link: &feeds.Link{Href: fmt.Sprintf("%s/%s/issues/%d", rp.config.Core.BaseUrl(), ownerSlashRepo, issue.IssueId)}, 128 Created: issue.Created, 129 Author: &feeds.Author{Name: fmt.Sprintf("@%s", owner.Handle)}, 130 }, nil
+1 -1
appview/settings/settings.go
··· 300 func (s *Settings) verifyUrl(did string, email string, code string) string { 301 return fmt.Sprintf( 302 "%s/settings/emails/verify?did=%s&email=%s&code=%s", 303 - s.Config.Core.Url(), 304 url.QueryEscape(did), 305 url.QueryEscape(email), 306 url.QueryEscape(code),
··· 300 func (s *Settings) verifyUrl(did string, email string, code string) string { 301 return fmt.Sprintf( 302 "%s/settings/emails/verify?did=%s&email=%s&code=%s", 303 + s.Config.Core.BaseUrl(), 304 url.QueryEscape(did), 305 url.QueryEscape(email), 306 url.QueryEscape(code),
+4 -4
appview/state/profile.go
··· 415 416 feed := feeds.Feed{ 417 Title: fmt.Sprintf("%s's timeline", author.Name), 418 - Link: &feeds.Link{Href: fmt.Sprintf("%s/@%s", s.config.Core.Url(), id.Handle), Type: "text/html", Rel: "alternate"}, 419 Items: make([]*feeds.Item, 0), 420 Updated: time.UnixMilli(0), 421 Author: author, ··· 483 func (s *State) createPullRequestItem(pull *models.Pull, owner *identity.Identity, author *feeds.Author) *feeds.Item { 484 return &feeds.Item{ 485 Title: fmt.Sprintf("%s created pull request '%s' in @%s/%s", author.Name, pull.Title, owner.Handle, pull.Repo.Name), 486 - Link: &feeds.Link{Href: fmt.Sprintf("%s/@%s/%s/pulls/%d", s.config.Core.Url(), owner.Handle, pull.Repo.Name, pull.PullId), Type: "text/html", Rel: "alternate"}, 487 Created: pull.Created, 488 Author: author, 489 } ··· 492 func (s *State) createIssueItem(issue *models.Issue, owner *identity.Identity, author *feeds.Author) *feeds.Item { 493 return &feeds.Item{ 494 Title: fmt.Sprintf("%s created issue '%s' in @%s/%s", author.Name, issue.Title, owner.Handle, issue.Repo.Name), 495 - Link: &feeds.Link{Href: fmt.Sprintf("%s/@%s/%s/issues/%d", s.config.Core.Url(), owner.Handle, issue.Repo.Name, issue.IssueId), Type: "text/html", Rel: "alternate"}, 496 Created: issue.Created, 497 Author: author, 498 } ··· 512 513 return &feeds.Item{ 514 Title: title, 515 - Link: &feeds.Link{Href: fmt.Sprintf("%s/@%s/%s", s.config.Core.Url(), author.Name[1:], repo.Repo.Name), Type: "text/html", Rel: "alternate"}, // Remove @ prefix 516 Created: repo.Repo.Created, 517 Author: author, 518 }, nil
··· 415 416 feed := feeds.Feed{ 417 Title: fmt.Sprintf("%s's timeline", author.Name), 418 + Link: &feeds.Link{Href: fmt.Sprintf("%s/@%s", s.config.Core.BaseUrl(), id.Handle), Type: "text/html", Rel: "alternate"}, 419 Items: make([]*feeds.Item, 0), 420 Updated: time.UnixMilli(0), 421 Author: author, ··· 483 func (s *State) createPullRequestItem(pull *models.Pull, owner *identity.Identity, author *feeds.Author) *feeds.Item { 484 return &feeds.Item{ 485 Title: fmt.Sprintf("%s created pull request '%s' in @%s/%s", author.Name, pull.Title, owner.Handle, pull.Repo.Name), 486 + Link: &feeds.Link{Href: fmt.Sprintf("%s/@%s/%s/pulls/%d", s.config.Core.BaseUrl(), owner.Handle, pull.Repo.Name, pull.PullId), Type: "text/html", Rel: "alternate"}, 487 Created: pull.Created, 488 Author: author, 489 } ··· 492 func (s *State) createIssueItem(issue *models.Issue, owner *identity.Identity, author *feeds.Author) *feeds.Item { 493 return &feeds.Item{ 494 Title: fmt.Sprintf("%s created issue '%s' in @%s/%s", author.Name, issue.Title, owner.Handle, issue.Repo.Name), 495 + Link: &feeds.Link{Href: fmt.Sprintf("%s/@%s/%s/issues/%d", s.config.Core.BaseUrl(), owner.Handle, issue.Repo.Name, issue.IssueId), Type: "text/html", Rel: "alternate"}, 496 Created: issue.Created, 497 Author: author, 498 } ··· 512 513 return &feeds.Item{ 514 Title: title, 515 + Link: &feeds.Link{Href: fmt.Sprintf("%s/@%s/%s", s.config.Core.BaseUrl(), author.Name[1:], repo.Repo.Name), Type: "text/html", Rel: "alternate"}, // Remove @ prefix 516 Created: repo.Repo.Created, 517 Author: author, 518 }, nil
+1 -1
appview/pulls/opengraph.go
··· 199 currentX += commentTextWidth + 40 200 201 // Draw files changed 202 - err = statusStatsArea.DrawLucideIcon("file-diff", currentX, statsY+iconBaselineOffset-iconSize/2+5, iconSize, iconColor) 203 if err != nil { 204 log.Printf("failed to draw file diff icon: %v", err) 205 }
··· 199 currentX += commentTextWidth + 40 200 201 // Draw files changed 202 + err = statusStatsArea.DrawLucideIcon("static/icons/file-diff", currentX, statsY+iconBaselineOffset-iconSize/2+5, iconSize, iconColor) 203 if err != nil { 204 log.Printf("failed to draw file diff icon: %v", err) 205 }
+2 -2
nix/modules/appview.nix
··· 41 42 appviewHost = mkOption { 43 type = types.str; 44 - default = "https://tangled.org"; 45 - example = "https://example.com"; 46 description = "Public host URL for the appview instance"; 47 }; 48
··· 41 42 appviewHost = mkOption { 43 type = types.str; 44 + default = "tangled.org"; 45 + example = "example.com"; 46 description = "Public host URL for the appview instance"; 47 }; 48

History

5 rounds 11 comments
sign up or login to add to the discussion
2 commits
expand
appview/pages/markup: smart commit autolink renderer
appview: strip scheme from CoreConfig.AppviewHost
3/3 success
expand
expand 2 comments

@oppi.li makes sense. I completely forgot the indigo oauth behavior. I reverted the change.

lgtm, this bug is still present:

next, in appview/config/config.go, the default value for AppivewHost should be set to tangled.org

but should be a quick fix, i can apply that on master!

pull request successfully merged
2 commits
expand
appview/pages/markup: smart commit autolink renderer
appview: strip scheme from CoreConfig.AppviewHost
expand 3 comments

when using dev, the callback URL should always be http://127.0.0.1/oauth/callback (or http://localhost), regardless of what the AppviewHost is set to. the port is ignored iirc.

next, in appview/config/config.go, the default value for AppivewHost should be set to tangled.org (without the scheme).

i think BaseUrl is good naming choice. happy with that!

when using dev, the callback URL should always be http://127.0.0.1/oauth/callback (or http://localhost), regardless of what the AppviewHost is set to. the port is ignored iirc.

If someone set AppviewHost, I suspect they are testing on that host. I used to test on my own domain long ago. http://127.0.0.1:3000/oauth/callback is what was used in original code.

next, in appview/config/config.go, the default value for AppivewHost should be set to tangled.org

will do!

if you are using oauth.NewLocalhostConfig (as we do in dev), the redirect URL's host cannot be anything other than localhost or 127.0.0.1, if it is something else, you will get an error when performing the PAR request:

URL must use \"localhost\", \"127.0.0.1\" or \"[::1]\" as hostname at body.redirect_uri]"

when in dev, we should just ignore AppviewHost and use one of the predefined hosts. in the original code, the host is 127.0.0.1 which is one of the predefined hosts. the port is ignored anyway.

2 commits
expand
appview/pages/markup: smart commit autolink renderer
appview: strip scheme from CoreConfig.AppviewHost
3/3 success
expand
expand 5 comments
  • here, i think Url is a bit ambiguous to have on config, could use a better name here

rest of the changeset lgtm, will give this a test, thanks!

after some local testing, this seems to oauth, i am unable to login!

2026/01/21 06:25:06 WARN auth server request failed request=PAR statusCode=400 body="map[error:invalid_request error_description:Invalid authorization request: URL must use \"localhost\", \"127.0.0.1\" or \"[::1]\" as hostname at body.redirect_uri]"
2026/01/21 06:25:06 ERRO appview: failed to start auth handler=Login err="auth request failed: PAR request failed (HTTP 400): invalid_request"

@oppi.li I can't reproduce the oauth issue. Have you tried after setting TANGLED_APPVIEWHOST=127.0.0.1? you should remove the scheme now.

for Url() naming, would BaseUrl() be fine enough?

Also naming/consistency nit: tangledlink.go โ†’ tangled_link.go? Keeping in line with reference_link.go. Rest looks OK!

@anirudh.fi I'm matching with extension/atlink.go. Doesn't reference_link.go violates the golang conventions? yes, I'm the one who wrote both... inconsistencies all over the place ๐Ÿ™ˆ

1 commit
expand
appview/pages/markup: smart commit autolink renderer
3/3 success
expand
expand 1 comment

would be good to avoid hardcoding the appview host here!

1 commit
expand
appview/pages/markup: smart commit autolink renderer
1/3 failed, 2/3 success
expand
expand 0 comments