happyDomain/model/service.go
Pierre-Olivier Mercier e73ed40d5c fix: use validate instead of binding for zero-value fields
Switch ServiceMeta.Domain, ServiceMeta.Ttl, ServiceMeta.NbResources,
and ZoneMeta.DefaultTTL from binding:"required" to validate:"required".
This keeps them marked as required in the OpenAPI spec (swaggo reads
both tags) without gin rejecting valid zero values (0 for uint32,
"" for root domain).
2026-04-11 12:25:10 +07:00

138 lines
4.8 KiB
Go

// This file is part of the happyDomain (R) project.
// Copyright (c) 2020-2024 happyDomain
// Authors: Pierre-Olivier Mercier, et al.
//
// This program is offered under a commercial and under the AGPL license.
// For commercial licensing, contact us at <contact@happydomain.org>.
//
// For AGPL licensing:
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package happydns
import (
"encoding/json"
"time"
)
type Service struct {
ServiceMeta
Service ServiceBody `json:"Service"`
}
func (msg *Service) Meta() *ServiceMeta {
return &msg.ServiceMeta
}
// MarshalJSON computes Comment and NbResources from Service just before
// serialization so that these derived fields are always fresh.
func (s *Service) MarshalJSON() ([]byte, error) {
if s.Service != nil {
s.Comment = s.Service.GenComment()
s.NbResources = s.Service.GetNbResources()
}
type serviceAlias Service
return json.Marshal((*serviceAlias)(s))
}
// ServiceBody represents a service provided by one or more DNS record.
type ServiceBody interface {
// GetNbResources get the number of main Resources contains in the Service.
GetNbResources() int
// GenComment sum up the content of the Service, in a small usefull string.
GenComment() string
// GetRecords retrieves underlying RRs.
GetRecords(domain string, ttl uint32, origin string) ([]Record, error)
}
// MetadataEnricher is implemented by ServiceBody types that store fields
// not derivable from DNS records alone. After re-analysis, EnrichFromPrevious
// is called on the new service body with the previous instance of the same type.
type MetadataEnricher interface {
EnrichFromPrevious(old ServiceBody)
}
// SPFContributor is implemented by services that contribute SPF directives.
// When multiple services implement this interface for the same domain, their
// directives are merged into a single SPF TXT record (RFC 7208 requires
// exactly one SPF record per domain).
type SPFContributor interface {
// GetSPFDirectives returns the SPF directives this service contributes
// (e.g. "include:_spf.google.com", "ip4:203.0.113.0/24").
// The "v=spf1" prefix and "all" mechanism must NOT be included.
GetSPFDirectives() []string
// GetSPFAllPolicy returns the desired "all" policy (e.g. "~all", "-all").
// Return "" if the service has no opinion; the default is "~all".
GetSPFAllPolicy() string
}
// ServiceMeta holds the metadata associated to a Service.
type ServiceMeta struct {
// Type is the string representation of the Service's type.
Type string `json:"_svctype" binding:"required" readonly:"true"`
// Id is the Service's identifier.
Id Identifier `json:"_id,omitempty" swaggertype:"string" readonly:"true"`
// OwnerId is the User's identifier for the current Service.
OwnerId Identifier `json:"_ownerid,omitempty" swaggertype:"string"`
// Domain contains the abstract domain where this Service relates.
Domain string `json:"_domain" validate:"required"`
// Ttl contains the specific TTL for the underlying Resources.
Ttl uint32 `json:"_ttl" validate:"required"`
// Comment is a string that helps user to distinguish the Service.
Comment string `json:"_comment,omitempty"`
// UserComment is a supplementary string defined by the user to
// distinguish the Service.
UserComment string `json:"_mycomment,omitempty"`
// Aliases exposes the aliases defined on this Service.
Aliases []string `json:"_aliases,omitempty"`
// NbResources holds the number of Resources stored inside this Service.
NbResources int `json:"_tmp_hint_nb" validate:"required"`
// PropagatedAt is the estimated time at which the last published changes
// for this service will be fully propagated (old cached records expired).
PropagatedAt *time.Time `json:"_propagated_at,omitempty" format:"date-time"`
}
// ServiceCombined combined ServiceMeta + Service
type ServiceMessage struct {
ServiceMeta
Service json.RawMessage `json:"Service"`
}
func (msg *ServiceMessage) Meta() *ServiceMeta {
return &msg.ServiceMeta
}
type ServiceRecord struct {
Type string `json:"type"`
String string `json:"str"`
RR Record `json:"rr,omitempty"`
}
type ServiceUsecase interface {
ListRecords(*Domain, *Zone, *Service) ([]Record, error)
ValidateService(ServiceBody, Subdomain, Origin) ([]byte, error)
}