mirror of https://github.com/h44z/wg-portal.git
rebuild v1 api
This commit is contained in:
parent
49048921e7
commit
14fdeddbe7
|
|
@ -186,14 +186,17 @@ func main() {
|
||||||
|
|
||||||
apiV1Auth := handlersV1.NewAuthenticationHandler(userManager)
|
apiV1Auth := handlersV1.NewAuthenticationHandler(userManager)
|
||||||
apiV1BackendUsers := backendV1.NewUserService(cfg, userManager)
|
apiV1BackendUsers := backendV1.NewUserService(cfg, userManager)
|
||||||
apiV1BackendPeers := backendV1.NewPeerService(cfg, wireGuardManager, userManager)
|
// apiV1BackendPeers := backendV1.NewPeerService(cfg, wireGuardManager, userManager)
|
||||||
|
coreV1Peers := backendV1.NewPeerService(cfg, wireGuardManager, userManager)
|
||||||
apiV1BackendInterfaces := backendV1.NewInterfaceService(cfg, wireGuardManager)
|
apiV1BackendInterfaces := backendV1.NewInterfaceService(cfg, wireGuardManager)
|
||||||
apiV1BackendProvisioning := backendV1.NewProvisioningService(cfg, userManager, wireGuardManager, cfgFileManager)
|
apiV1BackendProvisioning := backendV1.NewProvisioningService(cfg, userManager, wireGuardManager, cfgFileManager)
|
||||||
apiV1BackendMetrics := backendV1.NewMetricsService(cfg, database, userManager, wireGuardManager)
|
apiV1BackendMetrics := backendV1.NewMetricsService(cfg, database, userManager, wireGuardManager)
|
||||||
|
|
||||||
apiV1EndpointUsers := handlersV1.NewUserEndpoint(apiV1Auth, validatorManager, apiV1BackendUsers)
|
apiV1EndpointUsers := handlersV1.NewUserEndpoint(apiV1Auth, validatorManager, apiV1BackendUsers)
|
||||||
|
// apiV1EndpointPeers := handlersV1.NewPeerEndpoint(apiV1Auth, validatorManager, apiV1BackendPeers)
|
||||||
|
apiV1BackendPeers := handlersV1.NewEventingPeerService(coreV1Peers, eventBus)
|
||||||
apiV1EndpointPeers := handlersV1.NewPeerEndpoint(apiV1Auth, validatorManager, apiV1BackendPeers)
|
apiV1EndpointPeers := handlersV1.NewPeerEndpoint(apiV1Auth, validatorManager, apiV1BackendPeers)
|
||||||
apiV1EndpointPeers.SetEventBus(eventBus)
|
//apiV1EndpointPeers.SetEventBus(eventBus)
|
||||||
apiV1EndpointInterfaces := handlersV1.NewInterfaceEndpoint(apiV1Auth, validatorManager, apiV1BackendInterfaces)
|
apiV1EndpointInterfaces := handlersV1.NewInterfaceEndpoint(apiV1Auth, validatorManager, apiV1BackendInterfaces)
|
||||||
apiV1EndpointProvisioning := handlersV1.NewProvisioningEndpoint(apiV1Auth, validatorManager,
|
apiV1EndpointProvisioning := handlersV1.NewProvisioningEndpoint(apiV1Auth, validatorManager,
|
||||||
apiV1BackendProvisioning)
|
apiV1BackendProvisioning)
|
||||||
|
|
|
||||||
|
|
@ -59,18 +59,6 @@ func (e *PeerEndpoint) publish(topic string, args ...any) {
|
||||||
e.bus.Publish(topic, args...)
|
e.bus.Publish(topic, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0-arg для штатних подій (внутрішні підписники)
|
|
||||||
func (e *PeerEndpoint) publish0(topic string) {
|
|
||||||
if e.bus == nil || topic == "" { return }
|
|
||||||
e.bus.Publish(topic) // без аргументів
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1-arg для fanout (йому потрібен рівно ОДИН аргумент)
|
|
||||||
func (e *PeerEndpoint) publish1(topic string, arg any) {
|
|
||||||
if e.bus == nil || topic == "" { return }
|
|
||||||
e.bus.Publish(topic, arg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e PeerEndpoint) RegisterRoutes(g *routegroup.Bundle) {
|
func (e PeerEndpoint) RegisterRoutes(g *routegroup.Bundle) {
|
||||||
apiGroup := g.Mount("/peer")
|
apiGroup := g.Mount("/peer")
|
||||||
apiGroup.Use(e.authenticator.LoggedIn())
|
apiGroup.Use(e.authenticator.LoggedIn())
|
||||||
|
|
@ -259,13 +247,10 @@ func (e PeerEndpoint) handleCreatePost() http.HandlerFunc {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// внутрішні
|
e.publish(app.TopicPeerCreated)
|
||||||
e.publish0(app.TopicPeerCreated)
|
e.publish(app.TopicPeerUpdated)
|
||||||
e.publish0(app.TopicPeerUpdated)
|
e.publish("peer.save", newPeer)
|
||||||
|
e.publish("peers.updated", "v1:create")
|
||||||
// fanout
|
|
||||||
e.publish1("peer.save", newPeer)
|
|
||||||
e.publish1("peers.updated", "v1:create")
|
|
||||||
|
|
||||||
respond.JSON(w, http.StatusOK, models.NewPeer(newPeer))
|
respond.JSON(w, http.StatusOK, models.NewPeer(newPeer))
|
||||||
}
|
}
|
||||||
|
|
@ -314,12 +299,9 @@ func (e PeerEndpoint) handleUpdatePut() http.HandlerFunc {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// внутрішні
|
e.publish(app.TopicPeerUpdated)
|
||||||
e.publish0(app.TopicPeerUpdated)
|
e.publish("peer.save", updatedPeer)
|
||||||
|
e.publish("peers.updated", "v1:update")
|
||||||
// fanout
|
|
||||||
e.publish1("peer.save", updatedPeer)
|
|
||||||
e.publish1("peers.updated", "v1:update")
|
|
||||||
|
|
||||||
respond.JSON(w, http.StatusOK, models.NewPeer(updatedPeer))
|
respond.JSON(w, http.StatusOK, models.NewPeer(updatedPeer))
|
||||||
}
|
}
|
||||||
|
|
@ -357,47 +339,24 @@ func (e PeerEndpoint) handleDelete() http.HandlerFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
// внутрішні
|
// внутрішні
|
||||||
e.publish0(app.TopicPeerDeleted)
|
e.publish(app.TopicPeerDeleted)
|
||||||
e.publish0(app.TopicPeerUpdated)
|
e.publish(app.TopicPeerUpdated)
|
||||||
|
e.publish("peer.delete", domain.PeerIdentifier(id))
|
||||||
// fanout
|
e.publish("peers.updated", "v1:delete")
|
||||||
e.publish1("peer.delete", domain.PeerIdentifier(id))
|
|
||||||
e.publish1("peers.updated", "v1:delete")
|
|
||||||
|
|
||||||
respond.Status(w, http.StatusNoContent)
|
respond.Status(w, http.StatusNoContent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// func (e PeerEndpoint) handleSyncPost() http.HandlerFunc {
|
|
||||||
// return func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
// count, err := e.peers.SyncAllPeersFromDB(r.Context())
|
|
||||||
// if err != nil {
|
|
||||||
// status, model := ParseServiceError(err)
|
|
||||||
// respond.JSON(w, status, model)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// respond.JSON(w, http.StatusOK, map[string]any{
|
|
||||||
// "synced": count,
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
func (e PeerEndpoint) handleSyncPost() http.HandlerFunc {
|
func (e PeerEndpoint) handleSyncPost() http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
// Якщо fanout поставив заголовок "не ехо", забороняємо локальні публікації
|
|
||||||
if r.Header.Get("X-WGP-NoEcho") == "1" {
|
if r.Header.Get("X-WGP-NoEcho") == "1" {
|
||||||
ctx = app.WithNoFanout(ctx)
|
ctx = app.WithNoFanout(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
count, err := e.peers.SyncAllPeersFromDB(ctx)
|
count, err := e.peers.SyncAllPeersFromDB(ctx)
|
||||||
if err != nil {
|
if err != nil { /* ... */ }
|
||||||
status, model := ParseServiceError(err)
|
respond.JSON(w, http.StatusOK, map[string]any{"synced": count})
|
||||||
respond.JSON(w, status, model)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
respond.JSON(w, http.StatusOK, map[string]any{
|
|
||||||
"synced": count,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,24 +3,20 @@ package handlers
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/fedor-git/wg-portal-2/internal/app" // твої топіки і EventPublisher
|
"github.com/fedor-git/wg-portal-2/internal/app"
|
||||||
"github.com/fedor-git/wg-portal-2/internal/domain"
|
"github.com/fedor-git/wg-portal-2/internal/domain"
|
||||||
)
|
)
|
||||||
|
|
||||||
// eventingPeerService декорує будь-яку реалізацію PeerService
|
|
||||||
// та надсилає івенти після Create/Update/Delete.
|
|
||||||
type eventingPeerService struct {
|
type eventingPeerService struct {
|
||||||
inner PeerService
|
inner PeerService
|
||||||
bus app.EventPublisher
|
bus app.EventPublisher
|
||||||
}
|
}
|
||||||
|
|
||||||
// Конструктор: повертає саме PeerService, тож NewPeerEndpoint прийме як є.
|
|
||||||
func NewEventingPeerService(inner PeerService, bus app.EventPublisher) PeerService {
|
func NewEventingPeerService(inner PeerService, bus app.EventPublisher) PeerService {
|
||||||
return &eventingPeerService{inner: inner, bus: bus}
|
return &eventingPeerService{inner: inner, bus: bus}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- read-only як є ----------
|
// ---------- read-only ----------
|
||||||
|
|
||||||
func (s *eventingPeerService) GetForInterface(ctx context.Context, id domain.InterfaceIdentifier) ([]domain.Peer, error) {
|
func (s *eventingPeerService) GetForInterface(ctx context.Context, id domain.InterfaceIdentifier) ([]domain.Peer, error) {
|
||||||
return s.inner.GetForInterface(ctx, id)
|
return s.inner.GetForInterface(ctx, id)
|
||||||
}
|
}
|
||||||
|
|
@ -34,34 +30,37 @@ func (s *eventingPeerService) Prepare(ctx context.Context, id domain.InterfaceId
|
||||||
return s.inner.Prepare(ctx, id)
|
return s.inner.Prepare(ctx, id)
|
||||||
}
|
}
|
||||||
func (s *eventingPeerService) SyncAllPeersFromDB(ctx context.Context) (int, error) {
|
func (s *eventingPeerService) SyncAllPeersFromDB(ctx context.Context) (int, error) {
|
||||||
|
// вхідний sync нічого не фан-аутить сам по собі — і це ок
|
||||||
return s.inner.SyncAllPeersFromDB(ctx)
|
return s.inner.SyncAllPeersFromDB(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- мутації + події ----------
|
// ---------- мутації + fanout ----------
|
||||||
|
|
||||||
func (s *eventingPeerService) Create(ctx context.Context, p *domain.Peer) (*domain.Peer, error) {
|
func (s *eventingPeerService) Create(ctx context.Context, p *domain.Peer) (*domain.Peer, error) {
|
||||||
out, err := s.inner.Create(ctx, p)
|
out, err := s.inner.Create(ctx, p)
|
||||||
if err != nil { return nil, err }
|
if err != nil { return nil, err }
|
||||||
s.publish(app.TopicPeerCreated)
|
s.bumpFanout(ctx, "peer.save", out) // 1 аргумент
|
||||||
s.publish(app.TopicPeerUpdated)
|
s.bumpFanout(ctx, "peers.updated", "v1:create")
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *eventingPeerService) Update(ctx context.Context, id domain.PeerIdentifier, p *domain.Peer) (*domain.Peer, error) {
|
func (s *eventingPeerService) Update(ctx context.Context, id domain.PeerIdentifier, p *domain.Peer) (*domain.Peer, error) {
|
||||||
out, err := s.inner.Update(ctx, id, p)
|
out, err := s.inner.Update(ctx, id, p)
|
||||||
if err != nil { return nil, err }
|
if err != nil { return nil, err }
|
||||||
s.publish(app.TopicPeerUpdated)
|
s.bumpFanout(ctx, "peer.save", out) // 1 аргумент
|
||||||
|
s.bumpFanout(ctx, "peers.updated", "v1:update")
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *eventingPeerService) Delete(ctx context.Context, id domain.PeerIdentifier) error {
|
func (s *eventingPeerService) Delete(ctx context.Context, id domain.PeerIdentifier) error {
|
||||||
if err := s.inner.Delete(ctx, id); err != nil { return err }
|
if err := s.inner.Delete(ctx, id); err != nil { return err }
|
||||||
s.publish(app.TopicPeerDeleted)
|
s.bumpFanout(ctx, "peer.delete", id) // 1 аргумент
|
||||||
s.publish(app.TopicPeerUpdated)
|
s.bumpFanout(ctx, "peers.updated", "v1:delete")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *eventingPeerService) publish(topic string, args ...any) {
|
// ---- helpers ----
|
||||||
|
func (s *eventingPeerService) bumpFanout(ctx context.Context, topic string, arg any) {
|
||||||
if s.bus == nil || topic == "" { return }
|
if s.bus == nil || topic == "" { return }
|
||||||
s.bus.Publish(topic, args...)
|
if app.NoFanout(ctx) { return } // важливо: не ехо
|
||||||
|
s.bus.Publish(topic, arg) // рівно 1 аргумент
|
||||||
}
|
}
|
||||||
|
|
@ -42,15 +42,11 @@ func (m Manager) SyncAllPeersFromDB(ctx context.Context) (int, error) {
|
||||||
slog.ErrorContext(ctx, "peer sync: failed to load peers", "iface", in.Identifier, "err", err)
|
slog.ErrorContext(ctx, "peer sync: failed to load peers", "iface", in.Identifier, "err", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// >>> ДОДАЙ ЦЕ: якщо peers немає – прибираємо всіх на інтерфейсі
|
|
||||||
if len(peers) == 0 {
|
if len(peers) == 0 {
|
||||||
if err := m.clearPeers(ctx, in.Identifier); err != nil {
|
// або ReplacePeers=true з пустим списком, або спеціальний ClearPeers
|
||||||
|
if err := m.wg.ClearPeers(ctx, string(in.Identifier)); err != nil {
|
||||||
slog.ErrorContext(ctx, "clear peers failed", "iface", in.Identifier, "err", err)
|
slog.ErrorContext(ctx, "clear peers failed", "iface", in.Identifier, "err", err)
|
||||||
}
|
}
|
||||||
// не публікуємо івенти, якщо це fanout-sync
|
|
||||||
if !app.NoFanout(ctx) {
|
|
||||||
m.bus.Publish(app.TopicPeerUpdated)
|
|
||||||
}
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
desired := make([]domain.Peer, 0, len(peers))
|
desired := make([]domain.Peer, 0, len(peers))
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue