bug fix on webdav url
This commit is contained in:
+17
-28
@@ -34,7 +34,6 @@ var contentTypeByExt = map[string]string{
|
||||
// HealthResponse is the response for /health
|
||||
type HealthResponse struct {
|
||||
Status string `json:"status"`
|
||||
Role string `json:"role"`
|
||||
Redis bool `json:"redis"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
Leader *leader.LeaderLockInfo `json:"leader,omitempty"`
|
||||
@@ -43,7 +42,6 @@ type HealthResponse struct {
|
||||
// StatusResponse is the response for /status
|
||||
type StatusResponse struct {
|
||||
Status string `json:"status"`
|
||||
Role string `json:"role"`
|
||||
Redis bool `json:"redis"`
|
||||
ServiceName string `json:"serviceName"`
|
||||
Version string `json:"version"`
|
||||
@@ -77,10 +75,9 @@ var startTime = time.Now()
|
||||
func (s *Server) handleHealth(w http.ResponseWriter, r *http.Request) {
|
||||
response := HealthResponse{
|
||||
Status: "ok",
|
||||
Role: string(s.roleProvider.Role()),
|
||||
Redis: s.storage.Health(),
|
||||
Timestamp: time.Now().UTC().Format(time.RFC3339),
|
||||
Leader: s.roleProvider.LeaderInfo(),
|
||||
Leader: s.leaderProvider.LeaderInfo(),
|
||||
}
|
||||
|
||||
if !response.Redis {
|
||||
@@ -100,13 +97,12 @@ func (s *Server) handleStatus(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
response := StatusResponse{
|
||||
Status: "ok",
|
||||
Role: string(s.roleProvider.Role()),
|
||||
Redis: s.storage.Health(),
|
||||
ServiceName: s.config.ServiceName,
|
||||
Version: s.config.ServiceVersion,
|
||||
Uptime: int64(time.Since(startTime).Seconds()),
|
||||
FileCount: fileCount,
|
||||
Leader: s.roleProvider.LeaderInfo(),
|
||||
Leader: s.leaderProvider.LeaderInfo(),
|
||||
}
|
||||
|
||||
if !response.Redis {
|
||||
@@ -118,7 +114,7 @@ func (s *Server) handleStatus(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// handleLeader handles GET /leader
|
||||
func (s *Server) handleLeader(w http.ResponseWriter, r *http.Request) {
|
||||
info := s.roleProvider.LeaderInfo()
|
||||
info := s.leaderProvider.LeaderInfo()
|
||||
if info == nil {
|
||||
writeError(w, http.StatusServiceUnavailable, "no leader available")
|
||||
return
|
||||
@@ -127,39 +123,32 @@ func (s *Server) handleLeader(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, http.StatusOK, info)
|
||||
}
|
||||
|
||||
// handleRole handles GET /role
|
||||
func (s *Server) handleRole(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, http.StatusOK, map[string]string{
|
||||
"role": string(s.roleProvider.Role()),
|
||||
})
|
||||
}
|
||||
|
||||
// URLsResponse is the response for /urls
|
||||
type URLsResponse struct {
|
||||
Hostname string `json:"hostname"`
|
||||
BaseUrl string `json:"baseUrl"`
|
||||
ApiUrl string `json:"apiUrl"`
|
||||
RedisUrl string `json:"redisUrl"`
|
||||
WebdavUrl string `json:"webdavUrl"`
|
||||
IsLeader bool `json:"isLeader"`
|
||||
Hostname string `json:"hostname"`
|
||||
BaseUrl string `json:"baseUrl"`
|
||||
ApiUrl string `json:"apiUrl"`
|
||||
RedisUrl string `json:"redisUrl"`
|
||||
WebdavUrl string `json:"webdavUrl"`
|
||||
WebdavUrlInternal string `json:"webdavUrlInternal"`
|
||||
}
|
||||
|
||||
// handleURLs handles GET /urls
|
||||
// Returns URLs for the current leader (or this instance if leader)
|
||||
// Returns URLs for this instance (always leader since Go binary only runs as leader)
|
||||
func (s *Server) handleURLs(w http.ResponseWriter, r *http.Request) {
|
||||
info := s.roleProvider.LeaderInfo()
|
||||
info := s.leaderProvider.LeaderInfo()
|
||||
if info == nil {
|
||||
writeError(w, http.StatusServiceUnavailable, "no leader available")
|
||||
return
|
||||
}
|
||||
|
||||
response := URLsResponse{
|
||||
Hostname: info.Hostname,
|
||||
BaseUrl: info.BaseUrl,
|
||||
ApiUrl: info.ApiUrl,
|
||||
RedisUrl: info.RedisUrl,
|
||||
WebdavUrl: info.WebdavUrl,
|
||||
IsLeader: s.roleProvider.IsLeader(),
|
||||
Hostname: info.Hostname,
|
||||
BaseUrl: info.BaseUrl,
|
||||
ApiUrl: info.ApiUrl,
|
||||
RedisUrl: info.RedisUrl,
|
||||
WebdavUrl: info.WebdavUrl,
|
||||
WebdavUrlInternal: info.WebdavUrlInternal,
|
||||
}
|
||||
|
||||
writeJSON(w, http.StatusOK, response)
|
||||
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
// Server is the HTTP API server for meta-core
|
||||
type Server struct {
|
||||
config *config.Config
|
||||
roleProvider leader.RoleProvider
|
||||
leaderProvider *leader.LeaderInfoProvider
|
||||
discovery *discovery.Service
|
||||
cleaner *discovery.Cleaner
|
||||
storage *storage.Client
|
||||
@@ -48,18 +48,18 @@ type Server struct {
|
||||
// NewServer creates a new API server
|
||||
func NewServer(
|
||||
cfg *config.Config,
|
||||
roleProvider leader.RoleProvider,
|
||||
leaderProvider *leader.LeaderInfoProvider,
|
||||
disc *discovery.Service,
|
||||
cleaner *discovery.Cleaner,
|
||||
stor *storage.Client,
|
||||
) *Server {
|
||||
s := &Server{
|
||||
config: cfg,
|
||||
roleProvider: roleProvider,
|
||||
discovery: disc,
|
||||
cleaner: cleaner,
|
||||
storage: stor,
|
||||
router: mux.NewRouter(),
|
||||
config: cfg,
|
||||
leaderProvider: leaderProvider,
|
||||
discovery: disc,
|
||||
cleaner: cleaner,
|
||||
storage: stor,
|
||||
router: mux.NewRouter(),
|
||||
}
|
||||
|
||||
// Initialize file watcher/scanner and dispatcher
|
||||
@@ -130,7 +130,6 @@ func (s *Server) setupRoutes() {
|
||||
s.router.HandleFunc("/health", s.handleHealth).Methods("GET")
|
||||
s.router.HandleFunc("/status", s.handleStatus).Methods("GET")
|
||||
s.router.HandleFunc("/leader", s.handleLeader).Methods("GET")
|
||||
s.router.HandleFunc("/role", s.handleRole).Methods("GET")
|
||||
s.router.HandleFunc("/urls", s.handleURLs).Methods("GET")
|
||||
|
||||
// Metadata Editor API routes (must be before /meta/{hash} routes)
|
||||
|
||||
@@ -21,22 +21,15 @@ type ServiceInfo struct {
|
||||
BaseUrl string `json:"baseUrl"`
|
||||
Status string `json:"status"`
|
||||
LastHeartbeat string `json:"lastHeartbeat"`
|
||||
Role string `json:"role,omitempty"` // "leader", "follower", or empty (for non-meta-core services)
|
||||
}
|
||||
|
||||
// RoleProvider interface for getting the current role
|
||||
type RoleProvider interface {
|
||||
Role() string
|
||||
}
|
||||
|
||||
// Service handles service registration and discovery
|
||||
type Service struct {
|
||||
config *config.Config
|
||||
servicesDir string
|
||||
serviceFile string
|
||||
info *ServiceInfo
|
||||
roleProvider RoleProvider
|
||||
mu sync.RWMutex
|
||||
config *config.Config
|
||||
servicesDir string
|
||||
serviceFile string
|
||||
info *ServiceInfo
|
||||
mu sync.RWMutex
|
||||
|
||||
stopChan chan struct{}
|
||||
wg sync.WaitGroup
|
||||
@@ -53,11 +46,6 @@ func NewService(cfg *config.Config) *Service {
|
||||
}
|
||||
}
|
||||
|
||||
// SetRoleProvider sets the role provider for this service
|
||||
func (s *Service) SetRoleProvider(rp RoleProvider) {
|
||||
s.roleProvider = rp
|
||||
}
|
||||
|
||||
// Start begins service registration and heartbeat
|
||||
func (s *Service) Start() error {
|
||||
log.Printf("[Discovery] Starting service discovery for %s", s.config.ServiceName)
|
||||
@@ -115,11 +103,6 @@ func (s *Service) buildServiceInfo() *ServiceInfo {
|
||||
LastHeartbeat: time.Now().UTC().Format(time.RFC3339),
|
||||
}
|
||||
|
||||
// Add role if role provider is set (e.g., for meta-core)
|
||||
if s.roleProvider != nil {
|
||||
info.Role = s.roleProvider.Role()
|
||||
}
|
||||
|
||||
return info
|
||||
}
|
||||
|
||||
@@ -164,7 +147,7 @@ func (s *Service) heartbeatLoop() {
|
||||
}
|
||||
}
|
||||
|
||||
// heartbeat updates the last heartbeat timestamp and role
|
||||
// heartbeat updates the last heartbeat timestamp
|
||||
func (s *Service) heartbeat() {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
@@ -175,11 +158,6 @@ func (s *Service) heartbeat() {
|
||||
|
||||
s.info.LastHeartbeat = time.Now().UTC().Format(time.RFC3339)
|
||||
|
||||
// Update role in case it changed (e.g., leader election)
|
||||
if s.roleProvider != nil {
|
||||
s.info.Role = s.roleProvider.Role()
|
||||
}
|
||||
|
||||
if err := s.writeServiceInfo(s.info); err != nil {
|
||||
log.Printf("[Discovery] Failed to update heartbeat: %v", err)
|
||||
}
|
||||
|
||||
+32
-66
@@ -11,60 +11,27 @@ import (
|
||||
|
||||
// LeaderLockInfo contains all URLs and metadata for the leader
|
||||
type LeaderLockInfo struct {
|
||||
Hostname string `json:"hostname"`
|
||||
BaseUrl string `json:"baseUrl"`
|
||||
ApiUrl string `json:"apiUrl"` // meta-core API (port 9000)
|
||||
RedisUrl string `json:"redisUrl"`
|
||||
WebdavUrl string `json:"webdavUrl"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
PID int `json:"pid"`
|
||||
Hostname string `json:"hostname"`
|
||||
BaseUrl string `json:"baseUrl"`
|
||||
ApiUrl string `json:"apiUrl"` // meta-core API (port 9000)
|
||||
RedisUrl string `json:"redisUrl"`
|
||||
WebdavUrl string `json:"webdavUrl"` // External WebDAV URL (via baseUrl)
|
||||
WebdavUrlInternal string `json:"webdavUrlInternal"` // Internal WebDAV URL (direct to port 9000)
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
PID int `json:"pid"`
|
||||
}
|
||||
|
||||
// Role represents the current role of this instance
|
||||
type Role string
|
||||
|
||||
const (
|
||||
RoleUnknown Role = "unknown"
|
||||
RoleLeader Role = "leader"
|
||||
RoleFollower Role = "follower"
|
||||
)
|
||||
|
||||
// RoleProvider provides role and leader info
|
||||
// This interface is used by the API server to get role information
|
||||
// without needing the full election logic (which is now in bash)
|
||||
type RoleProvider interface {
|
||||
Role() Role
|
||||
LeaderInfo() *LeaderLockInfo
|
||||
IsLeader() bool
|
||||
}
|
||||
|
||||
// LocalRoleProvider provides role info for this instance
|
||||
// It reads the role from environment and builds leader info locally
|
||||
// This is used when running as the leader (started by leader-election.sh)
|
||||
type LocalRoleProvider struct {
|
||||
// LeaderInfoProvider provides leader info for this instance
|
||||
// The Go binary only ever runs as leader (followers loop in bash and never start the binary)
|
||||
type LeaderInfoProvider struct {
|
||||
config *config.Config
|
||||
role Role
|
||||
leaderInfo *LeaderLockInfo
|
||||
}
|
||||
|
||||
// NewLocalRoleProvider creates a RoleProvider for the local instance
|
||||
// It reads META_CORE_ROLE from environment (set by leader-election.sh)
|
||||
func NewLocalRoleProvider(cfg *config.Config) *LocalRoleProvider {
|
||||
roleStr := os.Getenv("META_CORE_ROLE")
|
||||
var role Role
|
||||
switch roleStr {
|
||||
case "leader":
|
||||
role = RoleLeader
|
||||
case "follower":
|
||||
role = RoleFollower
|
||||
default:
|
||||
// Default to leader if not set (for backward compatibility)
|
||||
role = RoleLeader
|
||||
}
|
||||
|
||||
provider := &LocalRoleProvider{
|
||||
// NewLeaderInfoProvider creates a LeaderInfoProvider for the local instance
|
||||
func NewLeaderInfoProvider(cfg *config.Config) *LeaderInfoProvider {
|
||||
provider := &LeaderInfoProvider{
|
||||
config: cfg,
|
||||
role: role,
|
||||
}
|
||||
|
||||
// Build leader info (always, since we're the leader when running)
|
||||
@@ -73,13 +40,8 @@ func NewLocalRoleProvider(cfg *config.Config) *LocalRoleProvider {
|
||||
return provider
|
||||
}
|
||||
|
||||
// Role returns the current role
|
||||
func (p *LocalRoleProvider) Role() Role {
|
||||
return p.role
|
||||
}
|
||||
|
||||
// LeaderInfo returns the leader info
|
||||
func (p *LocalRoleProvider) LeaderInfo() *LeaderLockInfo {
|
||||
func (p *LeaderInfoProvider) LeaderInfo() *LeaderLockInfo {
|
||||
if p.leaderInfo == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -90,13 +52,8 @@ func (p *LocalRoleProvider) LeaderInfo() *LeaderLockInfo {
|
||||
return &info
|
||||
}
|
||||
|
||||
// IsLeader returns true if this instance is the leader
|
||||
func (p *LocalRoleProvider) IsLeader() bool {
|
||||
return p.role == RoleLeader
|
||||
}
|
||||
|
||||
// buildLeaderInfo creates leader info for this instance
|
||||
func (p *LocalRoleProvider) buildLeaderInfo() *LeaderLockInfo {
|
||||
func (p *LeaderInfoProvider) buildLeaderInfo() *LeaderLockInfo {
|
||||
ip := getLocalIP()
|
||||
hostname, _ := os.Hostname()
|
||||
|
||||
@@ -106,14 +63,23 @@ func (p *LocalRoleProvider) buildLeaderInfo() *LeaderLockInfo {
|
||||
baseUrl = fmt.Sprintf("http://%s:%d", ip, p.config.APIPort)
|
||||
}
|
||||
|
||||
// External WebDAV URL: for clients outside Docker (via nginx, potentially HTTPS)
|
||||
// Uses baseUrl which may include custom scheme/port
|
||||
webdavUrl := baseUrl + "/webdav"
|
||||
|
||||
// Internal WebDAV URL: direct to Go WebDAV server (no nginx overhead)
|
||||
// Uses hostname (Docker DNS resolvable) and HTTPPort (9000)
|
||||
webdavUrlInternal := fmt.Sprintf("http://%s:%d/webdav", hostname, p.config.HTTPPort)
|
||||
|
||||
return &LeaderLockInfo{
|
||||
Hostname: hostname,
|
||||
BaseUrl: baseUrl,
|
||||
ApiUrl: fmt.Sprintf("http://%s:%d", ip, p.config.HTTPPort),
|
||||
RedisUrl: fmt.Sprintf("redis://%s:%d", ip, p.config.RedisPort),
|
||||
WebdavUrl: fmt.Sprintf("http://%s:%d/webdav", ip, p.config.APIPort),
|
||||
Timestamp: time.Now().UnixMilli(),
|
||||
PID: os.Getpid(),
|
||||
Hostname: hostname,
|
||||
BaseUrl: baseUrl,
|
||||
ApiUrl: fmt.Sprintf("http://%s:%d", ip, p.config.HTTPPort),
|
||||
RedisUrl: fmt.Sprintf("redis://%s:%d", ip, p.config.RedisPort),
|
||||
WebdavUrl: webdavUrl,
|
||||
WebdavUrlInternal: webdavUrlInternal,
|
||||
Timestamp: time.Now().UnixMilli(),
|
||||
PID: os.Getpid(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user