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 {
|
||||||
log.Printf("%s %s: %s", r.Method, r.URL.Path, fmt.Errorf("rendering markdown: %w", err))
|
if !errors.Is(err, gitea.ErrMarkdownCooldown) {
|
||||||
|
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