Skip to content

Commit

Permalink
Add render route in ui-server (#2430)
Browse files Browse the repository at this point in the history
* Add render route in ui-server

* Render markdown in ui-server for OSS
  • Loading branch information
Alex-Tideman authored Nov 20, 2024
1 parent 532f95f commit a4b9d3c
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 0 deletions.
1 change: 1 addition & 0 deletions server/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.23

require (
github.com/coreos/go-oidc/v3 v3.11.0
github.com/gomarkdown/markdown v0.0.0-20241105142532-d03b89096d81
github.com/gorilla/securecookie v1.1.1
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0
github.com/labstack/echo/v4 v4.9.1
Expand Down
2 changes: 2 additions & 0 deletions server/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0
github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/gomarkdown/markdown v0.0.0-20241105142532-d03b89096d81 h1:5lyLWsV+qCkoYqsKUDuycESh9DEIPVKN6iCFeL7ag50=
github.com/gomarkdown/markdown v0.0.0-20241105142532-d03b89096d81/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
Expand Down
104 changes: 104 additions & 0 deletions server/server/route/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,21 @@ package route

import (
"bytes"
"crypto/rand"
"encoding/hex"
"fmt"
"html/template"
"io/fs"
"log"
"net/http"
"path"
"regexp"
"strings"

"github.com/gomarkdown/markdown"
"github.com/gomarkdown/markdown/html"
"github.com/gomarkdown/markdown/parser"

"github.com/labstack/echo/v4"
)

Expand Down Expand Up @@ -86,3 +94,99 @@ func buildUIAssetsHandler(assets fs.FS) echo.HandlerFunc {
handler := http.FileServer(http.FS(assets))
return echo.WrapHandler(handler)
}

func generateNonce() string {
bytes := make([]byte, 16)
if _, err := rand.Read(bytes); err != nil {
panic(err)
}
return hex.EncodeToString(bytes)
}

// Generate CSP header
func generateCSP(nonce string) string {
return fmt.Sprintf(
"base-uri 'self'; default-src 'none'; style-src 'nonce-%s'; script-src 'nonce-%s'; frame-ancestors 'self'; form-action 'none'; sandbox allow-same-origin allow-popups allow-popups-to-escape-sandbox;",
nonce,
nonce,
)
}

// Process markdown content
func processMarkdown(content string) string {
// Create markdown parser with extensions
extensions := parser.CommonExtensions | parser.AutoHeadingIDs
p := parser.NewWithExtensions(extensions)

// Parse markdown to AST
ast := p.Parse([]byte(content))

// Setup HTML renderer
opts := html.RendererOptions{
Flags: html.CommonFlags | html.HrefTargetBlank,
}
renderer := html.NewRenderer(opts)

// Render to HTML
return string(markdown.Render(ast, renderer))
}

// Template for the HTML page
const pageTemplate = `
<!DOCTYPE html>
<html>
<head>
<title>Rendered Markdown</title>
<base target="_blank">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style nonce="{{.Nonce}}">
{{.CSS}}
</style>
</head>
<body class="prose" {{if .Theme}}data-theme="{{.Theme}}"{{end}}>
<main>
{{.Content}}
</main>
</body>
</html>
`

func SetRenderRoute(e *echo.Echo, publicPath string) {
renderPath := path.Join(publicPath, "render")

// Parse template once at startup
tmpl := template.Must(template.New("page").Parse(pageTemplate))

e.GET(renderPath, func(c echo.Context) error {
content := c.QueryParam("content")
theme := c.QueryParam("theme")

// Process markdown to HTML
renderedHTML := processMarkdown(content)

nonce := generateNonce()

data := struct {
Content template.HTML
Nonce string
Theme string
CSS string
}{
Content: template.HTML(renderedHTML),
Nonce: nonce,
Theme: theme,
}

// Set headers
c.Response().Header().Set("Content-Type", "text/html")
c.Response().Header().Set("Content-Security-Policy", generateCSP(nonce))

err := tmpl.Execute(c.Response().Writer, data)
if err != nil {
return err
}

return nil
})
}
1 change: 1 addition & 0 deletions server/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ func NewServer(opts ...server_options.ServerOption) *Server {
}
}
route.SetUIRoutes(e, cfg.PublicPath, assets)
route.SetRenderRoute(e, cfg.PublicPath)
}

s := &Server{
Expand Down

0 comments on commit a4b9d3c

Please sign in to comment.