Files
creator-hub/internal/controlplane/api/system/system.go
T

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"))
}
}