Compare commits
	
		
			3 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 0407a52fb6 | |||
| c3b3e19a42 | |||
| 0d806965ab | 
@@ -31,6 +31,10 @@ dokku storage:mount teafolio /srv/teafolio-dokku/config.toml:/app/config.toml
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
## CHANGELOG
 | 
					## CHANGELOG
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2024-03-19 v1.4.1
 | 
				
			||||||
 | 
					- Add cooldown for failed markdown API calls
 | 
				
			||||||
 | 
					- Fix missing extra html elements when using fallback markdown renderer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
2024-03-19 v1.4.0
 | 
					2024-03-19 v1.4.0
 | 
				
			||||||
- Support using application token for Gitea API
 | 
					- Support using application token for Gitea API
 | 
				
			||||||
- Add fallback to internal markdown renderer if Gitea's API is unavailable
 | 
					- Add fallback to internal markdown renderer if Gitea's API is unavailable
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,21 +4,27 @@ import (
 | 
				
			|||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"net/url"
 | 
						"net/url"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
						"sync/atomic"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"golang.org/x/sync/semaphore"
 | 
						"golang.org/x/sync/semaphore"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const MarkdownFailureCooldownSeconds = 3600 // 1 hour
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type APIClient struct {
 | 
					type APIClient struct {
 | 
				
			||||||
	urlBase string
 | 
						urlBase string
 | 
				
			||||||
	orgName string
 | 
						orgName string
 | 
				
			||||||
	token   string
 | 
						token   string
 | 
				
			||||||
	apiSem  *semaphore.Weighted
 | 
						apiSem  *semaphore.Weighted
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						lastMarkdownFailure atomic.Int64
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewAPIClient creates a new Gitea API client for a single Gitea organization.
 | 
					// NewAPIClient creates a new Gitea API client for a single Gitea organization.
 | 
				
			||||||
@@ -297,8 +303,22 @@ func (ac *APIClient) topicsForRepo(ctx context.Context, repo string) ([]string,
 | 
				
			|||||||
	return tr.Topics, nil
 | 
						return tr.Topics, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var ErrMarkdownCooldown = errors.New("Markdown API was recently not functional")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ac *APIClient) checkMarkdownCooldown() error {
 | 
				
			||||||
 | 
						if ac.lastMarkdownFailure.Load()+MarkdownFailureCooldownSeconds > time.Now().Unix() {
 | 
				
			||||||
 | 
							return ErrMarkdownCooldown
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// renderMarkdown calls the remote Gitea server's own markdown renderer.
 | 
					// renderMarkdown calls the remote Gitea server's own markdown renderer.
 | 
				
			||||||
func (ac *APIClient) RenderMarkdown(ctx context.Context, repoName string, body string) ([]byte, error) {
 | 
					func (ac *APIClient) RenderMarkdown(ctx context.Context, repoName string, body string) ([]byte, error) {
 | 
				
			||||||
 | 
						if err := ac.checkMarkdownCooldown(); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err := ac.apiSem.Acquire(ctx, 1)
 | 
						err := ac.apiSem.Acquire(ctx, 1)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err // e.g. ctx closed
 | 
							return nil, err // e.g. ctx closed
 | 
				
			||||||
@@ -328,6 +348,7 @@ func (ac *APIClient) RenderMarkdown(ctx context.Context, repoName string, body s
 | 
				
			|||||||
	defer resp.Body.Close()
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if resp.StatusCode != 200 {
 | 
						if resp.StatusCode != 200 {
 | 
				
			||||||
 | 
							ac.lastMarkdownFailure.Store(time.Now().Unix())
 | 
				
			||||||
		return nil, fmt.Errorf("HTTP %d", resp.StatusCode)
 | 
							return nil, fmt.Errorf("HTTP %d", resp.StatusCode)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -336,6 +357,10 @@ func (ac *APIClient) RenderMarkdown(ctx context.Context, repoName string, body s
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// renderMarkdownRaw calls the remote Gitea server's own markdown renderer.
 | 
					// renderMarkdownRaw calls the remote Gitea server's own markdown renderer.
 | 
				
			||||||
func (ac *APIClient) renderMarkdownRaw(ctx context.Context, body []byte) ([]byte, error) {
 | 
					func (ac *APIClient) renderMarkdownRaw(ctx context.Context, body []byte) ([]byte, error) {
 | 
				
			||||||
 | 
						if err := ac.checkMarkdownCooldown(); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err := ac.apiSem.Acquire(ctx, 1)
 | 
						err := ac.apiSem.Acquire(ctx, 1)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err // e.g. ctx closed
 | 
							return nil, err // e.g. ctx closed
 | 
				
			||||||
@@ -356,6 +381,7 @@ func (ac *APIClient) renderMarkdownRaw(ctx context.Context, body []byte) ([]byte
 | 
				
			|||||||
	defer resp.Body.Close()
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if resp.StatusCode != 200 {
 | 
						if resp.StatusCode != 200 {
 | 
				
			||||||
 | 
							ac.lastMarkdownFailure.Store(time.Now().Unix())
 | 
				
			||||||
		return nil, fmt.Errorf("HTTP %d", resp.StatusCode)
 | 
							return nil, fmt.Errorf("HTTP %d", resp.StatusCode)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@ package main
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"html"
 | 
						"html"
 | 
				
			||||||
	"log"
 | 
						"log"
 | 
				
			||||||
@@ -9,6 +10,8 @@ import (
 | 
				
			|||||||
	"net/url"
 | 
						"net/url"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"teafolio/gitea"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/yuin/goldmark"
 | 
						"github.com/yuin/goldmark"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -59,7 +62,9 @@ func (this *Application) Repopage(w http.ResponseWriter, r *http.Request, repoNa
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	readmeHtml, err := this.gitea.RenderMarkdown(ctx, repoName, strings.Join(lines, "\n"))
 | 
						readmeHtml, err := this.gitea.RenderMarkdown(ctx, repoName, strings.Join(lines, "\n"))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if !errors.Is(err, gitea.ErrMarkdownCooldown) {
 | 
				
			||||||
			log.Printf("%s %s: %s", r.Method, r.URL.Path, fmt.Errorf("rendering markdown: %w", err))
 | 
								log.Printf("%s %s: %s", r.Method, r.URL.Path, fmt.Errorf("rendering markdown: %w", err))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Failed to use Gitea's markdown renderer
 | 
							// Failed to use Gitea's markdown renderer
 | 
				
			||||||
		// HTTP 401 (Unauthorized) started happening with this API sometime
 | 
							// HTTP 401 (Unauthorized) started happening with this API sometime
 | 
				
			||||||
@@ -67,7 +72,7 @@ func (this *Application) Repopage(w http.ResponseWriter, r *http.Request, repoNa
 | 
				
			|||||||
		// sufficient to make it work again
 | 
							// sufficient to make it work again
 | 
				
			||||||
		// Use our own one instead as a fallback
 | 
							// Use our own one instead as a fallback
 | 
				
			||||||
		buff := bytes.Buffer{}
 | 
							buff := bytes.Buffer{}
 | 
				
			||||||
		err = goldmark.Convert(readme, &buff)
 | 
							err = goldmark.Convert([]byte(strings.Join(lines, "\n")), &buff)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			// Built-in markdown renderer didn't work either
 | 
								// Built-in markdown renderer didn't work either
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user