package main import ( "bytes" "io" "log" "net/http" "os" "strings" "github.com/labstack/echo/v5" "github.com/pocketbase/pocketbase" "github.com/pocketbase/pocketbase/apis" "github.com/pocketbase/pocketbase/core" ) func main() { app := pocketbase.New() app.OnBeforeServe().Add(func(e *core.ServeEvent) error { _, err := e.Router.AddRoute(echo.Route{ Method: http.MethodGet, Path: "/cors", Handler: func(c echo.Context) error { return corsProxyHandler(c) }, Middlewares: []echo.MiddlewareFunc{ apis.ActivityLogger(app), }, }) if err != nil { return err } return nil }) app.OnBeforeServe().Add(func(e *core.ServeEvent) error { // add new "GET /hello" route to the app router (echo) e.Router.GET("/*", apis.StaticDirectoryHandler(os.DirFS("./pb_public"), false)) return nil }) app.OnFileDownloadRequest().Add(func(e *core.FileDownloadEvent) error { if strings.HasSuffix(e.ServedPath, ".html") { e.HttpContext.Response().Header().Set("Content-Disposition", "inline") e.HttpContext.Response().Header().Set("Content-Security-Policy", "") } log.Println(e.ServedPath) log.Println(e.HttpContext) return nil }) if err := app.Start(); err != nil { log.Fatal(err) } } func corsProxyHandler(c echo.Context) error { // Get the target URL from the request query string url := c.QueryParam("url") if url == "" { return c.JSON(http.StatusBadRequest, "url param is required") } // Fetch the resource from the target URL resp, err := http.Get(url) if err != nil { return c.JSON(http.StatusInternalServerError, "Failed to fetch the URL") } defer resp.Body.Close() // If the content is a .m3u8 file, rewrite the URLs if strings.HasSuffix(url, ".m3u8") { bodyBytes, err := io.ReadAll(resp.Body) if err != nil { return c.JSON(http.StatusInternalServerError, "Failed to read response body") } proxyBaseURL := c.Scheme() + "://" + c.Request().Host + c.Path() + "?url=" + url[:strings.LastIndex(url, "/")+1] // Rewrite the URLs in the .m3u8 file lines := strings.Split(string(bodyBytes), "\n") for i, line := range lines { if strings.HasSuffix(line, ".ts") { lines[i] = proxyBaseURL + line } } modifiedContent := strings.Join(lines, "\n") // Write the modified content to the response c.Response().Header().Set("Content-Type", resp.Header.Get("Content-Type")) c.Response().WriteHeader(resp.StatusCode) _, err = io.Copy(c.Response().Writer, bytes.NewReader([]byte(modifiedContent))) if err != nil { return c.JSON(http.StatusInternalServerError, "Failed to copy modified response body") } return nil } // Copy the headers from the fetched response to the proxy response for key, values := range resp.Header { for _, value := range values { c.Response().Header().Add(key, value) } } // Copy the status code from the fetched response to the proxy response c.Response().WriteHeader(resp.StatusCode) // Copy the body from the fetched response to the proxy response _, err = io.Copy(c.Response().Writer, resp.Body) if err != nil { return c.JSON(http.StatusInternalServerError, "Failed to copy response body") } return nil }