Init with working version

Basic demo for Tally X AutoIxpert integration to showcase setup in
Go due to impossibility of open-sourcing n8n setup.
This commit is contained in:
rkmpa
2026-03-14 22:45:37 +01:00
commit 20acd6d77a
10 changed files with 791 additions and 0 deletions

91
helper.go Normal file
View File

@@ -0,0 +1,91 @@
package main
import (
"bytes"
"context"
"fmt"
"io"
"net/http"
"strconv"
"time"
)
const maxRetries = 3
var client = &http.Client{
Timeout: 30 * time.Second,
}
func Do(ctx context.Context, req *http.Request) (*http.Response, error) {
return client.Do(req)
}
func DoWithRateLimitReset(ctx context.Context, req *http.Request) (*http.Response, error) {
var getBody func() (io.ReadCloser, error)
if req.Body != nil {
b, err := io.ReadAll(req.Body)
if err != nil {
return nil, err
}
_ = req.Body.Close()
getBody = func() (io.ReadCloser, error) {
return io.NopCloser(bytes.NewReader(b)), nil
}
req.GetBody = getBody
req.Body, _ = getBody()
}
const safety = 250 * time.Millisecond // small buffer to avoid edge timing
for attempt := 0; attempt <= maxRetries; attempt++ {
r := req.Clone(ctx)
if getBody != nil {
r.Body, _ = getBody()
}
resp, err := client.Do(r)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusTooManyRequests {
return resp, nil
}
resetHeader := resp.Header.Get("X-RateLimit-Reset")
_ = resp.Body.Close()
if attempt == maxRetries {
return nil, fmt.Errorf("rate limited (429) after %d retries; last reset header=%q", maxRetries, resetHeader)
}
wait, err := waitUntilUnixReset(resetHeader, safety)
if err != nil {
// If the header is missing/invalid, fall back to a short backoff
wait = 2 * time.Second
}
select {
case <-time.After(wait):
case <-ctx.Done():
return nil, ctx.Err()
}
}
return nil, fmt.Errorf("unreachable")
}
func waitUntilUnixReset(h string, safety time.Duration) (time.Duration, error) {
secs, err := strconv.ParseInt(h, 10, 64)
if err != nil {
return 0, err
}
resetAt := time.Unix(secs, 0)
wait := time.Until(resetAt) + safety
if wait < 0 {
wait = 0
}
return wait, nil
}