forked from tangled.org/core
Monorepo for Tangled

appview: pages/markup: rewrite links links to camo url

anirudh.fi 6b54d85a 3867a5ac

verified
Changed files
+57 -16
appview
+2
appview/config.go
··· 13 Dev bool `env:"TANGLED_DEV, default=false"` 14 JetstreamEndpoint string `env:"TANGLED_JETSTREAM_ENDPOINT, default=wss://jetstream1.us-east.bsky.network/subscribe"` 15 ResendApiKey string `env:"TANGLED_RESEND_API_KEY"` 16 } 17 18 func LoadConfig(ctx context.Context) (*Config, error) {
··· 13 Dev bool `env:"TANGLED_DEV, default=false"` 14 JetstreamEndpoint string `env:"TANGLED_JETSTREAM_ENDPOINT, default=wss://jetstream1.us-east.bsky.network/subscribe"` 15 ResendApiKey string `env:"TANGLED_RESEND_API_KEY"` 16 + CamoHost string `env:"TANGLED_CAMO_HOST, default=https://camo.tangled.sh"` 17 + CamoSharedSecret string `env:"TANGLED_CAMO_SHARED_SECRET"` 18 } 19 20 func LoadConfig(ctx context.Context) (*Config, error) {
+1 -1
appview/pages/funcmap.go
··· 143 return v.Slice(start, end).Interface() 144 }, 145 "markdown": func(text string) template.HTML { 146 - rctx := &markup.RenderContext{} 147 return template.HTML(rctx.RenderMarkdown(text)) 148 }, 149 "isNil": func(t any) bool {
··· 143 return v.Slice(start, end).Interface() 144 }, 145 "markdown": func(text string) template.HTML { 146 + rctx := &markup.RenderContext{RendererType: markup.RendererTypeDefault} 147 return template.HTML(rctx.RenderMarkdown(text)) 148 }, 149 "isNil": func(t any) bool {
+31
appview/pages/markup/camo.go
···
··· 1 + package markup 2 + 3 + import ( 4 + "crypto/hmac" 5 + "crypto/sha256" 6 + "encoding/hex" 7 + "fmt" 8 + 9 + "github.com/yuin/goldmark/ast" 10 + ) 11 + 12 + func generateCamoURL(baseURL, secret, imageURL string) string { 13 + h := hmac.New(sha256.New, []byte(secret)) 14 + h.Write([]byte(imageURL)) 15 + signature := hex.EncodeToString(h.Sum(nil)) 16 + hexURL := hex.EncodeToString([]byte(imageURL)) 17 + return fmt.Sprintf("%s/%s/%s", baseURL, signature, hexURL) 18 + } 19 + 20 + func (rctx *RenderContext) camoImageLinkTransformer(img *ast.Image) { 21 + // don't camo on dev 22 + if rctx.IsDev { 23 + return 24 + } 25 + 26 + dst := string(img.Destination) 27 + 28 + if rctx.CamoUrl != "" && rctx.CamoSecret != "" { 29 + img.Destination = []byte(generateCamoURL(rctx.CamoUrl, rctx.CamoSecret, dst)) 30 + } 31 + }
+12 -1
appview/pages/markup/markdown.go
··· 21 const ( 22 // RendererTypeRepoMarkdown is for repository documentation markdown files 23 RendererTypeRepoMarkdown RendererType = iota 24 ) 25 26 // RenderContext holds the contextual data for rendering markdown. 27 // It can be initialized empty, and that'll skip any transformations. 28 type RenderContext struct { 29 repoinfo.RepoInfo 30 IsDev bool 31 RendererType RendererType ··· 73 a.rctx.relativeLinkTransformer(n.(*ast.Link)) 74 case *ast.Image: 75 a.rctx.imageFromKnotTransformer(n.(*ast.Image)) 76 } 77 - // more types here like RendererTypeIssue/Pull etc. 78 } 79 80 return ast.WalkContinue, nil
··· 21 const ( 22 // RendererTypeRepoMarkdown is for repository documentation markdown files 23 RendererTypeRepoMarkdown RendererType = iota 24 + // RendererTypeDefault is non-repo markdown, like issues/pulls/comments. 25 + RendererTypeDefault 26 ) 27 28 // RenderContext holds the contextual data for rendering markdown. 29 // It can be initialized empty, and that'll skip any transformations. 30 type RenderContext struct { 31 + CamoUrl string 32 + CamoSecret string 33 repoinfo.RepoInfo 34 IsDev bool 35 RendererType RendererType ··· 77 a.rctx.relativeLinkTransformer(n.(*ast.Link)) 78 case *ast.Image: 79 a.rctx.imageFromKnotTransformer(n.(*ast.Image)) 80 + a.rctx.camoImageLinkTransformer(n.(*ast.Image)) 81 } 82 + 83 + case RendererTypeDefault: 84 + switch n.(type) { 85 + case *ast.Image: 86 + a.rctx.imageFromKnotTransformer(n.(*ast.Image)) 87 + a.rctx.camoImageLinkTransformer(n.(*ast.Image)) 88 + } 89 } 90 91 return ast.WalkContinue, nil
+10 -13
appview/pages/pages.go
··· 15 "path/filepath" 16 "strings" 17 18 "tangled.sh/tangled.sh/core/appview/auth" 19 "tangled.sh/tangled.sh/core/appview/db" 20 "tangled.sh/tangled.sh/core/appview/pages/markup" ··· 43 rctx *markup.RenderContext 44 } 45 46 - func NewPages(dev bool) *Pages { 47 // initialized with safe defaults, can be overriden per use 48 rctx := &markup.RenderContext{ 49 - IsDev: dev, 50 } 51 52 p := &Pages{ 53 t: make(map[string]*template.Template), 54 - dev: dev, 55 embedFS: Files, 56 rctx: rctx, 57 templateDir: "appview/pages", ··· 379 return p.executeRepo("repo/empty", w, params) 380 } 381 382 - p.rctx = &markup.RenderContext{ 383 - RepoInfo: params.RepoInfo, 384 - IsDev: p.dev, 385 - RendererType: markup.RendererTypeRepoMarkdown, 386 - } 387 388 if params.ReadmeFileName != "" { 389 var htmlString string ··· 508 if params.ShowRendered { 509 switch markup.GetFormat(params.Path) { 510 case markup.FormatMarkdown: 511 - p.rctx = &markup.RenderContext{ 512 - RepoInfo: params.RepoInfo, 513 - IsDev: p.dev, 514 - RendererType: markup.RendererTypeRepoMarkdown, 515 - } 516 params.RenderedContents = template.HTML(p.rctx.RenderMarkdown(params.Contents)) 517 } 518 }
··· 15 "path/filepath" 16 "strings" 17 18 + "tangled.sh/tangled.sh/core/appview" 19 "tangled.sh/tangled.sh/core/appview/auth" 20 "tangled.sh/tangled.sh/core/appview/db" 21 "tangled.sh/tangled.sh/core/appview/pages/markup" ··· 44 rctx *markup.RenderContext 45 } 46 47 + func NewPages(config *appview.Config) *Pages { 48 // initialized with safe defaults, can be overriden per use 49 rctx := &markup.RenderContext{ 50 + IsDev: config.Dev, 51 + CamoUrl: config.CamoHost, 52 + CamoSecret: config.CamoSharedSecret, 53 } 54 55 p := &Pages{ 56 t: make(map[string]*template.Template), 57 + dev: config.Dev, 58 embedFS: Files, 59 rctx: rctx, 60 templateDir: "appview/pages", ··· 382 return p.executeRepo("repo/empty", w, params) 383 } 384 385 + p.rctx.RepoInfo = params.RepoInfo 386 + p.rctx.RendererType = markup.RendererTypeRepoMarkdown 387 388 if params.ReadmeFileName != "" { 389 var htmlString string ··· 508 if params.ShowRendered { 509 switch markup.GetFormat(params.Path) { 510 case markup.FormatMarkdown: 511 + p.rctx.RepoInfo = params.RepoInfo 512 + p.rctx.RendererType = markup.RendererTypeRepoMarkdown 513 params.RenderedContents = template.HTML(p.rctx.RenderMarkdown(params.Contents)) 514 } 515 }
+1 -1
appview/state/state.go
··· 55 56 clock := syntax.NewTIDClock(0) 57 58 - pgs := pages.NewPages(config.Dev) 59 60 resolver := appview.NewResolver() 61
··· 55 56 clock := syntax.NewTIDClock(0) 57 58 + pgs := pages.NewPages(config) 59 60 resolver := appview.NewResolver() 61