2 Commits
Author SHA1 Message Date
Story Crater Bot b6767e247c fix(deps,ci): update module path to forgejo.riotpiao.com/rock/homelab-frontend, switch to GITHUB_TOKEN
Build / Build and push image (push) Failing after 12s
CI / Test, vet, build (push) Successful in 2m18s
2026-08-21 20:46:45 -07:00
Story Crater Bot 6cf5c3e5a9 fix(server): stop WriteTimeout from killing in-progress LLM streams
Build / Build and push image (push) Successful in 34s
CI / Test, vet, build (push) Successful in 2m16s
http.Server.WriteTimeout is an absolute deadline over the whole
response, not an inactivity timeout -- 15s was cutting off SSE streams
from the reasoning model mid-generation, surfacing to clients as a
"terminated" error well before the model finished. Switch to
ReadHeaderTimeout (protects against slow headers without capping body
duration) and raise WriteTimeout to match the edge nginx Ingress's
proxy-read/send-timeout of 3600s.
2026-08-21 20:06:10 -07:00
24 changed files with 38 additions and 32 deletions
+2 -2
View File
@@ -67,10 +67,10 @@ jobs:
- name: Registry login
run: |
echo "${FORGEJO_PAT}" | docker login "${REGISTRY}" \
echo "${GITHUB_TOKEN}" | docker login "${REGISTRY}" \
--username rock --password-stdin
env:
FORGEJO_PAT: ${{ secrets.FORGEJO_RIOTPIAO_PAT }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build
run: |
+3 -3
View File
@@ -9,9 +9,9 @@ import (
"os/signal"
"syscall"
"github.com/Riotpiaole/homelab-frontend/internal/config"
"github.com/Riotpiaole/homelab-frontend/internal/proxy"
"github.com/Riotpiaole/homelab-frontend/internal/server"
"forgejo.riotpiao.com/rock/homelab-frontend/internal/config"
"forgejo.riotpiao.com/rock/homelab-frontend/internal/proxy"
"forgejo.riotpiao.com/rock/homelab-frontend/internal/server"
)
func main() {
+1 -1
View File
@@ -8,7 +8,7 @@ import (
"time"
"go.temporal.io/sdk/client"
"github.com/Riotpiaole/homelab-frontend/internal/workflow"
"forgejo.riotpiao.com/rock/homelab-frontend/internal/workflow"
)
func main() {
+1 -1
View File
@@ -9,7 +9,7 @@ import (
"go.temporal.io/sdk/client"
"go.temporal.io/sdk/worker"
"github.com/Riotpiaole/homelab-frontend/internal/workflow"
"forgejo.riotpiao.com/rock/homelab-frontend/internal/workflow"
)
func main() {
+1 -1
View File
@@ -1,4 +1,4 @@
module github.com/Riotpiaole/homelab-frontend
module forgejo.riotpiao.com/rock/homelab-frontend
go 1.25.4
+1 -1
View File
@@ -3,7 +3,7 @@ package config_test
import (
"testing"
"github.com/Riotpiaole/homelab-frontend/internal/config"
"forgejo.riotpiao.com/rock/homelab-frontend/internal/config"
)
// TestLoadRoutesMissingAuthRequired tests that the auth-required flag is not a silent default:
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"testing"
"time"
"github.com/Riotpiaole/homelab-frontend/internal/config"
"forgejo.riotpiao.com/rock/homelab-frontend/internal/config"
)
// TestLoadRoutesValidConfig tests that a valid configuration loads correctly.
+1 -1
View File
@@ -5,7 +5,7 @@ import (
"testing"
"time"
"github.com/Riotpiaole/homelab-frontend/internal/config"
"forgejo.riotpiao.com/rock/homelab-frontend/internal/config"
)
// TestLoadIntegration tests the full Load function with CONFIG_PATH env var
+1 -1
View File
@@ -11,7 +11,7 @@ import (
"testing"
"time"
"github.com/Riotpiaole/homelab-frontend/internal/config"
"forgejo.riotpiao.com/rock/homelab-frontend/internal/config"
)
// TestBodyBasedDispatch verifies that /v1/chat/completions routes based on model field.
+1 -1
View File
@@ -9,7 +9,7 @@ import (
"testing"
"time"
"github.com/Riotpiaole/homelab-frontend/internal/config"
"forgejo.riotpiao.com/rock/homelab-frontend/internal/config"
)
// TestBodySizeCapExact verifies that a body exactly at the cap is accepted.
+1 -1
View File
@@ -10,7 +10,7 @@ import (
"strings"
"testing"
"github.com/Riotpiaole/homelab-frontend/internal/config"
"forgejo.riotpiao.com/rock/homelab-frontend/internal/config"
)
// TestEmbeddingsPassthroughNoRewrite verifies /v1/embeddings is not rewritten
+1 -1
View File
@@ -8,7 +8,7 @@ import (
"testing"
"time"
"github.com/Riotpiaole/homelab-frontend/internal/config"
"forgejo.riotpiao.com/rock/homelab-frontend/internal/config"
)
// TestHeaderHygiene verifies that headers are properly filtered and forwarded.
+1 -1
View File
@@ -10,7 +10,7 @@ import (
"testing"
"time"
"github.com/Riotpiaole/homelab-frontend/internal/config"
"forgejo.riotpiao.com/rock/homelab-frontend/internal/config"
)
// ModelListResponse represents the response shape for GET /v1/models
+2 -2
View File
@@ -13,8 +13,8 @@ import (
"strings"
"time"
"github.com/Riotpiaole/homelab-frontend/internal/config"
"github.com/Riotpiaole/homelab-frontend/internal/logging"
"forgejo.riotpiao.com/rock/homelab-frontend/internal/config"
"forgejo.riotpiao.com/rock/homelab-frontend/internal/logging"
)
// Handler is a reverse proxy that routes requests to configured upstreams.
+1 -1
View File
@@ -9,7 +9,7 @@ import (
"testing"
"time"
"github.com/Riotpiaole/homelab-frontend/internal/config"
"forgejo.riotpiao.com/rock/homelab-frontend/internal/config"
)
func TestProxyBasic(t *testing.T) {
+1 -1
View File
@@ -9,7 +9,7 @@ import (
"net/http"
"net/url"
"github.com/Riotpiaole/homelab-frontend/internal/config"
"forgejo.riotpiao.com/rock/homelab-frontend/internal/config"
)
// RouteRequest determines which upstream should handle the request.
+1 -1
View File
@@ -10,7 +10,7 @@ import (
"testing"
"time"
"github.com/Riotpiaole/homelab-frontend/internal/config"
"forgejo.riotpiao.com/rock/homelab-frontend/internal/config"
)
// TestSSEUnbuffered verifies that SSE events stream to the client without buffering.
+1 -1
View File
@@ -10,7 +10,7 @@ import (
"testing"
"time"
"github.com/Riotpiaole/homelab-frontend/internal/config"
"forgejo.riotpiao.com/rock/homelab-frontend/internal/config"
)
// TestConnectTimeout verifies that connections fail at the configured timeout.
+1 -1
View File
@@ -11,7 +11,7 @@ import (
"testing"
"time"
"github.com/Riotpiaole/homelab-frontend/internal/config"
"forgejo.riotpiao.com/rock/homelab-frontend/internal/config"
)
// OpenAI-style tool definition
+1 -1
View File
@@ -9,7 +9,7 @@ import (
"strings"
"testing"
"github.com/Riotpiaole/homelab-frontend/internal/config"
"forgejo.riotpiao.com/rock/homelab-frontend/internal/config"
)
// RFC 9457 Problem Details
+1 -1
View File
@@ -8,7 +8,7 @@ import (
"testing"
"time"
"github.com/Riotpiaole/homelab-frontend/internal/server"
"forgejo.riotpiao.com/rock/homelab-frontend/internal/server"
)
// TestHealthEndpoints verifies health endpoint behavior.
+11 -5
View File
@@ -21,11 +21,17 @@ type Server struct {
func New(listenAddr string, shutdownTimeout time.Duration, handler http.Handler) *Server {
return &Server{
httpServer: &http.Server{
Addr: listenAddr,
Handler: handler,
ReadTimeout: 15 * time.Second,
WriteTimeout: 15 * time.Second,
IdleTimeout: 60 * time.Second,
Addr: listenAddr,
Handler: handler,
// ReadHeaderTimeout (not ReadTimeout) and a long WriteTimeout: both
// ReadTimeout and WriteTimeout are absolute deadlines covering the
// whole request/response body, not inactivity timeouts -- a 15s
// WriteTimeout here was killing in-progress LLM SSE streams (proxy.go's
// outbound transport deliberately avoids this same mistake). Mirrors
// the edge nginx Ingress's proxy-read/send-timeout of 3600s.
ReadHeaderTimeout: 15 * time.Second,
WriteTimeout: 1 * time.Hour,
IdleTimeout: 60 * time.Second,
},
shutdownTimeout: shutdownTimeout,
healthChecker: NewHealthChecker(false, false),
+1 -1
View File
@@ -10,7 +10,7 @@ import (
"testing"
"time"
"github.com/Riotpiaole/homelab-frontend/internal/server"
"forgejo.riotpiao.com/rock/homelab-frontend/internal/server"
)
// TestGracefulShutdown verifies that:
+1 -1
View File
@@ -8,7 +8,7 @@ import (
"testing"
"time"
"github.com/Riotpiaole/homelab-frontend/internal/config"
"forgejo.riotpiao.com/rock/homelab-frontend/internal/config"
)
// startForTest binds the stub on an ephemeral port so the suite does not fight