89 lines
3.0 KiB
Go
89 lines
3.0 KiB
Go
package system
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"crypto/subtle"
|
|
"encoding/base64"
|
|
"net/url"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
account "git.ipao.vip/rogee/creator-hub/internal/account"
|
|
"git.ipao.vip/rogee/creator-hub/internal/creator"
|
|
hub "git.ipao.vip/rogee/creator-hub/internal/environment"
|
|
"github.com/gofiber/fiber/v3"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
func RegisterHealth(app *fiber.App, accountStore *account.Store, environmentStore *hub.Store, creatorStore *creator.Store) {
|
|
app.Get("/healthz", func(c fiber.Ctx) error {
|
|
c.Status(fiber.StatusNoContent)
|
|
return nil
|
|
})
|
|
app.Get("/readyz", func(c fiber.Ctx) error {
|
|
if accountStore == nil || environmentStore == nil || creatorStore == nil {
|
|
return c.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{"error": "service is not ready"})
|
|
}
|
|
ctx, cancel := context.WithTimeout(c.Context(), 2*time.Second)
|
|
defer cancel()
|
|
checks := []struct {
|
|
name string
|
|
fn func(context.Context) error
|
|
}{
|
|
{"phase_a", accountStore.Ping},
|
|
{"hub", environmentStore.Ping},
|
|
{"creator", creatorStore.Ping},
|
|
{"creator_schema", creatorStore.EnsureSchema},
|
|
}
|
|
for _, check := range checks {
|
|
if err := check.fn(ctx); err != nil {
|
|
logrus.WithError(err).WithField("check", check.name).Warn("control plane readiness check failed")
|
|
return c.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{"error": "service is not ready"})
|
|
}
|
|
}
|
|
c.Status(fiber.StatusNoContent)
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func Authenticate(username, password string) fiber.Handler {
|
|
wantUser, wantPassword := sha256.Sum256([]byte(username)), sha256.Sum256([]byte(password))
|
|
return func(c fiber.Ctx) error {
|
|
encoded, ok := strings.CutPrefix(c.Get(fiber.HeaderAuthorization), "Basic ")
|
|
decoded, err := base64.StdEncoding.DecodeString(encoded)
|
|
user, suppliedPassword, found := strings.Cut(string(decoded), ":")
|
|
gotUser, gotPassword := sha256.Sum256([]byte(user)), sha256.Sum256([]byte(suppliedPassword))
|
|
if !ok || err != nil || !found || subtle.ConstantTimeCompare(gotUser[:], wantUser[:]) != 1 || subtle.ConstantTimeCompare(gotPassword[:], wantPassword[:]) != 1 {
|
|
c.Set(fiber.HeaderWWWAuthenticate, `Basic realm="CreatorHub", charset="UTF-8"`)
|
|
return c.Status(fiber.StatusUnauthorized).JSON(map[string]string{"error": "authentication required"})
|
|
}
|
|
return c.Next()
|
|
}
|
|
}
|
|
|
|
func IsAPIPath(path string) bool {
|
|
for _, prefix := range []string{"/api", "/phase-a", "/gateways", "/browsers", "/network-exits"} {
|
|
if path == prefix || strings.HasPrefix(path, prefix+"/") {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func SPAHandler(directory string) fiber.Handler {
|
|
return func(c fiber.Ctx) error {
|
|
path, err := url.PathUnescape(strings.TrimPrefix(c.Path(), "/"))
|
|
path = filepath.Clean(path)
|
|
if err == nil && filepath.IsLocal(path) {
|
|
name := filepath.Join(directory, path)
|
|
if info, err := os.Stat(name); err == nil && !info.IsDir() {
|
|
return c.SendFile(name)
|
|
}
|
|
}
|
|
return c.SendFile(filepath.Join(directory, "index.html"))
|
|
}
|
|
}
|