Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,20 @@ Health check path: `/widget`.

---

## Deploy to Vercel

The repository ships with a serverless handler under `api/index.go` and a
`vercel.json` rewrite that forwards every request to it. Deploying through the
"Deploy with Vercel" button or via `vercel deploy` builds a Go Serverless
Function, so no Docker support is required. Once deployed, the instance exposes
the same routes described above.

You can set `HOST`, `PORT`, or `ADDR` environment variables if you want to run
the binary locally with `vercel dev`, but they are not required for production
deployments on Vercel.

---

## Deploy to a generic VPS

### Option A: Docker on VPS
Expand Down
24 changes: 24 additions & 0 deletions api/index.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package handler

import (
"net/http"
"time"

"giscus-proxy/internal/cache"
"giscus-proxy/internal/proxy"
)

var defaultHandler http.Handler

func init() {
p := proxy.New(proxy.Config{
Client: &http.Client{Timeout: 25 * time.Second},
Cache: cache.NewMemoryCache(256),
})
defaultHandler = p.Handler()
}

// Handler is the entry point for Vercel's Go runtime.
func Handler(w http.ResponseWriter, r *http.Request) {
defaultHandler.ServeHTTP(w, r)
}
2 changes: 2 additions & 0 deletions cmd/giscus-proxy/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ func main() {
Cache: cache.NewMemoryCache(512),
})

handler := p.Handler()
mux := http.NewServeMux()
p.Register(mux)

Expand All @@ -34,6 +35,7 @@ func main() {

srv := &http.Server{
Addr: addr,
Handler: handler,
Handler: mux,
ReadHeaderTimeout: 5 * time.Second,
ErrorLog: log.New(os.Stdout, "", 0),
Expand Down
7 changes: 7 additions & 0 deletions internal/proxy/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,13 @@ func (p *Proxy) Register(mux *http.ServeMux) {
mux.HandleFunc("/", p.handlePassthrough)
}

// Handler returns a ready-to-use HTTP handler that serves the proxy.
func (p *Proxy) Handler() http.Handler {
mux := http.NewServeMux()
p.Register(mux)
return mux
}

func (p *Proxy) logf(format string, args ...any) {
if p.logger == nil {
log.Printf(format, args...)
Expand Down
14 changes: 7 additions & 7 deletions vercel.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"builds": [
{
"src": "Dockerfile",
"use": "@vercel/docker"
}
]
}
"rewrites": [
{
"source": "/(.*)",
"destination": "/api/index"
Comment on lines +2 to +5

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve original request path in Vercel rewrite

The new serverless deployment rewrites every request to /api/index, but the proxy handler builds the upstream target from r.URL.Path (internal/proxy/passthrough.go around target = p.upstreamOrigin + r.URL.Path). When Vercel applies a rewrite, the function receives the destination path, not the original URL, so every request will be forwarded to https://giscus.app/api/index instead of the requested resource (e.g. /widget). The proxy therefore cannot serve any endpoint when deployed on Vercel. The rewrite needs to pass the original path (for example /api/index$1 or a query parameter) so the handler can reconstruct the upstream URL.

Useful? React with 👍 / 👎.

}
]
}
Loading